2020-12-05 13:08:24 +01:00
# include "stdafx.h"
2020-10-30 21:26:22 +01:00
# include "Emu/System.h"
2020-02-15 23:36:20 +01:00
# include "Emu/VFS.h"
2021-04-21 23:20:33 +02:00
# include "Emu/IdManager.h"
2020-09-03 20:56:02 +02:00
# include "Emu/localized_string.h"
2024-03-27 13:44:33 +01:00
# include "Emu/savestate_utils.hpp"
2020-02-27 16:24:47 +01:00
# include "Emu/Cell/lv2/sys_fs.h"
2019-03-02 10:14:33 +01:00
# include "Emu/Cell/lv2/sys_sync.h"
2019-04-01 18:53:09 +02:00
# include "Emu/Cell/lv2/sys_process.h"
2016-03-21 20:43:03 +01:00
# include "Emu/Cell/PPUModule.h"
2025-02-11 03:00:37 +01:00
# include "Emu/Cell/timers.hpp"
2017-07-16 18:10:45 +02:00
# include "Emu/Cell/Modules/cellSysutil.h"
2020-03-20 06:01:05 +01:00
# include "Emu/Cell/Modules/cellUserInfo.h"
2024-11-04 21:53:34 +01:00
# include "Emu/RSX/Overlays/overlay_message.h"
# include "Emu/system_config.h"
2014-08-23 22:40:04 +02:00
2014-09-04 19:32:20 +02:00
# include "cellSaveData.h"
2019-07-13 21:48:54 +02:00
# include "cellMsgDialog.h"
2014-03-28 20:06:15 +01:00
2016-04-25 12:49:12 +02:00
# include "Loader/PSF.h"
2016-04-27 00:27:24 +02:00
# include "Utilities/StrUtil.h"
2021-04-24 17:59:55 +02:00
# include "Utilities/date_time.h"
2025-02-11 03:00:37 +01:00
# include "Utilities/sema.h"
2016-04-25 12:49:12 +02:00
2016-07-27 23:43:22 +02:00
# include <mutex>
2016-05-13 15:55:34 +02:00
# include <algorithm>
2021-05-30 16:10:46 +02:00
# include <span>
2016-05-13 15:55:34 +02:00
2020-12-18 15:43:34 +01:00
# include "util/asm.hpp"
2018-08-25 14:39:00 +02:00
LOG_CHANNEL ( cellSaveData ) ;
2014-03-28 20:06:15 +01:00
2019-01-13 14:25:50 +01:00
template < >
void fmt_class_string < CellSaveDataError > : : format ( std : : string & out , u64 arg )
{
format_enum ( out , arg , [ ] ( auto error )
{
switch ( error )
{
STR_CASE ( CELL_SAVEDATA_ERROR_CBRESULT ) ;
STR_CASE ( CELL_SAVEDATA_ERROR_ACCESS_ERROR ) ;
STR_CASE ( CELL_SAVEDATA_ERROR_INTERNAL ) ;
STR_CASE ( CELL_SAVEDATA_ERROR_PARAM ) ;
STR_CASE ( CELL_SAVEDATA_ERROR_NOSPACE ) ;
STR_CASE ( CELL_SAVEDATA_ERROR_BROKEN ) ;
STR_CASE ( CELL_SAVEDATA_ERROR_FAILURE ) ;
STR_CASE ( CELL_SAVEDATA_ERROR_BUSY ) ;
STR_CASE ( CELL_SAVEDATA_ERROR_NOUSER ) ;
STR_CASE ( CELL_SAVEDATA_ERROR_SIZEOVER ) ;
STR_CASE ( CELL_SAVEDATA_ERROR_NODATA ) ;
STR_CASE ( CELL_SAVEDATA_ERROR_NOTSUPPORTED ) ;
}
return unknown ;
} ) ;
}
2017-01-24 13:29:58 +01:00
SaveDialogBase : : ~ SaveDialogBase ( )
{
}
2021-04-24 17:59:55 +02:00
std : : string SaveDataEntry : : date ( ) const
{
return date_time : : fmt_time ( " %c " , mtime ) ;
}
std : : string SaveDataEntry : : data_size ( ) const
{
std : : string metric = " KB " ;
u64 sz = utils : : aligned_div ( size , 1000 ) ;
if ( sz > 1000 )
{
metric = " MB " ;
sz = utils : : aligned_div ( sz , 1000 ) ;
}
return fmt : : format ( " %lu %s " , sz , metric ) ;
}
2015-08-04 16:48:24 +02:00
// cellSaveData aliases (only for cellSaveData.cpp)
using PSetList = vm : : ptr < CellSaveDataSetList > ;
using PSetBuf = vm : : ptr < CellSaveDataSetBuf > ;
using PFuncFixed = vm : : ptr < CellSaveDataFixedCallback > ;
using PFuncList = vm : : ptr < CellSaveDataListCallback > ;
using PFuncStat = vm : : ptr < CellSaveDataStatCallback > ;
using PFuncFile = vm : : ptr < CellSaveDataFileCallback > ;
using PFuncDone = vm : : ptr < CellSaveDataDoneCallback > ;
2015-04-14 16:54:03 +02:00
enum : u32
{
2015-09-10 16:13:31 +02:00
SAVEDATA_OP_AUTO_SAVE = 0 ,
SAVEDATA_OP_AUTO_LOAD = 1 ,
2015-04-14 16:54:03 +02:00
SAVEDATA_OP_LIST_AUTO_SAVE = 2 ,
SAVEDATA_OP_LIST_AUTO_LOAD = 3 ,
2015-09-10 16:13:31 +02:00
SAVEDATA_OP_LIST_SAVE = 4 ,
SAVEDATA_OP_LIST_LOAD = 5 ,
SAVEDATA_OP_FIXED_SAVE = 6 ,
SAVEDATA_OP_FIXED_LOAD = 7 ,
2015-04-14 16:54:03 +02:00
2023-10-15 01:27:15 +02:00
SAVEDATA_OP_LIST_IMPORT = 9 ,
SAVEDATA_OP_LIST_EXPORT = 10 ,
SAVEDATA_OP_FIXED_IMPORT = 11 ,
SAVEDATA_OP_FIXED_EXPORT = 12 ,
2017-11-15 18:59:10 +01:00
SAVEDATA_OP_LIST_DELETE = 13 ,
2017-11-14 15:49:28 +01:00
SAVEDATA_OP_FIXED_DELETE = 14 ,
2015-04-14 16:54:03 +02:00
} ;
2017-04-03 15:01:50 +02:00
namespace
{
struct savedata_context
{
2020-03-20 19:34:44 +01:00
alignas ( 16 ) CellSaveDataCBResult result ;
alignas ( 16 ) CellSaveDataListGet listGet ;
alignas ( 16 ) CellSaveDataListSet listSet ;
alignas ( 16 ) CellSaveDataFixedSet fixedSet ;
alignas ( 16 ) CellSaveDataStatGet statGet ;
alignas ( 16 ) CellSaveDataStatSet statSet ;
alignas ( 16 ) CellSaveDataFileGet fileGet ;
alignas ( 16 ) CellSaveDataFileSet fileSet ;
alignas ( 16 ) CellSaveDataDoneGet doneGet ;
2017-04-03 15:01:50 +02:00
} ;
}
vm : : gvar < savedata_context > g_savedata_context ;
2021-03-17 08:48:07 +01:00
struct savedata_manager
2020-03-13 15:36:08 +01:00
{
semaphore < > mutex ;
2021-04-21 23:20:33 +02:00
atomic_t < bool > enable_overlay { false } ;
2021-07-28 21:09:11 +02:00
atomic_t < s32 > last_cbresult_error_dialog { 0 } ; // CBRESULT errors are negative
2020-03-13 15:36:08 +01:00
} ;
2015-09-18 00:41:14 +02:00
2022-02-14 20:03:40 +01:00
int check_filename ( std : : string_view file_path , bool disallow_system_files , bool account_sfo_pfd )
{
if ( file_path . size ( ) > = CELL_SAVEDATA_FILENAME_SIZE )
{
// ****** sysutil savedata parameter error : 71 ******
return 71 ;
}
auto dotpos = file_path . find_last_of ( ' . ' ) ;
if ( dotpos = = umax )
{
// Point to end of string instead
dotpos = file_path . size ( ) ;
}
if ( file_path . empty ( ) | | dotpos > 8u | | file_path . size ( ) - dotpos > 4u )
{
// ****** sysutil savedata parameter error : 70 ******
return 70 ;
}
if ( file_path = = " . " sv | | ( ! account_sfo_pfd & & ( file_path = = " PARAM.SFO " sv | | file_path = = " PARAM.PFD " sv ) ) )
{
// ****** sysutil savedata parameter error : 70 ******
return 70 ;
}
2022-09-13 15:08:55 +02:00
char name [ CELL_SAVEDATA_FILENAME_SIZE + 3 ] ;
2022-02-14 20:03:40 +01:00
if ( dotpos )
{
// Copy file name
std : : span dst ( name , dotpos + 1 ) ;
strcpy_trunc ( dst , file_path ) ;
// Allow multiple '.' even though sysutil_check_name_string does not
std : : replace ( name , name + dotpos , ' . ' , ' - ' ) ;
// Allow '_' at start even though sysutil_check_name_string does not
if ( name [ 0 ] = = ' _ ' )
{
name [ 0 ] = ' - ' ;
}
if ( disallow_system_files & & ( ( dotpos > = 5u & & std : : memcmp ( name , " PARAM " , 5 ) = = 0 ) | |
( dotpos > = 4u & & std : : memcmp ( name , " ICON " , 4 ) = = 0 ) | |
( dotpos > = 3u & & std : : memcmp ( name , " PIC " , 3 ) = = 0 ) | |
( dotpos > = 3u & & std : : memcmp ( name , " SND " , 3 ) = = 0 ) ) )
{
// ****** sysutil savedata parameter error : 70 ******
return 70 ;
}
// Check filename
if ( sysutil_check_name_string ( name , 1 , 9 ) = = - 1 )
{
// ****** sysutil savedata parameter error : 70 ******
return 70 ;
}
}
if ( file_path . size ( ) > dotpos + 1 )
{
// Copy file extension
std : : span dst ( name , file_path . size ( ) - dotpos ) ;
strcpy_trunc ( dst , file_path . substr ( dotpos + 1 ) ) ;
// Allow '_' at start even though sysutil_check_name_string does not
if ( name [ 0 ] = = ' _ ' )
{
name [ 0 ] = ' - ' ;
}
// Check file extension
if ( sysutil_check_name_string ( name , 1 , 4 ) = = - 1 )
{
// ****** sysutil savedata parameter error : 70 ******
return 70 ;
}
}
return 0 ;
}
2019-07-13 21:48:54 +02:00
static std : : vector < SaveDataEntry > get_save_entries ( const std : : string & base_dir , const std : : string & prefix )
{
std : : vector < SaveDataEntry > save_entries ;
if ( base_dir . empty ( ) | | prefix . empty ( ) )
{
return save_entries ;
}
// get the saves matching the supplied prefix
for ( auto & & entry : fs : : dir ( base_dir ) )
{
2020-03-13 16:34:15 +01:00
if ( ! entry . is_directory | | sysutil_check_name_string ( entry . name . c_str ( ) , 1 , CELL_SAVEDATA_DIRNAME_SIZE ) ! = 0 )
2019-07-13 21:48:54 +02:00
{
continue ;
}
2020-03-13 16:34:15 +01:00
if ( ! entry . name . starts_with ( prefix ) )
2019-07-13 21:48:54 +02:00
{
continue ;
}
// PSF parameters
2022-11-27 08:59:57 +01:00
const psf : : registry psf = psf : : load_object ( base_dir + entry . name + " /PARAM.SFO " ) ;
2019-07-13 21:48:54 +02:00
if ( psf . empty ( ) )
{
continue ;
}
SaveDataEntry save_entry ;
2021-12-13 00:13:54 +01:00
save_entry . dirName = psf : : get_string ( psf , " SAVEDATA_DIRECTORY " ) ;
save_entry . listParam = psf : : get_string ( psf , " SAVEDATA_LIST_PARAM " ) ;
save_entry . title = psf : : get_string ( psf , " TITLE " ) ;
save_entry . subtitle = psf : : get_string ( psf , " SUB_TITLE " ) ;
save_entry . details = psf : : get_string ( psf , " DETAIL " ) ;
2019-07-13 21:48:54 +02:00
2020-02-23 03:03:01 +01:00
for ( const auto & entry2 : fs : : dir ( base_dir + entry . name ) )
2019-07-13 21:48:54 +02:00
{
2022-02-14 20:03:40 +01:00
if ( entry2 . is_directory | | check_filename ( vfs : : unescape ( entry2 . name ) , false , true ) )
2020-09-18 21:21:03 +02:00
{
continue ;
}
2019-07-13 21:48:54 +02:00
save_entry . size + = entry2 . size ;
}
save_entry . atime = entry . atime ;
save_entry . mtime = entry . mtime ;
save_entry . ctime = entry . ctime ;
if ( fs : : file icon { base_dir + entry . name + " /ICON0.PNG " } )
save_entry . iconBuf = icon . to_vector < uchar > ( ) ;
save_entry . isNew = false ;
2019-09-27 18:00:34 +02:00
save_entry . escaped = std : : move ( entry . name ) ;
2019-07-13 21:48:54 +02:00
save_entries . emplace_back ( save_entry ) ;
}
return save_entries ;
}
static error_code select_and_delete ( ppu_thread & ppu )
{
2024-03-27 13:44:33 +01:00
std : : unique_lock hle_lock ( g_fxo - > get < hle_locks_t > ( ) , std : : try_to_lock ) ;
if ( ! hle_lock )
{
ppu . state + = cpu_flag : : again ;
return { } ;
}
2021-03-17 08:48:07 +01:00
std : : unique_lock lock ( g_fxo - > get < savedata_manager > ( ) . mutex , std : : try_to_lock ) ;
2019-07-13 21:48:54 +02:00
if ( ! lock )
{
return CELL_SAVEDATA_ERROR_BUSY ;
}
const std : : string base_dir = vfs : : get ( fmt : : format ( " /dev_hdd0/home/%08u/savedata/ " , Emu . GetUsrId ( ) ) ) ;
auto save_entries = get_save_entries ( base_dir , Emu . GetTitleID ( ) ) ;
s32 selected = - 1 ;
s32 focused = - 1 ;
while ( true )
{
2019-12-29 13:08:37 +01:00
// Yield before a blocking dialog is being spawned
2019-07-13 21:48:54 +02:00
lv2_obj : : sleep ( ppu ) ;
2019-12-29 13:08:37 +01:00
// Display a blocking Save Data List asynchronously in the GUI thread.
2019-12-03 00:47:14 +01:00
if ( auto save_dialog = Emu . GetCallbacks ( ) . get_save_dialog ( ) )
{
2021-03-17 08:48:07 +01:00
selected = save_dialog - > ShowSaveDataList ( save_entries , focused , SAVEDATA_OP_LIST_DELETE , vm : : null , g_fxo - > get < savedata_manager > ( ) . enable_overlay ) ;
2019-12-03 00:47:14 +01:00
}
2019-07-13 21:48:54 +02:00
2019-12-29 13:08:37 +01:00
// Reschedule after a blocking dialog returns
if ( ppu . check_state ( ) )
{
return 0 ;
}
2019-12-03 00:47:14 +01:00
// Abort if dialog was canceled or selection is invalid in this context
if ( selected < 0 )
2019-07-13 21:48:54 +02:00
{
return CELL_CANCEL ;
}
// Set focused entry for the next iteration
focused = save_entries . empty ( ) ? - 1 : selected ;
// Get information from the selected entry
SaveDataEntry entry = save_entries [ selected ] ;
const std : : string info = entry . title + " \n " + entry . subtitle + " \n " + entry . details ;
2019-12-29 13:08:37 +01:00
// Reusable display message string
2020-09-03 20:56:02 +02:00
std : : string msg = get_localized_string ( localized_string_id : : CELL_SAVEDATA_DELETE_CONFIRMATION , info . c_str ( ) ) ;
2019-12-29 13:08:37 +01:00
// Yield before a blocking dialog is being spawned
lv2_obj : : sleep ( ppu ) ;
// Get user confirmation by opening a blocking dialog
2023-02-14 01:01:30 +01:00
s32 return_code = CELL_MSGDIALOG_BUTTON_NONE ;
2024-09-20 16:38:31 +02:00
error_code res = open_msg_dialog ( true , CELL_MSGDIALOG_TYPE_SE_TYPE_NORMAL | CELL_MSGDIALOG_TYPE_BUTTON_TYPE_YESNO , vm : : make_str ( msg ) , msg_dialog_source : : _cellSaveData , vm : : null , vm : : null , vm : : null , & return_code ) ;
2019-07-13 21:48:54 +02:00
2019-12-29 13:08:37 +01:00
// Reschedule after a blocking dialog returns
2019-12-29 01:01:54 +01:00
if ( ppu . check_state ( ) )
{
return 0 ;
}
2019-07-13 21:48:54 +02:00
if ( res ! = CELL_OK )
{
return CELL_SAVEDATA_ERROR_INTERNAL ;
}
2023-02-14 01:01:30 +01:00
if ( return_code = = CELL_MSGDIALOG_BUTTON_YES )
2019-07-13 21:48:54 +02:00
{
// Remove directory
2019-09-27 18:00:34 +02:00
const std : : string path = base_dir + save_entries [ selected ] . escaped ;
2019-07-13 21:48:54 +02:00
fs : : remove_all ( path ) ;
// Remove entry from the list and reset the selection
save_entries . erase ( save_entries . cbegin ( ) + selected ) ;
selected = - 1 ;
2019-08-25 08:06:56 +02:00
// Reset the focused index if the new list is empty
if ( save_entries . empty ( ) )
{
focused = - 1 ;
}
2019-12-29 13:08:37 +01:00
// Update display message
2020-09-03 20:56:02 +02:00
msg = get_localized_string ( localized_string_id : : CELL_SAVEDATA_DELETE_SUCCESS , info . c_str ( ) ) ;
2019-07-13 21:48:54 +02:00
cellSaveData . success ( " %s " , msg ) ;
2019-12-29 13:08:37 +01:00
// Yield before blocking dialog is being spawned
lv2_obj : : sleep ( ppu ) ;
// Display success message by opening a blocking dialog (return value should be irrelevant here)
2024-09-20 16:38:31 +02:00
res = open_msg_dialog ( true , CELL_MSGDIALOG_TYPE_SE_TYPE_NORMAL | CELL_MSGDIALOG_TYPE_BUTTON_TYPE_OK , vm : : make_str ( msg ) , msg_dialog_source : : _cellSaveData ) ;
2019-12-29 13:08:37 +01:00
// Reschedule after blocking dialog returns
if ( ppu . check_state ( ) )
{
return 0 ;
}
2019-07-13 21:48:54 +02:00
}
}
return CELL_CANCEL ;
}
2019-12-29 13:08:37 +01:00
// Displays a CellSaveDataCBResult error message.
2020-02-28 12:28:13 +01:00
static error_code display_callback_result_error_message ( ppu_thread & ppu , const CellSaveDataCBResult & result , u32 errDialog )
2019-12-27 10:50:00 +01:00
{
std : : string msg ;
2019-12-29 13:08:37 +01:00
bool use_invalid_message = false ;
2019-12-27 10:50:00 +01:00
2020-02-28 12:28:13 +01:00
switch ( result . result )
2019-12-27 10:50:00 +01:00
{
case CELL_SAVEDATA_CBRESULT_ERR_NOSPACE :
2020-09-03 20:56:02 +02:00
msg = get_localized_string ( localized_string_id : : CELL_SAVEDATA_CB_NO_SPACE , fmt : : format ( " %d " , result . errNeedSizeKB ) . c_str ( ) ) ;
2019-12-27 10:50:00 +01:00
break ;
case CELL_SAVEDATA_CBRESULT_ERR_FAILURE :
2020-09-03 20:56:02 +02:00
msg = get_localized_string ( localized_string_id : : CELL_SAVEDATA_CB_FAILURE ) ;
2019-12-27 10:50:00 +01:00
break ;
case CELL_SAVEDATA_CBRESULT_ERR_BROKEN :
2020-09-03 20:56:02 +02:00
msg = get_localized_string ( localized_string_id : : CELL_SAVEDATA_CB_BROKEN ) ;
2019-12-27 10:50:00 +01:00
break ;
case CELL_SAVEDATA_CBRESULT_ERR_NODATA :
2020-09-03 20:56:02 +02:00
msg = get_localized_string ( localized_string_id : : CELL_SAVEDATA_CB_NO_DATA ) ;
2019-12-27 10:50:00 +01:00
break ;
case CELL_SAVEDATA_CBRESULT_ERR_INVALID :
2020-02-28 12:28:13 +01:00
if ( result . invalidMsg )
2019-12-29 13:08:37 +01:00
use_invalid_message = true ;
break ;
2019-12-27 10:50:00 +01:00
default :
2020-02-28 12:28:13 +01:00
// ****** sysutil savedata parameter error : 22 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 22 " } ;
2019-12-29 13:08:37 +01:00
}
2021-07-28 21:09:11 +02:00
if ( errDialog = = CELL_SAVEDATA_ERRDIALOG_NONE | |
( errDialog = = CELL_SAVEDATA_ERRDIALOG_NOREPEAT & & result . result = = g_fxo - > get < savedata_manager > ( ) . last_cbresult_error_dialog . exchange ( result . result ) ) )
{
// TODO: Find out if the "last error" is always tracked or only when NOREPEAT is set
2020-02-28 12:28:13 +01:00
return CELL_SAVEDATA_ERROR_CBRESULT ;
2021-07-28 21:09:11 +02:00
}
2020-02-28 12:28:13 +01:00
2019-12-29 13:08:37 +01:00
// Yield before a blocking dialog is being spawned
lv2_obj : : sleep ( ppu ) ;
// Get user confirmation by opening a blocking dialog (return value should be irrelevant here)
2024-09-20 16:38:31 +02:00
[[maybe_unused]] error_code res = open_msg_dialog ( true , CELL_MSGDIALOG_TYPE_SE_TYPE_NORMAL | CELL_MSGDIALOG_TYPE_BUTTON_TYPE_OK , use_invalid_message ? result . invalidMsg : vm : : make_str ( msg ) , msg_dialog_source : : _cellSaveData ) ;
2019-12-29 13:08:37 +01:00
// Reschedule after a blocking dialog returns
if ( ppu . check_state ( ) )
{
return 0 ;
2019-12-27 10:50:00 +01:00
}
2019-12-29 13:08:37 +01:00
return CELL_SAVEDATA_ERROR_CBRESULT ;
2019-12-27 10:50:00 +01:00
}
2020-09-03 20:56:02 +02:00
static std : : string get_confirmation_message ( u32 operation , const SaveDataEntry & entry )
2019-12-27 10:35:32 +01:00
{
2021-04-24 17:59:55 +02:00
const std : : string info = fmt : : format ( " %s \n %s \n %s \n %s \n \n %s " , entry . title , entry . subtitle , entry . date ( ) , entry . data_size ( ) , entry . details ) ;
2020-09-03 20:56:02 +02:00
2019-12-27 10:35:32 +01:00
if ( operation = = SAVEDATA_OP_LIST_DELETE | | operation = = SAVEDATA_OP_FIXED_DELETE )
{
2020-09-03 20:56:02 +02:00
return get_localized_string ( localized_string_id : : CELL_SAVEDATA_DELETE , info . c_str ( ) ) ;
2019-12-27 10:35:32 +01:00
}
else if ( operation = = SAVEDATA_OP_LIST_LOAD | | operation = = SAVEDATA_OP_FIXED_LOAD )
{
2020-09-03 20:56:02 +02:00
return get_localized_string ( localized_string_id : : CELL_SAVEDATA_LOAD , info . c_str ( ) ) ;
2019-12-27 10:35:32 +01:00
}
else if ( operation = = SAVEDATA_OP_LIST_SAVE | | operation = = SAVEDATA_OP_FIXED_SAVE )
{
2020-09-03 20:56:02 +02:00
return get_localized_string ( localized_string_id : : CELL_SAVEDATA_OVERWRITE , info . c_str ( ) ) ;
2019-12-27 10:35:32 +01:00
}
return " " ;
}
2019-09-23 02:07:23 +02:00
static s32 savedata_check_args ( u32 operation , u32 version , vm : : cptr < char > dirName ,
2019-04-01 18:53:09 +02:00
u32 errDialog , PSetList setList , PSetBuf setBuf , PFuncList funcList , PFuncFixed funcFixed , PFuncStat funcStat ,
2023-10-15 01:27:15 +02:00
PFuncFile funcFile , u32 /*container*/ , u32 unk_op_flags , vm : : ptr < void > /*userdata*/ , u32 userId , PFuncDone funcDone )
2019-04-01 18:53:09 +02:00
{
if ( version > CELL_SAVEDATA_VERSION_420 )
{
// ****** sysutil savedata parameter error : 1 ******
2019-09-23 02:07:23 +02:00
return 1 ;
2019-04-01 18:53:09 +02:00
}
if ( errDialog > CELL_SAVEDATA_ERRDIALOG_NOREPEAT )
{
// ****** sysutil savedata parameter error : 5 ******
2019-09-23 02:07:23 +02:00
return 5 ;
2019-04-01 18:53:09 +02:00
}
2023-10-15 01:27:15 +02:00
if ( operation < = SAVEDATA_OP_AUTO_LOAD | | operation = = SAVEDATA_OP_FIXED_IMPORT | | operation = = SAVEDATA_OP_FIXED_EXPORT )
2019-04-01 18:53:09 +02:00
{
2019-11-08 17:40:46 +01:00
if ( ! dirName )
{
// ****** sysutil savedata parameter error : 2 ******
return 2 ;
}
switch ( sysutil_check_name_string ( dirName . get_ptr ( ) , 1 , CELL_SAVEDATA_DIRNAME_SIZE ) )
{
case - 1 :
{
// ****** sysutil savedata parameter error : 3 ******
return 3 ;
}
case - 2 :
{
// ****** sysutil savedata parameter error : 4 ******
return 4 ;
}
case 0 : break ;
2020-12-10 11:37:26 +01:00
default : fmt : : throw_exception ( " Unreachable " ) ;
2019-11-08 17:40:46 +01:00
}
2019-04-01 18:53:09 +02:00
}
2023-10-15 01:27:15 +02:00
if ( ( operation > = SAVEDATA_OP_LIST_AUTO_SAVE & & operation < = SAVEDATA_OP_FIXED_LOAD ) | |
operation = = SAVEDATA_OP_LIST_IMPORT | | operation = = SAVEDATA_OP_LIST_EXPORT | |
operation = = SAVEDATA_OP_LIST_DELETE | | operation = = SAVEDATA_OP_FIXED_DELETE )
2019-04-01 18:53:09 +02:00
{
if ( ! setList )
{
// ****** sysutil savedata parameter error : 11 ******
2019-09-23 02:07:23 +02:00
return 11 ;
2019-04-01 18:53:09 +02:00
}
if ( setList - > sortType > CELL_SAVEDATA_SORTTYPE_SUBTITLE )
{
// ****** sysutil savedata parameter error : 12 ******
2019-09-23 02:07:23 +02:00
return 12 ;
2019-04-01 18:53:09 +02:00
}
if ( setList - > sortOrder > CELL_SAVEDATA_SORTORDER_ASCENT )
{
// ****** sysutil savedata parameter error : 13 ******
2019-09-23 02:07:23 +02:00
return 13 ;
2019-04-01 18:53:09 +02:00
}
if ( ! setList - > dirNamePrefix )
{
// ****** sysutil savedata parameter error : 15 ******
2019-09-23 02:07:23 +02:00
return 15 ;
2019-04-01 18:53:09 +02:00
}
if ( ! memchr ( setList - > dirNamePrefix . get_ptr ( ) , ' \0 ' , CELL_SAVEDATA_PREFIX_SIZE )
2019-07-03 19:17:04 +02:00
| | ( g_ps3_process_info . sdk_ver > 0x3FFFFF & & ! setList - > dirNamePrefix [ 0 ] ) )
2019-04-01 18:53:09 +02:00
{
// ****** sysutil savedata parameter error : 17 ******
2019-09-23 02:07:23 +02:00
return 17 ;
2019-04-01 18:53:09 +02:00
}
2023-10-15 01:27:15 +02:00
const bool allow_asterisk = ( operation = = SAVEDATA_OP_LIST_DELETE ) ; // TODO: SAVEDATA_OP_FIXED_DELETE ?
2019-11-08 17:40:46 +01:00
2023-10-15 01:27:15 +02:00
if ( ! allow_asterisk | | ! ( setList - > dirNamePrefix [ 0 ] = = ' * ' & & setList - > dirNamePrefix [ 1 ] = = ' \0 ' ) )
2019-11-08 17:40:46 +01:00
{
2023-10-15 01:27:15 +02:00
char cur , buf [ CELL_SAVEDATA_DIRNAME_SIZE + 1 ] { } ;
for ( s32 pos = 0 , posprefix = 0 ; cur = setList - > dirNamePrefix [ pos + + ] , true ; )
2019-11-08 17:40:46 +01:00
{
2023-10-15 01:27:15 +02:00
if ( cur = = ' \0 ' | | cur = = ' | ' )
2019-11-08 17:40:46 +01:00
{
2023-10-15 01:27:15 +02:00
// Check prefix if not empty
if ( posprefix )
2019-11-08 17:40:46 +01:00
{
2023-10-15 01:27:15 +02:00
switch ( sysutil_check_name_string ( buf , 1 , CELL_SAVEDATA_DIRNAME_SIZE ) )
{
case - 1 :
{
// ****** sysutil savedata parameter error : 16 ******
return 16 ;
}
case - 2 :
{
// ****** sysutil savedata parameter error : 17 ******
return 17 ;
}
case 0 : break ;
default : fmt : : throw_exception ( " Unreachable " ) ;
}
2019-11-08 17:40:46 +01:00
}
2023-10-15 01:27:15 +02:00
if ( cur = = ' \0 ' )
2019-11-08 17:40:46 +01:00
{
2023-10-15 01:27:15 +02:00
break ;
2019-11-08 17:40:46 +01:00
}
2023-10-15 01:27:15 +02:00
// Note: no need to reset buffer, only position
posprefix = 0 ;
continue ;
2019-11-08 17:40:46 +01:00
}
2023-10-15 01:27:15 +02:00
if ( posprefix = = CELL_SAVEDATA_DIRNAME_SIZE )
2019-11-08 17:40:46 +01:00
{
2023-10-15 01:27:15 +02:00
// ****** sysutil savedata parameter error : 17 ******
return 17 ;
2019-11-08 17:40:46 +01:00
}
2023-10-15 01:27:15 +02:00
buf [ posprefix + + ] = cur ;
2019-11-08 17:40:46 +01:00
}
}
2019-04-01 18:53:09 +02:00
if ( setList - > reserved )
{
// ****** sysutil savedata parameter error : 14 ******
2019-09-23 02:07:23 +02:00
return 14 ;
2019-04-01 18:53:09 +02:00
}
}
2023-10-15 01:27:15 +02:00
if ( operation > = SAVEDATA_OP_LIST_IMPORT & & operation < = SAVEDATA_OP_FIXED_EXPORT )
{
if ( ! funcDone | | userId > CELL_SYSUTIL_USERID_MAX )
{
// ****** sysutil savedata parameter error : 137 ******
return 137 ;
}
// There are no more parameters to check for the import and export functions.
return CELL_OK ;
}
2019-04-01 18:53:09 +02:00
if ( ! setBuf )
{
// ****** sysutil savedata parameter error : 74 ******
2019-09-23 02:07:23 +02:00
return 74 ;
2019-04-01 18:53:09 +02:00
}
2023-10-15 01:27:15 +02:00
if ( ( operation > = SAVEDATA_OP_LIST_AUTO_SAVE & & operation < = SAVEDATA_OP_FIXED_LOAD ) | | operation = = SAVEDATA_OP_LIST_DELETE | | operation = = SAVEDATA_OP_FIXED_DELETE )
2019-07-13 21:35:36 +02:00
{
2019-04-01 18:53:09 +02:00
if ( setBuf - > dirListMax > CELL_SAVEDATA_DIRLIST_MAX )
{
// ****** sysutil savedata parameter error : 8 ******
2019-09-23 02:07:23 +02:00
return 8 ;
2019-04-01 18:53:09 +02:00
}
CHECK_SIZE ( CellSaveDataDirList , 48 ) ;
if ( setBuf - > dirListMax * sizeof ( CellSaveDataDirList ) > setBuf - > bufSize )
{
// ****** sysutil savedata parameter error : 7 ******
2019-09-23 02:07:23 +02:00
return 7 ;
2019-04-01 18:53:09 +02:00
}
}
CHECK_SIZE ( CellSaveDataFileStat , 56 ) ;
2023-10-15 01:27:15 +02:00
if ( operation = = SAVEDATA_OP_LIST_DELETE | | operation = = SAVEDATA_OP_FIXED_DELETE )
2019-04-01 18:53:09 +02:00
{
2020-02-19 16:26:41 +01:00
if ( setBuf - > fileListMax ! = 0u )
2019-04-01 18:53:09 +02:00
{
// ****** sysutil savedata parameter error : 9 ******
2019-09-23 02:07:23 +02:00
return 9 ;
2019-04-01 18:53:09 +02:00
}
}
else if ( setBuf - > fileListMax * sizeof ( CellSaveDataFileStat ) > setBuf - > bufSize )
{
// ****** sysutil savedata parameter error : 7 ******
2019-09-23 02:07:23 +02:00
return 7 ;
2019-04-01 18:53:09 +02:00
}
if ( setBuf - > bufSize & & ! setBuf - > buf )
{
// ****** sysutil savedata parameter error : 6 ******
2019-09-23 02:07:23 +02:00
return 6 ;
2019-04-01 18:53:09 +02:00
}
2019-09-23 02:15:26 +02:00
for ( auto resv : setBuf - > reserved )
2019-04-01 18:53:09 +02:00
{
2019-09-26 20:57:03 +02:00
if ( resv )
2019-04-01 18:53:09 +02:00
{
// ****** sysutil savedata parameter error : 10 ******
2019-09-23 02:07:23 +02:00
return 10 ;
2019-04-01 18:53:09 +02:00
}
}
2023-10-15 01:27:15 +02:00
if ( ( operation = = SAVEDATA_OP_LIST_SAVE | | operation = = SAVEDATA_OP_LIST_LOAD | | operation = = SAVEDATA_OP_LIST_DELETE ) & & ! funcList )
2019-04-01 18:53:09 +02:00
{
// ****** sysutil savedata parameter error : 18 ******
2019-09-23 02:07:23 +02:00
return 18 ;
2019-04-01 18:53:09 +02:00
}
2023-10-15 01:27:15 +02:00
if ( ( operation = = SAVEDATA_OP_FIXED_SAVE | | operation = = SAVEDATA_OP_FIXED_LOAD | |
2019-04-01 18:53:09 +02:00
operation = = SAVEDATA_OP_LIST_AUTO_LOAD | | operation = = SAVEDATA_OP_LIST_AUTO_SAVE | | operation = = SAVEDATA_OP_FIXED_DELETE ) & & ! funcFixed )
{
// ****** sysutil savedata parameter error : 19 ******
2019-09-23 02:07:23 +02:00
return 19 ;
2019-04-01 18:53:09 +02:00
}
2023-10-15 01:27:15 +02:00
// NOTE: funcStat and funcFile are not present in the delete functions. unk_op_flags is 0x2 for SAVEDATA_OP_FIXED_DELETE, but I added the redundant check anyway for clarity.
if ( operation ! = SAVEDATA_OP_LIST_DELETE & & operation ! = SAVEDATA_OP_FIXED_DELETE & &
( ! ( unk_op_flags & 0x2 ) | | operation = = SAVEDATA_OP_AUTO_SAVE | | operation = = SAVEDATA_OP_AUTO_LOAD ) )
2019-04-01 18:53:09 +02:00
{
if ( ! funcStat )
{
// ****** sysutil savedata parameter error : 20 ******
2019-09-23 02:07:23 +02:00
return 20 ;
2019-04-01 18:53:09 +02:00
}
if ( ! ( unk_op_flags & 0x2 ) & & ! funcFile )
{
// ****** sysutil savedata parameter error : 18 ******
2019-09-23 02:07:23 +02:00
return 18 ;
2019-04-01 18:53:09 +02:00
}
}
2020-03-20 06:01:05 +01:00
if ( userId > CELL_SYSUTIL_USERID_MAX )
{
// ****** sysutil savedata parameter error : 91 ******
return 91 ;
}
2019-09-23 02:07:23 +02:00
return CELL_OK ;
2019-04-01 18:53:09 +02:00
}
2019-01-13 14:25:50 +01:00
static NEVER_INLINE error_code savedata_op ( ppu_thread & ppu , u32 operation , u32 version , vm : : cptr < char > dirName ,
2015-12-20 09:39:07 +01:00
u32 errDialog , PSetList setList , PSetBuf setBuf , PFuncList funcList , PFuncFixed funcFixed , PFuncStat funcStat ,
2019-04-01 18:53:09 +02:00
PFuncFile funcFile , u32 container , u32 unk_op_flags /*TODO*/ , vm : : ptr < void > userdata , u32 userId , PFuncDone funcDone )
2015-04-14 16:54:03 +02:00
{
2023-10-15 01:27:15 +02:00
if ( const auto & [ ok , list ] = setList . try_read ( ) ; ok )
2021-07-28 21:46:16 +02:00
cellSaveData . notice ( " savedata_op(): setList = { .sortType=%d, .sortOrder=%d, .dirNamePrefix='%s' } " , list . sortType , list . sortOrder , list . dirNamePrefix ) ;
2023-10-15 01:27:15 +02:00
if ( const auto & [ ok , buf ] = setBuf . try_read ( ) ; ok )
2021-07-28 21:46:16 +02:00
cellSaveData . notice ( " savedata_op(): setBuf = { .dirListMax=%d, .fileListMax=%d, .bufSize=%d } " , buf . dirListMax , buf . fileListMax , buf . bufSize ) ;
2019-09-23 02:07:23 +02:00
if ( const auto ecode = savedata_check_args ( operation , version , dirName , errDialog , setList , setBuf , funcList , funcFixed , funcStat ,
2019-09-23 02:15:26 +02:00
funcFile , container , unk_op_flags , userdata , userId , funcDone ) )
2019-04-01 18:53:09 +02:00
{
2021-05-19 09:12:57 +02:00
return { CELL_SAVEDATA_ERROR_PARAM , " (error %d) " , ecode } ;
2019-04-01 18:53:09 +02:00
}
2024-03-27 13:44:33 +01:00
std : : unique_lock hle_lock ( g_fxo - > get < hle_locks_t > ( ) , std : : try_to_lock ) ;
if ( ! hle_lock )
{
ppu . state + = cpu_flag : : again ;
return { } ;
}
2021-03-17 08:48:07 +01:00
std : : unique_lock lock ( g_fxo - > get < savedata_manager > ( ) . mutex , std : : try_to_lock ) ;
2015-04-16 17:33:55 +02:00
if ( ! lock )
{
return CELL_SAVEDATA_ERROR_BUSY ;
}
2019-04-02 22:57:20 +02:00
// Simulate idle time while data is being sent to VSH
2020-12-18 08:39:54 +01:00
const auto lv2_sleep = [ ] ( ppu_thread & ppu , usz sleep_time )
2019-04-02 22:57:20 +02:00
{
lv2_obj : : sleep ( ppu ) ;
2021-02-21 14:43:02 +01:00
lv2_obj : : wait_timeout ( sleep_time ) ;
2019-04-02 22:57:20 +02:00
ppu . check_state ( ) ;
} ;
lv2_sleep ( ppu , 500 ) ;
2020-02-27 16:37:36 +01:00
std : : memset ( g_savedata_context . get_ptr ( ) , 0 , g_savedata_context . size ( ) ) ;
2017-04-03 15:01:50 +02:00
vm : : ptr < CellSaveDataCBResult > result = g_savedata_context . ptr ( & savedata_context : : result ) ;
vm : : ptr < CellSaveDataListGet > listGet = g_savedata_context . ptr ( & savedata_context : : listGet ) ;
vm : : ptr < CellSaveDataListSet > listSet = g_savedata_context . ptr ( & savedata_context : : listSet ) ;
vm : : ptr < CellSaveDataFixedSet > fixedSet = g_savedata_context . ptr ( & savedata_context : : fixedSet ) ;
vm : : ptr < CellSaveDataStatGet > statGet = g_savedata_context . ptr ( & savedata_context : : statGet ) ;
vm : : ptr < CellSaveDataStatSet > statSet = g_savedata_context . ptr ( & savedata_context : : statSet ) ;
vm : : ptr < CellSaveDataFileGet > fileGet = g_savedata_context . ptr ( & savedata_context : : fileGet ) ;
vm : : ptr < CellSaveDataFileSet > fileSet = g_savedata_context . ptr ( & savedata_context : : fileSet ) ;
2017-11-15 18:59:10 +01:00
vm : : ptr < CellSaveDataDoneGet > doneGet = g_savedata_context . ptr ( & savedata_context : : doneGet ) ;
2015-08-13 15:28:42 +02:00
2018-03-17 07:17:27 +01:00
// userId(0) = CELL_SYSUTIL_USERID_CURRENT;
2015-05-28 17:14:22 +02:00
// path of the specified user (00000001 by default)
2018-07-26 00:37:56 +02:00
const std : : string base_dir = vfs : : get ( fmt : : format ( " /dev_hdd0/home/%08u/savedata/ " , userId ? userId : Emu . GetUsrId ( ) ) ) ;
2015-04-14 16:54:03 +02:00
2020-03-20 06:01:05 +01:00
if ( userId & & ! fs : : is_dir ( base_dir ) )
{
return CELL_SAVEDATA_ERROR_NOUSER ;
}
2015-04-17 06:37:13 +02:00
result - > userdata = userdata ; // probably should be assigned only once (allows the callback to change it)
2014-03-28 20:06:15 +01:00
2015-04-17 06:37:13 +02:00
SaveDataEntry save_entry ;
2014-03-28 20:06:15 +01:00
2015-04-15 16:27:37 +02:00
if ( setList )
{
2015-04-17 06:37:13 +02:00
std : : vector < SaveDataEntry > save_entries ;
2015-04-15 16:27:37 +02:00
listGet - > dirNum = 0 ;
listGet - > dirListNum = 0 ;
listGet - > dirList . set ( setBuf - > buf . addr ( ) ) ;
2017-11-14 15:49:28 +01:00
std : : memset ( listGet - > reserved , 0 , sizeof ( listGet - > reserved ) ) ;
2014-03-28 20:06:15 +01:00
2023-10-15 01:27:15 +02:00
std : : vector < std : : string > prefix_list = fmt : : split ( setList - > dirNamePrefix . get_ptr ( ) , { " | " } ) ;
2021-03-06 14:01:04 +01:00
// if prefix_list is empty game wants to check all savedata
if ( prefix_list . empty ( ) & & ( operation = = SAVEDATA_OP_LIST_LOAD | | operation = = SAVEDATA_OP_FIXED_LOAD ) )
{
2023-10-15 01:27:15 +02:00
cellSaveData . notice ( " savedata_op(): dirNamePrefix is empty. Listing all entries. operation=%d " , operation ) ;
2021-03-06 14:01:04 +01:00
prefix_list = { " " } ;
}
2014-08-22 21:34:43 +02:00
2023-10-15 01:27:15 +02:00
// if prefix_list only contains an asterisk the game wants to check all savedata
const bool allow_asterisk = ( operation = = SAVEDATA_OP_LIST_DELETE ) ; // TODO: SAVEDATA_OP_FIXED_DELETE ?
if ( allow_asterisk & & prefix_list . size ( ) = = 1 & & prefix_list . front ( ) = = " * " )
{
cellSaveData . notice ( " savedata_op(): dirNamePrefix is '*'. Listing all entries starting with '%s'. operation=%d " , Emu . GetTitleID ( ) , operation ) ;
prefix_list . front ( ) = Emu . GetTitleID ( ) ; // TODO: Let's be cautious for now and only list savedata starting with this game's ID
//prefix_list.front().clear(); // List savedata of all the games of this user
}
2017-07-16 18:10:45 +02:00
// get the saves matching the supplied prefix
2017-10-11 02:19:32 +02:00
for ( auto & & entry : fs : : dir ( base_dir ) )
2014-03-28 20:06:15 +01:00
{
2020-03-13 16:34:15 +01:00
if ( ! entry . is_directory | | sysutil_check_name_string ( entry . name . c_str ( ) , 1 , CELL_SAVEDATA_DIRNAME_SIZE ) ! = 0 )
2015-04-15 16:27:37 +02:00
{
2014-03-28 20:06:15 +01:00
continue ;
2015-04-15 16:27:37 +02:00
}
2014-03-28 20:06:15 +01:00
2023-10-15 01:27:15 +02:00
for ( const std : : string & prefix : prefix_list )
2015-04-15 16:27:37 +02:00
{
2020-02-17 22:43:23 +01:00
if ( entry . name . starts_with ( prefix ) )
2015-04-15 16:27:37 +02:00
{
// Count the amount of matches and the amount of listed directories
2020-03-20 17:39:04 +01:00
if ( ! listGet - > dirNum + + ) // total number of directories
{
// Clear buf exactly to bufSize only if dirNum becomes non-zero (regardless of dirListNum)
std : : memset ( setBuf - > buf . get_ptr ( ) , 0 , setBuf - > bufSize ) ;
}
2017-12-16 16:12:25 +01:00
if ( listGet - > dirListNum < setBuf - > dirListMax )
2015-04-15 16:27:37 +02:00
{
2017-12-16 16:12:25 +01:00
listGet - > dirListNum + + ; // number of directories in list
2015-04-15 16:27:37 +02:00
// PSF parameters
2022-11-27 08:59:57 +01:00
const psf : : registry psf = psf : : load_object ( base_dir + entry . name + " /PARAM.SFO " ) ;
2015-04-17 06:37:13 +02:00
2016-01-26 19:13:36 +01:00
if ( psf . empty ( ) )
2015-04-15 16:27:37 +02:00
{
break ;
}
2015-04-17 06:37:13 +02:00
SaveDataEntry save_entry2 ;
2021-12-13 00:13:54 +01:00
save_entry2 . dirName = psf : : get_string ( psf , " SAVEDATA_DIRECTORY " ) ;
save_entry2 . listParam = psf : : get_string ( psf , " SAVEDATA_LIST_PARAM " ) ;
save_entry2 . title = psf : : get_string ( psf , " TITLE " ) ;
save_entry2 . subtitle = psf : : get_string ( psf , " SUB_TITLE " ) ;
save_entry2 . details = psf : : get_string ( psf , " DETAIL " ) ;
2015-04-17 06:37:13 +02:00
2020-02-23 03:03:01 +01:00
for ( const auto & entry2 : fs : : dir ( base_dir + entry . name ) )
2015-04-16 01:17:42 +02:00
{
2022-02-14 20:03:40 +01:00
if ( entry2 . is_directory | | check_filename ( vfs : : unescape ( entry2 . name ) , false , true ) )
2020-09-18 21:21:03 +02:00
{
continue ;
}
2016-03-21 20:43:03 +01:00
save_entry2 . size + = entry2 . size ;
2015-04-16 01:17:42 +02:00
}
2016-03-21 20:43:03 +01:00
save_entry2 . atime = entry . atime ;
save_entry2 . mtime = entry . mtime ;
save_entry2 . ctime = entry . ctime ;
2018-04-02 13:27:38 +02:00
if ( fs : : file icon { base_dir + entry . name + " /ICON0.PNG " } )
save_entry2 . iconBuf = icon . to_vector < uchar > ( ) ;
2015-04-17 06:37:13 +02:00
save_entry2 . isNew = false ;
2019-09-27 18:00:34 +02:00
save_entry2 . escaped = std : : move ( entry . name ) ;
2017-07-13 17:38:13 +02:00
save_entries . emplace_back ( save_entry2 ) ;
2015-04-15 16:27:37 +02:00
}
break ;
}
}
2014-03-28 20:06:15 +01:00
}
2015-04-15 16:27:37 +02:00
// Sort the entries
2015-05-08 11:45:21 +02:00
{
const u32 order = setList - > sortOrder ;
const u32 type = setList - > sortType ;
std : : sort ( save_entries . begin ( ) , save_entries . end ( ) , [ = ] ( const SaveDataEntry & entry1 , const SaveDataEntry & entry2 )
{
if ( order = = CELL_SAVEDATA_SORTORDER_DESCENT & & type = = CELL_SAVEDATA_SORTTYPE_MODIFIEDTIME )
{
return entry1 . mtime > = entry2 . mtime ;
}
if ( order = = CELL_SAVEDATA_SORTORDER_DESCENT & & type = = CELL_SAVEDATA_SORTTYPE_SUBTITLE )
{
return entry1 . subtitle > = entry2 . subtitle ;
}
if ( order = = CELL_SAVEDATA_SORTORDER_ASCENT & & type = = CELL_SAVEDATA_SORTTYPE_MODIFIEDTIME )
{
return entry1 . mtime < entry2 . mtime ;
}
if ( order = = CELL_SAVEDATA_SORTORDER_ASCENT & & type = = CELL_SAVEDATA_SORTTYPE_SUBTITLE )
{
return entry1 . subtitle < entry2 . subtitle ;
}
return true ;
} ) ;
}
2014-08-22 21:34:43 +02:00
2015-04-15 16:27:37 +02:00
// Fill the listGet->dirList array
auto dir_list = listGet - > dirList . get_ptr ( ) ;
2014-03-28 20:06:15 +01:00
2015-04-15 16:27:37 +02:00
for ( const auto & entry : save_entries )
{
auto & dir = * dir_list + + ;
strcpy_trunc ( dir . dirName , entry . dirName ) ;
strcpy_trunc ( dir . listParam , entry . listParam ) ;
}
2014-08-22 21:34:43 +02:00
2015-04-17 06:37:13 +02:00
s32 selected = - 1 ;
2017-11-15 18:59:10 +01:00
s32 focused = - 1 ;
2015-04-17 06:37:13 +02:00
if ( funcList )
{
2020-03-20 06:39:10 +01:00
listSet - > focusPosition = CELL_SAVEDATA_FOCUSPOS_LISTHEAD ;
2020-03-20 19:34:44 +01:00
std : : memset ( result . get_ptr ( ) , 0 , : : offset32 ( & CellSaveDataCBResult : : userdata ) ) ;
2015-04-17 06:37:13 +02:00
// List Callback
2015-07-28 04:08:23 +02:00
funcList ( ppu , result , listGet , listSet ) ;
2015-04-17 06:37:13 +02:00
2020-02-28 12:28:13 +01:00
if ( const s32 res = result - > result ; res ! = CELL_SAVEDATA_CBRESULT_OK_NEXT )
2015-04-17 06:37:13 +02:00
{
2017-11-14 15:49:28 +01:00
cellSaveData . warning ( " savedata_op(): funcList returned result=%d. " , result - > result ) ;
2019-12-29 13:08:37 +01:00
2020-02-28 12:28:13 +01:00
// if the callback has returned ok, lets return OK.
// typically used at game launch when no list is actually required.
// CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM is only valid for funcFile and funcDone
if ( result - > result = = CELL_SAVEDATA_CBRESULT_OK_LAST | | result - > result = = CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM )
{
return CELL_OK ;
}
2015-04-17 06:37:13 +02:00
2020-02-28 12:28:13 +01:00
return display_callback_result_error_message ( ppu , * result , errDialog ) ;
2017-07-13 17:38:13 +02:00
}
2020-03-13 14:01:37 +01:00
if ( listSet - > fixedListNum > CELL_SAVEDATA_LISTITEM_MAX )
{
// ****** sysutil savedata parameter error : 38 ******
2023-07-25 08:26:59 +02:00
return { CELL_SAVEDATA_ERROR_PARAM , " 38 (fixedListNum=%d) " , listSet - > fixedListNum } ;
2020-03-13 14:01:37 +01:00
}
if ( listSet - > fixedListNum & & ! listSet - > fixedList )
{
// ****** sysutil savedata parameter error : 39 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 39 " } ;
}
else
{
// TODO: What happens if fixedListNum is zero?
}
std : : set < std : : string_view > selected_list ;
for ( u32 i = 0 ; i < listSet - > fixedListNum ; i + + )
2015-04-17 06:37:13 +02:00
{
2020-03-13 14:01:37 +01:00
switch ( sysutil_check_name_string ( listSet - > fixedList [ i ] . dirName , 1 , CELL_SAVEDATA_DIRNAME_SIZE ) )
{
case - 1 :
{
// ****** sysutil savedata parameter error : 40 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 40 " } ;
}
case - 2 :
2015-04-17 06:37:13 +02:00
{
2020-03-13 14:01:37 +01:00
if ( listSet - > fixedList [ i ] . dirName [ 0 ] ) // ???
2015-04-17 06:37:13 +02:00
{
2020-03-13 14:01:37 +01:00
// ****** sysutil savedata parameter error : 41 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 41 " } ;
2015-04-17 06:37:13 +02:00
}
2020-03-13 14:01:37 +01:00
break ;
}
case 0 : break ;
2020-12-10 11:37:26 +01:00
default : fmt : : throw_exception ( " Unreachable " ) ;
2015-04-17 06:37:13 +02:00
}
2020-03-13 14:01:37 +01:00
selected_list . emplace ( listSet - > fixedList [ i ] . dirName ) ;
}
// Clean save data list
save_entries . erase ( std : : remove_if ( save_entries . begin ( ) , save_entries . end ( ) , [ & selected_list ] ( const SaveDataEntry & entry ) - > bool
{
return selected_list . count ( entry . dirName ) = = 0 ;
2015-04-17 06:37:13 +02:00
} ) , save_entries . end ( ) ) ;
2020-03-13 14:01:37 +01:00
if ( listSet - > newData )
{
switch ( listSet - > newData - > iconPosition )
{
case CELL_SAVEDATA_ICONPOS_HEAD :
case CELL_SAVEDATA_ICONPOS_TAIL :
break ;
default :
{
// ****** sysutil savedata parameter error : 43 ******
2023-07-25 08:26:59 +02:00
return { CELL_SAVEDATA_ERROR_PARAM , " 43 (iconPosition=0x%x) " , listSet - > newData - > iconPosition } ;
2020-03-13 14:01:37 +01:00
}
}
if ( ! listSet - > newData - > dirName )
{
// ****** sysutil savedata parameter error : 44 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 44 " } ;
}
switch ( sysutil_check_name_string ( listSet - > newData - > dirName . get_ptr ( ) , 1 , CELL_SAVEDATA_DIRNAME_SIZE ) )
{
case - 1 :
{
// ****** sysutil savedata parameter error : 45 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 45 " } ;
}
case - 2 :
{
if ( listSet - > newData - > dirName [ 0 ] ) // ???
{
// ****** sysutil savedata parameter error : 4 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 46 " } ;
}
break ;
}
case 0 : break ;
2020-12-10 11:37:26 +01:00
default : fmt : : throw_exception ( " Unreachable " ) ;
2020-03-13 14:01:37 +01:00
}
}
2015-04-17 21:46:06 +02:00
switch ( const u32 pos_type = listSet - > focusPosition )
2015-04-17 06:37:13 +02:00
{
case CELL_SAVEDATA_FOCUSPOS_DIRNAME :
{
2020-03-13 14:01:37 +01:00
if ( ! listSet - > focusDirName )
{
// ****** sysutil savedata parameter error : 35 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 35 " } ;
}
switch ( sysutil_check_name_string ( listSet - > focusDirName . get_ptr ( ) , 1 , CELL_SAVEDATA_DIRNAME_SIZE ) )
{
case - 1 :
{
// ****** sysutil savedata parameter error : 36 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 36 " } ;
}
case - 2 :
{
if ( listSet - > focusDirName [ 0 ] ) // ???
{
// ****** sysutil savedata parameter error : 37 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 37 " } ;
}
break ;
}
case 0 : break ;
2020-12-10 11:37:26 +01:00
default : fmt : : throw_exception ( " Unreachable " ) ;
2020-03-13 14:01:37 +01:00
}
const std : : string dirStr = listSet - > focusDirName . get_ptr ( ) ;
2020-02-19 18:03:59 +01:00
for ( u32 i = 0 ; i < save_entries . size ( ) ; i + + )
2015-04-17 06:37:13 +02:00
{
2020-03-13 14:01:37 +01:00
if ( save_entries [ i ] . dirName = = dirStr )
2015-04-17 06:37:13 +02:00
{
focused = i ;
break ;
}
}
break ;
}
case CELL_SAVEDATA_FOCUSPOS_LISTHEAD :
{
focused = save_entries . empty ( ) ? - 1 : 0 ;
break ;
}
case CELL_SAVEDATA_FOCUSPOS_LISTTAIL :
{
2019-06-28 14:41:47 +02:00
focused = : : size32 ( save_entries ) - 1 ;
2015-04-17 06:37:13 +02:00
break ;
}
case CELL_SAVEDATA_FOCUSPOS_LATEST :
{
2021-05-22 09:35:15 +02:00
s64 max = smin ;
2015-04-17 06:37:13 +02:00
2020-02-19 18:03:59 +01:00
for ( u32 i = 0 ; i < save_entries . size ( ) ; i + + )
2015-04-17 06:37:13 +02:00
{
if ( save_entries [ i ] . mtime > max )
{
focused = i ;
max = save_entries [ i ] . mtime ;
}
}
break ;
}
case CELL_SAVEDATA_FOCUSPOS_OLDEST :
{
2021-05-22 09:35:15 +02:00
s64 min = smax ;
2015-04-17 06:37:13 +02:00
2020-02-19 18:03:59 +01:00
for ( u32 i = 0 ; i < save_entries . size ( ) ; i + + )
2015-04-17 06:37:13 +02:00
{
if ( save_entries [ i ] . mtime < min )
{
focused = i ;
min = save_entries [ i ] . mtime ;
}
}
break ;
}
case CELL_SAVEDATA_FOCUSPOS_NEWDATA :
{
2020-03-13 14:01:37 +01:00
if ( ! listSet - > newData )
{
// ****** sysutil savedata parameter error : 34 ******
cellSaveData . error ( " savedata_op(): listSet->newData is null while listSet->focusPosition is NEWDATA " ) ;
return { CELL_SAVEDATA_ERROR_PARAM , " 34 " } ;
}
2022-03-17 21:04:43 +01:00
if ( listSet - > newData - > iconPosition = = CELL_SAVEDATA_ICONPOS_TAIL )
{
focused = : : size32 ( save_entries ) ;
}
else
{
focused = 0 ;
}
2015-04-17 06:37:13 +02:00
break ;
}
default :
{
2019-09-23 02:07:23 +02:00
// ****** sysutil savedata parameter error : 34 ******
2016-03-21 20:43:03 +01:00
cellSaveData . error ( " savedata_op(): unknown listSet->focusPosition (0x%x) " , pos_type ) ;
2019-09-23 02:07:23 +02:00
return { CELL_SAVEDATA_ERROR_PARAM , " 34 " } ;
2015-04-17 06:37:13 +02:00
}
}
2017-11-15 18:59:10 +01:00
}
2019-09-25 00:49:13 +02:00
auto delete_save = [ & ] ( )
2017-11-15 18:59:10 +01:00
{
strcpy_trunc ( doneGet - > dirName , save_entries [ selected ] . dirName ) ;
2024-03-12 15:03:55 +01:00
doneGet - > hddFreeSizeKB = 40 * 1024 * 1024 - 256 ; // Read explanation in cellHddGameCheck
2017-11-15 18:59:10 +01:00
doneGet - > excResult = CELL_OK ;
std : : memset ( doneGet - > reserved , 0 , sizeof ( doneGet - > reserved ) ) ;
2019-09-27 18:00:34 +02:00
const std : : string old_path = base_dir + " .backup_ " + save_entries [ selected ] . escaped + " / " ;
const std : : string del_path = base_dir + save_entries [ selected ] . escaped + " / " ;
2019-09-25 00:49:13 +02:00
const fs : : dir _dir ( del_path ) ;
2020-03-03 09:24:49 +01:00
u64 size_bytes = 0 ;
2017-11-15 18:59:10 +01:00
for ( auto & & file : _dir )
{
if ( ! file . is_directory )
{
2020-12-18 15:43:34 +01:00
size_bytes + = utils : : align ( file . size , 1024 ) ;
2017-11-15 18:59:10 +01:00
}
}
2015-04-17 06:37:13 +02:00
2020-03-03 09:24:49 +01:00
doneGet - > sizeKB = : : narrow < s32 > ( size_bytes / 1024 ) ;
2019-09-25 00:49:13 +02:00
if ( _dir )
2017-11-15 18:59:10 +01:00
{
2019-09-25 00:49:13 +02:00
// Remove old backup
fs : : remove_all ( old_path ) ;
// Remove savedata by renaming
2020-09-11 13:06:46 +02:00
if ( ! vfs : : host : : rename ( del_path , old_path , & g_mp_sys_dev_hdd0 , false ) )
2019-09-25 00:49:13 +02:00
{
fmt : : throw_exception ( " Failed to move directory %s (%s) " , del_path , fs : : g_tls_error ) ;
}
2017-11-15 18:59:10 +01:00
2019-09-25 00:49:13 +02:00
// Cleanup
fs : : remove_all ( old_path ) ;
}
else
2017-11-15 18:59:10 +01:00
{
2019-09-25 00:49:13 +02:00
doneGet - > excResult = CELL_SAVEDATA_ERROR_NODATA ;
2017-11-15 18:59:10 +01:00
}
2020-03-20 19:34:44 +01:00
std : : memset ( result . get_ptr ( ) , 0 , : : offset32 ( & CellSaveDataCBResult : : userdata ) ) ;
2023-10-15 01:27:15 +02:00
if ( ! funcDone )
{
// TODO: return CELL_SAVEDATA_ERROR_PARAM at the correct location
fmt : : throw_exception ( " cellSaveData: funcDone is nullptr. operation=%d " , operation ) ;
}
2017-11-15 18:59:10 +01:00
funcDone ( ppu , result , doneGet ) ;
} ;
while ( funcList )
{
2019-12-29 13:08:37 +01:00
// Yield before a blocking dialog is being spawned
2019-03-02 10:14:33 +01:00
lv2_obj : : sleep ( ppu ) ;
2019-12-29 13:08:37 +01:00
// Display a blocking Save Data List asynchronously in the GUI thread.
2019-06-20 15:51:50 +02:00
if ( auto save_dialog = Emu . GetCallbacks ( ) . get_save_dialog ( ) )
{
2021-03-17 08:48:07 +01:00
selected = save_dialog - > ShowSaveDataList ( save_entries , focused , operation , listSet , g_fxo - > get < savedata_manager > ( ) . enable_overlay ) ;
2019-06-20 15:51:50 +02:00
}
else
{
selected = - 2 ;
}
2015-04-17 06:37:13 +02:00
2019-12-29 13:08:37 +01:00
// Reschedule after a blocking dialog returns
if ( ppu . check_state ( ) )
{
return 0 ;
}
2019-12-27 10:35:32 +01:00
// Cancel selected in UI
if ( selected = = - 2 )
{
return CELL_CANCEL ;
}
std : : string message ;
2017-07-13 17:38:13 +02:00
// UI returns -1 for new save games
2015-04-17 06:37:13 +02:00
if ( selected = = - 1 )
{
2021-04-24 16:00:24 +02:00
message = get_localized_string ( localized_string_id : : CELL_SAVEDATA_SAVE_CONFIRMATION ) ;
2017-07-13 17:38:13 +02:00
save_entry . dirName = listSet - > newData - > dirName . get_ptr ( ) ;
2019-09-27 18:00:34 +02:00
save_entry . escaped = vfs : : escape ( save_entry . dirName ) ;
2015-04-17 06:37:13 +02:00
}
2019-12-27 10:35:32 +01:00
else
{
// Get information from the selected entry
SaveDataEntry entry = save_entries [ selected ] ;
2020-09-03 20:56:02 +02:00
message = get_confirmation_message ( operation , entry ) ;
2019-12-27 10:35:32 +01:00
}
2017-07-13 17:38:13 +02:00
2019-12-29 13:08:37 +01:00
// Yield before a blocking dialog is being spawned
lv2_obj : : sleep ( ppu ) ;
// Get user confirmation by opening a blocking dialog
2023-02-14 01:01:30 +01:00
s32 return_code = CELL_MSGDIALOG_BUTTON_NONE ;
2024-09-20 16:38:31 +02:00
error_code res = open_msg_dialog ( true , CELL_MSGDIALOG_TYPE_SE_TYPE_NORMAL | CELL_MSGDIALOG_TYPE_BUTTON_TYPE_YESNO , vm : : make_str ( message ) , msg_dialog_source : : _cellSaveData , vm : : null , vm : : null , vm : : null , & return_code ) ;
2019-12-27 10:35:32 +01:00
2019-12-29 13:08:37 +01:00
// Reschedule after a blocking dialog returns
2019-12-29 01:01:54 +01:00
if ( ppu . check_state ( ) )
{
return 0 ;
}
2019-12-27 10:35:32 +01:00
if ( res ! = CELL_OK )
2017-07-01 20:42:09 +02:00
{
2019-12-27 10:35:32 +01:00
return CELL_SAVEDATA_ERROR_INTERNAL ;
}
2023-02-14 01:01:30 +01:00
if ( return_code ! = CELL_MSGDIALOG_BUTTON_YES )
2019-12-27 10:35:32 +01:00
{
2021-04-24 15:02:46 +02:00
if ( selected > = 0 )
{
focused = selected ;
}
2019-12-27 10:35:32 +01:00
continue ;
2017-07-01 20:42:09 +02:00
}
2017-11-15 18:59:10 +01:00
if ( operation = = SAVEDATA_OP_LIST_DELETE )
{
2019-09-25 00:49:13 +02:00
delete_save ( ) ;
2017-11-15 18:59:10 +01:00
2020-02-28 12:28:13 +01:00
if ( const s32 res = result - > result ; res ! = CELL_SAVEDATA_CBRESULT_OK_NEXT )
2017-11-15 18:59:10 +01:00
{
2020-02-28 12:28:13 +01:00
cellSaveData . warning ( " savedata_op(): funcDone returned result=%d. " , res ) ;
2019-12-29 13:08:37 +01:00
2020-02-28 12:28:13 +01:00
if ( res = = CELL_SAVEDATA_CBRESULT_OK_LAST | | res = = CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM )
{
return CELL_OK ;
}
2017-11-15 18:59:10 +01:00
2020-02-28 12:28:13 +01:00
return display_callback_result_error_message ( ppu , * result , errDialog ) ;
2017-11-15 18:59:10 +01:00
}
// CELL_SAVEDATA_CBRESULT_OK_NEXT expected
save_entries . erase ( save_entries . cbegin ( ) + selected ) ;
focused = save_entries . empty ( ) ? - 1 : selected ;
selected = - 1 ;
continue ;
}
break ;
2015-04-17 06:37:13 +02:00
}
if ( funcFixed )
{
2019-04-02 22:57:20 +02:00
lv2_sleep ( ppu , 250 ) ;
2020-03-20 19:34:44 +01:00
std : : memset ( result . get_ptr ( ) , 0 , : : offset32 ( & CellSaveDataCBResult : : userdata ) ) ;
2015-04-17 06:37:13 +02:00
// Fixed Callback
2015-07-28 04:08:23 +02:00
funcFixed ( ppu , result , listGet , fixedSet ) ;
2015-04-17 06:37:13 +02:00
2020-02-28 12:28:13 +01:00
if ( const s32 res = result - > result ; res ! = CELL_SAVEDATA_CBRESULT_OK_NEXT )
2017-07-16 18:10:45 +02:00
{
2020-02-28 12:28:13 +01:00
cellSaveData . warning ( " savedata_op(): funcFixed returned result=%d. " , res ) ;
2017-07-16 18:10:45 +02:00
2020-02-28 12:28:13 +01:00
// skip all following steps if OK_LAST (NOCONFIRM is not allowed)
if ( res = = CELL_SAVEDATA_CBRESULT_OK_LAST )
{
return CELL_OK ;
}
2019-12-29 13:08:37 +01:00
2020-02-28 12:28:13 +01:00
return display_callback_result_error_message ( ppu , * result , errDialog ) ;
2015-04-17 06:37:13 +02:00
}
2015-10-20 17:55:34 +02:00
if ( ! fixedSet - > dirName )
{
2019-09-23 02:07:23 +02:00
// ****** sysutil savedata parameter error : 26 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 26 " } ;
2015-10-20 17:55:34 +02:00
}
2020-03-01 06:10:27 +01:00
switch ( sysutil_check_name_string ( fixedSet - > dirName . get_ptr ( ) , 1 , CELL_SAVEDATA_DIRNAME_SIZE ) )
{
case - 1 :
{
// ****** sysutil savedata parameter error : 27 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 27 " } ;
}
case - 2 :
{
// ****** sysutil savedata parameter error : 28 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 28 " } ;
}
case 0 : break ;
2020-12-10 11:37:26 +01:00
default : fmt : : throw_exception ( " Unreachable " ) ;
2020-03-01 06:10:27 +01:00
}
const std : : string dirStr = fixedSet - > dirName . get_ptr ( ) ;
2020-02-19 18:03:59 +01:00
for ( u32 i = 0 ; i < save_entries . size ( ) ; i + + )
2015-04-17 06:37:13 +02:00
{
2020-03-01 06:10:27 +01:00
if ( save_entries [ i ] . dirName = = dirStr )
2015-04-17 06:37:13 +02:00
{
selected = i ;
break ;
}
}
2020-03-01 06:10:27 +01:00
switch ( fixedSet - > option )
{
case CELL_SAVEDATA_OPTION_NONE :
2019-12-27 11:45:37 +01:00
{
2020-03-01 06:10:27 +01:00
if ( operation ! = SAVEDATA_OP_FIXED_SAVE & & operation ! = SAVEDATA_OP_FIXED_LOAD & & operation ! = SAVEDATA_OP_FIXED_DELETE )
{
2020-06-26 16:48:13 +02:00
lv2_sleep ( ppu , 30000 ) ;
2020-03-01 06:10:27 +01:00
break ;
}
2019-12-27 11:45:37 +01:00
std : : string message ;
if ( selected = = - 1 )
{
2021-04-24 16:00:24 +02:00
message = get_localized_string ( localized_string_id : : CELL_SAVEDATA_SAVE_CONFIRMATION ) ;
2019-12-27 11:45:37 +01:00
}
else
{
// Get information from the selected entry
SaveDataEntry entry = save_entries [ selected ] ;
2020-09-03 20:56:02 +02:00
message = get_confirmation_message ( operation , entry ) ;
2019-12-27 11:45:37 +01:00
}
2019-12-29 13:08:37 +01:00
// Yield before a blocking dialog is being spawned
lv2_obj : : sleep ( ppu ) ;
// Get user confirmation by opening a blocking dialog
2023-02-14 01:01:30 +01:00
s32 return_code = CELL_MSGDIALOG_BUTTON_NONE ;
2024-09-20 16:38:31 +02:00
error_code res = open_msg_dialog ( true , CELL_MSGDIALOG_TYPE_SE_TYPE_NORMAL | CELL_MSGDIALOG_TYPE_BUTTON_TYPE_YESNO , vm : : make_str ( message ) , msg_dialog_source : : _cellSaveData , vm : : null , vm : : null , vm : : null , & return_code ) ;
2019-12-27 11:45:37 +01:00
2019-12-29 13:08:37 +01:00
// Reschedule after a blocking dialog returns
2019-12-29 01:01:54 +01:00
if ( ppu . check_state ( ) )
{
2021-02-13 15:50:07 +01:00
return { } ;
2019-12-29 01:01:54 +01:00
}
2019-12-27 11:45:37 +01:00
if ( res ! = CELL_OK )
{
return CELL_SAVEDATA_ERROR_INTERNAL ;
}
2023-02-14 01:01:30 +01:00
if ( return_code ! = CELL_MSGDIALOG_BUTTON_YES )
2019-12-27 11:45:37 +01:00
{
return CELL_CANCEL ;
}
2020-03-01 06:10:27 +01:00
break ;
}
case CELL_SAVEDATA_OPTION_NOCONFIRM :
2020-06-26 16:48:13 +02:00
lv2_sleep ( ppu , 30000 ) ;
2020-03-01 06:10:27 +01:00
break ;
default :
// ****** sysutil savedata parameter error : 81 ******
2023-07-25 08:26:59 +02:00
return { CELL_SAVEDATA_ERROR_PARAM , " 81 (option=0x%x) " , fixedSet - > option } ;
2019-12-27 11:45:37 +01:00
}
2015-04-17 06:37:13 +02:00
if ( selected = = - 1 )
{
2020-03-01 06:10:27 +01:00
save_entry . dirName = dirStr ;
2019-09-27 18:00:34 +02:00
save_entry . escaped = vfs : : escape ( save_entry . dirName ) ;
2015-04-17 06:37:13 +02:00
}
2017-07-16 18:10:45 +02:00
2017-11-15 18:59:10 +01:00
if ( operation = = SAVEDATA_OP_FIXED_DELETE )
{
2019-09-25 00:49:13 +02:00
delete_save ( ) ;
2017-11-15 18:59:10 +01:00
2020-02-28 12:28:13 +01:00
if ( const s32 res = result - > result ; res ! = CELL_SAVEDATA_CBRESULT_OK_NEXT )
2017-11-15 18:59:10 +01:00
{
2020-02-28 12:28:13 +01:00
cellSaveData . warning ( " savedata_op(): funcDone returned result=%d. " , res ) ;
2019-12-29 13:08:37 +01:00
2021-07-17 20:09:59 +02:00
if ( res = = CELL_SAVEDATA_CBRESULT_OK_LAST | | res = = CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM )
{
return CELL_OK ;
}
2020-02-28 12:28:13 +01:00
return display_callback_result_error_message ( ppu , * result , errDialog ) ;
2017-11-15 18:59:10 +01:00
}
return CELL_OK ;
}
2015-04-17 06:37:13 +02:00
}
2020-03-20 17:39:04 +01:00
if ( listGet - > dirNum )
{
// Clear buf exactly to bufSize again after funcFixed/List (for funcStat)
std : : memset ( setBuf - > buf . get_ptr ( ) , 0 , setBuf - > bufSize ) ;
}
2015-04-17 06:37:13 +02:00
if ( selected > = 0 )
{
2023-10-15 01:27:15 +02:00
if ( static_cast < u32 > ( selected ) < save_entries . size ( ) )
2015-04-21 21:43:40 +02:00
{
save_entry . dirName = std : : move ( save_entries [ selected ] . dirName ) ;
2019-09-27 18:00:34 +02:00
save_entry . escaped = vfs : : escape ( save_entry . dirName ) ;
2015-04-21 21:43:40 +02:00
}
else
{
2020-12-09 16:04:52 +01:00
fmt : : throw_exception ( " Invalid savedata selected " ) ;
2015-04-21 21:43:40 +02:00
}
2015-04-17 06:37:13 +02:00
}
2014-03-31 20:30:07 +02:00
}
2015-04-17 22:16:55 +02:00
2015-04-17 06:37:13 +02:00
if ( dirName )
{
save_entry . dirName = dirName . get_ptr ( ) ;
2019-09-27 18:00:34 +02:00
save_entry . escaped = vfs : : escape ( save_entry . dirName ) ;
2015-04-17 06:37:13 +02:00
}
2019-09-27 18:00:34 +02:00
const std : : string dir_path = base_dir + save_entry . escaped + " / " ;
const std : : string old_path = base_dir + " .backup_ " + save_entry . escaped + " / " ;
const std : : string new_path = base_dir + " .working_ " + save_entry . escaped + " / " ;
2014-03-31 20:30:07 +02:00
2022-11-27 08:59:57 +01:00
psf : : registry psf = psf : : load_object ( dir_path + " PARAM.SFO " ) ;
2018-11-01 14:22:33 +01:00
bool has_modified = false ;
2019-09-25 00:49:13 +02:00
bool recreated = false ;
2016-01-07 23:12:33 +01:00
2019-04-02 22:57:20 +02:00
lv2_sleep ( ppu , 250 ) ;
2022-09-03 06:29:07 +02:00
ppu . state + = cpu_flag : : wait ;
2019-04-02 22:57:20 +02:00
2019-09-03 02:39:24 +02:00
// Check if RPCS3_BLIST section exist in PARAM.SFO
// This section contains the list of files in the save ordered as they would be in BSD filesystem
std : : vector < std : : string > blist ;
2021-12-13 00:13:54 +01:00
if ( const auto it = psf . find ( " RPCS3_BLIST " ) ; it ! = psf . cend ( ) )
blist = fmt : : split ( it - > second . as_string ( ) , { " / " } , false ) ;
2019-09-03 02:39:24 +02:00
2015-04-18 15:38:42 +02:00
// Get save stats
2015-04-17 22:16:55 +02:00
{
2020-02-29 21:25:06 +01:00
if ( ! funcStat )
{
// ****** sysutil savedata parameter error : 20 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 20 " } ;
}
2016-03-21 20:43:03 +01:00
fs : : stat_t dir_info { } ;
2023-07-18 23:30:36 +02:00
if ( ! fs : : get_stat ( dir_path , dir_info ) )
2015-04-25 21:15:53 +02:00
{
2018-12-04 17:03:47 +01:00
// funcStat is called even if the directory doesn't exist.
2015-04-25 21:15:53 +02:00
}
2015-04-17 06:37:13 +02:00
2024-03-12 15:03:55 +01:00
statGet - > hddFreeSizeKB = 40 * 1024 * 1024 - 256 ; // Read explanation in cellHddGameCheck
2016-01-26 19:13:36 +01:00
statGet - > isNewData = save_entry . isNew = psf . empty ( ) ;
2015-04-17 06:37:13 +02:00
2015-04-17 22:16:55 +02:00
statGet - > dir . atime = save_entry . atime = dir_info . atime ;
statGet - > dir . mtime = save_entry . mtime = dir_info . mtime ;
statGet - > dir . ctime = save_entry . ctime = dir_info . ctime ;
strcpy_trunc ( statGet - > dir . dirName , save_entry . dirName ) ;
2015-04-17 06:37:13 +02:00
2016-01-26 19:13:36 +01:00
if ( ! psf . empty ( ) )
{
2021-12-13 00:13:54 +01:00
statGet - > getParam . parental_level = psf : : get_integer ( psf , " PARENTAL_LEVEL " ) ;
statGet - > getParam . attribute = psf : : get_integer ( psf , " ATTRIBUTE " ) ; // ???
strcpy_trunc ( statGet - > getParam . title , save_entry . title = psf : : get_string ( psf , " TITLE " ) ) ;
strcpy_trunc ( statGet - > getParam . subTitle , save_entry . subtitle = psf : : get_string ( psf , " SUB_TITLE " ) ) ;
strcpy_trunc ( statGet - > getParam . detail , save_entry . details = psf : : get_string ( psf , " DETAIL " ) ) ;
strcpy_trunc ( statGet - > getParam . listParam , save_entry . listParam = psf : : get_string ( psf , " SAVEDATA_LIST_PARAM " ) ) ;
2016-01-26 19:13:36 +01:00
}
2015-04-17 06:37:13 +02:00
2015-04-17 22:16:55 +02:00
statGet - > bind = 0 ;
statGet - > fileNum = 0 ;
statGet - > fileList . set ( setBuf - > buf . addr ( ) ) ;
statGet - > fileListNum = 0 ;
2020-03-20 17:39:04 +01:00
std : : memset ( statGet - > reserved , 0 , sizeof ( statGet - > reserved ) ) ;
if ( ! save_entry . isNew )
{
// Clear to bufSize if !isNew regardless of fileNum
std : : memset ( setBuf - > buf . get_ptr ( ) , 0 , setBuf - > bufSize ) ;
}
2015-04-15 16:27:37 +02:00
2015-04-17 22:16:55 +02:00
auto file_list = statGet - > fileList . get_ptr ( ) ;
2015-04-17 06:37:13 +02:00
2020-03-03 09:24:49 +01:00
u64 size_bytes = 0 ;
2018-02-12 21:51:33 +01:00
2019-09-03 02:39:24 +02:00
std : : vector < fs : : dir_entry > files_sorted ;
2017-10-11 02:19:32 +02:00
for ( auto & & entry : fs : : dir ( dir_path ) )
2015-04-17 22:16:55 +02:00
{
2017-10-11 02:19:32 +02:00
entry . name = vfs : : unescape ( entry . name ) ;
2018-02-12 21:51:33 +01:00
if ( ! entry . is_directory )
2015-04-17 06:37:13 +02:00
{
2022-02-14 20:03:40 +01:00
if ( check_filename ( entry . name , false , false ) )
2018-02-12 21:51:33 +01:00
{
continue ; // system files are not included in the file list
}
2019-09-03 02:39:24 +02:00
files_sorted . push_back ( entry ) ;
}
}
// clang-format off
std : : sort ( files_sorted . begin ( ) , files_sorted . end ( ) , [ & ] ( const fs : : dir_entry & a , const fs : : dir_entry & b ) - > bool
{
const auto a_it = std : : find ( blist . begin ( ) , blist . end ( ) , a . name ) ;
const auto b_it = std : : find ( blist . begin ( ) , blist . end ( ) , b . name ) ;
if ( a_it = = blist . end ( ) & & b_it = = blist . end ( ) )
{
// Order alphabetically for old saves
return a . name . compare ( b . name ) ;
}
return a_it < b_it ;
} ) ;
// clang-format on
for ( auto & & entry : files_sorted )
{
{
2015-04-17 22:16:55 +02:00
statGet - > fileNum + + ;
2015-04-17 06:37:13 +02:00
2020-12-18 15:43:34 +01:00
size_bytes + = utils : : align ( entry . size , 1024 ) ; // firmware rounds this value up
2018-02-12 21:51:33 +01:00
if ( statGet - > fileListNum > = setBuf - > fileListMax )
continue ;
statGet - > fileListNum + + ;
2015-04-17 22:16:55 +02:00
auto & file = * file_list + + ;
2018-02-12 21:51:33 +01:00
file . size = entry . size ;
file . atime = entry . atime ;
file . mtime = entry . mtime ;
file . ctime = entry . ctime ;
strcpy_trunc ( file . fileName , entry . name ) ;
2016-03-21 20:43:03 +01:00
if ( entry . name = = " ICON0.PNG " )
2015-04-17 22:16:55 +02:00
{
file . fileType = CELL_SAVEDATA_FILETYPE_CONTENT_ICON0 ;
}
2016-03-21 20:43:03 +01:00
else if ( entry . name = = " ICON1.PAM " )
2015-04-17 22:16:55 +02:00
{
file . fileType = CELL_SAVEDATA_FILETYPE_CONTENT_ICON1 ;
}
2016-03-21 20:43:03 +01:00
else if ( entry . name = = " PIC1.PNG " )
2015-04-17 22:16:55 +02:00
{
file . fileType = CELL_SAVEDATA_FILETYPE_CONTENT_PIC1 ;
}
2016-03-21 20:43:03 +01:00
else if ( entry . name = = " SND0.AT3 " )
2015-04-17 22:16:55 +02:00
{
file . fileType = CELL_SAVEDATA_FILETYPE_CONTENT_SND0 ;
}
2016-03-21 20:43:03 +01:00
else if ( psf : : get_integer ( psf , " * " + entry . name ) ) // let's put the list of protected files in PARAM.SFO (int param = 1 if protected)
2015-04-18 15:38:42 +02:00
{
file . fileType = CELL_SAVEDATA_FILETYPE_SECUREFILE ;
}
2015-04-17 22:16:55 +02:00
else
{
2015-04-18 15:38:42 +02:00
file . fileType = CELL_SAVEDATA_FILETYPE_NORMALFILE ;
2015-04-17 22:16:55 +02:00
}
}
2015-04-17 06:37:13 +02:00
}
2014-03-28 20:06:15 +01:00
2018-02-12 21:51:33 +01:00
statGet - > sysSizeKB = 35 ; // always reported as 35 regardless of actual file sizes
2020-03-03 09:24:49 +01:00
statGet - > sizeKB = ! save_entry . isNew ? : : narrow < s32 > ( ( size_bytes / 1024 ) + statGet - > sysSizeKB ) : 0 ;
2018-02-12 21:51:33 +01:00
2020-03-20 19:34:44 +01:00
std : : memset ( result . get_ptr ( ) , 0 , : : offset32 ( & CellSaveDataCBResult : : userdata ) ) ;
2015-04-17 22:16:55 +02:00
// Stat Callback
2015-07-28 04:08:23 +02:00
funcStat ( ppu , result , statGet , statSet ) ;
2022-09-03 06:29:07 +02:00
ppu . state + = cpu_flag : : wait ;
2014-03-31 20:30:07 +02:00
2020-02-28 12:28:13 +01:00
if ( const s32 res = result - > result ; res ! = CELL_SAVEDATA_CBRESULT_OK_NEXT )
2015-04-17 22:16:55 +02:00
{
2020-02-28 12:28:13 +01:00
cellSaveData . warning ( " savedata_op(): funcStat returned result=%d. " , res ) ;
2014-08-22 21:34:43 +02:00
2020-02-28 12:28:13 +01:00
// Skip and return without error on OK_LAST (NOCONFIRM is not allowed)
if ( res = = CELL_SAVEDATA_CBRESULT_OK_LAST )
2018-12-04 17:03:47 +01:00
{
return CELL_OK ;
}
2020-02-28 12:28:13 +01:00
return display_callback_result_error_message ( ppu , * result , errDialog ) ;
2017-07-16 18:10:45 +02:00
}
2017-11-15 18:59:10 +01:00
2015-04-17 22:16:55 +02:00
if ( statSet - > setParam )
{
2019-04-04 12:27:30 +02:00
if ( statSet - > setParam - > attribute > CELL_SAVEDATA_ATTR_NODUPLICATE )
{
// ****** sysutil savedata parameter error : 57 ******
2023-07-25 08:26:59 +02:00
return { CELL_SAVEDATA_ERROR_PARAM , " 57 (attribute=0x%x) " , statSet - > setParam - > attribute } ;
2019-04-04 12:27:30 +02:00
}
2023-08-07 16:08:19 +02:00
if ( statSet - > setParam - > parental_level > 11 )
2019-04-04 12:27:30 +02:00
{
2023-08-07 16:08:19 +02:00
// ****** sysutil savedata parameter error : 58 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 58 (sdk_ver=0x%x, parental_level=%d) " , g_ps3_process_info . sdk_ver , statSet - > setParam - > parental_level } ;
2019-04-04 12:27:30 +02:00
}
2023-08-07 16:08:19 +02:00
// Note: in firmware 3.70 or higher parental_level was changed to reserved2
for ( usz index = 0 ; ; index + + )
2020-03-13 14:01:37 +01:00
{
2023-08-07 16:08:19 +02:00
// Convert to pointer to avoid UB when accessing out of range
const u8 c = ( + statSet - > setParam - > listParam ) [ index ] ;
if ( c = = 0 | | index > = ( g_ps3_process_info . sdk_ver > 0x36FFFF ? std : : size ( statSet - > setParam - > listParam ) - 1 : std : : size ( statSet - > setParam - > listParam ) ) )
{
if ( c )
{
// ****** sysutil savedata parameter error : 76 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 76 (listParam=0x%016x) " , std : : bit_cast < be_t < u64 > > ( statSet - > setParam - > listParam ) } ;
}
break ;
}
if ( ( c < ' A ' | | c > ' Z ' ) & & ( c < ' 0 ' | | c > ' 9 ' ) & & c ! = ' - ' & & c ! = ' _ ' )
2020-03-13 14:01:37 +01:00
{
2023-08-07 16:08:19 +02:00
// ****** sysutil savedata parameter error : 77 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 77 (listParam=0x%016x) " , std : : bit_cast < be_t < u64 > > ( statSet - > setParam - > listParam ) } ;
2020-03-13 14:01:37 +01:00
}
}
2019-04-04 12:27:30 +02:00
for ( u8 resv : statSet - > setParam - > reserved )
{
if ( resv )
{
// ****** sysutil savedata parameter error : 59 ******
2019-09-23 02:07:23 +02:00
return { CELL_SAVEDATA_ERROR_PARAM , " 59 " } ;
2019-04-04 12:27:30 +02:00
}
}
2015-08-16 22:37:10 +02:00
// Update PARAM.SFO
2020-08-09 00:40:47 +02:00
psf : : assign ( psf , " ACCOUNT_ID " , psf : : array ( 16 , " 0000000000000000 " ) ) ; // ???
psf : : assign ( psf , " ATTRIBUTE " , statSet - > setParam - > attribute . value ( ) ) ;
psf : : assign ( psf , " CATEGORY " , psf : : string ( 4 , " SD " ) ) ; // ???
psf : : assign ( psf , " PARAMS " , psf : : string ( 1024 , { } ) ) ; // ???
psf : : assign ( psf , " PARAMS2 " , psf : : string ( 12 , { } ) ) ; // ???
psf : : assign ( psf , " PARENTAL_LEVEL " , statSet - > setParam - > parental_level . value ( ) ) ;
psf : : assign ( psf , " DETAIL " , psf : : string ( CELL_SAVEDATA_SYSP_DETAIL_SIZE , statSet - > setParam - > detail ) ) ;
psf : : assign ( psf , " SAVEDATA_DIRECTORY " , psf : : string ( CELL_SAVEDATA_DIRNAME_SIZE , save_entry . dirName ) ) ;
psf : : assign ( psf , " SAVEDATA_LIST_PARAM " , psf : : string ( CELL_SAVEDATA_SYSP_LPARAM_SIZE , statSet - > setParam - > listParam ) ) ;
psf : : assign ( psf , " SUB_TITLE " , psf : : string ( CELL_SAVEDATA_SYSP_SUBTITLE_SIZE , statSet - > setParam - > subTitle ) ) ;
psf : : assign ( psf , " TITLE " , psf : : string ( CELL_SAVEDATA_SYSP_TITLE_SIZE , statSet - > setParam - > title ) ) ;
2018-11-01 14:22:33 +01:00
has_modified = true ;
2016-01-26 19:13:36 +01:00
}
2019-04-04 12:27:30 +02:00
else if ( save_entry . isNew )
{
// ****** sysutil savedata parameter error : 50 ******
2019-09-23 02:07:23 +02:00
return { CELL_SAVEDATA_ERROR_PARAM , " 50 " } ;
2019-04-04 12:27:30 +02:00
}
2014-03-28 20:06:15 +01:00
2021-01-12 11:01:06 +01:00
switch ( statSet - > reCreateMode & CELL_SAVEDATA_RECREATE_MASK )
2015-04-17 22:16:55 +02:00
{
case CELL_SAVEDATA_RECREATE_NO :
2015-08-16 22:37:10 +02:00
{
2017-07-16 18:10:45 +02:00
//CELL_SAVEDATA_RECREATE_NO = overwrite and let the user know, not data is corrupt.
2017-11-15 18:59:10 +01:00
//cellSaveData.error("Savedata %s considered broken", save_entry.dirName);
2017-07-16 18:10:45 +02:00
//TODO: if this is a save, and it's not auto, then show a dialog
2024-01-07 12:56:08 +01:00
[[fallthrough]] ;
2015-08-16 22:37:10 +02:00
}
2015-04-17 22:16:55 +02:00
case CELL_SAVEDATA_RECREATE_NO_NOBROKEN :
2015-04-17 06:37:13 +02:00
{
2015-04-17 22:16:55 +02:00
break ;
}
2015-08-16 22:37:10 +02:00
2015-04-17 22:16:55 +02:00
case CELL_SAVEDATA_RECREATE_YES :
case CELL_SAVEDATA_RECREATE_YES_RESET_OWNER :
{
2019-04-04 12:27:30 +02:00
if ( ! statSet - > setParam )
{
// ****** sysutil savedata parameter error : 50 ******
2019-09-23 02:07:23 +02:00
return { CELL_SAVEDATA_ERROR_PARAM , " 50 " } ;
2019-04-04 12:27:30 +02:00
}
2017-07-16 18:10:45 +02:00
2020-08-09 00:40:47 +02:00
cellSaveData . warning ( " savedata_op(): Recreating savedata. (mode=%d) " , statSet - > reCreateMode ) ;
2019-09-25 00:49:13 +02:00
// Clear secure file info
for ( auto it = psf . cbegin ( ) , end = psf . cend ( ) ; it ! = end ; )
2015-04-17 06:37:13 +02:00
{
2019-09-25 00:49:13 +02:00
if ( it - > first [ 0 ] = = ' * ' )
it = psf . erase ( it ) ;
else
it + + ;
2015-04-17 06:37:13 +02:00
}
2019-09-25 00:49:13 +02:00
// Clear order info
blist . clear ( ) ;
2019-09-25 03:13:11 +02:00
// Set to not load files on next step
2019-09-25 00:49:13 +02:00
recreated = true ;
2019-09-25 03:13:11 +02:00
has_modified = true ;
2015-04-17 22:16:55 +02:00
break ;
}
2015-08-16 22:37:10 +02:00
2015-04-17 22:16:55 +02:00
default :
{
2019-09-23 02:07:23 +02:00
// ****** sysutil savedata parameter error : 48 ******
2016-03-21 20:43:03 +01:00
cellSaveData . error ( " savedata_op(): unknown statSet->reCreateMode (0x%x) " , statSet - > reCreateMode ) ;
2019-09-23 02:07:23 +02:00
return { CELL_SAVEDATA_ERROR_PARAM , " 48 " } ;
2015-04-17 22:16:55 +02:00
}
}
2015-04-18 15:38:42 +02:00
}
2015-04-17 06:37:13 +02:00
2015-07-16 18:31:58 +02:00
// Create save directory if necessary
2020-02-27 16:07:54 +01:00
if ( ! psf . empty ( ) & & save_entry . isNew & & ! fs : : create_dir ( dir_path ) & & fs : : g_tls_error ! = fs : : error : : exist )
2017-11-15 18:59:10 +01:00
{
2019-10-14 11:40:06 +02:00
cellSaveData . warning ( " savedata_op(): failed to create %s (%s) " , dir_path , fs : : g_tls_error ) ;
2017-07-16 18:10:45 +02:00
return CELL_SAVEDATA_ERROR_ACCESS_ERROR ;
2015-07-16 18:31:58 +02:00
}
2015-04-17 06:37:13 +02:00
// Enter the loop where the save files are read/created/deleted
2018-11-16 12:35:52 +01:00
std : : map < std : : string , std : : pair < s64 , s64 > > all_times ;
2018-11-01 14:22:33 +01:00
std : : map < std : : string , fs : : file > all_files ;
// First, preload all files (TODO: beware of possible lag, although it should be insignificant)
for ( auto & & entry : fs : : dir ( dir_path ) )
{
2019-09-25 00:49:13 +02:00
if ( ! recreated & & ! entry . is_directory )
2018-11-01 14:22:33 +01:00
{
// Read file into a vector and make a memory file
2019-09-27 18:00:34 +02:00
entry . name = vfs : : unescape ( entry . name ) ;
2021-02-23 10:08:22 +01:00
2022-02-14 20:03:40 +01:00
if ( check_filename ( entry . name , false , true ) )
2021-02-23 10:08:22 +01:00
{
continue ;
}
2018-11-16 12:35:52 +01:00
all_times . emplace ( entry . name , std : : make_pair ( entry . atime , entry . mtime ) ) ;
2018-11-01 14:22:33 +01:00
all_files . emplace ( std : : move ( entry . name ) , fs : : make_stream ( fs : : file ( dir_path + entry . name ) . to_vector < uchar > ( ) ) ) ;
}
}
2015-04-17 21:46:06 +02:00
2015-04-17 06:37:13 +02:00
fileGet - > excSize = 0 ;
2014-03-28 20:06:15 +01:00
2024-11-04 21:53:34 +01:00
// show indicator for automatic save or auto load interactions if the game requests it (statSet->indicator)
const bool show_auto_indicator = operation < = SAVEDATA_OP_LIST_AUTO_LOAD & & statSet & & statSet - > indicator & & g_cfg . misc . show_autosave_autoload_hint ;
if ( show_auto_indicator )
{
auto msg_text = localized_string_id : : INVALID ;
if ( operation = = SAVEDATA_OP_AUTO_SAVE | | operation = = SAVEDATA_OP_LIST_AUTO_SAVE )
{
msg_text = localized_string_id : : CELL_SAVEDATA_AUTOSAVE ;
}
else if ( operation = = SAVEDATA_OP_AUTO_LOAD | | operation = = SAVEDATA_OP_LIST_AUTO_LOAD )
{
msg_text = localized_string_id : : CELL_SAVEDATA_AUTOLOAD ;
}
auto msg_location = rsx : : overlays : : message_pin_location : : top_left ;
switch ( statSet - > indicator - > dispPosition & 0x0F )
{
case CELL_SAVEDATA_INDICATORPOS_LOWER_RIGHT :
msg_location = rsx : : overlays : : message_pin_location : : bottom_right ;
break ;
case CELL_SAVEDATA_INDICATORPOS_LOWER_LEFT :
msg_location = rsx : : overlays : : message_pin_location : : bottom_left ;
break ;
case CELL_SAVEDATA_INDICATORPOS_UPPER_RIGHT :
msg_location = rsx : : overlays : : message_pin_location : : top_right ;
break ;
case CELL_SAVEDATA_INDICATORPOS_UPPER_LEFT :
case CELL_SAVEDATA_INDICATORPOS_CENTER :
default :
msg_location = rsx : : overlays : : message_pin_location : : top_left ;
break ;
}
// TODO: Blinking variants
// RPCS3 saves basically instantaneously so there's not much point in showing auto indicator
// WHILE saving is in progress. Instead we show the indicator for 3 seconds to let the user
// know when the game autosaves.
rsx : : overlays : : queue_message ( msg_text , 3'000'000 , { } , msg_location ) ;
}
2019-09-25 03:13:11 +02:00
error_code savedata_result = CELL_OK ;
2023-03-04 18:51:19 +01:00
u64 delay_save_until = 0 ;
2015-05-28 17:14:22 +02:00
while ( funcFile )
2015-04-17 06:37:13 +02:00
{
2023-03-04 18:51:19 +01:00
lv2_sleep ( ppu , 2000 ) ;
2020-02-27 16:34:11 +01:00
std : : memset ( fileSet . get_ptr ( ) , 0 , fileSet . size ( ) ) ;
std : : memset ( fileGet - > reserved , 0 , sizeof ( fileGet - > reserved ) ) ;
2020-03-20 19:34:44 +01:00
std : : memset ( result . get_ptr ( ) , 0 , : : offset32 ( & CellSaveDataCBResult : : userdata ) ) ;
2020-02-27 16:34:11 +01:00
2015-07-28 04:08:23 +02:00
funcFile ( ppu , result , fileGet , fileSet ) ;
2022-09-03 06:29:07 +02:00
ppu . state + = cpu_flag : : wait ;
2015-04-17 06:37:13 +02:00
2020-02-28 12:28:13 +01:00
if ( const s32 res = result - > result ; res ! = CELL_SAVEDATA_CBRESULT_OK_NEXT )
2015-04-17 06:37:13 +02:00
{
2020-02-28 12:28:13 +01:00
if ( res = = CELL_SAVEDATA_CBRESULT_OK_LAST | | res = = CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM )
{
// TODO: display user prompt
2023-03-04 18:51:19 +01:00
// Some games (Jak II [NPUA80707]) rely on this delay
lv2_obj : : sleep ( ppu ) ;
delay_save_until = get_guest_system_time ( ) + ( has_modified ? 500'000 : 100'000 ) ;
2020-02-28 12:28:13 +01:00
break ;
}
2015-04-17 06:37:13 +02:00
2020-02-28 12:28:13 +01:00
cellSaveData . warning ( " savedata_op(): funcFile returned result=%d. " , res ) ;
if ( res < CELL_SAVEDATA_CBRESULT_ERR_INVALID | | res > CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM )
{
// ****** sysutil savedata parameter error : 22 ******
savedata_result = { CELL_SAVEDATA_ERROR_PARAM , " 22 " } ;
break ;
}
savedata_result = { CELL_SAVEDATA_ERROR_CBRESULT , res } ;
2015-04-17 06:37:13 +02:00
break ;
}
2019-01-13 14:25:50 +01:00
// TODO: Show progress if it's not an auto load/save
2017-07-16 18:10:45 +02:00
2015-04-19 19:14:16 +02:00
std : : string file_path ;
2015-04-17 06:37:13 +02:00
2015-04-17 22:43:54 +02:00
switch ( const u32 type = fileSet - > fileType )
2015-04-17 06:37:13 +02:00
{
case CELL_SAVEDATA_FILETYPE_SECUREFILE :
case CELL_SAVEDATA_FILETYPE_NORMALFILE :
{
2018-02-12 21:56:18 +01:00
if ( ! fileSet - > fileName )
{
// ****** sysutil savedata parameter error : 69 ******
2020-03-01 06:10:27 +01:00
savedata_result = { CELL_SAVEDATA_ERROR_PARAM , " 69 " } ;
break ;
}
2020-03-09 19:18:03 +01:00
const char * fileName = fileSet - > fileName . get_ptr ( ) ;
2020-03-01 06:10:27 +01:00
2020-03-09 19:18:03 +01:00
if ( const auto termpos = std : : memchr ( fileName , ' \0 ' , CELL_SAVEDATA_FILENAME_SIZE ) )
{
file_path . assign ( fileName , static_cast < const char * > ( termpos ) ) ;
}
else
2020-03-01 06:10:27 +01:00
{
// ****** sysutil savedata parameter error : 71 ******
savedata_result = { CELL_SAVEDATA_ERROR_PARAM , " 71 " } ;
break ;
2018-02-12 21:56:18 +01:00
}
2022-02-14 20:03:40 +01:00
if ( int error = check_filename ( file_path , true , false ) )
2020-03-09 19:18:03 +01:00
{
2022-02-14 20:03:40 +01:00
savedata_result = { CELL_SAVEDATA_ERROR_PARAM , " %d " , error } ;
2020-03-01 06:10:27 +01:00
break ;
}
2018-07-09 12:09:35 +02:00
if ( type = = CELL_SAVEDATA_FILETYPE_SECUREFILE )
{
cellSaveData . notice ( " SECUREFILE: %s -> %s " , file_path , fileSet - > secureFileId ) ;
}
2015-04-17 06:37:13 +02:00
break ;
}
case CELL_SAVEDATA_FILETYPE_CONTENT_ICON0 :
{
2015-04-19 19:14:16 +02:00
file_path = " ICON0.PNG " ;
2015-04-17 06:37:13 +02:00
break ;
}
case CELL_SAVEDATA_FILETYPE_CONTENT_ICON1 :
{
2015-04-19 19:14:16 +02:00
file_path = " ICON1.PAM " ;
2015-04-17 06:37:13 +02:00
break ;
}
case CELL_SAVEDATA_FILETYPE_CONTENT_PIC1 :
{
2015-04-19 19:14:16 +02:00
file_path = " PIC1.PNG " ;
2015-04-17 06:37:13 +02:00
break ;
}
case CELL_SAVEDATA_FILETYPE_CONTENT_SND0 :
{
2015-04-19 19:14:16 +02:00
file_path = " SND0.AT3 " ;
2015-04-17 06:37:13 +02:00
break ;
}
default :
{
2018-02-12 21:56:18 +01:00
// ****** sysutil savedata parameter error : 61 ******
2016-03-21 20:43:03 +01:00
cellSaveData . error ( " savedata_op(): unknown fileSet->fileType (0x%x) " , type ) ;
2019-09-25 03:13:11 +02:00
savedata_result = { CELL_SAVEDATA_ERROR_PARAM , " 61 " } ;
break ;
2015-04-17 06:37:13 +02:00
}
}
2019-09-25 03:13:11 +02:00
if ( savedata_result )
{
break ;
}
2019-09-03 02:39:24 +02:00
// clang-format off
auto add_to_blist = [ & ] ( const std : : string & to_add )
{
if ( std : : find ( blist . begin ( ) , blist . end ( ) , to_add ) = = blist . end ( ) )
{
2021-01-12 10:59:50 +01:00
if ( auto it = std : : find ( blist . begin ( ) , blist . end ( ) , " " ) ; it ! = blist . end ( ) )
2019-09-03 02:39:24 +02:00
* it = to_add ;
else
blist . push_back ( to_add ) ;
}
} ;
auto del_from_blist = [ & ] ( const std : : string & to_del )
{
if ( auto it = std : : find ( blist . begin ( ) , blist . end ( ) , to_del ) ; it ! = blist . end ( ) )
* it = " " ;
} ;
// clang-format on
2022-03-17 21:04:43 +01:00
cellSaveData . warning ( " savedata_op(): Fileop: file='%s', type=%d, op=%d, bufSize=%d, fileSize=%d, offset=%d " ,
file_path , fileSet - > fileType , fileSet - > fileOperation , fileSet - > fileBufSize , fileSet - > fileSize , fileSet - > fileOffset ) ;
2020-08-09 00:40:47 +02:00
2020-03-12 17:46:50 +01:00
if ( ( file_path = = " . " | | file_path = = " .. " ) & & fileSet - > fileOperation < = CELL_SAVEDATA_FILEOP_WRITE_NOTRUNC )
{
savedata_result = CELL_SAVEDATA_ERROR_BROKEN ;
break ;
}
2015-04-17 22:43:54 +02:00
switch ( const u32 op = fileSet - > fileOperation )
2015-04-17 06:37:13 +02:00
{
case CELL_SAVEDATA_FILEOP_READ :
{
2018-02-12 21:56:18 +01:00
if ( fileSet - > fileBufSize < fileSet - > fileSize )
{
// ****** sysutil savedata parameter error : 72 ******
2019-09-25 03:13:11 +02:00
savedata_result = { CELL_SAVEDATA_ERROR_PARAM , " 72 " } ;
break ;
2018-02-12 21:56:18 +01:00
}
2020-03-01 06:10:27 +01:00
if ( ! fileSet - > fileBuf & & fileSet - > fileBufSize )
2018-02-12 21:56:18 +01:00
{
// ****** sysutil savedata parameter error : 73 ******
2019-09-25 03:13:11 +02:00
savedata_result = { CELL_SAVEDATA_ERROR_PARAM , " 73 " } ;
break ;
2017-02-01 11:38:23 +01:00
}
2017-12-03 22:54:51 +01:00
2020-03-14 07:20:11 +01:00
const auto file = std : : as_const ( all_files ) . find ( file_path ) ;
2023-10-02 17:23:21 +02:00
const u64 pos = fileSet - > fileOffset ;
2020-03-12 17:46:50 +01:00
2023-10-02 17:23:21 +02:00
if ( file = = all_files . cend ( ) | | file - > second . size ( ) < = pos )
2020-03-12 17:46:50 +01:00
{
2023-04-12 03:26:44 +02:00
cellSaveData . error ( " Failed to open file %s%s (size=%d, fileOffset=%d) " , dir_path , file_path , file = = all_files . cend ( ) ? - 1 : file - > second . size ( ) , fileSet - > fileOffset ) ;
2020-03-12 17:46:50 +01:00
savedata_result = CELL_SAVEDATA_ERROR_FAILURE ;
break ;
}
2018-11-01 14:22:33 +01:00
// Read from memory file to vm
2023-10-02 17:23:21 +02:00
const u64 rr = lv2_file : : op_read ( file - > second , fileSet - > fileBuf , fileSet - > fileSize , pos ) ;
2018-11-01 14:22:33 +01:00
fileGet - > excSize = : : narrow < u32 > ( rr ) ;
2015-04-17 06:37:13 +02:00
break ;
}
case CELL_SAVEDATA_FILEOP_WRITE :
{
2020-02-29 06:55:59 +01:00
if ( fileSet - > fileBufSize < fileSet - > fileSize )
{
// ****** sysutil savedata parameter error : 72 ******
savedata_result = { CELL_SAVEDATA_ERROR_PARAM , " 72 " } ;
break ;
}
2020-03-01 06:10:27 +01:00
if ( ! fileSet - > fileBuf & & fileSet - > fileBufSize )
2020-02-29 06:55:59 +01:00
{
// ****** sysutil savedata parameter error : 73 ******
savedata_result = { CELL_SAVEDATA_ERROR_PARAM , " 73 " } ;
break ;
}
2020-03-12 17:46:50 +01:00
fs : : file & file = all_files [ file_path ] ;
if ( ! file )
{
file = fs : : make_stream < std : : vector < uchar > > ( ) ;
}
2018-11-01 14:22:33 +01:00
// Write to memory file and truncate
const u64 sr = file . seek ( fileSet - > fileOffset ) ;
2020-03-09 19:18:03 +01:00
const u64 wr = lv2_file : : op_write ( file , fileSet - > fileBuf , fileSet - > fileSize ) ;
2018-11-17 12:39:14 +01:00
file . trunc ( sr + wr ) ;
2018-11-01 14:22:33 +01:00
fileGet - > excSize = : : narrow < u32 > ( wr ) ;
2018-11-16 12:35:52 +01:00
all_times . erase ( file_path ) ;
2019-09-03 02:39:24 +02:00
add_to_blist ( file_path ) ;
2018-11-01 14:22:33 +01:00
has_modified = true ;
2015-04-17 06:37:13 +02:00
break ;
}
case CELL_SAVEDATA_FILEOP_DELETE :
{
2018-11-01 14:22:33 +01:00
// Delete memory file
2020-03-14 07:20:11 +01:00
if ( all_files . erase ( file_path ) = = 0 )
{
cellSaveData . error ( " Failed to delete file %s%s " , dir_path , file_path ) ;
savedata_result = CELL_SAVEDATA_ERROR_FAILURE ;
break ;
}
2018-11-01 14:22:33 +01:00
psf . erase ( " * " + file_path ) ;
2015-04-17 06:37:13 +02:00
fileGet - > excSize = 0 ;
2018-11-16 12:35:52 +01:00
all_times . erase ( file_path ) ;
2019-09-03 02:39:24 +02:00
del_from_blist ( file_path ) ;
2018-11-01 14:22:33 +01:00
has_modified = true ;
2015-04-17 06:37:13 +02:00
break ;
}
case CELL_SAVEDATA_FILEOP_WRITE_NOTRUNC :
{
2020-02-29 06:55:59 +01:00
if ( fileSet - > fileBufSize < fileSet - > fileSize )
{
// ****** sysutil savedata parameter error : 72 ******
savedata_result = { CELL_SAVEDATA_ERROR_PARAM , " 72 " } ;
break ;
}
2020-03-01 06:10:27 +01:00
if ( ! fileSet - > fileBuf & & fileSet - > fileBufSize )
2020-02-29 06:55:59 +01:00
{
// ****** sysutil savedata parameter error : 73 ******
savedata_result = { CELL_SAVEDATA_ERROR_PARAM , " 73 " } ;
break ;
}
2020-03-12 17:46:50 +01:00
fs : : file & file = all_files [ file_path ] ;
if ( ! file )
{
file = fs : : make_stream < std : : vector < uchar > > ( ) ;
}
2018-11-01 14:22:33 +01:00
// Write to memory file normally
2021-01-12 11:01:06 +01:00
file . seek ( fileSet - > fileOffset ) ;
2020-03-09 19:18:03 +01:00
const u64 wr = lv2_file : : op_write ( file , fileSet - > fileBuf , fileSet - > fileSize ) ;
2018-11-01 14:22:33 +01:00
fileGet - > excSize = : : narrow < u32 > ( wr ) ;
2018-11-16 12:35:52 +01:00
all_times . erase ( file_path ) ;
2019-09-03 02:39:24 +02:00
add_to_blist ( file_path ) ;
2018-11-01 14:22:33 +01:00
has_modified = true ;
2015-04-17 06:37:13 +02:00
break ;
}
default :
{
2019-09-23 02:07:23 +02:00
// ****** sysutil savedata parameter error : 60 ******
2016-03-21 20:43:03 +01:00
cellSaveData . error ( " savedata_op(): unknown fileSet->fileOperation (0x%x) " , op ) ;
2019-09-25 03:13:11 +02:00
savedata_result = { CELL_SAVEDATA_ERROR_PARAM , " 60 " } ;
break ;
2015-04-17 06:37:13 +02:00
}
}
2019-09-25 03:13:11 +02:00
if ( savedata_result )
{
break ;
}
2020-03-14 07:44:22 +01:00
if ( fileSet - > fileOperation ! = CELL_SAVEDATA_FILEOP_DELETE )
{
psf . emplace ( " * " + file_path , fileSet - > fileType = = CELL_SAVEDATA_FILETYPE_SECUREFILE ) ;
}
2015-04-18 15:38:42 +02:00
}
2018-11-01 14:22:33 +01:00
// Write PARAM.SFO and savedata
if ( ! psf . empty ( ) & & has_modified )
2015-04-18 15:38:42 +02:00
{
2018-11-01 14:22:33 +01:00
// First, create temporary directory
2018-12-18 17:43:13 +01:00
if ( fs : : create_dir ( new_path ) | | fs : : g_tls_error = = fs : : error : : exist )
2018-11-01 14:22:33 +01:00
{
fs : : remove_all ( new_path , false ) ;
}
else
{
fmt : : throw_exception ( " Failed to create directory %s (%s) " , new_path , fs : : g_tls_error ) ;
}
2019-09-03 02:39:24 +02:00
// add file list per FS order to PARAM.SFO
std : : string final_blist ;
final_blist = fmt : : merge ( blist , " / " ) ;
2020-12-18 15:43:34 +01:00
psf : : assign ( psf , " RPCS3_BLIST " , psf : : string ( utils : : align ( : : size32 ( final_blist ) + 1 , 4 ) , final_blist ) ) ;
2019-09-03 02:39:24 +02:00
2018-11-01 14:22:33 +01:00
// Write all files in temporary directory
auto & fsfo = all_files [ " PARAM.SFO " ] ;
fsfo = fs : : make_stream < std : : vector < uchar > > ( ) ;
2021-02-21 20:55:07 +01:00
fsfo . write ( psf : : save_object ( psf ) ) ;
2018-11-01 14:22:33 +01:00
for ( auto & & pair : all_files )
{
if ( auto file = pair . second . release ( ) )
{
2021-02-23 10:08:22 +01:00
auto & & fvec = static_cast < fs : : container_stream < std : : vector < uchar > > & > ( * file ) ;
2021-07-18 21:08:04 +02:00
# ifdef _WIN32
2021-06-20 13:45:33 +02:00
fs : : pending_file f ( new_path + vfs : : escape ( pair . first ) ) ;
f . file . write ( fvec . obj ) ;
ensure ( f . commit ( ) ) ;
2021-07-18 21:08:04 +02:00
# else
ensure ( fs : : write_file ( new_path + vfs : : escape ( pair . first ) , fs : : rewrite , fvec . obj ) ) ;
# endif
2018-11-01 14:22:33 +01:00
}
}
2018-11-16 12:35:52 +01:00
for ( auto & & pair : all_times )
{
// Restore atime/mtime for files which have not been modified
2019-09-27 18:00:34 +02:00
fs : : utime ( new_path + vfs : : escape ( pair . first ) , pair . second . first , pair . second . second ) ;
2018-11-16 12:35:52 +01:00
}
2018-11-01 14:22:33 +01:00
// Remove old backup
2019-09-25 00:49:13 +02:00
fs : : remove_all ( old_path ) ;
2021-02-23 11:09:20 +01:00
fs : : sync ( ) ;
2018-11-01 14:22:33 +01:00
// Backup old savedata
2020-09-11 13:06:46 +02:00
if ( ! vfs : : host : : rename ( dir_path , old_path , & g_mp_sys_dev_hdd0 , false ) )
2018-11-01 14:22:33 +01:00
{
2019-04-07 11:58:08 +02:00
fmt : : throw_exception ( " Failed to move directory %s (%s) " , dir_path , fs : : g_tls_error ) ;
2018-11-01 14:22:33 +01:00
}
// Commit new savedata
2020-09-11 13:06:46 +02:00
if ( ! vfs : : host : : rename ( new_path , dir_path , & g_mp_sys_dev_hdd0 , false ) )
2018-11-01 14:22:33 +01:00
{
2019-01-30 23:07:07 +01:00
// TODO: handle the case when only commit failed at the next save load
2019-04-07 11:58:08 +02:00
fmt : : throw_exception ( " Failed to move directory %s (%s) " , new_path , fs : : g_tls_error ) ;
2018-11-01 14:22:33 +01:00
}
// Remove backup again (TODO: may be changed to persistent backup implementation)
fs : : remove_all ( old_path ) ;
2015-04-17 06:37:13 +02:00
}
2024-11-04 21:53:34 +01:00
if ( show_auto_indicator )
{
// auto indicator should be hidden here if save/load throttling is added
}
2020-02-19 18:03:59 +01:00
if ( savedata_result + 0u = = CELL_SAVEDATA_ERROR_CBRESULT )
2019-12-27 10:50:00 +01:00
{
2020-02-28 12:28:13 +01:00
return display_callback_result_error_message ( ppu , * result , errDialog ) ;
2019-12-27 10:50:00 +01:00
}
2023-03-04 18:51:19 +01:00
if ( u64 current_time = get_guest_system_time ( ) ; current_time < delay_save_until )
{
lv2_sleep ( ppu , delay_save_until - current_time ) ;
}
2019-09-25 03:13:11 +02:00
return savedata_result ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
static NEVER_INLINE error_code savedata_get_list_item ( vm : : cptr < char > dirName , vm : : ptr < CellSaveDataDirStat > dir , vm : : ptr < CellSaveDataSystemFileParam > sysFileParam , vm : : ptr < u32 > bind , vm : : ptr < u32 > sizeKB , u32 userId )
2017-07-16 18:10:45 +02:00
{
2020-03-20 06:01:05 +01:00
if ( userId = = CELL_SYSUTIL_USERID_CURRENT )
2017-07-16 18:10:45 +02:00
{
2018-03-17 07:17:27 +01:00
userId = Emu . GetUsrId ( ) ;
2017-07-16 18:10:45 +02:00
}
2020-03-20 12:05:25 +01:00
else if ( userId > CELL_USERINFO_USER_MAX )
{
// ****** sysutil savedata parameter error : 137 ******
2023-07-25 08:26:59 +02:00
return { CELL_SAVEDATA_ERROR_PARAM , " 137 (userId=0x%x) " , userId } ;
2020-03-20 12:05:25 +01:00
}
if ( ! dirName )
{
// ****** sysutil savedata parameter error : 107 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 107 " } ;
}
switch ( sysutil_check_name_string ( dirName . get_ptr ( ) , 1 , CELL_SAVEDATA_DIRLIST_MAX ) )
{
case - 1 :
{
// ****** sysutil savedata parameter error : 108 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 108 " } ;
}
case - 2 :
{
// ****** sysutil savedata parameter error : 109 ******
return { CELL_SAVEDATA_ERROR_PARAM , " 109 " } ;
}
case 0 : break ;
2020-12-10 11:37:26 +01:00
default : fmt : : throw_exception ( " Unreachable " ) ;
2020-03-20 12:05:25 +01:00
}
const std : : string base_dir = fmt : : format ( " /dev_hdd0/home/%08u/savedata/ " , userId ) ;
if ( ! fs : : is_dir ( vfs : : get ( base_dir ) ) )
{
return CELL_SAVEDATA_ERROR_NOUSER ;
}
2020-03-20 06:01:05 +01:00
2020-03-20 12:05:25 +01:00
const std : : string save_path = vfs : : get ( base_dir + dirName . get_ptr ( ) + ' / ' ) ;
2017-07-24 22:53:20 +02:00
std : : string sfo = save_path + " PARAM.SFO " ;
2017-07-16 18:10:45 +02:00
if ( ! fs : : is_dir ( save_path ) & & ! fs : : is_file ( sfo ) )
{
cellSaveData . error ( " cellSaveDataGetListItem(): Savedata at %s does not exist " , dirName ) ;
return CELL_SAVEDATA_ERROR_NODATA ;
}
2022-11-27 08:59:57 +01:00
const psf : : registry psf = psf : : load_object ( sfo ) ;
2017-07-16 18:10:45 +02:00
if ( sysFileParam )
{
2021-12-13 00:13:54 +01:00
strcpy_trunc ( sysFileParam - > listParam , psf : : get_string ( psf , " SAVEDATA_LIST_PARAM " ) ) ;
strcpy_trunc ( sysFileParam - > title , psf : : get_string ( psf , " TITLE " ) ) ;
strcpy_trunc ( sysFileParam - > subTitle , psf : : get_string ( psf , " SUB_TITLE " ) ) ;
strcpy_trunc ( sysFileParam - > detail , psf : : get_string ( psf , " DETAIL " ) ) ;
2017-07-16 18:10:45 +02:00
}
2017-07-18 02:04:53 +02:00
if ( dir )
2017-07-16 18:10:45 +02:00
{
2017-07-18 02:04:53 +02:00
fs : : stat_t dir_info { } ;
2023-07-18 23:30:36 +02:00
if ( ! fs : : get_stat ( save_path , dir_info ) )
2017-07-18 02:04:53 +02:00
{
return CELL_SAVEDATA_ERROR_INTERNAL ;
}
2017-07-16 18:10:45 +02:00
2017-07-18 02:04:53 +02:00
// get file stats, namely directory
2020-03-04 15:08:40 +01:00
strcpy_trunc ( dir - > dirName , std : : string_view ( dirName . get_ptr ( ) ) ) ;
2017-07-18 02:04:53 +02:00
dir - > atime = dir_info . atime ;
dir - > ctime = dir_info . ctime ;
dir - > mtime = dir_info . mtime ;
}
2017-07-16 18:10:45 +02:00
2018-02-12 21:51:33 +01:00
if ( sizeKB )
{
u32 size_kbytes = 0 ;
for ( const auto & entry : fs : : dir ( save_path ) )
{
2022-02-14 20:03:40 +01:00
if ( entry . is_directory | | check_filename ( vfs : : unescape ( entry . name ) , false , false ) )
2020-03-20 12:15:58 +01:00
{
continue ;
}
2019-10-25 12:32:21 +02:00
size_kbytes + = : : narrow < u32 > ( ( entry . size + 1023 ) / 1024 ) ; // firmware rounds this value up
2018-02-12 21:51:33 +01:00
}
2022-02-14 20:03:40 +01:00
// Add a seemingly constant allocation disk space of PARAM.SFO + PARAM.PFD
* sizeKB = size_kbytes + 35 ;
2018-02-12 21:51:33 +01:00
}
2017-07-18 02:04:53 +02:00
if ( bind )
{
//TODO: Set bind in accordance to any problems
* bind = 0 ;
}
2017-07-16 18:10:45 +02:00
return CELL_OK ;
}
2015-04-15 16:27:37 +02:00
// Functions
2019-01-13 14:25:50 +01:00
error_code cellSaveDataListSave2 ( ppu_thread & ppu , u32 version , PSetList setList , PSetBuf setBuf , PFuncList funcList ,
2015-12-20 09:39:07 +01:00
PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . warning ( " cellSaveDataListSave2(version=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
version , setList , setBuf , funcList , funcStat , funcFile , container , userdata ) ;
2014-03-28 20:06:15 +01:00
2020-01-02 05:21:46 +01:00
return savedata_op ( ppu , SAVEDATA_OP_LIST_SAVE , version , vm : : null , CELL_SAVEDATA_ERRDIALOG_ALWAYS , setList , setBuf , funcList , vm : : null , funcStat , funcFile , container , 2 , userdata , 0 , vm : : null ) ;
2015-04-15 16:27:37 +02:00
}
2014-03-28 20:06:15 +01:00
2019-01-13 14:25:50 +01:00
error_code cellSaveDataListLoad2 ( ppu_thread & ppu , u32 version , PSetList setList , PSetBuf setBuf , PFuncList funcList ,
2015-12-20 09:39:07 +01:00
PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2015-04-15 16:27:37 +02:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . warning ( " cellSaveDataListLoad2(version=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-15 16:27:37 +02:00
version , setList , setBuf , funcList , funcStat , funcFile , container , userdata ) ;
2014-03-28 20:06:15 +01:00
2020-01-02 05:21:46 +01:00
return savedata_op ( ppu , SAVEDATA_OP_LIST_LOAD , version , vm : : null , CELL_SAVEDATA_ERRDIALOG_ALWAYS , setList , setBuf , funcList , vm : : null , funcStat , funcFile , container , 2 , userdata , 0 , vm : : null ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataListSave ( ppu_thread & ppu , u32 version , PSetList setList , PSetBuf setBuf , PFuncList funcList ,
2015-12-20 09:39:07 +01:00
PFuncStat funcStat , PFuncFile funcFile , u32 container )
2015-08-01 18:14:49 +02:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . warning ( " cellSaveDataListSave(version=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x) " ,
2015-12-20 09:39:07 +01:00
version , setList , setBuf , funcList , funcStat , funcFile , container ) ;
2020-01-02 05:21:46 +01:00
return savedata_op ( ppu , SAVEDATA_OP_LIST_SAVE , version , vm : : null , CELL_SAVEDATA_ERRDIALOG_ALWAYS , setList , setBuf , funcList , vm : : null , funcStat , funcFile , container , 2 , vm : : null , 0 , vm : : null ) ;
2015-08-01 18:14:49 +02:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataListLoad ( ppu_thread & ppu , u32 version , PSetList setList , PSetBuf setBuf , PFuncList funcList ,
2015-12-20 09:39:07 +01:00
PFuncStat funcStat , PFuncFile funcFile , u32 container )
2015-08-01 18:14:49 +02:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . warning ( " cellSaveDataListLoad(version=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x) " ,
2015-12-20 09:39:07 +01:00
version , setList , setBuf , funcList , funcStat , funcFile , container ) ;
2020-01-02 05:21:46 +01:00
return savedata_op ( ppu , SAVEDATA_OP_LIST_LOAD , version , vm : : null , CELL_SAVEDATA_ERRDIALOG_ALWAYS , setList , setBuf , funcList , vm : : null , funcStat , funcFile , container , 2 , vm : : null , 0 , vm : : null ) ;
2015-08-01 18:14:49 +02:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataFixedSave2 ( ppu_thread & ppu , u32 version , PSetList setList , PSetBuf setBuf , PFuncFixed funcFixed ,
2015-12-20 09:39:07 +01:00
PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . warning ( " cellSaveDataFixedSave2(version=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
version , setList , setBuf , funcFixed , funcStat , funcFile , container , userdata ) ;
2014-04-09 18:23:14 +02:00
2020-01-02 05:21:46 +01:00
return savedata_op ( ppu , SAVEDATA_OP_FIXED_SAVE , version , vm : : null , CELL_SAVEDATA_ERRDIALOG_ALWAYS , setList , setBuf , vm : : null , funcFixed , funcStat , funcFile , container , 2 , userdata , 0 , vm : : null ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataFixedLoad2 ( ppu_thread & ppu , u32 version , PSetList setList , PSetBuf setBuf , PFuncFixed funcFixed ,
2015-12-20 09:39:07 +01:00
PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . warning ( " cellSaveDataFixedLoad2(version=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
version , setList , setBuf , funcFixed , funcStat , funcFile , container , userdata ) ;
2014-04-09 18:23:14 +02:00
2020-01-02 05:21:46 +01:00
return savedata_op ( ppu , SAVEDATA_OP_FIXED_LOAD , version , vm : : null , CELL_SAVEDATA_ERRDIALOG_ALWAYS , setList , setBuf , vm : : null , funcFixed , funcStat , funcFile , container , 2 , userdata , 0 , vm : : null ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataFixedSave ( ppu_thread & ppu , u32 version , PSetList setList , PSetBuf setBuf , PFuncFixed funcFixed ,
2015-12-20 09:39:07 +01:00
PFuncStat funcStat , PFuncFile funcFile , u32 container )
2015-08-01 18:14:49 +02:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . warning ( " cellSaveDataFixedSave(version=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x) " ,
2015-12-20 09:39:07 +01:00
version , setList , setBuf , funcFixed , funcStat , funcFile , container ) ;
2020-01-02 05:21:46 +01:00
return savedata_op ( ppu , SAVEDATA_OP_FIXED_SAVE , version , vm : : null , CELL_SAVEDATA_ERRDIALOG_ALWAYS , setList , setBuf , vm : : null , funcFixed , funcStat , funcFile , container , 2 , vm : : null , 0 , vm : : null ) ;
2015-08-01 18:14:49 +02:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataFixedLoad ( ppu_thread & ppu , u32 version , PSetList setList , PSetBuf setBuf , PFuncFixed funcFixed ,
2015-12-20 09:39:07 +01:00
PFuncStat funcStat , PFuncFile funcFile , u32 container )
2015-08-01 18:14:49 +02:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . warning ( " cellSaveDataFixedLoad(version=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x) " ,
2015-12-20 09:39:07 +01:00
version , setList , setBuf , funcFixed , funcStat , funcFile , container ) ;
2020-01-02 05:21:46 +01:00
return savedata_op ( ppu , SAVEDATA_OP_FIXED_LOAD , version , vm : : null , CELL_SAVEDATA_ERRDIALOG_ALWAYS , setList , setBuf , vm : : null , funcFixed , funcStat , funcFile , container , 2 , vm : : null , 0 , vm : : null ) ;
2015-08-01 18:14:49 +02:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataAutoSave2 ( ppu_thread & ppu , u32 version , vm : : cptr < char > dirName , u32 errDialog , PSetBuf setBuf ,
2015-12-20 09:39:07 +01:00
PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2016-08-11 01:29:59 +02:00
cellSaveData . warning ( " cellSaveDataAutoSave2(version=%d, dirName=%s, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
version , dirName , errDialog , setBuf , funcStat , funcFile , container , userdata ) ;
2014-05-31 23:37:48 +02:00
2018-07-26 00:37:56 +02:00
return savedata_op ( ppu , SAVEDATA_OP_AUTO_SAVE , version , dirName , errDialog , vm : : null , setBuf , vm : : null , vm : : null , funcStat , funcFile , container , 2 , userdata , 0 , vm : : null ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataAutoLoad2 ( ppu_thread & ppu , u32 version , vm : : cptr < char > dirName , u32 errDialog , PSetBuf setBuf ,
2015-12-20 09:39:07 +01:00
PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2016-08-11 01:29:59 +02:00
cellSaveData . warning ( " cellSaveDataAutoLoad2(version=%d, dirName=%s, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
version , dirName , errDialog , setBuf , funcStat , funcFile , container , userdata ) ;
2014-05-31 23:37:48 +02:00
2018-07-26 00:37:56 +02:00
return savedata_op ( ppu , SAVEDATA_OP_AUTO_LOAD , version , dirName , errDialog , vm : : null , setBuf , vm : : null , vm : : null , funcStat , funcFile , container , 2 , userdata , 0 , vm : : null ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataAutoSave ( ppu_thread & ppu , u32 version , vm : : cptr < char > dirName , u32 errDialog , PSetBuf setBuf ,
2015-12-20 09:39:07 +01:00
PFuncStat funcStat , PFuncFile funcFile , u32 container )
2015-08-01 18:14:49 +02:00
{
2016-08-11 01:29:59 +02:00
cellSaveData . warning ( " cellSaveDataAutoSave(version=%d, dirName=%s, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x) " ,
2015-12-20 09:39:07 +01:00
version , dirName , errDialog , setBuf , funcStat , funcFile , container ) ;
2018-07-26 00:37:56 +02:00
return savedata_op ( ppu , SAVEDATA_OP_AUTO_SAVE , version , dirName , errDialog , vm : : null , setBuf , vm : : null , vm : : null , funcStat , funcFile , container , 2 , vm : : null , 0 , vm : : null ) ;
2015-08-01 18:14:49 +02:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataAutoLoad ( ppu_thread & ppu , u32 version , vm : : cptr < char > dirName , u32 errDialog , PSetBuf setBuf ,
2015-12-20 09:39:07 +01:00
PFuncStat funcStat , PFuncFile funcFile , u32 container )
2015-08-01 18:14:49 +02:00
{
2016-08-11 01:29:59 +02:00
cellSaveData . warning ( " cellSaveDataAutoLoad(version=%d, dirName=%s, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x) " ,
2015-12-20 09:39:07 +01:00
version , dirName , errDialog , setBuf , funcStat , funcFile , container ) ;
2018-07-26 00:37:56 +02:00
return savedata_op ( ppu , SAVEDATA_OP_AUTO_LOAD , version , dirName , errDialog , vm : : null , setBuf , vm : : null , vm : : null , funcStat , funcFile , container , 2 , vm : : null , 0 , vm : : null ) ;
2015-08-01 18:14:49 +02:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataListAutoSave ( ppu_thread & ppu , u32 version , u32 errDialog , PSetList setList , PSetBuf setBuf , PFuncFixed funcFixed , PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . warning ( " cellSaveDataListAutoSave(version=%d, errDialog=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
version , errDialog , setList , setBuf , funcFixed , funcStat , funcFile , container , userdata ) ;
2014-08-22 21:34:43 +02:00
2019-04-05 14:02:22 +02:00
return savedata_op ( ppu , SAVEDATA_OP_LIST_AUTO_SAVE , version , vm : : null , errDialog , setList , setBuf , vm : : null , funcFixed , funcStat , funcFile , container , 2 , userdata , 0 , vm : : null ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataListAutoLoad ( ppu_thread & ppu , u32 version , u32 errDialog , PSetList setList , PSetBuf setBuf , PFuncFixed funcFixed , PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . warning ( " cellSaveDataListAutoLoad(version=%d, errDialog=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
version , errDialog , setList , setBuf , funcFixed , funcStat , funcFile , container , userdata ) ;
2014-08-22 21:34:43 +02:00
2019-04-05 14:02:22 +02:00
return savedata_op ( ppu , SAVEDATA_OP_LIST_AUTO_LOAD , version , vm : : null , errDialog , setList , setBuf , vm : : null , funcFixed , funcStat , funcFile , container , 2 , userdata , 0 , vm : : null ) ;
2014-03-28 20:06:15 +01:00
}
2019-07-13 21:48:54 +02:00
error_code cellSaveDataDelete ( ppu_thread & ppu , u32 container )
2015-07-16 18:31:58 +02:00
{
2019-07-13 21:48:54 +02:00
cellSaveData . warning ( " cellSaveDataDelete(container=0x%x) " , container ) ;
2015-01-05 00:07:46 +01:00
2019-07-13 21:48:54 +02:00
return select_and_delete ( ppu ) ;
2014-03-28 20:06:15 +01:00
}
2019-07-13 21:48:54 +02:00
error_code cellSaveDataDelete2 ( ppu_thread & ppu , u32 container )
2015-08-01 18:14:49 +02:00
{
2019-07-13 21:48:54 +02:00
cellSaveData . warning ( " cellSaveDataDelete2(container=0x%x) " , container ) ;
2015-12-20 09:39:07 +01:00
2019-07-13 21:48:54 +02:00
return select_and_delete ( ppu ) ;
2015-08-01 18:14:49 +02:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataFixedDelete ( ppu_thread & ppu , PSetList setList , PSetBuf setBuf , PFuncFixed funcFixed , PFuncDone funcDone , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2017-11-15 18:59:10 +01:00
cellSaveData . warning ( " cellSaveDataFixedDelete(setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcDone=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
setList , setBuf , funcFixed , funcDone , container , userdata ) ;
2015-01-05 00:07:46 +01:00
2018-07-26 00:37:56 +02:00
return savedata_op ( ppu , SAVEDATA_OP_FIXED_DELETE , 0 , vm : : null , 1 , setList , setBuf , vm : : null , funcFixed , vm : : null , vm : : null , container , 2 , userdata , 0 , funcDone ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataUserListSave ( ppu_thread & ppu , u32 version , u32 userId , PSetList setList , PSetBuf setBuf , PFuncList funcList , PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . error ( " cellSaveDataUserListSave(version=%d, userId=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
version , userId , setList , setBuf , funcList , funcStat , funcFile , container , userdata ) ;
2015-01-05 00:07:46 +01:00
2015-07-28 04:08:23 +02:00
return savedata_op ( ppu , SAVEDATA_OP_LIST_SAVE , version , vm : : null , 0 , setList , setBuf , funcList , vm : : null , funcStat , funcFile , container , 6 , userdata , userId , vm : : null ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataUserListLoad ( ppu_thread & ppu , u32 version , u32 userId , PSetList setList , PSetBuf setBuf , PFuncList funcList , PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . error ( " cellSaveDataUserListLoad(version=%d, userId=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
version , userId , setList , setBuf , funcList , funcStat , funcFile , container , userdata ) ;
2015-01-05 00:07:46 +01:00
2015-07-28 04:08:23 +02:00
return savedata_op ( ppu , SAVEDATA_OP_LIST_LOAD , version , vm : : null , 0 , setList , setBuf , funcList , vm : : null , funcStat , funcFile , container , 6 , userdata , userId , vm : : null ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataUserFixedSave ( ppu_thread & ppu , u32 version , u32 userId , PSetList setList , PSetBuf setBuf , PFuncFixed funcFixed , PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . error ( " cellSaveDataUserFixedSave(version=%d, userId=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
version , userId , setList , setBuf , funcFixed , funcStat , funcFile , container , userdata ) ;
2015-01-05 00:07:46 +01:00
2015-07-28 04:08:23 +02:00
return savedata_op ( ppu , SAVEDATA_OP_FIXED_SAVE , version , vm : : null , 0 , setList , setBuf , vm : : null , funcFixed , funcStat , funcFile , container , 6 , userdata , userId , vm : : null ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataUserFixedLoad ( ppu_thread & ppu , u32 version , u32 userId , PSetList setList , PSetBuf setBuf , PFuncFixed funcFixed , PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . error ( " cellSaveDataUserFixedLoad(version=%d, userId=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
version , userId , setList , setBuf , funcFixed , funcStat , funcFile , container , userdata ) ;
2015-01-05 00:07:46 +01:00
2015-07-28 04:08:23 +02:00
return savedata_op ( ppu , SAVEDATA_OP_FIXED_LOAD , version , vm : : null , 0 , setList , setBuf , vm : : null , funcFixed , funcStat , funcFile , container , 6 , userdata , userId , vm : : null ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataUserAutoSave ( ppu_thread & ppu , u32 version , u32 userId , vm : : cptr < char > dirName , u32 errDialog , PSetBuf setBuf , PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2016-08-11 01:29:59 +02:00
cellSaveData . error ( " cellSaveDataUserAutoSave(version=%d, userId=%d, dirName=%s, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
version , userId , dirName , errDialog , setBuf , funcStat , funcFile , container , userdata ) ;
2015-01-05 00:07:46 +01:00
2015-07-28 04:08:23 +02:00
return savedata_op ( ppu , SAVEDATA_OP_AUTO_SAVE , version , dirName , errDialog , vm : : null , setBuf , vm : : null , vm : : null , funcStat , funcFile , container , 6 , userdata , userId , vm : : null ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataUserAutoLoad ( ppu_thread & ppu , u32 version , u32 userId , vm : : cptr < char > dirName , u32 errDialog , PSetBuf setBuf , PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2016-08-11 01:29:59 +02:00
cellSaveData . error ( " cellSaveDataUserAutoLoad(version=%d, userId=%d, dirName=%s, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
version , userId , dirName , errDialog , setBuf , funcStat , funcFile , container , userdata ) ;
2015-01-05 00:07:46 +01:00
2015-07-28 04:08:23 +02:00
return savedata_op ( ppu , SAVEDATA_OP_AUTO_LOAD , version , dirName , errDialog , vm : : null , setBuf , vm : : null , vm : : null , funcStat , funcFile , container , 6 , userdata , userId , vm : : null ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataUserListAutoSave ( ppu_thread & ppu , u32 version , u32 userId , u32 errDialog , PSetList setList , PSetBuf setBuf , PFuncFixed funcFixed , PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . error ( " cellSaveDataUserListAutoSave(version=%d, userId=%d, errDialog=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
version , userId , errDialog , setList , setBuf , funcFixed , funcStat , funcFile , container , userdata ) ;
2015-01-05 00:07:46 +01:00
2015-07-28 04:08:23 +02:00
return savedata_op ( ppu , SAVEDATA_OP_LIST_AUTO_SAVE , version , vm : : null , errDialog , setList , setBuf , vm : : null , funcFixed , funcStat , funcFile , container , 6 , userdata , userId , vm : : null ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataUserListAutoLoad ( ppu_thread & ppu , u32 version , u32 userId , u32 errDialog , PSetList setList , PSetBuf setBuf , PFuncFixed funcFixed , PFuncStat funcStat , PFuncFile funcFile , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2016-03-21 20:43:03 +01:00
cellSaveData . error ( " cellSaveDataUserListAutoLoad(version=%d, userId=%d, errDialog=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
version , userId , errDialog , setList , setBuf , funcFixed , funcStat , funcFile , container , userdata ) ;
2015-01-05 00:07:46 +01:00
2015-07-28 04:08:23 +02:00
return savedata_op ( ppu , SAVEDATA_OP_LIST_AUTO_LOAD , version , vm : : null , errDialog , setList , setBuf , vm : : null , funcFixed , funcStat , funcFile , container , 6 , userdata , userId , vm : : null ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataUserFixedDelete ( ppu_thread & ppu , u32 userId , PSetList setList , PSetBuf setBuf , PFuncFixed funcFixed , PFuncDone funcDone , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2017-11-15 18:59:10 +01:00
cellSaveData . error ( " cellSaveDataUserFixedDelete(userId=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcDone=*0x%x, container=0x%x, userdata=*0x%x) " ,
2015-04-14 16:54:03 +02:00
userId , setList , setBuf , funcFixed , funcDone , container , userdata ) ;
2015-01-05 00:07:46 +01:00
2017-11-15 18:59:10 +01:00
return savedata_op ( ppu , SAVEDATA_OP_FIXED_DELETE , 0 , vm : : null , 1 , setList , setBuf , vm : : null , funcFixed , vm : : null , vm : : null , container , 6 , userdata , userId , funcDone ) ;
2014-03-28 20:06:15 +01:00
}
2015-01-05 00:07:46 +01:00
void cellSaveDataEnableOverlay ( s32 enable )
{
2021-03-17 08:48:07 +01:00
cellSaveData . notice ( " cellSaveDataEnableOverlay(enable=%d) " , enable ) ;
auto & manager = g_fxo - > get < savedata_manager > ( ) ;
manager . enable_overlay = enable ! = 0 ;
2015-01-05 00:07:46 +01:00
}
2014-03-28 20:06:15 +01:00
2017-12-03 22:54:51 +01:00
// Functions (Extensions)
2019-01-13 14:25:50 +01:00
error_code cellSaveDataListDelete ( ppu_thread & ppu , PSetList setList , PSetBuf setBuf , PFuncList funcList , PFuncDone funcDone , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2017-11-15 18:59:10 +01:00
cellSaveData . warning ( " cellSaveDataListDelete(setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcDone=*0x%x, container=0x%x, userdata=*0x%x) " , setList , setBuf , funcList , funcDone , container , userdata ) ;
2015-01-05 00:07:46 +01:00
2018-07-26 00:37:56 +02:00
return savedata_op ( ppu , SAVEDATA_OP_LIST_DELETE , 0 , vm : : null , 0 , setList , setBuf , funcList , vm : : null , vm : : null , vm : : null , container , 0x40 , userdata , 0 , funcDone ) ;
2014-03-28 20:06:15 +01:00
}
2021-03-05 20:05:37 +01:00
// Temporarily
# ifndef _MSC_VER
# pragma GCC diagnostic ignored "-Wunused-parameter"
# endif
2019-01-13 14:25:50 +01:00
error_code cellSaveDataListImport ( ppu_thread & ppu , PSetList setList , u32 maxSizeKB , PFuncDone funcDone , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2023-10-15 01:27:15 +02:00
cellSaveData . todo ( " cellSaveDataListImport(setList=*0x%x, maxSizeKB=%d, funcDone=*0x%x, container=0x%x, userdata=*0x%x) " , setList , maxSizeKB , funcDone , container , userdata ) ;
if ( const auto ecode = savedata_check_args ( SAVEDATA_OP_LIST_IMPORT , CELL_SAVEDATA_VERSION_OLD , vm : : null , CELL_SAVEDATA_ERRDIALOG_NONE ,
setList , vm : : null , vm : : null , vm : : null , vm : : null , vm : : null , container , 0x40 , userdata , 0 , funcDone ) )
{
return { CELL_SAVEDATA_ERROR_PARAM , " (error %d) " , ecode } ;
}
// TODO
2015-01-05 00:07:46 +01:00
return CELL_OK ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataListExport ( ppu_thread & ppu , PSetList setList , u32 maxSizeKB , PFuncDone funcDone , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2023-10-15 01:27:15 +02:00
cellSaveData . todo ( " cellSaveDataListExport(setList=*0x%x, maxSizeKB=%d, funcDone=*0x%x, container=0x%x, userdata=*0x%x) " , setList , maxSizeKB , funcDone , container , userdata ) ;
if ( const auto ecode = savedata_check_args ( SAVEDATA_OP_LIST_EXPORT , CELL_SAVEDATA_VERSION_OLD , vm : : null , CELL_SAVEDATA_ERRDIALOG_NONE ,
setList , vm : : null , vm : : null , vm : : null , vm : : null , vm : : null , container , 0x40 , userdata , 0 , funcDone ) )
{
return { CELL_SAVEDATA_ERROR_PARAM , " (error %d) " , ecode } ;
}
// TODO
2015-01-05 00:07:46 +01:00
return CELL_OK ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataFixedImport ( ppu_thread & ppu , vm : : cptr < char > dirName , u32 maxSizeKB , PFuncDone funcDone , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2023-10-15 01:27:15 +02:00
cellSaveData . todo ( " cellSaveDataFixedImport(dirName=%s, maxSizeKB=%d, funcDone=*0x%x, container=0x%x, userdata=*0x%x) " , dirName , maxSizeKB , funcDone , container , userdata ) ;
if ( const auto ecode = savedata_check_args ( SAVEDATA_OP_FIXED_IMPORT , CELL_SAVEDATA_VERSION_OLD , dirName , CELL_SAVEDATA_ERRDIALOG_NONE ,
vm : : null , vm : : null , vm : : null , vm : : null , vm : : null , vm : : null , container , 0x44 , userdata , 0 , funcDone ) )
{
return { CELL_SAVEDATA_ERROR_PARAM , " (error %d) " , ecode } ;
}
// TODO
2015-01-05 00:07:46 +01:00
return CELL_OK ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataFixedExport ( ppu_thread & ppu , vm : : cptr < char > dirName , u32 maxSizeKB , PFuncDone funcDone , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2023-10-15 01:27:15 +02:00
cellSaveData . todo ( " cellSaveDataFixedExport(dirName=%s, maxSizeKB=%d, funcDone=*0x%x, container=0x%x, userdata=*0x%x) " , dirName , maxSizeKB , funcDone , container , userdata ) ;
if ( const auto ecode = savedata_check_args ( SAVEDATA_OP_FIXED_EXPORT , CELL_SAVEDATA_VERSION_OLD , dirName , CELL_SAVEDATA_ERRDIALOG_NONE ,
vm : : null , vm : : null , vm : : null , vm : : null , vm : : null , vm : : null , container , 0x44 , userdata , 0 , funcDone ) )
{
return { CELL_SAVEDATA_ERROR_PARAM , " (error %d) " , ecode } ;
}
// TODO
2015-01-05 00:07:46 +01:00
return CELL_OK ;
2014-03-28 20:06:15 +01:00
}
2022-09-03 06:29:07 +02:00
error_code cellSaveDataGetListItem ( ppu_thread & ppu , vm : : cptr < char > dirName , vm : : ptr < CellSaveDataDirStat > dir , vm : : ptr < CellSaveDataSystemFileParam > sysFileParam , vm : : ptr < u32 > bind , vm : : ptr < u32 > sizeKB )
2014-03-28 20:06:15 +01:00
{
2022-09-03 06:29:07 +02:00
ppu . state + = cpu_flag : : wait ;
2018-03-17 07:17:27 +01:00
cellSaveData . warning ( " cellSaveDataGetListItem(dirName=%s, dir=*0x%x, sysFileParam=*0x%x, bind=*0x%x, sizeKB=*0x%x) " , dirName , dir , sysFileParam , bind , sizeKB ) ;
2017-11-15 18:59:10 +01:00
return savedata_get_list_item ( dirName , dir , sysFileParam , bind , sizeKB , 0 ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataUserListDelete ( ppu_thread & ppu , u32 userId , PSetList setList , PSetBuf setBuf , PFuncList funcList , PFuncDone funcDone , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2017-11-15 18:59:10 +01:00
cellSaveData . error ( " cellSaveDataUserListDelete(userId=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcDone=*0x%x, container=0x%x, userdata=*0x%x) " , userId , setList , setBuf , funcList , funcDone , container , userdata ) ;
2015-01-05 00:07:46 +01:00
2017-11-15 18:59:10 +01:00
return savedata_op ( ppu , SAVEDATA_OP_LIST_DELETE , 0 , vm : : null , 0 , setList , setBuf , funcList , vm : : null , vm : : null , vm : : null , container , 0x40 , userdata , userId , funcDone ) ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataUserListImport ( ppu_thread & ppu , u32 userId , PSetList setList , u32 maxSizeKB , PFuncDone funcDone , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2023-10-15 01:27:15 +02:00
cellSaveData . todo ( " cellSaveDataUserListImport(userId=%d, setList=*0x%x, maxSizeKB=%d, funcDone=*0x%x, container=0x%x, userdata=*0x%x) " , userId , setList , maxSizeKB , funcDone , container , userdata ) ;
if ( const auto ecode = savedata_check_args ( SAVEDATA_OP_LIST_IMPORT , CELL_SAVEDATA_VERSION_OLD , vm : : null , CELL_SAVEDATA_ERRDIALOG_NONE ,
setList , vm : : null , vm : : null , vm : : null , vm : : null , vm : : null , container , 0x44 , userdata , userId , funcDone ) )
{
return { CELL_SAVEDATA_ERROR_PARAM , " (error %d) " , ecode } ;
}
// TODO
2015-01-05 00:07:46 +01:00
return CELL_OK ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataUserListExport ( ppu_thread & ppu , u32 userId , PSetList setList , u32 maxSizeKB , PFuncDone funcDone , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2023-10-15 01:27:15 +02:00
cellSaveData . todo ( " cellSaveDataUserListExport(userId=%d, setList=*0x%x, maxSizeKB=%d, funcDone=*0x%x, container=0x%x, userdata=*0x%x) " , userId , setList , maxSizeKB , funcDone , container , userdata ) ;
if ( const auto ecode = savedata_check_args ( SAVEDATA_OP_LIST_EXPORT , CELL_SAVEDATA_VERSION_OLD , vm : : null , CELL_SAVEDATA_ERRDIALOG_NONE ,
setList , vm : : null , vm : : null , vm : : null , vm : : null , vm : : null , container , 0x44 , userdata , userId , funcDone ) )
{
return { CELL_SAVEDATA_ERROR_PARAM , " (error %d) " , ecode } ;
}
// TODO
2015-01-05 00:07:46 +01:00
return CELL_OK ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataUserFixedImport ( ppu_thread & ppu , u32 userId , vm : : cptr < char > dirName , u32 maxSizeKB , PFuncDone funcDone , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2023-10-15 01:27:15 +02:00
cellSaveData . todo ( " cellSaveDataUserFixedImport(userId=%d, dirName=%s, maxSizeKB=%d, funcDone=*0x%x, container=0x%x, userdata=*0x%x) " , userId , dirName , maxSizeKB , funcDone , container , userdata ) ;
if ( const auto ecode = savedata_check_args ( SAVEDATA_OP_FIXED_IMPORT , CELL_SAVEDATA_VERSION_OLD , dirName , CELL_SAVEDATA_ERRDIALOG_NONE ,
vm : : null , vm : : null , vm : : null , vm : : null , vm : : null , vm : : null , container , 0x44 , userdata , userId , funcDone ) )
{
return { CELL_SAVEDATA_ERROR_PARAM , " (error %d) " , ecode } ;
}
// TODO
2015-01-05 00:07:46 +01:00
return CELL_OK ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataUserFixedExport ( ppu_thread & ppu , u32 userId , vm : : cptr < char > dirName , u32 maxSizeKB , PFuncDone funcDone , u32 container , vm : : ptr < void > userdata )
2014-03-28 20:06:15 +01:00
{
2023-10-15 01:27:15 +02:00
cellSaveData . todo ( " cellSaveDataUserFixedExport(userId=%d, dirName=%s, maxSizeKB=%d, funcDone=*0x%x, container=0x%x, userdata=*0x%x) " , userId , dirName , maxSizeKB , funcDone , container , userdata ) ;
if ( const auto ecode = savedata_check_args ( SAVEDATA_OP_FIXED_EXPORT , CELL_SAVEDATA_VERSION_OLD , dirName , CELL_SAVEDATA_ERRDIALOG_NONE ,
vm : : null , vm : : null , vm : : null , vm : : null , vm : : null , vm : : null , container , 0x44 , userdata , userId , funcDone ) )
{
return { CELL_SAVEDATA_ERROR_PARAM , " (error %d) " , ecode } ;
}
// TODO
2015-01-05 00:07:46 +01:00
return CELL_OK ;
2014-03-28 20:06:15 +01:00
}
2019-01-13 14:25:50 +01:00
error_code cellSaveDataUserGetListItem ( u32 userId , vm : : cptr < char > dirName , vm : : ptr < CellSaveDataDirStat > dir , vm : : ptr < CellSaveDataSystemFileParam > sysFileParam , vm : : ptr < u32 > bind , vm : : ptr < u32 > sizeKB )
2014-03-28 20:06:15 +01:00
{
2018-06-14 00:11:51 +02:00
cellSaveData . warning ( " cellSaveDataUserGetListItem(dirName=%s, dir=*0x%x, sysFileParam=*0x%x, bind=*0x%x, sizeKB=*0x%x, userID=*0x%x) " , dirName , dir , sysFileParam , bind , sizeKB , userId ) ;
2015-01-05 00:07:46 +01:00
2017-11-15 18:59:10 +01:00
return savedata_get_list_item ( dirName , dir , sysFileParam , bind , sizeKB , userId ) ;
2015-01-05 00:07:46 +01:00
}
void cellSysutil_SaveData_init ( )
{
2017-09-16 19:39:41 +02:00
REG_VAR ( cellSysutil , g_savedata_context ) . flag ( MFF_HIDDEN ) ;
2017-04-03 15:01:50 +02:00
2015-01-05 00:07:46 +01:00
// libsysutil functions:
REG_FUNC ( cellSysutil , cellSaveDataEnableOverlay ) ;
REG_FUNC ( cellSysutil , cellSaveDataDelete2 ) ;
2015-08-01 18:14:49 +02:00
REG_FUNC ( cellSysutil , cellSaveDataDelete ) ;
2015-01-05 00:07:46 +01:00
REG_FUNC ( cellSysutil , cellSaveDataUserFixedDelete ) ;
REG_FUNC ( cellSysutil , cellSaveDataFixedDelete ) ;
REG_FUNC ( cellSysutil , cellSaveDataUserFixedLoad ) ;
REG_FUNC ( cellSysutil , cellSaveDataUserFixedSave ) ;
REG_FUNC ( cellSysutil , cellSaveDataFixedLoad2 ) ;
REG_FUNC ( cellSysutil , cellSaveDataFixedSave2 ) ;
2015-08-01 18:14:49 +02:00
REG_FUNC ( cellSysutil , cellSaveDataFixedLoad ) ;
REG_FUNC ( cellSysutil , cellSaveDataFixedSave ) ;
2015-01-05 00:07:46 +01:00
REG_FUNC ( cellSysutil , cellSaveDataUserListLoad ) ;
REG_FUNC ( cellSysutil , cellSaveDataUserListSave ) ;
REG_FUNC ( cellSysutil , cellSaveDataListLoad2 ) ;
REG_FUNC ( cellSysutil , cellSaveDataListSave2 ) ;
2015-08-01 18:14:49 +02:00
REG_FUNC ( cellSysutil , cellSaveDataListLoad ) ;
REG_FUNC ( cellSysutil , cellSaveDataListSave ) ;
2015-01-05 00:07:46 +01:00
REG_FUNC ( cellSysutil , cellSaveDataUserListAutoLoad ) ;
REG_FUNC ( cellSysutil , cellSaveDataUserListAutoSave ) ;
REG_FUNC ( cellSysutil , cellSaveDataListAutoLoad ) ;
REG_FUNC ( cellSysutil , cellSaveDataListAutoSave ) ;
REG_FUNC ( cellSysutil , cellSaveDataUserAutoLoad ) ;
REG_FUNC ( cellSysutil , cellSaveDataUserAutoSave ) ;
REG_FUNC ( cellSysutil , cellSaveDataAutoLoad2 ) ;
REG_FUNC ( cellSysutil , cellSaveDataAutoSave2 ) ;
2015-08-01 18:14:49 +02:00
REG_FUNC ( cellSysutil , cellSaveDataAutoLoad ) ;
REG_FUNC ( cellSysutil , cellSaveDataAutoSave ) ;
2015-07-28 04:08:23 +02:00
}
2015-01-05 00:07:46 +01:00
2016-03-21 20:43:03 +01:00
DECLARE ( ppu_module_manager : : cellSaveData ) ( " cellSaveData " , [ ] ( )
2015-07-28 04:08:23 +02:00
{
2015-01-05 00:07:46 +01:00
// libsysutil_savedata functions:
2015-07-28 04:08:23 +02:00
REG_FUNC ( cellSaveData , cellSaveDataUserGetListItem ) ;
REG_FUNC ( cellSaveData , cellSaveDataGetListItem ) ;
REG_FUNC ( cellSaveData , cellSaveDataUserListDelete ) ;
REG_FUNC ( cellSaveData , cellSaveDataListDelete ) ;
REG_FUNC ( cellSaveData , cellSaveDataUserFixedExport ) ;
REG_FUNC ( cellSaveData , cellSaveDataUserFixedImport ) ;
REG_FUNC ( cellSaveData , cellSaveDataUserListExport ) ;
REG_FUNC ( cellSaveData , cellSaveDataUserListImport ) ;
REG_FUNC ( cellSaveData , cellSaveDataFixedExport ) ;
REG_FUNC ( cellSaveData , cellSaveDataFixedImport ) ;
REG_FUNC ( cellSaveData , cellSaveDataListExport ) ;
REG_FUNC ( cellSaveData , cellSaveDataListImport ) ;
} ) ;
2016-03-21 20:43:03 +01:00
DECLARE ( ppu_module_manager : : cellMinisSaveData ) ( " cellMinisSaveData " , [ ] ( )
2015-07-28 04:08:23 +02:00
{
2015-01-05 00:07:46 +01:00
// libsysutil_savedata_psp functions:
2015-07-28 04:08:23 +02:00
//REG_FUNC(cellMinisSaveData, cellMinisSaveDataDelete); // 0x6eb168b3
//REG_FUNC(cellMinisSaveData, cellMinisSaveDataListDelete); // 0xe63eb964
//REG_FUNC(cellMinisSaveData, cellMinisSaveDataFixedLoad); // 0x66515c18
//REG_FUNC(cellMinisSaveData, cellMinisSaveDataFixedSave); // 0xf3f974b8
//REG_FUNC(cellMinisSaveData, cellMinisSaveDataListLoad); // 0xba161d45
//REG_FUNC(cellMinisSaveData, cellMinisSaveDataListSave); // 0xa342a73f
//REG_FUNC(cellMinisSaveData, cellMinisSaveDataListAutoLoad); // 0x22f2a553
//REG_FUNC(cellMinisSaveData, cellMinisSaveDataListAutoSave); // 0xa931356e
//REG_FUNC(cellMinisSaveData, cellMinisSaveDataAutoLoad); // 0xfc3045d9
} ) ;