Initial commit

This commit is contained in:
Gericom
2025-11-22 11:08:28 +01:00
commit 9cf3ffbfcf
358 changed files with 58350 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
#pragma once
#include "SdkVersion.h"
/// @brief Class representing a function signature with a minimum and maximum SDK version.
class FunctionSignature
{
public:
consteval FunctionSignature(const u32 pattern[4], SdkVersion minSdkVersion, SdkVersion maxSdkVersion)
: _pattern { pattern[0], pattern[1], pattern[2], pattern[3] }
, _minSdkVersion(minSdkVersion), _maxSdkVersion(maxSdkVersion) { }
/// @brief Returns the pattern of the function signature.
/// @return The pattern of the function signature.
constexpr const u32* GetPattern() const { return _pattern; }
/// @brief Returns the minimum SDK version of this function signature.
/// @return The minimum SDK version of this function signature.
constexpr SdkVersion GetMinimumSdkVersion() const { return _minSdkVersion; }
/// @brief Returns the maximum SDK version of this function signature.
/// @return The maximum SDK version of this function signature.
constexpr SdkVersion GetMaximumSdkVersion() const { return _maxSdkVersion; }
private:
const u32 _pattern[4];
const SdkVersion _minSdkVersion;
const SdkVersion _maxSdkVersion;
};

View File

@@ -0,0 +1,20 @@
#pragma once
#include "sections.h"
#include "SectorRemapPatchCode.h"
DEFINE_SECTION_SYMBOLS(offsettosectorremap);
extern "C" u32 offset_to_sector_remap(u32 romOffset);
class OffsetToSectorRemapPatchCode : public SectorRemapPatchCode
{
public:
explicit OffsetToSectorRemapPatchCode(PatchHeap& patchHeap)
: SectorRemapPatchCode(SECTION_START(offsettosectorremap), SECTION_SIZE(offsettosectorremap), patchHeap)
{ }
const void* GetRemapFunction() const override
{
return GetAddressAtTarget((void*)offset_to_sector_remap);
}
};

View File

@@ -0,0 +1,22 @@
.cpu arm7tdmi
.section "offsettosectorremap", "ax"
.syntax unified
.thumb
// r0 = rom offset
// returns sd sector in r0
// returns remaining sectors in cluster in lr
.global offset_to_sector_remap
.type offset_to_sector_remap, %function
offset_to_sector_remap:
push {lr}
lsrs r0, r0, #9 // byte address to sector address
movs r1, #1
mov lr, r1 // read at most 1 sector at a time
pop {pc}
.balign 4
.pool
.end

View File

@@ -0,0 +1,21 @@
#pragma once
#include "PatchContext.h"
/// @brief Abstract class representing a patch intended for the arm9 or arm7 binary.
class Patch
{
public:
/// @brief Next patch in the singly linked list.
Patch* next = nullptr;
/// @brief Tries to find the patch target in the region corresponding
/// to the given patchContext.
/// @param patchContext The patch context to use.
/// @return True if the patch target was found, or false otherwise.
virtual bool FindPatchTarget(PatchContext& patchContext) = 0;
/// @brief Applies the patch to the target that was previously found
/// with FindPatchTarget.
/// @param patchContext The patch context to use.
virtual void ApplyPatch(PatchContext& patchContext) = 0;
};

View File

@@ -0,0 +1,47 @@
#pragma once
#include <string.h>
#include "PatchHeap.h"
/// @brief Class representing a block of patch code to be inserted in a \see PatchHeap.
class PatchCode
{
public:
/// @brief Constructs a PatchCode instance for the given block of code
/// to be inserted in the given patchHeap.
/// @param code The start of the block of code.
/// @param size The size of the block of code.
/// @param patchHeap The patch heap to use.
PatchCode(const void* code, u32 size, PatchHeap& patchHeap)
: _code(code), _size(size), _targetAddress(patchHeap.Alloc(size)) { }
~PatchCode()
{
LOG_FATAL("Patch code must not be deleted.\n");
while (1);
}
/// @brief Converts a pointer inside the original code block
/// to a pointer at the target location in the patch heap.
/// @param ptr The pointer to convert.
/// @return The converted pointer.
const void* GetAddressAtTarget(const void* ptr) const
{
return (const void*)((u32)ptr - (u32)_code + (u32)_targetAddress);
}
/// @brief Copies the patch code to the target address.
void CopyToTarget() const
{
memcpy(_targetAddress, _code, _size);
}
protected:
/// @brief The start of the code block.
const void* const _code;
/// @brief The size of the code block.
const u32 _size;
/// @brief The target address for the code block in the patch heap.
void* const _targetAddress;
};

View File

@@ -0,0 +1,56 @@
#include "common.h"
#include "PatchCodeCollection.h"
void PatchCodeCollection::AddUniquePatchCode(PatchCode* patchCode)
{
for (auto& entry : _patchCodeEntries)
{
if (entry.type == 0)
{
entry.type = 0xFFFFFFFFu;
entry.patchCode = patchCode;
return;
}
}
LOG_FATAL("Out of patch code entries.\n");
while (true);
}
PatchCodeCollection::PatchCodeEntry& PatchCodeCollection::GetOrAddSharedPatchCode(u32 id)//, create_patch_code_func_t factoryFunction)
{
PatchCodeEntry* firstFreeEntry = nullptr;
for (auto& entry : _patchCodeEntries)
{
if (entry.type == 0)
{
firstFreeEntry = &entry;
break;
}
else if (entry.type == id)
{
return entry;
}
}
if (firstFreeEntry == nullptr)
{
LOG_FATAL("Out of patch code entries.\n");
while (true);
}
return *firstFreeEntry;
}
void PatchCodeCollection::CopyAllToTarget() const
{
for (auto& entry : _patchCodeEntries)
{
if (entry.type == 0)
{
break;
}
entry.patchCode->CopyToTarget();
}
}

View File

@@ -0,0 +1,77 @@
#pragma once
#include <array>
#include <concepts>
#include "PatchCode.h"
#include "PatchHeap.h"
/// @brief Class representing a collection of patch codes.
class PatchCodeCollection
{
private:
using create_patch_code_func_t = PatchCode* (*)(PatchCodeCollection& patchCodeCollection);
public:
PatchCodeCollection()
{
_patchCodeEntries.fill({ 0, nullptr });
}
PatchCodeCollection(const PatchCodeCollection&) = delete;
/// @brief Adds a unique patch code to the collection. If the same patch code is added another time, it will be duplicated.
/// @tparam T The type of patch code.
/// @tparam ...Args The argument types for the patch code constructor.
/// @param ...args The arguments for the patch code constructor.
/// @return A pointer to the patch code.
template <typename T, typename... Args> requires std::derived_from<T, PatchCode>
const T* AddUniquePatchCode(Args&&... args)
{
T* patchCode = new T(std::forward<Args>(args)...);
AddUniquePatchCode(patchCode);
return patchCode;
}
/// @brief Gets or adds a shared patch code from/to the collection.
/// If the same patch code is requested another time, the same instance will be returned.
/// @tparam T The type of patch code.
/// @param factoryFunction A factory function that produces a pointer to the patch code class (T*).
/// @return A pointer to the patch code.
template <typename T> requires std::derived_from<std::remove_pointer_t<std::invoke_result_t<T>>, PatchCode>
auto GetOrAddSharedPatchCode(T factoryFunction)
// return type of the factory function converted from a pointer to a pointer to const
-> std::add_pointer_t<std::add_const_t<std::remove_pointer_t<decltype(factoryFunction())>>>
{
u32 id = GetPatchCodeId<std::remove_pointer_t<decltype(factoryFunction())>>();
auto& entry = GetOrAddSharedPatchCode(id);
if (entry.type == 0)
{
entry.type = id; // claim this slot before calling the factory function
entry.patchCode = factoryFunction();
}
return static_cast<decltype(factoryFunction())>(entry.patchCode);
}
/// @brief Copies all patch codes to their target location.
void CopyAllToTarget() const;
private:
struct PatchCodeEntry
{
u32 type;
PatchCode* patchCode;
};
std::array<PatchCodeEntry, 64> _patchCodeEntries;
void AddUniquePatchCode(PatchCode* patchCode);
PatchCodeEntry& GetOrAddSharedPatchCode(u32 id);
template <class T>
static u32 GetPatchCodeId()
{
static const char typeId = 0;
return (u32)&typeId;
}
};

View File

@@ -0,0 +1,28 @@
#include "common.h"
#include "PatchContext.h"
#include "PatchCollection.h"
bool PatchCollection::TryPerformPatches(PatchContext& patchContext)
{
LOG_DEBUG("PatchCollection::PerformPatches\n");
Patch* cur = _head;
while (cur)
{
if (!cur->FindPatchTarget(patchContext))
{
LOG_DEBUG("Patch target not found!\n");
return false;
}
cur = cur->next;
}
cur = _head;
while (cur)
{
cur->ApplyPatch(patchContext);
cur = cur->next;
}
patchContext.GetPatchCodeCollection().CopyAllToTarget();
return true;
}

View File

@@ -0,0 +1,32 @@
#pragma once
#include "Patch.h"
/// @brief Class containing a linked list of patches.
class PatchCollection
{
public:
/// @brief Adds the given \p patch to the collection.
/// @param patch The patch to add.
void AddPatch(Patch* patch)
{
LOG_DEBUG("PatchCollection::AddPatch\n");
if (!_head)
{
_head = patch;
}
if (_tail)
{
_tail->next = patch;
}
_tail = patch;
}
/// @brief Tries to perform all patches in this collection.
/// @param patchContext The patch context to use.
/// @return \c true when all patches were successfully performed, or \c false otherwise.
bool TryPerformPatches(PatchContext& patchContext);
private:
Patch* _head = nullptr;
Patch* _tail = nullptr;
};

View File

@@ -0,0 +1,31 @@
#include "common.h"
#include "fastSearch.h"
#include "platform/LoaderPlatform.h"
#include "PatchContext.h"
u32* PatchContext::FindPattern32(const u32* pattern, u32 byteLength) const
{
#ifdef LIBTWL_ARM9
if (byteLength == 16)
{
return (u32*)fastSearch16((const u32*)_data, _dataSize, pattern);
}
#endif
return nullptr;
}
u32* PatchContext::FindPattern32Twl(const u32* pattern, u32 byteLength) const
{
if (!_twlData || _twlDataSize == 0)
{
return nullptr;
}
#ifdef LIBTWL_ARM9
if (byteLength == 16)
{
return (u32*)fastSearch16((const u32*)_twlData, _twlDataSize, pattern);
}
#endif
return nullptr;
}

View File

@@ -0,0 +1,61 @@
#pragma once
#include <memory>
#include "SdkVersion.h"
#include "PatchHeap.h"
#include "PatchCodeCollection.h"
class LoaderPlatform;
/// @brief Class providing a context for performing patches on retail rom arm7 or arm9.
class PatchContext
{
public:
PatchContext(void* data, u32 dataSize, void* twlData, u32 twlDataSize,
SdkVersion sdkVersion, u32 gameCode, const LoaderPlatform* loaderPlatform)
: _data(data), _dataSize(dataSize), _twlData(twlData), _twlDataSize(twlDataSize)
, _sdkVersion(sdkVersion), _gameCode(gameCode), _loaderPlatform(loaderPlatform) { }
/// @brief Tries to find the given \p pattern of the given \p byteLength in the ntr region.
/// @param pattern The pattern to find.
/// @param byteLength The length of the pattern.
/// @return A pointer to the first location where the pattern was found, or \c nullptr if the pattern was not found.
u32* FindPattern32(const u32* pattern, u32 byteLength) const;
/// @brief Tries to find the given \p pattern of the given \p byteLength in the twl region.
/// @param pattern The pattern to find.
/// @param byteLength The length of the pattern.
/// @return A pointer to the first location where the pattern was found, or \c nullptr if the pattern was not found.
u32* FindPattern32Twl(const u32* pattern, u32 byteLength) const;
/// @brief Returns the patch heap of this context.
/// @return The patch heap of this context.
constexpr PatchHeap& GetPatchHeap() { return _patchHeap; }
/// @brief Returns the patch code collection of this context.
/// @return The patch code collection of this context.
constexpr PatchCodeCollection& GetPatchCodeCollection() { return _patchCodeCollection; }
/// @brief Returns the SDK version of the rom that is being patched.
/// @return The SDK version of the rom that is being patched.
constexpr SdkVersion GetSdkVersion() const { return _sdkVersion; }
/// @brief Returns the game code of the rom that is being patched.
/// @return The game code of the rom that is being patched.
constexpr u32 GetGameCode() const { return _gameCode; }
/// @brief Returns the loader platform that should be used for the patches.
/// @return The loader platform that should be used for the patches.
constexpr const LoaderPlatform* GetLoaderPlatform() { return _loaderPlatform; }
private:
void* _data;
u32 _dataSize;
void* _twlData;
u32 _twlDataSize;
SdkVersion _sdkVersion;
u32 _gameCode;
PatchHeap _patchHeap;
PatchCodeCollection _patchCodeCollection;
const LoaderPlatform* _loaderPlatform;
};

View File

@@ -0,0 +1,100 @@
#include "common.h"
#include "PatchHeap.h"
PatchHeap::PatchHeap()
: _freeBlocks(nullptr)
{
_unusedBlockPool = &_blocks[0];
for (u32 i = 0; i < _blocks.size() - 1; i++)
_blocks[i].next = &_blocks[i + 1];
_blocks[_blocks.size() - 1].next = nullptr;
}
void PatchHeap::AddFreeSpace(void* block, u32 size)
{
PatchHeapBlock* cur = _freeBlocks;
bool added = false;
while (cur)
{
if ((u8*)cur->block + cur->size == block)
{
cur->size += size;
added = true;
break;
}
else if ((u8*)block + size == cur->block)
{
cur->block = block;
cur->size += size;
added = true;
break;
}
cur = cur->next;
}
if (added)
return;
PatchHeapBlock* heapBlock = _unusedBlockPool;
_unusedBlockPool = _unusedBlockPool->next;
heapBlock->block = block;
heapBlock->size = size;
heapBlock->next = _freeBlocks;
_freeBlocks = heapBlock;
}
void* PatchHeap::Alloc(u32 size)
{
PatchHeapBlock* prev = nullptr;
PatchHeapBlock* cur = _freeBlocks;
PatchHeapBlock* bestBlock = nullptr;
PatchHeapBlock* bestBlockPrev = nullptr;
while (cur)
{
if (cur->size >= size && (!bestBlock || cur->size < bestBlock->size))
{
bestBlock = cur;
bestBlockPrev = prev;
if (bestBlock->size == size)
break;
}
prev = cur;
cur = cur->next;
}
if (!bestBlock)
{
LOG_FATAL("No space found to put patch of size 0x%x\n", size);
while (1);
return nullptr;
}
void* result = bestBlock->block;
if (bestBlock->size == size)
{
if (bestBlockPrev)
bestBlockPrev->next = bestBlock->next;
if (_freeBlocks == bestBlock)
_freeBlocks = bestBlock->next;
bestBlock->next = _unusedBlockPool;
_unusedBlockPool = bestBlock;
}
else
{
bestBlock->block = (u8*)bestBlock->block + size;
bestBlock->size -= size;
}
LOG_DEBUG("Allocated 0x%p, size = 0x%X\n", result, size);
return result;
}

View File

@@ -0,0 +1,34 @@
#pragma once
#include <array>
/// @brief Class implementing a heap intended for patch code.
/// The heap consists of a linked list of blocks of free
/// space. It is possible to add many small pieces.
class PatchHeap
{
public:
PatchHeap();
/// @brief Adds a block of free space to the patch heap.
/// @param block The start of the block.
/// @param size The size of the block.
void AddFreeSpace(void* block, u32 size);
/// @brief Allocated a block of the given size from the patch heap.
/// @param size The size to allocate.
/// @return A pointer to the allocated block if successful, or nullptr otherwise.
void* Alloc(u32 size);
private:
struct PatchHeapBlock
{
PatchHeapBlock* next;
void* block;
u32 size;
};
std::array<PatchHeapBlock, 64> _blocks;
PatchHeapBlock* _unusedBlockPool;
PatchHeapBlock* _freeBlocks;
};

View File

@@ -0,0 +1,11 @@
#pragma once
#include "PatchCode.h"
class SectorRemapPatchCode : public PatchCode
{
public:
SectorRemapPatchCode(const void* code, u32 size, PatchHeap& patchHeap)
: PatchCode(code, size, patchHeap) { }
virtual const void* GetRemapFunction() const = 0;
};

View File

@@ -0,0 +1,15 @@
#pragma once
#include "sections.h"
DEFINE_SECTION_SYMBOLS(patch_carditaskthread);
extern "C" void __patch_carditaskthread_entry();
extern "C" void __patch_carditaskthread_entry_sdk4();
extern u16 __patch_carditaskthread_mov_cardicommon_to_r4;
extern u16 __patch_carditaskthread_mov_command_to_r0;
extern u32 __patch_carditaskthread_failoffset;
extern u32 __patch_carditaskthread_successoffset;
extern u32 __patch_carditaskthread_readsave_asm_address;
extern u32 __patch_carditaskthread_writesave_asm_address;

View File

