Index: ps/trunk/source/lib/file/vfs/vfs.cpp =================================================================== --- ps/trunk/source/lib/file/vfs/vfs.cpp (revision 6332) +++ ps/trunk/source/lib/file/vfs/vfs.cpp (revision 6333) @@ -1,166 +1,167 @@ /** * ========================================================================= * File : vfs.cpp * Project : 0 A.D. * Description : * ========================================================================= */ // license: GPL; see lib/license.txt #include "precompiled.h" #include "vfs.h" #include "lib/allocators/shared_ptr.h" #include "lib/path_util.h" #include "lib/file/common/file_stats.h" #include "lib/file/common/trace.h" #include "lib/file/archive/archive.h" #include "lib/file/io/io.h" #include "vfs_tree.h" #include "vfs_lookup.h" +#include "vfs_populate.h" #include "file_cache.h" class VFS : public IVFS { public: VFS(size_t cacheSize) : m_cacheSize(cacheSize), m_fileCache(m_cacheSize) , m_trace(CreateTrace(4*MiB)) { } virtual LibError Mount(const VfsPath& mountPoint, const Path& path, int flags /* = 0 */, size_t priority /* = 0 */) { debug_assert(vfs_path_IsDirectory(mountPoint)); // note: mounting subdirectories is now allowed. VfsDirectory* directory; CHECK_ERR(vfs_Lookup(mountPoint, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE)); PRealDirectory realDirectory(new RealDirectory(path, priority, flags)); - directory->Attach(realDirectory); + RETURN_ERR(vfs_Attach(directory, realDirectory)); return INFO::OK; } virtual LibError GetFileInfo(const VfsPath& pathname, FileInfo* pfileInfo) const { VfsDirectory* directory; VfsFile* file; LibError ret = vfs_Lookup(pathname, &m_rootDirectory, directory, &file); if(!pfileInfo) // just indicate if the file exists without raising warnings. return ret; CHECK_ERR(ret); *pfileInfo = FileInfo(file->Name(), file->Size(), file->MTime()); return INFO::OK; } virtual LibError GetDirectoryEntries(const VfsPath& path, FileInfos* files, DirectoryNames* subdirectoryNames) const { debug_assert(vfs_path_IsDirectory(path)); VfsDirectory* directory; CHECK_ERR(vfs_Lookup(path, &m_rootDirectory, directory, 0)); directory->GetEntries(files, subdirectoryNames); return INFO::OK; } // note: only allowing either reads or writes simplifies file cache // coherency (need only invalidate when closing a FILE_WRITE file). virtual LibError CreateFile(const VfsPath& pathname, const shared_ptr& fileContents, size_t size) { VfsDirectory* directory; CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE)); const PRealDirectory& realDirectory = directory->AssociatedDirectory(); const std::string& name = pathname.leaf(); RETURN_ERR(realDirectory->Store(name, fileContents, size)); const VfsFile file(name, (off_t)size, time(0), realDirectory->Priority(), realDirectory); directory->AddFile(file); // wipe out any cached blocks. this is necessary to cover the (rare) case // of file cache contents predating the file write. m_fileCache.Remove(pathname); m_trace->NotifyStore(pathname.string().c_str(), size); return INFO::OK; } // read the entire file. // return number of bytes transferred (see above), or a negative error code. // // if non-NULL, is called for each block transferred, passing . // it returns how much data was actually transferred, or a negative error // code (in which case we abort the transfer and return that value). // the callback mechanism is useful for user progress notification or // processing data while waiting for the next I/O to complete // (quasi-parallel, without the complexity of threads). virtual LibError LoadFile(const VfsPath& pathname, shared_ptr& fileContents, size_t& size) { const bool isCacheHit = m_fileCache.Retrieve(pathname, fileContents, size); if(!isCacheHit) { VfsDirectory* directory; VfsFile* file; CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file)); size = file->Size(); // safely handle zero-length files if(!size) fileContents = DummySharedPtr((u8*)0); else if(size > m_cacheSize) { fileContents = io_Allocate(size); RETURN_ERR(file->Load(fileContents)); } else { fileContents = m_fileCache.Reserve(size); RETURN_ERR(file->Load(fileContents)); m_fileCache.Add(pathname, fileContents, size); } } stats_io_user_request(size); stats_cache(isCacheHit? CR_HIT : CR_MISS, size); m_trace->NotifyLoad(pathname.string().c_str(), size); return INFO::OK; } // rebuild the VFS, i.e. re-mount everything. open files are not affected. // necessary after loose files or directories change, so that the VFS // "notices" the changes and updates file locations. res calls this after // dir_watch reports changes; can also be called from the console after a // rebuild command. there is no provision for updating single VFS dirs - // it's not worth the trouble. virtual void Clear() { m_rootDirectory.ClearR(); } virtual void Display() const { m_rootDirectory.DisplayR(0); } virtual LibError GetRealPath(const VfsPath& pathname, Path& realPathname) { VfsDirectory* directory; CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, 0)); const PRealDirectory& realDirectory = directory->AssociatedDirectory(); realPathname = realDirectory->GetPath() / pathname.leaf(); return INFO::OK; } private: size_t m_cacheSize; FileCache m_fileCache; PITrace m_trace; mutable VfsDirectory m_rootDirectory; }; //----------------------------------------------------------------------------- PIVFS CreateVfs(size_t cacheSize) { return PIVFS(new VFS(cacheSize)); } Index: ps/trunk/source/lib/file/vfs/vfs_tree.h =================================================================== --- ps/trunk/source/lib/file/vfs/vfs_tree.h (revision 6332) +++ ps/trunk/source/lib/file/vfs/vfs_tree.h (revision 6333) @@ -1,106 +1,109 @@ /** * ========================================================================= * File : vfs_tree.h * Project : 0 A.D. * Description : 'tree' of VFS directories and files * ========================================================================= */ // license: GPL; see lib/license.txt #ifndef INCLUDED_VFS_TREE #define INCLUDED_VFS_TREE #include "lib/file/file_system.h" // FileInfo #include "lib/file/common/file_loader.h" // PIFileLoader #include "lib/file/common/real_directory.h" // PRealDirectory class VfsFile { public: VfsFile(const std::string& name, off_t size, time_t mtime, size_t priority, const PIFileLoader& provider); const std::string& Name() const { return m_name; } off_t Size() const { return m_size; } time_t MTime() const { return m_mtime; } bool IsSupersededBy(const VfsFile& file) const; void GenerateDescription(char* text, size_t maxChars) const; LibError Load(const shared_ptr& buf) const; private: std::string m_name; off_t m_size; time_t m_mtime; size_t m_priority; PIFileLoader m_loader; }; class VfsDirectory { public: VfsDirectory(); /** * @return address of existing or newly inserted file; remains * valid until ClearR is called (i.e. VFS is rebuilt). **/ VfsFile* AddFile(const VfsFile& file); /** * @return address of existing or newly inserted subdirectory; remains * valid until ClearR is called (i.e. VFS is rebuilt). **/ VfsDirectory* AddSubdirectory(const std::string& name); VfsFile* GetFile(const std::string& name); VfsDirectory* GetSubdirectory(const std::string& name); void GetEntries(FileInfos* files, DirectoryNames* subdirectories) const; void DisplayR(size_t depth) const; void ClearR(); - void Attach(const PRealDirectory& realDirectory); + /** + * side effect: the next ShouldPopulate() will return true. + **/ + void SetAssociatedDirectory(const PRealDirectory& realDirectory); const PRealDirectory& AssociatedDirectory() const { return m_realDirectory; } /** * @return whether this directory should be populated from its * AssociatedDirectory(). note that calling this is a promise to * do so if true is returned -- the flag is immediately reset. **/ bool ShouldPopulate(); private: typedef std::map VfsFiles; VfsFiles m_files; typedef std::map VfsSubdirectories; VfsSubdirectories m_subdirectories; PRealDirectory m_realDirectory; volatile uintptr_t m_shouldPopulate; // (cpu_CAS can't be used on bool) }; #endif // #ifndef INCLUDED_VFS_TREE Index: ps/trunk/source/lib/file/vfs/vfs_lookup.cpp =================================================================== --- ps/trunk/source/lib/file/vfs/vfs_lookup.cpp (revision 6332) +++ ps/trunk/source/lib/file/vfs/vfs_lookup.cpp (revision 6333) @@ -1,199 +1,84 @@ /** * ========================================================================= * File : vfs_lookup.cpp * Project : 0 A.D. * Description : look up directories/files by traversing path components. * ========================================================================= */ // license: GPL; see lib/license.txt #include "precompiled.h" #include "vfs_lookup.h" #include "lib/path_util.h" // path_foreach_component -#include "lib/file/file_system_posix.h" -#include "lib/file/archive/archive_zip.h" -#include "vfs_tree.h" #include "vfs.h" // error codes +#include "vfs_tree.h" +#include "vfs_populate.h" #include "lib/timer.h" TIMER_ADD_CLIENT(tc_lookup); -//----------------------------------------------------------------------------- - -static FileSystem_Posix s_fileSystemPosix; -static std::vector s_looseFiles; -static size_t s_numArchivedFiles; - -// helper class that allows breaking up the logic into sub-functions without -// always having to pass directory/realDirectory as parameters. -class PopulateHelper : noncopyable -{ -public: - PopulateHelper(VfsDirectory* directory, const PRealDirectory& realDirectory) - : m_directory(directory), m_realDirectory(realDirectory) - { - } - - LibError AddEntries() const - { - FileInfos files; files.reserve(100); - DirectoryNames subdirectoryNames; subdirectoryNames.reserve(20); - RETURN_ERR(s_fileSystemPosix.GetDirectoryEntries(m_realDirectory->GetPath(), &files, &subdirectoryNames)); - RETURN_ERR(AddFiles(files)); - AddSubdirectories(subdirectoryNames); - return INFO::OK; - } - -private: - void AddFile(const FileInfo& fileInfo) const - { - const VfsFile file(fileInfo.Name(), fileInfo.Size(), fileInfo.MTime(), m_realDirectory->Priority(), m_realDirectory); - const VfsFile* pfile = m_directory->AddFile(file); - - // notify archive builder that this file could be archived but - // currently isn't; if there are too many of these, archive will - // be rebuilt. - // note: check if archivable to exclude stuff like screenshots - // from counting towards the threshold. - if(m_realDirectory->Flags() & VFS_MOUNT_ARCHIVABLE) - s_looseFiles.push_back(pfile); - } - - static void AddArchiveFile(const VfsPath& pathname, const FileInfo& fileInfo, PIArchiveFile archiveFile, uintptr_t cbData) - { - PopulateHelper* this_ = (PopulateHelper*)cbData; - - // (we have to create missing subdirectoryNames because archivers - // don't always place directory entries before their files) - const int flags = VFS_LOOKUP_ADD; - VfsDirectory* directory; - WARN_ERR(vfs_Lookup(pathname, this_->m_directory, directory, 0, flags)); - const VfsFile file(fileInfo.Name(), fileInfo.Size(), fileInfo.MTime(), this_->m_realDirectory->Priority(), archiveFile); - directory->AddFile(file); - s_numArchivedFiles++; - } - - LibError AddFiles(const FileInfos& files) const - { - const Path path(m_realDirectory->GetPath()); - - for(size_t i = 0; i < files.size(); i++) - { - const std::string& name = files[i].Name(); - - const char* extension = path_extension(name.c_str()); - if(strcasecmp(extension, "zip") == 0) - { - PIArchiveReader archiveReader = CreateArchiveReader_Zip(path/name); - RETURN_ERR(archiveReader->ReadEntries(AddArchiveFile, (uintptr_t)this)); - } - else // regular (non-archive) file - AddFile(files[i]); - } - - return INFO::OK; - } - - void AddSubdirectories(const DirectoryNames& subdirectoryNames) const - { - for(size_t i = 0; i < subdirectoryNames.size(); i++) - { - // skip version control directories - this avoids cluttering the - // VFS with hundreds of irrelevant files. - if(strcasecmp(subdirectoryNames[i].c_str(), ".svn") == 0) - continue; - - VfsDirectory* subdirectory = m_directory->AddSubdirectory(subdirectoryNames[i]); - subdirectory->Attach(CreateRealSubdirectory(m_realDirectory, subdirectoryNames[i])); - } - } - - VfsDirectory* const m_directory; - PRealDirectory m_realDirectory; -}; - - -static LibError Populate(VfsDirectory* directory) -{ - if(!directory->ShouldPopulate()) - return INFO::OK; - - const PRealDirectory& realDirectory = directory->AssociatedDirectory(); - - if(realDirectory->Flags() & VFS_MOUNT_WATCH) - realDirectory->Watch(); - - PopulateHelper helper(directory, realDirectory); - RETURN_ERR(helper.AddEntries()); - - return INFO::OK; -} - - -//----------------------------------------------------------------------------- - LibError vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDirectory*& directory, VfsFile** pfile, int flags) { TIMER_ACCRUE(tc_lookup); // extract and validate flags (ensure no unknown bits are set) const bool addMissingDirectories = (flags & VFS_LOOKUP_ADD) != 0; const bool createMissingDirectories = (flags & VFS_LOOKUP_CREATE) != 0; debug_assert((flags & ~(VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE)) == 0); if(pfile) *pfile = 0; directory = startDirectory; - RETURN_ERR(Populate(directory)); + RETURN_ERR(vfs_Populate(directory)); // early-out for pathname == "" when mounting into VFS root if(pathname.empty()) // (prevent iterator error in loop end condition) return INFO::OK; // for each directory component: VfsPath::iterator it; // (used outside of loop to get filename) for(it = pathname.begin(); it != --pathname.end(); ++it) { const std::string& subdirectoryName = *it; VfsDirectory* subdirectory = directory->GetSubdirectory(subdirectoryName); if(!subdirectory) { if(addMissingDirectories) subdirectory = directory->AddSubdirectory(subdirectoryName); else return ERR::VFS_DIR_NOT_FOUND; // NOWARN } if(createMissingDirectories && !subdirectory->AssociatedDirectory()) { Path currentPath; if(directory->AssociatedDirectory()) // (is NULL when mounting into root) currentPath = directory->AssociatedDirectory()->GetPath()/subdirectoryName; if(mkdir(currentPath.external_directory_string().c_str(), S_IRWXO|S_IRWXU|S_IRWXG) == 0) { PRealDirectory realDirectory(new RealDirectory(currentPath, 0, 0)); - subdirectory->Attach(realDirectory); + RETURN_ERR(vfs_Attach(subdirectory, realDirectory)); } } - RETURN_ERR(Populate(subdirectory)); + RETURN_ERR(vfs_Populate(subdirectory)); directory = subdirectory; } if(pfile) { const std::string& filename = *it; debug_assert(filename != "."); // asked for file but specified directory path *pfile = directory->GetFile(filename); if(!*pfile) return ERR::VFS_FILE_NOT_FOUND; // NOWARN } return INFO::OK; } Index: ps/trunk/source/lib/file/vfs/vfs_populate.cpp =================================================================== --- ps/trunk/source/lib/file/vfs/vfs_populate.cpp (nonexistent) +++ ps/trunk/source/lib/file/vfs/vfs_populate.cpp (revision 6333) @@ -0,0 +1,138 @@ +/** + * ========================================================================= + * File : vfs_populate.cpp + * Project : 0 A.D. + * Description : populate VFS directories with files + * ========================================================================= + */ + +// license: GPL; see lib/license.txt + +#include "precompiled.h" +#include "vfs_populate.h" + +#include "lib/path_util.h" +#include "lib/file/file_system_posix.h" +#include "lib/file/archive/archive_zip.h" +#include "vfs_tree.h" +#include "vfs_lookup.h" +#include "vfs.h" // error codes + + +static FileSystem_Posix s_fileSystemPosix; +static std::vector s_looseFiles; +static size_t s_numArchivedFiles; + +// helper class that allows breaking up the logic into sub-functions without +// always having to pass directory/realDirectory as parameters. +class PopulateHelper : noncopyable +{ +public: + PopulateHelper(VfsDirectory* directory, const PRealDirectory& realDirectory) + : m_directory(directory), m_realDirectory(realDirectory) + { + } + + LibError AddEntries() const + { + FileInfos files; files.reserve(100); + DirectoryNames subdirectoryNames; subdirectoryNames.reserve(20); + RETURN_ERR(s_fileSystemPosix.GetDirectoryEntries(m_realDirectory->GetPath(), &files, &subdirectoryNames)); + RETURN_ERR(AddFiles(files)); + AddSubdirectories(subdirectoryNames); + return INFO::OK; + } + +private: + void AddFile(const FileInfo& fileInfo) const + { + const VfsFile file(fileInfo.Name(), fileInfo.Size(), fileInfo.MTime(), m_realDirectory->Priority(), m_realDirectory); + const VfsFile* pfile = m_directory->AddFile(file); + + // notify archive builder that this file could be archived but + // currently isn't; if there are too many of these, archive will + // be rebuilt. + // note: check if archivable to exclude stuff like screenshots + // from counting towards the threshold. + if(m_realDirectory->Flags() & VFS_MOUNT_ARCHIVABLE) + s_looseFiles.push_back(pfile); + } + + static void AddArchiveFile(const VfsPath& pathname, const FileInfo& fileInfo, PIArchiveFile archiveFile, uintptr_t cbData) + { + PopulateHelper* this_ = (PopulateHelper*)cbData; + + // (we have to create missing subdirectoryNames because archivers + // don't always place directory entries before their files) + const int flags = VFS_LOOKUP_ADD; + VfsDirectory* directory; + WARN_ERR(vfs_Lookup(pathname, this_->m_directory, directory, 0, flags)); + const VfsFile file(fileInfo.Name(), fileInfo.Size(), fileInfo.MTime(), this_->m_realDirectory->Priority(), archiveFile); + directory->AddFile(file); + s_numArchivedFiles++; + } + + LibError AddFiles(const FileInfos& files) const + { + const Path path(m_realDirectory->GetPath()); + + for(size_t i = 0; i < files.size(); i++) + { + const std::string& name = files[i].Name(); + + const char* extension = path_extension(name.c_str()); + if(strcasecmp(extension, "zip") == 0) + { + PIArchiveReader archiveReader = CreateArchiveReader_Zip(path/name); + RETURN_ERR(archiveReader->ReadEntries(AddArchiveFile, (uintptr_t)this)); + } + else // regular (non-archive) file + AddFile(files[i]); + } + + return INFO::OK; + } + + void AddSubdirectories(const DirectoryNames& subdirectoryNames) const + { + for(size_t i = 0; i < subdirectoryNames.size(); i++) + { + // skip version control directories - this avoids cluttering the + // VFS with hundreds of irrelevant files. + if(strcasecmp(subdirectoryNames[i].c_str(), ".svn") == 0) + continue; + + VfsDirectory* subdirectory = m_directory->AddSubdirectory(subdirectoryNames[i]); + PRealDirectory realDirectory = CreateRealSubdirectory(m_realDirectory, subdirectoryNames[i]); + vfs_Attach(subdirectory, realDirectory); + } + } + + VfsDirectory* const m_directory; + PRealDirectory m_realDirectory; +}; + + +LibError vfs_Populate(VfsDirectory* directory) +{ + if(!directory->ShouldPopulate()) + return INFO::OK; + + const PRealDirectory& realDirectory = directory->AssociatedDirectory(); + + if(realDirectory->Flags() & VFS_MOUNT_WATCH) + realDirectory->Watch(); + + PopulateHelper helper(directory, realDirectory); + RETURN_ERR(helper.AddEntries()); + + return INFO::OK; +} + + +LibError vfs_Attach(VfsDirectory* directory, const PRealDirectory& realDirectory) +{ + RETURN_ERR(vfs_Populate(directory)); + directory->SetAssociatedDirectory(realDirectory); + return INFO::OK; +} Index: ps/trunk/source/lib/file/vfs/vfs_populate.h =================================================================== --- ps/trunk/source/lib/file/vfs/vfs_populate.h (nonexistent) +++ ps/trunk/source/lib/file/vfs/vfs_populate.h (revision 6333) @@ -0,0 +1,41 @@ +/** + * ========================================================================= + * File : vfs_populate.h + * Project : 0 A.D. + * Description : populate VFS directories with files + * ========================================================================= + */ + +// license: GPL; see lib/license.txt + +#ifndef INCLUDED_VFS_POPULATE +#define INCLUDED_VFS_POPULATE + +#include "lib/file/common/real_directory.h" + +class VfsDirectory; + +/** + * attach a real directory to a VFS directory. + * + * when the VFS directory is accessed, it will first be populated from + * the real directory. (this delays the impact of mounting a large + * directory, distributing the cost from startup to the first accesses + * to each subdirectory.) + * + * note: the most recently attached real directory will be used when + * creating files in the VFS directory. + **/ +extern LibError vfs_Attach(VfsDirectory* directory, const PRealDirectory& realDirectory); + +/** + * populate the directory from the attached real directory. + * + * adds each real file and subdirectory entry to the VFS directory. + * the full contents of any archives in the real directory are also added. + * + * has no effect if no directory has been attached since the last populate. + **/ +extern LibError vfs_Populate(VfsDirectory* directory); + +#endif // #ifndef INCLUDED_VFS_POPULATE Index: ps/trunk/source/lib/file/vfs/vfs_tree.cpp =================================================================== --- ps/trunk/source/lib/file/vfs/vfs_tree.cpp (revision 6332) +++ ps/trunk/source/lib/file/vfs/vfs_tree.cpp (revision 6333) @@ -1,207 +1,203 @@ /** * ========================================================================= * File : vfs_tree.cpp * Project : 0 A.D. * Description : 'tree' of VFS directories and files * ========================================================================= */ // license: GPL; see lib/license.txt #include "precompiled.h" #include "vfs_tree.h" #include "lib/file/common/file_stats.h" #include "lib/sysdep/cpu.h" //----------------------------------------------------------------------------- VfsFile::VfsFile(const std::string& name, off_t size, time_t mtime, size_t priority, const PIFileLoader& loader) : m_name(name), m_size(size), m_mtime(mtime), m_priority(priority), m_loader(loader) { } bool VfsFile::IsSupersededBy(const VfsFile& file) const { // 1) priority (override mods) if(file.m_priority < m_priority) // lower priority return false; // 2) timestamp { const double howMuchNewer = difftime(file.MTime(), MTime()); const double threshold = 2.0; // [seconds]; resolution provided by FAT if(howMuchNewer > threshold) // newer timestamp return true; if(howMuchNewer < threshold) // older timestamp return false; // else: "equal" (tolerating small differences due to FAT's low // mtime resolution) } // 3) precedence (efficiency of file provider) if(file.m_loader->Precedence() < m_loader->Precedence()) // less efficient return false; return true; } void VfsFile::GenerateDescription(char* text, size_t maxChars) const { char timestamp[25]; const time_t mtime = MTime(); strftime(timestamp, ARRAY_SIZE(timestamp), "%a %b %d %H:%M:%S %Y", localtime(&mtime)); // build format string (set width of name field so that everything // lines up correctly) const char* fmt = "(%c; %6d; %s)\n"; sprintf_s(text, maxChars, fmt, m_loader->LocationCode(), Size(), timestamp); } LibError VfsFile::Load(const shared_ptr& buf) const { return m_loader->Load(Name(), buf, Size()); } //----------------------------------------------------------------------------- VfsDirectory::VfsDirectory() : m_shouldPopulate(0) { } VfsFile* VfsDirectory::AddFile(const VfsFile& file) { std::pair value = std::make_pair(file.Name(), file); std::pair ret = m_files.insert(value); if(!ret.second) // already existed { VfsFile& previousFile = ret.first->second; const VfsFile& newFile = value.second; if(previousFile.IsSupersededBy(newFile)) previousFile = newFile; } else stats_vfs_file_add(file.Size()); return &(*ret.first).second; } // rationale: passing in a pre-constructed VfsDirectory and copying that into // our map would be slower and less convenient for the caller. VfsDirectory* VfsDirectory::AddSubdirectory(const std::string& name) { std::pair value = std::make_pair(name, VfsDirectory()); std::pair ret = m_subdirectories.insert(value); return &(*ret.first).second; } VfsFile* VfsDirectory::GetFile(const std::string& name) { VfsFiles::iterator it = m_files.find(name); if(it == m_files.end()) return 0; return &it->second; } VfsDirectory* VfsDirectory::GetSubdirectory(const std::string& name) { VfsSubdirectories::iterator it = m_subdirectories.find(name); if(it == m_subdirectories.end()) return 0; return &it->second; } void VfsDirectory::GetEntries(FileInfos* files, DirectoryNames* subdirectoryNames) const { if(files) { files->clear(); files->reserve(m_files.size()); for(VfsFiles::const_iterator it = m_files.begin(); it != m_files.end(); ++it) files->push_back(FileInfo(it->second.Name(), it->second.Size(), it->second.MTime())); } if(subdirectoryNames) { subdirectoryNames->clear(); subdirectoryNames->reserve(m_subdirectories.size()); for(VfsSubdirectories::const_iterator it = m_subdirectories.begin(); it != m_subdirectories.end(); ++it) subdirectoryNames->push_back(it->first); } } void VfsDirectory::DisplayR(size_t depth) const { static const char indent[] = " "; const size_t maxNameChars = 80 - depth*(sizeof(indent)-1); char fmt[20]; sprintf_s(fmt, ARRAY_SIZE(fmt), "%%-%d.%ds %%s", maxNameChars, maxNameChars); for(VfsFiles::const_iterator it = m_files.begin(); it != m_files.end(); ++it) { const std::string& name = it->first; const VfsFile& file = it->second; char description[100]; file.GenerateDescription(description, ARRAY_SIZE(description)); for(size_t i = 0; i < depth+1; i++) printf(indent); printf(fmt, name.c_str(), description); } for(VfsSubdirectories::const_iterator it = m_subdirectories.begin(); it != m_subdirectories.end(); ++it) { const std::string& name = it->first; const VfsDirectory& directory = it->second; for(size_t i = 0; i < depth+1; i++) printf(indent); printf("[%s/]\n", name.c_str()); directory.DisplayR(depth+1); } } void VfsDirectory::ClearR() { for(VfsSubdirectories::iterator it = m_subdirectories.begin(); it != m_subdirectories.end(); ++it) it->second.ClearR(); m_files.clear(); m_subdirectories.clear(); m_realDirectory.reset(); m_shouldPopulate = 0; } -void VfsDirectory::Attach(const PRealDirectory& realDirectory) +void VfsDirectory::SetAssociatedDirectory(const PRealDirectory& realDirectory) { if(!cpu_CAS(&m_shouldPopulate, 0, 1)) - { - debug_assert(0); // multiple Attach() calls without an intervening ShouldPopulate() - return; - } - + debug_assert(0); // caller didn't check ShouldPopulate m_realDirectory = realDirectory; } bool VfsDirectory::ShouldPopulate() { return cpu_CAS(&m_shouldPopulate, 1, 0); // test and reset }