From a1981970e54cde66ec7c6a1dc4fb3d010ca06750 Mon Sep 17 00:00:00 2001 From: janwas Date: Sun, 27 Mar 2005 01:44:41 +0000 Subject: [PATCH] new EnumDirEnts call; replaces previous implementation in BaseEntityCollection.cpp that didn't handle subdirectories This was SVN commit r2057. --- source/ps/VFSUtil.cpp | 81 ++++++++++++++++++++++ source/ps/VFSUtil.h | 33 ++++++--- source/simulation/BaseEntityCollection.cpp | 77 ++++++-------------- source/simulation/BaseEntityCollection.h | 3 +- 4 files changed, 125 insertions(+), 69 deletions(-) diff --git a/source/ps/VFSUtil.cpp b/source/ps/VFSUtil.cpp index 3844c57faf..424a5a0c34 100755 --- a/source/ps/VFSUtil.cpp +++ b/source/ps/VFSUtil.cpp @@ -2,6 +2,7 @@ #include "VFSUtil.h" #include "lib/res/vfs.h" +#include "lib/res/vfs_path.h" #include "CLogger.h" #define LOG_CATEGORY "vfs" @@ -38,3 +39,83 @@ bool VFSUtil::FindFiles (CStr& dirname, const char* filter, FileList& files) return true; } + + +// call for each file in the directory; +// if , files in subdirectories are also returned. +// +// note: EnumFileCB path and ent are only valid during the callback. +int VFSUtil::EnumDirEnts(const CStr start_path, const char* user_filter, + bool recursive, EnumDirEntsCB cb, void* context) +{ + // note: currently no need to return subdirectories, + // but enabling it isn't hard. + + char filter_buf[VFS_MAX_PATH]; + const char* filter = user_filter; + bool want_dir = true; + if(user_filter) + { + if(user_filter[0] != '/') + want_dir = false; + + // we need subdirectories and the caller hasn't already requested them + if(recursive && !want_dir) + { + snprintf(filter_buf, sizeof(filter_buf), "/|%s", user_filter); + filter = filter_buf; + } + } + + + // note: FIFO queue instead of recursion is much more efficient + // (less stack usage; avoids seeks by reading all entries in a + // directory consecutively) + + std::deque dir_queue; + dir_queue.push_back(start_path); + + // for each directory: + do + { + // get current directory path from queue + // note: can't refer to the queue contents - those are invalidated + // as soon as a directory is pushed onto it. + char path[VFS_MAX_PATH]; + path_append(path, dir_queue.front().c_str(), ""); + // vfs_open_dir checks this, so ignore failure + const size_t path_len = strlen(path); + dir_queue.pop_front(); + + Handle hdir = vfs_open_dir(path); + if(hdir <= 0) + { + debug_warn("EnumFiles: vfs_open_dir failed"); + continue; + } + + // for each entry (file, subdir) in directory: + vfsDirEnt ent; + while(vfs_next_dirent(hdir, &ent, filter) == 0) + { + // build complete path (vfsDirEnt only stores entry name) + strcpy_s(path+path_len, VFS_MAX_PATH-path_len, ent.name); + + if(VFS_ENT_IS_DIR(ent)) + { + if(recursive) + dir_queue.push_back(path); + + if(want_dir) + cb(path, &ent, context); + } + else + cb(path, &ent, context); + } + + vfs_close_dir(hdir); + } + while(!dir_queue.empty()); + + return 0; +} diff --git a/source/ps/VFSUtil.h b/source/ps/VFSUtil.h index aed49b9884..8c067c3519 100755 --- a/source/ps/VFSUtil.h +++ b/source/ps/VFSUtil.h @@ -1,17 +1,28 @@ #include "ps/CStr.h" +#include "res/vfs.h" namespace VFSUtil { - typedef std::vector FileList; - // Puts the list of files inside 'dirname' matching 'filter' into 'files'. - // 'dirname' shouldn't end with a slash. - // 'filter' is as in vfs_next_dirent: - // - 0: any file; - // - "/": any subdirectory - // - anything else: pattern for name (may include '?' and '*' wildcards) - // 'files' is initially cleared, and undefined on failure. - // On failure, logs an error and returns false. - bool FindFiles(CStr& dirname, const char* filter, FileList& files); +typedef std::vector FileList; -}; +// Puts the list of files inside 'dirname' matching 'filter' into 'files'. +// 'dirname' shouldn't end with a slash. +// 'filter': see vfs_next_dirent +// 'files' is initially cleared, and undefined on failure. +// On failure, logs an error and returns false. +extern bool FindFiles(CStr& dirname, const char* filter, FileList& files); + + +// called by EnumFiles for each file in a directory (optionally +// its subdirectories as well), passing user-specified context. +// note: path and ent parameters are only valid during the callback. +typedef void (*EnumDirEntsCB)(const char* path, const vfsDirEnt* ent, + void* context); + +// call for each file in the directory; +// if , files in subdirectories are also returned. +extern int EnumDirEnts(const CStr path, const char* filter, bool recursive, + EnumDirEntsCB cb, void* context); + +}; // namespace VFSUtil diff --git a/source/simulation/BaseEntityCollection.cpp b/source/simulation/BaseEntityCollection.cpp index 893d832ddc..162d3cab4d 100755 --- a/source/simulation/BaseEntityCollection.cpp +++ b/source/simulation/BaseEntityCollection.cpp @@ -4,48 +4,34 @@ #include "ObjectManager.h" #include "Model.h" #include "CLogger.h" -#include "res/res.h" +#include "VFSUtil.h" #define LOG_CATEGORY "entity" -void CBaseEntityCollection::loadTemplates() + +void CBaseEntityCollection::LoadFile( const char* path ) { - Handle handle; - vfsDirEnt type; - - CStr basepath = "entities/"; - CStr pathname; - - // Iterate through all subdirectories - - Handle maindir = vfs_open_dir( basepath ); - - if( maindir > 0 ) + CBaseEntity* newTemplate = new CBaseEntity(); + if( newTemplate->loadXML( path ) ) { - LoadDirectory( maindir, basepath ); - while( !vfs_next_dirent( maindir, &type, "/" ) ) - { - pathname = basepath + type.name; - - handle = vfs_open_dir( pathname ); - - pathname += "/"; - - if( handle > 0 ) - { - LoadDirectory( handle, pathname ); - vfs_close_dir( handle ); - } - else - { - LOG(ERROR, LOG_CATEGORY, "CBaseEntityCollection::loadTemplates(): Failed to enumerate entity template directory"); - return; - } - } - vfs_close_dir( maindir ); + m_templates.push_back( newTemplate ); + LOG(NORMAL, LOG_CATEGORY, "CBaseEntityCollection::loadTemplates(): Loaded template \"%s\"", path); } else - LOG(ERROR, LOG_CATEGORY, "CBaseEntityCollection::loadTemplates(): Unable to open directory entities/"); + LOG(ERROR, LOG_CATEGORY, "CBaseEntityCollection::loadTemplates(): Couldn't load template \"%s\"", path); +} + +static void LoadFileThunk( const char* path, const vfsDirEnt* ent, void* context ) +{ + CBaseEntityCollection* this_ = (CBaseEntityCollection*)context; + this_->LoadFile(path); +} + +void CBaseEntityCollection::loadTemplates() +{ + // Load all files in entities/ and its subdirectories. + THROW_ERR( VFSUtil::EnumDirEnts( "entities/", "*.xml", true, LoadFileThunk, this ) ); + // Fix up parent links in the templates. @@ -91,27 +77,6 @@ void CBaseEntityCollection::loadTemplates() } } -void CBaseEntityCollection::LoadDirectory( Handle directory, CStr pathname ) -{ - vfsDirEnt file; - while( !vfs_next_dirent( directory, &file, "*.xml") ) - { - CBaseEntity* newTemplate = new CBaseEntity(); - if( newTemplate->loadXML( pathname + file.name ) ) - { - addTemplate( newTemplate ); - LOG(NORMAL, LOG_CATEGORY, "CBaseEntityCollection::loadTemplates(): Loaded template \"%s%s\"", pathname.c_str(), file.name); - } - else - LOG(ERROR, LOG_CATEGORY, "CBaseEntityCollection::loadTemplates(): Couldn't load template \"%s%s\"", pathname.c_str(), file.name); - } -} - -void CBaseEntityCollection::addTemplate( CBaseEntity* temp ) -{ - m_templates.push_back( temp ); -} - CBaseEntity* CBaseEntityCollection::getTemplate( CStrW name ) { for( u16 t = 0; t < m_templates.size(); t++ ) diff --git a/source/simulation/BaseEntityCollection.h b/source/simulation/BaseEntityCollection.h index f73e022720..5cb5f84395 100755 --- a/source/simulation/BaseEntityCollection.h +++ b/source/simulation/BaseEntityCollection.h @@ -33,8 +33,7 @@ public: ~CBaseEntityCollection(); CBaseEntity* getTemplate( CStrW entityType ); void loadTemplates(); - void LoadDirectory( Handle directory, CStr pathname ); - void addTemplate( CBaseEntity* temp ); + void LoadFile( const char* path ); CBaseEntity* getTemplateByActor( CStrW actorName ); // Create a list of the names of all templates, for display in ScEd's