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