dmnt: add theoretical 'else' support to cheat engine vm conditionals

This commit is contained in:
Michael Scire 2021-07-21 19:36:46 -07:00
parent 389c3b6baa
commit 4cb4707f34
3 changed files with 32 additions and 18 deletions

View file

@ -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: Begin Conditional Block
Code type 0x1 performs a comparison of the contents of memory to a static value. 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 #### Encoding
`1TMC00AA AAAAAAAA VVVVVVVV (VVVVVVVV)` `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: End Conditional Block
Code type 0x2 marks the end of a conditional block (started by Code Type 0x1 or Code Type 0x8). 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 #### Encoding
`20000000` `2X000000`
+ X: End type (0 = End, 1 = Else).
--- ---

View file

@ -387,8 +387,8 @@ namespace ams::dmnt::cheat::impl {
break; break;
case CheatVmOpcodeType_EndConditionalBlock: case CheatVmOpcodeType_EndConditionalBlock:
{ {
/* 20000000 */ /* 2X000000 */
/* There's actually nothing left to process here! */ opcode.end_cond.is_else = ((first_dword >> 28) & 0xF) == 1;
} }
break; break;
case CheatVmOpcodeType_ControlLoop: case CheatVmOpcodeType_ControlLoop:
@ -668,7 +668,7 @@ namespace ams::dmnt::cheat::impl {
return valid; return valid;
} }
void CheatVirtualMachine::SkipConditionalBlock() { void CheatVirtualMachine::SkipConditionalBlock(bool is_if) {
if (this->condition_depth > 0) { if (this->condition_depth > 0) {
/* We want to continue until we're out of the current block. */ /* We want to continue until we're out of the current block. */
const size_t desired_depth = this->condition_depth - 1; const size_t desired_depth = this->condition_depth - 1;
@ -685,15 +685,18 @@ namespace ams::dmnt::cheat::impl {
if (skip_opcode.begin_conditional_block) { if (skip_opcode.begin_conditional_block) {
this->condition_depth++; this->condition_depth++;
} else if (skip_opcode.opcode == CheatVmOpcodeType_EndConditionalBlock) { } 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 { } else {
/* Skipping, but this->condition_depth = 0. */ /* Skipping, but this->condition_depth = 0. */
/* This is an error condition. */ /* This is an error condition. */
/* However, I don't actually believe it is possible for this to happen. */ /* This could occur with a mismatched "else" opcode, for example. */
/* 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. */
R_ABORT_UNLESS(ResultVirtualMachineInvalidConditionDepth()); R_ABORT_UNLESS(ResultVirtualMachineInvalidConditionDepth());
} }
} }
@ -850,15 +853,20 @@ namespace ams::dmnt::cheat::impl {
} }
/* Skip conditional block if condition not met. */ /* Skip conditional block if condition not met. */
if (!cond_met) { if (!cond_met) {
this->SkipConditionalBlock(); this->SkipConditionalBlock(true);
} }
} }
break; break;
case CheatVmOpcodeType_EndConditionalBlock: case CheatVmOpcodeType_EndConditionalBlock:
/* Decrement the condition depth. */ if (cur_opcode.end_cond.is_else) {
/* We will assume, graciously, that mismatched conditional block ends are a nop. */ /* Skip to the end of the conditional block. */
if (this->condition_depth > 0) { this->SkipConditionalBlock(false);
this->condition_depth--; } 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; break;
case CheatVmOpcodeType_ControlLoop: case CheatVmOpcodeType_ControlLoop:
@ -965,7 +973,7 @@ namespace ams::dmnt::cheat::impl {
/* Check for keypress. */ /* Check for keypress. */
if ((cur_opcode.begin_keypress_cond.key_mask & kHeld) != cur_opcode.begin_keypress_cond.key_mask) { if ((cur_opcode.begin_keypress_cond.key_mask & kHeld) != cur_opcode.begin_keypress_cond.key_mask) {
/* Keys not pressed. Skip conditional block. */ /* Keys not pressed. Skip conditional block. */
this->SkipConditionalBlock(); this->SkipConditionalBlock(true);
} }
break; break;
case CheatVmOpcodeType_PerformArithmeticRegister: case CheatVmOpcodeType_PerformArithmeticRegister:
@ -1164,7 +1172,7 @@ namespace ams::dmnt::cheat::impl {
/* Skip conditional block if condition not met. */ /* Skip conditional block if condition not met. */
if (!cond_met) { if (!cond_met) {
this->SkipConditionalBlock(); this->SkipConditionalBlock(true);
} }
} }
break; break;

View file

@ -142,7 +142,9 @@ namespace ams::dmnt::cheat::impl {
VmInt value; VmInt value;
}; };
struct EndConditionalOpcode {}; struct EndConditionalOpcode {
bool is_else;
};
struct ControlLoopOpcode { struct ControlLoopOpcode {
bool start_loop; bool start_loop;
@ -286,7 +288,7 @@ namespace ams::dmnt::cheat::impl {
size_t loop_tops[NumRegisters] = {0}; size_t loop_tops[NumRegisters] = {0};
private: private:
bool DecodeNextOpcode(CheatVmOpcode *out); bool DecodeNextOpcode(CheatVmOpcode *out);
void SkipConditionalBlock(); void SkipConditionalBlock(bool is_if);
void ResetState(); void ResetState();
/* For implementing the DebugLog opcode. */ /* For implementing the DebugLog opcode. */