From 5bfd4b390ca4cfd9daceb9e8feeb395ba7aa16e8 Mon Sep 17 00:00:00 2001 From: Nab <22328803+NABN00B@users.noreply.github.com> Date: Wed, 3 Sep 2025 14:17:55 +0200 Subject: [PATCH 1/2] GameRegion improvements --- Core/ELF/ParamSFO.cpp | 76 ++++++++++++++++++++++++-------------- Core/ELF/ParamSFO.h | 6 ++- Core/RetroAchievements.cpp | 4 +- UI/GameInfoCache.h | 2 +- UI/GameScreen.cpp | 6 +-- 5 files changed, 56 insertions(+), 38 deletions(-) diff --git a/Core/ELF/ParamSFO.cpp b/Core/ELF/ParamSFO.cpp index 8db59097be..6c2e98b8b4 100644 --- a/Core/ELF/ParamSFO.cpp +++ b/Core/ELF/ParamSFO.cpp @@ -358,36 +358,53 @@ std::string ParamSFOData::GenerateFakeID(const Path &filename) const { return fakeID; } -GameRegion DetectGameRegionFromID(std::string_view id_version) { - if (id_version.size() >= 4) { - std::string_view regStr = id_version.substr(0, 4); +GameRegion DetectGameRegionFromID(std::string_view id_full) { + // DISC_ID format consists of a 4-letter categorization followed by a 5-digit catalog number. + if (id_full.size() == 9 || (id_full.size() == 10 && id_full[4] == '-')) { + std::string_view id_letters = id_full.substr(0, 4); + std::string_view id_release_type = id_letters.substr(0, 2); - // Guesswork - switch (regStr[2]) { - case 'E': return GameRegion::EUROPE; break; - case 'U': return GameRegion::USA; break; - case 'J': return GameRegion::JAPAN; break; - case 'H': return GameRegion::HONGKONG; break; - case 'A': return GameRegion::ASIA; break; - case 'K': return GameRegion::KOREA; break; - default: return GameRegion::OTHER; + // Determine the type of release from the first two letters, + // must be one of the following: + // "UC" -> first-party UMD game + // "UL" -> third-party (licensed) UMD game + // "NP" -> PSN digital download game or internal application + if (id_release_type == "UL" || id_release_type == "UC" || id_release_type == "NP") { + // Determine the region from the third letter. + // This isn't super accurate but it's all we have. + switch (id_letters[2]) { + case 'E': return GameRegion::EUROPE; break; + case 'U': return GameRegion::USA; break; + case 'J': return GameRegion::JAPAN; break; + case 'K': return GameRegion::KOREA; break; + case 'A': return GameRegion::ASIA; break; + case 'H': return GameRegion::HONGKONG; break; + default: return id_letters == "NPIA" ? GameRegion::INTERNAL : GameRegion::HOMEBREW ; + } + // The fourth letter could be used to determine the type of product. It isn't useful to us. + // Guesswork of what they could possibly mean: + // 'S' -> full (S)oftware (used by most games) + // 'M' -> (M)edia (typically used by Japanese games) + // 'A' -> (A)pplication + // 'B' -> (B)undle + // 'D' -> (D)emo + // 'G' -> digital download (G)ame + // 'H' -> digital download: game -or- Neo Geo series -or- PlayView series + // 'F' -> digital download: PC Engine series, (F)oreign (English port) + // 'J' -> digital download: PC Engine series, (J)apanese + // 'Z' -> digital download: Minis series, third-party + // 'X' -> e(X)perimental -or- digital download: Minis series, first-party + // 'P' -> (P)re-production + // 'T' -> (T)est + } // Misc patterns + else if (id_letters == "UTST") { + return GameRegion::TEST; + } else if (id_letters == "UMDT") { + return GameRegion::DIAGNOSTIC; } - /* - if (regStr == "NPEZ" || regStr == "NPEG" || regStr == "ULES" || regStr == "UCES" || - regStr == "NPEX") { - region = GameRegion::EUROPE; - } else if (regStr == "NPUG" || regStr == "NPUZ" || regStr == "ULUS" || regStr == "UCUS") { - region = GameRegion::USA; - } else if (regStr == "NPJH" || regStr == "NPJG" || regStr == "ULJM"|| regStr == "ULJS") { - region = GameRegion::JAPAN; - } else if (regStr == "NPHG") { - region = GameRegion::HONGKONG; - } else if (regStr == "UCAS") { - region = GameRegion::CHINA; - }*/ - } else { - return GameRegion::OTHER; } + + return GameRegion::HOMEBREW; } std::string_view GameRegionToString(GameRegion region) { @@ -399,6 +416,9 @@ std::string_view GameRegionToString(GameRegion region) { case GameRegion::ASIA: return "Asia"; case GameRegion::KOREA: return "Korea"; case GameRegion::HOMEBREW: return "Homebrew"; - default: return "Other"; + case GameRegion::INTERNAL: return "Internal application"; + case GameRegion::TEST: return "Test disc"; + case GameRegion::DIAGNOSTIC: return "Diagnostic tool"; + default: return "unknown region"; } } diff --git a/Core/ELF/ParamSFO.h b/Core/ELF/ParamSFO.h index 7316cc26d4..c28ad73cc8 100644 --- a/Core/ELF/ParamSFO.h +++ b/Core/ELF/ParamSFO.h @@ -114,8 +114,10 @@ enum class GameRegion { ASIA, KOREA, COUNT, - HOMEBREW = COUNT, // Like other but we actually know it's homebrew. - OTHER, + HOMEBREW = COUNT, + INTERNAL, + TEST, + DIAGNOSTIC, }; GameRegion DetectGameRegionFromID(std::string_view id_version); diff --git a/Core/RetroAchievements.cpp b/Core/RetroAchievements.cpp index 975072ceb6..cc64ffbdd6 100644 --- a/Core/RetroAchievements.cpp +++ b/Core/RetroAchievements.cpp @@ -954,7 +954,7 @@ void identify_and_load_callback(int result, const char *error_message, rc_client auto ga = GetI18NCategory(I18NCat::GAME); std::string_view regionStr = ga->T(GameRegionToString(region)); std::string title(gameInfo->title); - if (region != GameRegion::OTHER) { + if (region <= GameRegion::HOMEBREW) { title += " ("; title += regionStr; title += ")"; @@ -968,7 +968,7 @@ void identify_and_load_callback(int result, const char *error_message, rc_client auto ga = GetI18NCategory(I18NCat::GAME); std::string_view regionStr = ga->T(GameRegionToString(region)); std::string title(g_paramSFO.GetValueString("TITLE")); - if (region != GameRegion::OTHER) { + if (region <= GameRegion::HOMEBREW) { title += " ("; title += regionStr; title += ")"; diff --git a/UI/GameInfoCache.h b/UI/GameInfoCache.h index 76f7738a5c..1352078a9e 100644 --- a/UI/GameInfoCache.h +++ b/UI/GameInfoCache.h @@ -145,7 +145,7 @@ public: std::string id_version; int disc_total = 0; int disc_number = 0; - GameRegion region = GameRegion::OTHER; + GameRegion region = GameRegion::TEST; IdentifiedFileType fileType; bool hasConfig = false; diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index bd80617822..882cbc19bf 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -392,11 +392,7 @@ ScreenRenderFlags GameScreen::render(ScreenRenderMode mode) { } if (tvRegion_) { - if (info->region == GameRegion::OTHER) { - tvRegion_->SetText(ga->T("Homebrew")); - } else { - tvRegion_->SetText(ga->T(GameRegionToString(info->region))); - } + tvRegion_->SetText(ga->T(GameRegionToString(info->region))); updateMaxY(tvRegion_); } From 3c611355c620264b308817737a849fd9c56dcc54 Mon Sep 17 00:00:00 2001 From: Nab <22328803+NABN00B@users.noreply.github.com> Date: Wed, 3 Sep 2025 17:40:40 +0000 Subject: [PATCH 2/2] Stricter check for 'H' region --- Core/ELF/ParamSFO.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Core/ELF/ParamSFO.cpp b/Core/ELF/ParamSFO.cpp index 6c2e98b8b4..879573a9fd 100644 --- a/Core/ELF/ParamSFO.cpp +++ b/Core/ELF/ParamSFO.cpp @@ -378,8 +378,14 @@ GameRegion DetectGameRegionFromID(std::string_view id_full) { case 'J': return GameRegion::JAPAN; break; case 'K': return GameRegion::KOREA; break; case 'A': return GameRegion::ASIA; break; - case 'H': return GameRegion::HONGKONG; break; - default: return id_letters == "NPIA" ? GameRegion::INTERNAL : GameRegion::HOMEBREW ; + default: + if (id_letters.substr(0, 3) == "NPH") { + return GameRegion::HONGKONG; // All games in this region are PSN. + } else if (id_letters == "NPIA") { + return GameRegion::INTERNAL; + } else { + return GameRegion::HOMEBREW; + } } // The fourth letter could be used to determine the type of product. It isn't useful to us. // Guesswork of what they could possibly mean: