This commit is contained in:
ekeeke31 2007-08-20 09:45:09 +00:00
parent 254124dbca
commit fb745c3b0a
4 changed files with 1202 additions and 0 deletions

99
source/ngc/vfat/diskio.c Normal file
View File

@ -0,0 +1,99 @@
/****************************************************************************
* 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
****************************************************************************/
#include <gccore.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sdcard.h>
#include "vfat.h"
/* Not so public exports from card_io.c */
#define MAX_DRIVE 2
extern u8 g_CID[MAX_DRIVE][16];
extern u8 g_CSD[MAX_DRIVE][16];
extern u8 g_CardStatus[MAX_DRIVE][64];
extern s32 card_initIO(s32 drv_no);
extern s32 card_readSector(s32 drv_no,u32 sector_no,u8 *buf,u32 len);
extern s32 card_writeSector(s32 drv_no,u32 sector_no,const void *buf,u32 len);
extern s32 card_readStatus(s32 drv_no);
#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 = card_initIO( drive );
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 = card_readSector( (int)drive, sector + i, buffer + bytes, SECTOR_SIZE);
if ( res != CARDIO_ERROR_READY )
return FS_ERR_IO;
bytes += SECTOR_SIZE;
}
if ( res == CARDIO_ERROR_READY )
return FS_SUCCESS;
return FS_ERR_IO;
}

36
source/ngc/vfat/diskio.h Normal file
View File

@ -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

841
source/ngc/vfat/vfat.c Normal file
View File

@ -0,0 +1,841 @@
/****************************************************************************
* 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-03 13:23:19 +0100 (Fri, 03 Aug 2007) $
* $Rev: 3 $
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "vfat.h"
#include "diskio.h"
static BYTE sector[SECTOR_SIZE]; /* Local sector buffer */
static VFATFS *vfs[2]; /* VFAT 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.
*
*/
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;
}
static 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;
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;
}
/**
* 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
****************************************************************************/
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
****************************************************************************/
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.
****************************************************************************/
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
*/
static int SectorFromCluster( int drive, int cluster )
{
VFATFS *v = vfs[drive];
return ( ( cluster - 2 ) * v->SectorsPerCluster ) + v->FirstDataSector;
}
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;
}
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
****************************************************************************/
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.
****************************************************************************/
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
****************************************************************************/
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;
}

226
source/ngc/vfat/vfat.h Normal file
View File

@ -0,0 +1,226 @@
/****************************************************************************
* 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-03 13:23:19 +0100 (Fri, 03 Aug 2007) $
* $Rev: 3 $
****************************************************************************/
#ifndef __FATVFAT__
#define __FATVFAT__
/* x86 type definitions */
typedef unsigned int DWORD;
typedef unsigned short WORD;
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 */
#define SECTOR_SIZE 512
#define SECTOR_SHIFT_BITS 9
#define LFN_LAST_ENTRY 0x40
#define LFN_ENTRY_MASK 0x3F
#define ROOTCLUSTER 0xdeadc0de
#define PSEP '/'
#define PSEPS "/"
#define DIR_ROOT "."
#define DIR_PARENT ".."
/* 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 */
DWORD hiddenSec; /* Hidden sector count */
DWORD totSec32; /* Total sectors when >= 0x10000 */
BYTE drvNum; /* BIOS Drive Number (0x80) */
BYTE reserved1; /* Unused - always zero */
BYTE bootSig; /* Boot signature */
DWORD volID; /* Volume serial number */
BYTE volName[11]; /* Volume Name */
BYTE FilSysType[8]; /* File system type */
BYTE filler[SECTOR_SIZE-64];
BYTE sigkey1; /* 0x55 */
BYTE sigkey2; /* 0xAA */
}
__attribute__((__packed__)) BPB16;
/* Partition entry */
typedef struct
{
BYTE bootindicator;
BYTE startCHS[3];
BYTE partitiontype;
BYTE endCHS[3];
DWORD partitionstart;
DWORD partitionsize;
}
__attribute__((__packed__)) PARTENTRY;
/* VFAT - Main structure */
typedef struct
{
DWORD BaseOffset;
DWORD SectorsPerCluster;
DWORD BytesPerSector;
DWORD ReservedSectors;
DWORD RootDirSectors;
DWORD SectorsPerFAT;
DWORD NumberOfFATs;
DWORD FirstDataSector;
DWORD TotalSectors;
DWORD CountOfClusters;
DWORD DataSectors;
DWORD RootDirOffset;
DWORD FirstFATOffset;
DWORD RootDirEntries;
WORD *FAT; /* Holds first FAT copy */
BYTE *rootDir; /* Holds entire root directory */
}
__attribute__((__packed__)) VFATFS;
/**
* Directory
*/
#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 )
#define CLUSTER_END_CHAIN 0xFFF8
#define CLUSTER_BAD 0xFFF7
/* Short file name */
typedef struct
{
BYTE dirname[11]; /* Record name */
BYTE attribute; /* Attributes */
BYTE NTReserved; /* Reserved for Window 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 */
WORD dirWriteTime; /* Time of last write */
WORD dirWriteDate; /* Date of last write */
WORD fstClustLow; /* Low word of first cluster */
DWORD filesize; /* Filesize in bytes */
}
__attribute__((__packed__)) SFNDIRREC;
/* Long file name */
typedef struct
{
BYTE ordinal; /* Entry number */
BYTE dirname1[10];
BYTE attribute; /* Attributes */
BYTE type; /* Reserved */
BYTE checksum; /* SFN Checksum */
BYTE dirname2[12];
WORD fstClustLo; /* MUST BE ZERO */
BYTE dirname3[4];
}
__attribute__((__packed__)) LFNDIRREC;
/* User dir entry */
typedef struct
{
BYTE longname[MAX_LONG_NAME];
BYTE shortname[13]; /* Keep word aligned*/
DWORD fpos;
DWORD fsize;
DWORD driveid;
DWORD FirstCluster;
DWORD CurrentCluster;
DWORD CachedCluster;
DWORD CurrentDirEntry;
DWORD crosscluster;
BYTE *clusterdata;
/* Now a copy of the current directory entry */
SFNDIRREC dirent;
}
__attribute__((__packed__)) FSDIRENTRY;
/* VFAT API */
/* Directory */
int VFAT_opendir( int drive, FSDIRENTRY *d, char *search );
int VFAT_readdir( FSDIRENTRY *d );
void VFAT_closedir( FSDIRENTRY *d );
int VFAT_fopen( int drive, FSDIRENTRY *d, char *fname, int mode );
void VFAT_fclose( FSDIRENTRY *d );
int VFAT_fread( FSDIRENTRY *d, void *buffer, int length );
int VFAT_ftell( FSDIRENTRY *d );
int VFAT_fseek( FSDIRENTRY *d, int where, int whence );
int VFAT_mount( int driveid, VFATFS *v );
#endif