2012-12-10 12:08:54 +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
// 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/.
2022-10-16 15:14:48 +00:00
# include <algorithm>
# include <memory>
2020-08-15 10:25:39 +00:00
# include "Common/Log.h"
2020-10-04 18:48:47 +00:00
# include "Common/Data/Text/I18n.h"
# include "Common/Data/Format/ZIMLoad.h"
2020-08-10 07:12:51 +00:00
# include "Common/Serialize/Serializer.h"
# include "Common/Serialize/SerializeFuncs.h"
2023-06-30 15:15:49 +00:00
# include "Common/System/OSD.h"
2017-05-26 17:04:08 +00:00
# include "Common/StringUtils.h"
2014-03-15 17:38:46 +00:00
# include "Core/Config.h"
2013-05-31 06:32:13 +00:00
# include "Core/Reporting.h"
2014-03-15 17:48:30 +00:00
# include "Core/System.h"
2021-04-04 01:28:22 +00:00
# include "Core/Debugger/MemBlockInfo.h"
2013-05-31 06:32:13 +00:00
# include "Core/Dialog/SavedataParam.h"
# include "Core/Dialog/PSPSaveDialog.h"
2013-12-29 22:28:31 +00:00
# include "Core/FileSystems/MetaFileSystem.h"
2013-09-10 09:09:18 +00:00
# include "Core/HLE/sceIo.h"
2013-05-31 06:32:13 +00:00
# include "Core/HLE/sceKernelMemory.h"
# include "Core/HLE/sceChnnlsv.h"
# include "Core/ELF/ParamSFO.h"
2012-12-19 15:57:22 +00:00
# include "Core/HW/MemoryStick.h"
2014-03-15 17:48:30 +00:00
# include "Core/Util/PPGeDraw.h"
2013-05-31 06:32:13 +00:00
2013-05-31 06:55:49 +00:00
static const std : : string ICON0_FILENAME = " ICON0.PNG " ;
static const std : : string ICON1_FILENAME = " ICON1.PMF " ;
static const std : : string PIC1_FILENAME = " PIC1.PNG " ;
static const std : : string SND0_FILENAME = " SND0.AT3 " ;
static const std : : string SFO_FILENAME = " PARAM.SFO " ;
2012-12-10 12:08:54 +00:00
2022-10-16 15:14:48 +00:00
static const int FILE_LIST_COUNT_MAX = 99 ;
static const u32 FILE_LIST_TOTAL_SIZE = sizeof ( SaveSFOFileListEntry ) * FILE_LIST_COUNT_MAX ;
2014-01-24 09:21:13 +00:00
static const std : : string savePath = " ms0:/PSP/SAVEDATA/ " ;
2012-12-10 12:08:54 +00:00
2012-12-19 15:57:22 +00:00
namespace
{
int getSizeNormalized ( int size )
{
2012-12-25 01:05:54 +00:00
int sizeCluster = ( int ) MemoryStick_SectorSize ( ) ;
2012-12-19 17:15:02 +00:00
return ( ( int ) ( ( size + sizeCluster - 1 ) / sizeCluster ) ) * sizeCluster ;
2012-12-19 15:57:22 +00:00
}
2012-12-25 01:18:59 +00:00
void SetStringFromSFO ( ParamSFOData & sfoFile , const char * name , char * str , int strLength )
{
std : : string value = sfoFile . GetValueString ( name ) ;
2017-05-26 17:05:18 +00:00
truncate_cpy ( str , strLength , value . c_str ( ) ) ;
2012-12-25 01:18:59 +00:00
}
2012-12-25 01:32:14 +00:00
2023-12-14 11:22:24 +00:00
bool ReadPSPFile ( const std : : string & filename , u8 * * data , s64 dataSize , s64 * readSize )
2012-12-25 01:32:14 +00:00
{
2020-03-09 04:22:21 +00:00
int handle = pspFileSystem . OpenFile ( filename , FILEACCESS_READ ) ;
2019-10-20 18:03:37 +00:00
if ( handle < 0 )
2012-12-25 01:32:14 +00:00
return false ;
2021-09-11 17:33:28 +00:00
if ( dataSize = = - 1 ) {
// Determine the size through seeking instead of querying.
pspFileSystem . SeekFile ( handle , 0 , FILEMOVE_END ) ;
dataSize = pspFileSystem . GetSeekPos ( handle ) ;
pspFileSystem . SeekFile ( handle , 0 , FILEMOVE_BEGIN ) ;
2013-02-06 18:01:10 +00:00
* data = new u8 [ ( size_t ) dataSize ] ;
2013-01-28 23:11:02 +00:00
}
size_t result = pspFileSystem . ReadFile ( handle , * data , dataSize ) ;
2012-12-25 01:32:14 +00:00
pspFileSystem . CloseFile ( handle ) ;
2013-01-04 23:32:07 +00:00
if ( readSize )
* readSize = result ;
2012-12-25 01:32:14 +00:00
return result ! = 0 ;
}
2023-12-14 11:22:24 +00:00
bool WritePSPFile ( const std : : string & filename , const u8 * data , SceSize dataSize )
2012-12-25 01:32:14 +00:00
{
2020-03-09 04:22:21 +00:00
int handle = pspFileSystem . OpenFile ( filename , ( FileAccess ) ( FILEACCESS_WRITE | FILEACCESS_CREATE | FILEACCESS_TRUNCATE ) ) ;
2019-10-20 18:03:37 +00:00
if ( handle < 0 )
2012-12-25 01:32:14 +00:00
return false ;
2013-01-19 21:48:20 +00:00
size_t result = pspFileSystem . WriteFile ( handle , data , dataSize ) ;
2012-12-25 01:32:14 +00:00
pspFileSystem . CloseFile ( handle ) ;
2014-11-02 18:53:17 +00:00
return result = = dataSize ;
2012-12-25 01:32:14 +00:00
}
2012-12-25 12:30:10 +00:00
2022-10-09 16:33:39 +00:00
PSPFileInfo FileFromListing ( const std : : vector < PSPFileInfo > & listing , const std : : string & filename ) {
for ( const PSPFileInfo & sub : listing ) {
if ( sub . name = = filename )
return sub ;
}
PSPFileInfo info ;
info . name = filename ;
info . exists = false ;
return info ;
}
2024-01-24 08:23:41 +00:00
bool PSPMatch ( std : : string_view text , std : : string_view regexp ) {
if ( text . empty ( ) & & regexp . empty ( ) )
2012-12-29 22:55:34 +00:00
return true ;
2024-01-24 08:23:41 +00:00
else if ( regexp = = " * " )
2012-12-29 22:55:34 +00:00
return true ;
2024-01-24 08:23:41 +00:00
else if ( text . empty ( ) )
2012-12-29 22:55:34 +00:00
return false ;
2024-01-24 08:23:41 +00:00
else if ( regexp . empty ( ) )
2012-12-29 22:55:34 +00:00
return false ;
2024-01-24 08:23:41 +00:00
else if ( regexp = = " ? " & & text . length ( ) = = 1 )
2012-12-29 22:55:34 +00:00
return true ;
2024-01-24 08:23:41 +00:00
else if ( text = = regexp )
2012-12-29 22:55:34 +00:00
return true ;
2024-01-24 08:23:41 +00:00
else if ( regexp . data ( ) [ 0 ] = = ' * ' )
2012-12-29 22:55:34 +00:00
{
bool res = PSPMatch ( text . substr ( 1 ) , regexp . substr ( 1 ) ) ;
if ( ! res )
res = PSPMatch ( text . substr ( 1 ) , regexp ) ;
return res ;
}
2024-01-24 08:23:41 +00:00
else if ( regexp . data ( ) [ 0 ] = = ' ? ' )
2012-12-29 22:55:34 +00:00
{
return PSPMatch ( text . substr ( 1 ) , regexp . substr ( 1 ) ) ;
}
2024-01-24 08:23:41 +00:00
else if ( regexp . data ( ) [ 0 ] = = text . data ( ) [ 0 ] )
2012-12-29 22:55:34 +00:00
{
return PSPMatch ( text . substr ( 1 ) , regexp . substr ( 1 ) ) ;
}
return false ;
}
2013-01-28 23:11:02 +00:00
int align16 ( int address )
{
2024-04-07 09:33:47 +00:00
return ( address + 15 ) & ~ 15 ;
2013-01-28 23:11:02 +00:00
}
int GetSDKMainVersion ( int sdkVersion )
{
2013-08-23 06:23:48 +00:00
if ( sdkVersion > 0x0307FFFF )
2013-01-28 23:11:02 +00:00
return 6 ;
2013-08-23 06:23:48 +00:00
if ( sdkVersion > 0x0300FFFF )
2013-01-28 23:11:02 +00:00
return 5 ;
2013-08-23 06:23:48 +00:00
if ( sdkVersion > 0x0206FFFF )
2013-01-28 23:11:02 +00:00
return 4 ;
2013-08-23 06:23:48 +00:00
if ( sdkVersion > 0x0205FFFF )
2013-01-28 23:11:02 +00:00
return 3 ;
2013-08-23 06:23:48 +00:00
if ( sdkVersion > = 0x02000000 )
2013-01-28 23:11:02 +00:00
return 2 ;
2013-08-23 06:23:48 +00:00
if ( sdkVersion > = 0x01000000 )
2013-01-28 23:11:02 +00:00
return 1 ;
return 0 ;
} ;
2012-12-19 15:57:22 +00:00
}
2013-12-29 22:44:35 +00:00
void SaveFileInfo : : DoState ( PointerWrap & p )
{
auto s = p . Section ( " SaveFileInfo " , 1 , 2 ) ;
if ( ! s )
return ;
2020-08-10 04:20:42 +00:00
Do ( p , size ) ;
Do ( p , saveName ) ;
Do ( p , idx ) ;
2013-12-29 22:44:35 +00:00
2020-08-10 04:20:42 +00:00
DoArray ( p , title , sizeof ( title ) ) ;
DoArray ( p , saveTitle , sizeof ( saveTitle ) ) ;
DoArray ( p , saveDetail , sizeof ( saveDetail ) ) ;
2013-12-29 22:44:35 +00:00
2020-08-10 04:20:42 +00:00
Do ( p , modif_time ) ;
2013-12-29 22:44:35 +00:00
if ( s < = 1 ) {
u32 textureData ;
int textureWidth ;
int textureHeight ;
2020-08-10 04:20:42 +00:00
Do ( p , textureData ) ;
Do ( p , textureWidth ) ;
Do ( p , textureHeight ) ;
2013-12-29 22:44:35 +00:00
if ( textureData ! = 0 ) {
// Must be MODE_READ.
texture = new PPGeImage ( " " ) ;
texture - > CompatLoad ( textureData , textureWidth , textureHeight ) ;
}
} else {
bool hasTexture = texture ! = NULL ;
2020-08-10 04:20:42 +00:00
Do ( p , hasTexture ) ;
2013-12-29 22:44:35 +00:00
if ( hasTexture ) {
if ( p . mode = = p . MODE_READ ) {
2014-08-17 10:19:04 +00:00
delete texture ;
2013-12-29 22:44:35 +00:00
texture = new PPGeImage ( " " ) ;
}
texture - > DoState ( p ) ;
}
}
}
2022-08-18 13:20:16 +00:00
SavedataParam : : SavedataParam ( ) { }
2012-12-10 12:08:54 +00:00
2024-10-22 12:15:47 +00:00
void SavedataParam : : Init ( ) {
// If the folder already exists, this is a no-op.
pspFileSystem . MkDir ( savePath ) ;
2013-08-12 19:22:03 +00:00
// Create a nomedia file to hide save icons form Android image viewer
2021-07-19 10:44:56 +00:00
# if PPSSPP_PLATFORM(ANDROID)
2013-08-12 19:22:03 +00:00
int handle = pspFileSystem . OpenFile ( savePath + " .nomedia " , ( FileAccess ) ( FILEACCESS_CREATE | FILEACCESS_WRITE ) , 0 ) ;
2019-10-20 18:03:37 +00:00
if ( handle > = 0 ) {
2013-08-12 19:22:03 +00:00
pspFileSystem . CloseFile ( handle ) ;
} else {
2024-07-14 12:42:59 +00:00
INFO_LOG ( Log : : IO , " Failed to create .nomedia file (might be ok if it already exists) " ) ;
2013-08-12 19:22:03 +00:00
}
# endif
2012-12-10 12:08:54 +00:00
}
2014-01-24 09:21:13 +00:00
std : : string SavedataParam : : GetSaveDirName ( const SceUtilitySavedataParam * param , int saveId ) const
2013-01-06 00:29:14 +00:00
{
if ( ! param ) {
return " " ;
}
if ( saveId > = 0 & & saveNameListDataCount > 0 ) // if user selection, use it
2014-01-24 09:21:13 +00:00
return GetFilename ( saveId ) ;
else
return GetSaveName ( param ) ;
2013-01-06 00:29:14 +00:00
}
2014-01-24 09:21:13 +00:00
std : : string SavedataParam : : GetSaveDir ( const SceUtilitySavedataParam * param , const std : : string & saveDirName ) const
2012-12-10 12:08:54 +00:00
{
if ( ! param ) {
return " " ;
}
2013-05-12 03:27:54 +00:00
return GetGameName ( param ) + saveDirName ;
}
2012-12-10 12:08:54 +00:00
2014-01-24 09:21:13 +00:00
std : : string SavedataParam : : GetSaveDir ( const SceUtilitySavedataParam * param , int saveId ) const
2013-05-12 03:27:54 +00:00
{
return GetSaveDir ( param , GetSaveDirName ( param , saveId ) ) ;
2012-12-14 19:55:57 +00:00
}
2014-01-24 09:21:13 +00:00
std : : string SavedataParam : : GetSaveFilePath ( const SceUtilitySavedataParam * param , const std : : string & saveDir ) const
2012-12-14 19:55:57 +00:00
{
if ( ! param ) {
return " " ;
}
2014-07-21 18:28:23 +00:00
if ( ! saveDir . size ( ) )
return " " ;
2013-05-12 03:27:54 +00:00
return savePath + saveDir ;
}
2014-01-24 09:21:13 +00:00
std : : string SavedataParam : : GetSaveFilePath ( const SceUtilitySavedataParam * param , int saveId ) const
2013-05-12 03:27:54 +00:00
{
return GetSaveFilePath ( param , GetSaveDir ( param , saveId ) ) ;
2012-12-10 12:08:54 +00:00
}
2014-01-24 09:21:13 +00:00
inline static std : : string FixedToString ( const char * str , size_t n )
{
2020-08-26 20:17:42 +00:00
if ( ! str ) {
2022-09-30 09:26:30 +00:00
return std : : string ( ) ;
2020-08-26 20:17:42 +00:00
} else {
return std : : string ( str , strnlen ( str , n ) ) ;
}
2014-01-24 09:21:13 +00:00
}
std : : string SavedataParam : : GetGameName ( const SceUtilitySavedataParam * param ) const
2012-12-11 02:09:52 +00:00
{
2014-01-24 09:21:13 +00:00
return FixedToString ( param - > gameName , ARRAY_SIZE ( param - > gameName ) ) ;
2012-12-11 02:09:52 +00:00
}
2014-01-24 09:21:13 +00:00
std : : string SavedataParam : : GetSaveName ( const SceUtilitySavedataParam * param ) const
2012-12-11 02:09:52 +00:00
{
2014-01-24 09:21:13 +00:00
const std : : string saveName = FixedToString ( param - > saveName , ARRAY_SIZE ( param - > saveName ) ) ;
if ( saveName = = " <> " )
2012-12-31 18:08:46 +00:00
return " " ;
2012-12-11 02:09:52 +00:00
return saveName ;
}
2014-01-24 09:21:13 +00:00
std : : string SavedataParam : : GetFileName ( const SceUtilitySavedataParam * param ) const
2012-12-11 02:09:52 +00:00
{
2014-01-24 09:21:13 +00:00
return FixedToString ( param - > fileName , ARRAY_SIZE ( param - > fileName ) ) ;
2012-12-11 02:09:52 +00:00
}
2012-12-10 12:08:54 +00:00
2014-03-09 07:03:32 +00:00
std : : string SavedataParam : : GetKey ( const SceUtilitySavedataParam * param ) const
{
static const char * const lut = " 0123456789ABCDEF " ;
std : : string output ;
2014-07-07 02:27:51 +00:00
if ( HasKey ( param ) )
2014-03-09 07:03:32 +00:00
{
output . reserve ( 2 * sizeof ( param - > key ) ) ;
for ( size_t i = 0 ; i < sizeof ( param - > key ) ; + + i )
{
const unsigned char c = param - > key [ i ] ;
output . push_back ( lut [ c > > 4 ] ) ;
output . push_back ( lut [ c & 15 ] ) ;
}
}
return output ;
}
2014-07-07 02:27:51 +00:00
bool SavedataParam : : HasKey ( const SceUtilitySavedataParam * param ) const
{
for ( size_t i = 0 ; i < ARRAY_SIZE ( param - > key ) ; + + i )
{
if ( param - > key [ i ] ! = 0 )
return true ;
}
return false ;
}
2014-07-21 18:28:23 +00:00
bool SavedataParam : : Delete ( SceUtilitySavedataParam * param , int saveId ) {
if ( ! param ) {
return false ;
}
// Sanity check, preventing full delete of savedata/ in MGS PW demo (!)
2020-10-04 16:00:28 +00:00
if ( ! strlen ( param - > gameName ) & & param - > mode ! = SCE_UTILITY_SAVEDATA_TYPE_LISTALLDELETE ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG ( Log : : sceUtility , " Bad param with gameName empty - cannot delete save directory " ) ;
2012-12-10 12:08:54 +00:00
return false ;
}
2020-10-04 16:00:28 +00:00
std : : string dirPath = GetSaveFilePath ( param , GetSaveDir ( saveId ) ) ;
2014-07-21 18:28:23 +00:00
if ( dirPath . size ( ) = = 0 ) {
2024-10-03 16:18:02 +00:00
ERROR_LOG ( Log : : sceUtility , " GetSaveFilePath (%.*s) returned empty - cannot delete save directory. Might already be deleted? " , ( int ) sizeof ( param - > gameName ) , param - > gameName ) ;
2014-07-21 18:28:23 +00:00
return false ;
}
2014-02-15 17:43:43 +00:00
if ( ! pspFileSystem . GetFileInfo ( dirPath ) . exists ) {
return false ;
2012-12-10 12:08:54 +00:00
}
2024-07-16 15:07:11 +00:00
ClearSFOCache ( ) ;
2012-12-10 12:08:54 +00:00
pspFileSystem . RmDir ( dirPath ) ;
return true ;
}
2020-10-01 07:41:27 +00:00
int SavedataParam : : DeleteData ( SceUtilitySavedataParam * param ) {
2014-07-21 18:28:23 +00:00
if ( ! param ) {
2020-09-30 16:39:14 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_RW_FILE_NOT_FOUND ;
2014-07-21 18:28:23 +00:00
}
std : : string subFolder = GetGameName ( param ) + GetSaveName ( param ) ;
2020-10-01 07:41:27 +00:00
std : : string fileName = GetFileName ( param ) ;
std : : string dirPath = savePath + subFolder ;
std : : string filePath = dirPath + " / " + fileName ;
std : : string sfoPath = dirPath + " / " + SFO_FILENAME ;
if ( ! pspFileSystem . GetFileInfo ( dirPath ) . exists ) {
return SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA ;
}
if ( ! pspFileSystem . GetFileInfo ( sfoPath ) . exists )
return SCE_UTILITY_SAVEDATA_ERROR_RW_DATA_BROKEN ;
2022-09-30 09:31:32 +00:00
if ( ! fileName . empty ( ) & & ! pspFileSystem . GetFileInfo ( filePath ) . exists ) {
2020-10-01 07:41:27 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_RW_FILE_NOT_FOUND ;
}
2022-09-30 09:31:32 +00:00
if ( fileName . empty ( ) ) {
2014-07-21 18:28:23 +00:00
return 0 ;
}
2013-11-02 11:48:25 +00:00
2014-07-21 18:28:23 +00:00
if ( ! subFolder . size ( ) ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG ( Log : : sceUtility , " Bad subfolder, ignoring delete of %s " , filePath . c_str ( ) ) ;
2014-07-21 18:28:23 +00:00
return 0 ;
}
2013-11-02 11:48:25 +00:00
2024-07-16 15:07:11 +00:00
ClearSFOCache ( ) ;
2020-10-01 07:41:27 +00:00
pspFileSystem . RemoveFile ( filePath ) ;
2022-10-16 15:14:48 +00:00
// Update PARAM.SFO to remove the file, if it was in the list.
std : : shared_ptr < ParamSFOData > sfoFile = LoadCachedSFO ( sfoPath ) ;
if ( sfoFile ) {
// Note: do not update values such as TITLE in this operation.
u32 fileListSize = 0 ;
SaveSFOFileListEntry * fileList = ( SaveSFOFileListEntry * ) sfoFile - > GetValueData ( " SAVEDATA_FILE_LIST " , & fileListSize ) ;
size_t fileListCount = fileListSize / sizeof ( SaveSFOFileListEntry ) ;
bool changed = false ;
for ( size_t i = 0 ; i < fileListCount ; + + i ) {
if ( strncmp ( fileList [ i ] . filename , fileName . c_str ( ) , sizeof ( fileList [ i ] . filename ) ) ! = 0 )
continue ;
memset ( fileList [ i ] . filename , 0 , sizeof ( fileList [ i ] . filename ) ) ;
memset ( fileList [ i ] . hash , 0 , sizeof ( fileList [ i ] . hash ) ) ;
changed = true ;
break ;
}
if ( changed ) {
2023-12-14 12:44:16 +00:00
auto updatedList = std : : make_unique < u8 [ ] > ( fileListSize ) ;
2022-10-16 15:14:48 +00:00
memcpy ( updatedList . get ( ) , fileList , fileListSize ) ;
sfoFile - > SetValue ( " SAVEDATA_FILE_LIST " , updatedList . get ( ) , fileListSize , ( int ) FILE_LIST_TOTAL_SIZE ) ;
u8 * sfoData ;
size_t sfoSize ;
sfoFile - > WriteSFO ( & sfoData , & sfoSize ) ;
2024-07-16 15:07:11 +00:00
ClearSFOCache ( ) ;
2022-10-16 15:14:48 +00:00
WritePSPFile ( sfoPath , sfoData , ( SceSize ) sfoSize ) ;
delete [ ] sfoData ;
}
}
2013-11-02 11:48:25 +00:00
return 0 ;
}
2018-06-24 20:28:57 +00:00
int SavedataParam : : Save ( SceUtilitySavedataParam * param , const std : : string & saveDirName , bool secureMode ) {
2012-12-10 12:08:54 +00:00
if ( ! param ) {
2018-06-24 20:28:57 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE ;
2012-12-10 12:08:54 +00:00
}
2018-08-11 23:36:28 +00:00
if ( param - > dataSize > param - > dataBufSize ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG_REPORT ( Log : : sceUtility , " Savedata buffer overflow: %d / %d " , param - > dataSize , param - > dataBufSize ) ;
2018-08-11 23:36:28 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS ;
}
2018-08-11 23:52:44 +00:00
auto validateSize = [ ] ( const PspUtilitySavedataFileData & data ) {
if ( data . buf . IsValid ( ) & & data . bufSize < data . size ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG_REPORT ( Log : : sceUtility , " Savedata subdata buffer overflow: %d / %d " , data . size , data . bufSize ) ;
2018-08-11 23:52:44 +00:00
return false ;
}
return true ;
} ;
if ( ! validateSize ( param - > icon0FileData ) | | ! validateSize ( param - > icon1FileData ) | | ! validateSize ( param - > pic1FileData ) | | ! validateSize ( param - > snd0FileData ) ) {
return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS ;
}
2018-06-24 22:30:33 +00:00
if ( param - > secureVersion > 3 ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG_REPORT ( Log : : sceUtility , " Savedata version requested on save: %d " , param - > secureVersion ) ;
2018-06-24 22:30:33 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_SAVE_PARAM ;
} else if ( param - > secureVersion ! = 0 ) {
2022-05-29 20:23:16 +00:00
if ( param - > secureVersion ! = 1 & & ! HasKey ( param ) & & secureMode ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG_REPORT ( Log : : sceUtility , " Savedata version with missing key on save: %d " , param - > secureVersion ) ;
2018-06-24 22:30:33 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_SAVE_PARAM ;
}
2024-07-14 12:42:59 +00:00
WARN_LOG ( Log : : sceUtility , " Savedata version requested on save: %d " , param - > secureVersion ) ;
2018-06-24 22:30:33 +00:00
}
2012-12-10 12:08:54 +00:00
2013-05-12 03:27:54 +00:00
std : : string dirPath = GetSaveFilePath ( param , GetSaveDir ( param , saveDirName ) ) ;
2012-12-10 12:08:54 +00:00
2014-11-02 21:30:00 +00:00
if ( ! pspFileSystem . GetFileInfo ( dirPath ) . exists ) {
if ( ! pspFileSystem . MkDir ( dirPath ) ) {
2023-04-05 22:34:50 +00:00
auto err = GetI18NCategory ( I18NCat : : ERRORS ) ;
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_ERROR , err - > T ( " Unable to write savedata, disk may be full " ) ) ;
2014-11-02 21:30:00 +00:00
}
}
2012-12-10 12:08:54 +00:00
2024-10-22 12:15:47 +00:00
u8 * cryptedData = nullptr ;
2013-01-28 23:11:02 +00:00
int cryptedSize = 0 ;
2021-03-29 06:53:38 +00:00
u8 cryptedHash [ 0x10 ] { } ;
2013-01-28 23:11:02 +00:00
// Encrypt save.
2013-04-21 21:18:12 +00:00
// TODO: Is this the correct difference between MAKEDATA and MAKEDATASECURE?
2013-06-25 13:51:39 +00:00
if ( param - > dataBuf . IsValid ( ) & & g_Config . bEncryptSave & & secureMode )
2012-12-10 12:08:54 +00:00
{
2013-01-28 23:11:02 +00:00
cryptedSize = param - > dataSize ;
2024-07-15 23:38:33 +00:00
if ( cryptedSize = = 0 | | ( SceSize ) cryptedSize > param - > dataBufSize ) {
ERROR_LOG ( Log : : sceUtility , " Bad cryptedSize %d " , cryptedSize ) ;
2013-01-28 23:11:02 +00:00
cryptedSize = param - > dataBufSize ; // fallback, should never use this
2024-07-15 23:38:33 +00:00
}
2013-06-23 05:49:39 +00:00
u8 * data_ = param - > dataBuf ;
2012-12-24 21:28:10 +00:00
2013-01-28 23:11:02 +00:00
int aligned_len = align16 ( cryptedSize ) ;
2024-07-15 23:38:33 +00:00
if ( aligned_len ! = cryptedSize ) {
2024-07-15 23:38:33 +00:00
WARN_LOG ( Log : : sceUtility , " cryptedSize unaligned: %d (%d) " , cryptedSize , cryptedSize & 15 ) ;
2024-07-15 23:38:33 +00:00
}
2024-07-15 23:38:33 +00:00
cryptedData = new u8 [ aligned_len + 0x10 ] ( ) ;
2013-01-28 23:11:02 +00:00
memcpy ( cryptedData , data_ , cryptedSize ) ;
2024-07-15 23:38:33 +00:00
// EncryptData will do a memmove to make room for the key in front.
// Technically we could just copy it into place here to avoid that.
2013-01-06 00:29:14 +00:00
2014-07-07 02:27:51 +00:00
int decryptMode = DetermineCryptMode ( param ) ;
2018-06-24 22:34:27 +00:00
bool hasKey = decryptMode > 1 ;
if ( hasKey & & ! HasKey ( param ) ) {
delete [ ] cryptedData ;
return SCE_UTILITY_SAVEDATA_ERROR_SAVE_PARAM ;
}
if ( EncryptData ( decryptMode , cryptedData , & cryptedSize , & aligned_len , cryptedHash , ( hasKey ? param - > key : 0 ) ) ! = 0 ) {
2023-04-05 22:34:50 +00:00
auto err = GetI18NCategory ( I18NCat : : ERRORS ) ;
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_WARNING , err - > T ( " Save encryption failed. This save won't work on real PSP " ) , 6.0f ) ;
2024-07-14 12:42:59 +00:00
ERROR_LOG ( Log : : sceUtility , " Save encryption failed. This save won't work on real PSP " ) ;
2013-01-28 23:11:02 +00:00
delete [ ] cryptedData ;
cryptedData = 0 ;
2012-12-10 12:08:54 +00:00
}
2012-12-31 18:08:46 +00:00
}
// SAVE PARAM.SFO
2021-09-11 18:31:42 +00:00
std : : string sfopath = dirPath + " / " + SFO_FILENAME ;
2022-10-09 18:25:39 +00:00
std : : shared_ptr < ParamSFOData > sfoFile = LoadCachedSFO ( sfopath , true ) ;
2012-12-10 12:08:54 +00:00
2024-09-10 17:06:22 +00:00
// This was added in #18430, see below.
2023-11-17 18:18:17 +00:00
bool subWrite = param - > mode = = SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE | | param - > mode = = SCE_UTILITY_SAVEDATA_TYPE_WRITEDATA ;
bool wasCrypted = GetSaveCryptMode ( param , saveDirName ) ! = 0 ;
2024-09-10 17:06:22 +00:00
// Update values. NOTE! #18430 made this conditional on !subWrite, but this is not correct, as it causes #18687.
// So now we do a hacky trick and just check for a valid title before we proceed with updating the sfoFile.
if ( strnlen ( param - > sfoParam . title , sizeof ( param - > sfoParam . title ) ) > 0 ) {
2023-11-17 18:18:17 +00:00
sfoFile - > SetValue ( " TITLE " , param - > sfoParam . title , 128 ) ;
sfoFile - > SetValue ( " SAVEDATA_TITLE " , param - > sfoParam . savedataTitle , 128 ) ;
sfoFile - > SetValue ( " SAVEDATA_DETAIL " , param - > sfoParam . detail , 1024 ) ;
sfoFile - > SetValue ( " PARENTAL_LEVEL " , param - > sfoParam . parentalLevel , 4 ) ;
sfoFile - > SetValue ( " CATEGORY " , " MS " , 4 ) ;
sfoFile - > SetValue ( " SAVEDATA_DIRECTORY " , GetSaveDir ( param , saveDirName ) , 64 ) ;
}
2012-12-31 18:08:46 +00:00
2021-03-29 06:53:38 +00:00
// Always write and update the file list.
2012-12-31 18:08:46 +00:00
// For each file, 13 bytes for filename, 16 bytes for file hash (0 in PPSSPP), 3 byte for padding
2021-03-29 06:53:38 +00:00
u32 tmpDataSize = 0 ;
2022-10-09 18:25:39 +00:00
SaveSFOFileListEntry * tmpDataOrig = ( SaveSFOFileListEntry * ) sfoFile - > GetValueData ( " SAVEDATA_FILE_LIST " , & tmpDataSize ) ;
2021-03-29 06:53:38 +00:00
SaveSFOFileListEntry * updatedList = new SaveSFOFileListEntry [ FILE_LIST_COUNT_MAX ] ;
if ( tmpDataSize ! = 0 )
memcpy ( updatedList , tmpDataOrig , std : : min ( tmpDataSize , FILE_LIST_TOTAL_SIZE ) ) ;
if ( tmpDataSize < FILE_LIST_TOTAL_SIZE )
memset ( updatedList + tmpDataSize , 0 , FILE_LIST_TOTAL_SIZE - tmpDataSize ) ;
// Leave a hash there and unchanged if it was already there.
if ( secureMode & & param - > dataBuf . IsValid ( ) ) {
const std : : string saveFilename = GetFileName ( param ) ;
for ( auto entry = updatedList ; entry < updatedList + FILE_LIST_COUNT_MAX ; + + entry ) {
if ( entry - > filename [ 0 ] ! = ' \0 ' ) {
if ( strncmp ( entry - > filename , saveFilename . c_str ( ) , sizeof ( entry - > filename ) ) ! = 0 )
continue ;
2013-05-31 06:32:13 +00:00
}
2021-03-29 06:53:38 +00:00
snprintf ( entry - > filename , sizeof ( entry - > filename ) , " %s " , saveFilename . c_str ( ) ) ;
memcpy ( entry - > hash , cryptedHash , 16 ) ;
break ;
2013-05-31 06:32:13 +00:00
}
2012-12-31 18:08:46 +00:00
}
2022-10-09 18:25:39 +00:00
sfoFile - > SetValue ( " SAVEDATA_FILE_LIST " , ( u8 * ) updatedList , FILE_LIST_TOTAL_SIZE , ( int ) FILE_LIST_TOTAL_SIZE ) ;
2021-03-29 06:53:38 +00:00
delete [ ] updatedList ;
2013-01-28 23:11:02 +00:00
// Init param with 0. This will be used to detect crypted save or not on loading
2024-04-07 09:33:47 +00:00
u8 zeroes [ 128 ] { } ;
sfoFile - > SetValue ( " SAVEDATA_PARAMS " , zeroes , 128 , 128 ) ;
2012-12-31 18:08:46 +00:00
u8 * sfoData ;
size_t sfoSize ;
2022-10-09 18:25:39 +00:00
sfoFile - > WriteSFO ( & sfoData , & sfoSize ) ;
2013-01-28 23:11:02 +00:00
// Calc SFO hash for PSP.
2023-11-17 18:18:17 +00:00
if ( cryptedData ! = 0 | | ( subWrite & & wasCrypted ) ) {
2022-10-09 18:25:39 +00:00
int offset = sfoFile - > GetDataOffset ( sfoData , " SAVEDATA_PARAMS " ) ;
2024-09-10 17:06:22 +00:00
if ( offset > = 0 )
2014-07-07 02:27:51 +00:00
UpdateHash ( sfoData , ( int ) sfoSize , offset , DetermineCryptMode ( param ) ) ;
2013-01-28 23:11:02 +00:00
}
2022-10-09 18:25:39 +00:00
2024-07-16 15:07:11 +00:00
ClearSFOCache ( ) ;
2013-01-19 21:48:20 +00:00
WritePSPFile ( sfopath , sfoData , ( SceSize ) sfoSize ) ;
2012-12-31 18:08:46 +00:00
delete [ ] sfoData ;
2022-10-09 18:25:39 +00:00
sfoData = nullptr ;
2012-12-31 18:08:46 +00:00
2013-06-25 13:51:39 +00:00
if ( param - > dataBuf . IsValid ( ) ) // Can launch save without save data in mode 13
2013-01-28 23:11:02 +00:00
{
2020-10-01 02:03:30 +00:00
std : : string fileName = GetFileName ( param ) ;
std : : string filePath = dirPath + " / " + fileName ;
2013-06-23 05:49:39 +00:00
u8 * data_ = 0 ;
2013-01-28 23:11:02 +00:00
SceSize saveSize = 0 ;
if ( cryptedData = = 0 ) // Save decrypted data
{
saveSize = param - > dataSize ;
if ( saveSize = = 0 | | saveSize > param - > dataBufSize )
saveSize = param - > dataBufSize ; // fallback, should never use this
2013-06-23 05:49:39 +00:00
data_ = param - > dataBuf ;
2013-01-28 23:11:02 +00:00
}
else
{
data_ = cryptedData ;
saveSize = cryptedSize ;
}
2024-07-14 12:42:59 +00:00
INFO_LOG ( Log : : sceUtility , " Saving file with size %u in %s " , saveSize , filePath . c_str ( ) ) ;
2013-01-28 23:11:02 +00:00
// copy back save name in request
2013-05-12 03:27:54 +00:00
strncpy ( param - > saveName , saveDirName . c_str ( ) , 20 ) ;
2013-01-28 23:11:02 +00:00
2024-04-07 10:36:32 +00:00
if ( ! fileName . empty ( ) ) {
2020-10-01 02:03:30 +00:00
if ( ! WritePSPFile ( filePath , data_ , saveSize ) ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG ( Log : : sceUtility , " Error writing file %s " , filePath . c_str ( ) ) ;
2020-10-01 02:03:30 +00:00
delete [ ] cryptedData ;
return SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE ;
}
}
2024-04-07 10:36:32 +00:00
delete [ ] cryptedData ;
2013-01-28 23:11:02 +00:00
}
2012-12-31 18:08:46 +00:00
// SAVE ICON0
2013-06-25 13:51:39 +00:00
if ( param - > icon0FileData . buf . IsValid ( ) )
2012-12-31 18:08:46 +00:00
{
2013-05-31 06:55:49 +00:00
std : : string icon0path = dirPath + " / " + ICON0_FILENAME ;
2018-08-11 23:39:11 +00:00
WritePSPFile ( icon0path , param - > icon0FileData . buf , param - > icon0FileData . size ) ;
2012-12-31 18:08:46 +00:00
}
// SAVE ICON1
2013-06-25 13:51:39 +00:00
if ( param - > icon1FileData . buf . IsValid ( ) )
2012-12-31 18:08:46 +00:00
{
2013-05-31 06:55:49 +00:00
std : : string icon1path = dirPath + " / " + ICON1_FILENAME ;
2018-08-11 23:39:11 +00:00
WritePSPFile ( icon1path , param - > icon1FileData . buf , param - > icon1FileData . size ) ;
2012-12-31 18:08:46 +00:00
}
// SAVE PIC1
2013-06-25 13:51:39 +00:00
if ( param - > pic1FileData . buf . IsValid ( ) )
2012-12-31 18:08:46 +00:00
{
2013-05-31 06:55:49 +00:00
std : : string pic1path = dirPath + " / " + PIC1_FILENAME ;
2018-08-11 23:39:11 +00:00
WritePSPFile ( pic1path , param - > pic1FileData . buf , param - > pic1FileData . size ) ;
2012-12-31 18:08:46 +00:00
}
// Save SND
2013-06-25 13:51:39 +00:00
if ( param - > snd0FileData . buf . IsValid ( ) )
2012-12-31 18:08:46 +00:00
{
2013-05-31 06:55:49 +00:00
std : : string snd0path = dirPath + " / " + SND0_FILENAME ;
2018-08-11 23:39:11 +00:00
WritePSPFile ( snd0path , param - > snd0FileData . buf , param - > snd0FileData . size ) ;
2012-12-31 18:08:46 +00:00
}
2018-06-24 20:28:57 +00:00
return 0 ;
2012-12-10 12:08:54 +00:00
}
2018-06-24 20:28:57 +00:00
int SavedataParam : : Load ( SceUtilitySavedataParam * param , const std : : string & saveDirName , int saveId , bool secureMode ) {
2012-12-10 12:08:54 +00:00
if ( ! param ) {
2018-06-24 20:28:57 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA ;
2012-12-10 12:08:54 +00:00
}
2020-10-01 07:41:27 +00:00
bool isRWMode = param - > mode = = SCE_UTILITY_SAVEDATA_TYPE_READDATA | | param - > mode = = SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE ;
2013-05-12 03:27:54 +00:00
std : : string dirPath = GetSaveFilePath ( param , GetSaveDir ( param , saveDirName ) ) ;
2020-10-01 07:41:27 +00:00
std : : string fileName = GetFileName ( param ) ;
std : : string filePath = dirPath + " / " + fileName ;
if ( ! pspFileSystem . GetFileInfo ( dirPath ) . exists ) {
return isRWMode ? SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA : SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA ;
2012-12-10 12:08:54 +00:00
}
2013-11-03 15:52:16 +00:00
2022-09-30 09:31:32 +00:00
if ( ! fileName . empty ( ) & & ! pspFileSystem . GetFileInfo ( filePath ) . exists ) {
2022-09-13 07:10:27 +00:00
return isRWMode ? SCE_UTILITY_SAVEDATA_ERROR_RW_FILE_NOT_FOUND : SCE_UTILITY_SAVEDATA_ERROR_LOAD_FILE_NOT_FOUND ;
}
// If it wasn't zero, force to zero before loading and especially in case of error.
// This isn't reset if the path doesn't even exist.
param - > dataSize = 0 ;
int result = LoadSaveData ( param , saveDirName , dirPath , secureMode ) ;
if ( result ! = 0 )
return result ;
2021-09-11 17:53:19 +00:00
// Load sfo
if ( ! LoadSFO ( param , dirPath ) ) {
2024-07-15 13:28:44 +00:00
WARN_LOG ( Log : : sceUtility , " Load: Failed to load SFO from %s " , dirPath . c_str ( ) ) ;
2020-10-01 07:41:27 +00:00
return isRWMode ? SCE_UTILITY_SAVEDATA_ERROR_RW_DATA_BROKEN : SCE_UTILITY_SAVEDATA_ERROR_LOAD_DATA_BROKEN ;
2021-09-11 17:53:19 +00:00
}
2020-10-01 07:41:27 +00:00
2013-11-03 15:10:05 +00:00
// Don't know what it is, but PSP always respond this and this unlock some game
param - > bind = 1021 ;
2021-09-11 18:31:42 +00:00
// Load other files, seems these are required by some games, e.g. Fushigi no Dungeon Fuurai no Shiren 4 Plus.
2013-11-03 16:13:00 +00:00
2013-11-03 15:52:16 +00:00
// Load ICON0.PNG
LoadFile ( dirPath , ICON0_FILENAME , & param - > icon0FileData ) ;
// Load ICON1.PNG
LoadFile ( dirPath , ICON1_FILENAME , & param - > icon1FileData ) ;
// Load PIC1.PNG
LoadFile ( dirPath , PIC1_FILENAME , & param - > pic1FileData ) ;
// Load SND0.AT3
LoadFile ( dirPath , SND0_FILENAME , & param - > snd0FileData ) ;
2018-06-24 20:28:57 +00:00
return 0 ;
2013-11-03 15:10:05 +00:00
}
2012-12-10 12:08:54 +00:00
2018-06-24 20:28:57 +00:00
int SavedataParam : : LoadSaveData ( SceUtilitySavedataParam * param , const std : : string & saveDirName , const std : : string & dirPath , bool secureMode ) {
2018-06-24 22:30:33 +00:00
if ( param - > secureVersion > 3 ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG_REPORT ( Log : : sceUtility , " Savedata version requested: %d " , param - > secureVersion ) ;
2018-06-24 22:30:33 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_LOAD_PARAM ;
} else if ( param - > secureVersion ! = 0 ) {
2022-05-29 20:23:16 +00:00
if ( param - > secureVersion ! = 1 & & ! HasKey ( param ) & & secureMode ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG_REPORT ( Log : : sceUtility , " Savedata version with missing key: %d " , param - > secureVersion ) ;
2018-06-24 22:30:33 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_LOAD_PARAM ;
}
2024-07-14 12:42:59 +00:00
WARN_LOG_REPORT ( Log : : sceUtility , " Savedata version requested: %d " , param - > secureVersion ) ;
2018-04-29 16:46:23 +00:00
}
2021-04-04 01:28:22 +00:00
2018-06-30 19:17:52 +00:00
std : : string filename = GetFileName ( param ) ;
std : : string filePath = dirPath + " / " + filename ;
2022-09-13 07:10:27 +00:00
// Blank filename always means success, if secureVersion was correct.
2022-09-30 09:31:32 +00:00
if ( filename . empty ( ) )
2022-09-13 07:10:27 +00:00
return 0 ;
2013-01-04 23:32:07 +00:00
s64 readSize ;
2024-07-14 12:42:59 +00:00
INFO_LOG ( Log : : sceUtility , " Loading file with size %u in %s " , param - > dataBufSize , filePath . c_str ( ) ) ;
2018-06-30 19:17:52 +00:00
u8 * saveData = nullptr ;
2013-01-28 23:11:02 +00:00
int saveSize = - 1 ;
2013-11-03 15:10:05 +00:00
if ( ! ReadPSPFile ( filePath , & saveData , saveSize , & readSize ) ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG ( Log : : sceUtility , " Error reading file %s " , filePath . c_str ( ) ) ;
2018-06-24 20:28:57 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA ;
2012-12-10 12:08:54 +00:00
}
2013-01-31 07:54:26 +00:00
saveSize = ( int ) readSize ;
2013-01-06 00:29:14 +00:00
// copy back save name in request
2013-05-12 03:27:54 +00:00
strncpy ( param - > saveName , saveDirName . c_str ( ) , 20 ) ;
2013-01-06 00:29:14 +00:00
2014-07-07 02:27:51 +00:00
int prevCryptMode = GetSaveCryptMode ( param , saveDirName ) ;
bool isCrypted = prevCryptMode ! = 0 & & secureMode ;
2013-01-28 23:11:02 +00:00
bool saveDone = false ;
2021-04-04 01:28:22 +00:00
u32 loadedSize = 0 ;
2013-11-03 15:10:05 +00:00
if ( isCrypted ) {
2024-07-15 13:28:44 +00:00
if ( DetermineCryptMode ( param ) > 1 & & ! HasKey ( param ) ) {
2018-06-24 22:34:27 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_LOAD_PARAM ;
2024-07-15 13:28:44 +00:00
}
2018-06-30 19:17:52 +00:00
u8 hash [ 16 ] ;
bool hasExpectedHash = GetExpectedHash ( dirPath , filename , hash ) ;
2021-04-04 01:28:22 +00:00
loadedSize = LoadCryptedSave ( param , param - > dataBuf , saveData , saveSize , prevCryptMode , hasExpectedHash ? hash : nullptr , saveDone ) ;
2018-06-24 22:34:27 +00:00
// TODO: Should return SCE_UTILITY_SAVEDATA_ERROR_LOAD_DATA_BROKEN here if !saveDone.
2013-11-03 15:10:05 +00:00
}
if ( ! saveDone ) {
2021-04-04 01:28:22 +00:00
loadedSize = LoadNotCryptedSave ( param , param - > dataBuf , saveData , saveSize ) ;
2013-11-03 15:10:05 +00:00
}
delete [ ] saveData ;
2022-09-13 07:10:27 +00:00
// Ignore error codes.
if ( loadedSize ! = 0 & & ( loadedSize & 0x80000000 ) = = 0 ) {
2021-04-04 01:28:22 +00:00
std : : string tag = " LoadSaveData/ " + filePath ;
NotifyMemInfo ( MemBlockFlags : : WRITE , param - > dataBuf . ptr , loadedSize , tag . c_str ( ) , tag . size ( ) ) ;
}
2022-09-13 07:10:27 +00:00
if ( ( loadedSize & 0x80000000 ) ! = 0 )
return loadedSize ;
param - > dataSize = ( SceSize ) saveSize ;
2018-06-24 20:28:57 +00:00
return 0 ;
2013-11-03 15:10:05 +00:00
}
2014-07-07 02:27:51 +00:00
int SavedataParam : : DetermineCryptMode ( const SceUtilitySavedataParam * param ) const {
int decryptMode = 1 ;
2018-06-24 22:34:27 +00:00
if ( param - > secureVersion = = 1 ) {
decryptMode = 1 ;
} else if ( param - > secureVersion = = 2 ) {
decryptMode = 3 ;
} else if ( param - > secureVersion = = 3 ) {
decryptMode = GetSDKMainVersion ( sceKernelGetCompiledSdkVersion ( ) ) > = 4 ? 5 : 1 ;
2018-05-04 06:11:18 +00:00
} else if ( HasKey ( param ) ) {
2018-06-24 22:34:27 +00:00
// TODO: This should ignore HasKey(), which would trigger errors. Not doing that yet to play it safe.
2014-07-07 02:27:51 +00:00
decryptMode = GetSDKMainVersion ( sceKernelGetCompiledSdkVersion ( ) ) > = 4 ? 5 : 3 ;
}
return decryptMode ;
}
2013-01-28 23:11:02 +00:00
2021-04-04 01:28:22 +00:00
u32 SavedataParam : : LoadCryptedSave ( SceUtilitySavedataParam * param , u8 * data , const u8 * saveData , int & saveSize , int prevCryptMode , const u8 * expectedHash , bool & saveDone ) {
2018-07-28 16:37:12 +00:00
int orig_size = saveSize ;
2014-07-07 02:27:51 +00:00
int align_len = align16 ( saveSize ) ;
u8 * data_base = new u8 [ align_len ] ;
u8 * cryptKey = new u8 [ 0x10 ] ;
2018-05-04 06:11:18 +00:00
int decryptMode = DetermineCryptMode ( param ) ;
2018-06-30 19:17:52 +00:00
const int detectedMode = decryptMode ;
2018-07-28 16:37:12 +00:00
bool hasKey ;
auto resetData = [ & ] ( int mode ) {
saveSize = orig_size ;
align_len = align16 ( saveSize ) ;
hasKey = mode > 1 ;
if ( hasKey ) {
memcpy ( cryptKey , param - > key , 0x10 ) ;
}
memcpy ( data_base , saveData , saveSize ) ;
memset ( data_base + saveSize , 0 , align_len - saveSize ) ;
} ;
resetData ( decryptMode ) ;
2014-07-07 02:27:51 +00:00
if ( decryptMode ! = prevCryptMode ) {
if ( prevCryptMode = = 1 & & param - > key [ 0 ] = = 0 ) {
// Backwards compat for a bug we used to have.
2024-07-14 12:42:59 +00:00
WARN_LOG ( Log : : sceUtility , " Savedata loading with hashmode %d instead of detected %d " , prevCryptMode , decryptMode ) ;
2014-07-07 02:27:51 +00:00
decryptMode = prevCryptMode ;
2014-07-10 06:30:20 +00:00
// Don't notify the user if we're not going to upgrade the save.
if ( ! g_Config . bEncryptSave ) {
2023-04-05 22:34:50 +00:00
auto di = GetI18NCategory ( I18NCat : : DIALOG ) ;
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_WARNING , di - > T ( " When you save, it will load on a PSP, but not an older PPSSPP " ) , 6.0f ) ;
g_OSD . Show ( OSDType : : MESSAGE_WARNING , di - > T ( " Old savedata detected " ) , 6.0f ) ;
2014-07-10 06:30:20 +00:00
}
2014-07-07 02:27:51 +00:00
} else {
2014-07-10 06:30:20 +00:00
if ( decryptMode = = 5 & & prevCryptMode = = 3 ) {
2024-07-14 12:42:59 +00:00
WARN_LOG ( Log : : sceUtility , " Savedata loading with detected hashmode %d instead of file's %d " , decryptMode , prevCryptMode ) ;
2014-07-10 06:30:20 +00:00
} else {
2024-07-14 12:42:59 +00:00
WARN_LOG_REPORT ( Log : : sceUtility , " Savedata loading with detected hashmode %d instead of file's %d " , decryptMode , prevCryptMode ) ;
2014-07-10 06:30:20 +00:00
}
2017-10-04 15:16:21 +00:00
if ( g_Config . bSavedataUpgrade ) {
decryptMode = prevCryptMode ;
2023-04-05 22:34:50 +00:00
auto di = GetI18NCategory ( I18NCat : : DIALOG ) ;
2023-06-20 12:40:46 +00:00
g_OSD . Show ( OSDType : : MESSAGE_WARNING , di - > T ( " When you save, it will not work on outdated PSP Firmware anymore " ) , 6.0f ) ;
g_OSD . Show ( OSDType : : MESSAGE_WARNING , di - > T ( " Old savedata detected " ) , 6.0f ) ;
2017-10-04 15:16:21 +00:00
}
2013-01-28 23:11:02 +00:00
}
2018-05-04 06:11:18 +00:00
hasKey = decryptMode > 1 ;
2014-07-07 02:27:51 +00:00
}
2013-01-28 23:11:02 +00:00
2024-07-15 23:38:33 +00:00
int err = DecryptData ( decryptMode , data_base , & saveSize , & align_len , hasKey ? cryptKey : nullptr , expectedHash ) ;
2018-06-30 19:17:52 +00:00
// Perhaps the file had the wrong mode....
if ( err ! = 0 & & detectedMode ! = decryptMode ) {
2018-07-28 16:37:12 +00:00
resetData ( detectedMode ) ;
2024-07-15 23:38:33 +00:00
err = DecryptData ( detectedMode , data_base , & saveSize , & align_len , hasKey ? cryptKey : nullptr , expectedHash ) ;
2018-06-30 19:17:52 +00:00
}
// TODO: Should return an error, but let's just try with a bad hash.
if ( err ! = 0 & & expectedHash ! = nullptr ) {
2024-07-14 12:42:59 +00:00
WARN_LOG ( Log : : sceUtility , " Incorrect hash on save data, likely corrupt " ) ;
2018-07-28 16:37:12 +00:00
resetData ( decryptMode ) ;
2024-07-15 23:38:33 +00:00
err = DecryptData ( decryptMode , data_base , & saveSize , & align_len , hasKey ? cryptKey : nullptr , nullptr ) ;
2018-06-30 19:17:52 +00:00
}
2021-04-04 01:28:22 +00:00
u32 sz = 0 ;
2018-06-30 19:17:52 +00:00
if ( err = = 0 ) {
2021-04-04 01:28:22 +00:00
if ( param - > dataBuf . IsValid ( ) ) {
2022-09-13 07:10:27 +00:00
if ( ( u32 ) saveSize > param - > dataBufSize | | ! Memory : : IsValidRange ( param - > dataBuf . ptr , saveSize ) ) {
sz = SCE_UTILITY_SAVEDATA_ERROR_LOAD_DATA_BROKEN ;
} else {
sz = ( u32 ) saveSize ;
memcpy ( data , data_base , sz ) ;
}
2021-04-04 01:28:22 +00:00
}
2014-07-07 02:27:51 +00:00
saveDone = true ;
}
delete [ ] data_base ;
delete [ ] cryptKey ;
2021-04-04 01:28:22 +00:00
return sz ;
2013-11-03 15:10:05 +00:00
}
2013-01-28 23:11:02 +00:00
2021-04-04 01:28:22 +00:00
u32 SavedataParam : : LoadNotCryptedSave ( SceUtilitySavedataParam * param , u8 * data , u8 * saveData , int & saveSize ) {
if ( param - > dataBuf . IsValid ( ) ) {
2022-09-13 07:10:27 +00:00
if ( ( u32 ) saveSize > param - > dataBufSize | | ! Memory : : IsValidRange ( param - > dataBuf . ptr , saveSize ) ) {
return SCE_UTILITY_SAVEDATA_ERROR_LOAD_DATA_BROKEN ;
}
memcpy ( data , saveData , saveSize ) ;
return saveSize ;
2021-04-04 01:28:22 +00:00
}
return 0 ;
2013-11-03 15:10:05 +00:00
}
2021-09-11 17:53:19 +00:00
bool SavedataParam : : LoadSFO ( SceUtilitySavedataParam * param , const std : : string & dirPath ) {
std : : string sfopath = dirPath + " / " + SFO_FILENAME ;
2022-10-09 18:25:39 +00:00
std : : shared_ptr < ParamSFOData > sfoFile = LoadCachedSFO ( sfopath ) ;
if ( sfoFile ) {
2021-09-11 17:53:19 +00:00
// copy back info in request
2022-10-09 18:25:39 +00:00
strncpy ( param - > sfoParam . title , sfoFile - > GetValueString ( " TITLE " ) . c_str ( ) , 128 ) ;
strncpy ( param - > sfoParam . savedataTitle , sfoFile - > GetValueString ( " SAVEDATA_TITLE " ) . c_str ( ) , 128 ) ;
strncpy ( param - > sfoParam . detail , sfoFile - > GetValueString ( " SAVEDATA_DETAIL " ) . c_str ( ) , 1024 ) ;
param - > sfoParam . parentalLevel = sfoFile - > GetValueInt ( " PARENTAL_LEVEL " ) ;
2021-09-11 18:26:36 +00:00
return true ;
2013-11-03 15:10:05 +00:00
}
2022-10-09 18:25:39 +00:00
return false ;
2012-12-10 12:08:54 +00:00
}
2018-06-30 19:17:52 +00:00
std : : vector < SaveSFOFileListEntry > SavedataParam : : GetSFOEntries ( const std : : string & dirPath ) {
std : : vector < SaveSFOFileListEntry > result ;
const std : : string sfoPath = dirPath + " / " + SFO_FILENAME ;
2013-11-12 03:21:26 +00:00
2022-10-09 18:25:39 +00:00
std : : shared_ptr < ParamSFOData > sfoFile = LoadCachedSFO ( sfoPath ) ;
if ( ! sfoFile ) {
2021-09-11 17:53:19 +00:00
return result ;
}
2013-11-12 03:21:26 +00:00
2013-12-08 20:02:37 +00:00
u32 sfoFileListSize = 0 ;
2022-10-09 18:25:39 +00:00
SaveSFOFileListEntry * sfoFileList = ( SaveSFOFileListEntry * ) sfoFile - > GetValueData ( " SAVEDATA_FILE_LIST " , & sfoFileListSize ) ;
2018-06-30 19:17:52 +00:00
const u32 count = std : : min ( ( u32 ) FILE_LIST_COUNT_MAX , sfoFileListSize / ( u32 ) sizeof ( SaveSFOFileListEntry ) ) ;
for ( u32 i = 0 ; i < count ; + + i ) {
if ( sfoFileList [ i ] . filename [ 0 ] ! = ' \0 ' )
result . push_back ( sfoFileList [ i ] ) ;
}
2013-12-08 20:02:37 +00:00
2018-06-30 19:17:52 +00:00
return result ;
}
std : : set < std : : string > SavedataParam : : GetSecureFileNames ( const std : : string & dirPath ) {
auto entries = GetSFOEntries ( dirPath ) ;
std : : set < std : : string > secureFileNames ;
2023-12-12 14:38:50 +00:00
for ( const auto & entry : entries ) {
2018-06-30 19:17:52 +00:00
char temp [ 14 ] ;
truncate_cpy ( temp , entry . filename ) ;
2013-12-08 20:02:37 +00:00
secureFileNames . insert ( temp ) ;
}
return secureFileNames ;
2013-11-12 03:21:26 +00:00
}
2018-06-30 19:17:52 +00:00
bool SavedataParam : : GetExpectedHash ( const std : : string & dirPath , const std : : string & filename , u8 hash [ 16 ] ) {
auto entries = GetSFOEntries ( dirPath ) ;
for ( auto entry : entries ) {
if ( strncmp ( entry . filename , filename . c_str ( ) , sizeof ( entry . filename ) ) = = 0 ) {
memcpy ( hash , entry . hash , sizeof ( entry . hash ) ) ;
return true ;
}
}
return false ;
}
2014-12-25 02:42:41 +00:00
void SavedataParam : : LoadFile ( const std : : string & dirPath , const std : : string & filename , PspUtilitySavedataFileData * fileData ) {
2013-11-03 15:52:16 +00:00
std : : string filePath = dirPath + " / " + filename ;
2021-02-13 16:24:39 +00:00
if ( ! fileData - > buf . IsValid ( ) )
2013-11-03 16:13:00 +00:00
return ;
2021-02-13 16:24:39 +00:00
2013-11-03 15:52:16 +00:00
u8 * buf = fileData - > buf ;
2021-02-13 16:24:39 +00:00
u32 size = Memory : : ValidSize ( fileData - > buf . ptr , fileData - > bufSize ) ;
s64 readSize = - 1 ;
2021-04-04 01:45:24 +00:00
if ( ReadPSPFile ( filePath , & buf , size , & readSize ) ) {
2013-11-03 15:52:16 +00:00
fileData - > size = readSize ;
2021-04-04 01:45:24 +00:00
const std : : string tag = " SavedataLoad/ " + filePath ;
NotifyMemInfo ( MemBlockFlags : : WRITE , fileData - > buf . ptr , fileData - > size , tag . c_str ( ) , tag . size ( ) ) ;
2024-07-15 13:28:44 +00:00
INFO_LOG ( Log : : sceUtility , " Loaded subfile %s (size: %d bytes) into %08x " , filePath . c_str ( ) , fileData - > size , fileData - > buf . ptr ) ;
} else {
WARN_LOG ( Log : : sceUtility , " Failed to load subfile %s into %08x " , filePath . c_str ( ) , fileData - > buf . ptr ) ;
2021-04-04 01:45:24 +00:00
}
2013-11-03 15:52:16 +00:00
}
2024-07-15 23:38:33 +00:00
// Note: The work is done in-place, hence the memmove etc.
2013-01-28 23:11:02 +00:00
int SavedataParam : : EncryptData ( unsigned int mode ,
unsigned char * data ,
int * dataLen ,
int * alignedLen ,
unsigned char * hash ,
unsigned char * cryptkey )
{
2024-07-15 23:38:33 +00:00
pspChnnlsvContext1 ctx1 { } ;
pspChnnlsvContext2 ctx2 { } ;
2013-01-28 23:11:02 +00:00
2024-07-15 23:38:33 +00:00
INFO_LOG ( Log : : sceUtility , " EncryptData(mode=%d, *dataLen=%d, *alignedLen=%d) " , mode , * dataLen , * alignedLen ) ;
2013-01-28 23:11:02 +00:00
/* Make room for the IV in front of the data. */
memmove ( data + 0x10 , data , * alignedLen ) ;
/* Set up buffers */
memset ( hash , 0 , 0x10 ) ;
2024-07-15 23:38:33 +00:00
// Zero out the IV before we begin.
2013-01-28 23:11:02 +00:00
memset ( data , 0 , 0x10 ) ;
/* Build the 0x10-byte IV and setup encryption */
if ( sceSdCreateList_ ( ctx2 , mode , 1 , data , cryptkey ) < 0 )
return - 1 ;
if ( sceSdSetIndex_ ( ctx1 , mode ) < 0 )
return - 2 ;
if ( sceSdRemoveValue_ ( ctx1 , data , 0x10 ) < 0 )
return - 3 ;
if ( sceSdSetMember_ ( ctx2 , data + 0x10 , * alignedLen ) < 0 )
return - 4 ;
/* Clear any extra bytes left from the previous steps */
memset ( data + 0x10 + * dataLen , 0 , * alignedLen - * dataLen ) ;
/* Encrypt the data */
if ( sceSdRemoveValue_ ( ctx1 , data + 0x10 , * alignedLen ) < 0 )
return - 5 ;
/* Verify encryption */
2023-06-07 06:50:49 +00:00
if ( sceSdCleanList_ ( ctx2 ) < 0 )
2013-01-28 23:11:02 +00:00
return - 6 ;
/* Build the file hash from this PSP */
if ( sceSdGetLastIndex_ ( ctx1 , hash , cryptkey ) < 0 )
return - 7 ;
/* Adjust sizes to account for IV */
* alignedLen + = 0x10 ;
* dataLen + = 0x10 ;
/* All done */
return 0 ;
}
2024-07-15 23:38:33 +00:00
// Note: The work is done in-place, hence the memmove etc.
int SavedataParam : : DecryptData ( unsigned int mode , unsigned char * data , int * dataLen , int * alignedLen , unsigned char * cryptkey , const u8 * expectedHash ) {
pspChnnlsvContext1 ctx1 { } ;
pspChnnlsvContext2 ctx2 { } ;
2013-01-28 23:11:02 +00:00
/* Need a 16-byte IV plus some data */
if ( * alignedLen < = 0x10 )
return - 1 ;
* dataLen - = 0x10 ;
* alignedLen - = 0x10 ;
/* Perform the magic */
if ( sceSdSetIndex_ ( ctx1 , mode ) < 0 )
return - 2 ;
if ( sceSdCreateList_ ( ctx2 , mode , 2 , data , cryptkey ) < 0 )
return - 3 ;
if ( sceSdRemoveValue_ ( ctx1 , data , 0x10 ) < 0 )
return - 4 ;
if ( sceSdRemoveValue_ ( ctx1 , data + 0x10 , * alignedLen ) < 0 )
return - 5 ;
if ( sceSdSetMember_ ( ctx2 , data + 0x10 , * alignedLen ) < 0 )
return - 6 ;
/* Verify that it decrypted correctly */
2023-06-07 06:50:49 +00:00
if ( sceSdCleanList_ ( ctx2 ) < 0 )
2013-01-28 23:11:02 +00:00
return - 7 ;
2018-06-30 19:17:52 +00:00
if ( expectedHash ) {
u8 hash [ 16 ] ;
if ( sceSdGetLastIndex_ ( ctx1 , hash , cryptkey ) < 0 )
return - 7 ;
if ( memcmp ( hash , expectedHash , sizeof ( hash ) ) ! = 0 )
return - 8 ;
}
2013-01-28 23:11:02 +00:00
/* The decrypted data starts at data + 0x10, so shift it back. */
memmove ( data , data + 0x10 , * dataLen ) ;
return 0 ;
}
2024-04-07 09:56:51 +00:00
// Requires sfoData to be padded with zeroes to the next 16-byte boundary (due to BuildHash)
2013-01-28 23:11:02 +00:00
int SavedataParam : : UpdateHash ( u8 * sfoData , int sfoSize , int sfoDataParamsOffset , int encryptmode )
{
int alignedLen = align16 ( sfoSize ) ;
2024-07-15 23:38:33 +00:00
memset ( sfoData + sfoDataParamsOffset , 0 , 128 ) ;
2013-01-28 23:11:02 +00:00
u8 filehash [ 16 ] ;
int ret = 0 ;
2014-07-07 02:27:51 +00:00
int firstHashMode = encryptmode & 2 ? 4 : 2 ;
int secondHashMode = encryptmode & 2 ? 3 : 0 ;
if ( encryptmode & 4 ) {
firstHashMode = 6 ;
secondHashMode = 5 ;
}
// Compute 11D0 hash over entire file
if ( ( ret = BuildHash ( filehash , sfoData , sfoSize , alignedLen , firstHashMode , 0 ) ) < 0 )
{
// Not sure about "2"
2013-01-28 23:11:02 +00:00
return ret - 400 ;
}
2014-07-07 02:27:51 +00:00
// Copy 11D0 hash to param.sfo and set flag indicating it's there
2024-07-15 23:38:33 +00:00
memcpy ( sfoData + sfoDataParamsOffset + 0x20 , filehash , 0x10 ) ;
* ( sfoData + sfoDataParamsOffset ) | = 0x01 ;
2013-01-28 23:11:02 +00:00
2014-07-07 02:27:51 +00:00
// If new encryption mode, compute and insert the 1220 hash.
if ( encryptmode & 6 )
2013-01-28 23:11:02 +00:00
{
/* Enable the hash bit first */
2014-07-07 02:27:51 +00:00
* ( sfoData + sfoDataParamsOffset ) | = ( encryptmode & 6 ) < < 4 ;
2013-01-28 23:11:02 +00:00
2014-07-07 02:27:51 +00:00
if ( ( ret = BuildHash ( filehash , sfoData , sfoSize , alignedLen , secondHashMode , 0 ) ) < 0 )
2013-01-28 23:11:02 +00:00
{
return ret - 500 ;
}
memcpy ( sfoData + sfoDataParamsOffset + 0x70 , filehash , 0x10 ) ;
}
/* Compute and insert the 11C0 hash. */
if ( ( ret = BuildHash ( filehash , sfoData , sfoSize , alignedLen , 1 , 0 ) ) < 0 )
{
return ret - 600 ;
}
memcpy ( sfoData + sfoDataParamsOffset + 0x10 , filehash , 0x10 ) ;
/* All done. */
return 0 ;
}
2024-04-07 09:56:51 +00:00
// Requires sfoData to be padded with zeroes to the next 16-byte boundary.
2024-04-07 10:36:32 +00:00
int SavedataParam : : BuildHash ( uint8_t * output ,
const uint8_t * data ,
2013-01-28 23:11:02 +00:00
unsigned int len ,
unsigned int alignedLen ,
int mode ,
2024-04-07 10:36:32 +00:00
const uint8_t * cryptkey ) {
2013-01-28 23:11:02 +00:00
pspChnnlsvContext1 ctx1 ;
/* Set up buffers */
memset ( & ctx1 , 0 , sizeof ( pspChnnlsvContext1 ) ) ;
memset ( output , 0 , 0x10 ) ;
/* Perform the magic */
if ( sceSdSetIndex_ ( ctx1 , mode & 0xFF ) < 0 )
return - 1 ;
if ( sceSdRemoveValue_ ( ctx1 , data , alignedLen ) < 0 )
return - 2 ;
if ( sceSdGetLastIndex_ ( ctx1 , output , cryptkey ) < 0 )
{
// Got here since Kirk CMD5 missing, return random value;
memset ( output , 0x1 , 0x10 ) ;
return 0 ;
}
/* All done. */
return 0 ;
}
2021-07-26 20:11:07 +00:00
// TODO: Merge with NiceSizeFormat? That one has a decimal though.
2017-10-15 04:52:23 +00:00
std : : string SavedataParam : : GetSpaceText ( u64 size , bool roundUp )
2012-12-19 15:57:22 +00:00
{
2012-12-19 17:15:02 +00:00
char text [ 50 ] ;
2023-12-28 13:00:01 +00:00
static const char * const suffixes [ ] = { " B " , " KB " , " MB " , " GB " } ;
2014-11-01 06:48:08 +00:00
for ( size_t i = 0 ; i < ARRAY_SIZE ( suffixes ) ; + + i )
2012-12-19 17:15:02 +00:00
{
2014-11-01 06:48:08 +00:00
if ( size < 1024 )
{
2014-11-02 16:12:29 +00:00
snprintf ( text , sizeof ( text ) , " %lld %s " , size , suffixes [ i ] ) ;
2014-11-01 06:48:08 +00:00
return std : : string ( text ) ;
}
2017-10-15 04:52:23 +00:00
if ( roundUp ) {
size = ( size + 1023 ) / 1024 ;
} else {
size / = 1024 ;
}
2012-12-19 17:15:02 +00:00
}
2015-12-28 08:21:02 +00:00
snprintf ( text , sizeof ( text ) , " %llu TB " , size ) ;
2012-12-19 17:15:02 +00:00
return std : : string ( text ) ;
2012-12-19 15:57:22 +00:00
}
2024-07-15 13:28:44 +00:00
inline std : : string FmtPspTime ( const ScePspDateTime & dt ) {
return StringFromFormat ( " %04d-%02d-%02d %02d:%02d:%02d.%06d " , dt . year , dt . month , dt . day , dt . hour , dt . minute , dt . second , dt . microsecond ) ;
}
2013-06-23 18:14:13 +00:00
int SavedataParam : : GetSizes ( SceUtilitySavedataParam * param )
2012-12-11 02:09:52 +00:00
{
if ( ! param ) {
2013-06-23 18:14:13 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_DATA ;
2012-12-11 02:09:52 +00:00
}
2013-06-23 18:14:13 +00:00
int ret = 0 ;
2012-12-24 21:28:10 +00:00
2013-06-25 13:51:39 +00:00
if ( param - > msFree . IsValid ( ) )
2012-12-11 02:09:52 +00:00
{
2014-11-01 06:48:08 +00:00
const u64 freeBytes = MemoryStick_FreeSpace ( ) ;
2013-06-23 18:06:54 +00:00
param - > msFree - > clusterSize = ( u32 ) MemoryStick_SectorSize ( ) ;
2014-11-01 06:48:08 +00:00
param - > msFree - > freeClusters = ( u32 ) ( freeBytes / MemoryStick_SectorSize ( ) ) ;
param - > msFree - > freeSpaceKB = ( u32 ) ( freeBytes / 0x400 ) ;
2017-10-15 04:52:23 +00:00
const std : : string spaceTxt = SavedataParam : : GetSpaceText ( freeBytes , false ) ;
2013-06-23 18:06:54 +00:00
memset ( param - > msFree - > freeSpaceStr , 0 , sizeof ( param - > msFree - > freeSpaceStr ) ) ;
strncpy ( param - > msFree - > freeSpaceStr , spaceTxt . c_str ( ) , sizeof ( param - > msFree - > freeSpaceStr ) ) ;
2021-04-04 01:45:24 +00:00
NotifyMemInfo ( MemBlockFlags : : WRITE , param - > msFree . ptr , sizeof ( SceUtilitySavedataMsFreeInfo ) , " SavedataGetSizes " ) ;
2012-12-11 02:09:52 +00:00
}
2013-06-25 13:51:39 +00:00
if ( param - > msData . IsValid ( ) )
2012-12-11 02:09:52 +00:00
{
2020-09-13 07:13:18 +00:00
const SceUtilitySavedataMsDataInfo * msData = param - > msData ;
const std : : string gameName ( msData - > gameName , strnlen ( msData - > gameName , sizeof ( msData - > gameName ) ) ) ;
const std : : string saveName ( msData - > saveName , strnlen ( msData - > saveName , sizeof ( msData - > saveName ) ) ) ;
2014-03-16 01:50:14 +00:00
// TODO: How should <> be handled?
std : : string path = GetSaveFilePath ( param , gameName + ( saveName = = " <> " ? " " : saveName ) ) ;
2022-10-09 16:33:39 +00:00
bool listingExists = false ;
auto listing = pspFileSystem . GetDirListing ( path , & listingExists ) ;
if ( listingExists ) {
2014-03-16 01:50:14 +00:00
param - > msData - > info . usedClusters = 0 ;
2022-10-09 16:33:39 +00:00
for ( auto & item : listing ) {
param - > msData - > info . usedClusters + = ( item . size + ( u32 ) MemoryStick_SectorSize ( ) - 1 ) / ( u32 ) MemoryStick_SectorSize ( ) ;
2014-03-16 01:50:14 +00:00
}
// The usedSpaceKB value is definitely based on clusters, not bytes or even KB.
// Fieldrunners expects 736 KB, even though the files add up to ~600 KB.
int total_size = param - > msData - > info . usedClusters * ( u32 ) MemoryStick_SectorSize ( ) ;
param - > msData - > info . usedSpaceKB = total_size / 0x400 ;
2017-10-15 04:52:23 +00:00
std : : string spaceTxt = SavedataParam : : GetSpaceText ( total_size , true ) ;
2014-03-16 01:50:14 +00:00
strncpy ( param - > msData - > info . usedSpaceStr , spaceTxt . c_str ( ) , sizeof ( param - > msData - > info . usedSpaceStr ) ) ;
// TODO: What does this mean, then? Seems to be the same.
param - > msData - > info . usedSpace32KB = param - > msData - > info . usedSpaceKB ;
strncpy ( param - > msData - > info . usedSpace32Str , spaceTxt . c_str ( ) , sizeof ( param - > msData - > info . usedSpace32Str ) ) ;
2012-12-19 15:57:22 +00:00
}
else
{
2013-06-23 18:06:54 +00:00
param - > msData - > info . usedClusters = 0 ;
param - > msData - > info . usedSpaceKB = 0 ;
strncpy ( param - > msData - > info . usedSpaceStr , " " , sizeof ( param - > msData - > info . usedSpaceStr ) ) ;
param - > msData - > info . usedSpace32KB = 0 ;
strncpy ( param - > msData - > info . usedSpace32Str , " " , sizeof ( param - > msData - > info . usedSpace32Str ) ) ;
2013-06-23 18:14:13 +00:00
ret = SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_DATA ;
2012-12-19 15:57:22 +00:00
}
2021-04-04 01:45:24 +00:00
NotifyMemInfo ( MemBlockFlags : : WRITE , param - > msData . ptr , sizeof ( SceUtilitySavedataMsDataInfo ) , " SavedataGetSizes " ) ;
2012-12-11 02:09:52 +00:00
}
2013-06-25 13:51:39 +00:00
if ( param - > utilityData . IsValid ( ) )
2012-12-11 02:09:52 +00:00
{
2012-12-19 15:57:22 +00:00
int total_size = 0 ;
2014-10-15 03:22:26 +00:00
// The directory record itself.
// TODO: Account for number of files / actual record size?
total_size + = getSizeNormalized ( 1 ) ;
// Account for the SFO (is this always 1 sector?)
total_size + = getSizeNormalized ( 1 ) ;
// Add the size of the data itself (don't forget encryption overhead.)
// This is only added if a filename is specified.
if ( param - > fileName [ 0 ] ! = 0 ) {
if ( g_Config . bEncryptSave ) {
total_size + = getSizeNormalized ( ( u32 ) param - > dataSize + 16 ) ;
} else {
total_size + = getSizeNormalized ( ( u32 ) param - > dataSize ) ;
}
}
2012-12-19 15:57:22 +00:00
total_size + = getSizeNormalized ( param - > icon0FileData . size ) ;
total_size + = getSizeNormalized ( param - > icon1FileData . size ) ;
total_size + = getSizeNormalized ( param - > pic1FileData . size ) ;
total_size + = getSizeNormalized ( param - > snd0FileData . size ) ;
2013-06-23 18:06:54 +00:00
param - > utilityData - > usedClusters = total_size / ( u32 ) MemoryStick_SectorSize ( ) ;
param - > utilityData - > usedSpaceKB = total_size / 0x400 ;
2017-10-15 04:52:23 +00:00
std : : string spaceTxt = SavedataParam : : GetSpaceText ( total_size , true ) ;
2013-06-23 18:06:54 +00:00
memset ( param - > utilityData - > usedSpaceStr , 0 , sizeof ( param - > utilityData - > usedSpaceStr ) ) ;
strncpy ( param - > utilityData - > usedSpaceStr , spaceTxt . c_str ( ) , sizeof ( param - > utilityData - > usedSpaceStr ) ) ;
// TODO: Maybe these are rounded to the nearest 32KB? Or something?
param - > utilityData - > usedSpace32KB = total_size / 0x400 ;
2024-07-15 13:28:44 +00:00
std : : string spaceTxt32 = SavedataParam : : GetSpaceText ( total_size , true ) ;
2013-06-23 18:06:54 +00:00
memset ( param - > utilityData - > usedSpace32Str , 0 , sizeof ( param - > utilityData - > usedSpace32Str ) ) ;
2024-07-15 13:28:44 +00:00
strncpy ( param - > utilityData - > usedSpace32Str , spaceTxt32 . c_str ( ) , sizeof ( param - > utilityData - > usedSpace32Str ) ) ;
INFO_LOG ( Log : : sceUtility , " GetSize: usedSpaceKB: %d (str: %s) (clusters: %d) " , param - > utilityData - > usedSpaceKB , spaceTxt . c_str ( ) , param - > utilityData - > usedClusters ) ;
INFO_LOG ( Log : : sceUtility , " GetSize: usedSpace32KB: %d (str32: %s) " , param - > utilityData - > usedSpace32KB , spaceTxt32 . c_str ( ) ) ;
2021-04-04 01:45:24 +00:00
NotifyMemInfo ( MemBlockFlags : : WRITE , param - > utilityData . ptr , sizeof ( SceUtilitySavedataUsedDataInfo ) , " SavedataGetSizes " ) ;
2012-12-11 02:09:52 +00:00
}
2012-12-24 21:28:10 +00:00
return ret ;
2012-12-11 02:09:52 +00:00
}
2012-12-19 20:23:52 +00:00
bool SavedataParam : : GetList ( SceUtilitySavedataParam * param )
2012-12-11 02:09:52 +00:00
{
if ( ! param ) {
return false ;
}
2013-06-25 13:51:39 +00:00
if ( param - > idList . IsValid ( ) )
2012-12-11 02:09:52 +00:00
{
2024-07-15 13:28:44 +00:00
u32 maxFileCount = param - > idList - > maxCount ;
2012-12-29 22:55:34 +00:00
std : : vector < PSPFileInfo > validDir ;
2013-10-06 14:17:05 +00:00
std : : vector < PSPFileInfo > sfoFiles ;
2012-12-29 22:55:34 +00:00
std : : vector < PSPFileInfo > allDir = pspFileSystem . GetDirListing ( savePath ) ;
2021-09-11 18:31:42 +00:00
std : : string searchString = GetGameName ( param ) + GetSaveName ( param ) ;
2024-07-15 13:28:44 +00:00
for ( size_t i = 0 ; i < allDir . size ( ) & & validDir . size ( ) < maxFileCount ; i + + ) {
2021-04-04 01:45:24 +00:00
std : : string dirName = allDir [ i ] . name ;
2024-07-15 13:28:44 +00:00
if ( PSPMatch ( dirName , searchString ) ) {
2021-04-04 01:45:24 +00:00
validDir . push_back ( allDir [ i ] ) ;
2012-12-29 22:55:34 +00:00
}
2021-04-04 01:45:24 +00:00
}
2012-12-29 22:55:34 +00:00
2021-04-04 01:45:24 +00:00
PSPFileInfo sfoFile ;
for ( size_t i = 0 ; i < validDir . size ( ) ; + + i ) {
2021-09-11 18:31:42 +00:00
// GetFileName(param) == null here
2021-04-04 01:45:24 +00:00
// so use sfo files to set the date.
2021-08-24 06:01:02 +00:00
sfoFile = pspFileSystem . GetFileInfo ( savePath + validDir [ i ] . name + " / " + SFO_FILENAME ) ;
2021-04-04 01:45:24 +00:00
sfoFiles . push_back ( sfoFile ) ;
}
2013-10-06 14:17:05 +00:00
2021-04-04 01:45:24 +00:00
SceUtilitySavedataIdListEntry * entries = param - > idList - > entries ;
for ( u32 i = 0 ; i < ( u32 ) validDir . size ( ) ; i + + )
{
entries [ i ] . st_mode = 0x11FF ;
if ( sfoFiles [ i ] . exists ) {
__IoCopyDate ( entries [ i ] . st_ctime , sfoFiles [ i ] . ctime ) ;
__IoCopyDate ( entries [ i ] . st_atime , sfoFiles [ i ] . atime ) ;
__IoCopyDate ( entries [ i ] . st_mtime , sfoFiles [ i ] . mtime ) ;
} else {
__IoCopyDate ( entries [ i ] . st_ctime , validDir [ i ] . ctime ) ;
__IoCopyDate ( entries [ i ] . st_atime , validDir [ i ] . atime ) ;
__IoCopyDate ( entries [ i ] . st_mtime , validDir [ i ] . mtime ) ;
2012-12-29 22:55:34 +00:00
}
2021-04-04 01:45:24 +00:00
// folder name without gamename (max 20 u8)
std : : string outName = validDir [ i ] . name . substr ( GetGameName ( param ) . size ( ) ) ;
memset ( entries [ i ] . name , 0 , sizeof ( entries [ i ] . name ) ) ;
strncpy ( entries [ i ] . name , outName . c_str ( ) , sizeof ( entries [ i ] . name ) ) ;
2012-12-29 22:55:34 +00:00
}
// Save num of folder found
2013-06-23 19:41:37 +00:00
param - > idList - > resultCount = ( u32 ) validDir . size ( ) ;
2024-07-15 13:28:44 +00:00
// Log out the listing.
if ( GenericLogEnabled ( LogLevel : : LINFO , Log : : sceUtility ) ) {
INFO_LOG ( Log : : sceUtility , " LIST (searchstring=%s): %d files (max: %d) " , searchString . c_str ( ) , param - > idList - > resultCount , maxFileCount ) ;
for ( int i = 0 ; i < validDir . size ( ) ; i + + ) {
INFO_LOG ( Log : : sceUtility , " %s: mode %08x, ctime: %s, atime: %s, mtime: %s " ,
entries [ i ] . name , entries [ i ] . st_mode , FmtPspTime ( entries [ i ] . st_ctime ) . c_str ( ) , FmtPspTime ( entries [ i ] . st_atime ) . c_str ( ) , FmtPspTime ( entries [ i ] . st_mtime ) . c_str ( ) ) ;
}
}
2021-04-04 01:45:24 +00:00
NotifyMemInfo ( MemBlockFlags : : WRITE , param - > idList . ptr , sizeof ( SceUtilitySavedataIdListInfo ) , " SavedataGetList " ) ;
2021-04-06 03:55:52 +00:00
NotifyMemInfo ( MemBlockFlags : : WRITE , param - > idList - > entries . ptr , ( uint32_t ) validDir . size ( ) * sizeof ( SceUtilitySavedataIdListEntry ) , " SavedataGetList " ) ;
2012-12-11 02:09:52 +00:00
}
2012-12-11 09:54:13 +00:00
return true ;
2012-12-11 02:09:52 +00:00
}
2022-01-26 08:31:30 +00:00
int SavedataParam : : GetFilesList ( SceUtilitySavedataParam * param , u32 requestAddr ) {
2013-05-25 15:57:59 +00:00
if ( ! param ) {
2013-05-31 06:32:13 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_STATUS ;
2012-12-31 18:08:46 +00:00
}
2013-05-31 06:32:13 +00:00
2013-06-25 13:51:39 +00:00
if ( ! param - > fileList . IsValid ( ) ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG_REPORT ( Log : : sceUtility , " SavedataParam::GetFilesList(): bad fileList address %08x " , param - > fileList . ptr ) ;
2013-05-31 06:32:13 +00:00
// Should crash.
return - 1 ;
}
2013-06-02 22:17:02 +00:00
auto & fileList = param - > fileList ;
2013-06-25 13:51:39 +00:00
if ( fileList - > secureEntries . IsValid ( ) & & fileList - > maxSecureEntries > 99 ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG_REPORT ( Log : : sceUtility , " SavedataParam::GetFilesList(): too many secure entries, %d " , fileList - > maxSecureEntries ) ;
2013-05-31 06:32:13 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS ;
}
2013-06-25 13:51:39 +00:00
if ( fileList - > normalEntries . IsValid ( ) & & fileList - > maxNormalEntries > 8192 ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG_REPORT ( Log : : sceUtility , " SavedataParam::GetFilesList(): too many normal entries, %d " , fileList - > maxNormalEntries ) ;
2013-05-31 06:32:13 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS ;
}
2014-02-08 17:08:27 +00:00
if ( sceKernelGetCompiledSdkVersion ( ) > = 0x02060000 ) {
if ( fileList - > systemEntries . IsValid ( ) & & fileList - > maxSystemEntries > 5 ) {
2024-07-14 12:42:59 +00:00
ERROR_LOG_REPORT ( Log : : sceUtility , " SavedataParam::GetFilesList(): too many system entries, %d " , fileList - > maxSystemEntries ) ;
2014-02-08 17:08:27 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS ;
}
2013-05-31 06:32:13 +00:00
}
std : : string dirPath = savePath + GetGameName ( param ) + GetSaveName ( param ) ;
2022-10-09 16:33:39 +00:00
bool dirPathExists = false ;
auto files = pspFileSystem . GetDirListing ( dirPath , & dirPathExists ) ;
if ( ! dirPathExists ) {
2024-07-14 12:42:59 +00:00
DEBUG_LOG ( Log : : sceUtility , " SavedataParam::GetFilesList(): directory %s does not exist " , dirPath . c_str ( ) ) ;
2013-05-31 06:32:13 +00:00
return SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA ;
}
// Even if there are no files, initialize to 0.
fileList - > resultNumSecureEntries = 0 ;
fileList - > resultNumNormalEntries = 0 ;
fileList - > resultNumSystemEntries = 0 ;
2021-08-24 06:01:02 +00:00
// We need PARAM.SFO's SAVEDATA_FILE_LIST to determine which entries are secure.
2022-10-09 16:33:39 +00:00
PSPFileInfo sfoFileInfo = FileFromListing ( files , SFO_FILENAME ) ;
2013-05-31 07:47:38 +00:00
std : : set < std : : string > secureFilenames ;
2014-10-03 07:16:12 +00:00
2013-05-31 06:47:33 +00:00
if ( sfoFileInfo . exists ) {
2018-06-30 19:17:52 +00:00
secureFilenames = GetSecureFileNames ( dirPath ) ;
2014-10-03 07:16:12 +00:00
} else {
return SCE_UTILITY_SAVEDATA_ERROR_RW_DATA_BROKEN ;
2013-05-31 06:47:33 +00:00
}
2022-01-26 08:31:30 +00:00
// TODO: Does this always happen?
// Don't know what it is, but PSP always respond this.
param - > bind = 1021 ;
// This should be set around the same time as the file data. This runs on a thread, so set immediately.
auto requestPtr = PSPPointer < SceUtilitySavedataParam > : : Create ( requestAddr ) ;
requestPtr - > bind = 1021 ;
2013-05-31 07:47:38 +00:00
// Does not list directories, nor recurse into them, and ignores files not ALL UPPERCASE.
2022-10-09 18:25:39 +00:00
bool isCrypted = GetSaveCryptMode ( param , GetSaveDirName ( param , 0 ) ) ! = 0 ;
2013-05-31 07:47:38 +00:00
for ( auto file = files . begin ( ) , end = files . end ( ) ; file ! = end ; + + file ) {
if ( file - > type = = FILETYPE_DIRECTORY ) {
continue ;
}
// TODO: What are the exact rules? It definitely skips lowercase, and allows FILE or FILE.EXT.
if ( file - > name . find_first_of ( " abcdefghijklmnopqrstuvwxyz " ) ! = file - > name . npos ) {
2024-07-14 12:42:59 +00:00
DEBUG_LOG ( Log : : sceUtility , " SavedataParam::GetFilesList(): skipping file %s with lowercase " , file - > name . c_str ( ) ) ;
2013-05-31 07:47:38 +00:00
continue ;
}
bool isSystemFile = file - > name = = ICON0_FILENAME | | file - > name = = ICON1_FILENAME | | file - > name = = PIC1_FILENAME ;
isSystemFile = isSystemFile | | file - > name = = SND0_FILENAME | | file - > name = = SFO_FILENAME ;
SceUtilitySavedataFileListEntry * entry = NULL ;
int sizeOffset = 0 ;
if ( isSystemFile ) {
2013-06-25 13:51:39 +00:00
if ( fileList - > systemEntries . IsValid ( ) & & fileList - > resultNumSystemEntries < fileList - > maxSystemEntries ) {
2013-05-31 07:47:38 +00:00
entry = & fileList - > systemEntries [ fileList - > resultNumSystemEntries + + ] ;
}
} else if ( secureFilenames . find ( file - > name ) ! = secureFilenames . end ( ) ) {
2013-06-25 13:51:39 +00:00
if ( fileList - > secureEntries . IsValid ( ) & & fileList - > resultNumSecureEntries < fileList - > maxSecureEntries ) {
2013-05-31 07:47:38 +00:00
entry = & fileList - > secureEntries [ fileList - > resultNumSecureEntries + + ] ;
}
// Secure files are slightly bigger.
if ( isCrypted ) {
sizeOffset = - 0x10 ;
}
} else {
2013-06-25 13:51:39 +00:00
if ( fileList - > normalEntries . IsValid ( ) & & fileList - > resultNumNormalEntries < fileList - > maxNormalEntries ) {
2013-05-31 07:47:38 +00:00
entry = & fileList - > normalEntries [ fileList - > resultNumNormalEntries + + ] ;
2013-05-25 15:57:59 +00:00
}
2012-12-31 18:08:46 +00:00
}
2013-05-31 07:47:38 +00:00
// Out of space for this file in the list.
if ( entry = = NULL ) {
continue ;
}
entry - > st_mode = 0x21FF ;
entry - > st_size = file - > size + sizeOffset ;
2013-09-10 09:09:18 +00:00
__IoCopyDate ( entry - > st_ctime , file - > ctime ) ;
__IoCopyDate ( entry - > st_atime , file - > atime ) ;
__IoCopyDate ( entry - > st_mtime , file - > mtime ) ;
2013-05-31 07:47:38 +00:00
// TODO: Probably actually 13 + 3 pad...
strncpy ( entry - > name , file - > name . c_str ( ) , 16 ) ;
entry - > name [ 15 ] = ' \0 ' ;
2012-12-31 18:08:46 +00:00
}
2024-07-15 13:28:44 +00:00
if ( GenericLogEnabled ( LogLevel : : LINFO , Log : : sceUtility ) ) {
INFO_LOG ( Log : : sceUtility , " FILES: %d files listed " , fileList - > resultNumNormalEntries ) ;
for ( int i = 0 ; i < ( int ) fileList - > resultNumNormalEntries ; i + + ) {
const SceUtilitySavedataFileListEntry & info = fileList - > systemEntries [ i ] ;
INFO_LOG ( Log : : sceUtility , " %s: mode %08x, ctime: %s, atime: %s, mtime: %s " ,
info . name , info . st_mode , FmtPspTime ( info . st_ctime ) . c_str ( ) , FmtPspTime ( info . st_atime ) . c_str ( ) , FmtPspTime ( info . st_mtime ) . c_str ( ) ) ;
}
}
2021-04-04 01:45:24 +00:00
NotifyMemInfo ( MemBlockFlags : : WRITE , fileList . ptr , sizeof ( SceUtilitySavedataFileListInfo ) , " SavedataGetFilesList " ) ;
if ( fileList - > resultNumSystemEntries ! = 0 )
NotifyMemInfo ( MemBlockFlags : : WRITE , fileList - > systemEntries . ptr , fileList - > resultNumSystemEntries * sizeof ( SceUtilitySavedataFileListEntry ) , " SavedataGetFilesList " ) ;
if ( fileList - > resultNumSecureEntries ! = 0 )
NotifyMemInfo ( MemBlockFlags : : WRITE , fileList - > secureEntries . ptr , fileList - > resultNumSecureEntries * sizeof ( SceUtilitySavedataFileListEntry ) , " SavedataGetFilesList " ) ;
if ( fileList - > resultNumNormalEntries ! = 0 )
NotifyMemInfo ( MemBlockFlags : : WRITE , fileList - > normalEntries . ptr , fileList - > resultNumNormalEntries * sizeof ( SceUtilitySavedataFileListEntry ) , " SavedataGetFilesList " ) ;
2013-05-31 06:32:13 +00:00
return 0 ;
2012-12-31 18:08:46 +00:00
}
2024-07-15 13:28:44 +00:00
bool SavedataParam : : GetSize ( SceUtilitySavedataParam * param ) {
if ( ! param ) {
2012-12-31 18:08:46 +00:00
return false ;
}
2017-10-15 04:52:23 +00:00
const std : : string saveDir = savePath + GetGameName ( param ) + GetSaveName ( param ) ;
2022-10-09 16:33:39 +00:00
bool exists = false ;
2012-12-31 18:08:46 +00:00
2024-07-15 13:28:44 +00:00
if ( param - > sizeInfo . IsValid ( ) ) {
2023-12-20 09:33:56 +00:00
auto listing = pspFileSystem . GetDirListing ( saveDir , & exists ) ;
2014-11-01 06:48:08 +00:00
const u64 freeBytes = MemoryStick_FreeSpace ( ) ;
2017-10-15 04:52:23 +00:00
s64 overwriteBytes = 0 ;
s64 writeBytes = 0 ;
for ( int i = 0 ; i < param - > sizeInfo - > numNormalEntries ; + + i ) {
const auto & entry = param - > sizeInfo - > normalEntries [ i ] ;
2022-10-09 16:33:39 +00:00
overwriteBytes + = FileFromListing ( listing , entry . name ) . size ;
2017-10-15 04:52:23 +00:00
writeBytes + = entry . size ;
}
for ( int i = 0 ; i < param - > sizeInfo - > numSecureEntries ; + + i ) {
const auto & entry = param - > sizeInfo - > secureEntries [ i ] ;
2022-10-09 16:33:39 +00:00
overwriteBytes + = FileFromListing ( listing , entry . name ) . size ;
2017-10-15 04:52:23 +00:00
writeBytes + = entry . size + 0x10 ;
}
2013-03-18 00:47:37 +00:00
2013-06-23 20:56:18 +00:00
param - > sizeInfo - > sectorSize = ( int ) MemoryStick_SectorSize ( ) ;
2014-11-01 06:48:08 +00:00
param - > sizeInfo - > freeSectors = ( int ) ( freeBytes / MemoryStick_SectorSize ( ) ) ;
2013-03-18 00:47:37 +00:00
2017-10-15 04:52:23 +00:00
// TODO: Is this after the specified files? Probably before?
2014-11-01 06:48:08 +00:00
param - > sizeInfo - > freeKB = ( int ) ( freeBytes / 1024 ) ;
2017-10-15 04:52:23 +00:00
std : : string spaceTxt = SavedataParam : : GetSpaceText ( freeBytes , false ) ;
truncate_cpy ( param - > sizeInfo - > freeString , spaceTxt . c_str ( ) ) ;
if ( writeBytes - overwriteBytes < ( s64 ) freeBytes ) {
param - > sizeInfo - > neededKB = 0 ;
// Note: this is "needed to overwrite".
param - > sizeInfo - > overwriteKB = 0 ;
spaceTxt = GetSpaceText ( 0 , true ) ;
2024-07-15 13:28:44 +00:00
truncate_cpy ( param - > sizeInfo - > neededString , spaceTxt ) ;
truncate_cpy ( param - > sizeInfo - > overwriteString , spaceTxt ) ;
2017-10-15 04:52:23 +00:00
} else {
// Bytes needed to save additional data.
s64 neededBytes = writeBytes - freeBytes ;
param - > sizeInfo - > neededKB = ( neededBytes + 1023 ) / 1024 ;
spaceTxt = GetSpaceText ( neededBytes , true ) ;
2024-07-15 13:28:44 +00:00
truncate_cpy ( param - > sizeInfo - > neededString , spaceTxt ) ;
2017-10-15 04:52:23 +00:00
if ( writeBytes - overwriteBytes < ( s64 ) freeBytes ) {
param - > sizeInfo - > overwriteKB = 0 ;
spaceTxt = GetSpaceText ( 0 , true ) ;
2024-07-15 13:28:44 +00:00
truncate_cpy ( param - > sizeInfo - > overwriteString , spaceTxt ) ;
2017-10-15 04:52:23 +00:00
} else {
s64 neededOverwriteBytes = writeBytes - freeBytes - overwriteBytes ;
param - > sizeInfo - > overwriteKB = ( neededOverwriteBytes + 1023 ) / 1024 ;
spaceTxt = GetSpaceText ( neededOverwriteBytes , true ) ;
2024-07-15 13:28:44 +00:00
truncate_cpy ( param - > sizeInfo - > overwriteString , spaceTxt ) ;
2017-10-15 04:52:23 +00:00
}
}
2021-04-04 01:45:24 +00:00
2024-07-15 13:28:44 +00:00
INFO_LOG ( Log : : sceUtility , " SectorSize: %d FreeSectors: %d FreeKB: %d neededKb: %d overwriteKb: %d " ,
param - > sizeInfo - > sectorSize , param - > sizeInfo - > freeSectors , param - > sizeInfo - > freeKB , param - > sizeInfo - > neededKB , param - > sizeInfo - > overwriteKB ) ;
2021-04-04 01:45:24 +00:00
NotifyMemInfo ( MemBlockFlags : : WRITE , param - > sizeInfo . ptr , sizeof ( PspUtilitySavedataSizeInfo ) , " SavedataGetSize " ) ;
2013-03-18 00:47:37 +00:00
}
return exists ;
2012-12-31 18:08:46 +00:00
}
2012-12-13 21:06:45 +00:00
void SavedataParam : : Clear ( )
{
2012-12-19 20:23:52 +00:00
if ( saveDataList )
2012-12-13 21:06:45 +00:00
{
2012-12-19 20:23:52 +00:00
for ( int i = 0 ; i < saveNameListDataCount ; i + + )
2012-12-13 21:06:45 +00:00
{
2013-12-08 22:46:05 +00:00
if ( saveDataList [ i ] . texture ! = NULL & & ( ! noSaveIcon | | saveDataList [ i ] . texture ! = noSaveIcon - > texture ) )
2013-12-08 19:06:18 +00:00
delete saveDataList [ i ] . texture ;
saveDataList [ i ] . texture = NULL ;
2012-12-13 21:06:45 +00:00
}
2013-12-08 19:06:18 +00:00
delete [ ] saveDataList ;
2024-04-12 11:44:40 +00:00
saveDataList = NULL ;
2012-12-29 20:55:58 +00:00
saveDataListCount = 0 ;
2012-12-13 21:06:45 +00:00
}
2013-05-12 02:41:51 +00:00
if ( noSaveIcon )
2013-01-29 21:46:20 +00:00
{
2024-04-12 11:44:40 +00:00
delete noSaveIcon - > texture ;
2013-12-08 19:06:18 +00:00
noSaveIcon - > texture = NULL ;
2013-01-29 21:46:20 +00:00
delete noSaveIcon ;
2024-04-12 11:44:40 +00:00
noSaveIcon = NULL ;
2013-01-29 21:46:20 +00:00
}
2012-12-13 21:06:45 +00:00
}
2012-12-25 02:37:28 +00:00
int SavedataParam : : SetPspParam ( SceUtilitySavedataParam * param )
2012-12-10 12:08:54 +00:00
{
pspParam = param ;
2020-12-11 17:12:04 +00:00
if ( ! pspParam ) {
2012-12-13 21:06:45 +00:00
Clear ( ) ;
2012-12-19 20:23:52 +00:00
return 0 ;
2012-12-13 21:06:45 +00:00
}
2012-12-10 12:08:54 +00:00
2020-10-04 16:00:28 +00:00
if ( param - > mode = = SCE_UTILITY_SAVEDATA_TYPE_LISTALLDELETE ) {
Clear ( ) ;
int realCount = 0 ;
auto allSaves = pspFileSystem . GetDirListing ( savePath ) ;
saveDataListCount = ( int ) allSaves . size ( ) ;
saveDataList = new SaveFileInfo [ saveDataListCount ] ;
for ( auto save : allSaves ) {
2020-10-08 15:59:36 +00:00
if ( save . type ! = FILETYPE_DIRECTORY | | save . name = = " . " | | save . name = = " .. " )
2020-10-04 16:00:28 +00:00
continue ;
std : : string fileDataDir = savePath + save . name ;
2020-12-11 17:12:04 +00:00
PSPFileInfo info = GetSaveInfo ( fileDataDir ) ;
2020-10-04 16:00:28 +00:00
SetFileInfo ( realCount , info , " " , save . name ) ;
realCount + + ;
}
saveNameListDataCount = realCount ;
return 0 ;
}
2012-12-10 12:08:54 +00:00
bool listEmptyFile = true ;
2020-12-11 17:12:04 +00:00
if ( param - > mode = = SCE_UTILITY_SAVEDATA_TYPE_LISTLOAD | | param - > mode = = SCE_UTILITY_SAVEDATA_TYPE_LISTDELETE ) {
2012-12-10 12:08:54 +00:00
listEmptyFile = false ;
}
2013-06-23 05:49:39 +00:00
SceUtilitySavedataSaveName * saveNameListData ;
2013-01-06 00:29:14 +00:00
bool hasMultipleFileName = false ;
2020-12-11 17:12:04 +00:00
if ( param - > saveNameList . IsValid ( ) ) {
2012-12-31 23:39:49 +00:00
Clear ( ) ;
2013-06-23 05:49:39 +00:00
saveNameListData = param - > saveNameList ;
2012-12-10 12:08:54 +00:00
// Get number of fileName in array
2012-12-28 21:36:37 +00:00
saveDataListCount = 0 ;
2020-12-11 17:12:04 +00:00
while ( saveNameListData [ saveDataListCount ] [ 0 ] ! = 0 ) {
2012-12-28 21:36:37 +00:00
saveDataListCount + + ;
2013-01-06 00:29:14 +00:00
}
2012-12-10 12:08:54 +00:00
2022-12-11 05:02:44 +00:00
if ( saveDataListCount > 0 & & WouldHaveMultiSaveName ( param ) ) {
2013-01-06 00:29:14 +00:00
hasMultipleFileName = true ;
saveDataList = new SaveFileInfo [ saveDataListCount ] ;
2013-10-27 11:03:14 +00:00
2013-01-06 00:29:14 +00:00
// get and stock file info for each file
int realCount = 0 ;
2013-10-27 11:03:14 +00:00
for ( int i = 0 ; i < saveDataListCount ; i + + ) {
// "<>" means saveName can be anything...
2014-01-24 09:21:13 +00:00
if ( strncmp ( saveNameListData [ i ] , " <> " , ARRAY_SIZE ( saveNameListData [ i ] ) ) = = 0 ) {
2013-10-28 18:14:39 +00:00
// TODO:Maybe we need a way to reorder the files?
2013-10-27 11:03:14 +00:00
auto allSaves = pspFileSystem . GetDirListing ( savePath ) ;
std : : string gameName = GetGameName ( param ) ;
2022-10-09 15:50:48 +00:00
for ( auto it = allSaves . begin ( ) ; it ! = allSaves . end ( ) ; + + it ) {
if ( it - > name . compare ( 0 , gameName . length ( ) , gameName ) = = 0 ) {
std : : string saveName = it - > name . substr ( gameName . length ( ) ) ;
2013-10-27 16:07:32 +00:00
2022-10-09 15:50:48 +00:00
if ( IsInSaveDataList ( saveName , realCount ) ) // Already in SaveDataList, skip...
2013-10-27 16:07:32 +00:00
continue ;
2022-10-09 15:50:48 +00:00
std : : string fileDataPath = savePath + it - > name ;
if ( it - > exists ) {
SetFileInfo ( realCount , * it , saveName ) ;
2024-07-14 12:42:59 +00:00
DEBUG_LOG ( Log : : sceUtility , " %s Exist " , fileDataPath . c_str ( ) ) ;
2013-10-27 11:03:14 +00:00
+ + realCount ;
} else {
if ( listEmptyFile ) {
2013-10-28 17:41:48 +00:00
// If file doesn't exist,we only skip...
continue ;
2013-10-27 11:03:14 +00:00
}
}
break ;
}
}
2013-05-31 07:57:25 +00:00
continue ;
2013-10-27 11:03:14 +00:00
}
2013-05-31 07:57:25 +00:00
2014-01-24 09:21:13 +00:00
const std : : string thisSaveName = FixedToString ( saveNameListData [ i ] , ARRAY_SIZE ( saveNameListData [ i ] ) ) ;
2012-12-14 22:08:56 +00:00
2020-10-01 02:03:30 +00:00
std : : string fileDataDir = savePath + GetGameName ( param ) + thisSaveName ;
2020-12-11 17:12:04 +00:00
PSPFileInfo info = GetSaveInfo ( fileDataDir ) ;
2020-10-01 02:03:30 +00:00
if ( info . exists ) {
2014-01-24 09:21:13 +00:00
SetFileInfo ( realCount , info , thisSaveName ) ;
2024-07-16 13:14:15 +00:00
INFO_LOG ( Log : : sceUtility , " Save data exists: %s = %s " , thisSaveName . c_str ( ) , fileDataDir . c_str ( ) ) ;
2012-12-10 12:08:54 +00:00
realCount + + ;
2020-10-01 02:03:30 +00:00
} else {
if ( listEmptyFile ) {
2014-01-24 09:21:13 +00:00
ClearFileInfo ( saveDataList [ realCount ] , thisSaveName ) ;
2024-07-16 13:14:15 +00:00
INFO_LOG ( Log : : sceUtility , " Listing missing save data: %s = %s " , thisSaveName . c_str ( ) , fileDataDir . c_str ( ) ) ;
2013-01-06 00:29:14 +00:00
realCount + + ;
2021-01-05 15:15:12 +00:00
} else {
2024-07-16 13:14:15 +00:00
INFO_LOG ( Log : : sceUtility , " Save data not found: %s = %s " , thisSaveName . c_str ( ) , fileDataDir . c_str ( ) ) ;
2013-01-06 00:29:14 +00:00
}
}
2012-12-10 12:08:54 +00:00
}
2013-01-06 00:29:14 +00:00
saveNameListDataCount = realCount ;
2012-12-10 12:08:54 +00:00
}
}
2020-12-11 17:12:04 +00:00
// Load info on only save
if ( ! hasMultipleFileName ) {
2012-12-18 21:27:59 +00:00
saveNameListData = 0 ;
2012-12-16 11:51:02 +00:00
Clear ( ) ;
saveDataList = new SaveFileInfo [ 1 ] ;
2012-12-29 10:56:27 +00:00
saveDataListCount = 1 ;
2012-12-16 11:51:02 +00:00
// get and stock file info for each file
2020-10-01 02:03:30 +00:00
std : : string fileDataDir = savePath + GetGameName ( param ) + GetSaveName ( param ) ;
2020-12-11 17:12:04 +00:00
PSPFileInfo info = GetSaveInfo ( fileDataDir ) ;
if ( info . exists ) {
2013-05-12 02:41:51 +00:00
SetFileInfo ( 0 , info , GetSaveName ( param ) ) ;
2024-07-16 13:14:15 +00:00
INFO_LOG ( Log : : sceUtility , " Save data exists: %s = %s " , GetSaveName ( param ) . c_str ( ) , fileDataDir . c_str ( ) ) ;
2012-12-16 11:51:02 +00:00
saveNameListDataCount = 1 ;
2020-12-11 17:12:04 +00:00
} else {
if ( listEmptyFile ) {
2013-05-12 02:41:51 +00:00
ClearFileInfo ( saveDataList [ 0 ] , GetSaveName ( param ) ) ;
2024-07-16 13:14:15 +00:00
INFO_LOG ( Log : : sceUtility , " Listing missing save data: %s = %s " , GetSaveName ( param ) . c_str ( ) , fileDataDir . c_str ( ) ) ;
2021-01-05 15:15:12 +00:00
} else {
2024-07-16 13:14:15 +00:00
INFO_LOG ( Log : : sceUtility , " Save data not found: %s = %s " , GetSaveName ( param ) . c_str ( ) , fileDataDir . c_str ( ) ) ;
2012-12-16 11:51:02 +00:00
}
saveNameListDataCount = 0 ;
2012-12-19 20:23:52 +00:00
return 0 ;
2012-12-16 11:51:02 +00:00
}
}
2012-12-19 20:23:52 +00:00
return 0 ;
2012-12-10 12:08:54 +00:00
}
2023-12-14 11:22:24 +00:00
void SavedataParam : : SetFileInfo ( SaveFileInfo & saveInfo , PSPFileInfo & info , const std : : string & saveName , const std : : string & savrDir )
2012-12-25 01:00:05 +00:00
{
2013-05-12 02:41:51 +00:00
saveInfo . size = info . size ;
saveInfo . saveName = saveName ;
saveInfo . idx = 0 ;
saveInfo . modif_time = info . mtime ;
2012-12-25 01:00:05 +00:00
2022-09-30 09:31:32 +00:00
std : : string saveDir = savrDir . empty ( ) ? GetGameName ( pspParam ) + saveName : savrDir ;
2020-10-04 16:00:28 +00:00
saveInfo . saveDir = saveDir ;
2012-12-25 09:36:51 +00:00
// Start with a blank slate.
2013-12-08 19:06:18 +00:00
if ( saveInfo . texture ! = NULL ) {
if ( ! noSaveIcon | | saveInfo . texture ! = noSaveIcon - > texture ) {
delete saveInfo . texture ;
}
saveInfo . texture = NULL ;
}
2013-05-12 02:41:51 +00:00
saveInfo . title [ 0 ] = 0 ;
saveInfo . saveTitle [ 0 ] = 0 ;
saveInfo . saveDetail [ 0 ] = 0 ;
2012-12-25 09:36:51 +00:00
2012-12-25 01:00:05 +00:00
// Search save image icon0
// TODO : If icon0 don't exist, need to use icon1 which is a moving icon. Also play sound
2022-10-09 17:36:19 +00:00
if ( ! ignoreTextures_ ) {
saveInfo . texture = new PPGeImage ( savePath + saveDir + " / " + ICON0_FILENAME ) ;
}
2012-12-25 01:00:05 +00:00
// Load info in PARAM.SFO
2022-10-09 17:36:19 +00:00
std : : string sfoFilename = savePath + saveDir + " / " + SFO_FILENAME ;
2022-10-09 18:25:39 +00:00
std : : shared_ptr < ParamSFOData > sfoFile = LoadCachedSFO ( sfoFilename ) ;
if ( sfoFile ) {
SetStringFromSFO ( * sfoFile , " TITLE " , saveInfo . title , sizeof ( saveInfo . title ) ) ;
SetStringFromSFO ( * sfoFile , " SAVEDATA_TITLE " , saveInfo . saveTitle , sizeof ( saveInfo . saveTitle ) ) ;
SetStringFromSFO ( * sfoFile , " SAVEDATA_DETAIL " , saveInfo . saveDetail , sizeof ( saveInfo . saveDetail ) ) ;
2020-10-01 07:41:27 +00:00
} else {
saveInfo . broken = true ;
2024-07-15 13:28:44 +00:00
truncate_cpy ( saveInfo . title , saveDir ) ;
2012-12-25 01:00:05 +00:00
}
}
2023-12-14 11:22:24 +00:00
void SavedataParam : : SetFileInfo ( int idx , PSPFileInfo & info , const std : : string & saveName , const std : : string & saveDir )
2013-05-12 02:41:51 +00:00
{
2020-10-04 16:00:28 +00:00
SetFileInfo ( saveDataList [ idx ] , info , saveName , saveDir ) ;
2013-05-12 02:41:51 +00:00
saveDataList [ idx ] . idx = idx ;
}
2017-12-02 16:17:50 +00:00
void SavedataParam : : ClearFileInfo ( SaveFileInfo & saveInfo , const std : : string & saveName ) {
2013-05-12 02:41:51 +00:00
saveInfo . size = 0 ;
saveInfo . saveName = saveName ;
saveInfo . idx = 0 ;
2020-10-01 07:41:27 +00:00
saveInfo . broken = false ;
2013-12-08 19:06:18 +00:00
if ( saveInfo . texture ! = NULL ) {
if ( ! noSaveIcon | | saveInfo . texture ! = noSaveIcon - > texture ) {
delete saveInfo . texture ;
}
saveInfo . texture = NULL ;
}
2013-05-12 02:41:51 +00:00
2017-12-02 16:17:50 +00:00
if ( GetPspParam ( ) - > newData . IsValid ( ) & & GetPspParam ( ) - > newData - > buf . IsValid ( ) ) {
2013-05-12 02:41:51 +00:00
// We have a png to show
2017-12-02 16:17:50 +00:00
if ( ! noSaveIcon ) {
2013-05-12 02:41:51 +00:00
noSaveIcon = new SaveFileInfo ( ) ;
2013-06-23 05:40:48 +00:00
PspUtilitySavedataFileData * newData = GetPspParam ( ) - > newData ;
2013-12-08 19:06:18 +00:00
noSaveIcon - > texture = new PPGeImage ( newData - > buf . ptr , ( SceSize ) newData - > size ) ;
2013-05-12 02:41:51 +00:00
}
2013-12-08 19:06:18 +00:00
saveInfo . texture = noSaveIcon - > texture ;
2017-12-02 16:17:50 +00:00
} else if ( ( u32 ) GetPspParam ( ) - > mode = = SCE_UTILITY_SAVEDATA_TYPE_SAVE & & GetPspParam ( ) - > icon0FileData . buf . IsValid ( ) ) {
const PspUtilitySavedataFileData & icon0FileData = GetPspParam ( ) - > icon0FileData ;
saveInfo . texture = new PPGeImage ( icon0FileData . buf . ptr , ( SceSize ) icon0FileData . size ) ;
2013-05-12 02:41:51 +00:00
}
}
2023-12-14 11:22:24 +00:00
PSPFileInfo SavedataParam : : GetSaveInfo ( const std : : string & saveDir ) {
2020-12-11 17:12:04 +00:00
PSPFileInfo info = pspFileSystem . GetFileInfo ( saveDir ) ;
if ( info . exists ) {
info . access = 0777 ;
auto allFiles = pspFileSystem . GetDirListing ( saveDir ) ;
bool firstFile = true ;
for ( auto file : allFiles ) {
if ( file . type = = FILETYPE_DIRECTORY | | file . name = = " . " | | file . name = = " .. " )
continue ;
2020-12-13 16:42:09 +00:00
// Use a file to determine save date.
2020-12-11 17:12:04 +00:00
if ( firstFile ) {
info . ctime = file . ctime ;
info . mtime = file . mtime ;
info . atime = file . atime ;
info . size + = file . size ;
firstFile = false ;
} else {
info . size + = file . size ;
}
}
}
return info ;
}
2014-01-24 09:21:13 +00:00
SceUtilitySavedataParam * SavedataParam : : GetPspParam ( )
{
return pspParam ;
}
const SceUtilitySavedataParam * SavedataParam : : GetPspParam ( ) const
2012-12-10 12:08:54 +00:00
{
return pspParam ;
}
int SavedataParam : : GetFilenameCount ( )
{
return saveNameListDataCount ;
}
const SaveFileInfo & SavedataParam : : GetFileInfo ( int idx )
{
return saveDataList [ idx ] ;
}
2020-10-04 16:00:28 +00:00
2014-01-24 09:21:13 +00:00
std : : string SavedataParam : : GetFilename ( int idx ) const
2012-12-10 12:08:54 +00:00
{
2012-12-16 11:51:02 +00:00
return saveDataList [ idx ] . saveName ;
2012-12-10 12:08:54 +00:00
}
2020-10-04 16:00:28 +00:00
std : : string SavedataParam : : GetSaveDir ( int idx ) const {
return saveDataList [ idx ] . saveDir ;
}
2012-12-10 12:08:54 +00:00
int SavedataParam : : GetSelectedSave ( )
{
2013-05-19 13:11:54 +00:00
// The slot # of the same save on LOAD/SAVE lists can dismatch so this isn't right anyhow
return selectedSave < saveNameListDataCount ? selectedSave : 0 ;
2012-12-10 12:08:54 +00:00
}
2012-12-19 20:23:52 +00:00
2012-12-10 12:08:54 +00:00
void SavedataParam : : SetSelectedSave ( int idx )
{
selectedSave = idx ;
}
2012-12-28 21:36:37 +00:00
2013-05-20 11:03:58 +00:00
int SavedataParam : : GetFirstListSave ( )
{
return 0 ;
}
int SavedataParam : : GetLastListSave ( )
{
2013-10-23 06:07:56 +00:00
return saveNameListDataCount - 1 ;
2013-05-20 11:03:58 +00:00
}
2013-05-19 13:11:54 +00:00
int SavedataParam : : GetLatestSave ( )
{
int idx = 0 ;
2013-05-20 11:03:58 +00:00
time_t idxTime = 0 ;
2013-10-23 06:07:56 +00:00
for ( int i = 0 ; i < saveNameListDataCount ; + + i )
2013-05-20 11:03:58 +00:00
{
2013-10-23 13:25:25 +00:00
if ( saveDataList [ i ] . size = = 0 )
continue ;
2013-05-20 11:03:58 +00:00
time_t thisTime = mktime ( & saveDataList [ i ] . modif_time ) ;
2013-10-23 13:25:25 +00:00
if ( ( s64 ) idxTime < ( s64 ) thisTime )
2013-05-20 11:03:58 +00:00
{
idx = i ;
idxTime = thisTime ;
}
}
return idx ;
}
int SavedataParam : : GetOldestSave ( )
{
int idx = 0 ;
time_t idxTime = 0 ;
2013-10-23 06:07:56 +00:00
for ( int i = 0 ; i < saveNameListDataCount ; + + i )
2013-05-19 13:11:54 +00:00
{
2013-10-23 13:25:25 +00:00
if ( saveDataList [ i ] . size = = 0 )
continue ;
2013-05-19 13:11:54 +00:00
time_t thisTime = mktime ( & saveDataList [ i ] . modif_time ) ;
2013-10-23 13:25:25 +00:00
if ( ( s64 ) idxTime > ( s64 ) thisTime )
2013-05-20 11:03:58 +00:00
{
idx = i ;
idxTime = thisTime ;
}
}
return idx ;
}
int SavedataParam : : GetFirstDataSave ( )
{
int idx = 0 ;
2013-10-23 06:07:56 +00:00
for ( int i = 0 ; i < saveNameListDataCount ; + + i )
2013-05-20 11:03:58 +00:00
{
if ( saveDataList [ i ] . size ! = 0 )
{
2013-06-10 05:48:35 +00:00
idx = i ;
2013-05-20 11:03:58 +00:00
break ;
}
}
return idx ;
}
int SavedataParam : : GetLastDataSave ( )
{
int idx = 0 ;
2013-10-23 06:07:56 +00:00
for ( int i = saveNameListDataCount ; i > 0 ; )
2013-05-20 11:03:58 +00:00
{
- - i ;
if ( saveDataList [ i ] . size ! = 0 )
{
idx = i ;
break ;
}
}
return idx ;
}
int SavedataParam : : GetFirstEmptySave ( )
{
int idx = 0 ;
2013-10-23 06:07:56 +00:00
for ( int i = 0 ; i < saveNameListDataCount ; + + i )
2013-05-20 11:03:58 +00:00
{
if ( saveDataList [ i ] . size = = 0 )
{
idx = i ;
break ;
}
}
return idx ;
}
int SavedataParam : : GetLastEmptySave ( )
{
int idx = 0 ;
2013-10-23 06:07:56 +00:00
for ( int i = saveNameListDataCount ; i > 0 ; )
2013-05-20 11:03:58 +00:00
{
- - i ;
if ( saveDataList [ i ] . size = = 0 )
2013-05-19 13:11:54 +00:00
{
idx = i ;
2013-05-20 11:03:58 +00:00
break ;
2013-05-19 13:11:54 +00:00
}
}
return idx ;
}
2022-12-11 05:02:44 +00:00
int SavedataParam : : GetSaveNameIndex ( const SceUtilitySavedataParam * param ) {
2013-11-12 09:10:56 +00:00
std : : string saveName = GetSaveName ( param ) ;
for ( int i = 0 ; i < saveNameListDataCount ; i + + )
{
// TODO: saveName may contain wildcards
if ( saveDataList [ i ] . saveName = = saveName )
{
return i ;
}
}
return 0 ;
}
2022-12-11 05:02:44 +00:00
bool SavedataParam : : WouldHaveMultiSaveName ( const SceUtilitySavedataParam * param ) {
2020-09-15 13:48:55 +00:00
switch ( ( SceUtilitySavedataType ) ( u32 ) param - > mode ) {
case SCE_UTILITY_SAVEDATA_TYPE_LOAD :
case SCE_UTILITY_SAVEDATA_TYPE_AUTOLOAD :
case SCE_UTILITY_SAVEDATA_TYPE_SAVE :
case SCE_UTILITY_SAVEDATA_TYPE_AUTOSAVE :
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE :
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATA :
case SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE :
case SCE_UTILITY_SAVEDATA_TYPE_READDATA :
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE :
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATA :
2020-09-30 16:39:14 +00:00
case SCE_UTILITY_SAVEDATA_TYPE_AUTODELETE :
case SCE_UTILITY_SAVEDATA_TYPE_DELETE :
2020-09-15 13:48:55 +00:00
case SCE_UTILITY_SAVEDATA_TYPE_ERASESECURE :
case SCE_UTILITY_SAVEDATA_TYPE_ERASE :
case SCE_UTILITY_SAVEDATA_TYPE_DELETEDATA :
return false ;
default :
return true ;
}
}
2022-10-09 17:36:19 +00:00
void SavedataParam : : DoState ( PointerWrap & p ) {
auto s = p . Section ( " SavedataParam " , 1 , 2 ) ;
2013-09-15 03:23:03 +00:00
if ( ! s )
return ;
2012-12-28 21:36:37 +00:00
// pspParam is handled in PSPSaveDialog.
2020-08-10 04:20:42 +00:00
Do ( p , selectedSave ) ;
Do ( p , saveDataListCount ) ;
Do ( p , saveNameListDataCount ) ;
2022-10-09 17:36:19 +00:00
if ( p . mode = = p . MODE_READ ) {
2024-04-12 11:44:40 +00:00
delete [ ] saveDataList ;
2022-10-09 17:36:19 +00:00
if ( saveDataListCount ! = 0 ) {
2013-02-04 08:34:37 +00:00
saveDataList = new SaveFileInfo [ saveDataListCount ] ;
2020-08-10 04:20:42 +00:00
DoArray ( p , saveDataList , saveDataListCount ) ;
2022-10-09 17:36:19 +00:00
} else {
2022-12-02 12:28:06 +00:00
saveDataList = nullptr ;
2022-10-09 17:36:19 +00:00
}
2012-12-29 10:56:27 +00:00
}
2013-02-04 08:34:37 +00:00
else
2020-08-10 04:20:42 +00:00
DoArray ( p , saveDataList , saveDataListCount ) ;
2022-10-09 17:36:19 +00:00
if ( s > = 2 ) {
Do ( p , ignoreTextures_ ) ;
} else {
ignoreTextures_ = false ;
}
2012-12-28 21:36:37 +00:00
}
2013-01-28 23:11:02 +00:00
2024-07-16 15:07:11 +00:00
void SavedataParam : : ClearSFOCache ( ) {
2022-10-09 18:25:39 +00:00
std : : lock_guard < std : : mutex > guard ( cacheLock_ ) ;
sfoCache_ . clear ( ) ;
}
std : : shared_ptr < ParamSFOData > SavedataParam : : LoadCachedSFO ( const std : : string & path , bool orCreate ) {
std : : lock_guard < std : : mutex > guard ( cacheLock_ ) ;
if ( sfoCache_ . find ( path ) = = sfoCache_ . end ( ) ) {
std : : vector < u8 > data ;
2024-07-16 15:07:11 +00:00
if ( pspFileSystem . ReadEntireFile ( path , data , true ) < 0 ) {
2022-10-09 18:25:39 +00:00
// Mark as not existing for later.
sfoCache_ [ path ] . reset ( ) ;
} else {
sfoCache_ . emplace ( path , new ParamSFOData ( ) ) ;
// If it fails to load, also keep it to indicate failed.
if ( ! sfoCache_ . at ( path ) - > ReadSFO ( data ) )
sfoCache_ . at ( path ) . reset ( ) ;
}
}
if ( ! sfoCache_ . at ( path ) ) {
if ( ! orCreate )
return nullptr ;
sfoCache_ . at ( path ) . reset ( new ParamSFOData ( ) ) ;
}
return sfoCache_ . at ( path ) ;
}
2022-12-11 05:02:44 +00:00
int SavedataParam : : GetSaveCryptMode ( const SceUtilitySavedataParam * param , const std : : string & saveDirName ) {
2013-05-12 03:27:54 +00:00
std : : string dirPath = GetSaveFilePath ( param , GetSaveDir ( param , saveDirName ) ) ;
2013-05-31 06:55:49 +00:00
std : : string sfopath = dirPath + " / " + SFO_FILENAME ;
2022-10-09 18:25:39 +00:00
std : : shared_ptr < ParamSFOData > sfoFile = LoadCachedSFO ( sfopath ) ;
if ( sfoFile ) {
2022-10-09 15:50:48 +00:00
// save created in PPSSPP and not encrypted has '0' in SAVEDATA_PARAMS
u32 tmpDataSize = 0 ;
2022-10-09 18:25:39 +00:00
const u8 * tmpDataOrig = sfoFile - > GetValueData ( " SAVEDATA_PARAMS " , & tmpDataSize ) ;
2022-10-09 15:50:48 +00:00
if ( tmpDataSize = = 0 | | ! tmpDataOrig ) {
return 0 ;
}
switch ( tmpDataOrig [ 0 ] ) {
case 0 :
return 0 ;
case 0x01 :
return 1 ;
case 0x21 :
return 3 ;
case 0x41 :
return 5 ;
default :
// Well, it's not zero, so yes.
2024-07-14 12:42:59 +00:00
ERROR_LOG_REPORT ( Log : : sceUtility , " Unexpected SAVEDATA_PARAMS hash flag: %02x " , tmpDataOrig [ 0 ] ) ;
2022-10-09 15:50:48 +00:00
return 1 ;
2013-01-28 23:11:02 +00:00
}
}
2014-07-07 02:27:51 +00:00
return 0 ;
2013-01-28 23:11:02 +00:00
}
2023-12-14 11:22:24 +00:00
bool SavedataParam : : IsInSaveDataList ( const std : : string & saveName , int count ) {
2013-10-27 16:07:32 +00:00
for ( int i = 0 ; i < count ; + + i ) {
if ( strcmp ( saveDataList [ i ] . saveName . c_str ( ) , saveName . c_str ( ) ) = = 0 )
return true ;
}
return false ;
}