Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f4641ca
Add MemCardPro GameID support (using Swiss implementation)
jllottes Jul 8, 2025
d39bf64
Added "Off"/"Full ID"/"Short ID" options.
jllottes Jul 9, 2025
b3bbe92
Update README.md
Arrghus8 Jul 9, 2025
0e03757
Added "Off"/"Full ID"/"Short ID" options.
jllottes Jul 9, 2025
c8f1816
Replace spaces with tabs. Add Swiss copyright information to `MemCard…
jllottes Jul 10, 2025
fd79549
Replace spaces with tabs. Add Swiss copyright information to `MemCard…
jllottes Jul 10, 2025
2751853
Replace spaces with tabs. Add Swiss copyright information to `MemCard…
jllottes Jul 10, 2025
10539bb
Replace spaces with tabs. Add Swiss copyright information to `MemCard…
jllottes Jul 10, 2025
badf8c8
Send GameID information within `BootDevolution`, `BootNintendont`, an…
jllottes Jul 10, 2025
2aeb648
Send GameID information within `BootDevolution`, `BootNintendont`, an…
jllottes Jul 10, 2025
394fdea
Cleaning up, send GameID with (c)MIOS, fix `BootDIOSMIOS` GameID
jllottes Jul 10, 2025
2b4a0b8
Cleaning up, send GameID with (c)MIOS, fix `BootDIOSMIOS` GameID
jllottes Jul 10, 2025
2f8efe8
Cleaning up, send GameID with (c)MIOS, fix `BootDIOSMIOS` GameID
jllottes Jul 10, 2025
a82bc3c
Move "MemCardPro GameID" setting above "GameCube Mode" setting.
jllottes Jul 10, 2025
d6f41b6
Move "MemCardPro GameID" setting above "GameCube Mode" setting.
jllottes Jul 10, 2025
830f876
Move "MemCardPro GameID" setting above "GameCube Mode" setting.
jllottes Jul 10, 2025
468bf0c
Change setting name "MemCardPro GameID" -> "MemCard PRO Game ID"
jllottes Jul 10, 2025
17eca89
Change setting name "MemCard PRO Game ID" -> "MemCard PRO GameID"
jllottes Jul 10, 2025
7076bdc
Change setting name "MemCard PRO GameID" -> "MemCard PRO"
jllottes Jul 10, 2025
b1dfe4c
Move "MemCard PRO" setting below "GameCube Mode" in `LoaderSettings.cpp`
jllottes Jul 11, 2025
ed0959a
Fix incompability with MemCard PRO GC firmware 3.x.x
jllottes May 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 5 additions & 24 deletions README.md
Comment thread
Arrghus8 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,26 +1,7 @@
<p align="center"><a href="https://github.com/wiidev/usbloadergx/" title="USB Loader GX"><img src="data/web/logo.png"></a></p>
<p align="center">
<a href="https://github.com/wiidev/usbloadergx/releases" title="Releases"><img src="https://img.shields.io/github/v/release/wiidev/usbloadergx?logo=github"></a>
<a href="https://github.com/wiidev/usbloadergx/actions" title="Actions"><img src="https://img.shields.io/github/actions/workflow/status/wiidev/usbloadergx/main.yml?branch=enhanced&logo=github"></a>
</p>

## Description
USB Loader GX allows you to play Wii and GameCube games from a USB storage device or an SD card, launch other homebrew apps, create backups, use cheats in games, and a whole lot more.

