Author Topic: gba_nds_fat.c  (Read 5084 times)

0 Members and 1 Guest are viewing this topic.

Offline Dr.neo

  • Administrator
  • Hero Member
  • *****
  • Posts: 3826
gba_nds_fat.c
« on: July 02, 2007, 01:17:45 AM »
//
//
//     Welcome to join our develope team 
//      contact us now for more details
//
//    Mail&MSN: neoflash_team@hotmail.com
//           mIRC : #neo @ EFnet
//
//         http://www.neoflash.com     
//                                     
//

/*
   gba_nds_fat.c
   By chishm (Michael Chisholm)

   Routines for reading a compact flash card
   using the GBA Movie Player or M3.

   Some FAT routines are based on those in fat.c, which
   is part of avrlib by Pascal Stang.

   This software is completely free. No warranty is provided.
   If you use it, please give me credit and email me about your
   project at chishm@hotmail.com

   See gba_nds_fat.txt for help and license details.
*/

//---------------------------------------------------------------
// Includes

#include "gba_nds_fat.h"
#include "disc_io.h"
#include <string.h>
#ifdef NDS
 #include <nds/ipc.h>   // Time on the NDS
#endif
//----------------------------------------------------------------
// Data   types
#ifndef   NULL
 #define   NULL   0
#endif

//----------------------------------------------------------------
// NDS memory access control register
#ifdef NDS
 #ifndef WAIT_CR
  #define WAIT_CR (*(vu16*)0x04000204)
 #endif
#endif

//---------------------------------------------------------------
// Appropriate placement of CF functions and data
#ifdef NDS
 #define _VARS_IN_RAM
#else
 #define _VARS_IN_RAM __attribute__ ((section (".sbss")))
#endif


//-----------------------------------------------------------------
// FAT constants
#define CLUSTER_EOF_16   0xFFFF
#define   CLUSTER_EOF   0x0FFFFFFF
#define CLUSTER_FREE   0x0000
#define CLUSTER_FIRST   0x0002

#define FILE_LAST 0x00
#define FILE_FREE 0xE5

#define FAT16_ROOT_DIR_CLUSTER 0x00


//-----------------------------------------------------------------
// long file name constants
#define LFN_END 0x40
#define LFN_DEL 0x80

//-----------------------------------------------------------------
// Data Structures

// Take care of packing for GCC - it doesn't obey pragma pack()
// properly for ARM targets.
#ifdef __GNUC__
 #define __PACKED __attribute__ ((__packed__))
#else
 #define __PACKED
 #pragma pack(1)
#endif

// Boot Sector - must be packed
typedef struct
{
   __PACKED   u8   jmpBoot[3];
   __PACKED   u8   OEMName[8];
   // BIOS Parameter Block
   __PACKED   u16   bytesPerSector;
   __PACKED   u8   sectorsPerCluster;
   __PACKED   u16   reservedSectors;
   __PACKED   u8   numFATs;
   __PACKED   u16   rootEntries;
   __PACKED   u16   numSectorsSmall;
   __PACKED   u8   mediaDesc;
   __PACKED   u16   sectorsPerFAT;
   __PACKED   u16   sectorsPerTrk;
   __PACKED   u16   numHeads;
   __PACKED   u32   numHiddenSectors;
   __PACKED   u32   numSectors;
   union   // Different types of extended BIOS Parameter Block for FAT16 and FAT32
   {
      struct
      {
         // Ext BIOS Parameter Block for FAT16
         __PACKED   u8   driveNumber;
         __PACKED   u8   reserved1;
         __PACKED   u8   extBootSig;
         __PACKED   u32   volumeID;
         __PACKED   u8   volumeLabel[11];
         __PACKED   u8   fileSysType[8];
         // Bootcode
         __PACKED   u8   bootCode[448];
      }   fat16;
      struct
      {
         // FAT32 extended block
         __PACKED   u32   sectorsPerFAT32;
         __PACKED   u16   extFlags;
         __PACKED   u16   fsVer;
         __PACKED   u32   rootClus;
         __PACKED   u16   fsInfo;
         __PACKED   u16   bkBootSec;
         __PACKED   u8   reserved[12];
         // Ext BIOS Parameter Block for FAT16
         __PACKED   u8   driveNumber;
         __PACKED   u8   reserved1;
         __PACKED   u8   extBootSig;
         __PACKED   u32   volumeID;
         __PACKED   u8   volumeLabel[11];
         __PACKED   u8   fileSysType[8];
         // Bootcode
         __PACKED   u8   bootCode[420];
      }   fat32;
   }   extBlock;

   __PACKED   u16   bootSig;

}   BOOT_SEC;

// Directory entry - must be packed
typedef struct
{
   __PACKED   u8   name[8];
   __PACKED   u8   ext[3];
   __PACKED   u8   attrib;
   __PACKED   u8   reserved;
   __PACKED   u8   cTime_ms;
   __PACKED   u16   cTime;
   __PACKED   u16   cDate;
   __PACKED   u16   aDate;
   __PACKED   u16   startClusterHigh;
   __PACKED   u16   mTime;
   __PACKED   u16   mDate;
   __PACKED   u16   startCluster;
   __PACKED   u32   fileSize;
}   DIR_ENT;

// Long file name directory entry - must be packed
typedef struct
{
   __PACKED   u8 ordinal;   // Position within LFN
   __PACKED   u16 char0;
   __PACKED   u16 char1;
   __PACKED   u16 char2;
   __PACKED   u16 char3;
   __PACKED   u16 char4;
   __PACKED   u8 flag;   // Should be equal to ATTRIB_LFN
   __PACKED   u8 reserved1;   // Always 0x00
   __PACKED   u8 checkSum;   // Checksum of short file name (alias)
   __PACKED   u16 char5;
   __PACKED   u16 char6;
   __PACKED   u16 char7;
   __PACKED   u16 char8;
   __PACKED   u16 char9;
   __PACKED   u16 char10;
   __PACKED   u16 reserved2;   // Always 0x0000
   __PACKED   u16 char11;
   __PACKED   u16 char12;
}   DIR_ENT_LFN;

const char lfn_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};

// End of packed structs
#ifdef __PACKED
 #undef __PACKED
#endif
#ifndef __GNUC__
 #pragma pack()
#endif

//-----------------------------------------------------------------
// Global Variables

// _VARS_IN_RAM variables are stored in the largest section of WRAM
// available: IWRAM on NDS ARM7, EWRAM on NDS ARM9 and GBA

// Files
_VARS_IN_RAM FAT_FILE openFiles[MAX_FILES_OPEN];

// Long File names
_VARS_IN_RAM char lfnName[MAX_FILENAME_LENGTH];
bool lfnExists;

// Locations on card
int filesysRootDir;
int filesysRootDirClus;
int filesysFAT;
int filesysSecPerFAT;
int filesysNumSec;
int filesysData;
int filesysBytePerSec;
int filesysSecPerClus;
int filesysBytePerClus;

FS_TYPE filesysType = FS_UNKNOWN;
u32 filesysTotalSize;

// Info about FAT
u32 fatLastCluster;
u32 fatFirstFree;

// fatBuffer used to reduce wear on the CF card from multiple writes
_VARS_IN_RAM char fatBuffer[BYTE_PER_READ];
u32 fatBufferCurSector;

// Current working directory
u32 curWorkDirCluster;

// Position of the directory entry last retreived with FAT_GetDirEntry
u32 wrkDirCluster;
int wrkDirSector;
int wrkDirOffset;

