diff --git a/Makefile.wii b/Makefile.wii index 56691a7..33aeb1b 100644 --- a/Makefile.wii +++ b/Makefile.wii @@ -34,7 +34,7 @@ LANG := ENGLISH #CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE) -DFCEU_VERSION_NUMERIC=9812 -DNGC=1 -DZLIB -DBIG_ENDIAN -DHAVE_ASPRINTF=1 -DSTDC -D_SZ_ONE_DIRECTORY=1 -D_LZMA_IN_CB=1 -D_LZMA_OUT_READ CFLAGS = -g -Os -Wall $(MACHDEP) $(INCLUDE) -DFCEU_VERSION_NUMERIC=9812 -DNGC=1 \ - -DZLIB -DBIG_ENDIAN -DHAVE_ASPRINTF=1 -DSTDC \ + -DZLIB -DBIG_ENDIAN -DWORDS_BIGENDIAN -DHAVE_ASPRINTF=1 -DSTDC \ -D_SZ_ONE_DIRECTORY=1 -D_LZMA_IN_CB=1 -D_LZMA_OUT_READ \ -DINTL_$(LANG) CXXFLAGS = $(CFLAGS) diff --git a/source/drivers/gamecube/gcdvd.c b/source/drivers/gamecube/gcdvd.c index bd972ea..c29ad07 100644 --- a/source/drivers/gamecube/gcdvd.c +++ b/source/drivers/gamecube/gcdvd.c @@ -17,12 +17,11 @@ #include "gcdvd.h" #ifdef HW_RVL -#include "wiisd/sdio.h" -#include "wiisd/tff.h" +#include "wiisd/vfat.h" /*Front SCARD*/ -FATFS frontfs; -FILINFO finfo; +VFATFS vfs; +FSDIRENTRY vfsfile; #endif /*** Simplified Directory Entry Record @@ -37,7 +36,6 @@ FILINFO finfo; #define PAGESIZE 11 #define FCEUDIR "fceu" -#define SAVEDIR "saves" #define ROMSDIR "roms" FILEENTRIES filelist[MAXFILES]; @@ -561,51 +559,33 @@ int parseWiiSDdirectory() { int entries = 0; int nbfiles = 0; int numstored = 0; - DIRECTORY sddir; - s32 result; char msg[1024]; /* initialize selection */ selection = offset = 0; /* Get a list of files from the actual root directory */ - result = f_opendir(&sddir, rootWiiSDdir); - if(result != FR_OK) { - sprintf(msg, "f_opendir(%s) failed with %d.", rootWiiSDdir, result); + FSDIRENTRY vfsdir; + int result = VFAT_opendir(0, &vfsdir, rootWiiSDdir); + if(result != FS_SUCCESS) { + sprintf(msg, "Opendir(%s) failed with %d.", rootWiiSDdir, result); WaitPrompt(msg); return 0; } - memset(&finfo, 0, sizeof(finfo)); - - // f_readdir doesn't seem to find ".." dir, manually add it. - if (strlen(rootWiiSDdir) > 0) { - strcpy(filelist[numstored].filename, ".."); - filelist[numstored].length = 0; - filelist[numstored].flags = 1; + while (VFAT_readdir(&vfsdir) == FS_SUCCESS) { + sprintf(msg, "VFAT Adding %s", vfsdir.longname); + ShowAction(msg); + memset (&filelist[numstored], 0, sizeof (FILEENTRIES)); + strncpy(filelist[numstored].filename,(char *)(vfsdir.longname), MAX_LONG_NAME); + filelist[numstored].filename[MAX_LONG_NAME-1] = 0; + filelist[numstored].length = vfsdir.fsize; + filelist[numstored].flags = (char)(vfsdir.dirent.attribute & ATTR_DIRECTORY); numstored++; + nbfiles++; } - f_readdir(&sddir, &finfo); - finfo.fname[12] = 0; - while(strlen(finfo.fname) != 0) { - finfo.fname[12] = 0; - //if(!(finfo.fattrib & AM_DIR)) - //{ - if (strcmp((const char*)finfo.fname, ".") != 0) { // Skip "." directory - sprintf(msg, "Adding %s", finfo.fname); - //ShowAction(msg); - memset(&filelist[numstored], 0, sizeof (FILEENTRIES)); - strncpy(filelist[numstored].filename,(const char*)finfo.fname,MAXJOLIET); - filelist[numstored].filename[MAXJOLIET-1] = 0; - filelist[numstored].length = finfo.fsize; - filelist[numstored].flags = (char)(finfo.fattrib & AM_DIR); - numstored++; - } - nbfiles++; - //} - memset(&finfo, 0, sizeof(finfo)); - f_readdir(&sddir, &finfo); - } + VFAT_closedir(&vfsdir); + entries = nbfiles; if (entries < 0) entries = 0; if (entries > MAXFILES) entries = MAXFILES; @@ -786,16 +766,6 @@ void FileSelector() { maxfiles = parsedir(); } } else { -#ifdef HW_RVL - if (UseWiiSDCARD) { - strncpy(finfo.fname, filelist[selection].filename, 12); - int l = strlen(finfo.fname); - if (l > 12) l = 12; - finfo.fname[l] = 0; - finfo.fsize = filelist[selection].length; - finfo.fattrib = filelist[selection].flags ? AM_DIR : 0; - } -#endif rootdir = filelist[selection].offset; rootdirlength = filelist[selection].length; // Now load the DVD file to it's offset @@ -817,62 +787,18 @@ int LoadDVDFile( unsigned char *buffer ) { u64 discoffset; #ifdef HW_RVL - FIL fp; - u32 bytes_read = 0, bytes_read_total = 0; - if(UseWiiSDCARD) { ShowAction((char*)"Loading ... Wait"); char filename[1024]; - sprintf(filename, "%s/%s", rootWiiSDdir, finfo.fname); + sprintf(filename, "%s/%s", rootWiiSDdir, filelist[selection].filename); - /*if(f_mount(0, &frontfs) != FR_OK) { - WaitPrompt("f_mount failed"); - return 0; - }*/ - - int res = f_stat(filename, &finfo); - if(res != FR_OK) { + int res = VFAT_fopen(0, &vfsfile, filename, FS_READ); + if (res != FS_SUCCESS) { char msg[1024]; - sprintf(msg, "f_stat %s failed, error %d", filename, res); + sprintf(msg, "Open %s failed, error %d", filename, res); WaitPrompt(msg); - //f_mount(0, NULL); return 0; } - - res = f_open(&fp, filename, FA_READ); - if (res != FR_OK) { - char msg[1024]; - sprintf(msg, "f_open failed, error %d", res); - WaitPrompt(msg); - //f_mount(0, NULL); - return 0; - } - - while(bytes_read_total < finfo.fsize) { - if(f_read(&fp, buffer + bytes_read_total, 0x200, &bytes_read) != FR_OK) { - WaitPrompt((char*)"f_read failed"); - f_close(&fp); - //f_mount(0, NULL); - return 0; - } - - if(bytes_read == 0) - break; - bytes_read_total += bytes_read; - } - - if(bytes_read_total < finfo.fsize) { - //printf("error: read %u of %u bytes.\n", bytes_read_total, (unsigned int)finfo.fsize); - WaitPrompt((char*)"read failed : over read!"); - f_close(&fp); - //f_mount(0, NULL); - return 0; - } - - ShowAction((char*)"Loading Rom Succeeded"); - f_close(&fp); - //f_mount(0, NULL); - return bytes_read_total; } #endif @@ -887,12 +813,21 @@ int LoadDVDFile( unsigned char *buffer ) { ShowAction("Loading ... Wait"); if (UseSDCARD) SDCARD_ReadFile (filehandle, &readbuffer, 2048); +#ifdef HW_RVL + else if (UseWiiSDCARD) VFAT_fread(&vfsfile, &readbuffer, 2048); +#endif else dvd_read(&readbuffer, 2048, discoffset); if ( isZipFile() == false ) { if (UseSDCARD) SDCARD_SeekFile (filehandle, 0, SDCARD_SEEK_SET); +#ifdef HW_RVL + else if (UseWiiSDCARD) VFAT_fseek(&vfsfile, 0, SEEK_SET); +#endif for ( i = 0; i < blocks; i++ ) { if (UseSDCARD) SDCARD_ReadFile (filehandle, &readbuffer, 2048); +#ifdef HW_RVL + else if (UseWiiSDCARD) VFAT_fread(&vfsfile, &readbuffer, 2048); +#endif else dvd_read(&readbuffer, 2048, discoffset); memcpy(&buffer[offset], &readbuffer, 2048); offset += 2048; @@ -903,6 +838,9 @@ int LoadDVDFile( unsigned char *buffer ) { if( rootdirlength % 2048 ) { i = rootdirlength % 2048; if (UseSDCARD) SDCARD_ReadFile (filehandle, &readbuffer, i); +#ifdef HW_RVL + else if (UseWiiSDCARD) VFAT_fread(&vfsfile, &readbuffer, i); +#endif else dvd_read(&readbuffer, 2048, discoffset); memcpy(&buffer[offset], &readbuffer, i); } @@ -910,6 +848,9 @@ int LoadDVDFile( unsigned char *buffer ) { return unzipDVDFile( buffer, (u32)discoffset, rootdirlength); } if (UseSDCARD) SDCARD_CloseFile (filehandle); +#ifdef HW_RVL + else if (UseWiiSDCARD) VFAT_fclose(&vfsfile); +#endif return rootdirlength; } @@ -965,13 +906,17 @@ int OpenWiiSD () { haveSDdir = 0; char msg[128]; + memset(&vfs, 0, sizeof(VFATFS)); if (haveWiiSDdir == 0) { /* don't mess with DVD entries */ havedir = 0; - /* Mount WiiSD */ - if(f_mount(0, &frontfs) != FR_OK) { - WaitPrompt((char*)"f_mount failed"); + /* Mount WiiSD with VFAT */ + VFAT_unmount(0, &vfs); + int res = VFAT_mount(FS_SLOTA, &vfs); + if (res != FS_TYPE_FAT16) { + sprintf(msg, "Error mounting WiiSD: %d", res); + WaitPrompt(msg); return 0; } @@ -992,13 +937,11 @@ int OpenWiiSD () { /* no entries found */ sprintf (msg, "Error reading %s", rootWiiSDdir); WaitPrompt (msg); - //f_mount(0, NULL); return 0; } } /* Retrieve previous entries list and made a new selection */ else FileSelector (); - //f_mount(0, NULL); return 1; } diff --git a/source/drivers/gamecube/info.c b/source/drivers/gamecube/info.c index 6f4f1f5..591d17a 100644 --- a/source/drivers/gamecube/info.c +++ b/source/drivers/gamecube/info.c @@ -52,9 +52,15 @@ extern int UseSDCARD; extern unsigned char DecodeJoy( unsigned short pp ); extern unsigned char GetAnalog(int Joy); +#ifdef HW_RVL +void (*PSOReload)() = (void(*)())0x90000020; +#else +void (*PSOReload)() = (void(*)())0x80001800; +#endif + void Reboot() { #ifdef HW_RVL - // Thanks to hell_hibou + // Thanks to eke-eke SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); #else #define SOFTRESET_ADR ((volatile u32*)0xCC003024) @@ -1002,12 +1008,6 @@ int MainMenu() { short j; int redraw = 1; -#ifdef HW_RVL - void (*PSOReload)() = (void(*)())0x90000020; -#else - void (*PSOReload)() = (void(*)())0x80001800; -#endif - /*** Stop any running Audio ***/ AUDIO_StopDMA(); diff --git a/source/drivers/gamecube/main.c b/source/drivers/gamecube/main.c index b2661c6..ea3c915 100644 --- a/source/drivers/gamecube/main.c +++ b/source/drivers/gamecube/main.c @@ -45,13 +45,8 @@ long long basetime; void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count); +extern void *PSOReload(); static void reset_cb() { -#ifdef HW_RVL - void (*PSOReload)() = (void(*)())0x90000020; -#else - void (*PSOReload)() = (void(*)())0x80001800; -#endif - PSOReload(); } diff --git a/source/drivers/gamecube/memstate.c b/source/drivers/gamecube/memstate.c index 84d2e38..c4ea1ed 100644 --- a/source/drivers/gamecube/memstate.c +++ b/source/drivers/gamecube/memstate.c @@ -44,7 +44,8 @@ int mcversion = 0x981211; static u8 SysArea[CARD_WORKAREA] ATTRIBUTE_ALIGN(32); #ifdef HW_RVL -extern FILINFO finfo; +FATFS frontfs; +FILINFO finfo; #endif extern int ChosenSlot; extern int ChosenDevice; @@ -545,6 +546,13 @@ void SD_Manage(int mode, int slot) { int res; u32 offset = 0; + /* Mount WiiSD with TinyFatFS*/ + if(f_mount(0, &frontfs) != FR_OK) { + WaitPrompt((char*)"f_mount failed"); + return 0; + } + memset(&finfo, 0, sizeof(finfo)); + if (mode == 0) res = f_open(&fp, path, FA_CREATE_ALWAYS | FA_WRITE); else { diff --git a/source/drivers/gamecube/wiisd/sdio.c b/source/drivers/gamecube/wiisd/sdio.c index a84e0c1..4700cf4 100644 --- a/source/drivers/gamecube/wiisd/sdio.c +++ b/source/drivers/gamecube/wiisd/sdio.c @@ -29,6 +29,8 @@ static s32 sd_fd = -1; static u32 status __attribute__((aligned(32))); +s32 sd_deinit(); + s32 sd_send_cmd(u32 cmd, u32 type, u32 resp, u32 arg, u32 blocks, u32 bsize, u32 addr) { static u32 request[9] __attribute__((aligned(32))); @@ -45,12 +47,12 @@ s32 sd_send_cmd(u32 cmd, u32 type, u32 resp, u32 arg, u32 blocks, u32 bsize, u32 request[4] = blocks; request[5] = bsize; request[6] = addr; - request[7] = 0; - request[8] = 0; + //request[7] = 0; + //request[8] = 0; r = IOS_Ioctl(sd_fd, 7, (u8 *)request, 36, (u8 *)reply, 0x10); - printf("sd_send_cmd(%x, %x, %x, %x, %x, %x, %x) = %d", cmd, type, resp, arg, blocks, bsize, addr, r); - printf(" -> %x %x %x %x\n", reply[0], reply[1], reply[2], reply[3]); // TODO: add some argument for this reply + //printf("sd_send_cmd(%x, %x, %x, %x, %x, %x, %x) = %d", cmd, type, resp, arg, blocks, bsize, addr, r); + //printf(" -> %x %x %x %x\n", reply[0], reply[1], reply[2], reply[3]); // TODO: add some argument for this reply return r; } @@ -60,7 +62,7 @@ s32 sd_reset() s32 r; r = IOS_Ioctl(sd_fd, 4, 0, 0, (u8 *)&status, 4); - printf("sd_reset(): r = %d; status = %d\n", r, status); + //printf("sd_reset(): r = %d; status = %d\n", r, status); return r; } @@ -69,15 +71,7 @@ s32 sd_select() s32 r; r = sd_send_cmd(7, 3, 2, status & 0xFFFF0000, 0, 0, 0); - printf("sd_select(): r = %d\n", r); - return r; -} - -s32 sd_deselect() -{ - s32 r; - r = sd_send_cmd(7, 3, 2, 0, 0, 0, 0); - printf("sd_deselect(): r = %d\n", r); + //printf("sd_select(): r = %d\n", r); return r; } @@ -86,7 +80,7 @@ s32 sd_set_blocklen(u32 len) s32 r; r = sd_send_cmd(0x10, 3, 1, len, 0, 0, 0); - printf("sd_set_blocklen(%x) = %d\n", len, r); + //printf("sd_set_blocklen(%x) = %d\n", len, r); return r; } @@ -101,10 +95,10 @@ u8 sd_get_hcreg() query[0] = 0x28; query[3] = 1; - query[4] = 0; + //query[4] = 0; r = IOS_Ioctl(sd_fd, 2, (u8 *)query, 0x18, (u8 *)&data, 4); - printf("sd_get_hcreg() = %d; r = %d\n", data & 0xFF, r); + //printf("sd_get_hcreg() = %d; r = %d\n", data & 0xFF, r); return data & 0xFF; } @@ -120,7 +114,7 @@ s32 sd_set_hcreg(u8 value) query[4] = value; r = IOS_Ioctl(sd_fd, 1, (u8 *)query, 0x18, 0, 0); - printf("sd_set_hcreg(%d) = %d\n", value, r); + //printf("sd_set_hcreg(%d) = %d\n", value, r); return r; } @@ -151,7 +145,7 @@ s32 sd_clock() c = 1; r = IOS_Ioctl(sd_fd, 6, &c, 4, 0, 0); - printf("sd_clock() = %d\n", r); + //printf("sd_clock() = %d\n", r); return r; } @@ -195,8 +189,8 @@ s32 sd_read(u32 n, u8 *buf) if(r != 0) { - printf("sd_read() = %d\n", r); - printf(" %x %x %x %x\n", res[0], res[1], res[2], res[3]); + //printf("sd_read() = %d\n", r); + //printf(" %x %x %x %x\n", res[0], res[1], res[2], res[3]); return r; } @@ -241,8 +235,8 @@ s32 sd_write(u32 n, const u8 *buf) if(r != 0) { - printf("sd_write() = %d\n", r); - printf(" %x %x %x %x\n", res[0], res[1], res[2], res[3]); + //printf("sd_write() = %d\n", r); + //printf(" %x %x %x %x\n", res[0], res[1], res[2], res[3]); return r; } @@ -255,12 +249,13 @@ s32 sd_init() if(sd_fd > 0) { - printf("sd_init() called more than once. using old sd_fd: %d\n", sd_fd); - return 0; + //printf("sd_init() called more than once. using old sd_fd: %d\n", sd_fd); + //return 0; + sd_deinit(); } sd_fd = IOS_Open("/dev/sdio/slot0", 0); - printf("sd_fd = %d\n", sd_fd); + //printf("sd_fd = %d\n", sd_fd); if(sd_fd < 0) return sd_fd; @@ -287,7 +282,7 @@ s32 sd_init() s32 sd_deinit() { - sd_deselect(); + sd_reset(); return IOS_Close(sd_fd); } diff --git a/source/drivers/gamecube/wiisd/vdiskio.c b/source/drivers/gamecube/wiisd/vdiskio.c new file mode 100644 index 0000000..71ae623 --- /dev/null +++ b/source/drivers/gamecube/wiisd/vdiskio.c @@ -0,0 +1,93 @@ +/**************************************************************************** +* FAT16 - VFAT Support +* +* NOTE: Only supports FAT16 with Long File Names +* I have no interest in adding FAT32 +* +* Reference Documentation: +* +* FAT: General Overview of On-Disk Format +* Version 1.02 May 05, 1999 +* Microsoft Corporation +* +* FAT: General Overview of On-Disk Format +* Version 1.03 December 06, 2000 +* Microsoft Corporation +* +* This is targetted at MMC/SD cards. +* +* Copyright softdev 2007. All rights reserved. +* +* Diskio Module +* ------------- +* +* This module is almost identical to the one found in ChaN's TinyFAT FS. +* It's a logical abstration after all :) +* +* This covers stdio file on a SD image file +* +* 03/08: quickly modified by eke-eke to support Wii Front SD +****************************************************************************/ +#include +#include +#include +#include +#include "sdio.h" +#include "vfat.h" + +#define CARDIO_ERROR_READY 0 + + +/* End of not so public exports */ + +/**************************************************************************** +* DISKIO_Init +* +* Initialise communication with the disc +****************************************************************************/ +int DISKIO_Init( int drive ) +{ + int res; + + if ( drive < 0 || drive > 1 ) + return FS_ERR_PARAM; + + res = sd_init(); + + if ( res == CARDIO_ERROR_READY ) + return FS_SUCCESS; + + return FS_ERR_IO; + +} + +/**************************************************************************** +* DISKIO_ReadSectors +* +* Read sectors from the disc +****************************************************************************/ +int DISKIO_ReadSectors( int drive, void *buffer, int sector, int count ) +{ + int res = -1; + int i; + int bytes = 0; + + if ( drive != 0 && drive != 1 ) + return FS_ERR_PARAM; /* Must be 0 or 1 */ + + /* libOGC appears to only read in single sectors */ + for( i = 0; i < count; i++ ) + { + res = sd_read(sector + i, buffer + bytes); + if ( res != CARDIO_ERROR_READY ) + return FS_ERR_IO; + bytes += SECTOR_SIZE; + } + + if ( res == CARDIO_ERROR_READY ) + return FS_SUCCESS; + + return FS_ERR_IO; + +} + diff --git a/source/drivers/gamecube/wiisd/vdiskio.h b/source/drivers/gamecube/wiisd/vdiskio.h new file mode 100644 index 0000000..d9e9313 --- /dev/null +++ b/source/drivers/gamecube/wiisd/vdiskio.h @@ -0,0 +1,36 @@ +/**************************************************************************** +* FAT16 - VFAT Support +* +* NOTE: Only supports FAT16 with Long File Names +* I have no interest in adding FAT32 +* +* Reference Documentation: +* +* FAT: General Overview of On-Disk Format +* Version 1.02 May 05, 1999 +* Microsoft Corporation +* +* FAT: General Overview of On-Disk Format +* Version 1.03 December 06, 2000 +* Microsoft Corporation +* +* This is targetted at MMC/SD cards. +* +* Copyright softdev 2007. All rights reserved. +* +* Diskio Module +* ------------- +* +* This module is almost identical to the one found in ChaN's TinyFAT FS. +* It's a logical abstration after all :) +* +* This covers stdio file on a SD image file +****************************************************************************/ +#ifndef __DISKIO__ +#define __DISKIO__ + +int DISKIO_Init( int drive ); +int DISKIO_ReadSectors( int drive, void *buffer, int sector, int count ); + +#endif + diff --git a/source/drivers/gamecube/wiisd/vfat.c b/source/drivers/gamecube/wiisd/vfat.c new file mode 100644 index 0000000..ad8b03a --- /dev/null +++ b/source/drivers/gamecube/wiisd/vfat.c @@ -0,0 +1,924 @@ +/**************************************************************************** +* FAT16 - VFAT Support +* +* NOTE: Only supports FAT16 with Long File Names +* I have no interest in adding FAT32 +* +* Reference Documentation: +* +* FAT: General Overview of On-Disk Format +* Version 1.02 May 05, 1999 +* Microsoft Corporation +* +* FAT: General Overview of On-Disk Format +* Version 1.03 December 06, 2000 +* Microsoft Corporation +* +* This is targetted at MMC/SD cards. +* +* Copyright softdev 2007. All rights reserved. +* +* $Date: 2007-08-05 14:27:48 +0100 (Sun, 05 Aug 2007) $ +* $Rev: 8 $ +****************************************************************************/ +#include +#include +#include +#include +#include "vfat.h" +#include "vdiskio.h" + +static BYTE sector[SECTOR_SIZE]; /**< Local sector buffer */ +static VFATFS *vfs[2]; /**< VFATFS Pointers for 2 drives */ + +#ifdef WORDS_BIGENDIAN +#define strcasecmp stricmp +#endif + +/** + * Z E R O S E C T O R / B I O S P A R A M E T E R B L O C K + * + * These functions take care of parsing the 0th sector/BPB + * Supports SuperFloppy Format and standard partitioning. + * + */ + +/**\defgroup INTERNALS VFATFS Private Functions + * \brief These are internal helper functions and should + * not be called by an application!\n + * They are documented here for convenience only. + */ + +/**\ingroup INTERNALS + * \brief Check for BIOS Parameter Block + * \param sector 512 byte sector data + * \return FS_TYPE_FAT16 on success + */ +static int BPBCheck( BYTE *sector ) +{ + BPB16 *bpb = (BPB16 *)sector; + + /* Check signatures */ + if ( ( bpb->sigkey1 == 0x55 ) && ( bpb->sigkey2 == 0xAA ) ) + { + /* Check for FAT16 signature */ + if ( memcmp(bpb->FilSysType, "FAT16", 5) == 0 ) + return FS_TYPE_FAT16; + /* Non MS utilities simply put FAT */ + if ( memcmp(bpb->FilSysType, "FAT", 3) == 0 ) + return FS_TYPE_FAT16; + } + + return FS_TYPE_NONE; +} + +/** \ingroup INTERNALS + * \brief Partition Entry Check + * \param sector 512 byte sector data + * \return Partition Start LBA on success + */ +static unsigned int PECheck( BYTE *sector ) +{ + int i; + PARTENTRY *pe; + + if ( ( sector[SECTOR_SIZE-2] == 0x55 ) && ( sector[SECTOR_SIZE-1] == 0xAA ) ) + { + /* Find a FAT16 partition entry */ + for( i = 0; i < 4; i++ ) + { + pe = (PARTENTRY *)(sector + 446 + (i<<4)); + if ( pe->partitiontype == 0x06 ) + { + return SWAP32(pe->partitionstart); + } + } + } + + return FS_TYPE_NONE; +} + +/**************************************************************************** +* VFAT_Mount +* +* Function to mount a FAT16-VFAT volume +***************************************************************************/ +int VFAT_mount( int driveid, VFATFS *v ) +{ + int ret; + unsigned int bpbsector = 0; + BPB16 *bpb = (BPB16 *)sector; + BYTE media = 0; + + if ( driveid < 0 || driveid > 1 ) + return FS_TYPE_NONE; + + memset(v, 0, sizeof(VFATFS)); + + /* Copy pointer */ + vfs[driveid] = v; + + if ( DISKIO_Init( driveid ) != FS_SUCCESS ) + return FS_ERR_IO; + + if ( DISKIO_ReadSectors( driveid, sector, 0, 1 ) != FS_SUCCESS ) + return FS_ERR_IO; + + /* Check for SuperFloppy Format */ + ret = BPBCheck( sector ); + + if ( ret == FS_TYPE_NONE ) + { + /* Check for Partition Entry */ + bpbsector = PECheck(sector); + if ( !bpbsector ) + return FS_TYPE_NONE; + + if ( DISKIO_ReadSectors( driveid, sector, bpbsector, 1 ) != FS_SUCCESS ) + return FS_ERR_IO; + + /* Check BPB */ + ret = BPBCheck( sector ); + } + + if ( ret == FS_TYPE_FAT16 ) + { + /* Capture defaults to machine native format */ + v->BaseOffset = bpbsector; + v->BytesPerSector = SWAP16(bpb->bytesPerSec); + v->SectorsPerFAT = SWAP16(bpb->FATsz16); + v->ReservedSectors = SWAP16(bpb->reservedSec); + v->NumberOfFATs = bpb->numFATs; + v->SectorsPerCluster = bpb->secPerClust; + v->RootDirEntries = SWAP16(bpb->rootEntCount); + + /* Calculate number of root directory sectors */ + v->RootDirSectors = ( ( SWAP16(bpb->rootEntCount) << 5 ) + ( v->BytesPerSector - 1 ) ) / v->BytesPerSector; + + /* First data sector */ + v->FirstDataSector = v->ReservedSectors + (v->NumberOfFATs * v->SectorsPerFAT) + v->RootDirSectors + v->BaseOffset; + + /* Total sectors */ + if ( bpb->totSec16 == 0 ) + v->TotalSectors = SWAP32(bpb->totSec32); + else + v->TotalSectors = SWAP16(bpb->totSec16); + + /* Data Sectors */ + v->DataSectors = v->TotalSectors - ( v->ReservedSectors + ( v->NumberOfFATs * v->SectorsPerFAT ) + v->RootDirSectors ); + + /* Count of clusters */ + v->CountOfClusters = v->DataSectors / bpb->secPerClust; + + /* From v1.03 Document - Page 14 - FAT Type Determination */ + if ( v->CountOfClusters < 4085 ) + return FS_TYPE_NONE; /* FAT12 Volume */ + else + { + if ( v->CountOfClusters >= 65525 ) + return FS_TYPE_NONE; /* FAT32 Volume */ + } + + /* Root Directory Offset */ + v->RootDirOffset = v->ReservedSectors + ( bpb->numFATs * v->SectorsPerFAT ) + v->BaseOffset; + + /* First copy of FAT offset */ + v->FirstFATOffset = v->ReservedSectors + v->BaseOffset; + + media = bpb->media; + + /* Read first FAT */ + if ( DISKIO_ReadSectors( driveid, sector, v->FirstFATOffset, 1 ) != FS_SUCCESS ) + return FS_ERR_IO; + + if ( sector[0] == media ) + { + /* Allocate work spaces */ + v->FAT = (WORD *)malloc(v->SectorsPerFAT * SECTOR_SIZE); + if ( v->FAT == NULL ) + return FS_ERR_NOMEM; + + /* Save time running in and out - just load up the FAT table */ + if ( DISKIO_ReadSectors(driveid, v->FAT, v->FirstFATOffset, v->SectorsPerFAT) != FS_SUCCESS ) + { + free(v->FAT); + return FS_ERR_IO; + } + + /* Likewise, the same for the root directory */ + v->rootDir = (BYTE *)malloc(v->BytesPerSector * v->RootDirSectors); + if ( v->rootDir == NULL ) + { + free(v->FAT); + return FS_ERR_NOMEM; + } + + /* Read root directory */ + if ( DISKIO_ReadSectors(driveid, v->rootDir, v->RootDirOffset, v->RootDirSectors) != FS_SUCCESS ) + { + free(v->FAT); + free(v->rootDir); + return FS_ERR_IO; + } + return FS_TYPE_FAT16; + } + } + + return FS_TYPE_NONE; + +} + +/**************************************************************************** +* VFAT_unmount +* +* Release memory allocated by from a mounted VFATFS +****************************************************************************/ +void VFAT_unmount( int driveid, VFATFS *v ) +{ + if ( v->FAT != NULL ) + free(v->FAT); + + if ( v->rootDir != NULL ) + free(v->rootDir); + + vfs[driveid] = NULL; +} + +/** + * F I L E N A M I N G S U P P O R T + * + * Routines to en/decode long and short file names + */ + +/**************************************************************************** +* CalcShortNameChecksum +* +* Calculate the checksum for a short filename +* Filename should be in UPPER case, and padded with spaces to match +* a standard directory entry +****************************************************************************/ +/** \ingroup INTERNALS + * \brief Calculate the checksum of a short directory name + * \param fname 11 character, space padded short directory name + * \return checksum + */ +static unsigned char CalcShortNameCheckSum( BYTE *fname ) +{ + int i; + unsigned char sum = 0; + + for( i = 0; i < 11; i++ ) + sum = ( ( sum & 1 ) ? 0x80 : 0 ) + ( sum >> 1 ) + fname[i]; + + return sum; +} + +/**************************************************************************** +* BuildShortNameFromDirEntry +* +* User friendly shortname +****************************************************************************/ +/** \ingroup INTERNALS + * \brief Convert a short directory entry to an application friendly name + * \param sfn SFNDIRREC pointer + * \param out Buffer to hold converted file name + * \return None + */ +static void BuildShortNameFromDirEntry( SFNDIRREC *sfn, BYTE *out ) +{ + int i,j; + + for(i = 0, j = 0; i < 11; i++ ) + { + if ( sfn->dirname[i] != 32 ) + { + out[j++] = sfn->dirname[i]; + } + + if ( (i == 7) && ( sfn->dirname[8] != 32 ) ) + out[j++] = '.'; + } +} + +/**************************************************************************** +* BuildLongNameFromDirEntry +* +* Build a long name from unicode to asciiz. +* Each directory entry may contain up to 13 characters for sub entry. +****************************************************************************/ +/** \ingroup INTERNALS + * \brief Build a long file name as ASCIIZ + * \param lfn LFNDIRREC pointer + * \param position LFN directory record number + * \param out Output buffer + * \return None. + */ +static void BuildLongNameFromDirEntry( LFNDIRREC *lfn, int position, BYTE *out ) +{ + int j = ( ( position - 1 ) * 13 ); + int i; + + /* Part one */ + for( i = 0; i < 10; i += 2 ) + { + if ( lfn->dirname1[i] == 0xFF ) + return; + + out[j++] = lfn->dirname1[i]; + } + + /* Part two */ + for( i = 0; i < 12; i += 2 ) + { + if ( lfn->dirname2[i] == 0xFF ) + return; + + out[j++] = lfn->dirname2[i]; + } + + /* Part three */ + for( i = 0; i < 4; i += 2 ) + { + if ( lfn->dirname3[i] == 0xFF ) + return; + + out[j++] = lfn->dirname3[i]; + } +} + +/** + * D I R E C T O R Y F U N C T I O N S + * + * These routines take care of all directory level parsing + */ + +/** \ingroup INTERNALS + * \brief Retrieve first sector number of a cluster + * \param drive Device drive number + * \param cluster Cluster number to determine first sector of + * \return First sector of cluster + */ +static int SectorFromCluster( int drive, int cluster ) +{ + VFATFS *v = vfs[drive]; + return ( ( cluster - 2 ) * v->SectorsPerCluster ) + v->FirstDataSector; +} + +/** \ingroup INTERNALS + * \brief Read a complete cluster + * \param d FSDIRENTRY pointer. CurrentCluster is used + * \return FS_SUCCESS on success + */ +static int ReadCluster( FSDIRENTRY *d ) +{ + int sector; + + sector = SectorFromCluster( d->driveid, d->CurrentCluster ); + if ( DISKIO_ReadSectors( d->driveid, d->clusterdata, sector, vfs[d->driveid]->SectorsPerCluster) != FS_SUCCESS ) + return FS_ERR_IO; + + d->CachedCluster = d->CurrentCluster; + return FS_SUCCESS; +} + +/** \ingroup INTERNALS + * \brief Update entry with next cluster in chain + * \param d FSDIRENTRY pointer. CurrentCluster is used + * \return True on success + */ +static int NextCluster( FSDIRENTRY *d ) +{ + d->CurrentCluster = SWAP16(vfs[d->driveid]->FAT[d->CurrentCluster]); + if ( d->CurrentCluster >= CLUSTER_END_CHAIN ) + return 0; + + return 1; +} + +/**************************************************************************** +* FindEntry +* +* Look through a directory tree looking for an active entry. +* The current cluster should be available in d->clusterdata +****************************************************************************/ +/** \ingroup INTERNALS + * \param d FSDIRENTRY pointer + * \param maxentries Maximum directory entries held in cluster + * \return True if found, False if not found + */ +static int FindEntry( FSDIRENTRY *d, int maxentries ) +{ + int found = 0; + unsigned char *direntry; + VFATFS *v = vfs[d->driveid]; + SFNDIRREC *sfn; + LFNDIRREC *lfn; + static BYTE checksum = 0; + + if ( !d->crosscluster ) + { + /* Clear names */ + memset(d->shortname, 0, 13); + memset(d->longname, 0, MAX_LONG_NAME); + } + + while( d->CurrentDirEntry < maxentries && !found ) + { + /* Pointer to this directory entry */ + if ( d->CurrentCluster == ROOTCLUSTER ) + direntry = (v->rootDir + ( d->CurrentDirEntry << 5 ) ); + else + direntry = (d->clusterdata + ( d->CurrentDirEntry << 5 ) ); + + switch( direntry[0] ) + { + case 0x00: + case 0xE5: + break; /* Inactive entries */ + + default: + + sfn = (SFNDIRREC *)direntry; + d->crosscluster = 1; + + if ( ( sfn->attribute & ATTR_LONG_NAME_MASK ) == ATTR_LONG_NAME ) + { + if ( direntry[0] & LFN_LAST_ENTRY ) + memset(&d->longname, 0, MAX_LONG_NAME); + + lfn = (LFNDIRREC *)direntry; + BuildLongNameFromDirEntry( lfn, direntry[0] & LFN_ENTRY_MASK, d->longname); + checksum = lfn->checksum; + } + else + { + /* Short name entry */ + found = 1; + memcpy(&d->dirent, direntry, 32); + BuildShortNameFromDirEntry( sfn, d->shortname ); + d->fsize = SWAP32(sfn->filesize); + d->crosscluster = 0; + /* Ensure long name is populated with something */ + if ( strlen((char *)d->longname) == 0 ) + { + strcpy((char *)d->longname, (char *)d->shortname); + return found; + } + else + { + /* If checksums don't match - the FS is inconsistent + To do no harm, skip this entry */ + if ( checksum == CalcShortNameCheckSum(sfn->dirname) ) + return found; + else + found = 0; + } + } + } + + d->CurrentDirEntry++; + + } + + return found; +} + +/**************************************************************************** +* FindInRootDirectory +* +* Root directory is somewhat special. It's a fixed length and has no entry +* in the FAT as such. +* +* Logically, this should be the first 2 clusters, but the spec says it can +* be set to any size by the format utility (Think NT! FAT64/128/256) +* +* For speed, as all searches begin here, the root directory is held in +* memory throughout. +* +* FSDIRENTRY should only have the drive id set. +****************************************************************************/ +/** \ingroup INTERNALS + * \brief Find an entry in the root directory + * \param d FSDIRENTRY pointer + * \param search Name to find + * \return True if found, False if not + */ +static int FindInRootDirectory( FSDIRENTRY *d, char *search ) +{ + int found = 0; + + d->CurrentDirEntry++; + + while( (FindEntry(d, vfs[d->driveid]->RootDirEntries)) && !found ) + { + if ( strcasecmp(search, (char *) d->shortname) == 0 ) + { + found = 1; + } + + if ( strcasecmp(search, (char *) d->longname) == 0 ) + { + found = 1; + } + + if ( !found ) + d->CurrentDirEntry++; + } + + return found; +} + +/**************************************************************************** +* FindInClusters +* +* Generic routine to find a given name in a chain of clusters. +* Used for non-Root Directory searching +****************************************************************************/ +/** \ingroup INTERNALS + * \brief Find a directory record in a cluster chain + * \param d FSDIRENTRY pointer + * \param findme Name of directory record to find + * \return True if found, False if not + */ +static int FindInClusters( FSDIRENTRY *d, char *findme ) +{ + int found = 0; + + if ( d->CurrentDirEntry == -1 ) + d->CurrentDirEntry = 0; + + /* While not at end of chain */ + while( !found && ( d->CurrentCluster < CLUSTER_END_CHAIN ) ) + { + /* Retrieve dir entries looking for match */ + while( !found && (FindEntry( d, ( vfs[d->driveid]->BytesPerSector * vfs[d->driveid]->SectorsPerCluster) >> 5 ) ) ) + { + if ( strcasecmp((char *)d->shortname, findme) == 0 ) + found = 1; + if ( strcasecmp((char *)d->longname, findme) == 0 ) + found = 1; + + if (!found) + d->CurrentDirEntry++; + } + + /* Read next cluster */ + if ( !found ) + { + if ( NextCluster(d) ) + { + d->CurrentDirEntry = 0; + ReadCluster(d); + } + } + } + + return found; +} + +/**************************************************************************** +* VFAT_opendir +* +* Find the requested directory. +****************************************************************************/ +int VFAT_opendir( int drive, FSDIRENTRY *d, char *search ) +{ + char *p; + char srchtmp[1024]; + int searchroot = 1; + int found = 0; + + /* Clear out FSDIRENTRY */ + memset(d, 0, sizeof(FSDIRENTRY)); + + /* Set drive and root */ + d->driveid = drive; + d->CurrentCluster = ROOTCLUSTER; + d->CurrentDirEntry = -1; + + /* Is this a request for root ? */ + if ( ( strlen(search) == 0 ) || ( strcmp(search,PSEPS) == 0 ) || ( strcmp(search, DIR_ROOT) == 0 ) ) + { + return FS_FILE_OK; + } + + /* Searching for a sub-directory */ + if ( search[0] == PSEP ) + strcpy(srchtmp, &search[1]); + else + strcpy(srchtmp, search); + + p = strtok(srchtmp, PSEPS); + while ( p ) + { + found = 0; + if ( searchroot ) + { + if ( !FindInRootDirectory(d, p) ) + return FS_NO_FILE; + else + { + /* MUST be a directory */ + if ( d->dirent.attribute & ATTR_DIRECTORY ) + { + d->CurrentCluster = d->FirstCluster = SWAP16(d->dirent.fstClustLow); + d->CurrentDirEntry = -1; + + /* Allocate the cluster for this data record */ + d->clusterdata = (BYTE *)malloc(vfs[d->driveid]->SectorsPerCluster * vfs[d->driveid]->BytesPerSector); + ReadCluster(d); + found = 1; + searchroot = 0; + } + else + return FS_NO_FILE; + } + } + else + { + if ( FindInClusters( d, p ) ) + { + /* MUST be a directory */ + if ( !( d->dirent.attribute & ATTR_DIRECTORY ) ) + { + free(d->clusterdata); + return FS_NO_FILE; + } + + /* Read up this cluster */ + d->CurrentCluster = d->FirstCluster = SWAP16(d->dirent.fstClustLow); + d->CurrentDirEntry = 0; + ReadCluster(d); + found = 1; + } + } + + p = strtok(NULL, PSEPS); + } + + if ( !found ) + { + if ( d->clusterdata != NULL ) + { + free(d->clusterdata); + d->clusterdata = NULL; + } + return FS_NO_FILE; + } + + return FS_FILE_OK; + +} + +/**************************************************************************** +* VFAT_readdir +****************************************************************************/ +int VFAT_readdir( FSDIRENTRY *d ) +{ + int ret; + + d->CurrentDirEntry++; + /* Are we in root ? */ + if ( d->CurrentCluster == ROOTCLUSTER ) + { + if( FindEntry( d, vfs[d->driveid]->RootDirEntries ) ) + return FS_FILE_OK; + } + else + { + while( d->CurrentCluster < CLUSTER_END_CHAIN ) + { + ret = FindEntry( d, ( vfs[d->driveid]->BytesPerSector * vfs[d->driveid]->SectorsPerCluster) >> 5 ); + + if ( ret ) + return FS_FILE_OK; + + if ( NextCluster(d) ) + { + d->CurrentDirEntry = 0; + ReadCluster(d); + } + } + } + return FS_NO_FILE; +} + +/**************************************************************************** +* VFAT_closedir +****************************************************************************/ +void VFAT_closedir( FSDIRENTRY *d ) +{ + if ( d->clusterdata != NULL ) + { + free(d->clusterdata); + d->clusterdata = NULL; + } +} + +/**************************************************************************** +* VFAT_fopen +* +* v0.1 - VFAT_READ_ONLY Supported +****************************************************************************/ +int VFAT_fopen( int drive, FSDIRENTRY *d, char *fname, int mode ) +{ + char filename[1024]; + char path[1024]; + char temp[1024]; + char *p; + + if ( drive < 0 || drive > 1 ) + return FS_NO_FILE; + + if ( mode != FS_READ ) + return FS_NO_FILE; + + /* Clear */ + memset(d, 0, sizeof(FSDIRENTRY)); + d->driveid = drive; + + path[0] = temp[0] = filename[0] = 0; + + if ( fname[0] == PSEP ) + strcpy(temp, &fname[1]); + else + strcpy(temp, fname); + + /* Split into filename and path */ + p = strrchr(temp, PSEP); + if ( p ) + { + /* Have path and filename */ + *p = 0; + strcpy(path, temp); + p++; + strcpy(filename, p); + } + else + strcpy(filename, temp); + + /* Do search */ + if ( strlen(path) ) + { + if ( VFAT_opendir(drive, d, path) != FS_FILE_OK ) + { + VFAT_closedir(d); + return FS_NO_FILE; + } + + if ( !FindInClusters( d, filename ) ) + { + VFAT_closedir(d); + return FS_NO_FILE; + } + } + else + { + /* Much simpler check on root directory */ + d->CurrentCluster = ROOTCLUSTER; + d->CurrentDirEntry = -1; + if ( !FindInRootDirectory( d, filename ) ) + { + VFAT_closedir(d); + return FS_NO_FILE; + } + d->clusterdata = (BYTE *)malloc(vfs[d->driveid]->SectorsPerCluster * vfs[d->driveid]->BytesPerSector); + } + + /* Must be a file only */ + if ( d->dirent.attribute & ( ATTR_DIRECTORY | ATTR_VOLUME_ID ) ) + { + VFAT_closedir(d); + return FS_NO_FILE; + } + + d->FirstCluster = d->CurrentCluster = SWAP16(d->dirent.fstClustLow); + d->CachedCluster = -1; + + return FS_FILE_OK; + +} + +/**************************************************************************** +* VFAT_fclose +****************************************************************************/ +void VFAT_fclose( FSDIRENTRY *d ) +{ + VFAT_closedir(d); +} + +/**************************************************************************** +* VFAT_fread +****************************************************************************/ +int VFAT_fread( FSDIRENTRY *d, void *buffer, int length ) +{ + int cluster; + int tbytes; + int umask; + int i; + int bytesdone = 0; + int reallength; + BYTE *p = (BYTE *)buffer; + + if ( length <= 0 ) + return 0; + + /* Determine which cluster in the chain we are in */ + tbytes = ( vfs[d->driveid]->SectorsPerCluster * vfs[d->driveid]->BytesPerSector ); + umask = tbytes - 1; + cluster = ( d->fpos / tbytes ); + + /* Rewind current cluster */ + d->CurrentCluster = d->FirstCluster; + + /* Bring this cluster into view */ + for ( i = 0; i < cluster; i++ ) + d->CurrentCluster = SWAP16(vfs[d->driveid]->FAT[d->CurrentCluster]); + + /* Read the cluster */ + if ( d->CachedCluster != d->CurrentCluster ) + ReadCluster(d); + + /* Get real read length */ + reallength = ( d->fpos + length ) < d->fsize ? length : d->fsize - d->fpos; + + if ( reallength <= 0 ) + return 0; + + /* Move data */ + while( reallength ) + { + if ( !(d->fpos & umask) && ( reallength >= tbytes ) ) + { + /* Move a full cluster */ + memcpy(p + bytesdone, d->clusterdata, tbytes); + reallength -= tbytes; + bytesdone += tbytes; + d->fpos += tbytes; + } + else + { + p[bytesdone++] = d->clusterdata[d->fpos & umask]; + d->fpos++; + reallength--; + } + + if ( !( d->fpos & umask ) ) + { + if ( NextCluster(d) ) + { + ReadCluster(d); + } + else + return bytesdone; + } + } + + return bytesdone; +} + +/**************************************************************************** +* VFAT_fseek +****************************************************************************/ +int VFAT_fseek( FSDIRENTRY *d, int where, int whence ) +{ + switch( whence ) + { + case SEEK_SET: + if ( ( where >= 0 ) && ( where <= d->fsize ) ) + { + d->fpos = where; + return FS_FILE_OK; + } + break; + + case SEEK_CUR: + if ( ( ( d->fpos + where ) >= 0 ) && ( ( d->fpos + where ) <= d->fsize ) ) + { + d->fpos += where; + return FS_FILE_OK; + } + break; + + case SEEK_END: + if ( ( where <= 0 ) && ( abs(where) <= d->fsize ) ) + { + d->fpos = d->fsize + where; + return FS_FILE_OK; + } + break; + } + + return FS_NO_FILE; +} + +/**************************************************************************** +* VFAT_ftell +* +* Return the current position of a file +****************************************************************************/ +int VFAT_ftell( FSDIRENTRY *d ) +{ + return d->fpos; +} + diff --git a/source/drivers/gamecube/wiisd/vfat.h b/source/drivers/gamecube/wiisd/vfat.h new file mode 100644 index 0000000..9ad8619 --- /dev/null +++ b/source/drivers/gamecube/wiisd/vfat.h @@ -0,0 +1,364 @@ +/**************************************************************************** +* FAT16 - VFAT Support +* +* NOTE: Only supports FAT16 with Long File Names +* I have no interest in adding FAT32 +* +* Reference Documentation: +* +* FAT: General Overview of On-Disk Format +* Version 1.02 May 05, 1999 +* Microsoft Corporation +* +* FAT: General Overview of On-Disk Format +* Version 1.03 December 06, 2000 +* Microsoft Corporation +* +* This is targetted at MMC/SD cards. +* +* Copyright softdev 2007. All rights reserved. +* +* $Date: 2007-08-05 14:27:48 +0100 (Sun, 05 Aug 2007) $ +* $Rev: 8 $ +****************************************************************************/ +#ifndef __FATVFAT__ +#define __FATVFAT__ + +#include "integer.h" + +/* x86 type definitions */ +/**\typedef VDWORD + * Unsigned 32 bit integer + */ +typedef unsigned int VDWORD; +/**\typedef WORD + * Unsigned 16 bit short + */ +//typedef unsigned short WORD; +/**\typedef BYTE + * Unsigned 8 bit char + */ +//typedef unsigned char BYTE; + +/* Big Endian Support */ +#ifdef WORDS_BIGENDIAN +#define SWAP16(a) (((a&0xff)<<8) | ((a&0xff00)>>8)) +#define SWAP32(a) (((a&0xff000000)>>24) | ((a&0xff0000) >> 8) | ((a&0xff00)<<8) |((a&0xff)<<24)) +#else +#define SWAP16(a) (a) +#define SWAP32(a) (a) +#endif + +/* General */ +/**\def SECTOR_SIZE + * Set to sector size of device\n + * Default is 512, which is suitable for most SD cards\n + * All sector sizes must be a power of 2 + */ +#define SECTOR_SIZE 512 /* Default sector is 512 bytes */ +/**\def SECTOR_SHIFT_BITS + * Number of shift bits for faster multiplication and division + */ +#define SECTOR_SHIFT_BITS 9 /* Sector shift bits */ +#define LFN_LAST_ENTRY 0x40 /* Long File Name last entry mask */ +#define LFN_ENTRY_MASK 0x3F /* Long File Name entry number mask */ +#define ROOTCLUSTER 0xdeadc0de /* Unique root directory cluster id */ + +/**\def PSEP + * Path separator. Default unix style / + */ +#define PSEP '/' /* Path separator. Default unix / */ +/**\def PSEPS + * Path separator string. Default unix style / + */ +#define PSEPS "/" /* Path separator string. Default unix / */ + +#define DIR_ROOT "." /* Root directory entry */ +#define DIR_PARENT ".." /* Parent directory entry */ + +/* FSTYPES */ +#define FS_TYPE_NONE 0 +#define FS_TYPE_FAT16 1 + +/* Errors */ +#define FS_FILE_OK 0 +#define FS_SUCCESS FS_FILE_OK +#define FS_ERR_NOMEM -128 +#define FS_NO_FILE -64 +#define FS_ERR_IO -32 +#define FS_ERR_PARAM -16 + +/* File modes */ +#define FS_READ 1 + +/* Gamecube Specific */ +#define FS_SLOTA 0 +#define FS_SLOTB 1 + +/* FAT12/16 */ +typedef struct + { + BYTE jmpBoot[3]; /**< Always 0xEBxx90 or 0xE9xxxx */ + BYTE OEMName[8]; /**< OEM Name 'MSWIN4.1' or similar */ + WORD bytesPerSec; /**< Bytes per sector */ + BYTE secPerClust; /**< Sectors per cluster */ + WORD reservedSec; /**< Reserved Sector Count */ + BYTE numFATs; /**< Number of FAT copies */ + WORD rootEntCount; /**< FAT12/16 number of root entries. */ + WORD totSec16; /**< Sector count if < 0x10000 */ + BYTE media; /**< Media ID byte (HD == 0xF8) */ + WORD FATsz16; /**< Sectors occupied by one copy of FAT */ + WORD secPerTrack; /**< Sectors per track */ + WORD numHeads; /**< Number of heads */ + VDWORD hiddenSec; /**< Hidden sector count */ + VDWORD totSec32; /**< Total sectors when >= 0x10000 */ + BYTE drvNum; /**< BIOS Drive Number (0x80) */ + BYTE reserved1; /**< Unused - always zero */ + BYTE bootSig; /**< Boot signature */ + VDWORD volID; /**< Volume serial number */ + BYTE volName[11]; /**< Volume Name */ + BYTE FilSysType[8]; /**< File system type */ + BYTE filler[SECTOR_SIZE-64]; /**< Byte padding */ + BYTE sigkey1; /**< 0x55 */ + BYTE sigkey2; /**< 0xAA */ + } +#ifndef DOXYGEN_FIX_ATTR +__attribute__((__packed__)) +#endif +BPB16; + +/* Partition entry */ +typedef struct + { + BYTE bootindicator; /**< Boot indicator 00 == No 0x80 == Boot */ + BYTE startCHS[3]; /**< Start Cylinder / Head / Sector */ + BYTE partitiontype; /**< Partition Type. ID 06 FAT 16 */ + BYTE endCHS[3]; /**< End Cylinder / Head / Sector */ + VDWORD partitionstart; /**< LBA Start Sector */ + VDWORD partitionsize; /**< Partition size in sectors */ + } +#ifndef DOXYGEN_FIX_ATTR +__attribute__((__packed__)) +#endif +PARTENTRY; + +/* VFAT - Main structure */ +typedef struct + { + VDWORD BaseOffset; /**< Offset in sectors to BPB */ + VDWORD SectorsPerCluster; /**< Sectors per cluster (Native) */ + VDWORD BytesPerSector; /**< Bytes per sector (Native) */ + VDWORD ReservedSectors; /**< Reserved sectors (Native) */ + VDWORD RootDirSectors; /**< Root directory sectors (Native) */ + VDWORD SectorsPerFAT; /**< Sectors per FAT (Native) */ + VDWORD NumberOfFATs; /**< Number of FAT copies (Native) */ + VDWORD FirstDataSector; /**< First data sector offset (Native) */ + VDWORD TotalSectors; /**< Total sectors (Native) */ + VDWORD CountOfClusters; /**< Count of clusters (Native) */ + VDWORD DataSectors; /**< Number of Data sectors (Native) */ + VDWORD RootDirOffset; /**< Offset of root directory (Native) */ + VDWORD FirstFATOffset; /**< Offset to first copy of FAT (Native) */ + VDWORD RootDirEntries; /**< Number of root directory entries (Native) */ + WORD *FAT; /**< Holds first FAT copy */ + BYTE *rootDir; /**< Holds entire root directory */ + } +#ifndef DOXYGEN_FIX_ATTR +__attribute__((__packed__)) +#endif +VFATFS; + +/* + * Directory Functions + */ + +#define MAX_LONG_NAME 256 + +/* Directory entry attributes */ +#define ATTR_READ_ONLY 0x01 +#define ATTR_HIDDEN 0x02 +#define ATTR_SYSTEM 0x04 +#define ATTR_VOLUME_ID 0x08 +#define ATTR_DIRECTORY 0x10 +#define ATTR_ARCHIVE 0x20 +#define ATTR_LONG_NAME (ATTR_READ_ONLY | \ + ATTR_HIDDEN | \ + ATTR_SYSTEM | \ + ATTR_VOLUME_ID ) + +#define ATTR_LONG_NAME_MASK ( ATTR_READ_ONLY | \ + ATTR_HIDDEN | \ + ATTR_SYSTEM | \ + ATTR_VOLUME_ID | \ + ATTR_DIRECTORY | \ + ATTR_ARCHIVE ) + +/**\def CLUSTER_END_CHAIN + * Any value equal or greater than 0xFFF8 should be + * considered an end of chain marker. + */ +#define CLUSTER_END_CHAIN 0xFFF8 + +/**\def CLUSTER_BAD + * Documentation states that any BAD or unusable sector should be marked\n + * as 0xFFF7. + */ +#define CLUSTER_BAD 0xFFF7 + +/* Short file name */ +typedef struct + { + BYTE dirname[11]; /**< Record name */ + BYTE attribute; /**< Attributes */ + BYTE NTReserved; /**< Reserved for Windows NT - set 0 */ + BYTE dirTenthSecs; /**< Tenth of a second, 0-199 */ + WORD dirCreateTime; /**< Time of creation */ + WORD dirCreateDate; /**< Date of creation */ + WORD dirLastAccDate;/**< Date of last access */ + WORD fstClustHigh; /**< High word of first cluster - ZERO on FAT16 (LE)*/ + WORD dirWriteTime; /**< Time of last write */ + WORD dirWriteDate; /**< Date of last write */ + WORD fstClustLow; /**< Low word of first cluster (LE)*/ + VDWORD filesize; /**< Filesize in bytes (LE)*/ + } +#ifndef DOXYGEN_FIX_ATTR +__attribute__((__packed__)) +#endif +SFNDIRREC; + +/* Long file name */ +typedef struct + { + BYTE ordinal; /**< Entry number (0x4x) == Last entry */ + BYTE dirname1[10]; /**< First part of filename in unicode */ + BYTE attribute; /**< Attributes - MUST be ATTR_LONG_NAME (0x0F) */ + BYTE type; /**< Reserved */ + BYTE checksum; /**< SFN Checksum */ + BYTE dirname2[12]; /**< Second part of filename in unicode */ + WORD fstClustLo; /**< MUST BE ZERO! */ + BYTE dirname3[4]; /**< Third part of filename in unicode */ + } +#ifndef DOXYGEN_FIX_ATTR +__attribute__((__packed__)) +#endif +LFNDIRREC; + +/* User dir entry */ +typedef struct + { + BYTE longname[MAX_LONG_NAME]; /**< Long file name, application friendly */ + BYTE shortname[13]; /**< Short file name, application friendly */ + VDWORD fpos; /**< Current file position */ + VDWORD fsize; /**< File size in bytes (Native) */ + VDWORD driveid; /**< Device number */ + VDWORD FirstCluster; /**< First cluster in chain (Native) */ + VDWORD CurrentCluster; /**< Current cluster in chain (Native) */ + VDWORD CachedCluster; /**< Cached cluster (Native) */ + VDWORD CurrentDirEntry; /**< Current directory entry in current cluster (Native) */ + VDWORD crosscluster; /**< Record crosses cluster boundary */ + BYTE *clusterdata; /**< Cluster data */ + /* Now a copy of the current directory entry */ + SFNDIRREC dirent; /**< Copy of physical SFNDIRREC */ + } +#ifndef DOXYGEN_FIX_ATTR +__attribute__((__packed__)) +#endif +FSDIRENTRY; + +/* VFAT API */ +/** \defgroup VFATDir VFATFS Directory Level Functions + * \brief VFAT FS Directory Functions + */ + +/* Directory */ + +/** \ingroup VFATDir + * \brief Open a directory + * \param drive Device number to use + * \param d Pointer to user provided FSDIRENTRY + * \param search Path to open + * \return FS_SUCCESS if succesful + */ +int VFAT_opendir( int drive, FSDIRENTRY *d, char *search ); + +/** \ingroup VFATDir + * \brief Read directory entries + * \param d Pointer to user provided FSDIRENTRY, previously populated from VFAT_opendir + * \return FS_SUCCESS if successful + */ +int VFAT_readdir( FSDIRENTRY *d ); + +/** \ingroup VFATDir + * \brief Close a previously opened directory + * \param d Pointer to user provided FSDIRENTRY, previously populated from VFAT_opendir + * \return None. + */ +void VFAT_closedir( FSDIRENTRY *d ); + +/** \defgroup VFATFile VFATFS File Level Functions + * \brief VFAT FS File functions + */ + +/** \ingroup VFATFile + * \brief Open a file + * \param drive Device number to use + * \param d Pointer to user provided FSDIRENTRY + * \param fname Filename, including full path, to open + * \param mode Currently only FS_READ is supported + * \return FS_SUCCESS if successful. + */ +int VFAT_fopen( int drive, FSDIRENTRY *d, char *fname, int mode ); + +/** \ingroup VFATFile + * \brief Close a previously opened file + * \param d Pointer to user provided FSDIRENTRY, previously populated by VFAT_fopen + * \return None. + */ +void VFAT_fclose( FSDIRENTRY *d ); + +/** \ingroup VFATFile + * \brief Read from an open file + * \param d Pointer to user provided FSDIRENTRY, populated by a successful call to VFAT_fopen + * \param buffer Buffer to receive data + * \param length Number of bytes to read to the buffer + * \return Number of bytes read if positive. If <0 an error has occurred. + */ +int VFAT_fread( FSDIRENTRY *d, void *buffer, int length ); + +/** \ingroup VFATFile + * \brief Return the current file position + * \param d Pointer to user provided FSDIRENTRY + * \return Current file position + */ +int VFAT_ftell( FSDIRENTRY *d ); + +/** \ingroup VFATFile + * \brief Move current file pointer to the requested position + * \param d Pointer to user provided FSDIRENTRY + * \param where New byte position + * \param whence Define movement type. SEEK_SET, SEEK_END and SEEK_CUR are supported + * \return FS_SUCCESS if successful + */ +int VFAT_fseek( FSDIRENTRY *d, int where, int whence ); + +/** \defgroup VFATMount VFATFS Device Mount Functions + * \brief Device mount / unmount functions + */ + +/** \ingroup VFATMount + * \brief Mount a device + * \param driveid Device number to mount + * \param v Pointer to user provided VFATFS + * \return FS_TYPE_FAT16 on success. + */ +int VFAT_mount( int driveid, VFATFS *v ); + +/** \ingroup VFATMount + * \brief Unmount a device + * \param driveid Device drive number + * \param v Pointer to user provided VFATFS + * \return None + */ +void VFAT_unmount( int driveid, VFATFS *v ); + +#endif +