Skip to content

Commit 443bdef

Browse files
authored
Merge pull request #19096 from hrydgard/retroachievements-for-homebrew
RetroAchievements: Add support for hashing homebrew
2 parents 020b539 + 167faba commit 443bdef

File tree

2 files changed

+45
-14
lines changed

2 files changed

+45
-14
lines changed

Core/Config.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -1519,9 +1519,9 @@ void Config::RemoveRecent(const std::string &file) {
15191519
private_->ResetRecentIsosThread();
15201520
std::lock_guard<std::mutex> guard(private_->recentIsosLock);
15211521

1522-
const auto &filename = File::ResolvePath(file);
1522+
const std::string filename = File::ResolvePath(file);
15231523
auto iter = std::remove_if(recentIsos.begin(), recentIsos.end(), [filename](const auto &str) {
1524-
const auto &recent = File::ResolvePath(str);
1524+
const std::string recent = File::ResolvePath(str);
15251525
return filename == recent;
15261526
});
15271527
// remove_if is weird.

Core/RetroAchievements.cpp

+43-12
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,19 @@ static bool HashISOFile(ISOFileSystem *fs, const std::string filename, md5_conte
9191
return true;
9292
}
9393

94+
static std::string FormatRCheevosMD5(uint8_t digest[16]) {
95+
char hashStr[33];
96+
/* NOTE: sizeof(hash) is 4 because it's still treated like a pointer, despite specifying a size */
97+
snprintf(hashStr, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
98+
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
99+
digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]
100+
);
101+
return std::string(hashStr);
102+
}
103+
94104
// Consumes the blockDevice.
95105
// If failed, returns an empty string, otherwise a 32-character string with the hash in hex format.
96-
static std::string ComputePSPHash(BlockDevice *blockDevice) {
106+
static std::string ComputePSPISOHash(BlockDevice *blockDevice) {
97107
md5_context md5;
98108
ppsspp_md5_starts(&md5);
99109

@@ -111,14 +121,23 @@ static std::string ComputePSPHash(BlockDevice *blockDevice) {
111121

112122
uint8_t digest[16];
113123
ppsspp_md5_finish(&md5, digest);
124+
return FormatRCheevosMD5(digest);
125+
}
114126

115-
char hashStr[33];
116-
/* NOTE: sizeof(hash) is 4 because it's still treated like a pointer, despite specifying a size */
117-
snprintf(hashStr, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
118-
digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
119-
digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]
120-
);
121-
return std::string(hashStr);
127+
static std::string ComputePSPHomebrewHash(FileLoader *fileLoader) {
128+
md5_context md5;
129+
ppsspp_md5_starts(&md5);
130+
131+
// Cap the data we read to 64MB (MAX_BUFFER_SIZE in rcheevos' hash.c), and hash that.
132+
std::vector<uint8_t> buffer;
133+
size_t fileSize = std::min((s64)(1024 * 1024 * 64), fileLoader->FileSize());
134+
buffer.resize(fileSize);
135+
fileLoader->ReadAt(0, fileSize, buffer.data(), FileLoader::Flags::NONE);
136+
ppsspp_md5_update(&md5, buffer.data(), (int)buffer.size());
137+
138+
uint8_t digest[16];
139+
ppsspp_md5_finish(&md5, digest);
140+
return FormatRCheevosMD5(digest);
122141
}
123142

124143
static inline const char *DeNull(const char *ptr) {
@@ -927,11 +946,17 @@ bool IsReadyToStart() {
927946
}
928947

929948
void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoader) {
949+
bool homebrew = false;
930950
switch (fileType) {
931951
case IdentifiedFileType::PSP_ISO:
932952
case IdentifiedFileType::PSP_ISO_NP:
933953
// These file types are OK.
934954
break;
955+
case IdentifiedFileType::PSP_PBP_DIRECTORY:
956+
// This should be a homebrew, which we now support as well.
957+
// We select the homebrew hashing method.
958+
homebrew = true;
959+
break;
935960
default:
936961
// Other file types are not yet supported.
937962
// TODO: Should we show an OSD popup here?
@@ -957,8 +982,14 @@ void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoad
957982
g_gamePath = path;
958983
g_isIdentifying = true;
959984

960-
// TODO: Fish the block device out of the loading process somewhere else. Though, probably easier to just do it here.
961-
{
985+
if (homebrew) {
986+
// Homebrew hashing method - just hash the eboot.
987+
s_game_hash = ComputePSPHomebrewHash(fileLoader);
988+
} else {
989+
// ISO hashing method.
990+
//
991+
// TODO: Fish the block device out of the loading process somewhere else. Though, probably easier to just do it here,
992+
// we need a temporary blockdevice anyway since it gets consumed by ComputePSPISOHash.
962993
BlockDevice *blockDevice(constructBlockDevice(fileLoader));
963994
if (!blockDevice) {
964995
ERROR_LOG(ACHIEVEMENTS, "Failed to construct block device for '%s' - can't identify", path.c_str());
@@ -967,7 +998,7 @@ void SetGame(const Path &path, IdentifiedFileType fileType, FileLoader *fileLoad
967998
}
968999

9691000
// This consumes the blockDevice.
970-
s_game_hash = ComputePSPHash(blockDevice);
1001+
s_game_hash = ComputePSPISOHash(blockDevice);
9711002
if (!s_game_hash.empty()) {
9721003
INFO_LOG(ACHIEVEMENTS, "Hash: %s", s_game_hash.c_str());
9731004
}
@@ -1031,7 +1062,7 @@ void ChangeUMD(const Path &path, FileLoader *fileLoader) {
10311062
g_isIdentifying = true;
10321063

10331064
// This consumes the blockDevice.
1034-
s_game_hash = ComputePSPHash(blockDevice);
1065+
s_game_hash = ComputePSPISOHash(blockDevice);
10351066
if (s_game_hash.empty()) {
10361067
ERROR_LOG(ACHIEVEMENTS, "Failed to hash - can't identify");
10371068
return;

0 commit comments

Comments
 (0)