mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-12-22 09:51:55 +01:00
* Added possibility to install multi-Discs GameCube games
* Added auto-detection and loading multi-Disc games with both Devolution and DIOS MIOS (Lite). The Disc 2 must be named disc2.iso and placed in the same folder than game.iso * DML: Prevent temporary ocarina .gct file copy if the game is launched from disc Note: If you use "GameCube compress" option (Extracted game format): - If one of the disc is already extracted, it will not allows you to dump another disc (as it's now using the same folder, it would overwrite files from the other disc) - If Disc 1 doesn't exist, it will tell you that disc 2 needs to be in ISO format, but will asks if you really want to install the game in extracted format.
This commit is contained in:
parent
420c290d7b
commit
77f7daf9dc
@ -2,8 +2,8 @@
|
||||
<app version="1">
|
||||
<name> USB Loader GX</name>
|
||||
<coder>USB Loader GX Team</coder>
|
||||
<version>3.0 r1205</version>
|
||||
<release_date>20121209184858</release_date>
|
||||
<version>3.0 r1206</version>
|
||||
<release_date>20121209202238</release_date>
|
||||
<!-- // remove this line to enable arguments
|
||||
<arguments>
|
||||
<arg>--ios=250</arg>
|
||||
|
@ -39,7 +39,7 @@ enum DMLConfig
|
||||
DML_CFG_FORCE_WIDE = (1<<9), // DM v2.1+, Config v02
|
||||
DML_CFG_BOOT_DISC = (1<<10),
|
||||
// DML_CFG_BOOT_DOL = (1<<11), // unused since DML v1.0, removed in v2.1
|
||||
DML_CFG_BOOT_DISC2 = (1<<11), // DM v2.1+, Config v02
|
||||
DML_CFG_BOOT_DISC2 = (1<<11), // added in DM v2.1+, Config v02, used in v2.6+
|
||||
DML_CFG_NODISC2 = (1<<12), // added back in DM v2.2 update2 (r20) and removed in v2.3
|
||||
DML_CFG_SCREENSHOT = (1<<13), // added in v2.5
|
||||
};
|
||||
|
@ -263,11 +263,13 @@ s32 GCDumper::InstallGame(const char *installpath, u32 game)
|
||||
Wbfs_Fat::CleanTitleCharacters(gametitle);
|
||||
|
||||
char gamepath[512];
|
||||
snprintf(gamepath, sizeof(gamepath), "%s%s [%.6s]%s/", installpath, gametitle, gcheader.id, Disc ? "2" : "");
|
||||
// snprintf(gamepath, sizeof(gamepath), "%s%s [%.6s]%s/", installpath, gametitle, gcheader.id, Disc ? "2" : ""); // Disc2 currently needs to be on the same folder.
|
||||
snprintf(gamepath, sizeof(gamepath), "%s%s [%.6s]/", installpath, gametitle, gcheader.id);
|
||||
|
||||
CreateSubfolder(gamepath);
|
||||
|
||||
snprintf(gamepath, sizeof(gamepath), "%s%s [%.6s]%s/game.iso", installpath, gametitle, gcheader.id, Disc ? "2" : "");
|
||||
// snprintf(gamepath, sizeof(gamepath), "%s%s [%.6s]%s/game.iso", installpath, gametitle, gcheader.id, Disc ? "2" : ""); // Disc2 currently needs to be on the same folder.
|
||||
snprintf(gamepath, sizeof(gamepath), "%s%s [%.6s]/%s.iso", installpath, gametitle, gcheader.id, Disc ? "disc2" : "game");
|
||||
|
||||
FILE *f = fopen(gamepath, "wb");
|
||||
if(!f)
|
||||
|
@ -61,6 +61,7 @@ void GCGames::LoadGameList(const string &path, vector<struct discHdr> &headerLis
|
||||
struct discHdr tmpHdr;
|
||||
struct stat st;
|
||||
u8 id[8];
|
||||
u8 disc_number = 0;
|
||||
char fpath[1024];
|
||||
char fname_title[64];
|
||||
DIR *dir_iter;
|
||||
@ -123,6 +124,22 @@ void GCGames::LoadGameList(const string &path, vector<struct discHdr> &headerLis
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if only disc2.iso is present
|
||||
if(!found)
|
||||
{
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
char name[50];
|
||||
snprintf(name, sizeof(name), "disc2.%s", (i % 2) == 0 ? "gcm" : "iso"); // allow gcm, though DM(L) require "disc2.iso" filename
|
||||
snprintf(fpath, sizeof(fpath), "%s%s/%s", path.c_str(), dirname, name);
|
||||
if((found = (stat(fpath, &st) == 0)) == true)
|
||||
{
|
||||
disc_number = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!found)
|
||||
{
|
||||
snprintf(fpath, sizeof(fpath), "%s%s/sys/boot.bin", path.c_str(), dirname);
|
||||
@ -166,6 +183,7 @@ void GCGames::LoadGameList(const string &path, vector<struct discHdr> &headerLis
|
||||
strncpy(tmpHdr.title, title, sizeof(tmpHdr.title)-1);
|
||||
tmpHdr.magic = GCGames::MAGIC;
|
||||
tmpHdr.type = extracted ? TYPE_GAME_GC_EXTRACTED : TYPE_GAME_GC_IMG;
|
||||
tmpHdr.disc_no = disc_number;
|
||||
headerList.push_back(tmpHdr);
|
||||
pathList.push_back(gamePath);
|
||||
continue;
|
||||
@ -362,12 +380,31 @@ float GCGames::GetGameSize(const char *gameID)
|
||||
return ((float) st.st_size / GB_SIZE);
|
||||
}
|
||||
|
||||
bool GCGames::IsInstalled(const char *gameID) const
|
||||
bool GCGames::IsInstalled(const char *gameID, u8 disc_number) const
|
||||
{
|
||||
for(u32 n = 0; n < HeaderList.size(); n++)
|
||||
{
|
||||
if(memcmp(HeaderList[n].id, gameID, 6) == 0)
|
||||
return true;
|
||||
{
|
||||
if(HeaderList[n].type == TYPE_GAME_GC_EXTRACTED)
|
||||
return true; // Multi-disc games in extracted form are currently unsupported by DML, no need to check further.
|
||||
|
||||
if(HeaderList[n].disc_no == disc_number) // Disc number already in headerList. If Disc2 is loaded in headerList, then Disc1 is not installed yet
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if(disc_number == 1) // Check if the second Game Disc exists in the same folder than Disc1.
|
||||
{
|
||||
char filepath[512];
|
||||
snprintf(filepath, sizeof(filepath), "%s", GetPath(gameID));
|
||||
char *pathPtr = strrchr(filepath, '/');
|
||||
if(pathPtr) *pathPtr = 0;
|
||||
snprintf(filepath, sizeof(filepath), "%s/disc2.iso", filepath);
|
||||
|
||||
if(CheckFile(filepath))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ public:
|
||||
}
|
||||
|
||||
bool CopyUSB2SD(const struct discHdr *header);
|
||||
bool IsInstalled(const char *gameID) const;
|
||||
bool IsInstalled(const char *gameID, u8 disc_number) const;
|
||||
private:
|
||||
|
||||
static GCGames *instance;
|
||||
|
@ -70,7 +70,7 @@ int MenuGCInstall()
|
||||
gcDumper.SetCompressed(Settings.GCInstallCompressed);
|
||||
gcDumper.SetCompressed(Settings.GCInstallAligned);
|
||||
|
||||
//! If a different main path then the SD path is selected ask where to install
|
||||
//! If a different main path than the SD path is selected ask where to install
|
||||
int destination = 1;
|
||||
if(strcmp(Settings.GameCubePath, Settings.GameCubeSDPath) != 0)
|
||||
destination = WindowPrompt(tr("Where should the game be installed to?"), 0, tr("Main Path"), tr("SD Path"), tr("Cancel"));
|
||||
@ -95,7 +95,7 @@ int MenuGCInstall()
|
||||
for(u32 i = 0; i < installGames.size(); ++i)
|
||||
{
|
||||
//! check if the game is already installed on SD/USB
|
||||
if(GCGames::Instance()->IsInstalled((char *)gcDumper.GetDiscHeaders().at(installGames[i]).id))
|
||||
if(GCGames::Instance()->IsInstalled((char *)gcDumper.GetDiscHeaders().at(installGames[i]).id, gcDumper.GetDiscHeaders().at(installGames[i]).disc_no))
|
||||
{
|
||||
WindowPrompt(tr("Game is already installed:"), gcDumper.GetDiscHeaders().at(installGames[i]).title, tr("OK"));
|
||||
if(i+1 < installGames.size()) {
|
||||
@ -108,6 +108,23 @@ int MenuGCInstall()
|
||||
}
|
||||
}
|
||||
|
||||
// Check Disc2 installation format (DML 2.6+ auto-swap feature doesn't work with extracted game format)
|
||||
if(Settings.GCInstallCompressed && gcDumper.GetDiscHeaders().at(installGames[i]).disc_no == 1)
|
||||
{
|
||||
int choice = WindowPrompt(tr(gcDumper.GetDiscHeaders().at(installGames[i]).title), tr("Disc2 needs to be installed in uncompressed format to work with DM(L) v2.6+, are you sure you want to install in compressed format?"), tr("Yes"), tr("Cancel"));
|
||||
if(choice == 0)
|
||||
{
|
||||
if(i+1 < installGames.size()) {
|
||||
continue;
|
||||
}
|
||||
else if(i == 0)
|
||||
{
|
||||
result = MENU_DISCLIST;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// game is not yet installed so let's install it
|
||||
int ret = gcDumper.InstallGame(InstallPath, installGames[i]);
|
||||
if(ret >= 0) {
|
||||
|
@ -474,7 +474,7 @@ int GameBooter::BootDIOSMIOS(struct discHdr *gameHdr)
|
||||
}
|
||||
|
||||
// Check Ocarina and cheat file location. the .gct file need to be located on the same partition than the game.
|
||||
if(ocarinaChoice && strcmp(DeviceHandler::GetDevicePrefix(RealPath), DeviceHandler::GetDevicePrefix(Settings.Cheatcodespath)) != 0)
|
||||
if(gameHdr->type != TYPE_GAME_GC_DISC && ocarinaChoice && strcmp(DeviceHandler::GetDevicePrefix(RealPath), DeviceHandler::GetDevicePrefix(Settings.Cheatcodespath)) != 0)
|
||||
{
|
||||
char path[255], destPath[255];
|
||||
int res = -1;
|
||||
@ -491,6 +491,28 @@ int GameBooter::BootDIOSMIOS(struct discHdr *gameHdr)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if game has multi Discs
|
||||
bool bootDisc2 = false;
|
||||
if(gameHdr->type != TYPE_GAME_GC_DISC && gameHdr->disc_no == 0 && currentMIOS != QUADFORCE)
|
||||
{
|
||||
if(IosLoader::GetDMLVersion() >= DML_VERSION_DM_2_6_0)
|
||||
{
|
||||
char disc2Path[255];
|
||||
snprintf(disc2Path, sizeof(disc2Path), "%s", RealPath);
|
||||
char *pathPtr = strrchr(disc2Path, '/');
|
||||
if(pathPtr) *pathPtr = 0;
|
||||
snprintf(disc2Path, sizeof(disc2Path), "%s/disc2.iso", disc2Path);
|
||||
if(CheckFile(disc2Path))
|
||||
{
|
||||
int choice = WindowPrompt(gameHdr->title, tr("This game has multiple discs. Please select the disc to launch."), tr("Disc 1"), tr("Disc 2"), tr("Cancel"));
|
||||
if(choice == 0)
|
||||
return 0;
|
||||
|
||||
if(choice == 2)
|
||||
bootDisc2 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *gcPath = strchr(RealPath, '/');
|
||||
if(!gcPath) gcPath = "";
|
||||
@ -498,6 +520,13 @@ int GameBooter::BootDIOSMIOS(struct discHdr *gameHdr)
|
||||
char gamePath[255];
|
||||
snprintf(gamePath, sizeof(gamePath), "%s", gcPath);
|
||||
|
||||
if(bootDisc2)
|
||||
{
|
||||
char *pathPtr = strrchr(gamePath, '/');
|
||||
if(pathPtr) *pathPtr = 0;
|
||||
snprintf(gamePath, sizeof(gamePath), "%s/disc2.iso", gamePath);
|
||||
}
|
||||
|
||||
ExitApp();
|
||||
|
||||
// Game ID
|
||||
@ -532,14 +561,14 @@ int GameBooter::BootDIOSMIOS(struct discHdr *gameHdr)
|
||||
// setup cheat and path
|
||||
if(ocarinaChoice)
|
||||
{
|
||||
// Check is the .gct folder is on the same partition than the game, if not load the temporary .gct file.
|
||||
// Check if the .gct folder is on the same partition than the game, if not load the temporary .gct file.
|
||||
if(strcmp(DeviceHandler::GetDevicePrefix(RealPath), DeviceHandler::GetDevicePrefix(Settings.Cheatcodespath)) == 0)
|
||||
{
|
||||
const char *CheatPath = strchr(Settings.Cheatcodespath, '/');
|
||||
if(!CheatPath) CheatPath = "";
|
||||
snprintf(dml_config->CheatPath, sizeof(dml_config->CheatPath), "%s%.6s.gct", CheatPath, (char *)gameHdr->id);
|
||||
}
|
||||
else
|
||||
else if(gameHdr->type != TYPE_GAME_GC_DISC)
|
||||
{
|
||||
snprintf(dml_config->CheatPath, sizeof(dml_config->CheatPath), "DMLTemp.gct");
|
||||
}
|
||||
@ -561,6 +590,8 @@ int GameBooter::BootDIOSMIOS(struct discHdr *gameHdr)
|
||||
dml_config->Config |= DML_CFG_FORCE_WIDE;
|
||||
if(dmlScreenshotChoice)
|
||||
dml_config->Config |= DML_CFG_SCREENSHOT;
|
||||
if(bootDisc2)
|
||||
dml_config->Config |= DML_CFG_BOOT_DISC2;
|
||||
|
||||
|
||||
// Setup Video Mode
|
||||
@ -694,9 +725,18 @@ int GameBooter::BootDevolution(struct discHdr *gameHdr)
|
||||
const char *RealPath = GCGames::Instance()->GetPath((const char *) gameHdr->id);
|
||||
|
||||
char disc1[100];
|
||||
//char disc2[100];
|
||||
char disc2[100];
|
||||
bool multiDisc = false;
|
||||
char DEVO_memCard[100];
|
||||
snprintf(disc1, sizeof(disc1), "%s", RealPath);
|
||||
|
||||
snprintf(disc2, sizeof(disc2), "%s", RealPath);
|
||||
char *pathPtr = strrchr(disc2, '/');
|
||||
if(pathPtr) *pathPtr = 0;
|
||||
snprintf(disc2, sizeof(disc2), "%s/disc2.iso", disc2);
|
||||
if(CheckFile(disc2))
|
||||
multiDisc = true;
|
||||
|
||||
snprintf(DEVO_memCard, sizeof(DEVO_memCard), "%s", RealPath); // Set memory card folder to Disc1 folder
|
||||
char *ptr = strrchr(DEVO_memCard, '/');
|
||||
if(ptr) *ptr = 0;
|
||||
@ -711,8 +751,9 @@ int GameBooter::BootDevolution(struct discHdr *gameHdr)
|
||||
stat(disc1, &st1);
|
||||
|
||||
// Get the starting cluster for the ISO file 2
|
||||
//struct stat st2;
|
||||
//stat(disc2, &st2);
|
||||
struct stat st2;
|
||||
if(multiDisc)
|
||||
stat(disc2, &st2);
|
||||
|
||||
// setup Devolution
|
||||
memset(devo_config, 0, sizeof(*devo_config));
|
||||
@ -722,7 +763,8 @@ int GameBooter::BootDevolution(struct discHdr *gameHdr)
|
||||
// Only last two letters are returned by DevkitPro, so we set them manually to Devolution config.
|
||||
devo_config->device_signature = st1.st_dev == 'SD' ? 'SD' : 'SB'; // Set device type.
|
||||
devo_config->disc1_cluster = st1.st_ino; // set starting cluster for first disc ISO file
|
||||
//devo_config->disc2_cluster = st2.st_ino; // set starting cluster for second disc ISO file
|
||||
if(multiDisc)
|
||||
devo_config->disc2_cluster = st2.st_ino; // set starting cluster for second disc ISO file
|
||||
|
||||
// Devolution configs
|
||||
// use wifi logging if USB gecko is not found in slot B
|
||||
|
@ -14,8 +14,11 @@ extern "C"
|
||||
/* Game ID */
|
||||
u8 id[6];
|
||||
|
||||
/* Game Disc number */
|
||||
u8 disc_no;
|
||||
|
||||
/* Game version */
|
||||
u16 version;
|
||||
u8 disc_ver;
|
||||
|
||||
/* Audio streaming */
|
||||
u8 streaming;
|
||||
|
Loading…
Reference in New Issue
Block a user