// Global sector buffer to save on stack space
_VARS_IN_RAM unsigned char globalBuffer[BYTE_PER_READ];

//-----------------------------------------------------------------
// Functions contained in this file - predeclarations
char ucase (char character);
u16 getRTCtoFileTime (void);
u16 getRTCtoFileDate (void);

bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry);
bool FAT_ClearLinks (u32 cluster);
DIR_ENT FAT_DirEntFromPath (const char* path);
u32 FAT_FirstFreeCluster(void);
DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin);
u32 FAT_LinkFreeCluster(u32 cluster);
u32 FAT_NextCluster(u32 cluster);
bool FAT_WriteFatEntry (u32 cluster, u32 value);
bool FAT_GetFilename (DIR_ENT dirEntry, char* alias);

bool FAT_InitFiles (void);
bool FAT_FreeFiles (void);
int FAT_remove (const char* path);
bool FAT_chdir (const char* path);
FILE_TYPE FAT_FindFirstFile (char* filename);
FILE_TYPE FAT_FindNextFile (char* filename);
FILE_TYPE FAT_FileExists (const char* filename);
bool FAT_GetAlias (char* alias);
bool FAT_GetLongFilename (char* filename);
u32 FAT_GetFileSize (void);
u32 FAT_GetFileCluster (void);

FAT_FILE* FAT_fopen(const char* path, const char* mode);
bool FAT_fclose (FAT_FILE* file);
bool FAT_feof(FAT_FILE* file);
int FAT_fseek(FAT_FILE* file, s32 offset, int origin);
u32 FAT_ftell (FAT_FILE* file);
u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file);
u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file);
char FAT_fgetc (FAT_FILE* file);
char FAT_fputc (char c, FAT_FILE* file);

/*-----------------------------------------------------------------
ucase
Returns the uppercase version of the given char
char IN: a character
char return OUT: uppercase version of character
-----------------------------------------------------------------*/
char ucase (char character)
{
   if ((character > 0x60) && (character < 0x7B))
      character = character - 0x20;
   return (character);
}


/*-----------------------------------------------------------------
getRTCtoFileTime and getRTCtoFileDate
Returns the time / date in Dir Entry styled format
u16 return OUT: time / date in Dir Entry styled format
-----------------------------------------------------------------*/
u16 getRTCtoFileTime (void)
{
#ifdef NDS
   return (
      ( ( (IPC->rtc_hours > 11 ? IPC->rtc_hours - 40 : IPC->rtc_hours) & 0x1F) << 11) |
      ( (IPC->rtc_minutes & 0x3F) << 5) |
      ( (IPC->rtc_seconds >> 1) & 0x1F) );
#else
   return 0;
#endif
}

u16 getRTCtoFileDate (void)
{
#ifdef NDS
   return (
      ( ((IPC->rtc_year + 20) & 0x7F) <<9) |
      ( (IPC->rtc_month & 0xF) << 5) |
      (IPC->rtc_day & 0x1F) );
#else
   return 0;
#endif
}


/*-----------------------------------------------------------------
Disc level FAT routines
-----------------------------------------------------------------*/
#define FAT_ClustToSect(m) \
   (((m-2) * filesysSecPerClus) + filesysData)

/*-----------------------------------------------------------------
FAT_NextCluster
Internal function - gets the cluster linked from input cluster
-----------------------------------------------------------------*/
u32 FAT_NextCluster(u32 cluster)
{
   u32 nextCluster = CLUSTER_FREE;
   u32 sector;
   int offset;

   switch (filesysType)
   {
      case FS_UNKNOWN:
         nextCluster = CLUSTER_FREE;
         break;

      case FS_FAT12:
         sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
         offset = ((cluster * 3) / 2) % BYTE_PER_READ;

         // If FAT buffer contains wrong sector
         if (sector != fatBufferCurSector)
         {
            // Load correct sector to buffer
            fatBufferCurSector = sector;
            disc_ReadSector(fatBufferCurSector, fatBuffer);
         }

         nextCluster = ((u8*)fatBuffer)[offset];
         offset++;

         if (offset >= BYTE_PER_READ) {
            offset = 0;
            fatBufferCurSector++;
            disc_ReadSector(fatBufferCurSector, fatBuffer);
         }

         nextCluster |= (((u8*)fatBuffer)[offset]) << 8;

         if (cluster & 0x01) {
            nextCluster = nextCluster >> 4;
         } else    {
            nextCluster &= 0x0FFF;
         }

         if (nextCluster >= 0x0FF7)
         {
            nextCluster = CLUSTER_EOF;
         }

         break;

      case FS_FAT16:
         sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
         offset = cluster % (BYTE_PER_READ >> 1);

         // If FAT buffer contains wrong sector
         if (sector != fatBufferCurSector)
         {
            // Load correct sector to buffer
            fatBufferCurSector = sector;
            disc_ReadSector(fatBufferCurSector, fatBuffer);
         }

         // read the nextCluster value
         nextCluster = ((u16*)fatBuffer)[offset];

         if (nextCluster >= 0xFFF7)
         {
            nextCluster = CLUSTER_EOF;
         }
         break;

      case FS_FAT32:
         sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
         offset = cluster % (BYTE_PER_READ >> 2);

         // If FAT buffer contains wrong sector
         if (sector != fatBufferCurSector)
         {
            // Load correct sector to buffer
            fatBufferCurSector = sector;
            disc_ReadSector(fatBufferCurSector, fatBuffer);
         }

         // read the nextCluster value
         nextCluster = (((u32*)fatBuffer)[offset]) & 0x0FFFFFFF;

         if (nextCluster >= 0x0FFFFFF7)
         {
            nextCluster = CLUSTER_EOF;
         }
         break;

      default:
         nextCluster = CLUSTER_FREE;
         break;
   }

   return nextCluster;
}

#ifdef CAN_WRITE_TO_DISC
/*-----------------------------------------------------------------
FAT_WriteFatEntry
Internal function - writes FAT information about a cluster
-----------------------------------------------------------------*/
bool FAT_WriteFatEntry (u32 cluster, u32 value)
{
   u32 sector;
   int offset;

   if ((cluster < 0x0002) || (cluster > fatLastCluster))
   {
      return false;
   }

   switch (filesysType)
   {
      case FS_UNKNOWN:
         return false;
         break;

      case FS_FAT12:
         sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
         offset = ((cluster * 3) / 2) % BYTE_PER_READ;

         // If FAT buffer contains wrong sector
         if (sector != fatBufferCurSector)
         {
            // Load correct sector to buffer
            fatBufferCurSector = sector;
            disc_ReadSector(fatBufferCurSector, fatBuffer);
         }

         if (cluster & 0x01) {

            ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4);

            offset++;
            if (offset >= BYTE_PER_READ) {
               offset = 0;
               // write the buffer back to disc
               disc_WriteSector(fatBufferCurSector, fatBuffer);
               // read the next sector
               fatBufferCurSector++;
               disc_ReadSector(fatBufferCurSector, fatBuffer);
            }

            ((u8*)fatBuffer)[offset] =  (value & 0x0FF0) >> 4;

         } else {

            ((u8*)fatBuffer)[offset] = value & 0xFF;

            offset++;
            if (offset >= BYTE_PER_READ) {
               offset = 0;
               // write the buffer back to disc
               disc_WriteSector(fatBufferCurSector, fatBuffer);
               // read the next sector
               fatBufferCurSector++;
               disc_ReadSector(fatBufferCurSector, fatBuffer);
            }

            ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F);
         }

         break;

      case FS_FAT16:
         sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
         offset = cluster % (BYTE_PER_READ >> 1);

         // If FAT buffer contains wrong sector
         if (sector != fatBufferCurSector)
         {
            // Load correct sector to buffer
            fatBufferCurSector = sector;
            disc_ReadSector(fatBufferCurSector, fatBuffer);
         }

         // write the value to the FAT buffer
         ((u16*)fatBuffer)[offset] = (value & 0xFFFF);

         break;

      case FS_FAT32:
         sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
         offset = cluster % (BYTE_PER_READ >> 2);

         // If FAT buffer contains wrong sector
         if (sector != fatBufferCurSector)
         {
            // Load correct sector to buffer
            fatBufferCurSector = sector;
            disc_ReadSector(fatBufferCurSector, fatBuffer);
         }

         // write the value to the FAT buffer
         (((u32*)fatBuffer)[offset]) =  value;

         break;

      default:
         return false;
         break;
   }

   // write the buffer back to disc
   disc_WriteSector(fatBufferCurSector, fatBuffer);

   return true;
}
#endif

