mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-22 18:32:05 +00:00
Dmnt: Add break/continue commands, add static register api. (#899)
* dmnt: implement break/continue, static reg commands * dmnt: revise per WerWolv's feedback.
This commit is contained in:
parent
b7c4dae899
commit
be07035954
6 changed files with 242 additions and 8 deletions
|
@ -66,6 +66,14 @@ namespace ams::dmnt::cheat {
|
|||
return dmnt::cheat::impl::QueryCheatProcessMemory(mapping.GetPointer(), address);
|
||||
}
|
||||
|
||||
Result CheatService::BreakCheatProcess() {
|
||||
return dmnt::cheat::impl::BreakCheatProcess();
|
||||
}
|
||||
|
||||
Result CheatService::ContinueCheatProcess() {
|
||||
return dmnt::cheat::impl::ContinueCheatProcess();
|
||||
}
|
||||
|
||||
/* ========================================================================================= */
|
||||
/* =================================== Cheat Commands ==================================== */
|
||||
/* ========================================================================================= */
|
||||
|
@ -95,6 +103,18 @@ namespace ams::dmnt::cheat {
|
|||
return dmnt::cheat::impl::RemoveCheat(cheat_id);
|
||||
}
|
||||
|
||||
Result CheatService::ReadStaticRegister(sf::Out<u64> out, u8 which) {
|
||||
return dmnt::cheat::impl::ReadStaticRegister(out.GetPointer(), which);
|
||||
}
|
||||
|
||||
Result CheatService::WriteStaticRegister(u8 which, u64 value) {
|
||||
return dmnt::cheat::impl::WriteStaticRegister(which, value);
|
||||
}
|
||||
|
||||
Result CheatService::ResetStaticRegisters() {
|
||||
return dmnt::cheat::impl::ResetStaticRegisters();
|
||||
}
|
||||
|
||||
/* ========================================================================================= */
|
||||
/* =================================== Address Commands ================================== */
|
||||
/* ========================================================================================= */
|
||||
|
|
|
@ -33,14 +33,19 @@ namespace ams::dmnt::cheat {
|
|||
ReadCheatProcessMemory = 65102,
|
||||
WriteCheatProcessMemory = 65103,
|
||||
QueryCheatProcessMemory = 65104,
|
||||
BreakCheatProcess = 65105,
|
||||
ContinueCheatProcess = 65106,
|
||||
|
||||
/* Interact with Cheats */
|
||||
GetCheatCount = 65200,
|
||||
GetCheats = 65201,
|
||||
GetCheatById = 65202,
|
||||
ToggleCheat = 65203,
|
||||
AddCheat = 65204,
|
||||
RemoveCheat = 65205,
|
||||
GetCheatCount = 65200,
|
||||
GetCheats = 65201,
|
||||
GetCheatById = 65202,
|
||||
ToggleCheat = 65203,
|
||||
AddCheat = 65204,
|
||||
RemoveCheat = 65205,
|
||||
ReadStaticRegister = 65206,
|
||||
WriteStaticRegister = 65207,
|
||||
ResetStaticRegisters = 65208,
|
||||
|
||||
/* Interact with Frozen Addresses */
|
||||
GetFrozenAddressCount = 65300,
|
||||
|
@ -60,6 +65,8 @@ namespace ams::dmnt::cheat {
|
|||
Result ReadCheatProcessMemory(const sf::OutBuffer &buffer, u64 address, u64 out_size);
|
||||
Result WriteCheatProcessMemory(const sf::InBuffer &buffer, u64 address, u64 in_size);
|
||||
Result QueryCheatProcessMemory(sf::Out<MemoryInfo> mapping, u64 address);
|
||||
Result BreakCheatProcess();
|
||||
Result ContinueCheatProcess();
|
||||
|
||||
Result GetCheatCount(sf::Out<u64> out_count);
|
||||
Result GetCheats(const sf::OutArray<CheatEntry> &cheats, sf::Out<u64> out_count, u64 offset);
|
||||
|
@ -67,6 +74,9 @@ namespace ams::dmnt::cheat {
|
|||
Result ToggleCheat(u32 cheat_id);
|
||||
Result AddCheat(const CheatDefinition &cheat, sf::Out<u32> out_cheat_id, bool enabled);
|
||||
Result RemoveCheat(u32 cheat_id);
|
||||
Result ReadStaticRegister(sf::Out<u64> out, u8 which);
|
||||
Result WriteStaticRegister(u8 which, u64 value);
|
||||
Result ResetStaticRegisters();
|
||||
|
||||
Result GetFrozenAddressCount(sf::Out<u64> out_count);
|
||||
Result GetFrozenAddresses(const sf::OutArray<FrozenAddressEntry> &addresses, sf::Out<u64> out_count, u64 offset);
|
||||
|
@ -86,6 +96,8 @@ namespace ams::dmnt::cheat {
|
|||
MAKE_SERVICE_COMMAND_META(ReadCheatProcessMemory),
|
||||
MAKE_SERVICE_COMMAND_META(WriteCheatProcessMemory),
|
||||
MAKE_SERVICE_COMMAND_META(QueryCheatProcessMemory),
|
||||
MAKE_SERVICE_COMMAND_META(BreakCheatProcess),
|
||||
MAKE_SERVICE_COMMAND_META(ContinueCheatProcess),
|
||||
|
||||
MAKE_SERVICE_COMMAND_META(GetCheatCount),
|
||||
MAKE_SERVICE_COMMAND_META(GetCheats),
|
||||
|
@ -93,6 +105,9 @@ namespace ams::dmnt::cheat {
|
|||
MAKE_SERVICE_COMMAND_META(ToggleCheat),
|
||||
MAKE_SERVICE_COMMAND_META(AddCheat),
|
||||
MAKE_SERVICE_COMMAND_META(RemoveCheat),
|
||||
MAKE_SERVICE_COMMAND_META(ReadStaticRegister),
|
||||
MAKE_SERVICE_COMMAND_META(WriteStaticRegister),
|
||||
MAKE_SERVICE_COMMAND_META(ResetStaticRegisters),
|
||||
|
||||
MAKE_SERVICE_COMMAND_META(GetFrozenAddressCount),
|
||||
MAKE_SERVICE_COMMAND_META(GetFrozenAddresses),
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace ams::dmnt::cheat::impl {
|
|||
static constexpr size_t ThreadStackSize = 0x4000;
|
||||
private:
|
||||
os::Mutex cheat_lock;
|
||||
os::Event unsafe_break_event;
|
||||
os::Event debug_events_event; /* Autoclear. */
|
||||
os::ThreadType detect_thread, debug_events_thread;
|
||||
os::SystemEvent cheat_process_event;
|
||||
|
@ -38,6 +39,7 @@ namespace ams::dmnt::cheat::impl {
|
|||
CheatProcessMetadata cheat_process_metadata = {};
|
||||
|
||||
os::ThreadType vm_thread;
|
||||
bool broken_unsafe = false;
|
||||
bool needs_reload_vm = false;
|
||||
CheatVirtualMachine cheat_vm;
|
||||
|
||||
|
@ -85,6 +87,8 @@ namespace ams::dmnt::cheat::impl {
|
|||
for (size_t i = 0; i < MaxCheatCount; i++) {
|
||||
this->ResetCheatEntry(i);
|
||||
}
|
||||
|
||||
this->cheat_vm.ResetStaticRegisters();
|
||||
}
|
||||
|
||||
CheatEntry *GetCheatEntryById(size_t i) {
|
||||
|
@ -119,6 +123,10 @@ namespace ams::dmnt::cheat::impl {
|
|||
|
||||
void CloseActiveCheatProcess() {
|
||||
if (this->cheat_process_debug_handle != svc::InvalidHandle) {
|
||||
/* We don't need to do any unsafe brekaing. */
|
||||
this->broken_unsafe = false;
|
||||
this->unsafe_break_event.Signal();
|
||||
|
||||
/* Knock out the debug events thread. */
|
||||
os::CancelThreadSynchronization(std::addressof(this->debug_events_thread));
|
||||
|
||||
|
@ -182,7 +190,7 @@ namespace ams::dmnt::cheat::impl {
|
|||
}
|
||||
|
||||
public:
|
||||
CheatProcessManager() : cheat_lock(false), debug_events_event(os::EventClearMode_AutoClear), cheat_process_event(os::EventClearMode_AutoClear, true) {
|
||||
CheatProcessManager() : cheat_lock(false), unsafe_break_event(os::EventClearMode_ManualClear), debug_events_event(os::EventClearMode_AutoClear), cheat_process_event(os::EventClearMode_AutoClear, true) {
|
||||
/* Learn whether we should enable cheats by default. */
|
||||
{
|
||||
u8 en = 0;
|
||||
|
@ -257,6 +265,19 @@ namespace ams::dmnt::cheat::impl {
|
|||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BreakCheatProcessUnsafe() {
|
||||
this->broken_unsafe = true;
|
||||
this->unsafe_break_event.Clear();
|
||||
return svcBreakDebugProcess(this->GetCheatProcessHandle());
|
||||
}
|
||||
|
||||
Result ContinueCheatProcessUnsafe() {
|
||||
this->broken_unsafe = false;
|
||||
this->unsafe_break_event.Signal();
|
||||
dmnt::cheat::impl::ContinueCheatProcess(this->GetCheatProcessHandle());
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetCheatProcessMappingCount(u64 *out_count) {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
|
@ -335,6 +356,22 @@ namespace ams::dmnt::cheat::impl {
|
|||
return svcQueryDebugProcessMemory(mapping, &tmp, this->GetCheatProcessHandle(), address);
|
||||
}
|
||||
|
||||
Result BreakCheatProcess() {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
|
||||
return this->BreakCheatProcessUnsafe();
|
||||
}
|
||||
|
||||
Result ContinueCheatProcess() {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
|
||||
return this->ContinueCheatProcessUnsafe();
|
||||
}
|
||||
|
||||
Result GetCheatCount(u64 *out_count) {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
|
@ -436,6 +473,35 @@ namespace ams::dmnt::cheat::impl {
|
|||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ReadStaticRegister(u64 *out, size_t which) {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
R_UNLESS(which < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid());
|
||||
|
||||
*out = this->cheat_vm.GetStaticRegister(which);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result WriteStaticRegister(size_t which, u64 value) {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
R_UNLESS(which < CheatVirtualMachine::NumStaticRegisters, ResultCheatInvalid());
|
||||
|
||||
this->cheat_vm.SetStaticRegister(which, value);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ResetStaticRegisters() {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
R_TRY(this->EnsureCheatProcess());
|
||||
|
||||
this->cheat_vm.ResetStaticRegisters();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetFrozenAddressCount(u64 *out_count) {
|
||||
std::scoped_lock lk(this->cheat_lock);
|
||||
|
||||
|
@ -533,7 +599,20 @@ namespace ams::dmnt::cheat::impl {
|
|||
this_ptr->debug_events_event.Wait();
|
||||
while (true) {
|
||||
while (R_SUCCEEDED(svcWaitSynchronizationSingle(this_ptr->GetCheatProcessHandle(), std::numeric_limits<u64>::max()))) {
|
||||
std::scoped_lock lk(this_ptr->cheat_lock);
|
||||
this_ptr->cheat_lock.Lock();
|
||||
ON_SCOPE_EXIT { this_ptr->cheat_lock.Unlock(); };
|
||||
|
||||
/* If we did an unsafe break, wait until we're not broken. */
|
||||
if (this_ptr->broken_unsafe) {
|
||||
this_ptr->cheat_lock.Unlock();
|
||||
this_ptr->unsafe_break_event.Wait();
|
||||
this_ptr->cheat_lock.Lock();
|
||||
if (this_ptr->GetCheatProcessHandle() != svc::InvalidHandle) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle any pending debug events. */
|
||||
if (this_ptr->HasActiveCheatProcess()) {
|
||||
|
@ -680,6 +759,10 @@ namespace ams::dmnt::cheat::impl {
|
|||
/* Cancel process guard. */
|
||||
proc_guard.Cancel();
|
||||
|
||||
/* Reset broken state. */
|
||||
this->broken_unsafe = false;
|
||||
this->unsafe_break_event.Signal();
|
||||
|
||||
/* If new process, start the process. */
|
||||
if (on_process_launch) {
|
||||
this->StartProcess(this->cheat_process_metadata.process_id);
|
||||
|
@ -1021,6 +1104,14 @@ namespace ams::dmnt::cheat::impl {
|
|||
return GetReference(g_cheat_process_manager).WriteCheatProcessMemoryUnsafe(process_addr, data, size);
|
||||
}
|
||||
|
||||
Result BreakCheatProcessUnsafe() {
|
||||
return GetReference(g_cheat_process_manager).BreakCheatProcessUnsafe();
|
||||
}
|
||||
|
||||
Result ContinueCheatProcessUnsafe() {
|
||||
return GetReference(g_cheat_process_manager).ContinueCheatProcessUnsafe();
|
||||
}
|
||||
|
||||
Result GetCheatProcessMappingCount(u64 *out_count) {
|
||||
return GetReference(g_cheat_process_manager).GetCheatProcessMappingCount(out_count);
|
||||
}
|
||||
|
@ -1041,6 +1132,14 @@ namespace ams::dmnt::cheat::impl {
|
|||
return GetReference(g_cheat_process_manager).QueryCheatProcessMemory(mapping, address);
|
||||
}
|
||||
|
||||
Result BreakCheatProcess() {
|
||||
return GetReference(g_cheat_process_manager).BreakCheatProcess();
|
||||
}
|
||||
|
||||
Result ContinueCheatProcess() {
|
||||
return GetReference(g_cheat_process_manager).ContinueCheatProcess();
|
||||
}
|
||||
|
||||
Result GetCheatCount(u64 *out_count) {
|
||||
return GetReference(g_cheat_process_manager).GetCheatCount(out_count);
|
||||
}
|
||||
|
@ -1065,6 +1164,18 @@ namespace ams::dmnt::cheat::impl {
|
|||
return GetReference(g_cheat_process_manager).RemoveCheat(cheat_id);
|
||||
}
|
||||
|
||||
Result ReadStaticRegister(u64 *out, size_t which) {
|
||||
return GetReference(g_cheat_process_manager).ReadStaticRegister(out, which);
|
||||
}
|
||||
|
||||
Result WriteStaticRegister(size_t which, u64 value) {
|
||||
return GetReference(g_cheat_process_manager).WriteStaticRegister(which, value);
|
||||
}
|
||||
|
||||
Result ResetStaticRegisters() {
|
||||
return GetReference(g_cheat_process_manager).ResetStaticRegisters();
|
||||
}
|
||||
|
||||
Result GetFrozenAddressCount(u64 *out_count) {
|
||||
return GetReference(g_cheat_process_manager).GetFrozenAddressCount(out_count);
|
||||
}
|
||||
|
|
|
@ -28,11 +28,16 @@ namespace ams::dmnt::cheat::impl {
|
|||
Result ReadCheatProcessMemoryUnsafe(u64 process_addr, void *out_data, size_t size);
|
||||
Result WriteCheatProcessMemoryUnsafe(u64 process_addr, void *data, size_t size);
|
||||
|
||||
Result BreakCheatProcessUnsafe();
|
||||
Result ContinueCheatProcessUnsafe();
|
||||
|
||||
Result GetCheatProcessMappingCount(u64 *out_count);
|
||||
Result GetCheatProcessMappings(MemoryInfo *mappings, size_t max_count, u64 *out_count, u64 offset);
|
||||
Result ReadCheatProcessMemory(u64 proc_addr, void *out_data, size_t size);
|
||||
Result WriteCheatProcessMemory(u64 proc_addr, const void *data, size_t size);
|
||||
Result QueryCheatProcessMemory(MemoryInfo *mapping, u64 address);
|
||||
Result BreakCheatProcess();
|
||||
Result ContinueCheatProcess();
|
||||
|
||||
Result GetCheatCount(u64 *out_count);
|
||||
Result GetCheats(CheatEntry *cheats, size_t max_count, u64 *out_count, u64 offset);
|
||||
|
@ -40,6 +45,9 @@ namespace ams::dmnt::cheat::impl {
|
|||
Result ToggleCheat(u32 cheat_id);
|
||||
Result AddCheat(u32 *out_id, const CheatDefinition &def, bool enabled);
|
||||
Result RemoveCheat(u32 cheat_id);
|
||||
Result ReadStaticRegister(u64 *out, size_t which);
|
||||
Result WriteStaticRegister(size_t which, u64 value);
|
||||
Result ResetStaticRegisters();
|
||||
|
||||
Result GetFrozenAddressCount(u64 *out_count);
|
||||
Result GetFrozenAddresses(FrozenAddressEntry *frz_addrs, size_t max_count, u64 *out_count, u64 offset);
|
||||
|
|
|
@ -237,6 +237,22 @@ namespace ams::dmnt::cheat::impl {
|
|||
this->LogToDebugFile("Act[%02x]: %d\n", i, opcode->save_restore_regmask.should_operate[i]);
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_ReadWriteStaticRegister:
|
||||
this->LogToDebugFile("Opcode: Read/Write Static Register\n");
|
||||
if (opcode->rw_static_reg.static_idx < NumReadableStaticRegisters) {
|
||||
this->LogToDebugFile("Op Type: ReadStaticRegister\n");
|
||||
} else {
|
||||
this->LogToDebugFile("Op Type: WriteStaticRegister\n");
|
||||
}
|
||||
this->LogToDebugFile("Reg Idx: %x\n", opcode->rw_static_reg.idx);
|
||||
this->LogToDebugFile("Stc Idx: %x\n", opcode->rw_static_reg.static_idx);
|
||||
break;
|
||||
case CheatVmOpcodeType_BreakProcess:
|
||||
this->LogToDebugFile("Opcode: Break Cheat Process\n");
|
||||
break;
|
||||
case CheatVmOpcodeType_ContinueProcess:
|
||||
this->LogToDebugFile("Opcode: Continue Cheat Process\n");
|
||||
break;
|
||||
case CheatVmOpcodeType_DebugLog:
|
||||
this->LogToDebugFile("Opcode: Debug Log\n");
|
||||
this->LogToDebugFile("Bit Width: %x\n", opcode->debug_log.bit_width);
|
||||
|
@ -570,6 +586,30 @@ namespace ams::dmnt::cheat::impl {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_ReadWriteStaticRegister:
|
||||
{
|
||||
/* C3000XXx */
|
||||
/* C3 = opcode 0xC3. */
|
||||
/* XX = static register index. */
|
||||
/* x = register index. */
|
||||
opcode.rw_static_reg.static_idx = ((first_dword >> 4) & 0xFF);
|
||||
opcode.rw_static_reg.idx = (first_dword & 0xF);
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_BreakProcess:
|
||||
{
|
||||
/* FF0????? */
|
||||
/* FF0 = opcode 0xFF0 */
|
||||
/* Breaks the current process. */
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_ContinueProcess:
|
||||
{
|
||||
/* FF1????? */
|
||||
/* FF1 = opcode 0xFF1 */
|
||||
/* Continues the current process. */
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_DebugLog:
|
||||
{
|
||||
/* FFFTIX## */
|
||||
|
@ -1174,6 +1214,21 @@ namespace ams::dmnt::cheat::impl {
|
|||
}
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_ReadWriteStaticRegister:
|
||||
if (cur_opcode.rw_static_reg.static_idx < NumReadableStaticRegisters) {
|
||||
/* Load a register with a static register. */
|
||||
this->registers[cur_opcode.rw_static_reg.idx] = this->static_registers[cur_opcode.rw_static_reg.static_idx];
|
||||
} else {
|
||||
/* Store a register to a static register. */
|
||||
this->static_registers[cur_opcode.rw_static_reg.static_idx] = this->registers[cur_opcode.rw_static_reg.idx];
|
||||
}
|
||||
break;
|
||||
case CheatVmOpcodeType_BreakProcess:
|
||||
dmnt::cheat::impl::BreakCheatProcessUnsafe();
|
||||
break;
|
||||
case CheatVmOpcodeType_ContinueProcess:
|
||||
dmnt::cheat::impl::ContinueCheatProcessUnsafe();
|
||||
break;
|
||||
case CheatVmOpcodeType_DebugLog:
|
||||
{
|
||||
/* Read value from memory. */
|
||||
|
|
|
@ -42,12 +42,15 @@ namespace ams::dmnt::cheat::impl {
|
|||
CheatVmOpcodeType_BeginRegisterConditionalBlock = 0xC0,
|
||||
CheatVmOpcodeType_SaveRestoreRegister = 0xC1,
|
||||
CheatVmOpcodeType_SaveRestoreRegisterMask = 0xC2,
|
||||
CheatVmOpcodeType_ReadWriteStaticRegister = 0xC3,
|
||||
|
||||
/* This is a meta entry, and not a real opcode. */
|
||||
/* This is to facilitate multi-nybble instruction decoding. */
|
||||
CheatVmOpcodeType_DoubleExtendedWidth = 0xF0,
|
||||
|
||||
/* Double-extended width opcodes. */
|
||||
CheatVmOpcodeType_BreakProcess = 0xFF0,
|
||||
CheatVmOpcodeType_ContinueProcess = 0xFF1,
|
||||
CheatVmOpcodeType_DebugLog = 0xFFF,
|
||||
};
|
||||
|
||||
|
@ -223,6 +226,11 @@ namespace ams::dmnt::cheat::impl {
|
|||
bool should_operate[0x10];
|
||||
};
|
||||
|
||||
struct ReadWriteStaticRegisterOpcode {
|
||||
u32 static_idx;
|
||||
u32 idx;
|
||||
};
|
||||
|
||||
struct DebugLogOpcode {
|
||||
u32 bit_width;
|
||||
u32 log_id;
|
||||
|
@ -252,6 +260,7 @@ namespace ams::dmnt::cheat::impl {
|
|||
BeginRegisterConditionalOpcode begin_reg_cond;
|
||||
SaveRestoreRegisterOpcode save_restore_reg;
|
||||
SaveRestoreRegisterMaskOpcode save_restore_regmask;
|
||||
ReadWriteStaticRegisterOpcode rw_static_reg;
|
||||
DebugLogOpcode debug_log;
|
||||
};
|
||||
};
|
||||
|
@ -260,6 +269,9 @@ namespace ams::dmnt::cheat::impl {
|
|||
public:
|
||||
constexpr static size_t MaximumProgramOpcodeCount = 0x400;
|
||||
constexpr static size_t NumRegisters = 0x10;
|
||||
constexpr static size_t NumReadableStaticRegisters = 0x80;
|
||||
constexpr static size_t NumWritableStaticRegisters = 0x80;
|
||||
constexpr static size_t NumStaticRegisters = NumReadableStaticRegisters + NumWritableStaticRegisters;
|
||||
private:
|
||||
size_t num_opcodes = 0;
|
||||
size_t instruction_ptr = 0;
|
||||
|
@ -268,6 +280,7 @@ namespace ams::dmnt::cheat::impl {
|
|||
u32 program[MaximumProgramOpcodeCount] = {0};
|
||||
u64 registers[NumRegisters] = {0};
|
||||
u64 saved_values[NumRegisters] = {0};
|
||||
u64 static_registers[NumStaticRegisters] = {0};
|
||||
size_t loop_tops[NumRegisters] = {0};
|
||||
private:
|
||||
bool DecodeNextOpcode(CheatVmOpcode *out);
|
||||
|
@ -294,6 +307,18 @@ namespace ams::dmnt::cheat::impl {
|
|||
|
||||
bool LoadProgram(const CheatEntry *cheats, size_t num_cheats);
|
||||
void Execute(const CheatProcessMetadata *metadata);
|
||||
|
||||
u64 GetStaticRegister(size_t which) const {
|
||||
return this->static_registers[which];
|
||||
}
|
||||
|
||||
void SetStaticRegister(size_t which, u64 value) {
|
||||
this->static_registers[which] = value;
|
||||
}
|
||||
|
||||
void ResetStaticRegisters() {
|
||||
std::memset(this->static_registers, 0, sizeof(this->static_registers));
|
||||
}
|
||||
#ifdef DMNT_CHEAT_VM_DEBUG_LOG
|
||||
private:
|
||||
fs::FileHandle debug_log_file;
|
||||
|
|
Loading…
Reference in a new issue