/*------------------------------------------------------------- uneek_fs.c -- USB mass storage support, using a modified uneek fs-usb rev. 1.00 first draft rev. 1.01 second draft added detection and support for SDHC access in SNEEK added check_uneek_fs_type function to see if it's sneek or uneek rev. 1.02 third draft added the UNEEK_FS_BOTH option to the init_uneek_fs function. when the parameter is set, both sd and usb access are rerouted to the sd card. it can only be used with sneek and could serve programs like Joyflow on a sd only setup. the exit_uneek_fs function needs to be called when that setup is used. since I can't disable usb, not knowing if sd access is still needed, and I can't disable sd if usb access if sd is still needed. It's probably exceptional that one will be shutdown and the other still used, but it's better to stay on the safe area. rev. 1.04 fourth draft. shutdown function is now a stub as some programs call it before they end to force a remount exit_uneek_fs added to properly shutdown the uneek_usb_fs file system. max_write_sectors increased to speedup things. Transfer speed from wiixplorer gone up from 20KB/s to 265KB/s. rev. 1.05 fifth draft added "is_uneek" function rev. 1.06 sixth draft added "is_neek2" and "is_neek3" functions Crediar changed the boot2 version back from 5 to 4. The is_uneek function will not detect those neek versions anymore. Stfour created the is_neek2 method to detect if neek is running. JoostinOnline and GiantPune created the is_neek3 which is a little less code. So, you should use either is_neek2 or is_neek3 rev. 1.07 seventh draft added "is_neek4" function Dj_Skual created the is_neek4 method to detect if neek is running. added WII_launch_Channel. It's not really uneek_fs related, but it could be handy Copyright (C) 2011 Obcd This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------*/ #include #include #include #include #include #include //#include //#include //#include //#define SHOW_GECKO_DEBUG 1 #ifdef SHOW_GECKO_DEBUG #include "gecko.h" #endif #define MAX_READ_SECTORS 16 //yet to be determined how much is allowed? #define MAX_WRITE_SECTORS 16 //yet to be determined how much is allowed? #define CACHE_SECTOR_LOCATION 1 //not sure if it will improve speed much... #define UNEEK_FS_NONE 0 #define UNEEK_FS_USB 1 #define UNEEK_FS_SD 2 #define UNEEK_FS_BOTH 4 #define ISFS_OPEN_ALL 64 #define DEVICE_TYPE_WII_SD (('W'<<24)|('I'<<16)|('S'<<8)|'D') //Not yet implemented as most disk io buffers don't seem have proper alignment //It would require a more serious modification in the code (aligning those buffers) //#define USE_ORIGINAL_ALIGNED_BUF 0 //if the buffer is aligned already...why not? //This one triggers the unique functions in the uneek fs //Don't try to change it. //If you really need to access that file with ISFS, access it in read/write mode //As only read mode will trigger uneek fs static char _dev_obcd[] ATTRIBUTE_ALIGN(32) = "/sneek/obcd.txt"; static s32 fu = -1; // static bool uneek_fs_ok = false; static s32 uneek_fs_type = UNEEK_FS_NONE; s32 confirm_neek1 = 5; s32 confirm_neek2 = 5; s32 confirm_neek3 = 5; s32 confirm_neek4 = 5; #ifdef CACHE_SECTOR_LOCATION static u32 seek_cache; #endif // DISC_INTERFACE __io_usbstorage; DISC_INTERFACE __io_wiisd; extern DISC_INTERFACE __io_usbstorage; //extern DISC_INTERFACE __io_usb2storage; // DISC_INTERFACE methods static bool __io_uns_IsInserted(void) { return true; } static bool __io_uns_Startup(void) { return true; } int usb_verbose = 0; bool __io_uns_ReadSectors(u32 sector, u32 count, void *buffer) { u8* buf; s32 whence; u32 done; u32 sec; u32 amount; u8* resultbuf; done = 0; sec = sector; if (count <= MAX_READ_SECTORS) { amount = count; } else { amount = MAX_READ_SECTORS; } // buf = (u8 *)memalign(32, 512 * amount); // sdhc aligns it's buffers on 64 byte boundaries buf = (u8 *)memalign(64, 512 * amount); while(done < count) { whence = 0; if ((sec & 0x80000000)!= 0) { sec &= 0x7fffffff; whence = 1; } #ifdef CACHE_SECTOR_LOCATION if ((sector + done) != seek_cache) { #endif ISFS_Seek(fu,sec,whence); #ifdef CACHE_SECTOR_LOCATION seek_cache = sector + done; } #endif s32 ret = ISFS_Read(fu,buf,512*amount); if (ret == (s32)(512*amount)) { resultbuf = (u8*)buffer + (done * 512); memcpy(resultbuf,buf,512*amount); done+=amount; sec = sector + done; #ifdef CACHE_SECTOR_LOCATION seek_cache = sector + done; #endif if((count-done)<=MAX_READ_SECTORS) { amount = count - done; } else { amount = MAX_READ_SECTORS; } } else { #ifdef CACHE_SECTOR_LOCATION seek_cache = 0xfffffff8; #endif return false; } } free(buf); if (usb_verbose) { printf("usb-r: %x [%d]\n", sector, count); //sleep(1); } //printf("ret = %x\n",ret); return true; } bool __io_uns_WriteSectors(u32 sector, u32 count, void *buffer) { u8* buf; s32 whence; u32 done; u32 sec; u32 amount; u8* resultbuf; done = 0; sec = sector; if (count <= MAX_WRITE_SECTORS) { amount = count; } else { amount = MAX_WRITE_SECTORS; } // buf = (u8 *)memalign(32, 512 * amount); // sdhc aligns it's buffers on 64 byte boundaries buf = (u8 *)memalign(64, 512 * amount); while(done < count) { whence = 0; if ((sec & 0x80000000)!= 0) { sec &= 0x7fffffff; whence = 1; } #ifdef CACHE_SECTOR_LOCATION if ((sector + done) != seek_cache) { #endif ISFS_Seek(fu,sec,whence); #ifdef CACHE_SECTOR_LOCATION seek_cache = sector + done; } #endif resultbuf = (u8*)buffer + (done * 512); memcpy(buf,resultbuf,512*amount); s32 ret = ISFS_Write(fu,buf,512*amount); if (ret == (s32)(512*amount)) { done+=amount; sec = sector + done; #ifdef CACHE_SECTOR_LOCATION seek_cache = sector + done; #endif if((count-done)<=MAX_WRITE_SECTORS) { amount = count - done; } else { amount = MAX_WRITE_SECTORS; } } else { #ifdef CACHE_SECTOR_LOCATION seek_cache = 0xfffffff8; #endif return false; } } free(buf); if (usb_verbose) { printf("usb-r: %x [%d]\n", sector, count); //sleep(1); } return true; } static bool __io_uns_ClearStatus(void) { return true; } static bool __io_uns_Shutdown(void) { return true; /* if ((uneek_fs_type != UNEEK_FS_NONE)&&(uneek_fs_type != UNEEK_FS_BOTH)) { ISFS_Close(fu); ISFS_Deinitialize(); uneek_fs_type = UNEEK_FS_NONE; } // do nothing return true; */ } static bool __io_uns_NOP(void) { // do nothing return true; } bool init_uneek_fs(u32 mode) { u8* buf = NULL; struct _fstats* pstat = NULL; //struct _fstats stat; if (uneek_fs_type == UNEEK_FS_NONE){ ISFS_Initialize(); fu = ISFS_Open(_dev_obcd, ISFS_OPEN_READ); buf = (u8*)memalign(32, sizeof(struct _fstats)); pstat = (struct _fstats*)buf; pstat->file_length = 0; pstat->file_pos = 0; if(fu >= 0) { ISFS_GetFileStats(fu,pstat); //printf("Filestat length returned %x\n",pstat->file_length); //printf("Filestat pos returned %x\n",pstat->file_pos); } if ((pstat->file_length == 0xfffffff0)&&(pstat->file_pos == 0xfffffff8)&&(fu>=0)) { #ifdef CACHE_SECTOR_LOCATION seek_cache = 0xfffffff8; #endif if (mode & ISFS_OPEN_WRITE) { __io_usbstorage.ioType = DEVICE_TYPE_WII_USB; __io_usbstorage.features = FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB; __io_usbstorage.startup = (FN_MEDIUM_STARTUP)&__io_uns_Startup; __io_usbstorage.isInserted = (FN_MEDIUM_ISINSERTED)&__io_uns_IsInserted; __io_usbstorage.readSectors = (FN_MEDIUM_READSECTORS)&__io_uns_ReadSectors; __io_usbstorage.writeSectors = (FN_MEDIUM_WRITESECTORS)&__io_uns_WriteSectors; __io_usbstorage.clearStatus = (FN_MEDIUM_CLEARSTATUS)&__io_uns_ClearStatus; __io_usbstorage.shutdown = (FN_MEDIUM_SHUTDOWN)&__io_uns_Shutdown; } else { __io_usbstorage.ioType = DEVICE_TYPE_WII_USB; __io_usbstorage.features = FEATURE_MEDIUM_CANREAD | FEATURE_WII_USB; __io_usbstorage.startup = (FN_MEDIUM_STARTUP)&__io_uns_Startup; __io_usbstorage.isInserted = (FN_MEDIUM_ISINSERTED)&__io_uns_IsInserted; __io_usbstorage.readSectors = (FN_MEDIUM_READSECTORS)&__io_uns_ReadSectors; __io_usbstorage.writeSectors = (FN_MEDIUM_WRITESECTORS)&__io_uns_NOP; //&__io_uns_WriteSectors __io_usbstorage.clearStatus = (FN_MEDIUM_CLEARSTATUS)&__io_uns_ClearStatus; __io_usbstorage.shutdown = (FN_MEDIUM_SHUTDOWN)&__io_uns_Shutdown; } uneek_fs_type = UNEEK_FS_USB; free (buf); return true; } //sneek else if ((pstat->file_length == 0xfffffff1)&&(pstat->file_pos == 0xfffffff8)&&(fu>=0)) { #ifdef CACHE_SECTOR_LOCATION seek_cache = 0xfffffff8; #endif uneek_fs_type = UNEEK_FS_SD; if (mode & ISFS_OPEN_WRITE) { __io_wiisd.ioType = DEVICE_TYPE_WII_SD; __io_wiisd.features = FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_SD; __io_wiisd.startup = (FN_MEDIUM_STARTUP)&__io_uns_Startup; __io_wiisd.isInserted = (FN_MEDIUM_ISINSERTED)&__io_uns_IsInserted; __io_wiisd.readSectors = (FN_MEDIUM_READSECTORS)&__io_uns_ReadSectors; __io_wiisd.writeSectors = (FN_MEDIUM_WRITESECTORS)&__io_uns_WriteSectors; __io_wiisd.clearStatus = (FN_MEDIUM_CLEARSTATUS)&__io_uns_ClearStatus; __io_wiisd.shutdown = (FN_MEDIUM_SHUTDOWN)&__io_uns_Shutdown; //usb and sd will be treated equally //needed for joyflow with the sd only setup if (mode & ISFS_OPEN_ALL) { __io_usbstorage.ioType = DEVICE_TYPE_WII_USB; __io_usbstorage.features = FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB; __io_usbstorage.startup = (FN_MEDIUM_STARTUP)&__io_uns_Startup; __io_usbstorage.isInserted = (FN_MEDIUM_ISINSERTED)&__io_uns_IsInserted; __io_usbstorage.readSectors = (FN_MEDIUM_READSECTORS)&__io_uns_ReadSectors; __io_usbstorage.writeSectors = (FN_MEDIUM_WRITESECTORS)&__io_uns_WriteSectors; __io_usbstorage.clearStatus = (FN_MEDIUM_CLEARSTATUS)&__io_uns_ClearStatus; __io_usbstorage.shutdown = (FN_MEDIUM_SHUTDOWN)&__io_uns_Shutdown; uneek_fs_type = UNEEK_FS_BOTH; } } else { __io_wiisd.ioType = DEVICE_TYPE_WII_SD; __io_wiisd.features = FEATURE_MEDIUM_CANREAD | FEATURE_WII_SD; __io_wiisd.startup = (FN_MEDIUM_STARTUP)&__io_uns_Startup; __io_wiisd.isInserted = (FN_MEDIUM_ISINSERTED)&__io_uns_IsInserted; __io_wiisd.readSectors = (FN_MEDIUM_READSECTORS)&__io_uns_ReadSectors; __io_wiisd.writeSectors = (FN_MEDIUM_WRITESECTORS)&__io_uns_NOP; //&__io_uns_WriteSectors __io_wiisd.clearStatus = (FN_MEDIUM_CLEARSTATUS)&__io_uns_ClearStatus; __io_wiisd.shutdown = (FN_MEDIUM_SHUTDOWN)&__io_uns_Shutdown; if(mode & ISFS_OPEN_ALL) { __io_usbstorage.ioType = DEVICE_TYPE_WII_USB; __io_usbstorage.features = FEATURE_MEDIUM_CANREAD | FEATURE_WII_USB; __io_usbstorage.startup = (FN_MEDIUM_STARTUP)&__io_uns_Startup; __io_usbstorage.isInserted = (FN_MEDIUM_ISINSERTED)&__io_uns_IsInserted; __io_usbstorage.readSectors = (FN_MEDIUM_READSECTORS)&__io_uns_ReadSectors; __io_usbstorage.writeSectors = (FN_MEDIUM_WRITESECTORS)&__io_uns_NOP; //&__io_uns_WriteSectors __io_usbstorage.clearStatus = (FN_MEDIUM_CLEARSTATUS)&__io_uns_ClearStatus; __io_usbstorage.shutdown = (FN_MEDIUM_SHUTDOWN)&__io_uns_Shutdown; uneek_fs_type = UNEEK_FS_BOTH; } } free (buf); return true; } else { /* these should be initialised right in the ogc library */ /* so we will ignore those for the moment */ /* __io_usbstorage.ioType = DEVICE_TYPE_WII_USB; __io_usbstorage.features = FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB; __io_usbstorage.startup = (FN_MEDIUM_STARTUP)&__io_usb_Startup; __io_usbstorage.isInserted = (FN_MEDIUM_ISINSERTED)&__io_usb_IsInserted; __io_usbstorage.readSectors = (FN_MEDIUM_READSECTORS)&__io_usb_ReadSectors; __io_usbstorage.writeSectors = (FN_MEDIUM_WRITESECTORS)&__io_usb_WriteSectors; __io_usbstorage.clearStatus = (FN_MEDIUM_CLEARSTATUS)&__io_usb_ClearStatus; __io_usbstorage.shutdown = (FN_MEDIUM_SHUTDOWN)&__io_usb_Shutdown; */ uneek_fs_type = UNEEK_FS_NONE; free(buf); ISFS_Close(fu); ISFS_Deinitialize(); return false; } } else //was already initialised { return true; } } /* stay compatible with previous versions */ bool check_uneek_fs(void) { if(uneek_fs_type != UNEEK_FS_NONE) { return true; } else { return false; } } s32 check_uneek_fs_type(void) { return uneek_fs_type; } bool exit_uneek_fs(void) { if (uneek_fs_type != UNEEK_FS_NONE) { ISFS_Close(fu); ISFS_Deinitialize(); uneek_fs_type = UNEEK_FS_NONE; } // do nothing return true; } bool SenseSneek (bool isfsinit) { bool ret = true; char path[ISFS_MAXPATH] ATTRIBUTE_ALIGN(32); strcpy (path, "/SNEEK/kernel.bin"); if (isfsinit) ISFS_Initialize (); s32 fd = ISFS_Open(path, ISFS_OPEN_READ); if (fd < 0) ret = false; else ISFS_Close (fd); if (isfsinit) ISFS_Deinitialize(); return ret; } bool is_neek(void) { u32 boot2version; if(confirm_neek1 == 5) { ES_GetBoot2Version(&boot2version); if(boot2version < 5) confirm_neek1 = false; else confirm_neek1 = true; } return confirm_neek1; } bool is_neek2 (bool isfsinit) { char path[ISFS_MAXPATH] ATTRIBUTE_ALIGN(32); if(confirm_neek2 == 5) { confirm_neek2 = true; strcpy (path, "/SNEEK/kernel.bin"); if (isfsinit) ISFS_Initialize (); s32 fd = ISFS_Open(path, ISFS_OPEN_READ); if (fd < 0) confirm_neek2 = false; else ISFS_Close (fd); if (isfsinit) ISFS_Deinitialize (); } return confirm_neek2; } //! New method for determining if this is a real or emu nand. //! Works with new versions of classic Crediar NEEK bool is_neek3(bool isfsinit) { u32 num = 0; //! Thanks goes to the almighty giantpune for this if(confirm_neek3 == 5) { if (isfsinit) ISFS_Initialize (); if(ISFS_ReadDir("/Sneek", NULL, &num)==0) confirm_neek3 = true; else confirm_neek3 = false; if (isfsinit) ISFS_Deinitialize (); } return confirm_neek3; } bool is_neek4(bool isfsinit) { u32 ownerID; u16 groupID; u8 attributes; u8 ownerperm; u8 groupperm; u8 otherperm; if(confirm_neek4 == 5) { confirm_neek4 = false; if(isfsinit) ISFS_Initialize(); ISFS_GetAttr("/shared1/00000000.app", &ownerID, &groupID, &attributes, &ownerperm, &groupperm, &otherperm); if(otherperm) confirm_neek4 = true; if(isfsinit) ISFS_Deinitialize(); } return confirm_neek4; } bool WII_Launch_Channel(char* which) { #ifdef HW_RVL size_t wlen; u64 title_id; int titlemsb, titlelsb; char stitlexsb[16]; union lsbstufftype { char titlename[4]; u32 title_lsb; }lsbstuff; wlen = strlen(which); if ((wlen != 4) && (wlen != 16)) { #ifdef SHOW_GECKO_DEBUG gprintf("Title %s has an invalid length\n",which); #endif return false; } if (wlen == 4) { titlemsb = 0x00010001; strncpy(lsbstuff.titlename,which,4); titlelsb = lsbstuff.title_lsb; } else { strncpy(stitlexsb,which,8); stitlexsb[8]=0; sscanf(stitlexsb,"%x",&titlemsb); strncpy(stitlexsb,which+8,8); stitlexsb[8]=0; sscanf(stitlexsb,"%x",&titlelsb); } title_id = (u64)(titlemsb) << 32; title_id = title_id + (u64)(titlelsb); #ifdef SHOW_GECKO_DEBUG gprintf( "titleid = %08x %08x\r\n", (u32)((title_id)>>32), (u32)(title_id) ); s32 lret = WII_LaunchTitle(title_id); gprintf("WII_LaunchTitle returned %d\r\n",lret); #else WII_LaunchTitle(title_id); #endif // basically, it shouldn't get here I assume #endif //HW_RVL return false; }