From 12aa127df3826857149bfc4b787cfb7df3fdcafe Mon Sep 17 00:00:00 2001
From: Zach Hilman
Date: Thu, 30 May 2019 19:34:02 -0400
Subject: memory: Port Atmosphere's DmntCheatVm
This was done because the current VM contained many inaccuracies and this also allows cheats to have identical behavior between hardware and yuzu.---
src/core/memory/dmnt_cheat_types.h | 58 ++
src/core/memory/dmnt_cheat_vm.cpp | 1206 ++++++++++++++++++++++++++++++++++++
src/core/memory/dmnt_cheat_vm.h | 334 ++++++++++
3 files changed, 1598 insertions(+)
create mode 100644 src/core/memory/dmnt_cheat_types.h
create mode 100644 src/core/memory/dmnt_cheat_vm.cpp
create mode 100644 src/core/memory/dmnt_cheat_vm.h
(limited to 'src/core/memory')
diff --git a/src/core/memory/dmnt_cheat_types.h b/src/core/memory/dmnt_cheat_types.h
new file mode 100644
index 000000000..aa1264c32
--- /dev/null
+++ b/src/core/memory/dmnt_cheat_types.h
@@ -0,0 +1,58 @@
+/*
+ * 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 .
+ */
+
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Memory {
+
+struct MemoryRegionExtents {
+ u64 base;
+ u64 size;
+};
+
+struct CheatProcessMetadata {
+ u64 process_id;
+ u64 title_id;
+ MemoryRegionExtents main_nso_extents;
+ MemoryRegionExtents heap_extents;
+ MemoryRegionExtents alias_extents;
+ MemoryRegionExtents address_space_extents;
+ std::array main_nso_build_id;
+};
+
+struct CheatDefinition {
+ std::array readable_name;
+ u32 num_opcodes;
+ std::array opcodes;
+};
+
+struct CheatEntry {
+ bool enabled;
+ u32 cheat_id;
+ CheatDefinition definition;
+};
+
+} // namespace Memory
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp
new file mode 100644
index 000000000..a3f450dac
--- /dev/null
+++ b/src/core/memory/dmnt_cheat_vm.cpp
@@ -0,0 +1,1206 @@
+/*
+ * 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 .
+ */
+
+/*
+ * 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 {
+
+void DmntCheatVm::DebugLog(u32 log_id, u64 value) {
+ callbacks->DebugLog(static_cast(log_id), value);
+}
+
+void DmntCheatVm::LogOpcode(const CheatVmOpcode& opcode) {
+ switch (opcode.opcode) {
+ case CheatVmOpcodeType_StoreStatic:
+ this->LogToDebugFile("Opcode: Store Static\n");
+ this->LogToDebugFile("Bit Width: %x\n", opcode.store_static.bit_width);
+ this->LogToDebugFile("Mem Type: %x\n", opcode.store_static.mem_type);
+ this->LogToDebugFile("Reg Idx: %x\n", opcode.store_static.offset_register);
+ this->LogToDebugFile("Rel Addr: %lx\n", opcode.store_static.rel_address);
+ this->LogToDebugFile("Value: %lx\n", opcode.store_static.value.bit64);
+ break;
+ case CheatVmOpcodeType_BeginConditionalBlock:
+ this->LogToDebugFile("Opcode: Begin Conditional\n");
+ this->LogToDebugFile("Bit Width: %x\n", opcode.begin_cond.bit_width);
+ this->LogToDebugFile("Mem Type: %x\n", opcode.begin_cond.mem_type);
+ this->LogToDebugFile("Cond Type: %x\n", opcode.begin_cond.cond_type);
+ this->LogToDebugFile("Rel Addr: %lx\n", opcode.begin_cond.rel_address);
+ this->LogToDebugFile("Value: %lx\n", opcode.begin_cond.value.bit64);
+ break;
+ case CheatVmOpcodeType_EndConditionalBlock:
+ this->LogToDebugFile("Opcode: End Conditional\n");
+ break;
+ case CheatVmOpcodeType_ControlLoop:
+ if (opcode.ctrl_loop.start_loop) {
+ this->LogToDebugFile("Opcode: Start Loop\n");
+ this->LogToDebugFile("Reg Idx: %x\n", opcode.ctrl_loop.reg_index);
+ this->LogToDebugFile("Num Iters: %x\n", opcode.ctrl_loop.num_iters);
+ } else {
+ this->LogToDebugFile("Opcode: End Loop\n");
+ this->LogToDebugFile("Reg Idx: %x\n", opcode.ctrl_loop.reg_index);
+ }
+ break;
+ case CheatVmOpcodeType_LoadRegisterStatic:
+ this->LogToDebugFile("Opcode: Load Register Static\n");
+ this->LogToDebugFile("Reg Idx: %x\n", opcode.ldr_static.reg_index);
+ this->LogToDebugFile("Value: %lx\n", opcode.ldr_static.value);
+ break;
+ case CheatVmOpcodeType_LoadRegisterMemory:
+ this->LogToDebugFile("Opcode: Load Register Memory\n");
+ this->LogToDebugFile("Bit Width: %x\n", opcode.ldr_memory.bit_width);
+ this->LogToDebugFile("Reg Idx: %x\n", opcode.ldr_memory.reg_index);
+ this->LogToDebugFile("Mem Type: %x\n", opcode.ldr_memory.mem_type);
+ this->LogToDebugFile("From Reg: %d\n", opcode.ldr_memory.load_from_reg);
+ this->LogToDebugFile("Rel Addr: %lx\n", opcode.ldr_memory.rel_address);
+ break;
+ case CheatVmOpcodeType_StoreStaticToAddress:
+ this->LogToDebugFile("Opcode: Store Static to Address\n");
+ this->LogToDebugFile("Bit Width: %x\n", opcode.str_static.bit_width);
+ this->LogToDebugFile("Reg Idx: %x\n", opcode.str_static.reg_index);
+ if (opcode.str_static.add_offset_reg) {
+ this->LogToDebugFile("O Reg Idx: %x\n", opcode.str_static.offset_reg_index);
+ }
+ this->LogToDebugFile("Incr Reg: %d\n", opcode.str_static.increment_reg);
+ this->LogToDebugFile("Value: %lx\n", opcode.str_static.value);
+ break;
+ case CheatVmOpcodeType_PerformArithmeticStatic:
+ this->LogToDebugFile("Opcode: Perform Static Arithmetic\n");
+ this->LogToDebugFile("Bit Width: %x\n", opcode.perform_math_static.bit_width);
+ this->LogToDebugFile("Reg Idx: %x\n", opcode.perform_math_static.reg_index);
+ this->LogToDebugFile("Math Type: %x\n", opcode.perform_math_static.math_type);
+ this->LogToDebugFile("Value: %lx\n", opcode.perform_math_static.value);
+ break;
+ case CheatVmOpcodeType_BeginKeypressConditionalBlock:
+ this->LogToDebugFile("Opcode: Begin Keypress Conditional\n");
+ this->LogToDebugFile("Key Mask: %x\n", opcode.begin_keypress_cond.key_mask);
+ break;
+ case CheatVmOpcodeType_PerformArithmeticRegister:
+ this->LogToDebugFile("Opcode: Perform Register Arithmetic\n");
+ this->LogToDebugFile("Bit Width: %x\n", opcode.perform_math_reg.bit_width);
+ this->LogToDebugFile("Dst Idx: %x\n", opcode.perform_math_reg.dst_reg_index);
+ this->LogToDebugFile("Src1 Idx: %x\n", opcode.perform_math_reg.src_reg_1_index);
+ if (opcode.perform_math_reg.has_immediate) {
+ this->LogToDebugFile("Value: %lx\n", opcode.perform_math_reg.value.bit64);
+ } else {
+ this->LogToDebugFile("Src2 Idx: %x\n", opcode.perform_math_reg.src_reg_2_index);
+ }
+ break;
+ case CheatVmOpcodeType_StoreRegisterToAddress:
+ this->LogToDebugFile("Opcode: Store Register to Address\n");
+ this->LogToDebugFile("Bit Width: %x\n", opcode.str_register.bit_width);
+ this->LogToDebugFile("S Reg Idx: %x\n", opcode.str_register.str_reg_index);
+ this->LogToDebugFile("A Reg Idx: %x\n", opcode.str_register.addr_reg_index);
+ this->LogToDebugFile("Incr Reg: %d\n", opcode.str_register.increment_reg);
+ switch (opcode.str_register.ofs_type) {
+ case StoreRegisterOffsetType_None:
+ break;
+ case StoreRegisterOffsetType_Reg:
+ this->LogToDebugFile("O Reg Idx: %x\n", opcode.str_register.ofs_reg_index);
+ break;
+ case StoreRegisterOffsetType_Imm:
+ this->LogToDebugFile("Rel Addr: %lx\n", opcode.str_register.rel_address);
+ break;
+ case StoreRegisterOffsetType_MemReg:
+ this->LogToDebugFile("Mem Type: %x\n", opcode.str_register.mem_type);
+ break;
+ case StoreRegisterOffsetType_MemImm:
+ case StoreRegisterOffsetType_MemImmReg:
+ this->LogToDebugFile("Mem Type: %x\n", opcode.str_register.mem_type);
+ this->LogToDebugFile("Rel Addr: %lx\n", opcode.str_register.rel_address);
+ break;
+ }
+ break;
+ case CheatVmOpcodeType_BeginRegisterConditionalBlock:
+ this->LogToDebugFile("Opcode: Begin Register Conditional\n");
+ this->LogToDebugFile("Bit Width: %x\n", opcode.begin_reg_cond.bit_width);
+ this->LogToDebugFile("Cond Type: %x\n", opcode.begin_reg_cond.cond_type);
+ this->LogToDebugFile("V Reg Idx: %x\n", opcode.begin_reg_cond.val_reg_index);
+ switch (opcode.begin_reg_cond.comp_type) {
+ case CompareRegisterValueType_StaticValue:
+ this->LogToDebugFile("Comp Type: Static Value\n");
+ this->LogToDebugFile("Value: %lx\n", opcode.begin_reg_cond.value.bit64);
+ break;
+ case CompareRegisterValueType_OtherRegister:
+ this->LogToDebugFile("Comp Type: Other Register\n");
+ this->LogToDebugFile("X Reg Idx: %x\n", opcode.begin_reg_cond.other_reg_index);
+ break;
+ case CompareRegisterValueType_MemoryRelAddr:
+ this->LogToDebugFile("Comp Type: Memory Relative Address\n");
+ this->LogToDebugFile("Mem Type: %x\n", opcode.begin_reg_cond.mem_type);
+ this->LogToDebugFile("Rel Addr: %lx\n", opcode.begin_reg_cond.rel_address);
+ break;
+ case CompareRegisterValueType_MemoryOfsReg:
+ this->LogToDebugFile("Comp Type: Memory Offset Register\n");
+ this->LogToDebugFile("Mem Type: %x\n", opcode.begin_reg_cond.mem_type);
+ this->LogToDebugFile("O Reg Idx: %x\n", opcode.begin_reg_cond.ofs_reg_index);
+ break;
+ case CompareRegisterValueType_RegisterRelAddr:
+ this->LogToDebugFile("Comp Type: Register Relative Address\n");
+ this->LogToDebugFile("A Reg Idx: %x\n", opcode.begin_reg_cond.addr_reg_index);
+ this->LogToDebugFile("Rel Addr: %lx\n", opcode.begin_reg_cond.rel_address);
+ break;
+ case CompareRegisterValueType_RegisterOfsReg:
+ this->LogToDebugFile("Comp Type: Register Offset Register\n");
+ this->LogToDebugFile("A Reg Idx: %x\n", opcode.begin_reg_cond.addr_reg_index);
+ this->LogToDebugFile("O Reg Idx: %x\n", opcode.begin_reg_cond.ofs_reg_index);
+ break;
+ }
+ break;
+ case CheatVmOpcodeType_SaveRestoreRegister:
+ this->LogToDebugFile("Opcode: Save or Restore Register\n");
+ this->LogToDebugFile("Dst Idx: %x\n", opcode.save_restore_reg.dst_index);
+ this->LogToDebugFile("Src Idx: %x\n", opcode.save_restore_reg.src_index);
+ this->LogToDebugFile("Op Type: %d\n", opcode.save_restore_reg.op_type);
+ break;
+ case CheatVmOpcodeType_SaveRestoreRegisterMask:
+ this->LogToDebugFile("Opcode: Save or Restore Register Mask\n");
+ this->LogToDebugFile("Op Type: %d\n", opcode.save_restore_regmask.op_type);
+ for (size_t i = 0; i < NumRegisters; i++) {
+ this->LogToDebugFile("Act[%02x]: %d\n", i,
+ opcode.save_restore_regmask.should_operate[i]);
+ }
+ break;
+ case CheatVmOpcodeType_DebugLog:
+ this->LogToDebugFile("Opcode: Debug Log\n");
+ this->LogToDebugFile("Bit Width: %x\n", opcode.debug_log.bit_width);
+ this->LogToDebugFile("Log ID: %x\n", opcode.debug_log.log_id);
+ this->LogToDebugFile("Val Type: %x\n", opcode.debug_log.val_type);
+ switch (opcode.debug_log.val_type) {
+ case DebugLogValueType_RegisterValue:
+ this->LogToDebugFile("Val Type: Register Value\n");
+ this->LogToDebugFile("X Reg Idx: %x\n", opcode.debug_log.val_reg_index);
+ break;
+ case DebugLogValueType_MemoryRelAddr:
+ this->LogToDebugFile("Val Type: Memory Relative Address\n");
+ this->LogToDebugFile("Mem Type: %x\n", opcode.debug_log.mem_type);
+ this->LogToDebugFile("Rel Addr: %lx\n", opcode.debug_log.rel_address);
+ break;
+ case DebugLogValueType_MemoryOfsReg:
+ this->LogToDebugFile("Val Type: Memory Offset Register\n");
+ this->LogToDebugFile("Mem Type: %x\n", opcode.debug_log.mem_type);
+ this->LogToDebugFile("O Reg Idx: %x\n", opcode.debug_log.ofs_reg_index);
+ break;
+ case DebugLogValueType_RegisterRelAddr:
+ this->LogToDebugFile("Val Type: Register Relative Address\n");
+ this->LogToDebugFile("A Reg Idx: %x\n", opcode.debug_log.addr_reg_index);
+ this->LogToDebugFile("Rel Addr: %lx\n", opcode.debug_log.rel_address);
+ break;
+ case DebugLogValueType_RegisterOfsReg:
+ this->LogToDebugFile("Val Type: Register Offset Register\n");
+ this->LogToDebugFile("A Reg Idx: %x\n", opcode.debug_log.addr_reg_index);
+ this->LogToDebugFile("O Reg Idx: %x\n", opcode.debug_log.ofs_reg_index);
+ break;
+ }
+ default:
+ this->LogToDebugFile("Unknown opcode: %x\n", opcode.opcode);
+ break;
+ }
+}
+
+DmntCheatVm::Callbacks::~Callbacks() = default;
+
+bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
+ /* If we've ever seen a decode failure, return false. */
+ bool valid = this->decode_success;
+ CheatVmOpcode opcode = {};
+ SCOPE_EXIT({
+ this->decode_success &= valid;
+ if (valid) {
+ out = opcode;
+ }
+ });
+
+ /* Helper function for getting instruction dwords. */
+ auto GetNextDword = [&]() {
+ if (this->instruction_ptr >= this->num_opcodes) {
+ valid = false;
+ return static_cast(0);
+ }
+ return this->program[this->instruction_ptr++];
+ };
+
+ /* Helper function for parsing a VmInt. */
+ auto GetNextVmInt = [&](const u32 bit_width) {
+ VmInt val = {0};
+
+ const u32 first_dword = GetNextDword();
+ switch (bit_width) {
+ case 1:
+ val.bit8 = (u8)first_dword;
+ break;
+ case 2:
+ val.bit16 = (u16)first_dword;
+ break;
+ case 4:
+ val.bit32 = first_dword;
+ break;
+ case 8:
+ val.bit64 = (((u64)first_dword) << 32ul) | ((u64)GetNextDword());
+ break;
+ }
+
+ return val;
+ };
+
+ /* Read opcode. */
+ const u32 first_dword = GetNextDword();
+ if (!valid) {
+ return valid;
+ }
+
+ opcode.opcode = (CheatVmOpcodeType)(((first_dword >> 28) & 0xF));
+ if (opcode.opcode >= CheatVmOpcodeType_ExtendedWidth) {
+ opcode.opcode =
+ (CheatVmOpcodeType)((((u32)opcode.opcode) << 4) | ((first_dword >> 24) & 0xF));
+ }
+ if (opcode.opcode >= CheatVmOpcodeType_DoubleExtendedWidth) {
+ opcode.opcode =
+ (CheatVmOpcodeType)((((u32)opcode.opcode) << 4) | ((first_dword >> 20) & 0xF));
+ }
+
+ /* detect condition start. */
+ switch (opcode.opcode) {
+ case CheatVmOpcodeType_BeginConditionalBlock:
+ case CheatVmOpcodeType_BeginKeypressConditionalBlock:
+ case CheatVmOpcodeType_BeginRegisterConditionalBlock:
+ opcode.begin_conditional_block = true;
+ break;
+ default:
+ opcode.begin_conditional_block = false;
+ break;
+ }
+
+ switch (opcode.opcode) {
+ case CheatVmOpcodeType_StoreStatic: {
+ /* 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY) */
+ /* Read additional words. */
+ const u32 second_dword = GetNextDword();
+ opcode.store_static.bit_width = (first_dword >> 24) & 0xF;
+ opcode.store_static.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF);
+ opcode.store_static.offset_register = ((first_dword >> 16) & 0xF);
+ opcode.store_static.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword);
+ opcode.store_static.value = GetNextVmInt(opcode.store_static.bit_width);
+ } break;
+ case CheatVmOpcodeType_BeginConditionalBlock: {
+ /* 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY) */
+ /* Read additional words. */
+ const u32 second_dword = GetNextDword();
+ opcode.begin_cond.bit_width = (first_dword >> 24) & 0xF;
+ opcode.begin_cond.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF);
+ opcode.begin_cond.cond_type = (ConditionalComparisonType)((first_dword >> 16) & 0xF);
+ opcode.begin_cond.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword);
+ opcode.begin_cond.value = GetNextVmInt(opcode.store_static.bit_width);
+ } break;
+ case CheatVmOpcodeType_EndConditionalBlock: {
+ /* 20000000 */
+ /* There's actually nothing left to process here! */
+ } break;
+ case CheatVmOpcodeType_ControlLoop: {
+ /* 300R0000 VVVVVVVV */
+ /* 310R0000 */
+ /* Parse register, whether loop start or loop end. */
+ opcode.ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0;
+ opcode.ctrl_loop.reg_index = ((first_dword >> 20) & 0xF);
+
+ /* Read number of iters if loop start. */
+ if (opcode.ctrl_loop.start_loop) {
+ opcode.ctrl_loop.num_iters = GetNextDword();
+ }
+ } break;
+ case CheatVmOpcodeType_LoadRegisterStatic: {
+ /* 400R0000 VVVVVVVV VVVVVVVV */
+ /* Read additional words. */
+ opcode.ldr_static.reg_index = ((first_dword >> 16) & 0xF);
+ opcode.ldr_static.value = (((u64)GetNextDword()) << 32ul) | ((u64)GetNextDword());
+ } break;
+ case CheatVmOpcodeType_LoadRegisterMemory: {
+ /* 5TMRI0AA AAAAAAAA */
+ /* Read additional words. */
+ const u32 second_dword = GetNextDword();
+ opcode.ldr_memory.bit_width = (first_dword >> 24) & 0xF;
+ opcode.ldr_memory.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF);
+ opcode.ldr_memory.reg_index = ((first_dword >> 16) & 0xF);
+ opcode.ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0;
+ opcode.ldr_memory.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword);
+ } break;
+ case CheatVmOpcodeType_StoreStaticToAddress: {
+ /* 6T0RIor0 VVVVVVVV VVVVVVVV */
+ /* Read additional words. */
+ opcode.str_static.bit_width = (first_dword >> 24) & 0xF;
+ opcode.str_static.reg_index = ((first_dword >> 16) & 0xF);
+ opcode.str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0;
+ opcode.str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0;
+ opcode.str_static.offset_reg_index = ((first_dword >> 4) & 0xF);
+ opcode.str_static.value = (((u64)GetNextDword()) << 32ul) | ((u64)GetNextDword());
+ } break;
+ case CheatVmOpcodeType_PerformArithmeticStatic: {
+ /* 7T0RC000 VVVVVVVV */
+ /* Read additional words. */
+ opcode.perform_math_static.bit_width = (first_dword >> 24) & 0xF;
+ opcode.perform_math_static.reg_index = ((first_dword >> 16) & 0xF);
+ opcode.perform_math_static.math_type = (RegisterArithmeticType)((first_dword >> 12) & 0xF);
+ opcode.perform_math_static.value = GetNextDword();
+ } break;
+ case CheatVmOpcodeType_BeginKeypressConditionalBlock: {
+ /* 8kkkkkkk */
+ /* Just parse the mask. */
+ opcode.begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF;
+ } break;
+ case CheatVmOpcodeType_PerformArithmeticRegister: {
+ /* 9TCRSIs0 (VVVVVVVV (VVVVVVVV)) */
+ opcode.perform_math_reg.bit_width = (first_dword >> 24) & 0xF;
+ opcode.perform_math_reg.math_type = (RegisterArithmeticType)((first_dword >> 20) & 0xF);
+ opcode.perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF);
+ opcode.perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF);
+ opcode.perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0;
+ if (opcode.perform_math_reg.has_immediate) {
+ opcode.perform_math_reg.src_reg_2_index = 0;
+ opcode.perform_math_reg.value = GetNextVmInt(opcode.perform_math_reg.bit_width);
+ } else {
+ opcode.perform_math_reg.src_reg_2_index = ((first_dword >> 4) & 0xF);
+ }
+ } break;
+ case CheatVmOpcodeType_StoreRegisterToAddress: {
+ /* 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) */
+ opcode.str_register.bit_width = (first_dword >> 24) & 0xF;
+ opcode.str_register.str_reg_index = ((first_dword >> 20) & 0xF);
+ opcode.str_register.addr_reg_index = ((first_dword >> 16) & 0xF);
+ opcode.str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0;
+ opcode.str_register.ofs_type = (StoreRegisterOffsetType)(((first_dword >> 8) & 0xF));
+ opcode.str_register.ofs_reg_index = ((first_dword >> 4) & 0xF);
+ switch (opcode.str_register.ofs_type) {
+ case StoreRegisterOffsetType_None:
+ case StoreRegisterOffsetType_Reg:
+ /* Nothing more to do */
+ break;
+ case StoreRegisterOffsetType_Imm:
+ opcode.str_register.rel_address =
+ (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+ break;
+ case StoreRegisterOffsetType_MemReg:
+ opcode.str_register.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
+ break;
+ case StoreRegisterOffsetType_MemImm:
+ case StoreRegisterOffsetType_MemImmReg:
+ opcode.str_register.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
+ opcode.str_register.rel_address =
+ (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+ break;
+ default:
+ opcode.str_register.ofs_type = StoreRegisterOffsetType_None;
+ break;
+ }
+ } break;
+ case CheatVmOpcodeType_BeginRegisterConditionalBlock: {
+ /* 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. */
+ opcode.begin_reg_cond.bit_width = (first_dword >> 20) & 0xF;
+ opcode.begin_reg_cond.cond_type = (ConditionalComparisonType)((first_dword >> 16) & 0xF);
+ opcode.begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF);
+ opcode.begin_reg_cond.comp_type = (CompareRegisterValueType)((first_dword >> 8) & 0xF);
+
+ switch (opcode.begin_reg_cond.comp_type) {
+ case CompareRegisterValueType_StaticValue:
+ opcode.begin_reg_cond.value = GetNextVmInt(opcode.begin_reg_cond.bit_width);
+ break;
+ case CompareRegisterValueType_OtherRegister:
+ opcode.begin_reg_cond.other_reg_index = ((first_dword >> 4) & 0xF);
+ break;
+ case CompareRegisterValueType_MemoryRelAddr:
+ opcode.begin_reg_cond.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
+ opcode.begin_reg_cond.rel_address =
+ (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+ break;
+ case CompareRegisterValueType_MemoryOfsReg:
+ opcode.begin_reg_cond.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
+ opcode.begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
+ break;
+ case CompareRegisterValueType_RegisterRelAddr:
+ opcode.begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
+ opcode.begin_reg_cond.rel_address =
+ (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+ break;
+ case CompareRegisterValueType_RegisterOfsReg:
+ opcode.begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
+ opcode.begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
+ break;
+ }
+ } break;
+ case CheatVmOpcodeType_SaveRestoreRegister: {
+ /* 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. */
+ opcode.save_restore_reg.dst_index = (first_dword >> 16) & 0xF;
+ opcode.save_restore_reg.src_index = (first_dword >> 8) & 0xF;
+ opcode.save_restore_reg.op_type = (SaveRestoreRegisterOpType)((first_dword >> 4) & 0xF);
+ } break;
+ case CheatVmOpcodeType_SaveRestoreRegisterMask: {
+ /* 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. */
+ opcode.save_restore_regmask.op_type =
+ (SaveRestoreRegisterOpType)((first_dword >> 20) & 0xF);
+ for (size_t i = 0; i < NumRegisters; i++) {
+ opcode.save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0;
+ }
+ } break;
+ case CheatVmOpcodeType_DebugLog: {
+ /* 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. */
+ opcode.debug_log.bit_width = (first_dword >> 16) & 0xF;
+ opcode.debug_log.log_id = ((first_dword >> 12) & 0xF);
+ opcode.debug_log.val_type = (DebugLogValueType)((first_dword >> 8) & 0xF);
+
+ switch (opcode.debug_log.val_type) {
+ case DebugLogValueType_RegisterValue:
+ opcode.debug_log.val_reg_index = ((first_dword >> 4) & 0xF);
+ break;
+ case DebugLogValueType_MemoryRelAddr:
+ opcode.debug_log.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
+ opcode.debug_log.rel_address =
+ (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+ break;
+ case DebugLogValueType_MemoryOfsReg:
+ opcode.debug_log.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
+ opcode.debug_log.ofs_reg_index = (first_dword & 0xF);
+ break;
+ case DebugLogValueType_RegisterRelAddr:
+ opcode.debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
+ opcode.debug_log.rel_address =
+ (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+ break;
+ case DebugLogValueType_RegisterOfsReg:
+ opcode.debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
+ opcode.debug_log.ofs_reg_index = (first_dword & 0xF);
+ break;
+ }
+ } break;
+ case CheatVmOpcodeType_ExtendedWidth:
+ case CheatVmOpcodeType_DoubleExtendedWidth:
+ default:
+ /* Unrecognized instruction cannot be decoded. */
+ valid = false;
+ break;
+ }
+
+ /* End decoding. */
+ return valid;
+}
+
+void DmntCheatVm::SkipConditionalBlock() {
+ if (this->condition_depth > 0) {
+ /* We want to continue until we're out of the current block. */
+ const size_t desired_depth = this->condition_depth - 1;
+
+ CheatVmOpcode skip_opcode{};
+ while (this->condition_depth > desired_depth && this->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) {
+ this->condition_depth++;
+ } else if (skip_opcode.opcode == CheatVmOpcodeType_EndConditionalBlock) {
+ this->condition_depth--;
+ }
+ }
+ } else {
+ /* Skipping, but this->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() {
+ for (size_t i = 0; i < DmntCheatVm::NumRegisters; i++) {
+ this->registers[i] = 0;
+ this->saved_values[i] = 0;
+ this->loop_tops[i] = 0;
+ }
+ this->instruction_ptr = 0;
+ this->condition_depth = 0;
+ this->decode_success = true;
+}
+
+bool DmntCheatVm::LoadProgram(const std::vector& entries) {
+ /* Reset opcode count. */
+ this->num_opcodes = 0;
+
+ for (size_t i = 0; i < entries.size(); i++) {
+ if (entries[i].enabled) {
+ /* Bounds check. */
+ if (entries[i].definition.num_opcodes + this->num_opcodes > MaximumProgramOpcodeCount) {
+ this->num_opcodes = 0;
+ return false;
+ }
+
+ for (size_t n = 0; n < entries[i].definition.num_opcodes; n++) {
+ this->program[this->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();
+
+ this->LogToDebugFile("Started VM execution.\n");
+ this->LogToDebugFile("Main NSO: %012lx\n", metadata.main_nso_extents.base);
+ this->LogToDebugFile("Heap: %012lx\n", metadata.main_nso_extents.base);
+ this->LogToDebugFile("Keys Down: %08x\n", (u32)(kDown & 0x0FFFFFFF));
+
+ /* Clear VM state. */
+ this->ResetState();
+
+ /* Loop until program finishes. */
+ while (this->DecodeNextOpcode(cur_opcode)) {
+ this->LogToDebugFile("Instruction Ptr: %04x\n", (u32)this->instruction_ptr);
+
+ for (size_t i = 0; i < NumRegisters; i++) {
+ this->LogToDebugFile("Registers[%02x]: %016lx\n", i, this->registers[i]);
+ }
+
+ for (size_t i = 0; i < NumRegisters; i++) {
+ this->LogToDebugFile("SavedRegs[%02x]: %016lx\n", i, this->saved_values[i]);
+ }
+ this->LogOpcode(cur_opcode);
+
+ /* Increment conditional depth, if relevant. */
+ if (cur_opcode.begin_conditional_block) {
+ this->condition_depth++;
+ }
+
+ switch (cur_opcode.opcode) {
+ case CheatVmOpcodeType_StoreStatic: {
+ /* Calculate address, write value to memory. */
+ u64 dst_address = GetCheatProcessAddress(
+ metadata, cur_opcode.store_static.mem_type,
+ cur_opcode.store_static.rel_address +
+ this->registers[cur_opcode.store_static.offset_register]);
+ u64 dst_value =
+ GetVmInt(cur_opcode.store_static.value, cur_opcode.store_static.bit_width);
+ switch (cur_opcode.store_static.bit_width) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ callbacks->MemoryWrite(dst_address, &dst_value, cur_opcode.store_static.bit_width);
+ break;
+ }
+ } break;
+ case CheatVmOpcodeType_BeginConditionalBlock: {
+ /* Read value from memory. */
+ u64 src_address = GetCheatProcessAddress(metadata, cur_opcode.begin_cond.mem_type,
+ cur_opcode.begin_cond.rel_address);
+ u64 src_value = 0;
+ switch (cur_opcode.store_static.bit_width) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ callbacks->MemoryRead(src_address, &src_value, cur_opcode.begin_cond.bit_width);
+ break;
+ }
+ /* Check against condition. */
+ u64 cond_value = GetVmInt(cur_opcode.begin_cond.value, cur_opcode.begin_cond.bit_width);
+ bool cond_met = false;
+ switch (cur_opcode.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) {
+ this->SkipConditionalBlock();
+ }
+ } break;
+ case CheatVmOpcodeType_EndConditionalBlock:
+ /* Decrement the condition depth. */
+ /* We will assume, graciously, that mismatched conditional block ends are a nop. */
+ if (this->condition_depth > 0) {
+ this->condition_depth--;
+ }
+ break;
+ case CheatVmOpcodeType_ControlLoop:
+ if (cur_opcode.ctrl_loop.start_loop) {
+ /* Start a loop. */
+ this->registers[cur_opcode.ctrl_loop.reg_index] = cur_opcode.ctrl_loop.num_iters;
+ this->loop_tops[cur_opcode.ctrl_loop.reg_index] = this->instruction_ptr;
+ } else {
+ /* End a loop. */
+ this->registers[cur_opcode.ctrl_loop.reg_index]--;
+ if (this->registers[cur_opcode.ctrl_loop.reg_index] != 0) {
+ this->instruction_ptr = this->loop_tops[cur_opcode.ctrl_loop.reg_index];
+ }
+ }
+ break;
+ case CheatVmOpcodeType_LoadRegisterStatic:
+ /* Set a register to a static value. */
+ this->registers[cur_opcode.ldr_static.reg_index] = cur_opcode.ldr_static.value;
+ break;
+ case CheatVmOpcodeType_LoadRegisterMemory: {
+ /* Choose source address. */
+ u64 src_address;
+ if (cur_opcode.ldr_memory.load_from_reg) {
+ src_address = this->registers[cur_opcode.ldr_memory.reg_index] +
+ cur_opcode.ldr_memory.rel_address;
+ } else {
+ src_address = GetCheatProcessAddress(metadata, cur_opcode.ldr_memory.mem_type,
+ cur_opcode.ldr_memory.rel_address);
+ }
+ /* Read into register. Gateway only reads on valid bitwidth. */
+ switch (cur_opcode.ldr_memory.bit_width) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ callbacks->MemoryRead(src_address,
+ &this->registers[cur_opcode.ldr_memory.reg_index],
+ cur_opcode.ldr_memory.bit_width);
+ break;
+ }
+ } break;
+ case CheatVmOpcodeType_StoreStaticToAddress: {
+ /* Calculate address. */
+ u64 dst_address = this->registers[cur_opcode.str_static.reg_index];
+ u64 dst_value = cur_opcode.str_static.value;
+ if (cur_opcode.str_static.add_offset_reg) {
+ dst_address += this->registers[cur_opcode.str_static.offset_reg_index];
+ }
+ /* Write value to memory. Gateway only writes on valid bitwidth. */
+ switch (cur_opcode.str_static.bit_width) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ callbacks->MemoryWrite(dst_address, &dst_value, cur_opcode.str_static.bit_width);
+ break;
+ }
+ /* Increment register if relevant. */
+ if (cur_opcode.str_static.increment_reg) {
+ this->registers[cur_opcode.str_static.reg_index] += cur_opcode.str_static.bit_width;
+ }
+ } break;
+ case CheatVmOpcodeType_PerformArithmeticStatic: {
+ /* Do requested math. */
+ switch (cur_opcode.perform_math_static.math_type) {
+ case RegisterArithmeticType_Addition:
+ this->registers[cur_opcode.perform_math_static.reg_index] +=
+ (u64)cur_opcode.perform_math_static.value;
+ break;
+ case RegisterArithmeticType_Subtraction:
+ this->registers[cur_opcode.perform_math_static.reg_index] -=
+ (u64)cur_opcode.perform_math_static.value;
+ break;
+ case RegisterArithmeticType_Multiplication:
+ this->registers[cur_opcode.perform_math_static.reg_index] *=
+ (u64)cur_opcode.perform_math_static.value;
+ break;
+ case RegisterArithmeticType_LeftShift:
+ this->registers[cur_opcode.perform_math_static.reg_index] <<=
+ (u64)cur_opcode.perform_math_static.value;
+ break;
+ case RegisterArithmeticType_RightShift:
+ this->registers[cur_opcode.perform_math_static.reg_index] >>=
+ (u64)cur_opcode.perform_math_static.value;
+ break;
+ default:
+ /* Do not handle extensions here. */
+ break;
+ }
+ /* Apply bit width. */
+ switch (cur_opcode.perform_math_static.bit_width) {
+ case 1:
+ this->registers[cur_opcode.perform_math_static.reg_index] =
+ static_cast(this->registers[cur_opcode.perform_math_static.reg_index]);
+ break;
+ case 2:
+ this->registers[cur_opcode.perform_math_static.reg_index] =
+ static_cast(this->registers[cur_opcode.perform_math_static.reg_index]);
+ break;
+ case 4:
+ this->registers[cur_opcode.perform_math_static.reg_index] =
+ static_cast(this->registers[cur_opcode.perform_math_static.reg_index]);
+ break;
+ case 8:
+ this->registers[cur_opcode.perform_math_static.reg_index] =
+ static_cast(this->registers[cur_opcode.perform_math_static.reg_index]);
+ break;
+ }
+ } break;
+ case CheatVmOpcodeType_BeginKeypressConditionalBlock:
+ /* Check for keypress. */
+ if ((cur_opcode.begin_keypress_cond.key_mask & kDown) !=
+ cur_opcode.begin_keypress_cond.key_mask) {
+ /* Keys not pressed. Skip conditional block. */
+ this->SkipConditionalBlock();
+ }
+ break;
+ case CheatVmOpcodeType_PerformArithmeticRegister: {
+ const u64 operand_1_value =
+ this->registers[cur_opcode.perform_math_reg.src_reg_1_index];
+ const u64 operand_2_value =
+ cur_opcode.perform_math_reg.has_immediate
+ ? GetVmInt(cur_opcode.perform_math_reg.value,
+ cur_opcode.perform_math_reg.bit_width)
+ : this->registers[cur_opcode.perform_math_reg.src_reg_2_index];
+
+ u64 res_val = 0;
+ /* Do requested math. */
+ switch (cur_opcode.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 (cur_opcode.perform_math_reg.bit_width) {
+ case 1:
+ res_val = static_cast(res_val);
+ break;
+ case 2:
+ res_val = static_cast(res_val);
+ break;
+ case 4:
+ res_val = static_cast(res_val);
+ break;
+ case 8:
+ res_val = static_cast(res_val);
+ break;
+ }
+
+ /* Save to register. */
+ this->registers[cur_opcode.perform_math_reg.dst_reg_index] = res_val;
+ } break;
+ case CheatVmOpcodeType_StoreRegisterToAddress: {
+ /* Calculate address. */
+ u64 dst_value = this->registers[cur_opcode.str_register.str_reg_index];
+ u64 dst_address = this->registers[cur_opcode.str_register.addr_reg_index];
+ switch (cur_opcode.str_register.ofs_type) {
+ case StoreRegisterOffsetType_None:
+ /* Nothing more to do */
+ break;
+ case StoreRegisterOffsetType_Reg:
+ dst_address += this->registers[cur_opcode.str_register.ofs_reg_index];
+ break;
+ case StoreRegisterOffsetType_Imm:
+ dst_address += cur_opcode.str_register.rel_address;
+ break;
+ case StoreRegisterOffsetType_MemReg:
+ dst_address =
+ GetCheatProcessAddress(metadata, cur_opcode.str_register.mem_type,
+ this->registers[cur_opcode.str_register.addr_reg_index]);
+ break;
+ case StoreRegisterOffsetType_MemImm:
+ dst_address = GetCheatProcessAddress(metadata, cur_opcode.str_register.mem_type,
+ cur_opcode.str_register.rel_address);
+ break;
+ case StoreRegisterOffsetType_MemImmReg:
+ dst_address =
+ GetCheatProcessAddress(metadata, cur_opcode.str_register.mem_type,
+ this->registers[cur_opcode.str_register.addr_reg_index] +
+ cur_opcode.str_register.rel_address);
+ break;
+ }
+
+ /* Write value to memory. Write only on valid bitwidth. */
+ switch (cur_opcode.str_register.bit_width) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ callbacks->MemoryWrite(dst_address, &dst_value, cur_opcode.str_register.bit_width);
+ break;
+ }
+
+ /* Increment register if relevant. */
+ if (cur_opcode.str_register.increment_reg) {
+ this->registers[cur_opcode.str_register.addr_reg_index] +=
+ cur_opcode.str_register.bit_width;
+ }
+ } break;
+ case CheatVmOpcodeType_BeginRegisterConditionalBlock: {
+ /* Get value from register. */
+ u64 src_value = 0;
+ switch (cur_opcode.begin_reg_cond.bit_width) {
+ case 1:
+ src_value = static_cast(
+ this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFul);
+ break;
+ case 2:
+ src_value = static_cast(
+ this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFFFul);
+ break;
+ case 4:
+ src_value = static_cast(
+ this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFFFFFFFul);
+ break;
+ case 8:
+ src_value =
+ static_cast(this->registers[cur_opcode.begin_reg_cond.val_reg_index] &
+ 0xFFFFFFFFFFFFFFFFul);
+ break;
+ }
+
+ /* Read value from memory. */
+ u64 cond_value = 0;
+ if (cur_opcode.begin_reg_cond.comp_type == CompareRegisterValueType_StaticValue) {
+ cond_value =
+ GetVmInt(cur_opcode.begin_reg_cond.value, cur_opcode.begin_reg_cond.bit_width);
+ } else if (cur_opcode.begin_reg_cond.comp_type ==
+ CompareRegisterValueType_OtherRegister) {
+ switch (cur_opcode.begin_reg_cond.bit_width) {
+ case 1:
+ cond_value = static_cast(
+ this->registers[cur_opcode.begin_reg_cond.other_reg_index] & 0xFFul);
+ break;
+ case 2:
+ cond_value = static_cast(
+ this->registers[cur_opcode.begin_reg_cond.other_reg_index] & 0xFFFFul);
+ break;
+ case 4:
+ cond_value = static_cast(
+ this->registers[cur_opcode.begin_reg_cond.other_reg_index] & 0xFFFFFFFFul);
+ break;
+ case 8:
+ cond_value = static_cast(
+ this->registers[cur_opcode.begin_reg_cond.other_reg_index] &
+ 0xFFFFFFFFFFFFFFFFul);
+ break;
+ }
+ } else {
+ u64 cond_address = 0;
+ switch (cur_opcode.begin_reg_cond.comp_type) {
+ case CompareRegisterValueType_MemoryRelAddr:
+ cond_address =
+ GetCheatProcessAddress(metadata, cur_opcode.begin_reg_cond.mem_type,
+ cur_opcode.begin_reg_cond.rel_address);
+ break;
+ case CompareRegisterValueType_MemoryOfsReg:
+ cond_address = GetCheatProcessAddress(
+ metadata, cur_opcode.begin_reg_cond.mem_type,
+ this->registers[cur_opcode.begin_reg_cond.ofs_reg_index]);
+ break;
+ case CompareRegisterValueType_RegisterRelAddr:
+ cond_address = this->registers[cur_opcode.begin_reg_cond.addr_reg_index] +
+ cur_opcode.begin_reg_cond.rel_address;
+ break;
+ case CompareRegisterValueType_RegisterOfsReg:
+ cond_address = this->registers[cur_opcode.begin_reg_cond.addr_reg_index] +
+ this->registers[cur_opcode.begin_reg_cond.ofs_reg_index];
+ break;
+ default:
+ break;
+ }
+ switch (cur_opcode.begin_reg_cond.bit_width) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ callbacks->MemoryRead(cond_address, &cond_value,
+ cur_opcode.begin_reg_cond.bit_width);
+ break;
+ }
+ }
+
+ /* Check against condition. */
+ bool cond_met = false;
+ switch (cur_opcode.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) {
+ this->SkipConditionalBlock();
+ }
+ } break;
+ case CheatVmOpcodeType_SaveRestoreRegister:
+ /* Save or restore a register. */
+ switch (cur_opcode.save_restore_reg.op_type) {
+ case SaveRestoreRegisterOpType_ClearRegs:
+ this->registers[cur_opcode.save_restore_reg.dst_index] = 0ul;
+ break;
+ case SaveRestoreRegisterOpType_ClearSaved:
+ this->saved_values[cur_opcode.save_restore_reg.dst_index] = 0ul;
+ break;
+ case SaveRestoreRegisterOpType_Save:
+ this->saved_values[cur_opcode.save_restore_reg.dst_index] =
+ this->registers[cur_opcode.save_restore_reg.src_index];
+ break;
+ case SaveRestoreRegisterOpType_Restore:
+ default:
+ this->registers[cur_opcode.save_restore_reg.dst_index] =
+ this->saved_values[cur_opcode.save_restore_reg.src_index];
+ break;
+ }
+ break;
+ case CheatVmOpcodeType_SaveRestoreRegisterMask:
+ /* Save or restore register mask. */
+ u64* src;
+ u64* dst;
+ switch (cur_opcode.save_restore_regmask.op_type) {
+ case SaveRestoreRegisterOpType_ClearSaved:
+ case SaveRestoreRegisterOpType_Save:
+ src = this->registers.data();
+ dst = this->saved_values.data();
+ break;
+ case SaveRestoreRegisterOpType_ClearRegs:
+ case SaveRestoreRegisterOpType_Restore:
+ default:
+ src = this->registers.data();
+ dst = this->saved_values.data();
+ break;
+ }
+ for (size_t i = 0; i < NumRegisters; i++) {
+ if (cur_opcode.save_restore_regmask.should_operate[i]) {
+ switch (cur_opcode.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;
+ }
+ }
+ }
+ break;
+ case CheatVmOpcodeType_DebugLog: {
+ /* Read value from memory. */
+ u64 log_value = 0;
+ if (cur_opcode.debug_log.val_type == DebugLogValueType_RegisterValue) {
+ switch (cur_opcode.debug_log.bit_width) {
+ case 1:
+ log_value = static_cast(
+ this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFul);
+ break;
+ case 2:
+ log_value = static_cast(
+ this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFFFul);
+ break;
+ case 4:
+ log_value = static_cast(
+ this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFFFFFFFul);
+ break;
+ case 8:
+ log_value = static_cast(
+ this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFFFFFFFFFFFFFFFul);
+ break;
+ }
+ } else {
+ u64 val_address = 0;
+ switch (cur_opcode.debug_log.val_type) {
+ case DebugLogValueType_MemoryRelAddr:
+ val_address = GetCheatProcessAddress(metadata, cur_opcode.debug_log.mem_type,
+ cur_opcode.debug_log.rel_address);
+ break;
+ case DebugLogValueType_MemoryOfsReg:
+ val_address =
+ GetCheatProcessAddress(metadata, cur_opcode.debug_log.mem_type,
+ this->registers[cur_opcode.debug_log.ofs_reg_index]);
+ break;
+ case DebugLogValueType_RegisterRelAddr:
+ val_address = this->registers[cur_opcode.debug_log.addr_reg_index] +
+ cur_opcode.debug_log.rel_address;
+ break;
+ case DebugLogValueType_RegisterOfsReg:
+ val_address = this->registers[cur_opcode.debug_log.addr_reg_index] +
+ this->registers[cur_opcode.debug_log.ofs_reg_index];
+ break;
+ default:
+ break;
+ }
+ switch (cur_opcode.debug_log.bit_width) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ callbacks->MemoryRead(val_address, &log_value, cur_opcode.debug_log.bit_width);
+ break;
+ }
+ }
+
+ /* Log value. */
+ this->DebugLog(cur_opcode.debug_log.log_id, log_value);
+ } break;
+ default:
+ /* By default, we do a no-op. */
+ break;
+ }
+ }
+}
+
+} // namespace Memory
diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h
new file mode 100644
index 000000000..bea451db4
--- /dev/null
+++ b/src/core/memory/dmnt_cheat_vm.h
@@ -0,0 +1,334 @@
+/*
+ * 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 .
+ */
+
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include
+#include
+#include "common/common_types.h"
+#include "core/memory/dmnt_cheat_types.h"
+
+namespace Memory {
+
+enum CheatVmOpcodeType : u32 {
+ CheatVmOpcodeType_StoreStatic = 0,
+ CheatVmOpcodeType_BeginConditionalBlock = 1,
+ CheatVmOpcodeType_EndConditionalBlock = 2,
+ CheatVmOpcodeType_ControlLoop = 3,
+ CheatVmOpcodeType_LoadRegisterStatic = 4,
+ CheatVmOpcodeType_LoadRegisterMemory = 5,
+ CheatVmOpcodeType_StoreStaticToAddress = 6,
+ CheatVmOpcodeType_PerformArithmeticStatic = 7,
+ CheatVmOpcodeType_BeginKeypressConditionalBlock = 8,
+
+ /* These are not implemented by Gateway's VM. */
+ CheatVmOpcodeType_PerformArithmeticRegister = 9,
+ CheatVmOpcodeType_StoreRegisterToAddress = 10,
+ CheatVmOpcodeType_Reserved11 = 11,
+
+ /* This is a meta entry, and not a real opcode. */
+ /* This is to facilitate multi-nybble instruction decoding. */
+ CheatVmOpcodeType_ExtendedWidth = 12,
+
+ /* Extended width opcodes. */
+ CheatVmOpcodeType_BeginRegisterConditionalBlock = 0xC0,
+ CheatVmOpcodeType_SaveRestoreRegister = 0xC1,
+ CheatVmOpcodeType_SaveRestoreRegisterMask = 0xC2,
+
+ /* This is a meta entry, and not a real opcode. */
+ /* This is to facilitate multi-nybble instruction decoding. */
+ CheatVmOpcodeType_DoubleExtendedWidth = 0xF0,
+
+ /* Double-extended width opcodes. */
+ CheatVmOpcodeType_DebugLog = 0xFFF,
+};
+
+enum MemoryAccessType : u32 {
+ MemoryAccessType_MainNso = 0,
+ MemoryAccessType_Heap = 1,
+};
+
+enum ConditionalComparisonType : u32 {
+ ConditionalComparisonType_GT = 1,
+ ConditionalComparisonType_GE = 2,
+ ConditionalComparisonType_LT = 3,
+ ConditionalComparisonType_LE = 4,
+ ConditionalComparisonType_EQ = 5,
+ ConditionalComparisonType_NE = 6,
+};
+
+enum RegisterArithmeticType : u32 {
+ RegisterArithmeticType_Addition = 0,
+ RegisterArithmeticType_Subtraction = 1,
+ RegisterArithmeticType_Multiplication = 2,
+ RegisterArithmeticType_LeftShift = 3,
+ RegisterArithmeticType_RightShift = 4,
+
+ /* These are not supported by Gateway's VM. */
+ RegisterArithmeticType_LogicalAnd = 5,
+ RegisterArithmeticType_LogicalOr = 6,
+ RegisterArithmeticType_LogicalNot = 7,
+ RegisterArithmeticType_LogicalXor = 8,
+
+ RegisterArithmeticType_None = 9,
+};
+
+enum StoreRegisterOffsetType : u32 {
+ StoreRegisterOffsetType_None = 0,
+ StoreRegisterOffsetType_Reg = 1,
+ StoreRegisterOffsetType_Imm = 2,
+ StoreRegisterOffsetType_MemReg = 3,
+ StoreRegisterOffsetType_MemImm = 4,
+ StoreRegisterOffsetType_MemImmReg = 5,
+};
+
+enum CompareRegisterValueType : u32 {
+ CompareRegisterValueType_MemoryRelAddr = 0,
+ CompareRegisterValueType_MemoryOfsReg = 1,
+ CompareRegisterValueType_RegisterRelAddr = 2,
+ CompareRegisterValueType_RegisterOfsReg = 3,
+ CompareRegisterValueType_StaticValue = 4,
+ CompareRegisterValueType_OtherRegister = 5,
+};
+
+enum SaveRestoreRegisterOpType : u32 {
+ SaveRestoreRegisterOpType_Restore = 0,
+ SaveRestoreRegisterOpType_Save = 1,
+ SaveRestoreRegisterOpType_ClearSaved = 2,
+ SaveRestoreRegisterOpType_ClearRegs = 3,
+};
+
+enum DebugLogValueType : u32 {
+ DebugLogValueType_MemoryRelAddr = 0,
+ DebugLogValueType_MemoryOfsReg = 1,
+ DebugLogValueType_RegisterRelAddr = 2,
+ DebugLogValueType_RegisterOfsReg = 3,
+ DebugLogValueType_RegisterValue = 4,
+};
+
+union VmInt {
+ u8 bit8;
+ u16 bit16;
+ u32 bit32;
+ u64 bit64;
+};
+
+struct StoreStaticOpcode {
+ u32 bit_width;
+ MemoryAccessType mem_type;
+ u32 offset_register;
+ u64 rel_address;
+ VmInt value;
+};
+
+struct BeginConditionalOpcode {
+ u32 bit_width;
+ MemoryAccessType mem_type;
+ ConditionalComparisonType cond_type;
+ u64 rel_address;
+ VmInt value;
+};
+
+struct EndConditionalOpcode {};
+
+struct ControlLoopOpcode {
+ bool start_loop;
+ u32 reg_index;
+ u32 num_iters;
+};
+
+struct LoadRegisterStaticOpcode {
+ u32 reg_index;
+ u64 value;
+};
+
+struct LoadRegisterMemoryOpcode {
+ u32 bit_width;
+ MemoryAccessType mem_type;
+ u32 reg_index;
+ bool load_from_reg;
+ u64 rel_address;
+};
+
+struct StoreStaticToAddressOpcode {
+ u32 bit_width;
+ u32 reg_index;
+ bool increment_reg;
+ bool add_offset_reg;
+ u32 offset_reg_index;
+ u64 value;
+};
+
+struct PerformArithmeticStaticOpcode {
+ u32 bit_width;
+ u32 reg_index;
+ RegisterArithmeticType math_type;
+ u32 value;
+};
+
+struct BeginKeypressConditionalOpcode {
+ u32 key_mask;
+};
+
+struct PerformArithmeticRegisterOpcode {
+ u32 bit_width;
+ RegisterArithmeticType math_type;
+ u32 dst_reg_index;
+ u32 src_reg_1_index;
+ u32 src_reg_2_index;
+ bool has_immediate;
+ VmInt value;
+};
+
+struct StoreRegisterToAddressOpcode {
+ u32 bit_width;
+ u32 str_reg_index;
+ u32 addr_reg_index;
+ bool increment_reg;
+ StoreRegisterOffsetType ofs_type;
+ MemoryAccessType mem_type;
+ u32 ofs_reg_index;
+ u64 rel_address;
+};
+
+struct BeginRegisterConditionalOpcode {
+ u32 bit_width;
+ ConditionalComparisonType cond_type;
+ u32 val_reg_index;
+ CompareRegisterValueType comp_type;
+ MemoryAccessType mem_type;
+ u32 addr_reg_index;
+ u32 other_reg_index;
+ u32 ofs_reg_index;
+ u64 rel_address;
+ VmInt value;
+};
+
+struct SaveRestoreRegisterOpcode {
+ u32 dst_index;
+ u32 src_index;
+ SaveRestoreRegisterOpType op_type;
+};
+
+struct SaveRestoreRegisterMaskOpcode {
+ SaveRestoreRegisterOpType op_type;
+ std::array should_operate;
+};
+
+struct DebugLogOpcode {
+ u32 bit_width;
+ u32 log_id;
+ DebugLogValueType val_type;
+ MemoryAccessType mem_type;
+ u32 addr_reg_index;
+ u32 val_reg_index;
+ u32 ofs_reg_index;
+ u64 rel_address;
+};
+
+struct CheatVmOpcode {
+ CheatVmOpcodeType opcode;
+ bool begin_conditional_block;
+ union {
+ StoreStaticOpcode store_static;
+ BeginConditionalOpcode begin_cond;
+ EndConditionalOpcode end_cond;
+ ControlLoopOpcode ctrl_loop;
+ LoadRegisterStaticOpcode ldr_static;
+ LoadRegisterMemoryOpcode ldr_memory;
+ StoreStaticToAddressOpcode str_static;
+ PerformArithmeticStaticOpcode perform_math_static;
+ BeginKeypressConditionalOpcode begin_keypress_cond;
+ PerformArithmeticRegisterOpcode perform_math_reg;
+ StoreRegisterToAddressOpcode str_register;
+ BeginRegisterConditionalOpcode begin_reg_cond;
+ SaveRestoreRegisterOpcode save_restore_reg;
+ SaveRestoreRegisterMaskOpcode save_restore_regmask;
+ DebugLogOpcode debug_log;
+ };
+};
+
+class DmntCheatVm {
+public:
+ /// Helper Type for DmntCheatVm <=> yuzu Interface
+ class Callbacks {
+ public:
+ virtual ~Callbacks();
+
+ virtual void MemoryRead(VAddr address, void* data, u64 size) = 0;
+ virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0;
+
+ virtual u64 HidKeysDown() = 0;
+
+ virtual void DebugLog(u8 id, u64 value) = 0;
+ virtual void CommandLog(std::string_view data) = 0;
+ };
+
+ constexpr static size_t MaximumProgramOpcodeCount = 0x400;
+ constexpr static size_t NumRegisters = 0x10;
+
+private:
+ std::unique_ptr callbacks;
+
+ size_t num_opcodes = 0;
+ size_t instruction_ptr = 0;
+ size_t condition_depth = 0;
+ bool decode_success = false;
+ std::array program{};
+ std::array registers{};
+ std::array saved_values{};
+ std::array loop_tops{};
+
+private:
+ bool DecodeNextOpcode(CheatVmOpcode& out);
+ void SkipConditionalBlock();
+ void ResetState();
+
+ /* For implementing the DebugLog opcode. */
+ void DebugLog(u32 log_id, u64 value);
+
+ /* For debugging. These will be IFDEF'd out normally. */
+ template
+ void LogToDebugFile(const char* format, const Args&... args) {
+ callbacks->CommandLog(fmt::sprintf(format, args...));
+ }
+
+ void LogOpcode(const CheatVmOpcode& opcode);
+
+ static u64 GetVmInt(VmInt value, u32 bit_width);
+ static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata,
+ MemoryAccessType mem_type, u64 rel_address);
+
+public:
+ DmntCheatVm(std::unique_ptr callbacks) : callbacks(std::move(callbacks)) {}
+
+ size_t GetProgramSize() {
+ return this->num_opcodes;
+ }
+
+ bool LoadProgram(const std::vector& cheats);
+ void Execute(const CheatProcessMetadata& metadata);
+};
+
+}; // namespace Memory
--
cgit v1.2.3
From 7d41c1f52390abb47e67d3fc43310e9d87fbd862 Mon Sep 17 00:00:00 2001
From: Zach Hilman
Date: Thu, 30 May 2019 19:35:03 -0400
Subject: cheat_engine: Move to memory and strip VM
This is to go with the Atmosphere VM port, now it just contains the callbacks needed for the interface between DmntCheatVm and yuzu, along with the cheat parsers.---
src/core/memory/cheat_engine.cpp | 234 +++++++++++++++++++++++++++++++++++++++
src/core/memory/cheat_engine.h | 86 ++++++++++++++
2 files changed, 320 insertions(+)
create mode 100644 src/core/memory/cheat_engine.cpp
create mode 100644 src/core/memory/cheat_engine.h
(limited to 'src/core/memory')
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
new file mode 100644
index 000000000..ea5c76fc0
--- /dev/null
+++ b/src/core/memory/cheat_engine.cpp
@@ -0,0 +1,234 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include
+#include "common/hex_util.h"
+#include "common/microprofile.h"
+#include "common/swap.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/core_timing_util.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/service/hid/controllers/npad.h"
+#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/sm/sm.h"
+#include "core/memory/cheat_engine.h"
+
+namespace Memory {
+
+constexpr s64 CHEAT_ENGINE_TICKS = static_cast(Core::Timing::BASE_CLOCK_RATE / 12);
+constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
+
+StandardVmCallbacks::StandardVmCallbacks(const Core::System& system,
+ const CheatProcessMetadata& metadata)
+ : system(system), metadata(metadata) {}
+
+StandardVmCallbacks::~StandardVmCallbacks() = default;
+
+void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) {
+ ReadBlock(SanitizeAddress(address), data, size);
+}
+
+void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) {
+ WriteBlock(SanitizeAddress(address), data, size);
+}
+
+u64 StandardVmCallbacks::HidKeysDown() {
+ const auto applet_resource =
+ system.ServiceManager().GetService("hid")->GetAppletResource();
+ if (applet_resource == nullptr) {
+ LOG_WARNING(CheatEngine,
+ "Attempted to read input state, but applet resource is not initialized!");
+ return false;
+ }
+
+ const auto press_state =
+ applet_resource
+ ->GetController(Service::HID::HidController::NPad)
+ .GetAndResetPressState();
+ return press_state & KEYPAD_BITMASK;
+}
+
+void StandardVmCallbacks::DebugLog(u8 id, u64 value) {
+ LOG_INFO(CheatEngine, "Cheat triggered DebugLog: ID '{:01X}' Value '{:016X}'", id, value);
+}
+
+void StandardVmCallbacks::CommandLog(std::string_view data) {
+ LOG_DEBUG(CheatEngine, "[DmntCheatVm]: {}",
+ data.back() == '\n' ? data.substr(0, data.size() - 1) : data);
+}
+
+VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const {
+ if ((in < metadata.main_nso_extents.base ||
+ in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) &&
+ (in < metadata.heap_extents.base ||
+ in >= metadata.heap_extents.base + metadata.heap_extents.size)) {
+ LOG_ERROR(CheatEngine,
+ "Cheat attempting to access memory at invalid address={:016X}, if this "
+ "persists, "
+ "the cheat may be incorrect. However, this may be normal early in execution if "
+ "the game has not properly set up yet.",
+ in);
+ return 0; ///< Invalid addresses will hard crash
+ }
+
+ return in;
+}
+
+CheatParser::~CheatParser() = default;
+
+TextCheatParser::~TextCheatParser() = default;
+
+namespace {
+template
+std::string_view ExtractName(std::string_view data, std::size_t start_index) {
+ auto end_index = start_index;
+ while (data[end_index] != match) {
+ ++end_index;
+ if (end_index > data.size() ||
+ (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) {
+ return {};
+ }
+ }
+
+ return data.substr(start_index, end_index - start_index);
+}
+} // Anonymous namespace
+
+std::vector TextCheatParser::Parse(const Core::System& system,
+ std::string_view data) const {
+ std::vector out(1);
+ std::optional current_entry = std::nullopt;
+
+ for (std::size_t i = 0; i < data.size(); ++i) {
+ if (std::isspace(data[i])) {
+ continue;
+ }
+
+ if (data[i] == '{') {
+ current_entry = 0;
+
+ if (out[*current_entry].definition.num_opcodes > 0) {
+ return {};
+ }
+
+ const auto name = ExtractName<'}'>(data, i + 1);
+ if (name.empty()) {
+ return {};
+ }
+
+ std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
+ std::min(out[*current_entry].definition.readable_name.size(),
+ name.size()));
+ out[*current_entry]
+ .definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
+ '\0';
+
+ i += name.length() + 1;
+ } else if (data[i] == '[') {
+ current_entry = out.size();
+ out.emplace_back();
+
+ const auto name = ExtractName<']'>(data, i + 1);
+ if (name.empty()) {
+ return {};
+ }
+
+ std::memcpy(out[*current_entry].definition.readable_name.data(), name.data(),
+ std::min(out[*current_entry].definition.readable_name.size(),
+ name.size()));
+ out[*current_entry]
+ .definition.readable_name[out[*current_entry].definition.readable_name.size() - 1] =
+ '\0';
+
+ i += name.length() + 1;
+ } else if (std::isxdigit(data[i])) {
+ if (!current_entry || out[*current_entry].definition.num_opcodes >=
+ out[*current_entry].definition.opcodes.size()) {
+ return {};
+ }
+
+ const auto hex = std::string(data.substr(i, 8));
+ if (!std::all_of(hex.begin(), hex.end(), ::isxdigit)) {
+ return {};
+ }
+
+ out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] =
+ std::stoul(hex, nullptr, 0x10);
+
+ i += 8;
+ } else {
+ return {};
+ }
+ }
+
+ out[0].enabled = out[0].definition.num_opcodes > 0;
+ out[0].cheat_id = 0;
+
+ for (u32 i = 1; i < out.size(); ++i) {
+ out[i].enabled = out[i].definition.num_opcodes > 0;
+ out[i].cheat_id = i;
+ }
+
+ return out;
+}
+
+CheatEngine::CheatEngine(Core::System& system, std::vector cheats,
+ const std::array& build_id)
+ : system{system}, core_timing{system.CoreTiming()}, vm{std::make_unique(
+ system, metadata)},
+ cheats(std::move(cheats)) {
+ metadata.main_nso_build_id = build_id;
+}
+
+CheatEngine::~CheatEngine() {
+ core_timing.UnscheduleEvent(event, 0);
+}
+
+void CheatEngine::Initialize() {
+ event = core_timing.RegisterEvent(
+ "CheatEngine::FrameCallback::" + Common::HexArrayToString(metadata.main_nso_build_id),
+ [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
+ core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
+
+ metadata.process_id = system.CurrentProcess()->GetProcessID();
+ metadata.title_id = system.CurrentProcess()->GetTitleID();
+
+ const auto& vm_manager = system.CurrentProcess()->VMManager();
+ metadata.heap_extents = {vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionSize()};
+ metadata.address_space_extents = {vm_manager.GetAddressSpaceBaseAddress(),
+ vm_manager.GetAddressSpaceSize()};
+ metadata.alias_extents = {vm_manager.GetMapRegionBaseAddress(), vm_manager.GetMapRegionSize()};
+
+ is_pending_reload.exchange(true);
+}
+
+void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) {
+ metadata.main_nso_extents = {main_region_begin, main_region_size};
+}
+
+void CheatEngine::Reload(std::vector cheats) {
+ this->cheats = std::move(cheats);
+ is_pending_reload.exchange(true);
+}
+
+MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
+
+void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {
+ if (is_pending_reload.exchange(false)) {
+ vm.LoadProgram(cheats);
+ }
+
+ if (vm.GetProgramSize() == 0) {
+ return;
+ }
+
+ MICROPROFILE_SCOPE(Cheat_Engine);
+
+ vm.Execute(metadata);
+
+ core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
+}
+
+} // namespace Memory
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
new file mode 100644
index 000000000..0f012e9b5
--- /dev/null
+++ b/src/core/memory/cheat_engine.h
@@ -0,0 +1,86 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+#include "common/common_types.h"
+#include "core/memory/dmnt_cheat_types.h"
+#include "core/memory/dmnt_cheat_vm.h"
+
+namespace Core {
+class System;
+}
+
+namespace Core::Timing {
+class CoreTiming;
+struct EventType;
+} // namespace Core::Timing
+
+namespace Memory {
+
+class StandardVmCallbacks : public DmntCheatVm::Callbacks {
+public:
+ StandardVmCallbacks(const Core::System& system, const CheatProcessMetadata& metadata);
+ ~StandardVmCallbacks() override;
+
+ void MemoryRead(VAddr address, void* data, u64 size) override;
+ void MemoryWrite(VAddr address, const void* data, u64 size) override;
+ u64 HidKeysDown() override;
+ void DebugLog(u8 id, u64 value) override;
+ void CommandLog(std::string_view data) override;
+
+private:
+ VAddr SanitizeAddress(VAddr address) const;
+
+ const CheatProcessMetadata& metadata;
+ const Core::System& system;
+};
+
+// Intermediary class that parses a text file or other disk format for storing cheats into a
+// CheatList object, that can be used for execution.
+class CheatParser {
+public:
+ virtual ~CheatParser();
+
+ virtual std::vector Parse(const Core::System& system,
+ std::string_view data) const = 0;
+};
+
+// CheatParser implementation that parses text files
+class TextCheatParser final : public CheatParser {
+public:
+ ~TextCheatParser() override;
+
+ std::vector Parse(const Core::System& system, std::string_view data) const override;
+};
+
+// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
+class CheatEngine final {
+public:
+ CheatEngine(Core::System& system_, std::vector cheats_,
+ const std::array& build_id);
+ ~CheatEngine();
+
+ void Initialize();
+ void SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size);
+
+ void Reload(std::vector cheats);
+
+private:
+ void FrameCallback(u64 userdata, s64 cycles_late);
+
+ DmntCheatVm vm;
+ CheatProcessMetadata metadata;
+
+ std::vector cheats;
+ std::atomic_bool is_pending_reload{false};
+
+ Core::Timing::EventType* event{};
+ Core::Timing::CoreTiming& core_timing;
+ Core::System& system;
+};
+
+} // namespace Memory
--
cgit v1.2.3
From 682174b1123c5721b12eaf8472da46251fea3b1d Mon Sep 17 00:00:00 2001
From: Zach Hilman
Date: Sat, 21 Sep 2019 18:13:10 -0400
Subject: dmnt_cheat_vm: Make Cheat VM compliant to code style
---
src/core/memory/cheat_engine.cpp | 6 +-
src/core/memory/dmnt_cheat_vm.cpp | 1496 +++++++++++++++++++------------------
src/core/memory/dmnt_cheat_vm.h | 227 +++---
3 files changed, 861 insertions(+), 868 deletions(-)
(limited to 'src/core/memory')
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index ea5c76fc0..b56cb0627 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -102,7 +102,7 @@ std::vector TextCheatParser::Parse(const Core::System& system,
std::optional current_entry = std::nullopt;
for (std::size_t i = 0; i < data.size(); ++i) {
- if (std::isspace(data[i])) {
+ if (::isspace(data[i])) {
continue;
}
@@ -143,7 +143,7 @@ std::vector TextCheatParser::Parse(const Core::System& system,
'\0';
i += name.length() + 1;
- } else if (std::isxdigit(data[i])) {
+ } else if (::isxdigit(data[i])) {
if (!current_entry || out[*current_entry].definition.num_opcodes >=
out[*current_entry].definition.opcodes.size()) {
return {};
@@ -188,7 +188,7 @@ CheatEngine::~CheatEngine() {
void CheatEngine::Initialize() {
event = core_timing.RegisterEvent(
- "CheatEngine::FrameCallback::" + Common::HexArrayToString(metadata.main_nso_build_id),
+ "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
[this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp
index a3f450dac..cc16d15a4 100644
--- a/src/core/memory/dmnt_cheat_vm.cpp
+++ b/src/core/memory/dmnt_cheat_vm.cpp
@@ -29,264 +29,272 @@
namespace Memory {
+DmntCheatVm::DmntCheatVm(std::unique_ptr callbacks) : callbacks(std::move(callbacks)) {}
+
+DmntCheatVm::~DmntCheatVm() = default;
+
void DmntCheatVm::DebugLog(u32 log_id, u64 value) {
callbacks->DebugLog(static_cast(log_id), value);
}
void DmntCheatVm::LogOpcode(const CheatVmOpcode& opcode) {
- switch (opcode.opcode) {
- case CheatVmOpcodeType_StoreStatic:
- this->LogToDebugFile("Opcode: Store Static\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.store_static.bit_width);
- this->LogToDebugFile("Mem Type: %x\n", opcode.store_static.mem_type);
- this->LogToDebugFile("Reg Idx: %x\n", opcode.store_static.offset_register);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.store_static.rel_address);
- this->LogToDebugFile("Value: %lx\n", opcode.store_static.value.bit64);
- break;
- case CheatVmOpcodeType_BeginConditionalBlock:
- this->LogToDebugFile("Opcode: Begin Conditional\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.begin_cond.bit_width);
- this->LogToDebugFile("Mem Type: %x\n", opcode.begin_cond.mem_type);
- this->LogToDebugFile("Cond Type: %x\n", opcode.begin_cond.cond_type);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.begin_cond.rel_address);
- this->LogToDebugFile("Value: %lx\n", opcode.begin_cond.value.bit64);
- break;
- case CheatVmOpcodeType_EndConditionalBlock:
- this->LogToDebugFile("Opcode: End Conditional\n");
- break;
- case CheatVmOpcodeType_ControlLoop:
- if (opcode.ctrl_loop.start_loop) {
- this->LogToDebugFile("Opcode: Start Loop\n");
- this->LogToDebugFile("Reg Idx: %x\n", opcode.ctrl_loop.reg_index);
- this->LogToDebugFile("Num Iters: %x\n", opcode.ctrl_loop.num_iters);
+ if (auto store_static = std::get_if(&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(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(&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(begin_cond->mem_type)));
+ callbacks->CommandLog(
+ fmt::format("Cond Type: {:X}", static_cast(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(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: End Conditional");
+ } else if (auto ctrl_loop = std::get_if(&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 {
- this->LogToDebugFile("Opcode: End Loop\n");
- this->LogToDebugFile("Reg Idx: %x\n", opcode.ctrl_loop.reg_index);
+ callbacks->CommandLog("Opcode: End Loop");
+ callbacks->CommandLog(fmt::format("Reg Idx: {:X}", ctrl_loop->reg_index));
}
- break;
- case CheatVmOpcodeType_LoadRegisterStatic:
- this->LogToDebugFile("Opcode: Load Register Static\n");
- this->LogToDebugFile("Reg Idx: %x\n", opcode.ldr_static.reg_index);
- this->LogToDebugFile("Value: %lx\n", opcode.ldr_static.value);
- break;
- case CheatVmOpcodeType_LoadRegisterMemory:
- this->LogToDebugFile("Opcode: Load Register Memory\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.ldr_memory.bit_width);
- this->LogToDebugFile("Reg Idx: %x\n", opcode.ldr_memory.reg_index);
- this->LogToDebugFile("Mem Type: %x\n", opcode.ldr_memory.mem_type);
- this->LogToDebugFile("From Reg: %d\n", opcode.ldr_memory.load_from_reg);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.ldr_memory.rel_address);
- break;
- case CheatVmOpcodeType_StoreStaticToAddress:
- this->LogToDebugFile("Opcode: Store Static to Address\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.str_static.bit_width);
- this->LogToDebugFile("Reg Idx: %x\n", opcode.str_static.reg_index);
- if (opcode.str_static.add_offset_reg) {
- this->LogToDebugFile("O Reg Idx: %x\n", opcode.str_static.offset_reg_index);
+ } else if (auto ldr_static = std::get_if(&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(&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(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(&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));
}
- this->LogToDebugFile("Incr Reg: %d\n", opcode.str_static.increment_reg);
- this->LogToDebugFile("Value: %lx\n", opcode.str_static.value);
- break;
- case CheatVmOpcodeType_PerformArithmeticStatic:
- this->LogToDebugFile("Opcode: Perform Static Arithmetic\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.perform_math_static.bit_width);
- this->LogToDebugFile("Reg Idx: %x\n", opcode.perform_math_static.reg_index);
- this->LogToDebugFile("Math Type: %x\n", opcode.perform_math_static.math_type);
- this->LogToDebugFile("Value: %lx\n", opcode.perform_math_static.value);
- break;
- case CheatVmOpcodeType_BeginKeypressConditionalBlock:
- this->LogToDebugFile("Opcode: Begin Keypress Conditional\n");
- this->LogToDebugFile("Key Mask: %x\n", opcode.begin_keypress_cond.key_mask);
- break;
- case CheatVmOpcodeType_PerformArithmeticRegister:
- this->LogToDebugFile("Opcode: Perform Register Arithmetic\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.perform_math_reg.bit_width);
- this->LogToDebugFile("Dst Idx: %x\n", opcode.perform_math_reg.dst_reg_index);
- this->LogToDebugFile("Src1 Idx: %x\n", opcode.perform_math_reg.src_reg_1_index);
- if (opcode.perform_math_reg.has_immediate) {
- this->LogToDebugFile("Value: %lx\n", opcode.perform_math_reg.value.bit64);
+ 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(&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(perform_math_static->math_type)));
+ callbacks->CommandLog(fmt::format("Value: {:X}", perform_math_static->value));
+ } else if (auto begin_keypress_cond =
+ std::get_if(&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(&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 {
- this->LogToDebugFile("Src2 Idx: %x\n", opcode.perform_math_reg.src_reg_2_index);
+ callbacks->CommandLog(
+ fmt::format("Src2 Idx: {:X}", perform_math_reg->src_reg_2_index));
}
- break;
- case CheatVmOpcodeType_StoreRegisterToAddress:
- this->LogToDebugFile("Opcode: Store Register to Address\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.str_register.bit_width);
- this->LogToDebugFile("S Reg Idx: %x\n", opcode.str_register.str_reg_index);
- this->LogToDebugFile("A Reg Idx: %x\n", opcode.str_register.addr_reg_index);
- this->LogToDebugFile("Incr Reg: %d\n", opcode.str_register.increment_reg);
- switch (opcode.str_register.ofs_type) {
- case StoreRegisterOffsetType_None:
+ } else if (auto str_register = std::get_if(&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:
- this->LogToDebugFile("O Reg Idx: %x\n", opcode.str_register.ofs_reg_index);
+ case StoreRegisterOffsetType::Reg:
+ callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_register->ofs_reg_index));
break;
- case StoreRegisterOffsetType_Imm:
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.str_register.rel_address);
+ case StoreRegisterOffsetType::Imm:
+ callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
break;
- case StoreRegisterOffsetType_MemReg:
- this->LogToDebugFile("Mem Type: %x\n", opcode.str_register.mem_type);
+ case StoreRegisterOffsetType::MemReg:
+ callbacks->CommandLog(
+ fmt::format("Mem Type: {:X}", static_cast(str_register->mem_type)));
break;
- case StoreRegisterOffsetType_MemImm:
- case StoreRegisterOffsetType_MemImmReg:
- this->LogToDebugFile("Mem Type: %x\n", opcode.str_register.mem_type);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.str_register.rel_address);
+ case StoreRegisterOffsetType::MemImm:
+ case StoreRegisterOffsetType::MemImmReg:
+ callbacks->CommandLog(
+ fmt::format("Mem Type: {:X}", static_cast(str_register->mem_type)));
+ callbacks->CommandLog(fmt::format("Rel Addr: {:X}", str_register->rel_address));
break;
}
- break;
- case CheatVmOpcodeType_BeginRegisterConditionalBlock:
- this->LogToDebugFile("Opcode: Begin Register Conditional\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.begin_reg_cond.bit_width);
- this->LogToDebugFile("Cond Type: %x\n", opcode.begin_reg_cond.cond_type);
- this->LogToDebugFile("V Reg Idx: %x\n", opcode.begin_reg_cond.val_reg_index);
- switch (opcode.begin_reg_cond.comp_type) {
- case CompareRegisterValueType_StaticValue:
- this->LogToDebugFile("Comp Type: Static Value\n");
- this->LogToDebugFile("Value: %lx\n", opcode.begin_reg_cond.value.bit64);
+ } else if (auto begin_reg_cond = std::get_if(&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(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:
- this->LogToDebugFile("Comp Type: Other Register\n");
- this->LogToDebugFile("X Reg Idx: %x\n", opcode.begin_reg_cond.other_reg_index);
+ 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:
- this->LogToDebugFile("Comp Type: Memory Relative Address\n");
- this->LogToDebugFile("Mem Type: %x\n", opcode.begin_reg_cond.mem_type);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.begin_reg_cond.rel_address);
+ case CompareRegisterValueType::MemoryRelAddr:
+ callbacks->CommandLog("Comp Type: Memory Relative Address");
+ callbacks->CommandLog(
+ fmt::format("Mem Type: {:X}", static_cast(begin_reg_cond->mem_type)));
+ callbacks->CommandLog(fmt::format("Rel Addr: {:X}", begin_reg_cond->rel_address));
break;
- case CompareRegisterValueType_MemoryOfsReg:
- this->LogToDebugFile("Comp Type: Memory Offset Register\n");
- this->LogToDebugFile("Mem Type: %x\n", opcode.begin_reg_cond.mem_type);
- this->LogToDebugFile("O Reg Idx: %x\n", opcode.begin_reg_cond.ofs_reg_index);
+ case CompareRegisterValueType::MemoryOfsReg:
+ callbacks->CommandLog("Comp Type: Memory Offset Register");
+ callbacks->CommandLog(
+ fmt::format("Mem Type: {:X}", static_cast(begin_reg_cond->mem_type)));
+ callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
break;
- case CompareRegisterValueType_RegisterRelAddr:
- this->LogToDebugFile("Comp Type: Register Relative Address\n");
- this->LogToDebugFile("A Reg Idx: %x\n", opcode.begin_reg_cond.addr_reg_index);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.begin_reg_cond.rel_address);
+ 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:
- this->LogToDebugFile("Comp Type: Register Offset Register\n");
- this->LogToDebugFile("A Reg Idx: %x\n", opcode.begin_reg_cond.addr_reg_index);
- this->LogToDebugFile("O Reg Idx: %x\n", opcode.begin_reg_cond.ofs_reg_index);
+ 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;
}
- break;
- case CheatVmOpcodeType_SaveRestoreRegister:
- this->LogToDebugFile("Opcode: Save or Restore Register\n");
- this->LogToDebugFile("Dst Idx: %x\n", opcode.save_restore_reg.dst_index);
- this->LogToDebugFile("Src Idx: %x\n", opcode.save_restore_reg.src_index);
- this->LogToDebugFile("Op Type: %d\n", opcode.save_restore_reg.op_type);
- break;
- case CheatVmOpcodeType_SaveRestoreRegisterMask:
- this->LogToDebugFile("Opcode: Save or Restore Register Mask\n");
- this->LogToDebugFile("Op Type: %d\n", opcode.save_restore_regmask.op_type);
- for (size_t i = 0; i < NumRegisters; i++) {
- this->LogToDebugFile("Act[%02x]: %d\n", i,
- opcode.save_restore_regmask.should_operate[i]);
+ } else if (auto save_restore_reg = std::get_if(&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(save_restore_reg->op_type)));
+ } else if (auto save_restore_regmask =
+ std::get_if(&opcode.opcode)) {
+ callbacks->CommandLog("Opcode: Save or Restore Register Mask");
+ callbacks->CommandLog(
+ fmt::format("Op Type: {:d}", static_cast(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]));
}
- break;
- case CheatVmOpcodeType_DebugLog:
- this->LogToDebugFile("Opcode: Debug Log\n");
- this->LogToDebugFile("Bit Width: %x\n", opcode.debug_log.bit_width);
- this->LogToDebugFile("Log ID: %x\n", opcode.debug_log.log_id);
- this->LogToDebugFile("Val Type: %x\n", opcode.debug_log.val_type);
- switch (opcode.debug_log.val_type) {
- case DebugLogValueType_RegisterValue:
- this->LogToDebugFile("Val Type: Register Value\n");
- this->LogToDebugFile("X Reg Idx: %x\n", opcode.debug_log.val_reg_index);
+ } else if (auto debug_log = std::get_if(&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(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:
- this->LogToDebugFile("Val Type: Memory Relative Address\n");
- this->LogToDebugFile("Mem Type: %x\n", opcode.debug_log.mem_type);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.debug_log.rel_address);
+ case DebugLogValueType::MemoryRelAddr:
+ callbacks->CommandLog("Val Type: Memory Relative Address");
+ callbacks->CommandLog(
+ fmt::format("Mem Type: {:X}", static_cast(debug_log->mem_type)));
+ callbacks->CommandLog(fmt::format("Rel Addr: {:X}", debug_log->rel_address));
break;
- case DebugLogValueType_MemoryOfsReg:
- this->LogToDebugFile("Val Type: Memory Offset Register\n");
- this->LogToDebugFile("Mem Type: %x\n", opcode.debug_log.mem_type);
- this->LogToDebugFile("O Reg Idx: %x\n", opcode.debug_log.ofs_reg_index);
+ case DebugLogValueType::MemoryOfsReg:
+ callbacks->CommandLog("Val Type: Memory Offset Register");
+ callbacks->CommandLog(
+ fmt::format("Mem Type: {:X}", static_cast(debug_log->mem_type)));
+ callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
break;
- case DebugLogValueType_RegisterRelAddr:
- this->LogToDebugFile("Val Type: Register Relative Address\n");
- this->LogToDebugFile("A Reg Idx: %x\n", opcode.debug_log.addr_reg_index);
- this->LogToDebugFile("Rel Addr: %lx\n", opcode.debug_log.rel_address);
+ 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:
- this->LogToDebugFile("Val Type: Register Offset Register\n");
- this->LogToDebugFile("A Reg Idx: %x\n", opcode.debug_log.addr_reg_index);
- this->LogToDebugFile("O Reg Idx: %x\n", opcode.debug_log.ofs_reg_index);
+ 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;
}
- default:
- this->LogToDebugFile("Unknown opcode: %x\n", opcode.opcode);
- break;
+ } else if (auto instr = std::get_if(&opcode.opcode)) {
+ callbacks->CommandLog(fmt::format("Unknown opcode: {:X}", static_cast(instr->opcode)));
}
}
DmntCheatVm::Callbacks::~Callbacks() = default;
bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
- /* If we've ever seen a decode failure, return false. */
- bool valid = this->decode_success;
+ // If we've ever seen a decode failure, return false.
+ bool valid = decode_success;
CheatVmOpcode opcode = {};
SCOPE_EXIT({
- this->decode_success &= valid;
+ decode_success &= valid;
if (valid) {
out = opcode;
}
});
- /* Helper function for getting instruction dwords. */
- auto GetNextDword = [&]() {
- if (this->instruction_ptr >= this->num_opcodes) {
+ // Helper function for getting instruction dwords.
+ const auto GetNextDword = [&] {
+ if (instruction_ptr >= num_opcodes) {
valid = false;
return static_cast(0);
}
- return this->program[this->instruction_ptr++];
+ return program[instruction_ptr++];
};
- /* Helper function for parsing a VmInt. */
- auto GetNextVmInt = [&](const u32 bit_width) {
- VmInt val = {0};
+ // 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 = (u8)first_dword;
+ val.bit8 = static_cast(first_dword);
break;
case 2:
- val.bit16 = (u16)first_dword;
+ val.bit16 = static_cast(first_dword);
break;
case 4:
val.bit32 = first_dword;
break;
case 8:
- val.bit64 = (((u64)first_dword) << 32ul) | ((u64)GetNextDword());
+ val.bit64 = (static_cast(first_dword) << 32ul) | static_cast(GetNextDword());
break;
}
return val;
};
- /* Read opcode. */
+ // Read opcode.
const u32 first_dword = GetNextDword();
if (!valid) {
return valid;
}
- opcode.opcode = (CheatVmOpcodeType)(((first_dword >> 28) & 0xF));
- if (opcode.opcode >= CheatVmOpcodeType_ExtendedWidth) {
- opcode.opcode =
- (CheatVmOpcodeType)((((u32)opcode.opcode) << 4) | ((first_dword >> 24) & 0xF));
+ auto opcode_type = static_cast(((first_dword >> 28) & 0xF));
+ if (opcode_type >= CheatVmOpcodeType::ExtendedWidth) {
+ opcode_type = static_cast((static_cast(opcode_type) << 4) |
+ ((first_dword >> 24) & 0xF));
}
- if (opcode.opcode >= CheatVmOpcodeType_DoubleExtendedWidth) {
- opcode.opcode =
- (CheatVmOpcodeType)((((u32)opcode.opcode) << 4) | ((first_dword >> 20) & 0xF));
+ if (opcode_type >= CheatVmOpcodeType::DoubleExtendedWidth) {
+ opcode_type = static_cast((static_cast(opcode_type) << 4) |
+ ((first_dword >> 20) & 0xF));
}
- /* detect condition start. */
- switch (opcode.opcode) {
- case CheatVmOpcodeType_BeginConditionalBlock:
- case CheatVmOpcodeType_BeginKeypressConditionalBlock:
- case CheatVmOpcodeType_BeginRegisterConditionalBlock:
+ // detect condition start.
+ switch (opcode_type) {
+ case CheatVmOpcodeType::BeginConditionalBlock:
+ case CheatVmOpcodeType::BeginKeypressConditionalBlock:
+ case CheatVmOpcodeType::BeginRegisterConditionalBlock:
opcode.begin_conditional_block = true;
break;
default:
@@ -294,299 +302,335 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
break;
}
- switch (opcode.opcode) {
- case CheatVmOpcodeType_StoreStatic: {
- /* 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY) */
- /* Read additional words. */
+ switch (opcode_type) {
+ case CheatVmOpcodeType::StoreStatic: {
+ StoreStaticOpcode store_static{};
+ // 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
+ // Read additional words.
const u32 second_dword = GetNextDword();
- opcode.store_static.bit_width = (first_dword >> 24) & 0xF;
- opcode.store_static.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF);
- opcode.store_static.offset_register = ((first_dword >> 16) & 0xF);
- opcode.store_static.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword);
- opcode.store_static.value = GetNextVmInt(opcode.store_static.bit_width);
+ store_static.bit_width = (first_dword >> 24) & 0xF;
+ store_static.mem_type = static_cast((first_dword >> 20) & 0xF);
+ store_static.offset_register = ((first_dword >> 16) & 0xF);
+ store_static.rel_address =
+ (static_cast(first_dword & 0xFF) << 32ul) | static_cast(second_dword);
+ store_static.value = GetNextVmInt(store_static.bit_width);
+ opcode.opcode = store_static;
} break;
- case CheatVmOpcodeType_BeginConditionalBlock: {
- /* 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY) */
- /* Read additional words. */
+ case CheatVmOpcodeType::BeginConditionalBlock: {
+ BeginConditionalOpcode begin_cond{};
+ // 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
+ // Read additional words.
const u32 second_dword = GetNextDword();
- opcode.begin_cond.bit_width = (first_dword >> 24) & 0xF;
- opcode.begin_cond.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF);
- opcode.begin_cond.cond_type = (ConditionalComparisonType)((first_dword >> 16) & 0xF);
- opcode.begin_cond.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword);
- opcode.begin_cond.value = GetNextVmInt(opcode.store_static.bit_width);
+ begin_cond.bit_width = (first_dword >> 24) & 0xF;
+ begin_cond.mem_type = static_cast((first_dword >> 20) & 0xF);
+ begin_cond.cond_type = static_cast((first_dword >> 16) & 0xF);
+ begin_cond.rel_address =
+ (static_cast(first_dword & 0xFF) << 32ul) | static_cast(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! */
+ case CheatVmOpcodeType::EndConditionalBlock: {
+ // 20000000
+ // There's actually nothing left to process here!
+ opcode.opcode = EndConditionalOpcode{};
} break;
- case CheatVmOpcodeType_ControlLoop: {
- /* 300R0000 VVVVVVVV */
- /* 310R0000 */
- /* Parse register, whether loop start or loop end. */
- opcode.ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0;
- opcode.ctrl_loop.reg_index = ((first_dword >> 20) & 0xF);
+ 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 (opcode.ctrl_loop.start_loop) {
- opcode.ctrl_loop.num_iters = GetNextDword();
+ // 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: {
- /* 400R0000 VVVVVVVV VVVVVVVV */
- /* Read additional words. */
- opcode.ldr_static.reg_index = ((first_dword >> 16) & 0xF);
- opcode.ldr_static.value = (((u64)GetNextDword()) << 32ul) | ((u64)GetNextDword());
+ 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(GetNextDword()) << 32ul) | static_cast(GetNextDword());
+ opcode.opcode = ldr_static;
} break;
- case CheatVmOpcodeType_LoadRegisterMemory: {
- /* 5TMRI0AA AAAAAAAA */
- /* Read additional words. */
+ case CheatVmOpcodeType::LoadRegisterMemory: {
+ LoadRegisterMemoryOpcode ldr_memory{};
+ // 5TMRI0AA AAAAAAAA
+ // Read additional words.
const u32 second_dword = GetNextDword();
- opcode.ldr_memory.bit_width = (first_dword >> 24) & 0xF;
- opcode.ldr_memory.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF);
- opcode.ldr_memory.reg_index = ((first_dword >> 16) & 0xF);
- opcode.ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0;
- opcode.ldr_memory.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword);
+ ldr_memory.bit_width = (first_dword >> 24) & 0xF;
+ ldr_memory.mem_type = static_cast((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(first_dword & 0xFF) << 32ul) | static_cast(second_dword);
+ opcode.opcode = ldr_memory;
} break;
- case CheatVmOpcodeType_StoreStaticToAddress: {
- /* 6T0RIor0 VVVVVVVV VVVVVVVV */
- /* Read additional words. */
- opcode.str_static.bit_width = (first_dword >> 24) & 0xF;
- opcode.str_static.reg_index = ((first_dword >> 16) & 0xF);
- opcode.str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0;
- opcode.str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0;
- opcode.str_static.offset_reg_index = ((first_dword >> 4) & 0xF);
- opcode.str_static.value = (((u64)GetNextDword()) << 32ul) | ((u64)GetNextDword());
+ 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(GetNextDword()) << 32ul) | static_cast(GetNextDword());
+ opcode.opcode = str_static;
} break;
- case CheatVmOpcodeType_PerformArithmeticStatic: {
- /* 7T0RC000 VVVVVVVV */
- /* Read additional words. */
- opcode.perform_math_static.bit_width = (first_dword >> 24) & 0xF;
- opcode.perform_math_static.reg_index = ((first_dword >> 16) & 0xF);
- opcode.perform_math_static.math_type = (RegisterArithmeticType)((first_dword >> 12) & 0xF);
- opcode.perform_math_static.value = GetNextDword();
+ 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((first_dword >> 12) & 0xF);
+ perform_math_static.value = GetNextDword();
+ opcode.opcode = perform_math_static;
} break;
- case CheatVmOpcodeType_BeginKeypressConditionalBlock: {
- /* 8kkkkkkk */
- /* Just parse the mask. */
- opcode.begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF;
+ case CheatVmOpcodeType::BeginKeypressConditionalBlock: {
+ BeginKeypressConditionalOpcode begin_keypress_cond{};
+ // 8kkkkkkk
+ // Just parse the mask.
+ begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF;
} break;
- case CheatVmOpcodeType_PerformArithmeticRegister: {
- /* 9TCRSIs0 (VVVVVVVV (VVVVVVVV)) */
- opcode.perform_math_reg.bit_width = (first_dword >> 24) & 0xF;
- opcode.perform_math_reg.math_type = (RegisterArithmeticType)((first_dword >> 20) & 0xF);
- opcode.perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF);
- opcode.perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF);
- opcode.perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0;
- if (opcode.perform_math_reg.has_immediate) {
- opcode.perform_math_reg.src_reg_2_index = 0;
- opcode.perform_math_reg.value = GetNextVmInt(opcode.perform_math_reg.bit_width);
+ 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((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 {
- opcode.perform_math_reg.src_reg_2_index = ((first_dword >> 4) & 0xF);
+ perform_math_reg.src_reg_2_index = ((first_dword >> 4) & 0xF);
}
+ opcode.opcode = perform_math_reg;
} break;
- case CheatVmOpcodeType_StoreRegisterToAddress: {
- /* 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) */
- opcode.str_register.bit_width = (first_dword >> 24) & 0xF;
- opcode.str_register.str_reg_index = ((first_dword >> 20) & 0xF);
- opcode.str_register.addr_reg_index = ((first_dword >> 16) & 0xF);
- opcode.str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0;
- opcode.str_register.ofs_type = (StoreRegisterOffsetType)(((first_dword >> 8) & 0xF));
- opcode.str_register.ofs_reg_index = ((first_dword >> 4) & 0xF);
- switch (opcode.str_register.ofs_type) {
- case StoreRegisterOffsetType_None:
- case StoreRegisterOffsetType_Reg:
- /* Nothing more to do */
+ 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(((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:
- opcode.str_register.rel_address =
- (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+ case StoreRegisterOffsetType::Imm:
+ str_register.rel_address =
+ ((static_cast(first_dword & 0xF) << 32ul) | static_cast(GetNextDword()));
break;
- case StoreRegisterOffsetType_MemReg:
- opcode.str_register.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
+ case StoreRegisterOffsetType::MemReg:
+ str_register.mem_type = static_cast((first_dword >> 4) & 0xF);
break;
- case StoreRegisterOffsetType_MemImm:
- case StoreRegisterOffsetType_MemImmReg:
- opcode.str_register.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
- opcode.str_register.rel_address =
- (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+ case StoreRegisterOffsetType::MemImm:
+ case StoreRegisterOffsetType::MemImmReg:
+ str_register.mem_type = static_cast((first_dword >> 4) & 0xF);
+ str_register.rel_address =
+ ((static_cast(first_dword & 0xF) << 32ul) | static_cast(GetNextDword()));
break;
default:
- opcode.str_register.ofs_type = StoreRegisterOffsetType_None;
+ str_register.ofs_type = StoreRegisterOffsetType::None;
break;
}
+ opcode.opcode = str_register;
} break;
- case CheatVmOpcodeType_BeginRegisterConditionalBlock: {
- /* 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. */
- opcode.begin_reg_cond.bit_width = (first_dword >> 20) & 0xF;
- opcode.begin_reg_cond.cond_type = (ConditionalComparisonType)((first_dword >> 16) & 0xF);
- opcode.begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF);
- opcode.begin_reg_cond.comp_type = (CompareRegisterValueType)((first_dword >> 8) & 0xF);
+ 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((first_dword >> 16) & 0xF);
+ begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF);
+ begin_reg_cond.comp_type = static_cast((first_dword >> 8) & 0xF);
- switch (opcode.begin_reg_cond.comp_type) {
- case CompareRegisterValueType_StaticValue:
- opcode.begin_reg_cond.value = GetNextVmInt(opcode.begin_reg_cond.bit_width);
+ switch (begin_reg_cond.comp_type) {
+ case CompareRegisterValueType::StaticValue:
+ begin_reg_cond.value = GetNextVmInt(begin_reg_cond.bit_width);
break;
- case CompareRegisterValueType_OtherRegister:
- opcode.begin_reg_cond.other_reg_index = ((first_dword >> 4) & 0xF);
+ case CompareRegisterValueType::OtherRegister:
+ begin_reg_cond.other_reg_index = ((first_dword >> 4) & 0xF);
break;
- case CompareRegisterValueType_MemoryRelAddr:
- opcode.begin_reg_cond.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
- opcode.begin_reg_cond.rel_address =
- (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+ case CompareRegisterValueType::MemoryRelAddr:
+ begin_reg_cond.mem_type = static_cast((first_dword >> 4) & 0xF);
+ begin_reg_cond.rel_address =
+ ((static_cast(first_dword & 0xF) << 32ul) | static_cast(GetNextDword()));
break;
- case CompareRegisterValueType_MemoryOfsReg:
- opcode.begin_reg_cond.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
- opcode.begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
+ case CompareRegisterValueType::MemoryOfsReg:
+ begin_reg_cond.mem_type = static_cast((first_dword >> 4) & 0xF);
+ begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
break;
- case CompareRegisterValueType_RegisterRelAddr:
- opcode.begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
- opcode.begin_reg_cond.rel_address =
- (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+ case CompareRegisterValueType::RegisterRelAddr:
+ begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
+ begin_reg_cond.rel_address =
+ ((static_cast(first_dword & 0xF) << 32ul) | static_cast(GetNextDword()));
break;
- case CompareRegisterValueType_RegisterOfsReg:
- opcode.begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
- opcode.begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
+ 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: {
- /* 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. */
- opcode.save_restore_reg.dst_index = (first_dword >> 16) & 0xF;
- opcode.save_restore_reg.src_index = (first_dword >> 8) & 0xF;
- opcode.save_restore_reg.op_type = (SaveRestoreRegisterOpType)((first_dword >> 4) & 0xF);
+ 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((first_dword >> 4) & 0xF);
+ opcode.opcode = save_restore_reg;
} break;
- case CheatVmOpcodeType_SaveRestoreRegisterMask: {
- /* 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. */
- opcode.save_restore_regmask.op_type =
- (SaveRestoreRegisterOpType)((first_dword >> 20) & 0xF);
- for (size_t i = 0; i < NumRegisters; i++) {
- opcode.save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0;
+ 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((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: {
- /* 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. */
- opcode.debug_log.bit_width = (first_dword >> 16) & 0xF;
- opcode.debug_log.log_id = ((first_dword >> 12) & 0xF);
- opcode.debug_log.val_type = (DebugLogValueType)((first_dword >> 8) & 0xF);
+ 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((first_dword >> 8) & 0xF);
- switch (opcode.debug_log.val_type) {
- case DebugLogValueType_RegisterValue:
- opcode.debug_log.val_reg_index = ((first_dword >> 4) & 0xF);
+ switch (debug_log.val_type) {
+ case DebugLogValueType::RegisterValue:
+ debug_log.val_reg_index = ((first_dword >> 4) & 0xF);
break;
- case DebugLogValueType_MemoryRelAddr:
- opcode.debug_log.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
- opcode.debug_log.rel_address =
- (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+ case DebugLogValueType::MemoryRelAddr:
+ debug_log.mem_type = static_cast((first_dword >> 4) & 0xF);
+ debug_log.rel_address =
+ ((static_cast(first_dword & 0xF) << 32ul) | static_cast(GetNextDword()));
break;
- case DebugLogValueType_MemoryOfsReg:
- opcode.debug_log.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
- opcode.debug_log.ofs_reg_index = (first_dword & 0xF);
+ case DebugLogValueType::MemoryOfsReg:
+ debug_log.mem_type = static_cast((first_dword >> 4) & 0xF);
+ debug_log.ofs_reg_index = (first_dword & 0xF);
break;
- case DebugLogValueType_RegisterRelAddr:
- opcode.debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
- opcode.debug_log.rel_address =
- (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+ case DebugLogValueType::RegisterRelAddr:
+ debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
+ debug_log.rel_address =
+ ((static_cast(first_dword & 0xF) << 32ul) | static_cast(GetNextDword()));
break;
- case DebugLogValueType_RegisterOfsReg:
- opcode.debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
- opcode.debug_log.ofs_reg_index = (first_dword & 0xF);
+ 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:
+ case CheatVmOpcodeType::ExtendedWidth:
+ case CheatVmOpcodeType::DoubleExtendedWidth:
default:
- /* Unrecognized instruction cannot be decoded. */
+ // Unrecognized instruction cannot be decoded.
valid = false;
+ opcode.opcode = UnrecognizedInstruction{opcode_type};
break;
}
- /* End decoding. */
+ // End decoding.
return valid;
}
void DmntCheatVm::SkipConditionalBlock() {
- if (this->condition_depth > 0) {
- /* We want to continue until we're out of the current block. */
- const size_t desired_depth = this->condition_depth - 1;
+ 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 (this->condition_depth > desired_depth && this->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... */
+ 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. */
+ // We also support nesting of conditional blocks, and Gateway does not.
if (skip_opcode.begin_conditional_block) {
- this->condition_depth++;
- } else if (skip_opcode.opcode == CheatVmOpcodeType_EndConditionalBlock) {
- this->condition_depth--;
+ condition_depth++;
+ } else if (std::holds_alternative(skip_opcode.opcode)) {
+ condition_depth--;
}
}
} else {
- /* Skipping, but this->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. */
+ // 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");
}
}
@@ -602,7 +646,7 @@ u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) {
case 8:
return value.bit64;
default:
- /* Invalid bit width -> return 0. */
+ // Invalid bit width -> return 0.
return 0;
}
}
@@ -610,39 +654,37 @@ u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) {
u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
MemoryAccessType mem_type, u64 rel_address) {
switch (mem_type) {
- case MemoryAccessType_MainNso:
+ case MemoryAccessType::MainNso:
default:
return metadata.main_nso_extents.base + rel_address;
- case MemoryAccessType_Heap:
+ case MemoryAccessType::Heap:
return metadata.heap_extents.base + rel_address;
}
}
void DmntCheatVm::ResetState() {
- for (size_t i = 0; i < DmntCheatVm::NumRegisters; i++) {
- this->registers[i] = 0;
- this->saved_values[i] = 0;
- this->loop_tops[i] = 0;
- }
- this->instruction_ptr = 0;
- this->condition_depth = 0;
- this->decode_success = true;
+ 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& entries) {
- /* Reset opcode count. */
- this->num_opcodes = 0;
+ // Reset opcode count.
+ num_opcodes = 0;
- for (size_t i = 0; i < entries.size(); i++) {
+ for (std::size_t i = 0; i < entries.size(); i++) {
if (entries[i].enabled) {
- /* Bounds check. */
- if (entries[i].definition.num_opcodes + this->num_opcodes > MaximumProgramOpcodeCount) {
- this->num_opcodes = 0;
+ // Bounds check.
+ if (entries[i].definition.num_opcodes + num_opcodes > MaximumProgramOpcodeCount) {
+ num_opcodes = 0;
return false;
}
- for (size_t n = 0; n < entries[i].definition.num_opcodes; n++) {
- this->program[this->num_opcodes++] = entries[i].definition.opcodes[n];
+ for (std::size_t n = 0; n < entries[i].definition.num_opcodes; n++) {
+ program[num_opcodes++] = entries[i].definition.opcodes[n];
}
}
}
@@ -653,262 +695,249 @@ bool DmntCheatVm::LoadProgram(const std::vector& entries) {
void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
CheatVmOpcode cur_opcode{};
- /* Get Keys down. */
+ // Get Keys down.
u64 kDown = callbacks->HidKeysDown();
- this->LogToDebugFile("Started VM execution.\n");
- this->LogToDebugFile("Main NSO: %012lx\n", metadata.main_nso_extents.base);
- this->LogToDebugFile("Heap: %012lx\n", metadata.main_nso_extents.base);
- this->LogToDebugFile("Keys Down: %08x\n", (u32)(kDown & 0x0FFFFFFF));
+ 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(kDown & 0x0FFFFFFF)));
- /* Clear VM state. */
- this->ResetState();
+ // Clear VM state.
+ ResetState();
- /* Loop until program finishes. */
- while (this->DecodeNextOpcode(cur_opcode)) {
- this->LogToDebugFile("Instruction Ptr: %04x\n", (u32)this->instruction_ptr);
+ // Loop until program finishes.
+ while (DecodeNextOpcode(cur_opcode)) {
+ callbacks->CommandLog(
+ fmt::format("Instruction Ptr: {:04X}", static_cast(instruction_ptr)));
- for (size_t i = 0; i < NumRegisters; i++) {
- this->LogToDebugFile("Registers[%02x]: %016lx\n", i, this->registers[i]);
+ for (std::size_t i = 0; i < NumRegisters; i++) {
+ callbacks->CommandLog(fmt::format("Registers[{:02X}]: {:016X}", i, registers[i]));
}
- for (size_t i = 0; i < NumRegisters; i++) {
- this->LogToDebugFile("SavedRegs[%02x]: %016lx\n", i, this->saved_values[i]);
+ for (std::size_t i = 0; i < NumRegisters; i++) {
+ callbacks->CommandLog(fmt::format("SavedRegs[{:02X}]: {:016X}", i, saved_values[i]));
}
- this->LogOpcode(cur_opcode);
+ LogOpcode(cur_opcode);
- /* Increment conditional depth, if relevant. */
+ // Increment conditional depth, if relevant.
if (cur_opcode.begin_conditional_block) {
- this->condition_depth++;
+ condition_depth++;
}
- switch (cur_opcode.opcode) {
- case CheatVmOpcodeType_StoreStatic: {
- /* Calculate address, write value to memory. */
- u64 dst_address = GetCheatProcessAddress(
- metadata, cur_opcode.store_static.mem_type,
- cur_opcode.store_static.rel_address +
- this->registers[cur_opcode.store_static.offset_register]);
- u64 dst_value =
- GetVmInt(cur_opcode.store_static.value, cur_opcode.store_static.bit_width);
- switch (cur_opcode.store_static.bit_width) {
+ if (auto store_static = std::get_if(&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, cur_opcode.store_static.bit_width);
+ callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width);
break;
}
- } break;
- case CheatVmOpcodeType_BeginConditionalBlock: {
- /* Read value from memory. */
- u64 src_address = GetCheatProcessAddress(metadata, cur_opcode.begin_cond.mem_type,
- cur_opcode.begin_cond.rel_address);
+ } else if (auto begin_cond = std::get_if(&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 (cur_opcode.store_static.bit_width) {
+ switch (store_static->bit_width) {
case 1:
case 2:
case 4:
case 8:
- callbacks->MemoryRead(src_address, &src_value, cur_opcode.begin_cond.bit_width);
+ callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width);
break;
}
- /* Check against condition. */
- u64 cond_value = GetVmInt(cur_opcode.begin_cond.value, cur_opcode.begin_cond.bit_width);
+ // Check against condition.
+ u64 cond_value = GetVmInt(begin_cond->value, begin_cond->bit_width);
bool cond_met = false;
- switch (cur_opcode.begin_cond.cond_type) {
- case ConditionalComparisonType_GT:
+ switch (begin_cond->cond_type) {
+ case ConditionalComparisonType::GT:
cond_met = src_value > cond_value;
break;
- case ConditionalComparisonType_GE:
+ case ConditionalComparisonType::GE:
cond_met = src_value >= cond_value;
break;
- case ConditionalComparisonType_LT:
+ case ConditionalComparisonType::LT:
cond_met = src_value < cond_value;
break;
- case ConditionalComparisonType_LE:
+ case ConditionalComparisonType::LE:
cond_met = src_value <= cond_value;
break;
- case ConditionalComparisonType_EQ:
+ case ConditionalComparisonType::EQ:
cond_met = src_value == cond_value;
break;
- case ConditionalComparisonType_NE:
+ case ConditionalComparisonType::NE:
cond_met = src_value != cond_value;
break;
}
- /* Skip conditional block if condition not met. */
+ // Skip conditional block if condition not met.
if (!cond_met) {
- this->SkipConditionalBlock();
+ SkipConditionalBlock();
}
- } break;
- case CheatVmOpcodeType_EndConditionalBlock:
- /* Decrement the condition depth. */
- /* We will assume, graciously, that mismatched conditional block ends are a nop. */
- if (this->condition_depth > 0) {
- this->condition_depth--;
+ } else if (auto end_cond = std::get_if(&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--;
}
- break;
- case CheatVmOpcodeType_ControlLoop:
- if (cur_opcode.ctrl_loop.start_loop) {
- /* Start a loop. */
- this->registers[cur_opcode.ctrl_loop.reg_index] = cur_opcode.ctrl_loop.num_iters;
- this->loop_tops[cur_opcode.ctrl_loop.reg_index] = this->instruction_ptr;
+ } else if (auto ctrl_loop = std::get_if(&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. */
- this->registers[cur_opcode.ctrl_loop.reg_index]--;
- if (this->registers[cur_opcode.ctrl_loop.reg_index] != 0) {
- this->instruction_ptr = this->loop_tops[cur_opcode.ctrl_loop.reg_index];
+ // End a loop.
+ registers[ctrl_loop->reg_index]--;
+ if (registers[ctrl_loop->reg_index] != 0) {
+ instruction_ptr = loop_tops[ctrl_loop->reg_index];
}
}
- break;
- case CheatVmOpcodeType_LoadRegisterStatic:
- /* Set a register to a static value. */
- this->registers[cur_opcode.ldr_static.reg_index] = cur_opcode.ldr_static.value;
- break;
- case CheatVmOpcodeType_LoadRegisterMemory: {
- /* Choose source address. */
+ } else if (auto ldr_static = std::get_if(&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(&cur_opcode.opcode)) {
+ // Choose source address.
u64 src_address;
- if (cur_opcode.ldr_memory.load_from_reg) {
- src_address = this->registers[cur_opcode.ldr_memory.reg_index] +
- cur_opcode.ldr_memory.rel_address;
+ if (ldr_memory->load_from_reg) {
+ src_address = registers[ldr_memory->reg_index] + ldr_memory->rel_address;
} else {
- src_address = GetCheatProcessAddress(metadata, cur_opcode.ldr_memory.mem_type,
- cur_opcode.ldr_memory.rel_address);
+ src_address =
+ GetCheatProcessAddress(metadata, ldr_memory->mem_type, ldr_memory->rel_address);
}
- /* Read into register. Gateway only reads on valid bitwidth. */
- switch (cur_opcode.ldr_memory.bit_width) {
+ // 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,
- &this->registers[cur_opcode.ldr_memory.reg_index],
- cur_opcode.ldr_memory.bit_width);
+ callbacks->MemoryRead(src_address, ®isters[ldr_memory->reg_index],
+ ldr_memory->bit_width);
break;
}
- } break;
- case CheatVmOpcodeType_StoreStaticToAddress: {
- /* Calculate address. */
- u64 dst_address = this->registers[cur_opcode.str_static.reg_index];
- u64 dst_value = cur_opcode.str_static.value;
- if (cur_opcode.str_static.add_offset_reg) {
- dst_address += this->registers[cur_opcode.str_static.offset_reg_index];
+ } else if (auto str_static = std::get_if(&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 (cur_opcode.str_static.bit_width) {
+ // 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, cur_opcode.str_static.bit_width);
+ callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width);
break;
}
- /* Increment register if relevant. */
- if (cur_opcode.str_static.increment_reg) {
- this->registers[cur_opcode.str_static.reg_index] += cur_opcode.str_static.bit_width;
+ // Increment register if relevant.
+ if (str_static->increment_reg) {
+ registers[str_static->reg_index] += str_static->bit_width;
}
- } break;
- case CheatVmOpcodeType_PerformArithmeticStatic: {
- /* Do requested math. */
- switch (cur_opcode.perform_math_static.math_type) {
- case RegisterArithmeticType_Addition:
- this->registers[cur_opcode.perform_math_static.reg_index] +=
- (u64)cur_opcode.perform_math_static.value;
+ } else if (auto perform_math_static =
+ std::get_if(&cur_opcode.opcode)) {
+ // Do requested math.
+ switch (perform_math_static->math_type) {
+ case RegisterArithmeticType::Addition:
+ registers[perform_math_static->reg_index] +=
+ static_cast(perform_math_static->value);
break;
- case RegisterArithmeticType_Subtraction:
- this->registers[cur_opcode.perform_math_static.reg_index] -=
- (u64)cur_opcode.perform_math_static.value;
+ case RegisterArithmeticType::Subtraction:
+ registers[perform_math_static->reg_index] -=
+ static_cast(perform_math_static->value);
break;
- case RegisterArithmeticType_Multiplication:
- this->registers[cur_opcode.perform_math_static.reg_index] *=
- (u64)cur_opcode.perform_math_static.value;
+ case RegisterArithmeticType::Multiplication:
+ registers[perform_math_static->reg_index] *=
+ static_cast(perform_math_static->value);
break;
- case RegisterArithmeticType_LeftShift:
- this->registers[cur_opcode.perform_math_static.reg_index] <<=
- (u64)cur_opcode.perform_math_static.value;
+ case RegisterArithmeticType::LeftShift:
+ registers[perform_math_static->reg_index] <<=
+ static_cast(perform_math_static->value);
break;
- case RegisterArithmeticType_RightShift:
- this->registers[cur_opcode.perform_math_static.reg_index] >>=
- (u64)cur_opcode.perform_math_static.value;
+ case RegisterArithmeticType::RightShift:
+ registers[perform_math_static->reg_index] >>=
+ static_cast(perform_math_static->value);
break;
default:
- /* Do not handle extensions here. */
+ // Do not handle extensions here.
break;
}
- /* Apply bit width. */
- switch (cur_opcode.perform_math_static.bit_width) {
+ // Apply bit width.
+ switch (perform_math_static->bit_width) {
case 1:
- this->registers[cur_opcode.perform_math_static.reg_index] =
- static_cast(this->registers[cur_opcode.perform_math_static.reg_index]);
+ registers[perform_math_static->reg_index] =
+ static_cast(registers[perform_math_static->reg_index]);
break;
case 2:
- this->registers[cur_opcode.perform_math_static.reg_index] =
- static_cast(this->registers[cur_opcode.perform_math_static.reg_index]);
+ registers[perform_math_static->reg_index] =
+ static_cast(registers[perform_math_static->reg_index]);
break;
case 4:
- this->registers[cur_opcode.perform_math_static.reg_index] =
- static_cast(this->registers[cur_opcode.perform_math_static.reg_index]);
+ registers[perform_math_static->reg_index] =
+ static_cast(registers[perform_math_static->reg_index]);
break;
case 8:
- this->registers[cur_opcode.perform_math_static.reg_index] =
- static_cast(this->registers[cur_opcode.perform_math_static.reg_index]);
+ registers[perform_math_static->reg_index] =
+ static_cast(registers[perform_math_static->reg_index]);
break;
}
- } break;
- case CheatVmOpcodeType_BeginKeypressConditionalBlock:
- /* Check for keypress. */
- if ((cur_opcode.begin_keypress_cond.key_mask & kDown) !=
- cur_opcode.begin_keypress_cond.key_mask) {
- /* Keys not pressed. Skip conditional block. */
- this->SkipConditionalBlock();
+ } else if (auto begin_keypress_cond =
+ std::get_if(&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();
}
- break;
- case CheatVmOpcodeType_PerformArithmeticRegister: {
- const u64 operand_1_value =
- this->registers[cur_opcode.perform_math_reg.src_reg_1_index];
+ } else if (auto perform_math_reg =
+ std::get_if(&cur_opcode.opcode)) {
+ const u64 operand_1_value = registers[perform_math_reg->src_reg_1_index];
const u64 operand_2_value =
- cur_opcode.perform_math_reg.has_immediate
- ? GetVmInt(cur_opcode.perform_math_reg.value,
- cur_opcode.perform_math_reg.bit_width)
- : this->registers[cur_opcode.perform_math_reg.src_reg_2_index];
+ 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 (cur_opcode.perform_math_reg.math_type) {
- case RegisterArithmeticType_Addition:
+ // Do requested math.
+ switch (perform_math_reg->math_type) {
+ case RegisterArithmeticType::Addition:
res_val = operand_1_value + operand_2_value;
break;
- case RegisterArithmeticType_Subtraction:
+ case RegisterArithmeticType::Subtraction:
res_val = operand_1_value - operand_2_value;
break;
- case RegisterArithmeticType_Multiplication:
+ case RegisterArithmeticType::Multiplication:
res_val = operand_1_value * operand_2_value;
break;
- case RegisterArithmeticType_LeftShift:
+ case RegisterArithmeticType::LeftShift:
res_val = operand_1_value << operand_2_value;
break;
- case RegisterArithmeticType_RightShift:
+ case RegisterArithmeticType::RightShift:
res_val = operand_1_value >> operand_2_value;
break;
- case RegisterArithmeticType_LogicalAnd:
+ case RegisterArithmeticType::LogicalAnd:
res_val = operand_1_value & operand_2_value;
break;
- case RegisterArithmeticType_LogicalOr:
+ case RegisterArithmeticType::LogicalOr:
res_val = operand_1_value | operand_2_value;
break;
- case RegisterArithmeticType_LogicalNot:
+ case RegisterArithmeticType::LogicalNot:
res_val = ~operand_1_value;
break;
- case RegisterArithmeticType_LogicalXor:
+ case RegisterArithmeticType::LogicalXor:
res_val = operand_1_value ^ operand_2_value;
break;
- case RegisterArithmeticType_None:
+ case RegisterArithmeticType::None:
res_val = operand_1_value;
break;
}
- /* Apply bit width. */
- switch (cur_opcode.perform_math_reg.bit_width) {
+ // Apply bit width.
+ switch (perform_math_reg->bit_width) {
case 1:
res_val = static_cast(res_val);
break;
@@ -923,282 +952,259 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
break;
}
- /* Save to register. */
- this->registers[cur_opcode.perform_math_reg.dst_reg_index] = res_val;
- } break;
- case CheatVmOpcodeType_StoreRegisterToAddress: {
- /* Calculate address. */
- u64 dst_value = this->registers[cur_opcode.str_register.str_reg_index];
- u64 dst_address = this->registers[cur_opcode.str_register.addr_reg_index];
- switch (cur_opcode.str_register.ofs_type) {
- case StoreRegisterOffsetType_None:
- /* Nothing more to do */
+ // Save to register.
+ registers[perform_math_reg->dst_reg_index] = res_val;
+ } else if (auto str_register =
+ std::get_if(&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 += this->registers[cur_opcode.str_register.ofs_reg_index];
+ case StoreRegisterOffsetType::Reg:
+ dst_address += registers[str_register->ofs_reg_index];
break;
- case StoreRegisterOffsetType_Imm:
- dst_address += cur_opcode.str_register.rel_address;
+ case StoreRegisterOffsetType::Imm:
+ dst_address += str_register->rel_address;
break;
- case StoreRegisterOffsetType_MemReg:
- dst_address =
- GetCheatProcessAddress(metadata, cur_opcode.str_register.mem_type,
- this->registers[cur_opcode.str_register.addr_reg_index]);
+ 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, cur_opcode.str_register.mem_type,
- cur_opcode.str_register.rel_address);
+ case StoreRegisterOffsetType::MemImm:
+ dst_address = GetCheatProcessAddress(metadata, str_register->mem_type,
+ str_register->rel_address);
break;
- case StoreRegisterOffsetType_MemImmReg:
- dst_address =
- GetCheatProcessAddress(metadata, cur_opcode.str_register.mem_type,
- this->registers[cur_opcode.str_register.addr_reg_index] +
- cur_opcode.str_register.rel_address);
+ 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 (cur_opcode.str_register.bit_width) {
+ // 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, cur_opcode.str_register.bit_width);
+ callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width);
break;
}
- /* Increment register if relevant. */
- if (cur_opcode.str_register.increment_reg) {
- this->registers[cur_opcode.str_register.addr_reg_index] +=
- cur_opcode.str_register.bit_width;
+ // Increment register if relevant.
+ if (str_register->increment_reg) {
+ registers[str_register->addr_reg_index] += str_register->bit_width;
}
- } break;
- case CheatVmOpcodeType_BeginRegisterConditionalBlock: {
- /* Get value from register. */
+ } else if (auto begin_reg_cond =
+ std::get_if(&cur_opcode.opcode)) {
+ // Get value from register.
u64 src_value = 0;
- switch (cur_opcode.begin_reg_cond.bit_width) {
+ switch (begin_reg_cond->bit_width) {
case 1:
- src_value = static_cast(
- this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFul);
+ src_value = static_cast(registers[begin_reg_cond->val_reg_index] & 0xFFul);
break;
case 2:
- src_value = static_cast(
- this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFFFul);
+ src_value = static_cast(registers[begin_reg_cond->val_reg_index] & 0xFFFFul);
break;
case 4:
- src_value = static_cast(
- this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFFFFFFFul);
+ src_value =
+ static_cast(registers[begin_reg_cond->val_reg_index] & 0xFFFFFFFFul);
break;
case 8:
- src_value =
- static_cast(this->registers[cur_opcode.begin_reg_cond.val_reg_index] &
- 0xFFFFFFFFFFFFFFFFul);
+ src_value = static_cast(registers[begin_reg_cond->val_reg_index] &
+ 0xFFFFFFFFFFFFFFFFul);
break;
}
- /* Read value from memory. */
+ // Read value from memory.
u64 cond_value = 0;
- if (cur_opcode.begin_reg_cond.comp_type == CompareRegisterValueType_StaticValue) {
- cond_value =
- GetVmInt(cur_opcode.begin_reg_cond.value, cur_opcode.begin_reg_cond.bit_width);
- } else if (cur_opcode.begin_reg_cond.comp_type ==
- CompareRegisterValueType_OtherRegister) {
- switch (cur_opcode.begin_reg_cond.bit_width) {
+ 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(
- this->registers[cur_opcode.begin_reg_cond.other_reg_index] & 0xFFul);
+ cond_value =
+ static_cast(registers[begin_reg_cond->other_reg_index] & 0xFFul);
break;
case 2:
- cond_value = static_cast(
- this->registers[cur_opcode.begin_reg_cond.other_reg_index] & 0xFFFFul);
+ cond_value =
+ static_cast(registers[begin_reg_cond->other_reg_index] & 0xFFFFul);
break;
case 4:
- cond_value = static_cast(
- this->registers[cur_opcode.begin_reg_cond.other_reg_index] & 0xFFFFFFFFul);
+ cond_value =
+ static_cast(registers[begin_reg_cond->other_reg_index] & 0xFFFFFFFFul);
break;
case 8:
- cond_value = static_cast(
- this->registers[cur_opcode.begin_reg_cond.other_reg_index] &
- 0xFFFFFFFFFFFFFFFFul);
+ cond_value = static_cast(registers[begin_reg_cond->other_reg_index] &
+ 0xFFFFFFFFFFFFFFFFul);
break;
}
} else {
u64 cond_address = 0;
- switch (cur_opcode.begin_reg_cond.comp_type) {
- case CompareRegisterValueType_MemoryRelAddr:
- cond_address =
- GetCheatProcessAddress(metadata, cur_opcode.begin_reg_cond.mem_type,
- cur_opcode.begin_reg_cond.rel_address);
+ 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, cur_opcode.begin_reg_cond.mem_type,
- this->registers[cur_opcode.begin_reg_cond.ofs_reg_index]);
+ 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 = this->registers[cur_opcode.begin_reg_cond.addr_reg_index] +
- cur_opcode.begin_reg_cond.rel_address;
+ case CompareRegisterValueType::RegisterRelAddr:
+ cond_address =
+ registers[begin_reg_cond->addr_reg_index] + begin_reg_cond->rel_address;
break;
- case CompareRegisterValueType_RegisterOfsReg:
- cond_address = this->registers[cur_opcode.begin_reg_cond.addr_reg_index] +
- this->registers[cur_opcode.begin_reg_cond.ofs_reg_index];
+ case CompareRegisterValueType::RegisterOfsReg:
+ cond_address = registers[begin_reg_cond->addr_reg_index] +
+ registers[begin_reg_cond->ofs_reg_index];
break;
default:
break;
}
- switch (cur_opcode.begin_reg_cond.bit_width) {
+ switch (begin_reg_cond->bit_width) {
case 1:
case 2:
case 4:
case 8:
- callbacks->MemoryRead(cond_address, &cond_value,
- cur_opcode.begin_reg_cond.bit_width);
+ callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width);
break;
}
}
- /* Check against condition. */
+ // Check against condition.
bool cond_met = false;
- switch (cur_opcode.begin_reg_cond.cond_type) {
- case ConditionalComparisonType_GT:
+ switch (begin_reg_cond->cond_type) {
+ case ConditionalComparisonType::GT:
cond_met = src_value > cond_value;
break;
- case ConditionalComparisonType_GE:
+ case ConditionalComparisonType::GE:
cond_met = src_value >= cond_value;
break;
- case ConditionalComparisonType_LT:
+ case ConditionalComparisonType::LT:
cond_met = src_value < cond_value;
break;
- case ConditionalComparisonType_LE:
+ case ConditionalComparisonType::LE:
cond_met = src_value <= cond_value;
break;
- case ConditionalComparisonType_EQ:
+ case ConditionalComparisonType::EQ:
cond_met = src_value == cond_value;
break;
- case ConditionalComparisonType_NE:
+ case ConditionalComparisonType::NE:
cond_met = src_value != cond_value;
break;
}
- /* Skip conditional block if condition not met. */
+ // Skip conditional block if condition not met.
if (!cond_met) {
- this->SkipConditionalBlock();
+ SkipConditionalBlock();
}
- } break;
- case CheatVmOpcodeType_SaveRestoreRegister:
- /* Save or restore a register. */
- switch (cur_opcode.save_restore_reg.op_type) {
- case SaveRestoreRegisterOpType_ClearRegs:
- this->registers[cur_opcode.save_restore_reg.dst_index] = 0ul;
+ } else if (auto save_restore_reg =
+ std::get_if(&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:
- this->saved_values[cur_opcode.save_restore_reg.dst_index] = 0ul;
+ case SaveRestoreRegisterOpType::ClearSaved:
+ saved_values[save_restore_reg->dst_index] = 0ul;
break;
- case SaveRestoreRegisterOpType_Save:
- this->saved_values[cur_opcode.save_restore_reg.dst_index] =
- this->registers[cur_opcode.save_restore_reg.src_index];
+ case SaveRestoreRegisterOpType::Save:
+ saved_values[save_restore_reg->dst_index] = registers[save_restore_reg->src_index];
break;
- case SaveRestoreRegisterOpType_Restore:
+ case SaveRestoreRegisterOpType::Restore:
default:
- this->registers[cur_opcode.save_restore_reg.dst_index] =
- this->saved_values[cur_opcode.save_restore_reg.src_index];
+ registers[save_restore_reg->dst_index] = saved_values[save_restore_reg->src_index];
break;
}
- break;
- case CheatVmOpcodeType_SaveRestoreRegisterMask:
- /* Save or restore register mask. */
+ } else if (auto save_restore_regmask =
+ std::get_if(&cur_opcode.opcode)) {
+ // Save or restore register mask.
u64* src;
u64* dst;
- switch (cur_opcode.save_restore_regmask.op_type) {
- case SaveRestoreRegisterOpType_ClearSaved:
- case SaveRestoreRegisterOpType_Save:
- src = this->registers.data();
- dst = this->saved_values.data();
+ 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:
+ case SaveRestoreRegisterOpType::ClearRegs:
+ case SaveRestoreRegisterOpType::Restore:
default:
- src = this->registers.data();
- dst = this->saved_values.data();
+ src = registers.data();
+ dst = saved_values.data();
break;
}
- for (size_t i = 0; i < NumRegisters; i++) {
- if (cur_opcode.save_restore_regmask.should_operate[i]) {
- switch (cur_opcode.save_restore_regmask.op_type) {
- case SaveRestoreRegisterOpType_ClearSaved:
- case SaveRestoreRegisterOpType_ClearRegs:
+ 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:
+ case SaveRestoreRegisterOpType::Save:
+ case SaveRestoreRegisterOpType::Restore:
default:
dst[i] = src[i];
break;
}
}
}
- break;
- case CheatVmOpcodeType_DebugLog: {
- /* Read value from memory. */
+ } else if (auto debug_log = std::get_if(&cur_opcode.opcode)) {
+ // Read value from memory.
u64 log_value = 0;
- if (cur_opcode.debug_log.val_type == DebugLogValueType_RegisterValue) {
- switch (cur_opcode.debug_log.bit_width) {
+ if (debug_log->val_type == DebugLogValueType::RegisterValue) {
+ switch (debug_log->bit_width) {
case 1:
- log_value = static_cast(
- this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFul);
+ log_value = static_cast(registers[debug_log->val_reg_index] & 0xFFul);
break;
case 2:
- log_value = static_cast(
- this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFFFul);
+ log_value = static_cast(registers[debug_log->val_reg_index] & 0xFFFFul);
break;
case 4:
- log_value = static_cast(
- this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFFFFFFFul);
+ log_value =
+ static_cast(registers[debug_log->val_reg_index] & 0xFFFFFFFFul);
break;
case 8:
- log_value = static_cast(
- this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFFFFFFFFFFFFFFFul);
+ log_value = static_cast(registers[debug_log->val_reg_index] &
+ 0xFFFFFFFFFFFFFFFFul);
break;
}
} else {
u64 val_address = 0;
- switch (cur_opcode.debug_log.val_type) {
- case DebugLogValueType_MemoryRelAddr:
- val_address = GetCheatProcessAddress(metadata, cur_opcode.debug_log.mem_type,
- cur_opcode.debug_log.rel_address);
+ 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, cur_opcode.debug_log.mem_type,
- this->registers[cur_opcode.debug_log.ofs_reg_index]);
+ case DebugLogValueType::MemoryOfsReg:
+ val_address = GetCheatProcessAddress(metadata, debug_log->mem_type,
+ registers[debug_log->ofs_reg_index]);
break;
- case DebugLogValueType_RegisterRelAddr:
- val_address = this->registers[cur_opcode.debug_log.addr_reg_index] +
- cur_opcode.debug_log.rel_address;
+ case DebugLogValueType::RegisterRelAddr:
+ val_address = registers[debug_log->addr_reg_index] + debug_log->rel_address;
break;
- case DebugLogValueType_RegisterOfsReg:
- val_address = this->registers[cur_opcode.debug_log.addr_reg_index] +
- this->registers[cur_opcode.debug_log.ofs_reg_index];
+ case DebugLogValueType::RegisterOfsReg:
+ val_address =
+ registers[debug_log->addr_reg_index] + registers[debug_log->ofs_reg_index];
break;
default:
break;
}
- switch (cur_opcode.debug_log.bit_width) {
+ switch (debug_log->bit_width) {
case 1:
case 2:
case 4:
case 8:
- callbacks->MemoryRead(val_address, &log_value, cur_opcode.debug_log.bit_width);
+ callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width);
break;
}
}
- /* Log value. */
- this->DebugLog(cur_opcode.debug_log.log_id, log_value);
- } break;
- default:
- /* By default, we do a no-op. */
- break;
+ // Log value.
+ DebugLog(debug_log->log_id, log_value);
}
}
}
diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h
index bea451db4..d1580d7f6 100644
--- a/src/core/memory/dmnt_cheat_vm.h
+++ b/src/core/memory/dmnt_cheat_vm.h
@@ -24,6 +24,7 @@
#pragma once
+#include
#include
#include
#include "common/common_types.h"
@@ -31,100 +32,100 @@
namespace Memory {
-enum CheatVmOpcodeType : u32 {
- CheatVmOpcodeType_StoreStatic = 0,
- CheatVmOpcodeType_BeginConditionalBlock = 1,
- CheatVmOpcodeType_EndConditionalBlock = 2,
- CheatVmOpcodeType_ControlLoop = 3,
- CheatVmOpcodeType_LoadRegisterStatic = 4,
- CheatVmOpcodeType_LoadRegisterMemory = 5,
- CheatVmOpcodeType_StoreStaticToAddress = 6,
- CheatVmOpcodeType_PerformArithmeticStatic = 7,
- CheatVmOpcodeType_BeginKeypressConditionalBlock = 8,
-
- /* These are not implemented by Gateway's VM. */
- CheatVmOpcodeType_PerformArithmeticRegister = 9,
- CheatVmOpcodeType_StoreRegisterToAddress = 10,
- CheatVmOpcodeType_Reserved11 = 11,
-
- /* This is a meta entry, and not a real opcode. */
- /* This is to facilitate multi-nybble instruction decoding. */
- CheatVmOpcodeType_ExtendedWidth = 12,
-
- /* Extended width opcodes. */
- CheatVmOpcodeType_BeginRegisterConditionalBlock = 0xC0,
- CheatVmOpcodeType_SaveRestoreRegister = 0xC1,
- CheatVmOpcodeType_SaveRestoreRegisterMask = 0xC2,
-
- /* This is a meta entry, and not a real opcode. */
- /* This is to facilitate multi-nybble instruction decoding. */
- CheatVmOpcodeType_DoubleExtendedWidth = 0xF0,
-
- /* Double-extended width opcodes. */
- CheatVmOpcodeType_DebugLog = 0xFFF,
+enum class CheatVmOpcodeType : u32 {
+ StoreStatic = 0,
+ BeginConditionalBlock = 1,
+ EndConditionalBlock = 2,
+ ControlLoop = 3,
+ LoadRegisterStatic = 4,
+ LoadRegisterMemory = 5,
+ StoreStaticToAddress = 6,
+ PerformArithmeticStatic = 7,
+ BeginKeypressConditionalBlock = 8,
+
+ // These are not implemented by Gateway's VM.
+ PerformArithmeticRegister = 9,
+ StoreRegisterToAddress = 10,
+ Reserved11 = 11,
+
+ // This is a meta entry, and not a real opcode.
+ // This is to facilitate multi-nybble instruction decoding.
+ ExtendedWidth = 12,
+
+ // Extended width opcodes.
+ BeginRegisterConditionalBlock = 0xC0,
+ SaveRestoreRegister = 0xC1,
+ SaveRestoreRegisterMask = 0xC2,
+
+ // This is a meta entry, and not a real opcode.
+ // This is to facilitate multi-nybble instruction decoding.
+ DoubleExtendedWidth = 0xF0,
+
+ // Double-extended width opcodes.
+ DebugLog = 0xFFF,
};
-enum MemoryAccessType : u32 {
- MemoryAccessType_MainNso = 0,
- MemoryAccessType_Heap = 1,
+enum class MemoryAccessType : u32 {
+ MainNso = 0,
+ Heap = 1,
};
-enum ConditionalComparisonType : u32 {
- ConditionalComparisonType_GT = 1,
- ConditionalComparisonType_GE = 2,
- ConditionalComparisonType_LT = 3,
- ConditionalComparisonType_LE = 4,
- ConditionalComparisonType_EQ = 5,
- ConditionalComparisonType_NE = 6,
+enum class ConditionalComparisonType : u32 {
+ GT = 1,
+ GE = 2,
+ LT = 3,
+ LE = 4,
+ EQ = 5,
+ NE = 6,
};
-enum RegisterArithmeticType : u32 {
- RegisterArithmeticType_Addition = 0,
- RegisterArithmeticType_Subtraction = 1,
- RegisterArithmeticType_Multiplication = 2,
- RegisterArithmeticType_LeftShift = 3,
- RegisterArithmeticType_RightShift = 4,
+enum class RegisterArithmeticType : u32 {
+ Addition = 0,
+ Subtraction = 1,
+ Multiplication = 2,
+ LeftShift = 3,
+ RightShift = 4,
- /* These are not supported by Gateway's VM. */
- RegisterArithmeticType_LogicalAnd = 5,
- RegisterArithmeticType_LogicalOr = 6,
- RegisterArithmeticType_LogicalNot = 7,
- RegisterArithmeticType_LogicalXor = 8,
+ // These are not supported by Gateway's VM.
+ LogicalAnd = 5,
+ LogicalOr = 6,
+ LogicalNot = 7,
+ LogicalXor = 8,
- RegisterArithmeticType_None = 9,
+ None = 9,
};
-enum StoreRegisterOffsetType : u32 {
- StoreRegisterOffsetType_None = 0,
- StoreRegisterOffsetType_Reg = 1,
- StoreRegisterOffsetType_Imm = 2,
- StoreRegisterOffsetType_MemReg = 3,
- StoreRegisterOffsetType_MemImm = 4,
- StoreRegisterOffsetType_MemImmReg = 5,
+enum class StoreRegisterOffsetType : u32 {
+ None = 0,
+ Reg = 1,
+ Imm = 2,
+ MemReg = 3,
+ MemImm = 4,
+ MemImmReg = 5,
};
-enum CompareRegisterValueType : u32 {
- CompareRegisterValueType_MemoryRelAddr = 0,
- CompareRegisterValueType_MemoryOfsReg = 1,
- CompareRegisterValueType_RegisterRelAddr = 2,
- CompareRegisterValueType_RegisterOfsReg = 3,
- CompareRegisterValueType_StaticValue = 4,
- CompareRegisterValueType_OtherRegister = 5,
+enum class CompareRegisterValueType : u32 {
+ MemoryRelAddr = 0,
+ MemoryOfsReg = 1,
+ RegisterRelAddr = 2,
+ RegisterOfsReg = 3,
+ StaticValue = 4,
+ OtherRegister = 5,
};
-enum SaveRestoreRegisterOpType : u32 {
- SaveRestoreRegisterOpType_Restore = 0,
- SaveRestoreRegisterOpType_Save = 1,
- SaveRestoreRegisterOpType_ClearSaved = 2,
- SaveRestoreRegisterOpType_ClearRegs = 3,
+enum class SaveRestoreRegisterOpType : u32 {
+ Restore = 0,
+ Save = 1,
+ ClearSaved = 2,
+ ClearRegs = 3,
};
-enum DebugLogValueType : u32 {
- DebugLogValueType_MemoryRelAddr = 0,
- DebugLogValueType_MemoryOfsReg = 1,
- DebugLogValueType_RegisterRelAddr = 2,
- DebugLogValueType_RegisterOfsReg = 3,
- DebugLogValueType_RegisterValue = 4,
+enum class DebugLogValueType : u32 {
+ MemoryRelAddr = 0,
+ MemoryOfsReg = 1,
+ RegisterRelAddr = 2,
+ RegisterOfsReg = 3,
+ RegisterValue = 4,
};
union VmInt {
@@ -247,26 +248,19 @@ struct DebugLogOpcode {
u64 rel_address;
};
-struct CheatVmOpcode {
+struct UnrecognizedInstruction {
CheatVmOpcodeType opcode;
+};
+
+struct CheatVmOpcode {
bool begin_conditional_block;
- union {
- StoreStaticOpcode store_static;
- BeginConditionalOpcode begin_cond;
- EndConditionalOpcode end_cond;
- ControlLoopOpcode ctrl_loop;
- LoadRegisterStaticOpcode ldr_static;
- LoadRegisterMemoryOpcode ldr_memory;
- StoreStaticToAddressOpcode str_static;
- PerformArithmeticStaticOpcode perform_math_static;
- BeginKeypressConditionalOpcode begin_keypress_cond;
- PerformArithmeticRegisterOpcode perform_math_reg;
- StoreRegisterToAddressOpcode str_register;
- BeginRegisterConditionalOpcode begin_reg_cond;
- SaveRestoreRegisterOpcode save_restore_reg;
- SaveRestoreRegisterMaskOpcode save_restore_regmask;
- DebugLogOpcode debug_log;
- };
+ std::variant
+ opcode;
};
class DmntCheatVm {
@@ -285,50 +279,43 @@ public:
virtual void CommandLog(std::string_view data) = 0;
};
- constexpr static size_t MaximumProgramOpcodeCount = 0x400;
- constexpr static size_t NumRegisters = 0x10;
+ static constexpr std::size_t MaximumProgramOpcodeCount = 0x400;
+ static constexpr std::size_t NumRegisters = 0x10;
+
+ explicit DmntCheatVm(std::unique_ptr callbacks);
+ ~DmntCheatVm();
+
+ std::size_t GetProgramSize() const {
+ return this->num_opcodes;
+ }
+
+ bool LoadProgram(const std::vector& cheats);
+ void Execute(const CheatProcessMetadata& metadata);
private:
std::unique_ptr callbacks;
- size_t num_opcodes = 0;
- size_t instruction_ptr = 0;
- size_t condition_depth = 0;
+ std::size_t num_opcodes = 0;
+ std::size_t instruction_ptr = 0;
+ std::size_t condition_depth = 0;
bool decode_success = false;
std::array program{};
std::array registers{};
std::array saved_values{};
- std::array loop_tops{};
+ std::array loop_tops{};
-private:
bool DecodeNextOpcode(CheatVmOpcode& out);
void SkipConditionalBlock();
void ResetState();
- /* For implementing the DebugLog opcode. */
+ // For implementing the DebugLog opcode.
void DebugLog(u32 log_id, u64 value);
- /* For debugging. These will be IFDEF'd out normally. */
- template
- void LogToDebugFile(const char* format, const Args&... args) {
- callbacks->CommandLog(fmt::sprintf(format, args...));
- }
-
void LogOpcode(const CheatVmOpcode& opcode);
static u64 GetVmInt(VmInt value, u32 bit_width);
static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata,
MemoryAccessType mem_type, u64 rel_address);
-
-public:
- DmntCheatVm(std::unique_ptr callbacks) : callbacks(std::move(callbacks)) {}
-
- size_t GetProgramSize() {
- return this->num_opcodes;
- }
-
- bool LoadProgram(const std::vector& cheats);
- void Execute(const CheatProcessMetadata& metadata);
};
}; // namespace Memory
--
cgit v1.2.3
From 2bddc0346815bf5e893ad9611d89dbb0d511e32f Mon Sep 17 00:00:00 2001
From: Zach Hilman
Date: Sat, 21 Sep 2019 22:43:49 -0400
Subject: dmnt_cheat_vm: Default initialize structure values
---
src/core/memory/dmnt_cheat_types.h | 30 ++++----
src/core/memory/dmnt_cheat_vm.h | 144 ++++++++++++++++++-------------------
2 files changed, 87 insertions(+), 87 deletions(-)
(limited to 'src/core/memory')
diff --git a/src/core/memory/dmnt_cheat_types.h b/src/core/memory/dmnt_cheat_types.h
index aa1264c32..bf68fa0fe 100644
--- a/src/core/memory/dmnt_cheat_types.h
+++ b/src/core/memory/dmnt_cheat_types.h
@@ -29,30 +29,30 @@
namespace Memory {
struct MemoryRegionExtents {
- u64 base;
- u64 size;
+ u64 base{};
+ u64 size{};
};
struct CheatProcessMetadata {
- u64 process_id;
- u64 title_id;
- MemoryRegionExtents main_nso_extents;
- MemoryRegionExtents heap_extents;
- MemoryRegionExtents alias_extents;
- MemoryRegionExtents address_space_extents;
- std::array main_nso_build_id;
+ u64 process_id{};
+ u64 title_id{};
+ MemoryRegionExtents main_nso_extents{};
+ MemoryRegionExtents heap_extents{};
+ MemoryRegionExtents alias_extents{};
+ MemoryRegionExtents address_space_extents{};
+ std::array main_nso_build_id{};
};
struct CheatDefinition {
- std::array readable_name;
- u32 num_opcodes;
- std::array opcodes;
+ std::array readable_name{};
+ u32 num_opcodes{};
+ std::array opcodes{};
};
struct CheatEntry {
- bool enabled;
- u32 cheat_id;
- CheatDefinition definition;
+ bool enabled{};
+ u32 cheat_id{};
+ CheatDefinition definition{};
};
} // namespace Memory
diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h
index d1580d7f6..c36212cf1 100644
--- a/src/core/memory/dmnt_cheat_vm.h
+++ b/src/core/memory/dmnt_cheat_vm.h
@@ -136,131 +136,131 @@ union VmInt {
};
struct StoreStaticOpcode {
- u32 bit_width;
- MemoryAccessType mem_type;
- u32 offset_register;
- u64 rel_address;
- VmInt value;
+ u32 bit_width{};
+ MemoryAccessType mem_type{};
+ u32 offset_register{};
+ u64 rel_address{};
+ VmInt value{};
};
struct BeginConditionalOpcode {
- u32 bit_width;
- MemoryAccessType mem_type;
- ConditionalComparisonType cond_type;
- u64 rel_address;
- VmInt value;
+ u32 bit_width{};
+ MemoryAccessType mem_type{};
+ ConditionalComparisonType cond_type{};
+ u64 rel_address{};
+ VmInt value{};
};
struct EndConditionalOpcode {};
struct ControlLoopOpcode {
- bool start_loop;
- u32 reg_index;
- u32 num_iters;
+ bool start_loop{};
+ u32 reg_index{};
+ u32 num_iters{};
};
struct LoadRegisterStaticOpcode {
- u32 reg_index;
- u64 value;
+ u32 reg_index{};
+ u64 value{};
};
struct LoadRegisterMemoryOpcode {
- u32 bit_width;
- MemoryAccessType mem_type;
- u32 reg_index;
- bool load_from_reg;
- u64 rel_address;
+ u32 bit_width{};
+ MemoryAccessType mem_type{};
+ u32 reg_index{};
+ bool load_from_reg{};
+ u64 rel_address{};
};
struct StoreStaticToAddressOpcode {
- u32 bit_width;
- u32 reg_index;
- bool increment_reg;
- bool add_offset_reg;
- u32 offset_reg_index;
- u64 value;
+ u32 bit_width{};
+ u32 reg_index{};
+ bool increment_reg{};
+ bool add_offset_reg{};
+ u32 offset_reg_index{};
+ u64 value{};
};
struct PerformArithmeticStaticOpcode {
- u32 bit_width;
- u32 reg_index;
- RegisterArithmeticType math_type;
- u32 value;
+ u32 bit_width{};
+ u32 reg_index{};
+ RegisterArithmeticType math_type{};
+ u32 value{};
};
struct BeginKeypressConditionalOpcode {
- u32 key_mask;
+ u32 key_mask{};
};
struct PerformArithmeticRegisterOpcode {
- u32 bit_width;
- RegisterArithmeticType math_type;
- u32 dst_reg_index;
- u32 src_reg_1_index;
- u32 src_reg_2_index;
- bool has_immediate;
- VmInt value;
+ u32 bit_width{};
+ RegisterArithmeticType math_type{};
+ u32 dst_reg_index{};
+ u32 src_reg_1_index{};
+ u32 src_reg_2_index{};
+ bool has_immediate{};
+ VmInt value{};
};
struct StoreRegisterToAddressOpcode {
- u32 bit_width;
- u32 str_reg_index;
- u32 addr_reg_index;
- bool increment_reg;
- StoreRegisterOffsetType ofs_type;
- MemoryAccessType mem_type;
- u32 ofs_reg_index;
- u64 rel_address;
+ u32 bit_width{};
+ u32 str_reg_index{};
+ u32 addr_reg_index{};
+ bool increment_reg{};
+ StoreRegisterOffsetType ofs_type{};
+ MemoryAccessType mem_type{};
+ u32 ofs_reg_index{};
+ u64 rel_address{};
};
struct BeginRegisterConditionalOpcode {
- u32 bit_width;
- ConditionalComparisonType cond_type;
- u32 val_reg_index;
- CompareRegisterValueType comp_type;
- MemoryAccessType mem_type;
- u32 addr_reg_index;
- u32 other_reg_index;
- u32 ofs_reg_index;
- u64 rel_address;
- VmInt value;
+ u32 bit_width{};
+ ConditionalComparisonType cond_type{};
+ u32 val_reg_index{};
+ CompareRegisterValueType comp_type{};
+ MemoryAccessType mem_type{};
+ u32 addr_reg_index{};
+ u32 other_reg_index{};
+ u32 ofs_reg_index{};
+ u64 rel_address{};
+ VmInt value{};
};
struct SaveRestoreRegisterOpcode {
- u32 dst_index;
- u32 src_index;
- SaveRestoreRegisterOpType op_type;
+ u32 dst_index{};
+ u32 src_index{};
+ SaveRestoreRegisterOpType op_type{};
};
struct SaveRestoreRegisterMaskOpcode {
- SaveRestoreRegisterOpType op_type;
- std::array should_operate;
+ SaveRestoreRegisterOpType op_type{};
+ std::array should_operate{};
};
struct DebugLogOpcode {
- u32 bit_width;
- u32 log_id;
- DebugLogValueType val_type;
- MemoryAccessType mem_type;
- u32 addr_reg_index;
- u32 val_reg_index;
- u32 ofs_reg_index;
- u64 rel_address;
+ u32 bit_width{};
+ u32 log_id{};
+ DebugLogValueType val_type{};
+ MemoryAccessType mem_type{};
+ u32 addr_reg_index{};
+ u32 val_reg_index{};
+ u32 ofs_reg_index{};
+ u64 rel_address{};
};
struct UnrecognizedInstruction {
- CheatVmOpcodeType opcode;
+ CheatVmOpcodeType opcode{};
};
struct CheatVmOpcode {
- bool begin_conditional_block;
+ bool begin_conditional_block{};
std::variant
- opcode;
+ opcode{};
};
class DmntCheatVm {
--
cgit v1.2.3