Cheats: Disable restorable cheats before saving state

And then roll them back after saving.

Stops these cheats being "baked" into the save state, but we still
retain the tainted flag.
This commit is contained in:
Stenzek
2026-04-05 22:37:07 +10:00
parent bf3ae80ddf
commit aecd1a64f1
3 changed files with 74 additions and 8 deletions

View File

@@ -195,7 +195,7 @@ public:
virtual void SetOptionValue(u32 value) = 0;
virtual void Apply() const = 0;
virtual void ApplyOnDisable() const = 0;
virtual void ApplyOnDisable(RollbackLog* rollback_list) const = 0;
protected:
Metadata m_metadata;
@@ -977,7 +977,7 @@ void Cheats::ReloadCheats(bool reload_files, bool reload_enabled_list, bool verb
bool show_disabled_codes)
{
for (const CheatCode* code : s_locals.frame_end_codes)
code->ApplyOnDisable();
code->ApplyOnDisable(nullptr);
// Reload files if cheats or patches are enabled, and they were not previously.
const bool patches_are_enabled = AreAnyPatchesEnabled();
@@ -1172,6 +1172,37 @@ void Cheats::ApplyFrameEndCodes()
code->Apply();
}
Cheats::RollbackLog Cheats::ApplyOnDisableCodes()
{
RollbackLog ret;
for (const CheatCode* code : s_locals.frame_end_codes)
code->ApplyOnDisable(&ret);
return ret;
}
void Cheats::ReapplyOnDisableCodes(const RollbackLog& rollback_list)
{
for (const RollbackEntry& entry : rollback_list)
{
switch (entry.size)
{
case MemoryAccessSize::Byte:
CPU::SafeWriteMemoryByte(entry.address, Truncate8(entry.value));
break;
case MemoryAccessSize::HalfWord:
CPU::SafeWriteMemoryHalfWord(entry.address, Truncate16(entry.value));
break;
case MemoryAccessSize::Word:
CPU::SafeWriteMemoryWord(entry.address, Truncate32(entry.value));
break;
UnreachableCode();
}
}
}
bool Cheats::EnumerateManualCodes(std::function<bool(const std::string& name)> callback)
{
for (const std::unique_ptr<CheatCode>& code : s_locals.cheat_codes)
@@ -2143,7 +2174,7 @@ public:
void SetOptionValue(u32 value) override;
void Apply() const override;
void ApplyOnDisable() const override;
void ApplyOnDisable(RollbackLog* rollback_list) const override;
private:
enum class InstructionCode : u8
@@ -4358,7 +4389,7 @@ void Cheats::GamesharkCheatCode::Apply() const
}
}
void Cheats::GamesharkCheatCode::ApplyOnDisable() const
void Cheats::GamesharkCheatCode::ApplyOnDisable(RollbackLog* rollback_list) const
{
const u32 count = static_cast<u32>(instructions.size());
u32 index = 0;
@@ -4442,7 +4473,12 @@ void Cheats::GamesharkCheatCode::ApplyOnDisable() const
const u16 comparevalue = Truncate16(inst.value32 >> 16);
const u16 newvalue = Truncate16(inst.value32 & 0xFFFFu);
if (value == newvalue)
{
if (rollback_list)
rollback_list->emplace_back(MemoryAccessSize::HalfWord, inst.address, newvalue);
DoMemoryWrite<u16>(inst.address, comparevalue);
}
index++;
}
@@ -4454,7 +4490,12 @@ void Cheats::GamesharkCheatCode::ApplyOnDisable() const
const u8 comparevalue = Truncate8(inst.value16 >> 8);
const u8 newvalue = Truncate8(inst.value16 & 0xFFu);
if (value == newvalue)
{
if (rollback_list)
rollback_list->emplace_back(MemoryAccessSize::Byte, inst.address, newvalue);
DoMemoryWrite<u8>(inst.address, comparevalue);
}
index++;
}

View File

@@ -3,8 +3,6 @@
#pragma once
#include "common/bitfield.h"
#include "types.h"
#include <functional>
@@ -72,6 +70,15 @@ struct CodeInfo
u32 MapOptionNameToValue(const std::string_view opt_name) const;
};
/// Logged memory access for later reverting.
struct RollbackEntry
{
MemoryAccessSize size : 8;
u32 address : 24;
u32 value;
};
using RollbackLog = std::vector<RollbackEntry>;
using CodeInfoList = std::vector<CodeInfo>;
/// Returns the internal identifier for a code type.
@@ -148,6 +155,12 @@ void ApplySettingOverrides();
/// Applies all currently-registered frame end cheat codes.
void ApplyFrameEndCodes();
/// Reverses all reversible codes before creating a save state.
RollbackLog ApplyOnDisableCodes();
/// Reapplies any codes reversed by ApplyOnDisableCodes() after saving a save state.
void ReapplyOnDisableCodes(const RollbackLog& rollback_list);
/// Returns true if cheats are enabled in the current game's configuration.
bool AreCheatsEnabled();

View File

@@ -2615,8 +2615,20 @@ bool System::DoState(StateWrapper& sw, bool update_display)
CPU::PGXP::Reset();
}
if (!sw.DoMarker("Bus") || !Bus::DoState(sw))
return false;
// back up and restore memory affected by cheats when saving state
if (sw.IsReading())
{
if (!sw.DoMarker("Bus") || !Bus::DoState(sw))
return false;
}
else if (sw.IsWriting())
{
const Cheats::RollbackLog cheat_rollback_log = Cheats::ApplyOnDisableCodes();
const bool result = (sw.DoMarker("Bus") && Bus::DoState(sw));
Cheats::ReapplyOnDisableCodes(cheat_rollback_log);
if (!result)
return result;
}
if (!sw.DoMarker("DMA") || !DMA::DoState(sw))
return false;