#ifdef CAN_WRITE_TO_DISC
/*-----------------------------------------------------------------
FAT_ReadWriteFatEntryBuffered
Internal function - writes FAT information about a cluster to a
 buffer that should then be flushed to disc using
 FAT_WriteFatEntryFlushBuffer()
 Call FAT_WriteFatEntry first so as not to ruin the disc.
 Also returns the entry being replaced
-----------------------------------------------------------------*/
u32 FAT_ReadWriteFatEntryBuffered (u32 cluster, u32 value)
{
   u32 sector;
   int offset;
   u32 oldValue;

   if ((cluster < 0x0002) || (cluster > fatLastCluster))
      return CLUSTER_FREE;


   switch (filesysType)
   {
      case FS_UNKNOWN:
         oldValue = CLUSTER_FREE;
         break;

      case FS_FAT12:
         sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
         offset = ((cluster * 3) / 2) % BYTE_PER_READ;

         // If FAT buffer contains wrong sector
         if (sector != fatBufferCurSector)
         {
            // write the old buffer to disc
            if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
               disc_WriteSector(fatBufferCurSector, fatBuffer);
            // Load correct sector to buffer
            fatBufferCurSector = sector;
            disc_ReadSector(fatBufferCurSector, fatBuffer);
         }

         if (cluster & 0x01) {

            oldValue = (((u8*)fatBuffer)[offset] & 0xF0) >> 4;
            ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4);

            offset++;
            if (offset >= BYTE_PER_READ) {
               offset = 0;
               // write the buffer back to disc
               disc_WriteSector(fatBufferCurSector, fatBuffer);
               // read the next sector
               fatBufferCurSector++;
               disc_ReadSector(fatBufferCurSector, fatBuffer);
            }

            oldValue |= ((((u8*)fatBuffer)[offset]) << 4) & 0x0FF0;
            ((u8*)fatBuffer)[offset] =  (value & 0x0FF0) >> 4;

         } else {

            oldValue = ((u8*)fatBuffer)[offset] & 0xFF;
            ((u8*)fatBuffer)[offset] = value & 0xFF;

            offset++;
            if (offset >= BYTE_PER_READ) {
               offset = 0;
               // write the buffer back to disc
               disc_WriteSector(fatBufferCurSector, fatBuffer);
               // read the next sector
               fatBufferCurSector++;
               disc_ReadSector(fatBufferCurSector, fatBuffer);
            }

            oldValue |= (((u8*)fatBuffer)[offset] & 0x0F) << 8;
            ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F);
         }

         if (oldValue >= 0x0FF7)
         {
            oldValue = CLUSTER_EOF;
         }

         break;

      case FS_FAT16:
         sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
         offset = cluster % (BYTE_PER_READ >> 1);

         // If FAT buffer contains wrong sector
         if (sector != fatBufferCurSector)
         {
            // write the old buffer to disc
            if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
               disc_WriteSector(fatBufferCurSector, fatBuffer);
            // Load correct sector to buffer
            fatBufferCurSector = sector;
            disc_ReadSector(fatBufferCurSector, fatBuffer);
         }

         // write the value to the FAT buffer
         oldValue = ((u16*)fatBuffer)[offset];
         ((u16*)fatBuffer)[offset] = value;

         if (oldValue >= 0xFFF7)
         {
            oldValue = CLUSTER_EOF;
         }

         break;

      case FS_FAT32:
         sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
         offset = cluster % (BYTE_PER_READ >> 2);

         // If FAT buffer contains wrong sector
         if (sector != fatBufferCurSector)
         {
            // write the old buffer to disc
            if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
               disc_WriteSector(fatBufferCurSector, fatBuffer);
            // Load correct sector to buffer
            fatBufferCurSector = sector;
            disc_ReadSector(fatBufferCurSector, fatBuffer);
         }

         // write the value to the FAT buffer
         oldValue = ((u32*)fatBuffer)[offset];
         ((u32*)fatBuffer)[offset] =  value;

         if (oldValue >= 0x0FFFFFF7)
         {
            oldValue = CLUSTER_EOF;
         }

         break;

      default:
         oldValue = CLUSTER_FREE;
         break;
   }

   return oldValue;
}
#endif

#ifdef CAN_WRITE_TO_DISC
/*-----------------------------------------------------------------
FAT_WriteFatEntryFlushBuffer
Flush the FAT buffer back to the disc
-----------------------------------------------------------------*/
bool FAT_WriteFatEntryFlushBuffer (void)
{
   // write the buffer disc
   if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
   {
      disc_WriteSector(fatBufferCurSector, fatBuffer);
      return true;
   } else {
      return false;
   }
}
#endif

#ifdef CAN_WRITE_TO_DISC
/*-----------------------------------------------------------------
FAT_FirstFreeCluster
Internal function - gets the first available free cluster
-----------------------------------------------------------------*/
u32 FAT_FirstFreeCluster(void)
{
   // Start at first valid cluster
   if (fatFirstFree < CLUSTER_FIRST)
      fatFirstFree = CLUSTER_FIRST;

   while ((FAT_NextCluster(fatFirstFree) != CLUSTER_FREE) && (fatFirstFree <= fatLastCluster))
   {
      fatFirstFree++;
   }
   if (fatFirstFree > fatLastCluster)
   {
      return CLUSTER_EOF;
   }
   return fatFirstFree;
}
#endif

#ifdef CAN_WRITE_TO_DISC
/*-----------------------------------------------------------------
FAT_LinkFreeCluster
Internal function - gets the first available free cluster, sets it
to end of file, links the input cluster to it then returns the
cluster number
-----------------------------------------------------------------*/
u32 FAT_LinkFreeCluster(u32 cluster)
{
   u32 firstFree;
   u32 curLink;

   if (cluster > fatLastCluster)
   {
      return CLUSTER_FREE;
   }

   // Check if the cluster already has a link, and return it if so
   curLink = FAT_NextCluster (cluster);
   if ((curLink >= CLUSTER_FIRST) && (curLink < fatLastCluster))
   {
      return curLink;   // Return the current link - don't allocate a new one
   }

   // Get a free cluster
   firstFree = FAT_FirstFreeCluster();

   // If couldn't get a free cluster then return
   if (firstFree == CLUSTER_EOF)
   {
      return CLUSTER_FREE;
   }

   if ((cluster >= CLUSTER_FIRST) && (cluster < fatLastCluster))
   {
      // Update the linked from FAT entry
      FAT_WriteFatEntry (cluster, firstFree);
   }
   // Create the linked to FAT entry
   FAT_WriteFatEntry (firstFree, CLUSTER_EOF);

   return firstFree;
}
#endif


