diff --git a/Core/HLE/sceUmd.cpp b/Core/HLE/sceUmd.cpp index a0d16fe6dcab..cffc283707b3 100644 --- a/Core/HLE/sceUmd.cpp +++ b/Core/HLE/sceUmd.cpp @@ -487,12 +487,13 @@ static u32 sceUmdGetErrorStat() void __UmdReplace(const Path &filepath) { std::string error = ""; - if (!UmdReplace(filepath, error)) { + FileLoader *fileLoader; + if (!UmdReplace(filepath, &fileLoader, error)) { ERROR_LOG(SCEIO, "UMD Replace failed: %s", error.c_str()); return; } - Achievements::ChangeUMD(filepath); + Achievements::ChangeUMD(filepath, fileLoader); UMDInserted = false; // Wake any threads waiting for the disc to be removed. diff --git a/Core/Loaders.cpp b/Core/Loaders.cpp index 1da31122b530..06fd5b57d120 100644 --- a/Core/Loaders.cpp +++ b/Core/Loaders.cpp @@ -385,7 +385,7 @@ bool LoadFile(FileLoader **fileLoaderPtr, std::string *error_string) { return false; } -bool UmdReplace(const Path &filepath, std::string &error) { +bool UmdReplace(const Path &filepath, FileLoader **fileLoader, std::string &error) { IFileSystem *currentUMD = pspFileSystem.GetSystem("disc0:"); if (!currentUMD) { @@ -404,6 +404,8 @@ bool UmdReplace(const Path &filepath, std::string &error) { loadedFile = ResolveFileLoaderTarget(loadedFile); + *fileLoader = loadedFile; + std::string errorString; IdentifiedFileType type = Identify_File(loadedFile, &errorString); @@ -415,7 +417,6 @@ bool UmdReplace(const Path &filepath, std::string &error) { error = "reinit memory failed"; return false; } - break; default: error = "Unsupported file type: " + std::to_string((int)type) + " " + errorString; diff --git a/Core/Loaders.h b/Core/Loaders.h index 2bef2858f502..5bc90d3e2c4e 100644 --- a/Core/Loaders.h +++ b/Core/Loaders.h @@ -157,4 +157,4 @@ void RegisterFileLoaderFactory(std::string prefix, std::unique_ptr void *{ + if (!g_blockDevice) { + ERROR_LOG(ACHIEVEMENTS, "No block device"); + return nullptr; + } + + return (void *) new FileContext{ g_blockDevice, 0 }; + }; + rc_filereader.seek = [](void *file_handle, int64_t offset, int origin) { + FileContext *ctx = (FileContext *)file_handle; + switch (origin) { + case SEEK_SET: ctx->seekPos = offset; break; + case SEEK_END: ctx->seekPos = ctx->bd->GetBlockSize() * ctx->bd->GetNumBlocks() + offset; break; + case SEEK_CUR: ctx->seekPos += offset; break; + default: break; + } + }; + rc_filereader.tell = [](void *file_handle) -> int64_t { + return ((FileContext *)file_handle)->seekPos; + }; + rc_filereader.read = [](void *file_handle, void *buffer, size_t requested_bytes) -> size_t { + FileContext *ctx = (FileContext *)file_handle; + + int blockSize = ctx->bd->GetBlockSize(); + + int64_t offset = ctx->seekPos; + int64_t endOffset = ctx->seekPos + requested_bytes; + int firstBlock = offset / blockSize; + int afterLastBlock = (endOffset + blockSize - 1) / blockSize; + int numBlocks = afterLastBlock - firstBlock; + // This is suboptimal, but good enough since we're not doing a lot of accesses. + uint8_t *buf = new uint8_t[numBlocks * blockSize]; + bool success = ctx->bd->ReadBlocks(firstBlock, numBlocks, (u8 *)buf); + if (success) { + int64_t firstOffset = firstBlock * blockSize; + memcpy(buffer, buf + (offset - firstOffset), requested_bytes); + ctx->seekPos += requested_bytes; + delete[] buf; + return requested_bytes; + } else { + delete[] buf; + ERROR_LOG(ACHIEVEMENTS, "Block device load fail"); + return 0; + } + }; + rc_filereader.close = [](void *file_handle) { + FileContext *ctx = (FileContext *)file_handle; + delete ctx->bd; + delete ctx; + }; + rc_hash_init_custom_filereader(&rc_filereader); + rc_hash_init_default_cdreader(); + TryLoginByToken(true); } @@ -723,13 +783,6 @@ void identify_and_load_callback(int result, const char *error_message, rc_client g_isIdentifying = false; } -struct FileContext { - BlockDevice *bd; - int64_t seekPos; -}; - -static BlockDevice *g_blockDevice; - bool IsReadyToStart() { return !g_isLoggingIn; } @@ -770,58 +823,6 @@ void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoad return; } - rc_hash_filereader rc_filereader; - rc_filereader.open = [](const char *utf8Path) -> void * { - if (!g_blockDevice) { - ERROR_LOG(ACHIEVEMENTS, "No block device"); - return nullptr; - } - - return (void *) new FileContext{ g_blockDevice, 0 }; - }; - rc_filereader.seek = [](void *file_handle, int64_t offset, int origin) { - FileContext *ctx = (FileContext *)file_handle; - switch (origin) { - case SEEK_SET: ctx->seekPos = offset; break; - case SEEK_END: ctx->seekPos = ctx->bd->GetBlockSize() * ctx->bd->GetNumBlocks() + offset; break; - case SEEK_CUR: ctx->seekPos += offset; break; - default: break; - } - }; - rc_filereader.tell = [](void *file_handle) -> int64_t { - return ((FileContext *)file_handle)->seekPos; - }; - rc_filereader.read = [](void *file_handle, void *buffer, size_t requested_bytes) -> size_t { - FileContext *ctx = (FileContext *)file_handle; - - int blockSize = ctx->bd->GetBlockSize(); - - int64_t offset = ctx->seekPos; - int64_t endOffset = ctx->seekPos + requested_bytes; - int firstBlock = offset / blockSize; - int afterLastBlock = (endOffset + blockSize - 1) / blockSize; - int numBlocks = afterLastBlock - firstBlock; - // This is suboptimal, but good enough since we're not doing a lot of accesses. - uint8_t *buf = new uint8_t[numBlocks * blockSize]; - bool success = ctx->bd->ReadBlocks(firstBlock, numBlocks, (u8 *)buf); - if (success) { - int64_t firstOffset = firstBlock * blockSize; - memcpy(buffer, buf + (offset - firstOffset), requested_bytes); - ctx->seekPos += requested_bytes; - delete[] buf; - return requested_bytes; - } else { - delete[] buf; - ERROR_LOG(ACHIEVEMENTS, "Block device load fail"); - return 0; - } - }; - rc_filereader.close = [](void *file_handle) { - FileContext *ctx = (FileContext *)file_handle; - delete ctx->bd; - delete ctx; - }; - // The caller should hold off on executing game code until this turns false, checking with IsBlockingExecution() g_isIdentifying = true; @@ -830,8 +831,6 @@ void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoad rc_client_set_encore_mode_enabled(g_rcClient, g_Config.bAchievementsEncoreMode ? 1 : 0); rc_client_set_unofficial_enabled(g_rcClient, g_Config.bAchievementsUnofficial ? 1 : 0); - rc_hash_init_custom_filereader(&rc_filereader); - rc_hash_init_default_cdreader(); rc_client_begin_identify_and_load_game(g_rcClient, RC_CONSOLE_PSP, path.c_str(), nullptr, 0, &identify_and_load_callback, nullptr); // fclose above will have deleted it. @@ -845,17 +844,47 @@ void UnloadGame() { } void change_media_callback(int result, const char *error_message, rc_client_t *client, void *userdata) { + auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS); NOTICE_LOG(ACHIEVEMENTS, "Change media callback: %d (%s)", result, error_message); g_isIdentifying = false; + + switch (result) { + case RC_OK: + { + // Successful! Later, show a message that we succeeded. + break; + } + case RC_NO_GAME_LOADED: + // The current game does not support achievements. + g_OSD.Show(OSDType::MESSAGE_INFO, ac->T("RetroAchievements are not available for this game"), "", g_RAImageID, 3.0f); + break; + case RC_NO_RESPONSE: + // We lost the internet connection at some point and can't report achievements. + ShowNotLoggedInMessage(); + break; + default: + // Other various errors. + ERROR_LOG(ACHIEVEMENTS, "Failed to identify/load game: %d (%s)", result, error_message); + g_OSD.Show(OSDType::MESSAGE_ERROR, ac->T("Failed to identify game. Achievements will not unlock."), "", g_RAImageID, 6.0f); + break; + } } -void ChangeUMD(const Path &path) { +void ChangeUMD(const Path &path, FileLoader *fileLoader) { if (!IsActive()) { // Nothing to do. return; } - rc_client_begin_change_media(g_rcClient, + g_blockDevice = constructBlockDevice(fileLoader); + if (!g_blockDevice) { + ERROR_LOG(ACHIEVEMENTS, "Failed to construct block device for '%s' - can't identify", path.c_str()); + return; + } + + g_isIdentifying = true; + + rc_client_begin_change_media(g_rcClient, path.c_str(), nullptr, 0, @@ -863,7 +892,8 @@ void ChangeUMD(const Path &path) { nullptr ); - g_isIdentifying = true; + // fclose above will have deleted it. + g_blockDevice = nullptr; } std::set GetActiveChallengeIDs() { diff --git a/Core/RetroAchievements.h b/Core/RetroAchievements.h index 48c1ec9cddda..afd7833fedfc 100644 --- a/Core/RetroAchievements.h +++ b/Core/RetroAchievements.h @@ -108,7 +108,7 @@ void Logout(); bool IsReadyToStart(); void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoader); -void ChangeUMD(const Path &path); // for in-game UMD change +void ChangeUMD(const Path &path, FileLoader *fileLoader); // for in-game UMD change void UnloadGame(); // Call when leaving a game. Statistics GetStatistics();