mirror of
https://github.com/LNH-team/pico-loader.git
synced 2026-04-13 15:12:43 +02:00
Preprocess cheats to get rid of hacks that try to modify the cheat engine, add support for C2 cheat opcode
This commit is contained in:
80
arm7/source/loader/CheatPreprocessor.cpp
Normal file
80
arm7/source/loader/CheatPreprocessor.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "common.h"
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <span>
|
||||
#include "CheatPreprocessor.h"
|
||||
|
||||
// Based on original code by edo9300: https://github.com/edo9300/nitrohax-usercheat-supercard/blob/master/arm9/source/cheat.cpp.
|
||||
|
||||
static constexpr u64 NOP = 0x00000000D4000000;
|
||||
static constexpr u64 D4_ORR_HACK = 0xE1833004023FE424;
|
||||
static constexpr u64 D4_AND_HACK = 0xE0033004023FE424;
|
||||
static constexpr u64 D4_ADD_HACK = 0xE0833004023FE424;
|
||||
static constexpr u64 DB_CODE_FIX_HACK = 0x0A000003023FE4D8;
|
||||
|
||||
// Make 0x0E handler execute the data as arm
|
||||
static constexpr u64 ASM_HACK_START = 0x012FFF11023FE074;
|
||||
// Restore behaviour of 0x0E handler
|
||||
static constexpr u64 ASM_HACK_END = 0xE3520003023FE074;
|
||||
|
||||
void CheatPreprocessor::PreprocessCheat(pload_cheat_t* cheat) const
|
||||
{
|
||||
PatchD4Hack(cheat, D4_ORR_HACK, 0x1);
|
||||
PatchD4Hack(cheat, D4_AND_HACK, 0x2);
|
||||
PatchD4Hack(cheat, D4_ADD_HACK, 0x0);
|
||||
PatchDBFix(cheat);
|
||||
PatchAsmHack(cheat);
|
||||
}
|
||||
|
||||
void CheatPreprocessor::PatchD4Hack(pload_cheat_t* cheat, u64 hackPattern, u8 patchedInstructionFlag) const
|
||||
{
|
||||
std::span<u64> u64Data((u64*)cheat->opcodes, cheat->length / sizeof(u64));
|
||||
auto start = std::ranges::find(u64Data, hackPattern);
|
||||
if (start != u64Data.end())
|
||||
{
|
||||
*start++ = NOP;
|
||||
auto end = std::find(start, u64Data.end(), D4_ADD_HACK);
|
||||
auto it = std::find_if(start, end, [] (auto elem) { return (elem & 0xFFFFFFFF) == 0xD4000000; });
|
||||
if (it != end)
|
||||
{
|
||||
// Replace 0xD4 code with nitrohax's custom operator code
|
||||
*it |= patchedInstructionFlag;
|
||||
}
|
||||
if (end != u64Data.end())
|
||||
{
|
||||
*end++ = NOP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheatPreprocessor::PatchDBFix(pload_cheat_t* cheat) const
|
||||
{
|
||||
std::span<u64> u64Data((u64*)cheat->opcodes, cheat->length / sizeof(u64));
|
||||
auto start = std::ranges::find(u64Data, DB_CODE_FIX_HACK);
|
||||
if (start != u64Data.end())
|
||||
{
|
||||
*start++ = NOP;
|
||||
}
|
||||
}
|
||||
|
||||
void CheatPreprocessor::PatchAsmHack(pload_cheat_t* cheat) const
|
||||
{
|
||||
// Make the asm cheats designed for ards work with nitrohax
|
||||
std::span<u64> u64Data((u64*)cheat->opcodes, cheat->length / sizeof(u64));
|
||||
auto start = std::ranges::find(u64Data, ASM_HACK_START);
|
||||
if (start != u64Data.end())
|
||||
{
|
||||
*start++ = NOP;
|
||||
auto end = std::find(start, u64Data.end(), ASM_HACK_END);
|
||||
auto it = std::find_if(start, end, [] (auto elem) { return (elem & 0xFFFFFFFF) == 0xE0000000; });
|
||||
if (it != end)
|
||||
{
|
||||
// Replace 0x0E code with nitrohax's 0xC2 in arm mode (0xXXXXXXXXE0000000 - 0xXXXXXXXX1E000000 = 0xXXXXXXXXC2000000)
|
||||
*it -= 0x1E000000;
|
||||
}
|
||||
if (end != u64Data.end())
|
||||
{
|
||||
*end++ = NOP;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
arm7/source/loader/CheatPreprocessor.h
Normal file
16
arm7/source/loader/CheatPreprocessor.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "../../include/picoLoader7.h"
|
||||
|
||||
/// @brief Class for preprocessing cheats to get rid of hacks that try to modify the cheat engine.
|
||||
class CheatPreprocessor
|
||||
{
|
||||
public:
|
||||
/// @brief Preprocesses the given \p cheat.
|
||||
/// @param cheat The cheat to preprocess.
|
||||
void PreprocessCheat(pload_cheat_t* cheat) const;
|
||||
|
||||
private:
|
||||
void PatchD4Hack(pload_cheat_t* cheat, u64 hackPattern, u8 patchedInstructionFlag) const;
|
||||
void PatchDBFix(pload_cheat_t* cheat) const;
|
||||
void PatchAsmHack(pload_cheat_t* cheat) const;
|
||||
};
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "DSMode.h"
|
||||
#include "Arm7IoRegisterClearer.h"
|
||||
#include "PatchListFactory.h"
|
||||
#include "CheatPreprocessor.h"
|
||||
#include "NdsLoader.h"
|
||||
|
||||
#define AP_LIST_PATH "/_pico/aplist.bin"
|
||||
@@ -523,6 +524,7 @@ void NdsLoader::ApplyArm7Patches()
|
||||
{
|
||||
sendToArm9(IPC_COMMAND_ARM9_APPLY_ARM7_PATCHES);
|
||||
sendToArm9(_cheats ? _cheats->length : 0);
|
||||
PreprocessCheats();
|
||||
void* patchSpaceStart = (void*)receiveFromArm9();
|
||||
void* cheatsPtr = (void*)receiveFromArm9();
|
||||
if (cheatsPtr != nullptr && _cheats != nullptr)
|
||||
@@ -569,6 +571,20 @@ void NdsLoader::ApplyArm7Patches()
|
||||
}
|
||||
}
|
||||
|
||||
void NdsLoader::PreprocessCheats()
|
||||
{
|
||||
if (_cheats != nullptr)
|
||||
{
|
||||
CheatPreprocessor cheatPreprocessor;
|
||||
auto cheat = &_cheats->firstCheat;
|
||||
for (u32 i = 0; i < _cheats->numberOfCheats; i++)
|
||||
{
|
||||
cheatPreprocessor.PreprocessCheat(cheat);
|
||||
cheat = (pload_cheat_t*)((u8*)cheat + cheat->length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NdsLoader::SetupSharedMemory(u32 cardId, u32 agbMem, u32 resetParam, u32 romOffset, u32 bootType)
|
||||
{
|
||||
memcpy(TWL_SHARED_MEMORY->ntrSharedMem.cardRomHeader, &_romHeader, sizeof(nds_header_ntr_t) - 0x10);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "../../include/picoLoader7.h"
|
||||
#include "ndsHeader.h"
|
||||
#include "DsiWareSaveArranger.h"
|
||||
#include "BootMode.h"
|
||||
@@ -44,7 +45,7 @@ public:
|
||||
|
||||
/// @brief Sets the cheats to apply to the rom.
|
||||
/// @param cheats The cheats.
|
||||
void SetCheats(const pload_cheats_t* cheats)
|
||||
void SetCheats(pload_cheats_t* cheats)
|
||||
{
|
||||
_cheats = cheats;
|
||||
}
|
||||
@@ -60,12 +61,13 @@ private:
|
||||
const TCHAR* _launcherPath = nullptr;
|
||||
u32 _argumentsLength = 0;
|
||||
const char* _arguments = nullptr;
|
||||
const pload_cheats_t* _cheats = nullptr;
|
||||
pload_cheats_t* _cheats = nullptr;
|
||||
nds_header_twl_t _romHeader;
|
||||
DsiWareSaveResult _dsiwareSaveResult;
|
||||
|
||||
bool IsCloneBootRom(u32 romOffset);
|
||||
void ApplyArm7Patches();
|
||||
void PreprocessCheats();
|
||||
void SetupSharedMemory(u32 cardId, u32 agbMem, u32 resetParam, u32 romOffset, u32 bootType);
|
||||
void LoadFirmwareUserSettings();
|
||||
bool ShouldAttemptDldiPatch();
|
||||
|
||||
@@ -98,10 +98,13 @@ runCheat_opcode_loop:
|
||||
bcs 1f // if set, the opcode is executed
|
||||
|
||||
lsrs r3, r1, #24 // r3 = op
|
||||
cmp r3, #0xC2
|
||||
beq 2f
|
||||
cmp r3, #0xE0
|
||||
bne runCheat_opcode_loop
|
||||
|
||||
// opcode E has a dynamic length
|
||||
2:
|
||||
// opcode C2 and EX have a dynamic length
|
||||
adds r2, #7
|
||||
movs r3, #7
|
||||
bics r2, r3 // pad length to multiple of 8
|
||||
@@ -229,6 +232,8 @@ opcode_CX:
|
||||
lsrs r3, r1, #24 // r3 = op
|
||||
cmp r3, #0
|
||||
beq opcode_C0
|
||||
cmp r3, #2
|
||||
beq opcode_C2
|
||||
cmp r3, #4
|
||||
beq opcode_C4
|
||||
cmp r3, #5
|
||||
@@ -243,6 +248,26 @@ opcode_C0: // FOR 0..b
|
||||
mov r10, r7 // condition stack backup
|
||||
b runCheat_opcode_loop
|
||||
|
||||
opcode_C2: // execute code from cheat list
|
||||
movs r3, r0
|
||||
|
||||
// pad length to multiple of 8
|
||||
adds r2, r2, #7
|
||||
lsrs r2, r2, #3
|
||||
lsls r2, r2, #3
|
||||
adds r0, r2
|
||||
|
||||
lsrs r1, r1, #1
|
||||
bcc 1f
|
||||
adds r3, #1 // set thumb bit
|
||||
1:
|
||||
push {r0}
|
||||
bl 2f
|
||||
pop {r0}
|
||||
b runCheat_opcode_loop
|
||||
2:
|
||||
bx r3
|
||||
|
||||
opcode_C4: // offset = pointer to C4000000 opcode
|
||||
movs r5, r0
|
||||
subs r5, #8
|
||||
|
||||
Reference in New Issue
Block a user