diff options
Diffstat (limited to '')
-rw-r--r-- | src/core/memory/dmnt_cheat_vm.cpp | 1212 |
1 files changed, 1212 insertions, 0 deletions
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp new file mode 100644 index 000000000..cc16d15a4 --- /dev/null +++ b/src/core/memory/dmnt_cheat_vm.cpp @@ -0,0 +1,1212 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Adapted by DarkLordZach for use/interaction with yuzu + * + * Modifications Copyright 2019 yuzu emulator team + * Licensed under GPLv2 or any later version + * Refer to the license.txt file included. + */ + +#include "common/assert.h" +#include "common/scope_exit.h" +#include "core/memory/dmnt_cheat_types.h" +#include "core/memory/dmnt_cheat_vm.h" + +namespace Memory { + +DmntCheatVm::DmntCheatVm(std::unique_ptr<Callbacks> callbacks) : callbacks(std::move(callbacks)) {} + +DmntCheatVm::~DmntCheatVm() = default; + +void DmntCheatVm::DebugLog(u32 log_id, u64 value) { + callbacks->DebugLog(static_cast<u8>(log_id), value); +} + +void DmntCheatVm::LogOpcode(const CheatVmOpcode& opcode) { + if (auto store_static = std::get_if<StoreStaticOpcode>(&opcode.opcode)) { + callbacks->CommandLog("Opcode: Store Static"); + callbacks->CommandLog(fmt::format("Bit Width: {:X}", store_static->bit_width)); + callbacks->CommandLog( + fmt::format("Mem Type: {:X}", static_cast<u32>(store_static->mem_type))); + callbacks->CommandLog(fmt::format("Reg Idx: {:X}", store_static->offset_register)); + callbacks->CommandLog(fmt::format("Rel Addr: {:X}", store_static->rel_address)); + callbacks->CommandLog(fmt::format("Value: {:X}", store_static->value.bit64)); + } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&opcode.opcode)) { + callbacks->CommandLog("Opcode: Begin Conditional"); + callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_cond->bit_width)); + callbacks->CommandLog( + fmt::format("Mem Type: {:X}", static_cast<u32>(begin_cond->mem_type))); + callbacks->CommandLog( + fmt::format("Cond Type: {:X}", static_cast<u32>(begin_cond->cond_type))); + callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_cond->rel_address)); + callbacks->CommandLog(fmt::format("Value: {:X}", begin_cond->value.bit64)); + } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&opcode.opcode)) { + callbacks->CommandLog("Opcode: End Conditional"); + } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&opcode.opcode)) { + if (ctrl_loop->start_loop) { + callbacks->CommandLog("Opcode: Start Loop"); + callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index)); + callbacks->CommandLog(fmt::format("Num Iters: {:X}", ctrl_loop->num_iters)); + } else { + callbacks->CommandLog("Opcode: End Loop"); + callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index)); + } + } else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&opcode.opcode)) { + callbacks->CommandLog("Opcode: Load Register Static"); + callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_static->reg_index)); + callbacks->CommandLog(fmt::format("Value: {:X}", ldr_static->value)); + } else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&opcode.opcode)) { + callbacks->CommandLog("Opcode: Load Register Memory"); + callbacks->CommandLog(fmt::format("Bit Width: {:X}", ldr_memory->bit_width)); + callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ldr_memory->reg_index)); + callbacks->CommandLog( + fmt::format("Mem Type: {:X}", static_cast<u32>(ldr_memory->mem_type))); + callbacks->CommandLog(fmt::format("From Reg: {:d}", ldr_memory->load_from_reg)); + callbacks->CommandLog(fmt::format("Rel Addr: {:X}", ldr_memory->rel_address)); + } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&opcode.opcode)) { + callbacks->CommandLog("Opcode: Store Static to Address"); + callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_static->bit_width)); + callbacks->CommandLog(fmt::format("Reg Idx: {:X}", str_static->reg_index)); + if (str_static->add_offset_reg) { + callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_static->offset_reg_index)); + } + callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_static->increment_reg)); + callbacks->CommandLog(fmt::format("Value: {:X}", str_static->value)); + } else if (auto perform_math_static = + std::get_if<PerformArithmeticStaticOpcode>(&opcode.opcode)) { + callbacks->CommandLog("Opcode: Perform Static Arithmetic"); + callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_static->bit_width)); + callbacks->CommandLog(fmt::format("Reg Idx: {:X}", perform_math_static->reg_index)); + callbacks->CommandLog( + fmt::format("Math Type: {:X}", static_cast<u32>(perform_math_static->math_type))); + callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_static->value)); + } else if (auto begin_keypress_cond = + std::get_if<BeginKeypressConditionalOpcode>(&opcode.opcode)) { + callbacks->CommandLog("Opcode: Begin Keypress Conditional"); + callbacks->CommandLog(fmt::format("Key Mask: {:X}", begin_keypress_cond->key_mask)); + } else if (auto perform_math_reg = + std::get_if<PerformArithmeticRegisterOpcode>(&opcode.opcode)) { + callbacks->CommandLog("Opcode: Perform Register Arithmetic"); + callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_reg->bit_width)); + callbacks->CommandLog(fmt::format("Dst Idx: {:X}", perform_math_reg->dst_reg_index)); + callbacks->CommandLog(fmt::format("Src1 Idx: {:X}", perform_math_reg->src_reg_1_index)); + if (perform_math_reg->has_immediate) { + callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_reg->value.bit64)); + } else { + callbacks->CommandLog( + fmt::format("Src2 Idx: {:X}", perform_math_reg->src_reg_2_index)); + } + } else if (auto str_register = std::get_if<StoreRegisterToAddressOpcode>(&opcode.opcode)) { + callbacks->CommandLog("Opcode: Store Register to Address"); + callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_register->bit_width)); + callbacks->CommandLog(fmt::format("S Reg Idx: {:X}", str_register->str_reg_index)); + callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", str_register->addr_reg_index)); + callbacks->CommandLog(fmt::format("Incr Reg: {:d}", str_register->increment_reg)); + switch (str_register->ofs_type) { + case StoreRegisterOffsetType::None: + break; + case StoreRegisterOffsetType::Reg: + callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_register->ofs_reg_index)); + break; + case StoreRegisterOffsetType::Imm: + callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address)); + break; + case StoreRegisterOffsetType::MemReg: + callbacks->CommandLog( + fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type))); + break; + case StoreRegisterOffsetType::MemImm: + case StoreRegisterOffsetType::MemImmReg: + callbacks->CommandLog( + fmt::format("Mem Type: {:X}", static_cast<u32>(str_register->mem_type))); + callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address)); + break; + } + } else if (auto begin_reg_cond = std::get_if<BeginRegisterConditionalOpcode>(&opcode.opcode)) { + callbacks->CommandLog("Opcode: Begin Register Conditional"); + callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_reg_cond->bit_width)); + callbacks->CommandLog( + fmt::format("Cond Type: {:X}", static_cast<u32>(begin_reg_cond->cond_type))); + callbacks->CommandLog(fmt::format("V Reg Idx: {:X}", begin_reg_cond->val_reg_index)); + switch (begin_reg_cond->comp_type) { + case CompareRegisterValueType::StaticValue: + callbacks->CommandLog("Comp Type: Static Value"); + callbacks->CommandLog(fmt::format("Value: {:X}", begin_reg_cond->value.bit64)); + break; + case CompareRegisterValueType::OtherRegister: + callbacks->CommandLog("Comp Type: Other Register"); + callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", begin_reg_cond->other_reg_index)); + break; + case CompareRegisterValueType::MemoryRelAddr: + callbacks->CommandLog("Comp Type: Memory Relative Address"); + callbacks->CommandLog( + fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type))); + callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address)); + break; + case CompareRegisterValueType::MemoryOfsReg: + callbacks->CommandLog("Comp Type: Memory Offset Register"); + callbacks->CommandLog( + fmt::format("Mem Type: {:X}", static_cast<u32>(begin_reg_cond->mem_type))); + callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index)); + break; + case CompareRegisterValueType::RegisterRelAddr: + callbacks->CommandLog("Comp Type: Register Relative Address"); + callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index)); + callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address)); + break; + case CompareRegisterValueType::RegisterOfsReg: + callbacks->CommandLog("Comp Type: Register Offset Register"); + callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index)); + callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index)); + break; + } + } else if (auto save_restore_reg = std::get_if<SaveRestoreRegisterOpcode>(&opcode.opcode)) { + callbacks->CommandLog("Opcode: Save or Restore Register"); + callbacks->CommandLog(fmt::format("Dst Idx: {:X}", save_restore_reg->dst_index)); + callbacks->CommandLog(fmt::format("Src Idx: {:X}", save_restore_reg->src_index)); + callbacks->CommandLog( + fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_reg->op_type))); + } else if (auto save_restore_regmask = + std::get_if<SaveRestoreRegisterMaskOpcode>(&opcode.opcode)) { + callbacks->CommandLog("Opcode: Save or Restore Register Mask"); + callbacks->CommandLog( + fmt::format("Op Type: {:d}", static_cast<u32>(save_restore_regmask->op_type))); + for (std::size_t i = 0; i < NumRegisters; i++) { + callbacks->CommandLog( + fmt::format("Act[{:02X}]: {:d}", i, save_restore_regmask->should_operate[i])); + } + } else if (auto debug_log = std::get_if<DebugLogOpcode>(&opcode.opcode)) { + callbacks->CommandLog("Opcode: Debug Log"); + callbacks->CommandLog(fmt::format("Bit Width: {:X}", debug_log->bit_width)); + callbacks->CommandLog(fmt::format("Log ID: {:X}", debug_log->log_id)); + callbacks->CommandLog( + fmt::format("Val Type: {:X}", static_cast<u32>(debug_log->val_type))); + switch (debug_log->val_type) { + case DebugLogValueType::RegisterValue: + callbacks->CommandLog("Val Type: Register Value"); + callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", debug_log->val_reg_index)); + break; + case DebugLogValueType::MemoryRelAddr: + callbacks->CommandLog("Val Type: Memory Relative Address"); + callbacks->CommandLog( + fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type))); + callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address)); + break; + case DebugLogValueType::MemoryOfsReg: + callbacks->CommandLog("Val Type: Memory Offset Register"); + callbacks->CommandLog( + fmt::format("Mem Type: {:X}", static_cast<u32>(debug_log->mem_type))); + callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index)); + break; + case DebugLogValueType::RegisterRelAddr: + callbacks->CommandLog("Val Type: Register Relative Address"); + callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index)); + callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address)); + break; + case DebugLogValueType::RegisterOfsReg: + callbacks->CommandLog("Val Type: Register Offset Register"); + callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index)); + callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index)); + break; + } + } else if (auto instr = std::get_if<UnrecognizedInstruction>(&opcode.opcode)) { + callbacks->CommandLog(fmt::format("Unknown opcode: {:X}", static_cast<u32>(instr->opcode))); + } +} + +DmntCheatVm::Callbacks::~Callbacks() = default; + +bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { + // If we've ever seen a decode failure, return false. + bool valid = decode_success; + CheatVmOpcode opcode = {}; + SCOPE_EXIT({ + decode_success &= valid; + if (valid) { + out = opcode; + } + }); + + // Helper function for getting instruction dwords. + const auto GetNextDword = [&] { + if (instruction_ptr >= num_opcodes) { + valid = false; + return static_cast<u32>(0); + } + return program[instruction_ptr++]; + }; + + // Helper function for parsing a VmInt. + const auto GetNextVmInt = [&](const u32 bit_width) { + VmInt val{}; + + const u32 first_dword = GetNextDword(); + switch (bit_width) { + case 1: + val.bit8 = static_cast<u8>(first_dword); + break; + case 2: + val.bit16 = static_cast<u16>(first_dword); + break; + case 4: + val.bit32 = first_dword; + break; + case 8: + val.bit64 = (static_cast<u64>(first_dword) << 32ul) | static_cast<u64>(GetNextDword()); + break; + } + + return val; + }; + + // Read opcode. + const u32 first_dword = GetNextDword(); + if (!valid) { + return valid; + } + + auto opcode_type = static_cast<CheatVmOpcodeType>(((first_dword >> 28) & 0xF)); + if (opcode_type >= CheatVmOpcodeType::ExtendedWidth) { + opcode_type = static_cast<CheatVmOpcodeType>((static_cast<u32>(opcode_type) << 4) | + ((first_dword >> 24) & 0xF)); + } + if (opcode_type >= CheatVmOpcodeType::DoubleExtendedWidth) { + opcode_type = static_cast<CheatVmOpcodeType>((static_cast<u32>(opcode_type) << 4) | + ((first_dword >> 20) & 0xF)); + } + + // detect condition start. + switch (opcode_type) { + case CheatVmOpcodeType::BeginConditionalBlock: + case CheatVmOpcodeType::BeginKeypressConditionalBlock: + case CheatVmOpcodeType::BeginRegisterConditionalBlock: + opcode.begin_conditional_block = true; + break; + default: + opcode.begin_conditional_block = false; + break; + } + + switch (opcode_type) { + case CheatVmOpcodeType::StoreStatic: { + StoreStaticOpcode store_static{}; + // 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY) + // Read additional words. + const u32 second_dword = GetNextDword(); + store_static.bit_width = (first_dword >> 24) & 0xF; + store_static.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); + store_static.offset_register = ((first_dword >> 16) & 0xF); + store_static.rel_address = + (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); + store_static.value = GetNextVmInt(store_static.bit_width); + opcode.opcode = store_static; + } break; + case CheatVmOpcodeType::BeginConditionalBlock: { + BeginConditionalOpcode begin_cond{}; + // 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY) + // Read additional words. + const u32 second_dword = GetNextDword(); + begin_cond.bit_width = (first_dword >> 24) & 0xF; + begin_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); + begin_cond.cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF); + begin_cond.rel_address = + (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); + begin_cond.value = GetNextVmInt(begin_cond.bit_width); + opcode.opcode = begin_cond; + } break; + case CheatVmOpcodeType::EndConditionalBlock: { + // 20000000 + // There's actually nothing left to process here! + opcode.opcode = EndConditionalOpcode{}; + } break; + case CheatVmOpcodeType::ControlLoop: { + ControlLoopOpcode ctrl_loop{}; + // 300R0000 VVVVVVVV + // 310R0000 + // Parse register, whether loop start or loop end. + ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0; + ctrl_loop.reg_index = ((first_dword >> 20) & 0xF); + + // Read number of iters if loop start. + if (ctrl_loop.start_loop) { + ctrl_loop.num_iters = GetNextDword(); + } + opcode.opcode = ctrl_loop; + } break; + case CheatVmOpcodeType::LoadRegisterStatic: { + LoadRegisterStaticOpcode ldr_static{}; + // 400R0000 VVVVVVVV VVVVVVVV + // Read additional words. + ldr_static.reg_index = ((first_dword >> 16) & 0xF); + ldr_static.value = + (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword()); + opcode.opcode = ldr_static; + } break; + case CheatVmOpcodeType::LoadRegisterMemory: { + LoadRegisterMemoryOpcode ldr_memory{}; + // 5TMRI0AA AAAAAAAA + // Read additional words. + const u32 second_dword = GetNextDword(); + ldr_memory.bit_width = (first_dword >> 24) & 0xF; + ldr_memory.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); + ldr_memory.reg_index = ((first_dword >> 16) & 0xF); + ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0; + ldr_memory.rel_address = + (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); + opcode.opcode = ldr_memory; + } break; + case CheatVmOpcodeType::StoreStaticToAddress: { + StoreStaticToAddressOpcode str_static{}; + // 6T0RIor0 VVVVVVVV VVVVVVVV + // Read additional words. + str_static.bit_width = (first_dword >> 24) & 0xF; + str_static.reg_index = ((first_dword >> 16) & 0xF); + str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0; + str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0; + str_static.offset_reg_index = ((first_dword >> 4) & 0xF); + str_static.value = + (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword()); + opcode.opcode = str_static; + } break; + case CheatVmOpcodeType::PerformArithmeticStatic: { + PerformArithmeticStaticOpcode perform_math_static{}; + // 7T0RC000 VVVVVVVV + // Read additional words. + perform_math_static.bit_width = (first_dword >> 24) & 0xF; + perform_math_static.reg_index = ((first_dword >> 16) & 0xF); + perform_math_static.math_type = + static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF); + perform_math_static.value = GetNextDword(); + opcode.opcode = perform_math_static; + } break; + case CheatVmOpcodeType::BeginKeypressConditionalBlock: { + BeginKeypressConditionalOpcode begin_keypress_cond{}; + // 8kkkkkkk + // Just parse the mask. + begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF; + } break; + case CheatVmOpcodeType::PerformArithmeticRegister: { + PerformArithmeticRegisterOpcode perform_math_reg{}; + // 9TCRSIs0 (VVVVVVVV (VVVVVVVV)) + perform_math_reg.bit_width = (first_dword >> 24) & 0xF; + perform_math_reg.math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF); + perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF); + perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF); + perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0; + if (perform_math_reg.has_immediate) { + perform_math_reg.src_reg_2_index = 0; + perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width); + } else { + perform_math_reg.src_reg_2_index = ((first_dword >> 4) & 0xF); + } + opcode.opcode = perform_math_reg; + } break; + case CheatVmOpcodeType::StoreRegisterToAddress: { + StoreRegisterToAddressOpcode str_register{}; + // ATSRIOxa (aaaaaaaa) + // A = opcode 10 + // T = bit width + // S = src register index + // R = address register index + // I = 1 if increment address register, 0 if not increment address register + // O = offset type, 0 = None, 1 = Register, 2 = Immediate, 3 = Memory Region, + // 4 = Memory Region + Relative Address (ignore address register), 5 = Memory Region + + // Relative Address + // x = offset register (for offset type 1), memory type (for offset type 3) + // a = relative address (for offset type 2+3) + str_register.bit_width = (first_dword >> 24) & 0xF; + str_register.str_reg_index = ((first_dword >> 20) & 0xF); + str_register.addr_reg_index = ((first_dword >> 16) & 0xF); + str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0; + str_register.ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF)); + str_register.ofs_reg_index = ((first_dword >> 4) & 0xF); + switch (str_register.ofs_type) { + case StoreRegisterOffsetType::None: + case StoreRegisterOffsetType::Reg: + // Nothing more to do + break; + case StoreRegisterOffsetType::Imm: + str_register.rel_address = + ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); + break; + case StoreRegisterOffsetType::MemReg: + str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); + break; + case StoreRegisterOffsetType::MemImm: + case StoreRegisterOffsetType::MemImmReg: + str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); + str_register.rel_address = + ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); + break; + default: + str_register.ofs_type = StoreRegisterOffsetType::None; + break; + } + opcode.opcode = str_register; + } break; + case CheatVmOpcodeType::BeginRegisterConditionalBlock: { + BeginRegisterConditionalOpcode begin_reg_cond{}; + // C0TcSX## + // C0TcS0Ma aaaaaaaa + // C0TcS1Mr + // C0TcS2Ra aaaaaaaa + // C0TcS3Rr + // C0TcS400 VVVVVVVV (VVVVVVVV) + // C0TcS5X0 + // C0 = opcode 0xC0 + // T = bit width + // c = condition type. + // S = source register. + // X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset + // register, + // 2 = register with relative offset, 3 = register with offset register, 4 = static + // value, 5 = other register. + // M = memory type. + // R = address register. + // a = relative address. + // r = offset register. + // X = other register. + // V = value. + begin_reg_cond.bit_width = (first_dword >> 20) & 0xF; + begin_reg_cond.cond_type = + static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF); + begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF); + begin_reg_cond.comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF); + + switch (begin_reg_cond.comp_type) { + case CompareRegisterValueType::StaticValue: + begin_reg_cond.value = GetNextVmInt(begin_reg_cond.bit_width); + break; + case CompareRegisterValueType::OtherRegister: + begin_reg_cond.other_reg_index = ((first_dword >> 4) & 0xF); + break; + case CompareRegisterValueType::MemoryRelAddr: + begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); + begin_reg_cond.rel_address = + ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); + break; + case CompareRegisterValueType::MemoryOfsReg: + begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); + begin_reg_cond.ofs_reg_index = (first_dword & 0xF); + break; + case CompareRegisterValueType::RegisterRelAddr: + begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); + begin_reg_cond.rel_address = + ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); + break; + case CompareRegisterValueType::RegisterOfsReg: + begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); + begin_reg_cond.ofs_reg_index = (first_dword & 0xF); + break; + } + opcode.opcode = begin_reg_cond; + } break; + case CheatVmOpcodeType::SaveRestoreRegister: { + SaveRestoreRegisterOpcode save_restore_reg{}; + // C10D0Sx0 + // C1 = opcode 0xC1 + // D = destination index. + // S = source index. + // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring + // a register. + // NOTE: If we add more save slots later, current encoding is backwards compatible. + save_restore_reg.dst_index = (first_dword >> 16) & 0xF; + save_restore_reg.src_index = (first_dword >> 8) & 0xF; + save_restore_reg.op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF); + opcode.opcode = save_restore_reg; + } break; + case CheatVmOpcodeType::SaveRestoreRegisterMask: { + SaveRestoreRegisterMaskOpcode save_restore_regmask{}; + // C2x0XXXX + // C2 = opcode 0xC2 + // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring. + // X = 16-bit bitmask, bit i --> save or restore register i. + save_restore_regmask.op_type = + static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF); + for (std::size_t i = 0; i < NumRegisters; i++) { + save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0; + } + opcode.opcode = save_restore_regmask; + } break; + case CheatVmOpcodeType::DebugLog: { + DebugLogOpcode debug_log{}; + // FFFTIX## + // FFFTI0Ma aaaaaaaa + // FFFTI1Mr + // FFFTI2Ra aaaaaaaa + // FFFTI3Rr + // FFFTI4X0 + // FFF = opcode 0xFFF + // T = bit width. + // I = log id. + // X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset + // register, + // 2 = register with relative offset, 3 = register with offset register, 4 = register + // value. + // M = memory type. + // R = address register. + // a = relative address. + // r = offset register. + // X = value register. + debug_log.bit_width = (first_dword >> 16) & 0xF; + debug_log.log_id = ((first_dword >> 12) & 0xF); + debug_log.val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF); + + switch (debug_log.val_type) { + case DebugLogValueType::RegisterValue: + debug_log.val_reg_index = ((first_dword >> 4) & 0xF); + break; + case DebugLogValueType::MemoryRelAddr: + debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); + debug_log.rel_address = + ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); + break; + case DebugLogValueType::MemoryOfsReg: + debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); + debug_log.ofs_reg_index = (first_dword & 0xF); + break; + case DebugLogValueType::RegisterRelAddr: + debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); + debug_log.rel_address = + ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); + break; + case DebugLogValueType::RegisterOfsReg: + debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); + debug_log.ofs_reg_index = (first_dword & 0xF); + break; + } + opcode.opcode = debug_log; + } break; + case CheatVmOpcodeType::ExtendedWidth: + case CheatVmOpcodeType::DoubleExtendedWidth: + default: + // Unrecognized instruction cannot be decoded. + valid = false; + opcode.opcode = UnrecognizedInstruction{opcode_type}; + break; + } + + // End decoding. + return valid; +} + +void DmntCheatVm::SkipConditionalBlock() { + if (condition_depth > 0) { + // We want to continue until we're out of the current block. + const std::size_t desired_depth = condition_depth - 1; + + CheatVmOpcode skip_opcode{}; + while (condition_depth > desired_depth && DecodeNextOpcode(skip_opcode)) { + // Decode instructions until we see end of the current conditional block. + // NOTE: This is broken in gateway's implementation. + // Gateway currently checks for "0x2" instead of "0x20000000" + // In addition, they do a linear scan instead of correctly decoding opcodes. + // This causes issues if "0x2" appears as an immediate in the conditional block... + + // We also support nesting of conditional blocks, and Gateway does not. + if (skip_opcode.begin_conditional_block) { + condition_depth++; + } else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) { + condition_depth--; + } + } + } else { + // Skipping, but condition_depth = 0. + // This is an error condition. + // However, I don't actually believe it is possible for this to happen. + // I guess we'll throw a fatal error here, so as to encourage me to fix the VM + // in the event that someone triggers it? I don't know how you'd do that. + UNREACHABLE_MSG("Invalid condition depth in DMNT Cheat VM"); + } +} + +u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) { + switch (bit_width) { + case 1: + return value.bit8; + case 2: + return value.bit16; + case 4: + return value.bit32; + case 8: + return value.bit64; + default: + // Invalid bit width -> return 0. + return 0; + } +} + +u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata, + MemoryAccessType mem_type, u64 rel_address) { + switch (mem_type) { + case MemoryAccessType::MainNso: + default: + return metadata.main_nso_extents.base + rel_address; + case MemoryAccessType::Heap: + return metadata.heap_extents.base + rel_address; + } +} + +void DmntCheatVm::ResetState() { + registers.fill(0); + saved_values.fill(0); + loop_tops.fill(0); + instruction_ptr = 0; + condition_depth = 0; + decode_success = true; +} + +bool DmntCheatVm::LoadProgram(const std::vector<CheatEntry>& entries) { + // Reset opcode count. + num_opcodes = 0; + + for (std::size_t i = 0; i < entries.size(); i++) { + if (entries[i].enabled) { + // Bounds check. + if (entries[i].definition.num_opcodes + num_opcodes > MaximumProgramOpcodeCount) { + num_opcodes = 0; + return false; + } + + for (std::size_t n = 0; n < entries[i].definition.num_opcodes; n++) { + program[num_opcodes++] = entries[i].definition.opcodes[n]; + } + } + } + + return true; +} + +void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { + CheatVmOpcode cur_opcode{}; + + // Get Keys down. + u64 kDown = callbacks->HidKeysDown(); + + callbacks->CommandLog("Started VM execution."); + callbacks->CommandLog(fmt::format("Main NSO: {:012X}", metadata.main_nso_extents.base)); + callbacks->CommandLog(fmt::format("Heap: {:012X}", metadata.main_nso_extents.base)); + callbacks->CommandLog(fmt::format("Keys Down: {:08X}", static_cast<u32>(kDown & 0x0FFFFFFF))); + + // Clear VM state. + ResetState(); + + // Loop until program finishes. + while (DecodeNextOpcode(cur_opcode)) { + callbacks->CommandLog( + fmt::format("Instruction Ptr: {:04X}", static_cast<u32>(instruction_ptr))); + + for (std::size_t i = 0; i < NumRegisters; i++) { + callbacks->CommandLog(fmt::format("Registers[{:02X}]: {:016X}", i, registers[i])); + } + + for (std::size_t i = 0; i < NumRegisters; i++) { + callbacks->CommandLog(fmt::format("SavedRegs[{:02X}]: {:016X}", i, saved_values[i])); + } + LogOpcode(cur_opcode); + + // Increment conditional depth, if relevant. + if (cur_opcode.begin_conditional_block) { + condition_depth++; + } + + if (auto store_static = std::get_if<StoreStaticOpcode>(&cur_opcode.opcode)) { + // Calculate address, write value to memory. + u64 dst_address = GetCheatProcessAddress(metadata, store_static->mem_type, + store_static->rel_address + + registers[store_static->offset_register]); + u64 dst_value = GetVmInt(store_static->value, store_static->bit_width); + switch (store_static->bit_width) { + case 1: + case 2: + case 4: + case 8: + callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width); + break; + } + } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) { + // Read value from memory. + u64 src_address = + GetCheatProcessAddress(metadata, begin_cond->mem_type, begin_cond->rel_address); + u64 src_value = 0; + switch (store_static->bit_width) { + case 1: + case 2: + case 4: + case 8: + callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width); + break; + } + // Check against condition. + u64 cond_value = GetVmInt(begin_cond->value, begin_cond->bit_width); + bool cond_met = false; + switch (begin_cond->cond_type) { + case ConditionalComparisonType::GT: + cond_met = src_value > cond_value; + break; + case ConditionalComparisonType::GE: + cond_met = src_value >= cond_value; + break; + case ConditionalComparisonType::LT: + cond_met = src_value < cond_value; + break; + case ConditionalComparisonType::LE: + cond_met = src_value <= cond_value; + break; + case ConditionalComparisonType::EQ: + cond_met = src_value == cond_value; + break; + case ConditionalComparisonType::NE: + cond_met = src_value != cond_value; + break; + } + // Skip conditional block if condition not met. + if (!cond_met) { + SkipConditionalBlock(); + } + } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) { + // Decrement the condition depth. + // We will assume, graciously, that mismatched conditional block ends are a nop. + if (condition_depth > 0) { + condition_depth--; + } + } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) { + if (ctrl_loop->start_loop) { + // Start a loop. + registers[ctrl_loop->reg_index] = ctrl_loop->num_iters; + loop_tops[ctrl_loop->reg_index] = instruction_ptr; + } else { + // End a loop. + registers[ctrl_loop->reg_index]--; + if (registers[ctrl_loop->reg_index] != 0) { + instruction_ptr = loop_tops[ctrl_loop->reg_index]; + } + } + } else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&cur_opcode.opcode)) { + // Set a register to a static value. + registers[ldr_static->reg_index] = ldr_static->value; + } else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&cur_opcode.opcode)) { + // Choose source address. + u64 src_address; + if (ldr_memory->load_from_reg) { + src_address = registers[ldr_memory->reg_index] + ldr_memory->rel_address; + } else { + src_address = + GetCheatProcessAddress(metadata, ldr_memory->mem_type, ldr_memory->rel_address); + } + // Read into register. Gateway only reads on valid bitwidth. + switch (ldr_memory->bit_width) { + case 1: + case 2: + case 4: + case 8: + callbacks->MemoryRead(src_address, ®isters[ldr_memory->reg_index], + ldr_memory->bit_width); + break; + } + } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) { + // Calculate address. + u64 dst_address = registers[str_static->reg_index]; + u64 dst_value = str_static->value; + if (str_static->add_offset_reg) { + dst_address += registers[str_static->offset_reg_index]; + } + // Write value to memory. Gateway only writes on valid bitwidth. + switch (str_static->bit_width) { + case 1: + case 2: + case 4: + case 8: + callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width); + break; + } + // Increment register if relevant. + if (str_static->increment_reg) { + registers[str_static->reg_index] += str_static->bit_width; + } + } else if (auto perform_math_static = + std::get_if<PerformArithmeticStaticOpcode>(&cur_opcode.opcode)) { + // Do requested math. + switch (perform_math_static->math_type) { + case RegisterArithmeticType::Addition: + registers[perform_math_static->reg_index] += + static_cast<u64>(perform_math_static->value); + break; + case RegisterArithmeticType::Subtraction: + registers[perform_math_static->reg_index] -= + static_cast<u64>(perform_math_static->value); + break; + case RegisterArithmeticType::Multiplication: + registers[perform_math_static->reg_index] *= + static_cast<u64>(perform_math_static->value); + break; + case RegisterArithmeticType::LeftShift: + registers[perform_math_static->reg_index] <<= + static_cast<u64>(perform_math_static->value); + break; + case RegisterArithmeticType::RightShift: + registers[perform_math_static->reg_index] >>= + static_cast<u64>(perform_math_static->value); + break; + default: + // Do not handle extensions here. + break; + } + // Apply bit width. + switch (perform_math_static->bit_width) { + case 1: + registers[perform_math_static->reg_index] = + static_cast<u8>(registers[perform_math_static->reg_index]); + break; + case 2: + registers[perform_math_static->reg_index] = + static_cast<u16>(registers[perform_math_static->reg_index]); + break; + case 4: + registers[perform_math_static->reg_index] = + static_cast<u32>(registers[perform_math_static->reg_index]); + break; + case 8: + registers[perform_math_static->reg_index] = + static_cast<u64>(registers[perform_math_static->reg_index]); + break; + } + } else if (auto begin_keypress_cond = + std::get_if<BeginKeypressConditionalOpcode>(&cur_opcode.opcode)) { + // Check for keypress. + if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) { + // Keys not pressed. Skip conditional block. + SkipConditionalBlock(); + } + } else if (auto perform_math_reg = + std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) { + const u64 operand_1_value = registers[perform_math_reg->src_reg_1_index]; + const u64 operand_2_value = + perform_math_reg->has_immediate + ? GetVmInt(perform_math_reg->value, perform_math_reg->bit_width) + : registers[perform_math_reg->src_reg_2_index]; + + u64 res_val = 0; + // Do requested math. + switch (perform_math_reg->math_type) { + case RegisterArithmeticType::Addition: + res_val = operand_1_value + operand_2_value; + break; + case RegisterArithmeticType::Subtraction: + res_val = operand_1_value - operand_2_value; + break; + case RegisterArithmeticType::Multiplication: + res_val = operand_1_value * operand_2_value; + break; + case RegisterArithmeticType::LeftShift: + res_val = operand_1_value << operand_2_value; + break; + case RegisterArithmeticType::RightShift: + res_val = operand_1_value >> operand_2_value; + break; + case RegisterArithmeticType::LogicalAnd: + res_val = operand_1_value & operand_2_value; + break; + case RegisterArithmeticType::LogicalOr: + res_val = operand_1_value | operand_2_value; + break; + case RegisterArithmeticType::LogicalNot: + res_val = ~operand_1_value; + break; + case RegisterArithmeticType::LogicalXor: + res_val = operand_1_value ^ operand_2_value; + break; + case RegisterArithmeticType::None: + res_val = operand_1_value; + break; + } + + // Apply bit width. + switch (perform_math_reg->bit_width) { + case 1: + res_val = static_cast<u8>(res_val); + break; + case 2: + res_val = static_cast<u16>(res_val); + break; + case 4: + res_val = static_cast<u32>(res_val); + break; + case 8: + res_val = static_cast<u64>(res_val); + break; + } + + // Save to register. + registers[perform_math_reg->dst_reg_index] = res_val; + } else if (auto str_register = + std::get_if<StoreRegisterToAddressOpcode>(&cur_opcode.opcode)) { + // Calculate address. + u64 dst_value = registers[str_register->str_reg_index]; + u64 dst_address = registers[str_register->addr_reg_index]; + switch (str_register->ofs_type) { + case StoreRegisterOffsetType::None: + // Nothing more to do + break; + case StoreRegisterOffsetType::Reg: + dst_address += registers[str_register->ofs_reg_index]; + break; + case StoreRegisterOffsetType::Imm: + dst_address += str_register->rel_address; + break; + case StoreRegisterOffsetType::MemReg: + dst_address = GetCheatProcessAddress(metadata, str_register->mem_type, + registers[str_register->addr_reg_index]); + break; + case StoreRegisterOffsetType::MemImm: + dst_address = GetCheatProcessAddress(metadata, str_register->mem_type, + str_register->rel_address); + break; + case StoreRegisterOffsetType::MemImmReg: + dst_address = GetCheatProcessAddress(metadata, str_register->mem_type, + registers[str_register->addr_reg_index] + + str_register->rel_address); + break; + } + + // Write value to memory. Write only on valid bitwidth. + switch (str_register->bit_width) { + case 1: + case 2: + case 4: + case 8: + callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width); + break; + } + + // Increment register if relevant. + if (str_register->increment_reg) { + registers[str_register->addr_reg_index] += str_register->bit_width; + } + } else if (auto begin_reg_cond = + std::get_if<BeginRegisterConditionalOpcode>(&cur_opcode.opcode)) { + // Get value from register. + u64 src_value = 0; + switch (begin_reg_cond->bit_width) { + case 1: + src_value = static_cast<u8>(registers[begin_reg_cond->val_reg_index] & 0xFFul); + break; + case 2: + src_value = static_cast<u16>(registers[begin_reg_cond->val_reg_index] & 0xFFFFul); + break; + case 4: + src_value = + static_cast<u32>(registers[begin_reg_cond->val_reg_index] & 0xFFFFFFFFul); + break; + case 8: + src_value = static_cast<u64>(registers[begin_reg_cond->val_reg_index] & + 0xFFFFFFFFFFFFFFFFul); + break; + } + + // Read value from memory. + u64 cond_value = 0; + if (begin_reg_cond->comp_type == CompareRegisterValueType::StaticValue) { + cond_value = GetVmInt(begin_reg_cond->value, begin_reg_cond->bit_width); + } else if (begin_reg_cond->comp_type == CompareRegisterValueType::OtherRegister) { + switch (begin_reg_cond->bit_width) { + case 1: + cond_value = + static_cast<u8>(registers[begin_reg_cond->other_reg_index] & 0xFFul); + break; + case 2: + cond_value = + static_cast<u16>(registers[begin_reg_cond->other_reg_index] & 0xFFFFul); + break; + case 4: + cond_value = + static_cast<u32>(registers[begin_reg_cond->other_reg_index] & 0xFFFFFFFFul); + break; + case 8: + cond_value = static_cast<u64>(registers[begin_reg_cond->other_reg_index] & + 0xFFFFFFFFFFFFFFFFul); + break; + } + } else { + u64 cond_address = 0; + switch (begin_reg_cond->comp_type) { + case CompareRegisterValueType::MemoryRelAddr: + cond_address = GetCheatProcessAddress(metadata, begin_reg_cond->mem_type, + begin_reg_cond->rel_address); + break; + case CompareRegisterValueType::MemoryOfsReg: + cond_address = GetCheatProcessAddress(metadata, begin_reg_cond->mem_type, + registers[begin_reg_cond->ofs_reg_index]); + break; + case CompareRegisterValueType::RegisterRelAddr: + cond_address = + registers[begin_reg_cond->addr_reg_index] + begin_reg_cond->rel_address; + break; + case CompareRegisterValueType::RegisterOfsReg: + cond_address = registers[begin_reg_cond->addr_reg_index] + + registers[begin_reg_cond->ofs_reg_index]; + break; + default: + break; + } + switch (begin_reg_cond->bit_width) { + case 1: + case 2: + case 4: + case 8: + callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width); + break; + } + } + + // Check against condition. + bool cond_met = false; + switch (begin_reg_cond->cond_type) { + case ConditionalComparisonType::GT: + cond_met = src_value > cond_value; + break; + case ConditionalComparisonType::GE: + cond_met = src_value >= cond_value; + break; + case ConditionalComparisonType::LT: + cond_met = src_value < cond_value; + break; + case ConditionalComparisonType::LE: + cond_met = src_value <= cond_value; + break; + case ConditionalComparisonType::EQ: + cond_met = src_value == cond_value; + break; + case ConditionalComparisonType::NE: + cond_met = src_value != cond_value; + break; + } + + // Skip conditional block if condition not met. + if (!cond_met) { + SkipConditionalBlock(); + } + } else if (auto save_restore_reg = + std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) { + // Save or restore a register. + switch (save_restore_reg->op_type) { + case SaveRestoreRegisterOpType::ClearRegs: + registers[save_restore_reg->dst_index] = 0ul; + break; + case SaveRestoreRegisterOpType::ClearSaved: + saved_values[save_restore_reg->dst_index] = 0ul; + break; + case SaveRestoreRegisterOpType::Save: + saved_values[save_restore_reg->dst_index] = registers[save_restore_reg->src_index]; + break; + case SaveRestoreRegisterOpType::Restore: + default: + registers[save_restore_reg->dst_index] = saved_values[save_restore_reg->src_index]; + break; + } + } else if (auto save_restore_regmask = + std::get_if<SaveRestoreRegisterMaskOpcode>(&cur_opcode.opcode)) { + // Save or restore register mask. + u64* src; + u64* dst; + switch (save_restore_regmask->op_type) { + case SaveRestoreRegisterOpType::ClearSaved: + case SaveRestoreRegisterOpType::Save: + src = registers.data(); + dst = saved_values.data(); + break; + case SaveRestoreRegisterOpType::ClearRegs: + case SaveRestoreRegisterOpType::Restore: + default: + src = registers.data(); + dst = saved_values.data(); + break; + } + for (std::size_t i = 0; i < NumRegisters; i++) { + if (save_restore_regmask->should_operate[i]) { + switch (save_restore_regmask->op_type) { + case SaveRestoreRegisterOpType::ClearSaved: + case SaveRestoreRegisterOpType::ClearRegs: + dst[i] = 0ul; + break; + case SaveRestoreRegisterOpType::Save: + case SaveRestoreRegisterOpType::Restore: + default: + dst[i] = src[i]; + break; + } + } + } + } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) { + // Read value from memory. + u64 log_value = 0; + if (debug_log->val_type == DebugLogValueType::RegisterValue) { + switch (debug_log->bit_width) { + case 1: + log_value = static_cast<u8>(registers[debug_log->val_reg_index] & 0xFFul); + break; + case 2: + log_value = static_cast<u16>(registers[debug_log->val_reg_index] & 0xFFFFul); + break; + case 4: + log_value = + static_cast<u32>(registers[debug_log->val_reg_index] & 0xFFFFFFFFul); + break; + case 8: + log_value = static_cast<u64>(registers[debug_log->val_reg_index] & + 0xFFFFFFFFFFFFFFFFul); + break; + } + } else { + u64 val_address = 0; + switch (debug_log->val_type) { + case DebugLogValueType::MemoryRelAddr: + val_address = GetCheatProcessAddress(metadata, debug_log->mem_type, + debug_log->rel_address); + break; + case DebugLogValueType::MemoryOfsReg: + val_address = GetCheatProcessAddress(metadata, debug_log->mem_type, + registers[debug_log->ofs_reg_index]); + break; + case DebugLogValueType::RegisterRelAddr: + val_address = registers[debug_log->addr_reg_index] + debug_log->rel_address; + break; + case DebugLogValueType::RegisterOfsReg: + val_address = + registers[debug_log->addr_reg_index] + registers[debug_log->ofs_reg_index]; + break; + default: + break; + } + switch (debug_log->bit_width) { + case 1: + case 2: + case 4: + case 8: + callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width); + break; + } + } + + // Log value. + DebugLog(debug_log->log_id, log_value); + } + } +} + +} // namespace Memory |