#ifdef CAN_WRITE_TO_DISC
/*-----------------------------------------------------------------
FAT_ClearLinks
Internal function - frees any cluster used by a file
-----------------------------------------------------------------*/
bool FAT_ClearLinks (u32 cluster)
{
   u32 nextCluster;

   if ((cluster < 0x0002) || (cluster > fatLastCluster))
      return false;

   // Store next cluster before erasing the link
   nextCluster = FAT_NextCluster (cluster);

   // Erase the link
   FAT_WriteFatEntry (cluster, CLUSTER_FREE);

   // Move onto next cluster
   cluster = nextCluster;

   while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE))
   {
      cluster = FAT_ReadWriteFatEntryBuffered (cluster, CLUSTER_FREE);
   }

   // Flush fat write buffer
   FAT_WriteFatEntryFlushBuffer ();

   return true;
}
#endif


/*-----------------------------------------------------------------
FAT_InitFiles
Reads the FAT information from the CF card.
You need to call this before reading any files.
bool return OUT: true if successful.
-----------------------------------------------------------------*/
bool FAT_InitFiles (void)
{
   int i;
   int bootSector;
   BOOT_SEC* bootSec;

   if (!disc_Init())
   {
      return (false);
   }

   // Read first sector of CF card
   if ( !disc_ReadSector(0, globalBuffer)) {
      return false;
   }

   // Make sure it is a valid MBR or boot sector
   if ( (globalBuffer[0x1FE] != 0x55) || (globalBuffer[0x1FF] != 0xAA)) {
      return false;
   }

   // Check if there is a FAT string, which indicates this is a boot sector
   if ((globalBuffer[0x36] == 'F') && (globalBuffer[0x37] == 'A') && (globalBuffer[0x38] == 'T'))
   {
      bootSector = 0;
   }
   // Check for FAT32
   else if ((globalBuffer[0x52] == 'F') && (globalBuffer[0x53] == 'A') && (globalBuffer[0x54] == 'T'))
   {
      bootSector = 0;
   }
   else   // This is an MBR
   {
      // Find first valid partition from MBR
      // First check for an active partition
      for (i=0x1BE; (i < 0x1FE) && (globalBuffer != 0x80); i+= 0x10);
      // If it didn't find an active partition, search for any valid partition
      if (i == 0x1FE)
         for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i+0x04] == 0x00); i+= 0x10);

      // Go to first valid partition
      if ( i != 0x1FE)   // Make sure it found a partition
      {
         bootSector = globalBuffer[0x8 + i] + (globalBuffer[0x9 + i] << 8) + (globalBuffer[0xA + i] << 16) + ((globalBuffer[0xB + i] << 24) & 0x0F);
      } else {
         bootSector = 0;   // No partition found, assume this is a MBR free disk
      }
   }

   // Read in boot sector
   bootSec = (BOOT_SEC*) globalBuffer;
   if (!disc_ReadSector (bootSector,  bootSec)) {
      return false;
   }

   // Store required information about the file system
   if (bootSec->sectorsPerFAT != 0)
   {
      filesysSecPerFAT = bootSec->sectorsPerFAT;
   }
   else
   {
      filesysSecPerFAT = bootSec->extBlock.fat32.sectorsPerFAT32;
   }

   if (bootSec->numSectorsSmall != 0)
   {
      filesysNumSec = bootSec->numSectorsSmall;
   }
   else
   {
      filesysNumSec = bootSec->numSectors;
   }

   filesysBytePerSec = BYTE_PER_READ;   // Sector size is redefined to be 512 bytes
   filesysSecPerClus = bootSec->sectorsPerCluster * bootSec->bytesPerSector / BYTE_PER_READ;
   filesysBytePerClus = filesysBytePerSec * filesysSecPerClus;
   filesysFAT = bootSector + bootSec->reservedSectors;

   filesysRootDir = filesysFAT + (bootSec->numFATs * filesysSecPerFAT);

   // sanity check to return an error instead of let a divide by zero occur ( very nasty )
   if( filesysBytePerSec == 0 )   {
      return false;
   }

   filesysData = filesysRootDir + ((bootSec->rootEntries * sizeof(DIR_ENT)) / filesysBytePerSec);
   filesysTotalSize = (filesysNumSec - filesysData) * filesysBytePerSec;

   // another sanity check because there is nothing more annoying than a divide by zero!
   if( bootSec->sectorsPerCluster == 0 )   {
      return false;
   }

   // Store info about FAT
   fatLastCluster = (filesysNumSec - filesysData) / bootSec->sectorsPerCluster;
   fatFirstFree = CLUSTER_FIRST;
   fatBufferCurSector = 0;
   disc_ReadSector(fatBufferCurSector, fatBuffer);

   if (fatLastCluster < 4085)
   {
      filesysType = FS_FAT12;   // FAT12 volume - unsupported
   }
   else if (fatLastCluster < 65525)
   {
      filesysType = FS_FAT16;   // FAT16 volume
   }
   else
   {
      filesysType = FS_FAT32;   // FAT32 volume
   }

   if (filesysType != FS_FAT32)
   {
      filesysRootDirClus = FAT16_ROOT_DIR_CLUSTER;
   }
   else   // Set up for the FAT32 way
   {
      filesysRootDirClus = bootSec->extBlock.fat32.rootClus;
      // Check if FAT mirroring is enabled
      if (!(bootSec->extBlock.fat32.extFlags & 0x80))
      {
         // Use the active FAT
         filesysFAT = filesysFAT + ( filesysSecPerFAT * (bootSec->extBlock.fat32.extFlags & 0x0F));
      }
   }

   // Set current directory to the root
   curWorkDirCluster = filesysRootDirClus;
   wrkDirCluster = filesysRootDirClus;
   wrkDirSector = 0;
   wrkDirOffset = 0;

   // Set all files to free
   for (i=0; i < MAX_FILES_OPEN; i++)
   {
      openFiles.inUse = false;
   }

   // No long filenames so far
   lfnExists = false;
   for (i = 0; i < MAX_FILENAME_LENGTH; i++)
   {
      lfnName = '\0';
   }

   return (true);
}

/*-----------------------------------------------------------------
FAT_FreeFiles
Closes all open files then resets the CF card.
Call this before exiting back to the GBAMP
bool return OUT: true if successful.
-----------------------------------------------------------------*/
bool FAT_FreeFiles (void)
{
   int i;

   // Close all open files
   for (i=0; i < MAX_FILES_OPEN; i++)
   {
      if (openFiles.inUse == true)
      {
         FAT_fclose(&openFiles);
      }
   }

   // Flush any sectors in disc cache
   disc_CacheFlush();

   // Clear card status
   disc_ClearStatus();

   // Return status of card
   return disc_IsInserted();
}


