From 6ec9128e78cce42347227ad1614163a7f006fdd1 Mon Sep 17 00:00:00 2001 From: janwas Date: Fri, 6 Nov 2009 11:50:04 +0000 Subject: [PATCH] refactor: move reload functionality into ps/Filesystem to allow reloading non-resource files (e.g. scripts); VFS is now responsible for returning VirtualPath and invalidating the changed file/directory This was SVN commit r7171. --- source/graphics/ColladaManager.cpp | 2 +- source/lib/file/vfs/vfs.cpp | 78 +++++------------------------- source/lib/file/vfs/vfs.h | 14 +++--- source/lib/file/vfs/vfs_tree.cpp | 38 ++++++--------- source/lib/file/vfs/vfs_tree.h | 31 +++++------- source/main.cpp | 2 +- source/ps/Filesystem.cpp | 42 ++++++++++++++++ source/ps/Filesystem.h | 8 +++ source/ps/XML/Xeromyces.cpp | 2 +- 9 files changed, 99 insertions(+), 118 deletions(-) diff --git a/source/graphics/ColladaManager.cpp b/source/graphics/ColladaManager.cpp index 3d9587550b..06e203ae29 100644 --- a/source/graphics/ColladaManager.cpp +++ b/source/graphics/ColladaManager.cpp @@ -239,7 +239,7 @@ VfsPath CColladaManager::GetLoadableFilename(const VfsPath& pathnameNoExtension, // realDaePath_ is "[..]/mods/whatever/art/meshes/whatever.dae" fs::wpath realDaePath_; - LibError ret = g_VFS->RealPath(dae, realDaePath_); + LibError ret = g_VFS->GetRealPath(dae, realDaePath_); debug_assert(ret == INFO::OK); wchar_t realDaeBuf[PATH_MAX]; wcscpy_s(realDaeBuf, ARRAY_SIZE(realDaeBuf), realDaePath_.string().c_str()); diff --git a/source/lib/file/vfs/vfs.cpp b/source/lib/file/vfs/vfs.cpp index 6253c5f755..21c88b94cc 100644 --- a/source/lib/file/vfs/vfs.cpp +++ b/source/lib/file/vfs/vfs.cpp @@ -18,10 +18,6 @@ #include "precompiled.h" #include "vfs.h" -#include "lib/posix/posix_time.h" // usleep -#include "lib/res/h_mgr.h" // h_reload -#include "lib/sysdep/dir_watch.h" - #include "lib/allocators/shared_ptr.h" #include "lib/path_util.h" #include "lib/file/common/file_stats.h" @@ -154,7 +150,7 @@ public: return textRepresentation; } - virtual LibError RealPath(const VfsPath& pathname, fs::wpath& realPathname) + virtual LibError GetRealPath(const VfsPath& pathname, fs::wpath& realPathname) { VfsDirectory* directory; CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, 0)); @@ -162,25 +158,24 @@ public: return INFO::OK; } - virtual LibError VirtualPath(const fs::wpath& realPathname, VfsPath& pathname) + virtual LibError GetVirtualPath(const fs::wpath& realPathname, VfsPath& pathname) { const fs::wpath realPath = AddSlash(realPathname.branch_path()); VfsPath path; - RETURN_ERR(FindRealPathR(realPathname, m_rootDirectory, L"", path)); + RETURN_ERR(FindRealPathR(realPath, m_rootDirectory, L"", path)); pathname = path / realPathname.leaf(); return INFO::OK; } - virtual LibError ReloadChangedFiles() + virtual LibError Invalidate(const VfsPath& pathname) { - std::vector notifications; - RETURN_ERR(dir_watch_Poll(notifications)); - for(size_t i = 0; i < notifications.size(); i++) - { - if(CanIgnore(notifications[i])) - continue; - RETURN_ERR(NotifyChangedR(m_rootDirectory, L"", notifications[i].Pathname())); - } + m_fileCache.Remove(pathname); + + VfsDirectory* directory; + RETURN_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, 0)); + const std::wstring name = pathname.leaf(); + directory->Invalidate(name); + return INFO::OK; } @@ -190,27 +185,6 @@ public: } private: - // try to skip unnecessary work by ignoring uninteresting notifications. - static bool CanIgnore(const DirWatchNotification& notification) - { - // ignore directories - const fs::wpath& pathname = notification.Pathname(); - if(pathname.leaf() == L".") - return true; - - // ignore uninteresting file types (e.g. temp files, or the - // hundreds of XMB files that are generated from XML) - const std::wstring extension = fs::extension(pathname); - const wchar_t* extensionsToIgnore[] = { L".xmb", L".tmp" }; - for(size_t i = 0; i < ARRAY_SIZE(extensionsToIgnore); i++) - { - if(!wcscasecmp(extension.c_str(), extensionsToIgnore[i])) - return true; - } - - return false; - } - LibError FindRealPathR(const fs::wpath& realPath, const VfsDirectory& directory, const VfsPath& curPath, VfsPath& path) { PRealDirectory realDirectory = directory.AssociatedDirectory(); @@ -225,7 +199,7 @@ private: { const std::wstring& subdirectoryName = it->first; const VfsDirectory& subdirectory = it->second; - LibError ret = FindRealPathR(realPath, subdirectory, AddSlash(path/subdirectoryName), path); + LibError ret = FindRealPathR(realPath, subdirectory, AddSlash(curPath/subdirectoryName), path); if(ret == INFO::OK) return INFO::OK; } @@ -233,34 +207,6 @@ private: return ERR::PATH_NOT_FOUND; // NOWARN } - - LibError NotifyChangedR(VfsDirectory& directory, const VfsPath& path, const fs::wpath& realPathname) - { - const fs::wpath realPath = AddSlash(realPathname.branch_path()); - const std::wstring name = realPathname.leaf(); - LibError ret = directory.NotifyChanged(realPath, name); - if(ret == INFO::OK) - { - const VfsPath pathname(path/name); - m_fileCache.Remove(pathname); // invalidate cached data - debug_printf(L"NotifyChangedR: reloading %ls\n", pathname.string().c_str()); - RETURN_ERR(h_reload(pathname)); - return INFO::OK; - } - - VfsDirectory::VfsSubdirectories& subdirectories = directory.Subdirectories(); - for(VfsDirectory::VfsSubdirectories::iterator it = subdirectories.begin(); it != subdirectories.end(); ++it) - { - const std::wstring& subdirectoryName = it->first; - VfsDirectory& subdirectory = it->second; - ret = NotifyChangedR(subdirectory, path/subdirectoryName, realPathname); - if(ret != INFO::SKIPPED) - return ret; - } - - return INFO::SKIPPED; - } - size_t m_cacheSize; FileCache m_fileCache; PITrace m_trace; diff --git a/source/lib/file/vfs/vfs.h b/source/lib/file/vfs/vfs.h index 68a2c5ac9e..7b4a76efba 100644 --- a/source/lib/file/vfs/vfs.h +++ b/source/lib/file/vfs/vfs.h @@ -125,7 +125,7 @@ struct IVFS * * this is useful for passing paths to external libraries. **/ - virtual LibError RealPath(const VfsPath& pathname, fs::wpath& realPathname) = 0; + virtual LibError GetRealPath(const VfsPath& pathname, fs::wpath& realPathname) = 0; /** * retrieve the VFS pathname that corresponds to a real file. @@ -136,15 +136,13 @@ struct IVFS * number of directories; this could be accelerated by only checking * directories below a mount point with a matching real path. **/ - virtual LibError VirtualPath(const fs::wpath& realPathname, VfsPath& pathname) = 0; - + virtual LibError GetVirtualPath(const fs::wpath& realPathname, VfsPath& pathname) = 0; + /** - * poll for directory change notifications and reload all affected files. - * must be called regularly (e.g. once a frame), else notifications - * may be lost. - * note: polling is much simpler than asynchronous notifications. + * indicate that a file has changed; remove its data from the cache and + * arrange for its directory to be updated. **/ - virtual LibError ReloadChangedFiles() = 0; + virtual LibError Invalidate(const VfsPath& pathname) = 0; /** * empty the contents of the filesystem. diff --git a/source/lib/file/vfs/vfs_tree.cpp b/source/lib/file/vfs/vfs_tree.cpp index 6ff7951a86..22fc284603 100644 --- a/source/lib/file/vfs/vfs_tree.cpp +++ b/source/lib/file/vfs/vfs_tree.cpp @@ -122,29 +122,6 @@ VfsDirectory* VfsDirectory::GetSubdirectory(const std::wstring& name) } -void VfsDirectory::Clear() -{ - m_files.clear(); - m_subdirectories.clear(); - m_realDirectory.reset(); - m_shouldPopulate = 0; -} - - -LibError VfsDirectory::NotifyChanged(const fs::wpath& realPath, const std::wstring& name) -{ - // we're not associated with the directory that changed - if(!m_realDirectory || m_realDirectory->Path() != realPath) - return INFO::SKIPPED; - - // one of our files changed; ensure its new version supersedes the - // old by removing it and marking the directory for re-population. - m_files.erase(name); - m_shouldPopulate = 1; - return INFO::OK; -} - - void VfsDirectory::SetAssociatedDirectory(const PRealDirectory& realDirectory) { if(!cpu_CAS(&m_shouldPopulate, 0, 1)) @@ -159,6 +136,21 @@ bool VfsDirectory::ShouldPopulate() } +void VfsDirectory::Invalidate(const std::wstring& name) +{ + m_files.erase(name); + m_shouldPopulate = 1; +} + + +void VfsDirectory::Clear() +{ + m_files.clear(); + m_subdirectories.clear(); + m_realDirectory.reset(); + m_shouldPopulate = 0; +} + //----------------------------------------------------------------------------- diff --git a/source/lib/file/vfs/vfs_tree.h b/source/lib/file/vfs/vfs_tree.h index b0251eaa44..bbc08c6d3b 100644 --- a/source/lib/file/vfs/vfs_tree.h +++ b/source/lib/file/vfs/vfs_tree.h @@ -110,23 +110,6 @@ public: return m_subdirectories; } - VfsSubdirectories& Subdirectories() - { - return m_subdirectories; - } - - /** - * empty file and subdirectory lists (e.g. when rebuilding VFS). - * CAUTION: this invalidates all previously returned pointers. - **/ - void Clear(); - - /** - * check if this directory is affected by changes to . - * @return INFO::OK if this directory was affected, otherwise INFO::SKIPPED - **/ - LibError NotifyChanged(const fs::wpath& realPath, const std::wstring& name); - /** * side effect: the next ShouldPopulate() will return true. **/ @@ -140,10 +123,22 @@ public: /** * @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. + * do so if true is returned -- the flag is reset immediately. **/ bool ShouldPopulate(); + /** + * indicate that a file has changed; ensure its new version supersedes + * the old by removing it and marking the directory for re-population. + **/ + void Invalidate(const std::wstring& name); + + /** + * empty file and subdirectory lists (e.g. when rebuilding VFS). + * CAUTION: this invalidates all previously returned pointers. + **/ + void Clear(); + private: VfsFiles m_files; VfsSubdirectories m_subdirectories; diff --git a/source/main.cpp b/source/main.cpp index 2b9694be95..1ce9b93308 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -239,7 +239,7 @@ static void Frame() { PROFILE_START("reload changed files"); MICROLOG(L"reload changed files"); - g_VFS->ReloadChangedFiles(); + ReloadChangedFiles(); PROFILE_END( "reload changed files"); } diff --git a/source/ps/Filesystem.cpp b/source/ps/Filesystem.cpp index 80546d29ae..8c63a6e391 100644 --- a/source/ps/Filesystem.cpp +++ b/source/ps/Filesystem.cpp @@ -20,6 +20,10 @@ #include "ps/CLogger.h" +#include "lib/posix/posix_time.h" // usleep +#include "lib/res/h_mgr.h" // h_reload +#include "lib/sysdep/dir_watch.h" + #define LOG_CATEGORY L"file" @@ -31,6 +35,44 @@ bool FileExists(const VfsPath& pathname) } +// try to skip unnecessary work by ignoring uninteresting notifications. +static bool CanIgnore(const DirWatchNotification& notification) +{ + // ignore directories + const fs::wpath& pathname = notification.Pathname(); + if(pathname.leaf() == L".") + return true; + + // ignore uninteresting file types (e.g. temp files, or the + // hundreds of XMB files that are generated from XML) + const std::wstring extension = fs::extension(pathname); + const wchar_t* extensionsToIgnore[] = { L".xmb", L".tmp" }; + for(size_t i = 0; i < ARRAY_SIZE(extensionsToIgnore); i++) + { + if(!wcscasecmp(extension.c_str(), extensionsToIgnore[i])) + return true; + } + + return false; +} + +LibError ReloadChangedFiles() +{ + std::vector notifications; + RETURN_ERR(dir_watch_Poll(notifications)); + for(size_t i = 0; i < notifications.size(); i++) + { + if(!CanIgnore(notifications[i])) + { + VfsPath pathname; + RETURN_ERR(g_VFS->GetVirtualPath(notifications[i].Pathname(), pathname)); + RETURN_ERR(g_VFS->Invalidate(pathname)); + RETURN_ERR(h_reload(pathname)); + } + } + return INFO::OK; +} + CVFSFile::CVFSFile() { diff --git a/source/ps/Filesystem.h b/source/ps/Filesystem.h index 4a8250b48f..edf6cf201f 100644 --- a/source/ps/Filesystem.h +++ b/source/ps/Filesystem.h @@ -32,6 +32,14 @@ extern PIVFS g_VFS; extern bool FileExists(const VfsPath& pathname); +/** + * poll for directory change notifications and reload all affected files. + * must be called regularly (e.g. once a frame), else notifications + * may be lost. + * note: polling is much simpler than asynchronous notifications. + **/ +extern LibError ReloadChangedFiles(); + ERROR_GROUP(CVFSFile); ERROR_TYPE(CVFSFile, LoadFailed); ERROR_TYPE(CVFSFile, AlreadyLoaded); diff --git a/source/ps/XML/Xeromyces.cpp b/source/ps/XML/Xeromyces.cpp index 8bcbd3641b..ac84bbd201 100644 --- a/source/ps/XML/Xeromyces.cpp +++ b/source/ps/XML/Xeromyces.cpp @@ -75,7 +75,7 @@ void CXeromyces::GetXMBPath(const PIVFS& vfs, const VfsPath& xmlFilename, const // get real path of XML file (e.g. mods/official/entities/...) fs::wpath XMBRealPath_; - vfs->RealPath(xmlFilename, XMBRealPath_); + vfs->GetRealPath(xmlFilename, XMBRealPath_); wchar_t XMBRealPath[PATH_MAX]; wcscpy_s(XMBRealPath, ARRAY_SIZE(XMBRealPath), XMBRealPath_.string().c_str());