diff --git a/docs/features/cheats.md b/docs/features/cheats.md index f67236bba..d5d23f92a 100644 --- a/docs/features/cheats.md +++ b/docs/features/cheats.md @@ -49,7 +49,7 @@ Code type 0x0 allows writing a static value to a memory address. `0TMR00AA AAAAAAAA VVVVVVVV (VVVVVVVV)` + T: Width of memory write (1, 2, 4, or 8 bytes). -+ M: Memory region to write to (0 = Main NSO, 1 = Heap, 2 = Alias, 3 = Aslr). ++ M: Memory region to write to (0 = Main NSO, 1 = Heap, 2 = Alias, 3 = Aslr, 4 = none). + R: Register to use as an offset from memory region base. + A: Immediate offset to use from memory region base. + V: Value to write. @@ -137,6 +137,22 @@ Code type 0x5 allows loading a value from memory into a register, either using a + R: Register to load value into. (This register is also used as the base memory address). + A: Immediate offset to use from register R. +#### Load from Register Address Encoding +`5T0R2SAA AAAAAAAA` + ++ T: Width of memory read (1, 2, 4, or 8 bytes). ++ R: Register to load value into. ++ S: Register to use as the base memory address. ++ A: Immediate offset to use from register R. + +#### Load From Fixed Address Encoding with offset register +`5TMR3SAA AAAAAAAA` + ++ T: Width of memory read (1, 2, 4, or 8 bytes). ++ M: Memory region to write to (0 = Main NSO, 1 = Heap, 2 = Alias, 3 = Aslr). ++ R: Register to load value into. ++ S: Register to use as offset register. ++ A: Immediate offset to use from memory region base. --- ### Code Type 0x6: Store Static Value to Register Memory Address @@ -215,6 +231,7 @@ Note: This is the direct output of `hidKeysDown()`. + 0800000: Right Stick Down + 1000000: SL + 2000000: SR ++ 8000000: when this is set button only activate code once per keydown, need to be release before the code will run again --- @@ -250,6 +267,10 @@ Code type 0x9 allows performing arithmetic on registers. + 7: Logical Not (discards right-hand operand) + 8: Logical Xor + 9: None/Move (discards right-hand operand) ++ 10: Float Addition, Width force to 4 bytes ++ 11: Float Multiplication, Width force to 4 bytes ++ 12: Double Addition, Width force to 8 bytes ++ 13: Double Multiplication, Width force to 8 bytes --- @@ -303,6 +324,7 @@ C0TcS2Ra aaaaaaaa C0TcS3Rr C0TcS400 VVVVVVVV (VVVVVVVV) C0TcS5X0 +C0Tcr6Ma aaaaaaaa VVVVVVVV (VVVVVVVV) ``` + T: Width of memory write (1, 2, 4, or 8 bytes). @@ -323,6 +345,7 @@ C0TcS5X0 + 3: Register + Offset Register + 4: Static Value + 5: Other Register ++ 6: Compare [Memory Base + Offset Register + Relative Offset] against Static Value #### Conditions + 1: > diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp index e553efd0b..0131e0034 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.cpp @@ -228,6 +228,13 @@ namespace ams::dmnt::cheat::impl { this->LogToDebugFile("A Reg Idx: %x\n", opcode->begin_reg_cond.addr_reg_index); this->LogToDebugFile("O Reg Idx: %x\n", opcode->begin_reg_cond.ofs_reg_index); break; + case CompareRegisterValueType_OffsetValue: + this->LogToDebugFile("Comp Type: Offset Value\n"); + this->LogToDebugFile("Mem Type: %x\n", opcode->begin_reg_cond.mem_type); + this->LogToDebugFile("O Reg Idx: %x\n", opcode->begin_reg_cond.ofs_reg_index); + this->LogToDebugFile("Rel Addr: %lx\n", opcode->begin_reg_cond.rel_address); + this->LogToDebugFile("Value: %lx\n", opcode->begin_reg_cond.value.bit64); + break; } break; case CheatVmOpcodeType_SaveRestoreRegister: @@ -427,7 +434,8 @@ namespace ams::dmnt::cheat::impl { opcode.ldr_memory.bit_width = (first_dword >> 24) & 0xF; opcode.ldr_memory.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF); opcode.ldr_memory.reg_index = ((first_dword >> 16) & 0xF); - opcode.ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0; + opcode.ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF); + opcode.ldr_memory.offset_register = ((first_dword >> 8) & 0xF); opcode.ldr_memory.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword); } break; @@ -525,6 +533,7 @@ namespace ams::dmnt::cheat::impl { /* C0TcS3Rr */ /* C0TcS400 VVVVVVVV (VVVVVVVV) */ /* C0TcS5X0 */ + /* C0Tcr6Ma aaaaaaaa VVVVVVVV (VVVVVVVV) */ /* C0 = opcode 0xC0 */ /* T = bit width */ /* c = condition type. */ @@ -565,6 +574,12 @@ namespace ams::dmnt::cheat::impl { opcode.begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); opcode.begin_reg_cond.ofs_reg_index = (first_dword & 0xF); break; + case CompareRegisterValueType_OffsetValue: + opcode.begin_reg_cond.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF); + opcode.begin_reg_cond.rel_address = (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword())); + opcode.begin_reg_cond.ofs_reg_index = ((first_dword >> 12) & 0xF); + opcode.begin_reg_cond.value = GetNextVmInt(opcode.begin_reg_cond.bit_width); + break; } } break; @@ -734,6 +749,8 @@ namespace ams::dmnt::cheat::impl { return metadata->alias_extents.base + rel_address; case MemoryAccessType_Aslr: return metadata->aslr_extents.base + rel_address; + case MemoryAccessType_Blank: + return rel_address; } } @@ -769,6 +786,7 @@ namespace ams::dmnt::cheat::impl { return true; } + static u64 keyold = 0; void CheatVirtualMachine::Execute(const CheatProcessMetadata *metadata) { CheatVmOpcode cur_opcode; u64 kHeld = 0; @@ -896,8 +914,12 @@ namespace ams::dmnt::cheat::impl { { /* Choose source address. */ u64 src_address; - if (cur_opcode.ldr_memory.load_from_reg) { + if (cur_opcode.ldr_memory.load_from_reg == 1) { src_address = m_registers[cur_opcode.ldr_memory.reg_index] + cur_opcode.ldr_memory.rel_address; + } else if (cur_opcode.ldr_memory.load_from_reg == 2) { + src_address = m_registers[cur_opcode.ldr_memory.offset_register] + cur_opcode.ldr_memory.rel_address; + } else if (cur_opcode.ldr_memory.load_from_reg == 3) { + src_address = GetCheatProcessAddress(metadata, cur_opcode.ldr_memory.mem_type, m_registers[cur_opcode.ldr_memory.offset_register] + cur_opcode.ldr_memory.rel_address); } else { src_address = GetCheatProcessAddress(metadata, cur_opcode.ldr_memory.mem_type, cur_opcode.ldr_memory.rel_address); } @@ -977,7 +999,14 @@ namespace ams::dmnt::cheat::impl { break; case CheatVmOpcodeType_BeginKeypressConditionalBlock: /* 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 > 0x8000000) { + const u64 kDown = ~keyold & kHeld; + + if ((cur_opcode.begin_keypress_cond.key_mask & 0x7FFFFFF & kDown) != (cur_opcode.begin_keypress_cond.key_mask & 0x7FFFFFF)) { + /* Keys not pressed. Skip conditional block. */ + this->SkipConditionalBlock(true); + } + } else if ((cur_opcode.begin_keypress_cond.key_mask & kHeld) != cur_opcode.begin_keypress_cond.key_mask) { /* Keys not pressed. Skip conditional block. */ this->SkipConditionalBlock(true); } @@ -1022,6 +1051,22 @@ namespace ams::dmnt::cheat::impl { case RegisterArithmeticType_None: res_val = operand_1_value; break; + case RegisterArithmeticType_FloatAddition: { + cur_opcode.perform_math_reg.bit_width = 4; + *(float *)&res_val = *(float *)&operand_1_value + *(float *)&operand_2_value; + } break; + case RegisterArithmeticType_FloatMultiplication: { + cur_opcode.perform_math_reg.bit_width = 4; + *(float *)&res_val = *(float *)&operand_1_value * *(float *)&operand_2_value; + } break; + case RegisterArithmeticType_DoubleAddition: { + cur_opcode.perform_math_reg.bit_width = 8; + *(double *)&res_val = *(double *)&operand_1_value + *(double *)&operand_2_value; + } break; + case RegisterArithmeticType_DoubleMultiplication: { + cur_opcode.perform_math_reg.bit_width = 8; + *(double *)&res_val = *(double *)&operand_1_value * *(double *)&operand_2_value; + } break; } @@ -1140,6 +1185,10 @@ namespace ams::dmnt::cheat::impl { case CompareRegisterValueType_RegisterOfsReg: cond_address = m_registers[cur_opcode.begin_reg_cond.addr_reg_index] + m_registers[cur_opcode.begin_reg_cond.ofs_reg_index]; break; + case CompareRegisterValueType_OffsetValue: + cond_address = GetCheatProcessAddress(metadata, cur_opcode.begin_reg_cond.mem_type, cur_opcode.begin_reg_cond.rel_address + m_registers[cur_opcode.begin_reg_cond.ofs_reg_index]); + src_value = GetVmInt(cur_opcode.begin_reg_cond.value, cur_opcode.begin_reg_cond.bit_width); + break; default: break; } @@ -1304,6 +1353,7 @@ namespace ams::dmnt::cheat::impl { break; } } + keyold = kHeld; } } diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.hpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.hpp index 0eb47b2a2..c659dffdf 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.hpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_vm.hpp @@ -59,6 +59,7 @@ namespace ams::dmnt::cheat::impl { MemoryAccessType_Heap = 1, MemoryAccessType_Alias = 2, MemoryAccessType_Aslr = 3, + MemoryAccessType_Blank = 4, }; enum ConditionalComparisonType : u32 { @@ -84,6 +85,10 @@ namespace ams::dmnt::cheat::impl { RegisterArithmeticType_LogicalXor = 8, RegisterArithmeticType_None = 9, + RegisterArithmeticType_FloatAddition = 10, + RegisterArithmeticType_FloatMultiplication = 11, + RegisterArithmeticType_DoubleAddition = 12, + RegisterArithmeticType_DoubleMultiplication = 13, }; enum StoreRegisterOffsetType : u32 { @@ -102,6 +107,7 @@ namespace ams::dmnt::cheat::impl { CompareRegisterValueType_RegisterOfsReg = 3, CompareRegisterValueType_StaticValue = 4, CompareRegisterValueType_OtherRegister = 5, + CompareRegisterValueType_OffsetValue = 6, }; enum SaveRestoreRegisterOpType : u32 { @@ -161,7 +167,8 @@ namespace ams::dmnt::cheat::impl { u32 bit_width; MemoryAccessType mem_type; u32 reg_index; - bool load_from_reg; + u8 load_from_reg; + u8 offset_register; u64 rel_address; };