/*-----------------------------------------------------------------
FAT_GetDirEntry
Return the file info structure of the next valid file entry
u32 dirCluster: IN cluster of subdirectory table
int entry: IN the desired file entry
int origin IN: relative position of the entry
DIR_ENT return OUT: desired dirEntry. First char will be FILE_FREE if
   the entry does not exist.
-----------------------------------------------------------------*/
DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin)
{
   DIR_ENT dir;
   DIR_ENT_LFN lfn;
   int firstSector = 0;
   bool notFound = false;
   bool found = false;
   int maxSectors;
   int lfnPos, aliasPos;
   u8 lfnChkSum, chkSum;

   int i;

   dir.name[0] = FILE_FREE; // default to no file found
   dir.attrib = 0x00;

   // Check if fat has been initialised
   if (filesysBytePerSec == 0)
   {
      return (dir);
   }

   switch (origin)
   {
   case SEEK_SET:
      wrkDirCluster = dirCluster;
      wrkDirSector = 0;
      wrkDirOffset = -1;
      break;
   case SEEK_CUR:   // Don't change anything
      break;
   case SEEK_END:   // Find entry signifying end of directory
      // Subtraction will never reach 0, so it keeps going
      // until reaches end of directory
      wrkDirCluster = dirCluster;
      wrkDirSector = 0;
      wrkDirOffset = -1;
      entry = -1;
      break;
   default:
      return dir;
   }

   lfnChkSum = 0;
   maxSectors = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus);

   // Scan Dir for correct entry
   firstSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster));
   disc_ReadSector (firstSector + wrkDirSector, globalBuffer);
   found = false;
   notFound = false;
   do {
      wrkDirOffset++;
      if (wrkDirOffset == BYTE_PER_READ / sizeof (DIR_ENT))
      {
         wrkDirOffset = 0;
         wrkDirSector++;
         if ((wrkDirSector == filesysSecPerClus) && (wrkDirCluster != FAT16_ROOT_DIR_CLUSTER))
         {
            wrkDirSector = 0;
            wrkDirCluster = FAT_NextCluster(wrkDirCluster);
            if (wrkDirCluster == CLUSTER_EOF)
            {
               notFound = true;
            }
            firstSector = FAT_ClustToSect(wrkDirCluster);
         }
         else if ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER) && (wrkDirSector == (filesysData - filesysRootDir)))
         {
            notFound = true;   // Got to end of root dir
         }
         disc_ReadSector (firstSector + wrkDirSector, globalBuffer);
      }
      dir = ((DIR_ENT*) globalBuffer)[wrkDirOffset];
      if ((dir.name[0] != FILE_FREE) && (dir.name[0] > 0x20) && ((dir.attrib & ATTRIB_VOL) != ATTRIB_VOL))
      {
         entry--;
         if (lfnExists)
         {
            // Calculate file checksum
            chkSum = 0;
            for (aliasPos=0; aliasPos < 11; aliasPos++)
            {
               // NOTE: The operation is an unsigned char rotate right
               chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + (aliasPos < 8 ? dir.name[aliasPos] : dir.ext[aliasPos - 8]);
            }
            if (chkSum != lfnChkSum)
            {
               lfnExists = false;
               lfnName[0] = '\0';
            }
         }
         if (entry == 0)
         {
            if (!lfnExists)
            {
               FAT_GetFilename (dir, lfnName);
            }
            found = true;
         }
      }
      else if (dir.name[0] == FILE_LAST)
      {
         if (origin == SEEK_END)
         {
            found = true;
         }
         else
         {
            notFound = true;
         }
      }
      else if (dir.attrib == ATTRIB_LFN)
      {
         lfn = ((DIR_ENT_LFN*) globalBuffer)[wrkDirOffset];
         if (lfn.ordinal & LFN_DEL)
         {
            lfnExists = false;
         }
         else if (lfn.ordinal & LFN_END)   // Last part of LFN, make sure it isn't deleted (Thanks MoonLight)
         {
            lfnExists = true;
            lfnName[(lfn.ordinal & ~LFN_END) * 13] = '\0';   // Set end of lfn to null character
            lfnChkSum = lfn.checkSum;
         }
         if (lfnChkSum != lfn.checkSum)
         {
            lfnExists = false;
         }
         if (lfnExists)
         {
            lfnPos = ((lfn.ordinal & ~LFN_END) - 1) * 13;
            for (i = 0; i < 13; i++) {
               lfnName[lfnPos + i] = ((u8*)&lfn)[(int)(lfn_offset_table)] /* | ((u8*)&lfn)[(int)(lfn_offset_table) + 1]  include this for unicode support*/;
            }
         }
      }
   } while (!found && !notFound);

   // If no file is found, return FILE_FREE
   if (notFound)
   {
      dir.name[0] = FILE_FREE;
   }

   return (dir);
}


/*-----------------------------------------------------------------
FAT_GetLongFilename
Get the long name of the last file or directory retrived with
   GetDirEntry. Also works for FindFirstFile and FindNextFile.
   If a long name doesn't exist, it returns the short name
   instead.
char* filename: OUT will be filled with the filename, should be at
   least 256 bytes long
bool return OUT: return true if successful
-----------------------------------------------------------------*/
bool FAT_GetLongFilename (char* filename)
{
   if (filename == NULL)
      return false;

   strncpy (filename, lfnName, MAX_FILENAME_LENGTH - 1);
   filename[MAX_FILENAME_LENGTH - 1] = '\0';

   return true;
}


/*-----------------------------------------------------------------
FAT_GetFilename
Get the alias (short name) of the file or directory stored in
   dirEntry
DIR_ENT dirEntry: IN a valid directory table entry
char* alias OUT: will be filled with the alias (short filename),
   should be at least 13 bytes long
bool return OUT: return true if successful
-----------------------------------------------------------------*/
bool FAT_GetFilename (DIR_ENT dirEntry, char* alias)
{
   int i=0;
   int j=0;

   alias[0] = '\0';
   if (dirEntry.name[0] != FILE_FREE)
   {
      if (dirEntry.name[0] == '.')
      {
         alias[0] = '.';
         if (dirEntry.name[1] == '.')
         {
            alias[1] = '.';
            alias[2] = '\0';
         }
         else
         {
            alias[1] = '\0';
         }
      }
      else
      {
         // Copy the filename from the dirEntry to the string
         for (i = 0; (i < 8) && (dirEntry.name != ' '); i++)
         {
            alias = dirEntry.name;
         }
         // Copy the extension from the dirEntry to the string
         if (dirEntry.ext[0] != ' ')
         {
            alias[i++] = '.';
            for ( j = 0; (j < 3) && (dirEntry.ext[j] != ' '); j++)
            {
               alias[i++] = dirEntry.ext[j];
            }
         }
         alias = '\0';
      }
   }

   return (alias[0] != '\0');
}

/*-----------------------------------------------------------------
FAT_GetAlias
Get the alias (short name) of the last file or directory entry read
   using GetDirEntry. Works for FindFirstFile and FindNextFile
char* alias OUT: will be filled with the alias (short filename),
   should be at least 13 bytes long
bool return OUT: return true if successful
-----------------------------------------------------------------*/
bool FAT_GetAlias (char* alias)
{
   if (alias == NULL)
   {
      return false;
   }
   // Read in the last accessed directory entry
   disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);

   return    FAT_GetFilename (((DIR_ENT*)globalBuffer)[wrkDirOffset], alias);
}

/*-----------------------------------------------------------------
FAT_GetFileSize
Get the file size of the last file found or openned.
This idea is based on a modification by MoonLight
u32 return OUT: the file size
-----------------------------------------------------------------*/
u32 FAT_GetFileSize (void)
{
   // Read in the last accessed directory entry
   disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);

   return    ((DIR_ENT*)globalBuffer)[wrkDirOffset].fileSize;
}

