/**************************************************************************** * 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 . ****************************************************************************/ #include #include #include #include #include #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; }