From e3ca8bdad58c8f2918ab89c93c5068ec2dfe6de8 Mon Sep 17 00:00:00 2001 From: PugsyMAME <18102600+PugsyMAME@users.noreply.github.com> Date: Thu, 12 Mar 2026 20:04:19 +0000 Subject: [PATCH 1/2] Stealth Cheat Type Support Stealth ASM cheat support for Spyro 3, time will tell if any other game has quite so much protection. Supports upto 256 stealth registers - allowing the change of upto 256 32-bit asm instructions without changing the memory. Example (using stealth registers 0x0e & 0x0f & 0x10): #Stealth Codes (Use Recompiler)\Angle of View Fix 51E1000E 8004F7EC 51E2000E 20020140 51E3000E 200200E0 #Stealth Codes (Use Recompiler)\Widescreen + POV 51E1000F 8004F800 51E2000F 20020140 51E3000F 200200C0 #Stealth Codes (Use Recompiler)\Invincibility:Spyro keeps normal colour 51E10010 80048AAC 51E20010 00441023 51E30010 34020001 --- src/core/cheats.cpp | 37 ++++++++++++++++++++++++++++++++----- src/core/cheats.h | 3 +++ src/core/cpu_recompiler.cpp | 17 +++++++++++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/core/cheats.cpp b/src/core/cheats.cpp index 2ad6458cc..db2b219b6 100644 --- a/src/core/cheats.cpp +++ b/src/core/cheats.cpp @@ -2365,7 +2365,12 @@ std::unique_ptr Cheats::GamesharkCheatCode::Parse(Me return code; } -static std::array cht_register; // Used for D7 ,51 & 52 cheat types +static std::array cht_register; // 0-255 Used for D7 ,51 & 52 cheat types. 256-1023 for stealth cheats + +const std::array& Cheats::GetChtRegister() +{ + return cht_register; +} template NEVER_INLINE static T DoMemoryRead(VirtualMemoryAddress address) @@ -3055,7 +3060,7 @@ void Cheats::GamesharkCheatCode::Apply() const // set // (safety valve) // cht_offset = the index of the individual array to change (so must be 0 to cht_register[cht_reg_no1+1]) - if ((cht_reg_no1 <= (std::size(cht_register) - 4)) && cht_register[cht_reg_no1 + 3] == 0xD0D0 && + if (cht_reg_no1 <= 252 && cht_register[cht_reg_no1 + 3] == 0xD0D0 && cht_register[cht_reg_no1 + 2] > 0 && cht_register[cht_reg_no1 + 1] >= cht_offset) { DoMemoryWrite((cht_register[cht_reg_no1] - cht_register[cht_reg_no1 + 1]) + @@ -3099,7 +3104,7 @@ void Cheats::GamesharkCheatCode::Apply() const // set // (safety valve) // cht_offset = the index of the individual array to change (so must be 0 to cht_register[cht_reg_no1+1]) - if ((cht_reg_no1 <= (std::size(cht_register) - 4)) && cht_register[cht_reg_no1 + 3] == 0xD0D0 && + if (cht_reg_no1 <= 252 && cht_register[cht_reg_no1 + 3] == 0xD0D0 && cht_register[cht_reg_no1 + 2] > 0 && cht_register[cht_reg_no1 + 1] >= cht_offset) { DoMemoryWrite((cht_register[cht_reg_no1] - cht_register[cht_reg_no1 + 1]) + @@ -3173,6 +3178,17 @@ void Cheats::GamesharkCheatCode::Apply() const case 0xCA: // Reg3 = Reg1 >> X cht_register[cht_reg_no3] = cht_register[cht_reg_no1] >> cht_reg_no2; break; + case 0xE1: // Stealth Cheat - PC Address - uses registers 256-511 + cht_register[cht_reg_no1+256] = poke_value; + if (cht_register[1024] < (u32)(cht_reg_no1 + 1)) //Set to highest reg if any Stealth cheat is ever enabled for a game + cht_register[1024] = cht_reg_no1 + 1; + break; + case 0xE2: // Stealth Cheat - Actual Contents of PC Address - uses registers 512-767 + cht_register[cht_reg_no1+512] = poke_value; + break; + case 0xE3: // Stealth Cheat - Stealth Contents of PC Address - uses registers 768-1023 + cht_register[cht_reg_no1+768] = poke_value; + break; // Lots of options exist for expanding into this space default: break; @@ -4391,10 +4407,21 @@ void Cheats::GamesharkCheatCode::ApplyOnDisable() const case InstructionCode::ExtConstantSwap16: case InstructionCode::DelayActivation: // C1 case InstructionCode::ExtConstantWriteIfMatch16: - case InstructionCode::ExtCheatRegisters: index++; break; - + case InstructionCode::ExtCheatRegisters: // 51 + { + const u8 cht_reg_no = Truncate8(inst.first & 0xFFu); + const u8 sub_type = Truncate8((inst.first & 0xFF0000u) >> 16); + if (sub_type == 0xE1) //Stealth Cheat, clear all 3 registers on disable for all three parts + { + cht_register[cht_reg_no + 256] = 0; + cht_register[cht_reg_no + 512] = 0; + cht_register[cht_reg_no + 768] = 0; + } + index++; + break; + } case InstructionCode::ExtConstantForceRange16: case InstructionCode::Slide: case InstructionCode::ExtImprovedSlide: diff --git a/src/core/cheats.h b/src/core/cheats.h index fbb234872..4a77e032f 100644 --- a/src/core/cheats.h +++ b/src/core/cheats.h @@ -7,6 +7,7 @@ #include "types.h" +#include #include #include #include @@ -171,4 +172,6 @@ extern const char* PATCHES_CONFIG_SECTION; extern const char* CHEATS_CONFIG_SECTION; extern const char* PATCH_ENABLE_CONFIG_KEY; +const std::array& GetChtRegister(); + } // namespace Cheats diff --git a/src/core/cpu_recompiler.cpp b/src/core/cpu_recompiler.cpp index 54a3d5ad4..a96589c77 100644 --- a/src/core/cpu_recompiler.cpp +++ b/src/core/cpu_recompiler.cpp @@ -7,6 +7,7 @@ #include "cpu_disasm.h" #include "cpu_pgxp.h" #include "settings.h" +#include "cheats.h" #include "common/assert.h" #include "common/log.h" @@ -1195,6 +1196,22 @@ void CPU::Recompiler::Recompiler::CompileInstruction() UpdateLoadDelay(); return; } + + //Stealth Cheats + const auto& cht_reg = Cheats::GetChtRegister(); + if (cht_reg[1024] > 0) + { + for (int i = 0; i < cht_register[1024]; i++) + { + if (m_current_instruction_pc == cht_reg[i + 256] && inst->bits == cht_reg[i + 512]) + { + // inst is intentionally const - we cast away const only for the single asm changes needed. + const_cast(inst)->bits = cht_reg[i + 768]; + break; + } + } + } + switch (inst->op) { From bf3478d7a61fa886d3278cca9538eb37a51b8710 Mon Sep 17 00:00:00 2001 From: PugsyMAME <18102600+PugsyMAME@users.noreply.github.com> Date: Thu, 12 Mar 2026 20:14:08 +0000 Subject: [PATCH 2/2] Add files via upload --- src/core/cpu_recompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/cpu_recompiler.cpp b/src/core/cpu_recompiler.cpp index a96589c77..660a8b095 100644 --- a/src/core/cpu_recompiler.cpp +++ b/src/core/cpu_recompiler.cpp @@ -1201,7 +1201,7 @@ void CPU::Recompiler::Recompiler::CompileInstruction() const auto& cht_reg = Cheats::GetChtRegister(); if (cht_reg[1024] > 0) { - for (int i = 0; i < cht_register[1024]; i++) + for (unsigned int i = 0; i < cht_reg[1024]; i++) { if (m_current_instruction_pc == cht_reg[i + 256] && inst->bits == cht_reg[i + 512]) {