/*-----------------------------------------------------------------
FAT_GetFileCluster
Get the first cluster of the last file found or openned.
u32 return OUT: the file start cluster
-----------------------------------------------------------------*/
u32 FAT_GetFileCluster (void)
{
   // Read in the last accessed directory entry
   disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);

   return    (((DIR_ENT*)globalBuffer)[wrkDirOffset].startCluster) | (((DIR_ENT*)globalBuffer)[wrkDirOffset].startClusterHigh << 16);
}

/*-----------------------------------------------------------------
FAT_GetFileAttributes
Get the attributes of the last file found or openned.
u8 return OUT: the file's attributes
-----------------------------------------------------------------*/
u8 FAT_GetFileAttributes (void)
{
   // Read in the last accessed directory entry
   disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);

   return    ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib;
}

#ifdef CAN_WRITE_TO_DISC
/*-----------------------------------------------------------------
FAT_SetFileAttributes
Set the attributes of a file.
const char* filename IN: The name and path of the file to modify
u8 attributes IN: The attribute values to assign
u8 mask IN: Detemines which attributes are changed
u8 return OUT: the file's new attributes
-----------------------------------------------------------------*/
u8 FAT_SetFileAttributes (const char* filename, u8 attributes, u8 mask)
{
   // Get the file
   if (!FAT_FileExists(filename)) {
      return 0xff;
   }

   // Read in the last accessed directory entry
   disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);

   ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib = (((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib & ~(mask & 0x27)) | (attributes & 0x27);   // 0x27 is he settable attributes

   disc_WriteSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);

   return    ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib;
}
#endif

#ifdef FILE_TIME_SUPPORT
time_t FAT_FileTimeToCTime (u16 fileTime, u16 fileDate)
{
   struct tm timeInfo;

   timeInfo.tm_year = (fileDate >> 9) + 80;      // years since midnight January 1970
   timeInfo.tm_mon = ((fileDate >> 5) & 0xf) - 1;   // Months since january
   timeInfo.tm_mday = fileDate & 0x1f;            // Day of the month

   timeInfo.tm_hour = fileTime >> 11;            // hours past midnight
   timeInfo.tm_min = (fileTime >> 5) & 0x3f;      // minutes past the hour
   timeInfo.tm_sec = (fileTime & 0x1f) * 2;      // seconds past the minute

   return mktime(&timeInfo);
}

/*-----------------------------------------------------------------
FAT_GetFileCreationTime
Get the creation time of the last file found or openned.
time_t return OUT: the file's creation time
-----------------------------------------------------------------*/
time_t FAT_GetFileCreationTime (void)
{
   // Read in the last accessed directory entry
   disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);

   return    FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].cTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].cDate);
}

/*-----------------------------------------------------------------
FAT_GetFileLastWriteTime
Get the creation time of the last file found or openned.
time_t return OUT: the file's creation time
-----------------------------------------------------------------*/
time_t FAT_GetFileLastWriteTime (void)
{
   // Read in the last accessed directory entry
   disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);

   return    FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].mTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].mDate);
}
#endif

/*-----------------------------------------------------------------
FAT_DirEntFromPath
Finds the directory entry for a file or directory from a path
Path separator is a forward slash /
const char* path: IN null terminated string of path.
DIR_ENT return OUT: dirEntry of found file. First char will be FILE_FREE
   if the file was not found
-----------------------------------------------------------------*/
DIR_ENT FAT_DirEntFromPath (const char* path)
{
   int pathPos;
   char name[MAX_FILENAME_LENGTH];
   char alias[13];
   int namePos;
   bool found, notFound;
   DIR_ENT dirEntry;
   u32 dirCluster;
   bool flagLFN, dotSeen;

   // Start at beginning of path
   pathPos = 0;

   if (path[pathPos] == '/')
   {
      dirCluster = filesysRootDirClus;   // Start at root directory
   }
   else
   {
      dirCluster = curWorkDirCluster;   // Start at current working dir
   }

   // Eat any slash /
   while ((path[pathPos] == '/') && (path[pathPos] != '\0'))
   {
      pathPos++;
   }

   // Search until can't continue
   found = false;
   notFound = false;
   while (!notFound && !found)
   {
      flagLFN = false;
      // Copy name from path
      namePos = 0;
      if ((path[pathPos] == '.') && ((path[pathPos + 1] == '\0') || (path[pathPos + 1] == '/'))) {
         // Dot entry
         name[namePos++] = '.';
         pathPos++;
      } else if ((path[pathPos] == '.') && (path[pathPos + 1] == '.') && ((path[pathPos + 2] == '\0') || (path[pathPos + 2] == '/'))){
         // Double dot entry
         name[namePos++] = '.';
         pathPos++;
         name[namePos++] = '.';
         pathPos++;
      } else {
         // Copy name from path
         if (path[pathPos] == '.') {
            flagLFN = true;
         }
         dotSeen = false;
         while ((namePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0') && (path[pathPos] != '/'))
         {
            name[namePos] = ucase(path[pathPos]);
            if ((name[namePos] <= ' ') || ((name[namePos] >= ':') && (name[namePos] <= '?'))) // Invalid character
            {
               flagLFN = true;
            }
            if (name[namePos] == '.') {
               if (!dotSeen) {
                  dotSeen = true;
               } else {
                  flagLFN = true;
               }
            }
            namePos++;
            pathPos++;
         }
         // Check if a long filename was specified
         if (namePos > 12)
         {
            flagLFN = true;
         }
      }

      // Add end of string char
      name[namePos] = '\0';

      // Move through path to correct place
      while ((path[pathPos] != '/') && (path[pathPos] != '\0'))
         pathPos++;
      // Eat any slash /
      while ((path[pathPos] == '/') && (path[pathPos] != '\0'))
      {
         pathPos++;
      }

      // Search current Dir for correct entry
      dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_SET);
      while ( !found && !notFound)
      {
         // Match filename
         found = true;
         for (namePos = 0; (namePos < MAX_FILENAME_LENGTH) && found && (name[namePos] != '\0') && (lfnName[namePos] != '\0'); namePos++)
         {
            if (name[namePos] != ucase(lfnName[namePos]))
            {
               found = false;
            }
         }
         if ((name[namePos] == '\0') != (lfnName[namePos] == '\0'))
         {
            found = false;
         }

         // Check against alias as well.
         if (!found)
         {
            FAT_GetFilename(dirEntry, alias);
            found = true;
            for (namePos = 0; (namePos < 13) && found && (name[namePos] != '\0') && (alias[namePos] != '\0'); namePos++)
            {
               if (name[namePos] != ucase(alias[namePos]))
               {
                  found = false;
               }
            }
            if ((name[namePos] == '\0') != (alias[namePos] == '\0'))
            {
               found = false;
            }
         }

         if (dirEntry.name[0] == FILE_FREE)
            // Couldn't find specified file
         {
            found = false;
            notFound = true;
         }
         if (!found && !notFound)
         {
            dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_CUR);
         }
      }

      if (found && ((dirEntry.attrib & ATTRIB_DIR) == ATTRIB_DIR) && (path[pathPos] != '\0'))
         // It has found a directory from within the path that needs to be followed
      {
         found = false;
         dirCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
      }
   }

   if (notFound)
   {
      dirEntry.name[0] = FILE_FREE;
      dirEntry.attrib = 0x00;
   }

   return (dirEntry);
}


