diff options
| -rw-r--r-- | src/core/core.cpp | 3 | ||||
| -rw-r--r-- | src/core/memory/cheat_engine.cpp | 6 | ||||
| -rw-r--r-- | src/core/memory/dmnt_cheat_vm.cpp | 1496 | ||||
| -rw-r--r-- | src/core/memory/dmnt_cheat_vm.h | 227 |
4 files changed, 862 insertions, 870 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp index fc7039421..76bb2bae9 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "core/file_sys/bis_factory.h" | 17 | #include "core/file_sys/bis_factory.h" |
| 18 | #include "core/file_sys/card_image.h" | 18 | #include "core/file_sys/card_image.h" |
| 19 | #include "core/file_sys/mode.h" | 19 | #include "core/file_sys/mode.h" |
| 20 | #include "core/file_sys/patch_manager.h" | ||
| 20 | #include "core/file_sys/registered_cache.h" | 21 | #include "core/file_sys/registered_cache.h" |
| 21 | #include "core/file_sys/romfs_factory.h" | 22 | #include "core/file_sys/romfs_factory.h" |
| 22 | #include "core/file_sys/savedata_factory.h" | 23 | #include "core/file_sys/savedata_factory.h" |
| @@ -43,8 +44,6 @@ | |||
| 43 | #include "core/settings.h" | 44 | #include "core/settings.h" |
| 44 | #include "core/telemetry_session.h" | 45 | #include "core/telemetry_session.h" |
| 45 | #include "core/tools/freezer.h" | 46 | #include "core/tools/freezer.h" |
| 46 | #include "file_sys/cheat_engine.h" | ||
| 47 | #include "file_sys/patch_manager.h" | ||
| 48 | #include "video_core/debug_utils/debug_utils.h" | 47 | #include "video_core/debug_utils/debug_utils.h" |
| 49 | #include "video_core/renderer_base.h" | 48 | #include "video_core/renderer_base.h" |
| 50 | #include "video_core/video_core.h" | 49 | #include "video_core/video_core.h" |
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index ea5c76fc0..b56cb0627 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -102,7 +102,7 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system, | |||
| 102 | std::optional<u64> current_entry = std::nullopt; | 102 | std::optional<u64> current_entry = std::nullopt; |
| 103 | 103 | ||
| 104 | for (std::size_t i = 0; i < data.size(); ++i) { | 104 | for (std::size_t i = 0; i < data.size(); ++i) { |
| 105 | if (std::isspace(data[i])) { | 105 | if (::isspace(data[i])) { |
| 106 | continue; | 106 | continue; |
| 107 | } | 107 | } |
| 108 | 108 | ||
| @@ -143,7 +143,7 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system, | |||
| 143 | '\0'; | 143 | '\0'; |
| 144 | 144 | ||
| 145 | i += name.length() + 1; | 145 | i += name.length() + 1; |
| 146 | } else if (std::isxdigit(data[i])) { | 146 | } else if (::isxdigit(data[i])) { |
| 147 | if (!current_entry || out[*current_entry].definition.num_opcodes >= | 147 | if (!current_entry || out[*current_entry].definition.num_opcodes >= |
| 148 | out[*current_entry].definition.opcodes.size()) { | 148 | out[*current_entry].definition.opcodes.size()) { |
| 149 | return {}; | 149 | return {}; |
| @@ -188,7 +188,7 @@ CheatEngine::~CheatEngine() { | |||
| 188 | 188 | ||
| 189 | void CheatEngine::Initialize() { | 189 | void CheatEngine::Initialize() { |
| 190 | event = core_timing.RegisterEvent( | 190 | event = core_timing.RegisterEvent( |
| 191 | "CheatEngine::FrameCallback::" + Common::HexArrayToString(metadata.main_nso_build_id), | 191 | "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), |
| 192 | [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); | 192 | [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); |
| 193 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); | 193 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); |
| 194 | 194 | ||
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp index a3f450dac..cc16d15a4 100644 --- a/src/core/memory/dmnt_cheat_vm.cpp +++ b/src/core/memory/dmnt_cheat_vm.cpp | |||
| @@ -29,264 +29,272 @@ | |||
| 29 | 29 | ||
| 30 | namespace Memory { | 30 | namespace Memory { |
| 31 | 31 | ||
| 32 | DmntCheatVm::DmntCheatVm(std::unique_ptr<Callbacks> callbacks) : callbacks(std::move(callbacks)) {} | ||
| 33 | |||
| 34 | DmntCheatVm::~DmntCheatVm() = default; | ||
| 35 | |||
| 32 | void DmntCheatVm::DebugLog(u32 log_id, u64 value) { | 36 | void DmntCheatVm::DebugLog(u32 log_id, u64 value) { |
| 33 | callbacks->DebugLog(static_cast<u8>(log_id), value); | 37 | callbacks->DebugLog(static_cast<u8>(log_id), value); |
| 34 | } | 38 | } |
| 35 | 39 | ||
| 36 | void DmntCheatVm::LogOpcode(const CheatVmOpcode& opcode) { | 40 | void DmntCheatVm::LogOpcode(const CheatVmOpcode& opcode) { |
| 37 | switch (opcode.opcode) { | 41 | if (auto store_static = std::get_if<StoreStaticOpcode>(&opcode.opcode)) { |
| 38 | case CheatVmOpcodeType_StoreStatic: | 42 | callbacks->CommandLog("Opcode: Store Static"); |
| 39 | this->LogToDebugFile("Opcode: Store Static\n"); | 43 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", store_static->bit_width)); |
| 40 | this->LogToDebugFile("Bit Width: %x\n", opcode.store_static.bit_width); | 44 | callbacks->CommandLog( |
| 41 | this->LogToDebugFile("Mem Type: %x\n", opcode.store_static.mem_type); | 45 | fmt::format("Mem Type: {:X}", static_cast<u32>(store_static->mem_type))); |
| 42 | this->LogToDebugFile("Reg Idx: %x\n", opcode.store_static.offset_register); | 46 | callbacks->CommandLog(fmt::format("Reg Idx: {:X}", store_static->offset_register)); |
| 43 | this->LogToDebugFile("Rel Addr: %lx\n", opcode.store_static.rel_address); | 47 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", store_static->rel_address)); |
| 44 | this->LogToDebugFile("Value: %lx\n", opcode.store_static.value.bit64); | 48 | callbacks->CommandLog(fmt::format("Value: {:X}", store_static->value.bit64)); |
| 45 | break; | 49 | } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&opcode.opcode)) { |
| 46 | case CheatVmOpcodeType_BeginConditionalBlock: | 50 | callbacks->CommandLog("Opcode: Begin Conditional"); |
| 47 | this->LogToDebugFile("Opcode: Begin Conditional\n"); | 51 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_cond->bit_width)); |
| 48 | this->LogToDebugFile("Bit Width: %x\n", opcode.begin_cond.bit_width); | 52 | callbacks->CommandLog( |
| 49 | this->LogToDebugFile("Mem Type: %x\n", opcode.begin_cond.mem_type); | 53 | fmt::format("Mem Type: {:X}", static_cast<u32>(begin_cond->mem_type))); |
| 50 | this->LogToDebugFile("Cond Type: %x\n", opcode.begin_cond.cond_type); | 54 | callbacks->CommandLog( |
| 51 | this->LogToDebugFile("Rel Addr: %lx\n", opcode.begin_cond.rel_address); | 55 | fmt::format("Cond Type: {:X}", static_cast<u32>(begin_cond->cond_type))); |
| 52 | this->LogToDebugFile("Value: %lx\n", opcode.begin_cond.value.bit64); | 56 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_cond->rel_address)); |
| 53 | break; | 57 | callbacks->CommandLog(fmt::format("Value: {:X}", begin_cond->value.bit64)); |
| 54 | case CheatVmOpcodeType_EndConditionalBlock: | 58 | } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&opcode.opcode)) { |
| 55 | this->LogToDebugFile("Opcode: End Conditional\n"); | 59 | callbacks->CommandLog("Opcode: End Conditional"); |
| 56 | break; | 60 | } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&opcode.opcode)) { |
| 57 | case CheatVmOpcodeType_ControlLoop: | 61 | if (ctrl_loop->start_loop) { |
| 58 | if (opcode.ctrl_loop.start_loop) { | 62 | callbacks->CommandLog("Opcode: Start Loop"); |
| 59 | this->LogToDebugFile("Opcode: Start Loop\n"); | 63 | callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index)); |
| 60 | this->LogToDebugFile("Reg Idx: %x\n", opcode.ctrl_loop.reg_index); | 64 | callbacks->CommandLog(fmt::format("Num Iters: {:X}", ctrl_loop->num_iters)); |
| 61 | this->LogToDebugFile("Num Iters: %x\n", opcode.ctrl_loop.num_iters); | ||
| 62 | } else { | 65 | } else { |
| 63 | this->LogToDebugFile("Opcode: End Loop\n"); | 66 | callbacks->CommandLog("Opcode: End Loop"); |
| 64 | this->LogToDebugFile("Reg Idx: %x\n", opcode.ctrl_loop.reg_index); | 67 | callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index)); |
| 65 | } | 68 | } |
| 66 | break; | 69 | } else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&opcode.opcode)) { |
| 67 | case CheatVmOpcodeType_LoadRegisterStatic: | 70 | callbacks->CommandLog("Opcode: Load Register Static"); |
| 68 | this->LogToDebugFile("Opcode: Load Register Static\n"); | 71 | callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_static->reg_index)); |
| 69 | this->LogToDebugFile("Reg Idx: %x\n", opcode.ldr_static.reg_index); | 72 | callbacks->CommandLog(fmt::format("Value: {:X}", ldr_static->value)); |
| 70 | this->LogToDebugFile("Value: %lx\n", opcode.ldr_static.value); | 73 | } else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&opcode.opcode)) { |
| 71 | break; | 74 | callbacks->CommandLog("Opcode: Load Register Memory"); |
| 72 | case CheatVmOpcodeType_LoadRegisterMemory: | 75 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", ldr_memory->bit_width)); |
| 73 | this->LogToDebugFile("Opcode: Load Register Memory\n"); | 76 | callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_memory->reg_index)); |
| 74 | this->LogToDebugFile("Bit Width: %x\n", opcode.ldr_memory.bit_width); | 77 | callbacks->CommandLog( |
| 75 | this->LogToDebugFile("Reg Idx: %x\n", opcode.ldr_memory.reg_index); | 78 | fmt::format("Mem Type: {:X}", static_cast<u32>(ldr_memory->mem_type))); |
| 76 | this->LogToDebugFile("Mem Type: %x\n", opcode.ldr_memory.mem_type); | 79 | callbacks->CommandLog(fmt::format("From Reg: {:d}", ldr_memory->load_from_reg)); |
| 77 | this->LogToDebugFile("From Reg: %d\n", opcode.ldr_memory.load_from_reg); | 80 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", ldr_memory->rel_address)); |
| 78 | this->LogToDebugFile("Rel Addr: %lx\n", opcode.ldr_memory.rel_address); | 81 | } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&opcode.opcode)) { |
| 79 | break; | 82 | callbacks->CommandLog("Opcode: Store Static to Address"); |
| 80 | case CheatVmOpcodeType_StoreStaticToAddress: | 83 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_static->bit_width)); |
| 81 | this->LogToDebugFile("Opcode: Store Static to Address\n"); | 84 | callbacks->CommandLog(fmt::format("Reg Idx: {:X}", str_static->reg_index)); |
| 82 | this->LogToDebugFile("Bit Width: %x\n", opcode.str_static.bit_width); | 85 | if (str_static->add_offset_reg) { |
| 83 | this->LogToDebugFile("Reg Idx: %x\n", opcode.str_static.reg_index); | 86 | callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_static->offset_reg_index)); |
| 84 | if (opcode.str_static.add_offset_reg) { | ||
| 85 | this->LogToDebugFile("O Reg Idx: %x\n", opcode.str_static.offset_reg_index); | ||
| 86 | } | 87 | } |
| 87 | this->LogToDebugFile("Incr Reg: %d\n", opcode.str_static.increment_reg); | 88 | callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_static->increment_reg)); |
| 88 | this->LogToDebugFile("Value: %lx\n", opcode.str_static.value); | 89 | callbacks->CommandLog(fmt::format("Value: {:X}", str_static->value)); |
| 89 | break; | 90 | } else if (auto perform_math_static = |
| 90 | case CheatVmOpcodeType_PerformArithmeticStatic: | 91 | std::get_if<PerformArithmeticStaticOpcode>(&opcode.opcode)) { |
| 91 | this->LogToDebugFile("Opcode: Perform Static Arithmetic\n"); | 92 | callbacks->CommandLog("Opcode: Perform Static Arithmetic"); |
| 92 | this->LogToDebugFile("Bit Width: %x\n", opcode.perform_math_static.bit_width); | 93 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_static->bit_width)); |
| 93 | this->LogToDebugFile("Reg Idx: %x\n", opcode.perform_math_static.reg_index); | 94 | callbacks->CommandLog(fmt::format("Reg Idx: {:X}", perform_math_static->reg_index)); |
| 94 | this->LogToDebugFile("Math Type: %x\n", opcode.perform_math_static.math_type); | 95 | callbacks->CommandLog( |
| 95 | this->LogToDebugFile("Value: %lx\n", opcode.perform_math_static.value); | 96 | fmt::format("Math Type: {:X}", static_cast<u32>(perform_math_static->math_type))); |
| 96 | break; | 97 | callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_static->value)); |
| 97 | case CheatVmOpcodeType_BeginKeypressConditionalBlock: | 98 | } else if (auto begin_keypress_cond = |
| 98 | this->LogToDebugFile("Opcode: Begin Keypress Conditional\n"); | 99 | std::get_if<BeginKeypressConditionalOpcode>(&opcode.opcode)) { |
| 99 | this->LogToDebugFile("Key Mask: %x\n", opcode.begin_keypress_cond.key_mask); | 100 | callbacks->CommandLog("Opcode: Begin Keypress Conditional"); |
| 100 | break; | 101 | callbacks->CommandLog(fmt::format("Key Mask: {:X}", begin_keypress_cond->key_mask)); |
| 101 | case CheatVmOpcodeType_PerformArithmeticRegister: | 102 | } else if (auto perform_math_reg = |
| 102 | this->LogToDebugFile("Opcode: Perform Register Arithmetic\n"); | 103 | std::get_if<PerformArithmeticRegisterOpcode>(&opcode.opcode)) { |
| 103 | this->LogToDebugFile("Bit Width: %x\n", opcode.perform_math_reg.bit_width); | 104 | callbacks->CommandLog("Opcode: Perform Register Arithmetic"); |
| 104 | this->LogToDebugFile("Dst Idx: %x\n", opcode.perform_math_reg.dst_reg_index); | 105 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_reg->bit_width)); |
| 105 | this->LogToDebugFile("Src1 Idx: %x\n", opcode.perform_math_reg.src_reg_1_index); | 106 | callbacks->CommandLog(fmt::format("Dst Idx: {:X}", perform_math_reg->dst_reg_index)); |
| 106 | if (opcode.perform_math_reg.has_immediate) { | 107 | callbacks->CommandLog(fmt::format("Src1 Idx: {:X}", perform_math_reg->src_reg_1_index)); |
| 107 | this->LogToDebugFile("Value: %lx\n", opcode.perform_math_reg.value.bit64); | 108 | if (perform_math_reg->has_immediate) { |
| 109 | callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_reg->value.bit64)); | ||
| 108 | } else { | 110 | } else { |
| 109 | this->LogToDebugFile("Src2 Idx: %x\n", opcode.perform_math_reg.src_reg_2_index); | 111 | callbacks->CommandLog( |
| 112 | fmt::format("Src2 Idx: {:X}", perform_math_reg->src_reg_2_index)); | ||
| 110 | } | 113 | } |
| 111 | break; | 114 | } else if (auto str_register = std::get_if<StoreRegisterToAddressOpcode>(&opcode.opcode)) { |
| 112 | case CheatVmOpcodeType_StoreRegisterToAddress: | 115 | callbacks->CommandLog("Opcode: Store Register to Address"); |
| 113 | this->LogToDebugFile("Opcode: Store Register to Address\n"); | 116 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_register->bit_width)); |
| 114 | this->LogToDebugFile("Bit Width: %x\n", opcode.str_register.bit_width); | 117 | callbacks->CommandLog(fmt::format("S Reg Idx: {:X}", str_register->str_reg_index)); |
| 115 | this->LogToDebugFile("S Reg Idx: %x\n", opcode.str_register.str_reg_index); | 118 | callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", str_register->addr_reg_index)); |
| 116 | this->LogToDebugFile("A Reg Idx: %x\n", opcode.str_register.addr_reg_index); | 119 | callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_register->increment_reg)); |
| 117 | this->LogToDebugFile("Incr Reg: %d\n", opcode.str_register.increment_reg); | 120 | switch (str_register->ofs_type) { |
| 118 | switch (opcode.str_register.ofs_type) { | 121 | case StoreRegisterOffsetType::None: |
| 119 | case StoreRegisterOffsetType_None: | ||
| 120 | break; | 122 | break; |
| 121 | case StoreRegisterOffsetType_Reg: | 123 | case StoreRegisterOffsetType::Reg: |
| 122 | this->LogToDebugFile("O Reg Idx: %x\n", opcode.str_register.ofs_reg_index); | 124 | callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_register->ofs_reg_index)); |
| 123 | break; | 125 | break; |
| 124 | case StoreRegisterOffsetType_Imm: | 126 | case StoreRegisterOffsetType::Imm: |
| 125 | this->LogToDebugFile("Rel Addr: %lx\n", opcode.str_register.rel_address); | 127 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address)); |
| 126 | break; | 128 | break; |
| 127 | case StoreRegisterOffsetType_MemReg: | 129 | case StoreRegisterOffsetType::MemReg: |
| 128 | this->LogToDebugFile("Mem Type: %x\n", opcode.str_register.mem_type); | 130 | callbacks->CommandLog( |
| 131 | fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type))); | ||
| 129 | break; | 132 | break; |
| 130 | case StoreRegisterOffsetType_MemImm: | 133 | case StoreRegisterOffsetType::MemImm: |
| 131 | case StoreRegisterOffsetType_MemImmReg: | 134 | case StoreRegisterOffsetType::MemImmReg: |
| 132 | this->LogToDebugFile("Mem Type: %x\n", opcode.str_register.mem_type); | 135 | callbacks->CommandLog( |
| 133 | this->LogToDebugFile("Rel Addr: %lx\n", opcode.str_register.rel_address); | 136 | fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type))); |
| 137 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address)); | ||
| 134 | break; | 138 | break; |
| 135 | } | 139 | } |
| 136 | break; | 140 | } else if (auto begin_reg_cond = std::get_if<BeginRegisterConditionalOpcode>(&opcode.opcode)) { |
| 137 | case CheatVmOpcodeType_BeginRegisterConditionalBlock: | 141 | callbacks->CommandLog("Opcode: Begin Register Conditional"); |
| 138 | this->LogToDebugFile("Opcode: Begin Register Conditional\n"); | 142 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_reg_cond->bit_width)); |
| 139 | this->LogToDebugFile("Bit Width: %x\n", opcode.begin_reg_cond.bit_width); | 143 | callbacks->CommandLog( |
| 140 | this->LogToDebugFile("Cond Type: %x\n", opcode.begin_reg_cond.cond_type); | 144 | fmt::format("Cond Type: {:X}", static_cast<u32>(begin_reg_cond->cond_type))); |
| 141 | this->LogToDebugFile("V Reg Idx: %x\n", opcode.begin_reg_cond.val_reg_index); | 145 | callbacks->CommandLog(fmt::format("V Reg Idx: {:X}", begin_reg_cond->val_reg_index)); |
| 142 | switch (opcode.begin_reg_cond.comp_type) { | 146 | switch (begin_reg_cond->comp_type) { |
| 143 | case CompareRegisterValueType_StaticValue: | 147 | case CompareRegisterValueType::StaticValue: |
| 144 | this->LogToDebugFile("Comp Type: Static Value\n"); | 148 | callbacks->CommandLog("Comp Type: Static Value"); |
| 145 | this->LogToDebugFile("Value: %lx\n", opcode.begin_reg_cond.value.bit64); | 149 | callbacks->CommandLog(fmt::format("Value: {:X}", begin_reg_cond->value.bit64)); |
| 146 | break; | 150 | break; |
| 147 | case CompareRegisterValueType_OtherRegister: | 151 | case CompareRegisterValueType::OtherRegister: |
| 148 | this->LogToDebugFile("Comp Type: Other Register\n"); | 152 | callbacks->CommandLog("Comp Type: Other Register"); |
| 149 | this->LogToDebugFile("X Reg Idx: %x\n", opcode.begin_reg_cond.other_reg_index); | 153 | callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", begin_reg_cond->other_reg_index)); |
| 150 | break; | 154 | break; |
| 151 | case CompareRegisterValueType_MemoryRelAddr: | 155 | case CompareRegisterValueType::MemoryRelAddr: |
| 152 | this->LogToDebugFile("Comp Type: Memory Relative Address\n"); | 156 | callbacks->CommandLog("Comp Type: Memory Relative Address"); |
| 153 | this->LogToDebugFile("Mem Type: %x\n", opcode.begin_reg_cond.mem_type); | 157 | callbacks->CommandLog( |
| 154 | this->LogToDebugFile("Rel Addr: %lx\n", opcode.begin_reg_cond.rel_address); | 158 | fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type))); |
| 159 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address)); | ||
| 155 | break; | 160 | break; |
| 156 | case CompareRegisterValueType_MemoryOfsReg: | 161 | case CompareRegisterValueType::MemoryOfsReg: |
| 157 | this->LogToDebugFile("Comp Type: Memory Offset Register\n"); | 162 | callbacks->CommandLog("Comp Type: Memory Offset Register"); |
| 158 | this->LogToDebugFile("Mem Type: %x\n", opcode.begin_reg_cond.mem_type); | 163 | callbacks->CommandLog( |
| 159 | this->LogToDebugFile("O Reg Idx: %x\n", opcode.begin_reg_cond.ofs_reg_index); | 164 | fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type))); |
| 165 | callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index)); | ||
| 160 | break; | 166 | break; |
| 161 | case CompareRegisterValueType_RegisterRelAddr: | 167 | case CompareRegisterValueType::RegisterRelAddr: |
| 162 | this->LogToDebugFile("Comp Type: Register Relative Address\n"); | 168 | callbacks->CommandLog("Comp Type: Register Relative Address"); |
| 163 | this->LogToDebugFile("A Reg Idx: %x\n", opcode.begin_reg_cond.addr_reg_index); | 169 | callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index)); |
| 164 | this->LogToDebugFile("Rel Addr: %lx\n", opcode.begin_reg_cond.rel_address); | 170 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address)); |
| 165 | break; | 171 | break; |
| 166 | case CompareRegisterValueType_RegisterOfsReg: | 172 | case CompareRegisterValueType::RegisterOfsReg: |
| 167 | this->LogToDebugFile("Comp Type: Register Offset Register\n"); | 173 | callbacks->CommandLog("Comp Type: Register Offset Register"); |
| 168 | this->LogToDebugFile("A Reg Idx: %x\n", opcode.begin_reg_cond.addr_reg_index); | 174 | callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index)); |
| 169 | this->LogToDebugFile("O Reg Idx: %x\n", opcode.begin_reg_cond.ofs_reg_index); | 175 | callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index)); |
| 170 | break; | 176 | break; |
| 171 | } | 177 | } |
| 172 | break; | 178 | } else if (auto save_restore_reg = std::get_if<SaveRestoreRegisterOpcode>(&opcode.opcode)) { |
| 173 | case CheatVmOpcodeType_SaveRestoreRegister: | 179 | callbacks->CommandLog("Opcode: Save or Restore Register"); |
| 174 | this->LogToDebugFile("Opcode: Save or Restore Register\n"); | 180 | callbacks->CommandLog(fmt::format("Dst Idx: {:X}", save_restore_reg->dst_index)); |
| 175 | this->LogToDebugFile("Dst Idx: %x\n", opcode.save_restore_reg.dst_index); | 181 | callbacks->CommandLog(fmt::format("Src Idx: {:X}", save_restore_reg->src_index)); |
| 176 | this->LogToDebugFile("Src Idx: %x\n", opcode.save_restore_reg.src_index); | 182 | callbacks->CommandLog( |
| 177 | this->LogToDebugFile("Op Type: %d\n", opcode.save_restore_reg.op_type); | 183 | fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_reg->op_type))); |
| 178 | break; | 184 | } else if (auto save_restore_regmask = |
| 179 | case CheatVmOpcodeType_SaveRestoreRegisterMask: | 185 | std::get_if<SaveRestoreRegisterMaskOpcode>(&opcode.opcode)) { |
| 180 | this->LogToDebugFile("Opcode: Save or Restore Register Mask\n"); | 186 | callbacks->CommandLog("Opcode: Save or Restore Register Mask"); |
| 181 | this->LogToDebugFile("Op Type: %d\n", opcode.save_restore_regmask.op_type); | 187 | callbacks->CommandLog( |
| 182 | for (size_t i = 0; i < NumRegisters; i++) { | 188 | fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_regmask->op_type))); |
| 183 | this->LogToDebugFile("Act[%02x]: %d\n", i, | 189 | for (std::size_t i = 0; i < NumRegisters; i++) { |
| 184 | opcode.save_restore_regmask.should_operate[i]); | 190 | callbacks->CommandLog( |
| 191 | fmt::format("Act[{:02X}]: {:d}", i, save_restore_regmask->should_operate[i])); | ||
| 185 | } | 192 | } |
| 186 | break; | 193 | } else if (auto debug_log = std::get_if<DebugLogOpcode>(&opcode.opcode)) { |
| 187 | case CheatVmOpcodeType_DebugLog: | 194 | callbacks->CommandLog("Opcode: Debug Log"); |
| 188 | this->LogToDebugFile("Opcode: Debug Log\n"); | 195 | callbacks->CommandLog(fmt::format("Bit Width: {:X}", debug_log->bit_width)); |
| 189 | this->LogToDebugFile("Bit Width: %x\n", opcode.debug_log.bit_width); | 196 | callbacks->CommandLog(fmt::format("Log ID: {:X}", debug_log->log_id)); |
| 190 | this->LogToDebugFile("Log ID: %x\n", opcode.debug_log.log_id); | 197 | callbacks->CommandLog( |
| 191 | this->LogToDebugFile("Val Type: %x\n", opcode.debug_log.val_type); | 198 | fmt::format("Val Type: {:X}", static_cast<u32>(debug_log->val_type))); |
| 192 | switch (opcode.debug_log.val_type) { | 199 | switch (debug_log->val_type) { |
| 193 | case DebugLogValueType_RegisterValue: | 200 | case DebugLogValueType::RegisterValue: |
| 194 | this->LogToDebugFile("Val Type: Register Value\n"); | 201 | callbacks->CommandLog("Val Type: Register Value"); |
| 195 | this->LogToDebugFile("X Reg Idx: %x\n", opcode.debug_log.val_reg_index); | 202 | callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", debug_log->val_reg_index)); |
| 196 | break; | 203 | break; |
| 197 | case DebugLogValueType_MemoryRelAddr: | 204 | case DebugLogValueType::MemoryRelAddr: |
| 198 | this->LogToDebugFile("Val Type: Memory Relative Address\n"); | 205 | callbacks->CommandLog("Val Type: Memory Relative Address"); |
| 199 | this->LogToDebugFile("Mem Type: %x\n", opcode.debug_log.mem_type); | 206 | callbacks->CommandLog( |
| 200 | this->LogToDebugFile("Rel Addr: %lx\n", opcode.debug_log.rel_address); | 207 | fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type))); |
| 208 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address)); | ||
| 201 | break; | 209 | break; |
| 202 | case DebugLogValueType_MemoryOfsReg: | 210 | case DebugLogValueType::MemoryOfsReg: |
| 203 | this->LogToDebugFile("Val Type: Memory Offset Register\n"); | 211 | callbacks->CommandLog("Val Type: Memory Offset Register"); |
| 204 | this->LogToDebugFile("Mem Type: %x\n", opcode.debug_log.mem_type); | 212 | callbacks->CommandLog( |
| 205 | this->LogToDebugFile("O Reg Idx: %x\n", opcode.debug_log.ofs_reg_index); | 213 | fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type))); |
| 214 | callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index)); | ||
| 206 | break; | 215 | break; |
| 207 | case DebugLogValueType_RegisterRelAddr: | 216 | case DebugLogValueType::RegisterRelAddr: |
| 208 | this->LogToDebugFile("Val Type: Register Relative Address\n"); | 217 | callbacks->CommandLog("Val Type: Register Relative Address"); |
| 209 | this->LogToDebugFile("A Reg Idx: %x\n", opcode.debug_log.addr_reg_index); | 218 | callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index)); |
| 210 | this->LogToDebugFile("Rel Addr: %lx\n", opcode.debug_log.rel_address); | 219 | callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address)); |
| 211 | break; | 220 | break; |
| 212 | case DebugLogValueType_RegisterOfsReg: | 221 | case DebugLogValueType::RegisterOfsReg: |
| 213 | this->LogToDebugFile("Val Type: Register Offset Register\n"); | 222 | callbacks->CommandLog("Val Type: Register Offset Register"); |
| 214 | this->LogToDebugFile("A Reg Idx: %x\n", opcode.debug_log.addr_reg_index); | 223 | callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index)); |
| 215 | this->LogToDebugFile("O Reg Idx: %x\n", opcode.debug_log.ofs_reg_index); | 224 | callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index)); |
| 216 | break; | 225 | break; |
| 217 | } | 226 | } |
| 218 | default: | 227 | } else if (auto instr = std::get_if<UnrecognizedInstruction>(&opcode.opcode)) { |
| 219 | this->LogToDebugFile("Unknown opcode: %x\n", opcode.opcode); | 228 | callbacks->CommandLog(fmt::format("Unknown opcode: {:X}", static_cast<u32>(instr->opcode))); |
| 220 | break; | ||
| 221 | } | 229 | } |
| 222 | } | 230 | } |
| 223 | 231 | ||
| 224 | DmntCheatVm::Callbacks::~Callbacks() = default; | 232 | DmntCheatVm::Callbacks::~Callbacks() = default; |
| 225 | 233 | ||
| 226 | bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | 234 | bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { |
| 227 | /* If we've ever seen a decode failure, return false. */ | 235 | // If we've ever seen a decode failure, return false. |
| 228 | bool valid = this->decode_success; | 236 | bool valid = decode_success; |
| 229 | CheatVmOpcode opcode = {}; | 237 | CheatVmOpcode opcode = {}; |
| 230 | SCOPE_EXIT({ | 238 | SCOPE_EXIT({ |
| 231 | this->decode_success &= valid; | 239 | decode_success &= valid; |
| 232 | if (valid) { | 240 | if (valid) { |
| 233 | out = opcode; | 241 | out = opcode; |
| 234 | } | 242 | } |
| 235 | }); | 243 | }); |
| 236 | 244 | ||
| 237 | /* Helper function for getting instruction dwords. */ | 245 | // Helper function for getting instruction dwords. |
| 238 | auto GetNextDword = [&]() { | 246 | const auto GetNextDword = [&] { |
| 239 | if (this->instruction_ptr >= this->num_opcodes) { | 247 | if (instruction_ptr >= num_opcodes) { |
| 240 | valid = false; | 248 | valid = false; |
| 241 | return static_cast<u32>(0); | 249 | return static_cast<u32>(0); |
| 242 | } | 250 | } |
| 243 | return this->program[this->instruction_ptr++]; | 251 | return program[instruction_ptr++]; |
| 244 | }; | 252 | }; |
| 245 | 253 | ||
| 246 | /* Helper function for parsing a VmInt. */ | 254 | // Helper function for parsing a VmInt. |
| 247 | auto GetNextVmInt = [&](const u32 bit_width) { | 255 | const auto GetNextVmInt = [&](const u32 bit_width) { |
| 248 | VmInt val = {0}; | 256 | VmInt val{}; |
| 249 | 257 | ||
| 250 | const u32 first_dword = GetNextDword(); | 258 | const u32 first_dword = GetNextDword(); |
| 251 | switch (bit_width) { | 259 | switch (bit_width) { |
| 252 | case 1: | 260 | case 1: |
| 253 | val.bit8 = (u8)first_dword; | 261 | val.bit8 = static_cast<u8>(first_dword); |
| 254 | break; | 262 | break; |
| 255 | case 2: | 263 | case 2: |
| 256 | val.bit16 = (u16)first_dword; | 264 | val.bit16 = static_cast<u16>(first_dword); |
| 257 | break; | 265 | break; |
| 258 | case 4: | 266 | case 4: |
| 259 | val.bit32 = first_dword; | 267 | val.bit32 = first_dword; |
| 260 | break; | 268 | break; |
| 261 | case 8: | 269 | case 8: |
| 262 | val.bit64 = (((u64)first_dword) << 32ul) | ((u64)GetNextDword()); | 270 | val.bit64 = (static_cast<u64>(first_dword) << 32ul) | static_cast<u64>(GetNextDword()); |
| 263 | break; | 271 | break; |
| 264 | } | 272 | } |
| 265 | 273 | ||
| 266 | return val; | 274 | return val; |
| 267 | }; | 275 | }; |
| 268 | 276 | ||
| 269 | /* Read opcode. */ | 277 | // Read opcode. |
| 270 | const u32 first_dword = GetNextDword(); | 278 | const u32 first_dword = GetNextDword(); |
| 271 | if (!valid) { | 279 | if (!valid) { |
| 272 | return valid; | 280 | return valid; |
| 273 | } | 281 | } |
| 274 | 282 | ||
| 275 | opcode.opcode = (CheatVmOpcodeType)(((first_dword >> 28) & 0xF)); | 283 | auto opcode_type = static_cast<CheatVmOpcodeType>(((first_dword >> 28) & 0xF)); |
| 276 | if (opcode.opcode >= CheatVmOpcodeType_ExtendedWidth) { | 284 | if (opcode_type >= CheatVmOpcodeType::ExtendedWidth) { |
| 277 | opcode.opcode = | 285 | opcode_type = static_cast<CheatVmOpcodeType>((static_cast<u32>(opcode_type) << 4) | |
| 278 | (CheatVmOpcodeType)((((u32)opcode.opcode) << 4) | ((first_dword >> 24) & 0xF)); | 286 | ((first_dword >> 24) & 0xF)); |
| 279 | } | 287 | } |
| 280 | if (opcode.opcode >= CheatVmOpcodeType_DoubleExtendedWidth) { | 288 | if (opcode_type >= CheatVmOpcodeType::DoubleExtendedWidth) { |
| 281 | opcode.opcode = | 289 | opcode_type = static_cast<CheatVmOpcodeType>((static_cast<u32>(opcode_type) << 4) | |
| 282 | (CheatVmOpcodeType)((((u32)opcode.opcode) << 4) | ((first_dword >> 20) & 0xF)); | 290 | ((first_dword >> 20) & 0xF)); |
| 283 | } | 291 | } |
| 284 | 292 | ||
| 285 | /* detect condition start. */ | 293 | // detect condition start. |
| 286 | switch (opcode.opcode) { | 294 | switch (opcode_type) { |
| 287 | case CheatVmOpcodeType_BeginConditionalBlock: | 295 | case CheatVmOpcodeType::BeginConditionalBlock: |
| 288 | case CheatVmOpcodeType_BeginKeypressConditionalBlock: | 296 | case CheatVmOpcodeType::BeginKeypressConditionalBlock: |
| 289 | case CheatVmOpcodeType_BeginRegisterConditionalBlock: | 297 | case CheatVmOpcodeType::BeginRegisterConditionalBlock: |
| 290 | opcode.begin_conditional_block = true; | 298 | opcode.begin_conditional_block = true; |
| 291 | break; | 299 | break; |
| 292 | default: | 300 | default: |
| @@ -294,299 +302,335 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 294 | break; | 302 | break; |
| 295 | } | 303 | } |
| 296 | 304 | ||
| 297 | switch (opcode.opcode) { | 305 | switch (opcode_type) { |
| 298 | case CheatVmOpcodeType_StoreStatic: { | 306 | case CheatVmOpcodeType::StoreStatic: { |
| 299 | /* 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY) */ | 307 | StoreStaticOpcode store_static{}; |
| 300 | /* Read additional words. */ | 308 | // 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY) |
| 309 | // Read additional words. | ||
| 301 | const u32 second_dword = GetNextDword(); | 310 | const u32 second_dword = GetNextDword(); |
| 302 | opcode.store_static.bit_width = (first_dword >> 24) & 0xF; | 311 | store_static.bit_width = (first_dword >> 24) & 0xF; |
| 303 | opcode.store_static.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF); | 312 | store_static.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); |
| 304 | opcode.store_static.offset_register = ((first_dword >> 16) & 0xF); | 313 | store_static.offset_register = ((first_dword >> 16) & 0xF); |
| 305 | opcode.store_static.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword); | 314 | store_static.rel_address = |
| 306 | opcode.store_static.value = GetNextVmInt(opcode.store_static.bit_width); | 315 | (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); |
| 316 | store_static.value = GetNextVmInt(store_static.bit_width); | ||
| 317 | opcode.opcode = store_static; | ||
| 307 | } break; | 318 | } break; |
| 308 | case CheatVmOpcodeType_BeginConditionalBlock: { | 319 | case CheatVmOpcodeType::BeginConditionalBlock: { |
| 309 | /* 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY) */ | 320 | BeginConditionalOpcode begin_cond{}; |
| 310 | /* Read additional words. */ | 321 | // 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY) |
| 322 | // Read additional words. | ||
| 311 | const u32 second_dword = GetNextDword(); | 323 | const u32 second_dword = GetNextDword(); |
| 312 | opcode.begin_cond.bit_width = (first_dword >> 24) & 0xF; | 324 | begin_cond.bit_width = (first_dword >> 24) & 0xF; |
| 313 | opcode.begin_cond.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF); | 325 | begin_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); |
| 314 | opcode.begin_cond.cond_type = (ConditionalComparisonType)((first_dword >> 16) & 0xF); | 326 | begin_cond.cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF); |
| 315 | opcode.begin_cond.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword); | 327 | begin_cond.rel_address = |
| 316 | opcode.begin_cond.value = GetNextVmInt(opcode.store_static.bit_width); | 328 | (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); |
| 329 | begin_cond.value = GetNextVmInt(begin_cond.bit_width); | ||
| 330 | opcode.opcode = begin_cond; | ||
| 317 | } break; | 331 | } break; |
| 318 | case CheatVmOpcodeType_EndConditionalBlock: { | 332 | case CheatVmOpcodeType::EndConditionalBlock: { |
| 319 | /* 20000000 */ | 333 | // 20000000 |
| 320 | /* There's actually nothing left to process here! */ | 334 | // There's actually nothing left to process here! |
| 335 | opcode.opcode = EndConditionalOpcode{}; | ||
| 321 | } break; | 336 | } break; |
| 322 | case CheatVmOpcodeType_ControlLoop: { | 337 | case CheatVmOpcodeType::ControlLoop: { |
| 323 | /* 300R0000 VVVVVVVV */ | 338 | ControlLoopOpcode ctrl_loop{}; |
| 324 | /* 310R0000 */ | 339 | // 300R0000 VVVVVVVV |
| 325 | /* Parse register, whether loop start or loop end. */ | 340 | // 310R0000 |
| 326 | opcode.ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0; | 341 | // Parse register, whether loop start or loop end. |
| 327 | opcode.ctrl_loop.reg_index = ((first_dword >> 20) & 0xF); | 342 | ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0; |
| 343 | ctrl_loop.reg_index = ((first_dword >> 20) & 0xF); | ||
| 328 | 344 | ||
| 329 | /* Read number of iters if loop start. */ | 345 | // Read number of iters if loop start. |
| 330 | if (opcode.ctrl_loop.start_loop) { | 346 | if (ctrl_loop.start_loop) { |
| 331 | opcode.ctrl_loop.num_iters = GetNextDword(); | 347 | ctrl_loop.num_iters = GetNextDword(); |
| 332 | } | 348 | } |
| 349 | opcode.opcode = ctrl_loop; | ||
| 333 | } break; | 350 | } break; |
| 334 | case CheatVmOpcodeType_LoadRegisterStatic: { | 351 | case CheatVmOpcodeType::LoadRegisterStatic: { |
| 335 | /* 400R0000 VVVVVVVV VVVVVVVV */ | 352 | LoadRegisterStaticOpcode ldr_static{}; |
| 336 | /* Read additional words. */ | 353 | // 400R0000 VVVVVVVV VVVVVVVV |
| 337 | opcode.ldr_static.reg_index = ((first_dword >> 16) & 0xF); | 354 | // Read additional words. |
| 338 | opcode.ldr_static.value = (((u64)GetNextDword()) << 32ul) | ((u64)GetNextDword()); | 355 | ldr_static.reg_index = ((first_dword >> 16) & 0xF); |
| 356 | ldr_static.value = | ||
| 357 | (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword()); | ||
| 358 | opcode.opcode = ldr_static; | ||
| 339 | } break; | 359 | } break; |
| 340 | case CheatVmOpcodeType_LoadRegisterMemory: { | 360 | case CheatVmOpcodeType::LoadRegisterMemory: { |
| 341 | /* 5TMRI0AA AAAAAAAA */ | 361 | LoadRegisterMemoryOpcode ldr_memory{}; |
| 342 | /* Read additional words. */ | 362 | // 5TMRI0AA AAAAAAAA |
| 363 | // Read additional words. | ||
| 343 | const u32 second_dword = GetNextDword(); | 364 | const u32 second_dword = GetNextDword(); |
| 344 | opcode.ldr_memory.bit_width = (first_dword >> 24) & 0xF; | 365 | ldr_memory.bit_width = (first_dword >> 24) & 0xF; |
| 345 | opcode.ldr_memory.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF); | 366 | ldr_memory.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); |
| 346 | opcode.ldr_memory.reg_index = ((first_dword >> 16) & 0xF); | 367 | ldr_memory.reg_index = ((first_dword >> 16) & 0xF); |
| 347 | opcode.ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0; | 368 | ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0; |
| 348 | opcode.ldr_memory.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword); | 369 | ldr_memory.rel_address = |
| 370 | (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); | ||
| 371 | opcode.opcode = ldr_memory; | ||
| 349 | } break; | 372 | } break; |
| 350 | case CheatVmOpcodeType_StoreStaticToAddress: { | 373 | case CheatVmOpcodeType::StoreStaticToAddress: { |
| 351 | /* 6T0RIor0 VVVVVVVV VVVVVVVV */ | 374 | StoreStaticToAddressOpcode str_static{}; |
| 352 | /* Read additional words. */ | 375 | // 6T0RIor0 VVVVVVVV VVVVVVVV |
| 353 | opcode.str_static.bit_width = (first_dword >> 24) & 0xF; | 376 | // Read additional words. |
| 354 | opcode.str_static.reg_index = ((first_dword >> 16) & 0xF); | 377 | str_static.bit_width = (first_dword >> 24) & 0xF; |
| 355 | opcode.str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0; | 378 | str_static.reg_index = ((first_dword >> 16) & 0xF); |
| 356 | opcode.str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0; | 379 | str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0; |
| 357 | opcode.str_static.offset_reg_index = ((first_dword >> 4) & 0xF); | 380 | str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0; |
| 358 | opcode.str_static.value = (((u64)GetNextDword()) << 32ul) | ((u64)GetNextDword()); | 381 | str_static.offset_reg_index = ((first_dword >> 4) & 0xF); |
| 382 | str_static.value = | ||
| 383 | (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword()); | ||
| 384 | opcode.opcode = str_static; | ||
| 359 | } break; | 385 | } break; |
| 360 | case CheatVmOpcodeType_PerformArithmeticStatic: { | 386 | case CheatVmOpcodeType::PerformArithmeticStatic: { |
| 361 | /* 7T0RC000 VVVVVVVV */ | 387 | PerformArithmeticStaticOpcode perform_math_static{}; |
| 362 | /* Read additional words. */ | 388 | // 7T0RC000 VVVVVVVV |
| 363 | opcode.perform_math_static.bit_width = (first_dword >> 24) & 0xF; | 389 | // Read additional words. |
| 364 | opcode.perform_math_static.reg_index = ((first_dword >> 16) & 0xF); | 390 | perform_math_static.bit_width = (first_dword >> 24) & 0xF; |
| 365 | opcode.perform_math_static.math_type = (RegisterArithmeticType)((first_dword >> 12) & 0xF); | 391 | perform_math_static.reg_index = ((first_dword >> 16) & 0xF); |
| 366 | opcode.perform_math_static.value = GetNextDword(); | 392 | perform_math_static.math_type = |
| 393 | static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF); | ||
| 394 | perform_math_static.value = GetNextDword(); | ||
| 395 | opcode.opcode = perform_math_static; | ||
| 367 | } break; | 396 | } break; |
| 368 | case CheatVmOpcodeType_BeginKeypressConditionalBlock: { | 397 | case CheatVmOpcodeType::BeginKeypressConditionalBlock: { |
| 369 | /* 8kkkkkkk */ | 398 | BeginKeypressConditionalOpcode begin_keypress_cond{}; |
| 370 | /* Just parse the mask. */ | 399 | // 8kkkkkkk |
| 371 | opcode.begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF; | 400 | // Just parse the mask. |
| 401 | begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF; | ||
| 372 | } break; | 402 | } break; |
| 373 | case CheatVmOpcodeType_PerformArithmeticRegister: { | 403 | case CheatVmOpcodeType::PerformArithmeticRegister: { |
| 374 | /* 9TCRSIs0 (VVVVVVVV (VVVVVVVV)) */ | 404 | PerformArithmeticRegisterOpcode perform_math_reg{}; |
| 375 | opcode.perform_math_reg.bit_width = (first_dword >> 24) & 0xF; | 405 | // 9TCRSIs0 (VVVVVVVV (VVVVVVVV)) |
| 376 | opcode.perform_math_reg.math_type = (RegisterArithmeticType)((first_dword >> 20) & 0xF); | 406 | perform_math_reg.bit_width = (first_dword >> 24) & 0xF; |
| 377 | opcode.perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF); | 407 | perform_math_reg.math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF); |
| 378 | opcode.perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF); | 408 | perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF); |
| 379 | opcode.perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0; | 409 | perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF); |
| 380 | if (opcode.perform_math_reg.has_immediate) { | 410 | perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0; |
| 381 | opcode.perform_math_reg.src_reg_2_index = 0; | 411 | if (perform_math_reg.has_immediate) { |
| 382 | opcode.perform_math_reg.value = GetNextVmInt(opcode.perform_math_reg.bit_width); | 412 | perform_math_reg.src_reg_2_index = 0; |
| 413 | perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width); | ||
| 383 | } else { | 414 | } else { |
| 384 | opcode.perform_math_reg.src_reg_2_index = ((first_dword >> 4) & 0xF); | 415 | perform_math_reg.src_reg_2_index = ((first_dword >> 4) & 0xF); |
| 385 | } | 416 | } |
| 417 | opcode.opcode = perform_math_reg; | ||
| 386 | } break; | 418 | } break; |
| 387 | case CheatVmOpcodeType_StoreRegisterToAddress: { | 419 | case CheatVmOpcodeType::StoreRegisterToAddress: { |
| 388 | /* ATSRIOxa (aaaaaaaa) */ | 420 | StoreRegisterToAddressOpcode str_register{}; |
| 389 | /* A = opcode 10 */ | 421 | // ATSRIOxa (aaaaaaaa) |
| 390 | /* T = bit width */ | 422 | // A = opcode 10 |
| 391 | /* S = src register index */ | 423 | // T = bit width |
| 392 | /* R = address register index */ | 424 | // S = src register index |
| 393 | /* I = 1 if increment address register, 0 if not increment address register */ | 425 | // R = address register index |
| 394 | /* O = offset type, 0 = None, 1 = Register, 2 = Immediate, 3 = Memory Region, | 426 | // I = 1 if increment address register, 0 if not increment address register |
| 395 | 4 = Memory Region + Relative Address (ignore address register), 5 = Memory Region + | 427 | // O = offset type, 0 = None, 1 = Register, 2 = Immediate, 3 = Memory Region, |
| 396 | Relative Address */ | 428 | // 4 = Memory Region + Relative Address (ignore address register), 5 = Memory Region + |
| 397 | /* x = offset register (for offset type 1), memory type (for offset type 3) */ | 429 | // Relative Address |
| 398 | /* a = relative address (for offset type 2+3) */ | 430 | // x = offset register (for offset type 1), memory type (for offset type 3) |
| 399 | opcode.str_register.bit_width = (first_dword >> 24) & 0xF; | 431 | // a = relative address (for offset type 2+3) |
| 400 | opcode.str_register.str_reg_index = ((first_dword >> 20) & 0xF); | 432 | str_register.bit_width = (first_dword >> 24) & 0xF; |
| 401 | opcode.str_register.addr_reg_index = ((first_dword >> 16) & 0xF); | 433 | str_register.str_reg_index = ((first_dword >> 20) & 0xF); |
| 402 | opcode.str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0; | 434 | str_register.addr_reg_index = ((first_dword >> 16) & 0xF); |
| 403 | opcode.str_register.ofs_type = (StoreRegisterOffsetType)(((first_dword >> 8) & 0xF)); | 435 | str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0; |
| 404 | opcode.str_register.ofs_reg_index = ((first_dword >> 4) & 0xF); | 436 | str_register.ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF)); |
| 405 | switch (opcode.str_register.ofs_type) { | 437 | str_register.ofs_reg_index = ((first_dword >> 4) & 0xF); |
| 406 | case StoreRegisterOffsetType_None: | 438 | switch (str_register.ofs_type) { |
| 407 | case StoreRegisterOffsetType_Reg: | 439 | case StoreRegisterOffsetType::None: |
| 408 | /* Nothing more to do */ | 440 | case StoreRegisterOffsetType::Reg: |
| 441 | // Nothing more to do | ||
| 409 | break; | 442 | break; |
| 410 | case StoreRegisterOffsetType_Imm: | 443 | case StoreRegisterOffsetType::Imm: |
| 411 | opcode.str_register.rel_address = | 444 | str_register.rel_address = |
| 412 | (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword())); | 445 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); |
| 413 | break; | 446 | break; |
| 414 | case StoreRegisterOffsetType_MemReg: | 447 | case StoreRegisterOffsetType::MemReg: |
| 415 | opcode.str_register.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF); | 448 | str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 416 | break; | 449 | break; |
| 417 | case StoreRegisterOffsetType_MemImm: | 450 | case StoreRegisterOffsetType::MemImm: |
| 418 | case StoreRegisterOffsetType_MemImmReg: | 451 | case StoreRegisterOffsetType::MemImmReg: |
| 419 | opcode.str_register.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF); | 452 | str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 420 | opcode.str_register.rel_address = | 453 | str_register.rel_address = |
| 421 | (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword())); | 454 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); |
| 422 | break; | 455 | break; |
| 423 | default: | 456 | default: |
| 424 | opcode.str_register.ofs_type = StoreRegisterOffsetType_None; | 457 | str_register.ofs_type = StoreRegisterOffsetType::None; |
| 425 | break; | 458 | break; |
| 426 | } | 459 | } |
| 460 | opcode.opcode = str_register; | ||
| 427 | } break; | 461 | } break; |
| 428 | case CheatVmOpcodeType_BeginRegisterConditionalBlock: { | 462 | case CheatVmOpcodeType::BeginRegisterConditionalBlock: { |
| 429 | /* C0TcSX## */ | 463 | BeginRegisterConditionalOpcode begin_reg_cond{}; |
| 430 | /* C0TcS0Ma aaaaaaaa */ | 464 | // C0TcSX## |
| 431 | /* C0TcS1Mr */ | 465 | // C0TcS0Ma aaaaaaaa |
| 432 | /* C0TcS2Ra aaaaaaaa */ | 466 | // C0TcS1Mr |
| 433 | /* C0TcS3Rr */ | 467 | // C0TcS2Ra aaaaaaaa |
| 434 | /* C0TcS400 VVVVVVVV (VVVVVVVV) */ | 468 | // C0TcS3Rr |
| 435 | /* C0TcS5X0 */ | 469 | // C0TcS400 VVVVVVVV (VVVVVVVV) |
| 436 | /* C0 = opcode 0xC0 */ | 470 | // C0TcS5X0 |
| 437 | /* T = bit width */ | 471 | // C0 = opcode 0xC0 |
| 438 | /* c = condition type. */ | 472 | // T = bit width |
| 439 | /* S = source register. */ | 473 | // c = condition type. |
| 440 | /* X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset | 474 | // S = source register. |
| 441 | * register, */ | 475 | // X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset |
| 442 | /* 2 = register with relative offset, 3 = register with offset register, 4 = static | 476 | // register, |
| 443 | * value, 5 = other register. */ | 477 | // 2 = register with relative offset, 3 = register with offset register, 4 = static |
| 444 | /* M = memory type. */ | 478 | // value, 5 = other register. |
| 445 | /* R = address register. */ | 479 | // M = memory type. |
| 446 | /* a = relative address. */ | 480 | // R = address register. |
| 447 | /* r = offset register. */ | 481 | // a = relative address. |
| 448 | /* X = other register. */ | 482 | // r = offset register. |
| 449 | /* V = value. */ | 483 | // X = other register. |
| 450 | opcode.begin_reg_cond.bit_width = (first_dword >> 20) & 0xF; | 484 | // V = value. |
| 451 | opcode.begin_reg_cond.cond_type = (ConditionalComparisonType)((first_dword >> 16) & 0xF); | 485 | begin_reg_cond.bit_width = (first_dword >> 20) & 0xF; |
| 452 | opcode.begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF); | 486 | begin_reg_cond.cond_type = |
| 453 | opcode.begin_reg_cond.comp_type = (CompareRegisterValueType)((first_dword >> 8) & 0xF); | 487 | static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF); |
| 488 | begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF); | ||
| 489 | begin_reg_cond.comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF); | ||
| 454 | 490 | ||
| 455 | switch (opcode.begin_reg_cond.comp_type) { | 491 | switch (begin_reg_cond.comp_type) { |
| 456 | case CompareRegisterValueType_StaticValue: | 492 | case CompareRegisterValueType::StaticValue: |
| 457 | opcode.begin_reg_cond.value = GetNextVmInt(opcode.begin_reg_cond.bit_width); | 493 | begin_reg_cond.value = GetNextVmInt(begin_reg_cond.bit_width); |
| 458 | break; | 494 | break; |
| 459 | case CompareRegisterValueType_OtherRegister: | 495 | case CompareRegisterValueType::OtherRegister: |
| 460 | opcode.begin_reg_cond.other_reg_index = ((first_dword >> 4) & 0xF); | 496 | begin_reg_cond.other_reg_index = ((first_dword >> 4) & 0xF); |
| 461 | break; | 497 | break; |
| 462 | case CompareRegisterValueType_MemoryRelAddr: | 498 | case CompareRegisterValueType::MemoryRelAddr: |
| 463 | opcode.begin_reg_cond.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF); | 499 | begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 464 | opcode.begin_reg_cond.rel_address = | 500 | begin_reg_cond.rel_address = |
| 465 | (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword())); | 501 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); |
| 466 | break; | 502 | break; |
| 467 | case CompareRegisterValueType_MemoryOfsReg: | 503 | case CompareRegisterValueType::MemoryOfsReg: |
| 468 | opcode.begin_reg_cond.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF); | 504 | begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 469 | opcode.begin_reg_cond.ofs_reg_index = (first_dword & 0xF); | 505 | begin_reg_cond.ofs_reg_index = (first_dword & 0xF); |
| 470 | break; | 506 | break; |
| 471 | case CompareRegisterValueType_RegisterRelAddr: | 507 | case CompareRegisterValueType::RegisterRelAddr: |
| 472 | opcode.begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); | 508 | begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); |
| 473 | opcode.begin_reg_cond.rel_address = | 509 | begin_reg_cond.rel_address = |
| 474 | (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword())); | 510 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); |
| 475 | break; | 511 | break; |
| 476 | case CompareRegisterValueType_RegisterOfsReg: | 512 | case CompareRegisterValueType::RegisterOfsReg: |
| 477 | opcode.begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); | 513 | begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); |
| 478 | opcode.begin_reg_cond.ofs_reg_index = (first_dword & 0xF); | 514 | begin_reg_cond.ofs_reg_index = (first_dword & 0xF); |
| 479 | break; | 515 | break; |
| 480 | } | 516 | } |
| 517 | opcode.opcode = begin_reg_cond; | ||
| 481 | } break; | 518 | } break; |
| 482 | case CheatVmOpcodeType_SaveRestoreRegister: { | 519 | case CheatVmOpcodeType::SaveRestoreRegister: { |
| 483 | /* C10D0Sx0 */ | 520 | SaveRestoreRegisterOpcode save_restore_reg{}; |
| 484 | /* C1 = opcode 0xC1 */ | 521 | // C10D0Sx0 |
| 485 | /* D = destination index. */ | 522 | // C1 = opcode 0xC1 |
| 486 | /* S = source index. */ | 523 | // D = destination index. |
| 487 | /* x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring | 524 | // S = source index. |
| 488 | * a register. */ | 525 | // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring |
| 489 | /* NOTE: If we add more save slots later, current encoding is backwards compatible. */ | 526 | // a register. |
| 490 | opcode.save_restore_reg.dst_index = (first_dword >> 16) & 0xF; | 527 | // NOTE: If we add more save slots later, current encoding is backwards compatible. |
| 491 | opcode.save_restore_reg.src_index = (first_dword >> 8) & 0xF; | 528 | save_restore_reg.dst_index = (first_dword >> 16) & 0xF; |
| 492 | opcode.save_restore_reg.op_type = (SaveRestoreRegisterOpType)((first_dword >> 4) & 0xF); | 529 | save_restore_reg.src_index = (first_dword >> 8) & 0xF; |
| 530 | save_restore_reg.op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF); | ||
| 531 | opcode.opcode = save_restore_reg; | ||
| 493 | } break; | 532 | } break; |
| 494 | case CheatVmOpcodeType_SaveRestoreRegisterMask: { | 533 | case CheatVmOpcodeType::SaveRestoreRegisterMask: { |
| 495 | /* C2x0XXXX */ | 534 | SaveRestoreRegisterMaskOpcode save_restore_regmask{}; |
| 496 | /* C2 = opcode 0xC2 */ | 535 | // C2x0XXXX |
| 497 | /* x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring. */ | 536 | // C2 = opcode 0xC2 |
| 498 | /* X = 16-bit bitmask, bit i --> save or restore register i. */ | 537 | // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring. |
| 499 | opcode.save_restore_regmask.op_type = | 538 | // X = 16-bit bitmask, bit i --> save or restore register i. |
| 500 | (SaveRestoreRegisterOpType)((first_dword >> 20) & 0xF); | 539 | save_restore_regmask.op_type = |
| 501 | for (size_t i = 0; i < NumRegisters; i++) { | 540 | static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF); |
| 502 | opcode.save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0; | 541 | for (std::size_t i = 0; i < NumRegisters; i++) { |
| 542 | save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0; | ||
| 503 | } | 543 | } |
| 544 | opcode.opcode = save_restore_regmask; | ||
| 504 | } break; | 545 | } break; |
| 505 | case CheatVmOpcodeType_DebugLog: { | 546 | case CheatVmOpcodeType::DebugLog: { |
| 506 | /* FFFTIX## */ | 547 | DebugLogOpcode debug_log{}; |
| 507 | /* FFFTI0Ma aaaaaaaa */ | 548 | // FFFTIX## |
| 508 | /* FFFTI1Mr */ | 549 | // FFFTI0Ma aaaaaaaa |
| 509 | /* FFFTI2Ra aaaaaaaa */ | 550 | // FFFTI1Mr |
| 510 | /* FFFTI3Rr */ | 551 | // FFFTI2Ra aaaaaaaa |
| 511 | /* FFFTI4X0 */ | 552 | // FFFTI3Rr |
| 512 | /* FFF = opcode 0xFFF */ | 553 | // FFFTI4X0 |
| 513 | /* T = bit width. */ | 554 | // FFF = opcode 0xFFF |
| 514 | /* I = log id. */ | 555 | // T = bit width. |
| 515 | /* X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset | 556 | // I = log id. |
| 516 | * register, */ | 557 | // X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset |
| 517 | /* 2 = register with relative offset, 3 = register with offset register, 4 = register | 558 | // register, |
| 518 | * value. */ | 559 | // 2 = register with relative offset, 3 = register with offset register, 4 = register |
| 519 | /* M = memory type. */ | 560 | // value. |
| 520 | /* R = address register. */ | 561 | // M = memory type. |
| 521 | /* a = relative address. */ | 562 | // R = address register. |
| 522 | /* r = offset register. */ | 563 | // a = relative address. |
| 523 | /* X = value register. */ | 564 | // r = offset register. |
| 524 | opcode.debug_log.bit_width = (first_dword >> 16) & 0xF; | 565 | // X = value register. |
| 525 | opcode.debug_log.log_id = ((first_dword >> 12) & 0xF); | 566 | debug_log.bit_width = (first_dword >> 16) & 0xF; |
| 526 | opcode.debug_log.val_type = (DebugLogValueType)((first_dword >> 8) & 0xF); | 567 | debug_log.log_id = ((first_dword >> 12) & 0xF); |
| 568 | debug_log.val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF); | ||
| 527 | 569 | ||
| 528 | switch (opcode.debug_log.val_type) { | 570 | switch (debug_log.val_type) { |
| 529 | case DebugLogValueType_RegisterValue: | 571 | case DebugLogValueType::RegisterValue: |
| 530 | opcode.debug_log.val_reg_index = ((first_dword >> 4) & 0xF); | 572 | debug_log.val_reg_index = ((first_dword >> 4) & 0xF); |
| 531 | break; | 573 | break; |
| 532 | case DebugLogValueType_MemoryRelAddr: | 574 | case DebugLogValueType::MemoryRelAddr: |
| 533 | opcode.debug_log.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF); | 575 | debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 534 | opcode.debug_log.rel_address = | 576 | debug_log.rel_address = |
| 535 | (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword())); | 577 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); |
| 536 | break; | 578 | break; |
| 537 | case DebugLogValueType_MemoryOfsReg: | 579 | case DebugLogValueType::MemoryOfsReg: |
| 538 | opcode.debug_log.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF); | 580 | debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 539 | opcode.debug_log.ofs_reg_index = (first_dword & 0xF); | 581 | debug_log.ofs_reg_index = (first_dword & 0xF); |
| 540 | break; | 582 | break; |
| 541 | case DebugLogValueType_RegisterRelAddr: | 583 | case DebugLogValueType::RegisterRelAddr: |
| 542 | opcode.debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); | 584 | debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); |
| 543 | opcode.debug_log.rel_address = | 585 | debug_log.rel_address = |
| 544 | (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword())); | 586 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); |
| 545 | break; | 587 | break; |
| 546 | case DebugLogValueType_RegisterOfsReg: | 588 | case DebugLogValueType::RegisterOfsReg: |
| 547 | opcode.debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); | 589 | debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); |
| 548 | opcode.debug_log.ofs_reg_index = (first_dword & 0xF); | 590 | debug_log.ofs_reg_index = (first_dword & 0xF); |
| 549 | break; | 591 | break; |
| 550 | } | 592 | } |
| 593 | opcode.opcode = debug_log; | ||
| 551 | } break; | 594 | } break; |
| 552 | case CheatVmOpcodeType_ExtendedWidth: | 595 | case CheatVmOpcodeType::ExtendedWidth: |
| 553 | case CheatVmOpcodeType_DoubleExtendedWidth: | 596 | case CheatVmOpcodeType::DoubleExtendedWidth: |
| 554 | default: | 597 | default: |
| 555 | /* Unrecognized instruction cannot be decoded. */ | 598 | // Unrecognized instruction cannot be decoded. |
| 556 | valid = false; | 599 | valid = false; |
| 600 | opcode.opcode = UnrecognizedInstruction{opcode_type}; | ||
| 557 | break; | 601 | break; |
| 558 | } | 602 | } |
| 559 | 603 | ||
| 560 | /* End decoding. */ | 604 | // End decoding. |
| 561 | return valid; | 605 | return valid; |
| 562 | } | 606 | } |
| 563 | 607 | ||
| 564 | void DmntCheatVm::SkipConditionalBlock() { | 608 | void DmntCheatVm::SkipConditionalBlock() { |
| 565 | if (this->condition_depth > 0) { | 609 | if (condition_depth > 0) { |
| 566 | /* We want to continue until we're out of the current block. */ | 610 | // We want to continue until we're out of the current block. |
| 567 | const size_t desired_depth = this->condition_depth - 1; | 611 | const std::size_t desired_depth = condition_depth - 1; |
| 568 | 612 | ||
| 569 | CheatVmOpcode skip_opcode{}; | 613 | CheatVmOpcode skip_opcode{}; |
| 570 | while (this->condition_depth > desired_depth && this->DecodeNextOpcode(skip_opcode)) { | 614 | while (condition_depth > desired_depth && DecodeNextOpcode(skip_opcode)) { |
| 571 | /* Decode instructions until we see end of the current conditional block. */ | 615 | // Decode instructions until we see end of the current conditional block. |
| 572 | /* NOTE: This is broken in gateway's implementation. */ | 616 | // NOTE: This is broken in gateway's implementation. |
| 573 | /* Gateway currently checks for "0x2" instead of "0x20000000" */ | 617 | // Gateway currently checks for "0x2" instead of "0x20000000" |
| 574 | /* In addition, they do a linear scan instead of correctly decoding opcodes. */ | 618 | // In addition, they do a linear scan instead of correctly decoding opcodes. |
| 575 | /* This causes issues if "0x2" appears as an immediate in the conditional block... */ | 619 | // This causes issues if "0x2" appears as an immediate in the conditional block... |
| 576 | 620 | ||
| 577 | /* We also support nesting of conditional blocks, and Gateway does not. */ | 621 | // We also support nesting of conditional blocks, and Gateway does not. |
| 578 | if (skip_opcode.begin_conditional_block) { | 622 | if (skip_opcode.begin_conditional_block) { |
| 579 | this->condition_depth++; | 623 | condition_depth++; |
| 580 | } else if (skip_opcode.opcode == CheatVmOpcodeType_EndConditionalBlock) { | 624 | } else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) { |
| 581 | this->condition_depth--; | 625 | condition_depth--; |
| 582 | } | 626 | } |
| 583 | } | 627 | } |
| 584 | } else { | 628 | } else { |
| 585 | /* Skipping, but this->condition_depth = 0. */ | 629 | // Skipping, but condition_depth = 0. |
| 586 | /* This is an error condition. */ | 630 | // This is an error condition. |
| 587 | /* However, I don't actually believe it is possible for this to happen. */ | 631 | // However, I don't actually believe it is possible for this to happen. |
| 588 | /* I guess we'll throw a fatal error here, so as to encourage me to fix the VM */ | 632 | // I guess we'll throw a fatal error here, so as to encourage me to fix the VM |
| 589 | /* in the event that someone triggers it? I don't know how you'd do that. */ | 633 | // in the event that someone triggers it? I don't know how you'd do that. |
| 590 | UNREACHABLE_MSG("Invalid condition depth in DMNT Cheat VM"); | 634 | UNREACHABLE_MSG("Invalid condition depth in DMNT Cheat VM"); |
| 591 | } | 635 | } |
| 592 | } | 636 | } |
| @@ -602,7 +646,7 @@ u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) { | |||
| 602 | case 8: | 646 | case 8: |
| 603 | return value.bit64; | 647 | return value.bit64; |
| 604 | default: | 648 | default: |
| 605 | /* Invalid bit width -> return 0. */ | 649 | // Invalid bit width -> return 0. |
| 606 | return 0; | 650 | return 0; |
| 607 | } | 651 | } |
| 608 | } | 652 | } |
| @@ -610,39 +654,37 @@ u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) { | |||
| 610 | u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata, | 654 | u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata, |
| 611 | MemoryAccessType mem_type, u64 rel_address) { | 655 | MemoryAccessType mem_type, u64 rel_address) { |
| 612 | switch (mem_type) { | 656 | switch (mem_type) { |
| 613 | case MemoryAccessType_MainNso: | 657 | case MemoryAccessType::MainNso: |
| 614 | default: | 658 | default: |
| 615 | return metadata.main_nso_extents.base + rel_address; | 659 | return metadata.main_nso_extents.base + rel_address; |
| 616 | case MemoryAccessType_Heap: | 660 | case MemoryAccessType::Heap: |
| 617 | return metadata.heap_extents.base + rel_address; | 661 | return metadata.heap_extents.base + rel_address; |
| 618 | } | 662 | } |
| 619 | } | 663 | } |
| 620 | 664 | ||
| 621 | void DmntCheatVm::ResetState() { | 665 | void DmntCheatVm::ResetState() { |
| 622 | for (size_t i = 0; i < DmntCheatVm::NumRegisters; i++) { | 666 | registers.fill(0); |
| 623 | this->registers[i] = 0; | 667 | saved_values.fill(0); |
| 624 | this->saved_values[i] = 0; | 668 | loop_tops.fill(0); |
| 625 | this->loop_tops[i] = 0; | 669 | instruction_ptr = 0; |
| 626 | } | 670 | condition_depth = 0; |
| 627 | this->instruction_ptr = 0; | 671 | decode_success = true; |
| 628 | this->condition_depth = 0; | ||
| 629 | this->decode_success = true; | ||
| 630 | } | 672 | } |
| 631 | 673 | ||
| 632 | bool DmntCheatVm::LoadProgram(const std::vector<CheatEntry>& entries) { | 674 | bool DmntCheatVm::LoadProgram(const std::vector<CheatEntry>& entries) { |
| 633 | /* Reset opcode count. */ | 675 | // Reset opcode count. |
| 634 | this->num_opcodes = 0; | 676 | num_opcodes = 0; |
| 635 | 677 | ||
| 636 | for (size_t i = 0; i < entries.size(); i++) { | 678 | for (std::size_t i = 0; i < entries.size(); i++) { |
| 637 | if (entries[i].enabled) { | 679 | if (entries[i].enabled) { |
| 638 | /* Bounds check. */ | 680 | // Bounds check. |
| 639 | if (entries[i].definition.num_opcodes + this->num_opcodes > MaximumProgramOpcodeCount) { | 681 | if (entries[i].definition.num_opcodes + num_opcodes > MaximumProgramOpcodeCount) { |
| 640 | this->num_opcodes = 0; | 682 | num_opcodes = 0; |
| 641 | return false; | 683 | return false; |
| 642 | } | 684 | } |
| 643 | 685 | ||
| 644 | for (size_t n = 0; n < entries[i].definition.num_opcodes; n++) { | 686 | for (std::size_t n = 0; n < entries[i].definition.num_opcodes; n++) { |
| 645 | this->program[this->num_opcodes++] = entries[i].definition.opcodes[n]; | 687 | program[num_opcodes++] = entries[i].definition.opcodes[n]; |
| 646 | } | 688 | } |
| 647 | } | 689 | } |
| 648 | } | 690 | } |
| @@ -653,262 +695,249 @@ bool DmntCheatVm::LoadProgram(const std::vector<CheatEntry>& entries) { | |||
| 653 | void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | 695 | void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { |
| 654 | CheatVmOpcode cur_opcode{}; | 696 | CheatVmOpcode cur_opcode{}; |
| 655 | 697 | ||
| 656 | /* Get Keys down. */ | 698 | // Get Keys down. |
| 657 | u64 kDown = callbacks->HidKeysDown(); | 699 | u64 kDown = callbacks->HidKeysDown(); |
| 658 | 700 | ||
| 659 | this->LogToDebugFile("Started VM execution.\n"); | 701 | callbacks->CommandLog("Started VM execution."); |
| 660 | this->LogToDebugFile("Main NSO: %012lx\n", metadata.main_nso_extents.base); | 702 | callbacks->CommandLog(fmt::format("Main NSO: {:012X}", metadata.main_nso_extents.base)); |
| 661 | this->LogToDebugFile("Heap: %012lx\n", metadata.main_nso_extents.base); | 703 | callbacks->CommandLog(fmt::format("Heap: {:012X}", metadata.main_nso_extents.base)); |
| 662 | this->LogToDebugFile("Keys Down: %08x\n", (u32)(kDown & 0x0FFFFFFF)); | 704 | callbacks->CommandLog(fmt::format("Keys Down: {:08X}", static_cast<u32>(kDown & 0x0FFFFFFF))); |
| 663 | 705 | ||
| 664 | /* Clear VM state. */ | 706 | // Clear VM state. |
| 665 | this->ResetState(); | 707 | ResetState(); |
| 666 | 708 | ||
| 667 | /* Loop until program finishes. */ | 709 | // Loop until program finishes. |
| 668 | while (this->DecodeNextOpcode(cur_opcode)) { | 710 | while (DecodeNextOpcode(cur_opcode)) { |
| 669 | this->LogToDebugFile("Instruction Ptr: %04x\n", (u32)this->instruction_ptr); | 711 | callbacks->CommandLog( |
| 712 | fmt::format("Instruction Ptr: {:04X}", static_cast<u32>(instruction_ptr))); | ||
| 670 | 713 | ||
| 671 | for (size_t i = 0; i < NumRegisters; i++) { | 714 | for (std::size_t i = 0; i < NumRegisters; i++) { |
| 672 | this->LogToDebugFile("Registers[%02x]: %016lx\n", i, this->registers[i]); | 715 | callbacks->CommandLog(fmt::format("Registers[{:02X}]: {:016X}", i, registers[i])); |
| 673 | } | 716 | } |
| 674 | 717 | ||
| 675 | for (size_t i = 0; i < NumRegisters; i++) { | 718 | for (std::size_t i = 0; i < NumRegisters; i++) { |
| 676 | this->LogToDebugFile("SavedRegs[%02x]: %016lx\n", i, this->saved_values[i]); | 719 | callbacks->CommandLog(fmt::format("SavedRegs[{:02X}]: {:016X}", i, saved_values[i])); |
| 677 | } | 720 | } |
| 678 | this->LogOpcode(cur_opcode); | 721 | LogOpcode(cur_opcode); |
| 679 | 722 | ||
| 680 | /* Increment conditional depth, if relevant. */ | 723 | // Increment conditional depth, if relevant. |
| 681 | if (cur_opcode.begin_conditional_block) { | 724 | if (cur_opcode.begin_conditional_block) { |
| 682 | this->condition_depth++; | 725 | condition_depth++; |
| 683 | } | 726 | } |
| 684 | 727 | ||
| 685 | switch (cur_opcode.opcode) { | 728 | if (auto store_static = std::get_if<StoreStaticOpcode>(&cur_opcode.opcode)) { |
| 686 | case CheatVmOpcodeType_StoreStatic: { | 729 | // Calculate address, write value to memory. |
| 687 | /* Calculate address, write value to memory. */ | 730 | u64 dst_address = GetCheatProcessAddress(metadata, store_static->mem_type, |
| 688 | u64 dst_address = GetCheatProcessAddress( | 731 | store_static->rel_address + |
| 689 | metadata, cur_opcode.store_static.mem_type, | 732 | registers[store_static->offset_register]); |
| 690 | cur_opcode.store_static.rel_address + | 733 | u64 dst_value = GetVmInt(store_static->value, store_static->bit_width); |
| 691 | this->registers[cur_opcode.store_static.offset_register]); | 734 | switch (store_static->bit_width) { |
| 692 | u64 dst_value = | ||
| 693 | GetVmInt(cur_opcode.store_static.value, cur_opcode.store_static.bit_width); | ||
| 694 | switch (cur_opcode.store_static.bit_width) { | ||
| 695 | case 1: | 735 | case 1: |
| 696 | case 2: | 736 | case 2: |
| 697 | case 4: | 737 | case 4: |
| 698 | case 8: | 738 | case 8: |
| 699 | callbacks->MemoryWrite(dst_address, &dst_value, cur_opcode.store_static.bit_width); | 739 | callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width); |
| 700 | break; | 740 | break; |
| 701 | } | 741 | } |
| 702 | } break; | 742 | } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) { |
| 703 | case CheatVmOpcodeType_BeginConditionalBlock: { | 743 | // Read value from memory. |
| 704 | /* Read value from memory. */ | 744 | u64 src_address = |
| 705 | u64 src_address = GetCheatProcessAddress(metadata, cur_opcode.begin_cond.mem_type, | 745 | GetCheatProcessAddress(metadata, begin_cond->mem_type, begin_cond->rel_address); |
| 706 | cur_opcode.begin_cond.rel_address); | ||
| 707 | u64 src_value = 0; | 746 | u64 src_value = 0; |
| 708 | switch (cur_opcode.store_static.bit_width) { | 747 | switch (store_static->bit_width) { |
| 709 | case 1: | 748 | case 1: |
| 710 | case 2: | 749 | case 2: |
| 711 | case 4: | 750 | case 4: |
| 712 | case 8: | 751 | case 8: |
| 713 | callbacks->MemoryRead(src_address, &src_value, cur_opcode.begin_cond.bit_width); | 752 | callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width); |
| 714 | break; | 753 | break; |
| 715 | } | 754 | } |
| 716 | /* Check against condition. */ | 755 | // Check against condition. |
| 717 | u64 cond_value = GetVmInt(cur_opcode.begin_cond.value, cur_opcode.begin_cond.bit_width); | 756 | u64 cond_value = GetVmInt(begin_cond->value, begin_cond->bit_width); |
| 718 | bool cond_met = false; | 757 | bool cond_met = false; |
| 719 | switch (cur_opcode.begin_cond.cond_type) { | 758 | switch (begin_cond->cond_type) { |
| 720 | case ConditionalComparisonType_GT: | 759 | case ConditionalComparisonType::GT: |
| 721 | cond_met = src_value > cond_value; | 760 | cond_met = src_value > cond_value; |
| 722 | break; | 761 | break; |
| 723 | case ConditionalComparisonType_GE: | 762 | case ConditionalComparisonType::GE: |
| 724 | cond_met = src_value >= cond_value; | 763 | cond_met = src_value >= cond_value; |
| 725 | break; | 764 | break; |
| 726 | case ConditionalComparisonType_LT: | 765 | case ConditionalComparisonType::LT: |
| 727 | cond_met = src_value < cond_value; | 766 | cond_met = src_value < cond_value; |
| 728 | break; | 767 | break; |
| 729 | case ConditionalComparisonType_LE: | 768 | case ConditionalComparisonType::LE: |
| 730 | cond_met = src_value <= cond_value; | 769 | cond_met = src_value <= cond_value; |
| 731 | break; | 770 | break; |
| 732 | case ConditionalComparisonType_EQ: | 771 | case ConditionalComparisonType::EQ: |
| 733 | cond_met = src_value == cond_value; | 772 | cond_met = src_value == cond_value; |
| 734 | break; | 773 | break; |
| 735 | case ConditionalComparisonType_NE: | 774 | case ConditionalComparisonType::NE: |
| 736 | cond_met = src_value != cond_value; | 775 | cond_met = src_value != cond_value; |
| 737 | break; | 776 | break; |
| 738 | } | 777 | } |
| 739 | /* Skip conditional block if condition not met. */ | 778 | // Skip conditional block if condition not met. |
| 740 | if (!cond_met) { | 779 | if (!cond_met) { |
| 741 | this->SkipConditionalBlock(); | 780 | SkipConditionalBlock(); |
| 742 | } | 781 | } |
| 743 | } break; | 782 | } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) { |
| 744 | case CheatVmOpcodeType_EndConditionalBlock: | 783 | // Decrement the condition depth. |
| 745 | /* Decrement the condition depth. */ | 784 | // We will assume, graciously, that mismatched conditional block ends are a nop. |
| 746 | /* We will assume, graciously, that mismatched conditional block ends are a nop. */ | 785 | if (condition_depth > 0) { |
| 747 | if (this->condition_depth > 0) { | 786 | condition_depth--; |
| 748 | this->condition_depth--; | ||
| 749 | } | 787 | } |
| 750 | break; | 788 | } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) { |
| 751 | case CheatVmOpcodeType_ControlLoop: | 789 | if (ctrl_loop->start_loop) { |
| 752 | if (cur_opcode.ctrl_loop.start_loop) { | 790 | // Start a loop. |
| 753 | /* Start a loop. */ | 791 | registers[ctrl_loop->reg_index] = ctrl_loop->num_iters; |
| 754 | this->registers[cur_opcode.ctrl_loop.reg_index] = cur_opcode.ctrl_loop.num_iters; | 792 | loop_tops[ctrl_loop->reg_index] = instruction_ptr; |
| 755 | this->loop_tops[cur_opcode.ctrl_loop.reg_index] = this->instruction_ptr; | ||
| 756 | } else { | 793 | } else { |
| 757 | /* End a loop. */ | 794 | // End a loop. |
| 758 | this->registers[cur_opcode.ctrl_loop.reg_index]--; | 795 | registers[ctrl_loop->reg_index]--; |
| 759 | if (this->registers[cur_opcode.ctrl_loop.reg_index] != 0) { | 796 | if (registers[ctrl_loop->reg_index] != 0) { |
| 760 | this->instruction_ptr = this->loop_tops[cur_opcode.ctrl_loop.reg_index]; | 797 | instruction_ptr = loop_tops[ctrl_loop->reg_index]; |
| 761 | } | 798 | } |
| 762 | } | 799 | } |
| 763 | break; | 800 | } else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&cur_opcode.opcode)) { |
| 764 | case CheatVmOpcodeType_LoadRegisterStatic: | 801 | // Set a register to a static value. |
| 765 | /* Set a register to a static value. */ | 802 | registers[ldr_static->reg_index] = ldr_static->value; |
| 766 | this->registers[cur_opcode.ldr_static.reg_index] = cur_opcode.ldr_static.value; | 803 | } else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&cur_opcode.opcode)) { |
| 767 | break; | 804 | // Choose source address. |
| 768 | case CheatVmOpcodeType_LoadRegisterMemory: { | ||
| 769 | /* Choose source address. */ | ||
| 770 | u64 src_address; | 805 | u64 src_address; |
| 771 | if (cur_opcode.ldr_memory.load_from_reg) { | 806 | if (ldr_memory->load_from_reg) { |
| 772 | src_address = this->registers[cur_opcode.ldr_memory.reg_index] + | 807 | src_address = registers[ldr_memory->reg_index] + ldr_memory->rel_address; |
| 773 | cur_opcode.ldr_memory.rel_address; | ||
| 774 | } else { | 808 | } else { |
| 775 | src_address = GetCheatProcessAddress(metadata, cur_opcode.ldr_memory.mem_type, | 809 | src_address = |
| 776 | cur_opcode.ldr_memory.rel_address); | 810 | GetCheatProcessAddress(metadata, ldr_memory->mem_type, ldr_memory->rel_address); |
| 777 | } | 811 | } |
| 778 | /* Read into register. Gateway only reads on valid bitwidth. */ | 812 | // Read into register. Gateway only reads on valid bitwidth. |
| 779 | switch (cur_opcode.ldr_memory.bit_width) { | 813 | switch (ldr_memory->bit_width) { |
| 780 | case 1: | 814 | case 1: |
| 781 | case 2: | 815 | case 2: |
| 782 | case 4: | 816 | case 4: |
| 783 | case 8: | 817 | case 8: |
| 784 | callbacks->MemoryRead(src_address, | 818 | callbacks->MemoryRead(src_address, ®isters[ldr_memory->reg_index], |
| 785 | &this->registers[cur_opcode.ldr_memory.reg_index], | 819 | ldr_memory->bit_width); |
| 786 | cur_opcode.ldr_memory.bit_width); | ||
| 787 | break; | 820 | break; |
| 788 | } | 821 | } |
| 789 | } break; | 822 | } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) { |
| 790 | case CheatVmOpcodeType_StoreStaticToAddress: { | 823 | // Calculate address. |
| 791 | /* Calculate address. */ | 824 | u64 dst_address = registers[str_static->reg_index]; |
| 792 | u64 dst_address = this->registers[cur_opcode.str_static.reg_index]; | 825 | u64 dst_value = str_static->value; |
| 793 | u64 dst_value = cur_opcode.str_static.value; | 826 | if (str_static->add_offset_reg) { |
| 794 | if (cur_opcode.str_static.add_offset_reg) { | 827 | dst_address += registers[str_static->offset_reg_index]; |
| 795 | dst_address += this->registers[cur_opcode.str_static.offset_reg_index]; | ||
| 796 | } | 828 | } |
| 797 | /* Write value to memory. Gateway only writes on valid bitwidth. */ | 829 | // Write value to memory. Gateway only writes on valid bitwidth. |
| 798 | switch (cur_opcode.str_static.bit_width) { | 830 | switch (str_static->bit_width) { |
| 799 | case 1: | 831 | case 1: |
| 800 | case 2: | 832 | case 2: |
| 801 | case 4: | 833 | case 4: |
| 802 | case 8: | 834 | case 8: |
| 803 | callbacks->MemoryWrite(dst_address, &dst_value, cur_opcode.str_static.bit_width); | 835 | callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width); |
| 804 | break; | 836 | break; |
| 805 | } | 837 | } |
| 806 | /* Increment register if relevant. */ | 838 | // Increment register if relevant. |
| 807 | if (cur_opcode.str_static.increment_reg) { | 839 | if (str_static->increment_reg) { |
| 808 | this->registers[cur_opcode.str_static.reg_index] += cur_opcode.str_static.bit_width; | 840 | registers[str_static->reg_index] += str_static->bit_width; |
| 809 | } | 841 | } |
| 810 | } break; | 842 | } else if (auto perform_math_static = |
| 811 | case CheatVmOpcodeType_PerformArithmeticStatic: { | 843 | std::get_if<PerformArithmeticStaticOpcode>(&cur_opcode.opcode)) { |
| 812 | /* Do requested math. */ | 844 | // Do requested math. |
| 813 | switch (cur_opcode.perform_math_static.math_type) { | 845 | switch (perform_math_static->math_type) { |
| 814 | case RegisterArithmeticType_Addition: | 846 | case RegisterArithmeticType::Addition: |
| 815 | this->registers[cur_opcode.perform_math_static.reg_index] += | 847 | registers[perform_math_static->reg_index] += |
| 816 | (u64)cur_opcode.perform_math_static.value; | 848 | static_cast<u64>(perform_math_static->value); |
| 817 | break; | 849 | break; |
| 818 | case RegisterArithmeticType_Subtraction: | 850 | case RegisterArithmeticType::Subtraction: |
| 819 | this->registers[cur_opcode.perform_math_static.reg_index] -= | 851 | registers[perform_math_static->reg_index] -= |
| 820 | (u64)cur_opcode.perform_math_static.value; | 852 | static_cast<u64>(perform_math_static->value); |
| 821 | break; | 853 | break; |
| 822 | case RegisterArithmeticType_Multiplication: | 854 | case RegisterArithmeticType::Multiplication: |
| 823 | this->registers[cur_opcode.perform_math_static.reg_index] *= | 855 | registers[perform_math_static->reg_index] *= |
| 824 | (u64)cur_opcode.perform_math_static.value; | 856 | static_cast<u64>(perform_math_static->value); |
| 825 | break; | 857 | break; |
| 826 | case RegisterArithmeticType_LeftShift: | 858 | case RegisterArithmeticType::LeftShift: |
| 827 | this->registers[cur_opcode.perform_math_static.reg_index] <<= | 859 | registers[perform_math_static->reg_index] <<= |
| 828 | (u64)cur_opcode.perform_math_static.value; | 860 | static_cast<u64>(perform_math_static->value); |
| 829 | break; | 861 | break; |
| 830 | case RegisterArithmeticType_RightShift: | 862 | case RegisterArithmeticType::RightShift: |
| 831 | this->registers[cur_opcode.perform_math_static.reg_index] >>= | 863 | registers[perform_math_static->reg_index] >>= |
| 832 | (u64)cur_opcode.perform_math_static.value; | 864 | static_cast<u64>(perform_math_static->value); |
| 833 | break; | 865 | break; |
| 834 | default: | 866 | default: |
| 835 | /* Do not handle extensions here. */ | 867 | // Do not handle extensions here. |
| 836 | break; | 868 | break; |
| 837 | } | 869 | } |
| 838 | /* Apply bit width. */ | 870 | // Apply bit width. |
| 839 | switch (cur_opcode.perform_math_static.bit_width) { | 871 | switch (perform_math_static->bit_width) { |
| 840 | case 1: | 872 | case 1: |
| 841 | this->registers[cur_opcode.perform_math_static.reg_index] = | 873 | registers[perform_math_static->reg_index] = |
| 842 | static_cast<u8>(this->registers[cur_opcode.perform_math_static.reg_index]); | 874 | static_cast<u8>(registers[perform_math_static->reg_index]); |
| 843 | break; | 875 | break; |
| 844 | case 2: | 876 | case 2: |
| 845 | this->registers[cur_opcode.perform_math_static.reg_index] = | 877 | registers[perform_math_static->reg_index] = |
| 846 | static_cast<u16>(this->registers[cur_opcode.perform_math_static.reg_index]); | 878 | static_cast<u16>(registers[perform_math_static->reg_index]); |
| 847 | break; | 879 | break; |
| 848 | case 4: | 880 | case 4: |
| 849 | this->registers[cur_opcode.perform_math_static.reg_index] = | 881 | registers[perform_math_static->reg_index] = |
| 850 | static_cast<u32>(this->registers[cur_opcode.perform_math_static.reg_index]); | 882 | static_cast<u32>(registers[perform_math_static->reg_index]); |
| 851 | break; | 883 | break; |
| 852 | case 8: | 884 | case 8: |
| 853 | this->registers[cur_opcode.perform_math_static.reg_index] = | 885 | registers[perform_math_static->reg_index] = |
| 854 | static_cast<u64>(this->registers[cur_opcode.perform_math_static.reg_index]); | 886 | static_cast<u64>(registers[perform_math_static->reg_index]); |
| 855 | break; | 887 | break; |
| 856 | } | 888 | } |
| 857 | } break; | 889 | } else if (auto begin_keypress_cond = |
| 858 | case CheatVmOpcodeType_BeginKeypressConditionalBlock: | 890 | std::get_if<BeginKeypressConditionalOpcode>(&cur_opcode.opcode)) { |
| 859 | /* Check for keypress. */ | 891 | // Check for keypress. |
| 860 | if ((cur_opcode.begin_keypress_cond.key_mask & kDown) != | 892 | if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) { |
| 861 | cur_opcode.begin_keypress_cond.key_mask) { | 893 | // Keys not pressed. Skip conditional block. |
| 862 | /* Keys not pressed. Skip conditional block. */ | 894 | SkipConditionalBlock(); |
| 863 | this->SkipConditionalBlock(); | ||
| 864 | } | 895 | } |
| 865 | break; | 896 | } else if (auto perform_math_reg = |
| 866 | case CheatVmOpcodeType_PerformArithmeticRegister: { | 897 | std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) { |
| 867 | const u64 operand_1_value = | 898 | const u64 operand_1_value = registers[perform_math_reg->src_reg_1_index]; |
| 868 | this->registers[cur_opcode.perform_math_reg.src_reg_1_index]; | ||
| 869 | const u64 operand_2_value = | 899 | const u64 operand_2_value = |
| 870 | cur_opcode.perform_math_reg.has_immediate | 900 | perform_math_reg->has_immediate |
| 871 | ? GetVmInt(cur_opcode.perform_math_reg.value, | 901 | ? GetVmInt(perform_math_reg->value, perform_math_reg->bit_width) |
| 872 | cur_opcode.perform_math_reg.bit_width) | 902 | : registers[perform_math_reg->src_reg_2_index]; |
| 873 | : this->registers[cur_opcode.perform_math_reg.src_reg_2_index]; | ||
| 874 | 903 | ||
| 875 | u64 res_val = 0; | 904 | u64 res_val = 0; |
| 876 | /* Do requested math. */ | 905 | // Do requested math. |
| 877 | switch (cur_opcode.perform_math_reg.math_type) { | 906 | switch (perform_math_reg->math_type) { |
| 878 | case RegisterArithmeticType_Addition: | 907 | case RegisterArithmeticType::Addition: |
| 879 | res_val = operand_1_value + operand_2_value; | 908 | res_val = operand_1_value + operand_2_value; |
| 880 | break; | 909 | break; |
| 881 | case RegisterArithmeticType_Subtraction: | 910 | case RegisterArithmeticType::Subtraction: |
| 882 | res_val = operand_1_value - operand_2_value; | 911 | res_val = operand_1_value - operand_2_value; |
| 883 | break; | 912 | break; |
| 884 | case RegisterArithmeticType_Multiplication: | 913 | case RegisterArithmeticType::Multiplication: |
| 885 | res_val = operand_1_value * operand_2_value; | 914 | res_val = operand_1_value * operand_2_value; |
| 886 | break; | 915 | break; |
| 887 | case RegisterArithmeticType_LeftShift: | 916 | case RegisterArithmeticType::LeftShift: |
| 888 | res_val = operand_1_value << operand_2_value; | 917 | res_val = operand_1_value << operand_2_value; |
| 889 | break; | 918 | break; |
| 890 | case RegisterArithmeticType_RightShift: | 919 | case RegisterArithmeticType::RightShift: |
| 891 | res_val = operand_1_value >> operand_2_value; | 920 | res_val = operand_1_value >> operand_2_value; |
| 892 | break; | 921 | break; |
| 893 | case RegisterArithmeticType_LogicalAnd: | 922 | case RegisterArithmeticType::LogicalAnd: |
| 894 | res_val = operand_1_value & operand_2_value; | 923 | res_val = operand_1_value & operand_2_value; |
| 895 | break; | 924 | break; |
| 896 | case RegisterArithmeticType_LogicalOr: | 925 | case RegisterArithmeticType::LogicalOr: |
| 897 | res_val = operand_1_value | operand_2_value; | 926 | res_val = operand_1_value | operand_2_value; |
| 898 | break; | 927 | break; |
| 899 | case RegisterArithmeticType_LogicalNot: | 928 | case RegisterArithmeticType::LogicalNot: |
| 900 | res_val = ~operand_1_value; | 929 | res_val = ~operand_1_value; |
| 901 | break; | 930 | break; |
| 902 | case RegisterArithmeticType_LogicalXor: | 931 | case RegisterArithmeticType::LogicalXor: |
| 903 | res_val = operand_1_value ^ operand_2_value; | 932 | res_val = operand_1_value ^ operand_2_value; |
| 904 | break; | 933 | break; |
| 905 | case RegisterArithmeticType_None: | 934 | case RegisterArithmeticType::None: |
| 906 | res_val = operand_1_value; | 935 | res_val = operand_1_value; |
| 907 | break; | 936 | break; |
| 908 | } | 937 | } |
| 909 | 938 | ||
| 910 | /* Apply bit width. */ | 939 | // Apply bit width. |
| 911 | switch (cur_opcode.perform_math_reg.bit_width) { | 940 | switch (perform_math_reg->bit_width) { |
| 912 | case 1: | 941 | case 1: |
| 913 | res_val = static_cast<u8>(res_val); | 942 | res_val = static_cast<u8>(res_val); |
| 914 | break; | 943 | break; |
| @@ -923,282 +952,259 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 923 | break; | 952 | break; |
| 924 | } | 953 | } |
| 925 | 954 | ||
| 926 | /* Save to register. */ | 955 | // Save to register. |
| 927 | this->registers[cur_opcode.perform_math_reg.dst_reg_index] = res_val; | 956 | registers[perform_math_reg->dst_reg_index] = res_val; |
| 928 | } break; | 957 | } else if (auto str_register = |
| 929 | case CheatVmOpcodeType_StoreRegisterToAddress: { | 958 | std::get_if<StoreRegisterToAddressOpcode>(&cur_opcode.opcode)) { |
| 930 | /* Calculate address. */ | 959 | // Calculate address. |
| 931 | u64 dst_value = this->registers[cur_opcode.str_register.str_reg_index]; | 960 | u64 dst_value = registers[str_register->str_reg_index]; |
| 932 | u64 dst_address = this->registers[cur_opcode.str_register.addr_reg_index]; | 961 | u64 dst_address = registers[str_register->addr_reg_index]; |
| 933 | switch (cur_opcode.str_register.ofs_type) { | 962 | switch (str_register->ofs_type) { |
| 934 | case StoreRegisterOffsetType_None: | 963 | case StoreRegisterOffsetType::None: |
| 935 | /* Nothing more to do */ | 964 | // Nothing more to do |
| 936 | break; | 965 | break; |
| 937 | case StoreRegisterOffsetType_Reg: | 966 | case StoreRegisterOffsetType::Reg: |
| 938 | dst_address += this->registers[cur_opcode.str_register.ofs_reg_index]; | 967 | dst_address += registers[str_register->ofs_reg_index]; |
| 939 | break; | 968 | break; |
| 940 | case StoreRegisterOffsetType_Imm: | 969 | case StoreRegisterOffsetType::Imm: |
| 941 | dst_address += cur_opcode.str_register.rel_address; | 970 | dst_address += str_register->rel_address; |
| 942 | break; | 971 | break; |
| 943 | case StoreRegisterOffsetType_MemReg: | 972 | case StoreRegisterOffsetType::MemReg: |
| 944 | dst_address = | 973 | dst_address = GetCheatProcessAddress(metadata, str_register->mem_type, |
| 945 | GetCheatProcessAddress(metadata, cur_opcode.str_register.mem_type, | 974 | registers[str_register->addr_reg_index]); |
| 946 | this->registers[cur_opcode.str_register.addr_reg_index]); | ||
| 947 | break; | 975 | break; |
| 948 | case StoreRegisterOffsetType_MemImm: | 976 | case StoreRegisterOffsetType::MemImm: |
| 949 | dst_address = GetCheatProcessAddress(metadata, cur_opcode.str_register.mem_type, | 977 | dst_address = GetCheatProcessAddress(metadata, str_register->mem_type, |
| 950 | cur_opcode.str_register.rel_address); | 978 | str_register->rel_address); |
| 951 | break; | 979 | break; |
| 952 | case StoreRegisterOffsetType_MemImmReg: | 980 | case StoreRegisterOffsetType::MemImmReg: |
| 953 | dst_address = | 981 | dst_address = GetCheatProcessAddress(metadata, str_register->mem_type, |
| 954 | GetCheatProcessAddress(metadata, cur_opcode.str_register.mem_type, | 982 | registers[str_register->addr_reg_index] + |
| 955 | this->registers[cur_opcode.str_register.addr_reg_index] + | 983 | str_register->rel_address); |
| 956 | cur_opcode.str_register.rel_address); | ||
| 957 | break; | 984 | break; |
| 958 | } | 985 | } |
| 959 | 986 | ||
| 960 | /* Write value to memory. Write only on valid bitwidth. */ | 987 | // Write value to memory. Write only on valid bitwidth. |
| 961 | switch (cur_opcode.str_register.bit_width) { | 988 | switch (str_register->bit_width) { |
| 962 | case 1: | 989 | case 1: |
| 963 | case 2: | 990 | case 2: |
| 964 | case 4: | 991 | case 4: |
| 965 | case 8: | 992 | case 8: |
| 966 | callbacks->MemoryWrite(dst_address, &dst_value, cur_opcode.str_register.bit_width); | 993 | callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width); |
| 967 | break; | 994 | break; |
| 968 | } | 995 | } |
| 969 | 996 | ||
| 970 | /* Increment register if relevant. */ | 997 | // Increment register if relevant. |
| 971 | if (cur_opcode.str_register.increment_reg) { | 998 | if (str_register->increment_reg) { |
| 972 | this->registers[cur_opcode.str_register.addr_reg_index] += | 999 | registers[str_register->addr_reg_index] += str_register->bit_width; |
| 973 | cur_opcode.str_register.bit_width; | ||
| 974 | } | 1000 | } |
| 975 | } break; | 1001 | } else if (auto begin_reg_cond = |
| 976 | case CheatVmOpcodeType_BeginRegisterConditionalBlock: { | 1002 | std::get_if<BeginRegisterConditionalOpcode>(&cur_opcode.opcode)) { |
| 977 | /* Get value from register. */ | 1003 | // Get value from register. |
| 978 | u64 src_value = 0; | 1004 | u64 src_value = 0; |
| 979 | switch (cur_opcode.begin_reg_cond.bit_width) { | 1005 | switch (begin_reg_cond->bit_width) { |
| 980 | case 1: | 1006 | case 1: |
| 981 | src_value = static_cast<u8>( | 1007 | src_value = static_cast<u8>(registers[begin_reg_cond->val_reg_index] & 0xFFul); |
| 982 | this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFul); | ||
| 983 | break; | 1008 | break; |
| 984 | case 2: | 1009 | case 2: |
| 985 | src_value = static_cast<u16>( | 1010 | src_value = static_cast<u16>(registers[begin_reg_cond->val_reg_index] & 0xFFFFul); |
| 986 | this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFFFul); | ||
| 987 | break; | 1011 | break; |
| 988 | case 4: | 1012 | case 4: |
| 989 | src_value = static_cast<u32>( | 1013 | src_value = |
| 990 | this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFFFFFFFul); | 1014 | static_cast<u32>(registers[begin_reg_cond->val_reg_index] & 0xFFFFFFFFul); |
| 991 | break; | 1015 | break; |
| 992 | case 8: | 1016 | case 8: |
| 993 | src_value = | 1017 | src_value = static_cast<u64>(registers[begin_reg_cond->val_reg_index] & |
| 994 | static_cast<u64>(this->registers[cur_opcode.begin_reg_cond.val_reg_index] & | 1018 | 0xFFFFFFFFFFFFFFFFul); |
| 995 | 0xFFFFFFFFFFFFFFFFul); | ||
| 996 | break; | 1019 | break; |
| 997 | } | 1020 | } |
| 998 | 1021 | ||
| 999 | /* Read value from memory. */ | 1022 | // Read value from memory. |
| 1000 | u64 cond_value = 0; | 1023 | u64 cond_value = 0; |
| 1001 | if (cur_opcode.begin_reg_cond.comp_type == CompareRegisterValueType_StaticValue) { | 1024 | if (begin_reg_cond->comp_type == CompareRegisterValueType::StaticValue) { |
| 1002 | cond_value = | 1025 | cond_value = GetVmInt(begin_reg_cond->value, begin_reg_cond->bit_width); |
| 1003 | GetVmInt(cur_opcode.begin_reg_cond.value, cur_opcode.begin_reg_cond.bit_width); | 1026 | } else if (begin_reg_cond->comp_type == CompareRegisterValueType::OtherRegister) { |
| 1004 | } else if (cur_opcode.begin_reg_cond.comp_type == | 1027 | switch (begin_reg_cond->bit_width) { |
| 1005 | CompareRegisterValueType_OtherRegister) { | ||
| 1006 | switch (cur_opcode.begin_reg_cond.bit_width) { | ||
| 1007 | case 1: | 1028 | case 1: |
| 1008 | cond_value = static_cast<u8>( | 1029 | cond_value = |
| 1009 | this->registers[cur_opcode.begin_reg_cond.other_reg_index] & 0xFFul); | 1030 | static_cast<u8>(registers[begin_reg_cond->other_reg_index] & 0xFFul); |
| 1010 | break; | 1031 | break; |
| 1011 | case 2: | 1032 | case 2: |
| 1012 | cond_value = static_cast<u16>( | 1033 | cond_value = |
| 1013 | this->registers[cur_opcode.begin_reg_cond.other_reg_index] & 0xFFFFul); | 1034 | static_cast<u16>(registers[begin_reg_cond->other_reg_index] & 0xFFFFul); |
| 1014 | break; | 1035 | break; |
| 1015 | case 4: | 1036 | case 4: |
| 1016 | cond_value = static_cast<u32>( | 1037 | cond_value = |
| 1017 | this->registers[cur_opcode.begin_reg_cond.other_reg_index] & 0xFFFFFFFFul); | 1038 | static_cast<u32>(registers[begin_reg_cond->other_reg_index] & 0xFFFFFFFFul); |
| 1018 | break; | 1039 | break; |
| 1019 | case 8: | 1040 | case 8: |
| 1020 | cond_value = static_cast<u64>( | 1041 | cond_value = static_cast<u64>(registers[begin_reg_cond->other_reg_index] & |
| 1021 | this->registers[cur_opcode.begin_reg_cond.other_reg_index] & | 1042 | 0xFFFFFFFFFFFFFFFFul); |
| 1022 | 0xFFFFFFFFFFFFFFFFul); | ||
| 1023 | break; | 1043 | break; |
| 1024 | } | 1044 | } |
| 1025 | } else { | 1045 | } else { |
| 1026 | u64 cond_address = 0; | 1046 | u64 cond_address = 0; |
| 1027 | switch (cur_opcode.begin_reg_cond.comp_type) { | 1047 | switch (begin_reg_cond->comp_type) { |
| 1028 | case CompareRegisterValueType_MemoryRelAddr: | 1048 | case CompareRegisterValueType::MemoryRelAddr: |
| 1029 | cond_address = | 1049 | cond_address = GetCheatProcessAddress(metadata, begin_reg_cond->mem_type, |
| 1030 | GetCheatProcessAddress(metadata, cur_opcode.begin_reg_cond.mem_type, | 1050 | begin_reg_cond->rel_address); |
| 1031 | cur_opcode.begin_reg_cond.rel_address); | ||
| 1032 | break; | 1051 | break; |
| 1033 | case CompareRegisterValueType_MemoryOfsReg: | 1052 | case CompareRegisterValueType::MemoryOfsReg: |
| 1034 | cond_address = GetCheatProcessAddress( | 1053 | cond_address = GetCheatProcessAddress(metadata, begin_reg_cond->mem_type, |
| 1035 | metadata, cur_opcode.begin_reg_cond.mem_type, | 1054 | registers[begin_reg_cond->ofs_reg_index]); |
| 1036 | this->registers[cur_opcode.begin_reg_cond.ofs_reg_index]); | ||
| 1037 | break; | 1055 | break; |
| 1038 | case CompareRegisterValueType_RegisterRelAddr: | 1056 | case CompareRegisterValueType::RegisterRelAddr: |
| 1039 | cond_address = this->registers[cur_opcode.begin_reg_cond.addr_reg_index] + | 1057 | cond_address = |
| 1040 | cur_opcode.begin_reg_cond.rel_address; | 1058 | registers[begin_reg_cond->addr_reg_index] + begin_reg_cond->rel_address; |
| 1041 | break; | 1059 | break; |
| 1042 | case CompareRegisterValueType_RegisterOfsReg: | 1060 | case CompareRegisterValueType::RegisterOfsReg: |
| 1043 | cond_address = this->registers[cur_opcode.begin_reg_cond.addr_reg_index] + | 1061 | cond_address = registers[begin_reg_cond->addr_reg_index] + |
| 1044 | this->registers[cur_opcode.begin_reg_cond.ofs_reg_index]; | 1062 | registers[begin_reg_cond->ofs_reg_index]; |
| 1045 | break; | 1063 | break; |
| 1046 | default: | 1064 | default: |
| 1047 | break; | 1065 | break; |
| 1048 | } | 1066 | } |
| 1049 | switch (cur_opcode.begin_reg_cond.bit_width) { | 1067 | switch (begin_reg_cond->bit_width) { |
| 1050 | case 1: | 1068 | case 1: |
| 1051 | case 2: | 1069 | case 2: |
| 1052 | case 4: | 1070 | case 4: |
| 1053 | case 8: | 1071 | case 8: |
| 1054 | callbacks->MemoryRead(cond_address, &cond_value, | 1072 | callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width); |
| 1055 | cur_opcode.begin_reg_cond.bit_width); | ||
| 1056 | break; | 1073 | break; |
| 1057 | } | 1074 | } |
| 1058 | } | 1075 | } |
| 1059 | 1076 | ||
| 1060 | /* Check against condition. */ | 1077 | // Check against condition. |
| 1061 | bool cond_met = false; | 1078 | bool cond_met = false; |
| 1062 | switch (cur_opcode.begin_reg_cond.cond_type) { | 1079 | switch (begin_reg_cond->cond_type) { |
| 1063 | case ConditionalComparisonType_GT: | 1080 | case ConditionalComparisonType::GT: |
| 1064 | cond_met = src_value > cond_value; | 1081 | cond_met = src_value > cond_value; |
| 1065 | break; | 1082 | break; |
| 1066 | case ConditionalComparisonType_GE: | 1083 | case ConditionalComparisonType::GE: |
| 1067 | cond_met = src_value >= cond_value; | 1084 | cond_met = src_value >= cond_value; |
| 1068 | break; | 1085 | break; |
| 1069 | case ConditionalComparisonType_LT: | 1086 | case ConditionalComparisonType::LT: |
| 1070 | cond_met = src_value < cond_value; | 1087 | cond_met = src_value < cond_value; |
| 1071 | break; | 1088 | break; |
| 1072 | case ConditionalComparisonType_LE: | 1089 | case ConditionalComparisonType::LE: |
| 1073 | cond_met = src_value <= cond_value; | 1090 | cond_met = src_value <= cond_value; |
| 1074 | break; | 1091 | break; |
| 1075 | case ConditionalComparisonType_EQ: | 1092 | case ConditionalComparisonType::EQ: |
| 1076 | cond_met = src_value == cond_value; | 1093 | cond_met = src_value == cond_value; |
| 1077 | break; | 1094 | break; |
| 1078 | case ConditionalComparisonType_NE: | 1095 | case ConditionalComparisonType::NE: |
| 1079 | cond_met = src_value != cond_value; | 1096 | cond_met = src_value != cond_value; |
| 1080 | break; | 1097 | break; |
| 1081 | } | 1098 | } |
| 1082 | 1099 | ||
| 1083 | /* Skip conditional block if condition not met. */ | 1100 | // Skip conditional block if condition not met. |
| 1084 | if (!cond_met) { | 1101 | if (!cond_met) { |
| 1085 | this->SkipConditionalBlock(); | 1102 | SkipConditionalBlock(); |
| 1086 | } | 1103 | } |
| 1087 | } break; | 1104 | } else if (auto save_restore_reg = |
| 1088 | case CheatVmOpcodeType_SaveRestoreRegister: | 1105 | std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) { |
| 1089 | /* Save or restore a register. */ | 1106 | // Save or restore a register. |
| 1090 | switch (cur_opcode.save_restore_reg.op_type) { | 1107 | switch (save_restore_reg->op_type) { |
| 1091 | case SaveRestoreRegisterOpType_ClearRegs: | 1108 | case SaveRestoreRegisterOpType::ClearRegs: |
| 1092 | this->registers[cur_opcode.save_restore_reg.dst_index] = 0ul; | 1109 | registers[save_restore_reg->dst_index] = 0ul; |
| 1093 | break; | 1110 | break; |
| 1094 | case SaveRestoreRegisterOpType_ClearSaved: | 1111 | case SaveRestoreRegisterOpType::ClearSaved: |
| 1095 | this->saved_values[cur_opcode.save_restore_reg.dst_index] = 0ul; | 1112 | saved_values[save_restore_reg->dst_index] = 0ul; |
| 1096 | break; | 1113 | break; |
| 1097 | case SaveRestoreRegisterOpType_Save: | 1114 | case SaveRestoreRegisterOpType::Save: |
| 1098 | this->saved_values[cur_opcode.save_restore_reg.dst_index] = | 1115 | saved_values[save_restore_reg->dst_index] = registers[save_restore_reg->src_index]; |
| 1099 | this->registers[cur_opcode.save_restore_reg.src_index]; | ||
| 1100 | break; | 1116 | break; |
| 1101 | case SaveRestoreRegisterOpType_Restore: | 1117 | case SaveRestoreRegisterOpType::Restore: |
| 1102 | default: | 1118 | default: |
| 1103 | this->registers[cur_opcode.save_restore_reg.dst_index] = | 1119 | registers[save_restore_reg->dst_index] = saved_values[save_restore_reg->src_index]; |
| 1104 | this->saved_values[cur_opcode.save_restore_reg.src_index]; | ||
| 1105 | break; | 1120 | break; |
| 1106 | } | 1121 | } |
| 1107 | break; | 1122 | } else if (auto save_restore_regmask = |
| 1108 | case CheatVmOpcodeType_SaveRestoreRegisterMask: | 1123 | std::get_if<SaveRestoreRegisterMaskOpcode>(&cur_opcode.opcode)) { |
| 1109 | /* Save or restore register mask. */ | 1124 | // Save or restore register mask. |
| 1110 | u64* src; | 1125 | u64* src; |
| 1111 | u64* dst; | 1126 | u64* dst; |
| 1112 | switch (cur_opcode.save_restore_regmask.op_type) { | 1127 | switch (save_restore_regmask->op_type) { |
| 1113 | case SaveRestoreRegisterOpType_ClearSaved: | 1128 | case SaveRestoreRegisterOpType::ClearSaved: |
| 1114 | case SaveRestoreRegisterOpType_Save: | 1129 | case SaveRestoreRegisterOpType::Save: |
| 1115 | src = this->registers.data(); | 1130 | src = registers.data(); |
| 1116 | dst = this->saved_values.data(); | 1131 | dst = saved_values.data(); |
| 1117 | break; | 1132 | break; |
| 1118 | case SaveRestoreRegisterOpType_ClearRegs: | 1133 | case SaveRestoreRegisterOpType::ClearRegs: |
| 1119 | case SaveRestoreRegisterOpType_Restore: | 1134 | case SaveRestoreRegisterOpType::Restore: |
| 1120 | default: | 1135 | default: |
| 1121 | src = this->registers.data(); | 1136 | src = registers.data(); |
| 1122 | dst = this->saved_values.data(); | 1137 | dst = saved_values.data(); |
| 1123 | break; | 1138 | break; |
| 1124 | } | 1139 | } |
| 1125 | for (size_t i = 0; i < NumRegisters; i++) { | 1140 | for (std::size_t i = 0; i < NumRegisters; i++) { |
| 1126 | if (cur_opcode.save_restore_regmask.should_operate[i]) { | 1141 | if (save_restore_regmask->should_operate[i]) { |
| 1127 | switch (cur_opcode.save_restore_regmask.op_type) { | 1142 | switch (save_restore_regmask->op_type) { |
| 1128 | case SaveRestoreRegisterOpType_ClearSaved: | 1143 | case SaveRestoreRegisterOpType::ClearSaved: |
| 1129 | case SaveRestoreRegisterOpType_ClearRegs: | 1144 | case SaveRestoreRegisterOpType::ClearRegs: |
| 1130 | dst[i] = 0ul; | 1145 | dst[i] = 0ul; |
| 1131 | break; | 1146 | break; |
| 1132 | case SaveRestoreRegisterOpType_Save: | 1147 | case SaveRestoreRegisterOpType::Save: |
| 1133 | case SaveRestoreRegisterOpType_Restore: | 1148 | case SaveRestoreRegisterOpType::Restore: |
| 1134 | default: | 1149 | default: |
| 1135 | dst[i] = src[i]; | 1150 | dst[i] = src[i]; |
| 1136 | break; | 1151 | break; |
| 1137 | } | 1152 | } |
| 1138 | } | 1153 | } |
| 1139 | } | 1154 | } |
| 1140 | break; | 1155 | } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) { |
| 1141 | case CheatVmOpcodeType_DebugLog: { | 1156 | // Read value from memory. |
| 1142 | /* Read value from memory. */ | ||
| 1143 | u64 log_value = 0; | 1157 | u64 log_value = 0; |
| 1144 | if (cur_opcode.debug_log.val_type == DebugLogValueType_RegisterValue) { | 1158 | if (debug_log->val_type == DebugLogValueType::RegisterValue) { |
| 1145 | switch (cur_opcode.debug_log.bit_width) { | 1159 | switch (debug_log->bit_width) { |
| 1146 | case 1: | 1160 | case 1: |
| 1147 | log_value = static_cast<u8>( | 1161 | log_value = static_cast<u8>(registers[debug_log->val_reg_index] & 0xFFul); |
| 1148 | this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFul); | ||
| 1149 | break; | 1162 | break; |
| 1150 | case 2: | 1163 | case 2: |
| 1151 | log_value = static_cast<u16>( | 1164 | log_value = static_cast<u16>(registers[debug_log->val_reg_index] & 0xFFFFul); |
| 1152 | this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFFFul); | ||
| 1153 | break; | 1165 | break; |
| 1154 | case 4: | 1166 | case 4: |
| 1155 | log_value = static_cast<u32>( | 1167 | log_value = |
| 1156 | this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFFFFFFFul); | 1168 | static_cast<u32>(registers[debug_log->val_reg_index] & 0xFFFFFFFFul); |
| 1157 | break; | 1169 | break; |
| 1158 | case 8: | 1170 | case 8: |
| 1159 | log_value = static_cast<u64>( | 1171 | log_value = static_cast<u64>(registers[debug_log->val_reg_index] & |
| 1160 | this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFFFFFFFFFFFFFFFul); | 1172 | 0xFFFFFFFFFFFFFFFFul); |
| 1161 | break; | 1173 | break; |
| 1162 | } | 1174 | } |
| 1163 | } else { | 1175 | } else { |
| 1164 | u64 val_address = 0; | 1176 | u64 val_address = 0; |
| 1165 | switch (cur_opcode.debug_log.val_type) { | 1177 | switch (debug_log->val_type) { |
| 1166 | case DebugLogValueType_MemoryRelAddr: | 1178 | case DebugLogValueType::MemoryRelAddr: |
| 1167 | val_address = GetCheatProcessAddress(metadata, cur_opcode.debug_log.mem_type, | 1179 | val_address = GetCheatProcessAddress(metadata, debug_log->mem_type, |
| 1168 | cur_opcode.debug_log.rel_address); | 1180 | debug_log->rel_address); |
| 1169 | break; | 1181 | break; |
| 1170 | case DebugLogValueType_MemoryOfsReg: | 1182 | case DebugLogValueType::MemoryOfsReg: |
| 1171 | val_address = | 1183 | val_address = GetCheatProcessAddress(metadata, debug_log->mem_type, |
| 1172 | GetCheatProcessAddress(metadata, cur_opcode.debug_log.mem_type, | 1184 | registers[debug_log->ofs_reg_index]); |
| 1173 | this->registers[cur_opcode.debug_log.ofs_reg_index]); | ||
| 1174 | break; | 1185 | break; |
| 1175 | case DebugLogValueType_RegisterRelAddr: | 1186 | case DebugLogValueType::RegisterRelAddr: |
| 1176 | val_address = this->registers[cur_opcode.debug_log.addr_reg_index] + | 1187 | val_address = registers[debug_log->addr_reg_index] + debug_log->rel_address; |
| 1177 | cur_opcode.debug_log.rel_address; | ||
| 1178 | break; | 1188 | break; |
| 1179 | case DebugLogValueType_RegisterOfsReg: | 1189 | case DebugLogValueType::RegisterOfsReg: |
| 1180 | val_address = this->registers[cur_opcode.debug_log.addr_reg_index] + | 1190 | val_address = |
| 1181 | this->registers[cur_opcode.debug_log.ofs_reg_index]; | 1191 | registers[debug_log->addr_reg_index] + registers[debug_log->ofs_reg_index]; |
| 1182 | break; | 1192 | break; |
| 1183 | default: | 1193 | default: |
| 1184 | break; | 1194 | break; |
| 1185 | } | 1195 | } |
| 1186 | switch (cur_opcode.debug_log.bit_width) { | 1196 | switch (debug_log->bit_width) { |
| 1187 | case 1: | 1197 | case 1: |
| 1188 | case 2: | 1198 | case 2: |
| 1189 | case 4: | 1199 | case 4: |
| 1190 | case 8: | 1200 | case 8: |
| 1191 | callbacks->MemoryRead(val_address, &log_value, cur_opcode.debug_log.bit_width); | 1201 | callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width); |
| 1192 | break; | 1202 | break; |
| 1193 | } | 1203 | } |
| 1194 | } | 1204 | } |
| 1195 | 1205 | ||
| 1196 | /* Log value. */ | 1206 | // Log value. |
| 1197 | this->DebugLog(cur_opcode.debug_log.log_id, log_value); | 1207 | DebugLog(debug_log->log_id, log_value); |
| 1198 | } break; | ||
| 1199 | default: | ||
| 1200 | /* By default, we do a no-op. */ | ||
| 1201 | break; | ||
| 1202 | } | 1208 | } |
| 1203 | } | 1209 | } |
| 1204 | } | 1210 | } |
diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h index bea451db4..d1580d7f6 100644 --- a/src/core/memory/dmnt_cheat_vm.h +++ b/src/core/memory/dmnt_cheat_vm.h | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | 24 | ||
| 25 | #pragma once | 25 | #pragma once |
| 26 | 26 | ||
| 27 | #include <variant> | ||
| 27 | #include <vector> | 28 | #include <vector> |
| 28 | #include <fmt/printf.h> | 29 | #include <fmt/printf.h> |
| 29 | #include "common/common_types.h" | 30 | #include "common/common_types.h" |
| @@ -31,100 +32,100 @@ | |||
| 31 | 32 | ||
| 32 | namespace Memory { | 33 | namespace Memory { |
| 33 | 34 | ||
| 34 | enum CheatVmOpcodeType : u32 { | 35 | enum class CheatVmOpcodeType : u32 { |
| 35 | CheatVmOpcodeType_StoreStatic = 0, | 36 | StoreStatic = 0, |
| 36 | CheatVmOpcodeType_BeginConditionalBlock = 1, | 37 | BeginConditionalBlock = 1, |
| 37 | CheatVmOpcodeType_EndConditionalBlock = 2, | 38 | EndConditionalBlock = 2, |
| 38 | CheatVmOpcodeType_ControlLoop = 3, | 39 | ControlLoop = 3, |
| 39 | CheatVmOpcodeType_LoadRegisterStatic = 4, | 40 | LoadRegisterStatic = 4, |
| 40 | CheatVmOpcodeType_LoadRegisterMemory = 5, | 41 | LoadRegisterMemory = 5, |
| 41 | CheatVmOpcodeType_StoreStaticToAddress = 6, | 42 | StoreStaticToAddress = 6, |
| 42 | CheatVmOpcodeType_PerformArithmeticStatic = 7, | 43 | PerformArithmeticStatic = 7, |
| 43 | CheatVmOpcodeType_BeginKeypressConditionalBlock = 8, | 44 | BeginKeypressConditionalBlock = 8, |
| 44 | 45 | ||
| 45 | /* These are not implemented by Gateway's VM. */ | 46 | // These are not implemented by Gateway's VM. |
| 46 | CheatVmOpcodeType_PerformArithmeticRegister = 9, | 47 | PerformArithmeticRegister = 9, |
| 47 | CheatVmOpcodeType_StoreRegisterToAddress = 10, | 48 | StoreRegisterToAddress = 10, |
| 48 | CheatVmOpcodeType_Reserved11 = 11, | 49 | Reserved11 = 11, |
| 49 | 50 | ||
| 50 | /* This is a meta entry, and not a real opcode. */ | 51 | // This is a meta entry, and not a real opcode. |
| 51 | /* This is to facilitate multi-nybble instruction decoding. */ | 52 | // This is to facilitate multi-nybble instruction decoding. |
| 52 | CheatVmOpcodeType_ExtendedWidth = 12, | 53 | ExtendedWidth = 12, |
| 53 | 54 | ||
| 54 | /* Extended width opcodes. */ | 55 | // Extended width opcodes. |
| 55 | CheatVmOpcodeType_BeginRegisterConditionalBlock = 0xC0, | 56 | BeginRegisterConditionalBlock = 0xC0, |
| 56 | CheatVmOpcodeType_SaveRestoreRegister = 0xC1, | 57 | SaveRestoreRegister = 0xC1, |
| 57 | CheatVmOpcodeType_SaveRestoreRegisterMask = 0xC2, | 58 | SaveRestoreRegisterMask = 0xC2, |
| 58 | 59 | ||
| 59 | /* This is a meta entry, and not a real opcode. */ | 60 | // This is a meta entry, and not a real opcode. |
| 60 | /* This is to facilitate multi-nybble instruction decoding. */ | 61 | // This is to facilitate multi-nybble instruction decoding. |
| 61 | CheatVmOpcodeType_DoubleExtendedWidth = 0xF0, | 62 | DoubleExtendedWidth = 0xF0, |
| 62 | 63 | ||
| 63 | /* Double-extended width opcodes. */ | 64 | // Double-extended width opcodes. |
| 64 | CheatVmOpcodeType_DebugLog = 0xFFF, | 65 | DebugLog = 0xFFF, |
| 65 | }; | 66 | }; |
| 66 | 67 | ||
| 67 | enum MemoryAccessType : u32 { | 68 | enum class MemoryAccessType : u32 { |
| 68 | MemoryAccessType_MainNso = 0, | 69 | MainNso = 0, |
| 69 | MemoryAccessType_Heap = 1, | 70 | Heap = 1, |
| 70 | }; | 71 | }; |
| 71 | 72 | ||
| 72 | enum ConditionalComparisonType : u32 { | 73 | enum class ConditionalComparisonType : u32 { |
| 73 | ConditionalComparisonType_GT = 1, | 74 | GT = 1, |
| 74 | ConditionalComparisonType_GE = 2, | 75 | GE = 2, |
| 75 | ConditionalComparisonType_LT = 3, | 76 | LT = 3, |
| 76 | ConditionalComparisonType_LE = 4, | 77 | LE = 4, |
| 77 | ConditionalComparisonType_EQ = 5, | 78 | EQ = 5, |
| 78 | ConditionalComparisonType_NE = 6, | 79 | NE = 6, |
| 79 | }; | 80 | }; |
| 80 | 81 | ||
| 81 | enum RegisterArithmeticType : u32 { | 82 | enum class RegisterArithmeticType : u32 { |
| 82 | RegisterArithmeticType_Addition = 0, | 83 | Addition = 0, |
| 83 | RegisterArithmeticType_Subtraction = 1, | 84 | Subtraction = 1, |
| 84 | RegisterArithmeticType_Multiplication = 2, | 85 | Multiplication = 2, |
| 85 | RegisterArithmeticType_LeftShift = 3, | 86 | LeftShift = 3, |
| 86 | RegisterArithmeticType_RightShift = 4, | 87 | RightShift = 4, |
| 87 | 88 | ||
| 88 | /* These are not supported by Gateway's VM. */ | 89 | // These are not supported by Gateway's VM. |
| 89 | RegisterArithmeticType_LogicalAnd = 5, | 90 | LogicalAnd = 5, |
| 90 | RegisterArithmeticType_LogicalOr = 6, | 91 | LogicalOr = 6, |
| 91 | RegisterArithmeticType_LogicalNot = 7, | 92 | LogicalNot = 7, |
| 92 | RegisterArithmeticType_LogicalXor = 8, | 93 | LogicalXor = 8, |
| 93 | 94 | ||
| 94 | RegisterArithmeticType_None = 9, | 95 | None = 9, |
| 95 | }; | 96 | }; |
| 96 | 97 | ||
| 97 | enum StoreRegisterOffsetType : u32 { | 98 | enum class StoreRegisterOffsetType : u32 { |
| 98 | StoreRegisterOffsetType_None = 0, | 99 | None = 0, |
| 99 | StoreRegisterOffsetType_Reg = 1, | 100 | Reg = 1, |
| 100 | StoreRegisterOffsetType_Imm = 2, | 101 | Imm = 2, |
| 101 | StoreRegisterOffsetType_MemReg = 3, | 102 | MemReg = 3, |
| 102 | StoreRegisterOffsetType_MemImm = 4, | 103 | MemImm = 4, |
| 103 | StoreRegisterOffsetType_MemImmReg = 5, | 104 | MemImmReg = 5, |
| 104 | }; | 105 | }; |
| 105 | 106 | ||
| 106 | enum CompareRegisterValueType : u32 { | 107 | enum class CompareRegisterValueType : u32 { |
| 107 | CompareRegisterValueType_MemoryRelAddr = 0, | 108 | MemoryRelAddr = 0, |
| 108 | CompareRegisterValueType_MemoryOfsReg = 1, | 109 | MemoryOfsReg = 1, |
| 109 | CompareRegisterValueType_RegisterRelAddr = 2, | 110 | RegisterRelAddr = 2, |
| 110 | CompareRegisterValueType_RegisterOfsReg = 3, | 111 | RegisterOfsReg = 3, |
| 111 | CompareRegisterValueType_StaticValue = 4, | 112 | StaticValue = 4, |
| 112 | CompareRegisterValueType_OtherRegister = 5, | 113 | OtherRegister = 5, |
| 113 | }; | 114 | }; |
| 114 | 115 | ||
| 115 | enum SaveRestoreRegisterOpType : u32 { | 116 | enum class SaveRestoreRegisterOpType : u32 { |
| 116 | SaveRestoreRegisterOpType_Restore = 0, | 117 | Restore = 0, |
| 117 | SaveRestoreRegisterOpType_Save = 1, | 118 | Save = 1, |
| 118 | SaveRestoreRegisterOpType_ClearSaved = 2, | 119 | ClearSaved = 2, |
| 119 | SaveRestoreRegisterOpType_ClearRegs = 3, | 120 | ClearRegs = 3, |
| 120 | }; | 121 | }; |
| 121 | 122 | ||
| 122 | enum DebugLogValueType : u32 { | 123 | enum class DebugLogValueType : u32 { |
| 123 | DebugLogValueType_MemoryRelAddr = 0, | 124 | MemoryRelAddr = 0, |
| 124 | DebugLogValueType_MemoryOfsReg = 1, | 125 | MemoryOfsReg = 1, |
| 125 | DebugLogValueType_RegisterRelAddr = 2, | 126 | RegisterRelAddr = 2, |
| 126 | DebugLogValueType_RegisterOfsReg = 3, | 127 | RegisterOfsReg = 3, |
| 127 | DebugLogValueType_RegisterValue = 4, | 128 | RegisterValue = 4, |
| 128 | }; | 129 | }; |
| 129 | 130 | ||
| 130 | union VmInt { | 131 | union VmInt { |
| @@ -247,26 +248,19 @@ struct DebugLogOpcode { | |||
| 247 | u64 rel_address; | 248 | u64 rel_address; |
| 248 | }; | 249 | }; |
| 249 | 250 | ||
| 250 | struct CheatVmOpcode { | 251 | struct UnrecognizedInstruction { |
| 251 | CheatVmOpcodeType opcode; | 252 | CheatVmOpcodeType opcode; |
| 253 | }; | ||
| 254 | |||
| 255 | struct CheatVmOpcode { | ||
| 252 | bool begin_conditional_block; | 256 | bool begin_conditional_block; |
| 253 | union { | 257 | std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode, |
| 254 | StoreStaticOpcode store_static; | 258 | LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode, |
| 255 | BeginConditionalOpcode begin_cond; | 259 | PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, |
| 256 | EndConditionalOpcode end_cond; | 260 | PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, |
| 257 | ControlLoopOpcode ctrl_loop; | 261 | BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, |
| 258 | LoadRegisterStaticOpcode ldr_static; | 262 | SaveRestoreRegisterMaskOpcode, DebugLogOpcode, UnrecognizedInstruction> |
| 259 | LoadRegisterMemoryOpcode ldr_memory; | 263 | opcode; |
| 260 | StoreStaticToAddressOpcode str_static; | ||
| 261 | PerformArithmeticStaticOpcode perform_math_static; | ||
| 262 | BeginKeypressConditionalOpcode begin_keypress_cond; | ||
| 263 | PerformArithmeticRegisterOpcode perform_math_reg; | ||
| 264 | StoreRegisterToAddressOpcode str_register; | ||
| 265 | BeginRegisterConditionalOpcode begin_reg_cond; | ||
| 266 | SaveRestoreRegisterOpcode save_restore_reg; | ||
| 267 | SaveRestoreRegisterMaskOpcode save_restore_regmask; | ||
| 268 | DebugLogOpcode debug_log; | ||
| 269 | }; | ||
| 270 | }; | 264 | }; |
| 271 | 265 | ||
| 272 | class DmntCheatVm { | 266 | class DmntCheatVm { |
| @@ -285,50 +279,43 @@ public: | |||
| 285 | virtual void CommandLog(std::string_view data) = 0; | 279 | virtual void CommandLog(std::string_view data) = 0; |
| 286 | }; | 280 | }; |
| 287 | 281 | ||
| 288 | constexpr static size_t MaximumProgramOpcodeCount = 0x400; | 282 | static constexpr std::size_t MaximumProgramOpcodeCount = 0x400; |
| 289 | constexpr static size_t NumRegisters = 0x10; | 283 | static constexpr std::size_t NumRegisters = 0x10; |
| 284 | |||
| 285 | explicit DmntCheatVm(std::unique_ptr<Callbacks> callbacks); | ||
| 286 | ~DmntCheatVm(); | ||
| 287 | |||
| 288 | std::size_t GetProgramSize() const { | ||
| 289 | return this->num_opcodes; | ||
| 290 | } | ||
| 291 | |||
| 292 | bool LoadProgram(const std::vector<CheatEntry>& cheats); | ||
| 293 | void Execute(const CheatProcessMetadata& metadata); | ||
| 290 | 294 | ||
| 291 | private: | 295 | private: |
| 292 | std::unique_ptr<Callbacks> callbacks; | 296 | std::unique_ptr<Callbacks> callbacks; |
| 293 | 297 | ||
| 294 | size_t num_opcodes = 0; | 298 | std::size_t num_opcodes = 0; |
| 295 | size_t instruction_ptr = 0; | 299 | std::size_t instruction_ptr = 0; |
| 296 | size_t condition_depth = 0; | 300 | std::size_t condition_depth = 0; |
| 297 | bool decode_success = false; | 301 | bool decode_success = false; |
| 298 | std::array<u32, MaximumProgramOpcodeCount> program{}; | 302 | std::array<u32, MaximumProgramOpcodeCount> program{}; |
| 299 | std::array<u64, NumRegisters> registers{}; | 303 | std::array<u64, NumRegisters> registers{}; |
| 300 | std::array<u64, NumRegisters> saved_values{}; | 304 | std::array<u64, NumRegisters> saved_values{}; |
| 301 | std::array<size_t, NumRegisters> loop_tops{}; | 305 | std::array<std::size_t, NumRegisters> loop_tops{}; |
| 302 | 306 | ||
| 303 | private: | ||
| 304 | bool DecodeNextOpcode(CheatVmOpcode& out); | 307 | bool DecodeNextOpcode(CheatVmOpcode& out); |
| 305 | void SkipConditionalBlock(); | 308 | void SkipConditionalBlock(); |
| 306 | void ResetState(); | 309 | void ResetState(); |
| 307 | 310 | ||
| 308 | /* For implementing the DebugLog opcode. */ | 311 | // For implementing the DebugLog opcode. |
| 309 | void DebugLog(u32 log_id, u64 value); | 312 | void DebugLog(u32 log_id, u64 value); |
| 310 | 313 | ||
| 311 | /* For debugging. These will be IFDEF'd out normally. */ | ||
| 312 | template <typename... Args> | ||
| 313 | void LogToDebugFile(const char* format, const Args&... args) { | ||
| 314 | callbacks->CommandLog(fmt::sprintf(format, args...)); | ||
| 315 | } | ||
| 316 | |||
| 317 | void LogOpcode(const CheatVmOpcode& opcode); | 314 | void LogOpcode(const CheatVmOpcode& opcode); |
| 318 | 315 | ||
| 319 | static u64 GetVmInt(VmInt value, u32 bit_width); | 316 | static u64 GetVmInt(VmInt value, u32 bit_width); |
| 320 | static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata, | 317 | static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata, |
| 321 | MemoryAccessType mem_type, u64 rel_address); | 318 | MemoryAccessType mem_type, u64 rel_address); |
| 322 | |||
| 323 | public: | ||
| 324 | DmntCheatVm(std::unique_ptr<Callbacks> callbacks) : callbacks(std::move(callbacks)) {} | ||
| 325 | |||
| 326 | size_t GetProgramSize() { | ||
| 327 | return this->num_opcodes; | ||
| 328 | } | ||
| 329 | |||
| 330 | bool LoadProgram(const std::vector<CheatEntry>& cheats); | ||
| 331 | void Execute(const CheatProcessMetadata& metadata); | ||
| 332 | }; | 319 | }; |
| 333 | 320 | ||
| 334 | }; // namespace Memory | 321 | }; // namespace Memory |