Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/lib/file/vfs/vfs.cpp
/* Copyright (C) 2014 Wildfire Games. | /* Copyright (C) 2017 Wildfire Games. | ||||
* | * | ||||
* Permission is hereby granted, free of charge, to any person obtaining | * Permission is hereby granted, free of charge, to any person obtaining | ||||
* a copy of this software and associated documentation files (the | * a copy of this software and associated documentation files (the | ||||
* "Software"), to deal in the Software without restriction, including | * "Software"), to deal in the Software without restriction, including | ||||
* without limitation the rights to use, copy, modify, merge, publish, | * without limitation the rights to use, copy, modify, merge, publish, | ||||
* distribute, sublicense, and/or sell copies of the Software, and to | * distribute, sublicense, and/or sell copies of the Software, and to | ||||
* permit persons to whom the Software is furnished to do so, subject to | * permit persons to whom the Software is furnished to do so, subject to | ||||
* the following conditions: | * the following conditions: | ||||
Show All 18 Lines | |||||
#include "lib/file/file_system.h" | #include "lib/file/file_system.h" | ||||
#include "lib/file/common/file_stats.h" | #include "lib/file/common/file_stats.h" | ||||
#include "lib/file/common/trace.h" | #include "lib/file/common/trace.h" | ||||
#include "lib/file/archive/archive.h" | #include "lib/file/archive/archive.h" | ||||
#include "lib/file/io/io.h" | #include "lib/file/io/io.h" | ||||
#include "lib/file/vfs/vfs_tree.h" | #include "lib/file/vfs/vfs_tree.h" | ||||
#include "lib/file/vfs/vfs_lookup.h" | #include "lib/file/vfs/vfs_lookup.h" | ||||
#include "lib/file/vfs/vfs_populate.h" | #include "lib/file/vfs/vfs_populate.h" | ||||
#include "lib/file/vfs/file_cache.h" | |||||
static const StatusDefinition vfsStatusDefinitions[] = { | static const StatusDefinition vfsStatusDefinitions[] = { | ||||
{ ERR::VFS_DIR_NOT_FOUND, L"VFS directory not found" }, | { ERR::VFS_DIR_NOT_FOUND, L"VFS directory not found" }, | ||||
{ ERR::VFS_FILE_NOT_FOUND, L"VFS file not found" }, | { ERR::VFS_FILE_NOT_FOUND, L"VFS file not found" }, | ||||
{ ERR::VFS_ALREADY_MOUNTED, L"VFS path already mounted" } | { ERR::VFS_ALREADY_MOUNTED, L"VFS path already mounted" } | ||||
}; | }; | ||||
STATUS_ADD_DEFINITIONS(vfsStatusDefinitions); | STATUS_ADD_DEFINITIONS(vfsStatusDefinitions); | ||||
static pthread_mutex_t vfs_mutex = PTHREAD_MUTEX_INITIALIZER; | static pthread_mutex_t vfs_mutex = PTHREAD_MUTEX_INITIALIZER; | ||||
namespace { | namespace { | ||||
struct ScopedLock | struct ScopedLock | ||||
{ | { | ||||
ScopedLock() { pthread_mutex_lock(&vfs_mutex); } | ScopedLock() { pthread_mutex_lock(&vfs_mutex); } | ||||
~ScopedLock() { pthread_mutex_unlock(&vfs_mutex); } | ~ScopedLock() { pthread_mutex_unlock(&vfs_mutex); } | ||||
}; | }; | ||||
} // namespace | } // namespace | ||||
class VFS : public IVFS | class VFS : public IVFS | ||||
{ | { | ||||
public: | public: | ||||
VFS(size_t cacheSize) | VFS() : m_trace(CreateDummyTrace(8*MiB)) | ||||
: m_cacheSize(cacheSize), m_fileCache(m_cacheSize) | |||||
, m_trace(CreateDummyTrace(8*MiB)) | |||||
{ | { | ||||
} | } | ||||
virtual Status Mount(const VfsPath& mountPoint, const OsPath& path, size_t flags /* = 0 */, size_t priority /* = 0 */) | virtual Status Mount(const VfsPath& mountPoint, const OsPath& path, size_t flags /* = 0 */, size_t priority /* = 0 */) | ||||
{ | { | ||||
ScopedLock s; | ScopedLock s; | ||||
if(!DirectoryExists(path)) | if(!DirectoryExists(path)) | ||||
{ | { | ||||
Show All 9 Lines | virtual Status Mount(const VfsPath& mountPoint, const OsPath& path, size_t flags /* = 0 */, size_t priority /* = 0 */) | ||||
PRealDirectory realDirectory(new RealDirectory(path, priority, flags)); | PRealDirectory realDirectory(new RealDirectory(path, priority, flags)); | ||||
RETURN_STATUS_IF_ERR(vfs_Attach(directory, realDirectory)); | RETURN_STATUS_IF_ERR(vfs_Attach(directory, realDirectory)); | ||||
return INFO::OK; | return INFO::OK; | ||||
} | } | ||||
virtual Status GetFileInfo(const VfsPath& pathname, CFileInfo* pfileInfo) const | virtual Status GetFileInfo(const VfsPath& pathname, CFileInfo* pfileInfo) const | ||||
{ | { | ||||
ScopedLock s; | ScopedLock s; | ||||
VfsDirectory* directory; VfsFile* file; | VfsDirectory* directory; | ||||
VfsFile* file; | |||||
Status ret = vfs_Lookup(pathname, &m_rootDirectory, directory, &file); | Status ret = vfs_Lookup(pathname, &m_rootDirectory, directory, &file); | ||||
if(!pfileInfo) // just indicate if the file exists without raising warnings. | if(!pfileInfo) // just indicate if the file exists without raising warnings. | ||||
return ret; | return ret; | ||||
WARN_RETURN_STATUS_IF_ERR(ret); | WARN_RETURN_STATUS_IF_ERR(ret); | ||||
*pfileInfo = CFileInfo(file->Name(), file->Size(), file->MTime()); | *pfileInfo = CFileInfo(file->Name(), file->Size(), file->MTime()); | ||||
return INFO::OK; | return INFO::OK; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | if (st == ERR::FILE_ACCESS) | ||||
return ERR::FILE_ACCESS; | return ERR::FILE_ACCESS; | ||||
WARN_RETURN_STATUS_IF_ERR(st); | WARN_RETURN_STATUS_IF_ERR(st); | ||||
const PRealDirectory& realDirectory = directory->AssociatedDirectory(); | const PRealDirectory& realDirectory = directory->AssociatedDirectory(); | ||||
const OsPath name = pathname.Filename(); | const OsPath name = pathname.Filename(); | ||||
RETURN_STATUS_IF_ERR(realDirectory->Store(name, fileContents, size)); | RETURN_STATUS_IF_ERR(realDirectory->Store(name, fileContents, size)); | ||||
// 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); | |||||
const VfsFile file(name, size, time(0), realDirectory->Priority(), realDirectory); | const VfsFile file(name, size, time(0), realDirectory->Priority(), realDirectory); | ||||
directory->AddFile(file); | directory->AddFile(file); | ||||
m_trace->NotifyStore(pathname, size); | m_trace->NotifyStore(pathname, size); | ||||
return INFO::OK; | return INFO::OK; | ||||
} | } | ||||
virtual Status ReplaceFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size) | virtual Status ReplaceFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size) | ||||
Show All 11 Lines | if (st == ERR::VFS_FILE_NOT_FOUND) | ||||
return CreateFile(pathname, fileContents, size); | return CreateFile(pathname, fileContents, size); | ||||
} | } | ||||
WARN_RETURN_STATUS_IF_ERR(st); | WARN_RETURN_STATUS_IF_ERR(st); | ||||
RealDirectory realDirectory(file->Loader()->Path(), file->Priority(), directory->AssociatedDirectory()->Flags()); | RealDirectory realDirectory(file->Loader()->Path(), file->Priority(), directory->AssociatedDirectory()->Flags()); | ||||
RETURN_STATUS_IF_ERR(realDirectory.Store(pathname.Filename(), fileContents, size)); | RETURN_STATUS_IF_ERR(realDirectory.Store(pathname.Filename(), fileContents, size)); | ||||
// See comment in CreateFile | |||||
m_fileCache.Remove(pathname); | |||||
directory->AddFile(*file); | directory->AddFile(*file); | ||||
m_trace->NotifyStore(pathname, size); | m_trace->NotifyStore(pathname, size); | ||||
return INFO::OK; | return INFO::OK; | ||||
} | } | ||||
virtual Status LoadFile(const VfsPath& pathname, shared_ptr<u8>& fileContents, size_t& size) | virtual Status LoadFile(const VfsPath& pathname, shared_ptr<u8>& fileContents, size_t& size) | ||||
{ | { | ||||
ScopedLock s; | ScopedLock s; | ||||
const bool isCacheHit = m_fileCache.Retrieve(pathname, fileContents, size); | |||||
if(!isCacheHit) | |||||
{ | |||||
VfsDirectory* directory; VfsFile* file; | VfsDirectory* directory; VfsFile* file; | ||||
// per 2010-05-01 meeting, this shouldn't raise 'scary error | // per 2010-05-01 meeting, this shouldn't raise 'scary error | ||||
// dialogs', which might fail to display the culprit pathname | // dialogs', which might fail to display the culprit pathname | ||||
// instead, callers should log the error, including pathname. | // instead, callers should log the error, including pathname. | ||||
RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file)); | RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file)); | ||||
fileContents = DummySharedPtr((u8*)0); | fileContents = DummySharedPtr((u8*)0); | ||||
size = file->Size(); | size = file->Size(); | ||||
if(size != 0) // (the file cache can't handle zero-length allocations) | |||||
{ | |||||
if(size < m_cacheSize/2) // (avoid evicting lots of previous data) | |||||
fileContents = m_fileCache.Reserve(size); | |||||
if(fileContents) | |||||
{ | |||||
RETURN_STATUS_IF_ERR(file->Loader()->Load(file->Name(), fileContents, file->Size())); | |||||
m_fileCache.Add(pathname, fileContents, size); | |||||
} | |||||
else | |||||
{ | |||||
RETURN_STATUS_IF_ERR(AllocateAligned(fileContents, size, maxSectorSize)); | RETURN_STATUS_IF_ERR(AllocateAligned(fileContents, size, maxSectorSize)); | ||||
RETURN_STATUS_IF_ERR(file->Loader()->Load(file->Name(), fileContents, file->Size())); | RETURN_STATUS_IF_ERR(file->Loader()->Load(file->Name(), fileContents, file->Size())); | ||||
} | |||||
} | |||||
} | |||||
stats_io_user_request(size); | stats_io_user_request(size); | ||||
stats_cache(isCacheHit? CR_HIT : CR_MISS, size); | |||||
m_trace->NotifyLoad(pathname, size); | m_trace->NotifyLoad(pathname, size); | ||||
return INFO::OK; | return INFO::OK; | ||||
} | } | ||||
virtual std::wstring TextRepresentation() const | virtual std::wstring TextRepresentation() const | ||||
{ | { | ||||
ScopedLock s; | ScopedLock s; | ||||
Show All 29 Lines | virtual Status GetVirtualPath(const OsPath& realPathname, VfsPath& pathname) | ||||
RETURN_STATUS_IF_ERR(FindRealPathR(realPath, m_rootDirectory, L"", path)); | RETURN_STATUS_IF_ERR(FindRealPathR(realPath, m_rootDirectory, L"", path)); | ||||
pathname = path / realPathname.Filename(); | pathname = path / realPathname.Filename(); | ||||
return INFO::OK; | return INFO::OK; | ||||
} | } | ||||
virtual Status RemoveFile(const VfsPath& pathname) | virtual Status RemoveFile(const VfsPath& pathname) | ||||
{ | { | ||||
ScopedLock s; | ScopedLock s; | ||||
m_fileCache.Remove(pathname); | |||||
VfsDirectory* directory; VfsFile* file; | VfsDirectory* directory; VfsFile* file; | ||||
RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file)); | RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file)); | ||||
directory->RemoveFile(file->Name()); | directory->RemoveFile(file->Name()); | ||||
return INFO::OK; | return INFO::OK; | ||||
} | } | ||||
Show All 32 Lines | for(VfsDirectory::VfsSubdirectories::const_iterator it = subdirectories.begin(); it != subdirectories.end(); ++it) | ||||
Status ret = FindRealPathR(realPath, subdirectory, curPath / subdirectoryName/"", path); | Status ret = FindRealPathR(realPath, subdirectory, curPath / subdirectoryName/"", path); | ||||
if(ret == INFO::OK) | if(ret == INFO::OK) | ||||
return INFO::OK; | return INFO::OK; | ||||
} | } | ||||
return ERR::PATH_NOT_FOUND; // NOWARN | return ERR::PATH_NOT_FOUND; // NOWARN | ||||
} | } | ||||
size_t m_cacheSize; | |||||
FileCache m_fileCache; | |||||
PITrace m_trace; | PITrace m_trace; | ||||
mutable VfsDirectory m_rootDirectory; | mutable VfsDirectory m_rootDirectory; | ||||
}; | }; | ||||
//----------------------------------------------------------------------------- | //----------------------------------------------------------------------------- | ||||
PIVFS CreateVfs(size_t cacheSize) | PIVFS CreateVfs() | ||||
{ | { | ||||
return PIVFS(new VFS(cacheSize)); | return PIVFS(new VFS()); | ||||
} | } |
Wildfire Games · Phabricator