#ifdef CAN_WRITE_TO_DISC
/*-----------------------------------------------------------------
FAT_AddDirEntry
Creates a new dir entry for a file
Path separator is a forward slash /
const char* path: IN null terminated string of path to file.
DIR_ENT newDirEntry IN: The directory entry to use.
int file IN: The file being added (optional, use -1 if not used)
bool return OUT: true if successful
-----------------------------------------------------------------*/
bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry)
{
   char filename[MAX_FILENAME_LENGTH];
   int filePos, pathPos, aliasPos;
   char tempChar;
   bool flagLFN, dotSeen;
   char fileAlias[13] = {0};
   int tailNum;

   unsigned char chkSum = 0;

   u32 oldWorkDirCluster;

   DIR_ENT* dirEntries = (DIR_ENT*)globalBuffer;
   u32 dirCluster;
   int secOffset;
   int entryOffset;
   int maxSectors;
   u32 firstSector;

   DIR_ENT_LFN lfnEntry;
   int lfnPos = 0;

   int dirEntryLength = 0;
   int dirEntryRemain = 0;
   u32 tempDirCluster;
   int tempSecOffset;
   int tempEntryOffset;
   bool dirEndFlag = false;

   int i;

   // Store current working directory
   oldWorkDirCluster = curWorkDirCluster;

   // Find filename within path and change to correct directory
   if (path[0] == '/')
   {
      curWorkDirCluster = filesysRootDirClus;
   }

   pathPos = 0;
   filePos = 0;
   flagLFN = false;

   while (path[pathPos + filePos] != '\0')
   {
      if (path[pathPos + filePos] == '/')
      {
         filename[filePos] = '\0';
         if (FAT_chdir(filename) == false)
         {
            curWorkDirCluster = oldWorkDirCluster;
            return false; // Couldn't change directory
         }
         pathPos += filePos + 1;
         filePos = 0;
      }
      filename[filePos] = path[pathPos + filePos];
      filePos++;
   }

   // Skip over last slashes
   while (path[pathPos] == '/')
      pathPos++;

   // Check if the filename has a leading "."
   // If so, it is an LFN
   if (path[pathPos] == '.') {
      flagLFN = true;
   }

   // Copy name from path
   filePos = 0;
   dotSeen = false;

   while ((filePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0'))
   {
      filename[filePos] = path[pathPos];
      if ((filename[filePos] <= ' ') || ((filename[filePos] >= ':') && (filename[filePos] <= '?'))) // Invalid character
      {
         flagLFN = true;
      }
      if (filename[filePos] == '.') {
         if (!dotSeen) {
            dotSeen = true;
         } else {
            flagLFN = true;
         }
      }
      filePos++;
      pathPos++;
      if ((filePos > 8) && !dotSeen) {
         flagLFN = true;
      }
   }

   if (filePos == 0)   // No filename
   {
      return false;
   }

   // Check if a long filename was specified
   if (filePos > 12)
   {
      flagLFN = true;
   }

   // Check if extension is > 3 characters long
   if (!flagLFN && (strrchr (filename, '.') != NULL) && (strlen(strrchr(filename, '.')) > 4)) {
      flagLFN = true;
   }

   lfnPos = (filePos - 1) / 13;

   // Add end of string char
   filename[filePos++] = '\0';
   // Clear remaining chars
   while (filePos < MAX_FILENAME_LENGTH)
      filename[filePos++] = 0x01;   // Set for LFN compatibility


   if (flagLFN)
   {
      // Generate short filename - always a 2 digit number for tail
      // Get first 5 chars of alias from LFN
      aliasPos = 0;
      filePos = 0;
      if (filename[filePos] == '.') {
         filePos++;
      }
      for ( ; (aliasPos < 5) && (filename[filePos] != '\0') && (filename[filePos] != '.') ; filePos++)
      {
         tempChar = ucase(filename[filePos]);
         if (((tempChar > ' ' && tempChar < ':') || tempChar > '?') && tempChar != '.')
            fileAlias[aliasPos++] = tempChar;
      }
      // Pad Alias with underscores
      while (aliasPos < 5)
         fileAlias[aliasPos++] = '_';

      fileAlias[5] = '~';
      fileAlias[8] = '.';
      fileAlias[9] = ' ';
      fileAlias[10] = ' ';
      fileAlias[11] = ' ';
      if (strchr (filename, '.') != NULL) {
         while(filename[filePos] != '\0')
         {
            filePos++;
            if (filename[filePos] == '.')
            {
               pathPos = filePos;
            }
         }
         filePos = pathPos + 1;   //pathPos is used as a temporary variable
         // Copy first 3 characters of extension
         for (aliasPos = 9; (aliasPos < 12) && (filename[filePos] != '\0'); filePos++)
         {
            tempChar = ucase(filename[filePos]);
            if ((tempChar > ' ' && tempChar < ':') || tempChar > '?')
               fileAlias[aliasPos++] = tempChar;
         }
      } else {
         aliasPos = 9;
      }

      // Pad Alias extension with spaces
      while (aliasPos < 12)
         fileAlias[aliasPos++] = ' ';

      fileAlias[12] = '\0';


      // Get a valid tail number
      tailNum = 0;
      do {
         tailNum++;
         fileAlias[6] = 0x30 + ((tailNum / 10) % 10);   // 10's digit
         fileAlias[7] = 0x30 + (tailNum % 10);   // 1's digit
      } while ((FAT_DirEntFromPath(fileAlias).name[0] != FILE_FREE) && (tailNum < 100));

      if (tailNum < 100)   // Found an alias not being used
      {
         // Calculate file checksum
         chkSum = 0;
         for (aliasPos=0; aliasPos < 12; aliasPos++)
         {
            // Skip '.'
            if (fileAlias[aliasPos] == '.')
               aliasPos++;
            // NOTE: The operation is an unsigned char rotate right
            chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + fileAlias[aliasPos];
         }
      }
      else   // Couldn't find a valid alias
      {
         return false;
      }

      dirEntryLength = lfnPos + 2;
   }
   else   // Its not a long file name
   {
      // Just copy alias straight from filename
      for (aliasPos = 0; aliasPos < 13; aliasPos++)
      {
         tempChar = ucase(filename[aliasPos]);
         if ((tempChar > ' ' && tempChar < ':') || tempChar > '?')
            fileAlias[aliasPos] = tempChar;
      }
      fileAlias[12] = '\0';

      lfnPos = -1;

      dirEntryLength = 1;
   }

   // Change dirEntry name to match alias
   for (aliasPos = 0; ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0') && (aliasPos < 8)); aliasPos++)
   {
      newDirEntry.name[aliasPos] = fileAlias[aliasPos];
   }
   while (aliasPos < 8)
   {
      newDirEntry.name[aliasPos++] = ' ';
   }
   aliasPos = 0;
   while ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0'))
      aliasPos++;
   filePos = 0;
   while (( filePos < 3 ) && (fileAlias[aliasPos] != '\0'))
   {
      tempChar = fileAlias[aliasPos++];
      if ((tempChar > ' ' && tempChar < ':' && tempChar!='.') || tempChar > '?')
         newDirEntry.ext[filePos++] = tempChar;
   }
   while (filePos < 3)
   {
      newDirEntry.ext[filePos++] = ' ';
   }

   // Scan Dir for free entry
   dirCluster = curWorkDirCluster;
   secOffset = 0;
   entryOffset = 0;
   maxSectors = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus);
   firstSector = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(dirCluster));
   disc_ReadSector (firstSector + secOffset, dirEntries);

   dirEntryRemain = dirEntryLength;
   tempDirCluster = dirCluster;
   tempSecOffset = secOffset;
   tempEntryOffset = entryOffset;

   // Search for a large enough space to fit in new directory entry
   while ((dirEntries[entryOffset].name[0] != FILE_LAST) && (dirEntryRemain > 0))
   {

      entryOffset++;

      if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT))
      {
         entryOffset = 0;
         secOffset++;
         if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER))
         {
            secOffset = 0;
            if (FAT_NextCluster(dirCluster) == CLUSTER_EOF)
            {
               dirCluster = FAT_LinkFreeCluster(dirCluster);
               dirEntries[0].name[0] = FILE_LAST;
            }
            else
            {
               dirCluster = FAT_NextCluster(dirCluster);
            }
            firstSector = FAT_ClustToSect(dirCluster);
         }
         else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir)))
         {
            return false;   // Got to end of root dir - can't fit in more files
         }
         disc_ReadSector (firstSector + secOffset, dirEntries);
      }

      if ((dirEntries[entryOffset].name[0] == FILE_FREE) || (dirEntries[entryOffset].name[0] == FILE_LAST) )
      {
         dirEntryRemain--;
      } else {
         dirEntryRemain = dirEntryLength;
         tempDirCluster = dirCluster;
         tempSecOffset = secOffset;
         tempEntryOffset = entryOffset;
      }
   }

   // Modifying the last directory is a special case - have to erase following entries
   if (dirEntries[entryOffset].name[0] == FILE_LAST)
   {
      dirEndFlag = true;
   }

   // Recall last used entry
   dirCluster = tempDirCluster;
   secOffset = tempSecOffset;
   entryOffset = tempEntryOffset;
   dirEntryRemain = dirEntryLength;

   // Re-read in first sector that will be written to
   if (dirEndFlag && (entryOffset == 0))   {
      memset (dirEntries, FILE_LAST, BYTE_PER_READ);
   } else {
      disc_ReadSector (firstSector + secOffset, dirEntries);
   }

   // Add new directory entry
   while (dirEntryRemain > 0)
   {
      // Move to next entry, first pass advances from last used entry
      entryOffset++;
      if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT))
      {
         // Write out the current sector if we need to
         entryOffset = 0;
         if (dirEntryRemain < dirEntryLength) // Don't write out sector on first pass
         {
            disc_WriteSector (firstSector + secOffset, dirEntries);
         }
         secOffset++;
         if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER))
         {
            secOffset = 0;
            if (FAT_NextCluster(dirCluster) == CLUSTER_EOF)
            {
               dirCluster = FAT_LinkFreeCluster(dirCluster);
               dirEntries[0].name[0] = FILE_LAST;
            }
            else
            {
               dirCluster = FAT_NextCluster(dirCluster);
            }
            firstSector = FAT_ClustToSect(dirCluster);
         }
         else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir)))
         {
            return false;   // Got to end of root dir - can't fit in more files
         }
         if (dirEndFlag)
         {
            memset (dirEntries, FILE_LAST, BYTE_PER_READ);
         } else {
            disc_ReadSector (firstSector + secOffset, dirEntries);
         }
      }

      // Generate LFN entries
      if (lfnPos >= 0)
      {
         lfnEntry.ordinal = (lfnPos + 1) | (dirEntryRemain == dirEntryLength ? LFN_END : 0);
         for (i = 0; i < 13; i++) {
            if (filename [lfnPos * 13 + i] == 0x01) {
               ((u8*)&lfnEntry)[(int)lfn_offset_table] = 0xff;
               ((u8*)&lfnEntry)[(int)(lfn_offset_table) + 1] = 0xff;
            } else {
               ((u8*)&lfnEntry)[(int)lfn_offset_table] = filename [lfnPos * 13 + i];
               ((u8*)&lfnEntry)[(int)(lfn_offset_table) + 1] = 0x00;
            }
         }

         lfnEntry.checkSum = chkSum;
         lfnEntry.flag = ATTRIB_LFN;
         lfnEntry.reserved1 = 0;
         lfnEntry.reserved2 = 0;

         *((DIR_ENT_LFN*)&dirEntries[entryOffset]) = lfnEntry;
         lfnPos --;
         lfnEntry.ordinal = 0;
      }   // end writing long filename entries
      else
      {
         dirEntries[entryOffset] = newDirEntry;
         if (dirEndFlag && (entryOffset < (BYTE_PER_READ / sizeof (DIR_ENT))) )
            dirEntries[entryOffset+1].name[0] = FILE_LAST;
      }

      dirEntryRemain--;
   }

   // Write directory back to disk
   disc_WriteSector (firstSector + secOffset, dirEntries);

   // Change back to Working DIR
   curWorkDirCluster = oldWorkDirCluster;

   return true;
}
#endif

