mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-23 04:12:02 +00:00
dmnt-cheat: Support nested conditionals in VM
This commit is contained in:
parent
8c86074da2
commit
853a57e4d4
3 changed files with 52 additions and 11 deletions
|
@ -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,16 +368,32 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode *out) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DmntCheatVm::SkipConditionalBlock() {
|
void DmntCheatVm::SkipConditionalBlock() {
|
||||||
CheatVmOpcode skip_opcode;
|
if (this->condition_depth > 0) {
|
||||||
while (this->DecodeNextOpcode(&skip_opcode)) {
|
/* We want to continue until we're out of the current block. */
|
||||||
/* Decode instructions until we see end of conditional block. */
|
size_t desired_depth = this->condition_depth - 1;
|
||||||
/* NOTE: This is broken in gateway's implementation. */
|
|
||||||
/* Gateway currently checks for "0x2" instead of "0x20000000" */
|
CheatVmOpcode skip_opcode;
|
||||||
/* In addition, they do a linear scan instead of correctly decoding opcodes. */
|
while (this->DecodeNextOpcode(&skip_opcode) && this->condition_depth > desired_depth) {
|
||||||
/* This causes issues if "0x2" appears as an immediate in the conditional block... */
|
/* Decode instructions until we see end of the current conditional block. */
|
||||||
if (skip_opcode.opcode == CheatVmOpcodeType_EndConditionalBlock) {
|
/* NOTE: This is broken in gateway's implementation. */
|
||||||
break;
|
/* 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. */
|
||||||
|
fatalSimple(ResultDmntCheatVmInvalidCondDepth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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);
|
Loading…
Reference in a new issue