@@ -0,0 +1,123 @@
.cpu arm7tdmi
.section "patch_carditaskthread", "ax"
.syntax unified
.thumb
.global __patch_carditaskthread_entry_sdk4
.type __patch_carditaskthread_entry_sdk4, %function
__patch_carditaskthread_entry_sdk4:
push {r4,lr}
beq end_success
b __patch_carditaskthread_mov_cardicommon_to_r4
.global __patch_carditaskthread_entry
.type __patch_carditaskthread_entry, %function
__patch_carditaskthread_entry:
push {r4,lr}
.global __patch_carditaskthread_mov_cardicommon_to_r4
__patch_carditaskthread_mov_cardicommon_to_r4:
mov r4, r9
.global __patch_carditaskthread_mov_command_to_r0
__patch_carditaskthread_mov_command_to_r0:
ldr r0, [r4, #4] // r0 = command
cmp r0, #2
beq identify_backup
cmp r0, #6
beq read_backup
cmp r0, #7
beq write_backup
cmp r0, #8
beq write_backup
cmp r0, #9
beq verify_backup
// erase
cmp r0, #10
beq end_success
cmp r0, #11
beq end_success
cmp r0, #12
beq end_success
cmp r0, #15
beq end_success
end_fail:
ldr r0, __patch_carditaskthread_failoffset
b end
end_success:
ldr r0, __patch_carditaskthread_successoffset
end:
pop {r4}
pop {r3}
adds r3, r0
bx r3
identify_backup:
b end_success
read_backup:
ldr r0, [r4]
movs r1, #0
str r1, [r0] // result
ldr r1, [r0, #0xC] // src
ldr r2, [r0, #0x10] // dst
ldr r3, [r0, #0x14] // len
cmp r3, #0
beq end_success
ldr r0, __patch_carditaskthread_readsave_asm_address
bl blx_r0
b end_success
write_backup:
ldr r0, [r4]
movs r1, #0
str r1, [r0] // result
ldr r1, [r0, #0xC] // src
ldr r2, [r0, #0x10] // dst
ldr r3, [r0, #0x14] // len
cmp r3, #0
beq end_success
ldr r0, __patch_carditaskthread_writesave_asm_address
bl blx_r0
b end_success
verify_backup:
ldr r0, [r4]
movs r1, #0
str r1, [r0] // result
b end_success
blx_r0:
bx r0
.balign 4
.global __patch_carditaskthread_failoffset
__patch_carditaskthread_failoffset:
.word 0
.global __patch_carditaskthread_successoffset
__patch_carditaskthread_successoffset:
.word 4
.global __patch_carditaskthread_readsave_asm_address
__patch_carditaskthread_readsave_asm_address:
.word 0
.global __patch_carditaskthread_writesave_asm_address
__patch_carditaskthread_writesave_asm_address:
.word 0
.pool
.end

View File

@@ -0,0 +1,39 @@
#include "common.h"
#include "../PatchContext.h"
#include "sharedMemory.h"
#include "ndsHeader.h"
#include "DisableArm7WramClearPatch.h"
bool DisableArm7WramClearPatch::FindPatchTarget(PatchContext& patchContext)
{
auto romHeader = (const nds_header_ntr_t*)TWL_SHARED_MEMORY->ntrSharedMem.romHeader;
_clearEndPool = nullptr;
for (u32 i = 0; i < 0x100; i += 4)
{
u32* ptr = (u32*)(romHeader->arm7LoadAddress + i);
if (*ptr == 0x380FF00)
{
_clearEndPool = ptr;
break;
}
}
if (_clearEndPool)
{
LOG_DEBUG("Arm7 wram clear end pool value found at 0x%p\n", _clearEndPool);
}
else
{
LOG_WARNING("Arm7 wram clear end pool value not found\n");
}
return _clearEndPool != nullptr;
}
void DisableArm7WramClearPatch::ApplyPatch(PatchContext& patchContext)
{
if (!_clearEndPool)
return;
*_clearEndPool = 0;
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include "../Patch.h"
/// @brief Arm7 patch to disabling clearing of wram.
class DisableArm7WramClearPatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _clearEndPool = nullptr;
};

View File

@@ -0,0 +1,97 @@
#include "common.h"
#include "../PatchContext.h"
#include "OsGetInitArenaLoPatch.h"
static const u32 sOSGetInitArenaLoPatternOld[] = { 0xE3A0050Eu, 0xE59F1014u, 0xE351050Eu, 0x81A00001u }; // -0x34
static const u32 sOSGetInitArenaLoPattern[] = { 0xE59F1018u, 0xE3A0050Eu, 0xE351050Eu, 0x81A00001u }; // -0x34
static const u32 sOSGetInitArenaLoPattern2[] = { 0xE59F101Cu, 0xE3A0050Eu, 0xE351050Eu, 0x81A00001u }; // -0x3C
static const u32 sOSGetInitArenaLoPatternThumb[] = { 0xD0042801u, 0xD0042807u, 0xD0092808u, 0x4809E00Fu };
static const u32 sOSGetInitArenaLoPatternThumbSdk50[] = { 0x2801B508u, 0x2807D004u, 0x2808D006u, 0xE010D00Au };
bool OsGetInitArenaLoPatch::FindPatchTarget(PatchContext& patchContext)
{
if (_osGetInitArenaLo)
return true;
_osGetInitArenaLo = patchContext.FindPattern32(sOSGetInitArenaLoPattern, sizeof(sOSGetInitArenaLoPattern));
if (!_osGetInitArenaLo)
_osGetInitArenaLo = patchContext.FindPattern32(sOSGetInitArenaLoPatternOld, sizeof(sOSGetInitArenaLoPatternOld));
if (_osGetInitArenaLo)
{
_osGetInitArenaLo -= 13;
_wramBssOffset = 0x54;
}
else
{
_osGetInitArenaLo = patchContext.FindPattern32(sOSGetInitArenaLoPattern2, sizeof(sOSGetInitArenaLoPattern2));
if (_osGetInitArenaLo)
{
_osGetInitArenaLo -= 15;
_wramBssOffset = 0x60;
}
else
{
_osGetInitArenaLo = patchContext.FindPattern32(sOSGetInitArenaLoPatternThumb, sizeof(sOSGetInitArenaLoPatternThumb));
if (_osGetInitArenaLo)
{
_wramBssOffset = 0x38;
}
else
{
_osGetInitArenaLo = patchContext.FindPattern32(sOSGetInitArenaLoPatternThumbSdk50, sizeof(sOSGetInitArenaLoPatternThumbSdk50));
if (_osGetInitArenaLo)
{
_wramBssOffset = 0x40;
}
else
{
_osGetInitArenaLo = patchContext.FindPattern32Twl(sOSGetInitArenaLoPattern, sizeof(sOSGetInitArenaLoPattern));
if (!_osGetInitArenaLo)
_osGetInitArenaLo = patchContext.FindPattern32Twl(sOSGetInitArenaLoPatternOld, sizeof(sOSGetInitArenaLoPatternOld));
if (_osGetInitArenaLo)
{
_osGetInitArenaLo -= 13;
_wramBssOffset = 0x54;
}
else
{
_osGetInitArenaLo = patchContext.FindPattern32Twl(sOSGetInitArenaLoPattern2, sizeof(sOSGetInitArenaLoPattern2));
if (_osGetInitArenaLo)
{
_osGetInitArenaLo -= 15;
_wramBssOffset = 0x60;
}
else
{
_osGetInitArenaLo = patchContext.FindPattern32Twl(sOSGetInitArenaLoPatternThumb, sizeof(sOSGetInitArenaLoPatternThumb));
if (_osGetInitArenaLo)
{
_wramBssOffset = 0x38;
}
}
}
}
}
}
}
if (_osGetInitArenaLo)
{
LOG_DEBUG("Found OS_GetInitArenaLo at %p\n", _osGetInitArenaLo);
_wramBssEnd = (void*)*(u32*)((u8*)_osGetInitArenaLo + _wramBssOffset);
LOG_DEBUG("Bss end: 0x%p\n", _wramBssEnd);
_mainMemoryArenaLo = (void*)*(u32*)((u8*)_osGetInitArenaLo + _wramBssOffset - 4);
LOG_DEBUG("Main memory arena lo: 0x%p\n", _mainMemoryArenaLo);
}
else
{
LOG_WARNING("OS_GetInitArenaLo not found\n");
}
return _osGetInitArenaLo != nullptr;
}
void OsGetInitArenaLoPatch::ApplyPatch(PatchContext& patchContext)
{
if (!_osGetInitArenaLo)
return;
*(u32*)((u8*)_osGetInitArenaLo + _wramBssOffset) = (u32)_wramBssEnd;
*(u32*)((u8*)_osGetInitArenaLo + _wramBssOffset - 4) = (u32)_mainMemoryArenaLo;
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include "../Patch.h"
/// @brief Arm7 patch to get and update the arena low addresses for wram and main memory.
class OsGetInitArenaLoPatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
void* GetArm7PrivateWramArenaLo() const { return _wramBssEnd; }
void SetArm7PrivateWramArenaLo(void* wramBssEnd) { _wramBssEnd = wramBssEnd; }
void* GetMainMemoryArenaLo() const { return _mainMemoryArenaLo; }
void SetMainMemoryArenaLo(void* mainMemoryArenaLo) { _mainMemoryArenaLo = mainMemoryArenaLo; }
private:
u32* _osGetInitArenaLo = nullptr;
u32 _wramBssOffset = 0;
void* _wramBssEnd = nullptr;
void* _mainMemoryArenaLo = nullptr;
};

View File

@@ -0,0 +1,27 @@
#include "common.h"
#include "../PatchContext.h"
#include "PokemonDownloaderArm7PatchAsm.h"
#include "PokemonDownloaderArm7Patch.h"
static const u32 sBootFunctionPattern[] = { 0xE92D4070u, 0xE59F0098u, 0xE5904004u, 0xE3540000u };
bool PokemonDownloaderArm7Patch::FindPatchTarget(PatchContext& patchContext)
{
_bootFunction = patchContext.FindPattern32(sBootFunctionPattern, sizeof(sBootFunctionPattern));
return _bootFunction != nullptr;
}
void PokemonDownloaderArm7Patch::ApplyPatch(PatchContext& patchContext)
{
if (!_bootFunction)
{
return;
}
auto patchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<PokemonDownloaderArm7PatchCode>(
patchContext.GetPatchHeap());
_bootFunction[0] = 0xE59F0000; // ldr r0,= address
_bootFunction[1] = 0xE12FFF10; // bx r0
_bootFunction[2] = (u32)patchCode->GetBoot7Function(); // address
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "../Patch.h"
#include "LoaderInfo.h"
/// @brief Arm7 patch to make the Pokemon downloader reboot into Pico Loader.
class PokemonDownloaderArm7Patch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _bootFunction = nullptr;
};

View File

@@ -0,0 +1,19 @@
#pragma once
#include "../PatchCode.h"
#include "sections.h"
DEFINE_SECTION_SYMBOLS(patch_pokemondownloader7);
extern "C" void patch_pokemondownloader7_entry(void);
class PokemonDownloaderArm7PatchCode : public PatchCode
{
public:
explicit PokemonDownloaderArm7PatchCode(PatchHeap& patchHeap)
: PatchCode(SECTION_START(patch_pokemondownloader7), SECTION_SIZE(patch_pokemondownloader7), patchHeap) { }
const void* GetBoot7Function() const
{
return GetAddressAtTarget((void*)patch_pokemondownloader7_entry);
}
};

View File

@@ -0,0 +1,25 @@
.cpu arm7tdmi
.section "patch_pokemondownloader7", "ax"
.syntax unified
.thumb
.global patch_pokemondownloader7_entry
.type patch_pokemondownloader7_entry, %function
patch_pokemondownloader7_entry:
ldr r0,= 0x04000180 // REG_IPC_SYNC
movs r1, #1
strb r1, [r0, #1]
1:
ldrb r7, [r0] // ipc sync
cmp r7, #1
bne 1b // while ipc sync from arm9 is not 1
ldr r0,= 0x06000000
ldr r0, [r0]
bx r0
.balign 4
.pool
.end

View File

@@ -0,0 +1,31 @@
#pragma once
#include "sections.h"
#include "../PatchCode.h"
#include "../SectorRemapPatchCode.h"
#include "../platform/SdReadPatchCode.h"
DEFINE_SECTION_SYMBOLS(readsave);
extern "C" void readsave_asm(u32, u32 saveSrc, void* memoryDst, u32 byteLength);
extern u32 readsave_tmpBufferPtr;
extern u32 readsave_save_offset_to_sd_sector_asm_address;
extern u32 readsave_sdread_asm_address;
class ReadSavePatchCode : public PatchCode
{
public:
ReadSavePatchCode(PatchHeap& patchHeap, const SectorRemapPatchCode* sectorRemapPatchCode,
const SdReadPatchCode* sdReadPatchCode, void* tmpBuffer)
: PatchCode(SECTION_START(readsave), SECTION_SIZE(readsave), patchHeap)
{
readsave_save_offset_to_sd_sector_asm_address = (u32)sectorRemapPatchCode->GetRemapFunction();
readsave_sdread_asm_address = (u32)sdReadPatchCode->GetSdReadFunction();
readsave_tmpBufferPtr = (u32)tmpBuffer;
}
const void* GetReadSaveFunction() const
{
return GetAddressAtTarget((void*)readsave_asm);
}
};

View File

@@ -0,0 +1,75 @@
.cpu arm7tdmi
.section "readsave", "ax"
.syntax unified
.thumb
// r1 = save src
// r2 = memory dst
// r3 = byte length
.global readsave_asm
.type readsave_asm, %function
readsave_asm:
push {r4,r5,r6,r7,lr}
movs r4, r3 // remaining bytes
movs r5, r1
movs r6, r2
1:
movs r0, r5 // will be rounded down by function
ldr r3, readsave_save_offset_to_sd_sector_asm_address
bl blx_r3
cmp r0, #0
beq end // out of bounds
ldr r1, readsave_tmpBufferPtr
movs r2, #1 // single sector
lsls r7, r2, #9 // 512
ldr r3, readsave_sdread_asm_address
bl blx_r3
// copy bytes
lsls r2, r5, #23
lsrs r2, r2, #23 // r2 = byte offset in sector
subs r0, r6, r2
subs r7, r7, r2 // remaining in sector
cmp r7, r4 // if remaining in sector > requested read length
bls 2f
movs r7, r4 // clamp to requested read length
2:
subs r4, r7
adds r5, r7
adds r6, r7
ldr r1, readsave_tmpBufferPtr
3:
ldrb r3, [r1, r2]
strb r3, [r0, r2]
adds r2, #1
subs r7, #1
bne 3b
cmp r4, #0
bne 1b
end:
pop {r4,r5,r6,r7,pc}
blx_r3:
bx r3
.balign 4
.global readsave_tmpBufferPtr
readsave_tmpBufferPtr:
.word 0
.global readsave_save_offset_to_sd_sector_asm_address
readsave_save_offset_to_sd_sector_asm_address:
.word 0
.global readsave_sdread_asm_address
readsave_sdread_asm_address:
.word 0
.pool
.end

View File

@@ -0,0 +1,25 @@
#pragma once
#include "sections.h"
#include "../SectorRemapPatchCode.h"
#include "fileInfo.h"
DEFINE_SECTION_SYMBOLS(saveoffsettosdsector);
extern "C" u32 save_offset_to_sd_sector_asm(u32 saveOffset);
extern u32 saveoffsettosdsector_fatDataPtr;
class SaveOffsetToSdSectorPatchCode : public SectorRemapPatchCode
{
public:
SaveOffsetToSdSectorPatchCode(PatchHeap& patchHeap, const save_file_info_t* fatDataPtr)
: SectorRemapPatchCode(SECTION_START(saveoffsettosdsector), SECTION_SIZE(saveoffsettosdsector), patchHeap)
{
saveoffsettosdsector_fatDataPtr = (u32)fatDataPtr;
}
const void* GetRemapFunction() const override
{
return GetAddressAtTarget((void*)save_offset_to_sd_sector_asm);
}
};

View File

@@ -0,0 +1,50 @@
.cpu arm7tdmi
.section "saveoffsettosdsector", "ax"
.syntax unified
.thumb
// r0 = save offset
// returns sd sector in r0
// returns cluster shift in r1
// returns remaining sectors in cluster in lr
.global save_offset_to_sd_sector_asm
.type save_offset_to_sd_sector_asm, %function
save_offset_to_sd_sector_asm:
push {r4,r5,lr}
retry:
ldr r5, saveoffsettosdsector_fatDataPtr
ldmia r5!, {r1,r2,r3,r4} // clusterShift, database, clusterMask, clusterMap[0]
lsrs r0, r0, #9
adds r4, r3, #1
ands r3, r0
subs r4, r3
mov lr, r4 // remaining sectors in cluster
adds r3, r2
lsrs r0, r1 // cl
1:
ldmia r5!, {r2,r4} // ncl, startSector
cmp r2, #0
beq out_of_bounds
subs r0, r2 // cl -= ncl
bcs 1b
adds r0, r2
adds r0, r4 // + startSector
subs r0, #2 // - 2
lsls r0, r1
adds r0, r3 // r0 = src sector
pop {r4,r5,pc}
out_of_bounds:
movs r0, #0
pop {r4,r5,pc}
.balign 4
.global saveoffsettosdsector_fatDataPtr
saveoffsettosdsector_fatDataPtr:
.word 0x027FFA00
.pool
.end

View File

@@ -0,0 +1,34 @@
#pragma once
#include "sections.h"
#include "../PatchCode.h"
#include "../SectorRemapPatchCode.h"
#include "../platform/SdReadPatchCode.h"
#include "../platform/SdWritePatchCode.h"
DEFINE_SECTION_SYMBOLS(writesave);
extern "C" void writesave_asm(u32, const void* memorySrc, u32 saveDst, u32 byteLength);
extern u32 writesave_tmpBufferPtr;
extern u32 writesave_save_offset_to_sd_sector_asm_address;
extern u32 writesave_sdread_asm_address;
extern u32 writesave_sdwrite_asm_address;
class WriteSavePatchCode : public PatchCode
{
public:
WriteSavePatchCode(PatchHeap& patchHeap, const SectorRemapPatchCode* sectorRemapPatchCode,
const SdReadPatchCode* sdReadPatchCode, const SdWritePatchCode* sdWritePatchCode, void* tmpBuffer)
: PatchCode(SECTION_START(writesave), SECTION_SIZE(writesave), patchHeap)
{
writesave_save_offset_to_sd_sector_asm_address = (u32)sectorRemapPatchCode->GetRemapFunction();
writesave_sdread_asm_address = (u32)sdReadPatchCode->GetSdReadFunction();
writesave_sdwrite_asm_address = (u32)sdWritePatchCode->GetSdWriteFunction();
writesave_tmpBufferPtr = (u32)tmpBuffer;
}
const void* GetWriteSaveFunction() const
{
return GetAddressAtTarget((void*)writesave_asm);
}
};

View File

@@ -0,0 +1,86 @@
.cpu arm7tdmi
.section "writesave", "ax"
.syntax unified
.thumb
// r1 = memory src
// r2 = save dst
// r3 = byte length
.global writesave_asm
.type writesave_asm, %function
writesave_asm:
push {r4,r5,r6,r7,lr}
movs r4, r3 // remaining bytes
movs r5, r2
movs r6, r1
1:
movs r0, r5 // will be rounded down by function
ldr r3, writesave_save_offset_to_sd_sector_asm_address
bl blx_r3
cmp r0, #0
beq end // out of bounds
push {r0}
ldr r1, writesave_tmpBufferPtr
movs r2, #1 // single sector
lsls r7, r2, #9 // 512
ldr r3, writesave_sdread_asm_address
bl blx_r3
// copy bytes
lsls r2, r5, #23
lsrs r2, r2, #23 // r2 = byte offset in sector
subs r0, r6, r2
subs r7, r7, r2 // remaining in sector
cmp r7, r4 // if remaining in sector > requested read length
bls 2f
movs r7, r4 // clamp to requested read length
2:
subs r4, r7
adds r5, r7
adds r6, r7
ldr r1, writesave_tmpBufferPtr
3:
ldrb r3, [r0, r2]
strb r3, [r1, r2]
adds r2, #1
subs r7, #1
bne 3b
pop {r0}
movs r2, #1 // single sector
ldr r3, writesave_sdwrite_asm_address
bl blx_r3
cmp r4, #0
bne 1b
end:
pop {r4,r5,r6,r7,pc}
blx_r3:
bx r3
.balign 4
.global writesave_tmpBufferPtr
writesave_tmpBufferPtr:
.word 0
.global writesave_save_offset_to_sd_sector_asm_address
writesave_save_offset_to_sd_sector_asm_address:
.word 0
.global writesave_sdread_asm_address
writesave_sdread_asm_address:
.word 0
.global writesave_sdwrite_asm_address
writesave_sdwrite_asm_address:
.word 0
.pool
.end

View File

@@ -0,0 +1,285 @@
#include "common.h"
#include <algorithm>
#include <array>
#include "fileInfo.h"
#include "patches/PatchContext.h"
#include "patches/arm7/ReadSaveAsm.h"
#include "patches/arm7/WriteSaveAsm.h"
#include "patches/FunctionSignature.h"
#include "patches/platform/LoaderPlatform.h"
#include "patches/arm7/SaveOffsetToSdSectorAsm.h"
#include "patches/OffsetToSectorRemapAsm.h"
#include "patches/arm7/CardiTaskThreadPatchAsm.h"
#include "thumbInstructions.h"
#include "CardiTaskThreadPatch.h"
static constexpr auto sSignaturesArm = std::to_array<const FunctionSignature>
({
{ (const u32[]) { 0xE92D4FF0u, 0xE24DD004u, 0xE59FA108u, 0xE28A5040u }, 0x020029A0, 0x02005015 },
{ (const u32[]) { 0xE92D4FF0u, 0xE24DD004u, 0xE59FA10Cu, 0xE28A5040u }, 0x02004F50, 0x02017532 },
{ (const u32[]) { 0xE92D4FF0u, 0xE24DD004u, 0xE59F91DCu, 0xE2895044u }, 0x02017532, 0x02017532 },
{ (const u32[]) { 0xE92D4FF0u, 0xE24DD004u, 0xE59F91E0u, 0xE2895044u }, 0x02017532, 0x02027534 },
{ (const u32[]) { 0xE92D4FF0u, 0xE24DD004u, 0xE59F91E0u, 0xE2895048u }, 0x030028A0, 0x03004E86 },
{ (const u32[]) { 0xE92D4FF0u, 0xE24DD004u, 0xE59F91D4u, 0xE2895048u }, 0x03004F4C, 0x03017534 },
{ (const u32[]) { 0xE92D4FF0u, 0xE24DD004u, 0xE59F91F8u, 0xE2895048u }, 0x03017530, 0x04017530 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEADCu }, 0x04002774, 0x04017533 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEACEu }, 0x04007531, 0x04007531 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEABFu }, 0x04007530, 0x04007531 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEAAAu }, 0x04017530, 0x04017533 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEA94u }, 0x04017530, 0x04017533 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEA8Du }, 0x04017531, 0x04017533 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEA77u }, 0x04017533, 0x04017533 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4224u, 0xE3A05000u, 0xEBFFEA70u }, 0x04027530, 0x04027539 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4224u, 0xE3A05000u, 0xEBFFEA8Du }, 0x04027530, 0x04027539 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4224u, 0xE3A05000u, 0xEBFFEA6Cu }, 0x04027531, 0x04027531 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4224u, 0xE3A05000u, 0xEBFFEA77u }, 0x04027531, 0x04027539 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4224u, 0xE3A05000u, 0xEBFFEA5Au }, 0x04027533, 0x04027533 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFFAD7u }, 0x03017531, 0x03017531 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFFAA5u }, 0x03027531, 0x03027531 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4200u, 0xE3A05000u, 0xEBFFEADBu }, 0x03027531, 0x03027531 },
{ (const u32[]) { 0xE92D41F0u, 0xE59F4224u, 0xE3A05000u, 0xEBFFEA68u }, 0x04027533, 0x04027533 }
});
static constexpr auto sSignaturesThumb = std::to_array<const FunctionSignature>
({
{ (const u32[]) { 0xB081B5F0u, 0x1C2E4D2Du, 0x27103640u, 0xF7FC2400u }, 0x02004FB0, 0x02004FB4 },
{ (const u32[]) { 0xB081B5F0u, 0x1C2E4D2Bu, 0x27103640u, 0xF7FC2400u }, 0x02007531, 0x02017532 },
{ (const u32[]) { 0xB081B5F0u, 0x1C264C57u, 0x27103644u, 0xF7FC2500u }, 0x02027532, 0x02027535 },
{ (const u32[]) { 0xB081B5F0u, 0x1C264C55u, 0x27103648u, 0xF7FC2500u }, 0x03007530, 0x03012776 },
{ (const u32[]) { 0xB081B5F0u, 0x1C264C5Cu, 0x27033648u, 0xF7FC2500u }, 0x03017531, 0x03027532 },
{ (const u32[]) { 0x4C5CB5F8u, 0xF7FC2500u, 0x1C26FAB3u, 0x36481C07u }, 0x04007531, 0x04007531 },
{ (const u32[]) { 0x4C5CB5F8u, 0xF7FC2500u, 0x1C26FA97u, 0x36481C07u }, 0x04007531, 0x04007531 },
{ (const u32[]) { 0x4C5CB5F8u, 0xF7FC2500u, 0x1C26FA4Fu, 0x36481C07u }, 0x04017530, 0x04017533 },
{ (const u32[]) { 0x4C5CB5F8u, 0xF7FC2500u, 0x1C26FA23u, 0x36481C07u }, 0x04017531, 0x04017531 },
{ (const u32[]) { 0x4D62B5F8u, 0xF7FC2400u, 0x1C2EFA2Du, 0x36481C07u }, 0x04027531, 0x04027539 }
});
bool CardiTaskThreadPatch::FindPatchTarget(PatchContext& patchContext)
{
for (const auto& signature : sSignaturesArm)
{
if (CheckSignature(patchContext, signature))
{
if (signature.GetPattern()[2] == 0xE59F91DCu)
{
_peach = true;
}
else if (signature.GetPattern()[3] == 0xEBFFFAD7u
|| signature.GetPattern()[3] == 0xEBFFFAA5u
|| signature.GetPattern()[3] == 0xEBFFEADBu)
{
_pokemonDownloader = true;
}
break;
}
}
if (!_cardiTaskThread)
{
for (const auto& signature : sSignaturesThumb)
{
if (CheckSignature(patchContext, signature))
{
_thumb = true;
break;
}
}
}
if (!_cardiTaskThread)
{
LOG_FATAL("CARDi_TaskThread not found\n");
}
return _cardiTaskThread != nullptr;
}
void CardiTaskThreadPatch::ApplyPatch(PatchContext& patchContext)
{
if (!_cardiTaskThread)
return;
//0xA4 = ADDLS PC, PC, R0,LSL#2
u32 patch1Size = SECTION_SIZE(patch_carditaskthread);
void* patch1Address = patchContext.GetPatchHeap().Alloc(patch1Size);
auto loaderPlatform = patchContext.GetLoaderPlatform();
const SdReadPatchCode* readPatchCode;
const SdWritePatchCode* writePatchCode;
const SectorRemapPatchCode* sectorRemapPatchCode;
readPatchCode = loaderPlatform->CreateSdReadPatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
writePatchCode = loaderPlatform->CreateSdWritePatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
sectorRemapPatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<SaveOffsetToSdSectorPatchCode>
(
patchContext.GetPatchHeap(),
(const save_file_info_t*)((u32)SHARED_SAVE_FILE_INFO - 0x02F00000 + 0x02700000)
);
void* tmpBuffer = patchContext.GetPatchHeap().Alloc(512);
auto readSavePatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<ReadSavePatchCode>
(
patchContext.GetPatchHeap(),
sectorRemapPatchCode,
readPatchCode,
tmpBuffer
);
auto writeSavePatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<WriteSavePatchCode>
(
patchContext.GetPatchHeap(),
sectorRemapPatchCode,
readPatchCode,
writePatchCode,
tmpBuffer
);
__patch_carditaskthread_readsave_asm_address = (u32)readSavePatchCode->GetReadSaveFunction();
__patch_carditaskthread_writesave_asm_address = (u32)writeSavePatchCode->GetWriteSaveFunction();
u32 entryAddress;
u32 patchOffset;
if (_thumb)
{
if (patchContext.GetSdkVersion() >= 0x4027531)
{
patchOffset = 0x90;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_MOVS_REG(THUMB_R4, THUMB_R5);
__patch_carditaskthread_successoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x0C) + 9;
__patch_carditaskthread_failoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x14) + 9;
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x00) = THUMB_LDR_PC_IMM(THUMB_R1, 4);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x02) = THUMB_MOV_HIREG(THUMB_HI_LR, THUMB_HI_PC);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x04) = THUMB_BX(THUMB_R1);
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x08) = entryAddress;
}
else if (patchContext.GetSdkVersion() >= 0x4007530)
{
patchOffset = 0x90;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_NOP;
__patch_carditaskthread_successoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x0C) + 9;
__patch_carditaskthread_failoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x14) + 9;
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x00) = THUMB_LDR_PC_IMM(THUMB_R1, 4);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x02) = THUMB_MOV_HIREG(THUMB_HI_LR, THUMB_HI_PC);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x04) = THUMB_BX(THUMB_R1);
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x08) = entryAddress;
}
else if (patchContext.GetSdkVersion() >= 0x3027531)
{
patchOffset = 0x98;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_NOP;
__patch_carditaskthread_successoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x0E) + 8;
__patch_carditaskthread_failoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x18) + 8;
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x00) = THUMB_LDR_PC_IMM(THUMB_R1, 4);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x02) = THUMB_MOV_HIREG(THUMB_HI_LR, THUMB_HI_PC);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x04) = THUMB_BX(THUMB_R1);
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x08) = entryAddress;
}
else if (patchContext.GetSdkVersion() >= 0x3012776
|| (patchContext.GetSdkVersion() >= 0x2027532 && patchContext.GetSdkVersion() <= 0x2027535))
{
patchOffset = 0x82;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_NOP;
__patch_carditaskthread_successoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x0E) + 8;
__patch_carditaskthread_failoffset = *(u16*)((u8*)_cardiTaskThread + patchOffset + 0x18) + 8;
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x00) = THUMB_LDR_PC_IMM(THUMB_R1, 4);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x02) = THUMB_MOV_HIREG(THUMB_HI_LR, THUMB_HI_PC);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x04) = THUMB_BX(THUMB_R1);
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x06) = entryAddress;
}
else if (patchContext.GetSdkVersion() >= 0x2004FB0)
{
patchOffset = patchContext.GetSdkVersion() >= 0x2007531 ? 0x60 : 0x64;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_MOVS_REG(THUMB_R4, THUMB_R5);
__patch_carditaskthread_failoffset = 0;
__patch_carditaskthread_successoffset = 0;
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x00) = THUMB_LDR_PC_IMM(THUMB_R1, 0);
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x02) = 0xE001; // jump over entryAddress
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x04) = entryAddress;
*(u16*)((u8*)_cardiTaskThread + patchOffset + 0x08) = THUMB_NOP;
}
else
{
LOG_FATAL("Unsupported thumb CARDi_TaskThread\n");
while(1);
}
}
else
{
if (patchContext.GetSdkVersion() < 0x2020000 && !_peach)
{
// uses a table dispatch
//0xA4 = ldr r1,= table
__patch_carditaskthread_failoffset = 4;
__patch_carditaskthread_successoffset = 4;
if (patchContext.GetSdkVersion() >= 0x2005015)//0x2007532)//0x2012774)
patchOffset = 0xA4;
else
patchOffset = 0xA0;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_MOV_HIREG(THUMB_R4, THUMB_HI_R10);
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x00) = 0xe59f0004; // ldr r0,= entryAddress
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x04) = 0xe1a0e00f; // mov lr, pc
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x08) = 0xe12fff10; // bx r0
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x0C) = entryAddress;
}
else
{
if (patchContext.GetSdkVersion() >= 0x4027531)
{
patchOffset = 0xA8;
entryAddress = (u32)&__patch_carditaskthread_entry_sdk4 - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_NOP;
}
else if (patchContext.GetSdkVersion() >= 0x4007530 || _pokemonDownloader)
{
patchOffset = 0xA8;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_NOP;
}
else if (patchContext.GetSdkVersion() >= 0x3017531)
{
patchOffset = 0xB4;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_MOV_HIREG(THUMB_R4, THUMB_HI_R9);
}
else
{
patchOffset = 0x9C;
entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_MOV_HIREG(THUMB_R4, THUMB_HI_R9);
}
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x00) = 0xe59f000C; // ldr r0,= entryAddress
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x04) = 0xe1a0e00f; // mov lr, pc
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x08) = 0xe12fff10; // bx r0
*(u32*)((u8*)_cardiTaskThread + patchOffset + 0x14) = entryAddress;
}
}
memcpy(patch1Address, SECTION_START(patch_carditaskthread), patch1Size);
}
bool CardiTaskThreadPatch::CheckSignature(const PatchContext& patchContext, const FunctionSignature& signature)
{
if (patchContext.GetSdkVersion() >= signature.GetMinimumSdkVersion() &&
patchContext.GetSdkVersion() <= signature.GetMaximumSdkVersion())
{
_cardiTaskThread = patchContext.FindPattern32(signature.GetPattern(), 16);
if (_cardiTaskThread)
{
LOG_DEBUG("CARDi_TaskThread found at 0x%p\n", _cardiTaskThread);
return true;
}
}
return false;
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include "patches/Patch.h"
class FunctionSignature;
/// @brief Arm7 patch for redirecting save reads and writes on SDK 2-4.
class CardiTaskThreadPatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _cardiTaskThread = nullptr;
u16 _thumb = false;
u16 _peach = false;
u16 _pokemonDownloader = false;
bool CheckSignature(const PatchContext& patchContext, const FunctionSignature& signature);
};

View File

