diff --git a/stratosphere/dmnt/source/dmnt_cheat_manager.cpp b/stratosphere/dmnt/source/dmnt_cheat_manager.cpp index 2653fc25f..bb24343f6 100644 --- a/stratosphere/dmnt/source/dmnt_cheat_manager.cpp +++ b/stratosphere/dmnt/source/dmnt_cheat_manager.cpp @@ -16,12 +16,14 @@ #include #include "dmnt_cheat_manager.hpp" +#include "dmnt_cheat_vm.hpp" #include "pm_shim.h" static HosMutex g_cheat_lock; static HosThread g_detect_thread, g_vm_thread; static IEvent *g_cheat_process_event; +static DmntCheatVm *g_cheat_vm; static CheatProcessMetadata g_cheat_process_metadata = {0}; static Handle g_cheat_process_debug_hnd = 0; @@ -189,9 +191,13 @@ void DmntCheatManager::VmThread(void *arg) { std::scoped_lock lk(g_cheat_lock); if (HasActiveCheatProcess()) { + /* Handle any pending debug events. */ ContinueCheatProcess(); - /* TODO: Execute VM. */ + /* Execute VM. */ + if (g_cheat_vm->GetProgramSize() != 0) { + g_cheat_vm->Execute(&g_cheat_process_metadata); + } } } svcSleepThread(0x5000000ul); @@ -224,6 +230,9 @@ void DmntCheatManager::InitializeCheatManager() { /* Create cheat process detection event. */ g_cheat_process_event = CreateWriteOnlySystemEvent(); + /* Create cheat vm. */ + g_cheat_vm = new DmntCheatVm(); + /* Spawn application detection thread, spawn cheat vm thread. */ if (R_FAILED(g_detect_thread.Initialize(&DmntCheatManager::DetectThread, nullptr, 0x4000, 28))) { std::abort(); diff --git a/stratosphere/dmnt/source/dmnt_cheat_service.cpp b/stratosphere/dmnt/source/dmnt_cheat_service.cpp index f2088acf7..b67422344 100644 --- a/stratosphere/dmnt/source/dmnt_cheat_service.cpp +++ b/stratosphere/dmnt/source/dmnt_cheat_service.cpp @@ -72,7 +72,7 @@ Result DmntCheatService::ToggleCheat(u32 cheat_id) { return 0xF601; } -Result DmntCheatService::AddCheat(InBuffer cheat) { +Result DmntCheatService::AddCheat(InBuffer cheat, Out out_cheat_id, bool enabled) { /* TODO */ return 0xF601; } diff --git a/stratosphere/dmnt/source/dmnt_cheat_vm.cpp b/stratosphere/dmnt/source/dmnt_cheat_vm.cpp new file mode 100644 index 000000000..1264562df --- /dev/null +++ b/stratosphere/dmnt/source/dmnt_cheat_vm.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2018 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 . + */ + +#include +#include "dmnt_cheat_types.hpp" +#include "dmnt_cheat_vm.hpp" + +bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode *out) { + /* TODO: Parse opcodes */ + return false; +} + +void DmntCheatVm::SkipConditionalBlock() { + CheatVmOpcode skip_opcode; + while (this->DecodeNextOpcode(&skip_opcode)) { + /* Decode instructions until we see end of 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... */ + if (skip_opcode.opcode == CheatVmOpcodeType_EndConditionalBlock) { + break; + } + } +} + +void DmntCheatVm::Execute(const CheatProcessMetadata *metadata) { + CheatVmOpcode cur_opcode; + u64 kDown = 0; + + /* TODO: Get Keys down. */ + + /* Clear VM state. */ + for (size_t i = 0; i < DmntCheatVm::NumRegisters; i++) { + this->registers[i] = 0; + this->loop_tops[i] = 0; + } + + /* Loop until program finishes. */ + while (this->DecodeNextOpcode(&cur_opcode)) { + switch (cur_opcode.opcode) { + case CheatVmOpcodeType_StoreStatic: + { + /* TODO */ + } + break; + case CheatVmOpcodeType_BeginConditionalBlock: + { + /* TODO */ + } + break; + case CheatVmOpcodeType_EndConditionalBlock: + /* There is nothing to do here. Just move on to the next instruction. */ + break; + case CheatVmOpcodeType_ControlLoop: + if (cur_opcode.ctrl_loop.start_loop) { + /* Start a loop. */ + this->registers[cur_opcode.ctrl_loop.register_index] = cur_opcode.ctrl_loop.num_iters; + this->loop_tops[cur_opcode.ctrl_loop.register_index] = this->instruction_ptr; + } else { + /* End a loop. */ + this->registers[cur_opcode.ctrl_loop.register_index]--; + if (this->registers[cur_opcode.ctrl_loop.register_index] != 0) { + this->instruction_ptr = this->loop_tops[cur_opcode.ctrl_loop.register_index]; + } + } + break; + case CheatVmOpcodeType_LoadRegisterStatic: + /* Set a register to a static value. */ + this->registers[cur_opcode.ldr_static.register_index] = cur_opcode.ldr_static.value; + break; + case CheatVmOpcodeType_LoadRegisterMemory: + { + /* TODO */ + } + break; + case CheatVmOpcodeType_StoreToRegisterAddress: + { + /* TODO */ + } + break; + case CheatVmOpcodeType_PerformArithmetic: + { + /* TODO */ + } + 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; + } + } +} \ No newline at end of file diff --git a/stratosphere/dmnt/source/dmnt_cheat_vm.hpp b/stratosphere/dmnt/source/dmnt_cheat_vm.hpp new file mode 100644 index 000000000..869097040 --- /dev/null +++ b/stratosphere/dmnt/source/dmnt_cheat_vm.hpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2018 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 . + */ + +#pragma once +#include +#include + +#include "dmnt_cheat_types.hpp" + +enum CheatVmOpcodeType : u32 { + CheatVmOpcodeType_StoreStatic = 0, + CheatVmOpcodeType_BeginConditionalBlock = 1, + CheatVmOpcodeType_EndConditionalBlock = 2, + CheatVmOpcodeType_ControlLoop = 3, + CheatVmOpcodeType_LoadRegisterStatic = 4, + CheatVmOpcodeType_LoadRegisterMemory = 5, + CheatVmOpcodeType_StoreToRegisterAddress = 6, + CheatVmOpcodeType_PerformArithmetic = 7, + CheatVmOpcodeType_BeginKeypressConditionalBlock = 8, +}; + +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, +}; + +union VmInt { + u8 bit8; + u16 bit16; + u32 bit32; + u64 bit64; +}; + +struct StoreStaticOpcode { + u32 bit_width; + MemoryAccessType mem_type; + u32 offset_register; + u64 relative_address; + VmInt value; +}; + +struct BeginConditionalOpcode { + u32 bit_width; + MemoryAccessType mem_type; + ConditionalComparisonType cond_type; + u64 relative_address; + VmInt value; +}; + +struct EndConditionalOpcode {}; + +struct ControlLoopOpcode { + bool start_loop; + u32 register_index; + u32 num_iters; +}; + +struct LoadRegisterStaticOpcode { + u32 register_index; + u64 value; +}; + +struct LoadRegisterMemoryOpcode { + u32 bit_width; + MemoryAccessType mem_type; + u32 reg_index; + bool load_from_reg; + u64 relative_address; +}; + +struct StoreToRegisterAddressOpcode { + u32 bit_width; + u32 reg_index; + bool increment_reg; + bool add_offset_reg; + u32 offset_reg_index; + u64 value; +}; + +struct PerformArithmeticOpcode { + u32 bit_width; + u32 reg_index; + RegisterArithmeticType math_type; + VmInt value; +}; + +struct BeginKeypressConditionalOpcode { + u32 key_mask; +}; + +struct CheatVmOpcode { + CheatVmOpcodeType opcode; + union { + StoreStaticOpcode store_static; + BeginConditionalOpcode begin_cond; + EndConditionalOpcode end_cond; + ControlLoopOpcode ctrl_loop; + LoadRegisterStaticOpcode ldr_static; + LoadRegisterMemoryOpcode ldr_memory; + StoreToRegisterAddressOpcode str_regaddr; + PerformArithmeticOpcode perform_math; + BeginKeypressConditionalOpcode begin_keypress_cond; + }; +}; + +class DmntCheatVm { + public: + constexpr static size_t MaximumProgramOpcodeCount = 0x400; + constexpr static size_t NumRegisters = 0x10; + private: + size_t num_opcodes = 0; + size_t instruction_ptr = 0; + u32 program[MaximumProgramOpcodeCount] = {0}; + u64 registers[NumRegisters] = {0}; + size_t loop_tops[NumRegisters] = {0}; + private: + bool DecodeNextOpcode(CheatVmOpcode *out); + void SkipConditionalBlock(); + public: + DmntCheatVm() { } + + size_t GetProgramSize() { + return this->num_opcodes; + } + + void Execute(const CheatProcessMetadata *metadata); +};