ppsspp/Core/FileSystems/MetaFileSystem.cpp
Unknown W. Brackets 4b39e39455 Start save stating the filesystem state.
Plus minor fixes and at least an attempt to make states the same
on both 32 and 64 bit.
2012-12-28 13:55:27 -08:00

388 lines
8.9 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <set>
#include "MetaFileSystem.h"
static bool ApplyPathStringToComponentsVector(std::vector<std::string> &vector, const std::string &pathString)
{
size_t len = pathString.length();
size_t start = 0;
while (start < len)
{
size_t i = pathString.find('/', start);
if (i == std::string::npos)
i = len;
if (i > start)
{
std::string component = pathString.substr(start, i - start);
if (component != ".")
{
if (component == "..")
{
if (vector.size() != 0)
{
vector.pop_back();
}
else
{
// The PSP silently ignores attempts to .. to parent of root directory
WARN_LOG(HLE, "RealPath: ignoring .. beyond root - root directory is its own parent: \"%s\"", pathString.c_str());
}
}
else
{
vector.push_back(component);
}
}
}
start = i + 1;
}
return true;
}
/*
* Changes relative paths to absolute, removes ".", "..", and trailing "/"
* "drive:./blah" is absolute (ignore the dot) and "/blah" is relative (because it's missing "drive:")
* babel (and possibly other games) use "/directoryThatDoesNotExist/../directoryThatExists/filename"
*/
static bool RealPath(const std::string &currentDirectory, const std::string &inPath, std::string &outPath)
{
size_t inLen = inPath.length();
if (inLen == 0)
{
ERROR_LOG(HLE, "RealPath: inPath is empty");
return false;
}
size_t inColon = inPath.find(':');
if (inColon + 1 == inLen)
{
WARN_LOG(HLE, "RealPath: inPath is all prefix and no path: \"%s\"", inPath.c_str());
outPath = inPath;
return true;
}
bool relative = (inColon == std::string::npos);
std::string prefix, inAfterColon;
std::vector<std::string> cmpnts; // path components
size_t outPathCapacityGuess = inPath.length();
if (relative)
{
size_t curDirLen = currentDirectory.length();
if (curDirLen == 0)
{
ERROR_LOG(HLE, "RealPath: inPath \"%s\" is relative, but current directory is empty", inPath.c_str());
return false;
}
size_t curDirColon = currentDirectory.find(':');
if (curDirColon == std::string::npos)
{
ERROR_LOG(HLE, "RealPath: inPath \"%s\" is relative, but current directory \"%s\" has no prefix", inPath.c_str(), currentDirectory.c_str());
return false;
}
if (curDirColon + 1 == curDirLen)
{
ERROR_LOG(HLE, "RealPath: inPath \"%s\" is relative, but current directory \"%s\" is all prefix and no path. Using \"/\" as path for current directory.", inPath.c_str(), currentDirectory.c_str());
}
else
{
const std::string curDirAfter = currentDirectory.substr(curDirColon + 1);
if (! ApplyPathStringToComponentsVector(cmpnts, curDirAfter) )
{
ERROR_LOG(HLE,"RealPath: currentDirectory is not a valid path: \"%s\"", currentDirectory.c_str());
return false;
}
outPathCapacityGuess += curDirLen;
}
prefix = currentDirectory.substr(0, curDirColon + 1);
inAfterColon = inPath;
}
else
{
prefix = inPath.substr(0, inColon + 1);
inAfterColon = inPath.substr(inColon + 1);
}
if (! ApplyPathStringToComponentsVector(cmpnts, inAfterColon) )
{
WARN_LOG(HLE, "RealPath: inPath is not a valid path: \"%s\"", inPath.c_str());
return false;
}
outPath.clear();
outPath.reserve(outPathCapacityGuess);
outPath.append(prefix);
size_t numCmpnts = cmpnts.size();
for (size_t i = 0; i < numCmpnts; i++)
{
outPath.append(1, '/');
outPath.append(cmpnts[i]);
}
return true;
}
IFileSystem *MetaFileSystem::GetHandleOwner(u32 handle)
{
for (size_t i = 0; i < fileSystems.size(); i++)
{
if (fileSystems[i].system->OwnsHandle(handle))
return fileSystems[i].system; //got it!
}
//none found?
return 0;
}
bool MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpath, IFileSystem **system)
{
//TODO: implement current directory per thread (NOT per drive)
std::string realpath;
// Special handling: host0:command.txt (as seen in Super Monkey Ball Adventures, for example)
// appears to mean the current directory on the UMD. Let's just assume the current directory.
std::string inpath = _inpath;
if (inpath.substr(0, 6) == "host0:") {
INFO_LOG(HLE, "Host0 path detected, stripping: %s", inpath.c_str());
inpath = inpath.substr(6);
}
if ( RealPath(currentDirectory, inpath, realpath) )
{
for (size_t i = 0; i < fileSystems.size(); i++)
{
size_t prefLen = fileSystems[i].prefix.size();
if (fileSystems[i].prefix == realpath.substr(0, prefLen))
{
outpath = realpath.substr(prefLen);
*system = fileSystems[i].system;
DEBUG_LOG(HLE, "MapFilePath: mapped \"%s\" to prefix: \"%s\", path: \"%s\"", inpath.c_str(), fileSystems[i].prefix.c_str(), outpath.c_str());
return true;
}
}
}
DEBUG_LOG(HLE, "MapFilePath: failed mapping \"%s\", returning false", inpath.c_str());
return false;
}
void MetaFileSystem::Mount(std::string prefix, IFileSystem *system)
{
System x;
x.prefix=prefix;
x.system=system;
fileSystems.push_back(x);
}
void MetaFileSystem::UnmountAll()
{
current = 6;
// Ownership is a bit convoluted. Let's just delete everything once.
std::set<IFileSystem *> toDelete;
for (size_t i = 0; i < fileSystems.size(); i++) {
toDelete.insert(fileSystems[i].system);
}
for (auto iter = toDelete.begin(); iter != toDelete.end(); ++iter)
{
delete *iter;
}
fileSystems.clear();
currentDirectory = "";
}
u32 MetaFileSystem::OpenFile(std::string filename, FileAccess access)
{
std::string of;
IFileSystem *system;
if (MapFilePath(filename, of, &system))
{
return system->OpenFile(of, access);
}
else
{
return 0;
}
}
PSPFileInfo MetaFileSystem::GetFileInfo(std::string filename)
{
std::string of;
IFileSystem *system;
if (MapFilePath(filename, of, &system))
{
return system->GetFileInfo(of);
}
else
{
PSPFileInfo bogus; // TODO
return bogus;
}
}
//TODO: Not sure where this should live. Seems a bit wrong putting it in common
bool stringEndsWith (std::string const &fullString, std::string const &ending)
{
if (fullString.length() >= ending.length()) {
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
} else {
return false;
}
}
std::vector<PSPFileInfo> MetaFileSystem::GetDirListing(std::string path)
{
std::string of;
IFileSystem *system;
if (MapFilePath(path, of, &system))
{
return system->GetDirListing(of);
}
else
{
std::vector<PSPFileInfo> empty;
return empty;
}
}
bool MetaFileSystem::MkDir(const std::string &dirname)
{
std::string of;
IFileSystem *system;
if (MapFilePath(dirname, of, &system))
{
return system->MkDir(of);
}
else
{
return false;
}
}
bool MetaFileSystem::RmDir(const std::string &dirname)
{
std::string of;
IFileSystem *system;
if (MapFilePath(dirname, of, &system))
{
return system->RmDir(of);
}
else
{
return false;
}
}
bool MetaFileSystem::RenameFile(const std::string &from, const std::string &to)
{
std::string of;
std::string rf;
IFileSystem *system;
if (MapFilePath(from, of, &system) && MapFilePath(to, rf, &system))
{
return system->RenameFile(of, rf);
}
else
{
return false;
}
}
bool MetaFileSystem::DeleteFile(const std::string &filename)
{
std::string of;
IFileSystem *system;
if (MapFilePath(filename, of, &system))
{
return system->DeleteFile(of);
}
else
{
return false;
}
}
void MetaFileSystem::CloseFile(u32 handle)
{
IFileSystem *sys = GetHandleOwner(handle);
if (sys)
sys->CloseFile(handle);
}
size_t MetaFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
{
IFileSystem *sys = GetHandleOwner(handle);
if (sys)
return sys->ReadFile(handle,pointer,size);
else
return 0;
}
size_t MetaFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)
{
IFileSystem *sys = GetHandleOwner(handle);
if (sys)
return sys->WriteFile(handle,pointer,size);
else
return 0;
}
size_t MetaFileSystem::SeekFile(u32 handle, s32 position, FileMove type)
{
IFileSystem *sys = GetHandleOwner(handle);
if (sys)
return sys->SeekFile(handle,position,type);
else
return 0;
}
void MetaFileSystem::DoState(PointerWrap &p)
{
p.Do(current);
p.Do(currentDirectory);
int n = (int) fileSystems.size();
p.Do(n);
if (n != fileSystems.size())
{
ERROR_LOG(FILESYS, "Savestate failure: number of filesystems doesn't match.");
return;
}
for (int i = 0; i < n; ++i)
fileSystems[i].system->DoState(p);
p.DoMarker("MetaFileSystem");
}