@@ -0,0 +1,139 @@
#include "common.h"
#include "patches/PatchContext.h"
#include "thumbInstructions.h"
#include "fileInfo.h"
#include "patches/arm7/ReadSaveAsm.h"
#include "patches/arm7/WriteSaveAsm.h"
#include "patches/arm7/SaveOffsetToSdSectorAsm.h"
#include "patches/platform/LoaderPlatform.h"
#include "patches/OffsetToSectorRemapAsm.h"
#include "patches/arm7/CardiTaskThreadPatchAsm.h"
#include "CardiDoTaskFromArm9Patch.h"
static const u32 sCARDiDoTaskFromARM9Pattern5007538[] = { 0xE92D4038u, 0xE59F4190u, 0xE3A01001u, 0xE5943000u }; // this one has branches instead of an offset table
static const u32 sCARDiDoTaskFromARM9Pattern5017537[] = { 0xE92D4070u, 0xE59F5168u, 0xE3A01001u, 0xE5953000u };
static const u32 sCARDiDoTaskFromARM9Pattern5057530[] = { 0xE92D4070u, 0xE59F5174u, 0xE3A01001u, 0xE5953000u };
static const u32 sCARDiDoTaskFromARM9Pattern5017537Thumb[] = { 0x4C3FB538u, 0x68214A3Fu, 0x23016E92u, 0x40936D88u };
static const u32 sCARDiDoTaskFromARM9Pattern5037531Thumb[] = { 0x4C3EB538u, 0x68214A3Eu, 0x23016E92u, 0x40936D88u };
static const u32 sCARDiDoTaskFromARM9Pattern5057530Thumb[] = { 0x4C40B538u, 0x68214A40u, 0x23016E92u, 0x40936D88u };
void CardiDoTaskFromArm9Patch::TryPattern(PatchContext& patchContext, const u32* pattern)
{
_cardiDoTaskFromArm9 = patchContext.FindPattern32(pattern, 16);
if (_cardiDoTaskFromArm9)
{
_foundPattern = pattern;
}
}
bool CardiDoTaskFromArm9Patch::FindPatchTarget(PatchContext& patchContext)
{
if (patchContext.GetSdkVersion() <= 0x5027535)
TryPattern(patchContext, sCARDiDoTaskFromARM9Pattern5007538);
if (!_cardiDoTaskFromArm9 && patchContext.GetSdkVersion() >= 0x5017537 && patchContext.GetSdkVersion() <= 0x5057534)
TryPattern(patchContext, sCARDiDoTaskFromARM9Pattern5017537);
if (!_cardiDoTaskFromArm9 && patchContext.GetSdkVersion() >= 0x5057530)
TryPattern(patchContext, sCARDiDoTaskFromARM9Pattern5057530);
if (!_cardiDoTaskFromArm9)
{
TryPattern(patchContext, sCARDiDoTaskFromARM9Pattern5057530Thumb);
if (!_cardiDoTaskFromArm9)
TryPattern(patchContext, sCARDiDoTaskFromARM9Pattern5037531Thumb);
if (!_cardiDoTaskFromArm9)
TryPattern(patchContext, sCARDiDoTaskFromARM9Pattern5017537Thumb);
if (_cardiDoTaskFromArm9)
{
_thumb = true;
}
}
if (_cardiDoTaskFromArm9)
{
LOG_DEBUG("CARDi_DoTaskFromARM9 found at 0x%p\n", _cardiDoTaskFromArm9);
}
return _cardiDoTaskFromArm9 != nullptr;
}
void CardiDoTaskFromArm9Patch::ApplyPatch(PatchContext& patchContext)
{
if (!_cardiDoTaskFromArm9)
return;
u32 patch1Size = SECTION_SIZE(patch_carditaskthread);
void* patch1Address = patchContext.GetPatchHeap().Alloc(patch1Size);
auto loaderPlatform = patchContext.GetLoaderPlatform();
auto readPatchCode = loaderPlatform->CreateSdReadPatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
auto writePatchCode = loaderPlatform->CreateSdWritePatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
auto sectorRemapPatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<SaveOffsetToSdSectorPatchCode>(
patchContext.GetPatchHeap(), SHARED_SAVE_FILE_INFO);
void* tmpBuffer = patchContext.GetPatchHeap().Alloc(512);
auto readSavePatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<ReadSavePatchCode>(
patchContext.GetPatchHeap(),
sectorRemapPatchCode,
readPatchCode,
tmpBuffer
);
auto writeSavePatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<WriteSavePatchCode>(
patchContext.GetPatchHeap(),
sectorRemapPatchCode,
readPatchCode,
writePatchCode,
tmpBuffer
);
__patch_carditaskthread_readsave_asm_address = (u32)readSavePatchCode->GetReadSaveFunction();
__patch_carditaskthread_writesave_asm_address = (u32)writeSavePatchCode->GetWriteSaveFunction();
u32 patchOffset;
if (_thumb)
{
patchOffset = _foundPattern == sCARDiDoTaskFromARM9Pattern5057530Thumb ? 0x3A : 0x36;
u32 entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_NOP;
__patch_carditaskthread_mov_command_to_r0 = THUMB_NOP;
__patch_carditaskthread_successoffset = *(u16*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x0C) + 9;
__patch_carditaskthread_failoffset = *(u16*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x14) + 9;
*(u16*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x00) = THUMB_LDR_PC_IMM(THUMB_R1, 4);
*(u16*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x02) = THUMB_MOV_HIREG(THUMB_HI_LR, THUMB_HI_PC);
*(u16*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x04) = THUMB_BX(THUMB_R1);
*(u32*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x06) = entryAddress;
}
else
{
if (_foundPattern == sCARDiDoTaskFromARM9Pattern5007538)
{
// 0x4C = cmp r0, #0xF
patchOffset = 0x4C;
__patch_carditaskthread_successoffset = 4;
__patch_carditaskthread_failoffset = ((*(u32*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x08) & 0xFFFFFF) << 2) + 4;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_NOP; // already in r4
__patch_carditaskthread_mov_command_to_r0 = THUMB_NOP;
}
else
{
// 0x58 = add r0, pc, r0, lsl #1
if (_foundPattern == sCARDiDoTaskFromARM9Pattern5057530)
patchOffset = 0x58;
else
patchOffset = 0x54;
__patch_carditaskthread_successoffset = *(u16*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x0C) + 4;
__patch_carditaskthread_failoffset = *(u16*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x14) + 4;
__patch_carditaskthread_mov_cardicommon_to_r4 = THUMB_MOVS_REG(THUMB_R4, THUMB_R5);
__patch_carditaskthread_mov_command_to_r0 = THUMB_NOP;
}
u32 entryAddress = (u32)&__patch_carditaskthread_entry - (u32)SECTION_START(patch_carditaskthread) + (u32)patch1Address;
*(u32*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x00) = 0xe59f1004; // ldr r1,= entryAddress
*(u32*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x04) = 0xe1a0e00f; // mov lr, pc
*(u32*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x08) = 0xe12fff11; // bx r1
*(u32*)((u8*)_cardiDoTaskFromArm9 + patchOffset + 0x0C) = entryAddress;
}
memcpy(patch1Address, SECTION_START(patch_carditaskthread), patch1Size);
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include "patches/Patch.h"
/// @brief Arm7 patch for redirecting save reads and writes on SDK 5.
class CardiDoTaskFromArm9Patch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _cardiDoTaskFromArm9 = nullptr;
const u32* _foundPattern = nullptr;
u16 _thumb = false;
void TryPattern(PatchContext& patchContext, const u32* pattern);
};

View File

@@ -0,0 +1,14 @@
#pragma once
#include "sections.h"
DEFINE_SECTION_SYMBOLS(patch_cardidotaskfromarm9);
extern "C" void __patch_cardidotaskfromarm9_entry();
extern u32 __patch_cardidotaskfromarm9_failoffset;
extern u32 __patch_cardidotaskfromarm9_successoffset;
extern u16 __patch_cardidotaskfromarm9_move_statereg;
extern u32 __patch_cardidotaskfromarm9_readsave_asm_address;
extern u32 __patch_cardidotaskfromarm9_writesave_asm_address;

View File

