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.
This commit is contained in:
parent
9662666403
commit
6ec9128e78
@ -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());
|
||||
|
@ -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<DirWatchNotification> 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;
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
@ -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 <pathname>.
|
||||
* @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;
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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<DirWatchNotification> 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()
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user