14 Commits

Author SHA1 Message Date
Gericom
5c8e98a03f Initial work on homebrew return to loader 2026-01-04 15:39:47 +01:00
Alex Abbatiello
f1e5f0cddc Fix typo in detectNocashPrintSupport and add support for melonDS (#85) 2026-01-02 22:01:43 +01:00
Gericom
26cec4c421 A few more renames 2026-01-02 16:17:31 +01:00
Gericom
e0e33032b9 Updated some comments 2026-01-02 16:13:56 +01:00
Gericom
9be5424f23 Changed PatchCode::GetAddressAtTarget from public to protected 2026-01-02 15:36:33 +01:00
Gericom
3e4725c99c Renamed methods in IReadSectorsDmaPatchCode 2026-01-02 15:32:48 +01:00
Gericom
dcc71ca151 Fixed casing in some includes 2026-01-02 15:27:18 +01:00
Gericom
2d4f9cb29f Fix pipeline to run on all branches 2026-01-02 15:22:14 +01:00
Gericom
231f14a783 Refactored platform code to use interfaces for patch code with a special feature and improved patch code file names 2026-01-02 15:18:45 +01:00
Mow
d6080984d1 Add overlay patch for hombrew AP in Rabbids Go Homeh (#82) 2025-12-31 07:51:40 +01:00
Mow
6fb34c75f8 Add overlay patch to fix race condition in Kirby Super Star Ultra (#81) 2025-12-30 22:23:04 +01:00
Edoardo Lolletti
992e2d1053 DATEL: Make read sector function vram safe (#80)
* DATEL: Make read sector function vram safe

The platform code was performing reads in single byte units, breaking in case the destination buffer was in vram.
This caused issues with some games (e.g. Castelvania Order of Ecclesia), and also broke soft resetting.

* Halve wait timeout on sector read since we read 2 bytes at the time
2025-12-30 20:10:02 +01:00
Max
842dd8e63a Update BlocksDS installation link in README (#79)
The /options/ path does not exist anymore
2025-12-30 08:01:19 +01:00
Alex Abbatiello
92a6c6ecdc Save list cleanup (#78) 2025-12-29 12:37:48 +00:00
207 changed files with 1754 additions and 1099 deletions

View File

@@ -2,7 +2,6 @@ name: Build Pico Loader
on:
push:
branches: ["develop"]
paths-ignore:
- 'README.md'
pull_request:

View File

@@ -45,7 +45,7 @@ Note that there are still SDK versions and variants for which Pico Loader does n
We recommend using WSL (Windows Subsystem for Linux), or MSYS2 to compile this repository.
The steps provided will assume you already have one of those environments set up.
1. Install [BlocksDS](https://blocksds.skylyrac.net/docs/setup/options/)
1. Install [BlocksDS](https://blocksds.skylyrac.net/docs/setup/)
2. Install [.NET 9.0](https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu-install?tabs=dotnet9&pivots=os-linux-ubuntu-2404) for your system (note: this link points to the instructions for Ubuntu, but links for most OS'es are available on the same page)
## Compiling

View File

@@ -2,6 +2,9 @@
#include "picoAgbAdapter.h"
#include "Environment.h"
#define NOCASH_ID 0x67246F6E
#define NOCASH_ID_MELONDS 0x6F6C656D
u32 Environment::_flags;
static bool detectIsNitroEmulator()
@@ -15,10 +18,10 @@ static bool detectIsNitroEmulator()
return true;
}
static bool detectNocashPrintSuppport()
static bool detectNocashPrintSupport()
{
u32 nocashIdentifier = *(vu32*)0x04FFFA00;
return nocashIdentifier == 0x67246F6E; //no$g
return nocashIdentifier == NOCASH_ID || nocashIdentifier == NOCASH_ID_MELONDS;
}
static bool detectPicoAgbAdapter()
@@ -55,7 +58,7 @@ void Environment::Initialize(bool dsiMode)
}
if (!(_flags & ENVIRONMENT_FLAGS_IS_NITRO_EMULATOR))
{
if (detectNocashPrintSuppport())
if (detectNocashPrintSupport())
_flags |= ENVIRONMENT_FLAGS_NOCASH_PRINT;
}
}
}

View File

@@ -95,3 +95,8 @@ bool dldi_patchTo(dldi_header_t* stub)
{
return sDldiDriver.PatchTo(stub);
}
void dldi_copyTo(void* target)
{
memcpy(target, sDldiBuffer, sizeof(sDldiBuffer));
}

View File

@@ -3,6 +3,7 @@
bool dldi_init();
bool dldi_patchTo(dldi_header_t* stub);
void dldi_copyTo(void* target);
#ifdef __cplusplus
extern "C" {

View File

@@ -10,5 +10,6 @@ extern u8 __bss_size[];
pload_header7_t gLoaderHeader
{
.entryPoint = (void*)&_start,
.apiVersion = PICO_LOADER_API_VERSION
.apiVersion = PICO_LOADER_API_VERSION,
.launcherPath = "/_picoboot.nds"
};

View File

@@ -344,6 +344,7 @@ void NdsLoader::Load(BootMode bootMode)
if (isHomebrew)
{
InsertArgv();
HandleHomebrewPatching();
}
HandleDldiPatching();
@@ -429,6 +430,22 @@ void NdsLoader::InsertArgv()
HOMEBREW_ARGV->length = argSize;
}
void NdsLoader::HandleHomebrewPatching()
{
sendToArm9(IPC_COMMAND_ARM9_APPLY_HOMEBREW_PATCHES);
sendToArm9(16 * 1024); // required dldi space
void* dldiSpace = (void*)receiveFromArm9();
char* launcherPath = (char*)receiveFromArm9();
if (dldiSpace != nullptr)
{
dldi_copyTo(dldiSpace);
}
if (launcherPath != nullptr)
{
strncpy(launcherPath, _launcherPath, 256);
}
}
void NdsLoader::ApplyArm7Patches()
{
sendToArm9(IPC_COMMAND_ARM9_APPLY_ARM7_PATCHES);

View File

@@ -26,6 +26,13 @@ public:
_savePath = savePath;
}
/// @brief Sets the \p launcherPath to use.
/// @param launcherPath The launcher path to use.
void SetLauncherPath(const TCHAR* launcherPath)
{
_launcherPath = launcherPath;
}
/// @brief Sets the argv arguments to pass to the rom.
/// @param arguments The argv arguments.
/// @param argumentsLength The length of the argv arguments.
@@ -41,8 +48,9 @@ public:
private:
FIL _romFile;
const TCHAR* _romPath;
const TCHAR* _savePath;
const TCHAR* _romPath = nullptr;
const TCHAR* _savePath = nullptr;
const TCHAR* _launcherPath = nullptr;
u32 _argumentsLength = 0;
const char* _arguments = nullptr;
nds_header_twl_t _romHeader;
@@ -71,6 +79,7 @@ private:
char driveLetter, const char* deviceName, const char* path, u8 flags, u8 accessRights);
void SetupDsiDeviceList();
void InsertArgv();
void HandleHomebrewPatching();
bool TrySetupDsiWareSave();
bool TryDecryptSecureArea();
ConsoleRegion GetRomRegion(u32 gameCode);

View File

@@ -1,5 +1,4 @@
#pragma once
#include "common.h"
#define DLDI_MAGIC 0xBF8DA5ED
#define DLDI_DRIVER_MAGIC_NONE 0x49444C44

View File

@@ -222,6 +222,7 @@ extern "C" void loaderMain()
sLoader.SetRomPath(gLoaderHeader.loadParams.romPath);
handleSavePath();
sLoader.SetArguments(gLoaderHeader.loadParams.arguments, gLoaderHeader.loadParams.argumentsLength);
sLoader.SetLauncherPath(gLoaderHeader.launcherPath);
sLoader.Load(BootMode::Normal);
}

View File

@@ -21,6 +21,8 @@
#include "patches/arm9/OverlayPatches/DSProtectPatches/DSProtectOverlayPatch.h"
#include "patches/arm9/OverlayPatches/DSProtectPatches/DSProtectPuyoPuyo7Patch.h"
#include "patches/arm9/OverlayPatches/PokemonIr/PokemonIrApPatch.h"
#include "patches/arm9/OverlayPatches/KirbySuperStarUltra/KirbySuperStarUltraPatch.h"
#include "patches/arm9/OverlayPatches/RabbidsGoHome/RabbidsGoHomePatch.h"
#include "patches/arm9/OverlayPatches/GoldenSunDarkDawn/GoldenSunDarkDawnOverlayHookPatch.h"
#include "SecureSysCallsUnusedSpaceLocator.h"
#include "fastSearch.h"
@@ -404,6 +406,22 @@ void Arm9Patcher::AddGameSpecificPatches(
patchCollection.AddPatch(new NintendoDSGuideNandSavePatch());
break;
}
// Rabbids Go Home
case GAMECODE("VRGE"):
case GAMECODE("VRGV"):
{
overlayHookPatch->AddOverlayPatch(new RabbidsGoHomePatch());
break;
}
// Kirby Super Star Ultra
case GAMECODE("YKWE"):
case GAMECODE("YKWJ"):
case GAMECODE("YKWK"):
case GAMECODE("YKWP"):
{
overlayHookPatch->AddOverlayPatch(new KirbySuperStarUltraPatch());
break;
}
}
}

View File

@@ -27,6 +27,8 @@
#include "errorDisplay/ErrorDisplay.h"
#include "LoaderInfo.h"
#include "jumpToArm9EntryPoint.h"
#include "patches/homebrew/BootstubPatchCode.h"
#include "HomebrewBootstub.h"
#define HANDSHAKE_PART0 0xA
#define HANDSHAKE_PART1 0xB
@@ -192,8 +194,8 @@ static void handleGetSdFunctionsCommand()
{
auto sdReadPatchCode = sLoaderPlatform->CreateSdReadPatchCode(patchCodeCollection, patchHeap);
auto sdWritePatchCode = sLoaderPlatform->CreateSdWritePatchCode(patchCodeCollection, patchHeap);
*(vu32*)0x037F8000 = (u32)sdReadPatchCode->GetSdReadFunction();
*(vu32*)0x037F8004 = (u32)sdWritePatchCode->GetSdWriteFunction();
*(vu32*)0x037F8000 = (u32)sdReadPatchCode->GetReadSectorsFunction();
*(vu32*)0x037F8004 = (u32)sdWritePatchCode->GetWriteSectorFunction();
patchCodeCollection.CopyAllToTarget();
}
dc_flushAll();
@@ -231,6 +233,37 @@ static void handleBootCommand()
bootArm9();
}
static void handleApplyHomebrewPatches(u32 dldiRequiredSpace)
{
// Insert bootstub
PatchHeap patchHeap;
PatchCodeCollection patchCodeCollection;
void* patchSpace = (void*)&HOMEBREW_BOOTSTUB[1];
auto romHeader = (const nds_header_ntr_t*)TWL_SHARED_MEMORY->ntrSharedMem.romHeader;
if (!romHeader->IsTwlRom())
{
patchSpace = (u8*)patchSpace - 0x2F00000 + 0x2300000;
}
patchHeap.AddFreeSpace(patchSpace, 32 * 1024 - sizeof(homebrew_bootstub_t));
void* dldi = patchHeap.Alloc(dldiRequiredSpace);
char* launcherPath = (char*)patchHeap.Alloc(256);
auto bootstubPatchCode = patchCodeCollection.AddUniquePatchCode<BootstubPatchCode>(
patchHeap, dldi, launcherPath, &sLoaderInfo,
sLoaderPlatform->CreateSdReadPatchCode(patchCodeCollection, patchHeap));
HOMEBREW_BOOTSTUB->bootSig = HOMEBREW_BOOTSTUB_BOOTSIG;
HOMEBREW_BOOTSTUB->arm9Reboot = (void*)bootstubPatchCode->GetArm9RebootFunction();
HOMEBREW_BOOTSTUB->arm7Reboot = (void*)bootstubPatchCode->GetArm7RebootFunction();
HOMEBREW_BOOTSTUB->bootSize = 32 * 1024 - sizeof(homebrew_bootstub_t);
patchCodeCollection.CopyAllToTarget();
ipc_sendWordDirect((u32)dldi);
ipc_sendWordDirect((u32)launcherPath);
}
static void handleArm7Command(u32 command)
{
switch (command)
@@ -295,6 +328,12 @@ static void handleArm7Command(u32 command)
handleBootCommand();
break;
}
case IPC_COMMAND_ARM9_APPLY_HOMEBREW_PATCHES:
{
u32 dldiRequiredSpace = receiveFromArm7();
handleApplyHomebrewPatches(dldiRequiredSpace);
break;
}
}
}