@@ -0,0 +1,82 @@
#include "common.h"
#include "patches/platform/LoaderPlatform.h"
#include "Sdk5DsiSdCardRedirectPatchAsm.h"
#include "Sdk5DsiSdCardRedirectPatch.h"
static const u32 sAttachFunctionPattern[] = { 0xE92D4018u, 0xE24DDF5Du, 0xE24DDB01u, 0xE59FE050u };
static const u32 sAttachFunctionPatternThumb[] = { 0xB0FFB518u, 0xB0DFB0FFu, 0x4A0E490Du, 0x64CA4469u };
bool Sdk5DsiSdCardRedirectPatch::FindPatchTarget(PatchContext& patchContext)
{
_attachFunction = patchContext.FindPattern32Twl(sAttachFunctionPattern, sizeof(sAttachFunctionPattern));
if (!_attachFunction)
{
_attachFunction = patchContext.FindPattern32Twl(sAttachFunctionPatternThumb, sizeof(sAttachFunctionPatternThumb));
if (_attachFunction)
{
_thumb = true;
}
}
if (_attachFunction)
{
LOG_DEBUG("Found FATFSi_sdmcRtfsAttach at %p\n", _attachFunction);
}
else
{
LOG_WARNING("FATFSi_sdmcRtfsAttach not found\n");
}
return _attachFunction != nullptr;
}
static u32 getArmBlAddress(const u32* instructionPointer)
{
u32 blInstruction = *instructionPointer;
return (u32)instructionPointer + 8 + ((int)((blInstruction & 0xFFFFFF) << 8) >> 6);
}
static u32 getThumbBlAddress(const u32* instructionPointer)
{
u32 blInstruction1 = ((u16*)instructionPointer)[0];
u32 blInstruction2 = ((u16*)instructionPointer)[1];
return (u32)instructionPointer + 5 + ((int)((((blInstruction1 & 0x7FF) << 11) | (blInstruction2 & 0x7FF)) << 10) >> 9);
}
void Sdk5DsiSdCardRedirectPatch::ApplyPatch(PatchContext& patchContext)
{
if (!_attachFunction)
return;
u32 getDriveStructAddress;
if (_thumb)
{
getDriveStructAddress = getThumbBlAddress((u32*)((u8*)_attachFunction - 0x20));
}
else
{
getDriveStructAddress = getArmBlAddress((u32*)((u8*)_attachFunction - 0x20));
}
auto sdRead = patchContext.GetLoaderPlatform()->CreateSdReadPatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
auto sdWrite = patchContext.GetLoaderPlatform()->CreateSdWritePatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
auto patch = patchContext.GetPatchCodeCollection().AddUniquePatchCode<Sdk5DsiSdCardRedirectPatchCode>
(
patchContext.GetPatchHeap(),
sdRead,
sdWrite,
getDriveStructAddress
);
if (_thumb)
{
*(u32*)((u8*)_attachFunction + 0x44) = (u32)patch->GetIoFunction();
*(u32*)((u8*)_attachFunction + 0x48) = (u32)patch->GetControlFunction();
}
else
{
*(u32*)((u8*)_attachFunction + 0x64) = (u32)patch->GetIoFunction();
*(u32*)((u8*)_attachFunction + 0x68) = (u32)patch->GetControlFunction();
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "patches/Patch.h"
/// @brief Arm7 patch for redirecting access to the DSi sd card to the flashcard sd card.
class Sdk5DsiSdCardRedirectPatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _attachFunction = nullptr;
u16 _thumb = false;
};

View File

@@ -0,0 +1,37 @@
#pragma once
#include "patches/PatchCode.h"
#include "sections.h"
#include "patches/platform/SdReadPatchCode.h"
#include "patches/platform/SdWritePatchCode.h"
DEFINE_SECTION_SYMBOLS(patch_dsisdredirect);
extern "C" bool __patch_dsisdredirect_io(u32 driveNumber, u32 startSector, void* buffer, u32 sectorCount, bool isRead);
extern "C" u32 __patch_dsisdredirect_control(u32 driveNumber, u32 command, void* argumentBuffer);
extern u32 __patch_dsisdredirect_io_readsd_asm_address;
extern u32 __patch_dsisdredirect_io_writesd_asm_address;
extern u32 __patch_dsisdredirect_control_get_drive_struct_address;
class Sdk5DsiSdCardRedirectPatchCode : public PatchCode
{
public:
Sdk5DsiSdCardRedirectPatchCode(PatchHeap& patchHeap, const SdReadPatchCode* sdReadPatchCode,
const SdWritePatchCode* sdWritePatchCode, u32 getDriveStructAddress)
: PatchCode(SECTION_START(patch_dsisdredirect), SECTION_SIZE(patch_dsisdredirect), patchHeap)
{
__patch_dsisdredirect_io_readsd_asm_address = (u32)sdReadPatchCode->GetSdReadFunction();
__patch_dsisdredirect_io_writesd_asm_address = (u32)sdWritePatchCode->GetSdWriteFunction();
__patch_dsisdredirect_control_get_drive_struct_address = getDriveStructAddress;
}
const void* GetIoFunction() const
{
return GetAddressAtTarget((void*)__patch_dsisdredirect_io);
}
const void* GetControlFunction() const
{
return GetAddressAtTarget((void*)__patch_dsisdredirect_control);
}
};

View File

@@ -0,0 +1,80 @@
.cpu arm7tdmi
.section "patch_dsisdredirect", "ax"
.syntax unified
.thumb
// r0 = drive number
// r1 = start sector
// r2 = source/destination buffer
// r3 = sector count
// [sp] = isReading (0 = write, otherwise read)
.global __patch_dsisdredirect_io
.type __patch_dsisdredirect_io, %function
__patch_dsisdredirect_io:
push {r4,lr}
movs r0, r1
movs r1, r2
movs r2, r3
ldr r4, [sp, #8] // reading
cmp r4, #0
bne 1f
ldr r3, __patch_dsisdredirect_io_writesd_asm_address
bl blx_r3
movs r0, #1 // success
b return
1:
ldr r3, __patch_dsisdredirect_io_readsd_asm_address
bl blx_r3
movs r0, #1 // success
return:
pop {r4}
pop {r3}
blx_r3:
bx r3
// r0 = drive number
// r1 = command
// r2 = argument buffer
.global __patch_dsisdredirect_control
.type __patch_dsisdredirect_control, %function
__patch_dsisdredirect_control:
push {lr}
cmp r1, #1 // startup
bne returnZero
ldr r3, __patch_dsisdredirect_control_get_drive_struct_address
bl blx_r3
ldr r1,= 0x4B4
movs r2, #0
str r2, [r0, r1] // partition 0
subs r1, r1, #4
ldr r2, [r0, r1]
movs r3, #0x83 // valid, partitioned, inserted
orrs r2, r2, r3
str r2, [r0, r1]
returnZero:
movs r0, #0
pop {r3}
bx r3
.balign 4
.global __patch_dsisdredirect_io_readsd_asm_address
__patch_dsisdredirect_io_readsd_asm_address:
.word 0
.global __patch_dsisdredirect_io_writesd_asm_address
__patch_dsisdredirect_io_writesd_asm_address:
.word 0
.global __patch_dsisdredirect_control_get_drive_struct_address
__patch_dsisdredirect_control_get_drive_struct_address:
.word 0
.pool
.end

View File

@@ -0,0 +1,142 @@
#include "common.h"
#include "thumbInstructions.h"
#include "../PatchContext.h"
#include "../FunctionSignature.h"
#include "CardiReadRomIdCorePatchAsm.h"
#include "CardiReadRomIdCorePatch.h"
static constexpr auto sSignaturesArm = std::to_array<const FunctionSignature>
({
{ (const u32[]) { 0xE92D4000u, 0xE24DD004u, 0xE3A0032Eu, 0xE3A01000u }, 0x02004F50, 0x04017531 },
{ (const u32[]) { 0xE92D4008u, 0xE3A0032Eu, 0xE3A01000u, 0xEBFFFF0Bu }, 0x03007532, 0x03017531 },
{ (const u32[]) { 0xE92D4008u, 0xE3A0032Eu, 0xE3A01000u, 0xEBFFFF08u }, 0x03017530, 0x03017534 },
{ (const u32[]) { 0xE92D4008u, 0xE3A0032Eu, 0xE3A01000u, 0xEBFFFF07u }, 0x03027530, 0x04007531 },
{ (const u32[]) { 0xE92D4008u, 0xE3A0032Eu, 0xE3A01000u, 0xEBFFFEF2u }, 0x04007531, 0x04007531 },
{ (const u32[]) { 0xE92D4008u, 0xE3A0032Eu, 0xE3A01000u, 0xEBFFFEFDu }, 0x04007532, 0x04007532 },
{ (const u32[]) { 0xE92D4008u, 0xE3A0032Eu, 0xE3A01000u, 0xEBFFFEFBu }, 0x04017530, 0x04027539 },
{ (const u32[]) { 0xE92D4008u, 0xE3A0032Eu, 0xE3A01000u, 0xEBFFFEE6u }, 0x04017533, 0x04017533 },
{ (const u32[]) { 0xE92D4010u, 0xE3A04000u, 0xE1A01004u, 0xE3A0032Eu }, 0x04027537, 0x04027539 }
});
static constexpr auto sSignaturesThumb = std::to_array<const FunctionSignature>
({
{ (const u32[]) { 0xB081B500u, 0x2100480Bu, 0xF98AF000u, 0x6801480Au }, 0x03007530, 0x03012776 },
{ (const u32[]) { 0xB081B500u, 0x2100480Bu, 0xF990F000u, 0x6801480Au }, 0x03007532, 0x03007532 },
{ (const u32[]) { 0x202EB508u, 0x21000680u, 0xFE8CF7FFu, 0x68014809u }, 0x03017531, 0x03017531 },
{ (const u32[]) { 0xB081B500u, 0x2100480Bu, 0xF98EF000u, 0x6801480Au }, 0x03017531, 0x03017534 },
{ (const u32[]) { 0xB081B500u, 0x2100480Cu, 0xF992F000u, 0x6800480Bu }, 0x03027530, 0x03027532 },
{ (const u32[]) { 0x202EB508u, 0x21000680u, 0xFE8AF7FFu, 0x6800480Au }, 0x03027530, 0x04007531 },
{ (const u32[]) { 0x202EB508u, 0x21000680u, 0xFE74F7FFu, 0x6800480Au }, 0x04017530, 0x04027539 },
{ (const u32[]) { 0x202EB508u, 0x21000680u, 0xFE62F7FFu, 0x6800480Au }, 0x04027531, 0x04027531 },
{ (const u32[]) { 0x202EB508u, 0x21000680u, 0xFE56F7FFu, 0x6800480Au }, 0x04027537, 0x04027537 }
});
static constexpr auto sSignaturesArmSdk5 = std::to_array<const FunctionSignature>
({
{ (const u32[]) { 0xE92D4010u, 0xE3A040B8u, 0xEBFFFFD6u, 0xE3500000u }, 0x0500753B, 0x05027534 },
{ (const u32[]) { 0xE92D4008u, 0xE3A000B8u, 0xE3A01000u, 0xEBFFFFCEu }, 0x05017536, 0x05047531 },
{ (const u32[]) { 0xE92D4038u, 0xE3A050B8u, 0xEBFFFFD7u, 0xE3500000u }, 0x05017537, 0x05057535 },
{ (const u32[]) { 0xE92D4010u, 0xE3A04000u, 0xE1A01004u, 0xE3A000B8u }, 0x05037531, 0x05057535 }
});
static constexpr auto sSignaturesThumbSdk5 = std::to_array<const FunctionSignature>
({
{ (const u32[]) { 0x24B8B510u, 0xFFC4F7FFu, 0xD0002800u, 0x1C202490u }, 0x05007538, 0x05017537 },
{ (const u32[]) { 0x20B8B508u, 0xF7FF2100u, 0x480AFFABu, 0x480A6801u }, 0x05017537, 0x05057535 },
{ (const u32[]) { 0x24B8B510u, 0xFFC6F7FFu, 0xD0002800u, 0x1C202490u }, 0x05037531, 0x05057534 }
});
bool CardiReadRomIdCorePatch::FindPatchTarget(PatchContext& patchContext)
{
if (patchContext.GetSdkVersion().IsTwlSdk())
{
for (const auto& signature : sSignaturesArmSdk5)
{
if (CheckSignature(patchContext, signature))
{
break;
}
}
if (!_cardiReadRomIdCore)
{
for (const auto& signature : sSignaturesThumbSdk5)
{
if (CheckSignature(patchContext, signature))
{
_thumb = true;
break;
}
}
}
}
else
{
for (const auto& signature : sSignaturesArm)
{
if (CheckSignature(patchContext, signature))
{
break;
}
}
if (!_cardiReadRomIdCore)
{
for (const auto& signature : sSignaturesThumb)
{
if (CheckSignature(patchContext, signature))
{
_thumb = true;
break;
}
}
}
}
if (!_cardiReadRomIdCore)
{
LOG_WARNING("CARDi_ReadRomIDCore not found\n");
}
return true; //_cardiReadRomIdCore != nullptr;
}
void CardiReadRomIdCorePatch::ApplyPatch(PatchContext& patchContext)
{
if (!_cardiReadRomIdCore)
return;
auto patchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<CardiReadRomIdCorePatchCode>
(
patchContext.GetPatchHeap(),
(const void*)(patchContext.GetSdkVersion().IsTwlSdk() ? 0x02FFFC00 : 0x027FFC00)
);
if (_thumb)
{
((u16*)_cardiReadRomIdCore)[0] = 0x4800;
((u16*)_cardiReadRomIdCore)[1] = 0x4700;
}
else
{
_cardiReadRomIdCore[0] = 0xE51FF004;
}
_cardiReadRomIdCore[1] = (u32)patchCode->GetCardiReadRomIdCoreFunction();
}
bool CardiReadRomIdCorePatch::CheckSignature(const PatchContext& patchContext, const FunctionSignature& signature)
{
if (patchContext.GetSdkVersion() >= signature.GetMinimumSdkVersion() &&
patchContext.GetSdkVersion() <= signature.GetMaximumSdkVersion())
{
_cardiReadRomIdCore = patchContext.FindPattern32(signature.GetPattern(), 16);
if (_cardiReadRomIdCore)
{
LOG_DEBUG("Found CARDi_ReadRomIDCore at %p\n", _cardiReadRomIdCore);
return true;
}
}
return false;
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include "../Patch.h"
class FunctionSignature;
/// @brief Arm9 patch to avoid card id reads.
class CardiReadRomIdCorePatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
bool CheckSignature(const PatchContext& patchContext, const FunctionSignature& signature);
u32* _cardiReadRomIdCore = nullptr;
u16 _thumb = false;
};

View File

@@ -0,0 +1,24 @@
#pragma once
#include "../PatchCode.h"
#include "sections.h"
DEFINE_SECTION_SYMBOLS(patch_cardireadromidcore);
extern "C" u32 patch_cardireadromidcore_entry(void);
extern u32 patch_cardireadromidcore_cardid_address;
class CardiReadRomIdCorePatchCode : public PatchCode
{
public:
CardiReadRomIdCorePatchCode(PatchHeap& patchHeap, const void* cardIdPointer)
: PatchCode(SECTION_START(patch_cardireadromidcore), SECTION_SIZE(patch_cardireadromidcore), patchHeap)
{
patch_cardireadromidcore_cardid_address = (u32)cardIdPointer;
}
const void* GetCardiReadRomIdCoreFunction() const
{
return GetAddressAtTarget((void*)patch_cardireadromidcore_entry);
}
};

View File

@@ -0,0 +1,21 @@
.cpu arm946e-s
.section "patch_cardireadromidcore", "ax"
.syntax unified
.thumb
.global patch_cardireadromidcore_entry
.type patch_cardireadromidcore_entry, %function
patch_cardireadromidcore_entry:
ldr r0, patch_cardireadromidcore_cardid_address
ldr r0, [r0]
bx lr
.balign 4
.global patch_cardireadromidcore_cardid_address
patch_cardireadromidcore_cardid_address:
.word 0x027FFC00
.pool
.end

View File

@@ -0,0 +1,6 @@
#pragma once
#include "sections.h"
DEFINE_SECTION_SYMBOLS(fixcp15);
extern "C" void fix_cp15_asm();

View File

@@ -0,0 +1,26 @@
.cpu arm946e-s
.section "fixcp15", "ax"
.syntax unified
.arm
// to fix access to gba slot memory
.global fix_cp15_asm
.type fix_cp15_asm, %function
fix_cp15_asm:
mrc p15, 0, r0, c2, c0, 0
bic r0, r0, #(1 << 3)
mcr p15, 0, r0, c2, c0, 0
mrc p15, 0, r0, c3, c0, 0
bic r0, r0, #(1 << 3)
mcr p15, 0, r0, c3, c0, 0
mrc p15, 0, r0, c5, c0, 2
bic r0, r0, #(0xF << 12)
orr r0, r0, #(3 << 12)
mcr p15, 0, r0, c5, c0, 2
bx lr
.balign 4
.pool
.end

View File

@@ -0,0 +1,125 @@
#include "common.h"
#include "../PatchContext.h"
#include "thumbInstructions.h"
#include "../platform/LoaderPlatform.h"
#include "OSResetSystemPatchAsm.h"
#include "OSResetSystemPatch.h"
static const u32 sOSResetSystemPatternSdk2Old[] = { 0xE59F101Cu, 0xE3A00010u, 0xE5815000u, 0xEB000005u };
static const u32 sOSResetSystemPatternSdk2[] = { 0xE59F1018u, 0xE3A00010u, 0xE5814000u, 0xEB000004u };
static const u32 sOSResetSystemPatternSdk3[] = { 0xE59F1014u, 0xE3A00010u, 0xE5814000u, 0xEBFFFFD9u };
static const u32 sOSResetSystemPatternSdk4[] = { 0xE59F103Cu, 0xE59F003Cu, 0xE5814000u, 0xEBFFFFD6u };
static const u32 sOSResetSystemPatternPokemonDownloader[] = { 0xE59F1010u, 0xE3A00010u, 0xE5814000u, 0xEBFFFFDEu };
static const u32 sOSResetSystemPatternSdk5Old[] = { 0xE1A04000u, 0xE1D100B0u, 0xE3500002u, 0x1A000000u };
static const u32 sOSResetSystemPatternSdk5New[] = { 0xE1A05000u, 0xE1D100B0u, 0xE3500002u, 0x1A000000u };
static const u32 sOSResetSystemPatternSdk5HybridOld[] = { 0xE1A04000u, 0xE1D100B0u, 0xE3500002u, 0x0A000006 };
static const u32 sOSResetSystemPatternSdk5HybridNew[] = { 0xE1A05000u, 0xE1D100B0u, 0xE3500002u, 0x0A000006 };
bool OSResetSystemPatch::FindPatchTarget(PatchContext& patchContext)
{
_osResetSystem = nullptr;
if (patchContext.GetSdkVersion().IsTwlSdk())
{
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk5Old, sizeof(sOSResetSystemPatternSdk5Old));
if (!_osResetSystem)
{
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk5New, sizeof(sOSResetSystemPatternSdk5New));
}
if (!_osResetSystem)
{
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk5HybridOld, sizeof(sOSResetSystemPatternSdk5HybridOld));
_hybrid = true;
}
if (!_osResetSystem)
{
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk5HybridNew, sizeof(sOSResetSystemPatternSdk5HybridNew));
_hybrid = true;
}
}
else
{
if (patchContext.GetSdkVersion() >= 0x4017530)
{
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk4, sizeof(sOSResetSystemPatternSdk4));
}
if (!_osResetSystem && patchContext.GetSdkVersion() >= 0x3017530)
{
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk3, sizeof(sOSResetSystemPatternSdk3));
}
if (!_osResetSystem && patchContext.GetSdkVersion() >= 0x2004F50)
{
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk2, sizeof(sOSResetSystemPatternSdk2));
}
if (!_osResetSystem && patchContext.GetSdkVersion() >= 0x2004EE9)
{
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternSdk2Old, sizeof(sOSResetSystemPatternSdk2Old));
}
if (!_osResetSystem)
{
_osResetSystem = patchContext.FindPattern32(sOSResetSystemPatternPokemonDownloader, sizeof(sOSResetSystemPatternPokemonDownloader));
}
}
if (_osResetSystem)
{
LOG_DEBUG("Found end of OS_ResetSystem at %p\n", _osResetSystem);
}
else
{
LOG_DEBUG("OS_ResetSystem not found\n");
}
return true;
}
void OSResetSystemPatch::ApplyPatch(PatchContext& patchContext)
{
if (!_osResetSystem)
{
return;
}
u32 offset;
if (patchContext.GetSdkVersion().IsTwlSdk())
{
patch_osresetsystem_arm7Entry_address = 0x02FFFE34;
if (_hybrid)
{
offset = 0x80;
if (!gIsDsiMode)
{
patch_osresetsystem_entry_jump_to_twl_arm7_sync = THUMB_NOP;
}
}
else
{
offset = 0x44;
patch_osresetsystem_entry_jump_to_twl_arm7_sync = THUMB_NOP;
}
}
else
{
offset = 0xC;
patch_osresetsystem_entry_jump_to_twl_arm7_sync = THUMB_NOP;
}
auto loaderInfoTarget = (loader_info_t*)patchContext.GetPatchHeap().Alloc(sizeof(loader_info_t));
memcpy(loaderInfoTarget, _loaderInfo, sizeof(loader_info_t));
auto sdReadPatchCode = patchContext.GetLoaderPlatform()->CreateSdReadPatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
auto patchCodePart2 = patchContext.GetPatchCodeCollection().AddUniquePatchCode<OSResetSystemPart2PatchCode>(
patchContext.GetPatchHeap());
auto patchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<OSResetSystemPatchCode>
(
patchContext.GetPatchHeap(),
loaderInfoTarget,
sdReadPatchCode,
patchCodePart2
);
*(u32*)((u8*)_osResetSystem + offset) = 0xE51FF004;
*(u32*)((u8*)_osResetSystem + offset + 4) = (u32)patchCode->GetOSResetSystemFunction();
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include "../Patch.h"
#include "LoaderInfo.h"
/// @brief Arm9 patch to make OS_ResetSystem reboot into Pico Loader.
class OSResetSystemPatch : public Patch
{
public:
explicit OSResetSystemPatch(const loader_info_t* loaderInfo)
: _loaderInfo(loaderInfo) { }
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _osResetSystem = nullptr;
u32 _hybrid = false;
const loader_info_t* _loaderInfo;
};

View File

@@ -0,0 +1,49 @@
#pragma once
#include "../PatchCode.h"
#include "sections.h"
#include "LoaderInfo.h"
#include "../platform/SdReadPatchCode.h"
DEFINE_SECTION_SYMBOLS(patch_osresetsystem);
DEFINE_SECTION_SYMBOLS(patch_osresetsystem_boot);
extern "C" void patch_osresetsystem_entry(void);
extern "C" void patch_osresetsystem_bootPicoLoader(void);
extern const loader_info_t* patch_osresetsystem_loader_info_address;
extern u32 patch_osresetsystem_readSdSectors_address;
extern u32 patch_osresetsystem_bootPicoLoader_address;
extern u16 patch_osresetsystem_entry_jump_to_twl_arm7_sync;
extern u32 patch_osresetsystem_arm7Entry_address;
class OSResetSystemPart2PatchCode : public PatchCode
{
public:
explicit OSResetSystemPart2PatchCode(PatchHeap& patchHeap)
: PatchCode(SECTION_START(patch_osresetsystem_boot), SECTION_SIZE(patch_osresetsystem_boot), patchHeap)
{
}
const void* GetOSResetSystemPart2Function() const
{
return GetAddressAtTarget((void*)patch_osresetsystem_bootPicoLoader);
}
};
class OSResetSystemPatchCode : public PatchCode
{
public:
OSResetSystemPatchCode(PatchHeap& patchHeap, const loader_info_t* loaderInfo,
const SdReadPatchCode* sdReadPatchCode, const OSResetSystemPart2PatchCode* part2PatchCode)
: PatchCode(SECTION_START(patch_osresetsystem), SECTION_SIZE(patch_osresetsystem), patchHeap)
{
patch_osresetsystem_loader_info_address = loaderInfo;
patch_osresetsystem_readSdSectors_address = (u32)sdReadPatchCode->GetSdReadFunction();
patch_osresetsystem_bootPicoLoader_address = (u32)part2PatchCode->GetOSResetSystemPart2Function();
}
const void* GetOSResetSystemFunction() const
{
return GetAddressAtTarget((void*)patch_osresetsystem_entry);
}
};

View File

@@ -0,0 +1,178 @@
.cpu arm946e-s
.section "patch_osresetsystem", "ax"
.syntax unified
.thumb
.global patch_osresetsystem_entry
.type patch_osresetsystem_entry, %function
patch_osresetsystem_entry:
adr r0, regIpcSync
// regIpcSync, arm7ResetCommand, regVramCntA, vramAbcdLcdcSetting, readSdSectors_address, loader_info_address, vramCLcdcAddress, patch_osresetsystem_bootPicoLoader_address
ldmia r0, {r0, r1, r2, r3, r4, r5, r6, r7}
// r9 = readSdSectors_address
mov r9, r4
mov r10, r7
// reset arm7
1:
ldr r6, [r0, #4]
lsrs r6, r6, #2 // shift bit 1 to carry
bcs 1b // while fifo full
str r1, [r0, #8]
adds r7, #(twl_arm7_sync - patch_osresetsystem_bootPicoLoader)
mov lr, pc
.global patch_osresetsystem_entry_jump_to_twl_arm7_sync
patch_osresetsystem_entry_jump_to_twl_arm7_sync:
bx r7
2:
ldrb r7, [r0] // ipc sync
cmp r7, #1
bne 2b // while ipc sync from arm7 is not 1
// map vram ABCD to LCDC
str r3, [r2]
// load pico loader arm9
ldmia r5!, {r4,r6,r7} // clusterShift, database, clusterMap[0]
ldr r7,= 0x06800000
mov r11, pc
b loadData
// load pico loader arm7
ldr r5, patch_osresetsystem_loader_info_address
adds r5, #52
ldr r7, vramCLcdcAddress
mov r11, pc
b loadData
adr r5, patch_osresetsystem_loader_info_address
bx r10
loadData_loop:
subs r3, #2
lsls r3, r4
adds r0, r3, r6 // start sector
movs r1, r7 // dst
lsls r2, r4 // sector count
lsls r3, r2, #9
adds r7, r3
blx r9 // read sectors
loadData:
ldmia r5!, {r2, r3} // ncl, startSector
cmp r2, #0
bne loadData_loop
mov pc, r11
.balign 4
regIpcSync:
.word 0x04000180
arm7ResetCommand:
.word 0x4000C
regVramCntA:
.word 0x04000240
vramAbcdLcdcSetting:
.word 0x80808080
.global patch_osresetsystem_readSdSectors_address
patch_osresetsystem_readSdSectors_address:
.word 0
.global patch_osresetsystem_loader_info_address
patch_osresetsystem_loader_info_address:
.word 0
vramCLcdcAddress:
.word 0x06840000
.global patch_osresetsystem_bootPicoLoader_address
patch_osresetsystem_bootPicoLoader_address:
.word 0
.pool
.section "patch_osresetsystem_boot", "ax"
.thumb
.global patch_osresetsystem_bootPicoLoader
.type patch_osresetsystem_bootPicoLoader, %function
patch_osresetsystem_bootPicoLoader:
// loader_info_address, vramCLcdcAddress
ldmia r5, {r3, r5}
ldrh r0, [r3, #2] // loader_info_t::picoLoaderBootDrive
strh r0, [r5, #8] // pload_header7_t::bootDrive
ldr r0, [r5] // pload_header7_t::entryPoint
// set NTR_SHARED_MEMORY->romHeader.arm7EntryAddress
ldr r4, patch_osresetsystem_arm7Entry_address
str r0, [r4]
// map vram CD to arm7
ldr r0,= 0x04000240
ldr r7,= 0x8A82
strh r7, [r0, #2]
adds r0, #(0x04000180 - 0x04000240) // REG_IPC_SYNC
movs r1, #1
strb r1, [r0, #1]
1:
ldrb r7, [r0] // ipc sync
cmp r7, #0
bne 1b // while ipc sync from arm7 is not 0
movs r1, #0
strb r1, [r0, #1]
movs r0, #0x68
lsls r0, r0, #20 // 0x06800000
bx r0
twl_arm7_sync:
ldr r7,= 0x02FFFC24
movs r1, #1
strh r1, [r7, #4]
ldrh r6, [r7, #2]
ldrh r1, [r7]
1:
adds r1, #1
strh r1, [r7]
ldrh r4, [r7, #2]
cmp r6, r4
beq 1b
adds r1, #1
strh r1, [r7]
movs r1, #3
strh r1, [r7, #4]
ldrh r6, [r7, #2]
ldrh r1, [r7]
1:
adds r1, #1
strh r1, [r7]
ldrh r4, [r7, #2]
cmp r6, r4
beq 1b
adds r1, #1
strh r1, [r7]
mov pc, lr
.balign 4
.global patch_osresetsystem_arm7Entry_address
patch_osresetsystem_arm7Entry_address:
.word 0x027FFE34
.pool
.end

View File

@@ -0,0 +1,418 @@
#include "common.h"
#include "../../../PatchContext.h"
#include "thumbInstructions.h"
#include "gameCode.h"
#include "DSProtectOverlayPatchv1Asm.h"
#include "DSProtectOverlayPatchv2Asm.h"
#include "DSProtectOverlayPatchv2sAsm.h"
#include "DSProtectOverlayPatch.h"
const void* DSProtectOverlayPatch::InsertPatch(PatchContext& patchContext)
{
if (_version <= DSProtectVersion::v1_28)
{
return InsertPatchV1(patchContext);
}
else if (_version <= DSProtectVersion::v2_05)
{
return InsertPatchV2(patchContext);
}
else if (_version <= DSProtectVersion::v2_05s)
{
return InsertPatchV2s(patchContext);
}
else
{
LOG_WARNING("Unsupported DSProtect version\n");
return next ? (const void*)next->InsertPatch(patchContext) : nullptr;
}
}
void DSProtectOverlayPatch::SetOldV1Offsets(const u16* offsets) const
{
int offsetIdx = 0;
if (_functionMask & 0b000001)
dsprotectpatchv1_stub_protectb1_offset = offsets[offsetIdx++];
else
dsprotectpatchv1_stub_protectb1_offset = 0; // does not exist
if (_functionMask & 0b000010)
dsprotectpatchv1_stub_notprotectb1_offset = offsets[offsetIdx++];
else
dsprotectpatchv1_stub_notprotectb1_offset = 0; // does not exist
if (_functionMask & 0b000100)
dsprotectpatchv1_stub_protectb2_offset = offsets[offsetIdx++];
else
dsprotectpatchv1_stub_protectb2_offset = 0; // does not exist
if (_functionMask & 0b001000)
dsprotectpatchv1_stub_notprotectb2_offset = offsets[offsetIdx++];
else
dsprotectpatchv1_stub_notprotectb2_offset = 0; // does not exist
if (_functionMask & 0b010000)
dsprotectpatchv1_stub_protectb3_offset = offsets[offsetIdx++];
else
dsprotectpatchv1_stub_protectb3_offset = 0; // does not exist
if (_functionMask & 0b100000)
dsprotectpatchv1_stub_notprotectb3_offset = offsets[offsetIdx++];
else
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
}
const void* DSProtectOverlayPatch::InsertPatchV1(PatchContext& patchContext) const
{
const void* nextPatch = next ? (const void*)next->InsertPatch(patchContext) : nullptr;
dsprotectpatchv1_overlay_id = _overlayId;
dsprotectpatchv1_base_offset = _overlayOffset;
switch (_version)
{
case DSProtectVersion::v1_05:
{
static u16 v105Offsets[] = { 0x858, 0x934, 0xA10, 0xAEC, 0xBC8, 0xC94 };
SetOldV1Offsets(v105Offsets);
dsprotectpatchv1_nitro_static_init_offset = 0; // does not exist
dsprotectpatchv1_protectb1_return_value = 0;
dsprotectpatchv1_protectb2_return_value = 0;
dsprotectpatchv1_protectb3_return_value = 0;
dsprotectpatchv1_notprotectb1_return_value = 1;
dsprotectpatchv1_notprotectb2_return_value = 1;
dsprotectpatchv1_notprotectb3_return_value = 1;
// Remove xor 0x320 from the callback pointer
dsprotectpatchv1_moveCallback = 0xE230EE32; // eors lr, r0, #0x320
break;
}
case DSProtectVersion::v1_06:
{
static u16 v106Offsets[] = { 0x860, 0x938, 0xA10, 0xAE8, 0xBC0, 0xC88 };
SetOldV1Offsets(v106Offsets);
dsprotectpatchv1_nitro_static_init_offset = 0; // does not exist
dsprotectpatchv1_protectb1_return_value = 0;
dsprotectpatchv1_protectb2_return_value = 0;
dsprotectpatchv1_protectb3_return_value = 0;
dsprotectpatchv1_notprotectb1_return_value = 1;
dsprotectpatchv1_notprotectb2_return_value = 1;
dsprotectpatchv1_notprotectb3_return_value = 1;
break;
}
case DSProtectVersion::v1_08:
{
static u16 v108Offsets[] = { 0x858, 0x930, 0xA08, 0xAE0, 0xBB8, 0xC94 };
SetOldV1Offsets(v108Offsets);
dsprotectpatchv1_nitro_static_init_offset = 0; // does not exist
dsprotectpatchv1_protectb1_return_value = 0;
dsprotectpatchv1_protectb2_return_value = 0;
dsprotectpatchv1_protectb3_return_value = 0;
dsprotectpatchv1_notprotectb1_return_value = 1;
dsprotectpatchv1_notprotectb2_return_value = 1;
dsprotectpatchv1_notprotectb3_return_value = 1;
break;
}
case DSProtectVersion::v1_10:
{
static u16 v110Offsets[] = { 0x860, 0x940, 0xA20, 0xB00, 0xBE0, 0xCB0 };
SetOldV1Offsets(v110Offsets);
dsprotectpatchv1_nitro_static_init_offset = 0; // does not exist
dsprotectpatchv1_protectb1_return_value = 0;
dsprotectpatchv1_protectb2_return_value = 0;
dsprotectpatchv1_protectb3_return_value = 0;
dsprotectpatchv1_notprotectb1_return_value = 1;
dsprotectpatchv1_notprotectb2_return_value = 1;
dsprotectpatchv1_notprotectb3_return_value = 1;
break;
}
case DSProtectVersion::v1_20:
{
switch (_functionMask)
{
case 0b001111:
dsprotectpatchv1_stub_protectb1_offset = 0x864;
dsprotectpatchv1_stub_notprotectb1_offset = 0x94C;
dsprotectpatchv1_stub_protectb2_offset = 0xA34;
dsprotectpatchv1_stub_notprotectb2_offset = 0xB1C;
dsprotectpatchv1_stub_protectb3_offset = 0; // does not exist
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
break;
case 0b010101:
dsprotectpatchv1_stub_protectb1_offset = 0x644;
dsprotectpatchv1_stub_notprotectb1_offset = 0; // does not exist
dsprotectpatchv1_stub_protectb2_offset = 0x72C;
dsprotectpatchv1_stub_notprotectb2_offset = 0; // does not exist
dsprotectpatchv1_stub_protectb3_offset = 0x814;
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
break;
case 0b010110:
dsprotectpatchv1_stub_protectb1_offset = 0; // does not exist
dsprotectpatchv1_stub_notprotectb1_offset = 0x644;
dsprotectpatchv1_stub_protectb2_offset = 0x72C;
dsprotectpatchv1_stub_notprotectb2_offset = 0; // does not exist
dsprotectpatchv1_stub_protectb3_offset = 0x814;
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
break;
case 0b011001:
dsprotectpatchv1_stub_protectb1_offset = 0x644;
dsprotectpatchv1_stub_notprotectb1_offset = 0; // does not exist
dsprotectpatchv1_stub_protectb2_offset = 0;
dsprotectpatchv1_stub_notprotectb2_offset = 0x72C;
dsprotectpatchv1_stub_protectb3_offset = 0x814;
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
break;
case 0b110101:
dsprotectpatchv1_stub_protectb1_offset = 0x644;
dsprotectpatchv1_stub_notprotectb1_offset = 0; // does not exist
dsprotectpatchv1_stub_protectb2_offset = 0x72C;
dsprotectpatchv1_stub_notprotectb2_offset = 0; // does not exist
dsprotectpatchv1_stub_protectb3_offset = 0x814;
dsprotectpatchv1_stub_notprotectb3_offset = 0x8DC;
break;
case 0b111111:
dsprotectpatchv1_stub_protectb1_offset = 0x864;
dsprotectpatchv1_stub_notprotectb1_offset = 0x94C;
dsprotectpatchv1_stub_protectb2_offset = 0xA34;
dsprotectpatchv1_stub_notprotectb2_offset = 0xB1C;
dsprotectpatchv1_stub_protectb3_offset = 0xC04;
dsprotectpatchv1_stub_notprotectb3_offset = 0xCCC;
break;
default:
LOG_WARNING("Unsupported DSProtect function mask\n");
return nextPatch;
}
dsprotectpatchv1_nitro_static_init_offset = 0; // does not exist
dsprotectpatchv1_protectb1_return_value = 0;
dsprotectpatchv1_protectb2_return_value = 0;
dsprotectpatchv1_protectb3_return_value = 0;
dsprotectpatchv1_notprotectb1_return_value = 1;
dsprotectpatchv1_notprotectb2_return_value = 1;
dsprotectpatchv1_notprotectb3_return_value = 1;
break;
}
case DSProtectVersion::v1_22:
{
switch (_functionMask)
{
case 0b010101:
dsprotectpatchv1_stub_protectb1_offset = 0x64C;
dsprotectpatchv1_stub_notprotectb1_offset = 0; // does not exist
dsprotectpatchv1_stub_protectb2_offset = 0x734;
dsprotectpatchv1_stub_notprotectb2_offset = 0; // does not exist
dsprotectpatchv1_stub_protectb3_offset = 0x81C;
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
break;
case 0b010110:
dsprotectpatchv1_stub_protectb1_offset = 0; // does not exist
dsprotectpatchv1_stub_notprotectb1_offset = 0x64C;
dsprotectpatchv1_stub_protectb2_offset = 0x734;
dsprotectpatchv1_stub_notprotectb2_offset = 0; // does not exist
dsprotectpatchv1_stub_protectb3_offset = 0x81C;
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
break;
case 0b010111:
dsprotectpatchv1_stub_protectb1_offset = 0x754;
dsprotectpatchv1_stub_notprotectb1_offset = 0x83C;
dsprotectpatchv1_stub_protectb2_offset = 0x924;
dsprotectpatchv1_stub_notprotectb2_offset = 0; // does not exist
dsprotectpatchv1_stub_protectb3_offset = 0xA0C;
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
break;
case 0b011111:
dsprotectpatchv1_stub_protectb1_offset = 0x874;
dsprotectpatchv1_stub_notprotectb1_offset = 0x95C;
dsprotectpatchv1_stub_protectb2_offset = 0xA44;
dsprotectpatchv1_stub_notprotectb2_offset = 0xB2C;
dsprotectpatchv1_stub_protectb3_offset = 0xC14;
dsprotectpatchv1_stub_notprotectb3_offset = 0; // does not exist
break;
case 0b111111:
dsprotectpatchv1_stub_protectb1_offset = 0x874;
dsprotectpatchv1_stub_notprotectb1_offset = 0x95C;
dsprotectpatchv1_stub_protectb2_offset = 0xA44;
dsprotectpatchv1_stub_notprotectb2_offset = 0xB2C;
dsprotectpatchv1_stub_protectb3_offset = 0xC14;
dsprotectpatchv1_stub_notprotectb3_offset = 0xCDC;
break;
default:
LOG_WARNING("Unsupported DSProtect function mask\n");
return nextPatch;
}
dsprotectpatchv1_nitro_static_init_offset = 0; // does not exist
dsprotectpatchv1_protectb1_return_value = 0;
dsprotectpatchv1_protectb2_return_value = 0;
dsprotectpatchv1_protectb3_return_value = 0;
dsprotectpatchv1_notprotectb1_return_value = 1;
dsprotectpatchv1_notprotectb2_return_value = 1;
dsprotectpatchv1_notprotectb3_return_value = 1;
break;
}
case DSProtectVersion::v1_23:
case DSProtectVersion::v1_23Z:
{
dsprotectpatchv1_stub_protectb1_offset = 0x410;
dsprotectpatchv1_stub_protectb2_offset = 0x500;
dsprotectpatchv1_stub_protectb3_offset = 0x5F0;
dsprotectpatchv1_stub_notprotectb1_offset = 0x488;
dsprotectpatchv1_stub_notprotectb2_offset = 0x578;
dsprotectpatchv1_stub_notprotectb3_offset = 0x668;
// this seems to be a decrypt function that is called from 5 static init functions
dsprotectpatchv1_nitro_static_init_offset = 0xD78;
dsprotectpatchv1_protectb1_return_value = 0;
dsprotectpatchv1_protectb2_return_value = 0;
dsprotectpatchv1_protectb3_return_value = 0;
dsprotectpatchv1_notprotectb1_return_value = 1;
dsprotectpatchv1_notprotectb2_return_value = 1;
dsprotectpatchv1_notprotectb3_return_value = 1;
break;
}
case DSProtectVersion::v1_25:
case DSProtectVersion::v1_26:
{
dsprotectpatchv1_stub_protectb1_offset = 0x3DC;
dsprotectpatchv1_stub_protectb2_offset = 0x4CC;
dsprotectpatchv1_stub_protectb3_offset = 0x5BC;
dsprotectpatchv1_stub_notprotectb1_offset = 0x454;
dsprotectpatchv1_stub_notprotectb2_offset = 0x544;
dsprotectpatchv1_stub_notprotectb3_offset = 0x634;
dsprotectpatchv1_nitro_static_init_offset = 0x6AC;
break;
}
case DSProtectVersion::v1_27:
{
dsprotectpatchv1_stub_protectb1_offset = 0x3A0;
dsprotectpatchv1_stub_protectb2_offset = 0x418;
dsprotectpatchv1_stub_protectb3_offset = 0x490;
dsprotectpatchv1_stub_notprotectb1_offset = 0x3DC;
dsprotectpatchv1_stub_notprotectb2_offset = 0x454;
dsprotectpatchv1_stub_notprotectb3_offset = 0x4CC;
dsprotectpatchv1_nitro_static_init_offset = 0x508;
dsprotectpatchv1_loadReturnValue = 0; // Return the return value of the callback
break;
}
case DSProtectVersion::v1_28:
{
dsprotectpatchv1_stub_protectb1_offset = 0x3A0;
dsprotectpatchv1_stub_protectb2_offset = 0x418;
dsprotectpatchv1_stub_protectb3_offset = 0x490;
dsprotectpatchv1_stub_notprotectb1_offset = 0x3DC;
dsprotectpatchv1_stub_notprotectb2_offset = 0x454;
dsprotectpatchv1_stub_notprotectb3_offset = 0x4CC;
dsprotectpatchv1_nitro_static_init_offset = 0x508;
dsprotectpatchv1_loadReturnValue = 0; // Return the return value of the callback
break;
}
default:
{
LOG_WARNING("Unsupported DSProtect version\n");
return nextPatch;
}
}
dsprotectpatchv1_nextAddress = (u32)nextPatch;
u32 patch1Size = SECTION_SIZE(dsprotectpatchv1_part1);
void* patch1Address = patchContext.GetPatchHeap().Alloc(patch1Size);
u32 patch2Size = SECTION_SIZE(dsprotectpatchv1_part2);
void* patch2Address = patchContext.GetPatchHeap().Alloc(patch2Size);
dsprotectpatchv1_part2_address = (u32)&dsprotectpatchv1_entry2 - (u32)SECTION_START(dsprotectpatchv1_part2) + (u32)patch2Address;
u32 entryAddress1 = (u32)&dsprotectpatchv1_entry1 - (u32)SECTION_START(dsprotectpatchv1_part1) + (u32)patch1Address;
memcpy(patch1Address, SECTION_START(dsprotectpatchv1_part1), patch1Size);
memcpy(patch2Address, SECTION_START(dsprotectpatchv1_part2), patch2Size);
return (const void*)entryAddress1;
}
struct ds_protect_v2_patch_t
{
u32 checksumFix;
u16 stubProtectb1Offset;
u16 stubProtectb2Offset;
u16 stubProtectb3Offset;
u16 stubProtectb4Offset;
u16 amInitOffset;
u16 storeChecksumFix;
};
static const ds_protect_v2_patch_t sDSProtectV2Patches[] =
{
{ 0x7CDBA8BD, 0x670, 0x6AC, 0x6E8, 0x724, 0x760, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 0) }, // v2.00
{ 0xA6FD7CE1, 0x1120, 0x115C, 0x1198, 0x11D4, 0x1210, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 0) }, // v2.01
{ 0x934DD0, 0x129C, 0x1324, 0x13AC, 0x1434, 0x14BC, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 0) }, // v2.03
{ 0x32E120D5, 0x132C, 0x13D8, 0x1484, 0x1530, 0x15DC, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 8) }, // v2.05
};
const void* DSProtectOverlayPatch::InsertPatchV2(PatchContext& patchContext) const
{
dsprotectpatchv2_nextAddress = next ? (u32)next->InsertPatch(patchContext) : 0u;
dsprotectpatchv2_overlay_id = _overlayId;
dsprotectpatchv2_base_offset = _overlayOffset;
const auto& patch = sDSProtectV2Patches[(u32)_version - (u32)DSProtectVersion::v2_00];
dsprotectpatchv2_checksum_fix = patch.checksumFix;
dsprotectpatchv2_stub_protectb1_offset = patch.stubProtectb1Offset;
dsprotectpatchv2_stub_protectb2_offset = patch.stubProtectb2Offset;
dsprotectpatchv2_stub_protectb3_offset = patch.stubProtectb3Offset;
dsprotectpatchv2_stub_protectb4_offset = patch.stubProtectb4Offset;
dsprotectpatchv2_am_init_offset = patch.amInitOffset;
dsprotectpatchv2_store_checksum_fix = patch.storeChecksumFix;
u32 patchSize = SECTION_SIZE(dsprotectpatchv2);
void* patchAddress = patchContext.GetPatchHeap().Alloc(patchSize);
u32 entryAddress = (u32)&dsprotectpatchv2_entry - (u32)SECTION_START(dsprotectpatchv2) + (u32)patchAddress;
memcpy(patchAddress, SECTION_START(dsprotectpatchv2), patchSize);
return (const void*)entryAddress;
}
struct ds_protect_v2s_patch_t
{
u32 checksumFix;
u16 instantDetectOffset;
u16 storeChecksumFix;
};
static const ds_protect_v2s_patch_t sDSProtectV2sPatches[] =
{
{ 0xEDEA7F01, 0x1BC, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 0) }, // v2.00s
{ 0xEDEA7F1F, 0x1BC, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 0) }, // v2.01s
{ 0x167325AA, 0x20C, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 0) }, // v2.03s
{ 0x32E120D5, 0x230, THUMB_STR_IMM(THUMB_R7, THUMB_R0, 4) } // v2.05s
};
const void* DSProtectOverlayPatch::InsertPatchV2s(PatchContext& patchContext) const
{
dsprotectpatchv2s_nextAddress = next ? (u32)next->InsertPatch(patchContext) : 0u;
dsprotectpatchv2s_overlay_id = _overlayId;
const auto& patch = sDSProtectV2sPatches[(u32)_version - (u32)DSProtectVersion::v2_00s];
dsprotectpatchv2s_checksum_fix = patch.checksumFix;
dsprotectpatchv2s_stub_instantdetect_offset = patch.instantDetectOffset + _overlayOffset;
dsprotectpatchv2s_store_checksum_fix = patch.storeChecksumFix;
u32 patchSize = SECTION_SIZE(dsprotectpatchv2s);
void* patchAddress = patchContext.GetPatchHeap().Alloc(patchSize);
u32 entryAddress = (u32)&dsprotectpatchv2s_entry - (u32)SECTION_START(dsprotectpatchv2s) + (u32)patchAddress;
memcpy(patchAddress, SECTION_START(dsprotectpatchv2s), patchSize);
return (const void*)entryAddress;
}

View File

@@ -0,0 +1,24 @@
#pragma once
#include "../OverlayPatch.h"
#include "DSProtectVersion.h"
/// @brief Arm9 overlay patch for DS Protect.
class DSProtectOverlayPatch : public OverlayPatch
{
public:
DSProtectOverlayPatch(u32 overlayId, u32 overlayOffset, DSProtectVersion version, u32 functionMask)
: _overlayId(overlayId), _overlayOffset(overlayOffset), _version(version), _functionMask(functionMask) { }
const void* InsertPatch(PatchContext& patchContext) override;
private:
u32 _overlayId;
u32 _overlayOffset;
DSProtectVersion _version;
u32 _functionMask;
void SetOldV1Offsets(const u16* offsets) const;
const void* InsertPatchV1(PatchContext& patchContext) const;
const void* InsertPatchV2(PatchContext& patchContext) const;
const void* InsertPatchV2s(PatchContext& patchContext) const;
};

View File

@@ -0,0 +1,27 @@
#pragma once
#include "sections.h"
DEFINE_SECTION_SYMBOLS(dsprotectpatchv1_part1);
extern "C" void dsprotectpatchv1_entry1();
extern u16 dsprotectpatchv1_stub_protectb1_offset;
extern u16 dsprotectpatchv1_stub_protectb2_offset;
extern u16 dsprotectpatchv1_stub_protectb3_offset;
extern u16 dsprotectpatchv1_nitro_static_init_offset;
extern u16 dsprotectpatchv1_overlay_id;
extern u32 dsprotectpatchv1_base_offset;
extern u32 dsprotectpatchv1_part2_address;
extern u32 dsprotectpatchv1_protectb1_return_value;
extern u32 dsprotectpatchv1_protectb2_return_value;
extern u32 dsprotectpatchv1_protectb3_return_value;
DEFINE_SECTION_SYMBOLS(dsprotectpatchv1_part2);
extern "C" void dsprotectpatchv1_entry2();
extern u16 dsprotectpatchv1_stub_notprotectb1_offset;
extern u16 dsprotectpatchv1_stub_notprotectb2_offset;
extern u16 dsprotectpatchv1_stub_notprotectb3_offset;
extern u32 dsprotectpatchv1_nextAddress;
extern u32 dsprotectpatchv1_notprotectb1_return_value;
extern u32 dsprotectpatchv1_notprotectb2_return_value;
extern u32 dsprotectpatchv1_notprotectb3_return_value;
extern u32 dsprotectpatchv1_loadReturnValue;
extern u32 dsprotectpatchv1_moveCallback;

View File

@@ -0,0 +1,168 @@
.cpu arm946e-s
.syntax unified
.section "dsprotectpatchv1_part1", "ax"
.thumb
.global dsprotectpatchv1_entry1
.type dsprotectpatchv1_entry1, %function
dsprotectpatchv1_entry1:
push {r4-r7,lr}
adr r0, dsprotectpatchv1_stub_protectb1_offset
ldrh r4, [r0, #(dsprotectpatchv1_overlay_id - dsprotectpatchv1_stub_protectb1_offset)]
ldmia r5!, {r1, r2} // id, ram address
cmp r1, r4
bne continue_to_next
ldr r4, dsprotectpatchv1_base_offset
adds r2, r4 // add base offset to overlay address
ldrh r4, [r0, #(dsprotectpatchv1_nitro_static_init_offset - dsprotectpatchv1_stub_protectb1_offset)]
ldr r3,= 0xE12FFF1E // bx lr
str r3, [r4, r2]
adr r4, protectbX_patch
ldmia r4!, {r5,r6}
adds r3, r0, #(dsprotectpatchv1_nitro_static_init_offset - dsprotectpatchv1_stub_protectb1_offset)
1:
ldmia r4!, {r7}
ldrh r1, [r0]
adds r1, r2
stmia r1!, {r5,r6,r7}
adds r0, #2
cmp r0, r3
bne 1b
ldr r7, dsprotectpatchv1_part2_address
bx r7
continue_to_next:
ldr r7, dsprotectpatchv1_part2_address
adds r7, #(dsprotectpatchv1_continue_to_next - dsprotectpatchv1_entry2)
bx r7
.global dsprotectpatchv1_stub_protectb1_offset
dsprotectpatchv1_stub_protectb1_offset:
.short 0
.global dsprotectpatchv1_stub_protectb2_offset
dsprotectpatchv1_stub_protectb2_offset:
.short 0
.global dsprotectpatchv1_stub_protectb3_offset
dsprotectpatchv1_stub_protectb3_offset:
.short 0
.global dsprotectpatchv1_nitro_static_init_offset
dsprotectpatchv1_nitro_static_init_offset:
.short 0
.global dsprotectpatchv1_overlay_id
dsprotectpatchv1_overlay_id:
.short 0
.balign 4
.global dsprotectpatchv1_base_offset
dsprotectpatchv1_base_offset:
.word 0
.global dsprotectpatchv1_part2_address
dsprotectpatchv1_part2_address:
.word 0
.pool
.arm
protectbX_patch:
ldr r0, dsprotectpatchv1_protectb1_return_value
bx lr
.global dsprotectpatchv1_protectb1_return_value
dsprotectpatchv1_protectb1_return_value:
.word 1830601
.global dsprotectpatchv1_protectb2_return_value
dsprotectpatchv1_protectb2_return_value:
.word 1830203
.global dsprotectpatchv1_protectb3_return_value
dsprotectpatchv1_protectb3_return_value:
.word 1828014
.section "dsprotectpatchv1_part2", "ax"
.thumb
.global dsprotectpatchv1_entry2
.type dsprotectpatchv1_entry2, %function
dsprotectpatchv1_entry2:
adr r0, dsprotectpatchv1_stub_notprotectb1_offset
adds r1, r0, #(dsprotectpatchv1_stub_notprotectb3_offset - dsprotectpatchv1_stub_notprotectb1_offset + 2)
mov lr, r1
2:
ldrh r1, [r0]
adds r1, r2
adr r3, notprotectbX_patch
ldmia r3!, {r4,r5,r6,r7}
stmia r1!, {r4,r5,r6,r7}
ldmia r3!, {r4,r5,r6}
stmia r1!, {r4,r5,r6}
adds r0, #2
cmp r0, lr
bne 2b
ldmia r3!, {r6,r7}
adds r2, #0x18
adr r0, dsprotectpatchv1_stub_notprotectb1_offset
ldrh r1, [r0, #(dsprotectpatchv1_stub_notprotectb2_offset - dsprotectpatchv1_stub_notprotectb1_offset)]
str r6, [r1, r2]
ldrh r1, [r0, #(dsprotectpatchv1_stub_notprotectb3_offset - dsprotectpatchv1_stub_notprotectb1_offset)]
str r7, [r1, r2]
dsprotectpatchv1_continue_to_next:
ldr r0, dsprotectpatchv1_nextAddress
pop {r4-r7,pc}
.global dsprotectpatchv1_stub_notprotectb1_offset
dsprotectpatchv1_stub_notprotectb1_offset:
.short 0
.global dsprotectpatchv1_stub_notprotectb2_offset
dsprotectpatchv1_stub_notprotectb2_offset:
.short 0
.global dsprotectpatchv1_stub_notprotectb3_offset
dsprotectpatchv1_stub_notprotectb3_offset:
.short 0
.balign 4
.global dsprotectpatchv1_nextAddress
dsprotectpatchv1_nextAddress:
.word 0
.pool
.arm
notprotectbX_patch:
push {lr}
.global dsprotectpatchv1_moveCallback
dsprotectpatchv1_moveCallback:
movs lr, r0
movne r0, r1
blxne lr
.global dsprotectpatchv1_loadReturnValue
dsprotectpatchv1_loadReturnValue:
ldr r0, dsprotectpatchv1_notprotectb1_return_value
pop {pc}
.global dsprotectpatchv1_notprotectb1_return_value
dsprotectpatchv1_notprotectb1_return_value:
.word 1831551
.global dsprotectpatchv1_notprotectb2_return_value
dsprotectpatchv1_notprotectb2_return_value:
.word 1830859
.global dsprotectpatchv1_notprotectb3_return_value
dsprotectpatchv1_notprotectb3_return_value:
.word 1829648
.end

View File

@@ -0,0 +1,17 @@
#pragma once
#include "sections.h"
DEFINE_SECTION_SYMBOLS(dsprotectpatchv2);
extern "C" void dsprotectpatchv2_entry();
extern u16 dsprotectpatchv2_stub_protectb1_offset;
extern u16 dsprotectpatchv2_stub_protectb2_offset;
extern u16 dsprotectpatchv2_stub_protectb3_offset;
extern u16 dsprotectpatchv2_stub_protectb4_offset;
extern u16 dsprotectpatchv2_am_init_offset;
extern u16 dsprotectpatchv2_overlay_id;
extern u32 dsprotectpatchv2_nextAddress;
extern u32 dsprotectpatchv2_checksum_fix;
extern u32 dsprotectpatchv2_base_offset;
extern u16 dsprotectpatchv2_store_checksum_fix;

View File

@@ -0,0 +1,93 @@
.cpu arm946e-s
.section "dsprotectpatchv2", "ax"
.syntax unified
.thumb
.global dsprotectpatchv2_entry
.type dsprotectpatchv2_entry, %function
dsprotectpatchv2_entry:
push {r4-r7,lr}
ldmia r5!, {r0,r1,r2}
adds r3, r2, r1 // bss address
adr r2, dsprotectpatchv2_stub_protectb1_offset
ldrh r7, [r2, #(dsprotectpatchv2_overlay_id - dsprotectpatchv2_stub_protectb1_offset)]
cmp r0, r7
bne continue_to_next
ldr r7, [r2, #(dsprotectpatchv2_base_offset - dsprotectpatchv2_stub_protectb1_offset)]
adds r1, r7 // add base offset to overlay address
ldrh r0, [r2, #(dsprotectpatchv2_am_init_offset - dsprotectpatchv2_stub_protectb1_offset)]
ldr r7,= 0xE12FFF1E // bx lr
str r7, [r1, r0]
adr r0, stub_patch_code
ldmia r0!, {r4,r5,r6,r7}
subs r1, #4 // write from 4 bytes before the actual function address
adds r0, r2, #(dsprotectpatchv2_stub_protectb4_offset - dsprotectpatchv2_stub_protectb1_offset)
mov lr, r0
patch_loop:
ldrh r0, [r2]
adds r2, #2
adds r0, r1
stmia r0!, {r3,r4,r5,r6} // bss address + code
.global dsprotectpatchv2_store_checksum_fix
dsprotectpatchv2_store_checksum_fix:
str r7, [r0, #0] // checksum fix
cmp r2, lr
ble patch_loop
continue_to_next:
ldr r0, dsprotectpatchv2_nextAddress
pop {r4-r7,pc}
.balign 4
.global dsprotectpatchv2_stub_protectb1_offset
dsprotectpatchv2_stub_protectb1_offset:
.short 0
.global dsprotectpatchv2_stub_protectb2_offset
dsprotectpatchv2_stub_protectb2_offset:
.short 0
.global dsprotectpatchv2_stub_protectb3_offset
dsprotectpatchv2_stub_protectb3_offset:
.short 0
.global dsprotectpatchv2_stub_protectb4_offset
dsprotectpatchv2_stub_protectb4_offset:
.short 0
.global dsprotectpatchv2_am_init_offset
dsprotectpatchv2_am_init_offset:
.short 0
.global dsprotectpatchv2_overlay_id
dsprotectpatchv2_overlay_id:
.short 0
.global dsprotectpatchv2_base_offset
dsprotectpatchv2_base_offset:
.word 0
.global dsprotectpatchv2_nextAddress
dsprotectpatchv2_nextAddress:
.word 0
.pool
.arm
stub_patch_code:
ldr r12, (. - 4)
ldr r2, [r12], #4
ldr pc, [r12, r2, lsl #2]
.global dsprotectpatchv2_checksum_fix
dsprotectpatchv2_checksum_fix:
.word 0
.end

View File

@@ -0,0 +1,12 @@
#pragma once
#include "sections.h"
DEFINE_SECTION_SYMBOLS(dsprotectpatchv2s);
extern "C" void dsprotectpatchv2s_entry();
extern u32 dsprotectpatchv2s_stub_instantdetect_offset;
extern u32 dsprotectpatchv2s_overlay_id;
extern u32 dsprotectpatchv2s_nextAddress;
extern u32 dsprotectpatchv2s_checksum_fix;
extern u16 dsprotectpatchv2s_store_checksum_fix;

View File

@@ -0,0 +1,64 @@
.cpu arm946e-s
.section "dsprotectpatchv2s", "ax"
.syntax unified
.thumb
.global dsprotectpatchv2s_entry
.type dsprotectpatchv2s_entry, %function
dsprotectpatchv2s_entry:
push {r4-r7,lr}
ldmia r5!, {r0,r1,r2,r3,r4}
ldr r7, dsprotectpatchv2s_overlay_id
cmp r0, r7
bne continue_to_next
movs r3, #0
movs r7, #0
stmia r4!, {r3,r7}
stmia r4!, {r3,r7}
str r3, [r4]
adr r0, stub_patch_code
ldmia r0!, {r3,r4,r5,r6,r7}
ldr r0, dsprotectpatchv2s_stub_instantdetect_offset
adds r0, r1
stmia r0!, {r3,r4,r5,r6} // code
.global dsprotectpatchv2s_store_checksum_fix
dsprotectpatchv2s_store_checksum_fix:
str r7, [r0, #0] // checksum fix
continue_to_next:
ldr r0, dsprotectpatchv2s_nextAddress
pop {r4-r7,pc}
.balign 4
.global dsprotectpatchv2s_stub_instantdetect_offset
dsprotectpatchv2s_stub_instantdetect_offset:
.word 0
.global dsprotectpatchv2s_overlay_id
dsprotectpatchv2s_overlay_id:
.word 0
.global dsprotectpatchv2s_nextAddress
dsprotectpatchv2s_nextAddress:
.word 0
.pool
.arm
stub_patch_code:
movs r3, r0
pusheq {r0,r1,lr}
pushne {r1,r2,r3}
pop {r0,r1,pc}
.global dsprotectpatchv2s_checksum_fix
dsprotectpatchv2s_checksum_fix:
.word 0
.end

View File

@@ -0,0 +1,160 @@
#include "common.h"
#include "../../PatchContext.h"
#include "thumbInstructions.h"
#include "FsStartOverlayHookPatch.h"
static const u32 sFSStartOverlayPatternSdk4[] = { 0xE59F10D0u, 0xE1A04000u, 0xE1D100B0u, 0xE3500002u }; // -0xC
static const u32 sFSStartOverlayPatternSdk4Thumb[] = { 0x481F1C06u, 0x28028800u, 0x69E8D121u, 0x0E012700u }; // -8
static const u32 sFSStartOverlayPattern[] = { 0xE3500001u, 0x0A00001Cu, 0xE595001Cu, 0xE3A03000u }; // -0x14
static const u32 sFSStartOverlayPatternThumb[] = { 0x69E8D023u, 0x0E012600u, 0x42082002u, 0x491BD013u }; // -0x10
static const u32 sFSStartOverlayPatternThumbHybrid[] = { 0x68691C07u, 0x42814828u, 0x4828D30Cu, 0xD2094281u }; // -8
extern "C" void fsstartoverlayhook_entry();
extern u8 __fsstartoverlayhook_start[];
extern u8 __fsstartoverlayhook_end[];
extern u32 fsstartoverlayhook_dcFlushRangeOffset;
extern u32 fsstartoverlayhook_hookFuncAddress;
bool FsStartOverlayHookPatch::FindPatchTarget(PatchContext& patchContext)
{
if (!_patchHead) // no patches
return true;
if (patchContext.GetSdkVersion().GetMajor() <= 4)
{
_fsStartOverlay = patchContext.FindPattern32(sFSStartOverlayPatternSdk4, sizeof(sFSStartOverlayPatternSdk4));
if (_fsStartOverlay)
{
_fsStartOverlay -= 3;
}
else
{
_fsStartOverlay = patchContext.FindPattern32(sFSStartOverlayPatternSdk4Thumb, sizeof(sFSStartOverlayPatternSdk4Thumb));
if (_fsStartOverlay)
{
_fsStartOverlay -= 2;
_thumb = true;
}
}
}
else
{
_fsStartOverlay = patchContext.FindPattern32(sFSStartOverlayPattern, sizeof(sFSStartOverlayPattern));
if (_fsStartOverlay)
{
_fsStartOverlay -= 5;
}
else
{
_fsStartOverlay = patchContext.FindPattern32(sFSStartOverlayPatternThumb, sizeof(sFSStartOverlayPatternThumb));
if (_fsStartOverlay)
{
_fsStartOverlay -= 4;
_thumb = true;
}
else
{
_fsStartOverlay = patchContext.FindPattern32(sFSStartOverlayPatternThumbHybrid, sizeof(sFSStartOverlayPatternThumbHybrid));
if (_fsStartOverlay)
{
_fsStartOverlay -= 2;
_thumb = true;
_hybrid = true;
}
}
}
}
if (_fsStartOverlay)
{
LOG_DEBUG("FS_StartOverlay found at 0x%p\n", _fsStartOverlay);
}
else
{
LOG_WARNING("FS_StartOverlay not found\n");
}
return _fsStartOverlay != nullptr;
}
void FsStartOverlayHookPatch::ApplyPatch(PatchContext& patchContext)
{
if (!_fsStartOverlay)
return;
if (!_patchHead) // no patches
return;
const void* firstPatch = _patchHead->InsertPatch(patchContext);
if (!firstPatch)
return;
fsstartoverlayhook_hookFuncAddress = (u32)firstPatch;
u32 patchOffset;
if (_thumb)
{
u32 blDcFlushRange1;
u32 blDcFlushRange2;
if (_hybrid)
{
patchOffset = 0x8E;
blDcFlushRange1 = *(u16*)((u8*)_fsStartOverlay + 0x92);
blDcFlushRange2 = *(u16*)((u8*)_fsStartOverlay + 0x94);
}
else
{
if (patchContext.GetSdkVersion().GetMajor() <= 4)
{
patchOffset = 0x68;
blDcFlushRange1 = *(u16*)((u8*)_fsStartOverlay + 0x6C);
blDcFlushRange2 = *(u16*)((u8*)_fsStartOverlay + 0x6E);
}
else
{
patchOffset = 0x6C;
blDcFlushRange1 = *(u16*)((u8*)_fsStartOverlay + 0x70);
blDcFlushRange2 = *(u16*)((u8*)_fsStartOverlay + 0x72);
}
}
fsstartoverlayhook_dcFlushRangeOffset = ((int)((((blDcFlushRange1 & 0x7FF) << 11) | (blDcFlushRange2 & 0x7FF)) << 10) >> 9) - (_hybrid ? 5 : 1);
}
else
{
patchOffset = 0xAC;
u32 blDcFlushRange = *(u32*)((u8*)_fsStartOverlay + 0xB0);
fsstartoverlayhook_dcFlushRangeOffset = (int)((blDcFlushRange & 0xFFFFFF) << 8) >> 6;
}
u32 patch1Size = (u32)__fsstartoverlayhook_end - (u32)__fsstartoverlayhook_start;
void* patch1Address = patchContext.GetPatchHeap().Alloc(patch1Size);
u32 entryAddress = (u32)&fsstartoverlayhook_entry - (u32)__fsstartoverlayhook_start + (u32)patch1Address;
memcpy(patch1Address, __fsstartoverlayhook_start, patch1Size);
if (_thumb)
{
if (_hybrid)
{
*(u16*)((u8*)_fsStartOverlay + patchOffset + 0) = THUMB_LDR_PC_IMM(THUMB_R0, 4);
*(u16*)((u8*)_fsStartOverlay + patchOffset + 2) = THUMB_NOP;
*(u16*)((u8*)_fsStartOverlay + patchOffset + 4) = THUMB_BLX(THUMB_R0);
*(u32*)((u8*)_fsStartOverlay + patchOffset + 6) = entryAddress;
}
else
{
*(u16*)((u8*)_fsStartOverlay + patchOffset + 0) = THUMB_LDR_PC_IMM(THUMB_R0, 0);
*(u16*)((u8*)_fsStartOverlay + patchOffset + 2) = THUMB_BLX(THUMB_R0);
*(u32*)((u8*)_fsStartOverlay + patchOffset + 4) = entryAddress;
}
}
else
{
*(u32*)((u8*)_fsStartOverlay + patchOffset + 0) = 0xE59F0000; // ldr r0,= entryAddress
*(u32*)((u8*)_fsStartOverlay + patchOffset + 4) = 0xE12FFF30; // blx r0
*(u32*)((u8*)_fsStartOverlay + patchOffset + 8) = entryAddress;
}
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include "OverlayHookPatch.h"
/// @brief Arm9 patch to apply patches to overlays when they are loaded.
class FsStartOverlayHookPatch : public OverlayHookPatch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _fsStartOverlay = nullptr;
u16 _thumb = false;
u16 _hybrid = false;
};

View File

@@ -0,0 +1,45 @@
.cpu arm946e-s
.section ".fsstartoverlayhook", "ax"
.syntax unified
.thumb
.global __fsstartoverlayhook_start
__fsstartoverlayhook_start:
.global fsstartoverlayhook_entry
.type fsstartoverlayhook_entry, %function
fsstartoverlayhook_entry:
movs r0, #0x4
add lr, r0
ldr r2, fsstartoverlayhook_dcFlushRangeOffset
add r2, lr
push {r2,lr}
ldr r0, fsstartoverlayhook_hookFuncAddress
1:
blx r0
cmp r0, #0
bne 1b
ldr r0, [r5, #4]
ldr r1, [r5, #8]
pop {r2}
blx r2
ldr r4, [r5, #0x10]
pop {pc}
.balign 4
.global fsstartoverlayhook_dcFlushRangeOffset
fsstartoverlayhook_dcFlushRangeOffset:
.word 0
.global fsstartoverlayhook_hookFuncAddress
fsstartoverlayhook_hookFuncAddress:
.word 0
.pool
.global __fsstartoverlayhook_end
__fsstartoverlayhook_end:
.end

View File

@@ -0,0 +1,34 @@
#include "common.h"
#include "../../../PatchContext.h"
#include "GoldenSunDarkDawnOverlayHookPatchAsm.h"
#include "GoldenSunDarkDawnOverlayHookPatch.h"
static const u32 sStartOverlayPattern[] = { 0xE92D4038u, 0xE1A05000u, 0xEBFFFF92u, 0xE595101Cu };
bool GoldenSunDarkDawnOverlayHookPatch::FindPatchTarget(PatchContext& patchContext)
{
_overlayStartFunc = patchContext.FindPattern32(sStartOverlayPattern, sizeof(sStartOverlayPattern));
return _overlayStartFunc != nullptr;
}
void GoldenSunDarkDawnOverlayHookPatch::ApplyPatch(PatchContext& patchContext)
{
if (!_overlayStartFunc)
return;
if (!_patchHead) // no patches
return;
gsddoverlayhookpatch_hookFuncAddress = (u32)_patchHead->InsertPatch(patchContext);
gsddoverlayhookpatch_returnAddress = (u32)_overlayStartFunc + 0x30;
u32 patch1Size = SECTION_SIZE(gsddoverlayhookpatch);
void* patch1Address = patchContext.GetPatchHeap().Alloc(patch1Size);
u32 entryAddress = (u32)&gsddoverlayhookpatch_entry - (u32)SECTION_START(gsddoverlayhookpatch) + (u32)patch1Address;
*(u32*)((u8*)_overlayStartFunc + 0x28) = 0xE51FF004; // ldr pc,= entryAddress
*(u32*)((u8*)_overlayStartFunc + 0x2C) = entryAddress;
memcpy(patch1Address, SECTION_START(gsddoverlayhookpatch), patch1Size);
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include "../OverlayHookPatch.h"
/// @brief Arm9 patch to apply patches to overlays when they are loaded in Golden Sun Dark Dawn.
class GoldenSunDarkDawnOverlayHookPatch : public OverlayHookPatch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _overlayStartFunc;
};

View File

@@ -0,0 +1,9 @@
#pragma once
#include "sections.h"
DEFINE_SECTION_SYMBOLS(gsddoverlayhookpatch);
extern "C" void gsddoverlayhookpatch_entry();
extern u32 gsddoverlayhookpatch_hookFuncAddress;
extern u32 gsddoverlayhookpatch_returnAddress;

View File

@@ -0,0 +1,34 @@
.cpu arm946e-s
.section "gsddoverlayhookpatch", "ax"
.syntax unified
.thumb
.global gsddoverlayhookpatch_entry
.type gsddoverlayhookpatch_entry, %function
gsddoverlayhookpatch_entry:
ldr r0, gsddoverlayhookpatch_hookFuncAddress
1:
blx r0
cmp r0, #0
bne 1b
ldr r3, gsddoverlayhookpatch_returnAddress
ldr r1, [r5, #8]
movs r0, #1
lsls r0, r0, #14 // r0 = 0x4000
cmp r1, r0
bx r3
.balign 4
.global gsddoverlayhookpatch_hookFuncAddress
gsddoverlayhookpatch_hookFuncAddress:
.word 0
.global gsddoverlayhookpatch_returnAddress
gsddoverlayhookpatch_returnAddress:
.word 0
.pool
.end

View File

@@ -0,0 +1,24 @@
#pragma once
#include "../../Patch.h"
#include "OverlayPatch.h"
/// @brief Abstract base class for a patch that applies patches to overlays when they are loaded.
class OverlayHookPatch : public Patch
{
public:
/// @brief Adds an overlay patch to the list.
/// @param patch The overlay pach to add.
void AddOverlayPatch(OverlayPatch* patch)
{
LOG_DEBUG("OverlayHookPatch::AddOverlayPatch\n");
if (!_patchHead)
_patchHead = patch;
if (_patchTail)
_patchTail->next = patch;
_patchTail = patch;
}
protected:
OverlayPatch* _patchHead = nullptr;
OverlayPatch* _patchTail = nullptr;
};

View File

@@ -0,0 +1,15 @@
#pragma once
class PatchContext;
/// @brief Patch to be applied when an overlay is loaded.
class OverlayPatch
{
public:
/// @brief Pointer to the next overlay patch, or \c nullptr when none.
OverlayPatch* next = nullptr;
/// @brief Inserts the patch using the given \p patchContext.
/// @param patchContext The patch context to use.
/// @return A pointer to the patch function.
virtual const void* InsertPatch(PatchContext& patchContext) = 0;
};

View File

@@ -0,0 +1,13 @@
#include "common.h"
#include "../../../PatchContext.h"
#include "PokemonBw1IrApPatchAsm.h"
#include "PokemonBw1IrApPatch.h"
const void* PokemonBw1IrApPatch::InsertPatch(PatchContext& patchContext)
{
return patchContext.GetPatchCodeCollection().AddUniquePatchCode<PokemonBw1IrApPatchCode>
(
patchContext.GetPatchHeap(),
next ? next->InsertPatch(patchContext) : nullptr
)->GetPatchFunction();
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "../OverlayPatch.h"
/// @brief Arm9 overlay patch to disable the Pokemon Black & White IR-sensor anti-piracy.
class PokemonBw1IrApPatch : public OverlayPatch
{
public:
const void* InsertPatch(PatchContext& patchContext) override;
};

View File

@@ -0,0 +1,24 @@
#pragma once
#include "sections.h"
#include "../../../PatchCode.h"
DEFINE_SECTION_SYMBOLS(pokemonbw1irappatch);
extern "C" void pokemonbw1irappatch_entry();
extern u32 pokemonbw1irappatch_nextAddress;
class PokemonBw1IrApPatchCode : public PatchCode
{
public:
PokemonBw1IrApPatchCode(PatchHeap& patchHeap, const void* nextPatch)
: PatchCode(SECTION_START(pokemonbw1irappatch), SECTION_SIZE(pokemonbw1irappatch), patchHeap)
{
pokemonbw1irappatch_nextAddress = (u32)nextPatch;
}
const void* GetPatchFunction() const
{
return GetAddressAtTarget((void*)pokemonbw1irappatch_entry);
}
};

View File

@@ -0,0 +1,32 @@
.cpu arm946e-s
.syntax unified
.section "pokemonbw1irappatch", "ax"
.thumb
.global pokemonbw1irappatch_entry
.type pokemonbw1irappatch_entry, %function
pokemonbw1irappatch_entry:
push {r5,lr}
ldmia r5!, {r1, r2} // id, ram address
ldr r0,= 231 // target overlay id
cmp r0, r1
bne continue_to_next
ldr r1,= 0x1CA // patch offset in overlay
movs r0, #1 // force return value of function to 1 instead of 0
adds r2, r1
strb r0, [r2]
continue_to_next:
ldr r0, pokemonbw1irappatch_nextAddress
pop {r5,pc}
.balign 4
.global pokemonbw1irappatch_nextAddress
pokemonbw1irappatch_nextAddress:
.word 0
.pool
.end

View File

@@ -0,0 +1,13 @@
#include "common.h"
#include "../../../PatchContext.h"
#include "PokemonBw2IrApPatchAsm.h"
#include "PokemonBw2IrApPatch.h"
const void* PokemonBw2IrApPatch::InsertPatch(PatchContext& patchContext)
{
return patchContext.GetPatchCodeCollection().AddUniquePatchCode<PokemonBw2IrApPatchCode>
(
patchContext.GetPatchHeap(),
next ? next->InsertPatch(patchContext) : nullptr
)->GetPatchFunction();
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "../OverlayPatch.h"
/// @brief Arm9 overlay patch to disable the Pokemon Black & White 2 IR-sensor anti-piracy.
class PokemonBw2IrApPatch : public OverlayPatch
{
public:
const void* InsertPatch(PatchContext& patchContext) override;
};

View File

@@ -0,0 +1,24 @@
#pragma once
#include "sections.h"
#include "../../../PatchCode.h"
DEFINE_SECTION_SYMBOLS(pokemonbw2irappatch);
extern "C" void pokemonbw2irappatch_entry();
extern u32 pokemonbw2irappatch_nextAddress;
class PokemonBw2IrApPatchCode : public PatchCode
{
public:
PokemonBw2IrApPatchCode(PatchHeap& patchHeap, const void* nextPatch)
: PatchCode(SECTION_START(pokemonbw2irappatch), SECTION_SIZE(pokemonbw2irappatch), patchHeap)
{
pokemonbw2irappatch_nextAddress = (u32)nextPatch;
}
const void* GetPatchFunction() const
{
return GetAddressAtTarget((void*)pokemonbw2irappatch_entry);
}
};

View File

@@ -0,0 +1,36 @@
.cpu arm946e-s
.syntax unified
.section "pokemonbw2irappatch", "ax"
.thumb
.global pokemonbw2irappatch_entry
.type pokemonbw2irappatch_entry, %function
pokemonbw2irappatch_entry:
push {r5,lr}
ldmia r5!, {r1, r2} // id, ram address
ldr r0,= 338 // target overlay id
cmp r0, r1
bne continue_to_next
movs r0, #0
adds r2, #0xEC
strh r0, [r2]
adds r2, #(0x1B8 - 0xEC)
ldr r0,= 0x47702001
str r0, [r2]
movs r0, #1
strb r0, [r2, #(0x1CA - 0x1B8)]
continue_to_next:
ldr r0, pokemonbw2irappatch_nextAddress
pop {r5,pc}
.balign 4
.global pokemonbw2irappatch_nextAddress
pokemonbw2irappatch_nextAddress:
.word 0
.pool
.end

View File

@@ -0,0 +1,36 @@
#include "common.h"
#include "../PatchContext.h"
#include "../platform/LoaderPlatform.h"
#include "PokemonDownloaderArm9PatchAsm.h"
#include "PokemonDownloaderArm9Patch.h"
static const u32 sBootFunctionPattern[] = { 0xE92D4010u, 0xE59F003Cu, 0xE5904000u, 0xE3540000u };
bool PokemonDownloaderArm9Patch::FindPatchTarget(PatchContext& patchContext)
{
_bootFunction = patchContext.FindPattern32(sBootFunctionPattern, sizeof(sBootFunctionPattern));
return _bootFunction != nullptr;
}
void PokemonDownloaderArm9Patch::ApplyPatch(PatchContext& patchContext)
{
if (!_bootFunction)
{
return;
}
auto loaderInfoTarget = (loader_info_t*)patchContext.GetPatchHeap().Alloc(sizeof(loader_info_t));
memcpy(loaderInfoTarget, _loaderInfo, sizeof(loader_info_t));
auto sdReadPatchCode = patchContext.GetLoaderPlatform()->CreateSdReadPatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
auto patchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<PokemonDownloaderArm9PatchCode>
(
patchContext.GetPatchHeap(),
loaderInfoTarget,
sdReadPatchCode
);
_bootFunction[0] = 0xE51FF004;
_bootFunction[1] = (u32)patchCode->GetBoot9Function();
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include "../Patch.h"
#include "LoaderInfo.h"
/// @brief Arm9 patch to make the Pokemon downloader reboot into Pico Loader.
class PokemonDownloaderArm9Patch : public Patch
{
public:
explicit PokemonDownloaderArm9Patch(const loader_info_t* loaderInfo)
: _loaderInfo(loaderInfo) { }
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _bootFunction = nullptr;
const loader_info_t* _loaderInfo;
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include "../PatchCode.h"
#include "sections.h"
#include "LoaderInfo.h"
#include "../platform/SdReadPatchCode.h"
DEFINE_SECTION_SYMBOLS(patch_pokemondownloader9);
extern "C" void patch_pokemondownloader9_entry(void);
extern const loader_info_t* patch_pokemondownloader9_loader_info_address;
extern u32 patch_pokemondownloader9_readSdSectors_address;
class PokemonDownloaderArm9PatchCode : public PatchCode
{
public:
PokemonDownloaderArm9PatchCode(PatchHeap& patchHeap, const loader_info_t* loaderInfo,
const SdReadPatchCode* sdReadPatchCode)
: PatchCode(SECTION_START(patch_pokemondownloader9), SECTION_SIZE(patch_pokemondownloader9), patchHeap)
{
patch_pokemondownloader9_loader_info_address = loaderInfo;
patch_pokemondownloader9_readSdSectors_address = (u32)sdReadPatchCode->GetSdReadFunction();
}
const void* GetBoot9Function() const
{
return GetAddressAtTarget((void*)patch_pokemondownloader9_entry);
}
};

View File

@@ -0,0 +1,99 @@
.cpu arm946e-s
.section "patch_pokemondownloader9", "ax"
.syntax unified
.thumb
.global patch_pokemondownloader9_entry
.type patch_pokemondownloader9_entry, %function
patch_pokemondownloader9_entry:
ldr r0,= 0x04000204
ldrh r1, [r0]
movs r2, #1
lsls r2, r2, #11
bics r1, r2
strh r1, [r0]
adr r0, regVramCntA
// regVramCntA, vramAbcdLcdcSetting, readSdSectors_address, loader_info_address
ldmia r0!, {r2, r3, r4, r5}
// r9 = readSdSectors_address
mov r9, r4
// map vram ABCD to LCDC
str r3, [r2]
// load pico loader arm9
ldmia r5!, {r4,r6,r7} // clusterShift, database, clusterMap[0]
ldr r7,= 0x06800000
mov r11, pc
b loadData
// load pico loader arm7
ldr r5, patch_pokemondownloader9_loader_info_address
adds r5, #52
ldr r7,= 0x06840000
mov r11, pc
b loadData
ldr r3, patch_pokemondownloader9_loader_info_address
ldrh r0, [r3, #2] // loader_info_t::picoLoaderBootDrive
movs r7, #1
lsls r7, r7, #15 // PLOAD_BOOT_DRIVE_MULTIBOOT_FLAG
orrs r0, r7
ldr r5,= 0x06840000
strh r0, [r5, #8] // pload_header7_t::bootDrive
// map vram CD to arm7
ldr r0, regVramCntA
ldr r7,= 0x8A82
strh r7, [r0, #2]
adds r0, #(0x04000180 - 0x04000240) // REG_IPC_SYNC
1:
ldrb r7, [r0] // ipc sync
cmp r7, #1
bne 1b // while ipc sync from arm7 is not 1
movs r1, #1
strb r1, [r0, #1]
ldr r0,= 0x06800000
bx r0
loadData_loop:
subs r3, #2
lsls r3, r4
adds r0, r3, r6 // start sector
movs r1, r7 // dst
lsls r2, r4 // sector count
lsls r3, r2, #9
adds r7, r3
blx r9 // read sectors
loadData:
ldmia r5!, {r2, r3} // ncl, startSector
cmp r2, #0
bne loadData_loop
mov pc, r11
.balign 4
regVramCntA:
.word 0x04000240
vramAbcdLcdcSetting:
.word 0x80808080
.global patch_pokemondownloader9_readSdSectors_address
patch_pokemondownloader9_readSdSectors_address:
.word 0
.global patch_pokemondownloader9_loader_info_address
patch_pokemondownloader9_loader_info_address:
.word 0
.pool
.end

View File

@@ -0,0 +1,25 @@
#pragma once
#include "sections.h"
#include "../SectorRemapPatchCode.h"
#include "fileInfo.h"
DEFINE_SECTION_SYMBOLS(romoffsettosdsector);
extern "C" u32 rom_offset_to_sd_sector_asm(u32 romOffset);
extern u32 romoffsettosdsector_fatDataPtr;
class RomOffsetToSdSectorPatchCode : public SectorRemapPatchCode
{
public:
RomOffsetToSdSectorPatchCode(PatchHeap& patchHeap, const rom_file_info_t* fatDataPtr)
: SectorRemapPatchCode(SECTION_START(romoffsettosdsector), SECTION_SIZE(romoffsettosdsector), patchHeap)
{
romoffsettosdsector_fatDataPtr = (u32)fatDataPtr;
}
const void* GetRemapFunction() const override
{
return GetAddressAtTarget((void*)rom_offset_to_sd_sector_asm);
}
};

View File

@@ -0,0 +1,52 @@
.cpu arm946e-s
.section "romoffsettosdsector", "ax"
.syntax unified
.thumb
// r0 = rom offset
// returns sd sector in r0
// returns cluster shift in r1
// returns remaining sectors in cluster in lr
.global rom_offset_to_sd_sector_asm
.type rom_offset_to_sd_sector_asm, %function
rom_offset_to_sd_sector_asm:
push {r4,r5,lr}
retry:
ldr r5, romoffsettosdsector_fatDataPtr
ldmia r5!, {r1,r2,r3,r4} // clusterShift, database, clusterMask, clusterMap[0]
lsrs r0, r0, #9
2:
adds r4, r3, #1
ands r3, r0
subs r4, r3
mov lr, r4 // remaining sectors in cluster
adds r3, r2
lsrs r0, r1 // cl
1:
ldmia r5!, {r2,r4} // ncl, startSector
cmp r2, #0
beq out_of_bounds
subs r0, r2 // cl -= ncl
bcs 1b
adds r0, r2
adds r0, r4 // + startSector
subs r0, #2 // - 2
lsls r0, r1
adds r0, r3 // r0 = src sector
pop {r4,r5,pc}
out_of_bounds:
movs r0, #0x40
lsls r0, r0, #9
b retry
.balign 4
.global romoffsettosdsector_fatDataPtr
romoffsettosdsector_fatDataPtr:
.word 0x027FF800
.pool
.end

View File

@@ -0,0 +1,262 @@
#include "common.h"
#include "patches/PatchContext.h"
#include "fileInfo.h"
#include "thumbInstructions.h"
#include "patches/platform/LoaderPlatform.h"
#include "patches/arm9/RomOffsetToSdSectorAsm.h"
#include "patches/OffsetToSectorRemapAsm.h"
#include "patches/arm9/FixCp15Asm.h"
#include "CardiReadCardPatchAsm.h"
#include "CardiReadCardPatch.h"
static const u32 sCARDiReadCardPatternUnknown[] = { 0xE92D4FF0u, 0xE24DD004u, 0xE1A0A000u, 0xE59F90D8u };
static const u32 sCARDiReadCardPatternSdk20029A7[] = { 0xE92D4FF0u, 0xE24DD004u, 0xE1A0A000u, 0xE59F90E0u };
static const u32 sCARDiReadCardPatternSdk2004E8BPingPals[] = { 0xE92D4FF0u, 0xE24DD004u, 0xE1A0A000u, 0xE59F90FCu };
static const u32 sCARDiReadCardPatternSdk2004F4CThumb[] = { 0xB083B5F0u, 0x4E279000u, 0x30209001u, 0x4F269001u };
static const u32 sCARDiReadCardPatternSdk2004F4DDebug[] = { 0xE92D47F0u, 0xE1A05000u, 0xE59F40D4u, 0xE5941018u };
static const u32 sCARDiReadCardPatternSdk2027533ThumbChouSoujuu[] = { 0xB083B5F0u, 0x4E259000u, 0x30209001u, 0x4F249001u };
static const u32 sCARDiReadCardPatternSdk3027530Thumb[] = { 0xB082B5F8u, 0x90019000u, 0x90013020u, 0x69C24825u };
static const u32 sCARDiReadCardPatternSdk4002774[] = { 0xE92D4070u, 0xE59F40D0u, 0xE1A06000u, 0xE3A00C02u };
static const u32 sCARDiReadCardPatternSdk4017530[] = { 0xE92D4070u, 0xE59F40D8u, 0xE1A06000u, 0xE3A00C02u };
static const u32 sCARDiReadCardPatternSdk4017530Thumb[] = { 0xB082B5F8u, 0x90019000u, 0x90013020u, 0x69C24827u };
static const u32 sCARDiReadCardPatternSdk4027539SpiritTracks[] = { 0xE92D4070u, 0xE59F40D4u, 0xE1A06000u, 0xE3A00C02u };
void CardiReadCardPatch::TryPattern(PatchContext& patchContext, const u32* pattern, u32 byteLength)
{
_cardiReadCard = patchContext.FindPattern32(pattern, byteLength);
if (_cardiReadCard)
_foundPattern = pattern;
}
bool CardiReadCardPatch::FindPatchTarget(PatchContext& patchContext)
{
if (patchContext.GetSdkVersion() >= 0x4017530)
TryPattern(patchContext, sCARDiReadCardPatternSdk4017530, sizeof(sCARDiReadCardPatternSdk4017530));
else if (patchContext.GetSdkVersion() >= 0x4002774)
TryPattern(patchContext, sCARDiReadCardPatternSdk4002774, sizeof(sCARDiReadCardPatternSdk4002774));
else
TryPattern(patchContext, sCARDiReadCardPatternSdk20029A7, sizeof(sCARDiReadCardPatternSdk20029A7));
if (!_cardiReadCard)
{
if (patchContext.GetSdkVersion() >= 0x4017530)
TryPattern(patchContext, sCARDiReadCardPatternSdk4017530Thumb, sizeof(sCARDiReadCardPatternSdk4017530Thumb));
else if (patchContext.GetSdkVersion() >= 0x3027530)
TryPattern(patchContext, sCARDiReadCardPatternSdk3027530Thumb, sizeof(sCARDiReadCardPatternSdk3027530Thumb));
else if (patchContext.GetSdkVersion() >= 0x2004F4C)
TryPattern(patchContext, sCARDiReadCardPatternSdk2004F4CThumb, sizeof(sCARDiReadCardPatternSdk2004F4CThumb));
if (_cardiReadCard)
_thumb = true;
}
if (!_cardiReadCard)
{
// if still nothing found try some of the patterns that appear all over the place
TryPattern(patchContext, sCARDiReadCardPatternUnknown, sizeof(sCARDiReadCardPatternUnknown));
if (!_cardiReadCard)
TryPattern(patchContext, sCARDiReadCardPatternSdk4002774, sizeof(sCARDiReadCardPatternSdk4002774));
if (!_cardiReadCard)
TryPattern(patchContext, sCARDiReadCardPatternSdk20029A7, sizeof(sCARDiReadCardPatternSdk20029A7));
if (patchContext.GetSdkVersion().GetMajor() >= SDK_VERSION_MAJOR_NITRO_4)
{
if (!_cardiReadCard)
TryPattern(patchContext, sCARDiReadCardPatternSdk4027539SpiritTracks, sizeof(sCARDiReadCardPatternSdk4027539SpiritTracks));
}
if (patchContext.GetSdkVersion().GetMajor() <= SDK_VERSION_MAJOR_NITRO_2)
{
if (!_cardiReadCard)
TryPattern(patchContext, sCARDiReadCardPatternSdk2004F4DDebug, sizeof(sCARDiReadCardPatternSdk2004F4DDebug));
if (!_cardiReadCard)
TryPattern(patchContext, sCARDiReadCardPatternSdk2004E8BPingPals, sizeof(sCARDiReadCardPatternSdk2004E8BPingPals));
}
}
if (!_cardiReadCard)
{
TryPattern(patchContext, sCARDiReadCardPatternSdk2004F4CThumb, sizeof(sCARDiReadCardPatternSdk2004F4CThumb));
if (!_cardiReadCard)
TryPattern(patchContext, sCARDiReadCardPatternSdk3027530Thumb, sizeof(sCARDiReadCardPatternSdk3027530Thumb));
if (!_cardiReadCard)
TryPattern(patchContext, sCARDiReadCardPatternSdk2027533ThumbChouSoujuu, sizeof(sCARDiReadCardPatternSdk2027533ThumbChouSoujuu));
if (_cardiReadCard)
_thumb = true;
}
if (_cardiReadCard)
{
LOG_DEBUG("Found CARDi_ReadCard at %p\n", _cardiReadCard);
// patchContext.GetPatchHeap().AddFreeSpace((u8*)_cardiReadCard + 0x64, 0x34);
}
else
{
LOG_DEBUG("CARDi_ReadCard not found\n");
}
return _cardiReadCard != nullptr;
}
void CardiReadCardPatch::ApplyPatch(PatchContext& patchContext)
{
if (!_cardiReadCard)
return;
// patch at CARDi_ReadCard + 0x58
// r1 = rom src (512 byte aligned)
// r8 = dst (32 bit aligned)
// return to CARDi_ReadCard + 0x98
u32 patch1Size = SECTION_SIZE(patch_cardireadcard);
void* patch1Address = patchContext.GetPatchHeap().Alloc(patch1Size);
auto loaderPlatform = patchContext.GetLoaderPlatform();
if (loaderPlatform->HasRomReads())
{
auto romReadPatchCode = loaderPlatform->CreateRomReadPatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
auto offsetToSectorRemapPatchCode = patchContext.GetPatchCodeCollection().GetOrAddSharedPatchCode([&]
{
return new OffsetToSectorRemapPatchCode(patchContext.GetPatchHeap());
});
__patch_cardireadcard_rom_offset_to_sd_sector_asm_address = (u32)offsetToSectorRemapPatchCode->GetRemapFunction();
__patch_cardireadcard_sdread_asm_address = (u32)romReadPatchCode->GetSdReadFunction();
}
else
{
auto sdReadPatchCode = loaderPlatform->CreateSdReadPatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
auto romOffsetToSdSectorPatchCode = patchContext.GetPatchCodeCollection().GetOrAddSharedPatchCode([&]
{
return new RomOffsetToSdSectorPatchCode(patchContext.GetPatchHeap(),
(const rom_file_info_t*)((u32)SHARED_ROM_FILE_INFO - 0x02F00000 + 0x02700000));
});
__patch_cardireadcard_rom_offset_to_sd_sector_asm_address = (u32)romOffsetToSdSectorPatchCode->GetRemapFunction();
__patch_cardireadcard_sdread_asm_address = (u32)sdReadPatchCode->GetSdReadFunction();
}
u32 patch4Size = SECTION_SIZE(fixcp15);
void* patch4Address = patchContext.GetPatchHeap().Alloc(patch4Size);
__patch_cardireadcard_fix_cp15_asm_address = (u32)&fix_cp15_asm - (u32)SECTION_START(fixcp15) + (u32)patch4Address;
u32 entryAddress = (u32)&patch_cardireadcard_entry - (u32)SECTION_START(patch_cardireadcard) + (u32)patch1Address;
if (_thumb)
{
u32 patchOffset;
u32 cardiCommonOffset;
if (_foundPattern == sCARDiReadCardPatternSdk4017530Thumb)
{
patchOffset = 0x36;
cardiCommonOffset = 0x74;
patch_cardireadcard_return_offset = THUMB_MOVS_IMM(THUMB_R0, 0x3C);
patch_cardireadcard_mov_src_to_r0 = THUMB_MOVS_REG(THUMB_R0, THUMB_R1); // src
patch_cardireadcard_mov_dst_to_r1 = THUMB_MOVS_REG(THUMB_R1, THUMB_R4); // dst
patch_cardireadcard_mov_cardicommon_to_r6 = THUMB_LDR_SP_IMM(THUMB_R6, 0);
patch_cardireadcard_adjust_cardicommon_offset = THUMB_NOP;
patch_cardireadcard_mov_r3_to_dst = THUMB_STR_SP_IMM(THUMB_R3, 4);
}
else if (_foundPattern == sCARDiReadCardPatternSdk3027530Thumb)
{
patchOffset = 0x36;
cardiCommonOffset = 0x6C;
patch_cardireadcard_return_offset = THUMB_MOVS_IMM(THUMB_R0, 0x36);
patch_cardireadcard_mov_src_to_r0 = THUMB_MOVS_REG(THUMB_R0, THUMB_R1); // src
patch_cardireadcard_mov_dst_to_r1 = THUMB_MOVS_REG(THUMB_R1, THUMB_R4); // dst
patch_cardireadcard_mov_cardicommon_to_r6 = THUMB_LDR_SP_IMM(THUMB_R6, 0);
patch_cardireadcard_adjust_cardicommon_offset = THUMB_NOP;
patch_cardireadcard_mov_r3_to_dst = THUMB_STR_SP_IMM(THUMB_R3, 4);
}
else if (_foundPattern == sCARDiReadCardPatternSdk2004F4CThumb)
{
patchOffset = 0x36;
cardiCommonOffset = 0;
patch_cardireadcard_return_offset = THUMB_MOVS_IMM(THUMB_R0, 0x2E);
patch_cardireadcard_mov_src_to_r0 = THUMB_MOVS_REG(THUMB_R0, THUMB_R1); // src
patch_cardireadcard_mov_dst_to_r1 = THUMB_MOVS_REG(THUMB_R1, THUMB_R5); // dst
patch_cardireadcard_mov_cardicommon_to_r6 = THUMB_LDR_SP_IMM(THUMB_R6, 8); // r6 from stack
if (patchContext.GetSdkVersion().GetMajor() <= SDK_VERSION_MAJOR_NITRO_2)
patch_cardireadcard_adjust_cardicommon_offset = THUMB_SUBS_IMM(THUMB_R6, THUMB_R6, 4);
else
patch_cardireadcard_adjust_cardicommon_offset = THUMB_NOP;
patch_cardireadcard_mov_r3_to_dst = THUMB_MOVS_REG(THUMB_R5, THUMB_R3);
}
else
{
LOG_FATAL("Unsupported CARDi_ReadCard pattern\n");
while (1);
}
*(u16*)((u8*)_cardiReadCard + patchOffset + 0) = cardiCommonOffset == 0 ? THUMB_NOP : THUMB_LDR_PC_IMM(THUMB_R3, cardiCommonOffset); // ldr r0,= cardi_common
*(u16*)((u8*)_cardiReadCard + patchOffset + 2) = THUMB_LDR_PC_IMM(THUMB_R0, 0); // ldr r0,= entryAddress
*(u16*)((u8*)_cardiReadCard + patchOffset + 4) = THUMB_BLX(THUMB_R0);
*(u32*)((u8*)_cardiReadCard + patchOffset + 6) = entryAddress;
}
else
{
u32 patchOffset;
if (_foundPattern == sCARDiReadCardPatternSdk4027539SpiritTracks)
{
patchOffset = 0x44;
patch_cardireadcard_return_offset = THUMB_MOVS_IMM(THUMB_R0, 0x44);
patch_cardireadcard_mov_src_to_r0 = THUMB_MOVS_REG(THUMB_R0, THUMB_R2); // src
patch_cardireadcard_mov_dst_to_r1 = THUMB_MOVS_REG(THUMB_R1, THUMB_R5); // dst
patch_cardireadcard_mov_cardicommon_to_r6 = THUMB_MOVS_REG(THUMB_R6, THUMB_R4);
patch_cardireadcard_adjust_cardicommon_offset = THUMB_NOP;
patch_cardireadcard_mov_r3_to_dst = THUMB_MOVS_REG(THUMB_R5, THUMB_R3);
}
else if (_foundPattern == sCARDiReadCardPatternSdk4017530)
{
patchOffset = 0x44;
patch_cardireadcard_return_offset = THUMB_MOVS_IMM(THUMB_R0, 0x48);
patch_cardireadcard_mov_src_to_r0 = THUMB_MOVS_REG(THUMB_R0, THUMB_R2); // src
patch_cardireadcard_mov_dst_to_r1 = THUMB_MOVS_REG(THUMB_R1, THUMB_R5); // dst
patch_cardireadcard_mov_cardicommon_to_r6 = THUMB_MOVS_REG(THUMB_R6, THUMB_R4);
patch_cardireadcard_adjust_cardicommon_offset = THUMB_NOP;
patch_cardireadcard_mov_r3_to_dst = THUMB_MOVS_REG(THUMB_R5, THUMB_R3);
}
else if (_foundPattern == sCARDiReadCardPatternSdk4002774)
{
patchOffset = 0x44;
patch_cardireadcard_return_offset = THUMB_MOVS_IMM(THUMB_R0, 0x40);
patch_cardireadcard_mov_src_to_r0 = THUMB_MOVS_REG(THUMB_R0, THUMB_R2); // src
patch_cardireadcard_mov_dst_to_r1 = THUMB_MOVS_REG(THUMB_R1, THUMB_R5); // dst
patch_cardireadcard_mov_cardicommon_to_r6 = THUMB_MOVS_REG(THUMB_R6, THUMB_R4);
patch_cardireadcard_adjust_cardicommon_offset = THUMB_NOP;
patch_cardireadcard_mov_r3_to_dst = THUMB_MOVS_REG(THUMB_R5, THUMB_R3);
}
else if (_foundPattern == sCARDiReadCardPatternSdk20029A7 || _foundPattern == sCARDiReadCardPatternUnknown)
{
patchOffset = 0x58;
patch_cardireadcard_return_offset = THUMB_MOVS_IMM(THUMB_R0, 0x38);
patch_cardireadcard_mov_src_to_r0 = THUMB_MOVS_REG(THUMB_R0, THUMB_R1); // src
patch_cardireadcard_mov_dst_to_r1 = THUMB_MOV_HIREG(THUMB_R1, THUMB_HI_R8); // dst
patch_cardireadcard_mov_cardicommon_to_r6 = THUMB_MOV_HIREG(THUMB_R6, THUMB_HI_R9);
if (patchContext.GetSdkVersion().GetMajor() <= SDK_VERSION_MAJOR_NITRO_2)
patch_cardireadcard_adjust_cardicommon_offset = THUMB_SUBS_IMM(THUMB_R6, THUMB_R6, 4);
else
patch_cardireadcard_adjust_cardicommon_offset = THUMB_NOP;
patch_cardireadcard_mov_r3_to_dst = THUMB_MOV_HIREG(THUMB_HI_R8, THUMB_R3);
}
else if (_foundPattern == sCARDiReadCardPatternSdk2004E8BPingPals)
{
patchOffset = 0x5C;
patch_cardireadcard_return_offset = THUMB_MOVS_IMM(THUMB_R0, 0x40);
patch_cardireadcard_mov_src_to_r0 = THUMB_MOVS_REG(THUMB_R0, THUMB_R1); // src
patch_cardireadcard_mov_dst_to_r1 = THUMB_MOV_HIREG(THUMB_R1, THUMB_HI_R8); // dst
patch_cardireadcard_mov_cardicommon_to_r6 = THUMB_MOV_HIREG(THUMB_R6, THUMB_HI_R9);
patch_cardireadcard_adjust_cardicommon_offset = THUMB_SUBS_IMM(THUMB_R6, THUMB_R6, 4);
patch_cardireadcard_mov_r3_to_dst = THUMB_MOV_HIREG(THUMB_HI_R8, THUMB_R3);
}
else
{
LOG_FATAL("Unsupported CARDi_ReadCard pattern\n");
while (1);
}
*(u32*)((u8*)_cardiReadCard + patchOffset + 0) = 0xE59F0000; // ldr r0,= entryAddress
*(u32*)((u8*)_cardiReadCard + patchOffset + 4) = 0xE12FFF30; // blx r0
*(u32*)((u8*)_cardiReadCard + patchOffset + 8) = entryAddress;
}
memcpy(patch1Address, SECTION_START(patch_cardireadcard), patch1Size);
memcpy(patch4Address, SECTION_START(fixcp15), patch4Size);
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include "patches/Patch.h"
/// @brief Arm9 patch to redirect card reads on SDK 2-4.
class CardiReadCardPatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _cardiReadCard = nullptr;
u16 _thumb = false;
const u32* _foundPattern = nullptr;
void TryPattern(PatchContext& patchContext, const u32* pattern, u32 byteLength);
};

View File

@@ -0,0 +1,17 @@
#pragma once
#include "sections.h"
DEFINE_SECTION_SYMBOLS(patch_cardireadcard);
extern "C" void patch_cardireadcard_entry();
extern u32 __patch_cardireadcard_fix_cp15_asm_address;
extern u32 __patch_cardireadcard_rom_offset_to_sd_sector_asm_address;
extern u32 __patch_cardireadcard_sdread_asm_address;
extern u16 patch_cardireadcard_return_offset;
extern u16 patch_cardireadcard_mov_src_to_r0;
extern u16 patch_cardireadcard_mov_dst_to_r1;
extern u16 patch_cardireadcard_mov_cardicommon_to_r6;
extern u16 patch_cardireadcard_adjust_cardicommon_offset;
extern u16 patch_cardireadcard_mov_r3_to_dst;

View File

@@ -0,0 +1,80 @@
.cpu arm946e-s
.section "patch_cardireadcard", "ax"
.syntax unified
.thumb
.global patch_cardireadcard_entry
.type patch_cardireadcard_entry, %function
patch_cardireadcard_entry:
.global patch_cardireadcard_return_offset
patch_cardireadcard_return_offset:
movs r0, #0x38
add lr, r0
push {r3,r4,r6,lr}
ldr r3, __patch_cardireadcard_fix_cp15_asm_address
blx r3
.global patch_cardireadcard_mov_src_to_r0
patch_cardireadcard_mov_src_to_r0:
nop
ldr r3, __patch_cardireadcard_rom_offset_to_sd_sector_asm_address
blx r3
movs r2, #1 // r2 = sector count
.global patch_cardireadcard_mov_dst_to_r1
patch_cardireadcard_mov_dst_to_r1:
mov r1, r8 // r1 = dst
.global patch_cardireadcard_mov_cardicommon_to_r6
patch_cardireadcard_mov_cardicommon_to_r6:
movs r6, r4 // r6 = cardi_common
.global patch_cardireadcard_adjust_cardicommon_offset
patch_cardireadcard_adjust_cardicommon_offset:
subs r6, #0
ldr r4, [r6, #0x20] // r4 = actual dst
cmp r4, r1
bne do_read // if dst != actual dst the cache is in use
ldr r2, [r6, #0x24] // remaining length
lsrs r2, r2, #9 // sectors left to read
cmp r2, lr
ble 1f
mov r2, lr // clip to cluster boundary
1:
subs r4, r2, #1
lsls r4, r4, #9
ldr r3, [r6, #0x1C]
adds r3, r3, r4
str r3, [r6, #0x1C]
ldr r3, [r6, #0x20]
adds r3, r3, r4
str r3, [r6, #0x20]
.global patch_cardireadcard_mov_r3_to_dst
patch_cardireadcard_mov_r3_to_dst:
mov r8, r3
ldr r3, [r6, #0x24]
subs r3, r3, r4
str r3, [r6, #0x24]
do_read:
ldr r3, __patch_cardireadcard_sdread_asm_address
blx r3
pop {r3,r4,r6,pc}
.balign 4
.global __patch_cardireadcard_fix_cp15_asm_address
__patch_cardireadcard_fix_cp15_asm_address:
.word 0
.global __patch_cardireadcard_rom_offset_to_sd_sector_asm_address
__patch_cardireadcard_rom_offset_to_sd_sector_asm_address:
.word 0
.global __patch_cardireadcard_sdread_asm_address
__patch_cardireadcard_sdread_asm_address:
.word 0
.pool
.end

View File

@@ -0,0 +1,41 @@
#pragma once
#include "patches/PatchCode.h"
#include "sections.h"
#include "patches/arm9/RomOffsetToSdSectorAsm.h"
#include "patches/platform/SdReadDmaPatchCode.h"
DEFINE_SECTION_SYMBOLS(patch_cardisetcarddma);
extern "C" u32 patch_cardisetcarddma_entry(void);
extern "C" u32 patch_cardionreadcard_entry(void);
extern u32 patch_cardisetcarddma_cardi_common;
extern u32 patch_cardisetcarddma_rom_offset_to_sd_sector_asm_address;
extern u32 patch_cardisetcarddma_sdreaddma_asm_address;
extern u32 patch_cardionreadcard_osdisableirqmask_address;
extern u32 patch_cardionreadcard_sdreaddma_finish_asm_address;
class CardiSetCardDmaPatchCode : public PatchCode
{
public:
CardiSetCardDmaPatchCode(PatchHeap& patchHeap, const RomOffsetToSdSectorPatchCode* romOffsetToSdSectorPatchCode,
const SdReadDmaPatchCode* sdReadDmaPatchCode, const void* cardiCommonPointer, const void* osDisableIrqMaskPointer)
: PatchCode(SECTION_START(patch_cardisetcarddma), SECTION_SIZE(patch_cardisetcarddma), patchHeap)
{
patch_cardisetcarddma_cardi_common = (u32)cardiCommonPointer;
patch_cardisetcarddma_rom_offset_to_sd_sector_asm_address = (u32)romOffsetToSdSectorPatchCode->GetRemapFunction();
patch_cardisetcarddma_sdreaddma_asm_address = (u32)sdReadDmaPatchCode->GetSdReadDmaFunction();
patch_cardionreadcard_osdisableirqmask_address = (u32)osDisableIrqMaskPointer;
patch_cardionreadcard_sdreaddma_finish_asm_address = (u32)sdReadDmaPatchCode->GetSdReadDmaFinishFunction();
}
const void* GetCardiSetCardDmaFunction() const
{
return GetAddressAtTarget((void*)patch_cardisetcarddma_entry);
}
const void* GetCardiOnReadCardPatchFunction() const
{
return GetAddressAtTarget((void*)patch_cardionreadcard_entry);
}
};

View File

@@ -0,0 +1,75 @@
.cpu arm946e-s
.section "patch_cardisetcarddma", "ax"
.syntax unified
.thumb
.global patch_cardisetcarddma_entry
.type patch_cardisetcarddma_entry, %function
patch_cardisetcarddma_entry:
push {r4,lr}
ldr r4, patch_cardisetcarddma_cardi_common
ldr r0, [r4, #0x18] // src
ldr r3, patch_cardisetcarddma_rom_offset_to_sd_sector_asm_address
blx r3
ldr r1, previousSector
adr r2, previousSector
str r0, [r2]
ldr r2, [r4, #0x24] // dma channel
ldr r3, [r4, #0x1C] // dst
ldr r4, patch_cardisetcarddma_sdreaddma_asm_address
blx r4
pop {r4,pc}
.thumb
.global patch_cardionreadcard_entry
.type patch_cardionreadcard_entry, %function
patch_cardionreadcard_entry:
push {lr}
ldr r0,= 0x80000
ldr r1, patch_cardionreadcard_osdisableirqmask_address
blx r1
ldr r0, patch_cardionreadcard_sdreaddma_finish_asm_address
blx r0
movs r0, #0
adr r1, previousSector
str r0, [r1]
ldr r0,= 0x80000
pop {r1}
adds r1, #4
bx r1
.balign 4
previousSector:
.word 0
.global patch_cardisetcarddma_cardi_common
patch_cardisetcarddma_cardi_common:
.word 0
.global patch_cardisetcarddma_rom_offset_to_sd_sector_asm_address
patch_cardisetcarddma_rom_offset_to_sd_sector_asm_address:
.word 0
.global patch_cardisetcarddma_sdreaddma_asm_address
patch_cardisetcarddma_sdreaddma_asm_address:
.word 0
.global patch_cardionreadcard_osdisableirqmask_address
patch_cardionreadcard_osdisableirqmask_address:
.word 0
.global patch_cardionreadcard_sdreaddma_finish_asm_address
patch_cardionreadcard_sdreaddma_finish_asm_address:
.word 0
.pool
.end

View File

@@ -0,0 +1,299 @@
#include "common.h"
#include "patches/PatchContext.h"
#include "thumbInstructions.h"
#include "CardiSetCardDmaPatchAsm.h"
#include "patches/platform/LoaderPlatform.h"
#include "CardiTryReadCardDmaPatch.h"
static const u32 sCARDiTryReadCardDmaPatternPingPals[] = { 0xE92D47F0u, 0xE1A0A000u, 0xE59F9120u, 0xE3A08000u };
static const u32 sCARDiTryReadCardDmaPatternUnknown[] = { 0xE92D47F0u, 0xE59F9138u, 0xE3A06000u, 0xE5998020u };
static const u32 sCARDiTryReadCardDmaPatternUnknown2[] = { 0xE92D47F0u, 0xE59F4134u, 0xE3A06000u, 0xE5949020u };
static const u32 sCARDiTryReadCardDmaPatternUnknown3[] = { 0xE92D4FF8u, 0xE59FB140u, 0xE3A05000u, 0xE59B8020u };
static const u32 sCARDiTryReadCardDmaPatternUnknown4[] = { 0xE92D47F0u, 0xE59F9138u, 0xE3A06000u, 0xE599801Cu };
static const u32 sCARDiTryReadCardDmaPattern20029A7[] = { 0xE92D47F0u, 0xE59F9128u, 0xE3A07000u, 0xE599401Cu };
static const u32 sCARDiTryReadCardDmaPattern2012774[] = { 0xE92D47F0u, 0xE59F9134u, 0xE3A07000u, 0xE599401Cu };
static const u32 sCARDiTryReadCardDmaPattern2017532[] = { 0xE92D47F0u, 0xE59F9130u, 0xE3A07000u, 0xE599401Cu };
static const u32 sCARDiTryReadCardDmaPattern2027530[] = { 0xE92D47F0u, 0xE59F913Cu, 0xE3A06000u, 0xE599801Cu };
static const u32 sCARDiTryReadCardDmaPattern[] = { 0xE92D47F0u, 0xE59F913Cu, 0xE3A06000u, 0xE5998020u };
static const u32 sCARDiTryReadCardDmaPatternSdk3017530[] = { 0xE92D4FF0u, 0xE24DD004u, 0xE59FB14Cu, 0xE3A07000u };
static const u32 sCARDiTryReadCardDmaPatternSdk3017534[] = { 0xE92D4FF0u, 0xE24DD004u, 0xE59FB148u, 0xE3A07000u };
static const u32 sCARDiTryReadCardDmaPatternSdk3027530[] = { 0xE92D4FF0u, 0xE24DD004u, 0xE59FB150u, 0xE3A07000u };
static const u32 sCARDiTryReadCardDmaPatternSdk4007530[] = { 0xE92D4FF8u, 0xE59FB144u, 0xE3A05000u, 0xE59B8020u };
static const u32 sCARDiTryReadCardDmaPatternSdk4007532[] = { 0xE92D4FF8u, 0xE59F416Cu, 0xE3A06000u, 0xE5949020u };
static const u32 sCARDiTryReadCardDmaPatternSdk4027539SpiritTracks[] = { 0xE92D4FF8u, 0xE59F4174u, 0xE3A06000u, 0xE5949020u };
static const u32 sCARDiTryReadCardDmaPatternJpnPokemonDownloader[] = { 0xE92D4FF8u, 0xE59FB138u, 0xE3A05000u, 0xE59B8020u };
static const u32 sCARDiTryReadCardDmaPatternThumbChouSoujuu[] = { 0xB083B5F0u, 0x48399000u, 0x6A0469C5u, 0x96012600u };
static const u32 sCARDiTryReadCardDmaPatternSdk2004F4CThumb[] = { 0xB083B5F0u, 0x4D359000u, 0x94012400u, 0x90020020u };
static const u32 sCARDiTryReadCardDmaPatternSdk2007531Thumb[] = { 0xB083B5F0u, 0x4D379000u, 0x94012400u, 0x90020020u };
static const u32 sCARDiTryReadCardDmaPatternSdk2027530Thumb[] = { 0xB083B5F0u, 0x483A9000u, 0x6A0469C5u, 0x96012600u };
static const u32 sCARDiTryReadCardDmaPatternSdk3007530Thumb[] = { 0xB083B5F0u, 0x483A9000u, 0x6A446A05u, 0x96012600u };
static const u32 sCARDiTryReadCardDmaPatternSdk3012776Thumb[] = { 0xB083B5F0u, 0x483A9000u, 0x6A446A05u, 0x90012600u };
static const u32 sCARDiTryReadCardDmaPatternSdk3017531Thumb[] = { 0xB085B5F0u, 0x483C9000u, 0x6A446A05u, 0x90012000u };
static const u32 sCARDiTryReadCardDmaPatternSdk3027530Thumb[] = { 0xB085B5F0u, 0x483D9000u, 0x6A446A05u, 0x90012000u };
static const u32 sCARDiTryReadCardDmaPatternSdkThumb[] = { 0xB084B5F8u, 0x483D9000u, 0x6A446A05u, 0x90032000u };
static const u32 sCARDiTryReadCardDmaPatternSdk4007531Thumb[] = { 0xB084B5F8u, 0x483E9000u, 0x6A446A05u, 0x90032000u };
static const u32 sCARDiTryReadCardDmaPatternSdk4017530Thumb[] = { 0xB084B5F8u, 0x48479000u, 0x6A446A05u, 0x90032000u };
static const u32 sCARDiTryReadCardDmaPatternSdk4027530Thumb[] = { 0xB084B5F8u, 0x48479000u, 0x6A446A05u, 0x90032000u };
static const u16 sReturnFalsePatchThumb[] = { THUMB_MOVS_IMM(0, 0), THUMB_BX_LR };
static const u32 sReturnFalsePatchArm[] = { 0xE3A00000, 0xE12FFF1E }; // mov r0, #0; bx lr
void CardiTryReadCardDmaPatch::TryPattern(PatchContext& patchContext, const u32* pattern)
{
_cardiTryReadCardDma = patchContext.FindPattern32(pattern, 16);
if (_cardiTryReadCardDma)
{
_foundPattern = pattern;
}
}
bool CardiTryReadCardDmaPatch::FindPatchTarget(PatchContext& patchContext)
{
if (patchContext.GetSdkVersion() >= 0x4007532)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk4007532);
else if (patchContext.GetSdkVersion() >= 0x4007530)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk4007530);
else if (patchContext.GetSdkVersion() >= 0x3027530)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk3027530);
else if (patchContext.GetSdkVersion() >= 0x3017530)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk3017530);
else if (patchContext.GetSdkVersion() > 0x2027534)
TryPattern(patchContext, sCARDiTryReadCardDmaPattern);
else if (patchContext.GetSdkVersion() > 0x2017532)
TryPattern(patchContext, sCARDiTryReadCardDmaPattern2027530);
else if (patchContext.GetSdkVersion() > 0x2004FB4)
TryPattern(patchContext, sCARDiTryReadCardDmaPattern2012774);
else
TryPattern(patchContext, sCARDiTryReadCardDmaPattern20029A7);
if (!_cardiTryReadCardDma)
{
if (patchContext.GetSdkVersion() >= 0x4027530)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk4027530Thumb);
else if (patchContext.GetSdkVersion() >= 0x4017530)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk4017530Thumb);
else if (patchContext.GetSdkVersion() >= 0x4007531)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk4007531Thumb);
else if (patchContext.GetSdkVersion() >= 0x3027530)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk3027530Thumb);
else if (patchContext.GetSdkVersion() >= 0x3017531)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk3017531Thumb);
else if (patchContext.GetSdkVersion() >= 0x3017530)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk3012776Thumb);
else if (patchContext.GetSdkVersion() >= 0x3007530)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk3007530Thumb);
else if (patchContext.GetSdkVersion() >= 0x2027530)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk2027530Thumb);
else if (patchContext.GetSdkVersion() >= 0x2007531)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk2007531Thumb);
else if (patchContext.GetSdkVersion() >= 0x2004F4C)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk2004F4CThumb);
if (_cardiTryReadCardDma)
_thumb = true;
}
if (!_cardiTryReadCardDma)
{
TryPattern(patchContext, sCARDiTryReadCardDmaPatternUnknown);
if (!_cardiTryReadCardDma)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk4007530);
if (!_cardiTryReadCardDma)
TryPattern(patchContext, sCARDiTryReadCardDmaPattern20029A7);
if (!_cardiTryReadCardDma)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk3017530);
if (!_cardiTryReadCardDma)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternUnknown2);
if (!_cardiTryReadCardDma)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternUnknown3);
if (!_cardiTryReadCardDma)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk3017534);
if (!_cardiTryReadCardDma)
TryPattern(patchContext, sCARDiTryReadCardDmaPattern);
if (patchContext.GetSdkVersion() < 0x3000000)
{
if (!_cardiTryReadCardDma)
TryPattern(patchContext, sCARDiTryReadCardDmaPattern2017532);
if (!_cardiTryReadCardDma)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternPingPals);
if (!_cardiTryReadCardDma)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternUnknown4);
}
if (patchContext.GetSdkVersion() >= 0x4000000)
{
if (!_cardiTryReadCardDma)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk4027539SpiritTracks);
}
}
if (!_cardiTryReadCardDma)
{
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk4007531Thumb);
if (!_cardiTryReadCardDma)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdkThumb);
if (!_cardiTryReadCardDma)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternSdk2004F4CThumb);
if (patchContext.GetSdkVersion() < 0x3000000)
{
if (!_cardiTryReadCardDma)
TryPattern(patchContext, sCARDiTryReadCardDmaPatternThumbChouSoujuu);
}
if (_cardiTryReadCardDma)
_thumb = true;
}
if (!_cardiTryReadCardDma)
{
_cardiTryReadCardDma = patchContext.FindPattern32(sCARDiTryReadCardDmaPatternJpnPokemonDownloader, sizeof(sCARDiTryReadCardDmaPatternJpnPokemonDownloader));
}
if (!_cardiTryReadCardDma)
{
LOG_WARNING("CARDi_TryReadCardDma not found\n");
}
else
{
LOG_DEBUG("Found CARDi_TryReadCardDma at %p\n", _cardiTryReadCardDma);
}
// There are some sdk roms that do not contain this function at all
return true; //_cardiTryReadCardDma != nullptr;
}
static u32 getArmBlAddress(const u32* instructionPointer)
{
u32 blInstruction = *instructionPointer;
return (u32)instructionPointer + 8 + ((int)((blInstruction & 0xFFFFFF) << 8) >> 6);
}
void CardiTryReadCardDmaPatch::ApplyPatch(PatchContext& patchContext)
{
if (!_cardiTryReadCardDma)
return;
if (_thumb)
{
((u16*)_cardiTryReadCardDma)[0] = sReturnFalsePatchThumb[0];
((u16*)_cardiTryReadCardDma)[1] = sReturnFalsePatchThumb[1];
}
else
{
bool enableDma = patchContext.GetLoaderPlatform()->HasDmaSdReads();
if (enableDma)
{
u32 cardiCommon;
u32 cardiOnReadCard;
u32 cardiSetCardDma;
u32 miiCardDmaCopy32;
u32 cardiOnReadCardOffset;
if (_foundPattern == sCARDiTryReadCardDmaPattern20029A7)
{
cardiCommon = *(u32*)((u8*)_cardiTryReadCardDma + 0x134);
cardiOnReadCard = *(u32*)((u8*)_cardiTryReadCardDma + 0x144);
cardiSetCardDma = getArmBlAddress((u32*)((u8*)_cardiTryReadCardDma + 0x124));
if (*(u32*)cardiOnReadCard == 0xE92D40F0u)
{
miiCardDmaCopy32 = getArmBlAddress((u32*)(cardiSetCardDma + 0x18));
cardiOnReadCardOffset = 0x40;
}
else
{
// old version is not supported yet
_cardiTryReadCardDma[0] = sReturnFalsePatchArm[0];
_cardiTryReadCardDma[1] = sReturnFalsePatchArm[1];
return;
}
}
else if (_foundPattern == sCARDiTryReadCardDmaPattern2012774)
{
cardiCommon = *(u32*)((u8*)_cardiTryReadCardDma + 0x140);
cardiOnReadCard = *(u32*)((u8*)_cardiTryReadCardDma + 0x150);
cardiSetCardDma = getArmBlAddress((u32*)((u8*)_cardiTryReadCardDma + 0x130));
miiCardDmaCopy32 = getArmBlAddress((u32*)(cardiSetCardDma + 0x18));
cardiOnReadCardOffset = 0x40;
}
else if (_foundPattern == sCARDiTryReadCardDmaPattern)
{
cardiCommon = *(u32*)((u8*)_cardiTryReadCardDma + 0x148) + 4;
cardiOnReadCard = *(u32*)((u8*)_cardiTryReadCardDma + 0x158);
cardiSetCardDma = getArmBlAddress((u32*)((u8*)_cardiTryReadCardDma + 0x138));
miiCardDmaCopy32 = getArmBlAddress((u32*)(cardiSetCardDma + 0x18));
cardiOnReadCardOffset = 0x40;
}
else if (_foundPattern == sCARDiTryReadCardDmaPatternSdk3017530)
{
cardiCommon = *(u32*)((u8*)_cardiTryReadCardDma + 0x15C) + 4;
cardiOnReadCard = *(u32*)((u8*)_cardiTryReadCardDma + 0x16C);
cardiSetCardDma = getArmBlAddress((u32*)((u8*)_cardiTryReadCardDma + 0x148));
miiCardDmaCopy32 = getArmBlAddress((u32*)(cardiSetCardDma + 0x18));
cardiOnReadCardOffset = 0x40;
}
else if (_foundPattern == sCARDiTryReadCardDmaPatternSdk3027530)
{
cardiCommon = *(u32*)((u8*)_cardiTryReadCardDma + 0x160) + 4;
cardiOnReadCard = *(u32*)((u8*)_cardiTryReadCardDma + 0x170);
cardiSetCardDma = getArmBlAddress((u32*)((u8*)_cardiTryReadCardDma + 0x14C));
miiCardDmaCopy32 = getArmBlAddress((u32*)(cardiSetCardDma + 0x18));
cardiOnReadCardOffset = 0x40;
}
else if (_foundPattern == sCARDiTryReadCardDmaPatternSdk4007532)
{
cardiCommon = *(u32*)((u8*)_cardiTryReadCardDma + 0x178) + 4;
cardiOnReadCard = *(u32*)((u8*)_cardiTryReadCardDma + 0x188);
cardiSetCardDma = getArmBlAddress((u32*)((u8*)_cardiTryReadCardDma + 0x16C));
miiCardDmaCopy32 = getArmBlAddress((u32*)(cardiSetCardDma + 0x1C));
cardiOnReadCardOffset = 0x48;
}
else if (_foundPattern == sCARDiTryReadCardDmaPatternSdk4027539SpiritTracks)
{
cardiCommon = *(u32*)((u8*)_cardiTryReadCardDma + 0x180) + 4;
cardiOnReadCard = *(u32*)((u8*)_cardiTryReadCardDma + 0x190);
cardiSetCardDma = getArmBlAddress((u32*)((u8*)_cardiTryReadCardDma + 0x174));
miiCardDmaCopy32 = getArmBlAddress((u32*)(cardiSetCardDma + 0x1C));
cardiOnReadCardOffset = 0x48;
}
else
{
_cardiTryReadCardDma[0] = sReturnFalsePatchArm[0];
_cardiTryReadCardDma[1] = sReturnFalsePatchArm[1];
return;
}
u32 osDisableIrqMask = getArmBlAddress((u32*)(cardiOnReadCard + cardiOnReadCardOffset + 4));
// patch CARDi_SetCardDma
auto sdReadDmaPatchCode = patchContext.GetLoaderPlatform()->CreateSdReadDmaPatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap(), (const void*)miiCardDmaCopy32);
auto romOffsetToSdSectorPatchCode = patchContext.GetPatchCodeCollection().GetOrAddSharedPatchCode([&]
{
return new RomOffsetToSdSectorPatchCode(patchContext.GetPatchHeap(),
(const rom_file_info_t*)((u32)SHARED_ROM_FILE_INFO - 0x02F00000 + 0x02700000));
});
auto cardiSetCardDmaPatchCode = patchContext.GetPatchCodeCollection().AddUniquePatchCode<CardiSetCardDmaPatchCode>
(
patchContext.GetPatchHeap(),
romOffsetToSdSectorPatchCode,
sdReadDmaPatchCode,
(const void*)cardiCommon,
(const void*)osDisableIrqMask
);
*(u32*)(cardiSetCardDma + 0) = 0xE51FF004; // ldr pc,= entryAddress
*(u32*)(cardiSetCardDma + 4) = (u32)cardiSetCardDmaPatchCode->GetCardiSetCardDmaFunction();
// patch CARDi_OnReadCard
*(u32*)(cardiOnReadCard + cardiOnReadCardOffset + 0) = 0xE59F0000; // ldr r0,= entryAddress
*(u32*)(cardiOnReadCard + cardiOnReadCardOffset + 4) = 0xE12FFF30; // blx r0
*(u32*)(cardiOnReadCard + cardiOnReadCardOffset + 8) = (u32)cardiSetCardDmaPatchCode->GetCardiOnReadCardPatchFunction();
LOG_DEBUG("DMA enabled\n");
}
else
{
_cardiTryReadCardDma[0] = sReturnFalsePatchArm[0];
_cardiTryReadCardDma[1] = sReturnFalsePatchArm[1];
}
}
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include "patches/Patch.h"
/// @brief Arm9 patch to redirect DMA card reads, or to disable them entirely, on SDK 2-4.
class CardiTryReadCardDmaPatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _cardiTryReadCardDma = nullptr;
u32 _thumb = false;
const u32* _foundPattern = nullptr;
void TryPattern(PatchContext& patchContext, const u32* pattern);
};

View File

@@ -0,0 +1,54 @@
#include "common.h"
#include "patches/PatchContext.h"
#include "thumbInstructions.h"
#include "CardiIsRomDmaAvailablePatch.h"
static const u32 sCARDiIsRomDmaAvailablePattern[] = { 0xE92D43F8u, 0xE3A04000u, 0xE1A09001u, 0xE1A08002u };
static const u32 sCARDiIsRomDmaAvailablePatternThumb[] = { 0xB082B5F8u, 0x1C0D2400u, 0x1C1E9200u, 0x94011C21u };
static const u32 sCARDiIsRomDmaAvailablePatternThumbHybrid[] = { 0xB082B5F8u, 0x21001C0Du, 0x92009101u, 0x27001C1Cu };
static const u32 sReturnFalsePatchArm[] = { 0xE3A00000, 0xE12FFF1E }; // mov r0, #0; bx lr
bool CardiIsRomDmaAvailablePatch::FindPatchTarget(PatchContext& patchContext)
{
_cardiIsRomDmaAvailable = patchContext.FindPattern32(sCARDiIsRomDmaAvailablePattern, sizeof(sCARDiIsRomDmaAvailablePattern));
if (!_cardiIsRomDmaAvailable)
{
_cardiIsRomDmaAvailable = patchContext.FindPattern32(sCARDiIsRomDmaAvailablePatternThumbHybrid, sizeof(sCARDiIsRomDmaAvailablePatternThumbHybrid));
if (_cardiIsRomDmaAvailable)
{
_thumb = true;
}
else
{
_cardiIsRomDmaAvailable = patchContext.FindPattern32(sCARDiIsRomDmaAvailablePatternThumb, sizeof(sCARDiIsRomDmaAvailablePatternThumb));
if (_cardiIsRomDmaAvailable)
{
_thumb = true;
}
}
}
if (!_cardiIsRomDmaAvailable)
{
LOG_WARNING("CARDi_IsRomDmaAvailable not found\n");
}
// If an application uses no rom reads, the function is not included in the rom.
return true; //_cardiIsRomDmaAvailable != nullptr;
}
void CardiIsRomDmaAvailablePatch::ApplyPatch(PatchContext& patchContext)
{
if (!_cardiIsRomDmaAvailable)
return;
if (_thumb)
{
((u16*)_cardiIsRomDmaAvailable)[0] = THUMB_MOVS_IMM(THUMB_R0, 0);
((u16*)_cardiIsRomDmaAvailable)[1] = THUMB_BX_LR;
}
else
{
_cardiIsRomDmaAvailable[0] = sReturnFalsePatchArm[0];
_cardiIsRomDmaAvailable[1] = sReturnFalsePatchArm[1];
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "patches/Patch.h"
/// @brief Arm9 patch to disable DMA card reads on SDK 5.
class CardiIsRomDmaAvailablePatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _cardiIsRomDmaAvailable = nullptr;
u16 _thumb = false;
};

View File

@@ -0,0 +1,63 @@
#include "common.h"
#include "patches/PatchContext.h"
#include "thumbInstructions.h"
#include "CardiReadCardWithHashInternalAsyncPatch.h"
static const u32 sCARDiReadCardWithHashInternalAsyncPattern[] = { 0xE92D40F8u, 0xE59F405Cu, 0xE3E0C000u, 0xE594E508u };
static const u32 sCARDiReadCardWithHashInternalAsyncPattern54[] = { 0xE92D40F8u, 0xE59FC058u, 0xE1A04000u, 0xE59C0508u };
static const u32 sCARDiReadCardWithHashInternalAsyncPatternThumb[] = { 0x1C06B5F8u, 0x1C0D480Eu, 0x20006881u, 0x1C1743C0u };
// static const u32 sCARDiReadCardWithHashInternalAsyncPatternThumb5004E85[] = { 0x1C1CB538u, 0x689D4B0Bu, 0x43DB2300u, 0xD101429Du };
static const u32 sReturnZeroPatchArm[] = { 0xE3A00000, 0xE12FFF1E }; // mov r0, #0; bx lr
bool CardiReadCardWithHashInternalAsyncPatch::FindPatchTarget(PatchContext& patchContext)
{
_cardiReadCardWithHashInternalAsync = patchContext.FindPattern32Twl(
sCARDiReadCardWithHashInternalAsyncPattern, sizeof(sCARDiReadCardWithHashInternalAsyncPattern));
if (!_cardiReadCardWithHashInternalAsync)
{
_cardiReadCardWithHashInternalAsync = patchContext.FindPattern32Twl(
sCARDiReadCardWithHashInternalAsyncPattern54, sizeof(sCARDiReadCardWithHashInternalAsyncPattern54));
}
if (!_cardiReadCardWithHashInternalAsync)
{
_cardiReadCardWithHashInternalAsync = patchContext.FindPattern32Twl(
sCARDiReadCardWithHashInternalAsyncPatternThumb, sizeof(sCARDiReadCardWithHashInternalAsyncPatternThumb));
if (!_cardiReadCardWithHashInternalAsync)
{
_cardiReadCardWithHashInternalAsync = patchContext.FindPattern32(
sCARDiReadCardWithHashInternalAsyncPatternThumb, sizeof(sCARDiReadCardWithHashInternalAsyncPatternThumb));
}
// if (!_cardiReadCardWithHashInternalAsync)
// {
// _cardiReadCardWithHashInternalAsync = patchContext.FindPattern32(
// sCARDiReadCardWithHashInternalAsyncPatternThumb5004E85, sizeof(sCARDiReadCardWithHashInternalAsyncPatternThumb5004E85));
// }
if (_cardiReadCardWithHashInternalAsync)
{
_thumb = true;
}
}
if (!_cardiReadCardWithHashInternalAsync)
{
LOG_WARNING("CARDi_ReadCardWithHashInternalAsync not found\n");
}
return _cardiReadCardWithHashInternalAsync != nullptr;
}
void CardiReadCardWithHashInternalAsyncPatch::ApplyPatch(PatchContext& patchContext)
{
if (!_cardiReadCardWithHashInternalAsync)
return;
if (_thumb)
{
((u16*)_cardiReadCardWithHashInternalAsync)[0] = THUMB_MOVS_IMM(THUMB_R0, 0);
((u16*)_cardiReadCardWithHashInternalAsync)[1] = THUMB_BX_LR;
}
else
{
_cardiReadCardWithHashInternalAsync[0] = sReturnZeroPatchArm[0];
_cardiReadCardWithHashInternalAsync[1] = sReturnZeroPatchArm[1];
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "patches/Patch.h"
/// @brief Arm9 patch to disable DMA card reads on SDK 5.
class CardiReadCardWithHashInternalAsyncPatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _cardiReadCardWithHashInternalAsync = nullptr;
u16 _thumb = false;
};

View File

@@ -0,0 +1,145 @@
#include "common.h"
#include "patches/PatchContext.h"
#include "fileInfo.h"
#include "thumbInstructions.h"
#include "patches/platform/LoaderPlatform.h"
#include "patches/arm9/RomOffsetToSdSectorAsm.h"
#include "patches/OffsetToSectorRemapAsm.h"
#include "CardiReadRomWithCpuPatchAsm.h"
#include "patches/arm9/FixCp15Asm.h"
#include "CardiReadRomWithCpuPatch.h"
static const u32 sCARDiReadRomWithCPUPattern500753B[] = { 0xE92D4FF8u, 0xE24DD008u, 0xE59F00FCu, 0xE1A08003u };
static const u32 sCARDiReadRomWithCPUPatternThumb[] = { 0xB085B5F0u, 0x1C1D482Du, 0x91006840u, 0x95049201u };
static const u32 sCARDiReadRomWithCPUPatternThumb5017537[] = { 0xB085B5F0u, 0x1C1D482Du, 0x91006800u, 0x95049201u };
// static const u32 sCARDiReadRomWithCPUPatternThumb5004E85[] = { 0xB085B5F0u, 0x1C1D482Bu, 0x910068C0u, 0x95049201u };
static const u32 sCARDiReadRomWithCPUPatternThumb5007538[] = { 0xB085B5F0u, 0x1C1D482Bu, 0x91006800u, 0x95049201u };
static const u32 sCARDiReadRomWithCPUPatternThumb5037531[] = { 0xB086B5F8u, 0x1C1D482Fu, 0x91006800u, 0x482E9003u };
static const u32 sCARDiReadRomWithCPUPatternThumbHybrid5037531[] = { 0xB086B5F8u, 0x1C1D482Fu, 0x91006840u, 0x482E9003u };
void CardiReadRomWithCpuPatch::TryPattern(PatchContext& patchContext, const u32* pattern)
{
_cardiReadRomWithCpu = patchContext.FindPattern32(pattern, 16);
if (_cardiReadRomWithCpu)
{
_foundPattern = pattern;
}
}
bool CardiReadRomWithCpuPatch::FindPatchTarget(PatchContext& patchContext)
{
TryPattern(patchContext, sCARDiReadRomWithCPUPattern500753B);
if (!_cardiReadRomWithCpu)
{
TryPattern(patchContext, sCARDiReadRomWithCPUPatternThumb5037531);
if (!_cardiReadRomWithCpu)
{
TryPattern(patchContext, sCARDiReadRomWithCPUPatternThumbHybrid5037531);
}
if (!_cardiReadRomWithCpu)
{
TryPattern(patchContext, sCARDiReadRomWithCPUPatternThumb5017537);
}
if (!_cardiReadRomWithCpu)
{
TryPattern(patchContext, sCARDiReadRomWithCPUPatternThumb5007538);
}
if (!_cardiReadRomWithCpu)
{
TryPattern(patchContext, sCARDiReadRomWithCPUPatternThumb);
}
// if (!_cardiReadRomWithCpu)
// {
// TryPattern(patchContext, sCARDiReadRomWithCPUPatternThumb5004E85);
// }
if (_cardiReadRomWithCpu)
{
_thumb = true;
}
}
if (_cardiReadRomWithCpu)
{
LOG_DEBUG("Found CARDi_ReadRomWithCPU at %p\n", _cardiReadRomWithCpu);
if (_thumb)
{
LOG_DEBUG("thumb CARDi_ReadRomWithCPU\n");
}
}
else
{
LOG_WARNING("CARDi_ReadRomWithCPU not found\n");
}
return _cardiReadRomWithCpu != nullptr;
}
void CardiReadRomWithCpuPatch::ApplyPatch(PatchContext& patchContext)
{
if (!_cardiReadRomWithCpu)
return;
auto loaderPlatform = patchContext.GetLoaderPlatform();
if (loaderPlatform->HasRomReads())
{
auto romReadPatchCode = loaderPlatform->CreateRomReadPatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
auto offsetToSectorRemapPatchCode = patchContext.GetPatchCodeCollection().GetOrAddSharedPatchCode([&]
{
return new OffsetToSectorRemapPatchCode(patchContext.GetPatchHeap());
});
__patch_cardireadromwithcpu_rom_offset_to_sd_sector_asm_address = (u32)offsetToSectorRemapPatchCode->GetRemapFunction();
__patch_cardireadromwithcpu_sdread_asm_address = (u32)romReadPatchCode->GetSdReadFunction();
}
else
{
auto sdReadPatchCode = loaderPlatform->CreateSdReadPatchCode(
patchContext.GetPatchCodeCollection(), patchContext.GetPatchHeap());
auto romOffsetToSdSectorPatchCode = patchContext.GetPatchCodeCollection().GetOrAddSharedPatchCode([&]
{
return new RomOffsetToSdSectorPatchCode(patchContext.GetPatchHeap(), SHARED_ROM_FILE_INFO);
});
__patch_cardireadromwithcpu_rom_offset_to_sd_sector_asm_address = (u32)romOffsetToSdSectorPatchCode->GetRemapFunction();
__patch_cardireadromwithcpu_sdread_asm_address = (u32)sdReadPatchCode->GetSdReadFunction();
}
u32 patch1Size = SECTION_SIZE(patch_cardireadromwithcpu);
void* patch1Address = patchContext.GetPatchHeap().Alloc(patch1Size);
u32 patch4Size = SECTION_SIZE(fixcp15);
void* patch4Address = patchContext.GetPatchHeap().Alloc(patch4Size);
__patch_cardireadromwithcpu_fix_cp15_asm_address = (u32)&fix_cp15_asm - (u32)SECTION_START(fixcp15) + (u32)patch4Address;
u32 entryAddress = (u32)&patch_cardireadromwithcpu_entry - (u32)SECTION_START(patch_cardireadromwithcpu) + (u32)patch1Address;
if (_thumb)
{
bool old = _foundPattern == sCARDiReadRomWithCPUPatternThumb5017537
|| _foundPattern == sCARDiReadRomWithCPUPatternThumb5007538
// || _foundPattern == sCARDiReadRomWithCPUPatternThumb5004E85
|| _foundPattern == sCARDiReadRomWithCPUPatternThumb;
u32 patchOffset = old ? 0x48 : 0x4C;
u32 returnOffset = old ? 0x26 : 0x28;
patch_cardireadromwithcpu_return_offset = THUMB_MOVS_IMM(THUMB_R0, returnOffset);
patch_cardireadromwithcpu_mov_src_to_r0 = THUMB_MOVS_REG(THUMB_R0, THUMB_R1); // src
patch_cardireadromwithcpu_mov_dst_to_r1 = THUMB_MOVS_REG(THUMB_R1, THUMB_R4); // dst
patch_cardireadromwithcpu_mov_actual_dst_to_r3 = THUMB_LDR_SP_IMM(THUMB_R3, 4);
patch_cardireadromwithcpu_mov_left_to_read_to_r2 = THUMB_MOVS_REG(THUMB_R2, THUMB_R5);
*(u16*)((u8*)_cardiReadRomWithCpu + patchOffset - 2) = THUMB_LDR_SP_IMM(THUMB_R1, 8); // load rom source to r1 instead of r0
*(u16*)((u8*)_cardiReadRomWithCpu + patchOffset + 0) = 0x4800; // ldr r0,= entryAddress
*(u16*)((u8*)_cardiReadRomWithCpu + patchOffset + 2) = THUMB_BLX(THUMB_R0);
*(u32*)((u8*)_cardiReadRomWithCpu + patchOffset + 4) = entryAddress;
}
else
{
u32 patchOffset = 0x78;
patch_cardireadromwithcpu_return_offset = THUMB_MOVS_IMM(THUMB_R0, 0x28);
patch_cardireadromwithcpu_mov_src_to_r0 = THUMB_MOVS_REG(THUMB_R0, THUMB_R6); // src
patch_cardireadromwithcpu_mov_dst_to_r1 = THUMB_MOVS_REG(THUMB_R1, THUMB_R5); // dst
patch_cardireadromwithcpu_mov_actual_dst_to_r3 = THUMB_MOV_HIREG(THUMB_R3, THUMB_HI_R10);
patch_cardireadromwithcpu_mov_left_to_read_to_r2 = THUMB_MOV_HIREG(THUMB_R2, THUMB_HI_R8);
*(u32*)((u8*)_cardiReadRomWithCpu + patchOffset + 0) = 0xE59F0000; // ldr r0,= entryAddress
*(u32*)((u8*)_cardiReadRomWithCpu + patchOffset + 4) = 0xE12FFF30; // blx r0
*(u32*)((u8*)_cardiReadRomWithCpu + patchOffset + 8) = entryAddress;
}
memcpy(patch1Address, SECTION_START(patch_cardireadromwithcpu), patch1Size);
memcpy(patch4Address, SECTION_START(fixcp15), patch4Size);
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include "patches/Patch.h"
/// @brief Arm9 patch to redirect card reads on SDK 2-4.
class CardiReadRomWithCpuPatch : public Patch
{
public:
bool FindPatchTarget(PatchContext& patchContext) override;
void ApplyPatch(PatchContext& patchContext) override;
private:
u32* _cardiReadRomWithCpu = nullptr;
u16 _thumb = false;
const u32* _foundPattern = nullptr;
void TryPattern(PatchContext& patchContext, const u32* pattern);
};

View File

@@ -0,0 +1,14 @@
#pragma once
#include "sections.h"
DEFINE_SECTION_SYMBOLS(patch_cardireadromwithcpu);
extern "C" void patch_cardireadromwithcpu_entry();
extern u16 patch_cardireadromwithcpu_return_offset;
extern u16 patch_cardireadromwithcpu_mov_src_to_r0;
extern u16 patch_cardireadromwithcpu_mov_dst_to_r1;
extern u16 patch_cardireadromwithcpu_mov_actual_dst_to_r3;
extern u16 patch_cardireadromwithcpu_mov_left_to_read_to_r2;
extern u32 __patch_cardireadromwithcpu_fix_cp15_asm_address;
extern u32 __patch_cardireadromwithcpu_rom_offset_to_sd_sector_asm_address;
extern u32 __patch_cardireadromwithcpu_sdread_asm_address;

View File

@@ -0,0 +1,74 @@
.cpu arm946e-s
.section "patch_cardireadromwithcpu", "ax"
.syntax unified
.thumb
.global patch_cardireadromwithcpu_entry
.type patch_cardireadromwithcpu_entry, %function
patch_cardireadromwithcpu_entry:
.global patch_cardireadromwithcpu_return_offset
patch_cardireadromwithcpu_return_offset:
movs r0, #0x38
add lr, r0
push {lr}
ldr r3, __patch_cardireadromwithcpu_fix_cp15_asm_address
blx r3
.global patch_cardireadromwithcpu_mov_src_to_r0
patch_cardireadromwithcpu_mov_src_to_r0:
nop
ldr r3, __patch_cardireadromwithcpu_rom_offset_to_sd_sector_asm_address
blx r3
movs r2, #1 // r2 = sector count
.global patch_cardireadromwithcpu_mov_dst_to_r1
patch_cardireadromwithcpu_mov_dst_to_r1:
movs r1, r5 // r1 = dst
.global patch_cardireadromwithcpu_mov_actual_dst_to_r3
patch_cardireadromwithcpu_mov_actual_dst_to_r3:
mov r3, r10
cmp r3, r1
bne do_read // if dst != actual dst the cache is in use
.global patch_cardireadromwithcpu_mov_left_to_read_to_r2
patch_cardireadromwithcpu_mov_left_to_read_to_r2:
mov r2, r8
lsrs r2, r2, #9 // sectors left to read
cmp r2, lr
ble 1f
mov r2, lr // clip to cluster boundary
1:
lsls r7, r2, #9
do_read:
ldr r3, __patch_cardireadromwithcpu_sdread_asm_address
blx r3
// arm
// r7 = number of bytes read
// r8 = number of bytes left to read
// r10 = actual dst
// thumb
// r7 = number of bytes read
// r5 = number of bytes left to read
// [sp + 0 + 4] = actual dst
pop {pc}
.balign 4
.global __patch_cardireadromwithcpu_fix_cp15_asm_address
__patch_cardireadromwithcpu_fix_cp15_asm_address:
.word 0
.global __patch_cardireadromwithcpu_rom_offset_to_sd_sector_asm_address
__patch_cardireadromwithcpu_rom_offset_to_sd_sector_asm_address:
.word 0
.global __patch_cardireadromwithcpu_sdread_asm_address
__patch_cardireadromwithcpu_sdread_asm_address:
.word 0
.pool
.end

View File

@@ -0,0 +1,57 @@
#pragma once
#include <memory>
#include "SdReadPatchCode.h"
#include "SdReadDmaPatchCode.h"
#include "SdWritePatchCode.h"
#include "../PatchHeap.h"
#include "../PatchCodeCollection.h"
/// @brief Abstract class for platform (flashcard or other sd access method) specific parts of the loader.
class LoaderPlatform
{
public:
/// @brief Creates the SD read patch code for the platform.
/// @param patchCodeCollection The patch code collection.
/// @param patchHeap The patch heap.
/// @return A unique pointer to the created SD read patch code.
virtual const SdReadPatchCode* CreateSdReadPatchCode(
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const = 0;
/// @brief Creates the SD read dma patch code for the platform.
/// @param patchCodeCollection The patch code collection.
/// @param patchHeap The patch heap.
/// @return A unique pointer to the created SD read dma patch code.
virtual const SdReadDmaPatchCode* CreateSdReadDmaPatchCode(
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap, const void* miiCardDmaCopy32Ptr) const { return nullptr; }
/// @brief Creates the SD write patch code for the platform.
/// @param patchCodeCollection The patch code collection.
/// @param patchHeap The patch heap.
/// @return A unique pointer to the created SD write patch code.
virtual const SdWritePatchCode* CreateSdWritePatchCode(
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const = 0;
/// @brief Creates the rom read patch code for the platform.
/// @param patchCodeCollection The patch code collection.
/// @param patchHeap The patch heap.
/// @return A unique pointer to the created rom read patch code.
virtual const SdReadPatchCode* CreateRomReadPatchCode(
PatchCodeCollection& patchCodeCollection, PatchHeap& patchHeap) const { return nullptr; }
/// @brief Checks if the platform supports rom reads directly.
/// @return True if the platform supports rom reads, or false otherwise.
virtual bool HasRomReads() const { return false; }
/// @brief Checks if the platform supports sd reads via dma.
/// @return True if the platform supports sd reads via dma, or false otherwise.
virtual bool HasDmaSdReads() const { return false; }
/// @brief Prepares the platform for running a rom.
/// @param romDirSector The directory sector of the rom file, or 0 if not applicable.
/// @param romDirSectorOffset The byte offset of the rom fat entry in the directory sector.
virtual void PrepareRomBoot(u32 romDirSector, u32 romDirSectorOffset) const { }
/// @brief For platforms that need it this function may be used to (re)initialize the sd card.
/// @return True if the sd card initialization was successful, or false otherwise.
virtual bool InitializeSdCard() { return true; }
};

View File

@@ -0,0 +1,47 @@
#include "common.h"
#include "patches/platform/LoaderPlatform.h"
#include "patches/platform/dspico/DSPicoLoaderPlatform.h"
#include "patches/platform/isnitro/IsNitroLoaderPlatform.h"
#include "patches/platform/m3ds/M3DSLoaderPlatform.h"
#include "patches/platform/r4/R4LoaderPlatform.h"
#include "patches/platform/ace3ds/Ace3DSLoaderPlatform.h"
#include "patches/platform/g003/G003LoaderPlatform.h"
#include "patches/platform/melonds/MelonDSLoaderPlatform.h"
#include "patches/platform/dstt/DSTTLoaderPlatform.h"
#include "patches/platform/ak2/AK2LoaderPlatform.h"
#include "patches/platform/akrpg/AKRPGLoaderPlatform.h"
#include "patches/platform/r4idsn/R4iDSNLoaderPlatform.h"
#include "patches/platform/supercard/SuperCardLoaderPlatform.h"
#include "LoaderPlatformFactory.h"
LoaderPlatform* LoaderPlatformFactory::CreateLoaderPlatform() const
{
#if defined(PICO_LOADER_TARGET_ISNITRO)
return new IsNitroLoaderPlatform();
#elif defined(PICO_LOADER_TARGET_DSPICO)
return new DSPicoLoaderPlatform();
#elif defined(PICO_LOADER_TARGET_M3DS)
return new M3DSLoaderPlatform();
#elif defined(PICO_LOADER_TARGET_R4)
return new R4LoaderPlatform();
#elif defined(PICO_LOADER_TARGET_ACE3DS)
return new Ace3DSLoaderPlatform();
#elif defined(PICO_LOADER_TARGET_G003)
return new G003LoaderPlatform();
#elif defined(PICO_LOADER_TARGET_MELONDS)
return new MelonDSLoaderPlatform();
#elif defined(PICO_LOADER_TARGET_DSTT)
return new DSTTLoaderPlatform();
#elif defined(PICO_LOADER_TARGET_AK2)
return new AK2LoaderPlatform();
#elif defined(PICO_LOADER_TARGET_AKRPG)
return new AKRPGLoaderPlatform();
#elif defined(PICO_LOADER_TARGET_R4iDSN)
return new R4iDSNLoaderPlatform();
#elif defined(PICO_LOADER_TARGET_SUPERCARD)
return new SuperCardLoaderPlatform();
#else
#error "No loader platform defined"
return nullptr;
#endif
}

View File

@@ -0,0 +1,9 @@
#pragma once
class LoaderPlatform;
class LoaderPlatformFactory
{
public:
LoaderPlatform* CreateLoaderPlatform() const;
};

View File

@@ -0,0 +1,28 @@
#pragma once
#include "../PatchCode.h"
/// @brief Abstract base class for patch code implementing dma SD reads
class SdReadDmaPatchCode : public PatchCode
{
protected:
SdReadDmaPatchCode(const void* code, u32 size, PatchHeap& patchHeap)
: PatchCode(code, size, patchHeap) { }
public:
/// @brief Pointer to a function for reading a single SD sector using the given dmaChannel.
/// In first sector of batch read, previousSrcSector will be 0.
/// On further reads previousSrcSector will contains the previous srcSector
/// which can be used to continue a sequential read.
typedef void (*SdReadDmaFunc)(u32 srcSector, u32 previousSrcSector, u32 dmaChannel, void* dst);
/// @brief Pointer to a function for finishing a batch of dma reads.
typedef void (*SdReadDmaFinishFunc)(void);
/// @brief Gets a pointer to the dma SD read function in the patch code.
/// @return The pointer to the dma SD read function.
virtual const SdReadDmaFunc GetSdReadDmaFunction() const = 0;
/// @brief Gets a pointer to the dma SD read finish function in the patch code.
/// @return The pointer to the dma SD read finish function.
virtual const SdReadDmaFinishFunc GetSdReadDmaFinishFunction() const = 0;
};

Some files were not shown because too many files have changed in this diff Show More