Update rcheevos, switch back to upstream. Avoid using rcheevos' hash.c, by computing game hashes ourselves.

This commit is contained in:
Henrik Rydgård 2024-04-03 16:21:31 +02:00
parent 62b1450414
commit fc1d984b20
10 changed files with 105 additions and 111 deletions

2
.gitmodules vendored
View File

@ -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

View File

@ -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) {

View File

@ -87,7 +87,7 @@ private:
u32 openSize;
};
typedef std::map<u32,OpenFileEntry> EntryMap;
typedef std::map<u32, OpenFileEntry> EntryMap;
EntryMap entries;
IHandleAllocator *hAlloc;
TreeEntry *treeroot;

View File

@ -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 <algorithm>
#include <atomic>
#include <cstdarg>
@ -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<uint8_t[]> 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<ISOFileSystem> 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<uint32_t> GetActiveChallengeIDs() {

View File

@ -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)

@ -1 +1 @@
Subproject commit 88a671e3091b8a02cdda136d4de0345524fd2387
Subproject commit a6cdbb4a529d85b74777597fcff037dde7bef66b

View File

@ -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
)

View File

@ -57,8 +57,6 @@
<ClCompile Include="..\rcheevos\src\rc_client.c" />
<ClCompile Include="..\rcheevos\src\rc_compat.c" />
<ClCompile Include="..\rcheevos\src\rc_util.c" />
<ClCompile Include="..\rcheevos\src\rhash\cdreader.c" />
<ClCompile Include="..\rcheevos\src\rhash\hash.c" />
<ClCompile Include="..\rcheevos\src\rhash\md5.c" />
</ItemGroup>
<ItemGroup>

View File

@ -7,12 +7,12 @@
<Filter Include="rcheevos">
<UniqueIdentifier>{0acc1fc7-94ca-4eee-967e-5a64a884dbf1}</UniqueIdentifier>
</Filter>
<Filter Include="rhash">
<UniqueIdentifier>{55ec06aa-6d34-4edf-8b0b-f93f93a064b4}</UniqueIdentifier>
</Filter>
<Filter Include="include">
<UniqueIdentifier>{9e049d1f-4b83-4aa5-89f3-01a42e1773e2}</UniqueIdentifier>
</Filter>
<Filter Include="rhash">
<UniqueIdentifier>{e6d16c54-0892-4ed2-8b51-ab2f5bd8e2d3}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\rcheevos\src\rapi\rc_api_common.c">
@ -72,15 +72,6 @@
<ClCompile Include="..\rcheevos\src\rcheevos\value.c">
<Filter>rcheevos</Filter>
</ClCompile>
<ClCompile Include="..\rcheevos\src\rhash\cdreader.c">
<Filter>rhash</Filter>
</ClCompile>
<ClCompile Include="..\rcheevos\src\rhash\hash.c">
<Filter>rhash</Filter>
</ClCompile>
<ClCompile Include="..\rcheevos\src\rhash\md5.c">
<Filter>rhash</Filter>
</ClCompile>
<ClCompile Include="..\rcheevos\src\rc_client.c">
<Filter>rcheevos</Filter>
</ClCompile>
@ -90,6 +81,9 @@
<ClCompile Include="..\rcheevos\src\rc_util.c">
<Filter>rcheevos</Filter>
</ClCompile>
<ClCompile Include="..\rcheevos\src\rhash\md5.c">
<Filter>rhash</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\rcheevos\src\rapi\rc_api_common.h">
@ -137,9 +131,6 @@
<ClInclude Include="..\rcheevos\src\rcheevos\rc_validate.h">
<Filter>rcheevos</Filter>
</ClInclude>
<ClInclude Include="..\rcheevos\src\rhash\md5.h">
<Filter>rhash</Filter>
</ClInclude>
<ClInclude Include="..\rcheevos\include\rc_client.h">
<Filter>include</Filter>
</ClInclude>
@ -155,5 +146,8 @@
<ClInclude Include="..\rcheevos\include\rc_util.h">
<Filter>include</Filter>
</ClInclude>
<ClInclude Include="..\rcheevos\src\rhash\md5.h">
<Filter>rhash</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -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