View File

@@ -0,0 +1,13 @@
#pragma once
/// @brief Interface for sector remapping patch code.
class ISectorRemapPatchCode
{
protected:
ISectorRemapPatchCode() { }
public:
/// @brief Gets a pointer to the sector remap function in the patch code.
/// @return The pointer to the sector remap function.
virtual const void* GetRemapFunction() const = 0;
};

View File

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

View File

@@ -20,15 +20,6 @@ public:
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
{
@@ -44,4 +35,13 @@ protected:
/// @brief The target address for the code block in the patch heap.
void* const _targetAddress;
/// @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);
}
};

View File

@@ -1,6 +1,7 @@
#pragma once
#include "sections.h"
#include "SectorRemapPatchCode.h"
#include "PatchCode.h"
#include "ISectorRemapPatchCode.h"
#include "fileInfo.h"
DEFINE_SECTION_SYMBOLS(saveoffsettosdsector);
@@ -9,11 +10,11 @@ extern "C" u32 save_offset_to_sd_sector_asm(u32 saveOffset);
extern u32 saveoffsettosdsector_fatDataPtr;
class SaveOffsetToSdSectorPatchCode : public SectorRemapPatchCode
class SaveOffsetToSdSectorPatchCode : public PatchCode, public ISectorRemapPatchCode
{
public:
SaveOffsetToSdSectorPatchCode(PatchHeap& patchHeap, const save_file_info_t* fatDataPtr)
: SectorRemapPatchCode(SECTION_START(saveoffsettosdsector), SECTION_SIZE(saveoffsettosdsector), patchHeap)
: PatchCode(SECTION_START(saveoffsettosdsector), SECTION_SIZE(saveoffsettosdsector), patchHeap)
{
saveoffsettosdsector_fatDataPtr = (u32)fatDataPtr;
}

View File

@@ -1,11 +0,0 @@
#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

@@ -1,8 +1,8 @@
#pragma once
#include "sections.h"
#include "../PatchCode.h"
#include "../SectorRemapPatchCode.h"
#include "../platform/SdReadPatchCode.h"
#include "patches/PatchCode.h"
#include "patches/ISectorRemapPatchCode.h"
#include "patches/platform/IReadSectorsPatchCode.h"
DEFINE_SECTION_SYMBOLS(readsave);
@@ -15,12 +15,12 @@ extern u32 readsave_sdread_asm_address;
class ReadSavePatchCode : public PatchCode
{
public:
ReadSavePatchCode(PatchHeap& patchHeap, const SectorRemapPatchCode* sectorRemapPatchCode,
const SdReadPatchCode* sdReadPatchCode, void* tmpBuffer)
ReadSavePatchCode(PatchHeap& patchHeap, const ISectorRemapPatchCode* sectorRemapPatchCode,
const IReadSectorsPatchCode* readSectorsPatchCode, 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_sdread_asm_address = (u32)readSectorsPatchCode->GetReadSectorsFunction();
readsave_tmpBufferPtr = (u32)tmpBuffer;
}

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