ParamSFO: make GenerateFakeID independent of char signedness

The fake disc ID homebrew gets when it has no PARAM.SFO is built from the sum of
the bytes of its folder name, summed through a plain char - which is signed on
x86 and unsigned on ARM. So the same homebrew folder produced one ID on Windows
and a different one on Android, quietly splitting its savestates and per-game
config between platforms. Sum through unsigned char, which is what the ARM
builds (Android, iOS, Apple Silicon) already did.

Uppercasing is now explicit and ASCII-only rather than toupper(). Passing a
negative char to toupper() is undefined and trips MSVC's debug CRT assert, so a
folder with a non-ASCII name could stop a debug build dead, and what it did with
bytes above 0x7F otherwise depended on the locale.

ASCII folder names - very nearly all of them - produce exactly the same ID as
before. Non-ASCII ones change on the signed-char platforms, to what the
unsigned-char ones were already generating.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
This commit is contained in:
Henrik Rydgård
2026-08-19 19:19:06 +02:00
co-authored by Claude Opus 5
parent ec364e2cd1
commit 90a178aeb8
+8 -4
View File
@@ -369,14 +369,18 @@ std::string ParamSFOData::GenerateFakeID(const Path &filename) const {
std::string file = path.GetFilename();
// Deliberately byte-wise and ASCII-only. Filenames are UTF-8, and a plain char is signed on x86
// and unsigned on ARM - so summing chars directly gave Windows and Android different IDs for the
// same non-ASCII folder name, and toupper() on a negative value trips MSVC's debug CRT. ASCII
// names, which is very nearly all of them, produce exactly the same ID as before either way.
int sumOfAllLetters = 0;
for (char &c : file) {
sumOfAllLetters += c;
sumOfAllLetters += (unsigned char)c;
// Get rid of some garbage characters than can arise when opening content URIs. Well, I've only seen '%', but...
if (strchr("%() []", c) != nullptr) {
if (c && strchr("%() []", c) != nullptr) {
c = 'X';
} else {
c = toupper(c);
} else if (c >= 'a' && c <= 'z') {
c = c - 'a' + 'A';
}
}