From 4cb4707f34fb1753ebc33046be096ba38f987d6c Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 21 Jul 2021 19:36:46 -0700 Subject: [PATCH] dmnt: add theoretical 'else' support to cheat engine vm conditionals --- docs/features/cheats.md | 8 +++-- .../dmnt/source/cheat/impl/dmnt_cheat_vm.cpp | 36 +++++++++++-------- .../dmnt/source/cheat/impl/dmnt_cheat_vm.hpp | 6 ++-- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/docs/features/cheats.md b/docs/features/cheats.md index aaab58d57..f67236bba 100644 --- a/docs/features/cheats.md +++ b/docs/features/cheats.md @@ -59,7 +59,7 @@ Code type 0x0 allows writing a static value to a memory address. ### Code Type 0x1: Begin Conditional Block Code type 0x1 performs a comparison of the contents of memory to a static value. -If the condition is not met, all instructions until the appropriate conditional block terminator are skipped. +If the condition is not met, all instructions until the appropriate End or Else conditional block terminator are skipped. #### Encoding `1TMC00AA AAAAAAAA VVVVVVVV (VVVVVVVV)` @@ -83,8 +83,12 @@ If the condition is not met, all instructions until the appropriate conditional ### Code Type 0x2: End Conditional Block Code type 0x2 marks the end of a conditional block (started by Code Type 0x1 or Code Type 0x8). +When an Else is executed, all instructions until the appropriate End conditional block terminator are skipped. + #### Encoding -`20000000` +`2X000000` + ++ X: End type (0 = End, 1 = Else). --- diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp index 8d4241b7c..47933b2bf 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp @@ -387,8 +387,8 @@ namespace ams::dmnt::cheat::impl { break; case CheatVmOpcodeType_EndConditionalBlock: { - /* 20000000 */ - /* There's actually nothing left to process here! */ + /* 2X000000 */ + opcode.end_cond.is_else = ((first_dword >> 28) & 0xF) == 1; } break; case CheatVmOpcodeType_ControlLoop: @@ -668,7 +668,7 @@ namespace ams::dmnt::cheat::impl { return valid; } - void CheatVirtualMachine::SkipConditionalBlock() { + void CheatVirtualMachine::SkipConditionalBlock(bool is_if) { 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; @@ -685,15 +685,18 @@ namespace ams::dmnt::cheat::impl { if (skip_opcode.begin_conditional_block) { this->condition_depth++; } else if (skip_opcode.opcode == CheatVmOpcodeType_EndConditionalBlock) { - this->condition_depth--; + if (!skip_opcode.end_cond.is_else) { + this->condition_depth--; + } else if (is_if && this->condition_depth - 1 == desired_depth) { + /* An if will continue to an else at the same depth. */ + break; + } } } } 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. */ + /* This could occur with a mismatched "else" opcode, for example. */ R_ABORT_UNLESS(ResultVirtualMachineInvalidConditionDepth()); } } @@ -850,15 +853,20 @@ namespace ams::dmnt::cheat::impl { } /* Skip conditional block if condition not met. */ if (!cond_met) { - this->SkipConditionalBlock(); + this->SkipConditionalBlock(true); } } 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--; + if (cur_opcode.end_cond.is_else) { + /* Skip to the end of the conditional block. */ + this->SkipConditionalBlock(false); + } else { + /* 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: @@ -965,7 +973,7 @@ namespace ams::dmnt::cheat::impl { /* Check for keypress. */ if ((cur_opcode.begin_keypress_cond.key_mask & kHeld) != cur_opcode.begin_keypress_cond.key_mask) { /* Keys not pressed. Skip conditional block. */ - this->SkipConditionalBlock(); + this->SkipConditionalBlock(true); } break; case CheatVmOpcodeType_PerformArithmeticRegister: @@ -1164,7 +1172,7 @@ namespace ams::dmnt::cheat::impl { /* Skip conditional block if condition not met. */ if (!cond_met) { - this->SkipConditionalBlock(); + this->SkipConditionalBlock(true); } } break; diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.hpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.hpp index 3273c4700..736e3a334 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.hpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.hpp @@ -142,7 +142,9 @@ namespace ams::dmnt::cheat::impl { VmInt value; }; - struct EndConditionalOpcode {}; + struct EndConditionalOpcode { + bool is_else; + }; struct ControlLoopOpcode { bool start_loop; @@ -286,7 +288,7 @@ namespace ams::dmnt::cheat::impl { size_t loop_tops[NumRegisters] = {0}; private: bool DecodeNextOpcode(CheatVmOpcode *out); - void SkipConditionalBlock(); + void SkipConditionalBlock(bool is_if); void ResetState(); /* For implementing the DebugLog opcode. */