summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/debugger/gdbstub_arch.cpp4
-rw-r--r--src/core/memory/cheat_engine.cpp46
-rw-r--r--src/core/memory/cheat_engine.h8
-rw-r--r--src/core/memory/dmnt_cheat_types.h2
-rw-r--r--src/core/memory/dmnt_cheat_vm.cpp74
-rw-r--r--src/core/memory/dmnt_cheat_vm.h22
6 files changed, 105 insertions, 51 deletions
diff --git a/src/core/debugger/gdbstub_arch.cpp b/src/core/debugger/gdbstub_arch.cpp
index f2a407dc8..452f565be 100644
--- a/src/core/debugger/gdbstub_arch.cpp
+++ b/src/core/debugger/gdbstub_arch.cpp
@@ -383,7 +383,7 @@ std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const
383 } else if (id == CPSR_REGISTER) { 383 } else if (id == CPSR_REGISTER) {
384 return ValueToHex(context.pstate); 384 return ValueToHex(context.pstate);
385 } else if (id >= D0_REGISTER && id < Q0_REGISTER) { 385 } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
386 return ValueToHex(fprs[id - D0_REGISTER][0]); 386 return ValueToHex(fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2]);
387 } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { 387 } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
388 return ValueToHex(fprs[id - Q0_REGISTER]); 388 return ValueToHex(fprs[id - Q0_REGISTER]);
389 } else if (id == FPSCR_REGISTER) { 389 } else if (id == FPSCR_REGISTER) {
@@ -406,7 +406,7 @@ void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v
406 } else if (id == CPSR_REGISTER) { 406 } else if (id == CPSR_REGISTER) {
407 context.pstate = HexToValue<u32>(value); 407 context.pstate = HexToValue<u32>(value);
408 } else if (id >= D0_REGISTER && id < Q0_REGISTER) { 408 } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
409 fprs[id - D0_REGISTER] = {HexToValue<u64>(value), 0}; 409 fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2] = HexToValue<u64>(value);
410 } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { 410 } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
411 fprs[id - Q0_REGISTER] = HexToValue<u128>(value); 411 fprs[id - Q0_REGISTER] = HexToValue<u128>(value);
412 } else if (id == FPSCR_REGISTER) { 412 } else if (id == FPSCR_REGISTER) {
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 96fa7fa3a..14d1a3840 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -9,6 +9,7 @@
9#include "core/core_timing.h" 9#include "core/core_timing.h"
10#include "core/hle/kernel/k_page_table.h" 10#include "core/hle/kernel/k_page_table.h"
11#include "core/hle/kernel/k_process.h" 11#include "core/hle/kernel/k_process.h"
12#include "core/hle/kernel/k_process_page_table.h"
12#include "core/hle/service/hid/hid_server.h" 13#include "core/hle/service/hid/hid_server.h"
13#include "core/hle/service/sm/sm.h" 14#include "core/hle/service/sm/sm.h"
14#include "core/memory.h" 15#include "core/memory.h"
@@ -46,12 +47,23 @@ StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMeta
46 47
47StandardVmCallbacks::~StandardVmCallbacks() = default; 48StandardVmCallbacks::~StandardVmCallbacks() = default;
48 49
49void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) { 50void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) {
50 system.ApplicationMemory().ReadBlock(SanitizeAddress(address), data, size); 51 // Return zero on invalid address
52 if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
53 std::memset(data, 0, size);
54 return;
55 }
56
57 system.ApplicationMemory().ReadBlock(address, data, size);
51} 58}
52 59
53void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) { 60void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) {
54 system.ApplicationMemory().WriteBlock(SanitizeAddress(address), data, size); 61 // Skip invalid memory write address
62 if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
63 return;
64 }
65
66 system.ApplicationMemory().WriteBlock(address, data, size);
55} 67}
56 68
57u64 StandardVmCallbacks::HidKeysDown() { 69u64 StandardVmCallbacks::HidKeysDown() {
@@ -81,21 +93,25 @@ void StandardVmCallbacks::CommandLog(std::string_view data) {
81 data.back() == '\n' ? data.substr(0, data.size() - 1) : data); 93 data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
82} 94}
83 95
84VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const { 96bool StandardVmCallbacks::IsAddressInRange(VAddr in) const {
85 if ((in < metadata.main_nso_extents.base || 97 if ((in < metadata.main_nso_extents.base ||
86 in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) && 98 in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
87 (in < metadata.heap_extents.base || 99 (in < metadata.heap_extents.base ||
88 in >= metadata.heap_extents.base + metadata.heap_extents.size)) { 100 in >= metadata.heap_extents.base + metadata.heap_extents.size) &&
89 LOG_ERROR(CheatEngine, 101 (in < metadata.alias_extents.base ||
102 in >= metadata.heap_extents.base + metadata.alias_extents.size) &&
103 (in < metadata.aslr_extents.base ||
104 in >= metadata.heap_extents.base + metadata.aslr_extents.size)) {
105 LOG_DEBUG(CheatEngine,
90 "Cheat attempting to access memory at invalid address={:016X}, if this " 106 "Cheat attempting to access memory at invalid address={:016X}, if this "
91 "persists, " 107 "persists, "
92 "the cheat may be incorrect. However, this may be normal early in execution if " 108 "the cheat may be incorrect. However, this may be normal early in execution if "
93 "the game has not properly set up yet.", 109 "the game has not properly set up yet.",
94 in); 110 in);
95 return 0; ///< Invalid addresses will hard crash 111 return false; ///< Invalid addresses will hard crash
96 } 112 }
97 113
98 return in; 114 return true;
99} 115}
100 116
101CheatParser::~CheatParser() = default; 117CheatParser::~CheatParser() = default;
@@ -211,16 +227,14 @@ void CheatEngine::Initialize() {
211 .base = GetInteger(page_table.GetHeapRegionStart()), 227 .base = GetInteger(page_table.GetHeapRegionStart()),
212 .size = page_table.GetHeapRegionSize(), 228 .size = page_table.GetHeapRegionSize(),
213 }; 229 };
214 230 metadata.aslr_extents = {
215 metadata.address_space_extents = {
216 .base = GetInteger(page_table.GetAddressSpaceStart()),
217 .size = page_table.GetAddressSpaceSize(),
218 };
219
220 metadata.alias_extents = {
221 .base = GetInteger(page_table.GetAliasCodeRegionStart()), 231 .base = GetInteger(page_table.GetAliasCodeRegionStart()),
222 .size = page_table.GetAliasCodeRegionSize(), 232 .size = page_table.GetAliasCodeRegionSize(),
223 }; 233 };
234 metadata.alias_extents = {
235 .base = GetInteger(page_table.GetAliasRegionStart()),
236 .size = page_table.GetAliasRegionSize(),
237 };
224 238
225 is_pending_reload.exchange(true); 239 is_pending_reload.exchange(true);
226} 240}
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
index ced2168d1..619cabaa2 100644
--- a/src/core/memory/cheat_engine.h
+++ b/src/core/memory/cheat_engine.h
@@ -27,17 +27,17 @@ public:
27 StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_); 27 StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_);
28 ~StandardVmCallbacks() override; 28 ~StandardVmCallbacks() override;
29 29
30 void MemoryRead(VAddr address, void* data, u64 size) override; 30 void MemoryReadUnsafe(VAddr address, void* data, u64 size) override;
31 void MemoryWrite(VAddr address, const void* data, u64 size) override; 31 void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override;
32 u64 HidKeysDown() override; 32 u64 HidKeysDown() override;
33 void DebugLog(u8 id, u64 value) override; 33 void DebugLog(u8 id, u64 value) override;
34 void CommandLog(std::string_view data) override; 34 void CommandLog(std::string_view data) override;
35 35
36private: 36private:
37 VAddr SanitizeAddress(VAddr address) const; 37 bool IsAddressInRange(VAddr address) const;
38 38
39 const CheatProcessMetadata& metadata; 39 const CheatProcessMetadata& metadata;
40 System& system; 40 Core::System& system;
41}; 41};
42 42
43// Intermediary class that parses a text file or other disk format for storing cheats into a 43// Intermediary class that parses a text file or other disk format for storing cheats into a
diff --git a/src/core/memory/dmnt_cheat_types.h b/src/core/memory/dmnt_cheat_types.h
index c6b40e505..64c072d3d 100644
--- a/src/core/memory/dmnt_cheat_types.h
+++ b/src/core/memory/dmnt_cheat_types.h
@@ -18,7 +18,7 @@ struct CheatProcessMetadata {
18 MemoryRegionExtents main_nso_extents{}; 18 MemoryRegionExtents main_nso_extents{};
19 MemoryRegionExtents heap_extents{}; 19 MemoryRegionExtents heap_extents{};
20 MemoryRegionExtents alias_extents{}; 20 MemoryRegionExtents alias_extents{};
21 MemoryRegionExtents address_space_extents{}; 21 MemoryRegionExtents aslr_extents{};
22 std::array<u8, 0x20> main_nso_build_id{}; 22 std::array<u8, 0x20> main_nso_build_id{};
23}; 23};
24 24
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp
index 31ffc4fbb..8bc81e72d 100644
--- a/src/core/memory/dmnt_cheat_vm.cpp
+++ b/src/core/memory/dmnt_cheat_vm.cpp
@@ -322,8 +322,9 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
322 } break; 322 } break;
323 case CheatVmOpcodeType::EndConditionalBlock: { 323 case CheatVmOpcodeType::EndConditionalBlock: {
324 // 20000000 324 // 20000000
325 // There's actually nothing left to process here! 325 opcode.opcode = EndConditionalOpcode{
326 opcode.opcode = EndConditionalOpcode{}; 326 .is_else = ((first_dword >> 24) & 0xf) == 1,
327 };
327 } break; 328 } break;
328 case CheatVmOpcodeType::ControlLoop: { 329 case CheatVmOpcodeType::ControlLoop: {
329 // 300R0000 VVVVVVVV 330 // 300R0000 VVVVVVVV
@@ -555,6 +556,18 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
555 .idx = first_dword & 0xF, 556 .idx = first_dword & 0xF,
556 }; 557 };
557 } break; 558 } break;
559 case CheatVmOpcodeType::PauseProcess: {
560 /* FF0????? */
561 /* FF0 = opcode 0xFF0 */
562 /* Pauses the current process. */
563 opcode.opcode = PauseProcessOpcode{};
564 } break;
565 case CheatVmOpcodeType::ResumeProcess: {
566 /* FF0????? */
567 /* FF0 = opcode 0xFF0 */
568 /* Pauses the current process. */
569 opcode.opcode = ResumeProcessOpcode{};
570 } break;
558 case CheatVmOpcodeType::DebugLog: { 571 case CheatVmOpcodeType::DebugLog: {
559 // FFFTIX## 572 // FFFTIX##
560 // FFFTI0Ma aaaaaaaa 573 // FFFTI0Ma aaaaaaaa
@@ -621,7 +634,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
621 return valid; 634 return valid;
622} 635}
623 636
624void DmntCheatVm::SkipConditionalBlock() { 637void DmntCheatVm::SkipConditionalBlock(bool is_if) {
625 if (condition_depth > 0) { 638 if (condition_depth > 0) {
626 // We want to continue until we're out of the current block. 639 // We want to continue until we're out of the current block.
627 const std::size_t desired_depth = condition_depth - 1; 640 const std::size_t desired_depth = condition_depth - 1;
@@ -637,8 +650,12 @@ void DmntCheatVm::SkipConditionalBlock() {
637 // We also support nesting of conditional blocks, and Gateway does not. 650 // We also support nesting of conditional blocks, and Gateway does not.
638 if (skip_opcode.begin_conditional_block) { 651 if (skip_opcode.begin_conditional_block) {
639 condition_depth++; 652 condition_depth++;
640 } else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) { 653 } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&skip_opcode.opcode)) {
641 condition_depth--; 654 if (!end_cond->is_else) {
655 condition_depth--;
656 } else if (is_if && condition_depth - 1 == desired_depth) {
657 break;
658 }
642 } 659 }
643 } 660 }
644 } else { 661 } else {
@@ -675,6 +692,10 @@ u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
675 return metadata.main_nso_extents.base + rel_address; 692 return metadata.main_nso_extents.base + rel_address;
676 case MemoryAccessType::Heap: 693 case MemoryAccessType::Heap:
677 return metadata.heap_extents.base + rel_address; 694 return metadata.heap_extents.base + rel_address;
695 case MemoryAccessType::Alias:
696 return metadata.alias_extents.base + rel_address;
697 case MemoryAccessType::Aslr:
698 return metadata.aslr_extents.base + rel_address;
678 } 699 }
679} 700}
680 701
@@ -682,7 +703,6 @@ void DmntCheatVm::ResetState() {
682 registers.fill(0); 703 registers.fill(0);
683 saved_values.fill(0); 704 saved_values.fill(0);
684 loop_tops.fill(0); 705 loop_tops.fill(0);
685 static_registers.fill(0);
686 instruction_ptr = 0; 706 instruction_ptr = 0;
687 condition_depth = 0; 707 condition_depth = 0;
688 decode_success = true; 708 decode_success = true;
@@ -753,7 +773,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
753 case 2: 773 case 2:
754 case 4: 774 case 4:
755 case 8: 775 case 8:
756 callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width); 776 callbacks->MemoryWriteUnsafe(dst_address, &dst_value, store_static->bit_width);
757 break; 777 break;
758 } 778 }
759 } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) { 779 } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) {
@@ -766,7 +786,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
766 case 2: 786 case 2:
767 case 4: 787 case 4:
768 case 8: 788 case 8:
769 callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width); 789 callbacks->MemoryReadUnsafe(src_address, &src_value, begin_cond->bit_width);
770 break; 790 break;
771 } 791 }
772 // Check against condition. 792 // Check against condition.
@@ -794,13 +814,18 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
794 } 814 }
795 // Skip conditional block if condition not met. 815 // Skip conditional block if condition not met.
796 if (!cond_met) { 816 if (!cond_met) {
797 SkipConditionalBlock(); 817 SkipConditionalBlock(true);
798 } 818 }
799 } else if (std::holds_alternative<EndConditionalOpcode>(cur_opcode.opcode)) { 819 } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) {
800 // Decrement the condition depth. 820 if (end_cond->is_else) {
801 // We will assume, graciously, that mismatched conditional block ends are a nop. 821 /* Skip to the end of the conditional block. */
802 if (condition_depth > 0) { 822 this->SkipConditionalBlock(false);
803 condition_depth--; 823 } else {
824 /* Decrement the condition depth. */
825 /* We will assume, graciously, that mismatched conditional block ends are a nop. */
826 if (condition_depth > 0) {
827 condition_depth--;
828 }
804 } 829 }
805 } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) { 830 } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) {
806 if (ctrl_loop->start_loop) { 831 if (ctrl_loop->start_loop) {
@@ -832,8 +857,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
832 case 2: 857 case 2:
833 case 4: 858 case 4:
834 case 8: 859 case 8:
835 callbacks->MemoryRead(src_address, &registers[ldr_memory->reg_index], 860 callbacks->MemoryReadUnsafe(src_address, &registers[ldr_memory->reg_index],
836 ldr_memory->bit_width); 861 ldr_memory->bit_width);
837 break; 862 break;
838 } 863 }
839 } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) { 864 } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) {
@@ -849,7 +874,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
849 case 2: 874 case 2:
850 case 4: 875 case 4:
851 case 8: 876 case 8:
852 callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width); 877 callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_static->bit_width);
853 break; 878 break;
854 } 879 }
855 // Increment register if relevant. 880 // Increment register if relevant.
@@ -908,7 +933,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
908 // Check for keypress. 933 // Check for keypress.
909 if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) { 934 if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) {
910 // Keys not pressed. Skip conditional block. 935 // Keys not pressed. Skip conditional block.
911 SkipConditionalBlock(); 936 SkipConditionalBlock(true);
912 } 937 }
913 } else if (auto perform_math_reg = 938 } else if (auto perform_math_reg =
914 std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) { 939 std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) {
@@ -1007,7 +1032,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
1007 case 2: 1032 case 2:
1008 case 4: 1033 case 4:
1009 case 8: 1034 case 8:
1010 callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width); 1035 callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_register->bit_width);
1011 break; 1036 break;
1012 } 1037 }
1013 1038
@@ -1086,7 +1111,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
1086 case 2: 1111 case 2:
1087 case 4: 1112 case 4:
1088 case 8: 1113 case 8:
1089 callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width); 1114 callbacks->MemoryReadUnsafe(cond_address, &cond_value,
1115 begin_reg_cond->bit_width);
1090 break; 1116 break;
1091 } 1117 }
1092 } 1118 }
@@ -1116,7 +1142,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
1116 1142
1117 // Skip conditional block if condition not met. 1143 // Skip conditional block if condition not met.
1118 if (!cond_met) { 1144 if (!cond_met) {
1119 SkipConditionalBlock(); 1145 SkipConditionalBlock(true);
1120 } 1146 }
1121 } else if (auto save_restore_reg = 1147 } else if (auto save_restore_reg =
1122 std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) { 1148 std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) {
@@ -1178,6 +1204,10 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
1178 // Store a register to a static register. 1204 // Store a register to a static register.
1179 static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx]; 1205 static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx];
1180 } 1206 }
1207 } else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) {
1208 // TODO: Pause cheat process
1209 } else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) {
1210 // TODO: Resume cheat process
1181 } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) { 1211 } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
1182 // Read value from memory. 1212 // Read value from memory.
1183 u64 log_value = 0; 1213 u64 log_value = 0;
@@ -1224,7 +1254,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
1224 case 2: 1254 case 2:
1225 case 4: 1255 case 4:
1226 case 8: 1256 case 8:
1227 callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width); 1257 callbacks->MemoryReadUnsafe(val_address, &log_value, debug_log->bit_width);
1228 break; 1258 break;
1229 } 1259 }
1230 } 1260 }
diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h
index 641cb09c4..fed6a24ad 100644
--- a/src/core/memory/dmnt_cheat_vm.h
+++ b/src/core/memory/dmnt_cheat_vm.h
@@ -42,12 +42,16 @@ enum class CheatVmOpcodeType : u32 {
42 DoubleExtendedWidth = 0xF0, 42 DoubleExtendedWidth = 0xF0,
43 43
44 // Double-extended width opcodes. 44 // Double-extended width opcodes.
45 PauseProcess = 0xFF0,
46 ResumeProcess = 0xFF1,
45 DebugLog = 0xFFF, 47 DebugLog = 0xFFF,
46}; 48};
47 49
48enum class MemoryAccessType : u32 { 50enum class MemoryAccessType : u32 {
49 MainNso = 0, 51 MainNso = 0,
50 Heap = 1, 52 Heap = 1,
53 Alias = 2,
54 Aslr = 3,
51}; 55};
52 56
53enum class ConditionalComparisonType : u32 { 57enum class ConditionalComparisonType : u32 {
@@ -131,7 +135,9 @@ struct BeginConditionalOpcode {
131 VmInt value{}; 135 VmInt value{};
132}; 136};
133 137
134struct EndConditionalOpcode {}; 138struct EndConditionalOpcode {
139 bool is_else;
140};
135 141
136struct ControlLoopOpcode { 142struct ControlLoopOpcode {
137 bool start_loop{}; 143 bool start_loop{};
@@ -222,6 +228,10 @@ struct ReadWriteStaticRegisterOpcode {
222 u32 idx{}; 228 u32 idx{};
223}; 229};
224 230
231struct PauseProcessOpcode {};
232
233struct ResumeProcessOpcode {};
234
225struct DebugLogOpcode { 235struct DebugLogOpcode {
226 u32 bit_width{}; 236 u32 bit_width{};
227 u32 log_id{}; 237 u32 log_id{};
@@ -244,8 +254,8 @@ struct CheatVmOpcode {
244 PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, 254 PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
245 PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, 255 PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
246 BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, 256 BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
247 SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, DebugLogOpcode, 257 SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
248 UnrecognizedInstruction> 258 ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction>
249 opcode{}; 259 opcode{};
250}; 260};
251 261
@@ -256,8 +266,8 @@ public:
256 public: 266 public:
257 virtual ~Callbacks(); 267 virtual ~Callbacks();
258 268
259 virtual void MemoryRead(VAddr address, void* data, u64 size) = 0; 269 virtual void MemoryReadUnsafe(VAddr address, void* data, u64 size) = 0;
260 virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0; 270 virtual void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) = 0;
261 271
262 virtual u64 HidKeysDown() = 0; 272 virtual u64 HidKeysDown() = 0;
263 273
@@ -296,7 +306,7 @@ private:
296 std::array<std::size_t, NumRegisters> loop_tops{}; 306 std::array<std::size_t, NumRegisters> loop_tops{};
297 307
298 bool DecodeNextOpcode(CheatVmOpcode& out); 308 bool DecodeNextOpcode(CheatVmOpcode& out);
299 void SkipConditionalBlock(); 309 void SkipConditionalBlock(bool is_if);
300 void ResetState(); 310 void ResetState();
301 311
302 // For implementing the DebugLog opcode. 312 // For implementing the DebugLog opcode.