From fc1d984b202cefd943839a3b6ea2ec033c0e7c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 3 Apr 2024 16:21:31 +0200 Subject: [PATCH] Update rcheevos, switch back to upstream. Avoid using rcheevos' hash.c, by computing game hashes ourselves. --- .gitmodules | 2 +- Core/FileSystems/FileSystem.h | 4 + Core/FileSystems/ISOFileSystem.h | 2 +- Core/RetroAchievements.cpp | 172 ++++++++++---------- android/jni/Android.mk | 2 - ext/rcheevos | 2 +- ext/rcheevos-build/CMakeLists.txt | 4 +- ext/rcheevos-build/rcheevos.vcxproj | 2 - ext/rcheevos-build/rcheevos.vcxproj.filters | 24 +-- libretro/Makefile.common | 2 - 10 files changed, 105 insertions(+), 111 deletions(-) diff --git a/.gitmodules b/.gitmodules index 8a10fc7b12..17fee96935 100644 --- a/.gitmodules +++ b/.gitmodules @@ -46,7 +46,7 @@ url = https://github.com/google/cpu_features.git [submodule "ext/rcheevos"] path = ext/rcheevos - url = https://github.com/hrydgard/rcheevos.git + url = https://github.com/RetroAchievements/rcheevos.git [submodule "ext/naett"] path = ext/naett url = https://github.com/erkkah/naett.git diff --git a/Core/FileSystems/FileSystem.h b/Core/FileSystems/FileSystem.h index e98fb36300..693c1a55d9 100644 --- a/Core/FileSystems/FileSystem.h +++ b/Core/FileSystems/FileSystem.h @@ -83,6 +83,10 @@ public: class SequentialHandleAllocator : public IHandleAllocator { public: SequentialHandleAllocator() : handle_(1) {} + + SequentialHandleAllocator(SequentialHandleAllocator &) = delete; + void operator =(SequentialHandleAllocator &) = delete; + u32 GetNewHandle() override { u32 res = handle_++; if (handle_ < 0) { diff --git a/Core/FileSystems/ISOFileSystem.h b/Core/FileSystems/ISOFileSystem.h index be613bee93..668c4d7174 100644 --- a/Core/FileSystems/ISOFileSystem.h +++ b/Core/FileSystems/ISOFileSystem.h @@ -87,7 +87,7 @@ private: u32 openSize; }; - typedef std::map EntryMap; + typedef std::map EntryMap; EntryMap entries; IHandleAllocator *hAlloc; TreeEntry *treeroot; diff --git a/Core/RetroAchievements.cpp b/Core/RetroAchievements.cpp index 5ed7ee50e8..835cf88ae8 100644 --- a/Core/RetroAchievements.cpp +++ b/Core/RetroAchievements.cpp @@ -4,6 +4,14 @@ // Derived from Duckstation's RetroAchievements implementation by stenzek as can be seen // above, relicensed to GPL 2.0. +// Actually after the rc_client rewrite, barely anything of that remains. + +// The PSP hash function: +// md5_init +// md5_hash(PSP_GAME/PARAM.SFO) +// md5_hash(PSP_GAME/EBOOT.BIN) +// hash = md5_finalize() + #include #include #include @@ -23,11 +31,10 @@ #include "ext/rcheevos/include/rc_api_runtime.h" #include "ext/rcheevos/include/rc_api_user.h" #include "ext/rcheevos/include/rc_url.h" -#include "ext/rcheevos/include/rc_hash.h" -#include "ext/rcheevos/src/rhash/md5.h" #include "ext/rapidjson/include/rapidjson/document.h" +#include "Common/Crypto/md5.h" #include "Common/Log.h" #include "Common/File/Path.h" #include "Common/File/FileUtil.h" @@ -51,8 +58,59 @@ #include "Core/ELF/ParamSFO.h" #include "Core/System.h" #include "Core/FileSystems/MetaFileSystem.h" +#include "Core/FileSystems/ISOFileSystem.h" #include "Core/RetroAchievements.h" +static bool HashISOFile(ISOFileSystem *fs, const std::string filename, md5_context *md5) { + int handle = fs->OpenFile(filename, FILEACCESS_READ); + if (handle < 0) { + return false; + } + + uint32_t sz = fs->SeekFile(handle, 0, FILEMOVE_END); + fs->SeekFile(handle, 0, FILEMOVE_BEGIN); + if (!sz) { + return false; + } + + std::unique_ptr buffer(new uint8_t[sz]); + if (fs->ReadFile(handle, buffer.get(), sz) != sz) { + return false; + } + fs->CloseFile(handle); + + ppsspp_md5_update(md5, buffer.get(), sz); + return true; +} + +// Consumes the blockDevice. +std::string ComputePSPHash(BlockDevice *blockDevice) { + md5_context md5; + ppsspp_md5_starts(&md5); + + SequentialHandleAllocator alloc; + { + std::unique_ptr fs(new ISOFileSystem(&alloc, blockDevice)); + if (!HashISOFile(fs.get(), "PSP_GAME/PARAM.SFO", &md5)) { + return std::string(); + } + if (!HashISOFile(fs.get(), "PSP_GAME/SYSDIR/EBOOT.BIN", &md5)) { + return std::string(); + } + } + + uint8_t digest[16]; + ppsspp_md5_finish(&md5, digest); + + char hashStr[33]; + /* NOTE: sizeof(hash) is 4 because it's still treated like a pointer, despite specifying a size */ + snprintf(hashStr, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], + digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15] + ); + return std::string(hashStr); +} + static inline const char *DeNull(const char *ptr) { return ptr ? ptr : ""; } @@ -87,12 +145,6 @@ static rc_client_t *g_rcClient; static const std::string g_RAImageID = "I_RETROACHIEVEMENTS_LOGO"; constexpr double LOGIN_ATTEMPT_INTERVAL_S = 10.0; -struct FileContext { - BlockDevice *bd; - int64_t seekPos; -}; -static BlockDevice *g_blockDevice; - #define PSP_MEMORY_OFFSET 0x08000000 static void TryLoginByToken(bool isInitialAttempt); @@ -431,60 +483,6 @@ void Initialize() { rc_client_set_event_handler(g_rcClient, event_handler_callback); - 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; - }; - rc_hash_init_custom_filereader(&rc_filereader); - rc_hash_init_default_cdreader(); - TryLoginByToken(true); } @@ -831,27 +829,31 @@ void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoad return; } - _dbg_assert_(!g_blockDevice); - - // TODO: Fish the block device out of the loading process somewhere else. Though, probably easier to just do it here. - g_blockDevice = constructBlockDevice(fileLoader); - if (!g_blockDevice) { - ERROR_LOG(ACHIEVEMENTS, "Failed to construct block device for '%s' - can't identify", path.c_str()); - return; - } - // The caller should hold off on executing game code until this turns false, checking with IsBlockingExecution() g_isIdentifying = true; + // TODO: Fish the block device out of the loading process somewhere else. Though, probably easier to just do it here. + { + BlockDevice *blockDevice(constructBlockDevice(fileLoader)); + if (!blockDevice) { + ERROR_LOG(ACHIEVEMENTS, "Failed to construct block device for '%s' - can't identify", path.c_str()); + g_isIdentifying = false; + return; + } + + // This consumes the blockDevice. + s_game_hash = ComputePSPHash(blockDevice); + if (!s_game_hash.empty()) { + INFO_LOG(ACHIEVEMENTS, "Hash: %s", s_game_hash.c_str()); + } + } + // Apply pre-load settings. rc_client_set_hardcore_enabled(g_rcClient, g_Config.bAchievementsChallengeMode ? 1 : 0); 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_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. - g_blockDevice = nullptr; + rc_client_begin_load_game(g_rcClient, s_game_hash.c_str(), &identify_and_load_callback, nullptr); } void UnloadGame() { @@ -893,24 +895,26 @@ void ChangeUMD(const Path &path, FileLoader *fileLoader) { return; } - g_blockDevice = constructBlockDevice(fileLoader); - if (!g_blockDevice) { + BlockDevice *blockDevice = constructBlockDevice(fileLoader); + if (!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, + // This consumes the blockDevice. + s_game_hash = ComputePSPHash(blockDevice); + if (s_game_hash.empty()) { + ERROR_LOG(ACHIEVEMENTS, "Failed to hash - can't identify"); + return; + } + + rc_client_begin_change_media_from_hash(g_rcClient, + s_game_hash.c_str(), &change_media_callback, nullptr ); - - // fclose above will have deleted it. - g_blockDevice = nullptr; } std::set GetActiveChallengeIDs() { diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 32e53bf599..315e51f581 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -132,8 +132,6 @@ RCHEEVOS_FILES := \ ${SRC}/ext/rcheevos/src/rcheevos/runtime_progress.c \ ${SRC}/ext/rcheevos/src/rcheevos/trigger.c \ ${SRC}/ext/rcheevos/src/rcheevos/value.c \ - ${SRC}/ext/rcheevos/src/rhash/cdreader.c \ - ${SRC}/ext/rcheevos/src/rhash/hash.c \ ${SRC}/ext/rcheevos/src/rhash/md5.c ifeq ($(TARGET_ARCH_ABI),arm64-v8a) diff --git a/ext/rcheevos b/ext/rcheevos index 88a671e309..a6cdbb4a52 160000 --- a/ext/rcheevos +++ b/ext/rcheevos @@ -1 +1 @@ -Subproject commit 88a671e3091b8a02cdda136d4de0345524fd2387 +Subproject commit a6cdbb4a529d85b74777597fcff037dde7bef66b diff --git a/ext/rcheevos-build/CMakeLists.txt b/ext/rcheevos-build/CMakeLists.txt index 5638df3750..c72c1493b2 100644 --- a/ext/rcheevos-build/CMakeLists.txt +++ b/ext/rcheevos-build/CMakeLists.txt @@ -35,9 +35,7 @@ set(ALL_SOURCE_FILES ${SRC_DIR}/rcheevos/runtime_progress.c ${SRC_DIR}/rcheevos/trigger.c ${SRC_DIR}/rcheevos/value.c - # rhash - ${SRC_DIR}/rhash/cdreader.c - ${SRC_DIR}/rhash/hash.c + # rhash (still need just md5) ${SRC_DIR}/rhash/md5.c ${SRC_DIR}/rhash/md5.h ) diff --git a/ext/rcheevos-build/rcheevos.vcxproj b/ext/rcheevos-build/rcheevos.vcxproj index 70af3d7c65..79b363f0d1 100644 --- a/ext/rcheevos-build/rcheevos.vcxproj +++ b/ext/rcheevos-build/rcheevos.vcxproj @@ -57,8 +57,6 @@ - - diff --git a/ext/rcheevos-build/rcheevos.vcxproj.filters b/ext/rcheevos-build/rcheevos.vcxproj.filters index 6151abfe7e..ecfebb17c1 100644 --- a/ext/rcheevos-build/rcheevos.vcxproj.filters +++ b/ext/rcheevos-build/rcheevos.vcxproj.filters @@ -7,12 +7,12 @@ {0acc1fc7-94ca-4eee-967e-5a64a884dbf1} - - {55ec06aa-6d34-4edf-8b0b-f93f93a064b4} - {9e049d1f-4b83-4aa5-89f3-01a42e1773e2} + + {e6d16c54-0892-4ed2-8b51-ab2f5bd8e2d3} + @@ -72,15 +72,6 @@ rcheevos - - rhash - - - rhash - - - rhash - rcheevos @@ -90,6 +81,9 @@ rcheevos + + rhash + @@ -137,9 +131,6 @@ rcheevos - - rhash - include @@ -155,5 +146,8 @@ include + + rhash + \ No newline at end of file diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 55a4a9860b..117846e66e 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -222,8 +222,6 @@ SOURCES_C += \ $(EXTDIR)/rcheevos/src/rcheevos/runtime_progress.c \ $(EXTDIR)/rcheevos/src/rcheevos/trigger.c \ $(EXTDIR)/rcheevos/src/rcheevos/value.c \ - $(EXTDIR)/rcheevos/src/rhash/cdreader.c \ - $(EXTDIR)/rcheevos/src/rhash/hash.c \ $(EXTDIR)/rcheevos/src/rhash/md5.c COREFLAGS += -DSTACK_LINE_READER_BUFFER_SIZE=1024