Duckstation/src/core/cheats.h

320 lines
8.5 KiB
C
Raw Normal View History

// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com> and contributors.
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "common/bitfield.h"
#include "types.h"
#include <optional>
#include <string>
#include <vector>
struct CheatCode
{
2020-10-19 15:14:49 +00:00
enum class Type : u8
{
Gameshark,
Count
};
enum class Activation : u8
{
Manual,
EndFrame,
Count,
};
enum class InstructionCode : u8
{
2020-09-28 10:09:14 +00:00
Nop = 0x00,
ConstantWrite8 = 0x30,
ConstantWrite16 = 0x80,
ScratchpadWrite16 = 0x1F,
Increment16 = 0x10,
Decrement16 = 0x11,
Increment8 = 0x20,
Decrement8 = 0x21,
2020-12-06 05:50:58 +00:00
DelayActivation = 0xC1,
SkipIfNotEqual16 = 0xC0,
2020-12-06 05:47:00 +00:00
SkipIfButtonsNotEqual = 0xD5,
SkipIfButtonsEqual = 0xD6,
CompareButtons = 0xD4,
CompareEqual16 = 0xD0,
CompareNotEqual16 = 0xD1,
CompareLess16 = 0xD2,
CompareGreater16 = 0xD3,
CompareEqual8 = 0xE0,
CompareNotEqual8 = 0xE1,
CompareLess8 = 0xE2,
CompareGreater8 = 0xE3,
Slide = 0x50,
MemoryCopy = 0xC2,
ExtImprovedSlide = 0x53,
// Extension opcodes, not present on original GameShark.
ExtConstantWrite32 = 0x90,
ExtScratchpadWrite32 = 0xA5,
ExtCompareEqual32 = 0xA0,
ExtCompareNotEqual32 = 0xA1,
ExtCompareLess32 = 0xA2,
ExtCompareGreater32 = 0xA3,
ExtSkipIfNotEqual32 = 0xA4,
ExtIncrement32 = 0x60,
ExtDecrement32 = 0x61,
ExtConstantWriteIfMatch16 = 0xA6,
ExtConstantWriteIfMatchWithRestore16 = 0xA7,
ExtConstantForceRange8 = 0xF0,
ExtConstantForceRangeLimits16 = 0xF1,
ExtConstantForceRangeRollRound16 = 0xF2,
ExtConstantForceRange16 = 0xF3,
ExtFindAndReplace = 0xF4,
ExtConstantSwap16 = 0xF5,
ExtConstantBitSet8 = 0x31,
ExtConstantBitClear8 = 0x32,
ExtConstantBitSet16 = 0x81,
ExtConstantBitClear16 = 0x82,
ExtConstantBitSet32 = 0x91,
ExtConstantBitClear32 = 0x92,
Added new cheat cheat types: C3, C4, C5, C6, D7&52 C3-C7 are variants of C0 D7 is a BIT based joker to rule them all. It includes the analog sticks (@ggrtk thanks for adding the analog reading capability). Also added the facility of making dual single key joker by varying the amount of time a button is held down. 51 is a complicated beast that I still need to document * C3XXXXXX 00YY - 8-Bit Master Code, if ($XXXXXX) is less than 0xYY poke all 00000000 FFFF following codes for rest of the cheat or until it reaches the 00000000 FFFF line. * C4XXXXXX 00YY - 8-Bit Master Code, if ($XXXXXX) is greater than 0xYY poke all 00000000 FFFF following codes for rest of the cheat or until it reaches the 00000000 FFFF line. * C5XXXXXX YYYY - 16-Bit Master Code, if ($XXXXXX) is less than 0xYYYY poke all 00000000 FFFF following codes for rest of the cheat or until it reaches the 00000000 FFFF line. * C6XXXXXX YYYY - 16-Bit Master Code, if ($XXXXXX) is greater than 0xYYYY poke all 00000000 FFFF following codes for rest of the cheat or until it reaches the 00000000 FFFF line. * D7PQRRRR TTYYYYYY - 24-Bit Universal BIT Joker, OR the hex values to combine into a multi-button joker. Because it is BIT based it is better than D4, D5, D6 or using a D0 joker as you do not need to worry about any other buttons being pressed at the same time and you get both analog sticks for extra functionality. Note if you want to use it just as a enhanced joker just use D7000000 00YYYYYY when the buttons/directions are pressed or D7100000 00YYYYYY when you want to ensure they are not all pressed. QRRRR TT provides the capability of only activating the following codes after the keys have been held in for a set amount of frames. 003C = 60 Frames = 1 Second at 100% Speed YYYYYY = 000001 L2 Button YYYYYY = 000002 R2 Button YYYYYY = 000004 L1 Button YYYYYY = 000008 R1 Button YYYYYY = 000010 Triangle Button YYYYYY = 000020 Circle Button YYYYYY = 000040 X Button YYYYYY = 000080 Square Button YYYYYY = 000100 Select Button YYYYYY = 000200 L3 Button YYYYYY = 000400 R3 Button YYYYYY = 000800 Start Button YYYYYY = 001000 Up (Digital) YYYYYY = 002000 Right (Digital) YYYYYY = 004000 Down (Digital) YYYYYY = 008000 Left (Digital) YYYYYY = 010000 Up (Right Thumb) YYYYYY = 020000 Right (Right Thumb) YYYYYY = 040000 Down (Right Thumb) YYYYYY = 080000 Left (Right Thumb) YYYYYY = 100000 Up (Left Thumb) YYYYYY = 200000 Right (Left Thumb) YYYYYY = 400000 Down (Left Thumb) YYYYYY = 800000 Left (Left Thumb) NOTE: The 0s in the code are reserved for possible future use. TT=Temp Internal Variable 00-FF, 00 will mean it wont be used, if it's not 00 do not use the same value for jokers using different keypress combinations for the same game. P = 0 or 1. 0 = Check ALL YYYYYY Bits are ON 1 = Check ALL YYYYYY Bits are OFF Q = Frame Comparison 0 = Dont do any comparison 1 = Check that the button combination has been held down for exactly RRRR frames. 2 = Check that the button combination has been held down for more than RRRR frames. 3 = Check that the button combination has been held down for less than RRRR frames. 4 = Check that the button combination has been held down for anything but RRRR frames. RRRR = 0000 to FFFF, Frame Comparison Value It will then poke all following codes for rest of cheat 00000000 FFFF or until it reaches the 00000000 FFFF line.
2021-03-03 23:06:08 +00:00
ExtBitCompareButtons = 0xD7,
ExtSkipIfNotLess8 = 0xC3,
ExtSkipIfNotGreater8 = 0xC4,
ExtSkipIfNotLess16 = 0xC5,
ExtSkipIfNotGreater16 = 0xC6,
New F6 Cheat Type (IF,ELSE IF, ELSE structure) Cheat Type F6 lets you perform cheats in IF / ELSE IF / ELSE constructs along with multiple conditions for each one. It supports conditions D0, D1, D2, D3, E0, E1, E2, E3, A0, A1, A2, A3, D7 and two new types that will only be available in the F6 conditions :- E4 & E5 which are bit comparisons on bytes (set and clear). Any of those types which are "block conditionals" like D7 will be treated as a single condition with the other conditions so you wont need to worry about extra 00000000 FFFFs just for them (infact you can drop them completely apart from the last 00000000 FFFF, which you can also drop if you have no trailing cheat lines after the end of the construct. #IF / ELSE IF / ELSE Statement Example with Notes 80001000 00000001 Setup Test Condition 1 80001004 00000002 Setup Test Condition 2 80001008 00000003 Setup Test Condition 3 8000100C 00000004 Setup Test Condition 4 1F = IF, last two digits determine the number of conditions to check F600000x x = 0 All conditions must be true, x = 1 At least One condition must be true F6000000 1F000004 Check the next 4 lines as the conditions for the IF loop to be taken D0001000 00000001 ) D0001004 00000002 )Conditions D0001008 00000003 ) D000100C 00000004 ) 80001010 00005555 Poke(s) 00000000 0000FFFF End of IF Statement F6000000 E15E1F02 E15E1F = ELSE IF (same format as IF) - Can have multiple ELSE IF Blocks or none at all D0001008 00000003 ) D000100C 00000005 )Conditions 80001010 00008888 Poke(s) 00000000 0000FFFF End of ELSE IF Statement F6000000 E15E0000 E15E = ELSE (no need for conditions - this will poke if the others dont) 80001010 0000AAAA Poke(s) 00000000 0000FFFF End of ELSE Statement #Multiple Test Using Joypad Left Thumb Directions, Look at Address 1100 in Debugger F6000000 1F000001 D7020002 00100000 90001100 50205055 90001104 53534552 90001108 20204445 9000110C 20202020 F6000000 E15E1F01 D7020002 00400000 90001100 4E574F44 90001104 45525020 90001108 44455353 9000110C 20202020 F6000000 E15E1F01 D7020002 00800000 90001100 5446454C 90001104 45525020 90001108 44455353 9000110C 20202020 F6000000 E15E1F01 D7020002 00200000 90001100 48474952 90001104 52502054 90001108 45535345 9000110C 20202044 F6000000 E15E0000 90001100 20202020 90001104 20202020 90001108 20202020 9000110C 20202020 00000000 0000FFFF
2021-08-21 14:08:34 +00:00
ExtMultiConditionals = 0xF6,
Added new cheat cheat types: C3, C4, C5, C6, D7&52 C3-C7 are variants of C0 D7 is a BIT based joker to rule them all. It includes the analog sticks (@ggrtk thanks for adding the analog reading capability). Also added the facility of making dual single key joker by varying the amount of time a button is held down. 51 is a complicated beast that I still need to document * C3XXXXXX 00YY - 8-Bit Master Code, if ($XXXXXX) is less than 0xYY poke all 00000000 FFFF following codes for rest of the cheat or until it reaches the 00000000 FFFF line. * C4XXXXXX 00YY - 8-Bit Master Code, if ($XXXXXX) is greater than 0xYY poke all 00000000 FFFF following codes for rest of the cheat or until it reaches the 00000000 FFFF line. * C5XXXXXX YYYY - 16-Bit Master Code, if ($XXXXXX) is less than 0xYYYY poke all 00000000 FFFF following codes for rest of the cheat or until it reaches the 00000000 FFFF line. * C6XXXXXX YYYY - 16-Bit Master Code, if ($XXXXXX) is greater than 0xYYYY poke all 00000000 FFFF following codes for rest of the cheat or until it reaches the 00000000 FFFF line. * D7PQRRRR TTYYYYYY - 24-Bit Universal BIT Joker, OR the hex values to combine into a multi-button joker. Because it is BIT based it is better than D4, D5, D6 or using a D0 joker as you do not need to worry about any other buttons being pressed at the same time and you get both analog sticks for extra functionality. Note if you want to use it just as a enhanced joker just use D7000000 00YYYYYY when the buttons/directions are pressed or D7100000 00YYYYYY when you want to ensure they are not all pressed. QRRRR TT provides the capability of only activating the following codes after the keys have been held in for a set amount of frames. 003C = 60 Frames = 1 Second at 100% Speed YYYYYY = 000001 L2 Button YYYYYY = 000002 R2 Button YYYYYY = 000004 L1 Button YYYYYY = 000008 R1 Button YYYYYY = 000010 Triangle Button YYYYYY = 000020 Circle Button YYYYYY = 000040 X Button YYYYYY = 000080 Square Button YYYYYY = 000100 Select Button YYYYYY = 000200 L3 Button YYYYYY = 000400 R3 Button YYYYYY = 000800 Start Button YYYYYY = 001000 Up (Digital) YYYYYY = 002000 Right (Digital) YYYYYY = 004000 Down (Digital) YYYYYY = 008000 Left (Digital) YYYYYY = 010000 Up (Right Thumb) YYYYYY = 020000 Right (Right Thumb) YYYYYY = 040000 Down (Right Thumb) YYYYYY = 080000 Left (Right Thumb) YYYYYY = 100000 Up (Left Thumb) YYYYYY = 200000 Right (Left Thumb) YYYYYY = 400000 Down (Left Thumb) YYYYYY = 800000 Left (Left Thumb) NOTE: The 0s in the code are reserved for possible future use. TT=Temp Internal Variable 00-FF, 00 will mean it wont be used, if it's not 00 do not use the same value for jokers using different keypress combinations for the same game. P = 0 or 1. 0 = Check ALL YYYYYY Bits are ON 1 = Check ALL YYYYYY Bits are OFF Q = Frame Comparison 0 = Dont do any comparison 1 = Check that the button combination has been held down for exactly RRRR frames. 2 = Check that the button combination has been held down for more than RRRR frames. 3 = Check that the button combination has been held down for less than RRRR frames. 4 = Check that the button combination has been held down for anything but RRRR frames. RRRR = 0000 to FFFF, Frame Comparison Value It will then poke all following codes for rest of cheat 00000000 FFFF or until it reaches the 00000000 FFFF line.
2021-03-03 23:06:08 +00:00
ExtCheatRegisters = 0x51,
ExtCheatRegistersCompare = 0x52,
New F6 Cheat Type (IF,ELSE IF, ELSE structure) Cheat Type F6 lets you perform cheats in IF / ELSE IF / ELSE constructs along with multiple conditions for each one. It supports conditions D0, D1, D2, D3, E0, E1, E2, E3, A0, A1, A2, A3, D7 and two new types that will only be available in the F6 conditions :- E4 & E5 which are bit comparisons on bytes (set and clear). Any of those types which are "block conditionals" like D7 will be treated as a single condition with the other conditions so you wont need to worry about extra 00000000 FFFFs just for them (infact you can drop them completely apart from the last 00000000 FFFF, which you can also drop if you have no trailing cheat lines after the end of the construct. #IF / ELSE IF / ELSE Statement Example with Notes 80001000 00000001 Setup Test Condition 1 80001004 00000002 Setup Test Condition 2 80001008 00000003 Setup Test Condition 3 8000100C 00000004 Setup Test Condition 4 1F = IF, last two digits determine the number of conditions to check F600000x x = 0 All conditions must be true, x = 1 At least One condition must be true F6000000 1F000004 Check the next 4 lines as the conditions for the IF loop to be taken D0001000 00000001 ) D0001004 00000002 )Conditions D0001008 00000003 ) D000100C 00000004 ) 80001010 00005555 Poke(s) 00000000 0000FFFF End of IF Statement F6000000 E15E1F02 E15E1F = ELSE IF (same format as IF) - Can have multiple ELSE IF Blocks or none at all D0001008 00000003 ) D000100C 00000005 )Conditions 80001010 00008888 Poke(s) 00000000 0000FFFF End of ELSE IF Statement F6000000 E15E0000 E15E = ELSE (no need for conditions - this will poke if the others dont) 80001010 0000AAAA Poke(s) 00000000 0000FFFF End of ELSE Statement #Multiple Test Using Joypad Left Thumb Directions, Look at Address 1100 in Debugger F6000000 1F000001 D7020002 00100000 90001100 50205055 90001104 53534552 90001108 20204445 9000110C 20202020 F6000000 E15E1F01 D7020002 00400000 90001100 4E574F44 90001104 45525020 90001108 44455353 9000110C 20202020 F6000000 E15E1F01 D7020002 00800000 90001100 5446454C 90001104 45525020 90001108 44455353 9000110C 20202020 F6000000 E15E1F01 D7020002 00200000 90001100 48474952 90001104 52502054 90001108 45535345 9000110C 20202044 F6000000 E15E0000 90001100 20202020 90001104 20202020 90001108 20202020 9000110C 20202020 00000000 0000FFFF
2021-08-21 14:08:34 +00:00
ExtCompareBitsSet8 = 0xE4, //Only used inside ExtMultiConditionals
ExtCompareBitsClear8 = 0xE5, //Only used inside ExtMultiConditionals
};
union Instruction
{
u64 bits;
struct
{
u32 second;
u32 first;
};
BitField<u64, InstructionCode, 32 + 24, 8> code;
BitField<u64, u32, 32, 24> address;
BitField<u64, u32, 0, 32> value32;
BitField<u64, u16, 0, 16> value16;
BitField<u64, u8, 0, 8> value8;
};
2020-10-19 15:14:49 +00:00
std::string group;
std::string description;
std::vector<Instruction> instructions;
std::string comments;
2020-10-19 15:14:49 +00:00
Type type = Type::Gameshark;
Activation activation = Activation::EndFrame;
bool enabled = false;
ALWAYS_INLINE bool Valid() const { return !instructions.empty() && !description.empty(); }
2020-10-19 15:14:49 +00:00
ALWAYS_INLINE bool IsManuallyActivated() const { return (activation == Activation::Manual); }
std::string GetInstructionsAsString() const;
bool SetInstructionsFromString(const std::string& str);
u32 GetNextNonConditionalInstruction(u32 index) const;
void Apply() const;
void ApplyOnDisable() const;
2020-10-19 15:14:49 +00:00
static const char* GetTypeName(Type type);
static const char* GetTypeDisplayName(Type type);
static std::optional<Type> ParseTypeName(const char* str);
static const char* GetActivationName(Activation activation);
static const char* GetActivationDisplayName(Activation activation);
static std::optional<Activation> ParseActivationName(const char* str);
};
class CheatList final
{
public:
enum class Format
{
Autodetect,
PCSXR,
Libretro,
EPSXe,
Count
};
CheatList();
~CheatList();
ALWAYS_INLINE const CheatCode& GetCode(u32 i) const { return m_codes[i]; }
ALWAYS_INLINE CheatCode& GetCode(u32 i) { return m_codes[i]; }
ALWAYS_INLINE u32 GetCodeCount() const { return static_cast<u32>(m_codes.size()); }
ALWAYS_INLINE bool IsCodeEnabled(u32 index) const { return m_codes[index].enabled; }
ALWAYS_INLINE bool GetMasterEnable() const { return m_master_enable; }
ALWAYS_INLINE void SetMasterEnable(bool enable) { m_master_enable = enable; }
const CheatCode* FindCode(const char* name) const;
const CheatCode* FindCode(const char* group, const char* name) const;
void AddCode(CheatCode cc);
2020-09-25 11:16:33 +00:00
void SetCode(u32 index, CheatCode cc);
void RemoveCode(u32 i);
u32 GetEnabledCodeCount() const;
2020-10-19 15:14:49 +00:00
std::vector<std::string> GetCodeGroups() const;
void EnableCode(u32 index);
void DisableCode(u32 index);
void SetCodeEnabled(u32 index, bool state);
static std::optional<Format> DetectFileFormat(const char* filename);
static Format DetectFileFormat(const std::string& str);
2020-09-25 11:16:33 +00:00
static bool ParseLibretroCheat(CheatCode* cc, const char* line);
bool LoadFromFile(const char* filename, Format format);
bool LoadFromPCSXRFile(const char* filename);
bool LoadFromLibretroFile(const char* filename);
bool LoadFromString(const std::string& str, Format format);
bool LoadFromPCSXRString(const std::string& str);
bool LoadFromLibretroString(const std::string& str);
bool LoadFromEPSXeString(const std::string& str);
bool SaveToPCSXRFile(const char* filename);
2022-10-05 08:29:08 +00:00
bool LoadFromPackage(const std::string& serial);
void Apply();
void ApplyCode(u32 index);
void MergeList(const CheatList& cl);
private:
std::vector<CheatCode> m_codes;
bool m_master_enable = true;
};
2020-10-19 15:14:49 +00:00
class MemoryScan
{
public:
enum class Operator
{
Equal,
NotEqual,
GreaterThan,
GreaterEqual,
LessThan,
LessEqual,
IncreasedBy,
DecreasedBy,
ChangedBy,
EqualLast,
NotEqualLast,
GreaterThanLast,
GreaterEqualLast,
LessThanLast,
2020-10-21 11:40:17 +00:00
LessEqualLast,
Any
2020-10-19 15:14:49 +00:00
};
struct Result
{
PhysicalMemoryAddress address;
u32 value;
u32 last_value;
bool value_changed;
bool Filter(Operator op, u32 comp_value, bool is_signed) const;
void UpdateValue(MemoryAccessSize size, bool is_signed);
};
using ResultVector = std::vector<Result>;
MemoryScan();
~MemoryScan();
u32 GetValue() const { return m_value; }
bool GetValueSigned() const { return m_signed; }
MemoryAccessSize GetSize() const { return m_size; }
Operator GetOperator() const { return m_operator; }
PhysicalMemoryAddress GetStartAddress() const { return m_start_address; }
PhysicalMemoryAddress GetEndAddress() const { return m_end_address; }
const ResultVector& GetResults() const { return m_results; }
const Result& GetResult(u32 index) const { return m_results[index]; }
u32 GetResultCount() const { return static_cast<u32>(m_results.size()); }
void SetValue(u32 value) { m_value = value; }
void SetValueSigned(bool s) { m_signed = s; }
void SetSize(MemoryAccessSize size) { m_size = size; }
void SetOperator(Operator op) { m_operator = op; }
void SetStartAddress(PhysicalMemoryAddress addr) { m_start_address = addr; }
void SetEndAddress(PhysicalMemoryAddress addr) { m_end_address = addr; }
void ResetSearch();
void Search();
void SearchAgain();
void UpdateResultsValues();
void SetResultValue(u32 index, u32 value);
private:
void SearchBytes();
void SearchHalfwords();
void SearchWords();
u32 m_value = 0;
MemoryAccessSize m_size = MemoryAccessSize::HalfWord;
2020-10-19 15:14:49 +00:00
Operator m_operator = Operator::Equal;
PhysicalMemoryAddress m_start_address = 0;
PhysicalMemoryAddress m_end_address = 0x200000;
ResultVector m_results;
bool m_signed = false;
2020-10-19 15:14:49 +00:00
};
class MemoryWatchList
{
public:
MemoryWatchList();
~MemoryWatchList();
struct Entry
{
std::string description;
u32 address;
u32 value;
MemoryAccessSize size;
bool is_signed;
bool freeze;
bool changed;
};
using EntryVector = std::vector<Entry>;
const Entry* GetEntryByAddress(u32 address) const;
const EntryVector& GetEntries() const { return m_entries; }
const Entry& GetEntry(u32 index) const { return m_entries[index]; }
u32 GetEntryCount() const { return static_cast<u32>(m_entries.size()); }
bool AddEntry(std::string description, u32 address, MemoryAccessSize size, bool is_signed, bool freeze);
void RemoveEntry(u32 index);
bool RemoveEntryByDescription(const char* description);
bool RemoveEntryByAddress(u32 address);
void SetEntryDescription(u32 index, std::string description);
void SetEntryFreeze(u32 index, bool freeze);
void SetEntryValue(u32 index, u32 value);
void UpdateValues();
private:
static void SetEntryValue(Entry* entry, u32 value);
static void UpdateEntryValue(Entry* entry);
EntryVector m_entries;
};