mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-29 06:34:17 +01:00
489 lines
13 KiB
C++
489 lines
13 KiB
C++
/****************************************************************************
|
|
* Copyright (C) 2015 Cyan
|
|
*
|
|
* 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, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 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, see <http://www.gnu.org/licenses/>.
|
|
****************************************************************************/
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <malloc.h>
|
|
#include <gccore.h>
|
|
#include <ogc/machine/processor.h>
|
|
|
|
#include "neek.hpp"
|
|
#include "memory/mem2.h"
|
|
#include "Controls/DeviceHandler.hpp"
|
|
#include "FileOperations/fileops.h"
|
|
#include "settings/CSettings.h"
|
|
#include "sys.h"
|
|
#include "gecko.h"
|
|
|
|
/* GCC 11 false positives */
|
|
#if __GNUC__ > 10
|
|
#pragma GCC diagnostic ignored "-Wstringop-overread"
|
|
#endif
|
|
|
|
typedef struct {
|
|
u32 hdrsize;
|
|
u32 loadersize;
|
|
u32 elfsize;
|
|
u32 argument;
|
|
} ioshdr;
|
|
|
|
#define MEM_REG_BASE 0xd8b4000
|
|
#define MEM_PROT (MEM_REG_BASE + 0x20a)
|
|
#define TITLE_ID(x,y) (((u64)(x) << 32) | (y))
|
|
|
|
static u32 *kernel = NULL;
|
|
|
|
static inline void disable_memory_protection(void) {
|
|
write32(MEM_PROT, read32(MEM_PROT) & 0x0000FFFF);
|
|
}
|
|
|
|
bool neekLoadKernel (const char* nandpath)
|
|
{
|
|
gprintf( "NEEK: Loading Kernel.bin... ");
|
|
char kernelPath[30];
|
|
if(isWiiU())
|
|
snprintf(kernelPath, sizeof(kernelPath), "%s:/sneek/vwiikernel.bin", DeviceHandler::GetDevicePrefix(nandpath));
|
|
if(!isWiiU() || !CheckFile(kernelPath))
|
|
snprintf(kernelPath, sizeof(kernelPath), "%s:/sneek/kernel.bin", DeviceHandler::GetDevicePrefix(nandpath));
|
|
if(!CheckFile(kernelPath))
|
|
{
|
|
gprintf("File not found.\n");
|
|
return false;
|
|
}
|
|
|
|
FILE *f = NULL;
|
|
f = fopen(kernelPath, "rb");
|
|
if(!f)
|
|
{
|
|
gprintf("Failed loading file %s.\n", kernelPath);
|
|
return false;
|
|
}
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
long fsize = ftell(f);
|
|
rewind(f);
|
|
|
|
// Allocate kernel to mem2
|
|
kernel = (u32 *) MEM2_alloc(fsize);
|
|
if(!kernel)
|
|
{
|
|
fclose(f);
|
|
return false;
|
|
}
|
|
|
|
fread(kernel, 1, fsize, f);
|
|
//((ioshdr*)kernel)->argument = 0x42; // set argument size
|
|
DCFlushRange(kernel, fsize);
|
|
|
|
gprintf("Loaded to 0x%08x, size: %d\n", kernel, fsize);
|
|
gprintf("NEEK: offset memory address: %08x\n", (u32)kernel - 0x80000000); // offset
|
|
|
|
fclose(f);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
static void neekClearKernel(void)
|
|
{
|
|
if(kernel)
|
|
MEM2_free(kernel);
|
|
kernel = NULL;
|
|
}
|
|
*/
|
|
|
|
int neekBoot(void)
|
|
{
|
|
gprintf("Booting S/Uneek !!\n");
|
|
if(kernel == NULL)
|
|
{
|
|
gprintf("Kernel not loaded !! Exiting...\n");
|
|
return -1;
|
|
}
|
|
|
|
/** boot mini without BootMii IOS code by Crediar. **/
|
|
|
|
disable_memory_protection();
|
|
unsigned int i = 0x939F02F0;
|
|
unsigned char ES_ImportBoot2[16] =
|
|
{ 0x68, 0x4B, 0x2B, 0x06, 0xD1, 0x0C, 0x68, 0x8B, 0x2B, 0x00, 0xD1, 0x09, 0x68, 0xC8, 0x68, 0x42 };
|
|
|
|
if( memcmp( (void*)(i), ES_ImportBoot2, sizeof(ES_ImportBoot2) ) != 0 )
|
|
for( i = 0x939F0000; i < 0x939FE000; i+=4 )
|
|
if( memcmp( (void*)(i), ES_ImportBoot2, sizeof(ES_ImportBoot2) ) == 0 )
|
|
break;
|
|
|
|
if(i >= 0x939FE000)
|
|
{
|
|
gprintf("ES_ImportBoot2 not patched !! Exiting...\n");
|
|
//SYS_ResetSystem( SYS_RETURNTOMENU, 0, 0 );
|
|
return -1;
|
|
}
|
|
|
|
DCInvalidateRange( (void*)i, 0x20 );
|
|
|
|
*(vu32*)(i+0x00) = 0x48034904; // LDR R0, 0x10, LDR R1, 0x14
|
|
*(vu32*)(i+0x04) = 0x477846C0; // BX PC, NOP
|
|
*(vu32*)(i+0x08) = 0xE6000870; // SYSCALL
|
|
*(vu32*)(i+0x0C) = 0xE12FFF1E; // BLR
|
|
//*(vu32*)(i+0x10) = 0x11000000; // kernel offset from 0x80000000. Kernel loaded to (void *)0x91000000
|
|
*(vu32*)(i+0x10) = (u32)kernel - 0x80000000; // kernel offset
|
|
*(vu32*)(i+0x14) = 0x0000FF01; // version
|
|
|
|
DCFlushRange( (void*)i, 0x20 );
|
|
__IOS_ShutdownSubsystems();
|
|
|
|
s32 fd = IOS_Open( "/dev/es", 0 );
|
|
|
|
u8 *buffer = (u8*)memalign( 32, 0x100 );
|
|
memset( buffer, 0, 0x100 );
|
|
|
|
IOS_IoctlvAsync( fd, 0x1F, 0, 0, (ioctlv*)buffer, NULL, NULL );
|
|
return 0;
|
|
|
|
}
|
|
|
|
/****************************************************************************
|
|
* neekIsNeek2o
|
|
*
|
|
* Detects Kernel.bin format
|
|
*
|
|
* @return values :
|
|
* -1 : kernel.bin not found
|
|
* 0 : sneek or uneek kernel.bin
|
|
* 1 : sneek2o or uneek2o kernel.bin
|
|
***************************************************************************/
|
|
int neekIsNeek2o(const char* nandpath)
|
|
{
|
|
int found = 0;
|
|
char tempPath[100];
|
|
if(isWiiU())
|
|
snprintf(tempPath, sizeof(tempPath), "%s:/sneek/vwiikernel.bin", DeviceHandler::GetDevicePrefix(nandpath));
|
|
|
|
if(!isWiiU() || !CheckFile(tempPath))
|
|
snprintf(tempPath, sizeof(tempPath), "%s:/sneek/kernel.bin", DeviceHandler::GetDevicePrefix(nandpath));
|
|
|
|
if(!CheckFile(tempPath))
|
|
return -1;
|
|
|
|
u8 *buffer = NULL;
|
|
u32 filesize = 0;
|
|
const char* str = "NEEK2O";
|
|
if(LoadFileToMem(tempPath, &buffer, &filesize))
|
|
{
|
|
for(u32 i = filesize-strlen(str); i > 0; i--)
|
|
{
|
|
if( memcmp(buffer+i, str, strlen(str)) == 0)
|
|
{
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
return -1;
|
|
|
|
if(buffer)
|
|
free(buffer);
|
|
|
|
return found;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* neekPathFormat
|
|
*
|
|
* Convert and trim full path to path format used by neek
|
|
***************************************************************************/
|
|
int neekPathFormat(char* nandpath_out, const char* nandpath_in, u32 len)
|
|
{
|
|
const char* neekNandPathTemp = strchr(nandpath_in, '/');
|
|
if(!neekNandPathTemp)
|
|
return -1;
|
|
|
|
snprintf(nandpath_out, len, "%s", neekNandPathTemp);
|
|
|
|
if(nandpath_out[strlen(nandpath_out)-1] == '/')
|
|
*(strrchr(nandpath_out, '/')) = '\0'; // remove trailing slash
|
|
|
|
return 1;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* neek2oSetBootSettings
|
|
*
|
|
* Generate the neek2o autoboot settings
|
|
*
|
|
* neek_config: Pointer to cfg
|
|
* TitleID : Full path to Channel (00010001 + ABCD) or ID4 for Wii/GC games (00000000 + ID)
|
|
* Magic : 0x0 for channel, 0x5d1c9ea3 for Wii game, 0xC2339F3D for gamecube and quadforce games
|
|
* returnTo : true/false to return to another neek channel instead of system menu
|
|
* nandpath : Set a different temporary Nand Path to use (full path to nand)
|
|
*
|
|
***************************************************************************/
|
|
bool neek2oSetBootSettings(NEEK_CFG* neek_config, u64 TitleID, u32 Magic, u64 returnto, const char* nandpath )
|
|
{
|
|
if(!neek_config)
|
|
return false;
|
|
|
|
char tmpPath[100];
|
|
memset(neek_config, 0, sizeof(NEEK_CFG));
|
|
|
|
// Magic and version for DML
|
|
neek_config->magic = NEEK_MAGIC;
|
|
|
|
// GameID
|
|
if(!Magic)
|
|
neek_config->titleid = TitleID; // Set channel ID
|
|
else
|
|
{
|
|
neek_config->gameid = (u32)TitleID; // Wii or Gamecube title ID4 to autoboot
|
|
neek_config->gamemagic = Magic; // set to 0x5d1c9ea3 for Wii game, 0xC2339F3D for gamecube and quadforce games
|
|
neek_config->config |= NCON_EXT_BOOT_GAME ; // set Disc booting
|
|
}
|
|
|
|
// Return to
|
|
if(returnto)
|
|
{
|
|
// check if NK2O is installed
|
|
snprintf(tmpPath, sizeof(tmpPath), "%s/title/00010001/4e4b324f/content/title.tmd", nandpath != NULL ? nandpath : Settings.NandEmuChanPath);
|
|
if(CheckFile(tmpPath))
|
|
{
|
|
neek_config->returnto = TITLE_ID(0x00010001, 'NK2O'); // Currently forced to NK2O user channel
|
|
neek_config->config |= NCON_EXT_RETURN_TO; // enable "return to" patch
|
|
}
|
|
|
|
if(isWiiU())
|
|
{
|
|
neek_config->returnto = TITLE_ID(0x00010002, 'HCVA');// Currently forced to "Return to WiiU" system channel
|
|
neek_config->config |= NCON_EXT_RETURN_TO; // enable "return to" patch
|
|
}
|
|
}
|
|
|
|
if(!(neek_config->config & NCON_EXT_RETURN_TO))
|
|
{
|
|
// delete residual "return to" file if last shutdown was unclean.
|
|
snprintf(tmpPath, sizeof(tmpPath), "%s:/sneek/reload.sys", DeviceHandler::GetDevicePrefix(nandpath));
|
|
if(CheckFile(tmpPath))
|
|
RemoveFile(tmpPath);
|
|
}
|
|
|
|
if(nandpath && strlen(nandpath) > 0)
|
|
{
|
|
// ensure path is correct format
|
|
char neekNandPath[256] = "";
|
|
if(neekPathFormat(neekNandPath, nandpath, sizeof(neekNandPath)))
|
|
{
|
|
snprintf(neek_config->nandpath, sizeof(neek_config->nandpath), "%s", neekNandPath);
|
|
neek_config->config |= NCON_EXT_NAND_PATH ; // Use custom nand path
|
|
// neek_config->config |= NCON_HIDE_EXT_PATH; // Set custom nand path as temporary
|
|
}
|
|
}
|
|
|
|
//set a custom di folder
|
|
//snprintf(neek_config->dipath, sizeof(neek_config->dipath), "/sneek/vwii"); // Set path for di.bin and diconfig.bin
|
|
//neek_config->config |= NCON_EXT_DI_PATH; // Use custom di path
|
|
|
|
DCFlushRange(neek_config, sizeof(NEEK_CFG));
|
|
|
|
//hexdump(neek_config, sizeof(NEEK_CFG));
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
* neek2oSetNAND
|
|
*
|
|
* Generates nandcfg.bin if missing and adds missing EmuNAND path
|
|
* Sets default EmuNAND path for neek2o
|
|
*
|
|
* @return values :
|
|
* -1 : error
|
|
* x : Default path set to EmuNAND number x
|
|
***************************************************************************/
|
|
int neek2oSetNAND(const char* nandpath)
|
|
{
|
|
// format path string for neek
|
|
char neekNandPath[256] = "";
|
|
if(neekPathFormat(neekNandPath, nandpath, sizeof(neekNandPath)) < 0)
|
|
return -1;
|
|
|
|
FILE *f = NULL;
|
|
u32 ret = -1;
|
|
bool found = false;
|
|
u32 i = 0;
|
|
|
|
char nandconfigPath[100];
|
|
snprintf(nandconfigPath, sizeof(nandconfigPath), "%s:/sneek/nandcfg.bin", DeviceHandler::GetDevicePrefix(nandpath));
|
|
|
|
// vWii neek2o - different filename but not the same format?
|
|
//if(isWiiU())
|
|
// snprintf(nandconfigPath, sizeof(nandconfigPath), "%s:/sneek/vwiincfg.bin", DeviceHandler::GetDevicePrefix(nandpath));
|
|
|
|
#ifdef DEBUG
|
|
gprintf("nandconfigPath : %s\n", nandconfigPath);
|
|
#endif
|
|
|
|
// create the file if it doesn't exist
|
|
if(!CheckFile(nandconfigPath) || FileSize(nandconfigPath) < NANDCONFIG_HEADER_SIZE+1)
|
|
{
|
|
u8* nandConfigHeader[NANDCONFIG_HEADER_SIZE] = {};
|
|
|
|
f = fopen(nandconfigPath, "wb");
|
|
if(!f)
|
|
{
|
|
gprintf("Failed creating file %s.\n", nandconfigPath);
|
|
return -1;
|
|
}
|
|
|
|
// create an empty header with 0 NAND
|
|
fwrite(nandConfigHeader, 1, NANDCONFIG_HEADER_SIZE, f);
|
|
fclose(f);
|
|
}
|
|
|
|
f = fopen(nandconfigPath, "rb");
|
|
if(!f)
|
|
{
|
|
gprintf("Failed loading file %s.\n", nandconfigPath);
|
|
return -1;
|
|
}
|
|
|
|
fseek(f , 0 , SEEK_END);
|
|
u32 filesize = ftell(f);
|
|
rewind(f);
|
|
|
|
/* Allocate memory */
|
|
NandConfig *nandCfg = (NandConfig *) MEM2_alloc(filesize);
|
|
if (!nandCfg)
|
|
{
|
|
fclose (f);
|
|
return -1;
|
|
}
|
|
|
|
// Read the file
|
|
ret = fread (nandCfg, 1, filesize, f);
|
|
if(ret != filesize)
|
|
{
|
|
gprintf("Failed loading file %s to Mem.\n", nandconfigPath);
|
|
fclose (f);
|
|
MEM2_free(nandCfg);
|
|
return -1;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
hexdump(nandCfg, NANDCONFIG_HEADER_SIZE);
|
|
#endif
|
|
|
|
// don't parse if wrong file
|
|
if(nandCfg->NandCnt > NANDCONFIG_MAXNAND || nandCfg->NandSel > nandCfg->NandCnt) // wrong header, delete file.
|
|
{
|
|
fclose(f);
|
|
MEM2_free(nandCfg);
|
|
RemoveFile(nandconfigPath); // delete corrupted file.
|
|
return -1;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// List found nands from the file
|
|
gprintf("NandCnt = %d\n", nandCfg->NandCnt);
|
|
gprintf("NandSel = %d\n", nandCfg->NandSel);
|
|
for( i = 0 ; i < nandCfg->NandCnt ; i++)
|
|
{
|
|
gprintf("Path %d = %s %s\n", i, &nandCfg->Nands[i], strcmp((const char *)&nandCfg->Nands[i], neekNandPath) == 0 ? "found" : "");
|
|
}
|
|
#endif
|
|
|
|
for( i = 0 ; i < nandCfg->NandCnt ; i++)
|
|
{
|
|
if(strcmp((const char *)&nandCfg->Nands[i], neekNandPath) == 0)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(found) // NAND path already present in nandcfg.bin
|
|
{
|
|
// set selected nand in header if different
|
|
if(nandCfg->NandSel != i)
|
|
{
|
|
nandCfg->NandSel = i;
|
|
nandCfg->Padding1 = i; // same value?
|
|
DCFlushRange(nandCfg, NANDCONFIG_HEADER_SIZE);
|
|
#ifdef DEBUG
|
|
gprintf("new nandCfg->sel = %d", nandCfg->NandSel);
|
|
hexdump(nandCfg, NANDCONFIG_HEADER_SIZE);
|
|
#endif
|
|
freopen(nandconfigPath, "wb", f);
|
|
ret = fwrite(nandCfg, sizeof(char), filesize, f); // Write full file
|
|
}
|
|
}
|
|
else // new NAND path to nandcfg.bin
|
|
{
|
|
NandInfo * newNand = (NandInfo *) MEM2_alloc(sizeof(NandInfo));
|
|
if(newNand)
|
|
{
|
|
memset(newNand, 0, sizeof(NandInfo));
|
|
snprintf(newNand->Path, sizeof(newNand->Path), neekNandPath);
|
|
snprintf(newNand->Name, sizeof(newNand->Name), strlen(neekNandPath) == 0 ? "root" : strrchr(neekNandPath, '/')+1);
|
|
snprintf(newNand->DiPath, sizeof(newNand->DiPath), "/sneek");
|
|
DCFlushRange(newNand, sizeof(NandInfo));
|
|
#ifdef DEBUG
|
|
gprintf("new nandCfg");
|
|
hexdump(newNand, sizeof(NandInfo));
|
|
#endif
|
|
nandCfg->NandCnt++;
|
|
nandCfg->NandSel = ++i;
|
|
|
|
// prevent NAND selection bigger than number of NANDs.
|
|
if(nandCfg->NandSel >= nandCfg->NandCnt)
|
|
{
|
|
nandCfg->NandSel = nandCfg->NandCnt -1;
|
|
i--;
|
|
}
|
|
|
|
freopen(nandconfigPath, "wb", f);
|
|
ret = fwrite(nandCfg, sizeof(char), filesize, f); // Write full file
|
|
ret = fwrite(newNand,1,sizeof(NandInfo),f); // append new NANDInfo
|
|
if(ret != sizeof(NandInfo))
|
|
gprintf("Writing new NAND info failed\n");
|
|
|
|
MEM2_free(newNand);
|
|
}
|
|
}
|
|
|
|
// verify the header is correctly written
|
|
freopen(nandconfigPath, "rb", f);
|
|
ret = fread (nandCfg, 1, NANDCONFIG_HEADER_SIZE, f);
|
|
if(ret != NANDCONFIG_HEADER_SIZE)
|
|
{
|
|
gprintf("Failed loading file %s to Mem.\n", nandconfigPath);
|
|
fclose (f);
|
|
MEM2_free(nandCfg);
|
|
return -1;
|
|
}
|
|
ret = nandCfg->NandSel;
|
|
fclose (f);
|
|
MEM2_free(nandCfg);
|
|
#ifdef DEBUG
|
|
gprintf("verify header:\n");
|
|
hexdump(nandCfg, NANDCONFIG_HEADER_SIZE);
|
|
#endif
|
|
if(ret == i)
|
|
return ret ;
|
|
|
|
return -1;
|
|
}
|