Patch: stop Hardcore blocking presentation patches

Reported by EddyOP (60 FPS and Widescreen disabled under RetroAchievements
Hardcore) and diagnosed by Jetup, who found that moving the same lines under a
widescreen heading re-enabled them — ARMSX2 issue #541.

The Hardcore gate from 2f3f28faae allows a group through only if it DECLARES
gsaspectratio or gsinterlacemode. That rule was picked to separate a Skip
Cutscenes cheat from a Widescreen patch living in the same pnach, and it does
separate those two. What it does not survive is the rest of the database.

Measured across the shipped 4437 files, the groups it blocked were overwhelmingly
not cheats:

    305  50 FPS
    177  60 FPS
    168  50/60 FPS
    102  Remove Blackbars      <- a widescreen patch
    100  NTSC Mode
     31  480p Mode

Almost every real presentation patch changes the picture by writing EE memory and
declares nothing, so the declaration test caught the tail and missed the bulk.
The original commit sampled Car select and Auto-activate analogs and concluded
the blocked set read like cheats; across the whole database it does not.

So the name is now consulted too, matched case-insensitively against the labels
the database actually uses. Matched on the EFFECT rather than the verb — Remove
Blur, Disable Blur and No Blur are the same patch, and listing verbs caught one
spelling while blocking the others.

After: 4673 groups stay by declaration, 1206 more stay by name, 495 remain
blocked. What still blocks reads the way it should — Adjusted triggers
sensitivity, Trigger control mappings, GT3 Chase Camera, and Skip Cutscenes
itself, which is the case the gate was written for.

An unrecognised name still fails CLOSED, which is the safe direction: a new cheat
is blocked by default and only an understood presentation class is let through.

★ Frame-rate patches are the judgement call here. They are in the allowlist
because they are what was reported broken and desktop PCSX2 permits them, but
they are the entry RetroAchievements is most likely to object to. If RA rules
them out, deleting three strings from ALLOWED is the whole change.
This commit is contained in:
jpolo1224
2026-08-21 12:16:27 -04:00
parent 66aeaaeb96
commit 8c0ae12398
+56
View File
@@ -681,11 +681,67 @@ void Patch::ReloadEnabledLists()
// [Skip Cutscenes] as a patch, in the same pnach as its [Widescreen 16:9], so location cannot
// tell them apart. And "no memory writes" cannot either: the widescreen group writes EE memory
// too. Declared intent is the only thing that separates them.
/// Does this group's NAME describe a presentation change rather than a gameplay one?
///
/// ★ Needed because "declares gsaspectratio or gsinterlacemode" is not the line it looks like.
///
/// That rule was chosen to separate a Skip Cutscenes cheat from a Widescreen patch shipping in
/// the same pnach, and it does. But measured across the whole shipped database the groups it
/// blocks are overwhelmingly NOT cheats: 305 "50 FPS", 177 "60 FPS", 168 "50/60 FPS", 102
/// "Remove Blackbars" (a widescreen patch), 100 "NTSC Mode", 31 "480p Mode", and the blur and
/// ghosting removers. Almost every real presentation patch changes the picture by writing EE
/// memory and declares nothing, so the declaration test caught the tail and missed the bulk —
/// reported by EddyOP and Jetup, who found that moving lines under a widescreen heading was
/// enough to re-enable them.
///
/// Names are matched case-insensitively as substrings, against the labels the community patch
/// database actually uses. This is deliberately a list of PRESENTATION classes rather than a
/// blocklist of cheats: an unknown name still fails closed, which is the safe direction.
static bool IsPresentationPatchName(const std::string& name)
{
if (name.empty())
return false;
std::string lower(name);
std::transform(lower.begin(), lower.end(), lower.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
static constexpr const std::string_view ALLOWED[] = {
// Aspect / framing
"widescreen", "16:9", "16/9", "blackbar", "black bar", "letterbox",
// Scan-out
"interlac", "ntsc mode", "pal mode", "480p", "progressive", "scan mode",
// Frame rate. Contentious elsewhere, but it is what was reported as broken, it is
// allowed by desktop PCSX2, and it changes how the picture is delivered rather than
// what the game will let the player do.
"fps", "frame rate", "framerate",
// Post-processing the game applies to its own output. Matched on the EFFECT rather than
// the verb: the database says Remove Blur, Disable Blur and No Blur for the same thing,
// and listing verbs meant catching one spelling and blocking the others.
"blur", "bloom", "ghosting", "dither", "noise filter", "depth of field", " dof",
"color filter", "colour filter", "brown filter", "grain",
// HUD repositioning ships alongside widescreen and is meaningless without it.
"correct hud", "hud fix", "fix hud",
};
for (const std::string_view a : ALLOWED)
{
if (lower.find(a) != std::string::npos)
return true;
}
return false;
}
static bool IsHardcoreSafePatchGroup(const Patch::PatchGroup& p)
{
if (p.override_aspect_ratio.has_value() || p.override_interlace_mode.has_value())
return true;
// Named as a presentation change. See IsPresentationPatchName for why the declaration test
// alone is not sufficient.
if (IsPresentationPatchName(p.name))
return true;
// Nothing declared and nothing written is inert, so it costs nothing to allow.
return p.patches.empty() && p.dpatches.empty();
}