diff --git a/source/GameCube/MemCardPro.cpp b/source/GameCube/MemCardPro.cpp new file mode 100644 index 00000000..e3d40f5f --- /dev/null +++ b/source/GameCube/MemCardPro.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2022, Extrems + * + * This file is part of Swiss. + * + * Swiss is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Swiss is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * with Swiss. If not, see . + */ + +/* + * Lightly modified from Swiss, mcp.c + */ + +#include +#include +#include +#include +#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; +} + +/* + * Lightly modified 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; + } +} diff --git a/source/GameCube/MemCardPro.h b/source/GameCube/MemCardPro.h new file mode 100644 index 00000000..9d57e646 --- /dev/null +++ b/source/GameCube/MemCardPro.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022, Extrems + * + * This file is part of Swiss. + * + * Swiss is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Swiss is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * with Swiss. If not, see . + */ + + /* + * Lightly modified from Swiss, mcp.h + */ + +#ifndef __MCP_H__ +#define __MCP_H__ + +#include +#include +#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 diff --git a/source/settings/CGameSettings.cpp b/source/settings/CGameSettings.cpp index 342037a6..78af15cd 100644 --- a/source/settings/CGameSettings.cpp +++ b/source/settings/CGameSettings.cpp @@ -210,6 +210,7 @@ bool CGameSettings::Save() fprintf(f, "WiirdDebugger:%d; ", GameList[i].WiirdDebugger); fprintf(f, "wpadMotor:%d; ", GameList[i].wpadMotor); fprintf(f, "wpadSpeaker:%d; ", GameList[i].wpadSpeaker); + fprintf(f, "MemCardProGameID:%d; ", GameList[i].MemCardProGameID); fprintf(f, "GameCubeMode:%d; ", GameList[i].GameCubeMode); fprintf(f, "DMLVideo:%d; ", GameList[i].DMLVideo); fprintf(f, "DMLProgPatch:%d; ", GameList[i].DMLProgPatch); @@ -397,6 +398,11 @@ bool CGameSettings::SetSetting(GameCFG & game, const char *name, const char *val game.Locked = atoi(value); return true; } + else if(strcmp(name, "MemCardProGameID") == 0) + { + game.MemCardProGameID = atoi(value); + return true; + } else if(strcmp(name, "GameCubeMode") == 0) { game.GameCubeMode = atoi(value); @@ -735,6 +741,7 @@ void CGameSettings::SetDefault(GameCFG &game) game.WiirdDebugger = INHERIT; game.wpadMotor = INHERIT; game.wpadSpeaker = INHERIT; + game.MemCardProGameID = INHERIT; game.GameCubeMode = INHERIT; game.DMLVideo = INHERIT; game.DMLProgPatch = INHERIT; diff --git a/source/settings/CGameSettings.h b/source/settings/CGameSettings.h index 87bf68cd..9f4b5cb2 100644 --- a/source/settings/CGameSettings.h +++ b/source/settings/CGameSettings.h @@ -36,6 +36,7 @@ typedef struct _GameCFG short WiirdDebugger; short wpadMotor; short wpadSpeaker; + short MemCardProGameID; short GameCubeMode; short DMLVideo; short DMLProgPatch; @@ -109,6 +110,7 @@ typedef struct _GameCFG this->WiirdDebugger = game.WiirdDebugger; this->wpadMotor = game.wpadMotor; this->wpadSpeaker = game.wpadSpeaker; + this->MemCardProGameID = game.MemCardProGameID; this->GameCubeMode = game.GameCubeMode; this->DMLVideo = game.DMLVideo; this->DMLProgPatch = game.DMLProgPatch; diff --git a/source/settings/CSettings.cpp b/source/settings/CSettings.cpp index f1fffef7..68889625 100644 --- a/source/settings/CSettings.cpp +++ b/source/settings/CSettings.cpp @@ -201,6 +201,7 @@ void CSettings::SetDefault() BannerProjectionWidth = (Settings.widescreen ? (Settings.PAL50 ? 616 : 620.0f) : 608.0f); BannerProjectionHeight = (Settings.PAL50 ? 448.0f : (NTSC ? 470.0f : 464.0f)); GCBannerScale = 1.5f; + MemCardProGameID = OFF; GameCubeMode = GC_MODE_NINTENDONT; GameCubeSource = AUTO; MultiDiscPrompt = OFF; @@ -484,6 +485,7 @@ bool CSettings::Save() fprintf(file, "GCBannerScale = %g\n", GCBannerScale); fprintf(file, "GameCubePath = %s\n", GameCubePath); fprintf(file, "GameCubeSDPath = %s\n", GameCubeSDPath); + fprintf(file, "MemCardProGameID = %d\n", MemCardProGameID); fprintf(file, "GameCubeMode = %d\n", GameCubeMode); fprintf(file, "GameCubeSource = %d\n", GameCubeSource); fprintf(file, "MultiDiscPrompt = %d\n", MultiDiscPrompt); @@ -1041,6 +1043,11 @@ bool CSettings::SetSetting(char *name, char *value) ParentalBlocks = strtoul(value, 0, 16); return true; } + else if (strcmp(name, "MemCardProGameID") == 0) + { + MemCardProGameID = atoi(value); + return true; + } else if (strcmp(name, "GameCubeMode") == 0) { GameCubeMode = atoi(value); diff --git a/source/settings/CSettings.h b/source/settings/CSettings.h index 17004b1c..efe9cb45 100644 --- a/source/settings/CSettings.h +++ b/source/settings/CSettings.h @@ -207,6 +207,7 @@ class CSettings float BannerProjectionWidth; float BannerProjectionHeight; float GCBannerScale; + short MemCardProGameID; short GameCubeMode; short GameCubeSource; short MultiDiscPrompt; diff --git a/source/settings/SettingsEnums.h b/source/settings/SettingsEnums.h index 1854858c..fca24810 100644 --- a/source/settings/SettingsEnums.h +++ b/source/settings/SettingsEnums.h @@ -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, diff --git a/source/settings/menus/GCGameLoadSM.cpp b/source/settings/menus/GCGameLoadSM.cpp index c68eb905..61c044f8 100644 --- a/source/settings/menus/GCGameLoadSM.cpp +++ b/source/settings/menus/GCGameLoadSM.cpp @@ -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" ), @@ -183,6 +190,7 @@ void GCGameLoadSM::SetOptionNames() Options->SetName(Idx++, "%s", tr( "Favorite Level" )); Options->SetName(Idx++, "%s", tr( "Game Language" )); Options->SetName(Idx++, "%s", tr( "Parental Control" )); + Options->SetName(Idx++, "%s", tr( "MemCard PRO" )); Options->SetName(Idx++, "%s", tr( "GameCube Mode" )); if(currentGCmode == GC_MODE_MIOS &&IosLoader::GetMIOSInfo() > DEFAULT_MIOS) { @@ -269,6 +277,12 @@ void GCGameLoadSM::SetOptionValues() //! Settings: Parental Control Options->SetValue(Idx++, "%s", tr(ParentalText[GameConfig.parentalcontrol])); + //! Settings: MemCard PRO + if(GameConfig.MemCardProGameID == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr(MCPGameID[GameConfig.MemCardProGameID])); + //! Settings: GameCube Mode if(GameConfig.GameCubeMode == INHERIT) Options->SetValue(Idx++, tr("Use global")); @@ -654,6 +668,12 @@ int GCGameLoadSM::GetMenuInternal() if (++GameConfig.parentalcontrol >= 5) GameConfig.parentalcontrol = 0; } + //! Settings: MemCard PRO + else if (ret == ++Idx) + { + if (++GameConfig.MemCardProGameID >= MEMCARDPRO_GAMEID_MAX_CHOICE) GameConfig.MemCardProGameID = INHERIT; + } + //! Settings: GameCube Mode else if (ret == ++Idx) { diff --git a/source/settings/menus/LoaderSettings.cpp b/source/settings/menus/LoaderSettings.cpp index 0e3db825..facaa103 100644 --- a/source/settings/menus/LoaderSettings.cpp +++ b/source/settings/menus/LoaderSettings.cpp @@ -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" ), "", "" }, @@ -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( "MemCard PRO" )); Options->SetName(Idx++, "%s", tr( "Progressive Patch" )); Options->SetName(Idx++, "%s", tr( "--== DM(L) + Nintendont" )); Options->SetName(Idx++, "%s", tr( "Video Mode" )); @@ -500,6 +508,9 @@ void LoaderSettings::SetOptionValues() //! Settings: GameCube Mode Options->SetValue(Idx++, "%s", tr(GCMode[Settings.GameCubeMode])); + //! Settings: MemCard PRO + Options->SetValue(Idx++, "%s", tr(MCPGameID[Settings.MemCardProGameID])); + //! Settings: DML + NIN + Devo Progressive Patch Options->SetValue(Idx++, "%s", tr(OnOffText[Settings.DMLProgPatch])); @@ -921,6 +932,12 @@ int LoaderSettings::GetMenuInternal() if (++Settings.GameCubeMode >= CG_MODE_MAX_CHOICE) Settings.GameCubeMode = 0; } + //! Settings: MemCard PRO + else if (ret == ++Idx) + { + if (++Settings.MemCardProGameID >= MEMCARDPRO_GAMEID_MAX_CHOICE) Settings.MemCardProGameID = 0; + } + //! Settings: DML + NIN + Devo Progressive Patch else if (ret == ++Idx) { diff --git a/source/usbloader/GameBooter.cpp b/source/usbloader/GameBooter.cpp index 98d966fb..710518d8 100644 --- a/source/usbloader/GameBooter.cpp +++ b/source/usbloader/GameBooter.cpp @@ -64,6 +64,7 @@ #include "wad/nandtitle.h" #include "settings/GameTitles.h" #include "SystemMenu/SystemMenuResources.h" +#include "GameCube/MemCardPro.h" /* GCC 11 false positives */ #if __GNUC__ > 10 @@ -117,6 +118,14 @@ int GameBooter::BootGCMode(struct discHdr *gameHdr) // MIOS or Wiigator cMIOS if (gameHdr->type == TYPE_GAME_GC_DISC) { + // Send GameID + int MemCardProGameID = game_cfg->MemCardProGameID == INHERIT ? Settings.MemCardProGameID : game_cfg->MemCardProGameID; + if (MemCardProGameID == MEMCARDPRO_GAMEID_ON_FULL || MemCardProGameID == MEMCARDPRO_GAMEID_ON_SHORT) + { + bool shortID = (MemCardProGameID == MEMCARDPRO_GAMEID_ON_SHORT); + gameID_early_set(gameHdr, shortID); + } + ExitApp(); gprintf("\nLoading BC for GameCube"); WII_Initialize(); @@ -945,6 +954,14 @@ int GameBooter::BootDIOSMIOS(struct discHdr *gameHdr) snprintf(gamePath + strlen(gamePath), sizeof(gamePath) - strlen(gamePath), "/disc2.iso"); } + // Send GameID + int MemCardProGameID = game_cfg->MemCardProGameID == INHERIT ? Settings.MemCardProGameID : game_cfg->MemCardProGameID; + if (MemCardProGameID == MEMCARDPRO_GAMEID_ON_FULL || MemCardProGameID == MEMCARDPRO_GAMEID_ON_SHORT) + { + bool shortID = (MemCardProGameID == MEMCARDPRO_GAMEID_ON_SHORT); + gameID_early_set(gameHdr, shortID); + } + ExitApp(); // Game ID @@ -1288,6 +1305,14 @@ int GameBooter::BootDevolution(struct discHdr *gameHdr) // flush disc ID and Devolution config out to memory DCFlushRange(lowmem, 64); + + // Send GameID + int MemCardProGameID = game_cfg->MemCardProGameID == INHERIT ? Settings.MemCardProGameID : game_cfg->MemCardProGameID; + if (MemCardProGameID == MEMCARDPRO_GAMEID_ON_FULL || MemCardProGameID == MEMCARDPRO_GAMEID_ON_SHORT) + { + bool shortID = (MemCardProGameID == MEMCARDPRO_GAMEID_ON_SHORT); + gameID_early_set(gameHdr, shortID); + } ExitApp(); IosLoader::ReloadIosKeepingRights(58); // reload IOS 58 with AHBPROT rights @@ -1958,6 +1983,14 @@ int GameBooter::BootNintendont(struct discHdr *gameHdr) } } + // Send GameID + int MemCardProGameID = game_cfg->MemCardProGameID == INHERIT ? Settings.MemCardProGameID : game_cfg->MemCardProGameID; + if (MemCardProGameID == MEMCARDPRO_GAMEID_ON_FULL || MemCardProGameID == MEMCARDPRO_GAMEID_ON_SHORT) + { + bool shortID = (MemCardProGameID == MEMCARDPRO_GAMEID_ON_SHORT); + gameID_early_set(gameHdr, shortID); + } + if (NINArgsboot) { // initialize homebrew and arguments