mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-25 20:56:53 +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">
|
<app version="1">
|
||||||
<name> USB Loader GX</name>
|
<name> USB Loader GX</name>
|
||||||
<coder>USB Loader GX Team</coder>
|
<coder>USB Loader GX Team</coder>
|
||||||
<version>3.0 r1205</version>
|
<version>3.0 r1206</version>
|
||||||
<release_date>20121209184858</release_date>
|
<release_date>20121209202238</release_date>
|
||||||
<!-- // remove this line to enable arguments
|
<!-- // remove this line to enable arguments
|
||||||
<arguments>
|
<arguments>
|
||||||
<arg>--ios=250</arg>
|
<arg>--ios=250</arg>
|
||||||
|
@ -39,7 +39,7 @@ enum DMLConfig
|
|||||||
DML_CFG_FORCE_WIDE = (1<<9), // DM v2.1+, Config v02
|
DML_CFG_FORCE_WIDE = (1<<9), // DM v2.1+, Config v02
|
||||||
DML_CFG_BOOT_DISC = (1<<10),
|
DML_CFG_BOOT_DISC = (1<<10),
|
||||||
// DML_CFG_BOOT_DOL = (1<<11), // unused since DML v1.0, removed in v2.1
|
// 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_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
|
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);
|
Wbfs_Fat::CleanTitleCharacters(gametitle);
|
||||||
|
|
||||||
char gamepath[512];
|
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);
|
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");
|
FILE *f = fopen(gamepath, "wb");
|
||||||
if(!f)
|
if(!f)
|
||||||
|
@ -61,6 +61,7 @@ void GCGames::LoadGameList(const string &path, vector<struct discHdr> &headerLis
|
|||||||
struct discHdr tmpHdr;
|
struct discHdr tmpHdr;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
u8 id[8];
|
u8 id[8];
|
||||||
|
u8 disc_number = 0;
|
||||||
char fpath[1024];
|
char fpath[1024];
|
||||||
char fname_title[64];
|
char fname_title[64];
|
||||||
DIR *dir_iter;
|
DIR *dir_iter;
|
||||||
@ -123,6 +124,22 @@ void GCGames::LoadGameList(const string &path, vector<struct discHdr> &headerLis
|
|||||||
break;
|
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)
|
if(!found)
|
||||||
{
|
{
|
||||||
snprintf(fpath, sizeof(fpath), "%s%s/sys/boot.bin", path.c_str(), dirname);
|
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);
|
strncpy(tmpHdr.title, title, sizeof(tmpHdr.title)-1);
|
||||||
tmpHdr.magic = GCGames::MAGIC;
|
tmpHdr.magic = GCGames::MAGIC;
|
||||||
tmpHdr.type = extracted ? TYPE_GAME_GC_EXTRACTED : TYPE_GAME_GC_IMG;
|
tmpHdr.type = extracted ? TYPE_GAME_GC_EXTRACTED : TYPE_GAME_GC_IMG;
|
||||||
|
tmpHdr.disc_no = disc_number;
|
||||||
headerList.push_back(tmpHdr);
|
headerList.push_back(tmpHdr);
|
||||||
pathList.push_back(gamePath);
|
pathList.push_back(gamePath);
|
||||||
continue;
|
continue;
|
||||||
@ -362,12 +380,31 @@ float GCGames::GetGameSize(const char *gameID)
|
|||||||
return ((float) st.st_size / GB_SIZE);
|
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++)
|
for(u32 n = 0; n < HeaderList.size(); n++)
|
||||||
{
|
{
|
||||||
if(memcmp(HeaderList[n].id, gameID, 6) == 0)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CopyUSB2SD(const struct discHdr *header);
|
bool CopyUSB2SD(const struct discHdr *header);
|
||||||
bool IsInstalled(const char *gameID) const;
|
bool IsInstalled(const char *gameID, u8 disc_number) const;
|
||||||
private:
|
private:
|
||||||
|
|
||||||
static GCGames *instance;
|
static GCGames *instance;
|
||||||
|
@ -70,7 +70,7 @@ int MenuGCInstall()
|
|||||||
gcDumper.SetCompressed(Settings.GCInstallCompressed);
|
gcDumper.SetCompressed(Settings.GCInstallCompressed);
|
||||||
gcDumper.SetCompressed(Settings.GCInstallAligned);
|
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;
|
int destination = 1;
|
||||||
if(strcmp(Settings.GameCubePath, Settings.GameCubeSDPath) != 0)
|
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"));
|
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)
|
for(u32 i = 0; i < installGames.size(); ++i)
|
||||||
{
|
{
|
||||||
//! check if the game is already installed on SD/USB
|
//! 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"));
|
WindowPrompt(tr("Game is already installed:"), gcDumper.GetDiscHeaders().at(installGames[i]).title, tr("OK"));
|
||||||
if(i+1 < installGames.size()) {
|
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
|
// game is not yet installed so let's install it
|
||||||
int ret = gcDumper.InstallGame(InstallPath, installGames[i]);
|
int ret = gcDumper.InstallGame(InstallPath, installGames[i]);
|
||||||
if(ret >= 0) {
|
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.
|
// 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];
|
char path[255], destPath[255];
|
||||||
int res = -1;
|
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, '/');
|
const char *gcPath = strchr(RealPath, '/');
|
||||||
if(!gcPath) gcPath = "";
|
if(!gcPath) gcPath = "";
|
||||||
@ -498,6 +520,13 @@ int GameBooter::BootDIOSMIOS(struct discHdr *gameHdr)
|
|||||||
char gamePath[255];
|
char gamePath[255];
|
||||||
snprintf(gamePath, sizeof(gamePath), "%s", gcPath);
|
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();
|
ExitApp();
|
||||||
|
|
||||||
// Game ID
|
// Game ID
|
||||||
@ -532,14 +561,14 @@ int GameBooter::BootDIOSMIOS(struct discHdr *gameHdr)
|
|||||||
// setup cheat and path
|
// setup cheat and path
|
||||||
if(ocarinaChoice)
|
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)
|
if(strcmp(DeviceHandler::GetDevicePrefix(RealPath), DeviceHandler::GetDevicePrefix(Settings.Cheatcodespath)) == 0)
|
||||||
{
|
{
|
||||||
const char *CheatPath = strchr(Settings.Cheatcodespath, '/');
|
const char *CheatPath = strchr(Settings.Cheatcodespath, '/');
|
||||||
if(!CheatPath) CheatPath = "";
|
if(!CheatPath) CheatPath = "";
|
||||||
snprintf(dml_config->CheatPath, sizeof(dml_config->CheatPath), "%s%.6s.gct", CheatPath, (char *)gameHdr->id);
|
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");
|
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;
|
dml_config->Config |= DML_CFG_FORCE_WIDE;
|
||||||
if(dmlScreenshotChoice)
|
if(dmlScreenshotChoice)
|
||||||
dml_config->Config |= DML_CFG_SCREENSHOT;
|
dml_config->Config |= DML_CFG_SCREENSHOT;
|
||||||
|
if(bootDisc2)
|
||||||
|
dml_config->Config |= DML_CFG_BOOT_DISC2;
|
||||||
|
|
||||||
|
|
||||||
// Setup Video Mode
|
// Setup Video Mode
|
||||||
@ -694,9 +725,18 @@ int GameBooter::BootDevolution(struct discHdr *gameHdr)
|
|||||||
const char *RealPath = GCGames::Instance()->GetPath((const char *) gameHdr->id);
|
const char *RealPath = GCGames::Instance()->GetPath((const char *) gameHdr->id);
|
||||||
|
|
||||||
char disc1[100];
|
char disc1[100];
|
||||||
//char disc2[100];
|
char disc2[100];
|
||||||
|
bool multiDisc = false;
|
||||||
char DEVO_memCard[100];
|
char DEVO_memCard[100];
|
||||||
snprintf(disc1, sizeof(disc1), "%s", RealPath);
|
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
|
snprintf(DEVO_memCard, sizeof(DEVO_memCard), "%s", RealPath); // Set memory card folder to Disc1 folder
|
||||||
char *ptr = strrchr(DEVO_memCard, '/');
|
char *ptr = strrchr(DEVO_memCard, '/');
|
||||||
if(ptr) *ptr = 0;
|
if(ptr) *ptr = 0;
|
||||||
@ -711,8 +751,9 @@ int GameBooter::BootDevolution(struct discHdr *gameHdr)
|
|||||||
stat(disc1, &st1);
|
stat(disc1, &st1);
|
||||||
|
|
||||||
// Get the starting cluster for the ISO file 2
|
// Get the starting cluster for the ISO file 2
|
||||||
//struct stat st2;
|
struct stat st2;
|
||||||
//stat(disc2, &st2);
|
if(multiDisc)
|
||||||
|
stat(disc2, &st2);
|
||||||
|
|
||||||
// setup Devolution
|
// setup Devolution
|
||||||
memset(devo_config, 0, sizeof(*devo_config));
|
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.
|
// 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->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->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
|
// Devolution configs
|
||||||
// use wifi logging if USB gecko is not found in slot B
|
// use wifi logging if USB gecko is not found in slot B
|
||||||
|
@ -14,8 +14,11 @@ extern "C"
|
|||||||
/* Game ID */
|
/* Game ID */
|
||||||
u8 id[6];
|
u8 id[6];
|
||||||
|
|
||||||
|
/* Game Disc number */
|
||||||
|
u8 disc_no;
|
||||||
|
|
||||||
/* Game version */
|
/* Game version */
|
||||||
u16 version;
|
u8 disc_ver;
|
||||||
|
|
||||||
/* Audio streaming */
|
/* Audio streaming */
|
||||||
u8 streaming;
|
u8 streaming;
|
||||||
|
Loading…
Reference in New Issue
Block a user