## Installation
1. Extract the apps folder to the root of your SD card and replace any existing files.
2. Install the [latest d2x cIOS](https://github.com/wiidev/d2x-cios/releases).
3. Optional: Update wiitdb.xml by selecting the update option within the loaders settings menu.
4. Optional: Install the loaders [forwarder channel](https://raw.githubusercontent.com/wiidev/usbloadergx/updates/USBLoaderGX_forwarder%5BUNEO%5D.wad), then go into `Loader Settings` and set `Return To` to `UNEO`.

## d2x cIOS
1. Use the correct cIOS package for your console — e.g., `d2x-v11-beta3` for the Wii and `d2x-v11-beta3-vWii` for the Wii U.
2. When using the d2x cIOS installer, set the cIOS to the version that you downloaded — e.g., `d2x-v11-beta3`.
3. Install the cIOS into each slot with the following settings.
Added support for sending GameID information to MemCard PRO GC. Based on the [implementation in Swiss](https://github.com/FIX94/Nintendont/commit/a186ea2715cdb877b3cc5b3d4269a18e2ace8d48).

````
Slot 248 base 38
Slot 249 base 56
Slot 250 base 57
Slot 251 base 58
````
There are three settings:
* `Off`: No GameID information is sent to the memory slots.
* `Full ID`: The full GameID is sent, including Company and Revision data (e.g. RSBE0100). This matches the Swiss naming convention.
* `Short ID`: A shortened ID is sent, omitting the Company and Revision data (e.g. RSBE). This matches the Nintendont Emulated Memory Card naming convention.
124 changes: 124 additions & 0 deletions source/GameCube/MemCardPro.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
From Swiss, mcp.c
*/

#include <stdbool.h>
#include <string.h>
#include <ogc/card.h>
#include <ogc/exi.h>
#include "usbloader/disc.h"
#include "MemCardPro.h"

static const char digits[] = "0123456789ABCDEF";

s32 MCP_ProbeEx(s32 chan)
{
return CARD_ProbeEx(chan, NULL, NULL);
}

s32 MCP_GetDeviceID(s32 chan, u32 *id)
{
bool err = false;
u8 cmd[2];

if (!EXI_Lock(chan, EXI_DEVICE_0, NULL)) return MCP_RESULT_BUSY;
if (!EXI_Select(chan, EXI_DEVICE_0, EXI_SPEED16MHZ)) {
EXI_Unlock(chan);
return MCP_RESULT_NOCARD;
}

cmd[0] = 0x8B;
cmd[1] = 0x00;

err |= !EXI_Imm(chan, cmd, 2, EXI_WRITE, NULL);
err |= !EXI_Sync(chan);
err |= !EXI_Imm(chan, id, 4, EXI_READ, NULL);
err |= !EXI_Sync(chan);
err |= !EXI_Deselect(chan);
EXI_Unlock(chan);

if (err)
return MCP_RESULT_NOCARD;
else if (*id >> 16 != 0x3842)
return MCP_RESULT_WRONGDEVICE;
else
return MCP_RESULT_READY;
}

s32 MCP_SetDiskID(s32 chan, const dvddiskid *diskID, bool shortID)
{
bool err = false;
u8 cmd[12];

if (!EXI_Lock(chan, EXI_DEVICE_0, NULL)) return MCP_RESULT_BUSY;
if (!EXI_Select(chan, EXI_DEVICE_0, EXI_SPEED16MHZ)) {
EXI_Unlock(chan);
return MCP_RESULT_NOCARD;
}

memset(cmd, 0, sizeof(cmd));
cmd[0] = 0x8B;
cmd[1] = 0x11;

if (diskID) {
memcpy(&cmd[2], diskID->gamename, 4);
if (!shortID)
{
memcpy(&cmd[6], diskID->company, 2);
cmd[8] = digits[diskID->disknum / 16];
cmd[9] = digits[diskID->disknum % 16];
cmd[10] = digits[diskID->gamever / 16];
cmd[11] = digits[diskID->gamever % 16];
};
}

err |= !EXI_ImmEx(chan, cmd, sizeof(cmd), EXI_WRITE);
err |= !EXI_Deselect(chan);
EXI_Unlock(chan);

return err ? MCP_RESULT_NOCARD : MCP_RESULT_READY;
}

s32 MCP_SetDiskInfo(s32 chan, const char diskInfo[64])
{
bool err = false;
u8 cmd[2];

if (!EXI_Lock(chan, EXI_DEVICE_0, NULL)) return MCP_RESULT_BUSY;
if (!EXI_Select(chan, EXI_DEVICE_0, EXI_SPEED16MHZ)) {
EXI_Unlock(chan);
return MCP_RESULT_NOCARD;
}

cmd[0] = 0x8B;
cmd[1] = 0x13;

err |= !EXI_Imm(chan, cmd, 2, EXI_WRITE, NULL);
err |= !EXI_Sync(chan);
err |= !EXI_ImmEx(chan, (char *)diskInfo, 64, EXI_WRITE);
err |= !EXI_Deselect(chan);
EXI_Unlock(chan);

return err ? MCP_RESULT_NOCARD : MCP_RESULT_READY;
}

/*
From Swiss, gameid.c
*/

void gameID_early_set(const discHdr *header, bool shortID)
{
for (s32 chan = 0; chan < 2; chan++) {
u32 id;
s32 ret;

while ((ret = MCP_ProbeEx(chan)) == MCP_RESULT_BUSY);
if (ret < 0) continue;
while ((ret = MCP_GetDeviceID(chan, &id)) == MCP_RESULT_BUSY);
if (ret < 0) continue;
while ((ret = MCP_SetDiskID(chan, (dvddiskid *)header, shortID)) == MCP_RESULT_BUSY);
if (ret < 0) continue;
while ((ret = MCP_SetDiskInfo(chan, header->title)) == MCP_RESULT_BUSY);
if (ret < 0) continue;
}
}
Comment thread
Arrghus8 marked this conversation as resolved.
Outdated
32 changes: 32 additions & 0 deletions source/GameCube/MemCardPro.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
From Swiss, mcp.h
*/

#ifndef __MCP_H__
#define __MCP_H__

#include <gctypes.h>
#include <ogc/dvd.h>
#include "usbloader/disc.h"

#define MCP_RESULT_READY 0
#define MCP_RESULT_BUSY -1
#define MCP_RESULT_WRONGDEVICE -2
#define MCP_RESULT_NOCARD -3
#define MCP_RESULT_FATAL_ERROR -128

#ifdef __cplusplus
extern "C" {
#endif

s32 MCP_ProbeEx(s32 chan);
s32 MCP_GetDeviceID(s32 chan, u32 *id);
s32 MCP_SetDiskID(s32 chan, const dvddiskid *diskID, bool shortID);
s32 MCP_SetDiskInfo(s32 chan, const char diskInfo[64]);
void gameID_early_set(const discHdr *header, bool shortID);

#ifdef __cplusplus
}
#endif

#endif
Comment thread
Arrghus8 marked this conversation as resolved.
Outdated
7 changes: 7 additions & 0 deletions source/settings/CGameSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ bool CGameSettings::Save()
fprintf(f, "wpadMotor:%d; ", GameList[i].wpadMotor);
fprintf(f, "wpadSpeaker:%d; ", GameList[i].wpadSpeaker);
fprintf(f, "GameCubeMode:%d; ", GameList[i].GameCubeMode);
fprintf(f, "MemCardProGameID:%d; ", GameList[i].MemCardProGameID);
Comment thread
Arrghus8 marked this conversation as resolved.
Outdated
fprintf(f, "DMLVideo:%d; ", GameList[i].DMLVideo);
fprintf(f, "DMLProgPatch:%d; ", GameList[i].DMLProgPatch);
fprintf(f, "DMLNMM:%d; ", GameList[i].DMLNMM);
Expand Down Expand Up @@ -402,6 +403,11 @@ bool CGameSettings::SetSetting(GameCFG & game, const char *name, const char *val
game.GameCubeMode = atoi(value);
return true;
}
else if(strcmp(name, "MemCardProGameID") == 0)
{
game.MemCardProGameID = atoi(value);
return true;
}
else if(strcmp(name, "DMLVideo") == 0)
{
game.DMLVideo = atoi(value);
Expand Down Expand Up @@ -736,6 +742,7 @@ void CGameSettings::SetDefault(GameCFG &game)
game.wpadMotor = INHERIT;
game.wpadSpeaker = INHERIT;
game.GameCubeMode = INHERIT;
game.MemCardProGameID = INHERIT;
Comment thread
Arrghus8 marked this conversation as resolved.
Outdated
game.DMLVideo = INHERIT;
game.DMLProgPatch = INHERIT;
game.DMLNMM = INHERIT;
Expand Down
2 changes: 2 additions & 0 deletions source/settings/CGameSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ typedef struct _GameCFG
short wpadMotor;
short wpadSpeaker;
short GameCubeMode;
short MemCardProGameID;
Comment thread
Arrghus8 marked this conversation as resolved.
Outdated
short DMLVideo;
short DMLProgPatch;
short DMLNMM;
Expand Down Expand Up @@ -110,6 +111,7 @@ typedef struct _GameCFG
this->wpadMotor = game.wpadMotor;
this->wpadSpeaker = game.wpadSpeaker;
this->GameCubeMode = game.GameCubeMode;
this->MemCardProGameID = game.MemCardProGameID;
this->DMLVideo = game.DMLVideo;
this->DMLProgPatch = game.DMLProgPatch;
this->DMLNMM = game.DMLNMM;
Expand Down
7 changes: 7 additions & 0 deletions source/settings/CSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ void CSettings::SetDefault()
GCBannerScale = 1.5f;
GameCubeMode = GC_MODE_NINTENDONT;
GameCubeSource = AUTO;
MemCardProGameID = OFF;
MultiDiscPrompt = OFF;
DMLVideo = DML_VIDEO_AUTO;
DMLProgPatch = OFF;
Expand Down Expand Up @@ -486,6 +487,7 @@ bool CSettings::Save()
fprintf(file, "GameCubeSDPath = %s\n", GameCubeSDPath);
fprintf(file, "GameCubeMode = %d\n", GameCubeMode);
fprintf(file, "GameCubeSource = %d\n", GameCubeSource);
fprintf(file, "MemCardProGameID = %d\n", MemCardProGameID);
fprintf(file, "MultiDiscPrompt = %d\n", MultiDiscPrompt);
fprintf(file, "DMLVideo = %d\n", DMLVideo);
fprintf(file, "DMLProgPatch = %d\n", DMLProgPatch);
Expand Down Expand Up @@ -1051,6 +1053,11 @@ bool CSettings::SetSetting(char *name, char *value)
GameCubeSource = atoi(value);
return true;
}
else if (strcmp(name, "MemCardProGameID") == 0)
{
MemCardProGameID = atoi(value);
return true;
}
else if (strcmp(name, "MultiDiscPrompt") == 0)
{
MultiDiscPrompt = atoi(value);
Expand Down
1 change: 1 addition & 0 deletions source/settings/CSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ class CSettings
float GCBannerScale;
short GameCubeMode;
short GameCubeSource;
short MemCardProGameID;
short MultiDiscPrompt;
short DMLVideo;
short DMLProgPatch;
Expand Down
9 changes: 9 additions & 0 deletions source/settings/SettingsEnums.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,15 @@ enum

};

enum
{
MEMCARDPRO_GAMEID_OFF,
MEMCARDPRO_GAMEID_ON_FULL,
MEMCARDPRO_GAMEID_ON_SHORT,
MEMCARDPRO_GAMEID_MAX_CHOICE

};

enum
{
DEVO_MC_OFF,
Expand Down
22 changes: 21 additions & 1 deletion source/settings/menus/GCGameLoadSM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ static const char * GCMode[] =
trNOOP( "Nintendont" )
};

static const char * MCPGameID[] =
{
trNOOP( "Off"),
trNOOP( "Full ID" ),
trNOOP( "Short ID" )
};

static const char * DMLVideoText[] =
{
trNOOP( "Auto" ),
Expand Down Expand Up @@ -184,6 +191,7 @@ void GCGameLoadSM::SetOptionNames()
Options->SetName(Idx++, "%s", tr( "Game Language" ));
Options->SetName(Idx++, "%s", tr( "Parental Control" ));
Options->SetName(Idx++, "%s", tr( "GameCube Mode" ));
Options->SetName(Idx++, "%s", tr( "MemCardPro GameID" ));
if(currentGCmode == GC_MODE_MIOS &&IosLoader::GetMIOSInfo() > DEFAULT_MIOS)
{
Options->SetName(Idx++, "%s", tr( "--== DIOS MIOS (Lite)" ));
Expand Down Expand Up @@ -274,6 +282,12 @@ void GCGameLoadSM::SetOptionValues()
Options->SetValue(Idx++, tr("Use global"));
else
Options->SetValue(Idx++, "%s", tr(GCMode[GameConfig.GameCubeMode]));

//! Settings: MemCardPro GameID
if(GameConfig.MemCardProGameID == INHERIT)
Options->SetValue(Idx++, tr("Use global"));
else
Options->SetValue(Idx++, "%s", tr(MCPGameID[GameConfig.MemCardProGameID]));

if(currentGCmode == GC_MODE_MIOS && IosLoader::GetMIOSInfo() > DEFAULT_MIOS)
{
Expand Down Expand Up @@ -663,7 +677,13 @@ int GCGameLoadSM::GetMenuInternal()
SetOptionNames();
SetOptionValues();
}


//! Settings: MemCardPro GameID
else if (ret == ++Idx)
{
if (++GameConfig.MemCardProGameID >= MEMCARDPRO_GAMEID_MAX_CHOICE) GameConfig.MemCardProGameID = INHERIT;
}

//! Settings: GameCube TITLE : DIOS MIOS (Lite) + Nintendont
else if (currentGCmode == GC_MODE_MIOS && IosLoader::GetMIOSInfo() > DEFAULT_MIOS && ret == ++Idx)
{
Expand Down
17 changes: 17 additions & 0 deletions source/settings/menus/LoaderSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ static const char * GCMode[] =
trNOOP( "Nintendont" )
};

static const char * MCPGameID[] =
{
trNOOP( "Off"),
trNOOP( "Full ID" ),
trNOOP( "Short ID" )
};

static const char * GCSourceText[][3] =
{
{ trNOOP( "Main Path" ), "", "" },
Expand Down Expand Up @@ -322,6 +329,7 @@ void LoaderSettings::SetOptionNames()
Options->SetName(Idx++, "%s", tr( "=== GameCube Settings" ));
Options->SetName(Idx++, "%s", tr( "GameCube Source" ));
Options->SetName(Idx++, "%s", tr( "GameCube Mode" ));
Options->SetName(Idx++, "%s", tr( "MemCardPro GameID" ));
Options->SetName(Idx++, "%s", tr( "Progressive Patch" ));
Options->SetName(Idx++, "%s", tr( "--== DM(L) + Nintendont" ));
Options->SetName(Idx++, "%s", tr( "Video Mode" ));
Expand Down Expand Up @@ -499,6 +507,9 @@ void LoaderSettings::SetOptionValues()

//! Settings: GameCube Mode
Options->SetValue(Idx++, "%s", tr(GCMode[Settings.GameCubeMode]));

//! Settings: MemCardPro GameID
Options->SetValue(Idx++, "%s", tr(MCPGameID[Settings.MemCardProGameID]));

//! Settings: DML + NIN + Devo Progressive Patch
Options->SetValue(Idx++, "%s", tr(OnOffText[Settings.DMLProgPatch]));
Expand Down Expand Up @@ -921,6 +932,12 @@ int LoaderSettings::GetMenuInternal()
if (++Settings.GameCubeMode >= CG_MODE_MAX_CHOICE) Settings.GameCubeMode = 0;
}

//! Settings: MemCardPro GameID
else if (ret == ++Idx)
{
if (++Settings.MemCardProGameID >= MEMCARDPRO_GAMEID_MAX_CHOICE) Settings.MemCardProGameID = 0;
}

//! Settings: DML + NIN + Devo Progressive Patch
else if (ret == ++Idx)
{
Expand Down
Loading