mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-21 07:45:38 +00:00
7adbedd341
* Renamed Mem Scanner Op Text&added Virtual Address Renamed the Memory Scanner Operator Text for clarity. Replaced '...' with 'Value' and 'Previous' with 'Previous Result'. Also changed "Any Value" to "Any Value ('New Search' or 'Reset Result Value')", Any Valur should really be at the top of the index and the Previous Results should come before the other Value operators. But that's a job for another time. The Virtual Address extra will display the virtual address of DuckStation's PSX RAM so it can be easily edited with the likes of HXD or another tool where the location of the virtual memory needs to be known. * Replaced Mem Scanner Op Text with latest English text Not touched the translations, just added the updated english text for the Memory Scanner "Operator:" text * Rearranged Memory Scanner Operator class Re-arranged Memory Scanner Operator class into a more logical order - with likely frequency of use moving item up the list. New Order:- Better Order:- Any Value ('New Search' or 'Reset Result Value') x Any. Less Than Previous Result x LessThanLast, Less or Equal to Previous Result x LessEqualLast, Greater Than Previous Result x GreaterThanLast, Greater or Equal to Previous Result x GreaterEqualLast, Not Equal to Previous Result (Changed Value) x NotEqualLast, Equal to Previous Result (Unchanged Value) x EqualLast, Decreased By Value x DecreasedBy, Increased By Value x IncreasedBy, Changed By Value x ChangedBy, Equal to Value x Equal, Not Equal to Value x NotEqual, Less Than Value x LessThan, Less or Equal to Value x LessEqual, Greater Than Value x GreaterThan, Greater or Equal to Value x GreaterEqual * Rearranged Memory Scanner Operator class Changes operator order to:- Any Value ('New Search' or 'Reset Result Value') Less Than Previous Result Less or Equal to Previous Result Greater Than Previous Result Greater or Equal to Previous Result Not Equal to Previous Result (Changed Value) Equal to Previous Result (Unchanged Value) Decreased By Value Increased By Value Changed By Value Equal to Value Not Equal to Value Less Than Value Less or Equal to Value Greater Than Value Greater or Equal to Value * Memory Scanner line references updates Updated <location filename="../memoryscannerwindow.ui" line="???"/> line numbers for the Memory Scanner operation re-ordering. These are currently the only 4 translation files that make reference to memoryscannerwindow.ui, the other 12 still reference cheatmanagerdialog.ui so not replicated for them as this would only partially fix them - so I left them alone as I hope there's some automated tools to handle it. * Update src/duckstation-qt/memoryscannerwindow.cpp
320 lines
8.5 KiB
C++
320 lines
8.5 KiB
C++
// 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
|
|
{
|
|
enum class Type : u8
|
|
{
|
|
Gameshark,
|
|
Count
|
|
};
|
|
|
|
enum class Activation : u8
|
|
{
|
|
Manual,
|
|
EndFrame,
|
|
Count,
|
|
};
|
|
|
|
enum class InstructionCode : u8
|
|
{
|
|
Nop = 0x00,
|
|
ConstantWrite8 = 0x30,
|
|
ConstantWrite16 = 0x80,
|
|
ScratchpadWrite16 = 0x1F,
|
|
Increment16 = 0x10,
|
|
Decrement16 = 0x11,
|
|
Increment8 = 0x20,
|
|
Decrement8 = 0x21,
|
|
DelayActivation = 0xC1,
|
|
SkipIfNotEqual16 = 0xC0,
|
|
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,
|
|
|
|
ExtBitCompareButtons = 0xD7,
|
|
ExtSkipIfNotLess8 = 0xC3,
|
|
ExtSkipIfNotGreater8 = 0xC4,
|
|
ExtSkipIfNotLess16 = 0xC5,
|
|
ExtSkipIfNotGreater16 = 0xC6,
|
|
ExtMultiConditionals = 0xF6,
|
|
|
|
ExtCheatRegisters = 0x51,
|
|
ExtCheatRegistersCompare = 0x52,
|
|
|
|
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;
|
|
};
|
|
|
|
std::string group;
|
|
std::string description;
|
|
std::vector<Instruction> instructions;
|
|
std::string comments;
|
|
Type type = Type::Gameshark;
|
|
Activation activation = Activation::EndFrame;
|
|
bool enabled = false;
|
|
|
|
ALWAYS_INLINE bool Valid() const { return !instructions.empty() && !description.empty(); }
|
|
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;
|
|
|
|
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);
|
|
void SetCode(u32 index, CheatCode cc);
|
|
void RemoveCode(u32 i);
|
|
|
|
u32 GetEnabledCodeCount() const;
|
|
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);
|
|
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);
|
|
|
|
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;
|
|
};
|
|
|
|
class MemoryScan
|
|
{
|
|
public:
|
|
enum class Operator
|
|
{
|
|
Any,
|
|
LessThanLast,
|
|
LessEqualLast,
|
|
GreaterThanLast,
|
|
GreaterEqualLast,
|
|
NotEqualLast,
|
|
EqualLast,
|
|
DecreasedBy,
|
|
IncreasedBy,
|
|
ChangedBy,
|
|
Equal,
|
|
NotEqual,
|
|
LessThan,
|
|
LessEqual,
|
|
GreaterThan,
|
|
GreaterEqual
|
|
};
|
|
|
|
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;
|
|
Operator m_operator = Operator::Equal;
|
|
PhysicalMemoryAddress m_start_address = 0;
|
|
PhysicalMemoryAddress m_end_address = 0x200000;
|
|
ResultVector m_results;
|
|
bool m_signed = false;
|
|
};
|
|
|
|
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;
|
|
};
|