2020-12-05 13:08:24 +01:00
# include "stdafx.h"
2020-09-03 20:56:02 +02:00
# include "Emu/localized_string.h"
2014-06-02 19:27:24 +02:00
# include "Emu/System.h"
2021-04-21 22:12:21 +02:00
# include "Emu/system_utils.hpp"
2020-02-15 23:36:20 +01:00
# include "Emu/VFS.h"
2015-08-06 17:55:19 +02:00
# include "Emu/IdManager.h"
2020-10-30 21:26:22 +01:00
# include "Emu/Cell/ErrorCodes.h"
2016-03-21 20:43:03 +01:00
# include "Emu/Cell/PPUModule.h"
2020-09-11 13:06:46 +02:00
# include "Emu/Cell/lv2/sys_fs.h"
2020-09-28 21:18:23 +02:00
# include "Emu/Cell/lv2/sys_sync.h"
2013-09-28 04:36:57 +02:00
2015-04-13 16:05:44 +02:00
# include "cellSysutil.h"
2015-12-19 12:40:52 +01:00
# include "cellMsgDialog.h"
2014-07-01 00:53:29 +02:00
# include "cellGame.h"
2016-04-25 12:49:12 +02:00
# include "Loader/PSF.h"
2016-04-27 00:27:24 +02:00
# include "Utilities/StrUtil.h"
2019-09-26 15:10:35 +02:00
# include "util/init_mutex.hpp"
2021-03-13 16:02:37 +01:00
# include "util/asm.hpp"
2016-03-21 20:43:03 +01:00
2021-05-30 16:10:46 +02:00
# include <span>
2018-08-25 14:39:00 +02:00
LOG_CHANNEL ( cellGame ) ;
2013-09-28 04:36:57 +02:00
2016-08-16 17:46:24 +02:00
template < >
void fmt_class_string < CellGameError > : : format ( std : : string & out , u64 arg )
{
format_enum ( out , arg , [ ] ( auto error )
{
switch ( error )
{
STR_CASE ( CELL_GAME_ERROR_NOTFOUND ) ;
STR_CASE ( CELL_GAME_ERROR_BROKEN ) ;
STR_CASE ( CELL_GAME_ERROR_INTERNAL ) ;
STR_CASE ( CELL_GAME_ERROR_PARAM ) ;
STR_CASE ( CELL_GAME_ERROR_NOAPP ) ;
STR_CASE ( CELL_GAME_ERROR_ACCESS_ERROR ) ;
STR_CASE ( CELL_GAME_ERROR_NOSPACE ) ;
STR_CASE ( CELL_GAME_ERROR_NOTSUPPORTED ) ;
STR_CASE ( CELL_GAME_ERROR_FAILURE ) ;
STR_CASE ( CELL_GAME_ERROR_BUSY ) ;
STR_CASE ( CELL_GAME_ERROR_IN_SHUTDOWN ) ;
STR_CASE ( CELL_GAME_ERROR_INVALID_ID ) ;
STR_CASE ( CELL_GAME_ERROR_EXIST ) ;
STR_CASE ( CELL_GAME_ERROR_NOTPATCH ) ;
STR_CASE ( CELL_GAME_ERROR_INVALID_THEME_FILE ) ;
STR_CASE ( CELL_GAME_ERROR_BOOTPATH ) ;
}
return unknown ;
} ) ;
}
template < >
void fmt_class_string < CellGameDataError > : : format ( std : : string & out , u64 arg )
{
format_enum ( out , arg , [ ] ( auto error )
{
switch ( error )
{
STR_CASE ( CELL_GAMEDATA_ERROR_CBRESULT ) ;
STR_CASE ( CELL_GAMEDATA_ERROR_ACCESS_ERROR ) ;
STR_CASE ( CELL_GAMEDATA_ERROR_INTERNAL ) ;
STR_CASE ( CELL_GAMEDATA_ERROR_PARAM ) ;
STR_CASE ( CELL_GAMEDATA_ERROR_NOSPACE ) ;
STR_CASE ( CELL_GAMEDATA_ERROR_BROKEN ) ;
STR_CASE ( CELL_GAMEDATA_ERROR_FAILURE ) ;
}
return unknown ;
} ) ;
}
2018-03-03 00:11:25 +01:00
template < >
void fmt_class_string < CellDiscGameError > : : format ( std : : string & out , u64 arg )
{
format_enum ( out , arg , [ ] ( auto error )
{
switch ( error )
{
STR_CASE ( CELL_DISCGAME_ERROR_INTERNAL ) ;
STR_CASE ( CELL_DISCGAME_ERROR_NOT_DISCBOOT ) ;
STR_CASE ( CELL_DISCGAME_ERROR_PARAM ) ;
}
return unknown ;
} ) ;
}
2019-01-20 22:57:13 +01:00
template < >
void fmt_class_string < CellHddGameError > : : format ( std : : string & out , u64 arg )
{
format_enum ( out , arg , [ ] ( auto error )
{
switch ( error )
{
STR_CASE ( CELL_HDDGAME_ERROR_CBRESULT ) ;
STR_CASE ( CELL_HDDGAME_ERROR_ACCESS_ERROR ) ;
STR_CASE ( CELL_HDDGAME_ERROR_INTERNAL ) ;
STR_CASE ( CELL_HDDGAME_ERROR_PARAM ) ;
STR_CASE ( CELL_HDDGAME_ERROR_NOSPACE ) ;
STR_CASE ( CELL_HDDGAME_ERROR_BROKEN ) ;
STR_CASE ( CELL_HDDGAME_ERROR_FAILURE ) ;
}
return unknown ;
} ) ;
}
2016-08-17 20:59:42 +02:00
// If dir is empty:
// contentInfo = "/dev_bdvd/PS3_GAME"
// usrdir = "/dev_bdvd/PS3_GAME/USRDIR"
// Temporary content directory (dir is not empty):
2018-06-06 20:24:55 +02:00
// contentInfo = "/dev_hdd0/game/_GDATA_" + time_since_epoch
// usrdir = "/dev_hdd0/game/_GDATA_" + time_since_epoch + "/USRDIR"
2016-08-17 20:59:42 +02:00
// Normal content directory (dir is not empty):
2015-08-06 17:55:19 +02:00
// contentInfo = "/dev_hdd0/game/" + dir
// usrdir = "/dev_hdd0/game/" + dir + "/USRDIR"
2016-04-25 12:49:12 +02:00
struct content_permission final
2015-08-06 17:55:19 +02:00
{
2016-04-25 12:49:12 +02:00
// Content directory name or path
2019-09-26 15:10:35 +02:00
std : : string dir ;
2015-08-06 17:55:19 +02:00
2016-04-25 12:49:12 +02:00
// SFO file
psf : : registry sfo ;
2017-07-22 15:16:49 +02:00
// Temporary directory path
std : : string temp ;
2015-08-06 17:55:19 +02:00
2019-09-26 15:10:35 +02:00
stx : : init_mutex init ;
2018-03-12 13:33:18 +01:00
2019-09-26 15:10:35 +02:00
atomic_t < u32 > can_create = 0 ;
2020-08-29 20:20:37 +02:00
atomic_t < bool > exists = false ;
2020-01-23 18:09:37 +01:00
atomic_t < bool > restrict_sfo_params = true ;
2019-09-26 15:10:35 +02:00
content_permission ( ) = default ;
content_permission ( const content_permission & ) = delete ;
content_permission & operator = ( const content_permission & ) = delete ;
2015-08-06 17:55:19 +02:00
2020-08-29 20:20:37 +02:00
void reset ( )
{
dir . clear ( ) ;
sfo . clear ( ) ;
temp . clear ( ) ;
can_create = 0 ;
exists = false ;
restrict_sfo_params = true ;
}
2016-04-25 12:49:12 +02:00
~ content_permission ( )
2015-08-06 17:55:19 +02:00
{
2020-02-03 08:31:31 +01:00
bool success = false ;
fs : : g_tls_error = fs : : error : : ok ;
2020-03-09 17:18:39 +01:00
if ( temp . size ( ) < = 1 | | fs : : remove_all ( temp ) )
2016-08-18 12:27:20 +02:00
{
2020-03-09 17:18:39 +01:00
success = true ;
2020-02-03 08:31:31 +01:00
}
if ( ! success )
{
cellGame . fatal ( " Failed to clean directory '%s' (%s) " , temp , fs : : g_tls_error ) ;
2015-08-06 17:55:19 +02:00
}
}
} ;
2014-06-28 03:19:44 +02:00
2019-01-20 22:57:13 +01:00
error_code cellHddGameCheck ( ppu_thread & ppu , u32 version , vm : : cptr < char > dirName , u32 errDialog , vm : : ptr < CellHddGameStatCallback > funcStat , u32 container )
2013-11-29 05:56:26 +01:00
{
2021-03-17 15:19:35 +01:00
cellGame . warning ( " cellHddGameCheck(version=%d, dirName=%s, errDialog=%d, funcStat=*0x%x, container=%d) " , version , dirName , errDialog , funcStat , container ) ;
2015-04-13 16:05:44 +02:00
2019-11-08 17:40:46 +01:00
if ( ! dirName | | ! funcStat | | sysutil_check_name_string ( dirName . get_ptr ( ) , 1 , CELL_GAME_DIRNAME_SIZE ) ! = 0 )
2015-04-16 01:17:42 +02:00
{
2019-01-20 22:57:13 +01:00
return CELL_HDDGAME_ERROR_PARAM ;
2015-04-16 01:17:42 +02:00
}
2015-04-13 16:05:44 +02:00
2020-09-28 21:18:23 +02:00
std : : string game_dir = dirName . get_ptr ( ) ;
2019-11-08 17:40:46 +01:00
// TODO: Find error code
2020-12-09 08:47:45 +01:00
ensure ( game_dir . size ( ) = = 9 ) ;
2020-09-28 21:18:23 +02:00
const std : : string dir = " /dev_hdd0/game/ " + game_dir ;
2021-03-30 23:44:58 +02:00
auto [ sfo , psf_error ] = psf : : load ( vfs : : get ( dir + " /PARAM.SFO " ) ) ;
2020-09-28 21:18:23 +02:00
2021-03-30 23:44:58 +02:00
const u32 new_data = psf_error = = psf : : error : : stream ? CELL_GAMEDATA_ISNEWDATA_YES : CELL_GAMEDATA_ISNEWDATA_NO ;
2020-09-28 21:18:23 +02:00
if ( ! new_data )
{
const auto cat = psf : : get_string ( sfo , " CATEGORY " , " " ) ;
if ( cat ! = " HG " )
{
return CELL_GAMEDATA_ERROR_BROKEN ;
}
}
const std : : string usrdir = dir + " /USRDIR " ;
2019-11-08 17:40:46 +01:00
2015-09-26 22:46:04 +02:00
vm : : var < CellHddGameCBResult > result ;
vm : : var < CellHddGameStatGet > get ;
vm : : var < CellHddGameStatSet > set ;
2015-04-13 16:05:44 +02:00
2018-07-10 20:07:53 +02:00
// 40 GB - 1 kilobyte. The reasoning is that many games take this number and multiply it by 1024, to get the amount of bytes. With 40GB exactly,
// this will result in an overflow, and the size would be 0, preventing the game from running. By reducing 1 kilobyte, we make sure that even
// after said overflow, the number would still be high enough to contain the game's data.
get - > hddFreeSizeKB = 40 * 1024 * 1024 - 1 ;
2015-04-13 16:05:44 +02:00
get - > isNewData = CELL_HDDGAME_ISNEWDATA_EXIST ;
get - > sysSizeKB = 0 ; // TODO
get - > atime = 0 ; // TODO
get - > ctime = 0 ; // TODO
get - > mtime = 0 ; // TODO
get - > sizeKB = CELL_HDDGAME_SIZEKB_NOTCALC ;
2020-09-28 21:18:23 +02:00
strcpy_trunc ( get - > contentInfoPath , dir ) ;
strcpy_trunc ( get - > hddGamePath , usrdir ) ;
2015-04-13 16:05:44 +02:00
2018-05-11 03:38:10 +02:00
vm : : var < CellHddGameSystemFileParam > setParam ;
set - > setParam = setParam ;
2020-09-28 21:18:23 +02:00
const std : : string & local_dir = vfs : : get ( dir ) ;
2016-03-21 20:43:03 +01:00
if ( ! fs : : is_dir ( local_dir ) )
2015-04-13 16:05:44 +02:00
{
get - > isNewData = CELL_HDDGAME_ISNEWDATA_NODIR ;
2016-03-21 20:43:03 +01:00
get - > getParam = { } ;
2015-04-13 16:05:44 +02:00
}
else
{
// TODO: Is cellHddGameCheck really responsible for writing the information in get->getParam ? (If not, delete this else)
2016-03-21 20:43:03 +01:00
const auto & psf = psf : : load_object ( fs : : file ( local_dir + " /PARAM.SFO " ) ) ;
2015-04-13 16:05:44 +02:00
2017-12-16 01:03:49 +01:00
// Some following fields may be zero in old FW 1.00 version PARAM.SFO
2016-06-20 00:43:13 +02:00
if ( psf . count ( " PARENTAL_LEVEL " ) ! = 0 ) get - > getParam . parentalLevel = psf . at ( " PARENTAL_LEVEL " ) . as_integer ( ) ;
if ( psf . count ( " ATTRIBUTE " ) ! = 0 ) get - > getParam . attribute = psf . at ( " ATTRIBUTE " ) . as_integer ( ) ;
if ( psf . count ( " RESOLUTION " ) ! = 0 ) get - > getParam . resolution = psf . at ( " RESOLUTION " ) . as_integer ( ) ;
if ( psf . count ( " SOUND_FORMAT " ) ! = 0 ) get - > getParam . soundFormat = psf . at ( " SOUND_FORMAT " ) . as_integer ( ) ;
if ( psf . count ( " TITLE " ) ! = 0 ) strcpy_trunc ( get - > getParam . title , psf . at ( " TITLE " ) . as_string ( ) ) ;
if ( psf . count ( " APP_VER " ) ! = 0 ) strcpy_trunc ( get - > getParam . dataVersion , psf . at ( " APP_VER " ) . as_string ( ) ) ;
if ( psf . count ( " TITLE_ID " ) ! = 0 ) strcpy_trunc ( get - > getParam . titleId , psf . at ( " TITLE_ID " ) . as_string ( ) ) ;
2015-04-13 16:05:44 +02:00
2015-04-16 01:17:42 +02:00
for ( u32 i = 0 ; i < CELL_HDDGAME_SYSP_LANGUAGE_NUM ; i + + )
{
2016-01-26 19:13:36 +01:00
strcpy_trunc ( get - > getParam . titleLang [ i ] , psf : : get_string ( psf , fmt : : format ( " TITLE_%02d " , i ) ) ) ;
2015-04-13 16:05:44 +02:00
}
}
// TODO ?
2015-09-10 15:09:31 +02:00
funcStat ( ppu , result , get , set ) ;
2015-04-13 16:05:44 +02:00
2020-09-28 21:18:23 +02:00
std : : string error_msg ;
switch ( result - > result )
{
case CELL_HDDGAME_CBRESULT_OK :
2015-09-10 15:09:31 +02:00
{
2020-09-28 21:18:23 +02:00
// Game confirmed that it wants to create directory
const auto setParam = set - > setParam ;
if ( new_data )
{
if ( ! setParam )
{
return CELL_GAMEDATA_ERROR_PARAM ;
}
if ( ! fs : : create_path ( vfs : : get ( usrdir ) ) )
{
return { CELL_GAME_ERROR_ACCESS_ERROR , usrdir } ;
}
}
2020-10-28 06:57:26 +01:00
// Nuked until correctly reversed engineered
// if (setParam)
// {
// if (new_data)
// {
// psf::assign(sfo, "CATEGORY", psf::string(3, "HG"));
// }
// psf::assign(sfo, "TITLE_ID", psf::string(CELL_GAME_SYSP_TITLEID_SIZE, setParam->titleId));
// psf::assign(sfo, "TITLE", psf::string(CELL_GAME_SYSP_TITLE_SIZE, setParam->title));
// psf::assign(sfo, "VERSION", psf::string(CELL_GAME_SYSP_VERSION_SIZE, setParam->dataVersion));
// psf::assign(sfo, "PARENTAL_LEVEL", +setParam->parentalLevel);
// psf::assign(sfo, "RESOLUTION", +setParam->resolution);
// psf::assign(sfo, "SOUND_FORMAT", +setParam->soundFormat);
// for (u32 i = 0; i < CELL_HDDGAME_SYSP_LANGUAGE_NUM; i++)
// {
// if (!setParam->titleLang[i][0])
// {
// continue;
// }
// psf::assign(sfo, fmt::format("TITLE_%02d", i), psf::string(CELL_GAME_SYSP_TITLE_SIZE, setParam->titleLang[i]));
// }
// psf::save_object(fs::file(vfs::get(dir + "/PARAM.SFO"), fs::rewrite), sfo);
// }
2020-09-28 21:18:23 +02:00
return CELL_OK ;
2015-09-10 15:09:31 +02:00
}
2020-09-28 21:18:23 +02:00
case CELL_HDDGAME_CBRESULT_OK_CANCEL :
cellGame . warning ( " cellHddGameCheck(): callback returned CELL_HDDGAME_CBRESULT_OK_CANCEL " ) ;
return CELL_OK ;
2015-04-13 16:05:44 +02:00
2020-09-28 21:18:23 +02:00
case CELL_HDDGAME_CBRESULT_ERR_NOSPACE :
cellGame . error ( " cellHddGameCheck(): callback returned CELL_HDDGAME_CBRESULT_ERR_NOSPACE. Space Needed: %d KB " , result - > errNeedSizeKB ) ;
error_msg = get_localized_string ( localized_string_id : : CELL_HDD_GAME_CHECK_NOSPACE , fmt : : format ( " %d " , result - > errNeedSizeKB ) . c_str ( ) ) ;
break ;
2015-04-13 16:05:44 +02:00
2020-09-28 21:18:23 +02:00
case CELL_HDDGAME_CBRESULT_ERR_BROKEN :
cellGame . error ( " cellHddGameCheck(): callback returned CELL_HDDGAME_CBRESULT_ERR_BROKEN " ) ;
error_msg = get_localized_string ( localized_string_id : : CELL_HDD_GAME_CHECK_BROKEN , game_dir . c_str ( ) ) ;
break ;
case CELL_HDDGAME_CBRESULT_ERR_NODATA :
cellGame . error ( " cellHddGameCheck(): callback returned CELL_HDDGAME_CBRESULT_ERR_NODATA " ) ;
error_msg = get_localized_string ( localized_string_id : : CELL_HDD_GAME_CHECK_NODATA , game_dir . c_str ( ) ) ;
break ;
case CELL_HDDGAME_CBRESULT_ERR_INVALID :
cellGame . error ( " cellHddGameCheck(): callback returned CELL_HDDGAME_CBRESULT_ERR_INVALID. Error message: %s " , result - > invalidMsg ) ;
2020-11-17 06:38:05 +01:00
error_msg = get_localized_string ( localized_string_id : : CELL_HDD_GAME_CHECK_INVALID , fmt : : format ( " %s " , result - > invalidMsg ) . c_str ( ) ) ;
2020-09-28 21:18:23 +02:00
break ;
default :
cellGame . error ( " cellHddGameCheck(): callback returned unknown error (code=0x%x). Error message: %s " , result - > invalidMsg ) ;
2020-11-17 06:38:05 +01:00
error_msg = get_localized_string ( localized_string_id : : CELL_HDD_GAME_CHECK_INVALID , fmt : : format ( " %s " , result - > invalidMsg ) . c_str ( ) ) ;
2020-09-28 21:18:23 +02:00
break ;
}
if ( errDialog = = CELL_GAMEDATA_ERRDIALOG_ALWAYS ) // Maybe != CELL_GAMEDATA_ERRDIALOG_NONE
{
// Yield before a blocking dialog is being spawned
lv2_obj : : sleep ( ppu ) ;
// Get user confirmation by opening a blocking dialog
error_code res = open_msg_dialog ( true , CELL_MSGDIALOG_TYPE_SE_TYPE_ERROR | CELL_MSGDIALOG_TYPE_BUTTON_TYPE_OK | CELL_MSGDIALOG_TYPE_DISABLE_CANCEL_ON , vm : : make_str ( error_msg ) ) ;
// Reschedule after a blocking dialog returns
if ( ppu . check_state ( ) )
{
return 0 ;
}
if ( res ! = CELL_OK )
{
return CELL_GAMEDATA_ERROR_INTERNAL ;
}
}
return CELL_HDDGAME_ERROR_CBRESULT ;
2015-04-13 16:05:44 +02:00
}
2020-09-12 10:08:34 +02:00
error_code cellHddGameCheck2 ( ppu_thread & ppu , u32 version , vm : : cptr < char > dirName , u32 errDialog , vm : : ptr < CellHddGameStatCallback > funcStat , u32 container )
2015-08-01 18:14:49 +02:00
{
2020-09-12 10:08:34 +02:00
cellGame . trace ( " cellHddGameCheck2() " ) ;
// Identical function
return cellHddGameCheck ( ppu , version , dirName , errDialog , funcStat , container ) ;
2015-08-01 18:14:49 +02:00
}
2019-01-20 22:57:13 +01:00
error_code cellHddGameGetSizeKB ( vm : : ptr < u32 > size )
2015-08-01 18:14:49 +02:00
{
2017-03-22 11:09:10 +01:00
cellGame . warning ( " cellHddGameGetSizeKB(size=*0x%x) " , size ) ;
2018-03-11 00:35:41 +01:00
const std : : string local_dir = vfs : : get ( Emu . GetDir ( ) ) ;
2017-07-22 15:16:49 +02:00
2020-02-22 16:04:35 +01:00
const auto dirsz = fs : : get_dir_size ( local_dir , 1024 ) ;
if ( dirsz = = umax )
2017-03-22 11:09:10 +01:00
{
2020-02-22 16:04:35 +01:00
const auto error = fs : : g_tls_error ;
if ( fs : : exists ( local_dir ) )
{
cellGame . error ( " cellHddGameGetSizeKB(): Unknown failure on calculating directory '%s' size (%s) " , local_dir , error ) ;
}
2019-01-20 22:57:13 +01:00
return CELL_HDDGAME_ERROR_FAILURE ;
2017-03-22 11:09:10 +01:00
}
2020-02-22 16:04:35 +01:00
* size = : : narrow < u32 > ( dirsz / 1024 ) ;
2017-02-05 14:07:26 +01:00
return CELL_OK ;
2015-08-01 18:14:49 +02:00
}
2019-01-20 22:57:13 +01:00
error_code cellHddGameSetSystemVer ( vm : : cptr < char > systemVersion )
2015-08-01 18:14:49 +02:00
{
2018-07-25 23:32:21 +02:00
cellGame . todo ( " cellHddGameSetSystemVer(systemVersion=%s) " , systemVersion ) ;
if ( ! systemVersion )
{
return CELL_HDDGAME_ERROR_PARAM ;
}
2018-01-15 00:31:26 +01:00
return CELL_OK ;
2015-08-01 18:14:49 +02:00
}
2019-01-20 22:57:13 +01:00
error_code cellHddGameExitBroken ( )
2015-08-01 18:14:49 +02:00
{
2018-07-25 23:32:21 +02:00
cellGame . warning ( " cellHddGameExitBroken() " ) ;
2020-09-03 20:56:02 +02:00
return open_exit_dialog ( get_localized_string ( localized_string_id : : CELL_HDD_GAME_EXIT_BROKEN ) , true ) ;
2018-07-25 23:32:21 +02:00
}
2015-08-01 18:14:49 +02:00
2019-01-20 22:57:13 +01:00
error_code cellGameDataGetSizeKB ( vm : : ptr < u32 > size )
2015-08-01 18:14:49 +02:00
{
2017-03-22 11:09:10 +01:00
cellGame . warning ( " cellGameDataGetSizeKB(size=*0x%x) " , size ) ;
2018-07-25 23:32:21 +02:00
if ( ! size )
{
return CELL_GAMEDATA_ERROR_PARAM ;
}
2018-03-11 00:35:41 +01:00
const std : : string local_dir = vfs : : get ( Emu . GetDir ( ) ) ;
2017-07-22 15:16:49 +02:00
2020-02-22 16:04:35 +01:00
const auto dirsz = fs : : get_dir_size ( local_dir , 1024 ) ;
if ( dirsz = = umax )
2017-03-22 11:09:10 +01:00
{
2020-02-22 16:04:35 +01:00
const auto error = fs : : g_tls_error ;
if ( fs : : exists ( local_dir ) )
{
2020-02-28 07:20:37 +01:00
cellGame . error ( " cellGameDataGetSizeKB(): Unknown failure on calculating directory '%s' size (%s) " , local_dir , error ) ;
2020-02-22 16:04:35 +01:00
}
2017-03-22 11:09:10 +01:00
return CELL_GAMEDATA_ERROR_FAILURE ;
}
2020-02-22 16:04:35 +01:00
* size = : : narrow < u32 > ( dirsz / 1024 ) ;
2017-02-05 14:07:26 +01:00
2016-12-05 18:35:05 +01:00
return CELL_OK ;
2015-08-01 18:14:49 +02:00
}
2019-01-20 22:57:13 +01:00
error_code cellGameDataSetSystemVer ( vm : : cptr < char > systemVersion )
2015-08-01 18:14:49 +02:00
{
2018-07-25 23:32:21 +02:00
cellGame . todo ( " cellGameDataSetSystemVer(systemVersion=%s) " , systemVersion ) ;
if ( ! systemVersion )
{
return CELL_GAMEDATA_ERROR_PARAM ;
}
2018-01-15 00:31:26 +01:00
return CELL_OK ;
2015-08-01 18:14:49 +02:00
}
2019-01-20 22:57:13 +01:00
error_code cellGameDataExitBroken ( )
2015-08-01 18:14:49 +02:00
{
2018-07-25 23:32:21 +02:00
cellGame . warning ( " cellGameDataExitBroken() " ) ;
2020-09-03 20:56:02 +02:00
return open_exit_dialog ( get_localized_string ( localized_string_id : : CELL_GAME_DATA_EXIT_BROKEN ) , true ) ;
2018-07-25 23:32:21 +02:00
}
2015-08-01 18:14:49 +02:00
2016-08-16 17:46:24 +02:00
error_code cellGameBootCheck ( vm : : ptr < u32 > type , vm : : ptr < u32 > attributes , vm : : ptr < CellGameContentSize > size , vm : : ptr < char [ CELL_GAME_DIRNAME_SIZE ] > dirName )
2015-04-13 16:05:44 +02:00
{
2016-01-12 22:57:16 +01:00
cellGame . warning ( " cellGameBootCheck(type=*0x%x, attributes=*0x%x, size=*0x%x, dirName=*0x%x) " , type , attributes , size , dirName ) ;
2013-11-29 05:56:26 +01:00
2019-12-01 06:14:09 +01:00
if ( ! type | | ! attributes )
2014-08-12 19:44:28 +02:00
{
2019-12-01 06:14:09 +01:00
return CELL_GAME_ERROR_PARAM ;
2014-08-12 19:44:28 +02:00
}
2014-03-17 20:34:19 +01:00
2021-03-02 12:59:19 +01:00
auto & perm = g_fxo - > get < content_permission > ( ) ;
2019-12-01 06:14:09 +01:00
2021-03-02 12:59:19 +01:00
const auto init = perm . init . init ( ) ;
2019-12-01 06:14:09 +01:00
if ( ! init )
2017-12-25 21:14:57 +01:00
{
2019-12-03 15:57:41 +01:00
return CELL_GAME_ERROR_BUSY ;
2017-12-25 21:14:57 +01:00
}
2014-06-28 03:19:44 +02:00
2019-09-26 15:10:35 +02:00
std : : string dir ;
psf : : registry sfo ;
2017-12-25 21:14:57 +01:00
if ( Emu . GetCat ( ) = = " DG " )
2014-06-28 03:19:44 +02:00
{
2014-09-01 02:51:48 +02:00
* type = CELL_GAME_GAMETYPE_DISC ;
* attributes = 0 ; // TODO
2017-11-23 13:27:44 +01:00
// TODO: dirName might be a read only string when BootCheck is called on a disc game. (e.g. Ben 10 Ultimate Alien: Cosmic Destruction)
2015-08-06 17:55:19 +02:00
2019-09-26 15:10:35 +02:00
sfo = psf : : load_object ( fs : : file ( vfs : : get ( " /dev_bdvd/PS3_GAME/PARAM.SFO " ) ) ) ;
2014-06-28 03:19:44 +02:00
}
2017-12-25 21:14:57 +01:00
else if ( Emu . GetCat ( ) = = " GD " )
2014-06-28 03:19:44 +02:00
{
2014-09-01 02:51:48 +02:00
* type = CELL_GAME_GAMETYPE_DISC ;
* attributes = CELL_GAME_ATTRIBUTE_PATCH ; // TODO
2015-08-06 17:55:19 +02:00
2019-09-26 15:10:35 +02:00
sfo = psf : : load_object ( fs : : file ( vfs : : get ( Emu . GetDir ( ) + " PARAM.SFO " ) ) ) ;
2014-06-28 03:19:44 +02:00
}
2016-01-26 19:13:36 +01:00
else
2014-06-28 03:19:44 +02:00
{
2017-12-25 21:14:57 +01:00
* type = CELL_GAME_GAMETYPE_HDD ;
* attributes = 0 ; // TODO
2019-09-26 15:10:35 +02:00
sfo = psf : : load_object ( fs : : file ( vfs : : get ( Emu . GetDir ( ) + " PARAM.SFO " ) ) ) ;
dir = Emu . GetTitleID ( ) ;
}
2019-12-01 06:14:09 +01:00
if ( size )
{
// TODO: Use the free space of the computer's HDD where RPCS3 is being run.
size - > hddFreeSizeKB = 40 * 1024 * 1024 - 1 ; // Read explanation in cellHddGameCheck
2019-09-26 15:10:35 +02:00
2019-12-01 06:14:09 +01:00
// TODO: Calculate data size for HG and DG games, if necessary.
size - > sizeKB = CELL_GAME_SIZEKB_NOTCALC ;
size - > sysSizeKB = 4 ;
}
2019-09-26 15:10:35 +02:00
2020-02-19 16:26:41 +01:00
if ( * type = = u32 { CELL_GAME_GAMETYPE_HDD } & & dirName )
2019-09-26 15:10:35 +02:00
{
2019-12-01 06:14:09 +01:00
strcpy_trunc ( * dirName , Emu . GetTitleID ( ) ) ;
2014-06-28 03:19:44 +02:00
}
2013-11-29 05:56:26 +01:00
2021-03-02 12:59:19 +01:00
perm . dir = std : : move ( dir ) ;
perm . sfo = std : : move ( sfo ) ;
perm . restrict_sfo_params = * type = = u32 { CELL_GAME_GAMETYPE_HDD } ; // Ratchet & Clank: All 4 One (PSN versions) rely on this error checking (TODO: Needs proper hw tests)
perm . exists = true ;
2019-09-26 15:10:35 +02:00
2016-08-16 17:46:24 +02:00
return CELL_OK ;
2013-09-28 04:36:57 +02:00
}
2016-08-16 17:46:24 +02:00
error_code cellGamePatchCheck ( vm : : ptr < CellGameContentSize > size , vm : : ptr < void > reserved )
2013-09-28 04:36:57 +02:00
{
2016-01-12 22:57:16 +01:00
cellGame . warning ( " cellGamePatchCheck(size=*0x%x, reserved=*0x%x) " , size , reserved ) ;
2014-06-28 03:19:44 +02:00
2017-12-25 21:14:57 +01:00
if ( Emu . GetCat ( ) ! = " GD " )
2014-06-28 03:19:44 +02:00
{
return CELL_GAME_ERROR_NOTPATCH ;
}
2019-09-26 15:10:35 +02:00
psf : : registry sfo = psf : : load_object ( fs : : file ( vfs : : get ( Emu . GetDir ( ) + " PARAM.SFO " ) ) ) ;
2021-03-02 12:59:19 +01:00
auto & perm = g_fxo - > get < content_permission > ( ) ;
2019-09-26 15:10:35 +02:00
2021-03-02 12:59:19 +01:00
const auto init = perm . init . init ( ) ;
2019-09-26 15:10:35 +02:00
if ( ! init )
2015-08-06 17:55:19 +02:00
{
2019-12-03 15:57:41 +01:00
return CELL_GAME_ERROR_BUSY ;
2015-08-06 17:55:19 +02:00
}
2017-12-16 01:03:49 +01:00
2019-12-05 14:48:13 +01:00
if ( size )
{
// TODO: Use the free space of the computer's HDD where RPCS3 is being run.
size - > hddFreeSizeKB = 40 * 1024 * 1024 - 1 ; // Read explanation in cellHddGameCheck
// TODO: Calculate data size for patch data, if necessary.
size - > sizeKB = CELL_GAME_SIZEKB_NOTCALC ;
size - > sysSizeKB = 0 ; // TODO
}
2021-03-02 12:59:19 +01:00
perm . restrict_sfo_params = false ;
perm . dir = Emu . GetTitleID ( ) ;
perm . sfo = std : : move ( sfo ) ;
perm . exists = true ;
2019-09-26 15:10:35 +02:00
2015-08-06 17:55:19 +02:00
return CELL_OK ;
2013-09-28 04:36:57 +02:00
}
2016-08-16 17:46:24 +02:00
error_code cellGameDataCheck ( u32 type , vm : : cptr < char > dirName , vm : : ptr < CellGameContentSize > size )
2013-09-28 04:36:57 +02:00
{
2016-08-11 01:29:59 +02:00
cellGame . warning ( " cellGameDataCheck(type=%d, dirName=%s, size=*0x%x) " , type , dirName , size ) ;
2014-06-28 03:19:44 +02:00
2018-01-14 20:16:48 +01:00
if ( ( type - 1 ) > = 3 | | ( type ! = CELL_GAME_GAMETYPE_DISC & & ! dirName ) )
2014-06-28 03:19:44 +02:00
{
2018-01-14 20:16:48 +01:00
return { CELL_GAME_ERROR_PARAM , type } ;
2014-06-28 03:19:44 +02:00
}
2019-09-26 15:10:35 +02:00
std : : string name ;
if ( type ! = CELL_GAME_GAMETYPE_DISC )
{
name = dirName . get_ptr ( ) ;
}
const std : : string dir = type = = CELL_GAME_GAMETYPE_DISC ? " /dev_bdvd/PS3_GAME " s : " /dev_hdd0/game/ " + name ;
2016-03-21 20:43:03 +01:00
// TODO: not sure what should be checked there
2015-02-12 21:10:25 +01:00
2021-03-02 12:59:19 +01:00
auto & perm = g_fxo - > get < content_permission > ( ) ;
2019-09-26 15:10:35 +02:00
2021-03-02 12:59:19 +01:00
auto init = perm . init . init ( ) ;
2019-09-26 15:10:35 +02:00
if ( ! init )
2014-06-28 03:19:44 +02:00
{
2019-12-03 15:57:41 +01:00
return CELL_GAME_ERROR_BUSY ;
2016-03-21 20:43:03 +01:00
}
2015-02-12 21:10:25 +01:00
2021-03-30 23:44:58 +02:00
auto [ sfo , psf_error ] = psf : : load ( vfs : : get ( dir + " /PARAM.SFO " ) ) ;
2020-09-22 10:30:31 +02:00
if ( psf : : get_string ( sfo , " CATEGORY " ) ! = [ & ] ( )
{
switch ( type )
{
case CELL_GAME_GAMETYPE_HDD : return " HG " sv ;
case CELL_GAME_GAMETYPE_GAMEDATA : return " GD " sv ;
case CELL_GAME_GAMETYPE_DISC : return " DG " sv ;
2020-12-10 11:37:26 +01:00
default : fmt : : throw_exception ( " Unreachable " ) ;
2020-09-22 10:30:31 +02:00
}
} ( ) )
{
2021-03-30 23:44:58 +02:00
if ( psf_error ! = psf : : error : : stream )
2020-09-22 10:30:31 +02:00
{
2020-09-28 23:16:24 +02:00
init . cancel ( ) ;
2021-04-09 23:43:08 +02:00
return { CELL_GAME_ERROR_BROKEN , psf_error } ;
2020-09-22 10:30:31 +02:00
}
}
2019-12-05 14:48:13 +01:00
if ( size )
{
// TODO: Use the free space of the computer's HDD where RPCS3 is being run.
size - > hddFreeSizeKB = 40 * 1024 * 1024 - 1 ; // Read explanation in cellHddGameCheck
// TODO: Calculate data size for game data, if necessary.
size - > sizeKB = CELL_GAME_SIZEKB_NOTCALC ;
size - > sysSizeKB = 0 ; // TODO
}
2021-03-02 12:59:19 +01:00
perm . dir = std : : move ( name ) ;
2019-09-26 15:10:35 +02:00
2018-03-12 13:33:18 +01:00
if ( type = = CELL_GAME_GAMETYPE_GAMEDATA )
{
2021-03-02 12:59:19 +01:00
perm . can_create = true ;
2018-03-12 13:33:18 +01:00
}
2021-03-02 12:59:19 +01:00
perm . restrict_sfo_params = false ;
2020-03-03 16:02:22 +01:00
2020-09-22 10:30:31 +02:00
if ( sfo . empty ( ) )
2016-03-21 20:43:03 +01:00
{
2016-08-17 20:59:42 +02:00
cellGame . warning ( " cellGameDataCheck(): directory '%s' not found " , dir ) ;
return not_an_error ( CELL_GAME_RET_NONE ) ;
2014-06-28 03:19:44 +02:00
}
2021-03-02 12:59:19 +01:00
perm . exists = true ;
perm . sfo = std : : move ( sfo ) ;
2016-08-16 17:46:24 +02:00
return CELL_OK ;
2013-09-28 04:36:57 +02:00
}
2016-08-16 17:46:24 +02:00
error_code cellGameContentPermit ( vm : : ptr < char [ CELL_GAME_PATH_MAX ] > contentInfoPath , vm : : ptr < char [ CELL_GAME_PATH_MAX ] > usrdirPath )
2013-09-28 04:36:57 +02:00
{
2016-01-12 22:57:16 +01:00
cellGame . warning ( " cellGameContentPermit(contentInfoPath=*0x%x, usrdirPath=*0x%x) " , contentInfoPath , usrdirPath ) ;
2014-11-19 16:17:29 +01:00
2019-12-01 06:30:46 +01:00
if ( ! contentInfoPath | | ! usrdirPath )
2014-11-19 16:17:29 +01:00
{
return CELL_GAME_ERROR_PARAM ;
}
2015-04-13 16:05:44 +02:00
2021-03-02 12:59:19 +01:00
auto & perm = g_fxo - > get < content_permission > ( ) ;
2019-09-26 15:10:35 +02:00
2021-03-02 12:59:19 +01:00
const auto init = perm . init . reset ( ) ;
2017-12-16 01:03:49 +01:00
2019-09-26 15:10:35 +02:00
if ( ! init )
2014-06-28 03:19:44 +02:00
{
2014-03-12 23:34:35 +01:00
return CELL_GAME_ERROR_FAILURE ;
2014-06-28 03:19:44 +02:00
}
2014-03-12 23:34:35 +01:00
2021-03-02 12:59:19 +01:00
const std : : string dir = perm . dir . empty ( ) ? " /dev_bdvd/PS3_GAME " s : " /dev_hdd0/game/ " + perm . dir ;
2018-06-12 01:08:59 +02:00
2021-03-02 12:59:19 +01:00
if ( perm . temp . empty ( ) & & ! perm . exists )
2018-06-08 19:56:16 +02:00
{
2021-03-02 12:59:19 +01:00
perm . reset ( ) ;
2018-06-12 01:08:59 +02:00
strcpy_trunc ( * contentInfoPath , " " ) ;
strcpy_trunc ( * usrdirPath , " " ) ;
2018-06-08 19:56:16 +02:00
return CELL_OK ;
}
2021-03-02 12:59:19 +01:00
if ( ! perm . temp . empty ( ) )
2015-04-20 17:53:31 +02:00
{
2019-09-26 15:10:35 +02:00
// Create PARAM.SFO
2021-03-02 12:59:19 +01:00
fs : : pending_file temp ( perm . temp + " /PARAM.SFO " ) ;
temp . file . write ( psf : : save_object ( perm . sfo ) ) ;
2021-02-21 20:55:07 +01:00
ensure ( temp . commit ( ) ) ;
2016-08-17 20:59:42 +02:00
2019-09-26 15:10:35 +02:00
// Make temporary directory persistent (atomically)
2021-03-02 12:59:19 +01:00
if ( vfs : : host : : rename ( perm . temp , vfs : : get ( dir ) , & g_mp_sys_dev_hdd0 , false ) )
2015-04-20 17:53:31 +02:00
{
2016-08-17 20:59:42 +02:00
cellGame . success ( " cellGameContentPermit(): directory '%s' has been created " , dir ) ;
2019-09-26 15:10:35 +02:00
// Prevent cleanup
2021-03-02 12:59:19 +01:00
perm . temp . clear ( ) ;
2015-08-06 17:55:19 +02:00
}
else
{
2018-03-12 17:07:27 +01:00
cellGame . error ( " cellGameContentPermit(): failed to initialize directory '%s' (%s) " , dir , fs : : g_tls_error ) ;
2015-04-20 17:53:31 +02:00
}
2015-08-06 17:55:19 +02:00
}
2021-03-02 12:59:19 +01:00
else if ( perm . can_create )
2020-09-23 19:35:06 +02:00
{
// Update PARAM.SFO
2021-02-21 20:55:07 +01:00
fs : : pending_file temp ( vfs : : get ( dir + " /PARAM.SFO " ) ) ;
2021-03-02 12:59:19 +01:00
temp . file . write ( psf : : save_object ( perm . sfo ) ) ;
2021-02-21 20:55:07 +01:00
ensure ( temp . commit ( ) ) ;
2020-09-23 19:35:06 +02:00
}
2016-08-17 20:59:42 +02:00
2020-08-29 20:20:37 +02:00
// Cleanup
2021-03-02 12:59:19 +01:00
perm . reset ( ) ;
2020-08-29 20:20:37 +02:00
2016-08-17 20:59:42 +02:00
strcpy_trunc ( * contentInfoPath , dir ) ;
strcpy_trunc ( * usrdirPath , dir + " /USRDIR " ) ;
2015-08-06 17:55:19 +02:00
return CELL_OK ;
2014-06-28 03:19:44 +02:00
}
2016-08-16 17:46:24 +02:00
error_code cellGameDataCheckCreate2 ( ppu_thread & ppu , u32 version , vm : : cptr < char > dirName , u32 errDialog , vm : : ptr < CellGameDataStatCallback > funcStat , u32 container )
2014-06-28 03:19:44 +02:00
{
2016-08-11 01:29:59 +02:00
cellGame . error ( " cellGameDataCheckCreate2(version=0x%x, dirName=%s, errDialog=0x%x, funcStat=*0x%x, container=%d) " , version , dirName , errDialog , funcStat , container ) ;
2014-07-01 00:53:29 +02:00
2017-12-16 01:03:49 +01:00
//older sdk. it might not care about game type.
2017-03-11 09:59:50 +01:00
2020-02-20 22:21:27 +01:00
if ( version ! = CELL_GAMEDATA_VERSION_CURRENT | | errDialog > 1 | | ! funcStat | | sysutil_check_name_string ( dirName . get_ptr ( ) , 1 , CELL_GAME_DIRNAME_SIZE ) ! = 0 )
2014-07-01 00:53:29 +02:00
{
return CELL_GAMEDATA_ERROR_PARAM ;
}
2020-09-28 21:18:23 +02:00
const std : : string game_dir = dirName . get_ptr ( ) ;
const std : : string dir = " /dev_hdd0/game/ " s + game_dir ;
2014-07-01 00:53:29 +02:00
2021-03-30 23:44:58 +02:00
auto [ sfo , psf_error ] = psf : : load ( vfs : : get ( dir + " /PARAM.SFO " ) ) ;
2020-02-21 17:09:41 +01:00
2021-03-30 23:44:58 +02:00
const u32 new_data = psf_error = = psf : : error : : stream ? CELL_GAMEDATA_ISNEWDATA_YES : CELL_GAMEDATA_ISNEWDATA_NO ;
2020-02-21 17:09:41 +01:00
2020-04-07 13:03:03 +02:00
if ( ! new_data )
2020-02-21 17:09:41 +01:00
{
2020-04-07 13:03:03 +02:00
const auto cat = psf : : get_string ( sfo , " CATEGORY " , " " ) ;
if ( cat ! = " GD " & & cat ! = " DG " )
{
return CELL_GAMEDATA_ERROR_BROKEN ;
}
2020-02-21 17:09:41 +01:00
}
2020-09-28 21:18:23 +02:00
const std : : string usrdir = dir + " /USRDIR " ;
vm : : var < CellGameDataCBResult > cbResult ;
vm : : var < CellGameDataStatGet > cbGet ;
vm : : var < CellGameDataStatSet > cbSet ;
2020-02-21 09:40:14 +01:00
cbGet - > isNewData = new_data ;
2018-04-15 21:52:06 +02:00
2014-07-01 00:53:29 +02:00
// TODO: Use the free space of the computer's HDD where RPCS3 is being run.
2018-07-10 20:07:53 +02:00
cbGet - > hddFreeSizeKB = 40 * 1024 * 1024 - 1 ; // Read explanation in cellHddGameCheck
2014-07-01 00:53:29 +02:00
2014-07-01 17:34:25 +02:00
strcpy_trunc ( cbGet - > contentInfoPath , dir ) ;
2020-02-21 17:09:41 +01:00
strcpy_trunc ( cbGet - > gameDataPath , usrdir ) ;
2014-07-01 00:53:29 +02:00
// TODO: set correct time
2014-07-01 17:34:25 +02:00
cbGet - > st_atime_ = 0 ;
cbGet - > st_ctime_ = 0 ;
cbGet - > st_mtime_ = 0 ;
2014-07-01 00:53:29 +02:00
// TODO: calculate data size, if necessary
cbGet - > sizeKB = CELL_GAMEDATA_SIZEKB_NOTCALC ;
2019-12-05 14:48:13 +01:00
cbGet - > sysSizeKB = 0 ; // TODO
2014-07-01 00:53:29 +02:00
cbGet - > getParam . attribute = CELL_GAMEDATA_ATTR_NORMAL ;
2016-04-25 12:49:12 +02:00
cbGet - > getParam . parentalLevel = psf : : get_integer ( sfo , " PARENTAL_LEVEL " , 0 ) ;
strcpy_trunc ( cbGet - > getParam . dataVersion , psf : : get_string ( sfo , " APP_VER " , " " ) ) ;
strcpy_trunc ( cbGet - > getParam . titleId , psf : : get_string ( sfo , " TITLE_ID " , " " ) ) ;
strcpy_trunc ( cbGet - > getParam . title , psf : : get_string ( sfo , " TITLE " , " " ) ) ;
2017-03-11 09:59:50 +01:00
for ( u32 i = 0 ; i < CELL_HDDGAME_SYSP_LANGUAGE_NUM ; i + + )
2014-07-01 00:53:29 +02:00
{
2017-03-11 09:59:50 +01:00
strcpy_trunc ( cbGet - > getParam . titleLang [ i ] , psf : : get_string ( sfo , fmt : : format ( " TITLE_%02d " , i ) ) ) ;
2014-07-01 00:53:29 +02:00
}
2017-03-11 09:59:50 +01:00
funcStat ( ppu , cbResult , cbGet , cbSet ) ;
2017-12-16 01:03:49 +01:00
2020-09-28 21:18:23 +02:00
std : : string error_msg ;
2019-12-01 18:14:58 +01:00
switch ( cbResult - > result )
2014-07-01 00:53:29 +02:00
{
2014-07-01 19:50:57 +02:00
case CELL_GAMEDATA_CBRESULT_OK_CANCEL :
2017-12-29 00:54:12 +01:00
{
2016-01-12 22:57:16 +01:00
cellGame . warning ( " cellGameDataCheckCreate2(): callback returned CELL_GAMEDATA_CBRESULT_OK_CANCEL " ) ;
2017-03-11 09:59:50 +01:00
return CELL_OK ;
2017-12-29 00:54:12 +01:00
}
2017-12-16 01:03:49 +01:00
case CELL_GAMEDATA_CBRESULT_OK :
2017-12-29 00:54:12 +01:00
{
// Game confirmed that it wants to create directory
2020-02-21 17:09:41 +01:00
const auto setParam = cbSet - > setParam ;
2017-12-29 00:54:12 +01:00
2020-02-21 17:09:41 +01:00
if ( new_data )
2017-12-29 00:54:12 +01:00
{
2020-02-21 17:09:41 +01:00
if ( ! setParam )
{
return CELL_GAMEDATA_ERROR_PARAM ;
}
if ( ! fs : : create_path ( vfs : : get ( usrdir ) ) )
{
return { CELL_GAME_ERROR_ACCESS_ERROR , usrdir } ;
}
2017-12-29 00:54:12 +01:00
}
2020-02-21 17:09:41 +01:00
if ( setParam )
2017-03-11 09:59:50 +01:00
{
2020-04-07 13:03:03 +02:00
if ( new_data )
{
psf : : assign ( sfo , " CATEGORY " , psf : : string ( 3 , " GD " ) ) ;
}
2020-02-21 17:09:41 +01:00
psf : : assign ( sfo , " TITLE_ID " , psf : : string ( CELL_GAME_SYSP_TITLEID_SIZE , setParam - > titleId ) ) ;
psf : : assign ( sfo , " TITLE " , psf : : string ( CELL_GAME_SYSP_TITLE_SIZE , setParam - > title ) ) ;
psf : : assign ( sfo , " VERSION " , psf : : string ( CELL_GAME_SYSP_VERSION_SIZE , setParam - > dataVersion ) ) ;
psf : : assign ( sfo , " PARENTAL_LEVEL " , + setParam - > parentalLevel ) ;
2017-03-11 09:59:50 +01:00
for ( u32 i = 0 ; i < CELL_HDDGAME_SYSP_LANGUAGE_NUM ; i + + )
{
2020-09-28 21:18:23 +02:00
if ( ! setParam - > titleLang [ i ] [ 0 ] )
2017-12-29 18:04:10 +01:00
{
continue ;
}
2020-02-21 17:09:41 +01:00
psf : : assign ( sfo , fmt : : format ( " TITLE_%02d " , i ) , psf : : string ( CELL_GAME_SYSP_TITLE_SIZE , setParam - > titleLang [ i ] ) ) ;
2017-03-11 09:59:50 +01:00
}
2021-02-21 20:55:07 +01:00
fs : : pending_file temp ( vfs : : get ( dir + " /PARAM.SFO " ) ) ;
temp . file . write ( psf : : save_object ( sfo ) ) ;
ensure ( temp . commit ( ) ) ;
2020-02-21 09:40:14 +01:00
}
2014-07-01 00:53:29 +02:00
2017-12-29 00:54:12 +01:00
return CELL_OK ;
}
2020-09-28 21:18:23 +02:00
case CELL_GAMEDATA_CBRESULT_ERR_NOSPACE :
cellGame . error ( " cellGameDataCheckCreate2(): callback returned CELL_GAMEDATA_CBRESULT_ERR_NOSPACE. Space Needed: %d KB " , cbResult - > errNeedSizeKB ) ;
error_msg = get_localized_string ( localized_string_id : : CELL_GAMEDATA_CHECK_NOSPACE , fmt : : format ( " %d " , cbResult - > errNeedSizeKB ) . c_str ( ) ) ;
break ;
2014-07-01 00:53:29 +02:00
2014-07-01 19:50:57 +02:00
case CELL_GAMEDATA_CBRESULT_ERR_BROKEN :
2016-01-12 22:57:16 +01:00
cellGame . error ( " cellGameDataCheckCreate2(): callback returned CELL_GAMEDATA_CBRESULT_ERR_BROKEN " ) ;
2020-09-28 21:18:23 +02:00
error_msg = get_localized_string ( localized_string_id : : CELL_GAMEDATA_CHECK_BROKEN , game_dir . c_str ( ) ) ;
break ;
2014-07-01 00:53:29 +02:00
2014-07-01 19:50:57 +02:00
case CELL_GAMEDATA_CBRESULT_ERR_NODATA :
2016-01-12 22:57:16 +01:00
cellGame . error ( " cellGameDataCheckCreate2(): callback returned CELL_GAMEDATA_CBRESULT_ERR_NODATA " ) ;
2020-09-28 21:18:23 +02:00
error_msg = get_localized_string ( localized_string_id : : CELL_GAMEDATA_CHECK_NODATA , game_dir . c_str ( ) ) ;
break ;
2014-07-01 00:53:29 +02:00
2014-07-01 19:50:57 +02:00
case CELL_GAMEDATA_CBRESULT_ERR_INVALID :
2020-09-28 21:18:23 +02:00
cellGame . error ( " cellGameDataCheckCreate2(): callback returned CELL_GAMEDATA_CBRESULT_ERR_INVALID. Error message: %s " , cbResult - > invalidMsg ) ;
2020-11-17 06:38:05 +01:00
error_msg = get_localized_string ( localized_string_id : : CELL_GAMEDATA_CHECK_INVALID , fmt : : format ( " %s " , cbResult - > invalidMsg ) . c_str ( ) ) ;
2020-09-28 21:18:23 +02:00
break ;
2014-07-01 00:53:29 +02:00
default :
2020-09-28 21:18:23 +02:00
cellGame . error ( " cellGameDataCheckCreate2(): callback returned unknown error (code=0x%x). Error message: %s " , cbResult - > invalidMsg ) ;
2020-11-17 06:38:05 +01:00
error_msg = get_localized_string ( localized_string_id : : CELL_GAMEDATA_CHECK_INVALID , fmt : : format ( " %s " , cbResult - > invalidMsg ) . c_str ( ) ) ;
2020-09-28 21:18:23 +02:00
break ;
2014-07-01 00:53:29 +02:00
}
2020-09-28 21:18:23 +02:00
if ( errDialog = = CELL_GAMEDATA_ERRDIALOG_ALWAYS ) // Maybe != CELL_GAMEDATA_ERRDIALOG_NONE
{
// Yield before a blocking dialog is being spawned
lv2_obj : : sleep ( ppu ) ;
// Get user confirmation by opening a blocking dialog
error_code res = open_msg_dialog ( true , CELL_MSGDIALOG_TYPE_SE_TYPE_ERROR | CELL_MSGDIALOG_TYPE_BUTTON_TYPE_OK | CELL_MSGDIALOG_TYPE_DISABLE_CANCEL_ON , vm : : make_str ( error_msg ) ) ;
// Reschedule after a blocking dialog returns
if ( ppu . check_state ( ) )
{
return 0 ;
}
if ( res ! = CELL_OK )
{
return CELL_GAMEDATA_ERROR_INTERNAL ;
}
}
return CELL_GAMEDATA_ERROR_CBRESULT ;
2013-09-28 04:36:57 +02:00
}
2019-01-20 22:57:13 +01:00
error_code cellGameDataCheckCreate ( ppu_thread & ppu , u32 version , vm : : cptr < char > dirName , u32 errDialog , vm : : ptr < CellGameDataStatCallback > funcStat , u32 container )
2014-06-28 03:19:44 +02:00
{
2016-08-11 01:29:59 +02:00
cellGame . warning ( " cellGameDataCheckCreate(version=0x%x, dirName=%s, errDialog=0x%x, funcStat=*0x%x, container=%d) " , version , dirName , errDialog , funcStat , container ) ;
2015-04-13 16:05:44 +02:00
2014-07-01 00:53:29 +02:00
// TODO: almost identical, the only difference is that this function will always calculate the size of game data
2015-08-13 15:28:42 +02:00
return cellGameDataCheckCreate2 ( ppu , version , dirName , errDialog , funcStat , container ) ;
2014-06-28 03:19:44 +02:00
}
2016-08-16 17:46:24 +02:00
error_code cellGameCreateGameData ( vm : : ptr < CellGameSetInitParams > init , vm : : ptr < char [ CELL_GAME_PATH_MAX ] > tmp_contentInfoPath , vm : : ptr < char [ CELL_GAME_PATH_MAX ] > tmp_usrdirPath )
2013-09-28 04:36:57 +02:00
{
2016-01-12 22:57:16 +01:00
cellGame . error ( " cellGameCreateGameData(init=*0x%x, tmp_contentInfoPath=*0x%x, tmp_usrdirPath=*0x%x) " , init , tmp_contentInfoPath , tmp_usrdirPath ) ;
2015-04-13 16:05:44 +02:00
2020-08-29 20:20:37 +02:00
if ( ! init )
{
return CELL_GAME_ERROR_PARAM ;
}
2021-03-02 12:59:19 +01:00
auto & perm = g_fxo - > get < content_permission > ( ) ;
2016-08-17 20:59:42 +02:00
2021-03-02 12:59:19 +01:00
const auto _init = perm . init . access ( ) ;
2019-09-26 15:10:35 +02:00
2021-03-02 12:59:19 +01:00
if ( ! _init | | perm . dir . empty ( ) )
2016-08-17 20:59:42 +02:00
{
return CELL_GAME_ERROR_FAILURE ;
}
2016-04-25 12:49:12 +02:00
2021-03-02 12:59:19 +01:00
if ( ! perm . can_create )
2018-03-12 13:33:18 +01:00
{
return CELL_GAME_ERROR_NOTSUPPORTED ;
}
2021-03-02 12:59:19 +01:00
if ( perm . exists )
2020-08-29 20:20:37 +02:00
{
return CELL_GAME_ERROR_EXIST ;
}
2018-06-06 20:24:55 +02:00
std : : string dirname = " _GDATA_ " + std : : to_string ( steady_clock : : now ( ) . time_since_epoch ( ) . count ( ) ) ;
std : : string tmp_contentInfo = " /dev_hdd0/game/ " + dirname ;
std : : string tmp_usrdir = " /dev_hdd0/game/ " + dirname + " /USRDIR " ;
2015-04-13 16:05:44 +02:00
2016-03-21 20:43:03 +01:00
if ( ! fs : : create_dir ( vfs : : get ( tmp_contentInfo ) ) )
2015-04-13 16:05:44 +02:00
{
2019-10-14 11:40:06 +02:00
cellGame . error ( " cellGameCreateGameData(): failed to create directory '%s' (%s) " , tmp_contentInfo , fs : : g_tls_error ) ;
2015-04-13 16:05:44 +02:00
return CELL_GAME_ERROR_ACCESS_ERROR ; // ???
}
2018-10-17 20:57:24 +02:00
// cellGameContentPermit should then move files in non-temporary location and return their non-temporary displacement
if ( tmp_contentInfoPath ) strcpy_trunc ( * tmp_contentInfoPath , tmp_contentInfo ) ;
2016-03-21 20:43:03 +01:00
if ( ! fs : : create_dir ( vfs : : get ( tmp_usrdir ) ) )
2015-04-13 16:05:44 +02:00
{
2019-10-14 11:40:06 +02:00
cellGame . error ( " cellGameCreateGameData(): failed to create directory '%s' (%s) " , tmp_usrdir , fs : : g_tls_error ) ;
2015-04-13 16:05:44 +02:00
return CELL_GAME_ERROR_ACCESS_ERROR ; // ???
}
2014-06-29 05:21:57 +02:00
2018-10-17 20:57:24 +02:00
if ( tmp_usrdirPath ) strcpy_trunc ( * tmp_usrdirPath , tmp_usrdir ) ;
2016-08-17 20:59:42 +02:00
2021-03-02 12:59:19 +01:00
perm . temp = vfs : : get ( tmp_contentInfo ) ;
2016-08-17 20:59:42 +02:00
cellGame . success ( " cellGameCreateGameData(): temporary directory '%s' has been created " , tmp_contentInfo ) ;
// Initial PARAM.SFO parameters (overwrite)
2021-03-02 12:59:19 +01:00
perm . sfo =
2016-04-25 12:49:12 +02:00
{
2016-08-17 20:59:42 +02:00
{ " CATEGORY " , psf : : string ( 3 , " GD " ) } ,
2016-04-25 12:49:12 +02:00
{ " TITLE_ID " , psf : : string ( CELL_GAME_SYSP_TITLEID_SIZE , init - > titleId ) } ,
{ " TITLE " , psf : : string ( CELL_GAME_SYSP_TITLE_SIZE , init - > title ) } ,
{ " VERSION " , psf : : string ( CELL_GAME_SYSP_VERSION_SIZE , init - > version ) } ,
} ;
2013-09-28 04:36:57 +02:00
return CELL_OK ;
}
2019-01-20 22:57:13 +01:00
error_code cellGameDeleteGameData ( vm : : cptr < char > dirName )
2013-09-28 04:36:57 +02:00
{
2020-09-24 19:13:19 +02:00
cellGame . warning ( " cellGameDeleteGameData(dirName=%s) " , dirName ) ;
2018-07-25 23:32:21 +02:00
if ( ! dirName )
{
return CELL_GAME_ERROR_PARAM ;
}
2020-09-24 19:13:19 +02:00
const std : : string name = dirName . get_ptr ( ) ;
const std : : string dir = vfs : : get ( " /dev_hdd0/game/ " s + name ) ;
2021-03-02 12:59:19 +01:00
auto & perm = g_fxo - > get < content_permission > ( ) ;
2020-09-24 19:13:19 +02:00
auto remove_gd = [ & ] ( ) - > error_code
{
2020-09-24 19:37:49 +02:00
if ( Emu . GetCat ( ) = = " GD " & & Emu . GetDir ( ) . substr ( Emu . GetDir ( ) . find_last_of ( ' / ' ) + 1 ) = = vfs : : escape ( name ) )
2020-09-24 19:13:19 +02:00
{
// Boot patch cannot delete its own directory
return CELL_GAME_ERROR_NOTSUPPORTED ;
}
2021-03-30 23:44:58 +02:00
const auto [ sfo , psf_error ] = psf : : load ( dir + " /PARAM.SFO " ) ;
2020-09-24 19:13:19 +02:00
2021-03-30 23:44:58 +02:00
if ( psf : : get_string ( sfo , " CATEGORY " ) ! = " GD " & & psf_error ! = psf : : error : : stream )
2020-09-24 19:13:19 +02:00
{
2021-04-09 23:43:08 +02:00
return { CELL_GAME_ERROR_NOTSUPPORTED , psf_error } ;
2020-09-24 19:13:19 +02:00
}
if ( sfo . empty ( ) )
{
// Nothing to remove
return CELL_GAME_ERROR_NOTFOUND ;
}
if ( auto id = psf : : get_string ( sfo , " TITLE_ID " ) ; ! id . empty ( ) & & id ! = Emu . GetTitleID ( ) )
{
cellGame . error ( " cellGameDeleteGameData(%s): Attempts to delete GameData with TITLE ID which does not match the program's (%s) " , id , Emu . GetTitleID ( ) ) ;
}
// Actually remove game data
2021-04-21 22:12:21 +02:00
if ( ! vfs : : host : : remove_all ( dir , rpcs3 : : utils : : get_hdd0_dir ( ) , & g_mp_sys_dev_hdd0 , true ) )
2020-09-24 19:13:19 +02:00
{
return { CELL_GAME_ERROR_ACCESS_ERROR , dir } ;
}
return CELL_OK ;
} ;
while ( true )
{
// Obtain exclusive lock and cancel init
2021-03-02 12:59:19 +01:00
auto _init = perm . init . init ( ) ;
2020-09-24 19:13:19 +02:00
if ( ! _init )
{
// Or access it
2021-03-02 12:59:19 +01:00
if ( auto access = perm . init . access ( ) ; access )
2020-09-24 19:13:19 +02:00
{
// Cannot remove it when it is accessed by cellGameDataCheck
// If it is HG data then resort to remove_gd for ERROR_BROKEN
2021-03-02 12:59:19 +01:00
if ( perm . dir = = name & & perm . can_create )
2020-09-24 19:13:19 +02:00
{
return CELL_GAME_ERROR_NOTSUPPORTED ;
}
return remove_gd ( ) ;
}
else
{
// Reacquire lock
continue ;
}
}
auto err = remove_gd ( ) ;
_init . cancel ( ) ;
return err ;
}
2013-09-28 04:36:57 +02:00
}
2016-08-16 17:46:24 +02:00
error_code cellGameGetParamInt ( s32 id , vm : : ptr < s32 > value )
2013-09-28 04:36:57 +02:00
{
2016-01-12 22:57:16 +01:00
cellGame . warning ( " cellGameGetParamInt(id=%d, value=*0x%x) " , id , value ) ;
2013-11-23 05:47:19 +01:00
2018-07-25 23:32:21 +02:00
if ( ! value )
{
return CELL_GAME_ERROR_PARAM ;
}
2021-03-02 12:59:19 +01:00
auto & perm = g_fxo - > get < content_permission > ( ) ;
2019-09-26 15:10:35 +02:00
2021-03-02 12:59:19 +01:00
const auto init = perm . init . access ( ) ;
2016-04-25 12:49:12 +02:00
2019-09-26 15:10:35 +02:00
if ( ! init )
2016-04-25 12:49:12 +02:00
{
return CELL_GAME_ERROR_FAILURE ;
}
2016-01-07 23:12:33 +01:00
std : : string key ;
2013-11-23 05:47:19 +01:00
switch ( id )
2013-11-24 01:01:57 +01:00
{
2016-01-07 23:12:33 +01:00
case CELL_GAME_PARAMID_PARENTAL_LEVEL : key = " PARENTAL_LEVEL " ; break ;
case CELL_GAME_PARAMID_RESOLUTION : key = " RESOLUTION " ; break ;
case CELL_GAME_PARAMID_SOUND_FORMAT : key = " SOUND_FORMAT " ; break ;
2013-11-23 05:47:19 +01:00
default :
2016-04-25 12:49:12 +02:00
{
2013-11-23 05:47:19 +01:00
return CELL_GAME_ERROR_INVALID_ID ;
}
2016-04-25 12:49:12 +02:00
}
2013-11-23 05:47:19 +01:00
2021-03-02 12:59:19 +01:00
if ( ! perm . sfo . count ( key ) )
2020-02-03 11:31:12 +01:00
{
// TODO: Check if special values need to be set here
cellGame . warning ( " cellGameGetParamInt(): id=%d was not found " , id ) ;
}
2021-03-02 12:59:19 +01:00
* value = psf : : get_integer ( perm . sfo , key , 0 ) ;
2013-09-28 04:36:57 +02:00
return CELL_OK ;
}
2020-01-23 18:09:37 +01:00
// String key restriction flags
enum class strkey_flag : u32
{
get , // reading is restricted
set , // writing is restricted
read_only , // writing is disallowed (don't mind set flag in this case)
__bitset_enum_max
} ;
struct string_key_info
{
std : : string_view name ;
2020-09-28 23:16:24 +02:00
u32 max_size = 0 ;
2020-01-23 18:09:37 +01:00
bs_t < strkey_flag > flags ;
} ;
static string_key_info get_param_string_key ( s32 id )
2016-04-25 12:49:12 +02:00
{
switch ( id )
{
2020-09-28 23:16:24 +02:00
case CELL_GAME_PARAMID_TITLE : return { " TITLE " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set } ; // TODO: Is this value correct?
case CELL_GAME_PARAMID_TITLE_DEFAULT : return { " TITLE " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set } ;
case CELL_GAME_PARAMID_TITLE_JAPANESE : return { " TITLE_00 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_ENGLISH : return { " TITLE_01 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_FRENCH : return { " TITLE_02 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_SPANISH : return { " TITLE_03 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_GERMAN : return { " TITLE_04 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_ITALIAN : return { " TITLE_05 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_DUTCH : return { " TITLE_06 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_PORTUGUESE : return { " TITLE_07 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_RUSSIAN : return { " TITLE_08 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_KOREAN : return { " TITLE_09 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_CHINESE_T : return { " TITLE_10 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_CHINESE_S : return { " TITLE_11 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_FINNISH : return { " TITLE_12 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_SWEDISH : return { " TITLE_13 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_DANISH : return { " TITLE_14 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_NORWEGIAN : return { " TITLE_15 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_POLISH : return { " TITLE_16 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_PORTUGUESE_BRAZIL : return { " TITLE_17 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_ENGLISH_UK : return { " TITLE_18 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_TURKISH : return { " TITLE_19 " , CELL_GAME_SYSP_TITLE_SIZE , strkey_flag : : set + strkey_flag : : get } ;
case CELL_GAME_PARAMID_TITLE_ID : return { " TITLE_ID " , CELL_GAME_SYSP_TITLEID_SIZE , strkey_flag : : read_only } ;
case CELL_GAME_PARAMID_VERSION : return { " VERSION " , CELL_GAME_SYSP_VERSION_SIZE , strkey_flag : : get + strkey_flag : : read_only } ;
case CELL_GAME_PARAMID_PS3_SYSTEM_VER : return { " PS3_SYSTEM_VER " , CELL_GAME_SYSP_PS3_SYSTEM_VER_SIZE } ; // TODO
case CELL_GAME_PARAMID_APP_VER : return { " APP_VER " , CELL_GAME_SYSP_APP_VER_SIZE , strkey_flag : : read_only } ;
2020-01-23 18:09:37 +01:00
}
return { } ;
2016-04-25 12:49:12 +02:00
}
2016-08-16 17:46:24 +02:00
error_code cellGameGetParamString ( s32 id , vm : : ptr < char > buf , u32 bufsize )
2013-11-23 05:47:19 +01:00
{
2016-01-12 22:57:16 +01:00
cellGame . warning ( " cellGameGetParamString(id=%d, buf=*0x%x, bufsize=%d) " , id , buf , bufsize ) ;
2013-11-23 05:47:19 +01:00
2017-11-11 23:22:38 +01:00
if ( ! buf | | bufsize = = 0 )
{
return CELL_GAME_ERROR_PARAM ;
}
2021-03-02 12:59:19 +01:00
auto & perm = g_fxo - > get < content_permission > ( ) ;
2019-09-26 15:10:35 +02:00
2021-03-02 12:59:19 +01:00
const auto init = perm . init . access ( ) ;
2016-03-21 20:43:03 +01:00
2019-09-26 15:10:35 +02:00
if ( ! init )
2013-11-23 05:47:19 +01:00
{
2016-04-25 12:49:12 +02:00
return CELL_GAME_ERROR_FAILURE ;
}
2013-11-23 05:47:19 +01:00
2016-04-25 12:49:12 +02:00
const auto key = get_param_string_key ( id ) ;
2020-01-23 18:09:37 +01:00
if ( key . name . empty ( ) )
2016-04-25 12:49:12 +02:00
{
2013-11-23 05:47:19 +01:00
return CELL_GAME_ERROR_INVALID_ID ;
}
2021-03-02 12:59:19 +01:00
if ( key . flags & strkey_flag : : get & & perm . restrict_sfo_params )
2020-01-23 18:09:37 +01:00
{
return CELL_GAME_ERROR_NOTSUPPORTED ;
}
2021-03-02 12:59:19 +01:00
const auto value = psf : : get_string ( perm . sfo , std : : string ( key . name ) ) ;
2016-01-07 23:12:33 +01:00
2021-03-02 12:59:19 +01:00
if ( value . empty ( ) & & ! perm . sfo . count ( std : : string ( key . name ) ) )
2020-02-03 11:31:12 +01:00
{
// TODO: Check if special values need to be set here
cellGame . warning ( " cellGameGetParamString(): id=%d was not found " , id ) ;
}
2021-05-30 16:10:46 +02:00
std : : span dst ( buf . get_ptr ( ) , bufsize ) ;
2020-03-04 15:08:40 +01:00
strcpy_trunc ( dst , value ) ;
2013-09-28 04:36:57 +02:00
return CELL_OK ;
}
2016-08-16 17:46:24 +02:00
error_code cellGameSetParamString ( s32 id , vm : : cptr < char > buf )
2013-09-28 04:36:57 +02:00
{
2020-08-29 20:20:37 +02:00
cellGame . warning ( " cellGameSetParamString(id=%d, buf=*0x%x %s) " , id , buf , buf ) ;
2016-04-25 12:49:12 +02:00
2017-11-11 23:22:38 +01:00
if ( ! buf )
{
return CELL_GAME_ERROR_PARAM ;
}
2021-03-02 12:59:19 +01:00
auto & perm = g_fxo - > get < content_permission > ( ) ;
2016-04-25 12:49:12 +02:00
2021-03-02 12:59:19 +01:00
const auto init = perm . init . access ( ) ;
2019-09-26 15:10:35 +02:00
if ( ! init )
2016-04-25 12:49:12 +02:00
{
return CELL_GAME_ERROR_FAILURE ;
}
const auto key = get_param_string_key ( id ) ;
2020-01-23 18:09:37 +01:00
if ( key . name . empty ( ) )
2016-04-25 12:49:12 +02:00
{
return CELL_GAME_ERROR_INVALID_ID ;
}
2021-03-02 12:59:19 +01:00
if ( ! perm . can_create | | key . flags & strkey_flag : : read_only | | ( key . flags & strkey_flag : : set & & perm . restrict_sfo_params ) )
2020-01-23 18:09:37 +01:00
{
return CELL_GAME_ERROR_NOTSUPPORTED ;
}
2021-03-02 12:59:19 +01:00
psf : : assign ( perm . sfo , std : : string ( key . name ) , psf : : string ( key . max_size , buf . get_ptr ( ) ) ) ;
2016-04-25 12:49:12 +02:00
2013-09-28 04:36:57 +02:00
return CELL_OK ;
}
2017-07-22 15:16:49 +02:00
error_code cellGameGetSizeKB ( vm : : ptr < s32 > size )
2013-09-28 04:36:57 +02:00
{
2017-03-22 11:09:10 +01:00
cellGame . warning ( " cellGameGetSizeKB(size=*0x%x) " , size ) ;
2017-07-22 15:16:49 +02:00
2018-07-25 23:32:21 +02:00
if ( ! size )
{
return CELL_GAME_ERROR_PARAM ;
}
2020-09-18 21:11:36 +02:00
// Always reset to 0 at start
* size = 0 ;
2021-03-02 12:59:19 +01:00
auto & perm = g_fxo - > get < content_permission > ( ) ;
2019-09-26 15:10:35 +02:00
2021-03-02 12:59:19 +01:00
const auto init = perm . init . access ( ) ;
2017-03-22 11:09:10 +01:00
2019-09-26 15:10:35 +02:00
if ( ! init )
2017-03-22 11:09:10 +01:00
{
return CELL_GAME_ERROR_FAILURE ;
}
2021-03-02 12:59:19 +01:00
const std : : string local_dir = ! perm . temp . empty ( ) ? perm . temp : vfs : : get ( " /dev_hdd0/game/ " + perm . dir ) ;
2017-07-22 15:16:49 +02:00
2020-02-22 16:04:35 +01:00
const auto dirsz = fs : : get_dir_size ( local_dir , 1024 ) ;
if ( dirsz = = umax )
2017-03-22 11:09:10 +01:00
{
2020-02-22 16:04:35 +01:00
const auto error = fs : : g_tls_error ;
if ( ! fs : : exists ( local_dir ) )
2019-01-09 23:31:51 +01:00
{
return CELL_OK ;
}
else
{
2020-02-22 16:04:35 +01:00
cellGame . error ( " cellGameGetSizeKb(): Unknown failure on calculating directory size '%s' (%s) " , local_dir , error ) ;
2019-01-09 23:31:51 +01:00
return CELL_GAME_ERROR_ACCESS_ERROR ;
}
2017-03-22 11:09:10 +01:00
}
2020-02-22 16:04:35 +01:00
* size = : : narrow < u32 > ( dirsz / 1024 ) ;
2017-02-05 14:07:26 +01:00
2013-09-28 04:36:57 +02:00
return CELL_OK ;
}
2019-01-20 22:57:13 +01:00
error_code cellGameGetDiscContentInfoUpdatePath ( vm : : ptr < char > updatePath )
2013-09-28 04:36:57 +02:00
{
2018-07-25 23:32:21 +02:00
cellGame . todo ( " cellGameGetDiscContentInfoUpdatePath(updatePath=*0x%x) " , updatePath ) ;
if ( ! updatePath )
{
return CELL_GAME_ERROR_PARAM ;
}
2013-09-28 04:36:57 +02:00
return CELL_OK ;
}
2019-01-20 22:57:13 +01:00
error_code cellGameGetLocalWebContentPath ( vm : : ptr < char > contentPath )
2013-09-28 04:36:57 +02:00
{
2018-07-25 23:32:21 +02:00
cellGame . todo ( " cellGameGetLocalWebContentPath(contentPath=*0x%x) " , contentPath ) ;
if ( ! contentPath )
{
return CELL_GAME_ERROR_PARAM ;
}
2013-09-28 04:36:57 +02:00
return CELL_OK ;
}
2016-08-16 17:46:24 +02:00
error_code cellGameContentErrorDialog ( s32 type , s32 errNeedSizeKB , vm : : cptr < char > dirName )
2013-09-28 04:36:57 +02:00
{
2016-08-11 01:29:59 +02:00
cellGame . warning ( " cellGameContentErrorDialog(type=%d, errNeedSizeKB=%d, dirName=%s) " , type , errNeedSizeKB , dirName ) ;
2014-08-12 22:27:13 +02:00
2020-09-03 20:56:02 +02:00
std : : string error_msg ;
2014-08-12 22:27:13 +02:00
2020-09-03 20:56:02 +02:00
switch ( type )
2014-02-11 03:47:25 +01:00
{
2020-09-03 20:56:02 +02:00
case CELL_GAME_ERRDIALOG_BROKEN_GAMEDATA :
// Game data is corrupted. The application will continue.
error_msg = get_localized_string ( localized_string_id : : CELL_GAME_ERROR_BROKEN_GAMEDATA ) ;
break ;
case CELL_GAME_ERRDIALOG_BROKEN_HDDGAME :
// HDD boot game is corrupted. The application will continue.
error_msg = get_localized_string ( localized_string_id : : CELL_GAME_ERROR_BROKEN_HDDGAME ) ;
break ;
case CELL_GAME_ERRDIALOG_NOSPACE :
// Not enough available space. The application will continue.
error_msg = get_localized_string ( localized_string_id : : CELL_GAME_ERROR_NOSPACE , fmt : : format ( " %d " , errNeedSizeKB ) . c_str ( ) ) ;
break ;
case CELL_GAME_ERRDIALOG_BROKEN_EXIT_GAMEDATA :
// Game data is corrupted. The application will be terminated.
error_msg = get_localized_string ( localized_string_id : : CELL_GAME_ERROR_BROKEN_EXIT_GAMEDATA ) ;
break ;
case CELL_GAME_ERRDIALOG_BROKEN_EXIT_HDDGAME :
// HDD boot game is corrupted. The application will be terminated.
error_msg = get_localized_string ( localized_string_id : : CELL_GAME_ERROR_BROKEN_EXIT_HDDGAME ) ;
break ;
case CELL_GAME_ERRDIALOG_NOSPACE_EXIT :
// Not enough available space. The application will be terminated.
error_msg = get_localized_string ( localized_string_id : : CELL_GAME_ERROR_NOSPACE_EXIT , fmt : : format ( " %d " , errNeedSizeKB ) . c_str ( ) ) ;
break ;
default :
return CELL_GAME_ERROR_PARAM ;
2014-02-11 03:47:25 +01:00
}
2014-09-05 19:23:00 +02:00
if ( dirName )
{
2019-01-20 22:57:51 +01:00
if ( ! memchr ( dirName . get_ptr ( ) , ' \0 ' , CELL_GAME_DIRNAME_SIZE ) )
{
return CELL_GAME_ERROR_PARAM ;
}
2020-09-03 20:56:02 +02:00
error_msg + = " \n " + get_localized_string ( localized_string_id : : CELL_GAME_ERROR_DIR_NAME , fmt : : format ( " %s " , dirName ) . c_str ( ) ) ;
2014-09-05 19:23:00 +02:00
}
2020-09-03 20:56:02 +02:00
return open_exit_dialog ( error_msg , type > CELL_GAME_ERRDIALOG_NOSPACE ) ;
2018-07-25 23:32:21 +02:00
}
2015-12-19 12:40:52 +01:00
2018-03-24 15:32:33 +01:00
error_code cellGameThemeInstall ( vm : : cptr < char > usrdirPath , vm : : cptr < char > fileName , u32 option )
2018-07-25 23:32:21 +02:00
{
cellGame . todo ( " cellGameThemeInstall(usrdirPath=%s, fileName=%s, option=0x%x) " , usrdirPath , fileName , option ) ;
2015-12-19 12:40:52 +01:00
2018-03-24 15:32:33 +01:00
if ( ! usrdirPath | | ! fileName | | ! memchr ( usrdirPath . get_ptr ( ) , ' \0 ' , CELL_GAME_PATH_MAX ) | | option > CELL_GAME_THEME_OPTION_APPLY )
2016-04-25 12:49:12 +02:00
{
2018-07-25 23:32:21 +02:00
return CELL_GAME_ERROR_PARAM ;
2016-04-25 12:49:12 +02:00
}
2014-08-12 22:27:13 +02:00
2013-09-28 04:36:57 +02:00
return CELL_OK ;
}
2019-01-20 22:57:13 +01:00
error_code cellGameThemeInstallFromBuffer ( u32 fileSize , u32 bufSize , vm : : ptr < void > buf , vm : : ptr < CellGameThemeInstallCallback > func , u32 option )
2013-09-28 04:36:57 +02:00
{
2018-07-25 23:32:21 +02:00
cellGame . todo ( " cellGameThemeInstallFromBuffer(fileSize=%d, bufSize=%d, buf=*0x%x, func=*0x%x, option=0x%x) " , fileSize , bufSize , buf , func , option ) ;
2013-09-28 04:36:57 +02:00
2018-03-24 15:32:33 +01:00
if ( ! buf | | ! fileSize | | ( fileSize > bufSize & & ! func ) | | bufSize < = 4095 | | option > CELL_GAME_THEME_OPTION_APPLY )
{
return CELL_GAME_ERROR_PARAM ;
}
2013-09-28 04:36:57 +02:00
return CELL_OK ;
}
2019-01-20 22:57:13 +01:00
error_code cellDiscGameGetBootDiscInfo ( vm : : ptr < CellDiscGameSystemFileParam > getParam )
2015-08-01 18:14:49 +02:00
{
2017-02-15 09:49:18 +01:00
cellGame . warning ( " cellDiscGameGetBootDiscInfo(getParam=*0x%x) " , getParam ) ;
2017-02-28 16:42:45 +01:00
2018-03-03 00:11:25 +01:00
if ( ! getParam )
2020-02-20 23:11:53 +01:00
{
2018-03-03 00:11:25 +01:00
return CELL_DISCGAME_ERROR_PARAM ;
2020-02-20 23:11:53 +01:00
}
// Always sets 0 at first dword
2021-03-13 16:02:37 +01:00
* utils : : bless < nse_t < u32 , 1 > > ( getParam - > titleId + 0 ) = 0 ;
2018-03-03 00:11:25 +01:00
2017-02-28 16:42:45 +01:00
// This is also called by non-disc games, see NPUB90029
2020-02-20 23:11:53 +01:00
static const std : : string dir = " /dev_bdvd/PS3_GAME " s ;
2017-02-15 09:49:18 +01:00
if ( ! fs : : is_dir ( vfs : : get ( dir ) ) )
{
2018-03-03 00:11:25 +01:00
return CELL_DISCGAME_ERROR_NOT_DISCBOOT ;
2017-02-15 09:49:18 +01:00
}
2017-02-28 16:42:45 +01:00
2017-02-15 09:49:18 +01:00
const auto & psf = psf : : load_object ( fs : : file ( vfs : : get ( dir + " /PARAM.SFO " ) ) ) ;
if ( psf . count ( " PARENTAL_LEVEL " ) ! = 0 ) getParam - > parentalLevel = psf . at ( " PARENTAL_LEVEL " ) . as_integer ( ) ;
if ( psf . count ( " TITLE_ID " ) ! = 0 ) strcpy_trunc ( getParam - > titleId , psf . at ( " TITLE_ID " ) . as_string ( ) ) ;
2016-06-25 21:39:41 +02:00
return CELL_OK ;
2015-08-01 18:14:49 +02:00
}
2019-01-20 22:57:13 +01:00
error_code cellDiscGameRegisterDiscChangeCallback ( vm : : ptr < CellDiscGameDiscEjectCallback > funcEject , vm : : ptr < CellDiscGameDiscInsertCallback > funcInsert )
2015-08-01 18:14:49 +02:00
{
2018-07-25 23:32:21 +02:00
cellGame . todo ( " cellDiscGameRegisterDiscChangeCallback(funcEject=*0x%x, funcInsert=*0x%x) " , funcEject , funcInsert ) ;
2016-06-25 21:39:41 +02:00
return CELL_OK ;
2015-08-01 18:14:49 +02:00
}
2019-01-20 22:57:13 +01:00
error_code cellDiscGameUnregisterDiscChangeCallback ( )
2015-08-01 18:14:49 +02:00
{
2018-07-25 23:32:21 +02:00
cellGame . todo ( " cellDiscGameUnregisterDiscChangeCallback() " ) ;
2016-06-25 21:39:41 +02:00
return CELL_OK ;
2015-08-01 18:14:49 +02:00
}
2019-01-20 22:57:13 +01:00
error_code cellGameRegisterDiscChangeCallback ( vm : : ptr < CellGameDiscEjectCallback > funcEject , vm : : ptr < CellGameDiscInsertCallback > funcInsert )
2015-08-01 18:14:49 +02:00
{
2018-07-25 23:32:21 +02:00
cellGame . todo ( " cellGameRegisterDiscChangeCallback(funcEject=*0x%x, funcInsert=*0x%x) " , funcEject , funcInsert ) ;
2016-06-25 21:39:41 +02:00
return CELL_OK ;
2015-08-01 18:14:49 +02:00
}
2019-01-20 22:57:13 +01:00
error_code cellGameUnregisterDiscChangeCallback ( )
2015-08-01 18:14:49 +02:00
{
2018-07-25 23:32:21 +02:00
cellGame . todo ( " cellGameUnregisterDiscChangeCallback() " ) ;
2016-06-25 21:39:41 +02:00
return CELL_OK ;
2015-08-01 18:14:49 +02:00
}
void cellSysutil_GameData_init ( )
{
REG_FUNC ( cellSysutil , cellHddGameCheck ) ;
REG_FUNC ( cellSysutil , cellHddGameCheck2 ) ;
REG_FUNC ( cellSysutil , cellHddGameGetSizeKB ) ;
REG_FUNC ( cellSysutil , cellHddGameSetSystemVer ) ;
REG_FUNC ( cellSysutil , cellHddGameExitBroken ) ;
REG_FUNC ( cellSysutil , cellGameDataGetSizeKB ) ;
REG_FUNC ( cellSysutil , cellGameDataSetSystemVer ) ;
REG_FUNC ( cellSysutil , cellGameDataExitBroken ) ;
REG_FUNC ( cellSysutil , cellGameDataCheckCreate ) ;
REG_FUNC ( cellSysutil , cellGameDataCheckCreate2 ) ;
REG_FUNC ( cellSysutil , cellDiscGameGetBootDiscInfo ) ;
REG_FUNC ( cellSysutil , cellDiscGameRegisterDiscChangeCallback ) ;
REG_FUNC ( cellSysutil , cellDiscGameUnregisterDiscChangeCallback ) ;
REG_FUNC ( cellSysutil , cellGameRegisterDiscChangeCallback ) ;
REG_FUNC ( cellSysutil , cellGameUnregisterDiscChangeCallback ) ;
}
2016-03-21 20:43:03 +01:00
DECLARE ( ppu_module_manager : : cellGame ) ( " cellGame " , [ ] ( )
2013-09-28 04:36:57 +02:00
{
2015-02-20 14:58:40 +01:00
REG_FUNC ( cellGame , cellGameBootCheck ) ;
REG_FUNC ( cellGame , cellGamePatchCheck ) ;
REG_FUNC ( cellGame , cellGameDataCheck ) ;
REG_FUNC ( cellGame , cellGameContentPermit ) ;
2013-09-28 04:36:57 +02:00
2015-02-20 14:58:40 +01:00
REG_FUNC ( cellGame , cellGameCreateGameData ) ;
REG_FUNC ( cellGame , cellGameDeleteGameData ) ;
2013-09-28 04:36:57 +02:00
2015-02-20 14:58:40 +01:00
REG_FUNC ( cellGame , cellGameGetParamInt ) ;
REG_FUNC ( cellGame , cellGameGetParamString ) ;
REG_FUNC ( cellGame , cellGameSetParamString ) ;
REG_FUNC ( cellGame , cellGameGetSizeKB ) ;
REG_FUNC ( cellGame , cellGameGetDiscContentInfoUpdatePath ) ;
REG_FUNC ( cellGame , cellGameGetLocalWebContentPath ) ;
2013-09-28 04:36:57 +02:00
2015-02-20 14:58:40 +01:00
REG_FUNC ( cellGame , cellGameContentErrorDialog ) ;
2013-09-28 04:36:57 +02:00
2015-02-20 14:58:40 +01:00
REG_FUNC ( cellGame , cellGameThemeInstall ) ;
REG_FUNC ( cellGame , cellGameThemeInstallFromBuffer ) ;
2015-02-18 17:22:06 +01:00
} ) ;