2012-11-01 15:19:01 +00:00
// 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
2012-11-04 22:01:49 +00:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 15:19:01 +00:00
// 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"
2012-12-19 10:11:56 +00:00
static bool ApplyPathStringToComponentsVector ( std : : vector < std : : string > & vector , const std : : string & pathString )
2012-12-15 11:38:14 +00:00
{
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
{
2012-12-27 13:27:13 +00:00
// 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 ( ) ) ;
2012-12-15 11:38:14 +00:00
}
}
else
{
vector . push_back ( component ) ;
}
}
}
start = i + 1 ;
}
return true ;
}
/*
* Changes relative paths to absolute , removes " . " , " .. " , and trailing " / "
2012-12-20 13:33:05 +00:00
* " drive:./blah " is absolute ( ignore the dot ) and " /blah " is relative ( because it ' s missing " drive: " )
2012-12-15 11:38:14 +00:00
* babel ( and possibly other games ) use " /directoryThatDoesNotExist/../directoryThatExists/filename "
*/
2012-12-19 10:11:56 +00:00
static bool RealPath ( const std : : string & currentDirectory , const std : : string & inPath , std : : string & outPath )
2012-12-15 11:38:14 +00:00
{
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 ;
}
2012-12-20 13:33:05 +00:00
bool relative = ( inColon = = std : : string : : npos ) ;
std : : string prefix , inAfterColon ;
2012-12-15 11:38:14 +00:00
std : : vector < std : : string > cmpnts ; // path components
2012-12-20 13:33:05 +00:00
size_t outPathCapacityGuess = inPath . length ( ) ;
2012-12-15 11:38:14 +00:00
2012-12-20 13:33:05 +00:00
if ( relative )
2012-12-15 11:38:14 +00:00
{
2012-12-20 13:33:05 +00:00
size_t curDirLen = currentDirectory . length ( ) ;
2012-12-15 11:38:14 +00:00
if ( curDirLen = = 0 )
{
2012-12-15 11:47:31 +00:00
ERROR_LOG ( HLE , " RealPath: inPath \" %s \" is relative, but current directory is empty " , inPath . c_str ( ) ) ;
2012-12-15 11:38:14 +00:00
return false ;
}
2012-12-20 13:33:05 +00:00
size_t curDirColon = currentDirectory . find ( ' : ' ) ;
if ( curDirColon = = std : : string : : npos )
2012-12-15 11:38:14 +00:00
{
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 )
{
2012-12-15 12:09:37 +00:00
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 ( ) ) ;
2012-12-15 11:38:14 +00:00
}
2012-12-15 12:09:37 +00:00
else
2012-12-15 11:38:14 +00:00
{
2012-12-15 12:09:37 +00:00
const std : : string curDirAfter = currentDirectory . substr ( curDirColon + 1 ) ;
2012-12-19 10:11:56 +00:00
if ( ! ApplyPathStringToComponentsVector ( cmpnts , curDirAfter ) )
2012-12-15 12:09:37 +00:00
{
ERROR_LOG ( HLE , " RealPath: currentDirectory is not a valid path: \" %s \" " , currentDirectory . c_str ( ) ) ;
return false ;
}
2012-12-20 13:33:05 +00:00
outPathCapacityGuess + = curDirLen ;
2012-12-15 11:38:14 +00:00
}
2012-12-15 11:47:31 +00:00
2012-12-20 13:33:05 +00:00
prefix = currentDirectory . substr ( 0 , curDirColon + 1 ) ;
inAfterColon = inPath ;
}
else
{
prefix = inPath . substr ( 0 , inColon + 1 ) ;
inAfterColon = inPath . substr ( inColon + 1 ) ;
2012-12-15 11:38:14 +00:00
}
2012-12-20 13:33:05 +00:00
if ( ! ApplyPathStringToComponentsVector ( cmpnts , inAfterColon ) )
2012-12-15 11:38:14 +00:00
{
2012-12-20 13:33:05 +00:00
WARN_LOG ( HLE , " RealPath: inPath is not a valid path: \" %s \" " , inPath . c_str ( ) ) ;
2012-12-15 11:38:14 +00:00
return false ;
}
outPath . clear ( ) ;
2012-12-20 13:33:05 +00:00
outPath . reserve ( outPathCapacityGuess ) ;
2012-12-15 11:38:14 +00:00
2012-12-20 13:33:05 +00:00
outPath . append ( prefix ) ;
2012-12-15 11:38:14 +00:00
size_t numCmpnts = cmpnts . size ( ) ;
for ( size_t i = 0 ; i < numCmpnts ; i + + )
{
outPath . append ( 1 , ' / ' ) ;
outPath . append ( cmpnts [ i ] ) ;
}
return true ;
}
2012-11-01 15:19:01 +00:00
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 ;
}
2012-12-26 07:24:19 +00:00
bool MetaFileSystem : : MapFilePath ( const std : : string & _inpath , std : : string & outpath , IFileSystem * * system )
2012-11-01 15:19:01 +00:00
{
2012-12-15 11:38:14 +00:00
//TODO: implement current directory per thread (NOT per drive)
2012-12-19 10:11:56 +00:00
std : : string realpath ;
2012-12-26 07:24:19 +00:00
// 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 ) ;
}
2012-12-19 10:11:56 +00:00
if ( RealPath ( currentDirectory , inpath , realpath ) )
2012-11-01 15:19:01 +00:00
{
2012-12-15 11:38:14 +00:00
for ( size_t i = 0 ; i < fileSystems . size ( ) ; i + + )
2012-11-01 15:19:01 +00:00
{
2012-12-15 11:38:14 +00:00
size_t prefLen = fileSystems [ i ] . prefix . size ( ) ;
2012-12-19 10:11:56 +00:00
if ( fileSystems [ i ] . prefix = = realpath . substr ( 0 , prefLen ) )
2012-12-15 11:38:14 +00:00
{
2012-12-19 10:11:56 +00:00
outpath = realpath . substr ( prefLen ) ;
2012-12-15 11:38:14 +00:00
* system = fileSystems [ i ] . system ;
2012-12-19 10:11:56 +00:00
DEBUG_LOG ( HLE , " MapFilePath: mapped \" %s \" to prefix: \" %s \" , path: \" %s \" " , inpath . c_str ( ) , fileSystems [ i ] . prefix . c_str ( ) , outpath . c_str ( ) ) ;
2012-12-15 11:38:14 +00:00
return true ;
}
2012-11-01 15:19:01 +00:00
}
}
2012-12-15 11:38:14 +00:00
2012-12-19 10:11:56 +00:00
DEBUG_LOG ( HLE , " MapFilePath: failed mapping \" %s \" , returning false " , inpath . c_str ( ) ) ;
2012-11-01 15:19:01 +00:00
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 ;
}
}
2012-11-18 21:07:40 +00:00
//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 ;
}
}
2012-11-01 15:19:01 +00:00
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 ;
}
}
2012-11-27 18:57:30 +00:00
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 ;
}
}
2012-11-01 15:19:01 +00:00
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 ;
}
2012-12-28 06:14:31 +00:00
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 " ) ;
}