2012-11-01 16:19:01 +01: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 23:01:49 +01:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 16:19:01 +01: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/.
2013-12-29 23:28:31 +01:00
# include <cstring>
# include <cstdio>
# include <ctype.h>
2014-11-02 19:50:26 -08:00
# include <algorithm>
2013-12-29 23:28:31 +01:00
2013-12-30 10:17:11 +01:00
# include "Common/CommonTypes.h"
2020-08-10 00:12:51 -07:00
# include "Common/Serialize/Serializer.h"
# include "Common/Serialize/SerializeFuncs.h"
2013-05-26 20:23:09 -07:00
# include "Core/FileSystems/ISOFileSystem.h"
2013-12-26 14:47:17 -08:00
# include "Core/HLE/sceKernel.h"
# include "Core/MemMap.h"
2013-05-26 20:23:09 -07:00
# include "Core/Reporting.h"
2012-11-01 16:19:01 +01:00
const int sectorSize = 2048 ;
2023-12-14 14:22:24 +03:00
bool parseLBN ( const std : : string & filename , u32 * sectorStart , u32 * readSize ) {
2013-06-23 16:15:23 -07:00
// The format of this is: "/sce_lbn" "0x"? HEX* ANY* "_size" "0x"? HEX* ANY*
// That means that "/sce_lbn/_size1/" is perfectly valid.
// Most commonly, it looks like /sce_lbn0x10_size0x100 or /sce_lbn10_size100 (always hex.)
// If it doesn't starts with /sce_lbn or doesn't have _size, look for a file instead.
2013-01-09 00:10:52 -08:00
if ( filename . compare ( 0 , sizeof ( " /sce_lbn " ) - 1 , " /sce_lbn " ) ! = 0 )
2012-11-09 12:32:35 +01:00
return false ;
2013-06-23 16:15:23 -07:00
size_t size_pos = filename . find ( " _size " ) ;
if ( size_pos = = filename . npos )
return false ;
// TODO: Return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT when >= 32 long but passes above checks.
if ( filename . size ( ) > = 32 )
return false ;
2013-01-09 00:10:52 -08:00
const char * filename_c = filename . c_str ( ) ;
2013-06-23 16:15:23 -07:00
size_t pos = strlen ( " /sce_lbn " ) ;
2013-01-09 00:10:52 -08:00
2013-06-23 16:15:23 -07:00
if ( sscanf ( filename_c + pos , " %x " , sectorStart ) ! = 1 )
* sectorStart = 0 ;
2013-01-09 00:10:52 -08:00
2013-06-23 16:15:23 -07:00
pos = size_pos + strlen ( " _size " ) ;
2013-01-09 00:10:52 -08:00
2013-06-23 16:15:23 -07:00
if ( sscanf ( filename_c + pos , " %x " , readSize ) ! = 1 )
* readSize = 0 ;
2013-01-09 00:10:52 -08:00
2012-11-09 12:32:35 +01:00
return true ;
2012-11-06 18:32:28 +01:00
}
2012-11-01 16:19:01 +01:00
# pragma pack(push)
# pragma pack(1)
2021-01-30 12:00:40 -08:00
struct u32_le_be_pair {
u8 valueLE [ 4 ] ;
u8 valueBE [ 4 ] ;
operator u32 ( ) const {
return valueLE [ 0 ] + ( valueLE [ 1 ] < < 8 ) + ( valueLE [ 2 ] < < 16 ) + ( valueLE [ 3 ] < < 24 ) ;
}
} ;
struct u16_le_be_pair {
u8 valueLE [ 2 ] ;
u8 valueBE [ 2 ] ;
operator u16 ( ) const {
return valueLE [ 0 ] + ( valueLE [ 1 ] < < 8 ) ;
}
} ;
2016-02-28 11:39:57 +01:00
struct DirectoryEntry {
2012-11-01 16:19:01 +01:00
u8 size ;
u8 sectorsInExtendedRecord ;
2021-01-30 12:00:40 -08:00
u32_le_be_pair firstDataSector ; // LBA
u32_le_be_pair dataLength ; // Size
2012-11-01 16:19:01 +01:00
u8 years ;
u8 month ;
u8 day ;
u8 hour ;
u8 minute ;
u8 second ;
u8 offsetFromGMT ;
2015-09-07 16:35:23 -07:00
u8 flags ; // 2 = directory
2012-11-01 16:19:01 +01:00
u8 fileUnitSize ;
u8 interleaveGap ;
2021-01-30 12:00:40 -08:00
u16_le_be_pair volSeqNumber ;
2015-09-07 16:35:23 -07:00
u8 identifierLength ; //identifier comes right after
2012-11-01 16:19:01 +01:00
u8 firstIdChar ;
} ;
2016-02-28 11:39:57 +01:00
struct DirectorySector {
2012-11-01 16:19:01 +01:00
DirectoryEntry entry ;
char space [ 2048 - sizeof ( DirectoryEntry ) ] ;
} ;
2016-02-28 11:39:57 +01:00
struct VolDescriptor {
2012-11-01 16:19:01 +01:00
char type ;
char cd001 [ 6 ] ;
char version ;
char sysid [ 32 ] ;
char volid [ 32 ] ;
char zeros [ 8 ] ;
2021-01-30 12:00:40 -08:00
u32_le_be_pair numSectors ;
2012-11-01 16:19:01 +01:00
char morezeros [ 32 ] ;
2021-01-30 12:00:40 -08:00
u16_le_be_pair volSetSize ;
u16_le_be_pair volSeqNum ;
u16_le_be_pair sectorSize ;
u32_le_be_pair pathTableLength ;
u16_le_be_pair firstLETableSector ;
u16_le_be_pair secondLETableSector ;
u16_le_be_pair firstBETableSector ;
u16_le_be_pair secondBETableSector ;
2012-11-01 16:19:01 +01:00
DirectoryEntry root ;
char volumeSetIdentifier [ 128 ] ;
char publisherIdentifier [ 128 ] ;
char dataPreparerIdentifier [ 128 ] ;
char applicationIdentifier [ 128 ] ;
char copyrightFileIdentifier [ 37 ] ;
char abstractFileIdentifier [ 37 ] ;
char bibliographicalFileIdentifier [ 37 ] ;
char volCreationDateTime [ 17 ] ;
char mreModDateTime [ 17 ] ;
char volExpireDateTime [ 17 ] ;
char volEffectiveDateTime [ 17 ] ;
char one ;
char zero ;
char reserved [ 512 ] ;
char zeroos [ 653 ] ;
} ;
# pragma pack(pop)
2016-02-29 01:13:57 +01:00
ISOFileSystem : : ISOFileSystem ( IHandleAllocator * _hAlloc , BlockDevice * _blockDevice ) {
2012-11-01 16:19:01 +01:00
blockDevice = _blockDevice ;
hAlloc = _hAlloc ;
VolDescriptor desc ;
2022-07-11 18:58:57 -07:00
if ( ! blockDevice - > ReadBlock ( 16 , ( u8 * ) & desc ) )
blockDevice - > NotifyReadError ( ) ;
2012-11-01 16:19:01 +01:00
2022-09-30 12:26:30 +03:00
entireISO . name . clear ( ) ;
2012-11-01 16:19:01 +01:00
entireISO . isDirectory = false ;
entireISO . startingPosition = 0 ;
2014-01-11 08:34:04 -08:00
entireISO . size = _blockDevice - > GetNumBlocks ( ) ;
2012-11-01 16:19:01 +01:00
entireISO . flags = 0 ;
2012-12-27 22:14:31 -08:00
entireISO . parent = NULL ;
2012-11-01 16:19:01 +01:00
2013-04-13 12:56:33 -07:00
treeroot = new TreeEntry ( ) ;
2012-12-27 22:16:23 -08:00
treeroot - > isDirectory = true ;
treeroot - > startingPosition = 0 ;
treeroot - > size = 0 ;
treeroot - > flags = 0 ;
2012-12-27 22:14:31 -08:00
treeroot - > parent = NULL ;
2016-02-28 11:51:15 +01:00
treeroot - > valid = false ;
2012-11-01 16:19:01 +01:00
2013-10-01 07:53:40 -07:00
if ( memcmp ( desc . cd001 , " CD001 " , 5 ) ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " ISO looks bogus, expected CD001 signature not present? Giving up... " ) ;
2013-10-01 07:53:40 -07:00
return ;
}
2021-01-30 12:00:40 -08:00
treeroot - > startsector = desc . root . firstDataSector ;
treeroot - > dirsize = desc . root . dataLength ;
2012-11-01 16:19:01 +01:00
}
2016-02-28 11:46:21 +01:00
ISOFileSystem : : ~ ISOFileSystem ( ) {
2012-11-01 16:19:01 +01:00
delete blockDevice ;
2013-04-13 12:56:33 -07:00
delete treeroot ;
2012-11-01 16:19:01 +01:00
}
2023-07-08 10:44:49 +02:00
std : : string ISOFileSystem : : TreeEntry : : BuildPath ( ) {
if ( parent ) {
return parent - > BuildPath ( ) + " / " + name ;
} else {
return name ;
}
}
2016-02-28 11:51:15 +01:00
void ISOFileSystem : : ReadDirectory ( TreeEntry * root ) {
2016-06-04 22:10:47 -07:00
for ( u32 secnum = root - > startsector , endsector = root - > startsector + ( root - > dirsize + 2047 ) / 2048 ; secnum < endsector ; + + secnum ) {
2013-02-09 21:26:55 -08:00
u8 theSector [ 2048 ] ;
2016-02-28 12:02:01 +01:00
if ( ! blockDevice - > ReadBlock ( secnum , theSector ) ) {
2018-09-26 20:29:15 -07:00
blockDevice - > NotifyReadError ( ) ;
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " Error reading block for directory '%s' in sector %d - skipping " , root - > name . c_str ( ) , secnum ) ;
2016-02-28 12:02:01 +01:00
root - > valid = true ; // Prevents re-reading
return ;
}
2016-03-06 14:16:40 +01:00
lastReadBlock_ = secnum ; // Hm, this could affect timing... but lazy loading is probably more realistic.
2012-11-01 16:19:01 +01:00
2016-02-28 11:39:57 +01:00
for ( int offset = 0 ; offset < 2048 ; ) {
2013-02-09 21:26:55 -08:00
DirectoryEntry & dir = * ( DirectoryEntry * ) & theSector [ offset ] ;
u8 sz = theSector [ offset ] ;
2012-11-01 16:19:01 +01:00
2013-02-09 21:26:55 -08:00
// Nothing left in this sector. There might be more in the next one.
if ( sz = = 0 )
break ;
2012-11-01 16:19:01 +01:00
2013-02-09 21:26:55 -08:00
const int IDENTIFIER_OFFSET = 33 ;
2016-02-28 11:39:57 +01:00
if ( offset + IDENTIFIER_OFFSET + dir . identifierLength > 2048 ) {
2018-09-26 20:29:15 -07:00
blockDevice - > NotifyReadError ( ) ;
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " Directory entry crosses sectors, corrupt iso? " ) ;
2013-03-31 21:35:30 -07:00
return ;
2013-02-09 21:26:55 -08:00
}
offset + = dir . size ;
bool isFile = ( dir . flags & 2 ) ? false : true ;
bool relative ;
2016-02-28 11:39:57 +01:00
TreeEntry * entry = new TreeEntry ( ) ;
if ( dir . identifierLength = = 1 & & ( dir . firstIdChar = = ' \x00 ' | | dir . firstIdChar = = ' . ' ) ) {
entry - > name = " . " ;
2013-02-09 21:26:55 -08:00
relative = true ;
2016-06-04 22:10:47 -07:00
} else if ( dir . identifierLength = = 1 & & dir . firstIdChar = = ' \x01 ' ) {
2016-02-28 11:39:57 +01:00
entry - > name = " .. " ;
2013-02-09 21:26:55 -08:00
relative = true ;
2016-06-04 22:10:47 -07:00
} else {
2016-02-28 11:39:57 +01:00
entry - > name = std : : string ( ( const char * ) & dir . firstIdChar , dir . identifierLength ) ;
2013-02-09 21:26:55 -08:00
relative = false ;
2012-11-01 16:19:01 +01:00
}
2021-01-30 12:00:40 -08:00
entry - > size = dir . dataLength ;
entry - > startingPosition = dir . firstDataSector * 2048 ;
2016-02-28 11:39:57 +01:00
entry - > isDirectory = ! isFile ;
entry - > flags = dir . flags ;
entry - > parent = root ;
2021-01-30 12:00:40 -08:00
entry - > startsector = dir . firstDataSector ;
entry - > dirsize = dir . dataLength ;
2016-03-06 14:16:40 +01:00
entry - > valid = isFile ; // Can pre-mark as valid if file, as we don't recurse into those.
2024-07-14 14:42:59 +02:00
VERBOSE_LOG ( Log : : FileSystem , " %s: %s %08x %08x %d " , entry - > isDirectory ? " D " : " F " , entry - > name . c_str ( ) , ( u32 ) dir . firstDataSector , entry - > startingPosition , entry - > startingPosition ) ;
2013-02-09 21:26:55 -08:00
2022-07-11 18:58:57 -07:00
// Round down to avoid any false reports.
if ( isFile & & dir . firstDataSector + ( dir . dataLength / 2048 ) > blockDevice - > GetNumBlocks ( ) ) {
blockDevice - > NotifyReadError ( ) ;
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " File '%s' starts or ends outside ISO. firstDataSector: %d len: %d " , entry - > BuildPath ( ) . c_str ( ) , ( int ) dir . firstDataSector , ( int ) dir . dataLength ) ;
2022-07-11 18:58:57 -07:00
}
2016-02-28 11:39:57 +01:00
if ( entry - > isDirectory & & ! relative ) {
2016-02-28 12:02:01 +01:00
if ( entry - > startsector = = root - > startsector ) {
2018-09-26 20:29:15 -07:00
blockDevice - > NotifyReadError ( ) ;
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " WARNING: Appear to have a recursive file system, breaking recursion. Probably corrupt ISO. " ) ;
2013-02-09 21:26:55 -08:00
}
}
2016-02-28 11:39:57 +01:00
root - > children . push_back ( entry ) ;
2013-02-09 21:26:55 -08:00
}
}
2016-02-28 12:02:01 +01:00
root - > valid = true ;
2012-11-01 16:19:01 +01:00
}
2016-02-28 11:39:57 +01:00
ISOFileSystem : : TreeEntry * ISOFileSystem : : GetFromPath ( const std : : string & path , bool catchError ) {
2015-09-07 18:43:10 -07:00
const size_t pathLength = path . length ( ) ;
2015-09-07 18:52:17 -07:00
2015-09-07 18:43:10 -07:00
if ( pathLength = = 0 ) {
2014-07-27 23:42:46 +02:00
// Ah, the device! "umd0:"
2012-11-01 16:19:01 +01:00
return & entireISO ;
}
2015-09-07 18:43:10 -07:00
size_t pathIndex = 0 ;
2012-11-06 19:34:17 +01:00
2015-09-07 18:43:10 -07:00
// Skip "./"
if ( pathLength > pathIndex + 1 & & path [ pathIndex ] = = ' . ' & & path [ pathIndex + 1 ] = = ' / ' )
pathIndex + = 2 ;
2012-11-01 16:19:01 +01:00
2015-09-07 18:43:10 -07:00
// Skip "/"
if ( pathLength > pathIndex & & path [ pathIndex ] = = ' / ' )
+ + pathIndex ;
2015-09-07 18:52:17 -07:00
2015-09-07 18:43:10 -07:00
if ( pathLength < = pathIndex )
return treeroot ;
2012-11-01 16:19:01 +01:00
2016-02-28 11:39:57 +01:00
TreeEntry * entry = treeroot ;
while ( true ) {
2016-02-28 12:02:01 +01:00
if ( ! entry - > valid ) {
ReadDirectory ( entry ) ;
}
2016-02-28 11:39:57 +01:00
TreeEntry * nextEntry = nullptr ;
2012-11-01 16:19:01 +01:00
std : : string name = " " ;
2016-02-28 11:39:57 +01:00
if ( pathLength > pathIndex ) {
2015-09-07 18:43:10 -07:00
size_t nextSlashIndex = path . find_first_of ( ' / ' , pathIndex ) ;
if ( nextSlashIndex = = std : : string : : npos )
nextSlashIndex = pathLength ;
2012-12-17 20:21:51 +00:00
2015-09-07 18:43:10 -07:00
const std : : string firstPathComponent = path . substr ( pathIndex , nextSlashIndex - pathIndex ) ;
2016-02-28 11:39:57 +01:00
for ( size_t i = 0 ; i < entry - > children . size ( ) ; i + + ) {
const std : : string & n = entry - > children [ i ] - > name ;
if ( firstPathComponent = = n ) {
2012-11-01 16:19:01 +01:00
//yay we got it
2016-02-28 11:39:57 +01:00
nextEntry = entry - > children [ i ] ;
2012-11-01 16:19:01 +01:00
name = n ;
break ;
}
}
}
2015-09-12 21:12:11 -07:00
2016-02-28 11:39:57 +01:00
if ( nextEntry ) {
entry = nextEntry ;
2016-03-06 14:26:42 +01:00
if ( ! entry - > valid )
ReadDirectory ( entry ) ;
2015-09-07 18:43:10 -07:00
pathIndex + = name . length ( ) ;
2015-09-07 18:56:18 -07:00
if ( pathIndex < pathLength & & path [ pathIndex ] = = ' / ' )
2015-09-07 18:43:10 -07:00
+ + pathIndex ;
if ( pathLength < = pathIndex )
2016-02-28 11:39:57 +01:00
return entry ;
} else {
2013-01-02 01:08:18 +01:00
if ( catchError )
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " File '%s' not found " , path . c_str ( ) ) ;
2015-09-07 18:43:10 -07:00
2012-11-01 16:19:01 +01:00
return 0 ;
}
}
}
2019-10-20 11:03:37 -07:00
int ISOFileSystem : : OpenFile ( std : : string filename , FileAccess access , const char * devicename ) {
2012-11-01 16:19:01 +01:00
OpenFileEntry entry ;
2013-07-08 12:35:07 +08:00
entry . isRawSector = false ;
entry . isBlockSectorMode = false ;
2019-10-20 10:36:13 -07:00
if ( access & FILEACCESS_WRITE ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " Can't open file '%s' with write access on an ISO partition " , filename . c_str ( ) ) ;
2019-10-20 10:36:13 -07:00
return SCE_KERNEL_ERROR_ERRNO_INVALID_FLAG ;
}
2016-02-28 11:51:15 +01:00
if ( filename . compare ( 0 , 8 , " /sce_lbn " ) = = 0 ) {
// Raw sector read.
2012-11-01 16:19:01 +01:00
u32 sectorStart = 0xFFFFFFFF , readSize = 0xFFFFFFFF ;
2012-11-06 18:32:28 +01:00
parseLBN ( filename , & sectorStart , & readSize ) ;
2016-02-28 11:39:57 +01:00
if ( sectorStart > blockDevice - > GetNumBlocks ( ) ) {
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : FileSystem , " Unable to open raw sector, out of range: '%s', sector %08x, max %08x " , filename . c_str ( ) , sectorStart , blockDevice - > GetNumBlocks ( ) ) ;
2019-10-20 11:03:37 -07:00
return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND ;
2013-01-09 01:13:38 -08:00
}
2013-11-23 12:49:32 +01:00
else if ( sectorStart = = blockDevice - > GetNumBlocks ( ) )
{
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " Should not be able to open the block after the last on disc! %08x " , sectorStart ) ;
2013-11-23 12:49:32 +01:00
}
2013-01-09 01:13:38 -08:00
2024-07-14 14:42:59 +02:00
DEBUG_LOG ( Log : : FileSystem , " Got a raw sector open: '%s', sector %08x, size %08x " , filename . c_str ( ) , sectorStart , readSize ) ;
2012-11-01 16:19:01 +01:00
u32 newHandle = hAlloc - > GetNewHandle ( ) ;
entry . seekPos = 0 ;
entry . file = 0 ;
entry . isRawSector = true ;
entry . sectorStart = sectorStart ;
entry . openSize = readSize ;
2013-07-06 23:42:49 +08:00
// when open as "umd1:/sce_lbn0x0_size0x6B49D200", that mean open umd1 as a block device.
// the param in sceIoLseek and sceIoRead is lba mode. we must mark it.
2020-08-22 00:32:51 +02:00
if ( strncmp ( devicename , " umd0: " , 5 ) = = 0 | | strncmp ( devicename , " umd1: " , 5 ) = = 0 )
2013-07-08 12:35:07 +08:00
entry . isBlockSectorMode = true ;
2013-07-06 23:42:49 +08:00
2012-11-01 16:19:01 +01:00
entries [ newHandle ] = entry ;
return newHandle ;
}
2020-08-22 00:32:51 +02:00
// May return entireISO for "umd0:".
entry . file = GetFromPath ( filename , false ) ;
if ( ! entry . file ) {
2019-10-20 11:03:37 -07:00
return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND ;
2013-07-08 12:35:07 +08:00
}
2012-11-01 16:19:01 +01:00
2015-09-07 16:44:34 -07:00
if ( entry . file = = & entireISO )
2013-07-08 15:17:42 +08:00
entry . isBlockSectorMode = true ;
2012-11-01 16:19:01 +01:00
entry . seekPos = 0 ;
u32 newHandle = hAlloc - > GetNewHandle ( ) ;
entries [ newHandle ] = entry ;
return newHandle ;
}
2016-02-28 11:39:57 +01:00
void ISOFileSystem : : CloseFile ( u32 handle ) {
2012-11-01 16:19:01 +01:00
EntryMap : : iterator iter = entries . find ( handle ) ;
2016-02-28 11:39:57 +01:00
if ( iter ! = entries . end ( ) ) {
2012-11-01 16:19:01 +01:00
//CloseHandle((*iter).second.hFile);
hAlloc - > FreeHandle ( handle ) ;
entries . erase ( iter ) ;
2016-02-28 11:39:57 +01:00
} else {
2012-11-01 16:19:01 +01:00
//This shouldn't happen...
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " Hey, what are you doing? Closing non-open files? " ) ;
2012-11-01 16:19:01 +01:00
}
}
2016-02-28 11:39:57 +01:00
bool ISOFileSystem : : OwnsHandle ( u32 handle ) {
2012-11-01 16:19:01 +01:00
EntryMap : : iterator iter = entries . find ( handle ) ;
return ( iter ! = entries . end ( ) ) ;
}
2013-12-26 14:47:17 -08:00
int ISOFileSystem : : Ioctl ( u32 handle , u32 cmd , u32 indataPtr , u32 inlen , u32 outdataPtr , u32 outlen , int & usec ) {
EntryMap : : iterator iter = entries . find ( handle ) ;
if ( iter = = entries . end ( ) ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " Ioctl on a bad file handle " ) ;
2013-12-26 14:47:17 -08:00
return SCE_KERNEL_ERROR_BADF ;
}
OpenFileEntry & e = iter - > second ;
switch ( cmd ) {
// Get ISO9660 volume descriptor (from open ISO9660 file.)
case 0x01020001 :
if ( e . isBlockSectorMode ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " Unsupported read volume descriptor command on a umd block device " ) ;
2013-12-26 14:47:17 -08:00
return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED ;
}
2023-01-08 14:10:16 -08:00
if ( ! Memory : : IsValidRange ( outdataPtr , 0x800 ) | | outlen < 0x800 ) {
2024-07-14 14:42:59 +02:00
WARN_LOG_REPORT ( Log : : FileSystem , " sceIoIoctl: Invalid out pointer %08x while reading ISO9660 volume descriptor " , outdataPtr ) ;
2013-12-26 14:47:17 -08:00
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
}
2024-07-14 14:42:59 +02:00
INFO_LOG ( Log : : sceIo , " sceIoIoctl: reading ISO9660 volume descriptor read " ) ;
2022-07-20 12:40:22 +02:00
blockDevice - > ReadBlock ( 16 , Memory : : GetPointerWriteUnchecked ( outdataPtr ) ) ;
2013-12-26 14:47:17 -08:00
return 0 ;
2013-12-26 15:01:24 -08:00
// Get ISO9660 path table (from open ISO9660 file.)
case 0x01020002 :
if ( e . isBlockSectorMode ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " Unsupported read path table command on a umd block device " ) ;
2013-12-26 15:01:24 -08:00
return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED ;
}
VolDescriptor desc ;
blockDevice - > ReadBlock ( 16 , ( u8 * ) & desc ) ;
2021-01-30 12:00:40 -08:00
if ( outlen < ( u32 ) desc . pathTableLength ) {
2013-12-26 15:01:24 -08:00
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
} else {
2021-01-30 12:00:40 -08:00
int block = ( u16 ) desc . firstLETableSector ;
2021-02-13 08:24:39 -08:00
u32 size = Memory : : ValidSize ( outdataPtr , ( u32 ) desc . pathTableLength ) ;
2023-01-08 14:10:16 -08:00
u8 * out = Memory : : GetPointerWriteRange ( outdataPtr , size ) ;
2013-12-26 15:01:24 -08:00
2014-11-02 19:50:26 -08:00
int blocks = size / blockDevice - > GetBlockSize ( ) ;
blockDevice - > ReadBlocks ( block , blocks , out ) ;
size - = blocks * blockDevice - > GetBlockSize ( ) ;
out + = blocks * blockDevice - > GetBlockSize ( ) ;
2013-12-26 15:01:24 -08:00
// The remaining (or, usually, only) partial sector.
if ( size > 0 ) {
u8 temp [ 2048 ] ;
blockDevice - > ReadBlock ( block , temp ) ;
memcpy ( out , temp , size ) ;
}
return 0 ;
}
2013-12-26 14:47:17 -08:00
}
return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED ;
}
2020-05-21 17:57:41 -07:00
PSPDevType ISOFileSystem : : DevType ( u32 handle ) {
2013-12-26 22:07:41 -08:00
EntryMap : : iterator iter = entries . find ( handle ) ;
2021-04-21 20:11:23 -07:00
if ( iter = = entries . end ( ) )
return PSPDevType : : FILE ;
2020-05-21 17:57:41 -07:00
PSPDevType type = iter - > second . isBlockSectorMode ? PSPDevType : : BLOCK : PSPDevType : : FILE ;
if ( iter - > second . isRawSector )
type | = PSPDevType : : EMU_LBN ;
return type ;
2013-12-26 22:07:41 -08:00
}
2020-05-21 16:10:08 -07:00
FileSystemFlags ISOFileSystem : : Flags ( ) {
// TODO: Here may be a good place to force things, in case users recompress games
// as PBP or CSO when they were originally the other type.
return blockDevice - > IsDisc ( ) ? FileSystemFlags : : UMD : FileSystemFlags : : CARD ;
}
2012-11-01 16:19:01 +01:00
size_t ISOFileSystem : : ReadFile ( u32 handle , u8 * pointer , s64 size )
2013-12-27 16:36:51 -08:00
{
int ignored ;
return ReadFile ( handle , pointer , size , ignored ) ;
}
2016-03-06 14:16:40 +01:00
size_t ISOFileSystem : : ReadFile ( u32 handle , u8 * pointer , s64 size , int & usec ) {
2012-11-01 16:19:01 +01:00
EntryMap : : iterator iter = entries . find ( handle ) ;
2015-01-09 15:50:06 -08:00
if ( iter ! = entries . end ( ) ) {
2012-11-01 16:19:01 +01:00
OpenFileEntry & e = iter - > second ;
2015-01-09 15:50:06 -08:00
if ( size < 0 ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG_REPORT ( Log : : FileSystem , " Invalid read for %lld bytes from umd %s " , size , e . file ? e . file - > name . c_str ( ) : " device " ) ;
2015-01-09 15:50:06 -08:00
return 0 ;
}
2012-11-01 16:19:01 +01:00
2015-01-09 18:34:41 -08:00
if ( e . isBlockSectorMode ) {
2012-11-01 16:19:01 +01:00
// Whole sectors! Shortcut to this simple code.
2014-11-02 19:50:26 -08:00
blockDevice - > ReadBlocks ( e . seekPos , ( int ) size , pointer ) ;
2013-12-27 16:49:35 -08:00
if ( abs ( ( int ) lastReadBlock_ - ( int ) e . seekPos ) > 100 ) {
// This is an estimate, sometimes it takes 1+ seconds, but it definitely takes time.
usec = 100000 ;
}
2014-11-02 19:50:26 -08:00
e . seekPos + = ( int ) size ;
2013-12-27 16:49:35 -08:00
lastReadBlock_ = e . seekPos ;
2014-11-02 19:50:26 -08:00
return ( int ) size ;
2012-11-01 16:19:01 +01:00
}
2015-01-09 18:34:41 -08:00
u64 positionOnIso ;
s64 fileSize ;
if ( e . isRawSector ) {
positionOnIso = e . sectorStart * 2048ULL + e . seekPos ;
fileSize = ( s64 ) e . openSize ;
2015-12-24 14:31:23 -08:00
} else if ( e . file = = nullptr ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " File no longer exists (loaded savestate with different ISO?) " ) ;
2015-12-24 14:31:23 -08:00
return 0 ;
2015-01-09 18:34:41 -08:00
} else {
2012-11-09 14:32:40 +01:00
positionOnIso = e . file - > startingPosition + e . seekPos ;
2015-01-09 18:34:41 -08:00
fileSize = e . file - > size ;
2012-11-01 16:19:01 +01:00
}
2015-01-09 18:34:41 -08:00
if ( ( s64 ) e . seekPos > fileSize ) {
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : FileSystem , " Read starting outside of file, at %lld / %lld " , ( s64 ) e . seekPos , fileSize ) ;
2015-01-09 18:34:41 -08:00
return 0 ;
}
if ( ( s64 ) e . seekPos + size > fileSize ) {
// Clamp to the remaining size, but read what we can.
const s64 newSize = fileSize - ( s64 ) e . seekPos ;
2020-08-22 00:32:51 +02:00
// Reading beyond the file is really quite normal behavior (if return value handled correctly), so
// not doing WARN here. Still, can potentially be useful to see so leaving at INFO.
if ( newSize = = 0 ) {
2024-07-14 14:42:59 +02:00
INFO_LOG ( Log : : FileSystem , " Attempted read at end of file, 0-size read simulated " ) ;
2020-08-22 00:32:51 +02:00
} else {
2024-07-14 14:42:59 +02:00
INFO_LOG ( Log : : FileSystem , " Reading beyond end of file from seekPos %d, clamping size %lld to %lld " , e . seekPos , size , newSize ) ;
2020-08-22 00:32:51 +02:00
}
2015-01-09 18:34:41 -08:00
size = newSize ;
}
// Okay, we have size and position, let's rock.
2014-11-02 19:50:26 -08:00
const int firstBlockOffset = positionOnIso & 2047 ;
const int firstBlockSize = firstBlockOffset = = 0 ? 0 : ( int ) std : : min ( size , 2048LL - firstBlockOffset ) ;
const int lastBlockSize = ( size - firstBlockSize ) & 2047 ;
const s64 middleSize = size - firstBlockSize - lastBlockSize ;
2015-01-09 18:34:41 -08:00
u32 secNum = ( u32 ) ( positionOnIso / 2048 ) ;
2012-11-01 16:19:01 +01:00
u8 theSector [ 2048 ] ;
2020-07-19 17:47:02 +02:00
if ( ( middleSize & 2047 ) ! = 0 ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " Remaining size should be aligned " ) ;
2020-07-19 17:47:02 +02:00
}
2012-11-01 16:19:01 +01:00
2014-11-06 08:45:33 -08:00
const u8 * const start = pointer ;
2015-01-09 18:34:41 -08:00
if ( firstBlockSize > 0 ) {
2014-11-02 19:50:26 -08:00
blockDevice - > ReadBlock ( secNum + + , theSector ) ;
memcpy ( pointer , theSector + firstBlockOffset , firstBlockSize ) ;
pointer + = firstBlockSize ;
2012-11-01 16:19:01 +01:00
}
2015-01-09 18:34:41 -08:00
if ( middleSize > 0 ) {
2014-11-02 19:50:26 -08:00
const u32 sectors = ( u32 ) ( middleSize / 2048 ) ;
blockDevice - > ReadBlocks ( secNum , sectors , pointer ) ;
secNum + = sectors ;
pointer + = middleSize ;
}
2015-01-09 18:34:41 -08:00
if ( lastBlockSize > 0 ) {
2014-11-02 19:50:26 -08:00
blockDevice - > ReadBlock ( secNum + + , theSector ) ;
memcpy ( pointer , theSector , lastBlockSize ) ;
pointer + = lastBlockSize ;
}
2014-11-06 08:45:33 -08:00
size_t totalBytes = pointer - start ;
2013-12-27 16:49:35 -08:00
if ( abs ( ( int ) lastReadBlock_ - ( int ) secNum ) > 100 ) {
// This is an estimate, sometimes it takes 1+ seconds, but it definitely takes time.
usec = 100000 ;
}
lastReadBlock_ = secNum ;
2014-11-06 08:45:33 -08:00
e . seekPos + = ( unsigned int ) totalBytes ;
return ( size_t ) totalBytes ;
2015-01-09 18:34:41 -08:00
} else {
2012-11-01 16:19:01 +01:00
//This shouldn't happen...
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " Hey, what are you doing? Reading non-open files? " ) ;
2012-11-01 16:19:01 +01:00
return 0 ;
}
}
2016-02-28 11:39:57 +01:00
size_t ISOFileSystem : : WriteFile ( u32 handle , const u8 * pointer , s64 size ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " Hey, what are you doing? You can't write to an ISO! " ) ;
2013-12-27 16:36:51 -08:00
return 0 ;
}
2016-02-28 11:39:57 +01:00
size_t ISOFileSystem : : WriteFile ( u32 handle , const u8 * pointer , s64 size , int & usec ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " Hey, what are you doing? You can't write to an ISO! " ) ;
2012-11-01 16:19:01 +01:00
return 0 ;
}
2016-02-28 11:39:57 +01:00
size_t ISOFileSystem : : SeekFile ( u32 handle , s32 position , FileMove type ) {
2012-11-01 16:19:01 +01:00
EntryMap : : iterator iter = entries . find ( handle ) ;
2016-02-28 11:39:57 +01:00
if ( iter ! = entries . end ( ) ) {
2012-11-01 16:19:01 +01:00
OpenFileEntry & e = iter - > second ;
switch ( type )
{
case FILEMOVE_BEGIN :
e . seekPos = position ;
break ;
case FILEMOVE_CURRENT :
e . seekPos + = position ;
break ;
case FILEMOVE_END :
if ( e . isRawSector )
2013-01-09 00:43:07 -08:00
e . seekPos = e . openSize + position ;
2012-11-01 16:19:01 +01:00
else
2012-11-17 19:56:28 +01:00
e . seekPos = ( unsigned int ) ( e . file - > size + position ) ;
2012-11-01 16:19:01 +01:00
break ;
}
return ( size_t ) e . seekPos ;
2016-02-28 11:39:57 +01:00
} else {
2012-11-01 16:19:01 +01:00
//This shouldn't happen...
2024-07-14 14:42:59 +02:00
ERROR_LOG ( Log : : FileSystem , " Hey, what are you doing? Seeking in non-open files? " ) ;
2012-11-01 16:19:01 +01:00
return 0 ;
}
}
2016-02-28 11:39:57 +01:00
PSPFileInfo ISOFileSystem : : GetFileInfo ( std : : string filename ) {
if ( filename . compare ( 0 , 8 , " /sce_lbn " ) = = 0 ) {
2012-11-06 18:32:28 +01:00
u32 sectorStart = 0xFFFFFFFF , readSize = 0xFFFFFFFF ;
parseLBN ( filename , & sectorStart , & readSize ) ;
PSPFileInfo fileInfo ;
fileInfo . name = filename ;
fileInfo . exists = true ;
2020-05-21 17:57:41 -07:00
fileInfo . type = FILETYPE_NORMAL ;
2012-11-06 18:32:28 +01:00
fileInfo . size = readSize ;
2020-05-21 18:20:38 -07:00
fileInfo . access = 0444 ;
2012-11-06 18:32:28 +01:00
fileInfo . startSector = sectorStart ;
fileInfo . isOnSectorSystem = true ;
fileInfo . numSectors = ( readSize + sectorSize - 1 ) / sectorSize ;
return fileInfo ;
}
2013-01-02 01:08:18 +01:00
TreeEntry * entry = GetFromPath ( filename , false ) ;
2012-11-01 16:19:01 +01:00
PSPFileInfo x ;
2020-05-21 18:20:38 -07:00
if ( entry ) {
2012-11-01 16:19:01 +01:00
x . name = entry - > name ;
2020-05-21 18:20:38 -07:00
// Strangely, it seems to be executable even for files.
x . access = 0555 ;
2012-11-01 16:19:01 +01:00
x . size = entry - > size ;
2012-11-05 12:02:09 +01:00
x . exists = true ;
2012-11-01 16:19:01 +01:00
x . type = entry - > isDirectory ? FILETYPE_DIRECTORY : FILETYPE_NORMAL ;
x . isOnSectorSystem = true ;
2015-09-07 16:44:34 -07:00
x . startSector = entry - > startingPosition / 2048 ;
2012-11-01 16:19:01 +01:00
}
return x ;
}
2022-10-09 09:08:18 -07:00
std : : vector < PSPFileInfo > ISOFileSystem : : GetDirListing ( const std : : string & path , bool * exists ) {
2012-11-01 16:19:01 +01:00
std : : vector < PSPFileInfo > myVector ;
TreeEntry * entry = GetFromPath ( path ) ;
2022-10-09 09:08:18 -07:00
if ( ! entry ) {
if ( exists )
* exists = false ;
2012-11-01 16:19:01 +01:00
return myVector ;
2022-10-09 09:08:18 -07:00
}
2024-01-04 14:43:07 +02:00
if ( entry = = & entireISO ) {
entry = GetFromPath ( " / " ) ;
}
2012-11-01 16:19:01 +01:00
2015-09-07 18:52:17 -07:00
const std : : string dot ( " . " ) ;
const std : : string dotdot ( " .. " ) ;
2015-09-07 16:31:37 -07:00
2016-02-28 11:39:57 +01:00
for ( size_t i = 0 ; i < entry - > children . size ( ) ; i + + ) {
2012-11-01 16:19:01 +01:00
TreeEntry * e = entry - > children [ i ] ;
2012-11-10 12:15:44 +01:00
2015-09-07 18:52:17 -07:00
// do not include the relative entries in the list
if ( e - > name = = dot | | e - > name = = dotdot )
2012-11-10 12:15:44 +01:00
continue ;
2012-11-01 16:19:01 +01:00
PSPFileInfo x ;
x . name = e - > name ;
2020-05-21 18:20:38 -07:00
// Strangely, it seems to be executable even for files.
x . access = 0555 ;
2022-10-09 09:08:18 -07:00
x . exists = true ;
2012-11-01 16:19:01 +01:00
x . size = e - > size ;
x . type = e - > isDirectory ? FILETYPE_DIRECTORY : FILETYPE_NORMAL ;
x . isOnSectorSystem = true ;
2013-03-03 22:05:23 -08:00
x . startSector = e - > startingPosition / 2048 ;
2016-03-06 14:16:40 +01:00
x . sectorSize = sectorSize ;
2017-03-04 13:56:15 +01:00
x . numSectors = ( u32 ) ( ( e - > size + sectorSize - 1 ) / sectorSize ) ;
2012-11-01 16:19:01 +01:00
myVector . push_back ( x ) ;
}
2022-10-09 09:08:18 -07:00
if ( exists )
* exists = true ;
2012-11-01 16:19:01 +01:00
return myVector ;
}
2012-12-27 22:14:31 -08:00
2016-02-28 11:39:57 +01:00
std : : string ISOFileSystem : : EntryFullPath ( TreeEntry * e ) {
2013-05-12 16:37:03 -07:00
if ( e = = & entireISO )
return " " ;
2012-12-27 22:14:31 -08:00
size_t fullLen = 0 ;
TreeEntry * cur = e ;
2016-02-28 11:39:57 +01:00
while ( cur ! = NULL & & cur ! = treeroot ) {
2012-12-27 22:14:31 -08:00
// For the "/".
fullLen + = 1 + cur - > name . size ( ) ;
cur = cur - > parent ;
}
std : : string path ;
path . resize ( fullLen ) ;
cur = e ;
2016-03-06 14:16:40 +01:00
while ( cur ! = NULL & & cur ! = treeroot ) {
2012-12-27 22:14:31 -08:00
path . replace ( fullLen - cur - > name . size ( ) , cur - > name . size ( ) , cur - > name ) ;
path . replace ( fullLen - cur - > name . size ( ) - 1 , 1 , " / " ) ;
fullLen - = 1 + cur - > name . size ( ) ;
cur = cur - > parent ;
}
return path ;
}
2014-07-27 23:42:46 +02:00
ISOFileSystem : : TreeEntry : : ~ TreeEntry ( ) {
for ( size_t i = 0 ; i < children . size ( ) ; + + i )
delete children [ i ] ;
children . clear ( ) ;
}
2016-02-28 11:39:57 +01:00
void ISOFileSystem : : DoState ( PointerWrap & p ) {
2013-12-27 16:49:35 -08:00
auto s = p . Section ( " ISOFileSystem " , 1 , 2 ) ;
2013-09-14 20:23:03 -07:00
if ( ! s )
return ;
2012-12-27 22:14:31 -08:00
int n = ( int ) entries . size ( ) ;
2020-08-09 21:20:42 -07:00
Do ( p , n ) ;
2012-12-27 22:14:31 -08:00
2016-02-28 11:39:57 +01:00
if ( p . mode = = p . MODE_READ ) {
2012-12-27 22:14:31 -08:00
entries . clear ( ) ;
2016-02-28 11:39:57 +01:00
for ( int i = 0 ; i < n ; + + i ) {
2013-09-01 01:05:35 -07:00
u32 fd = 0 ;
2012-12-27 22:14:31 -08:00
OpenFileEntry of ;
2013-05-12 16:37:03 -07:00
2020-08-09 21:20:42 -07:00
Do ( p , fd ) ;
Do ( p , of . seekPos ) ;
Do ( p , of . isRawSector ) ;
Do ( p , of . isBlockSectorMode ) ;
Do ( p , of . sectorStart ) ;
Do ( p , of . openSize ) ;
2013-05-12 16:37:03 -07:00
2013-07-26 22:39:35 -07:00
bool hasFile = false ;
2020-08-09 21:20:42 -07:00
Do ( p , hasFile ) ;
2013-05-12 16:37:03 -07:00
if ( hasFile ) {
std : : string path ;
2020-08-09 21:20:42 -07:00
Do ( p , path ) ;
2013-05-12 16:37:03 -07:00
of . file = GetFromPath ( path ) ;
} else {
of . file = NULL ;
}
2012-12-27 22:14:31 -08:00
entries [ fd ] = of ;
}
2016-02-28 11:39:57 +01:00
} else {
for ( EntryMap : : iterator it = entries . begin ( ) , end = entries . end ( ) ; it ! = end ; + + it ) {
2013-05-12 16:37:03 -07:00
OpenFileEntry & of = it - > second ;
2020-08-09 21:20:42 -07:00
Do ( p , it - > first ) ;
Do ( p , of . seekPos ) ;
Do ( p , of . isRawSector ) ;
Do ( p , of . isBlockSectorMode ) ;
Do ( p , of . sectorStart ) ;
Do ( p , of . openSize ) ;
2013-05-12 16:37:03 -07:00
bool hasFile = of . file ! = NULL ;
2020-08-09 21:20:42 -07:00
Do ( p , hasFile ) ;
2013-05-12 16:37:03 -07:00
if ( hasFile ) {
2016-01-09 16:49:20 -03:00
std : : string path = EntryFullPath ( of . file ) ;
2020-08-09 21:20:42 -07:00
Do ( p , path ) ;
2013-05-12 16:37:03 -07:00
}
2012-12-27 22:14:31 -08:00
}
}
2013-12-27 16:49:35 -08:00
if ( s > = 2 ) {
2020-08-09 21:20:42 -07:00
Do ( p , lastReadBlock_ ) ;
2013-12-27 16:49:35 -08:00
} else {
lastReadBlock_ = 0 ;
}
2012-12-27 22:14:31 -08:00
}