diff --git a/rpcs3/Emu/Cell/Modules/cellGame.cpp b/rpcs3/Emu/Cell/Modules/cellGame.cpp index 1b25795dff..c32ee8ca17 100644 --- a/rpcs3/Emu/Cell/Modules/cellGame.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGame.cpp @@ -5,6 +5,7 @@ #include "Emu/IdManager.h" #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/lv2/sys_fs.h" +#include "Emu/Cell/lv2/sys_sync.h" #include "cellSysutil.h" #include "cellMsgDialog.h" @@ -172,10 +173,27 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName return CELL_HDDGAME_ERROR_PARAM; } - std::string dir = dirName.get_ptr(); + std::string game_dir = dirName.get_ptr(); // TODO: Find error code - verify(HERE), dir.size() == 9; + verify(HERE), game_dir.size() == 9; + + const std::string dir = "/dev_hdd0/game/" + game_dir; + + psf::registry sfo = psf::load_object(fs::file(vfs::get(dir + "/PARAM.SFO"))); + + const u32 new_data = sfo.empty() && !fs::is_file(vfs::get(dir + "/PARAM.SFO")) ? CELL_GAMEDATA_ISNEWDATA_YES : CELL_GAMEDATA_ISNEWDATA_NO; + + 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"; vm::var result; vm::var get; @@ -191,13 +209,13 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName get->ctime = 0; // TODO get->mtime = 0; // TODO get->sizeKB = CELL_HDDGAME_SIZEKB_NOTCALC; - strcpy_trunc(get->contentInfoPath, "/dev_hdd0/game/" + dir); - strcpy_trunc(get->hddGamePath, "/dev_hdd0/game/" + dir + "/USRDIR"); + strcpy_trunc(get->contentInfoPath, dir); + strcpy_trunc(get->hddGamePath, usrdir); vm::var setParam; set->setParam = setParam; - const std::string& local_dir = vfs::get("/dev_hdd0/game/" + dir); + const std::string& local_dir = vfs::get(dir); if (!fs::is_dir(local_dir)) { @@ -228,14 +246,107 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName funcStat(ppu, result, get, set); - if (result->result != u32{CELL_HDDGAME_CBRESULT_OK} && result->result != u32{CELL_HDDGAME_CBRESULT_OK_CANCEL}) + std::string error_msg; + + switch (result->result) { - return CELL_HDDGAME_ERROR_CBRESULT; + case CELL_HDDGAME_CBRESULT_OK: + { + // 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}; + } + } + + 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); + } + return CELL_OK; + } + case CELL_HDDGAME_CBRESULT_OK_CANCEL: + cellGame.warning("cellHddGameCheck(): callback returned CELL_HDDGAME_CBRESULT_OK_CANCEL"); + return CELL_OK; + + 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; + + 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); + error_msg = get_localized_string(localized_string_id::CELL_HDD_GAME_CHECK_INVALID, result->invalidMsg.get_ptr()); + break; + + default: + cellGame.error("cellHddGameCheck(): callback returned unknown error (code=0x%x). Error message: %s", result->invalidMsg); + error_msg = get_localized_string(localized_string_id::CELL_HDD_GAME_CHECK_INVALID, result->invalidMsg.get_ptr()); + break; } - // TODO ? + if (errDialog == CELL_GAMEDATA_ERRDIALOG_ALWAYS) // Maybe != CELL_GAMEDATA_ERRDIALOG_NONE + { + // Yield before a blocking dialog is being spawned + lv2_obj::sleep(ppu); - return CELL_OK; + // 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; } error_code cellHddGameCheck2(ppu_thread& ppu, u32 version, vm::cptr dirName, u32 errDialog, vm::ptr funcStat, u32 container) @@ -593,14 +704,8 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr return CELL_GAMEDATA_ERROR_PARAM; } - // TODO: output errors (errDialog) - - const std::string dir = "/dev_hdd0/game/"s + dirName.get_ptr(); - const std::string usrdir = dir + "/USRDIR"; - - vm::var cbResult; - vm::var cbGet; - vm::var cbSet; + const std::string game_dir = dirName.get_ptr(); + const std::string dir = "/dev_hdd0/game/"s + game_dir; psf::registry sfo = psf::load_object(fs::file(vfs::get(dir + "/PARAM.SFO"))); @@ -615,6 +720,12 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr } } + const std::string usrdir = dir + "/USRDIR"; + + vm::var cbResult; + vm::var cbGet; + vm::var cbSet; + cbGet->isNewData = new_data; // TODO: Use the free space of the computer's HDD where RPCS3 is being run. @@ -644,15 +755,15 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr funcStat(ppu, cbResult, cbGet, cbSet); + std::string error_msg; + switch (cbResult->result) { case CELL_GAMEDATA_CBRESULT_OK_CANCEL: { - // TODO: do not process game data(directory) cellGame.warning("cellGameDataCheckCreate2(): callback returned CELL_GAMEDATA_CBRESULT_OK_CANCEL"); return CELL_OK; } - case CELL_GAMEDATA_CBRESULT_OK: { // Game confirmed that it wants to create directory @@ -685,7 +796,7 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr for (u32 i = 0; i < CELL_HDDGAME_SYSP_LANGUAGE_NUM; i++) { - if (!cbSet->setParam->titleLang[i][0]) + if (!setParam->titleLang[i][0]) { continue; } @@ -698,26 +809,53 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr return CELL_OK; } - case CELL_GAMEDATA_CBRESULT_ERR_NOSPACE: // TODO: process errors, error message and needSizeKB result - cellGame.error("cellGameDataCheckCreate2(): callback returned CELL_GAMEDATA_CBRESULT_ERR_NOSPACE"); - return CELL_GAMEDATA_ERROR_CBRESULT; + 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; case CELL_GAMEDATA_CBRESULT_ERR_BROKEN: cellGame.error("cellGameDataCheckCreate2(): callback returned CELL_GAMEDATA_CBRESULT_ERR_BROKEN"); - return CELL_GAMEDATA_ERROR_CBRESULT; + error_msg = get_localized_string(localized_string_id::CELL_GAMEDATA_CHECK_BROKEN, game_dir.c_str()); + break; case CELL_GAMEDATA_CBRESULT_ERR_NODATA: cellGame.error("cellGameDataCheckCreate2(): callback returned CELL_GAMEDATA_CBRESULT_ERR_NODATA"); - return CELL_GAMEDATA_ERROR_CBRESULT; + error_msg = get_localized_string(localized_string_id::CELL_GAMEDATA_CHECK_NODATA, game_dir.c_str()); + break; case CELL_GAMEDATA_CBRESULT_ERR_INVALID: - cellGame.error("cellGameDataCheckCreate2(): callback returned CELL_GAMEDATA_CBRESULT_ERR_INVALID"); - return CELL_GAMEDATA_ERROR_CBRESULT; + cellGame.error("cellGameDataCheckCreate2(): callback returned CELL_GAMEDATA_CBRESULT_ERR_INVALID. Error message: %s", cbResult->invalidMsg); + error_msg = get_localized_string(localized_string_id::CELL_GAMEDATA_CHECK_INVALID, cbResult->invalidMsg.get_ptr()); + break; default: - cellGame.error("cellGameDataCheckCreate2(): callback returned unknown error (code=0x%x)"); - return CELL_GAMEDATA_ERROR_CBRESULT; + cellGame.error("cellGameDataCheckCreate2(): callback returned unknown error (code=0x%x). Error message: %s", cbResult->invalidMsg); + error_msg = get_localized_string(localized_string_id::CELL_GAMEDATA_CHECK_INVALID, cbResult->invalidMsg.get_ptr()); + 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_GAMEDATA_ERROR_CBRESULT; } error_code cellGameDataCheckCreate(ppu_thread& ppu, u32 version, vm::cptr dirName, u32 errDialog, vm::ptr funcStat, u32 container) diff --git a/rpcs3/Emu/Cell/Modules/cellGame.h b/rpcs3/Emu/Cell/Modules/cellGame.h index 493e76af98..7109a6dc5c 100644 --- a/rpcs3/Emu/Cell/Modules/cellGame.h +++ b/rpcs3/Emu/Cell/Modules/cellGame.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "Emu/Cell/ErrorCodes.h" @@ -73,6 +73,8 @@ enum CELL_GAME_SIZEKB_NOTCALC = -1, + CELL_GAME_THEMEINSTALL_BUFSIZE_MIN = 4096, + CELL_GAME_ATTRIBUTE_PATCH = 0x1, CELL_GAME_ATTRIBUTE_APP_HOME = 0x2, CELL_GAME_ATTRIBUTE_DEBUG = 0x4, @@ -87,6 +89,7 @@ enum CELL_GAME_DISCTYPE_OTHER = 0, CELL_GAME_DISCTYPE_PS3 = 1, + CELL_GAME_DISCTYPE_PS2 = 2, }; //Parameter IDs of PARAM.SFO @@ -137,6 +140,25 @@ enum CELL_GAME_ERRDIALOG_NOSPACE_EXIT = 102, }; +enum // CellGameResolution +{ + CELL_GAME_RESOLUTION_480 = 0x01, + CELL_GAME_RESOLUTION_576 = 0x02, + CELL_GAME_RESOLUTION_720 = 0x04, + CELL_GAME_RESOLUTION_1080 = 0x08, + CELL_GAME_RESOLUTION_480SQ = 0x10, + CELL_GAME_RESOLUTION_576SQ = 0x20, +}; + +enum // CellGameSoundFormat +{ + CELL_GAME_SOUNDFORMAT_2LPCM = 0x01, + CELL_GAME_SOUNDFORMAT_51LPCM = 0x04, + CELL_GAME_SOUNDFORMAT_71LPCM = 0x10, + CELL_GAME_SOUNDFORMAT_51DDENC = 0x102, + CELL_GAME_SOUNDFORMAT_51DTSENC = 0x202, +}; + struct CellGameContentSize { be_t hddFreeSizeKB; @@ -157,8 +179,8 @@ struct CellGameDataCBResult { be_t result; be_t errNeedSizeKB; - be_t invalidMsg_addr; - be_t reserved; + vm::bptr invalidMsg; + vm::bptr reserved; }; enum // old consts @@ -304,7 +326,7 @@ struct CellHddGameSystemFileParam struct CellHddGameCBResult { - be_t result; + be_t result; be_t errNeedSizeKB; vm::bptr invalidMsg; vm::bptr reserved; diff --git a/rpcs3/Emu/localized_string_id.h b/rpcs3/Emu/localized_string_id.h index 027544e3eb..3a7dcf0dc2 100644 --- a/rpcs3/Emu/localized_string_id.h +++ b/rpcs3/Emu/localized_string_id.h @@ -37,6 +37,16 @@ enum class localized_string_id CELL_GAME_DATA_EXIT_BROKEN, CELL_HDD_GAME_EXIT_BROKEN, + CELL_HDD_GAME_CHECK_NOSPACE, + CELL_HDD_GAME_CHECK_BROKEN, + CELL_HDD_GAME_CHECK_NODATA, + CELL_HDD_GAME_CHECK_INVALID, + + CELL_GAMEDATA_CHECK_NOSPACE, + CELL_GAMEDATA_CHECK_BROKEN, + CELL_GAMEDATA_CHECK_NODATA, + CELL_GAMEDATA_CHECK_INVALID, + CELL_MSG_DIALOG_ERROR_DEFAULT, CELL_MSG_DIALOG_ERROR_80010001, CELL_MSG_DIALOG_ERROR_80010002, diff --git a/rpcs3/rpcs3qt/localized_emu.h b/rpcs3/rpcs3qt/localized_emu.h index a3a006c88e..7cf620e929 100644 --- a/rpcs3/rpcs3qt/localized_emu.h +++ b/rpcs3/rpcs3qt/localized_emu.h @@ -65,6 +65,14 @@ private: case localized_string_id::CELL_GAME_ERROR_DIR_NAME: return tr("Directory name: %0", "Game Error").arg(std::forward(args)...); case localized_string_id::CELL_GAME_DATA_EXIT_BROKEN: return tr("There has been an error!\n\nPlease remove the game data for this title.", "Game Error"); case localized_string_id::CELL_HDD_GAME_EXIT_BROKEN: return tr("There has been an error!\n\nPlease reinstall the HDD boot game.", "Game Error"); + case localized_string_id::CELL_HDD_GAME_CHECK_NOSPACE: return tr("Not enough space to create HDD boot game.\nSpace Needed: %0 KB", "HDD Game Check Error").arg(std::forward(args)...); + case localized_string_id::CELL_HDD_GAME_CHECK_BROKEN: return tr("HDD boot game %0 is corrupt!", "HDD Game Check Error").arg(std::forward(args)...); + case localized_string_id::CELL_HDD_GAME_CHECK_NODATA: return tr("HDD boot game %0 could not be found!", "HDD Game Check Error").arg(std::forward(args)...); + case localized_string_id::CELL_HDD_GAME_CHECK_INVALID: return tr("Error: %0", "HDD Game Check Error").arg(std::forward(args)...); + case localized_string_id::CELL_GAMEDATA_CHECK_NOSPACE: return tr("Not enough space to create game data.\nSpace Needed: %0 KB", "Gamedata Check Error").arg(std::forward(args)...); + case localized_string_id::CELL_GAMEDATA_CHECK_BROKEN: return tr("The game data in %0 is corrupt!", "Gamedata Check Error").arg(std::forward(args)...); + case localized_string_id::CELL_GAMEDATA_CHECK_NODATA: return tr("The game data in %0 could not be found!", "Gamedata Check Error").arg(std::forward(args)...); + case localized_string_id::CELL_GAMEDATA_CHECK_INVALID: return tr("Error: %0", "Gamedata Check Error").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_80010001: return tr("The resource is temporarily unavailable.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_80010002: return tr("Invalid argument or flag.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_80010003: return tr("The feature is not yet implemented.\n(%0)", "Error code").arg(std::forward(args)...);