mirror of
https://github.com/stenzek/duckstation.git
synced 2026-04-05 21:50:48 +00:00
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:
@@ -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++;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -2615,8 +2615,20 @@ bool System::DoState(StateWrapper& sw, bool update_display)
|
||||
CPU::PGXP::Reset();
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
Reference in New Issue
Block a user