dmnt-cheat: Support nested conditionals in VM

This commit is contained in:
Michael Scire 2019-03-05 01:39:20 -08:00
parent 8c86074da2
commit 853a57e4d4
3 changed files with 52 additions and 11 deletions

View file

@ -204,6 +204,17 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode *out) {
opcode.opcode = (CheatVmOpcodeType)((((u32)opcode.opcode) << 4) | ((first_dword >> 24) & 0xF)); opcode.opcode = (CheatVmOpcodeType)((((u32)opcode.opcode) << 4) | ((first_dword >> 24) & 0xF));
} }
/* detect condition start. */
switch (opcode.opcode) {
case CheatVmOpcodeType_BeginConditionalBlock:
case CheatVmOpcodeType_BeginKeypressConditionalBlock:
opcode.begin_conditional_block = true;
break;
default:
opcode.begin_conditional_block = false;
break;
}
switch (opcode.opcode) { switch (opcode.opcode) {
case CheatVmOpcodeType_StoreStatic: case CheatVmOpcodeType_StoreStatic:
{ {
@ -357,17 +368,33 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode *out) {
} }
void DmntCheatVm::SkipConditionalBlock() { void DmntCheatVm::SkipConditionalBlock() {
if (this->condition_depth > 0) {
/* We want to continue until we're out of the current block. */
size_t desired_depth = this->condition_depth - 1;
CheatVmOpcode skip_opcode; CheatVmOpcode skip_opcode;
while (this->DecodeNextOpcode(&skip_opcode)) { while (this->DecodeNextOpcode(&skip_opcode) && this->condition_depth > desired_depth) {
/* Decode instructions until we see end of conditional block. */ /* Decode instructions until we see end of the current conditional block. */
/* NOTE: This is broken in gateway's implementation. */ /* NOTE: This is broken in gateway's implementation. */
/* Gateway currently checks for "0x2" instead of "0x20000000" */ /* Gateway currently checks for "0x2" instead of "0x20000000" */
/* In addition, they do a linear scan instead of correctly decoding opcodes. */ /* 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... */ /* This causes issues if "0x2" appears as an immediate in the conditional block... */
if (skip_opcode.opcode == CheatVmOpcodeType_EndConditionalBlock) {
break; /* 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. */
fatalSimple(ResultDmntCheatVmInvalidCondDepth);
}
} }
u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) { u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) {
@ -402,6 +429,7 @@ void DmntCheatVm::ResetState() {
this->loop_tops[i] = 0; this->loop_tops[i] = 0;
} }
this->instruction_ptr = 0; this->instruction_ptr = 0;
this->condition_depth = 0;
this->decode_success = true; this->decode_success = true;
} }
@ -452,6 +480,11 @@ void DmntCheatVm::Execute(const CheatProcessMetadata *metadata) {
} }
this->LogOpcode(&cur_opcode); this->LogOpcode(&cur_opcode);
/* Increment conditional depth, if relevant. */
if (cur_opcode.begin_conditional_block) {
this->condition_depth++;
}
switch (cur_opcode.opcode) { switch (cur_opcode.opcode) {
case CheatVmOpcodeType_StoreStatic: case CheatVmOpcodeType_StoreStatic:
{ {
@ -511,7 +544,11 @@ void DmntCheatVm::Execute(const CheatProcessMetadata *metadata) {
} }
break; break;
case CheatVmOpcodeType_EndConditionalBlock: case CheatVmOpcodeType_EndConditionalBlock:
/* There is nothing to do here. Just move on to the next instruction. */ /* 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:
if (cur_opcode.ctrl_loop.start_loop) { if (cur_opcode.ctrl_loop.start_loop) {

View file

@ -164,6 +164,7 @@ struct StoreRegisterToAddressOpcode {
struct CheatVmOpcode { struct CheatVmOpcode {
CheatVmOpcodeType opcode; CheatVmOpcodeType opcode;
bool begin_conditional_block;
union { union {
StoreStaticOpcode store_static; StoreStaticOpcode store_static;
BeginConditionalOpcode begin_cond; BeginConditionalOpcode begin_cond;
@ -186,6 +187,7 @@ class DmntCheatVm {
private: private:
size_t num_opcodes = 0; size_t num_opcodes = 0;
size_t instruction_ptr = 0; size_t instruction_ptr = 0;
size_t condition_depth = 0;
bool decode_success = false; bool decode_success = false;
u32 program[MaximumProgramOpcodeCount] = {0}; u32 program[MaximumProgramOpcodeCount] = {0};
u64 registers[NumRegisters] = {0}; u64 registers[NumRegisters] = {0};

View file

@ -34,3 +34,5 @@ static constexpr Result ResultDmntCheatInvalidFreezeWidth = MAKERESULT(Modul
static constexpr Result ResultDmntCheatAddressAlreadyFrozen = MAKERESULT(Module_Dmnt, 6601); static constexpr Result ResultDmntCheatAddressAlreadyFrozen = MAKERESULT(Module_Dmnt, 6601);
static constexpr Result ResultDmntCheatAddressNotFrozen = MAKERESULT(Module_Dmnt, 6602); static constexpr Result ResultDmntCheatAddressNotFrozen = MAKERESULT(Module_Dmnt, 6602);
static constexpr Result ResultDmntCheatTooManyFrozenAddresses = MAKERESULT(Module_Dmnt, 6603); static constexpr Result ResultDmntCheatTooManyFrozenAddresses = MAKERESULT(Module_Dmnt, 6603);
static constexpr Result ResultDmntCheatVmInvalidCondDepth = MAKERESULT(Module_Dmnt, 6700);