mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-17 19:55:47 -04:00
Qt: Add new cheat manager
This commit is contained in:
@ -1,10 +1,14 @@
|
||||
#include "cheats.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/string.h"
|
||||
#include "common/string_util.h"
|
||||
#include "cpu_core.h"
|
||||
#include "host_interface.h"
|
||||
#include <cctype>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
Log_SetChannel(Cheats);
|
||||
|
||||
using KeyValuePairVector = std::vector<std::pair<std::string, std::string>>;
|
||||
@ -37,6 +41,7 @@ bool CheatList::LoadFromPCSXRFile(const char* filename)
|
||||
|
||||
char line[1024];
|
||||
CheatCode current_code;
|
||||
current_code.group = "Ungrouped";
|
||||
while (std::fgets(line, sizeof(line), fp.get()))
|
||||
{
|
||||
char* start = line;
|
||||
@ -67,8 +72,9 @@ bool CheatList::LoadFromPCSXRFile(const char* filename)
|
||||
if (current_code.Valid())
|
||||
m_codes.push_back(std::move(current_code));
|
||||
|
||||
current_code = {};
|
||||
current_code.enabled = false;
|
||||
current_code = CheatCode();
|
||||
current_code.group = "Ungrouped";
|
||||
|
||||
if (*start == '*')
|
||||
{
|
||||
current_code.enabled = true;
|
||||
@ -191,6 +197,7 @@ bool CheatList::LoadFromLibretroFile(const char* filename)
|
||||
}
|
||||
|
||||
CheatCode cc;
|
||||
cc.group = "Ungrouped";
|
||||
cc.description = *desc;
|
||||
cc.enabled = StringUtil::FromChars<bool>(*enable).value_or(false);
|
||||
if (ParseLibretroCheat(&cc, code->c_str()))
|
||||
@ -357,6 +364,20 @@ u32 CheatList::GetEnabledCodeCount() const
|
||||
return count;
|
||||
}
|
||||
|
||||
std::vector<std::string> CheatList::GetCodeGroups() const
|
||||
{
|
||||
std::vector<std::string> groups;
|
||||
for (const CheatCode& cc : m_codes)
|
||||
{
|
||||
if (std::any_of(groups.begin(), groups.end(), [cc](const std::string& group) { return (group == cc.group); }))
|
||||
continue;
|
||||
|
||||
groups.emplace_back(cc.group);
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
void CheatList::SetCodeEnabled(u32 index, bool state)
|
||||
{
|
||||
if (index >= m_codes.size())
|
||||
@ -383,6 +404,73 @@ void CheatList::ApplyCode(u32 index)
|
||||
m_codes[index].Apply();
|
||||
}
|
||||
|
||||
std::string CheatCode::GetInstructionsAsString() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
for (const Instruction& inst : instructions)
|
||||
{
|
||||
ss << std::hex << std::uppercase << std::setw(8) << std::setfill('0') << inst.first;
|
||||
ss << " ";
|
||||
ss << std::hex << std::uppercase << std::setw(8) << std::setfill('0') << inst.second;
|
||||
ss << '\n';
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool CheatCode::SetInstructionsFromString(const std::string& str)
|
||||
{
|
||||
std::vector<Instruction> new_instructions;
|
||||
std::istringstream ss(str);
|
||||
|
||||
for (std::string line; std::getline(ss, line);)
|
||||
{
|
||||
char* start = line.data();
|
||||
while (*start != '\0' && std::isspace(*start))
|
||||
start++;
|
||||
|
||||
// skip empty lines
|
||||
if (*start == '\0')
|
||||
continue;
|
||||
|
||||
char* end = start + std::strlen(start) - 1;
|
||||
while (end > start && std::isspace(*end))
|
||||
{
|
||||
*end = '\0';
|
||||
end--;
|
||||
}
|
||||
|
||||
// skip comments and empty line
|
||||
if (*start == '#' || *start == ';' || *start == '/' || *start == '\"')
|
||||
continue;
|
||||
|
||||
while (!IsHexCharacter(*start) && start != end)
|
||||
start++;
|
||||
if (start == end)
|
||||
continue;
|
||||
|
||||
char* end_ptr;
|
||||
CheatCode::Instruction inst;
|
||||
inst.first = static_cast<u32>(std::strtoul(start, &end_ptr, 16));
|
||||
inst.second = 0;
|
||||
if (end_ptr)
|
||||
{
|
||||
while (!IsHexCharacter(*end_ptr) && end_ptr != end)
|
||||
end_ptr++;
|
||||
if (end_ptr != end)
|
||||
inst.second = static_cast<u32>(std::strtoul(end_ptr, nullptr, 16));
|
||||
}
|
||||
new_instructions.push_back(inst);
|
||||
}
|
||||
|
||||
if (new_instructions.empty())
|
||||
return false;
|
||||
|
||||
instructions = std::move(new_instructions);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CheatCode::Apply() const
|
||||
{
|
||||
const u32 count = static_cast<u32>(instructions.size());
|
||||
@ -622,3 +710,476 @@ void CheatCode::Apply() const
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::array<const char*, 1> s_cheat_code_type_names = {{"Gameshark"}};
|
||||
static std::array<const char*, 1> s_cheat_code_type_display_names{{TRANSLATABLE("Cheats", "Gameshark")}};
|
||||
|
||||
const char* CheatCode::GetTypeName(Type type)
|
||||
{
|
||||
return s_cheat_code_type_names[static_cast<u32>(type)];
|
||||
}
|
||||
|
||||
const char* CheatCode::GetTypeDisplayName(Type type)
|
||||
{
|
||||
return s_cheat_code_type_display_names[static_cast<u32>(type)];
|
||||
}
|
||||
|
||||
std::optional<CheatCode::Type> CheatCode::ParseTypeName(const char* str)
|
||||
{
|
||||
for (u32 i = 0; i < static_cast<u32>(s_cheat_code_type_names.size()); i++)
|
||||
{
|
||||
if (std::strcmp(s_cheat_code_type_names[i], str) == 0)
|
||||
return static_cast<Type>(i);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static std::array<const char*, 2> s_cheat_code_activation_names = {{"Manual", "EndFrame"}};
|
||||
static std::array<const char*, 2> s_cheat_code_activation_display_names{
|
||||
{TRANSLATABLE("Cheats", "Manual"), TRANSLATABLE("Cheats", "Automatic (Frame End)")}};
|
||||
|
||||
const char* CheatCode::GetActivationName(Activation activation)
|
||||
{
|
||||
return s_cheat_code_activation_names[static_cast<u32>(activation)];
|
||||
}
|
||||
|
||||
const char* CheatCode::GetActivationDisplayName(Activation activation)
|
||||
{
|
||||
return s_cheat_code_activation_display_names[static_cast<u32>(activation)];
|
||||
}
|
||||
|
||||
std::optional<CheatCode::Activation> CheatCode::ParseActivationName(const char* str)
|
||||
{
|
||||
for (u32 i = 0; i < static_cast<u32>(s_cheat_code_activation_names.size()); i++)
|
||||
{
|
||||
if (std::strcmp(s_cheat_code_activation_names[i], str) == 0)
|
||||
return static_cast<Activation>(i);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MemoryScan::MemoryScan() = default;
|
||||
|
||||
MemoryScan::~MemoryScan() = default;
|
||||
|
||||
void MemoryScan::ResetSearch()
|
||||
{
|
||||
m_results.clear();
|
||||
}
|
||||
|
||||
void MemoryScan::Search()
|
||||
{
|
||||
m_results.clear();
|
||||
|
||||
switch (m_size)
|
||||
{
|
||||
case MemoryAccessSize::Byte:
|
||||
SearchBytes();
|
||||
break;
|
||||
|
||||
case MemoryAccessSize::HalfWord:
|
||||
SearchHalfwords();
|
||||
break;
|
||||
|
||||
case MemoryAccessSize::Word:
|
||||
SearchWords();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryScan::SearchBytes()
|
||||
{
|
||||
for (PhysicalMemoryAddress address = m_start_address; address < m_end_address; address++)
|
||||
{
|
||||
u8 bvalue = 0;
|
||||
CPU::SafeReadMemoryByte(address, &bvalue);
|
||||
|
||||
Result res;
|
||||
res.address = address;
|
||||
res.value = m_signed ? SignExtend32(bvalue) : ZeroExtend32(bvalue);
|
||||
res.last_value = res.value;
|
||||
res.value_changed = false;
|
||||
|
||||
if (res.Filter(m_operator, m_value, m_signed))
|
||||
m_results.push_back(res);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryScan::SearchHalfwords()
|
||||
{
|
||||
for (PhysicalMemoryAddress address = m_start_address; address < m_end_address; address += 2)
|
||||
{
|
||||
u16 bvalue = 0;
|
||||
CPU::SafeReadMemoryHalfWord(address, &bvalue);
|
||||
|
||||
Result res;
|
||||
res.address = address;
|
||||
res.value = m_signed ? SignExtend32(bvalue) : ZeroExtend32(bvalue);
|
||||
res.last_value = res.value;
|
||||
res.value_changed = false;
|
||||
|
||||
if (res.Filter(m_operator, m_value, m_signed))
|
||||
m_results.push_back(res);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryScan::SearchWords()
|
||||
{
|
||||
for (PhysicalMemoryAddress address = m_start_address; address < m_end_address; address += 4)
|
||||
{
|
||||
Result res;
|
||||
res.address = address;
|
||||
CPU::SafeReadMemoryWord(address, &res.value);
|
||||
res.last_value = res.value;
|
||||
res.value_changed = false;
|
||||
|
||||
if (res.Filter(m_operator, m_value, m_signed))
|
||||
m_results.push_back(res);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryScan::SearchAgain()
|
||||
{
|
||||
ResultVector new_results;
|
||||
new_results.reserve(m_results.size());
|
||||
for (Result& res : m_results)
|
||||
{
|
||||
res.UpdateValue(m_size, m_signed);
|
||||
|
||||
if (res.Filter(m_operator, m_value, m_signed))
|
||||
{
|
||||
res.last_value = res.value;
|
||||
new_results.push_back(res);
|
||||
}
|
||||
}
|
||||
|
||||
m_results.swap(new_results);
|
||||
}
|
||||
|
||||
void MemoryScan::UpdateResultsValues()
|
||||
{
|
||||
for (Result& res : m_results)
|
||||
res.UpdateValue(m_size, m_signed);
|
||||
}
|
||||
|
||||
void MemoryScan::SetResultValue(u32 index, u32 value)
|
||||
{
|
||||
if (index >= m_results.size())
|
||||
return;
|
||||
|
||||
Result& res = m_results[index];
|
||||
if (res.value == value)
|
||||
return;
|
||||
|
||||
switch (m_size)
|
||||
{
|
||||
case MemoryAccessSize::Byte:
|
||||
CPU::SafeWriteMemoryByte(res.address, Truncate8(value));
|
||||
break;
|
||||
|
||||
case MemoryAccessSize::HalfWord:
|
||||
CPU::SafeWriteMemoryHalfWord(res.address, Truncate16(value));
|
||||
break;
|
||||
|
||||
case MemoryAccessSize::Word:
|
||||
CPU::SafeWriteMemoryWord(res.address, value);
|
||||
break;
|
||||
}
|
||||
|
||||
res.value = value;
|
||||
res.value_changed = true;
|
||||
}
|
||||
|
||||
bool MemoryScan::Result::Filter(Operator op, u32 comp_value, bool is_signed) const
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case Operator::Equal:
|
||||
{
|
||||
return (value == comp_value);
|
||||
}
|
||||
|
||||
case Operator::NotEqual:
|
||||
{
|
||||
return (value != comp_value);
|
||||
}
|
||||
|
||||
case Operator::GreaterThan:
|
||||
{
|
||||
return is_signed ? (static_cast<s32>(value) > static_cast<s32>(comp_value)) : (value > comp_value);
|
||||
}
|
||||
|
||||
case Operator::GreaterEqual:
|
||||
{
|
||||
return is_signed ? (static_cast<s32>(value) >= static_cast<s32>(comp_value)) : (value >= comp_value);
|
||||
}
|
||||
|
||||
case Operator::LessThan:
|
||||
{
|
||||
return is_signed ? (static_cast<s32>(value) < static_cast<s32>(comp_value)) : (value < comp_value);
|
||||
}
|
||||
|
||||
case Operator::LessEqual:
|
||||
{
|
||||
return is_signed ? (static_cast<s32>(value) <= static_cast<s32>(comp_value)) : (value <= comp_value);
|
||||
}
|
||||
|
||||
case Operator::IncreasedBy:
|
||||
{
|
||||
return is_signed ? ((static_cast<s32>(value) - static_cast<s32>(last_value)) == static_cast<s32>(comp_value)) :
|
||||
((value - last_value) == comp_value);
|
||||
}
|
||||
|
||||
case Operator::DecreasedBy:
|
||||
{
|
||||
return is_signed ? ((static_cast<s32>(last_value) - static_cast<s32>(value)) == static_cast<s32>(comp_value)) :
|
||||
((last_value - value) == comp_value);
|
||||
}
|
||||
|
||||
case Operator::ChangedBy:
|
||||
{
|
||||
if (is_signed)
|
||||
return (std::abs(static_cast<s32>(last_value) - static_cast<s32>(value)) == static_cast<s32>(comp_value));
|
||||
else
|
||||
return ((last_value > value) ? (last_value - value) : (value - last_value)) == comp_value;
|
||||
}
|
||||
|
||||
case Operator::EqualLast:
|
||||
{
|
||||
return (value == last_value);
|
||||
}
|
||||
|
||||
case Operator::NotEqualLast:
|
||||
{
|
||||
return (value != last_value);
|
||||
}
|
||||
|
||||
case Operator::GreaterThanLast:
|
||||
{
|
||||
return is_signed ? (static_cast<s32>(value) > static_cast<s32>(last_value)) : (value > last_value);
|
||||
}
|
||||
|
||||
case Operator::GreaterEqualLast:
|
||||
{
|
||||
return is_signed ? (static_cast<s32>(value) >= static_cast<s32>(last_value)) : (value >= last_value);
|
||||
}
|
||||
|
||||
case Operator::LessThanLast:
|
||||
{
|
||||
return is_signed ? (static_cast<s32>(value) < static_cast<s32>(last_value)) : (value < last_value);
|
||||
}
|
||||
|
||||
case Operator::LessEqualLast:
|
||||
{
|
||||
return is_signed ? (static_cast<s32>(value) <= static_cast<s32>(last_value)) : (value <= last_value);
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryScan::Result::UpdateValue(MemoryAccessSize size, bool is_signed)
|
||||
{
|
||||
const u32 old_value = value;
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case MemoryAccessSize::Byte:
|
||||
{
|
||||
u8 bvalue = 0;
|
||||
CPU::SafeReadMemoryByte(address, &bvalue);
|
||||
value = is_signed ? SignExtend32(bvalue) : ZeroExtend32(bvalue);
|
||||
}
|
||||
break;
|
||||
|
||||
case MemoryAccessSize::HalfWord:
|
||||
{
|
||||
u16 bvalue = 0;
|
||||
CPU::SafeReadMemoryHalfWord(address, &bvalue);
|
||||
value = is_signed ? SignExtend32(bvalue) : ZeroExtend32(bvalue);
|
||||
}
|
||||
break;
|
||||
|
||||
case MemoryAccessSize::Word:
|
||||
{
|
||||
CPU::SafeReadMemoryWord(address, &value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
value_changed = (value != old_value);
|
||||
}
|
||||
|
||||
MemoryWatchList::MemoryWatchList() = default;
|
||||
|
||||
MemoryWatchList::~MemoryWatchList() = default;
|
||||
|
||||
const MemoryWatchList::Entry* MemoryWatchList::GetEntryByAddress(u32 address) const
|
||||
{
|
||||
for (const Entry& entry : m_entries)
|
||||
{
|
||||
if (entry.address == address)
|
||||
return &entry;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool MemoryWatchList::AddEntry(std::string description, u32 address, MemoryAccessSize size, bool is_signed, bool freeze)
|
||||
{
|
||||
if (GetEntryByAddress(address))
|
||||
return false;
|
||||
|
||||
Entry entry;
|
||||
entry.description = std::move(description);
|
||||
entry.address = address;
|
||||
entry.size = size;
|
||||
entry.is_signed = is_signed;
|
||||
entry.freeze = false;
|
||||
|
||||
UpdateEntryValue(&entry);
|
||||
|
||||
entry.changed = false;
|
||||
entry.freeze = freeze;
|
||||
|
||||
m_entries.push_back(std::move(entry));
|
||||
return true;
|
||||
}
|
||||
|
||||
void MemoryWatchList::RemoveEntry(u32 index)
|
||||
{
|
||||
if (index >= m_entries.size())
|
||||
return;
|
||||
|
||||
m_entries.erase(m_entries.begin() + index);
|
||||
}
|
||||
|
||||
bool MemoryWatchList::RemoveEntryByAddress(u32 address)
|
||||
{
|
||||
for (auto it = m_entries.begin(); it != m_entries.end(); ++it)
|
||||
{
|
||||
if (it->address == address)
|
||||
{
|
||||
m_entries.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MemoryWatchList::SetEntryDescription(u32 index, std::string description)
|
||||
{
|
||||
if (index >= m_entries.size())
|
||||
return;
|
||||
|
||||
Entry& entry = m_entries[index];
|
||||
entry.description = std::move(description);
|
||||
}
|
||||
|
||||
void MemoryWatchList::SetEntryFreeze(u32 index, bool freeze)
|
||||
{
|
||||
if (index >= m_entries.size())
|
||||
return;
|
||||
|
||||
Entry& entry = m_entries[index];
|
||||
entry.freeze = freeze;
|
||||
}
|
||||
|
||||
void MemoryWatchList::SetEntryValue(u32 index, u32 value)
|
||||
{
|
||||
if (index >= m_entries.size())
|
||||
return;
|
||||
|
||||
Entry& entry = m_entries[index];
|
||||
if (entry.value == value)
|
||||
return;
|
||||
|
||||
SetEntryValue(&entry, value);
|
||||
}
|
||||
|
||||
bool MemoryWatchList::RemoveEntryByDescription(const char* description)
|
||||
{
|
||||
bool result = false;
|
||||
for (auto it = m_entries.begin(); it != m_entries.end();)
|
||||
{
|
||||
if (it->description == description)
|
||||
{
|
||||
it = m_entries.erase(it);
|
||||
result = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void MemoryWatchList::UpdateValues()
|
||||
{
|
||||
for (Entry& entry : m_entries)
|
||||
UpdateEntryValue(&entry);
|
||||
}
|
||||
|
||||
void MemoryWatchList::SetEntryValue(Entry* entry, u32 value)
|
||||
{
|
||||
switch (entry->size)
|
||||
{
|
||||
case MemoryAccessSize::Byte:
|
||||
CPU::SafeWriteMemoryByte(entry->address, Truncate8(value));
|
||||
break;
|
||||
|
||||
case MemoryAccessSize::HalfWord:
|
||||
CPU::SafeWriteMemoryHalfWord(entry->address, Truncate16(value));
|
||||
break;
|
||||
|
||||
case MemoryAccessSize::Word:
|
||||
CPU::SafeWriteMemoryWord(entry->address, value);
|
||||
break;
|
||||
}
|
||||
|
||||
entry->changed = (entry->value != value);
|
||||
entry->value = value;
|
||||
}
|
||||
|
||||
void MemoryWatchList::UpdateEntryValue(Entry* entry)
|
||||
{
|
||||
const u32 old_value = entry->value;
|
||||
|
||||
switch (entry->size)
|
||||
{
|
||||
case MemoryAccessSize::Byte:
|
||||
{
|
||||
u8 bvalue = 0;
|
||||
CPU::SafeReadMemoryByte(entry->address, &bvalue);
|
||||
entry->value = entry->is_signed ? SignExtend32(bvalue) : ZeroExtend32(bvalue);
|
||||
}
|
||||
break;
|
||||
|
||||
case MemoryAccessSize::HalfWord:
|
||||
{
|
||||
u16 bvalue = 0;
|
||||
CPU::SafeReadMemoryHalfWord(entry->address, &bvalue);
|
||||
entry->value = entry->is_signed ? SignExtend32(bvalue) : ZeroExtend32(bvalue);
|
||||
}
|
||||
break;
|
||||
|
||||
case MemoryAccessSize::Word:
|
||||
{
|
||||
CPU::SafeReadMemoryWord(entry->address, &entry->value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
entry->changed = (old_value != entry->value);
|
||||
|
||||
if (entry->freeze && entry->changed)
|
||||
SetEntryValue(entry, old_value);
|
||||
}
|
||||
|
@ -7,6 +7,19 @@
|
||||
|
||||
struct CheatCode
|
||||
{
|
||||
enum class Type : u8
|
||||
{
|
||||
Gameshark,
|
||||
Count
|
||||
};
|
||||
|
||||
enum class Activation : u8
|
||||
{
|
||||
Manual,
|
||||
EndFrame,
|
||||
Count,
|
||||
};
|
||||
|
||||
enum class InstructionCode : u8
|
||||
{
|
||||
Nop = 0x00,
|
||||
@ -45,13 +58,28 @@ struct CheatCode
|
||||
BitField<u64, u8, 0, 8> value8;
|
||||
};
|
||||
|
||||
std::string group;
|
||||
std::string description;
|
||||
std::vector<Instruction> instructions;
|
||||
bool enabled;
|
||||
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);
|
||||
|
||||
void Apply() 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
|
||||
@ -78,6 +106,7 @@ public:
|
||||
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);
|
||||
@ -98,3 +127,121 @@ public:
|
||||
private:
|
||||
std::vector<CheatCode> m_codes;
|
||||
};
|
||||
|
||||
class MemoryScan
|
||||
{
|
||||
public:
|
||||
enum class Operator
|
||||
{
|
||||
Equal,
|
||||
NotEqual,
|
||||
GreaterThan,
|
||||
GreaterEqual,
|
||||
LessThan,
|
||||
LessEqual,
|
||||
IncreasedBy,
|
||||
DecreasedBy,
|
||||
ChangedBy,
|
||||
EqualLast,
|
||||
NotEqualLast,
|
||||
GreaterThanLast,
|
||||
GreaterEqualLast,
|
||||
LessThanLast,
|
||||
LessEqualLast
|
||||
};
|
||||
|
||||
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::Word;
|
||||
Operator m_operator = Operator::Equal;
|
||||
PhysicalMemoryAddress m_start_address = 0;
|
||||
PhysicalMemoryAddress m_end_address = 0x200000;
|
||||
ResultVector m_results;
|
||||
bool m_signed = true;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
Reference in New Issue
Block a user