mirror of
https://github.com/project-slippi/Nintendont.git
synced 2025-10-06 00:22:40 +02:00
-fixed potential issue in arstartdma (issue #595) -properly fixed mario party 4 crash (issue #437)
1601 lines
46 KiB
C
1601 lines
46 KiB
C
/*
|
|
|
|
Nintendont (Loader) - Playing Gamecubes in Wii mode on a Wii U
|
|
|
|
Copyright (C) 2013 crediar
|
|
|
|
This program 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 version 2.
|
|
|
|
This program 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
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
#include <gccore.h>
|
|
#include <sys/param.h>
|
|
#include <ogc/lwp_watchdog.h>
|
|
#include <ogc/lwp_threads.h>
|
|
#include <wiiuse/wpad.h>
|
|
#include <wiidrc/wiidrc.h>
|
|
#include <wupc/wupc.h>
|
|
#include <di/di.h>
|
|
#include <unistd.h>
|
|
#include <locale.h>
|
|
|
|
#include "exi.h"
|
|
#include "dip.h"
|
|
#include "global.h"
|
|
#include "font.h"
|
|
#include "FPad.h"
|
|
#include "menu.h"
|
|
#include "MemCard.h"
|
|
#include "Patches.h"
|
|
#include "kernel_zip.h"
|
|
#include "kernelboot_bin.h"
|
|
#include "multidol_ldr_bin.h"
|
|
#include "stub_bin.h"
|
|
#include "titles.h"
|
|
#include "ipl.h"
|
|
#include "HID.h"
|
|
#include "TRI.h"
|
|
#include "Config.h"
|
|
#include "wdvd.h"
|
|
|
|
#include "ff_utf8.h"
|
|
#include "diskio.h"
|
|
// from diskio.c
|
|
extern DISC_INTERFACE *driver[_VOLUMES];
|
|
|
|
extern void __exception_setreload(int t);
|
|
extern void __SYS_ReadROM(void *buf,u32 len,u32 offset);
|
|
extern u32 __SYS_GetRTC(u32 *gctime);
|
|
|
|
extern syssram* __SYS_LockSram();
|
|
extern u32 __SYS_UnlockSram(u32 write);
|
|
extern u32 __SYS_SyncSram(void);
|
|
|
|
#define STATUS ((void*)0x90004100)
|
|
#define STATUS_LOADING (*(volatile unsigned int*)(0x90004100))
|
|
#define STATUS_SECTOR (*(volatile unsigned int*)(0x90004100 + 8))
|
|
#define STATUS_DRIVE (*(float*)(0x9000410C))
|
|
#define STATUS_GB_MB (*(volatile unsigned int*)(0x90004100 + 16))
|
|
#define STATUS_ERROR (*(volatile unsigned int*)(0x90004100 + 20))
|
|
|
|
#define HW_DIFLAGS 0xD800180
|
|
#define MEM_PROT 0xD8B420A
|
|
|
|
#define IOCTL_ExecSuspendScheduler 1
|
|
|
|
static GXRModeObj *vmode = NULL;
|
|
|
|
static unsigned char ESBootPatch[] =
|
|
{
|
|
0x48, 0x03, 0x49, 0x04, 0x47, 0x78, 0x46, 0xC0, 0xE6, 0x00, 0x08, 0x70, 0xE1, 0x2F, 0xFF, 0x1E,
|
|
0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x25,
|
|
};
|
|
/*static const unsigned char AHBAccessPattern[] =
|
|
{
|
|
0x68, 0x5B, 0x22, 0xEC, 0x00, 0x52, 0x18, 0x9B, 0x68, 0x1B, 0x46, 0x98, 0x07, 0xDB,
|
|
};
|
|
static const unsigned char AHBAccessPatch[] =
|
|
{
|
|
0x68, 0x5B, 0x22, 0xEC, 0x00, 0x52, 0x18, 0x9B, 0x23, 0x01, 0x46, 0x98, 0x07, 0xDB,
|
|
};*/
|
|
static const unsigned char FSAccessPattern[] =
|
|
{
|
|
0x9B, 0x05, 0x40, 0x03, 0x99, 0x05, 0x42, 0x8B,
|
|
};
|
|
static const unsigned char FSAccessPatch[] =
|
|
{
|
|
0x9B, 0x05, 0x40, 0x03, 0x1C, 0x0B, 0x42, 0x8B,
|
|
};
|
|
|
|
// Forbid the use of MEM2 through malloc
|
|
u32 MALLOC_MEM2 = 0;
|
|
|
|
s32 __IOS_LoadStartupIOS(void)
|
|
{
|
|
int res;
|
|
|
|
res = __ES_Init();
|
|
if(res < 0)
|
|
return res;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Storage devices. (defined in global.c)
|
|
// 0 == SD, 1 == USB
|
|
extern FATFS *devices[2];
|
|
|
|
vu32 KernelLoaded = 0;
|
|
u32 entrypoint = 0;
|
|
char launch_dir[MAXPATHLEN] = {0};
|
|
extern void __exception_closeall();
|
|
static u8 loader_stub[0x1800]; //save internally to prevent overwriting
|
|
static ioctlv IOCTL_Buf ALIGNED(32);
|
|
static const char ARGSBOOT_STR[9] ALIGNED(0x10) = {'a','r','g','s','b','o','o','t','\0'}; //makes it easier to go through the file
|
|
static const char NIN_BUILD_STRING[] ALIGNED(32) = NIN_VERSION_STRING; // Version detection string used by nintendont launchers "$$Version:x.xxx"
|
|
|
|
bool isWiiVC = false;
|
|
bool wiiVCInternal = false;
|
|
|
|
/**
|
|
* Update meta.xml.
|
|
*/
|
|
static void updateMetaXml(void)
|
|
{
|
|
char filepath[MAXPATHLEN];
|
|
bool dir_argument_exists = strlen(launch_dir);
|
|
|
|
snprintf(filepath, sizeof(filepath), "%smeta.xml",
|
|
dir_argument_exists ? launch_dir : "/apps/Nintendont/");
|
|
|
|
if (!dir_argument_exists) {
|
|
gprintf("Creating new directory\r\n");
|
|
f_mkdir_char("/apps");
|
|
f_mkdir_char("/apps/Nintendont");
|
|
}
|
|
|
|
char new_meta[1024];
|
|
int len = snprintf(new_meta, sizeof(new_meta),
|
|
META_XML "\r\n<app version=\"1\">\r\n"
|
|
"\t<name>" META_NAME "</name>\r\n"
|
|
"\t<coder>" META_AUTHOR "</coder>\r\n"
|
|
"\t<version>%d.%d%s</version>\r\n"
|
|
"\t<release_date>20160710000000</release_date>\r\n"
|
|
"\t<short_description>" META_SHORT "</short_description>\r\n"
|
|
"\t<long_description>" META_LONG1 "\r\n\r\n" META_LONG2 "</long_description>\r\n"
|
|
"\t<no_ios_reload/>\r\n"
|
|
"\t<ahb_access/>\r\n"
|
|
"</app>\r\n",
|
|
NIN_VERSION >> 16, NIN_VERSION & 0xFFFF,
|
|
#ifdef NIN_SPECIAL_VERSION
|
|
NIN_SPECIAL_VERSION
|
|
#else
|
|
""
|
|
#endif
|
|
);
|
|
if (len > sizeof(new_meta))
|
|
len = sizeof(new_meta);
|
|
|
|
// Check if the file already exists.
|
|
FIL meta;
|
|
if (f_open_char(&meta, filepath, FA_READ|FA_OPEN_EXISTING) == FR_OK)
|
|
{
|
|
// File exists. If it's the same as the new meta.xml,
|
|
// don't bother rewriting it.
|
|
char orig_meta[1024];
|
|
if (len == meta.obj.objsize)
|
|
{
|
|
// File is the same length.
|
|
UINT read;
|
|
f_read(&meta, orig_meta, len, &read);
|
|
if (read == (UINT)len &&
|
|
!strncmp(orig_meta, new_meta, len))
|
|
{
|
|
// File is identical.
|
|
// Don't rewrite it.
|
|
f_close(&meta);
|
|
return;
|
|
}
|
|
}
|
|
f_close(&meta);
|
|
}
|
|
|
|
// File does not exist, or file is not identical.
|
|
// Write the new meta.xml.
|
|
if (f_open_char(&meta, filepath, FA_WRITE|FA_CREATE_ALWAYS) == FR_OK)
|
|
{
|
|
// Reserve space in the file.
|
|
if (f_size(&meta) < len) {
|
|
f_expand(&meta, len, 1);
|
|
}
|
|
|
|
// Write the new meta.xml.
|
|
UINT wrote;
|
|
f_write(&meta, new_meta, len, &wrote);
|
|
f_close(&meta);
|
|
FlushDevices();
|
|
}
|
|
}
|
|
|
|
static const WCHAR *primaryDevice;
|
|
void changeToDefaultDrive()
|
|
{
|
|
f_chdrive(primaryDevice);
|
|
f_chdir_char("/");
|
|
}
|
|
|
|
/**
|
|
* Get multi-game and region code information.
|
|
* @param CurDICMD [in] DI command. (0 == disc image, DIP_CMD_NORMAL == GameCube disc, DIP_CMD_DVDR == DVD-R)
|
|
* @param ISOShift [out,opt] ISO Shift. (34-bit rshifted byte offset)
|
|
* @param BI2region [out,opt] bi2.bin region code.
|
|
* @return 0 on success; non-zero on error.
|
|
*/
|
|
static u32 CheckForMultiGameAndRegion(unsigned int CurDICMD, u32 *ISOShift, u32 *BI2region)
|
|
{
|
|
char GamePath[260];
|
|
|
|
// Re-read the disc header to get the full ID6.
|
|
u8 *MultiHdr = memalign(32, 0x800);
|
|
|
|
FIL f;
|
|
UINT read;
|
|
FRESULT fres = FR_DISK_ERR;
|
|
|
|
if(CurDICMD)
|
|
ReadRealDisc(MultiHdr, 0, 0x800, CurDICMD);
|
|
else if (IsSupportedFileExt(ncfg->GamePath) || wiiVCInternal)
|
|
{
|
|
if(wiiVCInternal)
|
|
{
|
|
if(WDVD_FST_OpenDisc(0) != 0)
|
|
{
|
|
// Error opening the file.
|
|
free(MultiHdr);
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snprintf(GamePath, sizeof(GamePath), "%s:%s", GetRootDevice(), ncfg->GamePath);
|
|
fres = f_open_char(&f, GamePath, FA_READ|FA_OPEN_EXISTING);
|
|
if (fres != FR_OK)
|
|
{
|
|
// Error opening the file.
|
|
free(MultiHdr);
|
|
return 1;
|
|
}
|
|
}
|
|
if(wiiVCInternal)
|
|
read = WDVD_FST_Read(MultiHdr, 0x800);
|
|
else
|
|
f_read(&f, MultiHdr, 0x800, &read);
|
|
if (read != 0x800)
|
|
{
|
|
// Error reading from the file.
|
|
if(wiiVCInternal)
|
|
WDVD_FST_Close();
|
|
else
|
|
f_close(&f);
|
|
free(MultiHdr);
|
|
return 2;
|
|
}
|
|
|
|
// Check for CISO magic with 2 MB block size.
|
|
// NOTE: CISO block size is little-endian.
|
|
static const uint8_t CISO_MAGIC[8] = {'C','I','S','O',0x00,0x00,0x20,0x00};
|
|
if (!memcmp(MultiHdr, CISO_MAGIC, sizeof(CISO_MAGIC)) && !IsGCGame(MultiHdr))
|
|
{
|
|
// CISO magic is present, and GCN magic isn't.
|
|
// This is most likely a CISO image.
|
|
|
|
// CISO+MultiGame is not supported, so read the
|
|
// BI2.bin region code if requested and then return.
|
|
int ret = 0;
|
|
if (ISOShift)
|
|
*ISOShift = 0;
|
|
if (BI2region)
|
|
{
|
|
if(wiiVCInternal)
|
|
{
|
|
WDVD_FST_LSeek(0x8458);
|
|
read = WDVD_FST_Read(wdvdTmpBuf, sizeof(*BI2region));
|
|
memcpy(&BI2region, wdvdTmpBuf, sizeof(*BI2region));
|
|
}
|
|
else
|
|
{
|
|
f_lseek(&f, 0x8458);
|
|
f_read(&f, BI2region, sizeof(*BI2region), &read);
|
|
}
|
|
if (read != sizeof(*BI2region))
|
|
{
|
|
// Error reading from the file.
|
|
ret = 3;
|
|
}
|
|
}
|
|
if(wiiVCInternal)
|
|
WDVD_FST_Close();
|
|
else
|
|
f_close(&f);
|
|
free(MultiHdr);
|
|
return ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Extracted FST format.
|
|
// Multi-game isn't supported.
|
|
if (ISOShift)
|
|
*ISOShift = 0;
|
|
if (!BI2region)
|
|
{
|
|
free(MultiHdr);
|
|
return 0;
|
|
}
|
|
|
|
// Get the bi2.bin region code.
|
|
snprintf(GamePath, sizeof(GamePath), "%s:%ssys/bi2.bin", GetRootDevice(), ncfg->GamePath);
|
|
fres = f_open_char(&f, GamePath, FA_READ|FA_OPEN_EXISTING);
|
|
if (fres != FR_OK)
|
|
{
|
|
// Error opening bi2.bin.
|
|
free(MultiHdr);
|
|
return 4;
|
|
}
|
|
|
|
// bi2.bin is normally 8 KB, but we only need
|
|
// the first 48 bytes.
|
|
f_read(&f, MultiHdr, 48, &read);
|
|
f_close(&f);
|
|
if (read != 48)
|
|
{
|
|
// Could not read bi2.bin.
|
|
free(MultiHdr);
|
|
return 5;
|
|
}
|
|
|
|
// BI2.bin is at 0x440.
|
|
// Region code is at 0x458. (0x18 within BI2.bin.)
|
|
*BI2region = *(u32*)(&MultiHdr[0x18]);
|
|
free(MultiHdr);
|
|
return 0;
|
|
}
|
|
|
|
if (!IsMultiGameDisc((const char*)MultiHdr))
|
|
{
|
|
// Not a multi-game disc.
|
|
if (!CurDICMD)
|
|
{
|
|
// Close the disc image file.
|
|
if(wiiVCInternal)
|
|
WDVD_FST_Close();
|
|
else
|
|
f_close(&f);
|
|
}
|
|
|
|
if (BI2region)
|
|
{
|
|
// BI2.bin is at 0x440.
|
|
// Region code is at 0x458.
|
|
*BI2region = *(u32*)(&MultiHdr[0x458]);
|
|
}
|
|
|
|
free(MultiHdr);
|
|
return 0;
|
|
}
|
|
|
|
// Up to 15 games are supported.
|
|
// In theory, the format supports up to 48 games, but
|
|
// you'll run into the DVD size limit way before you
|
|
// ever reach that limit.
|
|
u32 i = 0;
|
|
u32 gamecount = 0;
|
|
u32 Offsets[15]; // 34-bit, rshifted by 2
|
|
u32 BI2region_codes[15];
|
|
gameinfo gi[15];
|
|
|
|
// Games must be aligned to 4-byte boundaries, since
|
|
// we're using 34-bit rsh2 (Wii) offsets.
|
|
u8 gameIsUnaligned[15];
|
|
memset(gameIsUnaligned, 0, sizeof(gameIsUnaligned));
|
|
|
|
u8 *GameHdr = memalign(32, 0x800);
|
|
// GCOPDV(D9) uses Wii-style 34-bit shifted addresses.
|
|
// FIXME: Needs 64-bit offsets.
|
|
const u32 *hdr32 = (const u32*)MultiHdr;
|
|
bool IsShifted = (hdr32[1] == 0x44564439);
|
|
for (i = 0x10; i < 0x40 && gamecount < 15; i++)
|
|
{
|
|
const u32 TmpOffset = hdr32[i];
|
|
if (TmpOffset > 0)
|
|
{
|
|
u64 RealOffset;
|
|
if (IsShifted)
|
|
{
|
|
// Disc uses 34-bit shifted offsets.
|
|
Offsets[gamecount] = TmpOffset;
|
|
RealOffset = (u64)TmpOffset << 2;
|
|
}
|
|
else
|
|
{
|
|
// Disc uses 32-bit unshifted offsets.
|
|
// If the value isn't a multiple of 4, it's unusable.
|
|
// TODO: Fix this, or will this "never" happen?
|
|
gameIsUnaligned[gamecount] = !!(TmpOffset & 3);
|
|
Offsets[gamecount] = TmpOffset >> 2;
|
|
RealOffset = TmpOffset;
|
|
}
|
|
|
|
if(CurDICMD)
|
|
ReadRealDisc(GameHdr, RealOffset, 0x800, CurDICMD);
|
|
else
|
|
{
|
|
if(wiiVCInternal)
|
|
{
|
|
WDVD_FST_LSeek(RealOffset);
|
|
read = WDVD_FST_Read(GameHdr, 0x800);
|
|
}
|
|
else
|
|
{
|
|
f_lseek(&f, RealOffset);
|
|
f_read(&f, GameHdr, 0x800, &read);
|
|
}
|
|
}
|
|
|
|
// Make sure the title in the header is NULL terminated.
|
|
GameHdr[0x20+65] = 0;
|
|
|
|
// BI2.bin is at 0x440.
|
|
// Region code is at 0x458.
|
|
BI2region_codes[gamecount] = *(u32*)(&GameHdr[0x458]);
|
|
|
|
// TODO: titles.txt support?
|
|
memcpy(gi[gamecount].ID, GameHdr, 6);
|
|
gi[gamecount].Revision = GameHdr[0x07];
|
|
gi[gamecount].Flags = GIFLAG_NAME_ALLOC;
|
|
gi[gamecount].Name = strdup((char*)&GameHdr[0x20]);
|
|
gi[gamecount].Path = NULL;
|
|
gamecount++;
|
|
}
|
|
}
|
|
|
|
free(GameHdr);
|
|
free(MultiHdr);
|
|
if (!CurDICMD)
|
|
{
|
|
if(wiiVCInternal)
|
|
WDVD_FST_Close();
|
|
else
|
|
f_close(&f);
|
|
}
|
|
|
|
// TODO: Share code with menu.c.
|
|
bool redraw = true;
|
|
ClearScreen();
|
|
u32 PosX = 0;
|
|
u32 UpHeld = 0, DownHeld = 0;
|
|
while (true)
|
|
{
|
|
VIDEO_WaitVSync();
|
|
FPAD_Update();
|
|
if( FPAD_OK(0) )
|
|
{
|
|
// TODO: Fix support for unaligned games?
|
|
if (!gameIsUnaligned[PosX])
|
|
break;
|
|
}
|
|
|
|
if( FPAD_Down(1) )
|
|
{
|
|
if(DownHeld == 0 || DownHeld > 10)
|
|
{
|
|
PosX++;
|
|
if(PosX == gamecount) PosX = 0;
|
|
redraw = true;
|
|
}
|
|
DownHeld++;
|
|
}
|
|
else
|
|
{
|
|
DownHeld = 0;
|
|
}
|
|
|
|
if( FPAD_Up(1) )
|
|
{
|
|
if(UpHeld == 0 || UpHeld > 10)
|
|
{
|
|
if(PosX == 0) PosX = gamecount;
|
|
PosX--;
|
|
redraw = true;
|
|
}
|
|
UpHeld++;
|
|
}
|
|
else
|
|
{
|
|
UpHeld = 0;
|
|
}
|
|
|
|
// TODO: Home = Go Back?
|
|
|
|
if( redraw )
|
|
{
|
|
PrintInfo();
|
|
PrintButtonActions(NULL, "Select", NULL, NULL);
|
|
static const int subheader_x = (640 - (40*10)) / 2;
|
|
PrintFormat(DEFAULT_SIZE, BLACK, subheader_x, MENU_POS_Y + 20*3,
|
|
"Select a game from this multi-game disc:");
|
|
for (i = 0; i < gamecount; ++i)
|
|
{
|
|
const u32 color = gameIsUnaligned[i] ? MAROON : BLACK;
|
|
PrintFormat(DEFAULT_SIZE, color, MENU_POS_X, MENU_POS_Y + 20*4 + i * 20, "%50.50s [%.6s]%s",
|
|
gi[i].Name, gi[i].ID, i == PosX ? ARROW_LEFT : " " );
|
|
}
|
|
GRRLIB_Render();
|
|
Screenshot();
|
|
ClearScreen();
|
|
redraw = false;
|
|
}
|
|
}
|
|
|
|
// Free the allocated names.
|
|
for (i = 0; i < gamecount; ++i)
|
|
{
|
|
if (gi[i].Flags & GIFLAG_NAME_ALLOC)
|
|
free(gi[i].Name);
|
|
}
|
|
|
|
// Set the ISOShift and BI2region values.
|
|
if (ISOShift)
|
|
{
|
|
*ISOShift = Offsets[PosX];
|
|
}
|
|
if (BI2region)
|
|
{
|
|
*BI2region = BI2region_codes[PosX];
|
|
}
|
|
|
|
// Save the Game ID.
|
|
memcpy(&ncfg->GameID, gi[PosX].ID, 4);
|
|
return 0;
|
|
}
|
|
|
|
static char dev_es[] ATTRIBUTE_ALIGN(32) = "/dev/es";
|
|
|
|
extern vu32 FoundVersion;
|
|
extern void _jmp813();
|
|
int main(int argc, char **argv)
|
|
{
|
|
// Exit after 10 seconds if there is an error
|
|
__exception_setreload(10);
|
|
// u64 timeout = 0;
|
|
CheckForGecko();
|
|
DCInvalidateRange(loader_stub, 0x1800);
|
|
memcpy(loader_stub, (void*)0x80001800, 0x1800);
|
|
RAMInit();
|
|
//tell devkitPPC r29 that we use UTF-8
|
|
setlocale(LC_ALL,"C.UTF-8");
|
|
|
|
memset((void*)ncfg, 0, sizeof(NIN_CFG));
|
|
bool argsboot = false;
|
|
if(argc > 1) //every 0x00 gets counted as one arg so just make sure its more than the path and copy
|
|
{
|
|
memcpy(ncfg, argv[1], sizeof(NIN_CFG));
|
|
UpdateNinCFG(); //support for old versions with this
|
|
if(ncfg->Magicbytes == 0x01070CF6 && ncfg->Version == NIN_CFG_VERSION && ncfg->MaxPads <= NIN_CFG_MAXPAD)
|
|
{
|
|
if(ncfg->Config & NIN_CFG_AUTO_BOOT)
|
|
{ //do NOT remove, this can be used to see if nintendont knows args
|
|
gprintf(ARGSBOOT_STR);
|
|
argsboot = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Meh, doesnt do anything anymore anyways
|
|
//STM_RegisterEventHandler(HandleSTMEvent);
|
|
|
|
Initialise(argsboot);
|
|
|
|
//for BT.c
|
|
CONF_GetPadDevices((conf_pads*)0x932C0000);
|
|
DCFlushRange((void*)0x932C0000, sizeof(conf_pads));
|
|
*(vu32*)0x932C0490 = CONF_GetIRSensitivity();
|
|
*(vu32*)0x932C0494 = CONF_GetSensorBarPosition();
|
|
DCFlushRange((void*)0x932C0490, 8);
|
|
|
|
WiiDRC_Init();
|
|
isWiiVC = WiiDRC_Inited();
|
|
if(isWiiVC)
|
|
{
|
|
//Clear out stubs to make it (hopefully) reboot properly
|
|
memset(loader_stub, 0, 0x1800);
|
|
memset((void*)0x80001800, 0, 0x1800);
|
|
DCStoreRange((void*)0x80001800, 0x1800);
|
|
}
|
|
|
|
s32 fd;
|
|
|
|
/* Wii VC fw.img is pre-patched but Wii/vWii isnt, so we
|
|
still have to reload IOS on those with a patched kernel */
|
|
if(!isWiiVC)
|
|
{
|
|
// Preparing IOS58 Kernel...
|
|
if(argsboot == false)
|
|
ShowMessageScreen("Preparing IOS58 Kernel...");
|
|
|
|
u32 u;
|
|
//Disables MEMPROT for patches
|
|
write16(MEM_PROT, 0);
|
|
//Patches FS access
|
|
for( u = 0x93A00000; u < 0x94000000; u+=2 )
|
|
{
|
|
if( memcmp( (void*)(u), FSAccessPattern, sizeof(FSAccessPattern) ) == 0 )
|
|
{
|
|
//gprintf("FSAccessPatch:%08X\r\n", u );
|
|
memcpy( (void*)u, FSAccessPatch, sizeof(FSAccessPatch) );
|
|
DCFlushRange((void*)u, sizeof(FSAccessPatch));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Load and patch IOS58.
|
|
if (LoadKernel() < 0)
|
|
{
|
|
// NOTE: Attempting to initialize controllers here causes a crash.
|
|
// Hence, we can't wait for the user to press the HOME button, so
|
|
// we'll just wait for a timeout instead.
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*20, "Returning to loader in 10 seconds.");
|
|
UpdateScreen();
|
|
VIDEO_WaitVSync();
|
|
|
|
// Wait 10 seconds...
|
|
usleep(10000000);
|
|
|
|
// Return to the loader.
|
|
ExitToLoader(1);
|
|
}
|
|
PatchKernel();
|
|
u32 v = FoundVersion;
|
|
//this ensures all IOS modules get loaded in ES on reload
|
|
memcpy( ESBootPatch+0x14, &v, 4 );
|
|
DCInvalidateRange( (void*)0x939F0348, sizeof(ESBootPatch) );
|
|
memcpy( (void*)0x939F0348, ESBootPatch, sizeof(ESBootPatch) );
|
|
DCFlushRange( (void*)0x939F0348, sizeof(ESBootPatch) );
|
|
|
|
// Loading IOS58 Kernel...
|
|
if(argsboot == false)
|
|
ShowMessageScreen("Loading IOS58 Kernel...");
|
|
|
|
//libogc still has that, lets close it
|
|
__ES_Close();
|
|
fd = IOS_Open( dev_es, 0 );
|
|
|
|
raw_irq_handler_t irq_handler = BeforeIOSReload();
|
|
IOS_IoctlvAsync( fd, 0x25, 0, 0, &IOCTL_Buf, NULL, NULL );
|
|
sleep(1); //wait this time at least
|
|
AfterIOSReload( irq_handler, v );
|
|
//Disables MEMPROT for patches
|
|
write16(MEM_PROT, 0);
|
|
}
|
|
//else if(*(vu16*)0xCD8005A0 != 0xCAFE)
|
|
//{
|
|
/* WiiVC seems to have some bug that without any fake IOS
|
|
reload makes it impossible to read HW regs on PPC
|
|
and it seems to break some consoles to reload IOS */
|
|
// IOS_ReloadIOS(58);
|
|
//}
|
|
|
|
// Preparing Nintendont Kernel...
|
|
if(argsboot == false)
|
|
ShowMessageScreen("Preparing Nintendont Kernel...");
|
|
|
|
//inject nintendont thread
|
|
void *kernel_bin = NULL;
|
|
unsigned int kernel_bin_size = 0;
|
|
unzip_data(kernel_zip, kernel_zip_size, &kernel_bin, &kernel_bin_size);
|
|
gprintf("Decompressed kernel.bin with %i bytes\r\n", kernel_bin_size);
|
|
memcpy((void*)0x92F00000,kernel_bin,kernel_bin_size);
|
|
DCFlushRange((void*)0x92F00000,kernel_bin_size);
|
|
free(kernel_bin);
|
|
//inject kernelboot
|
|
memcpy((void*)0x92FFFE00,kernelboot_bin,kernelboot_bin_size);
|
|
DCFlushRange((void*)0x92FFFE00,kernelboot_bin_size);
|
|
//Loading Nintendont Kernel...
|
|
if(argsboot == false)
|
|
ShowMessageScreen("Loading Nintendont Kernel...");
|
|
//close in case this is wii vc
|
|
__ES_Close();
|
|
memset( STATUS, 0, 0x20 );
|
|
DCFlushRange( STATUS, 0x20 );
|
|
//make sure kernel doesnt reload
|
|
*(vu32*)0x93003420 = 0;
|
|
DCFlushRange((void*)0x93003420,0x20);
|
|
//Set some important kernel regs
|
|
*(vu32*)0x92FFFFC0 = isWiiVC; //cant be detected in IOS
|
|
if(WiiDRC_Connected()) //used in PADReadGC.c
|
|
*(vu32*)0x92FFFFC4 = (u32)WiiDRC_GetRawI2CAddr();
|
|
else //will disable gamepad spot for player 1
|
|
*(vu32*)0x92FFFFC4 = 0;
|
|
DCFlushRange((void*)0x92FFFFC0,0x20);
|
|
fd = IOS_Open( dev_es, 0 );
|
|
IOS_IoctlvAsync(fd, 0x1F, 0, 0, &IOCTL_Buf, NULL, NULL);
|
|
//Waiting for Nintendont...
|
|
if(argsboot == false)
|
|
ShowMessageScreen("Waiting for Nintendont...");
|
|
while(1)
|
|
{
|
|
DCInvalidateRange( STATUS, 0x20 );
|
|
if((STATUS_LOADING > 0 || abs(STATUS_LOADING) > 1) && STATUS_LOADING < 20)
|
|
{
|
|
gprintf("Kernel sent signal\n");
|
|
break;
|
|
}
|
|
}
|
|
//Async Ioctlv done by now
|
|
IOS_Close(fd);
|
|
|
|
gprintf("Nintendont at your service!\r\n%s\r\n", NIN_BUILD_STRING);
|
|
KernelLoaded = 1;
|
|
|
|
// Checking for storage devices...
|
|
if(argsboot == false)
|
|
ShowMessageScreen("Checking storage devices...");
|
|
|
|
// Initialize devices.
|
|
// TODO: Only mount the device Nintendont was launched from
|
|
// Mount the other device asynchronously.
|
|
bool foundOneDevice = false;
|
|
int i;
|
|
for (i = DEV_SD; i <= DEV_USB; i++)
|
|
{
|
|
//only check SD on Wii VC
|
|
if(i == DEV_USB && isWiiVC)
|
|
break;
|
|
//check SD and USB on Wii and WiiU
|
|
const WCHAR *devNameFF = MountDevice(i);
|
|
if (devNameFF && !foundOneDevice)
|
|
{
|
|
// Set this device as primary.
|
|
primaryDevice = devNameFF;
|
|
changeToDefaultDrive();
|
|
foundOneDevice = true;
|
|
}
|
|
}
|
|
|
|
// FIXME: Show this information in the menu instead of
|
|
// aborting here.
|
|
if (!devices[DEV_SD] && !devices[DEV_USB])
|
|
{
|
|
ClearScreen();
|
|
gprintf("No FAT device found!\n");
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, 232, "No FAT device found!");
|
|
ExitToLoader(1);
|
|
}
|
|
// Seems like some programs start without any args
|
|
if(argc > 0 && argv != NULL && argv[0] != NULL)
|
|
{
|
|
char* first_slash = strrchr(argv[0], '/');
|
|
if (first_slash != NULL) strncpy(launch_dir, argv[0], first_slash-argv[0]+1);
|
|
}
|
|
gprintf("launch_dir = %s\r\n", launch_dir);
|
|
|
|
// Initialize controllers.
|
|
// FIXME: Initialize before storage devices.
|
|
// Doing that right now causes usbstorage to fail...
|
|
FPAD_Init();
|
|
FPAD_Update();
|
|
|
|
/* Read IPL Font before doing any patches */
|
|
void *fontbuffer = memalign(32, 0x50000);
|
|
__SYS_ReadROM((void*)fontbuffer,0x50000,0x1AFF00);
|
|
memcpy((void*)0xD3100000, fontbuffer, 0x50000);
|
|
DCInvalidateRange( (void*)0x93100000, 0x50000 );
|
|
free(fontbuffer);
|
|
//gprintf("Font: 0x1AFF00 starts with %.4s, 0x1FCF00 with %.4s\n", (char*)0x93100000, (char*)0x93100000 + 0x4D000);
|
|
|
|
// Update meta.xml.
|
|
updateMetaXml();
|
|
|
|
if(argsboot == false)
|
|
{
|
|
// Load titles.txt.
|
|
LoadTitles();
|
|
|
|
if (LoadNinCFG() == false)
|
|
{
|
|
memset(ncfg, 0, sizeof(NIN_CFG));
|
|
|
|
ncfg->Magicbytes = 0x01070CF6;
|
|
ncfg->Version = NIN_CFG_VERSION;
|
|
ncfg->Language = NIN_LAN_AUTO;
|
|
ncfg->MaxPads = NIN_CFG_MAXPAD;
|
|
ncfg->MemCardBlocks = 0x2;//251 blocks
|
|
}
|
|
|
|
// Prevent autobooting if B is pressed
|
|
int i = 0;
|
|
while((ncfg->Config & NIN_CFG_AUTO_BOOT) && i < 1000000) // wait for wiimote re-synch
|
|
{
|
|
if (i == 0) {
|
|
PrintInfo();
|
|
PrintFormat(DEFAULT_SIZE, BLACK, 320 - 90, MENU_POS_Y + 20*10, "B: Cancel Autoboot");
|
|
GRRLIB_Render();
|
|
ClearScreen();
|
|
}
|
|
|
|
FPAD_Update();
|
|
|
|
if (FPAD_Cancel(0)) {
|
|
ncfg->Config &= ~NIN_CFG_AUTO_BOOT;
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
ReconfigVideo(rmode);
|
|
UseSD = (ncfg->Config & NIN_CFG_USB) == 0;
|
|
|
|
bool progressive = (CONF_GetProgressiveScan() > 0) && VIDEO_HaveComponentCable();
|
|
if(progressive) //important to prevent blackscreens
|
|
ncfg->VideoMode |= NIN_VID_PROG;
|
|
else
|
|
ncfg->VideoMode &= ~NIN_VID_PROG;
|
|
|
|
bool SaveSettings = false;
|
|
if(!(ncfg->Config & NIN_CFG_AUTO_BOOT))
|
|
{
|
|
// Not autobooting.
|
|
// Prompt the user to select a device and game.
|
|
SaveSettings = SelectDevAndGame();
|
|
}
|
|
else
|
|
{
|
|
// Autobooting.
|
|
gprintf("Autobooting:\"%s\"\r\n", ncfg->GamePath );
|
|
PrintInfo();
|
|
GRRLIB_Render();
|
|
ClearScreen();
|
|
}
|
|
|
|
//Init DI and set correct ID if needed
|
|
unsigned int CurDICMD = 0;
|
|
if( memcmp(ncfg->GamePath, "di", 3) == 0 )
|
|
{
|
|
if(argsboot == false)
|
|
ShowLoadingScreen();
|
|
if(isWiiVC)
|
|
{
|
|
if(WDVD_Init() != 0)
|
|
ShowMessageScreenAndExit("The Wii VC Disc could not be initialized!", 1);
|
|
if(WDVD_OpenDataPartition() != 0)
|
|
ShowMessageScreenAndExit("Found no Partition on Wii VC Disc!", 1);
|
|
if(!WDVD_FST_Mount())
|
|
ShowMessageScreenAndExit("Unable to open Partition on Wii VC Disc!", 1);
|
|
if(WDVD_FST_OpenDisc(0) != 0)
|
|
ShowMessageScreenAndExit("No game.iso on Wii VC Disc!", 1);
|
|
u8 *DIBuf = memalign(32,0x800);
|
|
if(WDVD_FST_Read(DIBuf, 0x800) != 0x800)
|
|
{
|
|
free(DIBuf);
|
|
ShowMessageScreenAndExit("Cant read game.iso start!", 1);
|
|
}
|
|
if( IsGCGame(DIBuf) == false )
|
|
{
|
|
WDVD_FST_LSeek(0x8000);
|
|
WDVD_FST_Read(DIBuf, 0x800);
|
|
if( IsGCGame(DIBuf) == false )
|
|
{
|
|
free(DIBuf);
|
|
ShowMessageScreenAndExit("game.iso is not a GC Disc!", 1);
|
|
}
|
|
}
|
|
memcpy(&(ncfg->GameID), DIBuf, 4);
|
|
free(DIBuf);
|
|
WDVD_FST_Close();
|
|
wiiVCInternal = true;
|
|
}
|
|
else
|
|
{
|
|
DI_UseCache(false);
|
|
DI_Init();
|
|
DI_Mount();
|
|
while (DI_GetStatus() & DVD_INIT)
|
|
usleep(20000);
|
|
if(!(DI_GetStatus() & DVD_READY))
|
|
{
|
|
ShowMessageScreenAndExit("The Disc Drive could not be initialized!", 1);
|
|
}
|
|
DI_Close();
|
|
|
|
u8 *DIBuf = memalign(32,0x800);
|
|
memset(DIBuf, 0, 0x20);
|
|
DCFlushRange(DIBuf, 0x20);
|
|
CurDICMD = DIP_CMD_NORMAL;
|
|
ReadRealDisc(DIBuf, 0, 0x20, CurDICMD);
|
|
if( IsGCGame(DIBuf) == false )
|
|
{
|
|
memset(DIBuf, 0, 0x800);
|
|
DCFlushRange(DIBuf, 0x800);
|
|
CurDICMD = DIP_CMD_DVDR;
|
|
ReadRealDisc(DIBuf, 0, 0x800, CurDICMD);
|
|
if( IsGCGame(DIBuf) == false )
|
|
{
|
|
free(DIBuf);
|
|
ShowMessageScreenAndExit("The Disc in the Drive is not a GC Disc!", 1);
|
|
}
|
|
}
|
|
memcpy(&(ncfg->GameID), DIBuf, 4);
|
|
free(DIBuf);
|
|
}
|
|
}
|
|
|
|
if(SaveSettings)
|
|
{
|
|
// TODO: If the boot device is the same as the game device,
|
|
// don't write it twice.
|
|
|
|
// Write config to the boot device, which is loaded on next launch.
|
|
FIL cfg;
|
|
if (f_open_char(&cfg, "/nincfg.bin", FA_WRITE|FA_OPEN_ALWAYS) == FR_OK)
|
|
{
|
|
// Reserve space in the file.
|
|
if (f_size(&cfg) < sizeof(NIN_CFG)) {
|
|
f_expand(&cfg, sizeof(NIN_CFG), 1);
|
|
}
|
|
|
|
// Write nincfg.bin.
|
|
UINT wrote;
|
|
f_write(&cfg, ncfg, sizeof(NIN_CFG), &wrote);
|
|
f_close(&cfg);
|
|
}
|
|
|
|
// Write config to the game device, used by the Nintendont kernel.
|
|
char ConfigPath[20];
|
|
snprintf(ConfigPath, sizeof(ConfigPath), "%s:/nincfg.bin", GetRootDevice());
|
|
if (f_open_char(&cfg, ConfigPath, FA_WRITE|FA_OPEN_ALWAYS) == FR_OK)
|
|
{
|
|
// Reserve space in the file.
|
|
if (f_size(&cfg) < sizeof(NIN_CFG)) {
|
|
f_expand(&cfg, sizeof(NIN_CFG), 1);
|
|
}
|
|
|
|
// Write nincfg.bin.
|
|
UINT wrote;
|
|
f_write(&cfg, ncfg, sizeof(NIN_CFG), &wrote);
|
|
f_close(&cfg);
|
|
}
|
|
|
|
FlushDevices();
|
|
}
|
|
|
|
// Get multi-game and region code information.
|
|
u32 ISOShift = 0; // NOTE: This is a 34-bit shifted offset.
|
|
u32 BI2region = 0; // bi2.bin region code [TODO: Validate?]
|
|
int ret = CheckForMultiGameAndRegion(CurDICMD, &ISOShift, &BI2region);
|
|
if (ret != 0) {
|
|
ClearScreen();
|
|
PrintInfo();
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*4, "CheckForMultiGameAndRegion() failed: %d", ret);
|
|
switch (ret) {
|
|
case 1:
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*5, "Unable to open the %s.",
|
|
CurDICMD == 0 ? "disc image file" : "disc drive");
|
|
break;
|
|
case 2:
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*5, "Unable to read the disc header.");
|
|
break;
|
|
case 3:
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*5, "Unable to read the CISO bi2.bin area.");
|
|
break;
|
|
case 4:
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*5, "Unable to open the extracted FST bi2.bin file.");
|
|
break;
|
|
case 5:
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*5, "Unable to read the extracted FST bi2.bin file.");
|
|
break;
|
|
default:
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*5, "Unknown error code.");
|
|
break;
|
|
}
|
|
|
|
if (CurDICMD) {
|
|
char unkdev[32];
|
|
const char *device;
|
|
switch (CurDICMD) {
|
|
case DIP_CMD_NORMAL:
|
|
device = "GameCube disc";
|
|
break;
|
|
case DIP_CMD_DVDR:
|
|
device = "DVD-R";
|
|
break;
|
|
default:
|
|
snprintf(unkdev, sizeof(unkdev), "Unknown (CMD: 0x%02x)", CurDICMD);
|
|
device = unkdev;
|
|
break;
|
|
}
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*7, "Device: %s", device);
|
|
} else {
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*7, "Filename: %s:%s", GetRootDevice(), ncfg->GamePath);
|
|
}
|
|
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*20, "Returning to loader in 10 seconds.");
|
|
UpdateScreen();
|
|
VIDEO_WaitVSync();
|
|
|
|
// Wait 10 seconds...
|
|
usleep(10000000);
|
|
|
|
// Return to the loader.
|
|
ExitToLoader(1);
|
|
}
|
|
|
|
// Save the ISO shift value for multi-game discs.
|
|
*(vu32*)0xD300300C = ISOShift;
|
|
|
|
//Set Language
|
|
if(ncfg->Language == NIN_LAN_AUTO || ncfg->Language >= NIN_LAN_LAST)
|
|
{
|
|
switch (CONF_GetLanguage())
|
|
{
|
|
case CONF_LANG_GERMAN:
|
|
ncfg->Language = NIN_LAN_GERMAN;
|
|
break;
|
|
case CONF_LANG_FRENCH:
|
|
ncfg->Language = NIN_LAN_FRENCH;
|
|
break;
|
|
case CONF_LANG_SPANISH:
|
|
ncfg->Language = NIN_LAN_SPANISH;
|
|
break;
|
|
case CONF_LANG_ITALIAN:
|
|
ncfg->Language = NIN_LAN_ITALIAN;
|
|
break;
|
|
case CONF_LANG_DUTCH:
|
|
ncfg->Language = NIN_LAN_DUTCH;
|
|
break;
|
|
default:
|
|
ncfg->Language = NIN_LAN_ENGLISH;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(ncfg->Config & NIN_CFG_MEMCARDEMU)
|
|
{
|
|
// Memory card emulation is enabled.
|
|
// Set up the memory card file.
|
|
char BasePath[20];
|
|
snprintf(BasePath, sizeof(BasePath), "%s:/saves", GetRootDevice());
|
|
f_mkdir_char(BasePath);
|
|
|
|
char MemCardName[8];
|
|
memset(MemCardName, 0, 8);
|
|
if ( ncfg->Config & NIN_CFG_MC_MULTI )
|
|
{
|
|
// "Multi" mode is enabled.
|
|
// Use one memory card for USA/PAL games,
|
|
// and another memory card for JPN games.
|
|
switch (BI2region)
|
|
{
|
|
case BI2_REGION_JAPAN:
|
|
case BI2_REGION_SOUTH_KOREA:
|
|
default:
|
|
// JPN games.
|
|
memcpy(MemCardName, "ninmemj", 7);
|
|
break;
|
|
|
|
case BI2_REGION_USA:
|
|
case BI2_REGION_PAL:
|
|
// USA/PAL games.
|
|
memcpy(MemCardName, "ninmem", 6);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// One card per game.
|
|
memcpy(MemCardName, &(ncfg->GameID), 4);
|
|
}
|
|
|
|
char MemCard[32];
|
|
snprintf(MemCard, sizeof(MemCard), "%s/%s.raw", BasePath, MemCardName);
|
|
gprintf("Using %s as Memory Card.\r\n", MemCard);
|
|
FIL f;
|
|
if (f_open_char(&f, MemCard, FA_READ|FA_OPEN_EXISTING) != FR_OK)
|
|
{
|
|
// Memory card file not found. Create it.
|
|
if(GenerateMemCard(MemCard, BI2region) == false)
|
|
{
|
|
ClearScreen();
|
|
ShowMessageScreenAndExit("Failed to create Memory Card File!", 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Memory card file found.
|
|
f_close(&f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Using real memory card slots. (Wii only)
|
|
// Setup real SRAM language.
|
|
syssram *sram;
|
|
sram = __SYS_LockSram();
|
|
sram->lang = ncfg->Language;
|
|
__SYS_UnlockSram(1); // 1 -> write changes
|
|
while(!__SYS_SyncSram());
|
|
}
|
|
|
|
#define GCN_IPL_SIZE 2097152
|
|
#define TRI_IPL_SIZE 1048576
|
|
void *iplbuf = NULL;
|
|
bool useipl = false;
|
|
bool useipltri = false;
|
|
|
|
//Check if game is Triforce game
|
|
u32 IsTRIGame = 0;
|
|
if (ncfg->GameID != 0x47545050) //Damn you Knights Of The Temple!
|
|
IsTRIGame = TRISetupGames(ncfg->GamePath, CurDICMD, ISOShift);
|
|
|
|
if (!(ncfg->Config & (NIN_CFG_SKIP_IPL)))
|
|
{
|
|
if(IsTRIGame == 0)
|
|
{
|
|
// Attempt to load the GameCube IPL.
|
|
char iplchar[32];
|
|
iplchar[0] = 0;
|
|
switch (BI2region)
|
|
{
|
|
case BI2_REGION_USA:
|
|
snprintf(iplchar, sizeof(iplchar), "%s:/iplusa.bin", GetRootDevice());
|
|
break;
|
|
|
|
case BI2_REGION_JAPAN:
|
|
case BI2_REGION_SOUTH_KOREA:
|
|
default:
|
|
snprintf(iplchar, sizeof(iplchar), "%s:/ipljap.bin", GetRootDevice());
|
|
break;
|
|
|
|
case BI2_REGION_PAL:
|
|
// FIXME: PAL IPL is broken on Wii U.
|
|
if (!IsWiiU())
|
|
snprintf(iplchar, sizeof(iplchar), "%s:/iplpal.bin", GetRootDevice());
|
|
break;
|
|
}
|
|
|
|
FIL f;
|
|
if (iplchar[0] != 0 &&
|
|
f_open_char(&f, iplchar, FA_READ|FA_OPEN_EXISTING) == FR_OK)
|
|
{
|
|
if (f.obj.objsize == GCN_IPL_SIZE)
|
|
{
|
|
iplbuf = malloc(GCN_IPL_SIZE);
|
|
UINT read;
|
|
f_read(&f, iplbuf, GCN_IPL_SIZE, &read);
|
|
useipl = (read == GCN_IPL_SIZE);
|
|
}
|
|
f_close(&f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Attempt to load the Triforce IPL. (segaboot)
|
|
char iplchar[32];
|
|
snprintf(iplchar, sizeof(iplchar), "%s:/segaboot.bin", GetRootDevice());
|
|
FIL f;
|
|
if (f_open_char(&f, iplchar, FA_READ|FA_OPEN_EXISTING) == FR_OK)
|
|
{
|
|
if (f.obj.objsize == TRI_IPL_SIZE)
|
|
{
|
|
f_lseek(&f, 0x20);
|
|
void *iplbuf = (void*)0x92A80000;
|
|
UINT read;
|
|
f_read(&f, iplbuf, TRI_IPL_SIZE - 0x20, &read);
|
|
useipltri = (read == (TRI_IPL_SIZE - 0x20));
|
|
}
|
|
f_close(&f);
|
|
}
|
|
}
|
|
}
|
|
|
|
//sync changes
|
|
CloseDevices();
|
|
|
|
WPAD_Disconnect(0);
|
|
WPAD_Disconnect(1);
|
|
WPAD_Disconnect(2);
|
|
WPAD_Disconnect(3);
|
|
|
|
WUPC_Shutdown();
|
|
WPAD_Shutdown();
|
|
|
|
if(wiiVCInternal)
|
|
{
|
|
WDVD_FST_Unmount();
|
|
WDVD_Close();
|
|
}
|
|
|
|
//before flushing do game specific patches
|
|
if(ncfg->Config & NIN_CFG_FORCE_PROG &&
|
|
ncfg->GameID == 0x47584745)
|
|
{ //Mega Man X Collection does progressive ingame so
|
|
//forcing it would mess with the interal game setup
|
|
gprintf("Disabling Force Progressive for this game\r\n");
|
|
ncfg->Config &= ~NIN_CFG_FORCE_PROG;
|
|
}
|
|
|
|
//make sure the cfg gets to the kernel
|
|
DCStoreRange((void*)ncfg, sizeof(NIN_CFG));
|
|
|
|
//set current time
|
|
u32 bias = 0, cur_time = 0;
|
|
__SYS_GetRTC(&cur_time);
|
|
if(CONF_GetCounterBias(&bias) >= 0)
|
|
cur_time += bias;
|
|
settime(secs_to_ticks(cur_time));
|
|
//hand over approx time passed since 1970
|
|
*(vu32*)0xD3003424 = (cur_time+946684800);
|
|
|
|
//set status for kernel to start running
|
|
*(vu32*)0xD3003420 = 0x0DEA;
|
|
while(1)
|
|
{
|
|
DCInvalidateRange( STATUS, 0x20 );
|
|
if( STATUS_LOADING == 0xdeadbeef )
|
|
break;
|
|
|
|
if(argsboot == false)
|
|
{
|
|
PrintInfo();
|
|
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*6, "Loading patched kernel... %d", STATUS_LOADING);
|
|
if(STATUS_LOADING == 0)
|
|
{
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*7, "ES_Init...");
|
|
// Cleans the -1 when it's past it to avoid confusion if another error happens. e.g. before it showed "81" instead of "8" if the controller was unplugged.
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X + 163, MENU_POS_Y + 20*6, " ");
|
|
}
|
|
if((STATUS_LOADING > 0 || abs(STATUS_LOADING) > 1) && STATUS_LOADING < 20)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*7, "ES_Init... Done!");
|
|
if(STATUS_LOADING == 2)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*8, "Initing storage devices...");
|
|
if(abs(STATUS_LOADING) > 2 && abs(STATUS_LOADING) < 20)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*8, "Initing storage devices... Done!");
|
|
if(STATUS_LOADING == -2)
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*8, "Initing storage devices... Error! %d Shutting down", STATUS_ERROR);
|
|
if(STATUS_LOADING == 3)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*9, "Mounting USB/SD device...");
|
|
if(abs(STATUS_LOADING) > 3 && abs(STATUS_LOADING) < 20)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*9, "Mounting USB/SD device... Done!");
|
|
if(STATUS_LOADING == -3)
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*9, "Mounting USB/SD device... Error! %d Shutting down", STATUS_ERROR);
|
|
if(STATUS_LOADING == 5) {
|
|
/* if (timeout == 0)
|
|
timeout = ticks_to_secs(gettime()) + 20; // Set timer for 20 seconds
|
|
else if (timeout <= ticks_to_secs(gettime())) {
|
|
STATUS_ERROR = -7;
|
|
DCFlushRange(STATUS, 0x20);
|
|
usleep(100);
|
|
//memset( (void*)0x92f00000, 0, 0x100000 );
|
|
//DCFlushRange( (void*)0x92f00000, 0x100000 );
|
|
//ExitToLoader(1);
|
|
}*/
|
|
PrintFormat(DEFAULT_SIZE, (STATUS_ERROR == -7) ? MAROON:BLACK, MENU_POS_X, MENU_POS_Y + 20*10, (STATUS_ERROR == -7) ? "Checking FS... Timeout!" : "Checking FS...");
|
|
}
|
|
if(abs(STATUS_LOADING) > 5 && abs(STATUS_LOADING) < 20)
|
|
{
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*10, "Checking FS... Done!");
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*11, "Drive size: %.02f%s Sector size: %d", STATUS_DRIVE, STATUS_GB_MB ? "GB" : "MB", STATUS_SECTOR);
|
|
}
|
|
if(STATUS_LOADING == -5)
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*10, "Checking FS... Error! %d Shutting down", STATUS_ERROR);
|
|
if(STATUS_LOADING == 6)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*12, "ES_LoadModules...");
|
|
if(abs(STATUS_LOADING) > 6 && abs(STATUS_LOADING) < 20)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*12, "ES_LoadModules... Done!");
|
|
if(STATUS_LOADING == -6)
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*12, "ES_LoadModules... Error! %d Shutting down", STATUS_ERROR);
|
|
if(STATUS_LOADING == 7)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*13, "Loading config...");
|
|
if(abs(STATUS_LOADING) > 7 && abs(STATUS_LOADING) < 20)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*13, "Loading config... Done!");
|
|
/*if(STATUS_LOADING == 8)
|
|
{
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*14, "Init HID devices... ");
|
|
if ( STATUS_ERROR == 1)
|
|
{
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*15, " Make sure the Controller is plugged in");
|
|
}
|
|
else
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*15, "%50s", " ");
|
|
}
|
|
if(abs(STATUS_LOADING) > 8 && abs(STATUS_LOADING) < 20)
|
|
{
|
|
if (ncfg->Config & NIN_CFG_NATIVE_SI)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*14, "Init HID devices... Using ONLY NATIVE Gamecube Ports");
|
|
else if ((ncfg->MaxPads == 1) && (ncfg->Config & NIN_CFG_HID))
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*14, "Init HID devices... Using Gamecube and HID Ports");
|
|
else if ((ncfg->MaxPads > 0) && (ncfg->Config & NIN_CFG_HID))
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*14, "Init HID devices... Using Gamecube, HID, and BT Ports");
|
|
else if (ncfg->MaxPads > 0)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*14, "Init HID devices... Using Gamecube and BT Ports");
|
|
else if (ncfg->Config & NIN_CFG_HID)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*14, "Init HID devices... Using HID and Bluetooth Ports");
|
|
else
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*14, "Init HID devices... Using Bluetooth Ports... Done!");
|
|
}
|
|
if(STATUS_LOADING == -8)
|
|
{
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*14, "Init HID devices... Failed! Shutting down");
|
|
switch (STATUS_ERROR)
|
|
{
|
|
case -1:
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*15, "No Controller plugged in! %25s", " ");
|
|
break;
|
|
case -2:
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*15, "Missing %s:/controller.ini %20s", GetRootDevice(), " ");
|
|
break;
|
|
case -3:
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*15, "Controller does not match %s:/controller.ini %6s", GetRootDevice(), " ");
|
|
break;
|
|
case -4:
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*15, "Invalid Polltype in %s:/controller.ini %12s", GetRootDevice(), " ");
|
|
break;
|
|
case -5:
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*15, "Invalid DPAD value in %s:/controller.ini %9s", GetRootDevice(), " ");
|
|
break;
|
|
case -6:
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*15, "PS3 controller init error %25s", " ");
|
|
break;
|
|
case -7:
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*15, "Gamecube adapter for Wii u init error %13s", " ");
|
|
break;
|
|
default:
|
|
PrintFormat(DEFAULT_SIZE, MAROON, MENU_POS_X, MENU_POS_Y + 20*15, "Unknown error %d %35s", STATUS_ERROR, " ");
|
|
break;
|
|
}
|
|
}*/
|
|
if(STATUS_LOADING == 9)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*14, "Init DI... %40s", " ");
|
|
if(abs(STATUS_LOADING) > 9 && abs(STATUS_LOADING) < 20)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*14, "Init DI... Done! %35s", " ");
|
|
if(STATUS_LOADING == 10)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*15, "Init CARD...");
|
|
if(abs(STATUS_LOADING) > 10 && abs(STATUS_LOADING) < 20)
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*15, "Init CARD... Done!");
|
|
GRRLIB_Screen2Texture(0, 0, screen_buffer, GX_FALSE); // Copy all status messages
|
|
GRRLIB_Render();
|
|
ClearScreen();
|
|
}
|
|
while((STATUS_LOADING < -1) && (STATUS_LOADING > -20)) //displaying a fatal error
|
|
; //do nothing wait for shutdown
|
|
}
|
|
if(argsboot == false)
|
|
{
|
|
DrawBuffer(); // Draw all status messages
|
|
PrintFormat(DEFAULT_SIZE, BLACK, MENU_POS_X, MENU_POS_Y + 20*17, "Nintendont kernel looping, loading game...");
|
|
GRRLIB_Render();
|
|
DrawBuffer(); // Draw all status messages
|
|
}
|
|
// memcpy( (void*)0x80000000, (void*)0x90140000, 0x1200000 );
|
|
GRRLIB_FreeTexture(background);
|
|
GRRLIB_FreeTexture(screen_buffer);
|
|
GRRLIB_FreeTTF(myFont);
|
|
GRRLIB_Exit();
|
|
|
|
gprintf("GameRegion:");
|
|
|
|
u32 vidForce = (ncfg->VideoMode & NIN_VID_FORCE);
|
|
u32 vidForceMode = (ncfg->VideoMode & NIN_VID_FORCE_MASK);
|
|
|
|
progressive = (ncfg->Config & NIN_CFG_FORCE_PROG)
|
|
&& !useipl && !useipltri;
|
|
|
|
switch (BI2region)
|
|
{
|
|
case BI2_REGION_PAL:
|
|
if (progressive || (vidForce &&
|
|
(vidForceMode == NIN_VID_FORCE_PAL60 ||
|
|
vidForceMode == NIN_VID_FORCE_MPAL ||
|
|
vidForceMode == NIN_VID_FORCE_NTSC)))
|
|
{
|
|
// PAL60 and/or PAL-M
|
|
*(vu32*)0x800000CC = 5;
|
|
}
|
|
else
|
|
{
|
|
// PAL50
|
|
*(vu32*)0x800000CC = 1;
|
|
}
|
|
vmode = &TVPal528IntDf;
|
|
break;
|
|
|
|
case BI2_REGION_USA:
|
|
if ((vidForce && vidForceMode == NIN_VID_FORCE_MPAL) ||
|
|
(!vidForce && CONF_GetVideo() == CONF_VIDEO_MPAL))
|
|
{
|
|
// PAL-M
|
|
*(vu32*)0x800000CC = 3;
|
|
vmode = &TVMpal480IntDf;
|
|
}
|
|
else
|
|
{
|
|
// NTSC
|
|
*(vu32*)0x800000CC = 0;
|
|
vmode = &TVNtsc480IntDf;
|
|
}
|
|
break;
|
|
|
|
case BI2_REGION_JAPAN:
|
|
case BI2_REGION_SOUTH_KOREA:
|
|
default:
|
|
// NTSC
|
|
*(vu32*)0x800000CC = 0;
|
|
vmode = &TVNtsc480IntDf;
|
|
break;
|
|
}
|
|
|
|
if(progressive)
|
|
vmode = &TVNtsc480Prog;
|
|
else if(vidForce)
|
|
{
|
|
switch(vidForceMode)
|
|
{
|
|
case NIN_VID_FORCE_PAL50:
|
|
vmode = &TVPal528IntDf;
|
|
break;
|
|
case NIN_VID_FORCE_PAL60:
|
|
vmode = &TVEurgb60Hz480IntDf;
|
|
break;
|
|
case NIN_VID_FORCE_MPAL:
|
|
vmode = &TVMpal480IntDf;
|
|
break;
|
|
case NIN_VID_FORCE_NTSC:
|
|
default:
|
|
vmode = &TVNtsc480IntDf;
|
|
break;
|
|
}
|
|
}
|
|
if((ncfg->Config & NIN_CFG_MEMCARDEMU) == 0) //setup real sram video
|
|
{
|
|
syssram *sram;
|
|
sram = __SYS_LockSram();
|
|
sram->display_offsetH = 0; // Clear Offset
|
|
sram->flags &= ~0x80; // Clear Progmode
|
|
sram->flags &= ~3; // Clear Videomode
|
|
|
|
// PAL60 flag.
|
|
if (BI2region == BI2_REGION_PAL)
|
|
{
|
|
// Enable PAL60.
|
|
sram->ntd |= 0x40;
|
|
|
|
// TODO: Set the progressive scan flag on PAL?
|
|
}
|
|
else
|
|
{
|
|
// Disable PAL60.
|
|
sram->ntd &= 0x40;
|
|
|
|
// NTSC Prince of Persia Warrior Within set to Spanish
|
|
// actually has a bug that cant display the progressive
|
|
// screen question so dont set progressive flag in that
|
|
bool spPopWW = (ncfg->GameID == 0x47324F45 &&
|
|
ncfg->Language == NIN_LAN_SPANISH);
|
|
|
|
// Set the progressive scan flag if a component cable
|
|
// is connected (or HDMI on Wii U), unless we're loading
|
|
// BMX XXX, since that game won't even boot on a real
|
|
// GameCube if a component cable is connected.
|
|
if ((ncfg->GameID >> 8) != 0x474233 && !spPopWW &&
|
|
(ncfg->VideoMode & NIN_VID_PROG))
|
|
{
|
|
sram->flags |= 0x80;
|
|
}
|
|
}
|
|
|
|
if(*(vu32*)0x800000CC == 1 || *(vu32*)0x800000CC == 5)
|
|
sram->flags |= 1; //PAL Video Mode
|
|
__SYS_UnlockSram(1); // 1 -> write changes
|
|
while(!__SYS_SyncSram());
|
|
}
|
|
|
|
ReconfigVideo(vmode);
|
|
VIDEO_SetBlack(FALSE);
|
|
VIDEO_Flush();
|
|
VIDEO_WaitVSync();
|
|
if(vmode->viTVMode & VI_NON_INTERLACE)
|
|
VIDEO_WaitVSync();
|
|
else while(VIDEO_GetNextField())
|
|
VIDEO_WaitVSync();
|
|
|
|
*(u16*)(0xCC00501A) = 156; // DSP refresh rate
|
|
/* from libogc, get all gc pads to work */
|
|
u32 buf[2];
|
|
u32 bits = 0;
|
|
u8 chan;
|
|
for(chan = 0; chan < 4; ++chan)
|
|
{
|
|
bits |= (0x80000000>>chan);
|
|
SI_GetResponse(chan,(void*)buf);
|
|
SI_SetCommand(chan,(0x00000300|0x00400000));
|
|
SI_EnablePolling(bits);
|
|
}
|
|
*(vu32*)0xD3003004 = 1; //ready up HID Thread
|
|
|
|
VIDEO_SetBlack(TRUE);
|
|
VIDEO_Flush();
|
|
VIDEO_WaitVSync();
|
|
if(vmode->viTVMode & VI_NON_INTERLACE)
|
|
VIDEO_WaitVSync();
|
|
else while(VIDEO_GetNextField())
|
|
VIDEO_WaitVSync();
|
|
GX_AbortFrame();
|
|
|
|
DCFlushRange((void*)0x93000000, 0x3000);
|
|
|
|
DCInvalidateRange((void*)0x93010010, 0x10000);
|
|
memcpy((void*)0x93010010, loader_stub, 0x1800);
|
|
memcpy((void*)0x93011810, stub_bin, stub_bin_size);
|
|
DCFlushRange((void*)0x93010010, 0x10000);
|
|
|
|
DCInvalidateRange((void*)0x93020000, 0x10000);
|
|
memset((void*)0x93020000, 0, 0x10000);
|
|
DCFlushRange((void*)0x93020000, 0x10000);
|
|
|
|
DCInvalidateRange((void*)0x93003000, 0x200);
|
|
//*(vu32*)0x93003000 = currev; //set kernel rev (now in LoadKernel)
|
|
*(vu32*)0x93003008 = 0x80000004; //just some address for SIGetType
|
|
//0x9300300C is already used for multi-iso
|
|
memset((void*)0x93003010, 0, 0x190); //clears alot of pad stuff
|
|
strcpy((char*)0x930031A0, "ARStartDMA: %08x %08x %08x\n"); //ARStartDMA Debug
|
|
memset((void*)0x930031E0, 0, 0x20); //clears tgc stuff
|
|
DCFlushRange((void*)0x93003000, 0x200);
|
|
|
|
//lets prevent weird events
|
|
__STM_Close();
|
|
|
|
//THIS thing right here, it interrupts some games and breaks them
|
|
//To fix that, call ExecSuspendScheduler so WC24 just sleeps
|
|
u32 out = 0;
|
|
fd = IOS_Open("/dev/net/kd/request", 0);
|
|
IOS_Ioctl(fd, IOCTL_ExecSuspendScheduler, NULL, 0, &out, 4);
|
|
IOS_Close(fd);
|
|
|
|
write16(0xD8B420A, 0); //disable MEMPROT again after reload
|
|
//u32 level = IRQ_Disable();
|
|
__exception_closeall();
|
|
__lwp_thread_closeall();
|
|
|
|
DVDStartCache(); //waits for kernel start
|
|
DCInvalidateRange((void*)0x90000000, 0x1000000);
|
|
memset((void*)(void*)0x90000000, 0, 0x1000000); //clear ARAM
|
|
DCFlushRange((void*)0x90000000, 0x1000000);
|
|
|
|
gprintf("Game Start\n");
|
|
//alow interrupts on Y2
|
|
write32(0x0d000004,0x22);
|
|
if(useipl)
|
|
{
|
|
load_ipl(iplbuf);
|
|
*(vu32*)0xD3003420 = 0x5DEA;
|
|
while(*(vu32*)0xD3003420 == 0x5DEA) ;
|
|
/* Patches */
|
|
DCInvalidateRange((void*)0x80001000, 0x2000);
|
|
ICInvalidateRange((void*)0x80001000, 0x2000);
|
|
/* IPL */
|
|
DCInvalidateRange((void*)0x81300000, 0x300000);
|
|
ICInvalidateRange((void*)0x81300000, 0x300000);
|
|
/* Seems to boot more stable this way */
|
|
//gprintf("Using 32kHz DSP (No Resample)\n");
|
|
write32(0xCD806C00, 0x68);
|
|
free(iplbuf);
|
|
}
|
|
else //use our own loader
|
|
{
|
|
if(useipltri)
|
|
{
|
|
*(vu32*)0xD3003420 = 0x6DEA;
|
|
while(*(vu32*)0xD3003420 == 0x6DEA) ;
|
|
}
|
|
memcpy((void*)0x81300000, multidol_ldr_bin, multidol_ldr_bin_size);
|
|
DCFlushRange((void*)0x81300000, multidol_ldr_bin_size);
|
|
ICInvalidateRange((void*)0x81300000, multidol_ldr_bin_size);
|
|
}
|
|
_jmp813();
|
|
|
|
//IRQ_Restore(level);
|
|
|
|
return 0;
|
|
}
|