File: C:\M91449\Perso\RTWdsPIC\Developpements\Forum\resodad_john\myFolder\SDtroubleshooting\..\Microchip\MDD File System\FSIO.c

    1   /******************************************************************************
    2   *
    3   *               Microchip Memory Disk Drive File System
    4   *
    5   ******************************************************************************
    6   * FileName:           FSIO.c
    7   * Dependencies:       GenericTypeDefs.h
    8   *                     FSIO.h
    9   *                     Physical interface include file (SD-SPI.h, CF-PMP.h, ...)
   10   *                     string.h
   11   *                     stdlib.h
   12   *                     FSDefs.h
   13   *                     ctype.h
   14   *                     salloc.h
   15   * Processor:          PIC18/PIC24/dsPIC30/dsPIC33/PIC32
   16   * Compiler:           C18/C30/C32
   17   * Company:            Microchip Technology, Inc.
   18   *
   19   * Software License Agreement
   20   *
   21   * The software supplied herewith by Microchip Technology Incorporated
   22   * (the “Company”) for its PICmicro® Microcontroller is intended and
   23   * supplied to you, the Company’s customer, for use solely and
   24   * exclusively on Microchip PICmicro Microcontroller products. The
   25   * software is owned by the Company and/or its supplier, and is
   26   * protected under applicable copyright laws. All rights are reserved.
   27   * Any use in violation of the foregoing restrictions may subject the
   28   * user to criminal sanctions under applicable laws, as well as to
   29   * civil liability for the breach of the terms and conditions of this
   30   * license.
   31   *
   32   * THIS SOFTWARE IS PROVIDED IN AN “AS IS” CONDITION. NO WARRANTIES,
   33   * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
   34   * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
   35   * PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
   36   * IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
   37   * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
   38   *
   39   ********************************************************************
   40    File Description:
   41   
   42    Change History:
   43     Rev     Description
   44     -----   -----------
   45     1.2.5   Fixed bug that prevented writes to alternate FAT tables
   46             Fixed bug that prevented FAT being updated when media is re-inserted
   47   
   48     1.2.6   Fixed bug that resulted in a bus error when attempts to read a invalid memory region
   49             Fixed bug that prevented the Windows Explorer to show the Date Creation field for directories
   50   
   51     x.x.x   Fixed issue on some USB drives where the information written
   52               to the drive is cached in a RAM for 500ms before it is 
   53               written to the flash unless the sector is accessed again.
   54             Add some error recovery for FAT32 systems when there is
   55               corruption in the boot sector.
   56   
   57     1.3.0   Modified to support Long File Name(LFN) format
   58     1.3.4   1) Initialized some of the local variables to default values
   59                to remove non-critical compiler warnings for code sanitation.
   60             2) The sector size of the media device is obtained from the MBR of media.So, 
   61                instead of using the hard coded macro "DIRENTRIES_PER_SECTOR", the variables
   62                "dirEntriesPerSector" & "disk->sectorSize" are used in the code. Refer 
   63                "Cache_File_Entry","EraseCluster" & "writeDotEntries" fucntions to see 
   64                the change.
   65   ********************************************************************/
   66   
   67   #include "Compiler.h"
   68   #include "MDD File System/FSIO.h"
   69   #include "GenericTypeDefs.h"
   70   #include "string.h"
   71   #include "stdlib.h"
   72   #include "ctype.h"
   73   #include "MDD File System/FSDefs.h"
   74   
   75   #ifdef ALLOW_FSFPRINTF
   76   #include "stdarg.h"
   77   #endif
   78   
   79   #ifdef FS_DYNAMIC_MEM
   80      #ifdef __18CXX
   81         #include "salloc.h"
   82      #endif
   83   #endif
   84   
   85   #ifndef ALLOW_WRITES
   86      #ifdef ALLOW_FORMATS
   87         #error Write functions must be enabled to use the format function
   88      #endif
   89      #ifdef ALLOW_FSFPRINTF
   90         #error Write functions must be enabled to use the FSfprintf function
   91      #endif
   92   #endif
   93   
   94   #ifdef USEREALTIMECLOCK
   95       #ifdef USERDEFINEDCLOCK
   96           #error Please select only one timestamp clocking mode in FSconfig.h
   97       #endif
   98       #ifdef INCREMENTTIMESTAMP
   99           #error Please select only one timestamp clocking mode in FSconfig.h
  100       #endif
  101   #elif defined USERDEFINEDCLOCK
  102       #ifdef INCREMENTTIMESTAMP
  103           #error Please select only one timestamp clocking mode in FSconfig.h
  104       #endif
  105   #endif
  106   /*****************************************************************************/
  107   /*                         Global Variables                                  */
  108   /*****************************************************************************/
  109   
  110   #ifndef FS_DYNAMIC_MEM
  111       FSFILE  gFileArray[FS_MAX_FILES_OPEN];      // Array that contains file information (static allocation)
  112       BYTE    gFileSlotOpen[FS_MAX_FILES_OPEN];   // Array that indicates which elements of gFileArray are available for use
  113   	#ifdef SUPPORT_LFN
  114   		// Array that stores long file name (static allocation)
  115   		unsigned short int lfnData[FS_MAX_FILES_OPEN][257];
  116   	#endif
  117   #endif
  118   
  119   #ifdef SUPPORT_LFN
  120   	#ifdef ALLOW_FILESEARCH
  121   		// Array that stores long file name for File Search operation (static allocation)
  122   		unsigned short int recordSearchName[257];
  123   		unsigned short int recordFoundName[257];
  124   		unsigned short int recordSearchLength;
  125   	#endif
  126   	unsigned short int fileFoundString[261];
  127   #endif
  128   
  129   #if defined(USEREALTIMECLOCK) || defined(USERDEFINEDCLOCK)
  130   // Timing variables
  131   BYTE    gTimeCrtMS;     // Global time variable (for timestamps) used to indicate create time (milliseconds)
  132   WORD    gTimeCrtTime;   // Global time variable (for timestamps) used to indicate create time
  133   WORD    gTimeCrtDate;   // Global time variable (for timestamps) used to indicate create date
  134   WORD    gTimeAccDate;   // Global time variable (for timestamps) used to indicate last access date
  135   WORD    gTimeWrtTime;   // Global time variable (for timestamps) used to indicate last update time
  136   WORD    gTimeWrtDate;   // Global time variable (for timestamps) used to indicate last update date
  137   #endif
  138   
  139   DWORD       gLastFATSectorRead = 0xFFFFFFFF;    // Global variable indicating which FAT sector was read last
  140   BYTE        gNeedFATWrite = FALSE;              // Global variable indicating that there is information that needs to be written to the FAT
  141   FSFILE  *   gBufferOwner = NULL;                // Global variable indicating which file is using the data buffer
  142   DWORD       gLastDataSectorRead = 0xFFFFFFFF;   // Global variable indicating which data sector was read last
  143   BYTE        gNeedDataWrite = FALSE;             // Global variable indicating that there is information that needs to be written to the data section
  144   BYTE        nextClusterIsLast = FALSE;          // Global variable indicating that the entries in a directory align with a cluster boundary
  145   
  146   BYTE    gBufferZeroed = FALSE;      // Global variable indicating that the data buffer contains all zeros
  147   
  148   DWORD   FatRootDirClusterValue;     // Global variable containing the cluster number of the root dir (0 for FAT12/16)
  149   
  150   BYTE    FSerrno;                    // Global error variable.  Set to one of many error codes after each function call.
  151   
  152   DWORD   TempClusterCalc;            // Global variable used to store the calculated value of the cluster of a specified sector.
  153   BYTE    dirCleared;                 // Global variable used by the "recursive" FSrmdir function to indicate that all subdirectories and files have been deleted from the target directory.
  154   BYTE    recache = FALSE;            // Global variable used by the "recursive" FSrmdir function to indicate that additional cache reads are needed.
  155   FSFILE  tempCWDobj;                 // Global variable used to preserve the current working directory information.
  156   FSFILE  gFileTemp;                  // Global variable used for file operations.
  157   
  158   FSFILE   cwd;               // Global current working directory
  159   FSFILE * cwdptr = &cwd;     // Pointer to the current working directory
  160   
  161   
  162   #ifdef __18CXX
  163       #pragma udata dataBuffer = DATA_BUFFER_ADDRESS
  164       BYTE gDataBuffer[MEDIA_SECTOR_SIZE];    // The global data sector buffer
  165       #pragma udata FATBuffer = FAT_BUFFER_ADDRESS
  166       BYTE gFATBuffer[MEDIA_SECTOR_SIZE];     // The global FAT sector buffer
  167   #endif
  168   
  169   #if defined (__C30__) || defined (__PIC32MX__)
  170       BYTE __attribute__ ((aligned(4)))   gDataBuffer[MEDIA_SECTOR_SIZE];     // The global data sector buffer
  171       BYTE __attribute__ ((aligned(4)))   gFATBuffer[MEDIA_SECTOR_SIZE];      // The global FAT sector buffer
  172   #endif
  173   
  174   
  175   #pragma udata
  176   
  177   DISK gDiskData;         // Global structure containing device information.
  178   
  179   /* Global Variables to handle ASCII & UTF16 file operations */
  180   char *asciiFilename;
  181   unsigned short int fileNameLength;
  182   #ifdef SUPPORT_LFN
  183   	unsigned short int *utf16Filename;
  184   	BOOL	utfModeFileName = FALSE;
  185   	BOOL	twoByteMode = FALSE;
  186   #endif
  187   /************************************************************************/
  188   /*                        Structures and defines                        */
  189   /************************************************************************/
  190   
  191   // Directory entry structure
  192   typedef struct
  193   {
  194       char      DIR_Name[DIR_NAMESIZE];           // File name
  195       char      DIR_Extension[DIR_EXTENSION];     // File extension
  196       BYTE      DIR_Attr;                         // File attributes
  197       BYTE      DIR_NTRes;                        // Reserved byte
  198       BYTE      DIR_CrtTimeTenth;                 // Create time (millisecond field)
  199       WORD      DIR_CrtTime;                      // Create time (second, minute, hour field)
  200       WORD      DIR_CrtDate;                      // Create date
  201       WORD      DIR_LstAccDate;                   // Last access date
  202       WORD      DIR_FstClusHI;                    // High word of the entry's first cluster number
  203       WORD      DIR_WrtTime;                      // Last update time
  204       WORD      DIR_WrtDate;                      // Last update date
  205       WORD      DIR_FstClusLO;                    // Low word of the entry's first cluster number
  206       DWORD     DIR_FileSize;                     // The 32-bit file size
  207   }_DIRENTRY;
  208   
  209   typedef _DIRENTRY * DIRENTRY;                   // A pointer to a directory entry structure
  210   
  211   #define DIRECTORY 0x12          // Value indicating that the CreateFileEntry function will be creating a directory
  212   
  213   #define DIRENTRIES_PER_SECTOR   (MEDIA_SECTOR_SIZE / 32)        // The number of directory entries in a sector
  214   
  215   // Maximum number of UTF16 words in single Root directory entry
  216   #define MAX_UTF16_CHARS_IN_LFN_ENTRY      (BYTE)13
  217   
  218   // Long File Name Entry
  219   typedef struct
  220   {
  221      BYTE LFN_SequenceNo;   // Sequence number,
  222      BYTE LFN_Part1[10];    // File name part 1
  223      BYTE LFN_Attribute;    // File attribute
  224      BYTE LFN_Type;		// LFN Type
  225      BYTE LFN_Checksum;     // Checksum
  226      unsigned short int LFN_Part2[6];    // File name part 2
  227      unsigned short int LFN_Reserved2;	// Reserved for future use
  228      unsigned short int LFN_Part3[2];     // File name part 3
  229   }LFN_ENTRY;
  230   
  231   /* Summary: Possible type of file or directory name.
  232   ** Description: See the FormatFileName() & FormatDirName() function for more information.
  233   */
  234   typedef enum
  235   {
  236       NAME_8P3_ASCII_CAPS_TYPE,
  237       NAME_8P3_ASCII_MIXED_TYPE,
  238       NAME_8P3_UTF16_TYPE, // SUBTYPES OF 8P3 UTF16 TYPE
  239   	    NAME_8P3_UTF16_ASCII_CAPS_TYPE,
  240   	    NAME_8P3_UTF16_ASCII_MIXED_TYPE,
  241   	    NAME_8P3_UTF16_NONASCII_TYPE,
  242       NAME_LFN_TYPE,
  243       NAME_ERROR
  244   } FILE_DIR_NAME_TYPE;
  245   
  246   // internal errors
  247   #define CE_FAT_EOF            60   // Error that indicates an attempt to read FAT entries beyond the end of the file
  248   #define CE_EOF                61   // Error that indicates that the end of the file has been reached
  249   
  250   typedef FSFILE   * FILEOBJ;         // Pointer to an FSFILE object
  251   
  252   #ifdef ALLOW_FSFPRINTF
  253   
  254   #define _FLAG_MINUS 0x1             // FSfprintf minus flag indicator
  255   #define _FLAG_PLUS  0x2             // FSfprintf plus flag indicator
  256   #define _FLAG_SPACE 0x4             // FSfprintf space flag indicator
  257   #define _FLAG_OCTO  0x8             // FSfprintf octothorpe (hash mark) flag indicator
  258   #define _FLAG_ZERO  0x10            // FSfprintf zero flag indicator
  259   #define _FLAG_SIGNED 0x80           // FSfprintf signed flag indicator
  260   
  261   #ifdef __18CXX
  262       #define _FMT_UNSPECIFIED 0      // FSfprintf unspecified argument size flag
  263       #define _FMT_LONG 1             // FSfprintf 32-bit argument size flag
  264       #define _FMT_SHRTLONG 2         // FSfprintf 24-bit argument size flag
  265       #define _FMT_BYTE   3           // FSfprintf 8-bit argument size flag
  266   #else
  267       #define _FMT_UNSPECIFIED 0      // FSfprintf unspecified argument size flag
  268       #define _FMT_LONGLONG 1         // FSfprintf 64-bit argument size flag
  269       #define _FMT_LONG 2             // FSfprintf 32-bit argument size flag
  270       #define _FMT_BYTE 3             // FSfprintf 8-bit argument size flag
  271   #endif
  272   
  273   #ifdef __18CXX
  274       static const rom char s_digits[] = "0123456789abcdef";      // FSfprintf table of conversion digits
  275   #else
  276       static const char s_digits[] = "0123456789abcdef";          // FSfprintf table of conversion digits
  277   #endif
  278   
  279   #endif
  280   
  281   /************************************************************************************/
  282   /*                               Prototypes                                         */
  283   /************************************************************************************/
  284   
  285   DWORD ReadFAT (DISK *dsk, DWORD ccls);
  286   DIRENTRY Cache_File_Entry( FILEOBJ fo, WORD * curEntry, BYTE ForceRead);
  287   BYTE Fill_File_Object(FILEOBJ fo, WORD *fHandle);
  288   DWORD Cluster2Sector(DISK * disk, DWORD cluster);
  289   DIRENTRY LoadDirAttrib(FILEOBJ fo, WORD *fHandle);
  290   #ifdef INCREMENTTIMESTAMP
  291       void IncrementTimeStamp(DIRENTRY dir);
  292   #elif defined USEREALTIMECLOCK
  293       void CacheTime (void);
  294   #endif
  295   
  296   #if defined (__C30__) || defined (__PIC32MX__)
  297       BYTE ReadByte( BYTE* pBuffer, WORD index );
  298       WORD ReadWord( BYTE* pBuffer, WORD index );
  299       DWORD ReadDWord( BYTE* pBuffer, WORD index );
  300   #endif
  301   
  302   void FileObjectCopy(FILEOBJ foDest,FILEOBJ foSource);
  303   FILE_DIR_NAME_TYPE ValidateChars(BYTE mode);
  304   BYTE FormatFileName( const char* fileName, FILEOBJ fptr, BYTE mode);
  305   CETYPE FILEfind( FILEOBJ foDest, FILEOBJ foCompareTo, BYTE cmd, BYTE mode);
  306   BYTE FILEget_next_cluster(FILEOBJ fo, DWORD n);
  307   CETYPE FILEopen (FILEOBJ fo, WORD *fHandle, char type);
  308   #if defined(SUPPORT_LFN)
  309   BOOL Alias_LFN_Object(FILEOBJ fo);
  310   BYTE Fill_LFN_Object(FILEOBJ fo, LFN_ENTRY *lfno, WORD *fHandle);
  311   #endif
  312   
  313   // Write functions
  314   #ifdef ALLOW_WRITES
  315       BYTE Write_File_Entry( FILEOBJ fo, WORD * curEntry);
  316       BYTE flushData (void);
  317       CETYPE FILEerase( FILEOBJ fo, WORD *fHandle, BYTE EraseClusters);
  318       BYTE FILEallocate_new_cluster( FILEOBJ fo, BYTE mode);
  319       BYTE FAT_erase_cluster_chain (DWORD cluster, DISK * dsk);
  320       DWORD FATfindEmptyCluster(FILEOBJ fo);
  321       BYTE FindEmptyEntries(FILEOBJ fo, WORD *fHandle);
  322       BYTE PopulateEntries(FILEOBJ fo, WORD *fHandle, BYTE mode);
  323       CETYPE FILECreateHeadCluster( FILEOBJ fo, DWORD *cluster);
  324       BYTE EraseCluster(DISK *disk, DWORD cluster);
  325       CETYPE CreateFirstCluster(FILEOBJ fo);
  326       DWORD WriteFAT (DISK *dsk, DWORD ccls, DWORD value, BYTE forceWrite);
  327       CETYPE CreateFileEntry(FILEOBJ fo, WORD *fHandle, BYTE mode, BOOL createFirstCluster);
  328   #endif
  329   
  330   // Directory functions
  331   #ifdef ALLOW_DIRS
  332       BYTE GetPreviousEntry (FSFILE * fo);
  333       BYTE FormatDirName (char * string,FILEOBJ fptr, BYTE mode);
  334       int CreateDIR (char * path);
  335       BYTE writeDotEntries (DISK * dsk, DWORD dotAddress, DWORD dotdotAddress);
  336       int eraseDir (char * path);
  337   #ifdef ALLOW_PGMFUNCTIONS
  338       #ifdef ALLOW_WRITES
  339           int mkdirhelper (BYTE mode, char * ramptr, const rom char * romptr);
  340           int rmdirhelper (BYTE mode, char * ramptr, const rom char * romptr, unsigned char rmsubdirs);
  341       #endif
  342   int chdirhelper (BYTE mode, char * ramptr, const rom char * romptr);
  343   #else
  344       #ifdef ALLOW_WRITES
  345           int mkdirhelper (BYTE mode, char * ramptr, char * romptr);
  346           int rmdirhelper (BYTE mode, char * ramptr, char * romptr, unsigned char rmsubdirs);
  347       #endif
  348       int chdirhelper (BYTE mode, char * ramptr, char * romptr);
  349   #endif
  350   #endif
  351   
  352   #ifdef ALLOW_FSFPRINTF
  353       #ifdef __18CXX
  354           int FSvfprintf (auto FSFILE *handle, auto const rom char *formatString, auto va_list ap);
  355       #else
  356           int FSvfprintf (FSFILE *handle, const char *formatString, va_list ap);
  357       #endif
  358       int FSputc (char c, FSFILE * file);
  359       unsigned char str_put_n_chars (FSFILE * handle, unsigned char n, char c);
  360   #endif
  361   
  362   BYTE DISKmount( DISK *dsk);
  363   BYTE LoadMBR(DISK *dsk);
  364   BYTE LoadBootSector(DISK *dsk);
  365   DWORD GetFullClusterNumber(DIRENTRY entry);
  366   
  367   
  368   /*************************************************************************
  369     Function:
  370       int FSInit(void)
  371     Summary:
  372       Function to initialize the device.
  373     Conditions:
  374       The physical device should be connected to the microcontroller.
  375     Input:
  376       None
  377     Return Values:
  378       TRUE -  Initialization successful
  379       FALSE - Initialization unsuccessful
  380     Side Effects:
  381       The FSerrno variable will be changed.
  382     Description:
  383       Initializes the static or dynamic memory slots for holding file
  384       structures.  Initializes the device with the DISKmount function. Loads
  385       MBR and boot sector information.  Initializes the current working
  386       directory to the root directory for the device if directory support
  387       is enabled.
  388     Remarks:
  389       None
  390     *************************************************************************/
  391   
  392   int FSInit(void)
  393   {
  394       int fIndex;
  395   #ifndef FS_DYNAMIC_MEM
  396       for( fIndex = 0; fIndex < FS_MAX_FILES_OPEN; fIndex++ )
  397           gFileSlotOpen[fIndex] = TRUE;
  398   #else
  399       #ifdef __18CXX
  400           SRAMInitHeap();
  401       #endif
  402   #endif
  403   
  404       gBufferZeroed = FALSE;
  405       gNeedFATWrite = FALSE;             
  406       gLastFATSectorRead = 0xFFFFFFFF;       
  407       gLastDataSectorRead = 0xFFFFFFFF;  
  408   
  409       MDD_InitIO();
  410   
  411       if(DISKmount(&gDiskData) == CE_GOOD)
  412       {
  413       // Initialize the current working directory to the root
  414   #ifdef ALLOW_DIRS
  415           cwdptr->dsk = &gDiskData;
  416           cwdptr->sec = 0;
  417           cwdptr->pos = 0;
  418           cwdptr->seek = 0;
  419           cwdptr->size = 0;
  420           cwdptr->name[0] = '\\';
  421           for (fIndex = 1; fIndex < 11; fIndex++)
  422           {
  423               cwdptr->name[fIndex] = 0x20;
  424           }
  425           cwdptr->entry = 0;
  426           cwdptr->attributes = ATTR_DIRECTORY;
  427           // "FatRootDirClusterValue" indicates the root
  428           cwdptr->dirclus = FatRootDirClusterValue;
  429           cwdptr->dirccls = FatRootDirClusterValue;
  430   	#if defined(SUPPORT_LFN)
  431   		// Initialize default values for LFN support
  432           cwdptr->AsciiEncodingType = TRUE;
  433           cwdptr->utf16LFNlength = 0x0000;
  434   	#endif
  435   #endif
  436   
  437           FSerrno = 0;
  438           return TRUE;
  439       }
  440   
  441       return FALSE;
  442   }
  443   
  444   
  445   /********************************************************************************
  446     Function:
  447       CETYPE FILEfind (FILEOBJ foDest, FILEOBJ foCompareTo, BYTE cmd, BYTE mode)
  448     Summary
  449       Finds a file on the device
  450     Conditions:
  451       This function should not be called by the user.
  452     Input:
  453       foDest -       FSFILE object containing information of the file found
  454       foCompareTo -  FSFILE object containing the name/attr of the file to be
  455                      found
  456       cmd -
  457           -          LOOK_FOR_EMPTY_ENTRY: Search for empty entry.
  458           -          LOOK_FOR_MATCHING_ENTRY: Search for matching entry.
  459       mode -
  460            -         0: Match file exactly with default attributes.
  461            -         1: Match file to user-specified attributes.
  462     Return Values:
  463       CE_GOOD -            File found.
  464       CE_FILE_NOT_FOUND -  File not found.
  465     Side Effects:
  466       None.
  467     Description:
  468       The FILEfind function will sequentially cache directory entries within
  469       the current working directory into the foDest FSFILE object.  If the cmd
  470       parameter is specified as LOOK_FOR_EMPTY_ENTRY the search will continue
  471       until an empty directory entry is found. If the cmd parameter is specified
  472       as LOOK_FOR_MATCHING_ENTRY these entries will be compared to the foCompareTo
  473       object until a match is found or there are no more entries in the current
  474       working directory. If the mode is specified a '0' the attributes of the FSFILE
  475       entries are irrelevant. If the mode is specified as '1' the attributes of the
  476       foDest entry must match the attributes specified in the foCompareTo file and
  477       partial string search characters may bypass portions of the comparison.
  478     Remarks:
  479       None
  480     ********************************************************************************/
  481   
  482   CETYPE FILEfind( FILEOBJ foDest, FILEOBJ foCompareTo, BYTE cmd, BYTE mode)
  483   {
  484       WORD   attrib, compareAttrib;
  485       WORD   fHandle = foDest->entry;                  // current entry counter
  486       CETYPE   statusB = CE_FILE_NOT_FOUND;
  487       BYTE   character,test,state,index;
  488   
  489   	#if defined(SUPPORT_LFN)
  490   
  491    	LFN_ENTRY lfnObject;	// Long File Name Object
  492   	unsigned char *dst = (unsigned char *)&fileFoundString[0];
  493   	unsigned short int *templfnPtr = (unsigned short int *)foCompareTo -> utf16LFNptr;
  494   	UINT16_VAL tempShift;
  495   	short int   fileCompareLfnIndex,fileFoundLfnIndex = 0,fileFoundMaxLfnIndex = 0,lfnCountIndex;
  496   	BOOL  lfnFirstCheck = FALSE,foundSFN,foundLFN,fileFoundDotPosition = FALSE,fileCompareDotPosition;
  497   	BYTE  lfnMaxSequenceNum = 0,reminder = 0;
  498   
  499   	fileNameLength = foCompareTo->utf16LFNlength;
  500   
  501   	// If 'fileNameLength' is non zero then it means that file name is of LFN format.
  502   	// If 'fileNameLength' is zero then it means that file name is of 8.3 format
  503   	if(fileNameLength)
  504   	{
  505   		// Find out the number of root entries for the given LFN
  506   		reminder = fileNameLength % MAX_UTF16_CHARS_IN_LFN_ENTRY;
  507   
  508   		index = fileNameLength/MAX_UTF16_CHARS_IN_LFN_ENTRY;
  509   
  510   		if(reminder || (fileNameLength < MAX_UTF16_CHARS_IN_LFN_ENTRY))
  511   		{
  512   			index++;
  513   		}
  514   
  515   		// The maximum sequence number of the LFN
  516   		lfnMaxSequenceNum = (BYTE)0x40 | (BYTE)index;
  517   	}
  518   	#endif
  519   
  520       // reset the cluster
  521       foDest->dirccls = foDest->dirclus;
  522   	// Attribute to be compared as per application layer request
  523       compareAttrib = 0xFFFF ^ foCompareTo->attributes;
  524   
  525       if (fHandle == 0)
  526       {
  527           if (Cache_File_Entry(foDest, &fHandle, TRUE) == NULL)
  528           {
  529               statusB = CE_BADCACHEREAD;
  530           }
  531       }
  532       else
  533       {
  534   		// Maximum 16 entries possible
  535           if ((fHandle & MASK_MAX_FILE_ENTRY_LIMIT_BITS) != 0)
  536           {
  537               if (Cache_File_Entry (foDest, &fHandle, TRUE) == NULL)
  538               {
  539                   statusB = CE_BADCACHEREAD;
  540               }
  541           }
  542       }
  543   
  544       if (statusB != CE_BADCACHEREAD)
  545       {
  546           // Loop until you reach the end or find the file
  547           while(1)
  548           {
  549               if(statusB != CE_GOOD) //First time entry always here
  550               {
  551   				#if defined(SUPPORT_LFN)
  552                   	state = Fill_LFN_Object(foDest,&lfnObject,&fHandle);
  553   				#else
  554                   	state = Fill_File_Object(foDest, &fHandle);
  555   				#endif
  556   
  557                   if(state == NO_MORE) // Reached the end of available files. Comparision over and file not found so quit.
  558                   {
  559                       break;
  560                   }
  561               }
  562               else // statusB == CE_GOOD then exit
  563               {
  564                   break; // Code below intializes"statusB = CE_GOOD;" so, if no problem in the filled file, Exit the while loop.
  565               }
  566   
  567               if(state == FOUND) // Validate the correct matching of filled file data with the required(to be found) one.
  568               {
  569   				#if defined(SUPPORT_LFN)
  570   				foundSFN = FALSE;
  571   				foundLFN = FALSE;
  572   
  573   				if(lfnObject.LFN_Attribute != ATTR_LONG_NAME)
  574   				{
  575   					lfnFirstCheck = FALSE;
  576   					if((mode == 0x00) && fileNameLength)
  577   					{
  578   						fHandle++;
  579   						continue;
  580   					}
  581   
  582   					*dst = lfnObject.LFN_SequenceNo;
  583   					for(index = 0;index < 10;index++)
  584   						dst[index + 1] = lfnObject.LFN_Part1[index];
  585   					foundSFN = TRUE;
  586   				}
  587   				else
  588   				{
  589   					if(lfnObject.LFN_SequenceNo & 0x40)
  590   					{
  591   						if((mode == 0x00) && ((fileNameLength && (lfnObject.LFN_SequenceNo != lfnMaxSequenceNum)) || !fileNameLength))
  592   						{
  593   //							fHandle = fHandle + (lfnObject.LFN_SequenceNo & 0xBF) + 1;
  594   							fHandle++;
  595   							continue;
  596   						}
  597   
  598   						fileFoundLfnIndex = (lfnObject.LFN_SequenceNo & 0xBF) * MAX_UTF16_CHARS_IN_LFN_ENTRY - 1;
  599   						fileCompareLfnIndex = fileFoundLfnIndex;
  600   
  601   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part3[1];
  602   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part3[0];
  603   
  604   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[5];
  605   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[4];
  606   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[3];
  607   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[2];
  608   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[1];
  609   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[0];
  610   
  611   						tempShift.byte.LB = lfnObject.LFN_Part1[8];
  612   						tempShift.byte.HB = lfnObject.LFN_Part1[9];
  613   						fileFoundString[fileFoundLfnIndex--] = tempShift.Val;
  614   						tempShift.byte.LB = lfnObject.LFN_Part1[6];
  615   						tempShift.byte.HB = lfnObject.LFN_Part1[7];
  616   						fileFoundString[fileFoundLfnIndex--] = tempShift.Val;
  617   						tempShift.byte.LB = lfnObject.LFN_Part1[4];
  618   						tempShift.byte.HB = lfnObject.LFN_Part1[5];
  619   						fileFoundString[fileFoundLfnIndex--] = tempShift.Val;
  620   						tempShift.byte.LB = lfnObject.LFN_Part1[2];
  621   						tempShift.byte.HB = lfnObject.LFN_Part1[3];
  622   						fileFoundString[fileFoundLfnIndex--] = tempShift.Val;
  623   						tempShift.byte.LB = lfnObject.LFN_Part1[0];
  624   						tempShift.byte.HB = lfnObject.LFN_Part1[1];
  625   						fileFoundString[fileFoundLfnIndex--] = tempShift.Val;
  626   
  627   						if(reminder)
  628   							index = reminder;
  629    						else
  630   							index = MAX_UTF16_CHARS_IN_LFN_ENTRY;
  631   
  632   						if(mode == 0x00)
  633   						{
  634   							if((fileFoundString[fileFoundLfnIndex + index] != 0x00) && fileNameLength)
  635   							{
  636   //								fHandle = fHandle + (lfnObject.LFN_SequenceNo & 0xBF) + 1;
  637   								fHandle++;
  638   								continue;
  639   							}
  640   						}
  641   						else
  642   						{
  643   							while(fileFoundString[fileCompareLfnIndex])
  644   								fileCompareLfnIndex--;
  645   							fileFoundMaxLfnIndex = fileCompareLfnIndex - 1;
  646   						}
  647   						lfnFirstCheck = TRUE;
  648   					}
  649   					else if(lfnFirstCheck == TRUE)
  650   					{
  651   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part3[1];
  652   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part3[0];
  653   
  654   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[5];
  655   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[4];
  656   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[3];
  657   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[2];
  658   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[1];
  659   						fileFoundString[fileFoundLfnIndex--] = lfnObject.LFN_Part2[0];
  660   
  661   						tempShift.byte.LB = lfnObject.LFN_Part1[8];
  662   						tempShift.byte.HB = lfnObject.LFN_Part1[9];
  663   						fileFoundString[fileFoundLfnIndex--] = tempShift.Val;
  664   						tempShift.byte.LB = lfnObject.LFN_Part1[6];
  665   						tempShift.byte.HB = lfnObject.LFN_Part1[7];
  666   						fileFoundString[fileFoundLfnIndex--] = tempShift.Val;
  667   						tempShift.byte.LB = lfnObject.LFN_Part1[4];
  668   						tempShift.byte.HB = lfnObject.LFN_Part1[5];
  669   						fileFoundString[fileFoundLfnIndex--] = tempShift.Val;
  670   						tempShift.byte.LB = lfnObject.LFN_Part1[2];
  671   						tempShift.byte.HB = lfnObject.LFN_Part1[3];
  672   						fileFoundString[fileFoundLfnIndex--] = tempShift.Val;
  673   						tempShift.byte.LB = lfnObject.LFN_Part1[0];
  674   						tempShift.byte.HB = lfnObject.LFN_Part1[1];
  675   						fileFoundString[fileFoundLfnIndex--] = tempShift.Val;
  676   					}
  677   					else
  678            			{
  679            				fHandle++;
  680   						continue;
  681   					}
  682   
  683            			if(fileFoundLfnIndex > 0)
  684            			{
  685            				fHandle++;
  686   						continue;
  687   					}
  688   
  689   					foundLFN = TRUE;
  690   				}
  691   
  692   				lfnFirstCheck = FALSE;
  693   				statusB = CE_GOOD;
  694             	    switch (mode)
  695             	    {
  696             	        case 0:
  697             	            	if(fileNameLength)
  698             	            	{
  699             	        			// see if we are a volume id or hidden, ignore
  700             	        			// search for one. if status = TRUE we found one
  701             	        			for(fileCompareLfnIndex = 0;fileCompareLfnIndex < fileNameLength;fileCompareLfnIndex++)
  702             	        			{
  703   				  					if(foCompareTo -> AsciiEncodingType)
  704   				  					{
  705             	        				       // get the source character
  706             	        				       character = (BYTE)templfnPtr[fileCompareLfnIndex];
  707             	        				       // get the destination character
  708             	        				       test = (BYTE)fileFoundString[fileCompareLfnIndex];
  709             	        				       if((fileFoundString[fileCompareLfnIndex] > 0xFF) || (tolower(character) != tolower(test)))
  710             	        				       {
  711             	        							statusB = CE_FILE_NOT_FOUND; // Nope its not a match
  712             	        							break;
  713             	        				       }
  714   				  					}
  715   				  					else
  716   				  					{
  717   				  						if(templfnPtr[fileCompareLfnIndex] != fileFoundString[fileCompareLfnIndex])
  718   				  						{
  719             	    	  						statusB = CE_FILE_NOT_FOUND; // Nope its not a match
  720   				  							break;
  721   				  						}
  722   				  					}
  723             	        			}// for loop
  724   							}
  725   							else
  726             	            	{
  727             	    				/* We got something */
  728             	     				character = (BYTE)'m'; // random value
  729   
  730             	     				// search for one. if status = TRUE we found one
  731             	     				for(index = 0; index < DIR_NAMECOMP; index++)
  732             	     				{
  733             	     				    // get the source character
  734             	     				    character = dst[index];
  735             	     				    // get the destination character
  736             	     				    test = foCompareTo->name[index];
  737             	     				    if(tolower(character) != tolower(test))
  738             	     				    {
  739             	     				        statusB = CE_FILE_NOT_FOUND; // Nope its not a match
  740             	     				        break;
  741             	     				    }
  742             	     				}// for loop
  743   							}
  744             	        		break;
  745   
  746             	        case 1:
  747   							if(fileNameLength)
  748             	            	{
  749   								fileFoundDotPosition = FALSE;
  750   								if(foundLFN)
  751   								{
  752   									lfnCountIndex = fileFoundMaxLfnIndex;
  753   									while(lfnCountIndex > 0)
  754   									{
  755   										if(fileFoundString[lfnCountIndex] == '.')
  756   										{
  757   											fileFoundDotPosition = TRUE;
  758   											lfnCountIndex--;
  759   											break;
  760   										}
  761   										lfnCountIndex--;
  762   									}
  763   
  764   									if(fileFoundDotPosition == FALSE)
  765   										lfnCountIndex = fileFoundMaxLfnIndex;
  766   								}
  767   								else
  768   								{
  769   									if(dst[DIR_NAMESIZE] != ' ')
  770   										fileFoundDotPosition = TRUE;
  771   									lfnCountIndex = DIR_NAMESIZE - 1;
  772   								}
  773   
  774   								fileFoundLfnIndex = fileNameLength - 1;
  775   								fileCompareDotPosition = FALSE;
  776   								while(fileFoundLfnIndex > 0)
  777   								{
  778   									if(templfnPtr[fileFoundLfnIndex] == '.')
  779   									{
  780   										fileCompareDotPosition = TRUE;
  781   										fileFoundLfnIndex--;
  782   										break;
  783   									}
  784   									fileFoundLfnIndex--;
  785   								}
  786   								if(fileCompareDotPosition == FALSE)
  787   									fileFoundLfnIndex = fileNameLength - 1;
  788   
  789             	         			// Check for attribute match
  790             	         			for(fileCompareLfnIndex = 0;;)
  791             	         			{
  792             	         				if (templfnPtr[fileCompareLfnIndex] == '*')
  793             	         					break;
  794             	         			   
  795   									if(fileCompareLfnIndex > lfnCountIndex)
  796             	         			    {
  797   //										if(foundLFN)
  798   //         									fHandle++;
  799             	         			    	statusB = CE_FILE_NOT_FOUND; // Nope its not a match
  800             	         			    	break;
  801             	         			    }
  802             	         			   
  803             	         			   if (templfnPtr[fileCompareLfnIndex] != '?')
  804             	         			   {
  805   				  						if(foCompareTo -> AsciiEncodingType)
  806   				  						{
  807             	         			    		// get the source character
  808             	         			    		character = (BYTE)templfnPtr[fileCompareLfnIndex];
  809             	         			    		// get the destination character
  810             	         			    		if(foundLFN)
  811             	         			    			test = (BYTE)fileFoundString[fileCompareLfnIndex];
  812   											else
  813             	         			    			test = dst[fileCompareLfnIndex];
  814   
  815   											if((foundLFN && (fileFoundString[fileCompareLfnIndex] > 0xFF)) ||
  816             	         			    			(tolower(character) != tolower(test)))
  817             	         			    		{
  818   //												if(foundLFN)
  819   //         											fHandle++;
  820             	         			    			statusB = CE_FILE_NOT_FOUND; // Nope its not a match
  821             	         			    			break;
  822             	         			    		}
  823   				  						}
  824   				  						else
  825   				  						{
  826   				  							if((templfnPtr[fileCompareLfnIndex] != fileFoundString[fileCompareLfnIndex]) || foundSFN)
  827   				  							{
  828   //												if(foundLFN)
  829   //         											fHandle++;
  830             	    	   							statusB = CE_FILE_NOT_FOUND; // Nope its not a match
  831   				  								break;
  832   				  							}
  833   				  						}
  834             	         				}
  835   
  836                  		    	 		fileCompareLfnIndex++;
  837                  		    	 		if(fileCompareLfnIndex > fileFoundLfnIndex)
  838                  		    	        {
  839                  		    	            if(fileCompareLfnIndex <= lfnCountIndex)
  840                  		    	            {
  841   //											if(foundLFN)
  842   //         										fHandle++;
  843             	    	   						statusB = CE_FILE_NOT_FOUND; // Nope its not a match
  844   										}
  845                  		    	            break;
  846   									}
  847             	         			}// for loop
  848   
  849   								if(fileCompareDotPosition == FALSE)
  850   								{
  851   									if(fileFoundDotPosition == TRUE)
  852             	         			    {
  853   //										if(foundLFN)
  854   //         									fHandle++;
  855             	         			    	statusB = CE_FILE_NOT_FOUND; // Nope its not a match
  856             	         			    }
  857   									break;
  858   								}
  859   								else
  860   								{
  861   									if(fileFoundDotPosition == FALSE)
  862             	         			    {
  863   //										if(foundLFN)
  864   //         									fHandle++;
  865             	         			    	statusB = CE_FILE_NOT_FOUND; // Nope its not a match
  866             	         			    	break;
  867             	         			    }
  868   
  869   									if(foundLFN)
  870   										lfnCountIndex = lfnCountIndex + 2;
  871   									else
  872   										lfnCountIndex = DIR_NAMESIZE;
  873   								}
  874   
  875             	         			// Check for attribute match
  876             	         			for(fileCompareLfnIndex = fileFoundLfnIndex + 2;;)
  877             	         			{
  878             	         				if (templfnPtr[fileCompareLfnIndex] == '*')
  879             	         					break;
  880             	         			   
  881   									if((foundLFN && (lfnCountIndex > fileFoundMaxLfnIndex)) || (foundSFN && (lfnCountIndex == 11)))
  882   									{
  883   //										if(foundLFN)
  884   //         									fHandle++;
  885             	         			    	statusB = CE_FILE_NOT_FOUND; // Nope its not a match
  886             	         			    	break;
  887   									}
  888             	         			   
  889             	         			   if (templfnPtr[fileCompareLfnIndex] != '?')
  890             	         			   {
  891   				  						if(foCompareTo -> AsciiEncodingType)
  892   				  						{
  893             	         			    	    // get the source character
  894             	         			    	    character = (BYTE)templfnPtr[fileCompareLfnIndex];
  895             	         			    	    // get the destination character
  896             	         			    		if(foundLFN)
  897             	         			    			test = (BYTE)fileFoundString[lfnCountIndex];
  898   											else
  899             	         			    			test = dst[lfnCountIndex];
  900   											if((foundLFN && (fileFoundString[lfnCountIndex] > 0xFF)) ||
  901             	         			    			(tolower(character) != tolower(test)))
  902             	         			    		{
  903   //												if(foundLFN)
  904   //         											fHandle++;
  905             	         			    			statusB = CE_FILE_NOT_FOUND; // Nope its not a match
  906             	         			    			break;
  907             	         			    		}
  908   				  						}
  909   				  						else
  910   				  						{
  911   				  							if((templfnPtr[fileCompareLfnIndex] != fileFoundString[lfnCountIndex]) || foundSFN)
  912   				  							{
  913   //												if(foundLFN)
  914   //         											fHandle++;
  915             	    	   							statusB = CE_FILE_NOT_FOUND; // Nope its not a match
  916   				  								break;
  917   				  							}
  918   				  						}
  919             	         				}
  920                  		    	 		lfnCountIndex++;
  921                  		    	 		fileCompareLfnIndex++;
  922                  		    	 		if(fileCompareLfnIndex == (fileNameLength - 1))
  923                  		    	        {
  924                  		    	            if((foundLFN && (lfnCountIndex <= fileFoundMaxLfnIndex)) || (foundSFN && (lfnCountIndex < 11) && (dst[lfnCountIndex] != ' ')))
  925                  		    	            {
  926   //											if(foundLFN)
  927   //         										fHandle++;
  928             	    	   						statusB = CE_FILE_NOT_FOUND; // Nope its not a match
  929   										}
  930                  		    	            break;
  931   									}
  932             	         			}// for loop
  933   							}
  934   							else
  935   							{
  936             	    				/* We got something */
  937                  		    		if(foundLFN)
  938                  		    		{
  939   									fileCompareLfnIndex = fileFoundMaxLfnIndex;
  940   									fileFoundDotPosition = FALSE;
  941   									while(fileCompareLfnIndex > 0)
  942   									{
  943   										if(fileFoundString[fileCompareLfnIndex] == '.')
  944   										{
  945   											fileFoundDotPosition = TRUE;
  946   											fileCompareLfnIndex--;
  947   											break;
  948   										}
  949   										fileCompareLfnIndex--;
  950   									}
  951   
  952   									if(fileFoundDotPosition == FALSE)
  953   										fileCompareLfnIndex = fileFoundMaxLfnIndex;
  954   								}
  955   								else
  956   									fileCompareLfnIndex = DIR_NAMESIZE - 1;	// Short File name last char position
  957   
  958                  		    	    character = (BYTE)'m';             // random value
  959                  		    	    if (foCompareTo->name[0] != '*')   //If "*" is passed for comparion as 1st char then don't proceed. Go back, file alreay found.
  960                  		    	    {
  961                  		    	        for (index = 0;;)
  962                  		    	        {
  963                  		    	            if(foundLFN)
  964                  		    	            {
  965                  		    	            	if((fileFoundString[index] > 0xFF) || (index > fileCompareLfnIndex))
  966                  		    	            	{
  967   //         										fHandle++;
  968                  		    	                	statusB = CE_FILE_NOT_FOUND; // it's not a match
  969                  		    	                	break;
  970   											}
  971   										}
  972   
  973                  		    	            // Get the source character
  974             	         					if(foundLFN)
  975             	         						character = (BYTE)fileFoundString[index];
  976   										else
  977             	         						character = dst[index];
  978   
  979                  		    	            // Get the destination character
  980                  		    	            test = foCompareTo->name[index];
  981                  		    	            if (test == '*')
  982                  		    	                break;
  983                  		    	            if (test != '?')
  984                  		    	            {
  985                  		    	                if(tolower(character) != tolower(test))
  986                  		    	                {
  987                  		    	                    statusB = CE_FILE_NOT_FOUND; // it's not a match
  988                  		    	                    break;
  989                  		    	                }
  990                  		    	            }
  991                  		    	 
  992                  		    	 			index++;
  993                  		    	 			if(index == DIR_NAMESIZE)
  994                  		    	        	{
  995                  		    	        	    if(foundLFN && (index <= fileCompareLfnIndex))
  996                  		    	        	    {
  997   //         										fHandle++;
  998                  		    	                	statusB = CE_FILE_NOT_FOUND; // it's not a match
  999   											}
 1000                  		    	                break;
 1001   										}
 1002                  		    	        }
 1003                  		    	    }
 1004   
 1005                  		    	    // Before calling this "FILEfind" fn, "formatfilename" must be called. Hence, extn always starts from position "8".
 1006                  		    	    if ((foCompareTo->name[8] != '*') && (statusB == CE_GOOD))
 1007                  		    	    {
 1008                  		    	        if(foundLFN)
 1009                  		    	        {
 1010   										if(foCompareTo->name[8] == ' ')
 1011   										{
 1012   											if(fileFoundDotPosition == TRUE)
 1013                  		    	        	    {
 1014   //         										fHandle++;
 1015                  		    	                	statusB = CE_FILE_NOT_FOUND; // it's not a match
 1016   											}
 1017   											break;
 1018   										}
 1019   										else
 1020   										{
 1021   											if(fileFoundDotPosition == FALSE)
 1022   											{
 1023   //         										fHandle++;
 1024                  		    	                	statusB = CE_FILE_NOT_FOUND; // it's not a match
 1025   												break;
 1026   											}
 1027   										}
 1028   										fileCompareLfnIndex = fileCompareLfnIndex + 2;
 1029   									}
 1030   									else
 1031   										fileCompareLfnIndex = DIR_NAMESIZE;
 1032   
 1033                  		    	        for (index = 8;;)
 1034                  		    	        {
 1035                  		    	            if(foundLFN)
 1036                  		    	            {
 1037                  		    	            	if((fileFoundString[fileCompareLfnIndex] > 0xFF) || (fileCompareLfnIndex > fileFoundMaxLfnIndex))
 1038                  		    	            	{
 1039   //         										fHandle++;
 1040                  		    	                	statusB = CE_FILE_NOT_FOUND; // it's not a match
 1041                  		    	                	break;
 1042   											}
 1043   										}
 1044                  		    	            // Get the destination character
 1045                  		    	            test = foCompareTo->name[index];
 1046                  		    	            // Get the source character
 1047             	         					if(foundLFN)
 1048             	         						character = (BYTE)fileFoundString[fileCompareLfnIndex++];
 1049   										else
 1050             	         						character = dst[fileCompareLfnIndex++];
 1051   
 1052                  		    	            if (test == '*')
 1053                  		    	                break;
 1054                  		    	            if (test != '?')
 1055                  		    	            {
 1056                  		    	                if(tolower(character) != tolower(test))
 1057                  		    	                {
 1058                  		    	                    statusB = CE_FILE_NOT_FOUND; // it's not a match
 1059                  		    	                    break;
 1060                  		    	                }
 1061                  		    	            }
 1062   
 1063   										index++;
 1064   										if(index == DIR_NAMECOMP)
 1065   										{
 1066   											if(foundLFN && (fileCompareLfnIndex <= fileFoundMaxLfnIndex))
 1067   												statusB = CE_FILE_NOT_FOUND; // it's not a match
 1068   											break;
 1069   										}
 1070                  		    	        }
 1071                  		    	    }
 1072   							}
 1073             	            	break;
 1074   				  }
 1075   
 1076   				// If the comparision of each character in LFN is completed
 1077   				if(statusB == CE_GOOD)
 1078   				{
 1079   					if(foundLFN)
 1080   						fHandle++;
 1081   
 1082                   	state = Fill_File_Object(foDest, &fHandle);
 1083   
 1084                  		/* We got something get the attributes */
 1085                  		attrib = foDest->attributes;
 1086   
 1087                  		attrib &= ATTR_MASK;
 1088   
 1089                  		switch (mode)
 1090                  		{
 1091                  		    case 0:
 1092                  		        // see if we are a volume id or hidden, ignore
 1093                  		        if(attrib == ATTR_VOLUME)
 1094                  		            statusB = CE_FILE_NOT_FOUND;
 1095                  		        break;
 1096   
 1097                  		    case 1:
 1098                  		        // Check for attribute match
 1099                  		        if ((attrib & compareAttrib) != 0)
 1100                  		            statusB = CE_FILE_NOT_FOUND; // Indicate the already filled file data is correct and go back
 1101                  		        if(foundLFN)
 1102                  		        	foDest->utf16LFNlength = fileFoundMaxLfnIndex + 1;
 1103                  		        else
 1104   								foDest->utf16LFNlength = 0;
 1105                  		        break;
 1106                  		}
 1107   				}
 1108   				#else
 1109   				{
 1110                  		 /* We got something */
 1111                  		 // get the attributes
 1112                  		 attrib = foDest->attributes;
 1113   
 1114                  		 attrib &= ATTR_MASK;
 1115                  		 switch (mode)
 1116                  		 {
 1117                  		     case 0:
 1118                  		         // see if we are a volume id or hidden, ignore
 1119                  		         if(attrib != ATTR_VOLUME)
 1120                  		         {
 1121                  		             statusB = CE_GOOD;
 1122                  		             character = (BYTE)'m'; // random value
 1123   
 1124                  		             // search for one. if status = TRUE we found one
 1125                  		             for(index = 0; index < DIR_NAMECOMP; index++)
 1126                  		             {
 1127                  		                 // get the source character
 1128                  		                 character = foDest->name[index];
 1129                  		                 // get the destination character
 1130                  		                 test = foCompareTo->name[index];
 1131                  		                 if(tolower(character) != tolower(test))
 1132                  		                 {
 1133                  		                     statusB = CE_FILE_NOT_FOUND; // Nope its not a match
 1134                  		                     break;
 1135                  		                 }
 1136                  		             }// for loop
 1137                  		         } // not dir nor vol
 1138                  		         break;
 1139   
 1140                  		     case 1:
 1141                  		         // Check for attribute match
 1142                  		         if (((attrib & compareAttrib) == 0) && (attrib != ATTR_LONG_NAME))
 1143                  		         {
 1144                  		             statusB = CE_GOOD;                 // Indicate the already filled file data is correct and go back
 1145                  		             character = (BYTE)'m';             // random value
 1146                  		             if (foCompareTo->name[0] != '*')   //If "*" is passed for comparion as 1st char then don't proceed. Go back, file alreay found.
 1147                  		             {
 1148                  		                 for (index = 0; index < DIR_NAMESIZE; index++)
 1149                  		                 {
 1150                  		                     // Get the source character
 1151                  		                     character = foDest->name[index];
 1152                  		                     // Get the destination character
 1153                  		                     test = foCompareTo->name[index];
 1154                  		                     if (test == '*')
 1155                  		                         break;
 1156                  		                     if (test != '?')
 1157                  		                     {
 1158                  		                         if(tolower(character) != tolower(test))
 1159                  		                         {
 1160                  		                             statusB = CE_FILE_NOT_FOUND; // it's not a match
 1161                  		                             break;
 1162                  		                         }
 1163                  		                     }
 1164                  		                 }
 1165                  		             }
 1166   
 1167                  		             // Before calling this "FILEfind" fn, "formatfilename" must be called. Hence, extn always starts from position "8".
 1168                  		             if ((foCompareTo->name[8] != '*') && (statusB == CE_GOOD))
 1169                  		             {
 1170                  		                 for (index = 8; index < DIR_NAMECOMP; index++)
 1171                  		                 {
 1172                  		                     // Get the source character
 1173                  		                     character = foDest->name[index];
 1174                  		                     // Get the destination character
 1175                  		                     test = foCompareTo->name[index];
 1176                  		                     if (test == '*')
 1177                  		                         break;
 1178                  		                     if (test != '?')
 1179                  		                     {
 1180                  		                         if(tolower(character) != tolower(test))
 1181                  		                         {
 1182                  		                             statusB = CE_FILE_NOT_FOUND; // it's not a match
 1183                  		                             break;
 1184                  		                         }
 1185                  		                     }
 1186                  		                 }
 1187                  		             }
 1188   
 1189                  		         } // Attribute match
 1190   
 1191                  		         break;
 1192                  		 }
 1193               	}
 1194     				#endif
 1195               } // not found
 1196               else
 1197               {
 1198   				#if defined(SUPPORT_LFN)
 1199   					lfnFirstCheck = FALSE;
 1200   				#endif
 1201                   /*** looking for an empty/re-usable entry ***/
 1202                   if ( cmd == LOOK_FOR_EMPTY_ENTRY)
 1203                       statusB = CE_GOOD;
 1204               } // found or not
 1205   
 1206               // increment it no matter what happened
 1207               fHandle++;
 1208   
 1209           }// while
 1210       }
 1211   
 1212       return(statusB);
 1213   } // FILEFind
 1214   
 1215   
 1216   /**************************************************************************
 1217     Function:
 1218       CETYPE FILEopen (FILEOBJ fo, WORD *fHandle, char type)
 1219     Summary:
 1220       Loads file information from the device
 1221     Conditions:
 1222       This function should not be called by the user.
 1223     Input:
 1224       fo -       File to be opened
 1225       fHandle -  Location of file
 1226       type -
 1227            -     WRITE -  Create a new file or replace an existing file
 1228            -     READ -   Read data from an existing file
 1229            -     APPEND - Append data to an existing file
 1230     Return Values:
 1231       CE_GOOD -            FILEopen successful
 1232       CE_NOT_INIT -        Device is not yet initialized
 1233       CE_FILE_NOT_FOUND -  Could not find the file on the device
 1234       CE_BAD_SECTOR_READ - A bad read of a sector occured
 1235     Side Effects:
 1236       None
 1237     Description:
 1238       This function will cache a directory entry in the directory specified
 1239       by the dirclus parameter of hte FSFILE object 'fo.'  The offset of the
 1240       entry in the directory is specified by fHandle.  Once the directory entry
 1241       has been loaded, the first sector of the file can be loaded using the
 1242       cluster value specified in the directory entry. The type argument will
 1243       specify the mode the files will be opened in.  This will allow this
 1244       function to set the correct read/write flags for the file.
 1245     Remarks:
 1246       If the mode the file is being opened in is a plus mode (e.g. READ+) the
 1247       flags will be modified further in the FSfopen function.
 1248     **************************************************************************/
 1249   
 1250   CETYPE FILEopen (FILEOBJ fo, WORD *fHandle, char type)
 1251   {
 1252       DISK   *dsk;      //Disk structure
 1253       BYTE    r;               //Result of search for file
 1254       DWORD    l;               //lba of first sector of first cluster
 1255       CETYPE    error = CE_GOOD;
 1256   
 1257       dsk = (DISK *)(fo->dsk);
 1258       if (dsk->mount == FALSE)
 1259       {
 1260           error = CE_NOT_INIT;
 1261       }
 1262       else
 1263       {
 1264           // load the sector
 1265           fo->dirccls = fo->dirclus;
 1266           // Cache no matter what if it's the first entry
 1267           if (*fHandle == 0)
 1268           {
 1269               if (Cache_File_Entry(fo, fHandle, TRUE) == NULL)
 1270               {
 1271                   error = CE_BADCACHEREAD;
 1272               }
 1273           }
 1274           else
 1275           {
 1276               // If it's not the first, only cache it if it's
 1277               // not divisible by the number of entries per sector
 1278               // If it is, Fill_File_Object will cache it
 1279               if ((*fHandle & 0xf) != 0)
 1280               {
 1281                   if (Cache_File_Entry (fo, fHandle, TRUE) == NULL)
 1282                   {
 1283                       error = CE_BADCACHEREAD;
 1284                   }
 1285               }
 1286           }
 1287   
 1288           // Fill up the File Object with the information pointed to by fHandle
 1289           r = Fill_File_Object(fo, fHandle);
 1290           if (r != FOUND)
 1291               error = CE_FILE_NOT_FOUND;
 1292           else
 1293           {
 1294               fo->seek = 0;               // first byte in file
 1295               fo->ccls = fo->cluster;     // first cluster
 1296               fo->sec = 0;                // first sector in the cluster
 1297               fo->pos = 0;                // first byte in sector/cluster
 1298   
 1299               if  ( r == NOT_FOUND)
 1300               {
 1301                   error = CE_FILE_NOT_FOUND;
 1302               }
 1303               else
 1304               {
 1305                   // Determine the lba of the selected sector and load
 1306                   l = Cluster2Sector(dsk,fo->ccls);
 1307   #ifdef ALLOW_WRITES
 1308                   if (gNeedDataWrite)
 1309                       if (flushData())
 1310                           return CE_WRITE_ERROR;
 1311   #endif
 1312                   gBufferOwner = fo;
 1313                   if (gLastDataSectorRead != l)
 1314                   {
 1315                       gBufferZeroed = FALSE;
 1316                       if ( !MDD_SectorRead( l, dsk->buffer))
 1317                           error = CE_BAD_SECTOR_READ;
 1318                       gLastDataSectorRead = l;
 1319                   }
 1320               } // -- found
 1321   
 1322               fo->flags.FileWriteEOF = FALSE;
 1323               // Set flag for operation type
 1324   #ifdef ALLOW_WRITES
 1325               if ((type == 'w') || (type == 'a'))
 1326               {
 1327                   fo->flags.write = 1;   //write or append
 1328                   fo->flags.read = 0;
 1329               }
 1330               else
 1331               {
 1332   #endif
 1333                   fo->flags.write = 0;   //read
 1334                   fo->flags.read = 1;
 1335   #ifdef ALLOW_WRITES
 1336               } // -- flags
 1337   #endif
 1338           } // -- r = Found
 1339       } // -- Mounted
 1340       return (error);
 1341   } // -- FILEopen
 1342   
 1343   
 1344   /*************************************************************************
 1345     Function:
 1346       BYTE FILEget_next_cluster(FILEOBJ fo, WORD n)
 1347     Summary:
 1348       Step through a chain of clusters
 1349     Conditions:
 1350       This function should not be called by the user.
 1351     Input:
 1352       fo - The file to get the next cluster of
 1353       n -  Number of links in the FAT cluster chain to jump through
 1354     Return Values:
 1355       CE_GOOD - Operation successful
 1356       CE_BAD_SECTOR_READ - A bad read occured of a sector
 1357       CE_INVALID_CLUSTER - Invalid cluster value \> maxcls
 1358       CE_FAT_EOF - Fat attempt to read beyond EOF
 1359     Side Effects:
 1360       None
 1361     Description:
 1362       This function will load 'n' proximate clusters for a file from
 1363       the FAT on the device.  It will stop checking for clusters if the
 1364       ReadFAT function returns an error, if it reaches the last cluster in
 1365       a file, or if the device tries to read beyond the last cluster used
 1366       by the device.
 1367     Remarks:
 1368       None
 1369     *************************************************************************/
 1370   
 1371   BYTE FILEget_next_cluster(FILEOBJ fo, DWORD n)
 1372   {
 1373       DWORD         c, c2, ClusterFailValue, LastClustervalue;
 1374       BYTE          error = CE_GOOD;
 1375       DISK *      disk;
 1376   
 1377       disk = fo->dsk;
 1378   
 1379       /* Settings based on FAT type */
 1380       switch (disk->type)
 1381       {
 1382   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 1383           case FAT32:
 1384               LastClustervalue = LAST_CLUSTER_FAT32;
 1385               ClusterFailValue  = CLUSTER_FAIL_FAT32;
 1386               break;
 1387   #endif
 1388           case FAT12:
 1389               LastClustervalue = LAST_CLUSTER_FAT12;
 1390               ClusterFailValue  = CLUSTER_FAIL_FAT16;
 1391               break;
 1392           case FAT16:
 1393           default:
 1394               LastClustervalue = LAST_CLUSTER_FAT16;
 1395               ClusterFailValue  = CLUSTER_FAIL_FAT16;
 1396               break;
 1397       }
 1398   
 1399       // loop n times
 1400       do
 1401       {
 1402           // get the next cluster link from FAT
 1403           c2 = fo->ccls;
 1404           if ( (c = ReadFAT( disk, c2)) == ClusterFailValue)
 1405               error = CE_BAD_SECTOR_READ;
 1406           else
 1407           {
 1408               // check if cluster value is valid
 1409               if ( c >= disk->maxcls)
 1410               {
 1411                   error = CE_INVALID_CLUSTER;
 1412               }
 1413   
 1414               // compare against max value of a cluster in FAT
 1415               // return if eof
 1416               if ( c >= LastClustervalue)    // check against eof
 1417               {
 1418                   error = CE_FAT_EOF;
 1419               }
 1420           }
 1421   
 1422           // update the FSFILE structure
 1423           fo->ccls = c;
 1424   
 1425       } while ((--n > 0) && (error == CE_GOOD));// loop end
 1426   
 1427       return(error);
 1428   } // get next cluster
 1429   
 1430   
 1431   /**************************************************************************
 1432     Function:
 1433       BYTE DISKmount ( DISK *dsk)
 1434     Summary:
 1435       Initialies the device and loads MBR and boot sector information
 1436     Conditions:
 1437       This function should not be called by the user.
 1438     Input:
 1439       dsk -  The disk structure to be initialized.
 1440     Return Values:
 1441       CE_GOOD -       Disk mounted
 1442       CE_INIT_ERROR - Initialization error has occured
 1443       CE_UNSUPPORTED_SECTOR_SIZE - Media sector size bigger than
 1444                   MEDIA_SECTOR_SIZE as defined in FSconfig.h.
 1445     Side Effects:
 1446       None
 1447     Description:
 1448       This function will use the function pointed to by the MDD_MediaInitialize
 1449       function pointer to initialize the device (if any initialization is
 1450       required).  It then attempts to load the master boot record with the
 1451       LoadMBR function and the boot sector with the LoadBootSector function.
 1452       These two functions will be used to initialize a global DISK structure
 1453       that will be used when accessing file information in the future.
 1454     Remarks:
 1455       None
 1456     **************************************************************************/
 1457   
 1458   BYTE DISKmount( DISK *dsk)
 1459   {
 1460       BYTE                error = CE_GOOD;
 1461       MEDIA_INFORMATION   *mediaInformation;
 1462   
 1463       dsk->mount = FALSE; // default invalid
 1464       dsk->buffer = gDataBuffer;    // assign buffer
 1465   
 1466       // Initialize the device
 1467       mediaInformation = MDD_MediaInitialize();
 1468       if (mediaInformation->errorCode != MEDIA_NO_ERROR)
 1469       {
 1470           error = CE_INIT_ERROR;
 1471           FSerrno = CE_INIT_ERROR;
 1472       }
 1473       else
 1474       {
 1475           // If the media initialization routine determined the sector size,
 1476           // check it and make sure we can support it.
 1477           if (mediaInformation->validityFlags.bits.sectorSize)
 1478           {
 1479   			dsk->sectorSize = mediaInformation->sectorSize;
 1480               if (mediaInformation->sectorSize > MEDIA_SECTOR_SIZE)
 1481               {
 1482                   error = CE_UNSUPPORTED_SECTOR_SIZE;
 1483                   FSerrno = CE_UNSUPPORTED_SECTOR_SIZE;
 1484                   return error;
 1485               }
 1486           }
 1487   
 1488           // Load the Master Boot Record (partition)
 1489           if((error = LoadMBR(dsk)) == CE_GOOD)
 1490           {
 1491               // Now the boot sector
 1492               if((error = LoadBootSector(dsk)) == CE_GOOD)
 1493                   dsk->mount = TRUE; // Mark that the DISK mounted successfully
 1494           }
 1495       } // -- Load file parameters
 1496   
 1497       return(error);
 1498   } // -- mount
 1499   
 1500   
 1501   
 1502   /********************************************************************
 1503     Function:
 1504       CETYPE LoadMBR ( DISK *dsk)
 1505     Summary:
 1506       Loads the MBR and extracts necessary information
 1507     Conditions:
 1508       This function should not be called by the user.
 1509     Input:
 1510       dsk -  The disk containing the master boot record to be loaded
 1511     Return Values:
 1512       CE_GOOD -            MBR loaded successfully
 1513       CE_BAD_SECTOR_READ - A bad read occured of a sector
 1514       CE_BAD_PARTITION -   The boot record is bad
 1515     Side Effects:
 1516       None
 1517     Description:
 1518       The LoadMBR function will use the function pointed to by the
 1519       MDD_SectorRead function pointer to read the 0 sector from the
 1520       device.  If a valid boot signature is obtained, this function
 1521       will compare fields in that cached sector to the values that
 1522       would be present if that sector was a boot sector.  If all of
 1523       those values match, it will be assumed that the device does not
 1524       have a master boot record and the 0 sector is actually the boot
 1525       sector.  Otherwise, data about the partition and the actual
 1526       location of the boot sector will be loaded from the MBR into
 1527       the DISK structure pointed to by 'dsk.'
 1528     Remarks:
 1529       None
 1530     ********************************************************************/
 1531   
 1532   BYTE LoadMBR(DISK *dsk)
 1533   {
 1534       PT_MBR  Partition;
 1535       BYTE error = CE_GOOD;
 1536       BYTE type;
 1537       BootSec BSec;
 1538   
 1539       // Get the partition table from the MBR
 1540       if ( MDD_SectorRead( FO_MBR, dsk->buffer) != TRUE)
 1541       {
 1542           error = CE_BAD_SECTOR_READ;
 1543           FSerrno = CE_BAD_SECTOR_READ;
 1544       }
 1545       else
 1546       {
 1547           // Check if the card has no MBR
 1548           BSec = (BootSec) dsk->buffer;
 1549   
 1550           if((BSec->Signature0 == FAT_GOOD_SIGN_0) && (BSec->Signature1 == FAT_GOOD_SIGN_1))
 1551           {
 1552            // Technically, the OEM name is not for indication
 1553            // The alternative is to read the CIS from attribute
 1554            // memory.  See the PCMCIA metaformat for more details
 1555   #if defined (__C30__) || defined (__PIC32MX__)
 1556               if ((ReadByte( dsk->buffer, BSI_FSTYPE ) == 'F') && \
 1557               (ReadByte( dsk->buffer, BSI_FSTYPE + 1 ) == 'A') && \
 1558               (ReadByte( dsk->buffer, BSI_FSTYPE + 2 ) == 'T') && \
 1559               (ReadByte( dsk->buffer, BSI_FSTYPE + 3 ) == '1') && \
 1560               (ReadByte( dsk->buffer, BSI_BOOTSIG) == 0x29))
 1561   #else
 1562               if ((BSec->FAT.FAT_16.BootSec_FSType[0] == 'F') && \
 1563                   (BSec->FAT.FAT_16.BootSec_FSType[1] == 'A') && \
 1564                   (BSec->FAT.FAT_16.BootSec_FSType[2] == 'T') && \
 1565                   (BSec->FAT.FAT_16.BootSec_FSType[3] == '1') && \
 1566                   (BSec->FAT.FAT_16.BootSec_BootSig == 0x29))
 1567   #endif
 1568                {
 1569                   dsk->firsts = 0;
 1570                   dsk->type = FAT16;
 1571                   return CE_GOOD;
 1572                }
 1573                else
 1574                {
 1575   #if defined (__C30__) || defined (__PIC32MX__)
 1576                   if ((ReadByte( dsk->buffer, BSI_FAT32_FSTYPE ) == 'F') && \
 1577                       (ReadByte( dsk->buffer, BSI_FAT32_FSTYPE + 1 ) == 'A') && \
 1578                       (ReadByte( dsk->buffer, BSI_FAT32_FSTYPE + 2 ) == 'T') && \
 1579                       (ReadByte( dsk->buffer, BSI_FAT32_FSTYPE + 3 ) == '3') && \
 1580                       (ReadByte( dsk->buffer, BSI_FAT32_BOOTSIG) == 0x29))
 1581   #else
 1582                   if ((BSec->FAT.FAT_32.BootSec_FilSysType[0] == 'F') && \
 1583                       (BSec->FAT.FAT_32.BootSec_FilSysType[1] == 'A') && \
 1584                       (BSec->FAT.FAT_32.BootSec_FilSysType[2] == 'T') && \
 1585                       (BSec->FAT.FAT_32.BootSec_FilSysType[3] == '3') && \
 1586                       (BSec->FAT.FAT_32.BootSec_BootSig == 0x29))
 1587   #endif
 1588                   {
 1589                       dsk->firsts = 0;
 1590                       dsk->type = FAT32;
 1591                       return CE_GOOD;
 1592                   }
 1593               }
 1594           }
 1595           // assign it the partition table strucutre
 1596           Partition = (PT_MBR)dsk->buffer;
 1597   
 1598           // Ensure its good
 1599           if((Partition->Signature0 != FAT_GOOD_SIGN_0) || (Partition->Signature1 != FAT_GOOD_SIGN_1))
 1600           {
 1601               FSerrno = CE_BAD_PARTITION;
 1602               error = CE_BAD_PARTITION;
 1603           }
 1604           else
 1605           {
 1606               /*    Valid Master Boot Record Loaded   */
 1607   
 1608               // Get the 32 bit offset to the first partition
 1609               dsk->firsts = Partition->Partition0.PTE_FrstSect;
 1610   
 1611               // check if the partition type is acceptable
 1612                 type = Partition->Partition0.PTE_FSDesc;
 1613   
 1614               switch (type)
 1615               {
 1616                   case 0x01:
 1617                       dsk->type = FAT12;
 1618                       break;
 1619   
 1620               case 0x04:
 1621                   case 0x06:
 1622                   case 0x0E:
 1623                       dsk->type = FAT16;
 1624                       break;
 1625   
 1626                   case 0x0B:
 1627                   case 0x0C:
 1628   
 1629   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 1630               dsk->type = FAT32;    // FAT32 is supported too
 1631   #else
 1632               FSerrno = CE_CARDFAT32;
 1633               error = CE_CARDFAT32;
 1634   #endif
 1635                       break;
 1636   
 1637                   default:
 1638                       FSerrno = CE_UNSUPPORTED_FS;
 1639                       error = CE_UNSUPPORTED_FS;
 1640               } // switch
 1641           }
 1642       }
 1643   
 1644       return(error);
 1645   }// -- LoadMBR
 1646   
 1647   
 1648   /**************************************************************************
 1649     Function:
 1650       BYTE LoadBootSector (DISK *dsk)
 1651     Summary:
 1652       Load the boot sector and extract the necessary information
 1653     Conditions:
 1654       This function should not be called by the user.
 1655     Input:
 1656       dsk -  The disk containing the boot sector
 1657     Return Values:
 1658       CE_GOOD -                    Boot sector loaded
 1659       CE_BAD_SECTOR_READ -         A bad read occured of a sector
 1660       CE_NOT_FORMATTED -           The disk is of an unsupported format
 1661       CE_CARDFAT32 -               FAT 32 device not supported
 1662       CE_UNSUPPORTED_SECTOR_SIZE - The sector size is not supported
 1663     Side Effects:
 1664       None
 1665     Description:
 1666       LoadBootSector will use the function pointed to by the MDD_SectorWrite
 1667       function pointer to load the boot sector, whose location was obtained
 1668       by a previous call of LoadMBR.  If the boot sector is loaded successfully,
 1669       partition information will be calcualted from it and copied into the DISK
 1670       structure pointed to by 'dsk.'
 1671     Remarks:
 1672       None
 1673     **************************************************************************/
 1674   
 1675   
 1676   BYTE LoadBootSector(DISK *dsk)
 1677   {
 1678       DWORD       RootDirSectors;
 1679       DWORD       TotSec,DataSec;
 1680       BYTE        error = CE_GOOD;
 1681       BootSec     BSec;
 1682       WORD        BytesPerSec;
 1683       WORD        ReservedSectorCount;
 1684   
 1685       #if defined(SUPPORT_FAT32)
 1686       BOOL        TriedSpecifiedBackupBootSec = FALSE;
 1687       BOOL        TriedBackupBootSecAtAddress6 = FALSE;
 1688       #endif
 1689       // Get the Boot sector
 1690       if ( MDD_SectorRead( dsk->firsts, dsk->buffer) != TRUE)
 1691       {
 1692           error = CE_BAD_SECTOR_READ;
 1693       }
 1694       else
 1695       {
 1696           BSec = (BootSec)dsk->buffer;
 1697   
 1698           do      //test each possible boot sector (FAT32 can have backup boot sectors)
 1699           {
 1700   
 1701               //Verify the Boot Sector has a valid signature
 1702               if(    (BSec->Signature0 != FAT_GOOD_SIGN_0)
 1703                   || (BSec->Signature1 != FAT_GOOD_SIGN_1)
 1704                 )
 1705               {
 1706                   error = CE_NOT_FORMATTED;
 1707               }
 1708               else   
 1709               {
 1710   
 1711                   do      //loop just to allow a break to jump out of this section of code
 1712                   {
 1713                       #ifdef __18CXX
 1714                       // Load count of sectors per cluster
 1715                       dsk->SecPerClus = BSec->FAT.FAT_16.BootSec_SPC;
 1716                       // Load the sector number of the first FAT sector
 1717                       dsk->fat        = dsk->firsts + BSec->FAT.FAT_16.BootSec_ResrvSec;
 1718                       // Load the count of FAT tables
 1719                       dsk->fatcopy    = BSec->FAT.FAT_16.BootSec_FATCount;
 1720                       // Load the size of the FATs
 1721                       dsk->fatsize = BSec->FAT.FAT_16.BootSec_SPF;
 1722                       if(dsk->fatsize == 0)
 1723                           dsk->fatsize  = BSec->FAT.FAT_32.BootSec_FATSz32;
 1724                       // Calculate the location of the root sector (for FAT12/16)
 1725                       dsk->root = dsk->fat + (DWORD)(dsk->fatcopy * (DWORD)dsk->fatsize);
 1726                       // Determine the max size of the root (will be 0 for FAT32)
 1727                       dsk->maxroot    = BSec->FAT.FAT_16.BootSec_RootDirEnts;
 1728       
 1729                       // Determine the total number of sectors in the partition
 1730                       if(BSec->FAT.FAT_16.BootSec_TotSec16 != 0)
 1731                       {
 1732                           TotSec = BSec->FAT.FAT_16.BootSec_TotSec16;
 1733                       }
 1734                       else
 1735                       {
 1736                           TotSec = BSec->FAT.FAT_16.BootSec_TotSec32;
 1737                       }
 1738       
 1739                       // Calculate the number of bytes in each sector
 1740                       BytesPerSec = BSec->FAT.FAT_16.BootSec_BPS;
 1741                       if( (BytesPerSec == 0) || ((BytesPerSec & 1) == 1) )
 1742                       {
 1743                           error = CE_UNSUPPORTED_SECTOR_SIZE;
 1744                           break;  //break out of the do while loop
 1745                       }
 1746       
 1747                       // Calculate the number of sectors in the root (will be 0 for FAT32)
 1748                       RootDirSectors = ((BSec->FAT.FAT_16.BootSec_RootDirEnts * 32) + (BSec->FAT.FAT_16.BootSec_BPS - 1)) / BSec->FAT.FAT_16.BootSec_BPS;
 1749                       // Calculate the number of data sectors on the card
 1750                       DataSec = TotSec - (dsk->root + RootDirSectors);
 1751                       // Calculate the maximum number of clusters on the card
 1752                       dsk->maxcls = DataSec / dsk->SecPerClus;
 1753       
 1754                       #else // PIC24/30/33
 1755       
 1756                       // Read the count of reserved sectors
 1757                       ReservedSectorCount = ReadWord( dsk->buffer, BSI_RESRVSEC );
 1758                       // Load the count of sectors per cluster
 1759                       dsk->SecPerClus = ReadByte( dsk->buffer, BSI_SPC );
 1760                       // Load the sector number of the first FAT sector
 1761                       dsk->fat = dsk->firsts + ReservedSectorCount;
 1762                       // Load the count of FAT tables
 1763                       dsk->fatcopy    = ReadByte( dsk->buffer, BSI_FATCOUNT );
 1764                       // Load the size of the FATs
 1765                       dsk->fatsize = ReadWord( dsk->buffer, BSI_SPF );
 1766                       if(dsk->fatsize == 0)
 1767                           dsk->fatsize  = ReadDWord( dsk->buffer, BSI_FATSZ32 );
 1768                       // Calculate the location of the root sector (for FAT12/16)
 1769                       dsk->root = dsk->fat + (DWORD)(dsk->fatcopy * (DWORD)dsk->fatsize);
 1770                       // Determine the max size of the root (will be 0 for FAT32)
 1771                       dsk->maxroot = ReadWord( dsk->buffer, BSI_ROOTDIRENTS );
 1772       
 1773                       // Determine the total number of sectors in the partition
 1774                       TotSec = ReadWord( dsk->buffer, BSI_TOTSEC16 );
 1775                       if( TotSec == 0 )
 1776                           TotSec = ReadDWord( dsk->buffer, BSI_TOTSEC32 );
 1777       
 1778                       // Calculate the number of bytes in each sector
 1779                       BytesPerSec = ReadWord( dsk->buffer, BSI_BPS );
 1780                       if( (BytesPerSec == 0) || ((BytesPerSec & 1) == 1) )
 1781                       {
 1782                           error = CE_UNSUPPORTED_SECTOR_SIZE;
 1783                           break;
 1784                       }
 1785       
 1786                       // Calculate the number of sectors in the root (will be 0 for FAT32)
 1787                       RootDirSectors = ((dsk->maxroot * NUMBER_OF_BYTES_IN_DIR_ENTRY) + (BytesPerSec - 1)) / BytesPerSec;
 1788                       // Calculate the number of data sectors on the card
 1789                       DataSec = TotSec - (ReservedSectorCount + (dsk->fatcopy * dsk->fatsize )  + RootDirSectors);
 1790                       // Calculate the maximum number of clusters on the card
 1791                       dsk->maxcls = DataSec / dsk->SecPerClus;
 1792       
 1793                       #endif
 1794       
 1795                       // Determine the file system type based on the number of clusters used
 1796                       if(dsk->maxcls < 4085)
 1797                       {
 1798                           dsk->type = FAT12;
 1799                       }
 1800                       else
 1801                       {
 1802                           if(dsk->maxcls < 65525)
 1803                           {
 1804                               dsk->type = FAT16;
 1805                           }
 1806                           else
 1807                           {
 1808                               #ifdef SUPPORT_FAT32
 1809                                   dsk->type = FAT32;
 1810                               #else
 1811                                   error = CE_CARDFAT32;
 1812                               #endif
 1813                           }
 1814                       }
 1815           
 1816                       #ifdef SUPPORT_FAT32
 1817                           if (dsk->type == FAT32)
 1818                           {
 1819                               #ifdef __18CXX
 1820                                   FatRootDirClusterValue =  BSec->FAT.FAT_32.BootSec_RootClus;
 1821                               #else
 1822                                   FatRootDirClusterValue = ReadDWord( dsk->buffer, BSI_ROOTCLUS );
 1823                               #endif
 1824                               dsk->data = dsk->root + RootDirSectors;
 1825                           }
 1826                           else
 1827                       #endif
 1828                       {
 1829                           FatRootDirClusterValue = 0;
 1830                           dsk->data = dsk->root + ( dsk->maxroot >> 4);
 1831                       }
 1832       
 1833                   #ifdef __18CXX
 1834                       if(BSec->FAT.FAT_16.BootSec_BPS > MEDIA_SECTOR_SIZE)
 1835                   #else
 1836                       if(BytesPerSec > MEDIA_SECTOR_SIZE)
 1837                   #endif
 1838                       {
 1839                           error = CE_UNSUPPORTED_SECTOR_SIZE;
 1840                       }
 1841   
 1842                   }while(0);  // do/while loop designed to allow to break out if
 1843                               //   there is an error detected without returning
 1844                               //   from the function.
 1845   
 1846               }
 1847   
 1848               #if defined(SUPPORT_FAT32)
 1849               if ((dsk->type == FAT32) || ((error != CE_GOOD) && ((BSec->FAT.FAT_32.BootSec_BootSig == 0x29) || (BSec->FAT.FAT_32.BootSec_BootSig == 0x28))))
 1850               {
 1851                   //Check for possible errors in the formatting
 1852                   if(    (BSec->FAT.FAT_32.BootSec_TotSec16 != 0)
 1853                       || (BSec->FAT.FAT_32.BootSec_Reserved[0] != 0)
 1854                       || (BSec->FAT.FAT_32.BootSec_Reserved[1] != 0)
 1855                       || (BSec->FAT.FAT_32.BootSec_Reserved[2] != 0)
 1856                       || (BSec->FAT.FAT_32.BootSec_Reserved[3] != 0)
 1857                       || (BSec->FAT.FAT_32.BootSec_Reserved[4] != 0)
 1858                       || (BSec->FAT.FAT_32.BootSec_Reserved[5] != 0)
 1859                       || (BSec->FAT.FAT_32.BootSec_Reserved[6] != 0)
 1860                       || (BSec->FAT.FAT_32.BootSec_Reserved[7] != 0)
 1861                       || (BSec->FAT.FAT_32.BootSec_Reserved[8] != 0)
 1862                       || (BSec->FAT.FAT_32.BootSec_Reserved[9] != 0)
 1863                       || (BSec->FAT.FAT_32.BootSec_Reserved[10] != 0)
 1864                       || (BSec->FAT.FAT_32.BootSec_Reserved[11] != 0)
 1865                       || ((BSec->FAT.FAT_32.BootSec_BootSig != 0x29) && (BSec->FAT.FAT_32.BootSec_BootSig != 0x28))
 1866                     )
 1867                   {
 1868                       error = CE_NOT_FORMATTED;
 1869                   }
 1870       
 1871                   //If there were formatting errors then in FAT32 we can try to use
 1872                   //  the backup boot sector
 1873                   if((error != CE_GOOD) && (TriedSpecifiedBackupBootSec == FALSE))
 1874                   {
 1875                       TriedSpecifiedBackupBootSec = TRUE;
 1876       
 1877                       if ( MDD_SectorRead( dsk->firsts + BSec->FAT.FAT_32.BootSec_BkBootSec, dsk->buffer) != TRUE)
 1878                       {
 1879                           FSerrno = CE_BAD_SECTOR_READ;
 1880                           return CE_BAD_SECTOR_READ;
 1881                       }
 1882                       else
 1883                       {
 1884                           error = CE_GOOD;
 1885                           continue;
 1886                       }
 1887                   }
 1888       
 1889                   if((error != CE_GOOD) && (TriedBackupBootSecAtAddress6 == FALSE))
 1890                   {
 1891                       TriedBackupBootSecAtAddress6 = TRUE;
 1892       
 1893                       //Here we are using the magic number 6 because the FAT32 specification
 1894                       //  recommends that "No value other than 6 is recommended."  We've
 1895                       //  already tried using the value specified in the BPB_BkBootSec
 1896                       //  field and it must have failed
 1897                       if ( MDD_SectorRead( dsk->firsts + 6, dsk->buffer) != TRUE)
 1898                       {
 1899                           FSerrno = CE_BAD_SECTOR_READ;
 1900                           return CE_BAD_SECTOR_READ;
 1901                       }
 1902                       else
 1903                       {
 1904                           error = CE_GOOD;
 1905                           continue;
 1906                       }
 1907                   }
 1908   
 1909               }   //type == FAT32
 1910               #endif  //SUPPORT_FAT32
 1911               break;
 1912           }
 1913           while(1);
 1914       }
 1915   
 1916       if(error != CE_GOOD)
 1917       {
 1918           FSerrno = error;
 1919       }
 1920   
 1921       return(error);
 1922   }
 1923   
 1924   
 1925   
 1926   /*************************************************************************
 1927     Function:
 1928       DWORD GetFullClusterNumber (DIRENTRY entry)
 1929     Summary:
 1930       Gets the cluster number from a directory entry
 1931     Conditions:
 1932       This function should not be called by the user.
 1933     Input:
 1934       entry - The cached directory entry to get the cluster number from
 1935     Returns:
 1936       The cluster value from the passed directory entry
 1937     Side Effects:
 1938       None.
 1939     Description:
 1940       This function will load both the high and low 16-bit first cluster
 1941       values of a file from a directory entry and copy them into a 32-bit
 1942       cluster number variable, which will be returned.
 1943     Remarks:
 1944       None
 1945     *************************************************************************/
 1946   
 1947   DWORD GetFullClusterNumber(DIRENTRY entry)
 1948   {
 1949   
 1950       DWORD TempFullClusterCalc = 0;
 1951   
 1952   #ifndef SUPPORT_FAT32 // If FAT32 Not supported.
 1953       entry->DIR_FstClusHI = 0; // If FAT32 is not supported then Higher Word of the address is "0"
 1954   #endif
 1955   
 1956       // Get the cluster
 1957       TempFullClusterCalc = (entry->DIR_FstClusHI);
 1958       TempFullClusterCalc = TempFullClusterCalc << 16;
 1959       TempFullClusterCalc |= entry->DIR_FstClusLO;
 1960   
 1961       return TempFullClusterCalc;
 1962   }
 1963   
 1964   
 1965   #ifdef ALLOW_FORMATS
 1966   #ifdef ALLOW_WRITES
 1967   
 1968   
 1969   /*********************************************************************************
 1970     Function:
 1971       int FSCreateMBR (unsigned long firstSector, unsigned long numSectors)
 1972     Summary:
 1973       Creates a master boot record
 1974     Conditions:
 1975       The I/O pins for the device have been initialized by the InitIO function.
 1976     Input:
 1977       firstSector -  The first sector of the partition on the device (cannot
 1978                      be 0; that's the MBR)
 1979       numSectors -   The number of sectors available in memory (including the
 1980                      MBR)
 1981     Return Values:
 1982       0 -   MBR was created successfully
 1983       EOF - MBR could not be created
 1984     Side Effects:
 1985       None
 1986     Description:
 1987       This function can be used to create a master boot record for a device.  Note
 1988       that this function should not be used on a device that is already formatted
 1989       with a master boot record (i.e. most SD cards, CF cards, USB keys).  This
 1990       function will fill the global data buffer with appropriate partition information
 1991       for a FAT partition with a type determined by the number of sectors available
 1992       to the partition.  It will then write the MBR information to the first sector
 1993       on the device.  This function should be followed by a call to FSformat, which
 1994       will create a boot sector, root dir, and FAT appropriate the the information
 1995       contained in the new master boot record.  Note that FSformat only supports
 1996       FAT12 and FAT16 formatting at this time, and so cannot be used to format a
 1997       device with more than 0x3FFD5F sectors.
 1998     Remarks:
 1999       This function can damage the device being used, and should not be called
 2000       unless the user is sure about the size of the device and the first sector value.
 2001     *********************************************************************************/
 2002   
 2003   int FSCreateMBR (unsigned long firstSector, unsigned long numSectors)
 2004   {
 2005       PT_MBR  Partition;
 2006       DWORD CyHdSc = 0x00000000;
 2007       DWORD tempSector;
 2008   
 2009       if ((firstSector == 0) || (numSectors <= 1))
 2010           return EOF;
 2011   
 2012       if (firstSector > (numSectors - 1))
 2013           return EOF;
 2014   
 2015       if (gNeedDataWrite)
 2016           if (flushData())
 2017               return EOF;
 2018   
 2019       memset (gDataBuffer, 0x00, MEDIA_SECTOR_SIZE);
 2020   
 2021       Partition = (PT_MBR) gDataBuffer;
 2022   
 2023       // Set Cylinder-head-sector address of the first sector
 2024       tempSector = firstSector;
 2025       CyHdSc = (tempSector / (unsigned int)16065 ) << 14;
 2026       tempSector %= 16065;
 2027       CyHdSc |= (tempSector / 63) << 6;
 2028       tempSector %= 63;
 2029       CyHdSc |= tempSector + 1;
 2030       gDataBuffer[447] = (BYTE)((CyHdSc >> 16) & 0xFF);
 2031       gDataBuffer[448] = (BYTE)((CyHdSc >> 8) & 0xFF);
 2032       gDataBuffer[449] = (BYTE)((CyHdSc) & 0xFF);
 2033   
 2034       // Set the count of sectors
 2035       Partition->Partition0.PTE_NumSect = numSectors - firstSector;
 2036   
 2037       // Set the partition type
 2038       // We only support creating FAT12 and FAT16 MBRs at this time
 2039       if (Partition->Partition0.PTE_NumSect < 0x1039)
 2040       {
 2041           // FAT12
 2042           Partition->Partition0.PTE_FSDesc = 0x01;
 2043       }
 2044       else if (Partition->Partition0.PTE_NumSect <= 0x3FFD5F)
 2045       {
 2046           // FAT16
 2047           Partition->Partition0.PTE_FSDesc = 0x06;
 2048       }
 2049       else
 2050           return EOF;
 2051   
 2052       // Set the LBA of the first sector
 2053       Partition->Partition0.PTE_FrstSect = firstSector;
 2054   
 2055       // Set the Cylinder-head-sector address of the last sector
 2056       tempSector = firstSector + numSectors - 1;
 2057       CyHdSc = (tempSector / (unsigned int)16065 ) << 14;
 2058       tempSector %= 16065;
 2059       CyHdSc |= (tempSector / 63) << 6;
 2060       tempSector %= 63;
 2061       CyHdSc |= tempSector + 1;
 2062       gDataBuffer[451] = (BYTE)((CyHdSc >> 16) & 0xFF);
 2063       gDataBuffer[452] = (BYTE)((CyHdSc >> 8) & 0xFF);
 2064       gDataBuffer[453] = (BYTE)((CyHdSc) & 0xFF);
 2065   
 2066       // Set the boot descriptor.  This will be 0, since we won't
 2067       // be booting anything from our device probably
 2068       Partition->Partition0.PTE_BootDes = 0x00;
 2069   
 2070       // Set the signature codes
 2071       Partition->Signature0 = 0x55;
 2072       Partition->Signature1 = 0xAA;
 2073   
 2074       if (MDD_SectorWrite (0x00, gDataBuffer, TRUE) != TRUE)
 2075           return EOF;
 2076       else
 2077           return 0;
 2078   
 2079   }
 2080   
 2081   
 2082   /*******************************************************************
 2083     Function:
 2084       int FSformat (char mode, long int serialNumber, char * volumeID)
 2085     Summary:
 2086       Formats a device
 2087     Conditions:
 2088       The device must possess a valid master boot record.
 2089     Input:
 2090       mode -          - 0 - Just erase the FAT and root
 2091                       - 1 - Create a new boot sector
 2092       serialNumber -  Serial number to write to the card
 2093       volumeID -      Name of the card
 2094     Return Values:
 2095       0 -    Format was successful
 2096       EOF -  Format was unsuccessful
 2097     Side Effects:
 2098       The FSerrno variable will be changed.
 2099     Description:
 2100       The FSformat function can be used to create a new boot sector
 2101       on a device, based on the information in the master boot record.
 2102       This function will first initialize the I/O pins and the device,
 2103       and then attempts to read the master boot record.  If the MBR
 2104       cannot be loaded successfully, the function will fail.  Next, if
 2105       the 'mode' argument is specified as '0' the existing boot sector
 2106       information will be loaded.  If the 'mode' argument is '1' an
 2107       entirely new boot sector will be constructed using the disk
 2108       values from the master boot record.  Once the boot sector has
 2109       been successfully loaded/created, the locations of the FAT and
 2110       root will be loaded from it, and they will be completely
 2111       erased.  If the user has specified a volumeID parameter, a
 2112       VOLUME attribute entry will be created in the root directory
 2113       to name the device.
 2114   
 2115       FAT12, FAT16 and FAT32 formatting are supported.
 2116   
 2117       Based on the number of sectors, the format function automatically
 2118       compute the smallest possible value for the cluster size in order to
 2119       accommodate the physical size of the media. In this case, if a media 
 2120       with a big capacity is formatted, the format function may take a very
 2121       long time to write all the FAT tables. 
 2122   
 2123       Therefore, the FORMAT_SECTORS_PER_CLUSTER macro may be used to 
 2124       specify the exact cluster size (in multiples of sector size). This 
 2125       macro can be defined in FSconfig.h
 2126   
 2127     Remarks:
 2128       Only devices with a sector size of 512 bytes are supported by the 
 2129       format function
 2130     *******************************************************************/
 2131   
 2132   int FSformat (char mode, long int serialNumber, char * volumeID)
 2133   {
 2134       PT_MBR   masterBootRecord;
 2135       DWORD    secCount, DataClusters, RootDirSectors;
 2136       BootSec   BSec;
 2137       DISK   d;
 2138       DISK * disk = &d;
 2139       WORD    j;
 2140       DWORD   fatsize, test;
 2141       DWORD Index;
 2142       MEDIA_INFORMATION * mediaInfo;
 2143   #ifdef __18CXX
 2144       // This is here because of a C18 compiler feature
 2145       BYTE *  dataBufferPointer = gDataBuffer;
 2146   #endif
 2147   
 2148       FSerrno = CE_GOOD;
 2149   
 2150       gBufferZeroed = FALSE;
 2151       gNeedFATWrite = FALSE;             
 2152       gLastFATSectorRead = 0xFFFFFFFF;       
 2153       gLastDataSectorRead = 0xFFFFFFFF;  
 2154   
 2155       disk->buffer = gDataBuffer;
 2156   
 2157       MDD_InitIO();
 2158   
 2159       mediaInfo = MDD_MediaInitialize();
 2160       if (mediaInfo->errorCode != MEDIA_NO_ERROR)
 2161       {
 2162           FSerrno = CE_INIT_ERROR;
 2163           return EOF;
 2164       }
 2165   
 2166       if (MDD_SectorRead (0x00, gDataBuffer) == FALSE)
 2167       {
 2168           FSerrno = CE_BADCACHEREAD;
 2169           return EOF;
 2170       }
 2171   
 2172       // Check if the card has no MBR
 2173       BSec = (BootSec) disk->buffer;
 2174       if((BSec->Signature0 == FAT_GOOD_SIGN_0) && (BSec->Signature1 == FAT_GOOD_SIGN_1))
 2175       {
 2176           // Technically, the OEM name is not for indication
 2177           // The alternative is to read the CIS from attribute
 2178           // memory.  See the PCMCIA metaformat for more details
 2179   #if defined (__C30__) || defined (__PIC32MX__)
 2180           if ((ReadByte( disk->buffer, BSI_FSTYPE ) == 'F') && \
 2181               (ReadByte( disk->buffer, BSI_FSTYPE + 1 ) == 'A') && \
 2182               (ReadByte( disk->buffer, BSI_FSTYPE + 2 ) == 'T') && \
 2183               (ReadByte( disk->buffer, BSI_FSTYPE + 3 ) == '1') && \
 2184               (ReadByte( disk->buffer, BSI_BOOTSIG) == 0x29))
 2185   #else
 2186           if ((BSec->FAT.FAT_16.BootSec_FSType[0] == 'F') && \
 2187               (BSec->FAT.FAT_16.BootSec_FSType[1] == 'A') && \
 2188               (BSec->FAT.FAT_16.BootSec_FSType[2] == 'T') && \
 2189               (BSec->FAT.FAT_16.BootSec_FSType[3] == '1') && \
 2190               (BSec->FAT.FAT_16.BootSec_BootSig == 0x29))
 2191   #endif
 2192           {
 2193               /* Mark that we do not have a MBR; 
 2194                   this is not actualy used - is here only to remove a compilation warning */
 2195               masterBootRecord = (PT_MBR) NULL;
 2196               switch (mode)
 2197               {
 2198                   case 1:
 2199                       // not enough info to construct our own boot sector
 2200                       FSerrno = CE_INVALID_ARGUMENT;
 2201                       return EOF;
 2202                   case 0:
 2203                       // We have to determine the operating system, and the
 2204                       // locations and sizes of the root dir and FAT, and the
 2205                       // count of FATs
 2206                       disk->firsts = 0;
 2207                       if (LoadBootSector (disk) != CE_GOOD)
 2208                       {
 2209                           FSerrno = CE_BADCACHEREAD;
 2210                           return EOF;
 2211                       }
 2212                   default:
 2213                       break;
 2214               }
 2215           }
 2216           else
 2217           {
 2218               masterBootRecord = (PT_MBR) &gDataBuffer;
 2219               disk->firsts = masterBootRecord->Partition0.PTE_FrstSect;
 2220           }
 2221       }
 2222       else
 2223       {
 2224           /* If the signature is not correct, this is neither a MBR, nor a VBR */
 2225           FSerrno = CE_BAD_PARTITION;
 2226           return EOF;
 2227       }
 2228   
 2229       switch (mode)
 2230       {
 2231           // True: Rewrite the whole boot sector
 2232           case 1:
 2233               secCount = masterBootRecord->Partition0.PTE_NumSect;
 2234   
 2235               if (secCount < 0x1039)
 2236               {
 2237                   disk->type = FAT12;
 2238                   // Format to FAT12 only if there are too few sectors to format
 2239                   // as FAT16
 2240                   masterBootRecord->Partition0.PTE_FSDesc = 0x01;
 2241                   if (MDD_SectorWrite (0x00, gDataBuffer, TRUE) == FALSE)
 2242                   {
 2243                       FSerrno = CE_WRITE_ERROR;
 2244                       return EOF;
 2245                   }
 2246   
 2247                   if (secCount >= 0x1028)
 2248                   {
 2249                       // More than 0x18 sectors for FATs, 0x20 for root dir,
 2250                       // 0x8 reserved, and 0xFED for data
 2251                       // So double the number of sectors in a cluster to reduce
 2252                       // the number of data clusters used
 2253                       disk->SecPerClus = 2;
 2254                   }
 2255                   else
 2256                   {
 2257                       // One sector per cluster
 2258                       disk->SecPerClus = 1;
 2259                   }
 2260   
 2261                   // Prepare a boot sector
 2262                   memset (gDataBuffer, 0x00, MEDIA_SECTOR_SIZE);
 2263   
 2264                   // Last digit of file system name (FAT12   )
 2265                   gDataBuffer[58] = '2';
 2266   
 2267                   // Calculate the size of the FAT
 2268                   fatsize = (secCount - 0x21  + (2*disk->SecPerClus));
 2269                   test =   (341 * disk->SecPerClus) + 2;
 2270                   fatsize = (fatsize + (test-1)) / test;
 2271       
 2272                   disk->fatcopy = 0x02;
 2273                   disk->maxroot = 0x200;
 2274       
 2275                   disk->fatsize = fatsize;
 2276   
 2277               }
 2278               else if (secCount <= 0x3FFD5F)
 2279               {
 2280                   disk->type = FAT16;
 2281                   // Format to FAT16
 2282                   masterBootRecord->Partition0.PTE_FSDesc = 0x06;
 2283                   if (MDD_SectorWrite (0x00, gDataBuffer, TRUE) == FALSE)
 2284                   {
 2285                       FSerrno = CE_WRITE_ERROR;
 2286                       return EOF;
 2287                   }
 2288   
 2289                   DataClusters = secCount - 0x218;
 2290                   // Figure out how many sectors per cluster we need
 2291                   disk->SecPerClus = 1;
 2292                   while (DataClusters > 0xFFED)
 2293                   {
 2294                       disk->SecPerClus *= 2;
 2295                       DataClusters /= 2;
 2296                   }
 2297                   // This shouldnt happen
 2298                   if (disk->SecPerClus > 128)
 2299                   {
 2300                       FSerrno = CE_BAD_PARTITION;
 2301                       return EOF;
 2302                   }
 2303   
 2304                   // Prepare a boot sector
 2305                   memset (gDataBuffer, 0x00, MEDIA_SECTOR_SIZE);
 2306   
 2307                   // Last digit of file system name (FAT16   )
 2308                   gDataBuffer[58] = '6';
 2309   
 2310                   // Calculate the size of the FAT
 2311                   fatsize = (secCount - 0x21  + (2*disk->SecPerClus));
 2312                   test =    (256  * disk->SecPerClus) + 2;
 2313                   fatsize = (fatsize + (test-1)) / test;
 2314       
 2315                   disk->fatcopy = 0x02;
 2316                   disk->maxroot = 0x200;
 2317       
 2318                   disk->fatsize = fatsize;
 2319               }
 2320               else
 2321               {
 2322                   disk->type = FAT32;
 2323                   // Format to FAT32
 2324                   masterBootRecord->Partition0.PTE_FSDesc = 0x0B;
 2325                   if (MDD_SectorWrite (0x00, gDataBuffer, TRUE) == FALSE)
 2326                   {
 2327                       FSerrno = CE_WRITE_ERROR;
 2328                       return EOF;
 2329                   }
 2330   
 2331                   #ifdef FORMAT_SECTORS_PER_CLUSTER
 2332                       disk->SecPerClus = FORMAT_SECTORS_PER_CLUSTER;
 2333                       DataClusters = secCount / disk->SecPerClus;
 2334   
 2335                       /* FAT32: 65526 < Number of clusters < 4177918 */
 2336                       if ((DataClusters <= 65526) || (DataClusters >= 4177918))
 2337                       {
 2338                           FSerrno = CE_BAD_PARTITION;
 2339                           return EOF;
 2340                       }
 2341                   #else               
 2342                       /*  FAT32: 65526 < Number of clusters < 4177918 */
 2343                       DataClusters = secCount;
 2344                       // Figure out how many sectors per cluster we need
 2345                       disk->SecPerClus = 1;
 2346                       while (DataClusters > 0x3FBFFE)
 2347                       {
 2348                           disk->SecPerClus *= 2;
 2349                           DataClusters /= 2;
 2350                       }
 2351                   #endif
 2352                   // Check the cluster size: FAT32 supports 512, 1024, 2048, 4096, 8192, 16K, 32K, 64K
 2353                   if (disk->SecPerClus > 128)
 2354                   {
 2355                       FSerrno = CE_BAD_PARTITION;
 2356                       return EOF;
 2357                   }
 2358   
 2359                   // Prepare a boot sector
 2360                   memset (gDataBuffer, 0x00, MEDIA_SECTOR_SIZE);
 2361   
 2362                  // Calculate the size of the FAT
 2363                   fatsize = (secCount - 0x20);
 2364                   test =    (128  * disk->SecPerClus) + 1;
 2365                   fatsize = (fatsize + (test-1)) / test;
 2366       
 2367                   disk->fatcopy = 0x02;
 2368                   disk->maxroot = 0x200;
 2369       
 2370                   disk->fatsize = fatsize;
 2371               }
 2372   
 2373               // Non-file system specific values
 2374               gDataBuffer[0] = 0xEB;         //Jump instruction
 2375               gDataBuffer[1] = 0x3C;
 2376               gDataBuffer[2] = 0x90;
 2377               gDataBuffer[3] =  'M';         //OEM Name "MCHP FAT"
 2378               gDataBuffer[4] =  'C';
 2379               gDataBuffer[5] =  'H';
 2380               gDataBuffer[6] =  'P';
 2381               gDataBuffer[7] =  ' ';
 2382               gDataBuffer[8] =  'F';
 2383               gDataBuffer[9] =  'A';
 2384               gDataBuffer[10] = 'T';
 2385   
 2386               gDataBuffer[11] = 0x00;             //Sector size 
 2387               gDataBuffer[12] = 0x02;
 2388   
 2389               gDataBuffer[13] = disk->SecPerClus;   //Sectors per cluster
 2390   
 2391               if ((disk->type == FAT12) || (disk->type == FAT16))
 2392               {
 2393                   gDataBuffer[14] = 0x08;         //Reserved sector count
 2394                   gDataBuffer[15] = 0x00;
 2395                   disk->fat = 0x08 + disk->firsts;
 2396   
 2397                   gDataBuffer[16] = 0x02;         //number of FATs
 2398   
 2399                   gDataBuffer[17] = 0x00;          //Max number of root directory entries - 512 files allowed
 2400                   gDataBuffer[18] = 0x02;
 2401   
 2402                   gDataBuffer[19] = 0x00;         //total sectors
 2403                   gDataBuffer[20] = 0x00;
 2404   
 2405                   gDataBuffer[21] = 0xF8;         //Media Descriptor
 2406   
 2407                   gDataBuffer[22] = fatsize & 0xFF;         //Sectors per FAT
 2408                   gDataBuffer[23] = (fatsize >> 8) & 0xFF;
 2409   
 2410                   gDataBuffer[24] = 0x3F;           //Sectors per track
 2411                   gDataBuffer[25] = 0x00;
 2412       
 2413                   gDataBuffer[26] = 0xFF;         //Number of heads
 2414                   gDataBuffer[27] = 0x00;
 2415       
 2416                   // Hidden sectors = sectors between the MBR and the boot sector
 2417                   gDataBuffer[28] = (BYTE)(disk->firsts & 0xFF);
 2418                   gDataBuffer[29] = (BYTE)((disk->firsts / 0x100) & 0xFF);
 2419                   gDataBuffer[30] = (BYTE)((disk->firsts / 0x10000) & 0xFF);
 2420                   gDataBuffer[31] = (BYTE)((disk->firsts / 0x1000000) & 0xFF);
 2421       
 2422                   // Total Sectors = same as sectors in the partition from MBR
 2423                   gDataBuffer[32] = (BYTE)(secCount & 0xFF);
 2424                   gDataBuffer[33] = (BYTE)((secCount / 0x100) & 0xFF);
 2425                   gDataBuffer[34] = (BYTE)((secCount / 0x10000) & 0xFF);
 2426                   gDataBuffer[35] = (BYTE)((secCount / 0x1000000) & 0xFF);
 2427   
 2428                   gDataBuffer[36] = 0x00;         // Physical drive number
 2429   
 2430                   gDataBuffer[37] = 0x00;         // Reserved (current head)
 2431   
 2432                   gDataBuffer[38] = 0x29;         // Signature code
 2433   
 2434                   gDataBuffer[39] = (BYTE)(serialNumber & 0xFF);
 2435                   gDataBuffer[40] = (BYTE)((serialNumber / 0x100) & 0xFF);
 2436                   gDataBuffer[41] = (BYTE)((serialNumber / 0x10000) & 0xFF);
 2437                   gDataBuffer[42] = (BYTE)((serialNumber / 0x1000000) & 0xFF);
 2438   
 2439                   // Volume ID
 2440                   if (volumeID != NULL)
 2441                   {
 2442                       for (Index = 0; (*(volumeID + Index) != 0) && (Index < 11); Index++)
 2443                       {
 2444                           gDataBuffer[Index + 43] = *(volumeID + Index);
 2445                       }
 2446                       while (Index < 11)
 2447                       {
 2448                           gDataBuffer[43 + Index++] = 0x20;
 2449                       }
 2450                   }
 2451                   else
 2452                   {
 2453                       for (Index = 0; Index < 11; Index++)
 2454                       {
 2455                           gDataBuffer[Index+43] = 0;
 2456                       }
 2457                   }
 2458   
 2459                   gDataBuffer[54] = 'F';
 2460                   gDataBuffer[55] = 'A';
 2461                   gDataBuffer[56] = 'T';
 2462                   gDataBuffer[57] = '1';
 2463                   gDataBuffer[59] = ' ';
 2464                   gDataBuffer[60] = ' ';
 2465                   gDataBuffer[61] = ' ';
 2466   
 2467               }
 2468               else //FAT32
 2469               {
 2470                   gDataBuffer[14] = 0x20;         //Reserved sector count
 2471                   gDataBuffer[15] = 0x00;
 2472                   disk->fat = 0x20 + disk->firsts;
 2473   
 2474                   gDataBuffer[16] = 0x02;         //number of FATs
 2475   
 2476                   gDataBuffer[17] = 0x00;          //Max number of root directory entries - 512 files allowed
 2477                   gDataBuffer[18] = 0x00;
 2478   
 2479                   gDataBuffer[19] = 0x00;         //total sectors
 2480                   gDataBuffer[20] = 0x00;
 2481   
 2482                   gDataBuffer[21] = 0xF8;         //Media Descriptor
 2483   
 2484                   gDataBuffer[22] = 0x00;         //Sectors per FAT
 2485                   gDataBuffer[23] = 0x00;
 2486   
 2487                   gDataBuffer[24] = 0x3F;         //Sectors per track
 2488                   gDataBuffer[25] = 0x00;
 2489       
 2490                   gDataBuffer[26] = 0xFF;         //Number of heads
 2491                   gDataBuffer[27] = 0x00;
 2492       
 2493                   // Hidden sectors = sectors between the MBR and the boot sector
 2494                   gDataBuffer[28] = (BYTE)(disk->firsts & 0xFF);
 2495                   gDataBuffer[29] = (BYTE)((disk->firsts / 0x100) & 0xFF);
 2496                   gDataBuffer[30] = (BYTE)((disk->firsts / 0x10000) & 0xFF);
 2497                   gDataBuffer[31] = (BYTE)((disk->firsts / 0x1000000) & 0xFF);
 2498       
 2499                   // Total Sectors = same as sectors in the partition from MBR
 2500                   gDataBuffer[32] = (BYTE)(secCount & 0xFF);
 2501                   gDataBuffer[33] = (BYTE)((secCount / 0x100) & 0xFF);
 2502                   gDataBuffer[34] = (BYTE)((secCount / 0x10000) & 0xFF);
 2503                   gDataBuffer[35] = (BYTE)((secCount / 0x1000000) & 0xFF);
 2504   
 2505                   gDataBuffer[36] = fatsize & 0xFF;         //Sectors per FAT
 2506                   gDataBuffer[37] = (fatsize >>  8) & 0xFF;
 2507                   gDataBuffer[38] = (fatsize >> 16) & 0xFF;         
 2508                   gDataBuffer[39] = (fatsize >> 24) & 0xFF;
 2509   
 2510                   gDataBuffer[40] = 0x00;         //Active FAT
 2511                   gDataBuffer[41] = 0x00;
 2512   
 2513                   gDataBuffer[42] = 0x00;         //File System version  
 2514                   gDataBuffer[43] = 0x00;
 2515   
 2516                   gDataBuffer[44] = 0x02;         //First cluster of the root directory
 2517                   gDataBuffer[45] = 0x00;
 2518                   gDataBuffer[46] = 0x00;
 2519                   gDataBuffer[47] = 0x00;
 2520   
 2521                   gDataBuffer[48] = 0x01;         //FSInfo
 2522                   gDataBuffer[49] = 0x00;
 2523   
 2524                   gDataBuffer[50] = 0x00;         //Backup Boot Sector
 2525                   gDataBuffer[51] = 0x00;
 2526   
 2527                   gDataBuffer[52] = 0x00;         //Reserved for future expansion
 2528                   gDataBuffer[53] = 0x00;
 2529                   gDataBuffer[54] = 0x00;                   
 2530                   gDataBuffer[55] = 0x00;
 2531                   gDataBuffer[56] = 0x00;                   
 2532                   gDataBuffer[57] = 0x00;
 2533                   gDataBuffer[58] = 0x00;                   
 2534                   gDataBuffer[59] = 0x00;
 2535                   gDataBuffer[60] = 0x00;                   
 2536                   gDataBuffer[61] = 0x00;
 2537                   gDataBuffer[62] = 0x00;                   
 2538                   gDataBuffer[63] = 0x00;
 2539   
 2540                   gDataBuffer[64] = 0x00;         // Physical drive number
 2541   
 2542                   gDataBuffer[65] = 0x00;         // Reserved (current head)
 2543   
 2544                   gDataBuffer[66] = 0x29;         // Signature code
 2545   
 2546                   gDataBuffer[67] = (BYTE)(serialNumber & 0xFF);
 2547                   gDataBuffer[68] = (BYTE)((serialNumber / 0x100) & 0xFF);
 2548                   gDataBuffer[69] = (BYTE)((serialNumber / 0x10000) & 0xFF);
 2549                   gDataBuffer[70] = (BYTE)((serialNumber / 0x1000000) & 0xFF);
 2550   
 2551                   // Volume ID
 2552                   if (volumeID != NULL)
 2553                   {
 2554                       for (Index = 0; (*(volumeID + Index) != 0) && (Index < 11); Index++)
 2555                       {
 2556                           gDataBuffer[Index + 71] = *(volumeID + Index);
 2557                       }
 2558                       while (Index < 11)
 2559                       {
 2560                           gDataBuffer[71 + Index++] = 0x20;
 2561                       }
 2562                   }
 2563                   else
 2564                   {
 2565                       for (Index = 0; Index < 11; Index++)
 2566                       {
 2567                           gDataBuffer[Index+71] = 0;
 2568                       }
 2569                   }
 2570   
 2571                   gDataBuffer[82] = 'F';
 2572                   gDataBuffer[83] = 'A';
 2573                   gDataBuffer[84] = 'T';
 2574                   gDataBuffer[85] = '3';
 2575                   gDataBuffer[86] = '2';
 2576                   gDataBuffer[87] = ' ';
 2577                   gDataBuffer[88] = ' ';
 2578                   gDataBuffer[89] = ' ';
 2579   
 2580   
 2581               }
 2582   
 2583   #ifdef __18CXX
 2584               // C18 can't reference a value greater than 256
 2585               // using an array name pointer
 2586               *(dataBufferPointer + 510) = 0x55;
 2587               *(dataBufferPointer + 511) = 0xAA;
 2588   #else
 2589               gDataBuffer[510] = 0x55;
 2590               gDataBuffer[511] = 0xAA;
 2591   #endif
 2592   
 2593               disk->root = disk->fat + (disk->fatcopy * disk->fatsize);
 2594   
 2595               if (MDD_SectorWrite (disk->firsts, gDataBuffer, FALSE) == FALSE)
 2596               {
 2597                   FSerrno = CE_WRITE_ERROR;
 2598                   return EOF;
 2599               }
 2600   
 2601               break;
 2602           case 0:
 2603               if (LoadBootSector (disk) != CE_GOOD)
 2604               {
 2605                   FSerrno = CE_BADCACHEREAD;
 2606                   return EOF;
 2607               }
 2608               break;
 2609           default:
 2610               FSerrno = CE_INVALID_ARGUMENT;
 2611               return EOF;
 2612       }
 2613   
 2614       // Erase the FAT
 2615       memset (gDataBuffer, 0x00, MEDIA_SECTOR_SIZE);
 2616   
 2617       if (disk->type == FAT32)
 2618       {
 2619           gDataBuffer[0] = 0xF8;          //BPB_Media byte value in its low 8 bits, and all other bits are set to 1
 2620           gDataBuffer[1] = 0xFF;
 2621           gDataBuffer[2] = 0xFF;
 2622           gDataBuffer[3] = 0xFF;
 2623   
 2624           gDataBuffer[4] = 0x00;          //Disk is clean and no read/write errors were encountered
 2625           gDataBuffer[5] = 0x00;
 2626           gDataBuffer[6] = 0x00;
 2627           gDataBuffer[7] = 0x0C;
 2628   
 2629           gDataBuffer[8]  = 0xFF;         //Root Directory EOF  
 2630           gDataBuffer[9]  = 0xFF;
 2631           gDataBuffer[10] = 0xFF;
 2632           gDataBuffer[11] = 0xFF;
 2633   
 2634           for (j = disk->fatcopy - 1; j != 0xFFFF; j--)
 2635           {
 2636               if (MDD_SectorWrite (disk->fat + (j * disk->fatsize), gDataBuffer, FALSE) == FALSE)
 2637                   return EOF;
 2638           }
 2639       
 2640           memset (gDataBuffer, 0x00, 12);
 2641       
 2642           for (Index = disk->fat + 1; Index < (disk->fat + disk->fatsize); Index++)
 2643           {
 2644               for (j = disk->fatcopy - 1; j != 0xFFFF; j--)
 2645               {
 2646                   if (MDD_SectorWrite (Index + (j * disk->fatsize), gDataBuffer, FALSE) == FALSE)
 2647                       return EOF;
 2648               }
 2649           }
 2650       
 2651           // Erase the root directory
 2652           for (Index = 1; Index < disk->SecPerClus; Index++)
 2653           {
 2654               if (MDD_SectorWrite (disk->root + Index, gDataBuffer, FALSE) == FALSE)
 2655                   return EOF;
 2656           }
 2657       
 2658           if (volumeID != NULL)
 2659           {
 2660               // Create a drive name entry in the root dir
 2661               Index = 0;
 2662               while ((*(volumeID + Index) != 0) && (Index < 11))
 2663               {
 2664                   gDataBuffer[Index] = *(volumeID + Index);
 2665                   Index++;
 2666               }
 2667               while (Index < 11)
 2668               {
 2669                   gDataBuffer[Index++] = ' ';
 2670               }
 2671               gDataBuffer[11] = 0x08;
 2672               gDataBuffer[17] = 0x11;
 2673               gDataBuffer[19] = 0x11;
 2674               gDataBuffer[23] = 0x11;
 2675       
 2676               if (MDD_SectorWrite (disk->root, gDataBuffer, FALSE) == FALSE)
 2677                   return EOF;
 2678           }
 2679           else
 2680           {
 2681               if (MDD_SectorWrite (disk->root, gDataBuffer, FALSE) == FALSE)
 2682                   return EOF;
 2683           }
 2684       
 2685           return 0;
 2686       }
 2687       else
 2688       {
 2689           gDataBuffer[0] = 0xF8;
 2690           gDataBuffer[1] = 0xFF;
 2691           gDataBuffer[2] = 0xFF;
 2692           if (disk->type == FAT16)
 2693               gDataBuffer[3] = 0xFF;
 2694       
 2695           for (j = disk->fatcopy - 1; j != 0xFFFF; j--)
 2696           {
 2697               if (MDD_SectorWrite (disk->fat + (j * disk->fatsize), gDataBuffer, FALSE) == FALSE)
 2698                   return EOF;
 2699           }
 2700       
 2701           memset (gDataBuffer, 0x00, 4);
 2702       
 2703           for (Index = disk->fat + 1; Index < (disk->fat + disk->fatsize); Index++)
 2704           {
 2705               for (j = disk->fatcopy - 1; j != 0xFFFF; j--)
 2706               {
 2707                   if (MDD_SectorWrite (Index + (j * disk->fatsize), gDataBuffer, FALSE) == FALSE)
 2708                       return EOF;
 2709               }
 2710           }
 2711       
 2712           // Erase the root directory
 2713           RootDirSectors = ((disk->maxroot * 32) + (disk->sectorSize - 1)) / disk->sectorSize;
 2714       
 2715           for (Index = 1; Index < RootDirSectors; Index++)
 2716           {
 2717               if (MDD_SectorWrite (disk->root + Index, gDataBuffer, FALSE) == FALSE)
 2718                   return EOF;
 2719           }
 2720       
 2721           if (volumeID != NULL)
 2722           {
 2723               // Create a drive name entry in the root dir
 2724               Index = 0;
 2725               while ((*(volumeID + Index) != 0) && (Index < 11))
 2726               {
 2727                   gDataBuffer[Index] = *(volumeID + Index);
 2728                   Index++;
 2729               }
 2730               while (Index < 11)
 2731               {
 2732                   gDataBuffer[Index++] = ' ';
 2733               }
 2734               gDataBuffer[11] = 0x08;
 2735               gDataBuffer[17] = 0x11;
 2736               gDataBuffer[19] = 0x11;
 2737               gDataBuffer[23] = 0x11;
 2738       
 2739               if (MDD_SectorWrite (disk->root, gDataBuffer, FALSE) == FALSE)
 2740                   return EOF;
 2741           }
 2742           else
 2743           {
 2744               if (MDD_SectorWrite (disk->root, gDataBuffer, FALSE) == FALSE)
 2745                   return EOF;
 2746           }
 2747       
 2748           return 0;
 2749       }
 2750   }
 2751   #endif
 2752   #endif
 2753   
 2754   
 2755   /*******************************************************
 2756     Function:
 2757       BYTE Write_File_Entry( FILEOBJ fo, WORD * curEntry)
 2758     Summary:
 2759       Write dir entry info into a specified entry
 2760     Conditions:
 2761       This function should not be called by the user.
 2762     Input:
 2763       fo -        \File structure
 2764       curEntry -  Write destination
 2765     Return Values:
 2766       TRUE - Operation successful
 2767       FALSE - Operation failed
 2768     Side Effects:
 2769       None
 2770     Description:
 2771       This function will calculate the sector of the
 2772       directory (whose base sector is pointed to by the
 2773       dirccls value in the FSFILE object 'fo') that contains
 2774       a directory entry whose offset is indicated by the
 2775       curEntry parameter.  It will then write the data
 2776       in the global data buffer (which should already
 2777       contain the entries for that sector) to the device.
 2778     Remarks:
 2779       None
 2780     *******************************************************/
 2781   
 2782   #ifdef ALLOW_WRITES
 2783   BYTE Write_File_Entry( FILEOBJ fo, WORD * curEntry)
 2784   {
 2785       DISK   *dsk;
 2786       BYTE   status;
 2787       BYTE   offset2;
 2788       DWORD   sector;
 2789       DWORD   ccls;
 2790   
 2791       dsk = fo->dsk;
 2792   
 2793       // get the cluster of this entry
 2794       ccls = fo->dirccls;
 2795   
 2796        // figure out the offset from the base sector
 2797       offset2  = (*curEntry / (dsk->sectorSize/32));
 2798   
 2799       /* Settings based on FAT type */
 2800       switch (dsk->type)
 2801       {
 2802   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 2803           case FAT32:
 2804               // Root is always cluster-based in FAT32
 2805               offset2 = offset2 % (dsk->SecPerClus);
 2806               break;
 2807   #endif
 2808           case FAT12:
 2809           case FAT16:
 2810               if(ccls != FatRootDirClusterValue)
 2811                   offset2 = offset2 % (dsk->SecPerClus);
 2812               break;
 2813       }
 2814   
 2815       sector = Cluster2Sector(dsk,ccls);
 2816   
 2817       // Now write it
 2818       // "Offset" ensures writing of data belonging to a file entry only. Hence it doesn't change other file entries.
 2819       if ( !MDD_SectorWrite( sector + offset2, dsk->buffer, FALSE))
 2820           status = FALSE;
 2821       else
 2822           status = TRUE;
 2823   
 2824       return(status);
 2825   } // Write_File_Entry
 2826   #endif
 2827   
 2828   
 2829   /**********************************************************
 2830     Function:
 2831       BYTE FAT_erase_cluster_chain (WORD cluster, DISK * dsk)
 2832     Summary:
 2833       Erase a chain of clusters
 2834     Conditions:
 2835       This function should not be called by the user.
 2836     Input:
 2837       cluster -  The cluster number
 2838       dsk -      The disk structure
 2839     Return Values:
 2840       TRUE -  Operation successful
 2841       FALSE - Operation failed
 2842     Side Effects:
 2843       None
 2844     Description:
 2845       This function will parse through a cluster chain
 2846       starting with the cluster pointed to by 'cluster' and
 2847       mark all of the FAT entries as empty until the end of
 2848       the chain has been reached or an error occurs.
 2849     Remarks:
 2850       None
 2851     **********************************************************/
 2852   
 2853   #ifdef ALLOW_WRITES
 2854   BYTE FAT_erase_cluster_chain (DWORD cluster, DISK * dsk)
 2855   {
 2856       DWORD     c,c2,ClusterFailValue;
 2857       enum    _status {Good, Fail, Exit}status;
 2858   
 2859       status = Good;
 2860   
 2861       /* Settings based on FAT type */
 2862       switch (dsk->type)
 2863       {
 2864   
 2865   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 2866           case FAT32:
 2867               ClusterFailValue = CLUSTER_FAIL_FAT32;
 2868               c2 =  LAST_CLUSTER_FAT32;
 2869               break;
 2870   #endif
 2871           case FAT12:
 2872               ClusterFailValue = CLUSTER_FAIL_FAT16; // FAT16 value itself
 2873               c2 =  LAST_CLUSTER_FAT12;
 2874               break;
 2875           case FAT16:
 2876           default:
 2877               ClusterFailValue = CLUSTER_FAIL_FAT16;
 2878               c2 =  LAST_CLUSTER_FAT16;
 2879               break;
 2880       }
 2881   
 2882       // Make sure there is actually a cluster assigned
 2883       if((cluster == 0) || (cluster == 1))  // Cluster assigned can't be "0" and "1"
 2884       {
 2885           status = Exit;
 2886       }
 2887       else
 2888       {
 2889           while(status == Good)
 2890           {
 2891               // Get the FAT entry
 2892               if((c = ReadFAT( dsk, cluster)) == ClusterFailValue)
 2893                   status = Fail;
 2894               else
 2895               {
 2896                   if((c == 0) || (c == 1))  // Cluster assigned can't be "0" and "1"
 2897                   {
 2898                       status = Exit;
 2899                   }
 2900                   else
 2901                   {
 2902                       // compare against max value of a cluster in FATxx
 2903                       // look for the last cluster in the chain
 2904                       if ( c >= c2)
 2905                           status = Exit;
 2906   
 2907                       // Now erase this FAT entry
 2908                       if(WriteFAT(dsk, cluster, CLUSTER_EMPTY, FALSE) == ClusterFailValue)
 2909                           status = Fail;
 2910   
 2911                       // now update what the current cluster is
 2912                       cluster = c;
 2913                   }
 2914               }
 2915           }// while status
 2916       }// cluster == 0
 2917   
 2918       WriteFAT (dsk, 0, 0, TRUE);
 2919   
 2920       if(status == Exit)
 2921           return(TRUE);
 2922       else
 2923           return(FALSE);
 2924   } // Erase cluster
 2925   #endif
 2926   
 2927   /**************************************************************************
 2928     Function:
 2929       DIRENTRY Cache_File_Entry( FILEOBJ fo, WORD * curEntry, BYTE ForceRead)
 2930     Summary:
 2931       Load a file entry
 2932     Conditions:
 2933       This function should not be called by the user.
 2934     Input:
 2935       fo -         File information
 2936       curEntry -   Offset of the directory entry to load.
 2937       ForceRead -  Forces loading of a new sector of the directory.
 2938     Return:
 2939       DIRENTRY - Pointer to the directory entry that was loaded.
 2940     Side Effects:
 2941       Any unwritten data in the data buffer will be written to the device.
 2942     Description:
 2943       Load the sector containing the file entry pointed to by 'curEntry'
 2944       from the directory pointed to by the variables in 'fo.'
 2945     Remarks:
 2946       Any modification of this function is extremely likely to
 2947       break something.
 2948     **************************************************************************/
 2949   
 2950   DIRENTRY Cache_File_Entry( FILEOBJ fo, WORD * curEntry, BYTE ForceRead)
 2951   {
 2952       DIRENTRY dir;
 2953       DISK *dsk;
 2954       DWORD sector;
 2955       DWORD cluster, LastClusterLimit;
 2956       DWORD ccls;
 2957       BYTE offset2;
 2958       BYTE numofclus;
 2959   	BYTE dirEntriesPerSector;
 2960   
 2961       dsk = fo->dsk;
 2962   
 2963       // get the base sector of this directory
 2964       cluster = fo->dirclus;
 2965       ccls = fo->dirccls;
 2966   
 2967   	dirEntriesPerSector = dsk->sectorSize/32;
 2968   
 2969        // figure out the offset from the base sector
 2970       offset2  = (*curEntry / dirEntriesPerSector);
 2971   
 2972       offset2 = offset2; // emulator issue
 2973   
 2974       /* Settings based on FAT type */
 2975       switch (dsk->type)
 2976       {
 2977   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 2978           case FAT32:
 2979               // the ROOT is always cluster based in FAT32
 2980               /* In FAT32: There is no ROOT region. Root etries are made in DATA region only.
 2981               Every cluster of DATA which is accupied by ROOT is tracked by FAT table/entry so the ROOT can grow
 2982               to an amount which is restricted only by available free DATA region. */
 2983               offset2  = offset2 % (dsk->SecPerClus);   // figure out the offset
 2984               LastClusterLimit = LAST_CLUSTER_FAT32;
 2985               break;
 2986   #endif
 2987           case FAT12:
 2988           case FAT16:
 2989           default:
 2990               // if its the root its not cluster based
 2991               if(cluster != 0)
 2992                   offset2  = offset2 % (dsk->SecPerClus);   // figure out the offset
 2993               LastClusterLimit = LAST_CLUSTER_FAT16;
 2994               break;
 2995       }
 2996   
 2997       // check if a new sector of the root must be loaded
 2998       if (ForceRead || ((*curEntry & MASK_MAX_FILE_ENTRY_LIMIT_BITS) == 0))     // only 16 entries per sector
 2999       {
 3000           // see if we have to load a new cluster
 3001           if(((offset2 == 0) && (*curEntry >= dirEntriesPerSector)) || ForceRead)
 3002           {
 3003               if(cluster == 0)
 3004               {
 3005                   ccls = 0;
 3006               }
 3007               else
 3008               {
 3009                   // If ForceRead, read the number of sectors from 0
 3010                   if(ForceRead)
 3011                       numofclus = ((WORD)(*curEntry) / (WORD)(((WORD)dirEntriesPerSector) * (WORD)dsk->SecPerClus));
 3012                   // Otherwise just read the next sector
 3013                   else
 3014                       numofclus = 1;
 3015   
 3016                   // move to the correct cluster
 3017                   while(numofclus)
 3018                   {
 3019                       ccls = ReadFAT(dsk, ccls);
 3020   
 3021                       if(ccls >= LastClusterLimit)
 3022                           break;
 3023                       else
 3024                           numofclus--;
 3025                   }
 3026               }
 3027           }
 3028   
 3029           // see if that we have a valid cluster number
 3030           if(ccls < LastClusterLimit)
 3031           {
 3032               fo->dirccls = ccls; // write it back
 3033   
 3034               sector = Cluster2Sector(dsk,ccls);
 3035   
 3036               /* see if we are root and about to go pass our boundaries
 3037               FAT32 stores the root directory in the Data Region along with files and other directories,
 3038               allowing it to grow without such a restraint */
 3039               if((ccls == FatRootDirClusterValue) && ((sector + offset2) >= dsk->data) && (FAT32 != dsk->type))
 3040               {
 3041                   dir = ((DIRENTRY)NULL);   // reached the end of the root
 3042               }
 3043               else
 3044               {
 3045   #ifdef ALLOW_WRITES
 3046                   if (gNeedDataWrite)
 3047                       if (flushData())
 3048                           return NULL;
 3049   #endif
 3050                   gBufferOwner = NULL;
 3051                   gBufferZeroed = FALSE;
 3052   
 3053                   if ( MDD_SectorRead( sector + offset2, dsk->buffer) != TRUE) // if FALSE: sector could not be read.
 3054                   {
 3055                       dir = ((DIRENTRY)NULL);
 3056                   }
 3057                   else // Sector has been read properly, Copy the root entry info of the file searched.
 3058                   {
 3059                       if(ForceRead)    // Buffer holds all 16 root entry info. Point to the one required.
 3060                           dir = (DIRENTRY)((DIRENTRY)dsk->buffer) + ((*curEntry)%dirEntriesPerSector);
 3061                       else
 3062                           dir = (DIRENTRY)dsk->buffer;
 3063                   }
 3064                   gLastDataSectorRead = 0xFFFFFFFF;
 3065               }
 3066           }
 3067           else
 3068           {
 3069               nextClusterIsLast = TRUE;
 3070               dir = ((DIRENTRY)NULL);
 3071           }
 3072       }
 3073       else
 3074           dir = (DIRENTRY)((DIRENTRY)dsk->buffer) + ((*curEntry)%dirEntriesPerSector);
 3075   
 3076       return(dir);
 3077   } // Cache_File_Entry
 3078   
 3079   
 3080   /*************************************************************************
 3081     Function:
 3082       CETYPE CreateFileEntry(FILEOBJ fo, WORD *fHandle, BYTE mode, BOOL createFirstCluster)
 3083     Summary:
 3084       Create a new file entry
 3085     Conditions:
 3086       Should not be called by the user.
 3087     Input:
 3088       fo -       Pointer to file structure
 3089       fHandle -  Location to create file
 3090       mode - DIRECTORY mode or ARCHIVE mode
 3091       createFirstCluster - If set to TRUE, first cluster is created
 3092     Return Values:
 3093       CE_GOOD -        File Creation successful
 3094       CE_DIR_FULL -    All root directory entries are taken
 3095       CE_WRITE_ERROR - The head cluster of the file could not be created.
 3096     Side Effects:
 3097       Modifies the FSerrno variable.
 3098     Description:
 3099       With the data passed within fo, create a new file entry in the current
 3100       directory.  This function will first search for empty file entries.
 3101       Once an empty entry is found, the entry will be populated with data
 3102       for a file or directory entry.  Finally, the first cluster of the
 3103       new file will be located and allocated, and its value will be
 3104       written into the file entry.
 3105     Remarks:
 3106       None
 3107     *************************************************************************/
 3108   
 3109   #ifdef ALLOW_WRITES
 3110   CETYPE CreateFileEntry(FILEOBJ fo, WORD *fHandle, BYTE mode, BOOL createFirstCluster)
 3111   {
 3112       CETYPE  error = CE_GOOD;
 3113   
 3114   	#if defined(SUPPORT_LFN)
 3115   	LFN_ENTRY *lfno;
 3116   	unsigned short int   *templfnPtr = (unsigned short int *)fo -> utf16LFNptr,*dest;
 3117   	unsigned short int	tempString[MAX_UTF16_CHARS_IN_LFN_ENTRY];
 3118   	UINT16_VAL tempShift;
 3119       BOOL    firstTime = TRUE;
 3120   	BYTE	checksum,sequenceNumber,reminder,tempCalc1,numberOfFileEntries;
 3121       char    index;
 3122   	char    *src;
 3123   	#endif
 3124   
 3125   	FSerrno = CE_GOOD;
 3126   
 3127      *fHandle = 0;
 3128   
 3129       // figure out where to put this file in the directory stucture
 3130       if(FindEmptyEntries(fo, fHandle))
 3131       {
 3132   		#if defined(SUPPORT_LFN)
 3133   		// If LFN entry
 3134   		if(fo->utf16LFNlength)
 3135   		{
 3136   			// Alias the LFN to short file name
 3137   			if(!Alias_LFN_Object(fo))
 3138   			{
 3139   				// If Aliasing of LFN is unsucessful
 3140   				error = FSerrno = CE_FILENAME_EXISTS;
 3141   				return(error);
 3142   			}
 3143   
 3144   			src = fo -> name;
 3145   
 3146       	    // Find the checksum for Short file name of LFN
 3147       	    checksum = 0;
 3148       	 	for (index = 11; index != 0; index--)
 3149       	    {
 3150   				checksum = ((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + *src++;
 3151   			}
 3152   
 3153   			// File Name + NULL character is file name length in LFN
 3154   			fileNameLength = fo->utf16LFNlength;
 3155   
 3156   			// Determine the number of entries for LFN
 3157   			reminder = tempCalc1 = fileNameLength % MAX_UTF16_CHARS_IN_LFN_ENTRY;
 3158   
 3159   			numberOfFileEntries = fileNameLength/MAX_UTF16_CHARS_IN_LFN_ENTRY;
 3160   
 3161   			if(tempCalc1 || (fileNameLength < MAX_UTF16_CHARS_IN_LFN_ENTRY))
 3162   			{
 3163   				numberOfFileEntries++;
 3164   			}
 3165   
 3166   			// Max sequence number for LFN root entry
 3167   			sequenceNumber = numberOfFileEntries | 0x40;
 3168   
 3169   			// Store the max sequence number entries in tempString
 3170   			if(tempCalc1)
 3171   			{
 3172   				index = 0;
 3173   				while(tempCalc1)
 3174   				{
 3175   					tempString[(BYTE)index++] = templfnPtr[fileNameLength - tempCalc1];
 3176   					tempCalc1--;
 3177   				}				 
 3178   
 3179   				// Store the remaining bytes of max sequence number entries with 0xFF
 3180   				for(;index < MAX_UTF16_CHARS_IN_LFN_ENTRY;index++)
 3181   				{
 3182   					tempString[(BYTE)index] = 0xFFFF;
 3183   				}				 
 3184   			}
 3185   			else
 3186   			{
 3187   				// Store the remaining bytes of max sequence number entries with 0xFF
 3188   				for(index = MAX_UTF16_CHARS_IN_LFN_ENTRY;index > 0;index--)
 3189   				{
 3190   					tempString[MAX_UTF16_CHARS_IN_LFN_ENTRY - (BYTE)index] = templfnPtr[fileNameLength - (BYTE)index];
 3191   				}
 3192   			}
 3193   
 3194   			dest = &tempString[12];
 3195   
 3196   			while(numberOfFileEntries)
 3197   			{
 3198   				fo->dirccls = fo->dirclus;
 3199   			    lfno = (LFN_ENTRY *)Cache_File_Entry( fo, fHandle, TRUE);
 3200   
 3201   			    if (lfno == NULL)
 3202   				{
 3203   			        return CE_BADCACHEREAD;
 3204   				}
 3205   
 3206   				// Write the 32 byte LFN Object as per FAT specification
 3207   				lfno->LFN_SequenceNo = sequenceNumber--;   // Sequence number,
 3208   
 3209   				lfno->LFN_Part3[1] = *dest--;
 3210   				lfno->LFN_Part3[0] = *dest--;
 3211   
 3212   				lfno->LFN_Part2[5] = *dest--;
 3213   				lfno->LFN_Part2[4] = *dest--;
 3214   				lfno->LFN_Part2[3] = *dest--;
 3215   				lfno->LFN_Part2[2] = *dest--;
 3216   				lfno->LFN_Part2[1] = *dest--;
 3217   				lfno->LFN_Part2[0] = *dest--;
 3218   
 3219   				tempShift.Val = *dest--;
 3220   				lfno->LFN_Part1[9] = tempShift.byte.HB;
 3221   				lfno->LFN_Part1[8] = tempShift.byte.LB;
 3222   				tempShift.Val = *dest--;
 3223   				lfno->LFN_Part1[7] = tempShift.byte.HB;
 3224   				lfno->LFN_Part1[6] = tempShift.byte.LB;
 3225   				tempShift.Val = *dest--;
 3226   				lfno->LFN_Part1[5] = tempShift.byte.HB;
 3227   				lfno->LFN_Part1[4] = tempShift.byte.LB;
 3228   				tempShift.Val = *dest--;
 3229   				lfno->LFN_Part1[3] = tempShift.byte.HB;
 3230   				lfno->LFN_Part1[2] = tempShift.byte.LB;
 3231   				tempShift.Val = *dest--;
 3232   				lfno->LFN_Part1[1] = tempShift.byte.HB;
 3233   				lfno->LFN_Part1[0] = tempShift.byte.LB;
 3234   
 3235   				lfno->LFN_Attribute = ATTR_LONG_NAME;
 3236   				lfno->LFN_Type = 0;
 3237   				lfno->LFN_Checksum = checksum;
 3238   				lfno->LFN_Reserved2 = 0;
 3239   
 3240   			    // just write the last entry in
 3241   			    if (Write_File_Entry(fo,fHandle) != TRUE)
 3242   			        error = CE_WRITE_ERROR;
 3243   
 3244           	    // 0x40 should be ORed with only max sequence number & in
 3245   				// all other cases it should not be present
 3246           	    sequenceNumber &= (~0x40);
 3247           	    *fHandle = *fHandle + 1;
 3248   				numberOfFileEntries--;
 3249   
 3250   				// Load the destination address only once and during first time,
 3251   				if(firstTime)
 3252   				{
 3253   					dest = (unsigned short int *)(fo -> utf16LFNptr + fileNameLength - reminder - 1);
 3254   					firstTime = FALSE;
 3255   				}
 3256   			}
 3257   		}
 3258   		#endif
 3259   
 3260           // found the entry, now populate it
 3261           if((error = PopulateEntries(fo, fHandle, mode)) == CE_GOOD)
 3262           {
 3263   			if(createFirstCluster)
 3264               	// if everything is ok, create a first cluster
 3265               	error = CreateFirstCluster(fo);
 3266   			else
 3267   			{
 3268   
 3269   			}
 3270           }
 3271       }
 3272       else
 3273       {
 3274           error = CE_DIR_FULL;
 3275       }
 3276   
 3277       FSerrno = error;
 3278   
 3279       return(error);
 3280   }
 3281   #endif
 3282   
 3283   /******************************************************
 3284     Function:
 3285       CETYPE CreateFirstCluster(FILEOBJ fo)
 3286     Summary:
 3287       Create the first cluster for a file
 3288     Conditions:
 3289       This function should not be called by the user.
 3290     Input:
 3291       fo -  The file that contains the first cluster
 3292     Return Values:
 3293       CE_GOOD -        First cluster created successfully
 3294       CE_WRITE_ERROR - Cluster creation failed
 3295     Side Effects:
 3296       None
 3297     Description:
 3298       This function will find an unused cluster, link it to
 3299       a file's directory entry, and write the entry back
 3300       to the device.
 3301     Remarks:
 3302       None.
 3303     ******************************************************/
 3304   
 3305   #ifdef ALLOW_WRITES
 3306   CETYPE CreateFirstCluster(FILEOBJ fo)
 3307   {
 3308       CETYPE       error;
 3309       DWORD      cluster,TempMsbCluster;
 3310       WORD        fHandle;
 3311       DIRENTRY   dir;
 3312       fHandle = fo->entry;
 3313   
 3314       // Now create the first cluster (head cluster)
 3315       if((error = FILECreateHeadCluster(fo,&cluster)) == CE_GOOD)
 3316       {
 3317           // load the file entry so the new cluster can be linked to it
 3318           dir = LoadDirAttrib(fo, &fHandle);
 3319   
 3320           // Now update the new cluster
 3321           dir->DIR_FstClusLO = (cluster & 0x0000FFFF);
 3322   
 3323   
 3324   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 3325           // Get the higher part of cluster and store it in directory entry.
 3326          TempMsbCluster = (cluster & 0x0FFF0000);    // Since only 28 bits usedin FAT32. Mask the higher MSB nibble.
 3327          TempMsbCluster = TempMsbCluster >> 16;      // Get the date into Lsb place.
 3328          dir->DIR_FstClusHI = TempMsbCluster;
 3329   #else // If FAT32 support not enabled
 3330          TempMsbCluster = 0;                         // Just to avoid compiler warnigng.
 3331          dir->DIR_FstClusHI = 0;
 3332   #endif
 3333   
 3334           // now write it
 3335           if(Write_File_Entry(fo, &fHandle) != TRUE)
 3336               error = CE_WRITE_ERROR;
 3337       } // Create Cluster
 3338   
 3339       return(error);
 3340   }// End of CreateFirstCluster
 3341   #endif
 3342   
 3343   /**********************************************************
 3344     Function:
 3345       BYTE FindEmptyEntries(FILEOBJ fo, WORD *fHandle)
 3346     Summary:
 3347       Find an empty dir entry
 3348     Conditions:
 3349       This function should not be called by the user.
 3350     Input:
 3351       fo -       Pointer to file structure
 3352       fHandle -  Start of entries
 3353     Return Values:
 3354       TRUE - One found
 3355       FALSE - None found
 3356     Side Effects:
 3357       None
 3358     Description:
 3359       This function will cache directory entries, starting
 3360       with the one pointed to by the fHandle argument.  It will
 3361       then search through the entries until an unused one
 3362       is found.  If the end of the cluster chain for the
 3363       directory is reached, a new cluster will be allocated
 3364       to the directory (unless it's a FAT12 or FAT16 root)
 3365       and the first entry of the new cluster will be used.
 3366     Remarks:
 3367       None.
 3368     **********************************************************/
 3369   
 3370   #ifdef ALLOW_WRITES
 3371   BYTE FindEmptyEntries(FILEOBJ fo, WORD *fHandle)
 3372   {
 3373       BYTE   status = NOT_FOUND;
 3374       BYTE   amountfound,numberOfFileEntries;
 3375       BYTE   a = 0;
 3376       WORD   bHandle;
 3377       DWORD b;
 3378       DIRENTRY    dir;
 3379   
 3380       fo->dirccls = fo->dirclus;
 3381       if((dir = Cache_File_Entry( fo, fHandle, TRUE)) == NULL)
 3382       {
 3383           status = CE_BADCACHEREAD;
 3384       }
 3385       else
 3386       {
 3387   		#if defined(SUPPORT_LFN)
 3388   		// If LFN entry
 3389   		if(fo->utf16LFNlength)
 3390   		{
 3391   			// File Name + NULL character is file name length in LFN
 3392   			fileNameLength = fo->utf16LFNlength;
 3393   
 3394   			// Determine the number of entries for LFN
 3395   			a = fileNameLength % MAX_UTF16_CHARS_IN_LFN_ENTRY;
 3396   
 3397   			numberOfFileEntries = fileNameLength/MAX_UTF16_CHARS_IN_LFN_ENTRY;
 3398   
 3399   			if(a || (fileNameLength < MAX_UTF16_CHARS_IN_LFN_ENTRY))
 3400   			{
 3401   				numberOfFileEntries++;
 3402   			}
 3403   
 3404               numberOfFileEntries = numberOfFileEntries + 1;
 3405   		}
 3406   		else
 3407   		#endif
 3408               numberOfFileEntries = 1;
 3409   
 3410           // while its still not found
 3411           while(status == NOT_FOUND)
 3412           {
 3413               amountfound = 0;
 3414               bHandle = *fHandle;
 3415   
 3416               // find (number) continuous entries
 3417               do
 3418               {
 3419                   // Get the entry
 3420                   dir = Cache_File_Entry( fo, fHandle, FALSE);
 3421   
 3422                   // Read the first char of the file name
 3423                   if(dir != NULL) // Last entry of the cluster
 3424                   {
 3425                       a = dir->DIR_Name[0];
 3426                   }
 3427                   // increase number
 3428                   (*fHandle)++;
 3429               }while((dir != (DIRENTRY)NULL) && ((a == DIR_DEL) || (a == DIR_EMPTY)) && (++amountfound < numberOfFileEntries));
 3430   
 3431               // --- now why did we exit?
 3432               if(dir == NULL) // Last entry of the cluster
 3433               {
 3434                   //setup the current cluster
 3435                   b = fo->dirccls; // write it back
 3436   
 3437                   // make sure we are not the root directory
 3438                   if(b == FatRootDirClusterValue)
 3439                   {
 3440                       if (fo->dsk->type != FAT32)
 3441                           status = NO_MORE;
 3442                       else
 3443                       {
 3444                           fo->ccls = b;
 3445   
 3446                           if(FILEallocate_new_cluster(fo, 1) == CE_DISK_FULL)
 3447                               status = NO_MORE;
 3448                           else
 3449                           {
 3450                               *fHandle = bHandle;
 3451                               status = FOUND;     // a new cluster will surely hold a new file name
 3452                           }
 3453                       }
 3454                   }
 3455                   else
 3456                   {
 3457                       fo->ccls = b;
 3458   
 3459                       if(FILEallocate_new_cluster(fo, 1) == CE_DISK_FULL)
 3460                           status = NO_MORE;
 3461                       else
 3462                       {
 3463                           *fHandle = bHandle;
 3464                           status = FOUND;     // a new cluster will surely hold a new file name
 3465                       }
 3466                   }
 3467               }
 3468               else
 3469               {
 3470                   if(amountfound == numberOfFileEntries)
 3471                   {
 3472                       status = FOUND;
 3473                       *fHandle = bHandle - numberOfFileEntries + 1;
 3474                   }
 3475               }
 3476           }// while
 3477   
 3478           // copy the base handle over
 3479           *fHandle = bHandle;
 3480       }
 3481   
 3482       if(status == FOUND)
 3483           return(TRUE);
 3484       else
 3485           return(FALSE);
 3486   }
 3487   #endif
 3488   
 3489   /**************************************************************************
 3490     Function:
 3491       BYTE PopulateEntries(FILEOBJ fo, WORD *fHandle, BYTE mode)
 3492     Summary:
 3493       Populate a dir entry with data
 3494     Conditions:
 3495       Should not be called by the user.
 3496     Input:
 3497       fo -      Pointer to file structure
 3498       fHandle - Location of the file
 3499       mode - DIRECTORY mode or ARCHIVE mode
 3500     Return Values:
 3501       CE_GOOD - Population successful
 3502     Side Effects:
 3503       None
 3504     Description:
 3505       This function will write data into a new file entry.  It will also
 3506       load timestamp data (based on the method selected by the user) and
 3507       update the timestamp variables.
 3508     Remarks:
 3509       None.
 3510     **************************************************************************/
 3511   
 3512   #ifdef ALLOW_WRITES
 3513   BYTE PopulateEntries(FILEOBJ fo, WORD *fHandle, BYTE mode)
 3514   {
 3515       BYTE error = CE_GOOD;
 3516       DIRENTRY    dir;
 3517   
 3518       fo->dirccls = fo->dirclus;
 3519       dir = Cache_File_Entry( fo, fHandle, TRUE);
 3520   
 3521       if (dir == NULL)
 3522           return CE_BADCACHEREAD;
 3523   
 3524       // copy the contents over
 3525       strncpy(dir->DIR_Name,fo->name,DIR_NAMECOMP);
 3526   
 3527       // setup no attributes
 3528       if (mode == DIRECTORY)
 3529           dir->DIR_Attr = ATTR_DIRECTORY;
 3530       else
 3531           dir->DIR_Attr   = ATTR_ARCHIVE;
 3532   
 3533       dir->DIR_NTRes  = 0x00;              // nt reserved
 3534       dir->DIR_FstClusHI =    0x0000;      // high word of this enty's first cluster number
 3535       dir->DIR_FstClusLO =    0x0000;      // low word of this entry's first cluster number
 3536       dir->DIR_FileSize =     0x0;         // file size in DWORD
 3537   
 3538      // Timing information for uncontrolled clock mode
 3539   #ifdef INCREMENTTIMESTAMP
 3540       dir->DIR_CrtTimeTenth = 0xB2;        // millisecond stamp
 3541       dir->DIR_CrtTime =      0x7278;      // time created
 3542       dir->DIR_CrtDate =      0x32B0;      // date created
 3543       dir->DIR_LstAccDate =   0x32B0;      // Last Access date
 3544       dir->DIR_WrtTime =      0x7279;      // last update time
 3545       dir->DIR_WrtDate =      0x32B0;      // last update date
 3546   #endif
 3547   
 3548   #ifdef USEREALTIMECLOCK
 3549       CacheTime();
 3550       dir->DIR_CrtTimeTenth = gTimeCrtMS;        // millisecond stamp
 3551       dir->DIR_CrtTime =      gTimeCrtTime;      // time created //
 3552       dir->DIR_CrtDate =      gTimeCrtDate;      // date created (1/1/2004)
 3553       dir->DIR_LstAccDate =   gTimeAccDate;      // Last Access date
 3554       dir->DIR_WrtTime =      gTimeWrtTime;      // last update time
 3555       dir->DIR_WrtDate =      gTimeWrtDate;      // last update date
 3556   #endif
 3557   
 3558   #ifdef USERDEFINEDCLOCK
 3559       // The user will have set the time before this funciton is called
 3560       dir->DIR_CrtTimeTenth = gTimeCrtMS;
 3561       dir->DIR_CrtTime =      gTimeCrtTime;
 3562       dir->DIR_CrtDate =       gTimeCrtDate;
 3563       dir->DIR_LstAccDate =   gTimeAccDate;
 3564       dir->DIR_WrtTime =       gTimeWrtTime;
 3565       dir->DIR_WrtDate =      gTimeWrtDate;
 3566   #endif
 3567   
 3568       fo->size        = dir->DIR_FileSize;
 3569       fo->time        = dir->DIR_CrtTime;
 3570       fo->date        = dir->DIR_CrtDate;
 3571       fo->attributes  = dir->DIR_Attr;
 3572       fo->entry       = *fHandle;
 3573   
 3574       // just write the last entry in
 3575       if (Write_File_Entry(fo,fHandle) != TRUE)
 3576           error = CE_WRITE_ERROR;
 3577   
 3578       return(error);
 3579   }
 3580   
 3581   /*****************************************************************
 3582     Function:
 3583       BOOL Alias_LFN_Object(FILEOBJ fo)
 3584     Summary:
 3585       Find the Short file name of the LFN entry
 3586     Conditions:
 3587       Long file name should be present.
 3588     Input:
 3589       fo -       Pointer to file structure
 3590     Return Values:
 3591       FOUND -     Operation successful
 3592       NOT_FOUND - Operation failed
 3593     Side Effects:
 3594       None
 3595     Description:
 3596       This function will find the short file name
 3597       of the long file name as mentioned in the FAT
 3598       specs.
 3599     Remarks:
 3600       None.
 3601     *****************************************************************/
 3602   #if defined(SUPPORT_LFN)
 3603   BOOL Alias_LFN_Object(FILEOBJ fo)
 3604   {
 3605   	FSFILE		filePtr1;
 3606   	FSFILE		filePtr2;
 3607   	unsigned long int index4;
 3608   	short int   lfnIndex,index1,index2;
 3609       BYTE  tempVariable,index;
 3610   	char  tempString[8];
 3611   	char  *lfnAliasPtr;
 3612   	unsigned short int  *templfnPtr;
 3613   	BOOL result = FALSE;
 3614   
 3615       // copy file object over
 3616       FileObjectCopy(&filePtr1, fo);
 3617   
 3618   	lfnAliasPtr = (char *)&filePtr1.name[0];
 3619   
 3620   	templfnPtr = (unsigned short int *)filePtr1.utf16LFNptr;
 3621   
 3622   	fileNameLength = fo->utf16LFNlength - 1;
 3623   
 3624   	// Initially fill the alias name with space characters
 3625   	for(index1 = 0;index1 < FILE_NAME_SIZE_8P3;index1++)
 3626   	{
 3627   		lfnAliasPtr[index1] = ' ';
 3628   	}
 3629   
 3630   	// find the location where '.' is present
 3631   	for(lfnIndex = fileNameLength - 1;lfnIndex > 0;lfnIndex--)
 3632   	{
 3633   		if (templfnPtr[lfnIndex] == '.')
 3634   		{
 3635   		    break;
 3636   		}
 3637   	}
 3638   
 3639   	index1 = lfnIndex + 1;
 3640   
 3641   	tempVariable = 0;
 3642   	if(lfnIndex)
 3643   	{
 3644   		index2 = 8;
 3645   		// Complete the extension part as per the FAT specifications
 3646   		for(;((index1 < fileNameLength) && (tempVariable < 3));index1++)
 3647   		{
 3648   			// Convert lower-case to upper-case
 3649   			index = (BYTE)templfnPtr[index1];
 3650   			if ((index >= 0x61) && (index <= 0x7A))
 3651   			{
 3652   			    lfnAliasPtr[index2++] = index - 0x20;
 3653   			}
 3654   			else if(index == ' ')
 3655   			{
 3656   				continue;
 3657   			}
 3658   			else if ((index == 0x2B) || (index == 0x2C) || (index == 0x3B) || 
 3659   					(index == 0x3D) || (index == 0x5B) || (index == 0x5D) || 
 3660   					(templfnPtr[index1] > 0xFF))
 3661   			{
 3662   			    lfnAliasPtr[index2++] = '_';
 3663   			}
 3664   			else
 3665   			{
 3666   			    lfnAliasPtr[index2++] = index;
 3667   			}
 3668   			
 3669   			tempVariable++;
 3670   		}
 3671   
 3672   		index2 = lfnIndex;
 3673   		tempVariable = 0;
 3674   	}
 3675   	else
 3676   	{
 3677   		index2 = fileNameLength;
 3678   	}
 3679   
 3680   	// Fill the base part as per the FAT specifications
 3681   	for(index1 = 0;((index1 < index2) && (tempVariable < 6));index1++)
 3682   	{
 3683   		// Convert lower-case to upper-case
 3684   		index = (BYTE)templfnPtr[index1];
 3685   		if ((index >= 0x61) && (index <= 0x7A))
 3686   		{
 3687   		    lfnAliasPtr[tempVariable] = index - 0x20;
 3688   		}
 3689   		else if(index == ' ')
 3690   		{
 3691   			continue;
 3692   		}
 3693   		else if ((index == 0x2B) || (index == 0x2C) || (index == 0x3B) || 
 3694   				(index == 0x3D) || (index == 0x5B) || (index == 0x5D) || 
 3695   				(templfnPtr[index1] > 0xFF))
 3696   		{
 3697   		    lfnAliasPtr[tempVariable] = '_';
 3698   		}
 3699   		else
 3700   		{
 3701   		    lfnAliasPtr[tempVariable] = index;
 3702   		}
 3703   		tempVariable++;
 3704   	}
 3705   
 3706   	// Aliasing of the predicted name should append ~1
 3707   	lfnAliasPtr[tempVariable] = '~';
 3708   	lfnAliasPtr[tempVariable + 1] = '1';
 3709   
 3710       filePtr1.attributes = ATTR_ARCHIVE;
 3711   
 3712   	filePtr1.utf16LFNlength = 0;
 3713   
 3714   	// Try for 9999999 combinations before telling error to the user
 3715   	for(index4 = 1;index4 < (unsigned long int)10000000;index4++)
 3716   	{
 3717   	    filePtr1.cluster = 0;
 3718   	    filePtr1.ccls    = 0;
 3719   	    filePtr1.entry = 0;
 3720   
 3721   		    // start at the current directory
 3722   		#ifdef ALLOW_DIRS
 3723   		    filePtr1.dirclus    = cwdptr->dirclus;
 3724   		    filePtr1.dirccls    = cwdptr->dirccls;
 3725   		#else
 3726   		    filePtr1.dirclus = FatRootDirClusterValue;
 3727   		    filePtr1.dirccls = FatRootDirClusterValue;
 3728   		#endif
 3729   
 3730   	    // copy file object over
 3731   	    FileObjectCopy(&filePtr2, &filePtr1);
 3732   
 3733   	    // See if the file is found
 3734   	    if(FILEfind (&filePtr2, &filePtr1, LOOK_FOR_MATCHING_ENTRY, 0) == CE_GOOD)
 3735   		{
 3736   			tempString[7] = index4 % (BYTE)10 + '0';
 3737   			tempString[6] = (index4 % (BYTE)100)/10 + '0';
 3738   			tempString[5] = (index4 % 1000)/100 + '0';
 3739   			tempString[4] = (index4 % 10000)/1000 + '0';
 3740   			tempString[3] = (index4 % 100000)/10000 + '0';
 3741   			tempString[2] = (index4 % 1000000)/100000 + '0';
 3742   			if((tempString[1] = ((index4 % 10000000)/1000000 + '0')) != '0')
 3743   			{
 3744   					tempString[index = 0] = '~';
 3745   			}
 3746   			else
 3747   			{
 3748   				for(index = 6;index > 0;index--)
 3749   				{
 3750   					if((tempString[index] == '0') && (tempString[index - 1] == '0'))
 3751   					{
 3752   						tempString[index] = '~';
 3753   						if(!filePtr1.AsciiEncodingType)
 3754   						{
 3755   							if(index % 2)
 3756   							{
 3757   								for(index2 = index - 1;index2 < 7;index2++)
 3758   								{
 3759   									tempString[index2] = tempString[index2 + 1];
 3760   								}
 3761   								tempString[7] = ' ';
 3762   								index--;
 3763   							}
 3764   						}
 3765   						break;
 3766   					}
 3767   				}
 3768   			}
 3769   
 3770   			if(index >= tempVariable)
 3771   			{
 3772   				index1 = tempVariable;
 3773   			}
 3774   			else
 3775   			{
 3776   				index1 = index;
 3777   			}
 3778   
 3779   			while(index < 8)
 3780   			{
 3781   				filePtr1.name[index1++] = tempString[index++];
 3782   			}
 3783   
 3784   			// Store the remaining bytes with leading spaces
 3785   			while(index1 < 8)
 3786   			{
 3787   				filePtr1.name[index1++] = ' ';
 3788   			}
 3789   
 3790   		}
 3791   		else
 3792   		{
 3793   			// short file name is found.Store it & quit
 3794   			lfnAliasPtr = &fo->name[0];
 3795   
 3796   			for(index = 0;index < FILE_NAME_SIZE_8P3;index++)
 3797   			{
 3798   				lfnAliasPtr[index] = filePtr1.name[index];
 3799   			}
 3800   
 3801   			result = TRUE;
 3802   			break;
 3803   		}
 3804   	}
 3805   
 3806   	return(result);
 3807   
 3808   } // Alias_LFN_Object
 3809   #endif
 3810   
 3811   #ifdef USEREALTIMECLOCK
 3812   
 3813   /*************************************************************************
 3814     Function:
 3815       void CacheTime (void)
 3816     Summary:
 3817       Automatically store timestamp information from the RTCC
 3818     Conditions:
 3819       RTCC module enabled.  Should not be called by the user.
 3820     Return Values:
 3821       None
 3822     Side Effects:
 3823       Modifies global timing variables
 3824     Description:
 3825       This function will automatically load information from an RTCC
 3826       module and use it to update the global timing variables.  These can
 3827       then be used to update file timestamps.
 3828     Remarks:
 3829       None.
 3830     *************************************************************************/
 3831   
 3832   void CacheTime (void)
 3833   {
 3834       WORD    year, monthday, weekhour, minsec, c, result;
 3835       BYTE    ptr1, ptr0;
 3836   
 3837   #if defined (__PIC32MX__)   // Added support for PIC32. -Bud (3/4/2008)
 3838   
 3839   	unsigned int	t0, t1;
 3840   	unsigned int	d0, d1;
 3841   
 3842   	do  // Get the time
 3843   	{
 3844   		t0=RTCTIME;
 3845   		t1=RTCTIME;
 3846   	}while(t0!=t1);
 3847   
 3848   	do  // Get the date
 3849   	{
 3850   		d0=RTCDATE;
 3851   		d1=RTCDATE;
 3852   	}while(d0!=d1);
 3853   
 3854       // Put them in place.
 3855       year        = (WORD)(d0 >> 24);
 3856       monthday    = (WORD)(d0 >> 8);
 3857       weekhour    = (WORD)((d0 & 0x0F) << 8);
 3858       weekhour   |= (WORD)(t0 >> 24);
 3859       minsec      = (WORD)(t0 >> 8);
 3860   
 3861   #else
 3862   
 3863       if(RCFGCALbits.RTCPTR0)
 3864           ptr0 = 1;
 3865       else
 3866           ptr0 = 0;
 3867       if (RCFGCALbits.RTCPTR1)
 3868           ptr1 = 1;
 3869       else
 3870           ptr1 = 0;
 3871   
 3872       RCFGCALbits.RTCPTR0 = 1;
 3873       RCFGCALbits.RTCPTR1 = 1;
 3874       year = RTCVAL;
 3875       monthday = RTCVAL;
 3876       weekhour = RTCVAL;
 3877       minsec = RTCVAL;
 3878   
 3879       if (ptr0 == 1)
 3880           RCFGCALbits.RTCPTR0 = 1;
 3881   
 3882       if (ptr1 == 1)
 3883           RCFGCALbits.RTCPTR1 = 1;
 3884   
 3885   #endif
 3886   
 3887       c = 0;
 3888       c += (year & 0x0F);
 3889       c += ((year & 0xF0) >> 4) * 10;
 3890       // c equals the last 2 digits of the year from 2000 to 2099
 3891       // Add 20 to adjust it to FAT time (from 1980 to 2107)
 3892       c += 20;
 3893       // shift the result to bits
 3894       result = c << 9;
 3895   
 3896       if ((monthday & 0x1000) == 0x1000)
 3897       {
 3898           c = 10;
 3899       }
 3900       else
 3901       {
 3902           c = 0;
 3903       }
 3904       c += ((monthday & 0x0F00) >> 8);
 3905       c <<= 5;
 3906       result |= c;
 3907   
 3908       c = (monthday & 0x00F0) >> 4;
 3909       c *= 10;
 3910       c += (monthday & 0x000F);
 3911   
 3912       result |= c;
 3913   
 3914       gTimeCrtDate = result;
 3915       gTimeWrtDate = result;
 3916       gTimeAccDate = result;
 3917   
 3918       c = ((weekhour & 0x00F0) >> 4) * 10;
 3919       c += (weekhour & 0x000F);
 3920       result = c << 11;
 3921       c = ((minsec & 0xF000) >> 12) * 10;
 3922       c += (minsec & 0x0F00) >> 8;
 3923       result |= (c << 5);
 3924       c = ((minsec & 0x00F0) >> 4) * 10;
 3925       c += (minsec & 0x000F);
 3926   
 3927       // If seconds mod 2 is 1, add 1000 ms
 3928       if (c % 2)
 3929           gTimeCrtMS = 100;
 3930       else
 3931           gTimeCrtMS = 0;
 3932   
 3933       c >>= 1;
 3934       result |= c;
 3935   
 3936       gTimeCrtTime = result;
 3937       gTimeWrtTime = result;
 3938   }
 3939   #endif
 3940   
 3941   #ifdef USERDEFINEDCLOCK
 3942   
 3943   /***********************************************************************************************************
 3944     Function:
 3945       int SetClockVars (unsigned int year, unsigned char month, unsigned char day, unsigned char hour, unsigned char minute, unsigned char second)
 3946     Summary:
 3947       Manually set timestamp variables
 3948     Conditions:
 3949       USERDEFINEDCLOCK macro defined in FSconfig.h.
 3950     Input:
 3951       year -     The year (1980\-2107)
 3952       month -   The month (1\-12)
 3953       day -     The day of the month (1\-31)
 3954       hour -    The hour (0\-23)
 3955       minute -  The minute (0\-59)
 3956       second -  The second (0\-59)
 3957     Return Values:
 3958       None
 3959     Side Effects:
 3960       Modifies global timing variables
 3961     Description:
 3962       Lets the user manually set the timing variables.  The values passed in will be converted to the format
 3963       used by the FAT timestamps.
 3964     Remarks:
 3965       Call this before creating a file or directory (set create time) and
 3966       before closing a file (set last access time, last modified time)
 3967     ***********************************************************************************************************/
 3968   
 3969   int SetClockVars (unsigned int year, unsigned char month, unsigned char day, unsigned char hour, unsigned char minute, unsigned char second)
 3970   {
 3971       unsigned int result;
 3972   
 3973       if ((year < 1980) || (year > 2107) || (month < 1) || (month > 12) ||
 3974           (day < 1) || (day > 31) || (hour > 23) || (minute > 59) || (second > 59))
 3975       {
 3976           FSerrno = CE_INVALID_ARGUMENT;
 3977           return -1;
 3978       }
 3979   
 3980       result = (year - 1980) << 9;
 3981       result |= (unsigned int)((unsigned int)month << 5);
 3982       result |= (day);
 3983   
 3984       gTimeAccDate = result;
 3985       gTimeCrtDate = result;
 3986       gTimeWrtDate = result;
 3987   
 3988       result = ((unsigned int)hour << 11);
 3989       result |= (unsigned int)((unsigned int)minute << 5);
 3990       result |= (second/2);
 3991   
 3992       gTimeCrtTime = result;
 3993       gTimeWrtTime = result;
 3994   
 3995       if (second % 2)
 3996           gTimeCrtMS = 100;
 3997       else
 3998           gTimeCrtMS = 0;
 3999   
 4000       FSerrno = CE_GOOD;
 4001       return 0;
 4002   }
 4003   #endif
 4004   
 4005   #endif
 4006   
 4007   /***********************************************************************
 4008     Function:
 4009       BYTE FILEallocate_new_cluster( FILEOBJ fo, BYTE mode)
 4010     Summary;
 4011       Allocate a new cluster to a file
 4012     Conditions:
 4013       Should not be called by the user.
 4014     Input:
 4015       fo -    Pointer to file structure
 4016       mode -
 4017            - 0 - Allocate a cluster to a file
 4018            - 1 - Allocate a cluster to a directory
 4019     Return Values:
 4020       CE_GOOD -      Cluster allocated
 4021       CE_DISK_FULL - No clusters available
 4022     Side Effects:
 4023       None
 4024     Description:
 4025       This function will find an empty cluster on the device using the
 4026       FATfindEmptyCluster function.  It will then mark it as the last
 4027       cluster in the file in the FAT chain, and link the current last
 4028       cluster of the passed file to the new cluster.  If the new
 4029       cluster is a directory cluster, it will be erased (so there are no
 4030       extraneous directory entries).  If it's allocated to a non-directory
 4031       file, it doesn't need to be erased; extraneous data in the cluster
 4032       will be unviewable because of the file size parameter.
 4033     Remarks:
 4034       None.
 4035     ***********************************************************************/
 4036   
 4037   #ifdef ALLOW_WRITES
 4038   BYTE FILEallocate_new_cluster( FILEOBJ fo, BYTE mode)
 4039   {
 4040       DISK *      dsk;
 4041       DWORD c,curcls;
 4042   
 4043       dsk = fo->dsk;
 4044       c = fo->ccls;
 4045   
 4046       // find the next empty cluster
 4047       c = FATfindEmptyCluster(fo);
 4048       if (c == 0)      // "0" is just an indication as Disk full in the fn "FATfindEmptyCluster()"
 4049           return CE_DISK_FULL;
 4050   
 4051   
 4052       // mark the cluster as taken, and last in chain
 4053       if(dsk->type == FAT12)
 4054           WriteFAT( dsk, c, LAST_CLUSTER_FAT12, FALSE);
 4055       else if (dsk->type == FAT16)
 4056           WriteFAT( dsk, c, LAST_CLUSTER_FAT16, FALSE);
 4057   
 4058   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 4059       else
 4060           WriteFAT( dsk, c, LAST_CLUSTER_FAT32, FALSE);
 4061   #endif
 4062   
 4063       // link current cluster to the new one
 4064       curcls = fo->ccls;
 4065   
 4066       WriteFAT( dsk, curcls, c, FALSE);
 4067   
 4068       // update the FILE structure
 4069       fo->ccls = c;
 4070   
 4071       // IF this is a dir, we need to erase the cluster
 4072       // If it's a file, we can leave it- the file size
 4073       // will limit the data we see to the data that's been
 4074       // written
 4075       if (mode == 1)
 4076           return (EraseCluster(dsk, c));
 4077       else
 4078           return CE_GOOD;
 4079   
 4080   } // allocate new cluster
 4081   #endif
 4082   
 4083   /***********************************************
 4084     Function:
 4085       DWORD FATfindEmptyCluster(FILEOBJ fo)
 4086     Summary:
 4087       Find the next available cluster on the device
 4088     Conditions:
 4089       This function should not be called by the
 4090       user.
 4091     Input:
 4092       fo -  Pointer to file structure
 4093     Return Values:
 4094       DWORD - Address of empty cluster
 4095       0 -     Could not find empty cluster
 4096     Side Effects:
 4097       None
 4098     Description:
 4099       This function will search through the FAT to
 4100       find the next available cluster on the device.
 4101     Remarks:
 4102       Should not be called by user
 4103     ***********************************************/
 4104   
 4105   #ifdef ALLOW_WRITES
 4106   DWORD FATfindEmptyCluster(FILEOBJ fo)
 4107   {
 4108       DISK *   disk;
 4109       DWORD    value = 0x0;
 4110       DWORD    c,curcls, EndClusterLimit, ClusterFailValue;
 4111   
 4112       disk = fo->dsk;
 4113       c = fo->ccls;
 4114   
 4115       /* Settings based on FAT type */
 4116       switch (disk->type)
 4117       {
 4118   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 4119           case FAT32:
 4120               EndClusterLimit = END_CLUSTER_FAT32;
 4121               ClusterFailValue = CLUSTER_FAIL_FAT32;
 4122               break;
 4123   #endif
 4124           case FAT12:
 4125               EndClusterLimit = END_CLUSTER_FAT12;
 4126               ClusterFailValue = CLUSTER_FAIL_FAT16;
 4127               break;
 4128           case FAT16:
 4129           default:
 4130               EndClusterLimit = END_CLUSTER_FAT16;
 4131               ClusterFailValue = CLUSTER_FAIL_FAT16;
 4132               break;
 4133       }
 4134   
 4135       // just in case
 4136       if(c < 2)
 4137           c = 2;
 4138   
 4139       curcls = c;
 4140       ReadFAT(disk, c);
 4141   
 4142       // sequentially scan through the FAT looking for an empty cluster
 4143       while(c)
 4144       {
 4145           // look at its value
 4146           if ( (value = ReadFAT(disk, c)) == ClusterFailValue)
 4147           {
 4148               c = 0;
 4149               break;
 4150           }
 4151   
 4152           // check if empty cluster found
 4153           if (value == CLUSTER_EMPTY)
 4154               break;
 4155   
 4156           c++;    // check next cluster in FAT
 4157           // check if reached last cluster in FAT, re-start from top
 4158           if ((value == EndClusterLimit) || (c >= (disk->maxcls+2)))
 4159               c = 2;
 4160   
 4161           // check if full circle done, disk full
 4162           if ( c == curcls)
 4163           {
 4164               c = 0;
 4165               break;
 4166           }
 4167       }  // scanning for an empty cluster
 4168   
 4169       return(c);
 4170   }
 4171   #endif
 4172   
 4173   
 4174   /*********************************************************************************
 4175     Function:
 4176       void FSGetDiskProperties(FS_DISK_PROPERTIES* properties)
 4177     Summary:
 4178       Allows user to get the disk properties (size of disk, free space, etc)
 4179     Conditions:
 4180       1) ALLOW_GET_DISK_PROPERTIES must be defined in FSconfig.h
 4181       2) a FS_DISK_PROPERTIES object must be created before the function is called
 4182       3) the new_request member of the FS_DISK_PROPERTIES object must be set before
 4183           calling the function for the first time.  This will start a new search.
 4184       4) this function should not be called while there is a file open.  Close all
 4185           files before calling this function.
 4186     Input:
 4187       properties - a pointer to a FS_DISK_PROPERTIES object where the results should
 4188         be stored.
 4189     Return Values:
 4190       This function returns void.  The properties_status of the previous call of this 
 4191         function is located in the properties.status field.  This field has the 
 4192         following possible values:
 4193   
 4194       FS_GET_PROPERTIES_NO_ERRORS - operation completed without error.  Results
 4195         are in the properties object passed into the function.
 4196       FS_GET_PROPERTIES_DISK_NOT_MOUNTED - there is no mounted disk.  Results in
 4197         properties object is not valid
 4198       FS_GET_PROPERTIES_CLUSTER_FAILURE - there was a failure trying to read a 
 4199         cluster from the drive.  The results in the properties object is a partial
 4200         result up until the point of the failure.
 4201       FS_GET_PROPERTIES_STILL_WORKING - the search for free sectors is still in
 4202         process.  Continue calling this function with the same properties pointer 
 4203         until either the function completes or until the partial results meets the
 4204         application needs.  The properties object contains the partial results of
 4205         the search and can be used by the application.  
 4206     Side Effects:
 4207       Can cause errors if called when files are open.  Close all files before
 4208       calling this function.
 4209   
 4210       Calling this function without setting the new_request member on the first
 4211       call can result in undefined behavior and results.
 4212   
 4213       Calling this function after a result is returned other than
 4214       FS_GET_PROPERTIES_STILL_WORKING can result in undefined behavior and results.
 4215     Description:  
 4216       This function returns the information about the mounted drive.  The results 
 4217       member of the properties object passed into the function is populated with 
 4218       the information about the drive.    
 4219   
 4220       Before starting a new request, the new_request member of the properties
 4221       input parameter should be set to TRUE.  This will initiate a new search
 4222       request.
 4223   
 4224       This function will return before the search is complete with partial results.
 4225       All of the results except the free_clusters will be correct after the first
 4226       call.  The free_clusters will contain the number of free clusters found up
 4227       until that point, thus the free_clusters result will continue to grow until
 4228       the entire drive is searched.  If an application only needs to know that a 
 4229       certain number of bytes is available and doesn't need to know the total free 
 4230       size, then this function can be called until the required free size is
 4231       verified.  To continue a search, pass a pointer to the same FS_DISK_PROPERTIES
 4232       object that was passed in to create the search.
 4233   
 4234       A new search request sould be made once this function has returned a value 
 4235       other than FS_GET_PROPERTIES_STILL_WORKING.  Continuing a completed search
 4236       can result in undefined behavior or results.
 4237   
 4238       Typical Usage:
 4239       <code>
 4240       FS_DISK_PROPERTIES disk_properties;
 4241   
 4242       disk_properties.new_request = TRUE;
 4243   
 4244       do
 4245       {
 4246           FSGetDiskProperties(&disk_properties);
 4247       } while (disk_properties.properties_status == FS_GET_PROPERTIES_STILL_WORKING);
 4248       </code>
 4249   
 4250       results.disk_format - contains the format of the drive.  Valid results are 
 4251         FAT12(1), FAT16(2), or FAT32(3).
 4252   
 4253       results.sector_size - the sector size of the mounted drive.  Valid values are
 4254         512, 1024, 2048, and 4096.
 4255   
 4256       results.sectors_per_cluster - the number sectors per cluster.
 4257   
 4258       results.total_clusters - the number of total clusters on the drive.  This 
 4259         can be used to calculate the total disk size (total_clusters * 
 4260         sectors_per_cluster * sector_size = total size of drive in bytes)
 4261   
 4262       results.free_clusters - the number of free (unallocated) clusters on the drive.
 4263         This can be used to calculate the total free disk size (free_clusters * 
 4264         sectors_per_cluster * sector_size = total size of drive in bytes)
 4265   
 4266     Remarks:
 4267       PIC24F size estimates:
 4268         Flash - 400 bytes (-Os setting)
 4269   
 4270       PIC24F speed estimates:
 4271         Search takes approximately 7 seconds per Gigabyte of drive space.  Speed
 4272           will vary based on the number of sectors per cluster and the sector size.
 4273     *********************************************************************************/
 4274   #if defined(ALLOW_GET_DISK_PROPERTIES)
 4275   void FSGetDiskProperties(FS_DISK_PROPERTIES* properties)
 4276   {
 4277       BYTE    i;
 4278       DWORD   value = 0x0;
 4279   
 4280       if(properties->new_request == TRUE)
 4281       {
 4282           properties->disk = &gDiskData;
 4283           properties->results.free_clusters = 0;
 4284           properties->new_request = FALSE;
 4285   
 4286           if(properties->disk->mount != TRUE)
 4287           {
 4288               properties->properties_status = FS_GET_PROPERTIES_DISK_NOT_MOUNTED;
 4289               return;
 4290           }
 4291   
 4292           properties->properties_status = FS_GET_PROPERTIES_STILL_WORKING;
 4293      
 4294           properties->results.disk_format = properties->disk->type;
 4295           properties->results.sector_size = properties->disk->sectorSize;
 4296           properties->results.sectors_per_cluster = properties->disk->SecPerClus;
 4297           properties->results.total_clusters = properties->disk->maxcls;
 4298   
 4299           /* Settings based on FAT type */
 4300           switch (properties->disk->type)
 4301           {
 4302       #ifdef SUPPORT_FAT32 // If FAT32 supported.
 4303               case FAT32:
 4304                   properties->private.EndClusterLimit = END_CLUSTER_FAT32;
 4305                   properties->private.ClusterFailValue = CLUSTER_FAIL_FAT32;
 4306                   break;
 4307       #endif
 4308               case FAT16:
 4309                   properties->private.EndClusterLimit = END_CLUSTER_FAT16;
 4310                   properties->private.ClusterFailValue = CLUSTER_FAIL_FAT16;
 4311                   break;
 4312               case FAT12:
 4313                   properties->private.EndClusterLimit = END_CLUSTER_FAT12;
 4314                   properties->private.ClusterFailValue = CLUSTER_FAIL_FAT16;
 4315                   break;
 4316           }
 4317       
 4318           properties->private.c = 2;
 4319   
 4320           properties->private.curcls = properties->private.c;
 4321           ReadFAT(properties->disk, properties->private.c);
 4322       }
 4323   
 4324       if(properties->disk == NULL)
 4325       {
 4326           properties->properties_status = FS_GET_PROPERTIES_DISK_NOT_MOUNTED;
 4327           return;
 4328       }
 4329   
 4330       if(properties->properties_status != FS_GET_PROPERTIES_STILL_WORKING)
 4331       {
 4332           return;
 4333       }
 4334   
 4335       // sequentially scan through the FAT looking for an empty cluster
 4336       for(i=0;i<255;i++)
 4337       {
 4338           // look at its value
 4339           if ( (value = ReadFAT(properties->disk, properties->private.c)) == properties->private.ClusterFailValue)
 4340           {
 4341               properties->properties_status = FS_GET_PROPERTIES_CLUSTER_FAILURE;
 4342               return;
 4343           }
 4344   
 4345           // check if empty cluster found
 4346           if (value == CLUSTER_EMPTY)
 4347           {
 4348               properties->results.free_clusters++;
 4349           }
 4350   
 4351           properties->private.c++;    // check next cluster in FAT
 4352           // check if reached last cluster in FAT, re-start from top
 4353           if ((value == properties->private.EndClusterLimit) || (properties->private.c >= (properties->results.total_clusters + 2)))
 4354               properties->private.c = 2;
 4355   
 4356           // check if full circle done, disk full
 4357           if ( properties->private.c == properties->private.curcls)
 4358           {
 4359               properties->properties_status = FS_GET_PROPERTIES_NO_ERRORS;
 4360               return;
 4361           }
 4362       }  // scanning for an empty cluster
 4363   
 4364       properties->properties_status = FS_GET_PROPERTIES_STILL_WORKING;
 4365       return;
 4366   }
 4367   #endif
 4368   
 4369   /************************************************************
 4370     Function:
 4371       int FSfclose(FSFILE *fo)
 4372     Summary:
 4373       Update file information and free FSFILE objects
 4374     Conditions:
 4375       File opened
 4376     Input:
 4377       fo -  Pointer to the file to close
 4378     Return Values:
 4379       0 -   File closed successfully
 4380       EOF - Error closing the file
 4381     Side Effects:
 4382       The FSerrno variable will be changed.
 4383     Description:
 4384       This function will update the directory entry for the
 4385       file pointed to by 'fo' with the information contained
 4386       in 'fo,' including the new file size and attributes.
 4387       Timestamp information will also be loaded based on the
 4388       method selected by the user and written to the entry
 4389       as the last modified time and date.  The file entry will
 4390       then be written to the device.  Finally, the memory
 4391       used for the specified file object will be freed from
 4392       the dynamic heap or the array of FSFILE objects.
 4393     Remarks:
 4394       A function to flush data to the device without closing the
 4395       file can be created by removing the portion of this
 4396       function that frees the memory and the line that clears
 4397       the write flag.
 4398     ************************************************************/
 4399   
 4400   int FSfclose(FSFILE   *fo)
 4401   {
 4402       WORD        fHandle;
 4403   #ifndef FS_DYNAMIC_MEM
 4404       WORD        fIndex;
 4405   #endif
 4406       int        error = 72;
 4407   #ifdef ALLOW_WRITES
 4408       DIRENTRY    dir;
 4409   #endif
 4410   
 4411       FSerrno = CE_GOOD;
 4412       fHandle = fo->entry;
 4413   
 4414   #ifdef ALLOW_WRITES
 4415       if(fo->flags.write)
 4416       {
 4417           if (gNeedDataWrite)
 4418           {
 4419               if (flushData())
 4420               {
 4421                   FSerrno = CE_WRITE_ERROR;
 4422                   return EOF;
 4423               }
 4424           }
 4425   
 4426           // Write the current FAT sector to the disk
 4427           WriteFAT (fo->dsk, 0, 0, TRUE);
 4428   
 4429           // Invalidate the currently cached FAT entry so that the next read will
 4430           //   result in an acutal read from the physical media instead of a read
 4431           //   from the RAM cache.
 4432           gLastFATSectorRead = 0;
 4433   
 4434           // Read the FAT entry from the physical media.  This is required because
 4435           //   some physical media cache the entries in RAM and only write them 
 4436           //   after a time expires for until the sector is accessed again.
 4437           ReadFAT (fo->dsk, fo->ccls);
 4438   
 4439           // Get the file entry
 4440           dir = LoadDirAttrib(fo, &fHandle);
 4441   
 4442           if (dir == NULL)
 4443           {
 4444               FSerrno = CE_BADCACHEREAD;
 4445               error = EOF;
 4446               return error;
 4447           }
 4448   
 4449         // update the time
 4450   #ifdef INCREMENTTIMESTAMP
 4451           IncrementTimeStamp(dir);
 4452   #elif defined USERDEFINEDCLOCK
 4453           dir->DIR_WrtTime = gTimeWrtTime;
 4454           dir->DIR_WrtDate = gTimeWrtDate;
 4455   #elif defined USEREALTIMECLOCK
 4456           CacheTime();
 4457           dir->DIR_WrtTime = gTimeWrtTime;
 4458           dir->DIR_WrtDate = gTimeWrtDate;
 4459   #endif
 4460   
 4461           dir->DIR_FileSize = fo->size;
 4462   
 4463           dir->DIR_Attr = fo->attributes;
 4464   
 4465           // just write the last entry in
 4466           if(Write_File_Entry(fo,&fHandle))
 4467           {
 4468               // Read the folder entry from the physical media.  This is required because
 4469               //   some physical media cache the entries in RAM and only write them 
 4470               //   after a time expires for until the sector is accessed again.
 4471               dir = LoadDirAttrib(fo, &fHandle);
 4472               error = 0;
 4473           }
 4474           else
 4475           {
 4476               FSerrno = CE_WRITE_ERROR;
 4477               error = EOF;
 4478           }
 4479   
 4480           // it's now closed
 4481           fo->flags.write = FALSE;
 4482       }
 4483   #endif
 4484   
 4485   #ifdef FS_DYNAMIC_MEM
 4486       #ifdef	SUPPORT_LFN
 4487   		FS_free((unsigned char *)fo->utf16LFNptr);
 4488   	#endif
 4489   	FS_free((unsigned char *)fo);
 4490   #else
 4491   
 4492       for( fIndex = 0; fIndex < FS_MAX_FILES_OPEN; fIndex++ )
 4493       {
 4494           if( fo == &gFileArray[fIndex] )
 4495           {
 4496               gFileSlotOpen[fIndex] = TRUE;
 4497               break;
 4498           }
 4499       }
 4500   #endif
 4501   
 4502       // File opened in read mode
 4503       if (error == 72)
 4504           error = 0;
 4505   
 4506       return(error);
 4507   } // FSfclose
 4508   
 4509   
 4510   /*******************************************************
 4511     Function:
 4512       void IncrementTimeStamp(DIRENTRY dir)
 4513     Summary:
 4514       Automatically set the timestamp to "don't care" data
 4515     Conditions:
 4516       Should not be called by the user.
 4517     Input:
 4518       dir -  Pointer to directory structure
 4519     Return Values:
 4520       None
 4521     Side Effects:
 4522       None
 4523     Description:
 4524       This function will increment the timestamp variable in
 4525       the 'dir' directory entry.  This is used for the
 4526       don't-care timing method.
 4527     Remarks:
 4528       None
 4529     *******************************************************/
 4530   #ifdef INCREMENTTIMESTAMP
 4531   void IncrementTimeStamp(DIRENTRY dir)
 4532   {
 4533       BYTE          seconds;
 4534       BYTE          minutes;
 4535       BYTE          hours;
 4536   
 4537       BYTE          day;
 4538       BYTE          month;
 4539       BYTE          year;
 4540   
 4541       seconds = (dir->DIR_WrtTime & 0x1f);
 4542       minutes = ((dir->DIR_WrtTime & 0x07E0) >> 5);
 4543       hours   = ((dir->DIR_WrtTime & 0xF800) >> 11);
 4544   
 4545       day     = (dir->DIR_WrtDate & 0x1f);
 4546       month   = ((dir->DIR_WrtDate & 0x01E0) >> 5);
 4547       year    = ((dir->DIR_WrtDate & 0xFE00) >> 9);
 4548   
 4549       if(seconds < 29)
 4550       {
 4551           // Increment number of seconds by 2
 4552           // This clock method isn't intended to be accurate anyway
 4553           seconds++;
 4554       }
 4555       else
 4556       {
 4557           seconds = 0x00;
 4558   
 4559           if(minutes < 59)
 4560           {
 4561               minutes++;
 4562           }
 4563           else
 4564           {
 4565               minutes = 0;
 4566   
 4567               if(hours < 23)
 4568               {
 4569                   hours++;
 4570               }
 4571               else
 4572               {
 4573                   hours = 0;
 4574                   if(day < 30)
 4575                   {
 4576                       day++;
 4577                   }
 4578                   else
 4579                   {
 4580                       day = 1;
 4581   
 4582                       if(month < 12)
 4583                       {
 4584                           month++;
 4585                       }
 4586                       else
 4587                       {
 4588                           month = 1;
 4589                           // new year
 4590                           year++;
 4591                           // This is only valid until 2107
 4592                       }
 4593                   }
 4594               }
 4595           }
 4596       }
 4597   
 4598       dir->DIR_WrtTime = (WORD)(seconds);
 4599       dir->DIR_WrtTime |= ((WORD)(minutes) << 5);
 4600       dir->DIR_WrtTime |= ((WORD)(hours) << 11);
 4601   
 4602       dir->DIR_WrtDate = (WORD)(day);
 4603       dir->DIR_WrtDate |= ((WORD)(month) << 5);
 4604       dir->DIR_WrtDate |= ((WORD)(year) << 9);
 4605   }
 4606   #endif
 4607   
 4608   /*****************************************************************
 4609     Function:
 4610       BYTE Fill_File_Object(FILEOBJ fo, WORD *fHandle)
 4611     Summary:
 4612       Fill a file object with specified dir entry data
 4613     Conditions:
 4614       This function should not be called by the user.
 4615     Input:
 4616       fo -       Pointer to file structure
 4617       fHandle -  Passed member's location
 4618     Return Values:
 4619       FOUND -     Operation successful
 4620       NOT_FOUND - Operation failed
 4621     Side Effects:
 4622       None
 4623     Description:
 4624       This function will cache the sector of directory entries
 4625       in the directory pointed to by the dirclus value in
 4626       the FSFILE object 'fo' that contains the entry that
 4627       corresponds to the fHandle offset.  It will then copy
 4628       the file information for that entry into the 'fo' FSFILE
 4629       object.
 4630     Remarks:
 4631       None.
 4632     *****************************************************************/
 4633   
 4634   BYTE Fill_File_Object(FILEOBJ fo, WORD *fHandle)
 4635   {
 4636       DIRENTRY    dir;
 4637       BYTE        index, a;
 4638       BYTE        character;
 4639       BYTE        status;
 4640       BYTE        test = 0;
 4641   
 4642       // Get the entry
 4643       if (((*fHandle & MASK_MAX_FILE_ENTRY_LIMIT_BITS) == 0) && (*fHandle != 0)) // 4-bit mask because 16-root entries max per sector
 4644       {
 4645           fo->dirccls = fo->dirclus;
 4646           dir = Cache_File_Entry(fo, fHandle, TRUE);
 4647       }
 4648       else
 4649       {
 4650           dir = Cache_File_Entry (fo, fHandle, FALSE);
 4651       }
 4652   
 4653   
 4654       // Make sure there is a directory left
 4655       if(dir == (DIRENTRY)NULL)
 4656       {
 4657           status = NO_MORE;
 4658       }
 4659       else
 4660       {
 4661           // Read the first char of the file name
 4662           a = dir->DIR_Name[0];
 4663   
 4664           // Check for empty or deleted directory
 4665           if ( a == DIR_DEL)
 4666   		{
 4667               status = NOT_FOUND;
 4668   		}
 4669   		else if ( a == DIR_EMPTY)
 4670   		{
 4671   			status = NO_MORE;
 4672   		}
 4673           else
 4674           {
 4675               // Get the attributes
 4676               a = dir->DIR_Attr;
 4677   
 4678               // print the file name and extension
 4679               for (index=0; index < DIR_NAMESIZE; index++)
 4680               {
 4681                   character = dir->DIR_Name[index];
 4682                   character = (BYTE)toupper(character);
 4683                   fo->name[test++] = character;
 4684               }
 4685   
 4686               // Get the attributes
 4687               a = dir->DIR_Attr;
 4688   
 4689               // its possible to have an extension in a directory
 4690               character = dir->DIR_Extension[0];
 4691   
 4692               // Get the file extension if its there
 4693               for (index=0; index < DIR_EXTENSION; index++)
 4694               {
 4695                   character = dir->DIR_Extension[index];
 4696                   character = (BYTE)toupper(character);
 4697                   fo->name[test++] = character;
 4698               }
 4699   
 4700               // done and done with the name
 4701               //         fo->name[++test] = (BYTE)'\0';
 4702   
 4703               // Now store the identifier
 4704               fo->entry = *fHandle;
 4705   
 4706               // see if we are still a good file
 4707               a = dir->DIR_Name[0];
 4708   
 4709               if(a == DIR_DEL)
 4710                   status = NOT_FOUND;
 4711               else
 4712                   status = FOUND;
 4713   
 4714               // Now store the size
 4715               fo->size = (dir->DIR_FileSize);
 4716   
 4717               fo->cluster = GetFullClusterNumber(dir); // Get Complete Cluster number.
 4718   
 4719               /// -Get and store the attributes
 4720               a = dir->DIR_Attr;
 4721               fo->attributes = a;
 4722   
 4723               // get the date and time
 4724               if ((a & ATTR_DIRECTORY) != 0)
 4725               {
 4726                   fo->time = dir->DIR_CrtTime;
 4727                   fo->date = dir->DIR_CrtDate;
 4728               }
 4729               else
 4730               {
 4731                   fo->time = dir->DIR_WrtTime;
 4732                   fo->date = dir->DIR_WrtDate;
 4733               }
 4734   
 4735           }// deleted directory
 4736       }// Ensure we are still good
 4737       return(status);
 4738   } // Fill_File_Object
 4739   
 4740   /*****************************************************************
 4741     Function:
 4742       BYTE Fill_LFN_Object(FILEOBJ fo, LFN_ENTRY *lfno, WORD *fHandle)
 4743     Summary:
 4744       Fill a LFN object with specified entry data
 4745     Conditions:
 4746       This function should not be called by the user.
 4747     Input:
 4748       fo -   Pointer to file structure
 4749       lfno - Pointer to Long File Name Object
 4750       fHandle -  Passed member's location
 4751     Return Values:
 4752       FOUND -     Operation successful
 4753       NOT_FOUND - Operation failed
 4754     Side Effects:
 4755       None
 4756     Description:
 4757       This function will cache the sector of LFN entries
 4758       in the directory pointed to by the dirclus value in
 4759       the FSFILE object 'fo' that contains the entry that
 4760       corresponds to the fHandle offset.  It will then copy
 4761       the file information for that entry into the 'fo' FSFILE
 4762       object.
 4763     Remarks:
 4764       None.
 4765     *****************************************************************/
 4766   #if defined(SUPPORT_LFN)
 4767   BYTE Fill_LFN_Object(FILEOBJ fo, LFN_ENTRY *lfno, WORD *fHandle)
 4768   {
 4769       DIRENTRY    dir;
 4770       BYTE        tempVariable;
 4771       BYTE        *src,*dst;
 4772       BYTE        status;
 4773   
 4774       // Get the entry
 4775       if (((*fHandle & MASK_MAX_FILE_ENTRY_LIMIT_BITS) == 0) && (*fHandle != 0)) // 4-bit mask because 16-root entries max per sector
 4776       {
 4777           fo->dirccls = fo->dirclus;
 4778           dir = Cache_File_Entry(fo, fHandle, TRUE);
 4779       }
 4780       else
 4781       {
 4782           dir = Cache_File_Entry (fo, fHandle, FALSE);
 4783       }
 4784   
 4785   
 4786       // Make sure there is a directory left
 4787       if(dir == (DIRENTRY)NULL)
 4788       {
 4789           status = NO_MORE;
 4790       }
 4791       else
 4792       {
 4793           // Read the first char of the file name
 4794           tempVariable = dir->DIR_Name[0];
 4795   
 4796           // Check for empty or deleted directory
 4797           if ( tempVariable == DIR_DEL)
 4798   		{
 4799               status = NOT_FOUND;
 4800   		}
 4801   		else if ( tempVariable == DIR_EMPTY)
 4802   		{
 4803   			status = NO_MORE;
 4804   		}
 4805           else
 4806           {
 4807               status = FOUND;
 4808   
 4809   			dst = (BYTE *)lfno;
 4810   			src = (BYTE *)dir;
 4811   
 4812   			// Copy the entry in the lfno object
 4813   			for(tempVariable = 0;tempVariable < 32;tempVariable++)
 4814   			{
 4815   				*dst++ = *src++;
 4816   			}
 4817           }// deleted directory
 4818       }// Ensure we are still good
 4819       return(status);
 4820   } // Fill_File_Object
 4821   #endif
 4822   /************************************************************************
 4823     Function:
 4824       DIRENTRY LoadDirAttrib(FILEOBJ fo, WORD *fHandle)
 4825     Summary:
 4826       Load file information from a directory entry and cache the entry
 4827     Conditions:
 4828       This function should not be called by the user.
 4829     Input:
 4830       fo -       Pointer to file structure
 4831       fHandle -  Information location
 4832     Return Values:
 4833       DIRENTRY - Pointer to the directory entry
 4834       NULL -     Directory entry could not be loaded
 4835     Side Effects:
 4836       None
 4837     Description:
 4838       This function will cache the sector of directory entries
 4839       in the directory pointed to by the dirclus value in
 4840       the FSFILE object 'fo' that contains the entry that
 4841       corresponds to the fHandle offset.  It will then return a pointer
 4842       to the directory entry in the global data buffer.
 4843     Remarks:
 4844       None.
 4845     ************************************************************************/
 4846   
 4847   DIRENTRY LoadDirAttrib(FILEOBJ fo, WORD *fHandle)
 4848   {
 4849       DIRENTRY    dir;
 4850       BYTE      a;
 4851   
 4852       fo->dirccls = fo->dirclus;
 4853       // Get the entry
 4854       dir = Cache_File_Entry( fo, fHandle, TRUE);
 4855       if (dir == NULL)
 4856           return NULL;
 4857   
 4858       // Read the first char of the file name
 4859       a = dir->DIR_Name[0];
 4860   
 4861       // Make sure there is a directory left
 4862       if(a == DIR_EMPTY)
 4863           dir = (DIRENTRY)NULL;
 4864   
 4865       if(dir != (DIRENTRY)NULL)
 4866       {
 4867           // Check for empty or deleted directory
 4868           if ( a == DIR_DEL)
 4869               dir = (DIRENTRY)NULL;
 4870           else
 4871           {
 4872               // Get the attributes
 4873               a = dir->DIR_Attr;
 4874   
 4875               // scan through all the long dir entries
 4876               while(a == ATTR_LONG_NAME)
 4877               {
 4878                   (*fHandle)++;
 4879                   dir = Cache_File_Entry( fo, fHandle, FALSE);
 4880                   if (dir == NULL)
 4881                       return NULL;
 4882                   a = dir->DIR_Attr;
 4883               } // long file name while loop
 4884           } // deleted dir
 4885       }// Ensure we are still good
 4886   
 4887       return(dir);
 4888   } // LoadDirAttrib
 4889   
 4890   
 4891   /**************************************************************************
 4892     Function:
 4893       CETYPE FILEerase( FILEOBJ fo, WORD *fHandle, BYTE EraseClusters)
 4894     Summary:
 4895       Erase a file
 4896     Conditions:
 4897       This function should not be called by the user.
 4898     Input:
 4899       fo -            Pointer to file structure
 4900       fHandle -       Location of file information
 4901       EraseClusters - If set to TRUE, delete the corresponding cluster of file
 4902     Return Values:
 4903       CE_GOOD - File erased successfully
 4904       CE_FILE_NOT_FOUND - Could not find the file on the card
 4905       CE_ERASE_FAIL - Internal Card erase failed
 4906     Side Effects:
 4907       None
 4908     Description:
 4909       This function will cache the sector of directory entries in the directory
 4910       pointed to by the dirclus value in the FSFILE object 'fo' that contains
 4911       the entry that corresponds to the fHandle offset.  It will then mark that
 4912       entry as deleted.  If the EraseClusters argument is TRUE, the chain of
 4913       clusters for that file will be marked as unused in the FAT by the
 4914       FAT_erase_cluster_chain function.
 4915     Remarks:
 4916       None.
 4917     **************************************************************************/
 4918   
 4919   #ifdef ALLOW_WRITES
 4920   CETYPE FILEerase( FILEOBJ fo, WORD *fHandle, BYTE EraseClusters)
 4921   {
 4922       DIRENTRY    dir;
 4923       BYTE        a;
 4924       CETYPE      status = CE_GOOD;
 4925       DWORD       clus = 0;
 4926       DISK *      disk;
 4927   
 4928       BYTE	numberOfFileEntries;
 4929   	BOOL	forFirstTime = TRUE;
 4930   	#if defined(SUPPORT_LFN)
 4931   		BYTE	tempCalc1;
 4932   	#endif
 4933   
 4934       disk = fo->dsk;
 4935   
 4936   	#if defined(SUPPORT_LFN)
 4937   
 4938   	fileNameLength = fo->utf16LFNlength;
 4939   
 4940   	// Find the number of entries of LFN in the root directory
 4941   	if(fileNameLength)
 4942   	{
 4943   		tempCalc1 = fileNameLength % 13;
 4944   
 4945   		numberOfFileEntries = fileNameLength/13;
 4946   
 4947   		if(tempCalc1 || (fileNameLength < 13))
 4948   		{
 4949   			numberOfFileEntries = numberOfFileEntries + 2;
 4950   		}
 4951   		else
 4952   		{
 4953   			numberOfFileEntries++;
 4954   		}
 4955   	}
 4956   	else
 4957   	#endif
 4958   	{
 4959   		numberOfFileEntries = 1;
 4960   	}
 4961   
 4962   	FSerrno = CE_ERASE_FAIL;
 4963   
 4964   	// delete all the entries of LFN in root directory
 4965   	while(numberOfFileEntries--)
 4966   	{
 4967   	    // reset the cluster
 4968   	    fo->dirccls = fo->dirclus;
 4969   
 4970   	    // load the sector
 4971   	    dir = Cache_File_Entry(fo, fHandle, TRUE);
 4972   
 4973   	    if (dir == NULL)
 4974   	    {
 4975   	        return CE_BADCACHEREAD;
 4976   	    }
 4977   
 4978   	    // Fill up the File Object with the information pointed to by fHandle
 4979   	    a = dir->DIR_Name[0];
 4980   
 4981   	    // see if there is something in the dir
 4982   	    if((dir == (DIRENTRY)NULL) || (a == DIR_EMPTY) || (a == DIR_DEL))
 4983   	    {
 4984   	        status = CE_FILE_NOT_FOUND;
 4985   			break;
 4986   	    }
 4987   		else
 4988   		{
 4989               /* 8.3 File Name - entry*/
 4990               dir->DIR_Name[0] = DIR_DEL; // mark as deleted
 4991   
 4992   			if(!(Write_File_Entry( fo, fHandle)))
 4993        		{
 4994        		    status = CE_ERASE_FAIL;
 4995   				break;
 4996        		}
 4997   		}
 4998   
 4999   		if(forFirstTime)
 5000   		{
 5001   			// Get the starting cluster
 5002   			clus = GetFullClusterNumber(dir); // Get Complete Cluster number.
 5003   			forFirstTime = FALSE;
 5004   		}
 5005   
 5006   		*fHandle = *fHandle - 1;
 5007   	}
 5008   
 5009   	if(status == CE_GOOD)
 5010   	{
 5011   		if (clus != FatRootDirClusterValue) //
 5012   		{
 5013   			// If 'EraseClusters' is set to TRUE, erase the cluster chain corresponding to file
 5014   		    if(EraseClusters)
 5015   		    {
 5016   		        /* Now remove the cluster allocation from the FAT */
 5017   		        status = ((FAT_erase_cluster_chain(clus, disk)) ? CE_GOOD : CE_ERASE_FAIL);
 5018   		    }
 5019   		}
 5020   
 5021   		FSerrno = status;
 5022   	}
 5023   
 5024       return (status);
 5025   }
 5026   #endif
 5027   
 5028   /***************************************************************
 5029     Function:
 5030       int FSrename (const rom char * fileName, FSFILE * fo)
 5031     Summary:
 5032       Change the name of a file or directory
 5033     Conditions:
 5034       File opened.
 5035     Input:
 5036       fileName -  The new name of the file
 5037       fo -        The file to rename
 5038     Return Values:
 5039       0 -   File was renamed successfully
 5040       EOF - File was not renamed
 5041     Side Effects:
 5042       The FSerrno variable will be changed.
 5043     Description:
 5044       The FSrename function will rename a file.  First, it will
 5045       search through the current working directory to ensure the
 5046       specified new filename is not already in use.  If it isn't,
 5047       the new filename will be written to the file entry of the
 5048       file pointed to by 'fo.'
 5049     Remarks:
 5050       None
 5051     ***************************************************************/
 5052   
 5053   #ifdef ALLOW_WRITES
 5054   
 5055   int FSrename (const char * fileName, FSFILE * fo)
 5056   {
 5057       WORD fHandle;
 5058   	FSFILE	tempFo1,tempFo2;
 5059       DIRENTRY dir;
 5060       #ifdef SUPPORT_LFN
 5061       DWORD  TempMsbCluster;
 5062       #else
 5063       BYTE j;
 5064       #endif
 5065   
 5066       FSerrno = CE_GOOD;
 5067   
 5068       if (MDD_WriteProtectState())
 5069       {
 5070           FSerrno = CE_WRITE_PROTECTED;
 5071           return (-1);
 5072       }
 5073   
 5074        // copy file object over
 5075       FileObjectCopy(&tempFo1, fo);
 5076   
 5077       //Format the source string
 5078       if(!FormatFileName(fileName, &tempFo1, 0) )
 5079       {
 5080           FSerrno = CE_INVALID_FILENAME;
 5081           return -1;
 5082       }
 5083   
 5084   	tempFo1.entry  = 0;
 5085   
 5086   	// start at the current directory
 5087   	tempFo1.dirclus  = cwdptr->dirclus;
 5088   	tempFo1.dirccls  = cwdptr->dirccls;
 5089   
 5090       // copy file object over
 5091       FileObjectCopy(&tempFo2, &tempFo1);
 5092   
 5093       // See if the file is found
 5094       if(FILEfind (&tempFo2, &tempFo1, LOOK_FOR_MATCHING_ENTRY, 0) == CE_FILE_NOT_FOUND)
 5095   	{
 5096   		fHandle = fo->entry;
 5097   
 5098   		#ifdef SUPPORT_LFN
 5099   
 5100   		if(CE_GOOD != FILEerase(fo, &fHandle, FALSE))
 5101   		{
 5102   			FSerrno = CE_ERASE_FAIL;
 5103   			return -1;
 5104   		}
 5105   
 5106   	   	// Create the new entry as per the user requested name
 5107   	   	FSerrno = CreateFileEntry (&tempFo1, &fHandle, tempFo1.attributes, FALSE);
 5108   
 5109   	   	// load the file entry so the new cluster can be linked to it
 5110   	   	dir = LoadDirAttrib(&tempFo1, &fHandle);
 5111   
 5112   	   	// Now update the new cluster
 5113   	   	dir->DIR_FstClusLO = (fo->cluster & 0x0000FFFF);
 5114   
 5115   	   	#ifdef SUPPORT_FAT32 // If FAT32 supported.
 5116   	   	// Get the higher part of cluster and store it in directory entry.
 5117   	   	TempMsbCluster = (fo->cluster & 0x0FFF0000);    // Since only 28 bits usedin FAT32. Mask the higher MSB nibble.
 5118   	   	TempMsbCluster = TempMsbCluster >> 16;      // Get the date into Lsb place.
 5119   	   	dir->DIR_FstClusHI = TempMsbCluster;
 5120   	   	#else // If FAT32 support not enabled
 5121   	   	TempMsbCluster = 0;                         // Just to avoid compiler warnigng.
 5122   	   	dir->DIR_FstClusHI = 0;
 5123   	   	#endif
 5124   	   
 5125   		// Update the file size
 5126           dir->DIR_FileSize = fo->size;
 5127   
 5128   	   	// now write it
 5129   	   	if(Write_File_Entry(&tempFo1, &fHandle) != TRUE)
 5130   		{
 5131   	    	FSerrno = CE_WRITE_ERROR;
 5132   	    	return -1;
 5133   		}
 5134   	   	
 5135      		tempFo1.size = fo->size;
 5136   
 5137   		// copy file object over
 5138   		FileObjectCopy(fo, &tempFo1);
 5139   		
 5140   		#else
 5141   
 5142           // Get the file entry
 5143           dir = LoadDirAttrib(fo, &fHandle);
 5144   
 5145           for (j = 0; j < 11; j++)
 5146           {
 5147               fo->name[j] = tempFo1.name[j];
 5148               dir->DIR_Name[j] = tempFo1.name[j];
 5149           }
 5150   
 5151           // just write the last entry in
 5152           if(!Write_File_Entry(fo,&fHandle))
 5153           {
 5154               FSerrno = CE_WRITE_ERROR;
 5155               return -1;
 5156           }
 5157   
 5158   		#endif
 5159   	}
 5160   	else
 5161   	{
 5162           FSerrno = CE_FILENAME_EXISTS;
 5163           return -1;
 5164   	}
 5165   
 5166       return 0;
 5167   }
 5168   
 5169   /***************************************************************
 5170     Function:
 5171       int wFSrename (const rom unsigned short int * fileName, FSFILE * fo)
 5172     Summary:
 5173       Change the name of a file or directory to the UTF16 input fileName
 5174     Conditions:
 5175       File opened.
 5176     Input:
 5177       fileName -  The new name of the file
 5178       fo -        The file to rename
 5179     Return Values:
 5180       0 -   File was renamed successfully
 5181       EOF - File was not renamed
 5182     Side Effects:
 5183       The FSerrno variable will be changed.
 5184     Description:
 5185       The wFSrename function will rename a file.  First, it will
 5186       search through the current working directory to ensure the
 5187       specified new UTF16 filename is not already in use.  If it isn't,
 5188       the new filename will be written to the file entry of the
 5189       file pointed to by 'fo.'
 5190     Remarks:
 5191       None
 5192     ***************************************************************/
 5193   #ifdef SUPPORT_LFN
 5194   int wFSrename (const unsigned short int * fileName, FSFILE * fo)
 5195   {
 5196   	int result;
 5197   	utfModeFileName = TRUE;
 5198   	result = FSrename ((const char *)fileName,fo);
 5199   	utfModeFileName = FALSE;
 5200   	return result;
 5201   }
 5202   #endif
 5203   
 5204   #endif // Allow writes
 5205   
 5206   /*********************************************************************
 5207     Function:
 5208       FSFILE * wFSfopen (const unsigned short int * fileName, const char *mode)
 5209     Summary:
 5210       Open a UTF16 file.
 5211     Conditions:
 5212       For read modes, file exists; FSInit performed
 5213     Input:
 5214       fileName -  The name of the file to open
 5215       mode -
 5216            - WRITE -      Create a new file or replace an existing file
 5217            - READ -       Read data from an existing file
 5218            - APPEND -     Append data to an existing file
 5219            - WRITEPLUS -  Create a new file or replace an existing file (reads also enabled)
 5220            - READPLUS -   Read data from an existing file (writes also enabled)
 5221            - APPENDPLUS - Append data to an existing file (reads also enabled)
 5222     Return Values:
 5223       FSFILE * - The pointer to the file object
 5224       NULL -     The file could not be opened
 5225     Side Effects:
 5226       The FSerrno variable will be changed.
 5227     Description:
 5228       This function will open a file or directory.  First, RAM in the
 5229       dynamic heap or static array will be allocated to a new FSFILE object.
 5230       Then, the specified file name will be formatted to ensure that it's
 5231       in 8.3 format or LFN format. Next, the FILEfind function will be used
 5232       to search for the specified file name. If the name is found, one of three
 5233       things will happen: if the file was opened in read mode, its file
 5234       info will be loaded using the FILEopen function; if it was opened in
 5235       write mode, it will be erased, and a new file will be constructed in
 5236       its place; if it was opened in append mode, its file info will be
 5237       loaded with FILEopen and the current location will be moved to the
 5238       end of the file using the FSfseek function.  If the file was not
 5239       found by FILEfind, a new file will be created if the mode was specified as
 5240       a write or append mode.  In these cases, a pointer to the heap or
 5241       static FSFILE object array will be returned. If the file was not
 5242       found and the mode was specified as a read mode, the memory
 5243       allocated to the file will be freed and the NULL pointer value
 5244       will be returned.
 5245     Remarks:
 5246       None.
 5247     *********************************************************************/
 5248   #ifdef SUPPORT_LFN
 5249   FSFILE * wFSfopen( const unsigned short int * fileName, const char *mode )
 5250   {
 5251   	FSFILE *result;
 5252   	utfModeFileName = TRUE;
 5253   	result = FSfopen((const char *)fileName,mode);
 5254   	utfModeFileName = FALSE;
 5255   	return result;
 5256   }
 5257   #endif
 5258   
 5259   /*********************************************************************
 5260     Function:
 5261       FSFILE * FSfopen (const char * fileName, const char *mode)
 5262     Summary:
 5263       Open a Ascii file
 5264     Conditions:
 5265       For read modes, file exists; FSInit performed
 5266     Input:
 5267       fileName -  The name of the file to open
 5268       mode -
 5269            - WRITE -      Create a new file or replace an existing file
 5270            - READ -       Read data from an existing file
 5271            - APPEND -     Append data to an existing file
 5272            - WRITEPLUS -  Create a new file or replace an existing file (reads also enabled)
 5273            - READPLUS -   Read data from an existing file (writes also enabled)
 5274            - APPENDPLUS - Append data to an existing file (reads also enabled)
 5275     Return Values:
 5276       FSFILE * - The pointer to the file object
 5277       NULL -     The file could not be opened
 5278     Side Effects:
 5279       The FSerrno variable will be changed.
 5280     Description:
 5281       This function will open a file or directory.  First, RAM in the
 5282       dynamic heap or static array will be allocated to a new FSFILE object.
 5283       Then, the specified file name will be formatted to ensure that it's
 5284       in 8.3 format or LFN format. Next, the FILEfind function will be used
 5285       to search for the specified file name. If the name is found, one of three
 5286       things will happen: if the file was opened in read mode, its file
 5287       info will be loaded using the FILEopen function; if it was opened in
 5288       write mode, it will be erased, and a new file will be constructed in
 5289       its place; if it was opened in append mode, its file info will be
 5290       loaded with FILEopen and the current location will be moved to the
 5291       end of the file using the FSfseek function.  If the file was not
 5292       found by FILEfind, a new file will be created if the mode was specified as
 5293       a write or append mode.  In these cases, a pointer to the heap or
 5294       static FSFILE object array will be returned. If the file was not
 5295       found and the mode was specified as a read mode, the memory
 5296       allocated to the file will be freed and the NULL pointer value
 5297       will be returned.
 5298     Remarks:
 5299       None.
 5300     *********************************************************************/
 5301   
 5302   FSFILE * FSfopen( const char * fileName, const char *mode )
 5303   {
 5304       FILEOBJ    filePtr;
 5305   #ifndef FS_DYNAMIC_MEM
 5306       int      fIndex;
 5307   #endif
 5308       BYTE   ModeC;
 5309       WORD    fHandle;
 5310       CETYPE   final;
 5311   
 5312   #ifdef FS_DYNAMIC_MEM
 5313       filePtr = (FILEOBJ) FS_malloc(sizeof(FSFILE));
 5314   #else
 5315   
 5316       filePtr = NULL;
 5317   
 5318       //Pick available file structure
 5319       for( fIndex = 0; fIndex < FS_MAX_FILES_OPEN; fIndex++ )
 5320       {
 5321           if( gFileSlotOpen[fIndex] )   //this slot is available
 5322           {
 5323               gFileSlotOpen[fIndex] = FALSE;
 5324               filePtr = &gFileArray[fIndex];
 5325   			break;
 5326           }
 5327       }
 5328   
 5329       if( filePtr == NULL )
 5330       {
 5331           FSerrno = CE_TOO_MANY_FILES_OPEN;
 5332           return NULL;      //no file structure slot available
 5333       }
 5334   #endif
 5335   
 5336   	#if defined(SUPPORT_LFN)
 5337   		#if defined(FS_DYNAMIC_MEM)
 5338   			filePtr -> utf16LFNptr = (unsigned short int *)FS_malloc(514);
 5339   		#else
 5340   			filePtr->utf16LFNptr = &lfnData[fIndex][0];
 5341   		#endif
 5342       #endif
 5343   
 5344       //Format the source string.
 5345       if( !FormatFileName(fileName, filePtr, 0) )
 5346       {
 5347   		#ifdef FS_DYNAMIC_MEM
 5348   			#if defined(SUPPORT_LFN)
 5349   				FS_free((unsigned char *)filePtr->utf16LFNptr);
 5350   			#endif
 5351           	FS_free( (unsigned char *)filePtr );
 5352   		#else
 5353           	gFileSlotOpen[fIndex] = TRUE;   //put this slot back to the pool
 5354   		#endif
 5355           
 5356   		FSerrno = CE_INVALID_FILENAME;
 5357           return NULL;   //bad filename
 5358       }
 5359   
 5360       //Read the mode character
 5361       ModeC = mode[0];
 5362   
 5363       filePtr->dsk = &gDiskData;
 5364       filePtr->cluster = 0;
 5365       filePtr->ccls    = 0;
 5366       filePtr->entry = 0;
 5367       filePtr->attributes = ATTR_ARCHIVE;
 5368   
 5369       // start at the current directory
 5370   #ifdef ALLOW_DIRS
 5371       filePtr->dirclus    = cwdptr->dirclus;
 5372       filePtr->dirccls    = cwdptr->dirccls;
 5373   #else
 5374       filePtr->dirclus = FatRootDirClusterValue;
 5375       filePtr->dirccls = FatRootDirClusterValue;
 5376   #endif
 5377   
 5378       // copy file object over
 5379       FileObjectCopy(&gFileTemp, filePtr);
 5380   
 5381       // See if the file is found
 5382       if(FILEfind (filePtr, &gFileTemp, LOOK_FOR_MATCHING_ENTRY, 0) == CE_GOOD)
 5383       {
 5384           // File is Found
 5385           switch(ModeC)
 5386           {
 5387   #ifdef ALLOW_WRITES
 5388               case 'w':
 5389               case 'W':
 5390               {
 5391                   // File exists, we want to create a new one, remove it first
 5392                   fHandle = filePtr->entry;
 5393                   final = FILEerase(filePtr, &fHandle, TRUE);
 5394   
 5395                   if (final == CE_GOOD)
 5396                   {
 5397                       // now create a new one
 5398                       final = CreateFileEntry (filePtr, &fHandle, 0, TRUE);
 5399   
 5400                       if (final == CE_GOOD)
 5401                       {
 5402                           final = FILEopen (filePtr, &fHandle, 'w');
 5403   
 5404                           if (filePtr->attributes & ATTR_DIRECTORY)
 5405                           {
 5406                               FSerrno = CE_INVALID_ARGUMENT;
 5407                               final = 0xFF;
 5408                           }
 5409   
 5410                           if (final == CE_GOOD)
 5411                           {
 5412                               final = FSfseek (filePtr, 0, SEEK_END);
 5413                               if (mode[1] == '+')
 5414                                   filePtr->flags.read = 1;
 5415                           }
 5416                       }
 5417                   }
 5418                   break;
 5419               }
 5420   
 5421               case 'A':
 5422               case 'a':
 5423               {
 5424                   if(filePtr->size != 0)
 5425                   {
 5426                       fHandle = filePtr->entry;
 5427   
 5428                       final = FILEopen (filePtr, &fHandle, 'w');
 5429   
 5430                       if (filePtr->attributes & ATTR_DIRECTORY)
 5431                       {
 5432                           FSerrno = CE_INVALID_ARGUMENT;
 5433                           final = 0xFF;
 5434                       }
 5435   
 5436                       if (final == CE_GOOD)
 5437                       {
 5438                           final = FSfseek (filePtr, 0, SEEK_END);
 5439                           if (final != CE_GOOD)
 5440                               FSerrno = CE_SEEK_ERROR;
 5441                           else
 5442                               ReadFAT (&gDiskData, filePtr->ccls);
 5443                           if (mode[1] == '+')
 5444                               filePtr->flags.read = 1;
 5445                       }
 5446                   }
 5447                   else
 5448                   {
 5449                       fHandle = filePtr->entry;
 5450                       final = FILEerase(filePtr, &fHandle, TRUE);
 5451   
 5452                       if (final == CE_GOOD)
 5453                       {
 5454                           // now create a new one
 5455                           final = CreateFileEntry (filePtr, &fHandle, 0, TRUE);
 5456   
 5457                           if (final == CE_GOOD)
 5458                           {
 5459                               final = FILEopen (filePtr, &fHandle, 'w');
 5460   
 5461                               if (filePtr->attributes & ATTR_DIRECTORY)
 5462                               {
 5463                                   FSerrno = CE_INVALID_ARGUMENT;
 5464                                   final = 0xFF;
 5465                               }
 5466   
 5467                               if (final == CE_GOOD)
 5468                               {
 5469                                   final = FSfseek (filePtr, 0, SEEK_END);
 5470                                   if (final != CE_GOOD)
 5471                                       FSerrno = CE_SEEK_ERROR;
 5472                                   if (mode[1] == '+')
 5473                                       filePtr->flags.read = 1;
 5474                               }
 5475                           }
 5476                       }
 5477                   }
 5478                   break;
 5479               }
 5480   #endif
 5481               case 'R':
 5482               case 'r':
 5483               {
 5484                   fHandle = filePtr->entry;
 5485   
 5486                   final = FILEopen (filePtr, &fHandle, 'r');
 5487   #ifdef ALLOW_WRITES
 5488                   if ((mode[1] == '+') && !(filePtr->attributes & ATTR_DIRECTORY))
 5489                       filePtr->flags.write = 1;
 5490   #endif
 5491                   break;
 5492               }
 5493   
 5494               default:
 5495                   FSerrno = CE_INVALID_ARGUMENT;
 5496                   final = 0xFF;;  //indicate error condition
 5497                   break;
 5498           }
 5499       }
 5500       else
 5501       {
 5502   #ifdef ALLOW_WRITES
 5503           // the file was not found, reset to the default asked
 5504           FileObjectCopy(filePtr, &gFileTemp);
 5505   
 5506           // File is not Found
 5507           if((ModeC == 'w') || (ModeC == 'W') || (ModeC == 'a') || (ModeC == 'A'))
 5508           {
 5509               // use the user requested name
 5510               fHandle = 0;
 5511               final = CreateFileEntry (filePtr, &fHandle, 0, TRUE);
 5512   
 5513               if (final == CE_GOOD)
 5514               {
 5515                   final = FILEopen (filePtr, &fHandle, 'w');
 5516                   if (filePtr->attributes & ATTR_DIRECTORY)
 5517                   {
 5518                       FSerrno = CE_INVALID_ARGUMENT;
 5519                       final = 0xFF;
 5520                   }
 5521   
 5522                   if (final == CE_GOOD)
 5523                   {
 5524                       final = FSfseek (filePtr, 0, SEEK_END);
 5525                       if (final != CE_GOOD)
 5526                           FSerrno = CE_SEEK_ERROR;
 5527                       if (mode[1] == '+')
 5528                           filePtr->flags.read = 1;
 5529                   }
 5530               }
 5531           }
 5532           else
 5533   #endif
 5534               final = CE_FILE_NOT_FOUND;
 5535       }
 5536   
 5537       if (MDD_WriteProtectState())
 5538       {
 5539           filePtr->flags.write = 0;;
 5540       }
 5541   
 5542   #ifdef FS_DYNAMIC_MEM
 5543       if( final != CE_GOOD )
 5544       {
 5545           #ifdef	SUPPORT_LFN
 5546   			FS_free((unsigned char *)filePtr->utf16LFNptr);
 5547   		#endif
 5548   		FS_free( (unsigned char *)filePtr );
 5549           filePtr = NULL;
 5550       }
 5551   #else
 5552       if( final != CE_GOOD )
 5553       {
 5554           gFileSlotOpen[fIndex] = TRUE;   //put this slot back to the pool
 5555           filePtr = NULL;
 5556       }
 5557   #endif
 5558       else
 5559       {
 5560           FSerrno = CE_GOOD;
 5561       }
 5562   
 5563       return filePtr;
 5564   }
 5565   
 5566   /*******************************************************************
 5567     Function:
 5568       long FSftell (FSFILE * fo)
 5569     Summary:
 5570       Determine the current location in a file
 5571     Conditions:
 5572       File opened
 5573     Input:
 5574       fo -  Pointer to file structure
 5575     Return: Current location in the file
 5576     Side Effects:
 5577       The FSerrno variable will be changed
 5578     Description:
 5579       The FSftell function will return the current position in the
 5580       file pointed to by 'fo' by returning the 'seek' variable in the
 5581       FSFILE object, which is used to keep track of the absolute
 5582       location of the current position in the file.
 5583     Remarks:
 5584       None
 5585     *******************************************************************/
 5586   
 5587   long FSftell (FSFILE * fo)
 5588   {
 5589       FSerrno = CE_GOOD;
 5590       return (fo->seek);
 5591   }
 5592   
 5593   
 5594   #ifdef ALLOW_WRITES
 5595   
 5596   /*********************************************************************
 5597     Function:
 5598       int FSremove (const char * fileName)
 5599     Summary:
 5600       Delete a Ascii file
 5601     Conditions:
 5602       File not opened, file exists
 5603     Input:
 5604       fileName -  Name of the file to erase
 5605     Return Values:
 5606       0 -   File removed
 5607       EOF - File was not removed
 5608     Side Effects:
 5609       The FSerrno variable will be changed.
 5610     Description:
 5611       The FSremove function will attempt to find the specified file with
 5612       the FILEfind function.  If the file is found, it will be erased
 5613       using the FILEerase function. The user can also provide ascii alias name
 5614       of the ascii long file name as the input to this function to get it erased
 5615       from the memory.
 5616     Remarks:
 5617       None
 5618     **********************************************************************/
 5619   
 5620   int FSremove (const char * fileName)
 5621   {
 5622       FILEOBJ fo = &tempCWDobj;
 5623   
 5624   	#ifdef SUPPORT_LFN
 5625   		FSFILE cwdTemp;
 5626   		char tempArray[514];
 5627   		LFN_ENTRY *lfno;
 5628   		WORD prevHandle;
 5629   		unsigned short int i = 0;
 5630   	#endif
 5631       FSerrno = CE_GOOD;
 5632   
 5633       if (MDD_WriteProtectState())
 5634       {
 5635           FSerrno = CE_WRITE_PROTECTED;
 5636           return (-1);
 5637       }
 5638   
 5639       //Format the source string
 5640   	#if defined(SUPPORT_LFN)
 5641   		fo->utf16LFNptr = (unsigned short int *)&tempArray[0];
 5642   	#endif
 5643   
 5644       if( !FormatFileName(fileName, fo, 0) )
 5645       {
 5646           FSerrno = CE_INVALID_FILENAME;
 5647           return -1;
 5648       }
 5649   
 5650       fo->dsk = &gDiskData;
 5651       fo->cluster = 0;
 5652       fo->ccls    = 0;
 5653       fo->entry = 0;
 5654       fo->attributes = ATTR_ARCHIVE;
 5655   
 5656   #ifndef ALLOW_DIRS
 5657       // start at the root directory
 5658       fo->dirclus    = FatRootDirClusterValue;
 5659       fo->dirccls    = FatRootDirClusterValue;
 5660   #else
 5661       fo->dirclus = cwdptr->dirclus;
 5662       fo->dirccls = cwdptr->dirccls;
 5663   #endif
 5664   
 5665       // copy file object over
 5666       FileObjectCopy(&gFileTemp, fo);
 5667   
 5668       // See if the file is found
 5669       if (FILEfind (fo, &gFileTemp, LOOK_FOR_MATCHING_ENTRY, 0) != CE_GOOD)
 5670       {
 5671           FSerrno = CE_FILE_NOT_FOUND;
 5672           return -1;
 5673       }
 5674   
 5675       if (fo->attributes & ATTR_DIRECTORY)
 5676       {
 5677           FSerrno = CE_DELETE_DIR;
 5678           return -1;
 5679       }
 5680   
 5681   	// Find the long file name assosciated with the short file name if present
 5682   	#ifdef SUPPORT_LFN
 5683   	if(!fo->utf16LFNlength)
 5684   	{
 5685   		FileObjectCopy (&cwdTemp, fo);
 5686   		prevHandle = fo->entry - 1;

 5687   		lfno = (LFN_ENTRY *)Cache_File_Entry (fo, &prevHandle, FALSE);
 5688   

 5689   	   	while((lfno->LFN_Attribute == ATTR_LONG_NAME) && (lfno->LFN_SequenceNo != DIR_DEL)

 5690   	   			&& (lfno->LFN_SequenceNo != DIR_EMPTY))

 5691   	   	{

 5692   
 5693   			i = i + MAX_UTF16_CHARS_IN_LFN_ENTRY;
 5694   
 5695   	   		prevHandle = prevHandle - 1;

 5696   	   		lfno = (LFN_ENTRY *)Cache_File_Entry (fo, &prevHandle, FALSE);

 5697   	   	}

 5698   

 5699   	   	FileObjectCopy (fo, &cwdTemp);
 5700   
 5701   		// Find the length of LFN file
 5702   		fo->utf16LFNlength = i;

 5703   	}
 5704   	#endif
 5705   
 5706   	// Erase the file
 5707       if( FILEerase(fo, &fo->entry, TRUE) == CE_GOOD )
 5708           return 0;
 5709       else
 5710       {
 5711           FSerrno = CE_ERASE_FAIL;
 5712           return -1;
 5713       }
 5714   }
 5715   
 5716   /*********************************************************************
 5717     Function:
 5718       int wFSremove (const unsigned short int * fileName)
 5719     Summary:
 5720       Delete a UTF16 file
 5721     Conditions:
 5722       File not opened, file exists
 5723     Input:
 5724       fileName -  Name of the file to erase
 5725     Return Values:
 5726       0 -   File removed
 5727       EOF - File was not removed
 5728     Side Effects:
 5729       The FSerrno variable will be changed.
 5730     Description:
 5731       The wFSremove function will attempt to find the specified UTF16 file
 5732       name with the FILEfind function. If the file is found, it will be erased
 5733       using the FILEerase function.
 5734     Remarks:
 5735       None
 5736     **********************************************************************/
 5737   #ifdef SUPPORT_LFN
 5738   int wFSremove (const unsigned short int * fileName)
 5739   {
 5740   	int result;
 5741   	utfModeFileName = TRUE;
 5742   	result = FSremove ((const char *)fileName);
 5743   	utfModeFileName = FALSE;
 5744   	return result;
 5745   }
 5746   #endif
 5747   
 5748   #endif
 5749   
 5750   /*********************************************************
 5751     Function:
 5752       void FSrewind (FSFILE * fo)
 5753     Summary:
 5754       Set the current position in a file to the beginning
 5755     Conditions:
 5756       File opened.
 5757     Input:
 5758       fo -  Pointer to file structure
 5759     Return Values:
 5760       None
 5761     Side Effects:
 5762       None.
 5763     Description:
 5764       The FSrewind funciton will reset the position of the
 5765       specified file to the beginning of the file.  This
 5766       functionality is faster than using FSfseek to reset
 5767       the position in the file.
 5768     Remarks:
 5769       None.
 5770     *********************************************************/
 5771   
 5772   void FSrewind (FSFILE * fo)
 5773   {
 5774   #ifdef ALLOW_WRITES
 5775       if (gNeedDataWrite)
 5776           flushData();
 5777   #endif
 5778       fo->seek = 0;
 5779       fo->pos = 0;
 5780       fo->sec = 0;
 5781       fo->ccls = fo->cluster;
 5782       gBufferOwner = NULL;
 5783       return;
 5784   }
 5785   
 5786   /**************************************************************************
 5787     Function:
 5788       int FSerror (void)
 5789     Summary:
 5790       Return an error code for the last function call
 5791     Conditions:
 5792       The return value depends on the last function called.
 5793     Input:
 5794       None
 5795     Side Effects:
 5796       None.
 5797     Return Values:
 5798       FSInit       -
 5799                    - CE_GOOD –                  No Error
 5800                    - CE_INIT_ERROR –            The physical media could not be initialized
 5801                    - CE_BAD_SECTOR_READ –       The MBR or the boot sector could not be
 5802                                                 read correctly
 5803                    - CE_BAD_PARITION –          The MBR signature code was incorrect.
 5804                    - CE_NOT_FORMATTED –         The boot sector signature code was incorrect or
 5805                                                 indicates an invalid number of bytes per sector.
 5806                    - CE_UNSUPPORTED_SECTOR_SIZE - The number of bytes per sector is unsupported
 5807                    - CE_CARDFAT32 –             The physical media is FAT32 type (only an error
 5808                                                 when FAT32 support is disabled).
 5809                    - CE_UNSUPPORTED_FS –        The device is formatted with an unsupported file
 5810                                                 system (not FAT12 or 16).
 5811       FSfopen      -
 5812                    - CE_GOOD –                  No Error
 5813                    - CE_NOT_INIT –              The device has not been initialized.
 5814                    - CE_TOO_MANY_FILES_OPEN –   The function could not allocate any
 5815                                                 additional file information to the array
 5816                                                 of FSFILE structures or the heap.
 5817                    - CE_INVALID_FILENAME –      The file name argument was invalid.
 5818                    - CE_INVALID_ARGUMENT –      The user attempted to open a directory in a
 5819                                                 write mode or specified an invalid mode argument.
 5820                    - CE_FILE_NOT_FOUND –        The specified file (which was to be opened in read
 5821                                                 mode) does not exist on the device.
 5822                    - CE_BADCACHEREAD –          A read from the device failed.
 5823                    - CE_ERASE_FAIL –            The existing file could not be erased (when opening
 5824                                                 a file in WRITE mode).
 5825                    - CE_DIR_FULL –              The directory is full.
 5826                    - CE_DISK_FULL–              The data memory section is full.
 5827                    - CE_WRITE_ERROR –           A write to the device failed.
 5828                    - CE_SEEK_ERROR –            The current position in the file could not be set to
 5829                                                 the end (when the file was opened in APPEND mode).
 5830       FSfclose     -
 5831                    - CE_GOOD –                  No Error
 5832                    - CE_WRITE_ERROR –           The existing data in the data buffer or the new file
 5833                                                 entry information could not be written to the device.
 5834                    - CE_BADCACHEREAD –          The file entry information could not be cached
 5835       FSfread      -
 5836                    - CE_GOOD –                  No Error
 5837                    - CE_WRITEONLY –             The file was opened in a write-only mode.
 5838                    - CE_WRITE_ERROR –           The existing data in the data buffer could not be
 5839                                                 written to the device.
 5840                    - CE_BAD_SECTOR_READ –       The data sector could not be read.
 5841                    - CE_EOF –                   The end of the file was reached.
 5842                    - CE_COULD_NOT_GET_CLUSTER – Additional clusters in the file could not be loaded.
 5843       FSfwrite     -
 5844                    - CE_GOOD –                  No Error
 5845                    - CE_READONLY –              The file was opened in a read-only mode.
 5846                    - CE_WRITE_PROTECTED –       The device write-protect check function indicated
 5847                                                 that the device has been write-protected.
 5848                    - CE_WRITE_ERROR –           There was an error writing data to the device.
 5849                    - CE_BADCACHEREAD –          The data sector to be modified could not be read from
 5850                                                 the device.
 5851                    - CE_DISK_FULL –             All data clusters on the device are in use.
 5852       FSfseek      -
 5853                    - CE_GOOD –                  No Error
 5854                    - CE_WRITE_ERROR –           The existing data in the data buffer could not be
 5855                                                 written to the device.
 5856                    - CE_INVALID_ARGUMENT –      The specified offset exceeds the size of the file.
 5857                    - CE_BADCACHEREAD –          The sector that contains the new current position
 5858                                                 could not be loaded.
 5859                    - CE_COULD_NOT_GET_CLUSTER – Additional clusters in the file could not be
 5860                                                 loaded/allocated.
 5861       FSftell      -
 5862                    - CE_GOOD –                  No Error
 5863       FSattrib     -
 5864                    - CE_GOOD –                  No Error
 5865                    - CE_INVALID_ARGUMENT –      The attribute argument was invalid.
 5866                    - CE_BADCACHEREAD –          The existing file entry information could not be
 5867                                                 loaded.
 5868                    - CE_WRITE_ERROR –           The file entry information could not be written to
 5869                                                 the device.
 5870       FSrename     -
 5871                    - CE_GOOD –                  No Error
 5872                    - CE_FILENOTOPENED –         A null file pointer was passed into the function.
 5873                    - CE_INVALID_FILENAME –      The file name passed into the function was invalid.
 5874                    - CE_BADCACHEREAD –          A read from the device failed.
 5875                    - CE_FILENAME_EXISTS –       A file with the specified name already exists.
 5876                    - CE_WRITE_ERROR –           The new file entry data could not be written to the
 5877                                                 device.
 5878       FSfeof       -
 5879                    - CE_GOOD –                  No Error
 5880       FSformat     -
 5881                    - CE_GOOD –                  No Error
 5882                    - CE_INIT_ERROR –            The device could not be initialized.
 5883                    - CE_BADCACHEREAD –          The master boot record or boot sector could not be
 5884                                                 loaded successfully.
 5885                    - CE_INVALID_ARGUMENT –      The user selected to create their own boot sector on
 5886                                                 a device that has no master boot record, or the mode
 5887                                                 argument was invalid.
 5888                    - CE_WRITE_ERROR –           The updated MBR/Boot sector could not be written to
 5889                                                 the device.
 5890                    - CE_BAD_PARTITION –         The calculated number of sectors per clusters was
 5891                                                 invalid.
 5892                    - CE_NONSUPPORTED_SIZE –     The card has too many sectors to be formatted as
 5893                                                 FAT12 or FAT16.
 5894       FSremove     -
 5895                    - CE_GOOD –                  No Error
 5896                    - CE_WRITE_PROTECTED –       The device write-protect check function indicated
 5897                                                 that the device has been write-protected.
 5898                    - CE_INVALID_FILENAME –      The specified filename was invalid.
 5899                    - CE_FILE_NOT_FOUND –        The specified file could not be found.
 5900                    - CE_ERASE_FAIL –            The file could not be erased.
 5901       FSchdir      -
 5902                    - CE_GOOD –                  No Error
 5903                    - CE_INVALID_ARGUMENT –      The path string was mis-formed or the user tried to
 5904                                                 change to a non-directory file.
 5905                    - CE_BADCACHEREAD –          A directory entry could not be cached.
 5906                    - CE_DIR_NOT_FOUND –         Could not find a directory in the path.
 5907       FSgetcwd     -
 5908                    - CE_GOOD –                  No Error
 5909                    - CE_INVALID_ARGUMENT –      The user passed a 0-length buffer into the function.
 5910                    - CE_BADCACHEREAD –          A directory entry could not be cached.
 5911                    - CE_BAD_SECTOR_READ –       The function could not determine a previous directory
 5912                                                 of the current working directory.
 5913       FSmkdir      -
 5914                    - CE_GOOD –                  No Error
 5915                    - CE_WRITE_PROTECTED –       The device write-protect check function indicated
 5916                                                 that the device has been write-protected.
 5917                    - CE_INVALID_ARGUMENT –      The path string was mis-formed.
 5918                    - CE_BADCACHEREAD –          Could not successfully change to a recently created
 5919                                                 directory to store its dir entry information, or
 5920                                                 could not cache directory entry information.
 5921                    - CE_INVALID_FILENAME –      One or more of the directory names has an invalid
 5922                                                 format.
 5923                    - CE_WRITE_ERROR –           The existing data in the data buffer could not be
 5924                                                 written to the device or the dot/dotdot entries could
 5925                                                 not be written to a newly created directory.
 5926                    - CE_DIR_FULL –              There are no available dir entries in the CWD.
 5927                    - CE_DISK_FULL –             There are no available clusters in the data region of
 5928                                                 the device.
 5929       FSrmdir      -
 5930                    - CE_GOOD –                  No Error
 5931                    - CE_DIR_NOT_FOUND –         The directory specified could not be found or the
 5932                                                 function could not change to a subdirectory within
 5933                                                 the directory to be deleted (when recursive delete is
 5934                                                 enabled).
 5935                    - CE_INVALID_ARGUMENT –      The user tried to remove the CWD or root directory.
 5936                    - CE_BADCACHEREAD –          A directory entry could not be cached.
 5937                    - CE_DIR_NOT_EMPTY –         The directory to be deleted was not empty and
 5938                                                 recursive subdirectory removal was disabled.
 5939                    - CE_ERASE_FAIL –            The directory or one of the directories or files
 5940                                                 within it could not be deleted.
 5941                    - CE_BAD_SECTOR_READ –       The function could not determine a previous directory
 5942                                                 of the CWD.
 5943       SetClockVars -
 5944                    - CE_GOOD –                  No Error
 5945                    - CE_INVALID_ARGUMENT –      The time values passed into the function were
 5946                                                 invalid.
 5947       FindFirst    -
 5948                    - CE_GOOD –                  No Error
 5949                    - CE_INVALID_FILENAME –      The specified filename was invalid.
 5950                    - CE_FILE_NOT_FOUND –        No file matching the specified criteria was found.
 5951                    - CE_BADCACHEREAD –          The file information for the file that was found
 5952                                                 could not be cached.
 5953       FindNext     -
 5954                    - CE_GOOD –                  No Error
 5955                    - CE_NOT_INIT –              The SearchRec object was not initialized by a call to
 5956                                                 FindFirst.
 5957                    - CE_INVALID_ARGUMENT –      The SearchRec object was initialized in a different
 5958                                                 directory from the CWD.
 5959                    - CE_INVALID_FILENAME –      The filename is invalid.
 5960                    - CE_FILE_NOT_FOUND –        No file matching the specified criteria was found.
 5961       FSfprintf    -
 5962                    - CE_GOOD –                  No Error
 5963                    - CE_WRITE_ERROR –           Characters could not be written to the file.
 5964     Description:
 5965       The FSerror function will return the FSerrno variable.  This global
 5966       variable will have been set to an error value during the last call of a
 5967       library function.
 5968     Remarks:
 5969       None
 5970     **************************************************************************/
 5971   
 5972   int FSerror (void)
 5973   {
 5974       return FSerrno;
 5975   }
 5976   
 5977   
 5978   /**************************************************************
 5979     Function:
 5980       void FileObjectCopy(FILEOBJ foDest,FILEOBJ foSource)
 5981     Summary:
 5982       Copy a file object
 5983     Conditions:
 5984       This function should not be called by the user.
 5985     Input:
 5986       foDest -    The destination
 5987       foSource -  the source
 5988     Return:
 5989       None
 5990     Side Effects:
 5991       None
 5992     Description:
 5993       The FileObjectCopy function will make an exacy copy of
 5994       a specified FSFILE object.
 5995     Remarks:
 5996       None
 5997     **************************************************************/
 5998   
 5999   void FileObjectCopy(FILEOBJ foDest,FILEOBJ foSource)
 6000   {
 6001       int size;
 6002       BYTE *dest;
 6003       BYTE *source;
 6004       int Index;
 6005   
 6006       dest = (BYTE *)foDest;
 6007       source = (BYTE *)foSource;
 6008   
 6009       size = sizeof(FSFILE);
 6010   
 6011       for(Index=0;Index< size; Index++)
 6012       {
 6013           dest[Index] = source[Index];
 6014       }
 6015   }
 6016   
 6017   /*************************************************************************
 6018     Function:
 6019       CETYPE FILECreateHeadCluster( FILEOBJ fo, DWORD *cluster)
 6020     Summary:
 6021       Create the first cluster of a file
 6022     Conditions:
 6023       This function should not be called by the user.
 6024     Input:
 6025       fo -       Pointer to file structure
 6026       cluster -  Cluster location
 6027     Return Values:
 6028       CE_GOOD - File closed successfully
 6029       CE_WRITE_ERROR - Could not write to the sector
 6030       CE_DISK_FULL - All clusters in partition are taken
 6031     Side Effects:
 6032       None
 6033     Description:
 6034       The FILECreateHeadCluster function will create the first cluster
 6035       of a file.  First, it will find an empty cluster with the
 6036       FATfindEmptyCluster function and mark it as the last cluster in the
 6037       file.  It will then erase the cluster using the EraseCluster function.
 6038     Remarks:
 6039       None.
 6040     *************************************************************************/
 6041   
 6042   #ifdef ALLOW_WRITES
 6043   CETYPE FILECreateHeadCluster( FILEOBJ fo, DWORD *cluster)
 6044   {
 6045       DISK *      disk;
 6046       CETYPE        error = CE_GOOD;
 6047   
 6048       disk = fo->dsk;
 6049   
 6050       // find the next empty cluster
 6051       *cluster = FATfindEmptyCluster(fo);
 6052   
 6053       if(*cluster == 0)  // "0" is just an indication as Disk full in the fn "FATfindEmptyCluster()"
 6054       {
 6055           error = CE_DISK_FULL;
 6056       }
 6057       else
 6058       {
 6059           // mark the cluster as taken, and last in chain
 6060           if(disk->type == FAT12)
 6061           {
 6062               if(WriteFAT( disk, *cluster, LAST_CLUSTER_FAT12, FALSE) == CLUSTER_FAIL_FAT16)
 6063               {
 6064                   error = CE_WRITE_ERROR;
 6065               }
 6066           }
 6067           else if(disk->type == FAT16)
 6068           {
 6069               if(WriteFAT( disk, *cluster, LAST_CLUSTER_FAT16, FALSE) == CLUSTER_FAIL_FAT16)
 6070               {
 6071                   error = CE_WRITE_ERROR;
 6072               }
 6073           }
 6074   
 6075    #ifdef SUPPORT_FAT32 // If FAT32 supported.
 6076           else
 6077           {
 6078               if(WriteFAT( disk, *cluster, LAST_CLUSTER_FAT32, FALSE) == CLUSTER_FAIL_FAT32)
 6079               {
 6080                   error = CE_WRITE_ERROR;
 6081               }
 6082           }
 6083   #endif
 6084   
 6085           // lets erase this cluster
 6086           if(error == CE_GOOD)
 6087           {
 6088               error = EraseCluster(disk,*cluster);
 6089           }
 6090       }
 6091   
 6092       return(error);
 6093   } // allocate head cluster
 6094   #endif
 6095   
 6096   /*************************************************************************
 6097     Function:
 6098       BYTE EraseCluster(DISK *disk, DWORD cluster)
 6099     Summary:
 6100       Erase a cluster
 6101     Conditions:
 6102       This function should not be called by the user.
 6103     Input:
 6104       dsk -      Disk structure
 6105       cluster -  Cluster to be erased
 6106     Return Values:
 6107       CE_GOOD - File closed successfully
 6108       CE_WRITE_ERROR - Could not write to the sector
 6109     Side Effects:
 6110       None
 6111     Description:
 6112       The EraseCluster function will write a 0 value into every byte of
 6113       the specified cluster.
 6114     Remarks:
 6115       None.
 6116     *************************************************************************/
 6117   
 6118   #ifdef ALLOW_WRITES
 6119   BYTE EraseCluster(DISK *disk, DWORD cluster)
 6120   {
 6121       BYTE index;
 6122       DWORD SectorAddress;
 6123       BYTE error = CE_GOOD;
 6124   
 6125       SectorAddress = Cluster2Sector(disk,cluster);
 6126       if (gNeedDataWrite)
 6127           if (flushData())
 6128               return CE_WRITE_ERROR;
 6129   
 6130       gBufferOwner = NULL;
 6131   
 6132       if (gBufferZeroed == FALSE)
 6133       {
 6134           // clear out the memory first
 6135           memset(disk->buffer, 0x00, disk->sectorSize);
 6136           gBufferZeroed = TRUE;
 6137       }
 6138   
 6139       // Now clear them out
 6140       for(index = 0; (index < disk->SecPerClus) && (error == CE_GOOD); index++)
 6141       {
 6142           if (MDD_SectorWrite( SectorAddress++, disk->buffer, FALSE) != TRUE)
 6143               error = CE_WRITE_ERROR;
 6144       }
 6145   
 6146       return(error);
 6147   }
 6148   #endif
 6149   
 6150   
 6151   #if defined (__C30__) || defined (__PIC32MX__)
 6152   
 6153   /***************************************************
 6154     Function:
 6155       BYTE ReadByte(BYTE * pBuffer, WORD index)
 6156     Summary:
 6157       Read a byte from a buffer
 6158     Conditions:
 6159       This function should not be called by the user.
 6160     Input:
 6161       pBuffer -  pointer to a buffer to read from
 6162       index -    index in the buffer to read to
 6163     Return:
 6164       BYTE - the byte read
 6165     Side Effects:
 6166       None
 6167     Description:
 6168       Reads a byte from a buffer
 6169     Remarks:
 6170       None.
 6171     ***************************************************/
 6172   
 6173   BYTE ReadByte( BYTE* pBuffer, WORD index )
 6174   {
 6175       return( pBuffer[index] );
 6176   }
 6177   
 6178   
 6179   /***************************************************
 6180     Function:
 6181       BYTE ReadWord(BYTE * pBuffer, WORD index)
 6182     Summary:
 6183       Read a 16-bit word from a buffer
 6184     Conditions:
 6185       This function should not be called by the user.
 6186     Input:
 6187       pBuffer -  pointer to a buffer to read from
 6188       index -    index in the buffer to read to
 6189     Return:
 6190       WORD - the word read
 6191     Side Effects:
 6192       None
 6193     Description:
 6194       Reads a 16-bit word from a buffer
 6195     Remarks:
 6196       None.
 6197     ***************************************************/
 6198   
 6199   WORD ReadWord( BYTE* pBuffer, WORD index )
 6200   {
 6201       BYTE loByte, hiByte;
 6202       WORD res;
 6203   
 6204       loByte = pBuffer[index];
 6205       hiByte = pBuffer[index+1];
 6206       res = hiByte;
 6207       res *= 0x100;
 6208       res |= loByte;
 6209       return( res );
 6210   }
 6211   
 6212   
 6213   /****************************************************
 6214     Function:
 6215       BYTE ReadDWord(BYTE * pBuffer, WORD index)
 6216     Summary:
 6217       Read a 32-bit double word from a buffer
 6218     Conditions:
 6219       This function should not be called by the user.
 6220     Input:
 6221       pBuffer -  pointer to a buffer to read from
 6222       index -    index in the buffer to read to
 6223     Return:
 6224       DWORD - the double word read
 6225     Side Effects:
 6226       None
 6227     Description:
 6228       Reads a 32-bit double word from a buffer
 6229     Remarks:
 6230       None.
 6231     ****************************************************/
 6232   
 6233   DWORD ReadDWord( BYTE* pBuffer, WORD index )
 6234   {
 6235       WORD loWord, hiWord;
 6236       DWORD result;
 6237   
 6238       loWord = ReadWord( pBuffer, index );
 6239       hiWord = ReadWord( pBuffer, index+2 );
 6240   
 6241       result = hiWord;
 6242       result *= 0x10000;
 6243       result |= loWord;
 6244       return result;
 6245   }
 6246   
 6247   #endif
 6248   
 6249   
 6250   
 6251   /****************************************************
 6252     Function:
 6253       DWORD Cluster2Sector(DISK * dsk, DWORD cluster)
 6254     Summary:
 6255       Convert a cluster number to the corresponding sector
 6256     Conditions:
 6257       This function should not be called by the user.
 6258     Input:
 6259       disk -     Disk structure
 6260       cluster -  Cluster to be converted
 6261     Return:
 6262       sector - Sector that corresponds to given cluster
 6263     Side Effects:
 6264       None
 6265     Description:
 6266       The Cluster2Sector function will calculate the
 6267       sector number that corresponds to the first sector
 6268       of the cluster whose value was passed into the
 6269       function.
 6270     Remarks:
 6271       None.
 6272     ****************************************************/
 6273   
 6274   DWORD Cluster2Sector(DISK * dsk, DWORD cluster)
 6275   {
 6276       DWORD sector;
 6277   
 6278       /* Rt: Settings based on FAT type */
 6279       switch (dsk->type)
 6280       {
 6281   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 6282           case FAT32:
 6283               /* In FAT32, there is no separate ROOT region. It is as well stored in DATA region */
 6284               sector = (((DWORD)cluster-2) * dsk->SecPerClus) + dsk->data;
 6285               break;
 6286   #endif
 6287           case FAT12:
 6288           case FAT16:
 6289           default:
 6290               // The root dir takes up cluster 0 and 1
 6291               if((cluster == 0) || (cluster == 1))
 6292                   sector = dsk->root + cluster;
 6293               else
 6294                   sector = (((DWORD)cluster-2) * dsk->SecPerClus) + dsk->data;
 6295               break;
 6296       }
 6297   
 6298       return(sector);
 6299   
 6300   }
 6301   
 6302   
 6303   /***************************************************************************
 6304     Function:
 6305       int FSattrib (FSFILE * file, unsigned char attributes)
 6306     Summary:
 6307       Change the attributes of a file
 6308     Conditions:
 6309       File opened
 6310     Input:
 6311       file -        Pointer to file structure
 6312       attributes -  The attributes to set for the file
 6313                  -  Attribute -      Value - Indications
 6314                  -  ATTR_READ_ONLY - 0x01  - The read-only attribute
 6315                  -  ATTR_HIDDEN -    0x02  - The hidden attribute
 6316                  -  ATTR_SYSTEM -    0x04  - The system attribute
 6317                  -  ATTR_ARCHIVE -   0x20  - The archive attribute
 6318     Return Values:
 6319       0 -  Attribute change was successful
 6320       -1 - Attribute change was unsuccessful
 6321     Side Effects:
 6322       The FSerrno variable will be changed.
 6323     Description:
 6324       The FSattrib funciton will set the attributes of the specified file
 6325       to the attributes passed in by the user.  This function will load the
 6326       file entry, replace the attributes with the ones specified, and write
 6327       the attributes back.  If the specified file is a directory, the
 6328       directory attribute will be preserved.
 6329     Remarks:
 6330       None
 6331     ***************************************************************************/
 6332   
 6333   #ifdef ALLOW_WRITES
 6334   int FSattrib (FSFILE * file, unsigned char attributes)
 6335   {
 6336       WORD fHandle;
 6337       DIRENTRY dir;
 6338   
 6339       FSerrno = CE_GOOD;
 6340   
 6341       // Check for valid attributes
 6342       if ((attributes & ~0x27) != 0)
 6343       {
 6344           FSerrno = CE_INVALID_ARGUMENT;
 6345           return -1;
 6346       }
 6347   
 6348       fHandle = file->entry;
 6349   
 6350       file->dirccls = file->dirclus;
 6351   
 6352       // Get the file entry
 6353       dir = LoadDirAttrib(file, &fHandle);
 6354   
 6355       if (dir == NULL)
 6356       {
 6357           FSerrno = CE_BADCACHEREAD;
 6358           return -1;
 6359       }
 6360   
 6361       // Ensure that we aren't trying to change the
 6362       // attributes of a volume entry
 6363       if (dir->DIR_Attr & ATTR_VOLUME)
 6364       {
 6365           FSerrno = CE_INVALID_ARGUMENT;
 6366           return -1;
 6367       }
 6368   
 6369       // Don't remove the directory attribute from DIR files
 6370       if (file->attributes & ATTR_DIRECTORY)
 6371           file->attributes = attributes | ATTR_DIRECTORY;
 6372       else
 6373           file->attributes = attributes;
 6374   
 6375       // just write the last entry in
 6376       if(!Write_File_Entry(file,&fHandle))
 6377       {
 6378           FSerrno = CE_WRITE_ERROR;
 6379           return -1;
 6380       }
 6381   
 6382       return 0;
 6383   }
 6384   #endif
 6385   
 6386   
 6387   /*********************************************************************************
 6388     Function:
 6389       size_t FSfwrite(const void *data_to_write, size_t size, size_t n, FSFILE *stream)
 6390     Summary:
 6391       Write data to a file
 6392     Conditions:
 6393       File opened in WRITE, APPEND, WRITE+, APPEND+, READ+ mode
 6394     Input:
 6395       data_to_write -     Pointer to source buffer
 6396       size -              Size of units in bytes
 6397       n -                 Number of units to transfer
 6398       stream -  Pointer to file structure
 6399     Return:
 6400       size_t - number of units written
 6401     Side Effects:
 6402       The FSerrno variable will be changed.
 6403     Description:
 6404       The FSfwrite function will write data to a file.  First, the sector that
 6405       corresponds to the current position in the file will be loaded (if it hasn't
 6406       already been cached in the global data buffer).  Data will then be written to
 6407       the device from the specified buffer until the specified amount has been written.
 6408       If the end of a cluster is reached, the next cluster will be loaded, unless
 6409       the end-of-file flag for the specified file has been set.  If it has, a new
 6410       cluster will be allocated to the file.  Finally, the new position and filesize
 6411       will be stored in the FSFILE object.  The parameters 'size' and 'n' indicate how
 6412       much data to write.  'Size' refers to the size of one object to write (in bytes),
 6413       and 'n' will refer to the number of these objects to write.  The value returned
 6414       will be equal  to 'n' unless an error occured.
 6415     Remarks:
 6416       None.
 6417     *********************************************************************************/
 6418   
 6419   #ifdef ALLOW_WRITES
 6420   size_t FSfwrite(const void *data_to_write, size_t size, size_t n, FSFILE *stream)
 6421   {
 6422       DWORD       count = size * n;
 6423       BYTE   *    src = (BYTE *) data_to_write;
 6424       DISK   *    dsk;                 // pointer to disk structure
 6425       CETYPE      error = CE_GOOD;
 6426       WORD        pos;
 6427       DWORD       l;                     // absolute lba of sector to load
 6428       DWORD       seek, filesize;
 6429       WORD        writeCount = 0;
 6430   
 6431       // see if the file was opened in a write mode
 6432       if(!(stream->flags.write))
 6433       {
 6434           FSerrno = CE_READONLY;
 6435           error = CE_WRITE_ERROR;
 6436           return 0;
 6437       }
 6438   
 6439       if (count == 0)
 6440           return 0;
 6441   
 6442       if (MDD_WriteProtectState())
 6443       {
 6444           FSerrno = CE_WRITE_PROTECTED;
 6445           error = CE_WRITE_PROTECTED;
 6446           return 0;
 6447       }
 6448   
 6449       gBufferZeroed = FALSE;
 6450       dsk = stream->dsk;
 6451       // get the stated position
 6452       pos = stream->pos;
 6453       seek = stream->seek;
 6454       l = Cluster2Sector(dsk,stream->ccls);
 6455       l += (WORD)stream->sec;      // add the sector number to it
 6456   
 6457       // Check if the current stream was the last one to use the
 6458       // buffer. If not, check if we need to write data from the
 6459       // old stream
 6460       if (gBufferOwner != stream)
 6461       {
 6462           if (gNeedDataWrite)
 6463           {
 6464               if (flushData())
 6465               {
 6466                   FSerrno = CE_WRITE_ERROR;
 6467                   return 0;
 6468               }
 6469           }
 6470           gBufferOwner = stream;
 6471       }
 6472       if (gLastDataSectorRead != l)
 6473       {
 6474           if (gNeedDataWrite)
 6475           {
 6476               if (flushData())
 6477               {
 6478                   FSerrno = CE_WRITE_ERROR;
 6479                   return 0;
 6480               }
 6481           }
 6482   
 6483           gBufferZeroed = FALSE;
 6484           if(!MDD_SectorRead( l, dsk->buffer) )
 6485           {
 6486               FSerrno = CE_BADCACHEREAD;
 6487               error = CE_BAD_SECTOR_READ;
 6488           }
 6489           gLastDataSectorRead = l;
 6490       }
 6491       // exit loop if EOF reached
 6492       filesize = stream->size;
 6493   
 6494       // Loop while writing bytes
 6495       while ((error == CE_GOOD) && (count > 0))
 6496       {
 6497           if( seek == filesize )
 6498               stream->flags.FileWriteEOF = TRUE;
 6499   
 6500           // load a new sector if necessary, multiples of sector
 6501           if (pos == dsk->sectorSize)
 6502           {
 6503               BYTE needRead = TRUE;
 6504   
 6505               if (gNeedDataWrite)
 6506                   if (flushData())
 6507                   {
 6508                       FSerrno = CE_WRITE_ERROR;
 6509                       return 0;
 6510                   }
 6511   
 6512               // reset position
 6513               pos = 0;
 6514   
 6515               // point to the next sector
 6516               stream->sec++;
 6517   
 6518               // get a new cluster if necessary
 6519               if (stream->sec == dsk->SecPerClus)
 6520               {
 6521                   stream->sec = 0;
 6522   
 6523                   if(stream->flags.FileWriteEOF)
 6524                   {
 6525                       error = FILEallocate_new_cluster(stream, 0);    // add new cluster to the file
 6526                       needRead = FALSE;
 6527                   }
 6528                   else
 6529                       error = FILEget_next_cluster( stream, 1);
 6530               }
 6531   
 6532               if (error == CE_DISK_FULL)
 6533               {
 6534                   FSerrno = CE_DISK_FULL;
 6535                   return 0;
 6536               }
 6537   
 6538               if(error == CE_GOOD)
 6539               {
 6540                   l = Cluster2Sector(dsk,stream->ccls);
 6541                   l += (WORD)stream->sec;      // add the sector number to it
 6542                   gBufferOwner = stream;
 6543                   // If we just allocated a new cluster, then the cluster will
 6544                   // contain garbage data, so it doesn't matter what we write to it
 6545                   // Whatever is in the buffer will work fine
 6546                   if (needRead)
 6547                   {
 6548                       if( !MDD_SectorRead( l, dsk->buffer) )
 6549                       {
 6550                           FSerrno = CE_BADCACHEREAD;
 6551                           error = CE_BAD_SECTOR_READ;
 6552                           gLastDataSectorRead = 0xFFFFFFFF;
 6553                           return 0;
 6554                       }
 6555                       else
 6556                       {
 6557                           gLastDataSectorRead = l;
 6558                       }
 6559                   }
 6560                   else
 6561                       gLastDataSectorRead = l;
 6562               }
 6563           } //  load new sector
 6564   
 6565           if(error == CE_GOOD)
 6566           {
 6567               // Write one byte at a time
 6568               RAMwrite(dsk->buffer, pos++, *(char *)src);
 6569               src = src + 1; // compiler bug
 6570               seek++;
 6571               count--;
 6572               writeCount++;
 6573               // now increment the size of the part
 6574               if(stream->flags.FileWriteEOF)
 6575                   filesize++;
 6576               gNeedDataWrite = TRUE;
 6577           }
 6578       } // while count
 6579   
 6580       // save off the positon
 6581       stream->pos = pos;
 6582   
 6583       // save off the seek
 6584       stream->seek = seek;
 6585   
 6586       // now the new size
 6587       stream->size = filesize;
 6588   
 6589       return(writeCount / size);
 6590   } // fwrite
 6591   #endif
 6592   
 6593   
 6594   /**********************************************************
 6595     Function:
 6596       BYTE flushData (void)
 6597     Summary:
 6598       Flush unwritten data to a file
 6599     Conditions:
 6600       File opened in a write mode, data needs to be written
 6601     Return Values:
 6602       CE_GOOD -        Data was updated successfully
 6603       CE_WRITE_ERROR - Data could not be updated
 6604     Side Effects:
 6605       None
 6606     Description:
 6607       The flushData function is called when it is necessary to
 6608       read new data into the global data buffer and the
 6609       gNeedDataWrite variable indicates that there is data
 6610       in the buffer that hasn't been written to the device.
 6611       The flushData function will write the data from the
 6612       buffer into the current cluster of the FSFILE object
 6613       that is stored in the gBufferOwner global variable.
 6614     Remarks:
 6615       None
 6616     **********************************************************/
 6617   
 6618   #ifdef ALLOW_WRITES
 6619   BYTE flushData (void)
 6620   {
 6621       DWORD l;
 6622       DISK * dsk;
 6623   
 6624       // This will either be the pointer to the last file, or the handle
 6625       FILEOBJ stream = gBufferOwner;
 6626   
 6627       dsk = stream->dsk;
 6628   
 6629       // figure out the lba
 6630       l = Cluster2Sector(dsk,stream->ccls);
 6631       l += (WORD)stream->sec;      // add the sector number to it
 6632   
 6633       if(!MDD_SectorWrite( l, dsk->buffer, FALSE))
 6634       {
 6635           return CE_WRITE_ERROR;
 6636       }
 6637   
 6638       gNeedDataWrite = FALSE;
 6639   
 6640       return CE_GOOD;
 6641   }
 6642   #endif
 6643   
 6644   /****************************************************
 6645     Function:
 6646       int FSfeof( FSFILE * stream )
 6647     Summary:
 6648       Indicate whether the current file position is at the end
 6649     Conditions:
 6650       File is open in a read mode
 6651     Input:
 6652       stream -  Pointer to the target file
 6653     Return Values:
 6654       Non-Zero - EOF reached
 6655       0 - Not at end of File
 6656     Side Effects:
 6657       The FSerrno variable will be changed.
 6658     Description:
 6659       The FSfeof function will indicate that the end-of-
 6660       file has been reached for the specified file by
 6661       comparing the absolute location in the file to the
 6662       size of the file.
 6663     Remarks:
 6664       None.
 6665     ****************************************************/
 6666   
 6667   int FSfeof( FSFILE * stream )
 6668   {
 6669       FSerrno = CE_GOOD;
 6670       return( stream->seek == stream->size );
 6671   }
 6672   
 6673   
 6674   /**************************************************************************
 6675     Function:
 6676       size_t FSfread(void *ptr, size_t size, size_t n, FSFILE *stream)
 6677     Summary:
 6678       Read data from a file
 6679     Conditions:
 6680       File is opened in a read mode
 6681     Input:
 6682       ptr -     Destination buffer for read bytes
 6683       size -    Size of units in bytes
 6684       n -       Number of units to be read
 6685       stream -  File to be read from
 6686     Return:
 6687       size_t - number of units read
 6688     Side Effects:
 6689       The FSerrno variable will be changed.
 6690     Description:
 6691       The FSfread function will read data from the specified file.  First,
 6692       the appropriate sector of the file is loaded.  Then, data is read into
 6693       the specified buffer until the specified number of bytes have been read.
 6694       When a cluster boundary is reached, a new cluster will be loaded.  The
 6695       parameters 'size' and 'n' indicate how much data to read.  'Size'
 6696       refers to the size of one object to read (in bytes), and 'n' will refer
 6697       to the number of these objects to read.  The value returned will be equal
 6698       to 'n' unless an error occured or the user tried to read beyond the end
 6699       of the file.
 6700     Remarks:
 6701       None.
 6702     **************************************************************************/
 6703   
 6704   size_t FSfread (void *ptr, size_t size, size_t n, FSFILE *stream)
 6705   {
 6706       DWORD   len = size * n;
 6707       BYTE    *pointer = (BYTE *) ptr;
 6708       DISK    *dsk;               // Disk structure
 6709       DWORD    seek, sec_sel;
 6710       WORD    pos;       //position within sector
 6711       CETYPE   error = CE_GOOD;
 6712       WORD    readCount = 0;
 6713   
 6714       FSerrno = CE_GOOD;
 6715   
 6716       dsk    = (DISK *)stream->dsk;
 6717       pos    = stream->pos;
 6718       seek    = stream->seek;
 6719   
 6720       if( !stream->flags.read )
 6721       {
 6722           FSerrno = CE_WRITEONLY;
 6723           return 0;   // CE_WRITEONLY
 6724       }
 6725   
 6726   #ifdef ALLOW_WRITES
 6727       if (gNeedDataWrite)
 6728           if (flushData() != CE_GOOD)
 6729           {
 6730               FSerrno = CE_WRITE_ERROR;
 6731               return 0;
 6732           }
 6733   #endif
 6734   
 6735       // if it not my buffer, then get it from the disk.
 6736       if( (gBufferOwner != stream) && (pos != dsk->sectorSize))
 6737       {
 6738           gBufferOwner = stream;
 6739           sec_sel = Cluster2Sector(dsk,stream->ccls);
 6740           sec_sel += (WORD)stream->sec;      // add the sector number to it
 6741   
 6742           gBufferZeroed = FALSE;
 6743           if( !MDD_SectorRead( sec_sel, dsk->buffer) )
 6744           {
 6745               FSerrno = CE_BAD_SECTOR_READ;
 6746               error = CE_BAD_SECTOR_READ;
 6747               return 0;
 6748           }
 6749           gLastDataSectorRead = sec_sel;
 6750       }
 6751   
 6752       //loop reading (count) bytes
 6753       while( len )
 6754       {
 6755           if( seek == stream->size )
 6756           {
 6757               FSerrno = CE_EOF;
 6758               error = CE_EOF;
 6759               break;
 6760           }
 6761   
 6762           // In fopen, pos is init to 0 and the sect is loaded
 6763           if( pos == dsk->sectorSize )
 6764           {
 6765               // reset position
 6766               pos = 0;
 6767               // point to the next sector
 6768               stream->sec++;
 6769   
 6770               // get a new cluster if necessary
 6771               if( stream->sec == dsk->SecPerClus )
 6772               {
 6773                   stream->sec = 0;
 6774                   if( (error = FILEget_next_cluster( stream, 1)) != CE_GOOD )
 6775                   {
 6776                       FSerrno = CE_COULD_NOT_GET_CLUSTER;
 6777                       break;
 6778                   }
 6779               }
 6780   
 6781               sec_sel = Cluster2Sector(dsk,stream->ccls);
 6782               sec_sel += (WORD)stream->sec;      // add the sector number to it
 6783   
 6784   
 6785               gBufferOwner = stream;
 6786               gBufferZeroed = FALSE;
 6787               if( !MDD_SectorRead( sec_sel, dsk->buffer) )
 6788               {
 6789                   FSerrno = CE_BAD_SECTOR_READ;
 6790                   error = CE_BAD_SECTOR_READ;
 6791                   break;
 6792               }
 6793               gLastDataSectorRead = sec_sel;
 6794           }
 6795   
 6796           // copy one byte at a time
 6797           *pointer = RAMread( dsk->buffer, pos++ );
 6798           pointer++;
 6799           seek++;
 6800           readCount++;
 6801           len--;
 6802       }
 6803   
 6804       // save off the positon
 6805       stream->pos = pos;
 6806       // save off the seek
 6807       stream->seek = seek;
 6808   
 6809       return(readCount / size);
 6810   } // fread
 6811   
 6812   
 6813   /***************************************************************************
 6814     Function:
 6815       BYTE FormatFileName( const char* fileName, FILEOBJ fptr, BYTE mode )
 6816     Summary:
 6817       Format a file name into dir entry format
 6818     Conditions:
 6819       This function should not be called by the user.
 6820     Input:
 6821       fileName -  The name to be formatted
 6822       fN2 -       The location the formatted name will be stored
 6823       mode -      Non-zero if parital string search chars are allowed
 6824     Return Values:
 6825       TRUE - Name formatted successfully
 6826       FALSE - File name could not be formatted
 6827     Side Effects:
 6828       None
 6829     Description:
 6830       Format an 8.3 filename into FSFILE structure format. If filename is less
 6831       than 8 chars, then it will be padded with spaces. If the extension name is
 6832       fewer than 3 chars, then it will also be oadded with spaces. The
 6833       ValidateChars function is used to ensure the characters in the specified
 6834       filename are valid in this filesystem.
 6835     Remarks:
 6836       None.
 6837     ***************************************************************************/
 6838   BYTE FormatFileName( const char* fileName, FILEOBJ fptr, BYTE mode)
 6839   {
 6840   	char *fN2;
 6841   	FILE_DIR_NAME_TYPE fileNameType;
 6842       int temp,count1,count2,count3,count4;
 6843       BOOL supportLFN = FALSE;
 6844   	char *localFileName = NULL;
 6845   
 6846   	// go with static allocation
 6847   	#if defined(SUPPORT_LFN)
 6848   		unsigned short int tempString[256];
 6849   		BOOL	AscciIndication = TRUE;
 6850   		count1 = 256;
 6851   	#else
 6852   		unsigned short int	tempString[13];
 6853   		count1 = 12;
 6854   	#endif
 6855   
 6856   	// Check whether the length of the file name is valid
 6857   	// for LFN support as well as Non LFN support
 6858   	#ifdef SUPPORT_LFN
 6859   	if(utfModeFileName)
 6860   	{
 6861   		utf16Filename = (unsigned short int *)fileName;
 6862   		fileNameLength = 0;
 6863   		while(utf16Filename[fileNameLength])
 6864   		{
 6865   			fileNameLength++;
 6866   		}
 6867   
 6868   		if((fileNameLength > count1) || (*utf16Filename == '.') ||
 6869   			(*utf16Filename == 0))
 6870   		{
 6871   			return FALSE;
 6872   		}
 6873   		
 6874   		for (count1 = 0;count1 < fileNameLength; count1++)
 6875   		{
 6876   			tempString[count1] = utf16Filename[count1];
 6877   		}
 6878   		
 6879   		utf16Filename = tempString;
 6880   	}
 6881   	else
 6882   	#endif
 6883   	{
 6884   		fileNameLength = strlen(fileName);
 6885   
 6886   		if((fileNameLength > count1) || (*fileName == '.') || (*fileName == 0))
 6887   		{
 6888   			return FALSE;
 6889   		}
 6890   		
 6891   		asciiFilename = (char *)tempString;
 6892   		for (count1 = 0;count1 < fileNameLength; count1++)
 6893   		{
 6894   			asciiFilename[count1] = fileName[count1];
 6895   		}
 6896   	}
 6897   
 6898       // Make sure the characters are valid
 6899      	fileNameType = ValidateChars(mode);
 6900   
 6901       // If the file name doesn't follow 8P3 or LFN format, then return FALSE
 6902       if(NAME_ERROR == fileNameType)
 6903   	{
 6904   		return FALSE;
 6905   	}
 6906   
 6907   	temp = fileNameLength;
 6908   
 6909   	#if defined(SUPPORT_LFN)
 6910   		fptr->AsciiEncodingType = TRUE;
 6911   		fptr->utf16LFNlength = 0;
 6912   	#endif
 6913   
 6914   	// If LFN is supported and the file name is UTF16 type or Ascii mixed type,
 6915   	// go for LFN support rather than trying to adjust in 8P3 format
 6916   	if(NAME_8P3_ASCII_MIXED_TYPE == fileNameType)
 6917   	{
 6918   		#if defined(SUPPORT_LFN)
 6919   			supportLFN = TRUE;
 6920   		#endif
 6921   	}
 6922   
 6923   	#if defined(SUPPORT_LFN)
 6924   	if(NAME_8P3_UTF16_TYPE == fileNameType)
 6925   	{
 6926   		for (count3 = 0; count3 < temp; count3++)
 6927   		{
 6928   			if(utf16Filename[count3] > 0xFF)
 6929   			{
 6930   				fileNameType = NAME_8P3_UTF16_NONASCII_TYPE;
 6931   				supportLFN = TRUE;
 6932   				break;
 6933   			}
 6934   		}
 6935   
 6936   		if(count3 == temp)
 6937   		{
 6938   			fileNameType = NAME_8P3_UTF16_ASCII_CAPS_TYPE;
 6939   
 6940   			for (count3 = 0; count3 < temp; count3++)
 6941   			{
 6942   				if((utf16Filename[count3] >= 0x61) && (utf16Filename[count3] <= 0x7A))
 6943   				{
 6944   					fileNameType = NAME_8P3_UTF16_ASCII_MIXED_TYPE;
 6945   					supportLFN = TRUE;
 6946   					break;
 6947   				}
 6948   			}
 6949   		}
 6950   	}
 6951   	#endif
 6952   
 6953   	// If the file name follows 8P3 type
 6954   	if((NAME_LFN_TYPE != fileNameType) && (FALSE == supportLFN))
 6955   	{
 6956   		for (count3 = 0; count3 < temp; count3++)
 6957   		{
 6958   			#ifdef SUPPORT_LFN
 6959   			if(utfModeFileName)
 6960   			{
 6961   				if(((utf16Filename[count3] == '.') && ((temp - count3) > 4)) ||
 6962   					(count3 > 8))
 6963   				{
 6964   					// UTF File name extension greater then 3 characters or
 6965   				    // UTF File name greater then 8 charcters
 6966   					supportLFN = TRUE;
 6967   					break;
 6968   				}
 6969   				else if(utf16Filename[count3] == '.')
 6970   				{
 6971   					break;
 6972   				}
 6973   			}
 6974   			else
 6975   			#endif
 6976   			{
 6977   				if(((asciiFilename[count3] == '.') && ((temp - count3) > 4)) ||
 6978   					(count3 > 8))
 6979   				{
 6980   					// File extension greater then 3 characters or
 6981   					// File name greater then 8 charcters
 6982   					#if !defined(SUPPORT_LFN)
 6983   						return FALSE;
 6984   					#endif
 6985   					supportLFN = TRUE;
 6986   					break;
 6987   				}
 6988   				else if(asciiFilename[count3] == '.')
 6989   				{
 6990   					break;
 6991   				}
 6992   			}
 6993   		}
 6994   		
 6995   		// If LFN not supported try to adjust in 8P3 format
 6996   		if(FALSE == supportLFN)
 6997   		{
 6998   		    // point fN2 to short file name
 6999   		    fN2 = fptr -> name;
 7000   		    
 7001   		    // Load destination filename to be space intially.
 7002   		    for (count1 = 0; count1 < FILE_NAME_SIZE_8P3; count1++)
 7003   		    {
 7004   		        *(fN2 + count1) = ' ';
 7005   		    }
 7006   
 7007   			// multiply the length by 2 as each UTF word has 2 byte
 7008   			#ifdef SUPPORT_LFN
 7009   			if(utfModeFileName)
 7010   			{
 7011   				count4 = count3 * 2;
 7012   				temp = temp * 2;
 7013   				localFileName = (char *)utf16Filename;
 7014   			}
 7015   			else
 7016   			#endif
 7017   			{
 7018   				count4 = count3;
 7019   				localFileName = asciiFilename;
 7020   			}
 7021   
 7022   		    //copy only file name ( not the extension part )
 7023   		    for (count1 = 0,count2 = 0; (count2 < 8) && (count1 < count4);count1++ )
 7024   		    {
 7025   				if(localFileName[count1])
 7026   				{
 7027   					fN2[count2] = localFileName[count1]; // Destination filename initially filled with SPACE. Now copy only available chars.
 7028   
 7029   	    			// Convert lower-case to upper-case
 7030   	    			if ((fN2[count2] >= 0x61) && (fN2[count2] <= 0x7A))
 7031   	    			{
 7032   	    			    fN2[count2] -= 0x20;
 7033   					}
 7034   					count2++;
 7035   				}
 7036   		    }
 7037   
 7038   			if(count4 < temp)
 7039   			{
 7040   				// Discard the '.' part
 7041   				count4++;
 7042   
 7043       		    // Copy the extn to 8th position onwards. Ex: "FILE    .Tx "
 7044       		    for (count3 = 8; (count3 < 11) && (count4 < temp);count4++ )
 7045       		    {
 7046   					if(localFileName[count4])
 7047   					{
 7048       		        	fN2[count3] = localFileName[count4];
 7049   
 7050   		   	 			// Convert lower-case to upper-case
 7051   		   	 			if ((fN2[count3] >= 0x61) && (fN2[count3] <= 0x7A))
 7052   		   	 			{
 7053   		   	 			    fN2[count3] -= 0x20;
 7054   						}
 7055   						count3++;
 7056   					}
 7057       		    }
 7058   			}
 7059   		}
 7060   	}
 7061   
 7062   	// If the file name follows LFN format
 7063       if((NAME_LFN_TYPE == fileNameType) || (TRUE == supportLFN))
 7064   	{
 7065   		#if defined(SUPPORT_LFN)  	
 7066   
 7067   			// point fN2 to long file name
 7068   			fN2 = (char *)(fptr -> utf16LFNptr);
 7069   
 7070   			if(!utfModeFileName)
 7071   			{
 7072   				localFileName = asciiFilename;
 7073   			}
 7074   
 7075   			// Copy the LFN name in the adress specified by FSFILE pointer
 7076   			count2 = 0;
 7077   			for(count1 = 0;count1 < temp;count1++)
 7078   			{
 7079   				if(utfModeFileName)
 7080   				{
 7081   					fptr -> utf16LFNptr[count1] = utf16Filename[count1];
 7082   					if(AscciIndication)
 7083   					{
 7084   						if(utf16Filename[count1] > 0xFF)
 7085   						{
 7086   							fptr->AsciiEncodingType = FALSE;
 7087   							AscciIndication = FALSE;
 7088   						}
 7089   					}
 7090   				}
 7091   				else
 7092   				{
 7093   					fN2[count2++] = localFileName[count1];
 7094   					fN2[count2++] = (BYTE)0x00;
 7095   				}
 7096   			}
 7097   			fptr -> utf16LFNptr[count1] = 0x0000;
 7098   
 7099   			fptr->utf16LFNlength = fileNameLength + 1;
 7100   		#else
 7101   			return FALSE;
 7102   		#endif
 7103   	}
 7104   
 7105   	// Free the temporary heap used for intermediate execution
 7106   	return TRUE;
 7107   }
 7108   
 7109   #ifdef ALLOW_DIRS
 7110   
 7111   /*************************************************************************
 7112     Function:
 7113       BYTE FormatDirName (char * string,FILEOBJ fptr, BYTE mode)
 7114     Summary:
 7115       Format a dir name into dir entry format
 7116     Conditions:
 7117       This function should not be called by the user.
 7118     Input:
 7119       string -  The name to be formatted
 7120       mode -
 7121            - TRUE -  Partial string search characters are allowed
 7122            - FALSE - Partial string search characters are forbidden
 7123     Return Values:
 7124       TRUE - The name was formatted correctly
 7125       FALSE - The name contained invalid characters
 7126     Side Effects:
 7127       None
 7128     Description:
 7129       Format an 8.3 filename into directory structure format. If the name is less
 7130       than 8 chars, then it will be padded with spaces. If the extension name is
 7131       fewer than 3 chars, then it will also be oadded with spaces. The
 7132       ValidateChars function is used to ensure the characters in the specified
 7133       directory name are valid in this filesystem.
 7134     Remarks:
 7135       None.
 7136     *************************************************************************/
 7137   
 7138   BYTE FormatDirName (char * string,FILEOBJ fptr, BYTE mode)
 7139   {
 7140       char tempString [12];
 7141   	FILE_DIR_NAME_TYPE fileNameType;
 7142       int temp,count1,count2;
 7143       BOOL supportLFN = FALSE;
 7144   	char *localFileName;
 7145   
 7146   	// go with static allocation
 7147   	#if defined(SUPPORT_LFN)
 7148       	int count3,count4;
 7149   		BOOL	AscciIndication = TRUE;
 7150   		count1 = 256;
 7151   	#else
 7152   		count1 = 12;
 7153   	#endif
 7154   
 7155   	// Calculate the String length
 7156   	#ifdef SUPPORT_LFN
 7157   	if(utfModeFileName)
 7158   	{
 7159   		utf16Filename = (unsigned short int *)string;
 7160   		fileNameLength = 0;
 7161   		while(utf16Filename[fileNameLength])
 7162   		{
 7163   			fileNameLength++;
 7164   		}
 7165   	}
 7166   	else
 7167   	#endif
 7168   	{
 7169   		asciiFilename = string;
 7170   		fileNameLength = strlen(string);
 7171   	}
 7172   
 7173   	if(fileNameLength > count1)
 7174   	{
 7175   		return FALSE;
 7176   	}
 7177   
 7178       // Make sure the characters are valid
 7179       fileNameType = ValidateChars(mode);
 7180   
 7181       // If the file name doesn't follow 8P3 or LFN format, then return FALSE
 7182       if(NAME_ERROR == fileNameType)
 7183   	{
 7184   		return FALSE;
 7185   	}
 7186   
 7187   	temp = fileNameLength;
 7188   
 7189   	#if defined(SUPPORT_LFN)
 7190   		fptr->AsciiEncodingType = TRUE;
 7191   		fptr->utf16LFNlength = 0;
 7192   	#endif
 7193   
 7194   	// If LFN is supported and the file name is UTF16 type or Ascii mixed type,
 7195   	// go for LFN support rather than trying to adjust in 8P3 format
 7196   	if(NAME_8P3_ASCII_MIXED_TYPE == fileNameType)
 7197   	{
 7198   		#if defined(SUPPORT_LFN)
 7199   			supportLFN = TRUE;
 7200   		#endif
 7201   	}
 7202   
 7203   	#if defined(SUPPORT_LFN)
 7204   	if(NAME_8P3_UTF16_TYPE == fileNameType)
 7205   	{
 7206   		for (count3 = 0; count3 < temp; count3++)
 7207   		{
 7208   			if(utf16Filename[count3] > 0xFF)
 7209   			{
 7210   				fileNameType = NAME_8P3_UTF16_NONASCII_TYPE;
 7211   				supportLFN = TRUE;
 7212   				break;
 7213   			}
 7214   		}
 7215   
 7216   		if(count3 == temp)
 7217   		{
 7218   			fileNameType = NAME_8P3_UTF16_ASCII_CAPS_TYPE;
 7219   
 7220   			for (count3 = 0; count3 < temp; count3++)
 7221   			{
 7222   				if((utf16Filename[count3] >= 0x61) && (utf16Filename[count3] <= 0x7A))
 7223   				{
 7224   					fileNameType = NAME_8P3_UTF16_ASCII_MIXED_TYPE;
 7225   					supportLFN = TRUE;
 7226   					break;
 7227   				}
 7228   			}
 7229   		}
 7230   	}
 7231   	#endif
 7232   
 7233   	// If the file name follows LFN format
 7234       if((NAME_LFN_TYPE == fileNameType) || (TRUE == supportLFN))
 7235   	{
 7236   		#if !defined(SUPPORT_LFN)
 7237           	return FALSE;
 7238   		#else
 7239   			fptr -> utf16LFNptr = (unsigned short int *)string;
 7240   		
 7241   			if(utfModeFileName)
 7242   			{
 7243   				if(utf16Filename != (unsigned short int *)string)
 7244   				{
 7245   					// Copy the validated/Fomated name in the UTF16 string
 7246   					for(count1 = 0; count1 < temp; count1++)
 7247   					{
 7248   						fptr -> utf16LFNptr[count1] = utf16Filename[count1];
 7249   						if(AscciIndication)
 7250   						{
 7251   							if(utf16Filename[count1] > 0xFF)
 7252   							{
 7253   								fptr->AsciiEncodingType = FALSE;
 7254   								AscciIndication = FALSE;
 7255   							}
 7256   						}
 7257   					}
 7258   					fptr -> utf16LFNptr[count1] = 0x0000;
 7259   				}
 7260   				else
 7261   				{
 7262   					for(count1 = 0; count1 < temp; count1++)
 7263   					{
 7264   						if(AscciIndication)
 7265   						{
 7266   							if(utf16Filename[count1] > 0xFF)
 7267   							{
 7268   								fptr->AsciiEncodingType = FALSE;
 7269   								AscciIndication = FALSE;
 7270   								break;
 7271   							}
 7272   						}
 7273   					}
 7274   				}
 7275   			}
 7276   			else
 7277   			{
 7278   				#ifdef FS_DYNAMIC_MEM
 7279   	    			unsigned short int *tempAsciiLFN = (unsigned short int *)FS_malloc((temp + 1) * 2);
 7280   				#else
 7281   					unsigned short int	tempAsciiLFN[temp + 1];
 7282   				#endif
 7283   
 7284   				localFileName = (char *)tempAsciiLFN;
 7285   
 7286   				// Copy the validated/Fomated name in the Ascii string
 7287   				count2 = 0;

 7288   				for(count1 = 0; count1 < temp; count1++)
 7289   				{
 7290   					localFileName[count2++] = asciiFilename[count1];

 7291   					localFileName[count2++] = (BYTE)0x00;

 7292   				}
 7293   
 7294   				// Copy the validated/Fomated name in the UTF16 string
 7295   				for(count1 = 0; count1 < temp; count1++)
 7296   				{
 7297   					fptr -> utf16LFNptr[count1] = tempAsciiLFN[count1];
 7298   				}
 7299   
 7300   				#ifdef FS_DYNAMIC_MEM
 7301   	    			FS_free((unsigned char *)tempAsciiLFN);
 7302   				#endif
 7303   				fptr -> utf16LFNptr[count1] = 0x0000;
 7304   			}
 7305   
 7306   			fptr->utf16LFNlength = fileNameLength + 1;
 7307   		#endif
 7308   	}
 7309   	else
 7310   	{
 7311   		#ifdef SUPPORT_LFN
 7312   		if(utfModeFileName)
 7313   		{
 7314   			localFileName = (char *)utf16Filename;
 7315   
 7316   			// Copy the name part in the temporary string
 7317   		    count4 = 0;
 7318   		    for (count3 = 0; (count3 < temp) && (utf16Filename[count3] != '.') && (utf16Filename[count3] != 0); count3++)
 7319   		    {
 7320   				count1 = count3 * 2;
 7321   				if(localFileName[count1])
 7322   				{
 7323   			        tempString[count4] = localFileName[count1];
 7324   					count4++;
 7325   					if(count4 == 8)
 7326   						break;
 7327   				}
 7328   		 
 7329   				if(localFileName[count1 + 1])
 7330   				{
 7331   			        tempString[count4] = localFileName[count1 + 1];
 7332   					count4++;
 7333   					if(count4 == 8)
 7334   						break;
 7335   				}
 7336   		    }
 7337   
 7338   			// File the remaining name portion with spaces
 7339   		    while (count4 < 8)
 7340   		    {
 7341   		        tempString [count4++] = 0x20;
 7342   		    }
 7343   
 7344   			// Copy the extension part in the temporary string
 7345   		    if (utf16Filename[count3] == '.')
 7346   		    {
 7347   				count1 = count3 * 2 + 2;
 7348   		        while (localFileName[count1] != 0)
 7349   		        {
 7350   					if(localFileName[count3])
 7351   					{
 7352   				        tempString[count4] = localFileName[count3];
 7353   						count4++;
 7354   						if(count4 == 11)
 7355   							break;
 7356   					}
 7357   		        }
 7358   		    }
 7359   
 7360   			count1 = count4;
 7361   		}
 7362   		else
 7363   		#endif
 7364   		{
 7365   			// Copy the name part in the temporary string
 7366   		    for (count1 = 0; (count1 < 8) && (*(asciiFilename + count1) != '.') && (*(asciiFilename + count1) != 0); count1++)
 7367   		    {
 7368   		        tempString[count1] = *(asciiFilename + count1);
 7369   		    }
 7370   
 7371   			count2 = count1;
 7372   
 7373   			// File the remaining name portion with spaces
 7374   		    while (count1 < 8)
 7375   		    {
 7376   		        tempString [count1++] = 0x20;
 7377   		    }
 7378   
 7379   			// Copy the extension part in the temporary string
 7380   		    if (*(asciiFilename + count2) == '.')
 7381   		    {
 7382   		        count2++;
 7383   		        while (*(asciiFilename + count2) != 0)
 7384   		        {
 7385   		            tempString[count1++] = *(asciiFilename + count2++);
 7386   		        }
 7387   		    }
 7388   		}
 7389   
 7390   		// File the remaining portion with spaces
 7391   		while (count1 < FILE_NAME_SIZE_8P3)
 7392   		{
 7393   		    tempString[count1++] = 0x20;
 7394   		}
 7395   
 7396   		tempString[FILE_NAME_SIZE_8P3] = 0;
 7397   
 7398   		// Forbidden
 7399   		if (tempString[0] == 0x20)
 7400   		{
 7401   		    tempString[0] = '_';
 7402   		}
 7403   
 7404   		// point fN2 to short file name
 7405   		localFileName = fptr -> name;
 7406   
 7407   		// Copy the formated name in string
 7408   		for (count1 = 0; count1 < TOTAL_FILE_SIZE_8P3; count1++)
 7409   		{
 7410   		    localFileName[count1] = tempString[count1];
 7411   
 7412   			// Convert lower-case to upper-case
 7413   			if ((localFileName[count1] >= 0x61) && (localFileName[count1] <= 0x7A))
 7414   			{
 7415   			    localFileName[count1] -= 0x20;
 7416   			}
 7417   		}
 7418   	}
 7419   
 7420   	return TRUE;
 7421   }
 7422   #endif
 7423   
 7424   
 7425   /*************************************************************
 7426     Function:
 7427       FILE_DIR_NAME_TYPE ValidateChars(BYTE mode)
 7428     Summary:
 7429       Validate the characters in a given file name
 7430     Conditions:
 7431       This function should not be called by the user.
 7432     Input:
 7433       fileName -  The name to be validated
 7434       mode -      Determines if partial string search is allowed
 7435     Return Values:
 7436       TRUE - Name was validated
 7437       FALSE - File name was not valid
 7438     Side Effects:
 7439       None
 7440     Description:
 7441       The ValidateChars function will compare characters in a
 7442       specified filename to determine if they're permissable
 7443       in the FAT file system.  Lower-case characters will be
 7444       converted to upper-case.  If the mode argument is specifed
 7445       to be 'TRUE,' partial string search characters are allowed.
 7446     Remarks:
 7447       None.
 7448     *************************************************************/
 7449   FILE_DIR_NAME_TYPE ValidateChars(BYTE mode)
 7450   {
 7451   	FILE_DIR_NAME_TYPE fileNameType;
 7452       unsigned short int count1;
 7453   	#if defined(SUPPORT_LFN)
 7454   	unsigned short int utf16Value;
 7455       unsigned short int count2;
 7456   	int		count3;
 7457   	#endif
 7458       unsigned char radix = FALSE,asciiValue;
 7459   
 7460   	#if defined(SUPPORT_LFN)
 7461   
 7462   		// Remove the spaces if they are present before the file name
 7463   		for (count1 = 0; count1 < fileNameLength; count1++)
 7464   		{
 7465   	        if(utfModeFileName)
 7466   			{
 7467   				if((utf16Filename[count1] != ' ') && (utf16Filename[count1] != '.'))
 7468   				{
 7469   					utf16Filename = utf16Filename + count1;
 7470   					break;
 7471   				}
 7472   			}
 7473   			else if((asciiFilename[count1] != ' ') && (asciiFilename[count1] != '.'))
 7474   			{
 7475   				asciiFilename = asciiFilename + count1;
 7476   				break;
 7477   	    	}
 7478   	    }
 7479   
 7480   	    count2 = 0;
 7481   
 7482   		// Remove the spaces  & dots if they are present after the file name
 7483   	    for (count3 = fileNameLength - count1 - 1; count3 > 0; count3--)
 7484   	    {
 7485   	        if(utfModeFileName)
 7486   			{
 7487   				if((utf16Filename[count3] != ' ') && (utf16Filename[count3] != '.'))
 7488   				{
 7489   					break;
 7490   				}
 7491   			}
 7492   			else if((asciiFilename[count3] != ' ') && (asciiFilename[count3] != '.'))
 7493   			{
 7494   				break;
 7495   	    	}
 7496   
 7497   	    	count2++;
 7498   	    }
 7499   
 7500   		fileNameLength = fileNameLength - count1 - count2;
 7501   
 7502       	if(( fileNameLength > MAX_FILE_NAME_LENGTH_LFN ) || (fileNameLength == 0))// 255
 7503           	return NAME_ERROR; //long file name
 7504   
 7505       #endif
 7506   
 7507    	// If the string length is greater then 8P3 length, then assume
 7508   	// the file name as LFN type provided there are no errors in the
 7509   	// below for loop.
 7510   	#ifdef SUPPORT_LFN
 7511   	if(utfModeFileName)
 7512   	{
 7513   		if((fileNameLength * 2) > (TOTAL_FILE_SIZE_8P3 * 2))
 7514   		{
 7515   			fileNameType = NAME_LFN_TYPE;
 7516   		}
 7517   		else
 7518   		{
 7519   			fileNameType = NAME_8P3_UTF16_TYPE;
 7520   		}
 7521   	}
 7522   	else
 7523   	#endif
 7524   	{
 7525   		if(fileNameLength > TOTAL_FILE_SIZE_8P3)
 7526   		{
 7527   			fileNameType = NAME_LFN_TYPE;
 7528   		}
 7529   		else
 7530   		{
 7531   			fileNameType = NAME_8P3_ASCII_CAPS_TYPE;
 7532   		}
 7533   	}
 7534   
 7535   	for( count1 = 0; count1 < fileNameLength; count1++ )
 7536   	{
 7537   		#ifdef SUPPORT_LFN
 7538   		if(utfModeFileName)
 7539   		{
 7540   			utf16Value = utf16Filename[count1];
 7541   		    // Characters not valid for either of 8P3 & LFN format
 7542   		    if (((utf16Value < 0x20) && (utf16Value != 0x05)) || (utf16Value == 0x22) || 
 7543   				(utf16Value == 0x2F) || (utf16Value == 0x3A) || (utf16Value == 0x3C) || 
 7544   		        (utf16Value == 0x3E) || (utf16Value == 0x5C) || (utf16Value == 0x7C))
 7545   		    {
 7546   		        return NAME_ERROR;
 7547   		    }
 7548   
 7549   	        // Check for partial string search chars
 7550   	        if (mode == FALSE)
 7551   	        {
 7552   	            if ((utf16Value == '*') || (utf16Value == '?'))
 7553   	            {
 7554   	    		    return NAME_ERROR;
 7555   	        	}
 7556   	        }
 7557   
 7558   			if(fileNameType != NAME_LFN_TYPE)
 7559   			{
 7560   				// Characters valid for LFN format only
 7561   			    if ((utf16Value == 0x20) || (utf16Value == 0x2B) || (utf16Value == 0x2C) || 
 7562   					(utf16Value == 0x3B) || (utf16Value == 0x3D) || (utf16Value == 0x5B) || 
 7563   					(utf16Value == 0x5D) || ((utf16Value == 0x2E) && (radix == TRUE)))
 7564   			    {
 7565   					fileNameType = NAME_LFN_TYPE;
 7566   					continue;
 7567   			    }
 7568   
 7569   	    	    // only one radix ('.') character is allowed in 8P3 format, where as 
 7570   				// multiple radices can be present in LFN format
 7571   	    	    if (utf16Filename[count1] == 0x2E)
 7572   	    	    {
 7573   	    	        radix = TRUE;
 7574   	    	    }
 7575   			}
 7576   		}
 7577   		else 
 7578   		#endif
 7579   		{
 7580   			asciiValue = asciiFilename[count1];
 7581   			if(((asciiValue < 0x20) && (asciiValue != 0x05)) || (asciiValue == 0x22) ||
 7582   				(asciiValue == 0x2F) || (asciiValue == 0x3A) || (asciiValue == 0x3C) ||
 7583   				(asciiValue == 0x3E) || (asciiValue == 0x5C) || (asciiValue == 0x7C))
 7584   			{
 7585   				return NAME_ERROR;
 7586   			}
 7587   
 7588   	        // Check for partial string search chars
 7589   	        if (mode == FALSE)
 7590   	        {
 7591   	            if ((asciiValue == '*') || (asciiValue == '?'))
 7592   	            {
 7593   	    		    return NAME_ERROR;
 7594   	        	}
 7595   	        }
 7596   
 7597   			if(fileNameType != NAME_LFN_TYPE)
 7598   			{
 7599   				// Characters valid for LFN format only
 7600   			    if ((asciiValue == 0x20) || (asciiValue == 0x2B) || (asciiValue == 0x2C) ||
 7601   			    	(asciiValue == 0x3B) || (asciiValue == 0x3D) || (asciiValue == 0x5B) ||
 7602   			        (asciiValue == 0x5D) || ((asciiValue == 0x2E) && (radix == TRUE)))
 7603   			    {
 7604   					fileNameType = NAME_LFN_TYPE;
 7605   					continue;
 7606   			    }
 7607   
 7608   	    	    // only one radix ('.') character is allowed in 8P3 format, where as 
 7609   				// multiple radices can be present in LFN format
 7610   	    	    if (asciiValue == 0x2E)
 7611   	    	    {
 7612   	    	        radix = TRUE;
 7613   	    	    }
 7614   
 7615   				// If the characters are mixed type & are within 8P3 length range
 7616   				// then store file type as 8P3 mixed type format
 7617   				if(fileNameType != NAME_8P3_ASCII_MIXED_TYPE)
 7618   				{
 7619   					if((asciiValue >= 0x61) && (asciiValue <= 0x7A))
 7620   					{
 7621   						fileNameType = NAME_8P3_ASCII_MIXED_TYPE;
 7622   					}
 7623   				}
 7624   			}
 7625   		}
 7626   	}
 7627   
 7628   	return fileNameType;
 7629   }
 7630   
 7631   
 7632   /**********************************************************************
 7633     Function:
 7634       int FSfseek(FSFILE *stream, long offset, int whence)
 7635     Summary:
 7636       Change the current position in a file
 7637     Conditions:
 7638       File opened
 7639     Input:
 7640       stream -    Pointer to file structure
 7641       offset -    Offset from base location
 7642       whence -
 7643              - SEEK_SET -  Seek from start of file
 7644              - SEEK_CUR -  Seek from current location
 7645              - SEEK_END -  Seek from end of file (subtract offset)
 7646     Return Values:
 7647       0 -  Operation successful
 7648       -1 - Operation unsuccesful
 7649     Side Effects:
 7650       The FSerrno variable will be changed.
 7651     Description:
 7652       The FSfseek function will change the current position in the file to
 7653       one specified by the user.  First, an absolute offset is calculated
 7654       using the offset and base location passed in by the user.  Then, the
 7655       position variables are updated, and the sector number that corresponds
 7656       to the new location.  That sector is then loaded.  If the offset
 7657       falls exactly on a cluster boundary, a new cluster will be allocated
 7658       to the file and the position will be set to the first byte of that
 7659       cluster.
 7660     Remarks:
 7661       None
 7662     **********************************************************************/
 7663   
 7664   int FSfseek(FSFILE *stream, long offset, int whence)
 7665   {
 7666       DWORD    numsector, temp;   // lba of first sector of first cluster
 7667       DISK*   dsk;            // pointer to disk structure
 7668       BYTE   test;
 7669       long offset2 = offset;
 7670   
 7671       dsk = stream->dsk;
 7672   
 7673       switch(whence)
 7674       {
 7675           case SEEK_CUR:
 7676               // Apply the offset to the current position
 7677               offset2 += stream->seek;
 7678               break;
 7679           case SEEK_END:
 7680               // Apply the offset to the end of the file
 7681               offset2 = stream->size - offset2;
 7682               break;
 7683           case SEEK_SET:
 7684               // automatically there
 7685           default:
 7686               break;
 7687      }
 7688   
 7689   #ifdef ALLOW_WRITES
 7690       if (gNeedDataWrite)
 7691           if (flushData())
 7692           {
 7693               FSerrno = CE_WRITE_ERROR;
 7694               return EOF;
 7695           }
 7696   #endif
 7697   
 7698       // start from the beginning
 7699       temp = stream->cluster;
 7700       stream->ccls = temp;
 7701   
 7702       temp = stream->size;
 7703   
 7704       if (offset2 > temp)
 7705       {
 7706           FSerrno = CE_INVALID_ARGUMENT;
 7707           return (-1);      // past the limits
 7708       }
 7709       else
 7710       {
 7711           // if we are writing we are no longer at the end
 7712           stream->flags.FileWriteEOF = FALSE;
 7713   
 7714           // set the new postion
 7715           stream->seek = offset2;
 7716   
 7717           // figure out how many sectors
 7718           numsector = offset2 / dsk->sectorSize;
 7719   
 7720           // figure out how many bytes off of the offset
 7721           offset2 = offset2 - (numsector * dsk->sectorSize);
 7722           stream->pos = offset2;
 7723   
 7724           // figure out how many clusters
 7725           temp = numsector / dsk->SecPerClus;
 7726   
 7727           // figure out the stranded sectors
 7728           numsector = numsector - (dsk->SecPerClus * temp);
 7729           stream->sec = numsector;
 7730   
 7731           // if we are in the current cluster stay there
 7732           if (temp > 0)
 7733           {
 7734               test = FILEget_next_cluster(stream, temp);
 7735               if (test != CE_GOOD)
 7736               {
 7737                   if (test == CE_FAT_EOF)
 7738                   {
 7739   #ifdef ALLOW_WRITES
 7740                       if (stream->flags.write)
 7741                       {
 7742                           // load the previous cluster
 7743                           stream->ccls = stream->cluster;
 7744                           // Don't perform this operation if there's only one cluster
 7745                           if (temp != 1)
 7746                           test = FILEget_next_cluster(stream, temp - 1);
 7747                           if (FILEallocate_new_cluster(stream, 0) != CE_GOOD)
 7748                           {
 7749                               FSerrno = CE_COULD_NOT_GET_CLUSTER;
 7750                               return -1;
 7751                           }
 7752                           // sec and pos should already be zero
 7753                       }
 7754                       else
 7755                       {
 7756   #endif
 7757                           stream->ccls = stream->cluster;
 7758                           test = FILEget_next_cluster(stream, temp - 1);
 7759                           if (test != CE_GOOD)
 7760                           {
 7761                               FSerrno = CE_COULD_NOT_GET_CLUSTER;
 7762                               return (-1);
 7763                           }
 7764                           stream->pos = dsk->sectorSize;
 7765                           stream->sec = dsk->SecPerClus - 1;
 7766   #ifdef ALLOW_WRITES
 7767                       }
 7768   #endif
 7769                   }
 7770                   else
 7771                   {
 7772                       FSerrno = CE_COULD_NOT_GET_CLUSTER;
 7773                       return (-1);   // past the limits
 7774                   }
 7775               }
 7776           }
 7777   
 7778           // Determine the lba of the selected sector and load
 7779           temp = Cluster2Sector(dsk,stream->ccls);
 7780   
 7781           // now the extra sectors
 7782           numsector = stream->sec;
 7783           temp += numsector;
 7784   
 7785           gBufferOwner = NULL;
 7786           gBufferZeroed = FALSE;
 7787           if( !MDD_SectorRead(temp, dsk->buffer) )
 7788           {
 7789               FSerrno = CE_BADCACHEREAD;
 7790               return (-1);   // Bad read
 7791           }
 7792           gLastDataSectorRead = temp;
 7793       }
 7794   
 7795       FSerrno = CE_GOOD;
 7796   
 7797       return (0);
 7798   }
 7799   
 7800   
 7801   // FSfopenpgm, FSremovepgm, and FSrenamepgm will only work on PIC18s
 7802   #ifdef __18CXX
 7803   #ifdef ALLOW_PGMFUNCTIONS
 7804   
 7805   #ifdef ALLOW_WRITES
 7806   
 7807   /*****************************************************************
 7808     Function:
 7809       int FSrenamepgm(const rom char * fileName, FSFILE * fo)
 7810     Summary:
 7811       Rename a file named with a ROM string on PIC18
 7812     Conditions:
 7813       File opened.
 7814     Input:
 7815       fileName -  The new name of the file (in ROM)
 7816       fo -        The file to rename
 7817     Return Values:
 7818       0 -  File renamed successfully
 7819       -1 - File could not be renamed
 7820     Side Effects:
 7821       The FSerrno variable will be changed.
 7822     Description:
 7823       The Fsrenamepgm function will copy the rom fileName specified
 7824       by the user into a RAM array and pass that array into the
 7825       FSrename function.
 7826     Remarks:
 7827       This function is for use with PIC18 when passing arguments in ROM.
 7828     *****************************************************************/
 7829   
 7830   int FSrenamepgm (const rom char * fileName, FSFILE * fo)
 7831   {
 7832   	#if defined(SUPPORT_LFN)

 7833   		char tempArray[257];
 7834   		unsigned short int count;
 7835   	#else

 7836   		char	tempArray[13];

 7837   	    BYTE count;
 7838   	#endif

 7839   
 7840       *fileName;
 7841       for(count = 0; count < sizeof(tempArray); count++)
 7842       {
 7843           _asm TBLRDPOSTINC _endasm
 7844           tempArray[count] = TABLAT;
 7845       }//end for(...)
 7846   
 7847       return FSrename (tempArray, fo);
 7848   }
 7849   #endif
 7850   
 7851   /******************************************************************************
 7852     Function:
 7853       FSFILE * FSfopenpgm(const rom char * fileName, const rom char *mode)
 7854     Summary:
 7855       Open a Ascii file named with a ROM string on PIC18
 7856     Conditions:
 7857       For read modes, file exists; FSInit performed
 7858     Input:
 7859       fileName -  The name of the file to be opened (ROM)
 7860       mode -      The mode the file will be opened in (ROM)
 7861     Return Values:
 7862       FSFILE * - A pointer to the file object
 7863       NULL -     File could not be opened
 7864     Side Effects:
 7865       The FSerrno variable will be changed.
 7866     Description:
 7867       The FSfopenpgm function will copy a PIC18 ROM fileName and mode argument
 7868       into RAM arrays, and then pass those arrays to the FSfopen function.
 7869     Remarks:
 7870       This function is for use with PIC18 when passing arguments in ROM.
 7871     ******************************************************************************/
 7872   
 7873   
 7874   FSFILE * FSfopenpgm(const rom char * fileName, const rom char *mode)
 7875   {
 7876   	#if defined(SUPPORT_LFN)
 7877       	char tempArray[257];
 7878       	unsigned short int count = 0;
 7879       #else
 7880       	char tempArray[13];
 7881       	BYTE count = 0;
 7882   	#endif
 7883       char M[2];
 7884   
 7885       for(;;)
 7886   	{
 7887   		tempArray[count] = fileName[count];
 7888   		if(tempArray[count])
 7889   			count++;
 7890   		else
 7891   			break;
 7892   	}
 7893   
 7894       for (count = 0; count < 2; count++)
 7895       {
 7896           M[count] = *(mode + count);
 7897       }
 7898   
 7899       return FSfopen(tempArray, M);
 7900   }
 7901   
 7902   /*************************************************************
 7903     Function:
 7904       int FSremovepgm (const rom char * fileName)
 7905     Summary:
 7906       Delete a file named with a ROM string on PIC18
 7907     Conditions:
 7908       File not opened; file exists
 7909     Input:
 7910       fileName -  The name of the file to be deleted (ROM)
 7911     Return Values:
 7912       0 -  File was removed successfully
 7913       -1 - File could not be removed
 7914     Side Effects:
 7915       The FSerrno variable will be changed.
 7916     Description:
 7917       The FSremovepgm function will copy a PIC18 ROM fileName argument
 7918       into a RAM array, and then pass that array to the FSremove function.
 7919     Remarks:
 7920       This function is for use with PIC18 when passing arguments in ROM.
 7921     *************************************************************/
 7922   #ifdef ALLOW_WRITES
 7923   int FSremovepgm (const rom char * fileName)
 7924   {
 7925   	#ifdef SUPPORT_LFN
 7926   		char tempArray[257];
 7927   		unsigned short int count;
 7928   	#else

 7929   		char	tempArray[13];

 7930   	    BYTE count;
 7931   	#endif

 7932   
 7933       *fileName;
 7934       for(count = 0; count < sizeof(tempArray); count++)
 7935       {
 7936           _asm TBLRDPOSTINC _endasm
 7937           tempArray[count] = TABLAT;
 7938       }//end for(...)
 7939   
 7940       return FSremove (tempArray);
 7941   }
 7942   #endif
 7943   
 7944   /**************************************************************************************
 7945     Function:
 7946       int FindFirstpgm (const char * fileName, unsigned int attr, SearchRec * rec)
 7947     Summary:
 7948       Find a file named with a ROM string on PIC18
 7949     Conditions:
 7950       None
 7951     Input:
 7952       fileName -  The name of the file to be found (ROM)
 7953       attr -      The attributes of the file to be found
 7954       rec -       Pointer to a search record to store the file info in
 7955     Return Values:
 7956       0 -  File was found
 7957       -1 - No file matching the given parameters was found
 7958     Side Effects:
 7959       Search criteria from previous FindFirst call on passed SearchRec object will be lost.
 7960       The FSerrno variable will be changed.
 7961     Description:
 7962       The FindFirstpgm function will copy a PIC18 ROM fileName argument
 7963       into a RAM array, and then pass that array to the FindFirst function.
 7964     Remarks:
 7965       Call FindFirstpgm or FindFirst before calling FindNext.
 7966       This function is for use with PIC18 when passing arguments in ROM.
 7967     **************************************************************************************/
 7968   #ifdef ALLOW_FILESEARCH
 7969   int FindFirstpgm (const rom char * fileName, unsigned int attr, SearchRec * rec)
 7970   {
 7971   	#if defined(SUPPORT_LFN)

 7972   		char tempArray[257];
 7973   		unsigned short int count;
 7974   	#else

 7975   		char	tempArray[13];

 7976   	    BYTE count;
 7977   	#endif

 7978   
 7979       *fileName;
 7980       for(count = 0; count < sizeof(tempArray); count++)
 7981       {
 7982           _asm TBLRDPOSTINC _endasm
 7983           tempArray[count] = TABLAT;
 7984       }//end for
 7985   
 7986       return FindFirst (tempArray,attr,rec);
 7987   }
 7988   #endif
 7989   #endif
 7990   #endif
 7991   
 7992   
 7993   /***********************************************
 7994     Function:
 7995       DWORD ReadFAT (DISK *dsk, DWORD ccls)
 7996     Summary:
 7997       Read the next entry from the FAT
 7998     Conditions:
 7999       This function should not be called by the user.
 8000     Input:
 8001       dsk -   The disk structure
 8002       ccls -  The current cluster
 8003     Return:
 8004       DWORD - The next cluster in a file chain
 8005     Side Effects:
 8006       None
 8007     Description:
 8008       The ReadFAT function will read the FAT and
 8009       determine the next cluster value after the
 8010       cluster specified by 'ccls.' Note that the
 8011       FAT sector that is read is stored in the
 8012       global FAT cache buffer.
 8013     Remarks:
 8014       None.
 8015     ***********************************************/
 8016   
 8017   DWORD ReadFAT (DISK *dsk, DWORD ccls)
 8018   {
 8019       BYTE q;
 8020       DWORD p, l;  // "l" is the sector Address
 8021       DWORD c = 0, d, ClusterFailValue,LastClusterLimit;   // ClusterEntries
 8022   
 8023       gBufferZeroed = FALSE;
 8024   
 8025       /* Settings based on FAT type */
 8026       switch (dsk->type)
 8027       {
 8028   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 8029           case FAT32:
 8030               p = (DWORD)ccls * 4;
 8031               q = 0; // "q" not used for FAT32, only initialized to remove a warning
 8032               ClusterFailValue = CLUSTER_FAIL_FAT32;
 8033               LastClusterLimit = LAST_CLUSTER_FAT32;
 8034               break;
 8035   #endif
 8036           case FAT12:
 8037               p = (DWORD) ccls *3;  // Mulby1.5 to find cluster pos in FAT
 8038               q = p&1;
 8039               p >>= 1;
 8040               ClusterFailValue = CLUSTER_FAIL_FAT16;
 8041               LastClusterLimit = LAST_CLUSTER_FAT12;
 8042               break;
 8043           case FAT16:
 8044           default:
 8045               p = (DWORD)ccls *2;     // Mulby 2 to find cluster pos in FAT
 8046               q = 0; // "q" not used for FAT16, only initialized to remove a warning
 8047               ClusterFailValue = CLUSTER_FAIL_FAT16;
 8048               LastClusterLimit = LAST_CLUSTER_FAT16;
 8049               break;
 8050       }
 8051   
 8052       l = dsk->fat + (p / dsk->sectorSize);     //
 8053       p &= dsk->sectorSize - 1;                 // Restrict 'p' within the FATbuffer size
 8054   
 8055       // Check if the appropriate FAT sector is already loaded
 8056       if (gLastFATSectorRead == l)
 8057       {
 8058   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 8059           if (dsk->type == FAT32)
 8060               c = RAMreadD (gFATBuffer, p);
 8061           else
 8062   #endif
 8063               if(dsk->type == FAT16)
 8064                   c = RAMreadW (gFATBuffer, p);
 8065               else if(dsk->type == FAT12)
 8066               {
 8067                   c = RAMread (gFATBuffer, p);
 8068                   if (q)
 8069                   {
 8070                       c >>= 4;
 8071                   }
 8072                   // Check if the MSB is across the sector boundry
 8073                   p = (p +1) & (dsk->sectorSize-1);
 8074                   if (p == 0)
 8075                   {
 8076                       // Start by writing the sector we just worked on to the card
 8077                       // if we need to
 8078   #ifdef ALLOW_WRITES
 8079                       if (gNeedFATWrite)
 8080                           if(WriteFAT (dsk, 0, 0, TRUE))
 8081                               return ClusterFailValue;
 8082   #endif
 8083                       if (!MDD_SectorRead (l+1, gFATBuffer))
 8084                       {
 8085                           gLastFATSectorRead = 0xFFFF;
 8086                           return ClusterFailValue;
 8087                       }
 8088                       else
 8089                       {
 8090                           gLastFATSectorRead = l +1;
 8091                       }
 8092                   }
 8093                   d = RAMread (gFATBuffer, p);
 8094                   if (q)
 8095                   {
 8096                       c += (d <<4);
 8097                   }
 8098                   else
 8099                   {
 8100                       c += ((d & 0x0F)<<8);
 8101                   }
 8102               }
 8103           }
 8104           else
 8105           {
 8106               // If there's a currently open FAT sector,
 8107               // write it back before reading into the buffer
 8108   #ifdef ALLOW_WRITES
 8109               if (gNeedFATWrite)
 8110               {
 8111                   if(WriteFAT (dsk, 0, 0, TRUE))
 8112                       return ClusterFailValue;
 8113               }
 8114   #endif
 8115               if (!MDD_SectorRead (l, gFATBuffer))
 8116               {
 8117                   gLastFATSectorRead = 0xFFFF;  // Note: It is Sector not Cluster.
 8118                   return ClusterFailValue;
 8119               }
 8120               else
 8121               {
 8122                   gLastFATSectorRead = l;
 8123   
 8124   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 8125                   if (dsk->type == FAT32)
 8126                       c = RAMreadD (gFATBuffer, p);
 8127                   else
 8128   #endif
 8129                       if(dsk->type == FAT16)
 8130                           c = RAMreadW (gFATBuffer, p);
 8131                       else if (dsk->type == FAT12)
 8132                       {
 8133                           c = RAMread (gFATBuffer, p);
 8134                           if (q)
 8135                           {
 8136                               c >>= 4;
 8137                           }
 8138                           p = (p +1) & (dsk->sectorSize-1);
 8139                           d = RAMread (gFATBuffer, p);
 8140                           if (q)
 8141                           {
 8142                               c += (d <<4);
 8143                           }
 8144                           else
 8145                           {
 8146                               c += ((d & 0x0F)<<8);
 8147                           }
 8148                       }
 8149               }
 8150       }
 8151   
 8152       // Normalize it so 0xFFFF is an error
 8153       if (c >= LastClusterLimit)
 8154           c = LastClusterLimit;
 8155   
 8156      return c;
 8157   }   // ReadFAT
 8158   
 8159   
 8160   
 8161   /****************************************************************************
 8162     Function:
 8163       WORD WriteFAT (DISK *dsk, DWORD ccls, WORD value, BYTE forceWrite)
 8164     Summary:
 8165       Write an entry to the FAT
 8166     Conditions:
 8167       This function should not be called by the user.
 8168     Input:
 8169       dsk -         The disk structure
 8170       ccls -        The current cluster
 8171       value -       The value to write in
 8172       forceWrite -  Force the function to write the current FAT sector
 8173     Return:
 8174       0 -    The FAT write was successful
 8175       FAIL - The FAT could not be written
 8176     Side Effects:
 8177       None
 8178     Description:
 8179       The WriteFAT function writes an entry to the FAT.  If the function
 8180       is called and the 'forceWrite' argument is TRUE, the function will
 8181       write the existing FAT data to the device.  Otherwise, the function
 8182       will replace a single entry in the FAT buffer (indicated by 'ccls')
 8183       with a new value (indicated by 'value.')
 8184     Remarks:
 8185       None.
 8186     ****************************************************************************/
 8187   
 8188   #ifdef ALLOW_WRITES
 8189   DWORD WriteFAT (DISK *dsk, DWORD ccls, DWORD value, BYTE forceWrite)
 8190   {
 8191       BYTE i, q, c;
 8192       DWORD p, li, l, ClusterFailValue;
 8193   
 8194   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 8195       if ((dsk->type != FAT32) && (dsk->type != FAT16) && (dsk->type != FAT12))
 8196           return CLUSTER_FAIL_FAT32;
 8197   #else // If FAT32 support not enabled
 8198       if ((dsk->type != FAT16) && (dsk->type != FAT12))
 8199           return CLUSTER_FAIL_FAT16;
 8200   #endif
 8201   
 8202       /* Settings based on FAT type */
 8203       switch (dsk->type)
 8204       {
 8205   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 8206           case FAT32:
 8207               ClusterFailValue = CLUSTER_FAIL_FAT32;
 8208               break;
 8209   #endif
 8210           case FAT12:
 8211           case FAT16:
 8212           default:
 8213               ClusterFailValue = CLUSTER_FAIL_FAT16;
 8214               break;
 8215       }
 8216   
 8217       gBufferZeroed = FALSE;
 8218   
 8219       // The only purpose for calling this function with forceWrite
 8220       // is to write the current FAT sector to the card
 8221       if (forceWrite)
 8222       {
 8223           for (i = 0, li = gLastFATSectorRead; i < dsk->fatcopy; i++, li += dsk->fatsize)
 8224           {
 8225               if (!MDD_SectorWrite (li, gFATBuffer, FALSE))
 8226               {
 8227                   return ClusterFailValue;
 8228               }
 8229           }
 8230   
 8231           gNeedFATWrite = FALSE;
 8232   
 8233           return 0;
 8234       }
 8235   
 8236       /* Settings based on FAT type */
 8237       switch (dsk->type)
 8238       {
 8239   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 8240           case FAT32:
 8241               p = (DWORD)ccls *4;   // "p" is the position in "gFATBuffer" for corresponding cluster.
 8242               q = 0;      // "q" not used for FAT32, only initialized to remove a warning
 8243               break;
 8244   #endif
 8245           case FAT12:
 8246               p = (DWORD) ccls * 3; // "p" is the position in "gFATBuffer" for corresponding cluster.
 8247               q = p & 1;   // Odd or even?
 8248               p >>= 1;
 8249               break;
 8250           case FAT16:
 8251           default:
 8252               p = (DWORD) ccls *2;   // "p" is the position in "gFATBuffer" for corresponding cluster.
 8253               q = 0;      // "q" not used for FAT16, only initialized to remove a warning
 8254               break;
 8255       }
 8256   
 8257       l = dsk->fat + (p / dsk->sectorSize);     //
 8258       p &= dsk->sectorSize - 1;                 // Restrict 'p' within the FATbuffer size
 8259   
 8260       if (gLastFATSectorRead != l)
 8261       {
 8262           // If we are loading a new sector then write
 8263           // the current one to the card if we need to
 8264           if (gNeedFATWrite)
 8265           {
 8266               for (i = 0, li = gLastFATSectorRead; i < dsk->fatcopy; i++, li += dsk->fatsize)
 8267               {
 8268                   if (!MDD_SectorWrite (li, gFATBuffer, FALSE))
 8269                   {
 8270                       return ClusterFailValue;
 8271                   }
 8272               }
 8273   
 8274               gNeedFATWrite = FALSE;
 8275           }
 8276   
 8277           // Load the new sector
 8278           if (!MDD_SectorRead (l, gFATBuffer))
 8279           {
 8280               gLastFATSectorRead = 0xFFFF;
 8281               return ClusterFailValue;
 8282           }
 8283           else
 8284           {
 8285               gLastFATSectorRead = l;
 8286           }
 8287       }
 8288   
 8289   #ifdef SUPPORT_FAT32 // If FAT32 supported.
 8290       if (dsk->type == FAT32)  // Refer page 16 of FAT requirement.
 8291       {
 8292           RAMwrite (gFATBuffer, p,   ((value & 0x000000ff)));         // lsb,1st byte of cluster value
 8293           RAMwrite (gFATBuffer, p+1, ((value & 0x0000ff00) >> 8));
 8294           RAMwrite (gFATBuffer, p+2, ((value & 0x00ff0000) >> 16));
 8295           RAMwrite (gFATBuffer, p+3, ((value & 0x0f000000) >> 24));   // the MSB nibble is supposed to be "0" in FAT32. So mask it.
 8296       }
 8297       else
 8298       
 8299   #endif
 8300       {
 8301           if (dsk->type == FAT16)
 8302           {
 8303               RAMwrite (gFATBuffer, p, value);            //lsB
 8304               RAMwrite (gFATBuffer, p+1, ((value&0x0000ff00) >> 8));    // msB
 8305           }
 8306           else if (dsk->type == FAT12)
 8307           {
 8308               // Get the current byte from the FAT
 8309               c = RAMread (gFATBuffer, p);
 8310               if (q)
 8311               {
 8312                   c = ((value & 0x0F) << 4) | ( c & 0x0F);
 8313               }
 8314               else
 8315               {
 8316                   c = (value & 0xFF);
 8317               }
 8318               // Write in those bits
 8319               RAMwrite (gFATBuffer, p, c);
 8320   
 8321               // FAT12 entries can cross sector boundaries
 8322               // Check if we need to load a new sector
 8323               p = (p +1) & (dsk->sectorSize-1);
 8324               if (p == 0)
 8325               {
 8326                   // call this function to update the FAT on the card
 8327                   if (WriteFAT (dsk, 0,0,TRUE))
 8328                       return ClusterFailValue;
 8329   
 8330                   // Load the next sector
 8331                   if (!MDD_SectorRead (l +1, gFATBuffer))
 8332                   {
 8333                       gLastFATSectorRead = 0xFFFF;
 8334                       return ClusterFailValue;
 8335                   }
 8336                   else
 8337                   {
 8338                       gLastFATSectorRead = l + 1;
 8339                   }
 8340               }
 8341   
 8342               // Get the second byte of the table entry
 8343               c = RAMread (gFATBuffer, p);
 8344               if (q)
 8345               {
 8346                   c = (value >> 4);
 8347               }
 8348               else
 8349               {
 8350                   c = ((value >> 8) & 0x0F) | (c & 0xF0);
 8351               }
 8352               RAMwrite (gFATBuffer, p, c);
 8353           }
 8354       }
 8355       gNeedFATWrite = TRUE;
 8356   
 8357       return 0;
 8358   }
 8359   #endif
 8360   
 8361   
 8362   #ifdef ALLOW_DIRS
 8363   
 8364   // This string is used by dir functions to hold dir names temporarily
 8365   #if defined(SUPPORT_LFN)
 8366   	char tempDirectoryString [522];
 8367   #else
 8368   	char tempDirectoryString [14];
 8369   #endif
 8370   /**************************************************************************
 8371     Function:
 8372       int FSchdir (char * path)
 8373     Summary:
 8374       Change the current working directory as per the path specified in Ascii format
 8375     Conditions:
 8376       None
 8377     Input:
 8378       path - The path of the directory to change to.
 8379     Return Values:
 8380       0 -   The current working directory was changed successfully
 8381       EOF - The current working directory could not be changed
 8382     Side Effects:
 8383       The current working directory may be changed. The FSerrno variable will
 8384       be changed.
 8385     Description:
 8386       The FSchdir function passes a RAM pointer to the path to the
 8387       chdirhelper function.
 8388     Remarks:
 8389       None
 8390     **************************************************************************/
 8391   
 8392   int FSchdir (char * path)
 8393   {
 8394       return chdirhelper (0, path, NULL);
 8395   }
 8396   
 8397   /**************************************************************************
 8398     Function:
 8399       int wFSchdir (unsigned short int * path)
 8400     Summary:
 8401       Change the current working directory as per the path specified in UTF16 format
 8402     Conditions:
 8403       None
 8404     Input:
 8405       path - The path of the directory to change to.
 8406     Return Values:
 8407       0 -   The current working directory was changed successfully
 8408       EOF - The current working directory could not be changed
 8409     Side Effects:
 8410       The current working directory may be changed. The FSerrno variable will
 8411       be changed.
 8412     Description:
 8413       The wFSchdir function passes a RAM pointer to the path to the
 8414       chdirhelper function.
 8415     Remarks:
 8416       None
 8417     **************************************************************************/
 8418   #ifdef SUPPORT_LFN
 8419   int wFSchdir (unsigned short int * path)
 8420   {
 8421   	int result;
 8422   	utfModeFileName = TRUE;
 8423       result = chdirhelper (0, (char *)path, NULL);
 8424   	utfModeFileName = FALSE;
 8425       return result;
 8426   }
 8427   #endif
 8428   
 8429   /**************************************************************************
 8430     Function:
 8431       int FSchdirpgm (const rom char * path)
 8432     Summary:
 8433       Changed the CWD with a path in ROM on PIC18
 8434     Conditions:
 8435       None
 8436     Input:
 8437       path - The path of the directory to change to (ROM)
 8438     Return Values:
 8439       0 -   The current working directory was changed successfully
 8440       EOF - The current working directory could not be changed
 8441     Side Effects:
 8442       The current working directory may be changed. The FSerrno variable will
 8443       be changed.
 8444     Description:
 8445       The FSchdirpgm function passes a PIC18 ROM path pointer to the
 8446       chdirhelper function.
 8447     Remarks:
 8448       This function is for use with PIC18 when passing arguments in ROM
 8449     **************************************************************************/
 8450   
 8451   #ifdef ALLOW_PGMFUNCTIONS
 8452   int FSchdirpgm (const rom char * path)
 8453   {
 8454       return chdirhelper (1, NULL, path);
 8455   }
 8456   
 8457   /**************************************************************************
 8458     Function:
 8459       int wFSchdirpgm (const rom unsigned short int * path)
 8460     Summary:
 8461       Changed the CWD with a path in ROM on PIC18
 8462     Conditions:
 8463       None
 8464     Input:
 8465       path - The path of the directory to change to (ROM)
 8466     Return Values:
 8467       0 -   The current working directory was changed successfully
 8468       EOF - The current working directory could not be changed
 8469     Side Effects:
 8470       The current working directory may be changed. The FSerrno variable will
 8471       be changed.
 8472     Description:
 8473       The FSchdirpgm function passes a PIC18 ROM path pointer to the
 8474       chdirhelper function.
 8475     Remarks:
 8476       This function is for use with PIC18 when passing arguments in ROM
 8477     **************************************************************************/
 8478   #ifdef SUPPORT_LFN
 8479   int wFSchdirpgm (const rom unsigned short int * path)
 8480   {
 8481   	int result;
 8482   	utfModeFileName = TRUE;
 8483       result = chdirhelper (1, NULL, (const char *)path);
 8484   	utfModeFileName = FALSE;
 8485   	return result;
 8486   }
 8487   #endif
 8488   
 8489   #endif
 8490   
 8491   /*************************************************************************
 8492     Function:
 8493       // PIC24/30/33/32
 8494       int chdirhelper (BYTE mode, char * ramptr, char * romptr);
 8495       // PIC18
 8496       int chdirhelper (BYTE mode, char * ramptr, const rom char * romptr);
 8497     Summary:
 8498       Helper function for FSchdir
 8499     Conditions:
 8500       None
 8501     Input:
 8502       mode -    Indicates which path pointer to use
 8503       ramptr -  Pointer to the path specified in RAM
 8504       romptr -  Pointer to the path specified in ROM
 8505     Return Values:
 8506       0 -   Directory was changed successfully.
 8507       EOF - Directory could not be changed.
 8508     Side Effects:
 8509       The current working directory will be changed. The FSerrno variable
 8510       will be changed. Any unwritten data in the data buffer will be written
 8511       to the device.
 8512     Description:
 8513       This helper function is used by the FSchdir function. If the path
 8514       argument is specified in ROM for PIC18 this function will be able to
 8515       parse it correctly.  The function will loop through a switch statement
 8516       to process the tokens in the path string.  Dot or dotdot entries are
 8517       handled in the first case statement.  A backslash character is handled
 8518       in the second case statement (note that this case statement will only
 8519       be used if backslash is the first character in the path; backslash
 8520       token delimiters will automatically be skipped after each token in the
 8521       path is processed).  The third case statement will handle actual
 8522       directory name strings.
 8523     Remarks:
 8524       None.
 8525     *************************************************************************/
 8526   
 8527   #ifdef ALLOW_PGMFUNCTIONS
 8528   int chdirhelper (BYTE mode, char * ramptr, const rom char * romptr)
 8529   #else
 8530   int chdirhelper (BYTE mode, char * ramptr, char * romptr)
 8531   #endif
 8532   {
 8533       unsigned short int i,j,k = 0;
 8534       WORD curent = 1;
 8535       DIRENTRY entry;
 8536       char * temppath = ramptr;
 8537   #ifdef ALLOW_PGMFUNCTIONS
 8538       rom char * temppath2 = romptr;
 8539       rom unsigned short int * utf16path2 = (rom unsigned short int *)romptr;
 8540   #endif
 8541   	#ifdef SUPPORT_LFN
 8542   		unsigned short int *utf16path = (unsigned short int *)ramptr;
 8543   	#endif
 8544   
 8545       FSFILE tempCWDobj2;
 8546       FILEOBJ tempCWD = &tempCWDobj2;
 8547   
 8548       FileObjectCopy (tempCWD, cwdptr);
 8549   
 8550       FSerrno = CE_GOOD;
 8551   
 8552      // Check the first char of the path
 8553   #ifdef ALLOW_PGMFUNCTIONS
 8554       if (mode)
 8555   	{
 8556   		#ifdef SUPPORT_LFN
 8557   		if(utfModeFileName)

 8558   		{

 8559   			i = *utf16path2;
 8560   		}
 8561   		else
 8562   		#endif
 8563   		{
 8564   			i = *temppath2;
 8565   		}
 8566       }
 8567       else
 8568   #endif
 8569   	{
 8570   		#ifdef SUPPORT_LFN
 8571   		if(utfModeFileName)

 8572   		{

 8573   			i = *utf16path;
 8574   		}
 8575   		else
 8576   		#endif
 8577   		{
 8578   			i = *temppath;
 8579   		}
 8580       }
 8581   
 8582   	// if NULL character return error
 8583       if (i == 0)
 8584       {
 8585           FSerrno = CE_INVALID_ARGUMENT;
 8586           return -1;
 8587       }
 8588   
 8589       while(1)
 8590       {
 8591           switch (i)
 8592           {
 8593               // First case: dot or dotdot entry
 8594               case '.':
 8595                   // Move past the dot
 8596   #ifdef ALLOW_PGMFUNCTIONS
 8597                   if (mode)
 8598                   {
 8599   					#ifdef SUPPORT_LFN
 8600   					if(utfModeFileName)

 8601   					{

 8602                   	    utf16path2++;
 8603                   	    i = *utf16path2;
 8604   					}
 8605   					else
 8606   					#endif
 8607   					{
 8608                   	    temppath2++;
 8609                   	    i = *temppath2;
 8610   					}
 8611                   }
 8612                   else
 8613                   {
 8614   #endif
 8615   					#ifdef SUPPORT_LFN
 8616   					if(utfModeFileName)

 8617   					{

 8618                   	    utf16path++;
 8619                   	    i = *utf16path;
 8620   					}
 8621   					else
 8622   					#endif
 8623   					{
 8624                   	    temppath++;
 8625                   	    i = *temppath;
 8626   					}
 8627   #ifdef ALLOW_PGMFUNCTIONS
 8628                   }
 8629   #endif
 8630                   // Check if it's a dotdot entry
 8631                   if (i == '.')
 8632                   {
 8633                       // Increment the path variable
 8634   #ifdef ALLOW_PGMFUNCTIONS
 8635                       if (mode)
 8636                       {
 8637   						#ifdef SUPPORT_LFN
 8638   						if(utfModeFileName)

 8639   						{

 8640                   		    utf16path2++;
 8641                   		    i = *utf16path2;
 8642   						}
 8643   						else
 8644   						#endif
 8645   						{
 8646                   		    temppath2++;
 8647                   		    i = *temppath2;
 8648   						}
 8649                       }
 8650                       else
 8651                       {
 8652   #endif
 8653   						#ifdef SUPPORT_LFN
 8654   						if(utfModeFileName)

 8655   						{

 8656                   		    utf16path++;
 8657                   		    i = *utf16path;
 8658   						}
 8659   						else
 8660   						#endif
 8661   						{
 8662                   		    temppath++;
 8663                   		    i = *temppath;
 8664   						}
 8665   #ifdef ALLOW_PGMFUNCTIONS
 8666                       }
 8667   #endif
 8668                       // Check if we're in the root
 8669                       if (tempCWD->dirclus == FatRootDirClusterValue)
 8670                       {
 8671                           // Fails if there's a dotdot chdir from the root
 8672                           FSerrno = CE_INVALID_ARGUMENT;
 8673                           return -1;
 8674                       }
 8675                       else
 8676                       {
 8677                           // Cache the dotdot entry
 8678                           tempCWD->dirccls = tempCWD->dirclus;
 8679                           curent = 1;
 8680                           entry = Cache_File_Entry (tempCWD, &curent, TRUE);
 8681                           if (entry == NULL)
 8682                           {
 8683                               FSerrno = CE_BADCACHEREAD;
 8684                               return -1;
 8685                           }
 8686   
 8687                           // Get the cluster
 8688                           tempCWD->dirclus = GetFullClusterNumber(entry); // Get Complete Cluster number.
 8689                           tempCWD->dirccls = tempCWD->dirclus;
 8690   
 8691                           // If we changed to root, record the name
 8692                           if (tempCWD->dirclus == VALUE_DOTDOT_CLUSTER_VALUE_FOR_ROOT) // "0" is the value of Dotdot entry for Root in both FAT types.
 8693                           {
 8694                               j = 0;
 8695                               tempCWD->name[j++] = '\\';
 8696   //                            if(utfModeFileName)
 8697   //							{
 8698   //                            	tempCWD->name[j++] = 0x00;
 8699   //							}
 8700                               for (;j < 11;)
 8701                               {
 8702                                   tempCWD->name[j] = 0x20;
 8703                               	++j;
 8704                               }
 8705   
 8706                               /* While moving to Root, get the Root cluster value */
 8707                               tempCWD->dirccls = FatRootDirClusterValue;
 8708                               tempCWD->dirclus = FatRootDirClusterValue;
 8709                           }
 8710                           else
 8711                           {
 8712                               // Otherwise set the name to ..
 8713                               j = 0;
 8714                               tempCWD->name[j++] = '.';
 8715   //                            if(utfModeFileName)
 8716   //							{
 8717   //                            	tempCWD->name[j++] = 0x00;
 8718   //                            	tempCWD->name[j++] = '.';
 8719   //                            	tempCWD->name[j++] = 0x00;
 8720   //							}
 8721   //							else
 8722   							{
 8723                               	tempCWD->name[j++] = '.';
 8724                               }
 8725                               for (; j < 11;)
 8726                               {
 8727                                   tempCWD->name[j] = 0x20;
 8728                               	++j;
 8729                               }
 8730                           }
 8731                           // Cache the dot entry
 8732                           curent = 0;
 8733                           if (Cache_File_Entry(tempCWD, &curent, TRUE) == NULL)
 8734                           {
 8735                               FSerrno = CE_BADCACHEREAD;
 8736                               return -1;
 8737                           }
 8738                           // Move past the next backslash, if necessary
 8739                           while (i == '\\')
 8740                           {
 8741   #ifdef ALLOW_PGMFUNCTIONS
 8742                               if (mode)
 8743                               {
 8744   								#ifdef SUPPORT_LFN
 8745   								if(utfModeFileName)

 8746   								{

 8747                   				    utf16path2++;
 8748                   				    i = *utf16path2;
 8749   								}
 8750   								else
 8751   								#endif
 8752   								{
 8753                   				    temppath2++;
 8754                   				    i = *temppath2;
 8755   								}
 8756                               }
 8757                               else
 8758                               {
 8759   #endif
 8760   								#ifdef SUPPORT_LFN
 8761   								if(utfModeFileName)

 8762   								{

 8763                   				    utf16path++;
 8764                   				    i = *utf16path;
 8765   								}
 8766   								else
 8767   								#endif
 8768   								{
 8769                   				    temppath++;
 8770                   				    i = *temppath;
 8771   								}
 8772   #ifdef ALLOW_PGMFUNCTIONS
 8773                               }
 8774   #endif
 8775                           }
 8776                           // Copy and return, if we're at the end
 8777                           if (i == 0)
 8778                           {
 8779                               FileObjectCopy (cwdptr, tempCWD);
 8780                               return 0;
 8781                           }
 8782                       }
 8783                   }
 8784                   else
 8785                   {
 8786                       // If we ended with a . entry,
 8787                       // just return what we have
 8788                       if (i == 0)
 8789                       {
 8790                           FileObjectCopy (cwdptr, tempCWD);
 8791                           return 0;
 8792                       }
 8793                       else
 8794                       {
 8795                           if (i == '\\')
 8796                           {
 8797                               while (i == '\\')
 8798                               {
 8799   #ifdef ALLOW_PGMFUNCTIONS
 8800                                   if (mode)
 8801                                   {
 8802   									#ifdef SUPPORT_LFN
 8803   									if(utfModeFileName)

 8804   									{

 8805                   					    utf16path2++;
 8806                   					    i = *utf16path2;
 8807   									}
 8808   									else
 8809   									#endif
 8810   									{
 8811                   					    temppath2++;
 8812                   					    i = *temppath2;
 8813   									}
 8814                                   }
 8815                                   else
 8816                                   {
 8817   #endif
 8818   									#ifdef SUPPORT_LFN
 8819   									if(utfModeFileName)

 8820   									{

 8821                   					    utf16path++;
 8822                   					    i = *utf16path;
 8823   									}
 8824   									else
 8825   									#endif
 8826   									{
 8827                   					    temppath++;
 8828                   					    i = *temppath;
 8829   									}
 8830   #ifdef ALLOW_PGMFUNCTIONS
 8831                                   }
 8832   #endif
 8833                               }
 8834                               if (i == 0)
 8835                               {
 8836                                   FileObjectCopy (cwdptr, tempCWD);
 8837                                   return 0;
 8838                               }
 8839                           }
 8840                           else
 8841                           {
 8842                               // Anything else after a dot doesn't make sense
 8843                               FSerrno = CE_INVALID_ARGUMENT;
 8844                               return -1;
 8845                           }
 8846                       }
 8847                   }
 8848   
 8849                   break;
 8850   
 8851               // Second case: the first char is the root backslash
 8852               // We will ONLY switch to this case if the first char
 8853               // of the path is a backslash
 8854               case '\\':
 8855               // Increment pointer to second char
 8856   #ifdef ALLOW_PGMFUNCTIONS
 8857               if (mode)
 8858               {
 8859   				#ifdef SUPPORT_LFN
 8860   				if(utfModeFileName)

 8861   				{

 8862                       utf16path2++;
 8863                       i = *utf16path2;
 8864   				}
 8865   				else
 8866   				#endif
 8867   				{
 8868                       temppath2++;
 8869                       i = *temppath2;
 8870   				}
 8871               }
 8872               else
 8873               {
 8874   #endif
 8875   				#ifdef SUPPORT_LFN
 8876   				if(utfModeFileName)

 8877   				{

 8878                       utf16path++;
 8879                       i = *utf16path;
 8880   				}
 8881   				else
 8882   				#endif
 8883   				{
 8884                       temppath++;
 8885                       i = *temppath;
 8886   				}
 8887   #ifdef ALLOW_PGMFUNCTIONS
 8888               }
 8889   #endif
 8890               // Can't start the path with multiple backslashes
 8891               if (i == '\\')
 8892               {
 8893                   FSerrno = CE_INVALID_ARGUMENT;
 8894                   return -1;
 8895               }
 8896   
 8897               if (i == 0)
 8898               {
 8899                   // The user is changing directory to
 8900                   // the root
 8901                   cwdptr->dirclus = FatRootDirClusterValue;
 8902                   cwdptr->dirccls = FatRootDirClusterValue;
 8903                   j = 0;
 8904                   cwdptr->name[j++] = '\\';
 8905   //                if(utfModeFileName)
 8906   //				{
 8907   //                	cwdptr->name[j++] = 0x00;
 8908   //				}
 8909                   for (; j < 11;)
 8910                   {
 8911                       cwdptr->name[j] = 0x20;
 8912                   	++j;
 8913                   }
 8914                   return 0;
 8915               }
 8916               else
 8917               {
 8918                   // Our first char is the root dir switch
 8919                   tempCWD->dirclus = FatRootDirClusterValue;
 8920                   tempCWD->dirccls = FatRootDirClusterValue;
 8921                   j = 0;
 8922                   tempCWD->name[j++] = '\\';
 8923   //                if(utfModeFileName)
 8924   //				{
 8925   //                	tempCWD->name[j++] = 0x00;
 8926   //				}
 8927                   for (; j < 11;)
 8928                   {
 8929                       tempCWD->name[j] = 0x20;
 8930                   	++j;
 8931                   }
 8932               }
 8933               break;
 8934   
 8935           default:
 8936               // We should be at the beginning of a string of letters/numbers
 8937               j = 0;
 8938   #ifdef ALLOW_PGMFUNCTIONS
 8939               if (mode)
 8940               {
 8941   				#ifdef SUPPORT_LFN
 8942   				if(utfModeFileName)

 8943   				{

 8944               	    // Change directories as specified
 8945   					k = 512;

 8946   

 8947               	    // Parse the next token
 8948               	    while ((i != 0) && (i != '\\') && (j < k))
 8949               	    {
 8950               	        tempDirectoryString[j++] = i;
 8951               	        tempDirectoryString[j++] = i >> 8;
 8952               	        i = *(++utf16path2);
 8953               	    }
 8954   
 8955   					tempDirectoryString[j++] = 0;
 8956   				}

 8957   				else

 8958   				#endif
 8959           		{

 8960   					#if defined(SUPPORT_LFN)

 8961   						k = 256;

 8962   					#else

 8963   						k = 12;

 8964   					#endif

 8965   

 8966               	    // Parse the next token
 8967               	    while ((i != 0) && (i != '\\') && (j < k))
 8968               	    {
 8969               	        tempDirectoryString[j++] = i;
 8970               	        i = *(++temppath2);
 8971               	    }
 8972   				}

 8973               }
 8974               else
 8975               {
 8976   #endif
 8977   				#ifdef SUPPORT_LFN
 8978   				if(utfModeFileName)

 8979   				{

 8980               	    // Change directories as specified
 8981   					k = 512;

 8982   

 8983               	    // Parse the next token
 8984               	    while ((i != 0) && (i != '\\') && (j < k))
 8985               	    {
 8986               	        tempDirectoryString[j++] = i;
 8987               	        tempDirectoryString[j++] = i >> 8;
 8988               	        i = *(++utf16path);
 8989               	    }
 8990   
 8991   					tempDirectoryString[j++] = 0;
 8992   				}

 8993   				else

 8994   				#endif
 8995           		{

 8996   					#if defined(SUPPORT_LFN)

 8997   						k = 256;

 8998   					#else

 8999   						k = 12;

 9000   					#endif

 9001   

 9002               	    // Parse the next token
 9003               	    while ((i != 0) && (i != '\\') && (j < k))
 9004               	    {
 9005               	        tempDirectoryString[j++] = i;
 9006               	        i = *(++temppath);
 9007               	    }
 9008   				}

 9009   #ifdef ALLOW_PGMFUNCTIONS
 9010               }
 9011   #endif
 9012    
 9013               tempDirectoryString[j++] = 0;
 9014   
 9015               // We got a whole 12 chars
 9016               // There could be more- truncate it
 9017               if (j > k)
 9018               {
 9019                   while ((i != 0) && (i != '\\'))
 9020                   {
 9021   #ifdef ALLOW_PGMFUNCTIONS
 9022                       if (mode)
 9023                       {
 9024   						#ifdef SUPPORT_LFN
 9025   						if(utfModeFileName)

 9026   						{

 9027                           	i = *(++utf16path2);
 9028                       	}
 9029   						else
 9030   						#endif
 9031   						{

 9032                           	i = *(++temppath2);
 9033                       	}
 9034                       }
 9035                       else
 9036                       {
 9037   #endif
 9038   						#ifdef SUPPORT_LFN
 9039   						if(utfModeFileName)

 9040   						{

 9041                           	i = *(++utf16path);
 9042                       	}
 9043   						else
 9044   						#endif
 9045   						{

 9046                           	i = *(++temppath);
 9047                       	}
 9048   #ifdef ALLOW_PGMFUNCTIONS
 9049                       }
 9050   #endif
 9051                   }
 9052               }
 9053   
 9054               if (FormatDirName (tempDirectoryString, tempCWD,0) == FALSE)
 9055                   return -1;
 9056   
 9057               // copy file object over
 9058               FileObjectCopy(&gFileTemp, tempCWD);
 9059   
 9060               // See if the directory is there
 9061               if(FILEfind (&gFileTemp, tempCWD, LOOK_FOR_MATCHING_ENTRY, 0) != CE_GOOD)
 9062               {
 9063                   // Couldn't find the DIR
 9064                   FSerrno = CE_DIR_NOT_FOUND;
 9065                   return -1;
 9066               }
 9067               else
 9068               {
 9069                   // Found the file
 9070                   // Check to make sure it's actually a directory
 9071                   if ((gFileTemp.attributes & ATTR_DIRECTORY) == 0 )
 9072                   {
 9073                       FSerrno = CE_INVALID_ARGUMENT;
 9074                       return -1;
 9075                   }
 9076   
 9077                   // Get the new name
 9078   				#if defined(SUPPORT_LFN)
 9079   					if(!tempCWD->utf16LFNlength)
 9080   				#endif
 9081                   		for (j = 0; j < 11; j++)
 9082                   		{
 9083                       		tempCWD->name[j] = gFileTemp.name[j];
 9084                   		}
 9085   
 9086                   tempCWD->dirclus = gFileTemp.cluster;
 9087                   tempCWD->dirccls = tempCWD->dirclus;
 9088               }
 9089   
 9090               if (i == 0)
 9091               {
 9092                   // If we're at the end of the string, we're done
 9093                   FileObjectCopy (cwdptr, tempCWD);
 9094                   return 0;
 9095               }
 9096               else
 9097               {
 9098                   while (i == '\\')
 9099                   {
 9100                       // If we get to another backslash, increment past it
 9101   #ifdef ALLOW_PGMFUNCTIONS
 9102                       if (mode)
 9103                       {
 9104   						#ifdef SUPPORT_LFN
 9105   						if(utfModeFileName)

 9106   						{

 9107                   		    utf16path2++;
 9108                   		    i = *utf16path2;
 9109   						}
 9110   						else
 9111   						#endif
 9112   						{
 9113                   		    temppath2++;
 9114                   		    i = *temppath2;
 9115   						}
 9116                       }
 9117                       else
 9118                       {
 9119   #endif
 9120   						#ifdef SUPPORT_LFN
 9121   						if(utfModeFileName)

 9122   						{

 9123                   		    utf16path++;
 9124                   		    i = *utf16path;
 9125   						}
 9126   						else
 9127   						#endif
 9128   						{
 9129                   		    temppath++;
 9130                   		    i = *temppath;
 9131   						}
 9132   #ifdef ALLOW_PGMFUNCTIONS
 9133                       }
 9134   #endif
 9135                       if (i == 0)
 9136                       {
 9137                           FileObjectCopy (cwdptr, tempCWD);
 9138                           return 0;
 9139                       }
 9140                   }
 9141               }
 9142               break;
 9143           }
 9144       } // loop
 9145   }
 9146   
 9147   
 9148   
 9149   // This string is used by FSgetcwd to return the cwd name if the path
 9150   // passed into the function is NULL
 9151   char defaultArray [10];
 9152   
 9153   
 9154   /**************************************************************
 9155     Function:
 9156       char * FSgetcwd (char * path, int numchars)
 9157     Summary:
 9158       Get the current working directory path in Ascii format
 9159     Conditions:
 9160       None
 9161     Input:
 9162       path -      Pointer to the array to return the cwd name in
 9163       numchars -  Number of chars in the path
 9164     Return Values:
 9165       char * - The cwd name string pointer (path or defaultArray)
 9166       NULL -   The current working directory name could not be loaded.
 9167     Side Effects:
 9168       The FSerrno variable will be changed
 9169     Description:
 9170       The FSgetcwd function will get the name of the current
 9171       working directory and return it to the user.  The name
 9172       will be copied into the buffer pointed to by 'path,'
 9173       starting at the root directory and copying as many chars
 9174       as possible before the end of the buffer.  The buffer
 9175       size is indicated by the 'numchars' argument.  The first
 9176       thing this function will do is load the name of the current
 9177       working directory, if it isn't already present.  This could
 9178       occur if the user switched to the dotdot entry of a
 9179       subdirectory immediately before calling this function.  The
 9180       function will then copy the current working directory name
 9181       into the buffer backwards, and insert a backslash character.
 9182       Next, the function will continuously switch to the previous
 9183       directories and copy their names backwards into the buffer
 9184       until it reaches the root.  If the buffer overflows, it
 9185       will be treated as a circular buffer, and data will be
 9186       copied over existing characters, starting at the beginning.
 9187       Once the root directory is reached, the text in the buffer
 9188       will be swapped, so that the buffer contains as much of the
 9189       current working directory name as possible, starting at the
 9190       root.
 9191     Remarks:
 9192       None
 9193     **************************************************************/
 9194   char * FSgetcwd (char * path, int numchars)
 9195   {
 9196       // If path is passed in as null, set up a default
 9197       // array with 10 characters
 9198       unsigned short int totalchars = (path == NULL) ? 10 : numchars;
 9199       char * returnPointer;
 9200       char * bufferEnd;
 9201       FILEOBJ tempCWD = &gFileTemp;
 9202       BYTE bufferOverflow = FALSE;
 9203       signed char j;
 9204       DWORD curclus;
 9205       WORD fHandle, tempindex;
 9206       short int i = 0, index = 0;
 9207       char aChar;
 9208       DIRENTRY entry;
 9209   
 9210   	#if defined(SUPPORT_LFN)
 9211   	WORD prevHandle;
 9212   	UINT16_VAL tempShift;
 9213   	FSFILE cwdTemp;
 9214   	LFN_ENTRY *lfno;
 9215   	unsigned short int *tempLFN = (unsigned short int *)&tempDirectoryString[0];
 9216   	#endif
 9217   
 9218       FSerrno = CE_GOOD;
 9219   
 9220       // Set up the return value
 9221       if (path == NULL)
 9222           returnPointer = defaultArray;
 9223       else
 9224       {
 9225           returnPointer = path;
 9226           if (numchars == 0)
 9227           {
 9228               FSerrno = CE_INVALID_ARGUMENT;
 9229               return NULL;
 9230           }
 9231       }
 9232   
 9233       bufferEnd = returnPointer + totalchars - 1;
 9234   
 9235       FileObjectCopy (tempCWD, cwdptr);
 9236   
 9237       if (((tempCWD->name[0] == '.') && (tempCWD->name[1] == '.'))
 9238   		#if defined(SUPPORT_LFN)	
 9239   	 	|| tempCWD->utf16LFNlength
 9240   		#endif
 9241   		)
 9242       {
 9243           // We last changed directory into a dotdot entry
 9244           // Save the value of the current directory
 9245           curclus = tempCWD->dirclus;
 9246           // Put this dir's dotdot entry into the dirclus
 9247           // Our cwd absolutely is not the root
 9248           fHandle = 1;
 9249           tempCWD->dirccls = tempCWD->dirclus;
 9250           entry = Cache_File_Entry (tempCWD,&fHandle, TRUE);
 9251           if (entry == NULL)
 9252           {
 9253               FSerrno = CE_BADCACHEREAD;
 9254               return NULL;
 9255           }
 9256   
 9257          // Get the cluster
 9258          TempClusterCalc = GetFullClusterNumber(entry); // Get complete cluster number.
 9259   
 9260           // For FAT32, if the .. entry is 0, the cluster won't be 0
 9261   #ifdef SUPPORT_FAT32
 9262           if (TempClusterCalc == VALUE_DOTDOT_CLUSTER_VALUE_FOR_ROOT)
 9263           {
 9264               tempCWD->dirclus = FatRootDirClusterValue;
 9265           }
 9266           else
 9267   #endif
 9268               tempCWD->dirclus = TempClusterCalc;
 9269   
 9270           tempCWD->dirccls = tempCWD->dirclus;
 9271   
 9272           // Find the direntry for the entry we were just in
 9273           fHandle = 0;
 9274           entry = Cache_File_Entry (tempCWD, &fHandle, TRUE);
 9275           if (entry == NULL)
 9276           {
 9277               FSerrno = CE_BADCACHEREAD;
 9278               return NULL;
 9279           }
 9280   
 9281           // Get the cluster
 9282           TempClusterCalc = GetFullClusterNumber(entry); // Get complete cluster number.
 9283   
 9284           while ((TempClusterCalc != curclus) ||
 9285               ((TempClusterCalc == curclus) &&
 9286               (((unsigned char)entry->DIR_Name[0] == 0xE5) || (entry->DIR_Attr == ATTR_VOLUME) || (entry->DIR_Attr == ATTR_LONG_NAME))))
 9287           {
 9288               fHandle++;
 9289               entry = Cache_File_Entry (tempCWD, &fHandle, FALSE);
 9290               if (entry == NULL)
 9291               {
 9292                   FSerrno = CE_BADCACHEREAD;
 9293                   return NULL;
 9294               }
 9295   
 9296               // Get the cluster
 9297               TempClusterCalc = GetFullClusterNumber(entry); // Get complete cluster number in a loop.
 9298           }
 9299   
 9300   		#if defined(SUPPORT_LFN)
 9301          	FileObjectCopy (&cwdTemp, tempCWD);
 9302   	   	prevHandle = fHandle - 1;

 9303   	   	lfno = (LFN_ENTRY *)Cache_File_Entry (tempCWD, &prevHandle, FALSE);
 9304   

 9305   	   	while((lfno->LFN_Attribute == ATTR_LONG_NAME) && (lfno->LFN_SequenceNo != DIR_DEL)

 9306   	   			&& (lfno->LFN_SequenceNo != DIR_EMPTY))

 9307   	   	{

 9308   	   		tempShift.byte.LB = lfno->LFN_Part1[0];

 9309   	   		tempShift.byte.HB = lfno->LFN_Part1[1];

 9310   	   		tempLFN[i++] = tempShift.Val;

 9311   	   		tempShift.byte.LB = lfno->LFN_Part1[2];

 9312   	   		tempShift.byte.HB = lfno->LFN_Part1[3];

 9313   	   		tempLFN[i++] = tempShift.Val;

 9314   	   		tempShift.byte.LB = lfno->LFN_Part1[4];

 9315   	   		tempShift.byte.HB = lfno->LFN_Part1[5];

 9316   	   		tempLFN[i++] = tempShift.Val;

 9317   	   		tempShift.byte.LB = lfno->LFN_Part1[6];

 9318   	   		tempShift.byte.HB = lfno->LFN_Part1[7];

 9319   	   		tempLFN[i++] = tempShift.Val;

 9320   	   		tempShift.byte.LB = lfno->LFN_Part1[8];

 9321   	   		tempShift.byte.HB = lfno->LFN_Part1[9];

 9322   	   		tempLFN[i++] = tempShift.Val;

 9323   

 9324   	   		tempLFN[i++] = lfno->LFN_Part2[0];

 9325   	   		tempLFN[i++] = lfno->LFN_Part2[1];

 9326   	   		tempLFN[i++] = lfno->LFN_Part2[2];

 9327   	   		tempLFN[i++] = lfno->LFN_Part2[3];

 9328   	   		tempLFN[i++] = lfno->LFN_Part2[4];

 9329   	   		tempLFN[i++] = lfno->LFN_Part2[5];

 9330   

 9331   	   		tempLFN[i++] = lfno->LFN_Part3[0];

 9332   	   		tempLFN[i++] = lfno->LFN_Part3[1];

 9333   	   

 9334   	   		prevHandle = prevHandle - 1;

 9335   	   		lfno = (LFN_ENTRY *)Cache_File_Entry (tempCWD, &prevHandle, FALSE);

 9336   	   	}

 9337   	   	FileObjectCopy (tempCWD, &cwdTemp);
 9338   		#endif
 9339   
 9340   	   	if(i == 0)
 9341   	   	{
 9342   	   	    for (j = 0; j < 11; j++)
 9343          	    {
 9344           	    tempCWD->name[j] = entry->DIR_Name[j];
 9345           	    cwdptr->name[j] = entry->DIR_Name[j];
 9346          	    }
 9347   			#if defined(SUPPORT_LFN)
 9348   	   		cwdptr->utf16LFNlength = 0;

 9349   	   		tempCWD->utf16LFNlength = 0;

 9350   			#endif
 9351   	   	}

 9352   		#if defined(SUPPORT_LFN)
 9353   	   	else
 9354   	   	{

 9355   	   		tempCWD->utf16LFNlength = i;

 9356   			for(j = 12;j >= 0;j--)
 9357   			{
 9358   				if((tempLFN[i - j - 1]) == 0x0000)
 9359   				{
 9360   					tempCWD->utf16LFNlength = i - j;
 9361   					break;
 9362   				}
 9363   			}
 9364   			cwdptr->utf16LFNlength = tempCWD->utf16LFNlength;

 9365   	   		tempCWD->utf16LFNptr = (unsigned short int *)&tempDirectoryString[0];

 9366   	   		cwdptr->utf16LFNptr = (unsigned short int *)&tempDirectoryString[0];

 9367   	   	}

 9368   		#endif
 9369           // Reset our temp dir back to that cluster
 9370           tempCWD->dirclus = curclus;
 9371           tempCWD->dirccls = curclus;
 9372           // This will set us at the cwd, but it will actually
 9373           // have the name in the name field this time
 9374       }
 9375   
 9376       // There's actually some kind of name value in the cwd
 9377   	#if defined(SUPPORT_LFN)
 9378       if (((tempCWD->name[0] == '\\') && (tempCWD->utf16LFNlength == 0x0000)) || 
 9379   		((tempCWD->utf16LFNlength != 0x0000) && (tempCWD->utf16LFNptr[0] == (unsigned short int)'\\')) || (numchars == 0x02)) 
 9380   	#else
 9381       if ((tempCWD->name[0] == '\\') || (numchars == 0x02))
 9382   	#endif
 9383       {
 9384           // Easy, our CWD is the root
 9385           *returnPointer = '\\';
 9386           *(returnPointer + 1) = 0;
 9387           return returnPointer;
 9388       }
 9389       else
 9390       {
 9391           index = 0;
 9392           // Loop until we get back to the root
 9393           while (tempCWD->dirclus != FatRootDirClusterValue)
 9394           {
 9395   			#if defined(SUPPORT_LFN)
 9396               if(tempCWD->utf16LFNlength)
 9397               {
 9398   			    i = tempCWD->utf16LFNlength * 2 - 3;
 9399   			    while(i >= 0)
 9400   				{
 9401   					#ifdef SUPPORT_LFN
 9402   					if(twoByteMode)
 9403   					{
 9404   						returnPointer[index++] = tempDirectoryString[i--];
 9405   	            	    if (index == totalchars)
 9406   	            	    {
 9407   	            	        index = 0;
 9408   	            	        bufferOverflow = TRUE;
 9409   	            	    }
 9410   					}
 9411   					else
 9412   					#endif
 9413   					{
 9414   						if(tempDirectoryString[i])
 9415   						{
 9416   							returnPointer[index++] = tempDirectoryString[i];
 9417   	           		  	  	if (index == totalchars)
 9418   	           		  	  	{
 9419   	           		  	  	    index = 0;
 9420   	           		  	  	    bufferOverflow = TRUE;
 9421   	           		  	  	}
 9422   							
 9423   						}
 9424   						i--;
 9425   					}
 9426   				}
 9427   			}
 9428   			else
 9429   			#endif
 9430   			{
 9431   	            j = 10;
 9432   	            while (tempCWD->name[j] == 0x20)
 9433   	                j--;
 9434   	            if (j >= 8)
 9435   	            {
 9436   	                while (j >= 8)
 9437   	                {
 9438   	                    *(returnPointer + index++) = tempCWD->name[j--];
 9439   	                    // This is a circular buffer
 9440   	                    // Any unnecessary values will be overwritten
 9441   	                    if (index == totalchars)
 9442   	                    {
 9443   	                        index = 0;
 9444   	                        bufferOverflow = TRUE;
 9445   	                    }
 9446   
 9447   						#ifdef SUPPORT_LFN
 9448   						if(twoByteMode)
 9449   						{
 9450   							returnPointer[index++] = 0x00;
 9451   	   	    		  	   if (index == totalchars)
 9452   	   	    		  	   {
 9453   	   	    		  	       index = 0;
 9454   	   	    		  	       bufferOverflow = TRUE;
 9455   	   	    		  	   }
 9456   						}
 9457   						#endif
 9458   	                }
 9459   
 9460   	                *(returnPointer + index++) = '.';
 9461   	                if (index == totalchars)
 9462   	                {
 9463   	                    index = 0;
 9464   	                    bufferOverflow = TRUE;
 9465   	                }
 9466   
 9467   					#ifdef SUPPORT_LFN
 9468   					if(twoByteMode)
 9469   					{
 9470   						returnPointer[index++] = 0x00;
 9471   	   	    		    if (index == totalchars)
 9472   	   	    		    {
 9473   	   	    		        index = 0;
 9474   	   	    		        bufferOverflow = TRUE;
 9475   	   	    		    }
 9476   					}
 9477   					#endif
 9478   	            }
 9479   
 9480   	            while (tempCWD->name[j] == 0x20)
 9481   	                j--;
 9482   
 9483   	            while (j >= 0)
 9484   	            {
 9485   	                *(returnPointer + index++) = tempCWD->name[j--];
 9486   	                // This is a circular buffer
 9487   	                // Any unnecessary values will be overwritten
 9488   	                if (index == totalchars)
 9489   	                {
 9490   	                    index = 0;
 9491   	                    bufferOverflow = TRUE;
 9492   	                }
 9493   
 9494   					#ifdef SUPPORT_LFN
 9495   					if(twoByteMode)
 9496   					{
 9497   						returnPointer[index++] = 0x00;
 9498   	   	    		    if (index == totalchars)
 9499   	   	    		    {
 9500   	   	    		        index = 0;
 9501   	   	    		        bufferOverflow = TRUE;
 9502   	   	    		    }
 9503   					}
 9504   					#endif
 9505   	            }
 9506   			}
 9507   
 9508   			#ifdef SUPPORT_LFN
 9509   			if(twoByteMode)
 9510   			{
 9511   				returnPointer[index++] = 0x00;
 9512   	            if (index == totalchars)
 9513   	            {
 9514   	                index = 0;
 9515   	                bufferOverflow = TRUE;
 9516   	            }
 9517   			}
 9518   			#endif
 9519   
 9520               // Put a backslash delimiter in front of the dir name
 9521               *(returnPointer + index++) = '\\';
 9522               if (index == totalchars)
 9523               {
 9524                   index = 0;
 9525                   bufferOverflow = TRUE;
 9526               }
 9527   
 9528               // Load the previous entry
 9529               tempCWD->dirccls = tempCWD->dirclus;
 9530               if (GetPreviousEntry (tempCWD))
 9531               {
 9532                   FSerrno = CE_BAD_SECTOR_READ;
 9533                   return NULL;
 9534               }
 9535           }
 9536       }
 9537   
 9538       // Point the index back at the last char in the string
 9539       index--;
 9540   
 9541       i = 0;
 9542       // Swap the chars in the buffer so they are in the right places
 9543       if (bufferOverflow)
 9544       {
 9545           tempindex = index;
 9546           // Swap the overflowed values in the buffer
 9547           while ((index - i) > 0)
 9548           {
 9549                aChar = *(returnPointer + i);
 9550                *(returnPointer + i) = * (returnPointer + index);
 9551                *(returnPointer + index) = aChar;
 9552                index--;
 9553                i++;
 9554           }
 9555   
 9556           // Point at the non-overflowed values
 9557           i = tempindex + 1;
 9558           index = bufferEnd - returnPointer;
 9559   
 9560           // Swap the non-overflowed values into the right places
 9561           while ((index - i) > 0)
 9562           {
 9563                aChar = *(returnPointer + i);
 9564                *(returnPointer + i) = * (returnPointer + index);
 9565                *(returnPointer + index) = aChar;
 9566                index--;
 9567                i++;
 9568           }
 9569           // All the values should be in the right place now
 9570           // Null-terminate the string
 9571           *(bufferEnd) = 0;
 9572       }
 9573       else
 9574       {
 9575           // There was no overflow, just do one set of swaps
 9576           tempindex = index;
 9577           while ((index - i) > 0)
 9578           {
 9579               aChar = *(returnPointer + i);
 9580               *(returnPointer + i) = * (returnPointer + index);
 9581               *(returnPointer + index) = aChar;
 9582               index--;
 9583               i++;
 9584           }
 9585           *(returnPointer + tempindex + 1) = 0;
 9586       }
 9587   
 9588       return returnPointer;
 9589   }
 9590   
 9591   #ifdef SUPPORT_LFN
 9592   
 9593   /**************************************************************
 9594     Function:
 9595       char * wFSgetcwd (unsigned short int * path, int numchars)
 9596     Summary:
 9597       Get the current working directory path in UTF16 format
 9598     Conditions:
 9599       None
 9600     Input:
 9601       path -      Pointer to the array to return the cwd name in
 9602       numchars -  Number of chars in the path
 9603     Return Values:
 9604       char * - The cwd name string pointer (path or defaultArray)
 9605       NULL -   The current working directory name could not be loaded.
 9606     Side Effects:
 9607       The FSerrno variable will be changed
 9608     Description:
 9609       The FSgetcwd function will get the name of the current
 9610       working directory and return it to the user.  The name
 9611       will be copied into the buffer pointed to by 'path,'
 9612       starting at the root directory and copying as many chars
 9613       as possible before the end of the buffer.  The buffer
 9614       size is indicated by the 'numchars' argument.  The first
 9615       thing this function will do is load the name of the current
 9616       working directory, if it isn't already present.  This could
 9617       occur if the user switched to the dotdot entry of a
 9618       subdirectory immediately before calling this function.  The
 9619       function will then copy the current working directory name
 9620       into the buffer backwards, and insert a backslash character.
 9621       Next, the function will continuously switch to the previous
 9622       directories and copy their names backwards into the buffer
 9623       until it reaches the root.  If the buffer overflows, it
 9624       will be treated as a circular buffer, and data will be
 9625       copied over existing characters, starting at the beginning.
 9626       Once the root directory is reached, the text in the buffer
 9627       will be swapped, so that the buffer contains as much of the
 9628       current working directory name as possible, starting at the
 9629       root.
 9630     Remarks:
 9631       None
 9632     **************************************************************/
 9633   char * wFSgetcwd (unsigned short int * path, int numchars)
 9634   {
 9635   	char *result;
 9636   	twoByteMode = TRUE;
 9637       result = FSgetcwd ((char *)path,numchars);
 9638   	twoByteMode = FALSE;
 9639   	return result;
 9640   }
 9641   #endif
 9642   
 9643   /**************************************************************************
 9644     Function:
 9645       void GetPreviousEntry (FSFILE * fo)
 9646     Summary:
 9647       Get the file entry info for the parent dir of the specified dir
 9648     Conditions:
 9649       Should not be called by the user.
 9650     Input:
 9651       fo -  The file to get the previous entry of
 9652     Return Values:
 9653       0 -  The previous entry was successfully retrieved
 9654       -1 - The previous entry could not be retrieved
 9655     Side Effects:
 9656       None
 9657     Description:
 9658       The GetPreviousEntry function is used by the FSgetcwd function to
 9659       load the previous (parent) directory.  This function will load the
 9660       parent directory and then search through the file entries in that
 9661       directory for one that matches the cluster number of the original
 9662       directory.  When the matching entry is found, the name of the
 9663       original directory is copied into the 'fo' FSFILE object.
 9664     Remarks:
 9665       None.
 9666     **************************************************************************/
 9667   
 9668   BYTE GetPreviousEntry (FSFILE * fo)
 9669   {
 9670       int i,j;
 9671       WORD fHandle = 1;
 9672       DWORD dirclus;
 9673       DIRENTRY dirptr;
 9674   
 9675   	#ifdef SUPPORT_LFN
 9676   		unsigned short int *tempLFN = (unsigned short int *)&tempDirectoryString[0];
 9677   		FSFILE cwdTemp;
 9678   		LFN_ENTRY *lfno;

 9679   		WORD prevHandle;
 9680   		UINT16_VAL tempShift;

 9681   	#endif
 9682   
 9683       // Load the previous entry
 9684       dirptr = Cache_File_Entry (fo, &fHandle, TRUE);
 9685       if (dirptr == NULL)
 9686           return -1;
 9687   
 9688       // Get the cluster
 9689       TempClusterCalc = GetFullClusterNumber(dirptr); // Get complete cluster number.
 9690   
 9691       if (TempClusterCalc == VALUE_DOTDOT_CLUSTER_VALUE_FOR_ROOT)
 9692       {
 9693           // The previous directory is the root
 9694           fo->name[0] = '\\';
 9695           for (i = 0; i < 11; i++)
 9696           {
 9697               fo->name[i] = 0x20;
 9698           }
 9699           fo->dirclus = FatRootDirClusterValue;
 9700           fo->dirccls = FatRootDirClusterValue;
 9701       }
 9702       else
 9703       {
 9704           // Get the directory name
 9705           // Save the previous cluster value
 9706          // Get the cluster
 9707   
 9708           dirclus = TempClusterCalc;
 9709           fo->dirclus = TempClusterCalc;
 9710           fo->dirccls = TempClusterCalc;
 9711   
 9712   
 9713           // Load the previous previous cluster
 9714           dirptr = Cache_File_Entry (fo, &fHandle, TRUE);
 9715           if (dirptr == NULL)
 9716               return -1;
 9717   
 9718          // Get the cluster
 9719           TempClusterCalc = GetFullClusterNumber(dirptr); // Get complete cluster number.
 9720   #ifdef SUPPORT_FAT32
 9721           // If we're using FAT32 and the previous previous cluster is the root, the
 9722           // value in the dotdot entry will be 0, but the actual cluster won't
 9723           if (TempClusterCalc == VALUE_DOTDOT_CLUSTER_VALUE_FOR_ROOT)
 9724           {
 9725               fo->dirclus = FatRootDirClusterValue;
 9726           }
 9727           else
 9728   #endif
 9729               fo->dirclus = TempClusterCalc;
 9730   
 9731           fo->dirccls = fo->dirclus;
 9732   
 9733           fHandle = 0;
 9734           dirptr = Cache_File_Entry (fo, &fHandle, TRUE);
 9735           if (dirptr == NULL)
 9736               return -1;
 9737           // Look through it until we get the name
 9738           // of the previous cluster
 9739           // Get the cluster
 9740           TempClusterCalc = GetFullClusterNumber(dirptr); // Get complete cluster number.
 9741           while ((TempClusterCalc != dirclus) ||
 9742               ((TempClusterCalc == dirclus) &&
 9743               (((unsigned char)dirptr->DIR_Name[0] == 0xE5) || (dirptr->DIR_Attr == ATTR_VOLUME) || (dirptr->DIR_Attr == ATTR_LONG_NAME))))
 9744           {
 9745               // Look through the entries until we get the
 9746               // right one
 9747               dirptr = Cache_File_Entry (fo, &fHandle, FALSE);
 9748               if (dirptr == NULL)
 9749                   return -1;
 9750               fHandle++;
 9751   
 9752              TempClusterCalc = GetFullClusterNumber(dirptr); // Get complete cluster number in a loop.
 9753           }
 9754   
 9755           // The name should be in the entry now
 9756           // Copy the actual directory location back
 9757           fo->dirclus = dirclus;
 9758           fo->dirccls = dirclus;
 9759   	}
 9760   
 9761      	i = 0;
 9762   	#ifdef SUPPORT_LFN
 9763          	FileObjectCopy (&cwdTemp, fo);
 9764   	   	prevHandle = fHandle - 2;

 9765   	   	lfno = (LFN_ENTRY *)Cache_File_Entry (fo, &prevHandle, FALSE);
 9766   
 9767   		// Get the long file name of the short file name(if present)
 9768   	   	while((lfno->LFN_Attribute == ATTR_LONG_NAME) && (lfno->LFN_SequenceNo != DIR_DEL)

 9769   	   			&& (lfno->LFN_SequenceNo != DIR_EMPTY))

 9770   	   	{

 9771   	   		tempShift.byte.LB = lfno->LFN_Part1[0];

 9772   	   		tempShift.byte.HB = lfno->LFN_Part1[1];

 9773   	   		tempLFN[i++] = tempShift.Val;

 9774   	   		tempShift.byte.LB = lfno->LFN_Part1[2];

 9775   	   		tempShift.byte.HB = lfno->LFN_Part1[3];

 9776   	   		tempLFN[i++] = tempShift.Val;

 9777   	   		tempShift.byte.LB = lfno->LFN_Part1[4];

 9778   	   		tempShift.byte.HB = lfno->LFN_Part1[5];

 9779   	   		tempLFN[i++] = tempShift.Val;

 9780   	   		tempShift.byte.LB = lfno->LFN_Part1[6];

 9781   	   		tempShift.byte.HB = lfno->LFN_Part1[7];

 9782   	   		tempLFN[i++] = tempShift.Val;

 9783   	   		tempShift.byte.LB = lfno->LFN_Part1[8];

 9784   	   		tempShift.byte.HB = lfno->LFN_Part1[9];

 9785   	   		tempLFN[i++] = tempShift.Val;

 9786   
 9787   	   		tempLFN[i++] = lfno->LFN_Part2[0];

 9788   	   		tempLFN[i++] = lfno->LFN_Part2[1];

 9789   	   		tempLFN[i++] = lfno->LFN_Part2[2];

 9790   	   		tempLFN[i++] = lfno->LFN_Part2[3];

 9791   	   		tempLFN[i++] = lfno->LFN_Part2[4];

 9792   	   		tempLFN[i++] = lfno->LFN_Part2[5];

 9793   
 9794   	   		tempLFN[i++] = lfno->LFN_Part3[0];

 9795   	   		tempLFN[i++] = lfno->LFN_Part3[1];

 9796   	   

 9797   	   		prevHandle = prevHandle - 1;

 9798   	   		lfno = (LFN_ENTRY *)Cache_File_Entry (fo, &prevHandle, FALSE);

 9799   	   	}

 9800   
 9801   	   	FileObjectCopy (fo, &cwdTemp);
 9802   	#endif
 9803   
 9804      	if(i == 0)

 9805   	{
 9806      	    for (j = 0; j < 11; j++)
 9807           	fo->name[j] = dirptr->DIR_Name[j];
 9808   		#ifdef SUPPORT_LFN
 9809      			fo->utf16LFNlength = 0;

 9810      		#endif
 9811      	}
 9812   	#ifdef SUPPORT_LFN
 9813      	else

 9814      	{

 9815   		fo->utf16LFNlength = i;

 9816   		

 9817   		for(j = 12;j >= 0;j--)

 9818   		{

 9819   			if((tempLFN[i - j - 1]) == 0x0000)

 9820   			{

 9821   				fo->utf16LFNlength = i - j;

 9822   				break;

 9823   			}

 9824   		}

 9825   		
 9826      		fo->utf16LFNptr = (unsigned short int *)&tempDirectoryString[0];

 9827      	}

 9828   	#endif
 9829   
 9830       return 0;
 9831   }
 9832   
 9833   
 9834   /**************************************************************************
 9835     Function:
 9836       int FSmkdir (char * path)
 9837     Summary:
 9838       Create a directory as per the Ascii input path
 9839     Conditions:
 9840       None
 9841     Input:
 9842       path - The path of directories to create.
 9843     Return Values:
 9844       0 -   The specified directory was created successfully
 9845       EOF - The specified directory could not be created
 9846     Side Effects:
 9847       Will create all non-existent directories in the path. The FSerrno
 9848       variable will be changed.
 9849     Description:
 9850       The FSmkdir function passes a RAM pointer to the path to the
 9851       mkdirhelper function.
 9852     Remarks:
 9853       None
 9854     **************************************************************************/
 9855   
 9856   #ifdef ALLOW_WRITES
 9857   int FSmkdir (char * path)
 9858   {
 9859       return mkdirhelper (0, path, NULL);
 9860   }
 9861   
 9862   /**************************************************************************
 9863     Function:
 9864       int wFSmkdir (unsigned short int * path)
 9865     Summary:
 9866       Create a directory as per the UTF16 input path
 9867     Conditions:
 9868       None
 9869     Input:
 9870       path - The path of directories to create.
 9871     Return Values:
 9872       0 -   The specified directory was created successfully
 9873       EOF - The specified directory could not be created
 9874     Side Effects:
 9875       Will create all non-existent directories in the path. The FSerrno
 9876       variable will be changed.
 9877     Description:
 9878       The wFSmkdir function passes a RAM pointer to the path to the
 9879       mkdirhelper function.
 9880     Remarks:
 9881       None
 9882     **************************************************************************/
 9883   #ifdef SUPPORT_LFN
 9884   int wFSmkdir (unsigned short int * path)
 9885   {
 9886   	int	result;
 9887   	utfModeFileName = TRUE;
 9888       result = mkdirhelper (0, (char *)path, NULL);
 9889   	utfModeFileName = FALSE;
 9890       return result;
 9891   }
 9892   #endif
 9893   
 9894   /**************************************************************************
 9895     Function:
 9896       int FSmkdirpgm (const rom char * path)
 9897     Summary:
 9898       Create a directory with a path in ROM on PIC18
 9899     Conditions:
 9900       None
 9901     Input:
 9902       path - The path of directories to create (ROM)
 9903     Return Values:
 9904       0 -   The specified directory was created successfully
 9905       EOF - The specified directory could not be created
 9906     Side Effects:
 9907       Will create all non-existent directories in the path. The FSerrno
 9908       variable will be changed.
 9909     Description:
 9910       The FSmkdirpgm function passes a PIC18 ROM path pointer to the
 9911       mkdirhelper function.
 9912     Remarks:
 9913       This function is for use with PIC18 when passing arugments in ROM
 9914     **************************************************************************/
 9915   
 9916   #ifdef ALLOW_PGMFUNCTIONS
 9917   int FSmkdirpgm (const rom char * path)
 9918   {
 9919       return mkdirhelper (1, NULL, path);
 9920   }
 9921   
 9922   /**************************************************************************
 9923     Function:
 9924       int wFSmkdirpgm (const rom unsigned short int * path)
 9925     Summary:
 9926       Create a directory with a path in ROM on PIC18
 9927     Conditions:
 9928       None
 9929     Input:
 9930       path - The path of directories to create (ROM)
 9931     Return Values:
 9932       0 -   The specified directory was created successfully
 9933       EOF - The specified directory could not be created
 9934     Side Effects:
 9935       Will create all non-existent directories in the path. The FSerrno
 9936       variable will be changed.
 9937     Description:
 9938       The FSmkdirpgm function passes a PIC18 ROM path pointer to the
 9939       mkdirhelper function.
 9940     Remarks:
 9941       This function is for use with PIC18 when passing arugments in ROM
 9942     **************************************************************************/
 9943   #ifdef SUPPORT_LFN
 9944   int wFSmkdirpgm (const rom unsigned short int * path)
 9945   {
 9946   	int result;
 9947   	utfModeFileName = TRUE;
 9948       result = mkdirhelper (1, NULL, (const char *)path);
 9949   	utfModeFileName = FALSE;
 9950       return result;
 9951   }
 9952   #endif
 9953   
 9954   #endif
 9955   
 9956   /*************************************************************************
 9957     Function:
 9958       // PIC24/30/33/32
 9959       int mkdirhelper (BYTE mode, char * ramptr, char * romptr)
 9960       // PIC18
 9961       int mkdirhelper (BYTE mode, char * ramptr, const rom char * romptr)
 9962     Summary:
 9963       Helper function for FSmkdir
 9964     Conditions:
 9965       None
 9966     Input:
 9967       mode -   Indicates which path pointer to use
 9968       ramptr - Pointer to the path specified in RAM
 9969       romptr - Pointer to the path specified in ROM
 9970     Return Values:
 9971       0 -  Directory was created
 9972       -1 - Directory could not be created
 9973     Side Effects:
 9974       Will create all non-existant directories in the path.
 9975       The FSerrno variable will be changed.
 9976     Description:
 9977       This helper function is used by the FSchdir function. If the path
 9978       argument is specified in ROM for PIC18 this function will be able
 9979       to parse it correctly.  This function will first scan through the path
 9980       to ensure that any DIR names don't exceed 11 characters.  It will then
 9981       backup the current working directory and begin changing directories
 9982       through the path until it reaches a directory than can't be changed to.
 9983       It will then create the specified directory and change directories to
 9984       the new directory. The function will continue creating and changing to
 9985       directories until the end of the path is reached.  The function will
 9986       then restore the original current working directory.
 9987     Remarks:
 9988       None
 9989     **************************************************************************/
 9990   
 9991   #ifdef ALLOW_PGMFUNCTIONS
 9992   int mkdirhelper (BYTE mode, char * ramptr, const rom char * romptr)
 9993   #else
 9994   int mkdirhelper (BYTE mode, char * ramptr, char * romptr)
 9995   #endif
 9996   {
 9997       unsigned short int i,j = 0,k = 0;
 9998       char * temppath = ramptr;
 9999   #ifdef ALLOW_PGMFUNCTIONS
10000       rom char * temppath2 = romptr;
10001       rom unsigned short int * utf16path2 = (rom unsigned short int *)romptr;
10002   #endif
10003   	unsigned short int *utf16path = (unsigned short int *)ramptr;
10004       FILEOBJ tempCWD = &tempCWDobj;
10005   
10006   #ifdef __18CXX
10007       char dotdotPath[] = "..";
10008       char dotdotPath1[5] = {'.','\0','.','\0','\0'};
10009   #endif
10010   
10011   // Do Dynamic allocation if the macro is defined or
10012   // go with static allocation
10013   #if defined(SUPPORT_LFN)
10014   	char tempArray[514];
10015   #else
10016   	char tempArray[14];
10017   #endif
10018   
10019       FSerrno = CE_GOOD;
10020   
10021       if (MDD_WriteProtectState())
10022       {
10023           FSerrno = CE_WRITE_PROTECTED;
10024           return (-1);
10025       }
10026   
10027   #ifdef ALLOW_PGMFUNCTIONS
10028       if (mode == 1)
10029       {
10030   		#ifdef SUPPORT_LFN
10031   		if(utfModeFileName)
10032   		{
10033   	        // Scan for too-long file names
10034   	        while (1)
10035   	        {
10036   	            i = 0;
10037   	            while((*utf16path2 != 0) && (*utf16path2 != '.')&& (*utf16path2 != '\\'))
10038   	            {
10039   	                utf16path2++;
10040   	                i++;
10041   	            }
10042   
10043   		        if (i > 256)
10044   		        {
10045   		            FSerrno = CE_INVALID_ARGUMENT;
10046   		            return -1;
10047   		        }
10048   
10049   	            j = 0;
10050   	            if (*utf16path2 == '.')
10051   	            {
10052   	                utf16path2++;
10053   	                while ((*utf16path2 != 0) && (*utf16path2 != '\\'))
10054   	                {
10055   	                    utf16path2++;
10056   	                    j++;
10057   	                }
10058   		    	    if ((i + j) > 256)
10059   		    	    {
10060   		    	        FSerrno = CE_INVALID_ARGUMENT;
10061   		    	        return -1;
10062   		    	    }
10063   	            }
10064   
10065   				if((i + j) > k)
10066   				{
10067   					k = (i + j);
10068   				}
10069   
10070   	            while (*utf16path2 == '\\')
10071   	                utf16path2++;
10072   	            if (*utf16path2 == 0)
10073   	                break;
10074   	        }
10075       	}
10076   		else
10077   		#endif
10078   		{
10079   	        // Scan for too-long file names
10080   	        while (1)
10081   	        {
10082   	            i = 0;
10083   	            while((*temppath2 != 0) && (*temppath2 != '.')&& (*temppath2 != '\\'))
10084   	            {
10085   	                temppath2++;
10086   	                i++;
10087   	            }
10088   
10089   				#if defined(SUPPORT_LFN)
10090   		            if (i > 256)
10091   		            {
10092   		                FSerrno = CE_INVALID_ARGUMENT;
10093   		                return -1;
10094   		            }
10095   				#else
10096   		            if (i > 8)
10097   		            {
10098   		                FSerrno = CE_INVALID_ARGUMENT;
10099   		                return -1;
10100   		            }
10101   				#endif
10102   
10103   	            j = 0;
10104   	            if (*temppath2 == '.')
10105   	            {
10106   	                temppath2++;
10107   	                while ((*temppath2 != 0) && (*temppath2 != '\\'))
10108   	                {
10109   	                    temppath2++;
10110   	                    j++;
10111   	                }
10112   					#if defined(SUPPORT_LFN)
10113   		    	        if ((i + j) > 256)
10114   		    	        {
10115   		    	            FSerrno = CE_INVALID_ARGUMENT;
10116   		    	            return -1;
10117   		    	        }
10118   					#else
10119   		    	        if (j > 3)
10120   		    	        {
10121   		    	            FSerrno = CE_INVALID_ARGUMENT;
10122   		    	            return -1;
10123   		    	        }
10124   					#endif
10125   	            }
10126   
10127   				if((i + j) > k)
10128   				{
10129   					k = (i + j);
10130   				}
10131   
10132   	            while (*temppath2 == '\\')
10133   	                temppath2++;
10134   	            if (*temppath2 == 0)
10135   	                break;
10136   	        }
10137       	}
10138       }
10139       else
10140   #endif
10141   	{
10142   		#ifdef SUPPORT_LFN
10143   		if(utfModeFileName)
10144   		{
10145   			utf16path = (unsigned short int *)ramptr;
10146   	        // Scan for too-long file names
10147   	        while (1)
10148   	        {
10149   	            i = 0;
10150   	            while((*utf16path != 0) && (*utf16path != '.')&& (*utf16path != '\\'))
10151   	            {
10152   	                utf16path++;
10153   	                i++;
10154   	            }
10155   		        if (i > 256)
10156   		        {
10157   		            FSerrno = CE_INVALID_ARGUMENT;
10158   		            return -1;
10159   		        }
10160   
10161   	            j = 0;
10162   	            if (*utf16path == '.')
10163   	            {
10164   	                utf16path++;
10165   	                while ((*utf16path != 0) && (*utf16path != '\\'))
10166   	                {
10167   	                    utf16path++;
10168   	                    j++;
10169   	                }
10170   		    	    if ((i + j) > 256)
10171   		    	    {
10172   		    	        FSerrno = CE_INVALID_ARGUMENT;
10173   		    	        return -1;
10174   		    	    }
10175   	            }
10176   
10177   				if((i + j) > k)
10178   				{
10179   					k = (i + j);
10180   				}
10181   
10182   	            while (*utf16path == '\\')
10183   	                utf16path++;
10184   	            if (*utf16path == 0)
10185   	                break;
10186   	        }
10187   		}
10188   		else
10189   		#endif
10190   		{
10191   	        // Scan for too-long file names
10192   	        while (1)
10193   	        {
10194   	            i = 0;
10195   	            while((*temppath != 0) && (*temppath != '.')&& (*temppath != '\\'))
10196   	            {
10197   	                temppath++;
10198   	                i++;
10199   	            }
10200   				#if defined(SUPPORT_LFN)
10201   		            if (i > 256)
10202   		            {
10203   		                FSerrno = CE_INVALID_ARGUMENT;
10204   		                return -1;
10205   		            }
10206   				#else
10207   		            if (i > 8)
10208   		            {
10209   		                FSerrno = CE_INVALID_ARGUMENT;
10210   		                return -1;
10211   		            }
10212   				#endif
10213   
10214   	            j = 0;
10215   	            if (*temppath == '.')
10216   	            {
10217   	                temppath++;
10218   	                while ((*temppath != 0) && (*temppath != '\\'))
10219   	                {
10220   	                    temppath++;
10221   	                    j++;
10222   	                }
10223   					#if defined(SUPPORT_LFN)
10224   		    	        if ((i + j) > 256)
10225   		    	        {
10226   		    	            FSerrno = CE_INVALID_ARGUMENT;
10227   		    	            return -1;
10228   		    	        }
10229   					#else
10230   		    	        if (j > 3)
10231   		    	        {
10232   		    	            FSerrno = CE_INVALID_ARGUMENT;
10233   		    	            return -1;
10234   		    	        }
10235   					#endif
10236   	            }
10237   
10238   				if((i + j) > k)
10239   				{
10240   					k = (i + j);
10241   				}
10242   
10243   	            while (*temppath == '\\')
10244   	                temppath++;
10245   	            if (*temppath == 0)
10246   	                break;
10247   	        }
10248   		}
10249   	}
10250   
10251   	utf16path = (unsigned short int *)ramptr;
10252       temppath = ramptr;
10253   #ifdef ALLOW_PGMFUNCTIONS
10254   	utf16path2 = (rom unsigned short int *)romptr;
10255       temppath2 = romptr;
10256   #endif
10257   
10258       // We're going to be moving the CWD
10259       // Back up the CWD
10260       FileObjectCopy (tempCWD, cwdptr);
10261   
10262       // get to the target directory
10263       while (1)
10264       {
10265   #ifdef ALLOW_PGMFUNCTIONS
10266           if (mode == 1)
10267           {
10268   			#ifdef SUPPORT_LFN
10269   			if(utfModeFileName)
10270               	i = *utf16path2;
10271   			else
10272   			#endif
10273               	i = *temppath2;
10274   		}
10275           else
10276   #endif
10277   		{
10278   			#ifdef SUPPORT_LFN
10279   			if(utfModeFileName)
10280               	i = *utf16path;
10281   			else
10282   			#endif
10283               	i = *temppath;
10284   		}
10285   
10286           if (i == '.')
10287           {
10288   #ifdef ALLOW_PGMFUNCTIONS
10289               if (mode == 1)
10290               {
10291   				#ifdef SUPPORT_LFN
10292   				if(utfModeFileName)
10293   				{
10294               	    utf16path2++;
10295               	    i = *utf16path2;
10296   				}
10297   				else
10298   				#endif
10299           	    {
10300               	    temppath2++;
10301               	    i = *temppath2;
10302   				}
10303               }
10304               else
10305               {
10306   #endif
10307   				#ifdef SUPPORT_LFN
10308   				if(utfModeFileName)
10309   				{
10310               	    utf16path++;
10311               	    i = *utf16path;
10312   				}
10313   				else
10314   				#endif
10315           	    {
10316               	    temppath++;
10317               	    i = *temppath;
10318   				}
10319   #ifdef ALLOW_PGMFUNCTIONS
10320               }
10321   #endif
10322   
10323               if ((i != '.') && (i != 0) && (i != '\\'))
10324               {
10325                   FSerrno = CE_INVALID_ARGUMENT;
10326                   return -1;
10327               }
10328   
10329               if (i == '.')
10330               {
10331                   if (cwdptr->dirclus ==  FatRootDirClusterValue)
10332                   {
10333                       // If we try to change to the .. from the
10334                       // root, operation fails
10335                       FSerrno = CE_INVALID_ARGUMENT;
10336                       return -1;
10337                   }
10338   #ifdef ALLOW_PGMFUNCTIONS
10339                   if (mode == 1)
10340                   {
10341   					#ifdef SUPPORT_LFN
10342   					if(utfModeFileName)
10343   					{
10344               		    utf16path2++;
10345               		    i = *utf16path2;
10346   					}
10347   					else
10348   					#endif
10349           		    {
10350               		    temppath2++;
10351               		    i = *temppath2;
10352   					}
10353                   }
10354                   else
10355   #endif
10356                   {
10357   					#ifdef SUPPORT_LFN
10358   					if(utfModeFileName)
10359   					{
10360               		    utf16path++;
10361               		    i = *utf16path;
10362   					}
10363   					else
10364   					#endif
10365           		    {
10366               		    temppath++;
10367               		    i = *temppath;
10368   					}
10369   				}
10370   
10371                   if ((i != '\\') && (i != 0))
10372                   {
10373                       FSerrno = CE_INVALID_ARGUMENT;
10374                       return -1;
10375                   }
10376   // dotdot entry
10377   #ifndef __18CXX
10378   			#ifdef SUPPORT_LFN
10379   			if(utfModeFileName)
10380                   FSchdir (".\0.\0\0");
10381   			#endif
10382   			else
10383                   FSchdir ("..");
10384   #else
10385   			#ifdef SUPPORT_LFN
10386   			if(utfModeFileName)
10387                   FSchdir (dotdotPath1);
10388   			else
10389   			#endif
10390                   FSchdir (dotdotPath);
10391   #endif
10392               }
10393               // Skip past any backslashes
10394               while (i == '\\')
10395               {
10396   #ifdef ALLOW_PGMFUNCTIONS
10397                   if (mode == 1)
10398                   {
10399   					#ifdef SUPPORT_LFN
10400   					if(utfModeFileName)
10401   					{
10402               		    utf16path2++;
10403               		    i = *utf16path2;
10404   					}
10405   					else
10406   					#endif
10407           		    {
10408               		    temppath2++;
10409               		    i = *temppath2;
10410   					}
10411                   }
10412                   else
10413                   {
10414   #endif
10415   					#ifdef SUPPORT_LFN
10416   					if(utfModeFileName)
10417   					{
10418               		    utf16path++;
10419               		    i = *utf16path;
10420   					}
10421   					else
10422   					#endif
10423           		    {
10424               		    temppath++;
10425               		    i = *temppath;
10426   					}
10427   #ifdef ALLOW_PGMFUNCTIONS
10428                   }
10429   #endif
10430               }
10431               if (i == 0)
10432               {
10433                   // No point in creating a dot or dotdot entry directly
10434                   FileObjectCopy (cwdptr, tempCWD);
10435                   FSerrno = CE_INVALID_ARGUMENT;
10436                   return -1;
10437               }
10438           }
10439           else
10440           {
10441               if (i == '\\')
10442               {
10443                   // Start at the root
10444                   cwdptr->dirclus = FatRootDirClusterValue;
10445                   cwdptr->dirccls = FatRootDirClusterValue;
10446                   i = 0;
10447                   cwdptr->name[i++] = '\\';
10448   //                if(utfModeFileName)
10449   //				{
10450   //                	cwdptr->name[i++] = 0x00;
10451   //				}
10452                   for (; i < 11; i++)
10453                   {
10454                       cwdptr->name[i] = 0x20;
10455                   }
10456   
10457   #ifdef ALLOW_PGMFUNCTIONS
10458                   if (mode == 1)
10459                   {
10460   					#ifdef SUPPORT_LFN
10461   					if(utfModeFileName)
10462   					{
10463               		    utf16path2++;
10464               		    i = *utf16path2;
10465   					}
10466   					else
10467   					#endif
10468           		    {
10469               		    temppath2++;
10470               		    i = *temppath2;
10471   					}
10472                   }
10473                   else
10474                   {
10475   #endif
10476   					#ifdef SUPPORT_LFN
10477   					if(utfModeFileName)
10478   					{
10479               		    utf16path++;
10480               		    i = *utf16path;
10481   					}
10482   					else
10483   					#endif
10484           		    {
10485               		    temppath++;
10486               		    i = *temppath;
10487   					}
10488   #ifdef ALLOW_PGMFUNCTIONS
10489                   }
10490   #endif
10491                   // If we just got two backslashes in a row at the
10492                   // beginning of the path, the function fails
10493                   if ((i == '\\') || (i == 0))
10494                   {
10495                       FileObjectCopy (cwdptr, tempCWD);
10496                       FSerrno = CE_INVALID_ARGUMENT;
10497                       return -1;
10498                   }
10499               }
10500               else
10501               {
10502                   break;
10503               }
10504           }
10505       }
10506   
10507       while (1)
10508       {
10509           while(1)
10510           {
10511   #ifdef ALLOW_PGMFUNCTIONS
10512               if (mode == 1)
10513               {
10514   				#ifdef SUPPORT_LFN
10515   				if(utfModeFileName)
10516   				{
10517               	    // Change directories as specified
10518               	    i = *utf16path2;
10519               	    j = 0;
10520   
10521   					k = 512;
10522   
10523               	    // Parse the next token
10524               	    while ((i != 0) && (i != '\\') && (j < k))
10525               	    {
10526               	        tempArray[j++] = i;
10527               	        tempArray[j++] = i >> 8;
10528               	        utf16path2++;
10529               	        i = *utf16path2;
10530               	    }
10531   				}
10532   				else
10533   				#endif
10534           		{
10535               	    // Change directories as specified
10536               	    i = *temppath2;
10537               	    j = 0;
10538   
10539   					#if defined(SUPPORT_LFN)
10540   						k = 256;
10541   					#else
10542   						k = 12;
10543   					#endif
10544   
10545               	    // Parse the next token
10546               	    while ((i != 0) && (i != '\\') && (j < k))
10547               	    {
10548               	        tempArray[j++] = i;
10549               	        temppath2++;
10550               	        i = *temppath2;
10551               	    }
10552   				}
10553               }
10554               else
10555               {
10556   #endif
10557   				#ifdef SUPPORT_LFN
10558   				if(utfModeFileName)
10559   				{
10560               	    // Change directories as specified
10561               	    i = *utf16path;
10562               	    j = 0;
10563   
10564   					k = 512;
10565   
10566               	    // Parse the next token
10567               	    while ((i != 0) && (i != '\\') && (j < k))
10568               	    {
10569               	        tempArray[j++] = i;
10570               	        tempArray[j++] = i >> 8;
10571               	        utf16path++;
10572               	        i = *utf16path;
10573               	    }
10574   				}
10575   				else
10576   				#endif
10577           		{
10578               	    // Change directories as specified
10579               	    i = *temppath;
10580               	    j = 0;
10581   
10582   					#if defined(SUPPORT_LFN)
10583   						k = 256;
10584   					#else
10585   						k = 12;
10586   					#endif
10587   
10588               	    // Parse the next token
10589               	    while ((i != 0) && (i != '\\') && (j < k))
10590               	    {
10591               	        tempArray[j++] = i;
10592               	        temppath++;
10593               	        i = *temppath;
10594               	    }
10595   				}
10596   #ifdef ALLOW_PGMFUNCTIONS
10597               }
10598   #endif
10599   			#ifdef SUPPORT_LFN
10600   			if(utfModeFileName)
10601   			{
10602               	tempArray[j++] = 0;
10603               	tempArray[j] = 0;
10604   
10605           	    if ((tempArray[0] == '.') && (tempArray[1] == 0))
10606           	    {
10607           	        if (((tempArray[2] != 0) || (tempArray[3] != 0)) && ((tempArray[2] != '.') || (tempArray[3] != 0)))
10608           	        {
10609           	            FileObjectCopy (cwdptr, tempCWD);
10610           	            FSerrno = CE_INVALID_ARGUMENT;
10611           	            return -1;
10612           	        }
10613           	        if (((tempArray[2] == '.') && (tempArray[3] == 0)) && ((tempArray[4] != 0) || (tempArray[5] != 0)))
10614           	        {
10615           	            FileObjectCopy (cwdptr, tempCWD);
10616           	            FSerrno = CE_INVALID_ARGUMENT;
10617           	            return -1;
10618           	        }
10619           	    }
10620   			}
10621   			else
10622   			#endif
10623   			{
10624               	tempArray[j] = 0;
10625   
10626           	    if (tempArray[0] == '.')
10627           	    {
10628           	        if ((tempArray[1] != 0) && (tempArray[1] != '.'))
10629           	        {
10630           	            FileObjectCopy (cwdptr, tempCWD);
10631           	            FSerrno = CE_INVALID_ARGUMENT;
10632           	            return -1;
10633           	        }
10634           	        if ((tempArray[1] == '.') && (tempArray[2] != 0))
10635           	        {
10636           	            FileObjectCopy (cwdptr, tempCWD);
10637           	            FSerrno = CE_INVALID_ARGUMENT;
10638           	            return -1;
10639           	        }
10640           	    }
10641   			}
10642   
10643               // Try to change to it
10644               // If you can't we need to create it
10645               if (FSchdir (tempArray))
10646   				break;
10647               else
10648               {
10649                   // We changed into the directory
10650                   while (i == '\\')
10651                   {
10652                       // Next char is a backslash
10653                       // Move past it
10654   #ifdef ALLOW_PGMFUNCTIONS
10655                       if (mode == 1)
10656                       {
10657   						#ifdef SUPPORT_LFN
10658   						if(utfModeFileName)
10659   						{
10660               			    utf16path2++;
10661               			    i = *utf16path2;
10662   						}
10663   						else
10664   						#endif
10665           			    {
10666               			    temppath2++;
10667               			    i = *temppath2;
10668   						}
10669                       }
10670                       else
10671                       {
10672   #endif
10673   						#ifdef SUPPORT_LFN
10674   						if(utfModeFileName)
10675   						{
10676               			    utf16path++;
10677               			    i = *utf16path;
10678   						}
10679   						else
10680   						#endif
10681           			    {
10682               			    temppath++;
10683               			    i = *temppath;
10684   						}
10685   #ifdef ALLOW_PGMFUNCTIONS
10686                       }
10687   #endif
10688                   }
10689                   // If it's the last one, return success
10690                   if (i == 0)
10691                   {
10692                       FileObjectCopy (cwdptr, tempCWD);
10693                       return 0;
10694                   }
10695               }
10696           }
10697   
10698   		#ifdef SUPPORT_LFN
10699   		if(utfModeFileName)
10700   		{
10701   			unsigned short int *tempPtr1;
10702   			unsigned short int *tempPtr2;
10703   			k = 0;
10704   			tempPtr1 = (unsigned short int *)&tempArray[0];
10705   			tempPtr2 = (unsigned short int *)&tempDirectoryString[0];
10706   
10707   			for(;;)
10708   			{
10709   				tempPtr2[k] = tempPtr1[k];
10710   				if(tempPtr2[k])
10711   					k++;
10712   				else
10713   					break;
10714   			}
10715   		}
10716   		else
10717   		#endif
10718   		{
10719   			strcpy(&tempDirectoryString[0],&tempArray[0]);
10720   		}
10721   
10722           // Create a dir here
10723           if (!CreateDIR (tempDirectoryString))
10724           {
10725               FileObjectCopy (cwdptr, tempCWD);
10726           	return -1;
10727           }
10728   
10729           // Try to change to that directory
10730           if (FSchdir (tempArray))
10731           {
10732               FileObjectCopy (cwdptr, tempCWD);
10733               FSerrno = CE_BADCACHEREAD;
10734           	return -1;
10735           }
10736   
10737   #ifdef ALLOW_PGMFUNCTIONS
10738           if (mode == 1)
10739           {
10740   			#ifdef SUPPORT_LFN
10741   			if(utfModeFileName)
10742   			{
10743   	            while (*utf16path2 == '\\')
10744   	            {
10745   	                utf16path2++;
10746   	                i = *utf16path2;
10747   	            }
10748   			}
10749   			else
10750   			#endif
10751           	{
10752           	    // Check for another backslash
10753           	    while (*temppath2 == '\\')
10754           	    {
10755           	        temppath2++;
10756           	        i = *temppath2;
10757           	    }
10758   			}
10759           }
10760           else
10761           {
10762   #endif
10763   			#ifdef SUPPORT_LFN
10764   			if(utfModeFileName)
10765   			{
10766           	    while (*utf16path == '\\')
10767           	    {
10768           	        utf16path++;
10769           	        i = *utf16path;
10770           	    }
10771   			}
10772   			else
10773   			#endif
10774           	{
10775           	    while (*temppath == '\\')
10776           	    {
10777           	        temppath++;
10778           	        i = *temppath;
10779           	    }
10780   			}
10781   #ifdef ALLOW_PGMFUNCTIONS
10782           }
10783   #endif
10784   
10785           // Check to see if we're at the end of the path string
10786           if (i == 0)
10787           {
10788               // We already have one
10789               FileObjectCopy (cwdptr, tempCWD);
10790           	return 0;
10791           }
10792       }
10793   }
10794   
10795   
10796   /**************************************************************************
10797     Function:
10798       int CreateDIR (char * path)
10799     Summary:
10800       FSmkdir helper function to create a directory
10801     Conditions:
10802       This function should not be called by the user.
10803     Input:
10804       path -  The name of the dir to create
10805     Return Values:
10806       TRUE -  Directory was created successfully
10807       FALSE - Directory could not be created.
10808     Side Effects:
10809       Any unwritten data in the data buffer or the FAT buffer will be written
10810       to the device.
10811     Description:
10812       The CreateDIR function is a helper function for the mkdirhelper
10813       function.  The CreateDIR function will create a new file entry for
10814       a directory and assign a cluster to it.  It will erase the cluster
10815       and write a dot and dotdot entry to it.
10816     Remarks:
10817       None.
10818     **************************************************************************/
10819   
10820   int CreateDIR (char * path)
10821   {
10822       FSFILE * dirEntryPtr = &gFileTemp;
10823       DIRENTRY dir;
10824       WORD handle = 0;
10825       DWORD dot, dotdot;
10826   
10827       if (FormatDirName(path, dirEntryPtr,0) == FALSE)
10828       {
10829           FSerrno = CE_INVALID_FILENAME;
10830           return FALSE;
10831       }
10832   
10833       dirEntryPtr->dirclus = cwdptr->dirclus;
10834       dirEntryPtr->dirccls = cwdptr->dirccls;
10835       dirEntryPtr->cluster = 0;
10836       dirEntryPtr->ccls = 0;
10837       dirEntryPtr->dsk = cwdptr->dsk;
10838   
10839       // Create a directory entry
10840       if(CreateFileEntry(dirEntryPtr, &handle, DIRECTORY, TRUE) != CE_GOOD)
10841       {
10842           return FALSE;
10843       }
10844       else
10845       {
10846           if (gNeedFATWrite)
10847               if(WriteFAT (dirEntryPtr->dsk, 0, 0, TRUE))
10848               {
10849                   FSerrno = CE_WRITE_ERROR;
10850                   return FALSE;
10851               }
10852           // Zero that cluster
10853           if (dirEntryPtr->dirclus == FatRootDirClusterValue)
10854               dotdot = 0;
10855           else
10856               dotdot = dirEntryPtr->dirclus;
10857           dirEntryPtr->dirccls = dirEntryPtr->dirclus;
10858           dir = Cache_File_Entry(dirEntryPtr, &handle, TRUE);
10859           if (dir == NULL)
10860           {
10861               FSerrno = CE_BADCACHEREAD;
10862               return FALSE;
10863           }
10864   
10865           // Get the cluster
10866           dot = GetFullClusterNumber(dir); // Get complete cluster number.
10867   
10868           if (writeDotEntries (dirEntryPtr->dsk, dot, dotdot))
10869               return TRUE;
10870           else
10871               return FALSE;
10872   
10873       }
10874   }
10875   
10876   
10877   /***********************************************************************************
10878     Function:
10879       BYTE writeDotEntries (DISK * disk, DWORD dotAddress, DWORD dotdotAddress)
10880     Summary:
10881       Create dot and dotdot entries in a non-root directory
10882     Conditions:
10883       This function should not be called by the user.
10884     Input:
10885       disk -           The global disk structure
10886       dotAddress -     The cluster the current dir is in
10887       dotdotAddress -  The cluster the previous directory was in
10888     Return Values:
10889       TRUE -  The dot and dotdot entries were created
10890       FALSE - The dot and dotdot entries could not be created in the new directory
10891     Side Effects:
10892       None
10893     Description:
10894       The writeDotEntries function will create and write dot and dotdot entries
10895       to a newly created directory.
10896     Remarks:
10897       None.
10898     ***********************************************************************************/
10899   
10900   BYTE writeDotEntries (DISK * disk, DWORD dotAddress, DWORD dotdotAddress)
10901   {
10902       WORD i;
10903       WORD size;
10904       _DIRENTRY entry;
10905       DIRENTRY entryptr = &entry;
10906       DWORD sector;
10907   
10908       gBufferOwner = NULL;
10909   
10910       size = sizeof (_DIRENTRY);
10911   
10912   	memset(disk->buffer, 0x00, disk->sectorSize);
10913   
10914       entry.DIR_Name[0] = '.';
10915   
10916       for (i = 1; i < 11; i++)
10917       {
10918           entry.DIR_Name[i] = 0x20;
10919       }
10920       entry.DIR_Attr = ATTR_DIRECTORY;
10921       entry.DIR_NTRes = 0x00;
10922   
10923       entry.DIR_FstClusLO = (WORD)(dotAddress & 0x0000FFFF); // Lower 16 bit address
10924   
10925   #ifdef SUPPORT_FAT32 // If FAT32 supported.
10926       entry.DIR_FstClusHI = (WORD)((dotAddress & 0x0FFF0000)>> 16); // Higher 16 bit address. FAT32 uses only 28 bits. Mask even higher nibble also.
10927   #else // If FAT32 support not enabled
10928       entry.DIR_FstClusHI = 0;
10929   #endif
10930   
10931       entry.DIR_FileSize = 0x00;
10932   
10933   // Times need to be the same as the times in the directory entry
10934   
10935   // Set dir date for uncontrolled clock source
10936   #ifdef INCREMENTTIMESTAMP
10937       entry.DIR_CrtTimeTenth = 0xB2;
10938       entry.DIR_CrtTime = 0x7278;
10939       entry.DIR_CrtDate = 0x32B0;
10940       entry.DIR_LstAccDate = 0x0000;
10941       entry.DIR_WrtTime = 0x0000;
10942       entry.DIR_WrtDate = 0x0000;
10943   #endif
10944   
10945   #ifdef USEREALTIMECLOCK
10946       entry.DIR_CrtTimeTenth = gTimeCrtMS;         // millisecond stamp
10947       entry.DIR_CrtTime =      gTimeCrtTime;      // time created //
10948       entry.DIR_CrtDate =      gTimeCrtDate;      // date created (1/1/2004)
10949       entry.DIR_LstAccDate =   0x0000;         // Last Access date
10950       entry.DIR_WrtTime =      0x0000;         // last update time
10951       entry.DIR_WrtDate =      0x0000;         // last update date
10952   #endif
10953   
10954   #ifdef USERDEFINEDCLOCK
10955       entry.DIR_CrtTimeTenth  =   gTimeCrtMS;         // millisecond stamp
10956       entry.DIR_CrtTime       =   gTimeCrtTime;       // time created //
10957       entry.DIR_CrtDate       =   gTimeCrtDate;       // date created (1/1/2004)
10958       entry.DIR_LstAccDate    =   0x0000;             // Last Access date
10959       entry.DIR_WrtTime       =   0x0000;             // last update time
10960       entry.DIR_WrtDate       =   0x0000;             // last update date
10961   #endif
10962   
10963       for (i = 0; i < size; i++)
10964       {
10965           *(disk->buffer + i) = *((char *)entryptr + i);
10966       }
10967       entry.DIR_Name[1] = '.';
10968   
10969       entry.DIR_FstClusLO = (WORD)(dotdotAddress & 0x0000FFFF); // Lower 16 bit address
10970   
10971   #ifdef SUPPORT_FAT32 // If FAT32 supported.
10972       entry.DIR_FstClusHI = (WORD)((dotdotAddress & 0x0FFF0000)>> 16); // Higher 16 bit address. FAT32 uses only 28 bits. Mask even higher nibble also.
10973   #else // If FAT32 support not enabled
10974       entry.DIR_FstClusHI = 0;
10975   #endif
10976   
10977   
10978       for (i = 0; i < size; i++)
10979       {
10980           *(disk->buffer + i + size) = *((char *)entryptr + i);
10981       }
10982   
10983       sector = Cluster2Sector (disk, dotAddress);
10984   
10985       if (MDD_SectorWrite(sector, disk->buffer, FALSE) == FALSE)
10986       {
10987           FSerrno = CE_WRITE_ERROR;
10988           return FALSE;
10989       }
10990   
10991       return TRUE;
10992   }
10993   
10994   // This array is used to prevent a stack frame error
10995   #ifdef __18CXX
10996       char tempArray[13] = "           ";
10997   #endif
10998   
10999   
11000   /**************************************************************************
11001     Function:
11002       int FSrmdir (char * path)
11003     Summary:
11004       Delete a directory as per the Ascii input path
11005     Conditions:
11006       None
11007     Input:
11008       path -      The path of the directory to remove
11009       rmsubdirs -
11010                 - TRUE -  All sub-dirs and files in the target dir will be removed
11011                 - FALSE - FSrmdir will not remove non-empty directories
11012     Return Values:
11013       0 -   The specified directory was deleted successfully
11014       EOF - The specified directory could not be deleted
11015     Side Effects:
11016       The FSerrno variable will be changed.
11017     Description:
11018       The FSrmdir function passes a RAM pointer to the path to the
11019       rmdirhelper function.
11020     Remarks:
11021       None.
11022     **************************************************************************/
11023   
11024   int FSrmdir (char * path, unsigned char rmsubdirs)
11025   {
11026       return rmdirhelper (0, path, NULL, rmsubdirs);
11027   }
11028   
11029   
11030   /**************************************************************************
11031     Function:
11032       int wFSrmdir (unsigned short int * path, unsigned char rmsubdirs)
11033     Summary:
11034       Delete a directory as per the UTF16 input path
11035     Conditions:
11036       None
11037     Input:
11038       path -      The path of the directory to remove
11039       rmsubdirs -
11040                 - TRUE -  All sub-dirs and files in the target dir will be removed
11041                 - FALSE - FSrmdir will not remove non-empty directories
11042     Return Values:
11043       0 -   The specified directory was deleted successfully
11044       EOF - The specified directory could not be deleted
11045     Side Effects:
11046       The FSerrno variable will be changed.
11047     Description:
11048       The wFSrmdir function passes a RAM pointer to the path to the
11049       rmdirhelper function.
11050     Remarks:
11051       None.
11052     **************************************************************************/
11053   #ifdef SUPPORT_LFN
11054   int wFSrmdir (unsigned short int * path, unsigned char rmsubdirs)
11055   {
11056   	int result;
11057   	utfModeFileName = TRUE;
11058       result = rmdirhelper (0, (char *)path, NULL, rmsubdirs);
11059   	utfModeFileName = FALSE;
11060       return result;
11061   }
11062   #endif
11063   
11064   /**************************************************************************
11065     Function:
11066       int FSrmdirpgm (const rom char * path)
11067     Summary:
11068       Delete a directory with a path in ROM on PIC18
11069     Conditions:
11070       None.
11071     Input:
11072       path -      The path of the directory to remove (ROM)
11073       rmsubdirs -
11074                 - TRUE -  All sub-dirs and files in the target dir will be removed
11075                 - FALSE - FSrmdir will not remove non-empty directories
11076     Return Values:
11077       0 -   The specified directory was deleted successfully
11078       EOF - The specified directory could not be deleted
11079     Side Effects:
11080       The FSerrno variable will be changed.
11081     Description:
11082       The FSrmdirpgm function passes a PIC18 ROM path pointer to the
11083       rmdirhelper function.
11084     Remarks:
11085       This function is for use with PIC18 when passing arguments in ROM.
11086     **************************************************************************/
11087   
11088   #ifdef ALLOW_PGMFUNCTIONS
11089   int FSrmdirpgm (const rom char * path, unsigned char rmsubdirs)
11090   {
11091       return rmdirhelper (1, NULL, path, rmsubdirs);
11092   }
11093   #endif
11094   
11095   /**************************************************************************
11096     Function:
11097           int wFSrmdirpgm (const rom unsigned short int * path, unsigned char rmsubdirs)
11098     Summary:
11099       Delete a directory with a path in ROM on PIC18
11100     Conditions:
11101       None.
11102     Input:
11103       path -      The path of the directory to remove (ROM)
11104       rmsubdirs -
11105                 - TRUE -  All sub-dirs and files in the target dir will be removed
11106                 - FALSE - FSrmdir will not remove non-empty directories
11107     Return Values:
11108       0 -   The specified directory was deleted successfully
11109       EOF - The specified directory could not be deleted
11110     Side Effects:
11111       The FSerrno variable will be changed.
11112     Description:
11113       The FSrmdirpgm function passes a PIC18 ROM path pointer to the
11114       rmdirhelper function.
11115     Remarks:
11116       This function is for use with PIC18 when passing arguments in ROM.
11117     **************************************************************************/
11118   
11119   #ifdef ALLOW_PGMFUNCTIONS
11120   
11121   #ifdef SUPPORT_LFN
11122   int wFSrmdirpgm (const rom unsigned short int * path, unsigned char rmsubdirs)
11123   {
11124   	int result;
11125   	utfModeFileName = TRUE;
11126       result = rmdirhelper (1, NULL, (const char *)path, rmsubdirs);
11127   	utfModeFileName = FALSE;
11128       return result;
11129   }
11130   #endif
11131   
11132   #endif
11133   
11134   /************************************************************************************************
11135     Function:
11136       // PIC24/30/33/32
11137       int rmdirhelper (BYTE mode, char * ramptr, char * romptr, unsigned char rmsubdirs)
11138       // PIC18
11139       int rmdirhelper (BYTE mode, char * ramptr, const rom char * romptr, unsigned char rmsubdirs)
11140     Summary:
11141       Helper function for FSrmdir
11142     Conditions:
11143       This function should not be called by the user.
11144     Input:
11145       path -      The path of the dir to delete
11146       rmsubdirs -
11147                 - TRUE -  Remove all sub-directories and files in the directory
11148                 - FALSE - Non-empty directories can not be removed
11149     Return Values:
11150       0 -   The specified directory was successfully removed.
11151       EOF - The specified directory could not be removed.
11152     Side Effects:
11153       The FSerrno variable will be changed.
11154     Description:
11155       This helper function is used by the FSmkdir function.  If the path
11156       argument is specified in ROM for PIC18 this function will be able
11157       to parse it correctly.  This function will first change to the
11158       specified directory.  If the rmsubdirs argument is FALSE the function
11159       will search through the directory to ensure that it is empty and then
11160       remove it.  If the rmsubdirs argument is TRUE the function will also
11161       search through the directory for subdirectories or files.  When the
11162       function finds a file, the file will be erased.  When the function
11163       finds a subdirectory, it will switch to the subdirectory and begin
11164       removing all of the files in that subdirectory.  Once the subdirectory
11165       is empty, the function will switch back to the original directory.
11166       return to the original position in that directory, and continue removing
11167       files.  Once the specified directory is empty, the function will
11168       change to the parent directory, search through it for the directory
11169       to remove, and then erase that directory.
11170     Remarks:
11171       None.
11172     ************************************************************************************************/
11173   
11174   #ifdef ALLOW_PGMFUNCTIONS
11175   int rmdirhelper (BYTE mode, char * ramptr, const rom char * romptr, unsigned char rmsubdirs)
11176   #else
11177   int rmdirhelper (BYTE mode, char * ramptr, char * romptr, unsigned char rmsubdirs)
11178   #endif
11179   {
11180       FILEOBJ tempCWD = &tempCWDobj;
11181       FILEOBJ fo = &gFileTemp;
11182       DIRENTRY entry;
11183       WORD handle = 0;
11184       WORD handle2;
11185       WORD subDirDepth;
11186       short int Index3 = 0;
11187       char Index, Index2;
11188   
11189   	#if defined(SUPPORT_LFN)

11190   		BOOL prevUtfModeFileName = utfModeFileName;
11191   		char tempArray[514];
11192       	WORD prevHandle;
11193       	LFN_ENTRY *lfno;
11194       	FSFILE cwdTemp;
11195   		UINT16_VAL tempShift;
11196   		unsigned short int *tempLFN = (unsigned short int *)&tempArray[0];
11197   		BOOL	forFirstTime;
11198   	#else

11199   		char	tempArray[13];

11200   	#endif

11201   #ifndef __18CXX
11202   
11203   #else
11204       char dotdotname[] = "..";
11205       char dotdotname1[5] = {'.','\0','.','\0','\0'};
11206   #endif
11207   
11208       FSerrno = CE_GOOD;
11209   
11210       // Back up the current working directory
11211       FileObjectCopy (tempCWD, cwdptr);
11212   
11213   #ifdef ALLOW_PGMFUNCTIONS
11214       if (mode)
11215       {
11216           if (chdirhelper (1, NULL, romptr))
11217           {
11218               FSerrno = CE_DIR_NOT_FOUND;
11219               return -1;
11220           }
11221       }
11222       else
11223       {
11224   #endif
11225           if (FSchdir (ramptr))
11226           {
11227               FSerrno = CE_DIR_NOT_FOUND;
11228               return -1;
11229           }
11230   #ifdef ALLOW_PGMFUNCTIONS
11231       }
11232   #endif
11233   
11234       // Make sure we aren't trying to remove the root dir or the CWD
11235       if ((cwdptr->dirclus == FatRootDirClusterValue) || (cwdptr->dirclus == tempCWD->dirclus))
11236       {
11237           FileObjectCopy (cwdptr, tempCWD);
11238           FSerrno = CE_INVALID_ARGUMENT;
11239           return -1;
11240       }
11241   
11242       handle++;
11243       entry = Cache_File_Entry (cwdptr, &handle, TRUE);
11244   
11245       if (entry == NULL)
11246       {
11247           FileObjectCopy (cwdptr, tempCWD);
11248           FSerrno = CE_BADCACHEREAD;
11249           return -1;
11250       }
11251   
11252       handle++;
11253       entry = Cache_File_Entry (cwdptr, &handle, FALSE);
11254       if (entry == NULL)
11255       {
11256           FileObjectCopy (cwdptr, tempCWD);
11257           FSerrno = CE_BADCACHEREAD;
11258           return -1;
11259       }
11260       // Don't remove subdirectories and sub-files
11261       if (!rmsubdirs)
11262       {
11263           while (entry->DIR_Name[0] != 0)
11264           {
11265               if ((unsigned char)entry->DIR_Name[0] != 0xE5)
11266               {
11267                   FileObjectCopy (cwdptr, tempCWD);
11268                   FSerrno = CE_DIR_NOT_EMPTY;
11269                   return -1;
11270               }
11271               handle++;
11272               entry = Cache_File_Entry (cwdptr, &handle, FALSE);
11273               if ((entry == NULL))
11274               {
11275                   FileObjectCopy (cwdptr, tempCWD);
11276                   FSerrno = CE_BADCACHEREAD;
11277                   return -1;
11278               }
11279           }
11280       }
11281       else
11282       {
11283           // Do remove subdirectories and sub-files
11284           dirCleared = FALSE;
11285           subDirDepth = 0;
11286   		#if defined(SUPPORT_LFN)
11287   		tempCWD-> utf16LFNptr = (unsigned short int *)&tempArray[0];
11288   		fo-> utf16LFNptr = (unsigned short int *)&tempArray[0];
11289   		#endif
11290   
11291           while (!dirCleared)
11292           {
11293               if (entry->DIR_Name[0] != 0)
11294               {
11295                   if (((unsigned char)entry->DIR_Name[0] != 0xE5) && (entry->DIR_Attr == ATTR_LONG_NAME))
11296   				{
11297   					#if defined(SUPPORT_LFN)
11298   					lfno = (LFN_ENTRY *)entry;
11299   
11300   					if(lfno->LFN_SequenceNo & 0x40)
11301   					{
11302   						Index3 = (lfno->LFN_SequenceNo - 0x41) * 13;
11303   						tempLFN[Index3 + 13] = 0x0000;
11304   						forFirstTime = TRUE;
11305   					}
11306   					else
11307   					{
11308   						Index3 = (lfno->LFN_SequenceNo - 1) * 13;
11309   						forFirstTime = FALSE;
11310   					}
11311   
11312   					tempShift.byte.LB = lfno->LFN_Part1[0];
11313   					tempShift.byte.HB = lfno->LFN_Part1[1];
11314   					tempLFN[Index3++] = tempShift.Val;
11315   					tempShift.byte.LB = lfno->LFN_Part1[2];
11316   					tempShift.byte.HB = lfno->LFN_Part1[3];
11317   					tempLFN[Index3++] = tempShift.Val;
11318   					tempShift.byte.LB = lfno->LFN_Part1[4];
11319   					tempShift.byte.HB = lfno->LFN_Part1[5];
11320   					tempLFN[Index3++] = tempShift.Val;
11321   					tempShift.byte.LB = lfno->LFN_Part1[6];
11322   					tempShift.byte.HB = lfno->LFN_Part1[7];
11323   					tempLFN[Index3++] = tempShift.Val;
11324   					tempShift.byte.LB = lfno->LFN_Part1[8];
11325   					tempShift.byte.HB = lfno->LFN_Part1[9];
11326   					tempLFN[Index3++] = tempShift.Val;
11327   
11328   					tempLFN[Index3++] = lfno->LFN_Part2[0];
11329   					tempLFN[Index3++] = lfno->LFN_Part2[1];
11330   					tempLFN[Index3++] = lfno->LFN_Part2[2];
11331   					tempLFN[Index3++] = lfno->LFN_Part2[3];
11332   					tempLFN[Index3++] = lfno->LFN_Part2[4];
11333   					tempLFN[Index3++] = lfno->LFN_Part2[5];
11334   
11335   					tempLFN[Index3++] = lfno->LFN_Part3[0];
11336   					tempLFN[Index3] = lfno->LFN_Part3[1];
11337   
11338   					if(forFirstTime)
11339   					{
11340   						tempCWD->utf16LFNlength = Index3;
11341   						
11342   						for(Index = 12;Index >= 0;Index--)
11343   						{
11344   							if((tempLFN[Index3 - Index - 1]) == 0x0000)
11345   							{
11346   								tempCWD->utf16LFNlength = Index3 - Index;
11347   								break;
11348   							}
11349   						}
11350   					
11351   						fo->utf16LFNlength = tempCWD->utf16LFNlength;
11352   					}
11353                   	handle++;
11354   					#endif
11355   				}
11356                   else if (((unsigned char)entry->DIR_Name[0] != 0xE5) && (entry->DIR_Attr != ATTR_VOLUME) && (entry->DIR_Attr != ATTR_LONG_NAME))
11357                   {
11358                       if ((entry->DIR_Attr & ATTR_DIRECTORY) == ATTR_DIRECTORY)
11359                       {
11360                           // We have a directory
11361                           subDirDepth++;
11362   						#if defined(SUPPORT_LFN)
11363   						if(tempCWD-> utf16LFNlength)
11364   						{
11365   							utfModeFileName = 1;
11366   							Index = FSchdir(&tempArray[0]);
11367   							utfModeFileName = prevUtfModeFileName;
11368   							tempCWD-> utf16LFNlength = 0;
11369   							fo-> utf16LFNlength = 0;
11370   						}
11371   						else
11372   						#endif
11373   						{
11374                       	    for (Index = 0; (Index < DIR_NAMESIZE) && (entry->DIR_Name[(BYTE)Index] != 0x20); Index++)
11375                       	    {
11376                       	        tempArray[(BYTE)Index] = entry->DIR_Name[(BYTE)Index];
11377                       	    }
11378                       	    if (entry->DIR_Name[8] != 0x20)
11379                       	    {
11380                       	        tempArray[(BYTE)Index++] = '.';
11381                       	        for (Index2 = 0; (Index2 < DIR_EXTENSION) && (entry->DIR_Name[Index2 + DIR_NAMESIZE] != 0x20); Index2++)
11382                       	        {
11383                       	            tempArray[(BYTE)Index++] = entry->DIR_Name[Index2 + DIR_NAMESIZE];
11384                       	        }
11385                       	    }
11386                       	    tempArray[(BYTE)Index] = 0;
11387   							#ifdef SUPPORT_LFN
11388   							utfModeFileName = 0;
11389   							#endif
11390   							Index = FSchdir(&tempArray[0]);
11391   							#ifdef SUPPORT_LFN
11392   							utfModeFileName = prevUtfModeFileName;
11393   							#endif
11394   						}
11395   
11396                           // Change to the subdirectory
11397                           if (Index)
11398                           {
11399                               FileObjectCopy (cwdptr, tempCWD);
11400                               FSerrno = CE_DIR_NOT_FOUND;
11401                               return -1;
11402                           }
11403                           else
11404                           {
11405                               // Make sure we're not trying to delete the CWD
11406                               if (cwdptr->dirclus == tempCWD->dirclus)
11407                               {
11408                                   FileObjectCopy (cwdptr, tempCWD);
11409                                   FSerrno = CE_INVALID_ARGUMENT;
11410                                   return -1;
11411                               }
11412                           }
11413                           handle = 2;
11414                           recache = TRUE;
11415                       }
11416                       else
11417                       {
11418   						#if defined(SUPPORT_LFN)
11419   						if(!tempCWD-> utf16LFNlength)
11420   						#endif
11421   						{
11422                       	    for (Index = 0; Index < 11; Index++)
11423                       	    {
11424                       	        fo->name[(BYTE)Index] = entry->DIR_Name[(BYTE)Index];
11425                       	    }
11426   						}
11427   
11428                           fo->dsk = &gDiskData;
11429   
11430                           fo->entry = handle;
11431                           fo->dirclus = cwdptr->dirclus;
11432                           fo->dirccls = cwdptr->dirccls;
11433                           fo->cluster = 0;
11434                           fo->ccls    = 0;
11435   
11436                           if (FILEerase(fo, &handle, TRUE))
11437                           {
11438   							#if defined(SUPPORT_LFN)
11439   							tempCWD-> utf16LFNlength = 0;
11440   							fo-> utf16LFNlength = 0;
11441   							#endif
11442                               FileObjectCopy (cwdptr, tempCWD);
11443                               FSerrno = CE_ERASE_FAIL;
11444                               return -1;
11445                           }
11446                           else
11447                           {
11448                               handle++;
11449                           }
11450   						#if defined(SUPPORT_LFN)
11451   						tempCWD-> utf16LFNlength = 0;
11452   						fo-> utf16LFNlength = 0;
11453   						#endif
11454                       } // Check to see if it's a DIR entry
11455                   }// Check non-dir entry to see if its a valid file
11456                   else
11457                   {
11458                       handle++;
11459                   }
11460   
11461                   if (recache)
11462                   {
11463                       recache = FALSE;
11464                       cwdptr->dirccls = cwdptr->dirclus;
11465                       entry = Cache_File_Entry (cwdptr, &handle, TRUE);
11466                   }
11467                   else
11468                   {
11469                       entry = Cache_File_Entry (cwdptr, &handle, FALSE);
11470                   }
11471                   
11472   				if (entry == NULL)
11473                   {
11474   					#if defined(SUPPORT_LFN)
11475   					tempCWD-> utf16LFNlength = 0;
11476   					fo-> utf16LFNlength = 0;
11477   					#endif
11478                       FileObjectCopy (cwdptr, tempCWD);
11479                       FSerrno = CE_BADCACHEREAD;
11480                       return -1;
11481                   }
11482               }
11483               else
11484               {
11485   				#if defined(SUPPORT_LFN)
11486   				tempCWD-> utf16LFNlength = 0;
11487   				fo-> utf16LFNlength = 0;
11488   				#endif
11489   
11490                   // We have reached the end of the directory
11491                   if (subDirDepth != 0)
11492                   {
11493                       handle2 = 0;
11494   
11495                       cwdptr->dirccls = cwdptr->dirclus;
11496                       entry = Cache_File_Entry (cwdptr, &handle2, TRUE);
11497                       if (entry == NULL)
11498                       {
11499                           FileObjectCopy (cwdptr, tempCWD);
11500                           FSerrno = CE_BADCACHEREAD;
11501                           return -1;
11502                       }
11503   
11504                       // Get the cluster
11505                       handle2 = GetFullClusterNumber(entry); // Get complete cluster number.
11506   
11507   #ifndef __18CXX
11508   	#ifdef SUPPORT_LFN
11509   	if(utfModeFileName)

11510           Index3 = FSchdir (".\0.\0\0");

11511   	else

11512   	#endif
11513           Index3 = FSchdir ("..");

11514   #else
11515   	#ifdef SUPPORT_LFN
11516   	if(utfModeFileName)

11517           Index3 = FSchdir (dotdotname1);

11518   	else

11519   	#endif
11520           Index3 = FSchdir (dotdotname);

11521   #endif
11522                       if(Index3)
11523                       {
11524                           FileObjectCopy (cwdptr, tempCWD);
11525                           FSerrno = CE_DIR_NOT_FOUND;
11526                           return -1;
11527                       }
11528                       // Return to our previous position in this directory
11529                       handle = 2;
11530                       cwdptr->dirccls = cwdptr->dirclus;
11531                       entry = Cache_File_Entry (cwdptr, &handle, TRUE);
11532                       if (entry == NULL)
11533                       {
11534                           FileObjectCopy (cwdptr, tempCWD);
11535                           FSerrno = CE_BADCACHEREAD;
11536                           return -1;
11537                       }
11538   
11539                       // Get the cluster
11540                       TempClusterCalc = GetFullClusterNumber(entry); // Get complete cluster number.
11541   
11542                       while ((TempClusterCalc != handle2) ||
11543                       ((TempClusterCalc == handle2) &&
11544                       (((unsigned char)entry->DIR_Name[0] == 0xE5) || (entry->DIR_Attr == ATTR_VOLUME))))
11545                       {
11546                           handle++;
11547                           entry = Cache_File_Entry (cwdptr, &handle, FALSE);
11548                           if (entry == NULL)
11549                           {
11550                               FileObjectCopy (cwdptr, tempCWD);
11551                               FSerrno = CE_BADCACHEREAD;
11552                               return -1;
11553                           }
11554                           // Get the cluster
11555                           TempClusterCalc = GetFullClusterNumber(entry); // Get complete cluster number in a loop.
11556                       }
11557   
11558   					Index3 = 0;
11559   					#if defined(SUPPORT_LFN)
11560           			FileObjectCopy (&cwdTemp, cwdptr);
11561   					prevHandle = handle - 1;
11562   					lfno = (LFN_ENTRY *)Cache_File_Entry (cwdptr, &prevHandle, FALSE);
11563   
11564   
11565   					while((lfno->LFN_Attribute == ATTR_LONG_NAME) && (lfno->LFN_SequenceNo != DIR_DEL)
11566   							&& (lfno->LFN_SequenceNo != DIR_EMPTY))
11567   					{
11568   						tempShift.byte.LB = lfno->LFN_Part1[0];
11569   						tempShift.byte.HB = lfno->LFN_Part1[1];
11570   						tempLFN[Index3++] = tempShift.Val;
11571   						tempShift.byte.LB = lfno->LFN_Part1[2];
11572   						tempShift.byte.HB = lfno->LFN_Part1[3];
11573   						tempLFN[Index3++] = tempShift.Val;
11574   						tempShift.byte.LB = lfno->LFN_Part1[4];
11575   						tempShift.byte.HB = lfno->LFN_Part1[5];
11576   						tempLFN[Index3++] = tempShift.Val;
11577   						tempShift.byte.LB = lfno->LFN_Part1[6];
11578   						tempShift.byte.HB = lfno->LFN_Part1[7];
11579   						tempLFN[Index3++] = tempShift.Val;
11580   						tempShift.byte.LB = lfno->LFN_Part1[8];
11581   						tempShift.byte.HB = lfno->LFN_Part1[9];
11582   						tempLFN[Index3++] = tempShift.Val;
11583   
11584   						tempLFN[Index3++] = lfno->LFN_Part2[0];
11585   						tempLFN[Index3++] = lfno->LFN_Part2[1];
11586   						tempLFN[Index3++] = lfno->LFN_Part2[2];
11587   						tempLFN[Index3++] = lfno->LFN_Part2[3];
11588   						tempLFN[Index3++] = lfno->LFN_Part2[4];
11589   						tempLFN[Index3++] = lfno->LFN_Part2[5];
11590   
11591   						tempLFN[Index3++] = lfno->LFN_Part3[0];
11592   						tempLFN[Index3++] = lfno->LFN_Part3[1];
11593   				
11594   						prevHandle = prevHandle - 1;
11595   						lfno = (LFN_ENTRY *)Cache_File_Entry (cwdptr, &prevHandle, FALSE);
11596   					}
11597   
11598   					FileObjectCopy (cwdptr, &cwdTemp);
11599   
11600   					#endif
11601   
11602   					if(Index3 == 0)
11603   					{
11604                   	    memset (tempArray, 0x00, 12);
11605                   	    for (Index = 0; Index < 11; Index++)
11606                   	    {
11607                   	        tempArray[(BYTE)Index] = entry->DIR_Name[(BYTE)Index];
11608                   	    }
11609   						#if defined(SUPPORT_LFN)
11610   						cwdptr->utf16LFNlength = 0;
11611   						#endif
11612   					}
11613   					#if defined(SUPPORT_LFN)
11614   					else
11615   					{
11616   						cwdptr->utf16LFNlength = Index3;
11617   						
11618   						for(Index = 12;Index >= 0;Index--)
11619   						{
11620   							if((tempLFN[Index3 - Index - 1]) == 0x0000)
11621   							{
11622   								cwdptr->utf16LFNlength = Index3 - Index;
11623   								break;
11624   							}
11625   						}
11626   
11627   						cwdptr->utf16LFNptr = (unsigned short int *)&tempArray[0];
11628   					}
11629   					#endif
11630                       // Erase the directory that we just cleared the subdirectories out of
11631   
11632                       if (eraseDir (&tempArray[0]))
11633                       {
11634                           FileObjectCopy (cwdptr, tempCWD);
11635                           FSerrno = CE_ERASE_FAIL;
11636                           return -1;
11637                       }
11638                       else
11639                       {
11640                           handle++;
11641                           cwdptr->dirccls = cwdptr->dirclus;
11642                           entry = Cache_File_Entry (cwdptr, &handle, TRUE);
11643                           if (entry == NULL)
11644                           {
11645                               FileObjectCopy (cwdptr, tempCWD);
11646                               FSerrno = CE_BADCACHEREAD;
11647                               return -1;
11648                           }
11649                       }
11650   
11651                       // Decrease the subdirectory depth
11652                       subDirDepth--;
11653                   }
11654                   else
11655                   {
11656                       dirCleared = TRUE;
11657                   } // Check subdirectory depth
11658               } // Check until we get an empty entry
11659           } // Loop until the whole dir is cleared
11660       }
11661   
11662       // Cache the current directory name
11663       // tempArray is used so we don't disturb the
11664       // global getcwd buffer
11665       if (FSgetcwd (&tempArray[0], 2) == NULL)
11666       {
11667           FileObjectCopy (cwdptr, tempCWD);
11668           return -1;
11669       }
11670   	else
11671   	{
11672   		#if defined(SUPPORT_LFN)
11673   			if(!cwdptr->utf16LFNlength)
11674   		#endif
11675   			{
11676           		memset (tempArray, 0x00, 12);
11677           		for (Index = 0; Index < 11; Index++)
11678           		{
11679               		tempArray[(BYTE)Index] = cwdptr->name[(BYTE)Index];
11680           		}			
11681   			}
11682   	}
11683   
11684       // If we're here, this directory is empty
11685   #ifndef __18CXX
11686   	#ifdef SUPPORT_LFN
11687   	if(utfModeFileName)

11688           Index3 = FSchdir (".\0.\0\0");

11689   	else

11690   	#endif
11691           Index3 = FSchdir ("..");

11692   #else
11693   	#ifdef SUPPORT_LFN
11694   	if(utfModeFileName)

11695           Index3 = FSchdir (dotdotname1);

11696   	else

11697   	#endif
11698           Index3 = FSchdir (dotdotname);

11699   #endif
11700       if(Index3)
11701       {
11702           FileObjectCopy (cwdptr, tempCWD);
11703           FSerrno = CE_DIR_NOT_FOUND;
11704           return -1;
11705       }
11706   
11707   	#if defined(SUPPORT_LFN)
11708   	if(cwdptr->utf16LFNlength)
11709   	{
11710   		Index3 = eraseDir((char *)cwdptr->utf16LFNptr);
11711   	}
11712   	else
11713   	#endif
11714   	{
11715   		Index3 = eraseDir(tempArray);
11716   	}
11717   
11718       if (Index3)
11719       {
11720           FileObjectCopy (cwdptr, tempCWD);
11721           FSerrno = CE_ERASE_FAIL;
11722           return -1;
11723       }
11724       else
11725       {
11726           FileObjectCopy (cwdptr, tempCWD);
11727           return 0;
11728       }
11729   }
11730   
11731   
11732   /****************************************************************
11733     Function:
11734       int eraseDir (char * path)
11735     Summary:
11736       FSrmdir helper function to erase dirs
11737     Conditions:
11738       This function should not be called by the user.
11739     Input:
11740       path -  The name of the directory to delete
11741     Return Values:
11742       0 -  Dir was deleted successfully
11743       -1 - Dir could not be deleted.
11744     Side Effects:
11745       None
11746     Description:
11747       The eraseDir function is a helper function for the rmdirhelper
11748       function.  The eraseDir function will search for the
11749       directory that matches the specified path name and then erase
11750       it with the FILEerase function.
11751     Remarks:
11752       None.
11753     *****************************************************************/
11754   
11755   int eraseDir (char * path)
11756   {
11757       int result;
11758       BYTE Index;
11759       FSFILE tempCWDobj2;
11760   
11761       if (MDD_WriteProtectState())
11762       {
11763           return (-1);
11764       }
11765   
11766       // preserve CWD
11767       FileObjectCopy(&tempCWDobj2, cwdptr);
11768   
11769   	// If long file name not present, copy the 8.3 name in cwdptr
11770   	#if defined(SUPPORT_LFN)
11771       if(!cwdptr->utf16LFNlength)
11772   	#endif
11773       {
11774       	for (Index = 0; Index <11; Index++)
11775       	{
11776           	cwdptr->name[Index] = *(path + Index);
11777       	}
11778   	}
11779   
11780       // copy file object over
11781       FileObjectCopy(&gFileTemp, cwdptr);
11782   
11783       // See if the file is found
11784   	if(FILEfind (cwdptr, &gFileTemp, LOOK_FOR_MATCHING_ENTRY, 0) == CE_GOOD)
11785   	{
11786   		if(FILEerase(cwdptr, &cwdptr->entry, TRUE) == CE_GOOD)
11787   			result = 0;
11788   		else
11789   			result = -1;
11790   	}
11791   	else
11792   		result = -1;
11793   
11794   	FileObjectCopy(cwdptr, &tempCWDobj2);
11795   	return(result);
11796   }
11797   #endif
11798   
11799   
11800   #endif
11801   
11802   
11803   #ifdef ALLOW_FILESEARCH
11804   
11805   
11806   /***********************************************************************************
11807     Function:
11808       int FindFirst (const char * fileName, unsigned int attr, SearchRec * rec)
11809     Summary:
11810       Initial search function for the input Ascii fileName
11811     Conditions:
11812       None
11813     Input:
11814       fileName - The name to search for
11815                - Parital string search characters
11816                - * - Indicates the rest of the filename or extension can vary (e.g. FILE.*)
11817                - ? - Indicates that one character in a filename can vary (e.g. F?LE.T?T)
11818       attr -            The attributes that a found file may have
11819            - ATTR_READ_ONLY -  File may be read only
11820            - ATTR_HIDDEN -     File may be a hidden file
11821            - ATTR_SYSTEM -     File may be a system file
11822            - ATTR_VOLUME -     Entry may be a volume label
11823            - ATTR_DIRECTORY -  File may be a directory
11824            - ATTR_ARCHIVE -    File may have archive attribute
11825            - ATTR_MASK -       All attributes
11826       rec -             pointer to a structure to put the file information in
11827     Return Values:
11828       0 -  File was found
11829       -1 - No file matching the specified criteria was found
11830     Side Effects:
11831       Search criteria from previous FindFirst call on passed SearchRec object
11832       will be lost. "utf16LFNfound" is overwritten after subsequent FindFirst/FindNext
11833       operations.It is the responsibility of the application to read the "utf16LFNfound"
11834       before it is lost.The FSerrno variable will be changed.
11835     Description:
11836       The FindFirst function will search for a file based on parameters passed in
11837       by the user.  This function will use the FILEfind function to parse through
11838       the current working directory searching for entries that match the specified
11839       parameters.  If a file is found, its parameters are copied into the SearchRec
11840       structure, as are the initial parameters passed in by the user and the position
11841       of the file entry in the current working directory.If the return value of the 
11842       function is 0 then "utf16LFNfoundLength" indicates whether the file found was 
11843       long file name or short file name(8P3 format). The "utf16LFNfoundLength" is non-zero
11844       for long file name and is zero for 8P3 format."utf16LFNfound" points to the
11845       address of long file name if found during the operation.
11846     Remarks:
11847       Call FindFirst or FindFirstpgm before calling FindNext
11848     ***********************************************************************************/
11849   
11850   int FindFirst (const char * fileName, unsigned int attr, SearchRec * rec)
11851   {
11852       FSFILE f;
11853       FILEOBJ fo = &f;
11854       WORD fHandle;
11855       BYTE j;
11856       BYTE Index;
11857   	#ifdef SUPPORT_LFN
11858   		short int indexLFN;
11859   	#endif
11860   
11861       FSerrno = CE_GOOD;
11862   
11863   	#ifdef SUPPORT_LFN
11864   		fo->utf16LFNptr = &recordSearchName[0];
11865   		rec->utf16LFNfound = &recordFoundName[0];
11866   	#endif
11867   
11868   	// Format the file name as per 8.3 format or LFN format
11869       if( !FormatFileName(fileName, fo, 1) )
11870       {
11871           FSerrno = CE_INVALID_FILENAME;
11872           return -1;
11873       }
11874   
11875       rec->initialized = FALSE;
11876   
11877   	#if defined(SUPPORT_LFN)
11878   	rec->AsciiEncodingType = fo->AsciiEncodingType;
11879   	recordSearchLength = fo->utf16LFNlength;
11880   
11881   	// If file name is 8.3 format copy it in 'searchname' string
11882       if(!recordSearchLength)
11883   	#endif
11884       {
11885       	for (Index = 0; (Index < 12) && (fileName[Index] != 0); Index++)
11886       	{
11887           	rec->searchname[Index] = fileName[Index];
11888       	}
11889   
11890       	for (;Index < FILE_NAME_SIZE_8P3 + 2; Index++)
11891       	{
11892           	rec->searchname[Index] = 0;
11893       	}
11894   	}
11895   
11896       rec->searchattr = attr;
11897   #ifdef ALLOW_DIRS
11898       rec->cwdclus = cwdptr->dirclus;
11899   #else
11900       rec->cwdclus = FatRootDirClusterValue;
11901   #endif
11902   
11903       fo->dsk = &gDiskData;
11904       fo->cluster = 0;
11905       fo->ccls    = 0;
11906       fo->entry = 0;
11907       fo->attributes = attr;
11908   
11909   #ifndef ALLOW_DIRS
11910       // start at the root directory
11911       fo->dirclus    = FatRootDirClusterValue;
11912       fo->dirccls    = FatRootDirClusterValue;
11913   #else
11914       fo->dirclus = cwdptr->dirclus;
11915       fo->dirccls = cwdptr->dirccls;
11916   #endif
11917   
11918       // copy file object over
11919       FileObjectCopy(&gFileTemp, fo);
11920   
11921       // See if the file is found
11922       if (FILEfind (fo, &gFileTemp,LOOK_FOR_MATCHING_ENTRY, 1) != CE_GOOD)
11923       {
11924           FSerrno = CE_FILE_NOT_FOUND;
11925           return -1;
11926       }
11927   
11928       fHandle = fo->entry;
11929   
11930       if (FILEopen (fo, &fHandle, 'r') == CE_GOOD)
11931       {
11932   		#if defined(SUPPORT_LFN)
11933   		rec->utf16LFNfoundLength = fo->utf16LFNlength;
11934   		if(fo->utf16LFNlength)
11935   		{
11936   			indexLFN = fo->utf16LFNlength;
11937   			recordFoundName[indexLFN] = 0x0000;
11938   			while(indexLFN--)
11939   				recordFoundName[indexLFN] = fileFoundString[indexLFN];
11940   		}
11941   		#endif
11942   
11943   		for(j = 0; j < FILE_NAME_SIZE_8P3 + 2 ; j++)
11944   		{
11945               rec->filename[j] = 0;
11946   		}
11947   
11948           // Copy as much name as there is
11949           if (fo->attributes != ATTR_VOLUME)
11950           {
11951               for (Index = 0, j = 0; (j < 8) && (fo->name[j] != 0x20); Index++, j++)
11952               {
11953                  rec->filename[Index] = fo->name[j];
11954               }
11955   
11956   			if(fo->name[8] != 0x20)
11957   			{
11958               	rec->filename[Index++] = '.';
11959   
11960   	            // Move to the extension, even if there are more space chars
11961   	            for (j = 8; (j < 11) && (fo->name[j] != 0x20); Index++, j++)
11962   	            {
11963   	               rec->filename[Index] = fo->name[j];
11964   	            }
11965   			}
11966           }
11967           else
11968           {
11969               for (Index = 0; Index < DIR_NAMECOMP; Index++)
11970               {
11971                   rec->filename[Index] = fo->name[Index];
11972               }
11973           }
11974   
11975           rec->attributes = fo->attributes;
11976           rec->filesize = fo->size;
11977           rec->timestamp = (DWORD)((DWORD)fo->date << 16) + fo->time;
11978           rec->entry = fo->entry;
11979           rec->initialized = TRUE;
11980           return 0;
11981       }
11982       else
11983       {
11984           FSerrno = CE_BADCACHEREAD;
11985           return -1;
11986       }
11987   }
11988   
11989   
11990   /**********************************************************************
11991     Function:
11992       int FindNext (SearchRec * rec)
11993     Summary:
11994       Sequential search function
11995     Conditions:
11996       None
11997     Input:
11998       rec -  The structure to store the file information in
11999     Return Values:
12000       0 -  File was found
12001       -1 - No additional files matching the specified criteria were found
12002     Side Effects:
12003       Search criteria from previous FindNext call on passed SearchRec object
12004       will be lost. "utf16LFNfound" is overwritten after subsequent FindFirst/FindNext
12005       operations.It is the responsibility of the application to read the "utf16LFNfound"
12006       before it is lost.The FSerrno variable will be changed.
12007     Description:
12008       The FindNext function performs the same function as the FindFirst
12009       funciton, except it does not copy any search parameters into the
12010       SearchRec structure (only info about found files) and it begins
12011       searching at the last directory entry offset at which a file was
12012       found, rather than at the beginning of the current working
12013       directory.If the return value of the function is 0 then "utf16LFNfoundLength"
12014       indicates whether the file found was long file name or short file
12015       name(8P3 format). The "utf16LFNfoundLength" is non-zero for long file name
12016       and is zero for 8P3 format."utf16LFNfound" points to the address of long 
12017       file name if found during the operation.
12018     Remarks:
12019       Call FindFirst or FindFirstpgm before calling this function
12020     **********************************************************************/
12021   
12022   int FindNext (SearchRec * rec)
12023   {
12024       FSFILE f;
12025       FILEOBJ fo = &f;
12026       BYTE i, j;
12027   	#ifdef SUPPORT_LFN
12028   		short int indexLFN;
12029   	#endif
12030   
12031       FSerrno = CE_GOOD;
12032   
12033       // Make sure we called FindFirst on this object
12034       if (rec->initialized == FALSE)
12035       {
12036           FSerrno = CE_NOT_INIT;
12037           return -1;
12038       }
12039   
12040       // Make sure we called FindFirst in the cwd
12041   #ifdef ALLOW_DIRS
12042       if (rec->cwdclus != cwdptr->dirclus)
12043       {
12044           FSerrno = CE_INVALID_ARGUMENT;
12045           return -1;
12046       }
12047   #endif
12048       
12049   	#if defined(SUPPORT_LFN)
12050       fo->AsciiEncodingType = rec->AsciiEncodingType;
12051       fo->utf16LFNlength = recordSearchLength;
12052   	if(fo->utf16LFNlength)
12053   	{
12054   	    fo->utf16LFNptr = &recordSearchName[0];
12055       }
12056   	else
12057   	#endif
12058   	{
12059   		// Format the file name
12060   	    if( !FormatFileName(rec->searchname, fo, 1) )
12061   	    {
12062   	        FSerrno = CE_INVALID_FILENAME;
12063   	        return -1;
12064   	    }
12065       }
12066   
12067       /* Brn: Copy the formatted name to "fo" which is necesary before calling "FILEfind" function */
12068       //strcpy(fo->name,rec->searchname);
12069   
12070       fo->dsk = &gDiskData;
12071       fo->cluster = 0;
12072       fo->ccls    = 0;
12073       fo->entry = rec->entry + 1;
12074       fo->attributes = rec->searchattr;
12075   
12076   #ifndef ALLOW_DIRS
12077       // start at the root directory
12078       fo->dirclus    = FatRootDirClusterValue;
12079       fo->dirccls    = FatRootDirClusterValue;
12080   #else
12081       fo->dirclus = cwdptr->dirclus;
12082       fo->dirccls = cwdptr->dirccls;
12083   #endif
12084   
12085       // copy file object over
12086       FileObjectCopy(&gFileTemp, fo);
12087   
12088       // See if the file is found
12089       if (CE_GOOD != FILEfind (fo, &gFileTemp,LOOK_FOR_MATCHING_ENTRY, 1))
12090       {
12091           FSerrno = CE_FILE_NOT_FOUND;
12092           return -1;
12093       }
12094       else
12095       {
12096   		#if defined(SUPPORT_LFN)
12097   		rec->utf16LFNfoundLength = fo->utf16LFNlength;
12098   		if(fo->utf16LFNlength)
12099   		{
12100   			indexLFN = fo->utf16LFNlength;
12101   			recordFoundName[indexLFN] = 0x0000;
12102   			while(indexLFN--)
12103   				recordFoundName[indexLFN] = fileFoundString[indexLFN];
12104   		}
12105   		#endif
12106   
12107   		for(j = 0; j < FILE_NAME_SIZE_8P3 + 2 ; j++)
12108   		{
12109               rec->filename[j] = 0;
12110   		}
12111   
12112           if (fo->attributes != ATTR_VOLUME)
12113           {
12114               for (i = 0, j = 0; (j < 8) && (fo->name[j] != 0x20); i++, j++)
12115               {
12116                  rec->filename[i] = fo->name[j];
12117               }
12118   
12119   			if(fo->name[8] != 0x20)
12120   			{
12121               	rec->filename[i++] = '.';
12122   
12123   	            // Move to the extension, even if there are more space chars
12124   	            for (j = 8; (j < 11) && (fo->name[j] != 0x20); i++, j++)
12125   	            {
12126   	               rec->filename[i] = fo->name[j];
12127   	            }
12128   			}
12129           }
12130           else
12131           {
12132               for (i = 0; i < DIR_NAMECOMP; i++)
12133               {
12134                   rec->filename[i] = fo->name[i];
12135               }
12136           }
12137   
12138           rec->attributes = fo->attributes;
12139           rec->filesize = fo->size;
12140           rec->timestamp = (DWORD)((DWORD)fo->date << 16) + fo->time;
12141           rec->entry = fo->entry;
12142           return 0;
12143       }
12144   }
12145   
12146   /***********************************************************************************
12147     Function:
12148       int wFindFirst (const unsigned short int * fileName, unsigned int attr, SearchRec * rec)
12149     Summary:
12150       Initial search function for the input UTF16 fileName
12151     Conditions:
12152       None
12153     Input:
12154       fileName - The name to search for
12155                - Parital string search characters
12156                - * - Indicates the rest of the filename or extension can vary (e.g. FILE.*)
12157                - ? - Indicates that one character in a filename can vary (e.g. F?LE.T?T)
12158       attr -            The attributes that a found file may have
12159            - ATTR_READ_ONLY -  File may be read only
12160            - ATTR_HIDDEN -     File may be a hidden file
12161            - ATTR_SYSTEM -     File may be a system file
12162            - ATTR_VOLUME -     Entry may be a volume label
12163            - ATTR_DIRECTORY -  File may be a directory
12164            - ATTR_ARCHIVE -    File may have archive attribute
12165            - ATTR_MASK -       All attributes
12166       rec -             pointer to a structure to put the file information in
12167     Return Values:
12168       0 -  File was found
12169       -1 - No file matching the specified criteria was found
12170     Side Effects:
12171       Search criteria from previous wFindFirst call on passed SearchRec object
12172       will be lost. "utf16LFNfound" is overwritten after subsequent wFindFirst/FindNext
12173       operations.It is the responsibility of the application to read the "utf16LFNfound"
12174       before it is lost.The FSerrno variable will be changed.
12175     Description:
12176       The wFindFirst function will search for a file based on parameters passed in
12177       by the user.  This function will use the FILEfind function to parse through
12178       the current working directory searching for entries that match the specified
12179       parameters.  If a file is found, its parameters are copied into the SearchRec
12180       structure, as are the initial parameters passed in by the user and the position
12181       of the file entry in the current working directory.If the return value of the 
12182       function is 0 then "utf16LFNfoundLength" indicates whether the file found was 
12183       long file name or short file name(8P3 format). The "utf16LFNfoundLength" is non-zero
12184       for long file name and is zero for 8P3 format."utf16LFNfound" points to the
12185       address of long file name if found during the operation.
12186     Remarks:
12187       Call FindFirst or FindFirstpgm before calling FindNext
12188     ***********************************************************************************/
12189   
12190   #ifdef SUPPORT_LFN
12191   int wFindFirst (const unsigned short int * fileName, unsigned int attr, SearchRec * rec)
12192   {
12193   	int result;
12194   	utfModeFileName = TRUE;
12195   	result = FindFirst ((const char *)fileName,attr,rec);
12196   	utfModeFileName = FALSE;
12197   	return result;
12198   }
12199   #endif
12200   
12201   #endif
12202   
12203   #ifdef ALLOW_FSFPRINTF
12204   
12205   
12206   /**********************************************************************
12207     Function:
12208       int FSputc (char c, FSFILE * file)
12209     Summary:
12210       FSfprintf helper function to write a char
12211     Conditions:
12212       This function should not be called by the user.
12213     Input:
12214       c - The character to write to the file.
12215       file - The file to write to.
12216     Return Values:
12217       0 -   The character was written successfully
12218       EOF - The character was not written to the file.
12219     Side Effects:
12220       None
12221     Description:
12222       This is a helper function for FSfprintf.  It will write one
12223       character to a file.
12224     Remarks:
12225       None
12226     **********************************************************************/
12227   
12228   int FSputc (char c, FSFILE * file)
12229   {
12230       if (FSfwrite ((void *)&c, 1, 1, file) != 1)
12231           return EOF;
12232       else
12233           return 0;
12234   }
12235   
12236   
12237   /**********************************************************************
12238     Function:
12239       int str_put_n_chars (FSFILE * handle, unsigned char n, char c)
12240     Summary:
12241       FSfprintf helper function to write a char multiple times
12242     Conditions:
12243       This function should not be called by the user.
12244     Input:
12245       handle - The file to write to.
12246       n -      The number of times to write that character to a file.
12247       c - The character to write to the file.
12248     Return Values:
12249       0 -   The characters were written successfully
12250       EOF - The characters were not written to the file.
12251     Side Effects:
12252       None
12253     Description:
12254       This funciton is used by the FSfprintf function to write multiple
12255       instances of a single character to a file (for example, when
12256       padding a format specifier with leading spacez or zeros).
12257     Remarks:
12258       None.
12259     **********************************************************************/
12260   
12261   
12262   unsigned char str_put_n_chars (FSFILE * handle, unsigned char n, char c)
12263   {
12264       while (n--)
12265       if (FSputc (c, handle) == EOF)
12266           return 1;
12267       return 0;
12268   }
12269   
12270   
12271   /**********************************************************************
12272     Function:
12273       // PIC24/30/33/32
12274       int FSfprintf (FSFILE * fptr, const char * fmt, ...)
12275       // PIC18
12276       int FSfpritnf (FSFILE * fptr, const rom char * fmt, ...)
12277     Summary:
12278       Function to write formatted strings to a file
12279     Conditions:
12280       For PIC18, integer promotion must be enabled in the project build
12281       options menu.  File opened in a write mode.
12282     Input:
12283       fptr - A pointer to the file to write to.
12284       fmt -  A string of characters and format specifiers to write to
12285              the file
12286       ... -  Additional arguments inserted in the string by format
12287              specifiers
12288     Returns:
12289       The number of characters written to the file
12290     Side Effects:
12291       The FSerrno variable will be changed.
12292     Description:
12293       Writes a specially formatted string to a file.
12294     Remarks:
12295       Consult AN1045 for a full description of how to use format
12296       specifiers.
12297     **********************************************************************/
12298   
12299   #ifdef __18CXX
12300   int FSfprintf (FSFILE *fptr, const rom char *fmt, ...)
12301   #else
12302   int FSfprintf (FSFILE *fptr, const char * fmt, ...)
12303   #endif
12304   {
12305       va_list ap;
12306       int n;
12307   
12308       va_start (ap, fmt);
12309       n = FSvfprintf (fptr, fmt, ap);
12310       va_end (ap);
12311       return n;
12312   }
12313   
12314   
12315   /**********************************************************************
12316     Function:
12317       // PIC24/30/33/32
12318       int FSvfprintf (FSFILE * handle, const char * formatString, va_list ap)
12319       // PIC18
12320       int FSvfpritnf (auto FSFILE * handle, auto const rom char * formatString, auto va_list ap)
12321     Summary:
12322       Helper function for FSfprintf
12323     Conditions:
12324       This function should not be called by the user.
12325     Input:
12326       handle -        A pointer to the file to write to.
12327       formatString -  A string of characters and format specifiers to write to
12328                       the file
12329       ap -            A structure pointing to the arguments on the stack
12330     Returns:
12331       The number of characters written to the file
12332     Side Effects:
12333       The FSerrno variable will be changed.
12334     Description:
12335       This helper function will access the elements passed to FSfprintf
12336     Remarks:
12337       Consult AN1045 for a full description of how to use format
12338       specifiers.
12339     **********************************************************************/
12340   
12341   #ifdef __18CXX
12342   int FSvfprintf (auto FSFILE *handle, auto const rom char * formatString, auto va_list ap)
12343   #else
12344   int FSvfprintf (FSFILE *handle, const char * formatString, va_list ap)
12345   #endif
12346   {
12347       unsigned char c;
12348       int count = 0;
12349   
12350       for (c = *formatString; c; c = *++formatString)
12351       {
12352           if (c == '%')
12353           {
12354               unsigned char    flags = 0;
12355               unsigned char    width = 0;
12356               unsigned char    precision = 0;
12357               unsigned char    have_precision = 0;
12358               unsigned char    size = 0;
12359   #ifndef __18CXX
12360               unsigned char   size2 = 0;
12361   #endif
12362               unsigned char    space_cnt;
12363               unsigned char    cval;
12364   #ifdef __18CXX
12365               unsigned long    larg;
12366               far rom char *   romstring;
12367   #else
12368               unsigned long long larg;
12369   #endif
12370               char *         ramstring;
12371               int n;
12372   
12373               FSerrno = CE_GOOD;
12374   
12375               c = *++formatString;
12376   
12377               while ((c == '-') || (c == '+') || (c == ' ') || (c == '#') || (c == '0'))
12378               {
12379                   switch (c)
12380                   {
12381                       case '-':
12382                           flags |= _FLAG_MINUS;
12383                           break;
12384                       case '+':
12385                           flags |= _FLAG_PLUS;
12386                           break;
12387                       case ' ':
12388                           flags |= _FLAG_SPACE;
12389                           break;
12390                       case '#':
12391                           flags |= _FLAG_OCTO;
12392                           break;
12393                       case '0':
12394                           flags |= _FLAG_ZERO;
12395                           break;
12396                   }
12397                   c = *++formatString;
12398               }
12399               /* the optional width field is next */
12400               if (c == '*')
12401               {
12402                   n = va_arg (ap, int);
12403                   if (n < 0)
12404                   {
12405                       flags |= _FLAG_MINUS;
12406                       width = -n;
12407                   }
12408                   else
12409                       width = n;
12410                   c = *++formatString;
12411               }
12412               else
12413               {
12414                   cval = 0;
12415                   while ((unsigned char) isdigit (c))
12416                   {
12417                       cval = cval * 10 + c - '0';
12418                       c = *++formatString;
12419                   }
12420                   width = cval;
12421               }
12422   
12423               /* if '-' is specified, '0' is ignored */
12424               if (flags & _FLAG_MINUS)
12425                   flags &= ~_FLAG_ZERO;
12426   
12427               /* the optional precision field is next */
12428               if (c == '.')
12429               {
12430                   c = *++formatString;
12431                   if (c == '*')
12432                   {
12433                       n = va_arg (ap, int);
12434                       if (n >= 0)
12435                       {
12436                           precision = n;
12437                           have_precision = 1;
12438                       }
12439                       c = *++formatString;
12440                   }
12441                   else
12442                   {
12443                       cval = 0;
12444                       while ((unsigned char) isdigit (c))
12445                       {
12446                           cval = cval * 10 + c - '0';
12447                           c = *++formatString;
12448                       }
12449                       precision = cval;
12450                       have_precision = 1;
12451                   }
12452               }
12453   
12454               /* the optional 'h' specifier. since int and short int are
12455                   the same size for MPLAB C18, this is a NOP for us. */
12456               if (c == 'h')
12457               {
12458                   c = *++formatString;
12459                   /* if 'c' is another 'h' character, this is an 'hh'
12460                       specifier and the size is 8 bits */
12461                   if (c == 'h')
12462                   {
12463                       size = _FMT_BYTE;
12464                       c = *++formatString;
12465                   }
12466               }
12467               else if ((c == 't') || (c == 'z'))
12468                   c = *++formatString;
12469   #ifdef __18CXX
12470               else if ((c == 'H') || (c == 'T') || (c == 'Z'))
12471               {
12472                   size = _FMT_SHRTLONG;
12473                   c = *++formatString;
12474               }
12475               else if ((c == 'l') || (c == 'j'))
12476   #else
12477               else if ((c == 'q') || (c == 'j'))
12478               {
12479                   size = _FMT_LONGLONG;
12480                   c = *++formatString;
12481               }
12482               else if (c == 'l')
12483   #endif
12484               {
12485                   size = _FMT_LONG;
12486                   c = *++formatString;
12487               }
12488   
12489               switch (c)
12490               {
12491                   case '\0':
12492                   /* this is undefined behaviour. we have a trailing '%' character
12493                       in the string, perhaps with some flags, width, precision
12494                       stuff as well, but no format specifier. We'll, arbitrarily,
12495                       back up a character so that the loop will terminate
12496                       properly when it loops back and we'll output a '%'
12497                       character. */
12498                       --formatString;
12499                   /* fallthrough */
12500                   case '%':
12501                       if (FSputc ('%', handle) == EOF)
12502                       {
12503                           FSerrno = CE_WRITE_ERROR;
12504                           return EOF;
12505                       }
12506                       ++count;
12507                       break;
12508                   case 'c':
12509                       space_cnt = 0;
12510                       if (width > 1)
12511                       {
12512                           space_cnt = width - 1;
12513                           count += space_cnt;
12514                       }
12515                       if (space_cnt && !(flags & _FLAG_MINUS))
12516                       {
12517                           if (str_put_n_chars (handle, space_cnt, ' '))
12518                           {
12519                               FSerrno = CE_WRITE_ERROR;
12520                               return EOF;
12521                           }
12522                           space_cnt = 0;
12523                       }
12524                       c = va_arg (ap, int);
12525                       if (FSputc (c, handle) == EOF)
12526                       {
12527                           FSerrno = CE_WRITE_ERROR;
12528                           return EOF;
12529                       }
12530                       ++count;
12531                       if (str_put_n_chars (handle, space_cnt, ' '))
12532                       {
12533                           FSerrno = CE_WRITE_ERROR;
12534                           return EOF;
12535                       }
12536                       break;
12537                   case 'S':
12538   #ifdef __18CXX
12539                       if (size == _FMT_SHRTLONG)
12540                           romstring = va_arg (ap, rom far char *);
12541                       else
12542                           romstring = (far rom char*)va_arg (ap, rom near char *);
12543                       n = strlenpgm (romstring);
12544                       /* Normalize the width based on the length of the actual
12545                           string and the precision. */
12546                       if (have_precision && precision < (unsigned char) n)
12547                           n = precision;
12548                       if (width < (unsigned char) n)
12549                           width = n;
12550                       space_cnt = width - (unsigned char) n;
12551                       count += space_cnt;
12552                       /* we've already calculated the space count that the width
12553                           will require. now we want the width field to have the
12554                           number of character to display from the string itself,
12555                           limited by the length of the actual string and the
12556                           specified precision. */
12557                       if (have_precision && precision < width)
12558                           width = precision;
12559                       /* if right justified, we print the spaces before the
12560                           string */
12561                       if (!(flags & _FLAG_MINUS))
12562                       {
12563                           if (str_put_n_chars (handle, space_cnt, ' '))
12564                           {
12565                               FSerrno = CE_WRITE_ERROR;
12566                               return EOF;
12567                           }
12568                           space_cnt = 0;
12569                       }
12570                       cval = 0;
12571                       for (c = *romstring; c && cval < width; c = *++romstring)
12572                       {
12573                           if (FSputc (c, handle) == EOF)
12574                           {
12575                               FSerrno = CE_WRITE_ERROR;
12576                               return EOF;
12577                           }
12578                           ++count;
12579                           ++cval;
12580                       }
12581                       /* If there are spaces left, it's left justified.
12582                           Either way, calling the function unconditionally
12583                           is smaller code. */
12584                       if (str_put_n_chars (handle, space_cnt, ' '))
12585                       {
12586                           FSerrno = CE_WRITE_ERROR;
12587                           return EOF;
12588                       }
12589                       break;
12590   #endif
12591                   case 's':
12592                       ramstring = va_arg (ap, char *);
12593                       n = strlen (ramstring);
12594                       /* Normalize the width based on the length of the actual
12595                           string and the precision. */
12596                       if (have_precision && precision < (unsigned char) n)
12597                           n = precision;
12598                       if (width < (unsigned char) n)
12599                           width = n;
12600                       space_cnt = width - (unsigned char) n;
12601                       count += space_cnt;
12602                       /* we've already calculated the space count that the width
12603                           will require. now we want the width field to have the
12604                           number of character to display from the string itself,
12605                           limited by the length of the actual string and the
12606                           specified precision. */
12607                       if (have_precision && precision < width)
12608                           width = precision;
12609                       /* if right justified, we print the spaces before the string */
12610                       if (!(flags & _FLAG_MINUS))
12611                       {
12612                           if (str_put_n_chars (handle, space_cnt, ' '))
12613                           {
12614                               FSerrno = CE_WRITE_ERROR;
12615                               return EOF;
12616                           }
12617                           space_cnt = 0;
12618                       }
12619                       cval = 0;
12620                       for (c = *ramstring; c && cval < width; c = *++ramstring)
12621                       {
12622                           if (FSputc (c, handle) == EOF)
12623                           {
12624                               FSerrno = CE_WRITE_ERROR;
12625                               return EOF;
12626                           }
12627                           ++count;
12628                           ++cval;
12629                       }
12630                       /* If there are spaces left, it's left justified.
12631                           Either way, calling the function unconditionally
12632                           is smaller code. */
12633                       if (str_put_n_chars (handle, space_cnt, ' '))
12634                       {
12635                           FSerrno = CE_WRITE_ERROR;
12636                           return EOF;
12637                       }
12638                       break;
12639                   case 'd':
12640                   case 'i':
12641                       flags |= _FLAG_SIGNED;
12642                   /* fall through */
12643                   case 'o':
12644                   case 'u':
12645                   case 'x':
12646                   case 'X':
12647                   case 'b':
12648                   case 'B':
12649                       /* This is a bit of a trick. The 'l' and 'hh' size
12650                           specifiers are valid only for the integer conversions,
12651                           not the 'p' or 'P' conversions, and are ignored for the
12652                           latter. By jumping over the additional size specifier
12653                           checks here we get the best code size since we can
12654                           limit the size checks in the remaining code. */
12655                       if (size == _FMT_LONG)
12656                       {
12657                           if (flags & _FLAG_SIGNED)
12658                               larg = va_arg (ap, long int);
12659                           else
12660                               larg = va_arg (ap, unsigned long int);
12661                           goto _do_integer_conversion;
12662                       }
12663                       else if (size == _FMT_BYTE)
12664                       {
12665                           if (flags & _FLAG_SIGNED)
12666                               larg = (signed char) va_arg (ap, int);
12667                           else
12668                               larg = (unsigned char) va_arg (ap, unsigned int);
12669                           goto _do_integer_conversion;
12670                       }
12671   #ifndef __18CXX
12672                       else if (size == _FMT_LONGLONG)
12673                       {
12674                           if (flags & _FLAG_SIGNED)
12675                               larg = (signed long long)va_arg (ap, long long);
12676                           else
12677                               larg = (unsigned long long) va_arg (ap, unsigned long long);
12678                           goto _do_integer_conversion;
12679                       }
12680   #endif
12681                       /* fall trough */
12682                   case 'p':
12683                   case 'P':
12684   #ifdef __18CXX
12685                       if (size == _FMT_SHRTLONG)
12686                       {
12687                           if (flags & _FLAG_SIGNED)
12688                               larg = va_arg (ap, short long int);
12689                           else
12690                               larg = va_arg (ap, unsigned short long int);
12691                       }
12692                       else
12693   #endif
12694                           if (flags & _FLAG_SIGNED)
12695                               larg = va_arg (ap, int);
12696                           else
12697                               larg = va_arg (ap, unsigned int);
12698                       _do_integer_conversion:
12699                           /* default precision is 1 */
12700                           if (!have_precision)
12701                               precision = 1;
12702                           {
12703                               unsigned char digit_cnt = 0;
12704                               unsigned char prefix_cnt = 0;
12705                               unsigned char sign_char;
12706                               /* A 32 bit number will require at most 32 digits in the
12707                                   string representation (binary format). */
12708   #ifdef __18CXX
12709                               char buf[33];
12710                               /* Start storing digits least-significant first */
12711                               char *q = &buf[31];
12712                               /* null terminate the string */
12713                               buf[32] = '\0';
12714   #else
12715                               char buf[65];
12716                               char *q = &buf[63];
12717                               buf[64] = '\0';
12718   #endif
12719                               space_cnt = 0;
12720                               size = 10;
12721   
12722                               switch (c)
12723                               {
12724                                   case 'b':
12725                                   case 'B':
12726                                       size = 2;
12727   #ifndef __18CXX
12728                                       size2 = 1;
12729   #endif
12730                                       break;
12731                                   case 'o':
12732                                       size = 8;
12733   #ifndef __18CXX
12734                                       size2 = 3;
12735   #endif
12736                                       break;
12737                                   case 'p':
12738                                   case 'P':
12739                                       /* from here on out, treat 'p' conversions just
12740                                           like 'x' conversions. */
12741                                       c += 'x' - 'p';
12742                                   /* fall through */
12743                                   case 'x':
12744                                   case 'X':
12745                                       size = 16;
12746   #ifndef __18CXX
12747                                       size2 = 4;
12748   #endif
12749                                       break;
12750                               }// switch (c)
12751   
12752                               /* if it's an unsigned conversion, we should ignore the
12753                                   ' ' and '+' flags */
12754                               if (!(flags & _FLAG_SIGNED))
12755                                   flags &= ~(_FLAG_PLUS | _FLAG_SPACE);
12756   
12757                               /* if it's a negative value, we need to negate the
12758                                   unsigned version before we convert to text. Using
12759                                   unsigned for this allows us to (ab)use the 2's
12760                                   complement system to avoid overflow and be able to
12761                                   adequately handle LONG_MIN.
12762   
12763                                   We'll figure out what sign character to print, if
12764                                   any, here as well. */
12765   #ifdef __18CXX
12766                               if (flags & _FLAG_SIGNED && ((long) larg < 0))
12767                               {
12768                                   larg = -(long) larg;
12769   #else
12770                               if (flags & _FLAG_SIGNED && ((long long) larg < 0))
12771                               {
12772                                   larg = -(long long) larg;
12773   #endif
12774                                   sign_char = '-';
12775                                   ++digit_cnt;
12776                               }
12777                               else if (flags & _FLAG_PLUS)
12778                               {
12779                           sign_char = '+';
12780                           ++digit_cnt;
12781                        }
12782                         else if (flags & _FLAG_SPACE)
12783                         {
12784                                   sign_char = ' ';
12785                                   ++digit_cnt;
12786                               }
12787                               else
12788                                   sign_char = '\0';
12789                               /* get the digits for the actual number. If the
12790                                   precision is zero and the value is zero, the result
12791                                   is no characters. */
12792                               if (precision || larg)
12793                               {
12794                                   do
12795                                   {
12796   #ifdef __18CXX
12797                                       cval = s_digits[larg % size];
12798                                       if ((c == 'X') && (cval >= 'a'))
12799                                           cval -= 'a' - 'A';
12800                                       larg /= size;
12801   #else
12802                                       // larg is congruent mod size2 to its lower 16 bits
12803                                       // for size2 = 2^n, 0 <= n <= 4
12804                                       if (size2 != 0)
12805                                           cval = s_digits[(unsigned int) larg % size];
12806                                       else
12807                                           cval = s_digits[larg % size];
12808                                       if ((c == 'X') && (cval >= 'a'))
12809                                           cval -= 'a' - 'A';
12810                                       if (size2 != 0)
12811                                           larg = larg >> size2;
12812                                       else
12813                                           larg /= size;
12814   #endif
12815                                       *q-- = cval;
12816                                       ++digit_cnt;
12817                                   } while (larg);
12818                                   /* if the '#' flag was specified and we're dealing
12819                                       with an 'o', 'b', 'B', 'x', or 'X' conversion,
12820                                       we need a bit more. */
12821                                   if (flags & _FLAG_OCTO)
12822                                   {
12823                                       if (c == 'o')
12824                                       {
12825                                           /* per the standard, for octal, the '#' flag
12826                                               makes the precision be at least one more
12827                                               than the number of digits in the number */
12828                                           if (precision <= digit_cnt)
12829                                               precision = digit_cnt + 1;
12830                                       }
12831                                       else if ((c == 'x') || (c == 'X') || (c == 'b') || (c == 'B'))
12832                                           prefix_cnt = 2;
12833                                   }
12834                               }
12835                               else
12836                                   digit_cnt = 0;
12837   
12838                               /* The leading zero count depends on whether the '0'
12839                                   flag was specified or not. If it was not, then the
12840                                   count is the difference between the specified
12841                                   precision and the number of digits (including the
12842                                   sign character, if any) to be printed; otherwise,
12843                                   it's as if the precision were equal to the max of
12844                                   the specified precision and the field width. If a
12845                                   precision was specified, the '0' flag is ignored,
12846                                   however. */
12847                               if ((flags & _FLAG_ZERO) && (width > precision)
12848                                   && !have_precision)
12849                                   precision = width;
12850                               /* for the rest of the processing, precision contains
12851                                   the leading zero count for the conversion. */
12852                               if (precision > digit_cnt)
12853                                   precision -= digit_cnt;
12854                               else
12855                                   precision = 0;
12856                               /* the space count is the difference between the field
12857                                   width and the digit count plus the leading zero
12858                                   count. If the width is less than the digit count
12859                                   plus the leading zero count, the space count is
12860                                   zero. */
12861                               if (width > precision + digit_cnt + prefix_cnt)
12862                                   space_cnt =   width - precision - digit_cnt - prefix_cnt;
12863   
12864                               /* for output, we check the justification, if it's
12865                                   right justified and the space count is positive, we
12866                                   emit the space characters first. */
12867                               if (!(flags & _FLAG_MINUS) && space_cnt)
12868                               {
12869                                   if (str_put_n_chars (handle, space_cnt, ' '))
12870                                   {
12871                                       FSerrno = CE_WRITE_ERROR;
12872                                       return EOF;
12873                                   }
12874                                   count += space_cnt;
12875                                   space_cnt = 0;
12876                               }
12877                               /* if we have a sign character to print, that comes
12878                                   next */
12879                               if (sign_char)
12880                                   if (FSputc (sign_char, handle) == EOF)
12881                                   {
12882                                       FSerrno = CE_WRITE_ERROR;
12883                                       return EOF;
12884                                   }
12885                               /* if we have a prefix (0b, 0B, 0x or 0X), that's next */
12886                               if (prefix_cnt)
12887                               {
12888                                   if (FSputc ('0', handle) == EOF)
12889                                   {
12890                                       FSerrno = CE_WRITE_ERROR;
12891                                       return EOF;
12892                                   }
12893                                   if (FSputc (c, handle) == EOF)
12894                                   {
12895                                       FSerrno = CE_WRITE_ERROR;
12896                                       return EOF;
12897                                   }
12898                               }
12899                               /* if we have leading zeros, they follow. the prefix, if any
12900                                   is included in the number of digits when determining how
12901                                   many leading zeroes are needed. */
12902   //                            if (precision > prefix_cnt)
12903     //                              precision -= prefix_cnt;
12904                               if (str_put_n_chars (handle, precision, '0'))
12905                               {
12906                                   FSerrno = CE_WRITE_ERROR;
12907                                   return EOF;
12908                               }
12909                               /* print the actual number */
12910                               for (cval = *++q; cval; cval = *++q)
12911                                   if (FSputc (cval, handle) == EOF)
12912                                   {
12913                                       FSerrno = CE_WRITE_ERROR;
12914                                       return EOF;
12915                                   }
12916                               /* if there are any spaces left, they go to right-pad
12917                                   the field */
12918                               if (str_put_n_chars (handle, space_cnt, ' '))
12919                               {
12920                                   FSerrno = CE_WRITE_ERROR;
12921                                   return EOF;
12922                               }
12923   
12924                               count += precision + digit_cnt + space_cnt + prefix_cnt;
12925                           }
12926                           break;
12927                   case 'n':
12928                       switch (size)
12929                       {
12930                           case _FMT_LONG:
12931                               *(long *) va_arg (ap, long *) = count;
12932                               break;
12933   #ifdef __18CXX
12934                           case _FMT_SHRTLONG:
12935                               *(short long *) va_arg (ap, short long *) = count;
12936                               break;
12937   #else
12938                           case _FMT_LONGLONG:
12939                               *(long long *) va_arg (ap, long long *) = count;
12940                               break;
12941   #endif
12942                           case _FMT_BYTE:
12943                               *(signed char *) va_arg (ap, signed char *) = count;
12944                               break;
12945                           default:
12946                               *(int *) va_arg (ap, int *) = count;
12947                               break;
12948                       }
12949                       break;
12950                   default:
12951                       /* undefined behaviour. we do nothing */
12952                       break;
12953               }
12954           }
12955           else
12956           {
12957               if (FSputc (c, handle) == EOF)
12958               {
12959                   FSerrno = CE_WRITE_ERROR;
12960                   return EOF;
12961               }
12962               ++count;
12963           }
12964       }
12965       return count;
12966   }
12967   
12968   
12969   
12970   #endif
12971   
12972   
12973   
12974   
12975