/*-----------------------------------------------------------------
FAT_FindNextFile
Gets the name of the next directory entry
   (can be a file or subdirectory)
char* filename: OUT filename, must be at least 13 chars long
FILE_TYPE return: OUT returns FT_NONE if failed,
   FT_FILE if it found a file and FT_DIR if it found a directory
-----------------------------------------------------------------*/
FILE_TYPE FAT_FindNextFile(char* filename)
{
   // Get the next directory entry
   DIR_ENT file;
   file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_CUR);

   if (file.name[0] == FILE_FREE)
   {
      return FT_NONE;   // Did not find a file
   }

   // Get the filename
   if (filename != NULL)
      FAT_GetFilename (file, filename);

   if ((file.attrib & ATTRIB_DIR) != 0)
   {
      return FT_DIR;   // Found a directory
   }
   else
   {
      return FT_FILE;   // Found a file
   }
}

/*-----------------------------------------------------------------
FAT_FindFirstFile
Gets the name of the first directory entry and resets the count
   (can be a file or subdirectory)
char* filename: OUT filename, must be at least 13 chars long
FILE_TYPE return: OUT returns FT_NONE if failed,
   FT_FILE if it found a file and FT_DIR if it found a directory
-----------------------------------------------------------------*/
FILE_TYPE FAT_FindFirstFile(char* filename)
{
   // Get the first directory entry
   DIR_ENT file;
   file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_SET);

   if (file.name[0] == FILE_FREE)
   {
      return FT_NONE;   // Did not find a file
   }

   // Get the filename
   if (filename != NULL)
      FAT_GetFilename (file, filename);

   if ((file.attrib & ATTRIB_DIR) != 0)
   {
      return FT_DIR;   // Found a directory
   }
   else
   {
      return FT_FILE;   // Found a file
   }
}

/*-----------------------------------------------------------
In my world,have 0 and 1 only ......Matrix World......

Offline cory1492

  • Neo MOD
  • Hero Member
  • *
  • Posts: 1443
    • Cory1492's NDS + PSP corner
Re: gba_nds_fat.c
« Reply #1 on: July 07, 2007, 06:08:40 AM »
Dr.neo: this one is cut a bit short too, though it should be the same as the regular "legacy" lib from chishm's site.
Those who have come here to hate should leave now, for in their hatred they only betray themselves.
translated from The Book of Life (Src: Sword of Truth - Phantom by Terry Goodkind)

Offline Dr.neo

  • Administrator
  • Hero Member
  • *****
  • Posts: 3826
Re: gba_nds_fat.c
« Reply #2 on: July 07, 2007, 01:58:20 PM »
i'll upload this file.
« Last Edit: July 07, 2007, 02:13:27 PM by Dr.neo »
In my world,have 0 and 1 only ......Matrix World......