?lang_form? ?lang_select? ?lang_submit? ?lang_endform?
{HEADER END}
{BLAME START}

library

?curdirlinks? -

Blame information for rev 32

Line No. Rev Author Line
1 32 kaklik /******************************************************************************
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 1.2.5 Fixed bug that prevented FAT being updated when media is re-inserted
47 1.2.6 Fixed bug that resulted in a bus error when attempts to read a invalid memory region
48 1.2.6 Fixed bug that prevented the Windows Explorer to show the Date Creation field for directories
49 ********************************************************************/
50  
51 #include "Compiler.h"
52 #include "MDD File System\FSIO.h"
53 #include "GenericTypeDefs.h"
54 #include "string.h"
55 #include "stdlib.h"
56 #include "ctype.h"
57 #include "MDD File System\FSDefs.h"
58  
59 #ifdef ALLOW_FSFPRINTF
60 #include "stdarg.h"
61 #endif
62  
63 #ifdef FS_DYNAMIC_MEM
64 #ifdef __18CXX
65 #include "salloc.h"
66 #endif
67 #endif
68  
69 #ifndef ALLOW_WRITES
70 #ifdef ALLOW_FORMATS
71 #error Write functions must be enabled to use the format function
72 #endif
73 #ifdef ALLOW_FSFPRINTF
74 #error Write functions must be enabled to use the FSfprintf function
75 #endif
76 #endif
77  
78 #ifdef USEREALTIMECLOCK
79 #ifdef USERDEFINEDCLOCK
80 #error Please select only one timestamp clocking mode in FSconfig.h
81 #endif
82 #ifdef INCREMENTTIMESTAMP
83 #error Please select only one timestamp clocking mode in FSconfig.h
84 #endif
85 #elif defined USERDEFINEDCLOCK
86 #ifdef INCREMENTTIMESTAMP
87 #error Please select only one timestamp clocking mode in FSconfig.h
88 #endif
89 #endif
90 /*****************************************************************************/
91 /* Global Variables */
92 /*****************************************************************************/
93  
94 #ifndef FS_DYNAMIC_MEM
95 FSFILE gFileArray[FS_MAX_FILES_OPEN]; // Array that contains file information (static allocation)
96 BYTE gFileSlotOpen[FS_MAX_FILES_OPEN]; // Array that indicates which elements of gFileArray are available for use
97 #endif
98  
99 #if defined(USEREALTIMECLOCK) || defined(USERDEFINEDCLOCK)
100 // Timing variables
101 BYTE gTimeCrtMS; // Global time variable (for timestamps) used to indicate create time (milliseconds)
102 WORD gTimeCrtTime; // Global time variable (for timestamps) used to indicate create time
103 WORD gTimeCrtDate; // Global time variable (for timestamps) used to indicate create date
104 WORD gTimeAccDate; // Global time variable (for timestamps) used to indicate last access date
105 WORD gTimeWrtTime; // Global time variable (for timestamps) used to indicate last update time
106 WORD gTimeWrtDate; // Global time variable (for timestamps) used to indicate last update date
107 #endif
108  
109 DWORD gLastFATSectorRead = 0xFFFFFFFF; // Global variable indicating which FAT sector was read last
110 BYTE gNeedFATWrite = FALSE; // Global variable indicating that there is information that needs to be written to the FAT
111 FSFILE * gBufferOwner = NULL; // Global variable indicating which file is using the data buffer
112 DWORD gLastDataSectorRead = 0xFFFFFFFF; // Global variable indicating which data sector was read last
113 BYTE gNeedDataWrite = FALSE; // Global variable indicating that there is information that needs to be written to the data section
114 BYTE nextClusterIsLast = FALSE; // Global variable indicating that the entries in a directory align with a cluster boundary
115  
116 BYTE gBufferZeroed = FALSE; // Global variable indicating that the data buffer contains all zeros
117  
118 DWORD FatRootDirClusterValue; // Global variable containing the cluster number of the root dir (0 for FAT12/16)
119  
120 BYTE FSerrno; // Global error variable. Set to one of many error codes after each function call.
121  
122 DWORD TempClusterCalc; // Global variable used to store the calculated value of the cluster of a specified sector.
123 BYTE dirCleared; // Global variable used by the "recursive" FSrmdir function to indicate that all subdirectories and files have been deleted from the target directory.
124 BYTE recache = FALSE; // Global variable used by the "recursive" FSrmdir function to indicate that additional cache reads are needed.
125 FSFILE tempCWDobj; // Global variable used to preserve the current working directory information.
126 FSFILE gFileTemp; // Global variable used for file operations.
127  
128 #ifdef ALLOW_DIRS
129 FSFILE cwd; // Global current working directory
130 FSFILE * cwdptr = &cwd; // Pointer to the current working directory
131 #endif
132  
133  
134 #ifdef __18CXX
135 #pragma udata dataBuffer = DATA_BUFFER_ADDRESS
136 BYTE gDataBuffer[MEDIA_SECTOR_SIZE]; // The global data sector buffer
137 #pragma udata FATBuffer = FAT_BUFFER_ADDRESS
138 BYTE gFATBuffer[MEDIA_SECTOR_SIZE]; // The global FAT sector buffer
139 #endif
140  
141 #if defined (__C30__) || defined (__PIC32MX__)
142 BYTE __attribute__ ((aligned(4))) gDataBuffer[MEDIA_SECTOR_SIZE]; // The global data sector buffer
143 BYTE __attribute__ ((aligned(4))) gFATBuffer[MEDIA_SECTOR_SIZE]; // The global FAT sector buffer
144 #endif
145  
146  
147 #pragma udata
148  
149 DISK gDiskData; // Global structure containing device information.
150  
151  
152  
153 /************************************************************************/
154 /* Structures and defines */
155 /************************************************************************/
156  
157 // Directory entry structure
158 typedef struct
159 {
160 char DIR_Name[DIR_NAMESIZE]; // File name
161 char DIR_Extension[DIR_EXTENSION]; // File extension
162 BYTE DIR_Attr; // File attributes
163 BYTE DIR_NTRes; // Reserved byte
164 BYTE DIR_CrtTimeTenth; // Create time (millisecond field)
165 WORD DIR_CrtTime; // Create time (second, minute, hour field)
166 WORD DIR_CrtDate; // Create date
167 WORD DIR_LstAccDate; // Last access date
168 WORD DIR_FstClusHI; // High word of the entry's first cluster number
169 WORD DIR_WrtTime; // Last update time
170 WORD DIR_WrtDate; // Last update date
171 WORD DIR_FstClusLO; // Low word of the entry's first cluster number
172 DWORD DIR_FileSize; // The 32-bit file size
173 }_DIRENTRY;
174  
175 typedef _DIRENTRY * DIRENTRY; // A pointer to a directory entry structure
176  
177 #define DIRECTORY 0x12 // Value indicating that the CreateFileEntry function will be creating a directory
178  
179 #define DIRENTRIES_PER_SECTOR (MEDIA_SECTOR_SIZE / 32) // The number of directory entries in a sector
180  
181 // internal errors
182 #define CE_FAT_EOF 60 // Error that indicates an attempt to read FAT entries beyond the end of the file
183 #define CE_EOF 61 // Error that indicates that the end of the file has been reached
184  
185 typedef FSFILE * FILEOBJ; // Pointer to an FSFILE object
186  
187 #ifdef ALLOW_FSFPRINTF
188  
189 #define _FLAG_MINUS 0x1 // FSfprintf minus flag indicator
190 #define _FLAG_PLUS 0x2 // FSfprintf plus flag indicator
191 #define _FLAG_SPACE 0x4 // FSfprintf space flag indicator
192 #define _FLAG_OCTO 0x8 // FSfprintf octothorpe (hash mark) flag indicator
193 #define _FLAG_ZERO 0x10 // FSfprintf zero flag indicator
194 #define _FLAG_SIGNED 0x80 // FSfprintf signed flag indicator
195  
196 #ifdef __18CXX
197 #define _FMT_UNSPECIFIED 0 // FSfprintf unspecified argument size flag
198 #define _FMT_LONG 1 // FSfprintf 32-bit argument size flag
199 #define _FMT_SHRTLONG 2 // FSfprintf 24-bit argument size flag
200 #define _FMT_BYTE 3 // FSfprintf 8-bit argument size flag
201 #else
202 #define _FMT_UNSPECIFIED 0 // FSfprintf unspecified argument size flag
203 #define _FMT_LONGLONG 1 // FSfprintf 64-bit argument size flag
204 #define _FMT_LONG 2 // FSfprintf 32-bit argument size flag
205 #define _FMT_BYTE 3 // FSfprintf 8-bit argument size flag
206 #endif
207  
208 #ifdef __18CXX
209 static const rom char s_digits[] = "0123456789abcdef"; // FSfprintf table of conversion digits
210 #else
211 static const char s_digits[] = "0123456789abcdef"; // FSfprintf table of conversion digits
212 #endif
213  
214 #endif
215  
216 /************************************************************************************/
217 /* Prototypes */
218 /************************************************************************************/
219  
220 DWORD ReadFAT (DISK *dsk, DWORD ccls);
221 DIRENTRY Cache_File_Entry( FILEOBJ fo, WORD * curEntry, BYTE ForceRead);
222 BYTE Fill_File_Object(FILEOBJ fo, WORD *fHandle);
223 DWORD Cluster2Sector(DISK * disk, DWORD cluster);
224 DIRENTRY LoadDirAttrib(FILEOBJ fo, WORD *fHandle);
225 #ifdef INCREMENTTIMESTAMP
226 void IncrementTimeStamp(DIRENTRY dir);
227 #elif defined USEREALTIMECLOCK
228 void CacheTime (void);
229 #endif
230  
231 #if defined (__C30__) || defined (__PIC32MX__)
232 BYTE ReadByte( BYTE* pBuffer, WORD index );
233 WORD ReadWord( BYTE* pBuffer, WORD index );
234 DWORD ReadDWord( BYTE* pBuffer, WORD index );
235 #endif
236  
237 void FileObjectCopy(FILEOBJ foDest,FILEOBJ foSource);
238 BYTE ValidateChars (char * FileName, BYTE mode);
239 BYTE FormatFileName( const char* fileName, char* fN2, BYTE mode);
240 CETYPE FILEfind( FILEOBJ foDest, FILEOBJ foCompareTo, BYTE cmd, BYTE mode);
241 BYTE FILEget_next_cluster(FILEOBJ fo, DWORD n);
242 CETYPE FILEopen (FILEOBJ fo, WORD *fHandle, char type);
243  
244 // Write functions
245 #ifdef ALLOW_WRITES
246 BYTE Write_File_Entry( FILEOBJ fo, WORD * curEntry);
247 BYTE flushData (void);
248 CETYPE FILEerase( FILEOBJ fo, WORD *fHandle, BYTE EraseClusters);
249 BYTE FILEallocate_new_cluster( FILEOBJ fo, BYTE mode);
250 BYTE FAT_erase_cluster_chain (DWORD cluster, DISK * dsk);
251 DWORD FATfindEmptyCluster(FILEOBJ fo);
252 BYTE FindEmptyEntries(FILEOBJ fo, WORD *fHandle);
253 BYTE PopulateEntries(FILEOBJ fo, char *name , WORD *fHandle, BYTE mode);
254 CETYPE FILECreateHeadCluster( FILEOBJ fo, DWORD *cluster);
255 BYTE EraseCluster(DISK *disk, DWORD cluster);
256 CETYPE CreateFirstCluster(FILEOBJ fo);
257 DWORD WriteFAT (DISK *dsk, DWORD ccls, DWORD value, BYTE forceWrite);
258 CETYPE CreateFileEntry(FILEOBJ fo, WORD *fHandle, BYTE mode);
259 #endif
260  
261 // Directory functions
262 #ifdef ALLOW_DIRS
263 BYTE GetPreviousEntry (FSFILE * fo);
264 BYTE FormatDirName (char * string, BYTE mode);
265 int CreateDIR (char * path);
266 BYTE writeDotEntries (DISK * dsk, DWORD dotAddress, DWORD dotdotAddress);
267 int eraseDir (char * path);
268 #ifdef ALLOW_PGMFUNCTIONS
269 #ifdef ALLOW_WRITES
270 int mkdirhelper (BYTE mode, char * ramptr, const rom char * romptr);
271 int rmdirhelper (BYTE mode, char * ramptr, const rom char * romptr, unsigned char rmsubdirs);
272 #endif
273 int chdirhelper (BYTE mode, char * ramptr, const rom char * romptr);
274 #else
275 #ifdef ALLOW_WRITES
276 int mkdirhelper (BYTE mode, char * ramptr, char * romptr);
277 int rmdirhelper (BYTE mode, char * ramptr, char * romptr, unsigned char rmsubdirs);
278 #endif
279 int chdirhelper (BYTE mode, char * ramptr, char * romptr);
280 #endif
281 #endif
282  
283 #ifdef ALLOW_FSFPRINTF
284 #ifdef __18CXX
285 int FSvfprintf (auto FSFILE *handle, auto const rom char *formatString, auto va_list ap);
286 #else
287 int FSvfprintf (FSFILE *handle, const char *formatString, va_list ap);
288 #endif
289 int FSputc (char c, FSFILE * file);
290 unsigned char str_put_n_chars (FSFILE * handle, unsigned char n, char c);
291 #endif
292  
293 BYTE DISKmount( DISK *dsk);
294 BYTE LoadMBR(DISK *dsk);
295 BYTE LoadBootSector(DISK *dsk);
296 DWORD GetFullClusterNumber(DIRENTRY entry);
297  
298  
299 /*************************************************************************
300 Function:
301 int FSInit(void)
302 Summary:
303 Function to initialize the device.
304 Conditions:
305 The physical device should be connected to the microcontroller.
306 Input:
307 None
308 Return Values:
309 TRUE - Initialization successful
310 FALSE - Initialization unsuccessful
311 Side Effects:
312 The FSerrno variable will be changed.
313 Description:
314 Initializes the static or dynamic memory slots for holding file
315 structures. Initializes the device with the DISKmount function. Loads
316 MBR and boot sector information. Initializes the current working
317 directory to the root directory for the device if directory support
318 is enabled.
319 Remarks:
320 None
321 *************************************************************************/
322  
323 int FSInit(void)
324 {
325 int fIndex;
326 #ifndef FS_DYNAMIC_MEM
327 for( fIndex = 0; fIndex < FS_MAX_FILES_OPEN; fIndex++ )
328 gFileSlotOpen[fIndex] = TRUE;
329 #else
330 #ifdef __18CXX
331 SRAMInitHeap();
332 #endif
333 #endif
334  
335 gBufferZeroed = FALSE;
336 gNeedFATWrite = FALSE;
337 gLastFATSectorRead = 0xFFFFFFFF;
338 gLastDataSectorRead = 0xFFFFFFFF;
339  
340 MDD_InitIO();
341  
342 if(DISKmount(&gDiskData) == CE_GOOD)
343 {
344 // Initialize the current working directory to the root
345 #ifdef ALLOW_DIRS
346 cwdptr->dsk = &gDiskData;
347 cwdptr->sec = 0;
348 cwdptr->pos = 0;
349 cwdptr->seek = 0;
350 cwdptr->size = 0;
351 cwdptr->name[0] = '\\';
352 for (fIndex = 1; fIndex < 11; fIndex++)
353 {
354 cwdptr->name[fIndex] = 0x20;
355 }
356 cwdptr->entry = 0;
357 cwdptr->attributes = ATTR_DIRECTORY;
358 // "FatRootDirClusterValue" indicates the root
359 cwdptr->dirclus = FatRootDirClusterValue;
360 cwdptr->dirccls = FatRootDirClusterValue;
361 #endif
362  
363 FSerrno = 0;
364 return TRUE;
365 }
366  
367 return FALSE;
368 }
369  
370  
371 /********************************************************************************
372 Function:
373 CETYPE FILEfind (FILEOBJ foDest, FILEOBJ foCompareTo, BYTE cmd, BYTE mode)
374 Summary
375 Finds a file on the device
376 Conditions:
377 This function should not be called by the user.
378 Input:
379 foDest - FSFILE object containing information of the file found
380 foCompareTo - FSFILE object containing the name/attr of the file to be
381 found
382 cmd -
383 - LOOK_FOR_EMPTY_ENTRY: Search for empty entry.
384 - LOOK_FOR_MATCHING_ENTRY: Search for matching entry.
385 mode -
386 - 0: Match file exactly with default attributes.
387 - 1: Match file to user-specified attributes.
388 Return Values:
389 CE_GOOD - File found.
390 CE_FILE_NOT_FOUND - File not found.
391 Side Effects:
392 None.
393 Description:
394 The FILEfind function will sequentially cache directory entries within
395 the current working directory into the foDest FSFILE object. If the cmd
396 parameter is specified as LOOK_FOR_EMPTY_ENTRY the search will continue
397 until an empty directory entry is found. If the cmd parameter is specified
398 as LOOK_FOR_MATCHING_ENTRY these entries will be compared to the foCompareTo
399 object until a match is found or there are no more entries in the current
400 working directory. If the mode is specified a '0' the attributes of the FSFILE
401 entries are irrelevant. If the mode is specified as '1' the attributes of the
402 foDest entry must match the attributes specified in the foCompareTo file and
403 partial string search characters may bypass portions of the comparison.
404 Remarks:
405 None
406 ********************************************************************************/
407  
408 CETYPE FILEfind( FILEOBJ foDest, FILEOBJ foCompareTo, BYTE cmd, BYTE mode)
409 {
410 WORD attrib, compareAttrib;
411 WORD fHandle = foDest->entry; // current entry counter
412 BYTE state,index; // state of the current object
413 CETYPE statusB = CE_FILE_NOT_FOUND;
414 BYTE character,test;
415  
416 // reset the cluster
417 foDest->dirccls = foDest->dirclus;
418 compareAttrib = 0xFFFF ^ foCompareTo->attributes; // Attribute to be compared as per application layer request
419  
420 if (fHandle == 0)
421 {
422 if (Cache_File_Entry(foDest, &fHandle, TRUE) == NULL)
423 {
424 statusB = CE_BADCACHEREAD;
425 }
426 }
427 else
428 {
429 if ((fHandle & MASK_MAX_FILE_ENTRY_LIMIT_BITS) != 0) // Maximum 16 entries possible
430 {
431 if (Cache_File_Entry (foDest, &fHandle, TRUE) == NULL)
432 {
433 statusB = CE_BADCACHEREAD;
434 }
435 }
436 }
437  
438 if (statusB != CE_BADCACHEREAD)
439 {
440 // Loop until you reach the end or find the file
441 while(1)
442 {
443 if(statusB!=CE_GOOD) //First time entry always here
444 {
445 state = Fill_File_Object(foDest, &fHandle);
446 if(state == NO_MORE) // Reached the end of available files. Comparision over and file not found so quit.
447 {
448 break;
449 }
450 }
451 else // statusB == CE_GOOD then exit
452 {
453 break; // Code below intializes"statusB = CE_GOOD;" so, if no problem in the filled file, Exit the while loop.
454 }
455  
456 if(state == FOUND) // Validate the correct matching of filled file data with the required(to be found) one.
457 {
458 /* We got something */
459 // get the attributes
460 attrib = foDest->attributes;
461  
462 attrib &= ATTR_MASK;
463 switch (mode)
464 {
465 case 0:
466 // see if we are a volume id or hidden, ignore
467 if(attrib != ATTR_VOLUME)
468 {
469 statusB = CE_GOOD;
470 character = (BYTE)'m'; // random value
471  
472 // search for one. if status = TRUE we found one
473 for(index = 0; index < DIR_NAMECOMP; index++)
474 {
475 // get the source character
476 character = foDest->name[index];
477 // get the destination character
478 test = foCompareTo->name[index];
479 if(tolower(character) != tolower(test))
480 {
481 statusB = CE_FILE_NOT_FOUND; // Nope its not a match
482 break;
483 }
484 }// for loop
485 } // not dir nor vol
486 break;
487  
488 case 1:
489 // Check for attribute match
490 if (((attrib & compareAttrib) == 0) && (attrib != ATTR_LONG_NAME))
491 {
492 statusB = CE_GOOD; // Indicate the already filled file data is correct and go back
493 character = (BYTE)'m'; // random value
494 if (foCompareTo->name[0] != '*') //If "*" is passed for comparion as 1st char then don't proceed. Go back, file alreay found.
495 {
496 for (index = 0; index < DIR_NAMESIZE; index++)
497 {
498 // Get the source character
499 character = foDest->name[index];
500 // Get the destination character
501 test = foCompareTo->name[index];
502 if (test == '*')
503 break;
504 if (test != '?')
505 {
506 if(tolower(character) != tolower(test))
507 {
508 statusB = CE_FILE_NOT_FOUND; // it's not a match
509 break;
510 }
511 }
512 }
513 }
514  
515 // Before calling this "FILEfind" fn, "formatfilename" must be called. Hence, extn always starts from position "8".
516 if ((foCompareTo->name[8] != '*') && (statusB == CE_GOOD))
517 {
518 for (index = 8; index < DIR_NAMECOMP; index++)
519 {
520 // Get the source character
521 character = foDest->name[index];
522 // Get the destination character
523 test = foCompareTo->name[index];
524 if (test == '*')
525 break;
526 if (test != '?')
527 {
528 if(tolower(character) != tolower(test))
529 {
530 statusB = CE_FILE_NOT_FOUND; // it's not a match
531 break;
532 }
533 }
534 }
535 }
536  
537 } // Attribute match
538  
539 break;
540 }
541 } // not found
542 else
543 {
544 /*** looking for an empty/re-usable entry ***/
545 if ( cmd == LOOK_FOR_EMPTY_ENTRY)
546 statusB = CE_GOOD;
547 } // found or not
548  
549 // increment it no matter what happened
550 fHandle++;
551  
552 }// while
553 }
554  
555 return(statusB);
556 } // FILEFind
557  
558  
559 /**************************************************************************
560 Function:
561 CETYPE FILEopen (FILEOBJ fo, WORD *fHandle, char type)
562 Summary:
563 Loads file information from the device
564 Conditions:
565 This function should not be called by the user.
566 Input:
567 fo - File to be opened
568 fHandle - Location of file
569 type -
570 - WRITE - Create a new file or replace an existing file
571 - READ - Read data from an existing file
572 - APPEND - Append data to an existing file
573 Return Values:
574 CE_GOOD - FILEopen successful
575 CE_NOT_INIT - Device is not yet initialized
576 CE_FILE_NOT_FOUND - Could not find the file on the device
577 CE_BAD_SECTOR_READ - A bad read of a sector occured
578 Side Effects:
579 None
580 Description:
581 This function will cache a directory entry in the directory specified
582 by the dirclus parameter of hte FSFILE object 'fo.' The offset of the
583 entry in the directory is specified by fHandle. Once the directory entry
584 has been loaded, the first sector of the file can be loaded using the
585 cluster value specified in the directory entry. The type argument will
586 specify the mode the files will be opened in. This will allow this
587 function to set the correct read/write flags for the file.
588 Remarks:
589 If the mode the file is being opened in is a plus mode (e.g. READ+) the
590 flags will be modified further in the FSfopen function.
591 **************************************************************************/
592  
593 CETYPE FILEopen (FILEOBJ fo, WORD *fHandle, char type)
594 {
595 DISK *dsk; //Disk structure
596 BYTE r; //Result of search for file
597 DWORD l; //lba of first sector of first cluster
598 CETYPE error = CE_GOOD;
599  
600 dsk = (DISK *)(fo->dsk);
601 if (dsk->mount == FALSE)
602 {
603 error = CE_NOT_INIT;
604 }
605 else
606 {
607 // load the sector
608 fo->dirccls = fo->dirclus;
609 // Cache no matter what if it's the first entry
610 if (*fHandle == 0)
611 {
612 if (Cache_File_Entry(fo, fHandle, TRUE) == NULL)
613 {
614 error = CE_BADCACHEREAD;
615 }
616 }
617 else
618 {
619 // If it's not the first, only cache it if it's
620 // not divisible by the number of entries per sector
621 // If it is, Fill_File_Object will cache it
622 if ((*fHandle & 0xf) != 0)
623 {
624 if (Cache_File_Entry (fo, fHandle, TRUE) == NULL)
625 {
626 error = CE_BADCACHEREAD;
627 }
628 }
629 }
630  
631 // Fill up the File Object with the information pointed to by fHandle
632 r = Fill_File_Object(fo, fHandle);
633 if (r != FOUND)
634 error = CE_FILE_NOT_FOUND;
635 else
636 {
637 fo->seek = 0; // first byte in file
638 fo->ccls = fo->cluster; // first cluster
639 fo->sec = 0; // first sector in the cluster
640 fo->pos = 0; // first byte in sector/cluster
641  
642 if ( r == NOT_FOUND)
643 {
644 error = CE_FILE_NOT_FOUND;
645 }
646 else
647 {
648 // Determine the lba of the selected sector and load
649 l = Cluster2Sector(dsk,fo->ccls);
650 #ifdef ALLOW_WRITES
651 if (gNeedDataWrite)
652 if (flushData())
653 return CE_WRITE_ERROR;
654 #endif
655 gBufferOwner = fo;
656 if (gLastDataSectorRead != l)
657 {
658 gBufferZeroed = FALSE;
659 if ( !MDD_SectorRead( l, dsk->buffer))
660 error = CE_BAD_SECTOR_READ;
661 gLastDataSectorRead = l;
662 }
663 } // -- found
664  
665 fo->flags.FileWriteEOF = FALSE;
666 // Set flag for operation type
667 #ifdef ALLOW_WRITES
668 if (type == 'w' || type == 'a')
669 {
670 fo->flags.write = 1; //write or append
671 fo->flags.read = 0;
672 }
673 else
674 {
675 #endif
676 fo->flags.write = 0; //read
677 fo->flags.read = 1;
678 #ifdef ALLOW_WRITES
679 } // -- flags
680 #endif
681 } // -- r = Found
682 } // -- Mounted
683 return (error);
684 } // -- FILEopen
685  
686  
687 /*************************************************************************
688 Function:
689 BYTE FILEget_next_cluster(FILEOBJ fo, WORD n)
690 Summary:
691 Step through a chain of clusters
692 Conditions:
693 This function should not be called by the user.
694 Input:
695 fo - The file to get the next cluster of
696 n - Number of links in the FAT cluster chain to jump through
697 Return Values:
698 CE_GOOD - Operation successful
699 CE_BAD_SECTOR_READ - A bad read occured of a sector
700 CE_INVALID_CLUSTER - Invalid cluster value \> maxcls
701 CE_FAT_EOF - Fat attempt to read beyond EOF
702 Side Effects:
703 None
704 Description:
705 This function will load 'n' proximate clusters for a file from
706 the FAT on the device. It will stop checking for clusters if the
707 ReadFAT function returns an error, if it reaches the last cluster in
708 a file, or if the device tries to read beyond the last cluster used
709 by the device.
710 Remarks:
711 None
712 *************************************************************************/
713  
714 BYTE FILEget_next_cluster(FILEOBJ fo, DWORD n)
715 {
716 DWORD c, c2, ClusterFailValue, LastClustervalue;
717 BYTE error = CE_GOOD;
718 DISK * disk;
719  
720 disk = fo->dsk;
721  
722 /* Settings based on FAT type */
723 switch (disk->type)
724 {
725 #ifdef SUPPORT_FAT32 // If FAT32 supported.
726 case FAT32:
727 LastClustervalue = LAST_CLUSTER_FAT32;
728 ClusterFailValue = CLUSTER_FAIL_FAT32;
729 break;
730 #endif
731 case FAT12:
732 LastClustervalue = LAST_CLUSTER_FAT12;
733 ClusterFailValue = CLUSTER_FAIL_FAT16;
734 break;
735 case FAT16:
736 default:
737 LastClustervalue = LAST_CLUSTER_FAT16;
738 ClusterFailValue = CLUSTER_FAIL_FAT16;
739 break;
740 }
741  
742 // loop n times
743 do
744 {
745 // get the next cluster link from FAT
746 c2 = fo->ccls;
747 if ( (c = ReadFAT( disk, c2)) == ClusterFailValue)
748 error = CE_BAD_SECTOR_READ;
749 else
750 {
751 // check if cluster value is valid
752 if ( c >= disk->maxcls)
753 {
754 error = CE_INVALID_CLUSTER;
755 }
756  
757 // compare against max value of a cluster in FAT
758 // return if eof
759 if ( c >= LastClustervalue) // check against eof
760 {
761 error = CE_FAT_EOF;
762 }
763 }
764  
765 // update the FSFILE structure
766 fo->ccls = c;
767  
768 } while (--n > 0 && error == CE_GOOD);// loop end
769  
770 return(error);
771 } // get next cluster
772  
773  
774 /**************************************************************************
775 Function:
776 BYTE DISKmount ( DISK *dsk)
777 Summary:
778 Initialies the device and loads MBR and boot sector information
779 Conditions:
780 This function should not be called by the user.
781 Input:
782 dsk - The disk structure to be initialized.
783 Return Values:
784 CE_GOOD - Disk mounted
785 CE_INIT_ERROR - Initialization error has occured
786 CE_UNSUPPORTED_SECTOR_SIZE - Media sector size bigger than
787 MEDIA_SECTOR_SIZE as defined in FSconfig.h.
788 Side Effects:
789 None
790 Description:
791 This function will use the function pointed to by the MDD_MediaInitialize
792 function pointer to initialize the device (if any initialization is
793 required). It then attempts to load the master boot record with the
794 LoadMBR function and the boot sector with the LoadBootSector function.
795 These two functions will be used to initialize a global DISK structure
796 that will be used when accessing file information in the future.
797 Remarks:
798 None
799 **************************************************************************/
800  
801 BYTE DISKmount( DISK *dsk)
802 {
803 BYTE error = CE_GOOD;
804 MEDIA_INFORMATION *mediaInformation;
805  
806 dsk->mount = FALSE; // default invalid
807 dsk->buffer = gDataBuffer; // assign buffer
808  
809 // Initialize the device
810 mediaInformation = MDD_MediaInitialize();
811 if (mediaInformation->errorCode != MEDIA_NO_ERROR)
812 {
813 error = CE_INIT_ERROR;
814 FSerrno = CE_INIT_ERROR;
815 }
816 else
817 {
818 // If the media initialization routine determined the sector size,
819 // check it and make sure we can support it.
820 if (mediaInformation->validityFlags.bits.sectorSize)
821 {
822 dsk->sectorSize = mediaInformation->sectorSize;
823 if (mediaInformation->sectorSize > MEDIA_SECTOR_SIZE)
824 {
825 error = CE_UNSUPPORTED_SECTOR_SIZE;
826 FSerrno = CE_UNSUPPORTED_SECTOR_SIZE;
827 return error;
828 }
829 }
830  
831 // Load the Master Boot Record (partition)
832 if((error = LoadMBR(dsk)) == CE_GOOD)
833 {
834 // Now the boot sector
835 if((error = LoadBootSector(dsk)) == CE_GOOD)
836 dsk->mount = TRUE; // Mark that the DISK mounted successfully
837 }
838 } // -- Load file parameters
839  
840 return(error);
841 } // -- mount
842  
843  
844  
845 /********************************************************************
846 Function:
847 CETYPE LoadMBR ( DISK *dsk)
848 Summary:
849 Loads the MBR and extracts necessary information
850 Conditions:
851 This function should not be called by the user.
852 Input:
853 dsk - The disk containing the master boot record to be loaded
854 Return Values:
855 CE_GOOD - MBR loaded successfully
856 CE_BAD_SECTOR_READ - A bad read occured of a sector
857 CE_BAD_PARTITION - The boot record is bad
858 Side Effects:
859 None
860 Description:
861 The LoadMBR function will use the function pointed to by the
862 MDD_SectorRead function pointer to read the 0 sector from the
863 device. If a valid boot signature is obtained, this function
864 will compare fields in that cached sector to the values that
865 would be present if that sector was a boot sector. If all of
866 those values match, it will be assumed that the device does not
867 have a master boot record and the 0 sector is actually the boot
868 sector. Otherwise, data about the partition and the actual
869 location of the boot sector will be loaded from the MBR into
870 the DISK structure pointed to by 'dsk.'
871 Remarks:
872 None
873 ********************************************************************/
874  
875 BYTE LoadMBR(DISK *dsk)
876 {
877 PT_MBR Partition;
878 BYTE error = CE_GOOD;
879 BYTE type;
880 BootSec BSec;
881  
882 // Get the partition table from the MBR
883 if ( MDD_SectorRead( FO_MBR, dsk->buffer) != TRUE)
884 {
885 error = CE_BAD_SECTOR_READ;
886 FSerrno = CE_BAD_SECTOR_READ;
887 }
888 else
889 {
890 // Check if the card has no MBR
891 BSec = (BootSec) dsk->buffer;
892  
893 if((BSec->Signature0 == FAT_GOOD_SIGN_0) && (BSec->Signature1 == FAT_GOOD_SIGN_1))
894 {
895 // Technically, the OEM name is not for indication
896 // The alternative is to read the CIS from attribute
897 // memory. See the PCMCIA metaformat for more details
898 #if defined (__C30__) || defined (__PIC32MX__)
899 if (ReadByte( dsk->buffer, BSI_FSTYPE ) == 'F' && \
900 ReadByte( dsk->buffer, BSI_FSTYPE + 1 ) == 'A' && \
901 ReadByte( dsk->buffer, BSI_FSTYPE + 2 ) == 'T' && \
902 ReadByte( dsk->buffer, BSI_FSTYPE + 3 ) == '1' && \
903 ReadByte( dsk->buffer, BSI_BOOTSIG) == 0x29)
904 #else
905 if (BSec->FAT.FAT_16.BootSec_FSType[0] == 'F' && \
906 BSec->FAT.FAT_16.BootSec_FSType[1] == 'A' && \
907 BSec->FAT.FAT_16.BootSec_FSType[2] == 'T' && \
908 BSec->FAT.FAT_16.BootSec_FSType[3] == '1' && \
909 BSec->FAT.FAT_16.BootSec_BootSig == 0x29)
910 #endif
911 {
912 dsk->firsts = 0;
913 dsk->type = FAT16;
914 return CE_GOOD;
915 }
916 else
917 {
918 #if defined (__C30__) || defined (__PIC32MX__)
919 if (ReadByte( dsk->buffer, BSI_FAT32_FSTYPE ) == 'F' && \
920 ReadByte( dsk->buffer, BSI_FAT32_FSTYPE + 1 ) == 'A' && \
921 ReadByte( dsk->buffer, BSI_FAT32_FSTYPE + 2 ) == 'T' && \
922 ReadByte( dsk->buffer, BSI_FAT32_FSTYPE + 3 ) == '3' && \
923 ReadByte( dsk->buffer, BSI_FAT32_BOOTSIG) == 0x29)
924 #else
925 if (BSec->FAT.FAT_32.BootSec_FilSysType[0] == 'F' && \
926 BSec->FAT.FAT_32.BootSec_FilSysType[1] == 'A' && \
927 BSec->FAT.FAT_32.BootSec_FilSysType[2] == 'T' && \
928 BSec->FAT.FAT_32.BootSec_FilSysType[3] == '3' && \
929 BSec->FAT.FAT_32.BootSec_BootSig == 0x29)
930 #endif
931 {
932 dsk->firsts = 0;
933 dsk->type = FAT32;
934 return CE_GOOD;
935 }
936 }
937 }
938 // assign it the partition table strucutre
939 Partition = (PT_MBR)dsk->buffer;
940  
941 // Ensure its good
942 if((Partition->Signature0 != FAT_GOOD_SIGN_0) || (Partition->Signature1 != FAT_GOOD_SIGN_1))
943 {
944 FSerrno = CE_BAD_PARTITION;
945 error = CE_BAD_PARTITION;
946 }
947 else
948 {
949 /* Valid Master Boot Record Loaded */
950  
951 // Get the 32 bit offset to the first partition
952 dsk->firsts = Partition->Partition0.PTE_FrstSect;
953  
954 // check if the partition type is acceptable
955 type = Partition->Partition0.PTE_FSDesc;
956  
957 switch (type)
958 {
959 case 0x01:
960 dsk->type = FAT12;
961 break;
962  
963 case 0x04:
964 case 0x06:
965 case 0x0E:
966 dsk->type = FAT16;
967 break;
968  
969 case 0x0B:
970 case 0x0C:
971  
972 #ifdef SUPPORT_FAT32 // If FAT32 supported.
973 dsk->type = FAT32; // FAT32 is supported too
974 #else
975 FSerrno = CE_CARDFAT32;
976 error = CE_CARDFAT32;
977 #endif
978 break;
979  
980 default:
981 FSerrno = CE_UNSUPPORTED_FS;
982 error = CE_UNSUPPORTED_FS;
983 } // switch
984 }
985 }
986  
987 return(error);
988 }// -- LoadMBR
989  
990  
991 /**************************************************************************
992 Function:
993 BYTE LoadBootSector (DISK *dsk)
994 Summary:
995 Load the boot sector and extract the necessary information
996 Conditions:
997 This function should not be called by the user.
998 Input:
999 dsk - The disk containing the boot sector
1000 Return Values:
1001 CE_GOOD - Boot sector loaded
1002 CE_BAD_SECTOR_READ - A bad read occured of a sector
1003 CE_NOT_FORMATTED - The disk is of an unsupported format
1004 CE_CARDFAT32 - FAT 32 device not supported
1005 CE_UNSUPPORTED_SECTOR_SIZE - The sector size is not supported
1006 Side Effects:
1007 None
1008 Description:
1009 LoadBootSector will use the function pointed to by the MDD_SectorWrite
1010 function pointer to load the boot sector, whose location was obtained
1011 by a previous call of LoadMBR. If the boot sector is loaded successfully,
1012 partition information will be calcualted from it and copied into the DISK
1013 structure pointed to by 'dsk.'
1014 Remarks:
1015 None
1016 **************************************************************************/
1017  
1018  
1019 BYTE LoadBootSector(DISK *dsk)
1020 {
1021 DWORD RootDirSectors;
1022 DWORD TotSec,DataSec;
1023 BYTE error = CE_GOOD;
1024 BootSec BSec;
1025 WORD BytesPerSec;
1026 WORD ReservedSectorCount;
1027  
1028 // Get the Boot sector
1029 if ( MDD_SectorRead( dsk->firsts, dsk->buffer) != TRUE)
1030 {
1031 FSerrno = CE_BAD_SECTOR_READ;
1032 error = CE_BAD_SECTOR_READ;
1033 }
1034 else
1035 {
1036 BSec = (BootSec)dsk->buffer;
1037  
1038 //Verify the Boot Sector is valid
1039 if((BSec->Signature0 != FAT_GOOD_SIGN_0) || (BSec->Signature1 != FAT_GOOD_SIGN_1))
1040 {
1041 FSerrno = CE_NOT_FORMATTED;
1042 error = CE_NOT_FORMATTED;
1043 }
1044 else
1045 {
1046 #ifdef __18CXX
1047  
1048 // Load count of sectors per cluster
1049 dsk->SecPerClus = BSec->FAT.FAT_16.BootSec_SPC;
1050 // Load the sector number of the first FAT sector
1051 dsk->fat = dsk->firsts + BSec->FAT.FAT_16.BootSec_ResrvSec;
1052 // Load the count of FAT tables
1053 dsk->fatcopy = BSec->FAT.FAT_16.BootSec_FATCount;
1054 // Load the size of the FATs
1055 dsk->fatsize = BSec->FAT.FAT_16.BootSec_SPF;
1056 if(dsk->fatsize == 0)
1057 dsk->fatsize = BSec->FAT.FAT_32.BootSec_FATSz32;
1058 // Calculate the location of the root sector (for FAT12/16)
1059 dsk->root = dsk->fat + (DWORD)(dsk->fatcopy * (DWORD)dsk->fatsize);
1060 // Determine the max size of the root (will be 0 for FAT32)
1061 dsk->maxroot = BSec->FAT.FAT_16.BootSec_RootDirEnts;
1062  
1063 // Determine the total number of sectors in the partition
1064 if(BSec->FAT.FAT_16.BootSec_TotSec16 != 0)
1065 {
1066 TotSec = BSec->FAT.FAT_16.BootSec_TotSec16;
1067 }
1068 else
1069 {
1070 TotSec = BSec->FAT.FAT_16.BootSec_TotSec32;
1071 }
1072  
1073 // Calculate the number of bytes in each sector
1074 BytesPerSec = BSec->FAT.FAT_16.BootSec_BPS;
1075 if( BytesPerSec == 0 || (BytesPerSec & 1) == 1 )
1076 {
1077 FSerrno = CE_UNSUPPORTED_SECTOR_SIZE;
1078 return( CE_UNSUPPORTED_SECTOR_SIZE );
1079 }
1080  
1081 // Calculate the number of sectors in the root (will be 0 for FAT32)
1082 RootDirSectors = ((BSec->FAT.FAT_16.BootSec_RootDirEnts * 32) + (BSec->FAT.FAT_16.BootSec_BPS - 1)) / BSec->FAT.FAT_16.BootSec_BPS;
1083 // Calculate the number of data sectors on the card
1084 DataSec = TotSec - (dsk->root + RootDirSectors);
1085 // Calculate the maximum number of clusters on the card
1086 dsk->maxcls = DataSec / dsk->SecPerClus;
1087  
1088 #else // PIC24/30/33
1089  
1090 // Read the count of reserved sectors
1091 ReservedSectorCount = ReadWord( dsk->buffer, BSI_RESRVSEC );
1092 // Load the count of sectors per cluster
1093 dsk->SecPerClus = ReadByte( dsk->buffer, BSI_SPC );
1094 // Load the sector number of the first FAT sector
1095 dsk->fat = dsk->firsts + ReservedSectorCount;
1096 // Load the count of FAT tables
1097 dsk->fatcopy = ReadByte( dsk->buffer, BSI_FATCOUNT );
1098 // Load the size of the FATs
1099 dsk->fatsize = ReadWord( dsk->buffer, BSI_SPF );
1100 if(dsk->fatsize == 0)
1101 dsk->fatsize = ReadDWord( dsk->buffer, BSI_FATSZ32 );
1102 // Calculate the location of the root sector (for FAT12/16)
1103 dsk->root = dsk->fat + (DWORD)(dsk->fatcopy * (DWORD)dsk->fatsize);
1104 // Determine the max size of the root (will be 0 for FAT32)
1105 dsk->maxroot = ReadWord( dsk->buffer, BSI_ROOTDIRENTS );
1106  
1107 // Determine the total number of sectors in the partition
1108 TotSec = ReadWord( dsk->buffer, BSI_TOTSEC16 );
1109 if( TotSec == 0 )
1110 TotSec = ReadDWord( dsk->buffer, BSI_TOTSEC32 );
1111  
1112 // Calculate the number of bytes in each sector
1113 BytesPerSec = ReadWord( dsk->buffer, BSI_BPS );
1114 if( BytesPerSec == 0 || (BytesPerSec & 1) == 1 )
1115 {
1116 FSerrno = CE_UNSUPPORTED_SECTOR_SIZE;
1117 return( CE_UNSUPPORTED_SECTOR_SIZE );
1118 }
1119  
1120 // Calculate the number of sectors in the root (will be 0 for FAT32)
1121 RootDirSectors = ((dsk->maxroot * NUMBER_OF_BYTES_IN_DIR_ENTRY) + (BytesPerSec - 1)) / BytesPerSec;
1122 // Calculate the number of data sectors on the card
1123 DataSec = TotSec - (ReservedSectorCount + (dsk->fatcopy * dsk->fatsize ) + RootDirSectors);
1124 // Calculate the maximum number of clusters on the card
1125 dsk->maxcls = DataSec / dsk->SecPerClus;
1126  
1127 #endif
1128  
1129 // Determine the file system type based on the number of clusters used
1130 if(dsk->maxcls < 4085)
1131 {
1132 dsk->type = FAT12;
1133 }
1134 else
1135 {
1136 if(dsk->maxcls < 65525)
1137 {
1138 dsk->type = FAT16;
1139 }
1140 else
1141 {
1142 #ifdef SUPPORT_FAT32
1143 dsk->type = FAT32;
1144 #else
1145 error = CE_CARDFAT32;
1146 FSerrno = CE_CARDFAT32;
1147 #endif
1148 }
1149 }
1150  
1151 #ifdef SUPPORT_FAT32
1152 if (dsk->type == FAT32)
1153 {
1154 #ifdef __18CXX
1155 FatRootDirClusterValue = BSec->FAT.FAT_32.BootSec_RootClus;
1156 #else
1157 FatRootDirClusterValue = ReadDWord( dsk->buffer, BSI_ROOTCLUS );
1158 #endif
1159 dsk->data = dsk->root + RootDirSectors;
1160 }
1161 else
1162 #endif
1163 {
1164 FatRootDirClusterValue = 0;
1165 dsk->data = dsk->root + ( dsk->maxroot >> 4);
1166 }
1167  
1168 #ifdef __18CXX
1169 if(BSec->FAT.FAT_16.BootSec_BPS > MEDIA_SECTOR_SIZE)
1170 #else
1171 if(BytesPerSec > MEDIA_SECTOR_SIZE)
1172 #endif
1173 {
1174 error = CE_UNSUPPORTED_SECTOR_SIZE;
1175 FSerrno = CE_UNSUPPORTED_SECTOR_SIZE;
1176 }
1177 }
1178 }
1179 return(error);
1180 }
1181  
1182  
1183  
1184 /*************************************************************************
1185 Function:
1186 DWORD GetFullClusterNumber (DIRENTRY entry)
1187 Summary:
1188 Gets the cluster number from a directory entry
1189 Conditions:
1190 This function should not be called by the user.
1191 Input:
1192 entry - The cached directory entry to get the cluster number from
1193 Returns:
1194 The cluster value from the passed directory entry
1195 Side Effects:
1196 None.
1197 Description:
1198 This function will load both the high and low 16-bit first cluster
1199 values of a file from a directory entry and copy them into a 32-bit
1200 cluster number variable, which will be returned.
1201 Remarks:
1202 None
1203 *************************************************************************/
1204  
1205 DWORD GetFullClusterNumber(DIRENTRY entry)
1206 {
1207  
1208 DWORD TempFullClusterCalc = 0;
1209  
1210 #ifndef SUPPORT_FAT32 // If FAT32 Not supported.
1211 entry->DIR_FstClusHI = 0; // If FAT32 is not supported then Higher Word of the address is "0"
1212 #endif
1213  
1214 // Get the cluster
1215 TempFullClusterCalc = (entry->DIR_FstClusHI);
1216 TempFullClusterCalc = TempFullClusterCalc << 16;
1217 TempFullClusterCalc |= entry->DIR_FstClusLO;
1218  
1219 return TempFullClusterCalc;
1220 }
1221  
1222  
1223 #ifdef ALLOW_FORMATS
1224 #ifdef ALLOW_WRITES
1225  
1226  
1227 /*********************************************************************************
1228 Function:
1229 int FSCreateMBR (unsigned long firstSector, unsigned long numSectors)
1230 Summary:
1231 Creates a master boot record
1232 Conditions:
1233 The I/O pins for the device have been initialized by the InitIO function.
1234 Input:
1235 firstSector - The first sector of the partition on the device (cannot
1236 be 0; that's the MBR)
1237 numSectors - The number of sectors available in memory (including the
1238 MBR)
1239 Return Values:
1240  
1241 EOF - MBR could not be created
1242 Side Effects:
1243 None
1244 Description:
1245 This function can be used to create a master boot record for a device. Note
1246 that this function should not be used on a device that is already formatted
1247 with a master boot record (i.e. most SD cards, CF cards, USB keys). This
1248 function will fill the global data buffer with appropriate partition information
1249 for a FAT partition with a type determined by the number of sectors available
1250 to the partition. It will then write the MBR information to the first sector
1251 on the device. This function should be followed by a call to FSformat, which
1252 will create a boot sector, root dir, and FAT appropriate the the information
1253 contained in the new master boot record. Note that FSformat only supports
1254 FAT12 and FAT16 formatting at this time, and so cannot be used to format a
1255 device with more than 0x3FFD5F sectors.
1256 Remarks:
1257 This function can damage the device being used, and should not be called
1258 unless the user is sure about the size of the device and the first sector value.
1259 *********************************************************************************/
1260  
1261 int FSCreateMBR (unsigned long firstSector, unsigned long numSectors)
1262 {
1263 PT_MBR Partition;
1264 DWORD CyHdSc = 0x00000000;
1265 DWORD tempSector;
1266  
1267 if ((firstSector == 0) || (numSectors <= 1))
1268 return EOF;
1269  
1270 if (firstSector > (numSectors - 1))
1271 return EOF;
1272  
1273 if (gNeedDataWrite)
1274 if (flushData())
1275 return EOF;
1276  
1277 memset (gDataBuffer, 0x00, MEDIA_SECTOR_SIZE);
1278  
1279 Partition = (PT_MBR) gDataBuffer;
1280  
1281 // Set Cylinder-head-sector address of the first sector
1282 tempSector = firstSector;
1283 CyHdSc = (tempSector / (unsigned int)16065 ) << 14;
1284 tempSector %= 16065;
1285 CyHdSc |= (tempSector / 63) << 6;
1286 tempSector %= 63;
1287 CyHdSc |= tempSector + 1;
1288 gDataBuffer[447] = (BYTE)((CyHdSc >> 16) & 0xFF);
1289 gDataBuffer[448] = (BYTE)((CyHdSc >> 8) & 0xFF);
1290 gDataBuffer[449] = (BYTE)((CyHdSc) & 0xFF);
1291  
1292 // Set the count of sectors
1293 Partition->Partition0.PTE_NumSect = numSectors - firstSector;
1294  
1295 // Set the partition type
1296 // We only support creating FAT12 and FAT16 MBRs at this time
1297 if (Partition->Partition0.PTE_NumSect < 0x1039)
1298 {
1299 // FAT12
1300 Partition->Partition0.PTE_FSDesc = 0x01;
1301 }
1302 else if (Partition->Partition0.PTE_NumSect <= 0x3FFD5F)
1303 {
1304 // FAT16
1305 Partition->Partition0.PTE_FSDesc = 0x06;
1306 }
1307 else
1308 return EOF;
1309  
1310 // Set the LBA of the first sector
1311 Partition->Partition0.PTE_FrstSect = firstSector;
1312  
1313 // Set the Cylinder-head-sector address of the last sector
1314 tempSector = firstSector + numSectors - 1;
1315 CyHdSc = (tempSector / (unsigned int)16065 ) << 14;
1316 tempSector %= 16065;
1317 CyHdSc |= (tempSector / 63) << 6;
1318 tempSector %= 63;
1319 CyHdSc |= tempSector + 1;
1320 gDataBuffer[451] = (BYTE)((CyHdSc >> 16) & 0xFF);
1321 gDataBuffer[452] = (BYTE)((CyHdSc >> 8) & 0xFF);
1322 gDataBuffer[453] = (BYTE)((CyHdSc) & 0xFF);
1323  
1324 // Set the boot descriptor. This will be 0, since we won't
1325 // be booting anything from our device probably
1326 Partition->Partition0.PTE_BootDes = 0x00;
1327  
1328 // Set the signature codes
1329 Partition->Signature0 = 0x55;
1330 Partition->Signature1 = 0xAA;
1331  
1332 if (MDD_SectorWrite (0x00, gDataBuffer, TRUE) != TRUE)
1333 return EOF;
1334 else
1335 return 0;
1336  
1337 }
1338  
1339  
1340  
1341 /*******************************************************************
1342 Function:
1343 int FSformat (char mode, long int serialNumber, char * volumeID)
1344 Summary:
1345 Formats a device
1346 Conditions:
1347 The device must possess a valid master boot record.
1348 Input:
1349 mode - - 0 - Just erase the FAT and root
1350 - 1 - Create a new boot sector
1351 serialNumber - Serial number to write to the card
1352 volumeID - Name of the card
1353 Return Values:
1354  
1355 EOF - Format was unsuccessful
1356 Side Effects:
1357 The FSerrno variable will be changed.
1358 Description:
1359 The FSformat function can be used to create a new boot sector
1360 on a device, based on the information in the master boot record.
1361 This function will first initialize the I/O pins and the device,
1362 and then attempts to read the master boot record. If the MBR
1363 cannot be loaded successfully, the function will fail. Next, if
1364 the 'mode' argument is specified as '0' the existing boot sector
1365 information will be loaded. If the 'mode' argument is '1' an
1366 entirely new boot sector will be constructed using the disk
1367 values from the master boot record. Once the boot sector has
1368 been successfully loaded/created, the locations of the FAT and
1369 root will be loaded from it, and they will be completely
1370 erased. If the user has specified a volumeID parameter, a
1371 VOLUME attribute entry will be created in the root directory
1372 to name the device.
1373  
1374 FAT12, FAT16 and FAT32 formatting are supported.
1375  
1376 Based on the number of sectors, the format function automatically
1377 compute the smallest possible value for the cluster size in order to
1378 accommodate the physical size of the media. In this case, if a media
1379 with a big capacity is formatted, the format function may take a very
1380 long time to write all the FAT tables.
1381  
1382 Therefore, the FORMAT_SECTORS_PER_CLUSTER macro may be used to
1383 specify the exact cluster size (in multiples of sector size). This
1384 macro can be defined in FSconfig.h
1385  
1386 Remarks:
1387 Only devices with a sector size of 512 bytes are supported by the
1388 format function
1389 *******************************************************************/
1390  
1391 int FSformat (char mode, long int serialNumber, char * volumeID)
1392 {
1393 PT_MBR masterBootRecord;
1394 DWORD secCount, DataClusters, RootDirSectors;
1395 BootSec BSec;
1396 DISK d;
1397 DISK * disk = &d;
1398 WORD j;
1399 DWORD fatsize, test;
1400 DWORD Index;
1401 MEDIA_INFORMATION * mediaInfo;
1402 #ifdef __18CXX
1403 // This is here because of a C18 compiler feature
1404 BYTE * dataBufferPointer = gDataBuffer;
1405 #endif
1406  
1407 FSerrno = CE_GOOD;
1408  
1409 gBufferZeroed = FALSE;
1410 gNeedFATWrite = FALSE;
1411 gLastFATSectorRead = 0xFFFFFFFF;
1412 gLastDataSectorRead = 0xFFFFFFFF;
1413  
1414 disk->buffer = gDataBuffer;
1415  
1416 MDD_InitIO();
1417  
1418 mediaInfo = MDD_MediaInitialize();
1419 if (mediaInfo->errorCode != MEDIA_NO_ERROR)
1420 {
1421 FSerrno = CE_INIT_ERROR;
1422 return EOF;
1423 }
1424  
1425 if (MDD_SectorRead (0x00, gDataBuffer) == FALSE)
1426 {
1427 FSerrno = CE_BADCACHEREAD;
1428 return EOF;
1429 }
1430  
1431 // Check if the card has no MBR
1432 BSec = (BootSec) disk->buffer;
1433 if((BSec->Signature0 == FAT_GOOD_SIGN_0) && (BSec->Signature1 == FAT_GOOD_SIGN_1))
1434 {
1435 // Technically, the OEM name is not for indication
1436 // The alternative is to read the CIS from attribute
1437 // memory. See the PCMCIA metaformat for more details
1438 #if defined (__C30__) || defined (__PIC32MX__)
1439 if (ReadByte( disk->buffer, BSI_FSTYPE ) == 'F' && \
1440 ReadByte( disk->buffer, BSI_FSTYPE + 1 ) == 'A' && \
1441 ReadByte( disk->buffer, BSI_FSTYPE + 2 ) == 'T' && \
1442 ReadByte( disk->buffer, BSI_FSTYPE + 3 ) == '1' && \
1443 ReadByte( disk->buffer, BSI_BOOTSIG) == 0x29)
1444 #else
1445 if (BSec->FAT.FAT_16.BootSec_FSType[0] == 'F' && \
1446 BSec->FAT.FAT_16.BootSec_FSType[1] == 'A' && \
1447 BSec->FAT.FAT_16.BootSec_FSType[2] == 'T' && \
1448 BSec->FAT.FAT_16.BootSec_FSType[3] == '1' && \
1449 BSec->FAT.FAT_16.BootSec_BootSig == 0x29)
1450 #endif
1451 {
1452 /* Mark that we do not have a MBR;
1453 this is not actualy used - is here only to remove a compilation warning */
1454 masterBootRecord = (PT_MBR) NULL;
1455 switch (mode)
1456 {
1457 case 1:
1458 // not enough info to construct our own boot sector
1459 FSerrno = CE_INVALID_ARGUMENT;
1460 return EOF;
1461 case 0:
1462 // We have to determine the operating system, and the
1463 // locations and sizes of the root dir and FAT, and the
1464 // count of FATs
1465 disk->firsts = 0;
1466 if (LoadBootSector (disk) != CE_GOOD)
1467 {
1468 FSerrno = CE_BADCACHEREAD;
1469 return EOF;
1470 }
1471 default:
1472 break;
1473 }
1474 }
1475 else
1476 {
1477 masterBootRecord = (PT_MBR) &gDataBuffer;
1478 disk->firsts = masterBootRecord->Partition0.PTE_FrstSect;
1479 }
1480 }
1481 else
1482 {
1483 /* If the signature is not correct, this is neither a MBR, nor a VBR */
1484 FSerrno = CE_BAD_PARTITION;
1485 return EOF;
1486 }
1487  
1488 switch (mode)
1489 {
1490 // True: Rewrite the whole boot sector
1491 case 1:
1492 secCount = masterBootRecord->Partition0.PTE_NumSect;
1493  
1494 if (secCount < 0x1039)
1495 {
1496 disk->type = FAT12;
1497 // Format to FAT12 only if there are too few sectors to format
1498 // as FAT16
1499 masterBootRecord->Partition0.PTE_FSDesc = 0x01;
1500 if (MDD_SectorWrite (0x00, gDataBuffer, TRUE) == FALSE)
1501 {
1502 FSerrno = CE_WRITE_ERROR;
1503 return EOF;
1504 }
1505  
1506 if (secCount >= 0x1028)
1507 {
1508 // More than 0x18 sectors for FATs, 0x20 for root dir,
1509 // 0x8 reserved, and 0xFED for data
1510 // So double the number of sectors in a cluster to reduce
1511 // the number of data clusters used
1512 disk->SecPerClus = 2;
1513 }
1514 else
1515 {
1516 // One sector per cluster
1517 disk->SecPerClus = 1;
1518 }
1519  
1520 // Prepare a boot sector
1521 memset (gDataBuffer, 0x00, MEDIA_SECTOR_SIZE);
1522  
1523 // Last digit of file system name (FAT12 )
1524 gDataBuffer[58] = '2';
1525  
1526 // Calculate the size of the FAT
1527 fatsize = (secCount - 0x21 + (2*disk->SecPerClus));
1528 test = (341 * disk->SecPerClus) + 2;
1529 fatsize = (fatsize + (test-1)) / test;
1530  
1531 disk->fatcopy = 0x02;
1532 disk->maxroot = 0x200;
1533  
1534 disk->fatsize = fatsize;
1535  
1536 }
1537 else if (secCount <= 0x3FFD5F)
1538 {
1539 disk->type = FAT16;
1540 // Format to FAT16
1541 masterBootRecord->Partition0.PTE_FSDesc = 0x06;
1542 if (MDD_SectorWrite (0x00, gDataBuffer, TRUE) == FALSE)
1543 {
1544 FSerrno = CE_WRITE_ERROR;
1545 return EOF;
1546 }
1547  
1548 DataClusters = secCount - 0x218;
1549 // Figure out how many sectors per cluster we need
1550 disk->SecPerClus = 1;
1551 while (DataClusters > 0xFFED)
1552 {
1553 disk->SecPerClus *= 2;
1554 DataClusters /= 2;
1555 }
1556 // This shouldnt happen
1557 if (disk->SecPerClus > 128)
1558 {
1559 FSerrno = CE_BAD_PARTITION;
1560 return EOF;
1561 }
1562  
1563 // Prepare a boot sector
1564 memset (gDataBuffer, 0x00, MEDIA_SECTOR_SIZE);
1565  
1566 // Last digit of file system name (FAT16 )
1567 gDataBuffer[58] = '6';
1568  
1569 // Calculate the size of the FAT
1570 fatsize = (secCount - 0x21 + (2*disk->SecPerClus));
1571 test = (256 * disk->SecPerClus) + 2;
1572 fatsize = (fatsize + (test-1)) / test;
1573  
1574 disk->fatcopy = 0x02;
1575 disk->maxroot = 0x200;
1576  
1577 disk->fatsize = fatsize;
1578 }
1579 else
1580 {
1581 disk->type = FAT32;
1582 // Format to FAT32
1583 masterBootRecord->Partition0.PTE_FSDesc = 0x0B;
1584 if (MDD_SectorWrite (0x00, gDataBuffer, TRUE) == FALSE)
1585 {
1586 FSerrno = CE_WRITE_ERROR;
1587 return EOF;
1588 }
1589  
1590 #ifdef FORMAT_SECTORS_PER_CLUSTER
1591 disk->SecPerClus = FORMAT_SECTORS_PER_CLUSTER;
1592 DataClusters = secCount / disk->SecPerClus;
1593  
1594 /* FAT32: 65526 < Number of clusters < 4177918 */
1595 if ((DataClusters <= 65526) || (DataClusters >= 4177918))
1596 {
1597 FSerrno = CE_BAD_PARTITION;
1598 return EOF;
1599 }
1600 #else
1601 /* FAT32: 65526 < Number of clusters < 4177918 */
1602 DataClusters = secCount;
1603 // Figure out how many sectors per cluster we need
1604 disk->SecPerClus = 1;
1605 while (DataClusters > 0x3FBFFE)
1606 {
1607 disk->SecPerClus *= 2;
1608 DataClusters /= 2;
1609 }
1610 #endif
1611 // Check the cluster size: FAT32 supports 512, 1024, 2048, 4096, 8192, 16K, 32K, 64K
1612 if (disk->SecPerClus > 128)
1613 {
1614 FSerrno = CE_BAD_PARTITION;
1615 return EOF;
1616 }
1617  
1618 // Prepare a boot sector
1619 memset (gDataBuffer, 0x00, MEDIA_SECTOR_SIZE);
1620  
1621 // Calculate the size of the FAT
1622 fatsize = (secCount - 0x20);
1623 test = (128 * disk->SecPerClus) + 1;
1624 fatsize = (fatsize + (test-1)) / test;
1625  
1626 disk->fatcopy = 0x02;
1627 disk->maxroot = 0x200;
1628  
1629 disk->fatsize = fatsize;
1630 }
1631  
1632 // Non-file system specific values
1633 gDataBuffer[0] = 0xEB; //Jump instruction
1634 gDataBuffer[1] = 0x3C;
1635 gDataBuffer[2] = 0x90;
1636 gDataBuffer[3] = 'M'; //OEM Name "MCHP FAT"
1637 gDataBuffer[4] = 'C';
1638 gDataBuffer[5] = 'H';
1639 gDataBuffer[6] = 'P';
1640 gDataBuffer[7] = ' ';
1641 gDataBuffer[8] = 'F';
1642 gDataBuffer[9] = 'A';
1643 gDataBuffer[10] = 'T';
1644  
1645 gDataBuffer[11] = 0x00; //Sector size
1646 gDataBuffer[12] = 0x02;
1647  
1648 gDataBuffer[13] = disk->SecPerClus; //Sectors per cluster
1649  
1650 if (disk->type == FAT12 || disk->type == FAT16)
1651 {
1652 gDataBuffer[14] = 0x08; //Reserved sector count
1653 gDataBuffer[15] = 0x00;
1654 disk->fat = 0x08 + disk->firsts;
1655  
1656 gDataBuffer[16] = 0x02; //number of FATs
1657  
1658 gDataBuffer[17] = 0x00; //Max number of root directory entries - 512 files allowed
1659 gDataBuffer[18] = 0x02;
1660  
1661 gDataBuffer[19] = 0x00; //total sectors
1662 gDataBuffer[20] = 0x00;
1663  
1664 gDataBuffer[21] = 0xF8; //Media Descriptor
1665  
1666 gDataBuffer[22] = fatsize & 0xFF; //Sectors per FAT
1667 gDataBuffer[23] = (fatsize >> 8) & 0xFF;
1668  
1669 gDataBuffer[24] = 0x3F; //Sectors per track
1670 gDataBuffer[25] = 0x00;
1671  
1672 gDataBuffer[26] = 0xFF; //Number of heads
1673 gDataBuffer[27] = 0x00;
1674  
1675 // Hidden sectors = sectors between the MBR and the boot sector
1676 gDataBuffer[28] = (BYTE)(disk->firsts & 0xFF);
1677 gDataBuffer[29] = (BYTE)((disk->firsts / 0x100) & 0xFF);
1678 gDataBuffer[30] = (BYTE)((disk->firsts / 0x10000) & 0xFF);
1679 gDataBuffer[31] = (BYTE)((disk->firsts / 0x1000000) & 0xFF);
1680  
1681 // Total Sectors = same as sectors in the partition from MBR
1682 gDataBuffer[32] = (BYTE)(secCount & 0xFF);
1683 gDataBuffer[33] = (BYTE)((secCount / 0x100) & 0xFF);
1684 gDataBuffer[34] = (BYTE)((secCount / 0x10000) & 0xFF);
1685 gDataBuffer[35] = (BYTE)((secCount / 0x1000000) & 0xFF);
1686  
1687 gDataBuffer[36] = 0x00; // Physical drive number
1688  
1689 gDataBuffer[37] = 0x00; // Reserved (current head)
1690  
1691 gDataBuffer[38] = 0x29; // Signature code
1692  
1693 gDataBuffer[39] = (BYTE)(serialNumber & 0xFF);
1694 gDataBuffer[40] = (BYTE)((serialNumber / 0x100) & 0xFF);
1695 gDataBuffer[41] = (BYTE)((serialNumber / 0x10000) & 0xFF);
1696 gDataBuffer[42] = (BYTE)((serialNumber / 0x1000000) & 0xFF);
1697  
1698 // Volume ID
1699 if (volumeID != NULL)
1700 {
1701 for (Index = 0; (*(volumeID + Index) != 0) && (Index < 11); Index++)
1702 {
1703 gDataBuffer[Index + 43] = *(volumeID + Index);
1704 }
1705 while (Index < 11)
1706 {
1707 gDataBuffer[43 + Index++] = 0x20;
1708 }
1709 }
1710 else
1711 {
1712 for (Index = 0; Index < 11; Index++)
1713 {
1714 gDataBuffer[Index+43] = 0;
1715 }
1716 }
1717  
1718 gDataBuffer[54] = 'F';
1719 gDataBuffer[55] = 'A';
1720 gDataBuffer[56] = 'T';
1721 gDataBuffer[57] = '1';
1722 gDataBuffer[59] = ' ';
1723 gDataBuffer[60] = ' ';
1724 gDataBuffer[61] = ' ';
1725  
1726 }
1727 else //FAT32
1728 {
1729 gDataBuffer[14] = 0x20; //Reserved sector count
1730 gDataBuffer[15] = 0x00;
1731 disk->fat = 0x20 + disk->firsts;
1732  
1733 gDataBuffer[16] = 0x02; //number of FATs
1734  
1735 gDataBuffer[17] = 0x00; //Max number of root directory entries - 512 files allowed
1736 gDataBuffer[18] = 0x00;
1737  
1738 gDataBuffer[19] = 0x00; //total sectors
1739 gDataBuffer[20] = 0x00;
1740  
1741 gDataBuffer[21] = 0xF8; //Media Descriptor
1742  
1743 gDataBuffer[22] = 0x00; //Sectors per FAT
1744 gDataBuffer[23] = 0x00;
1745  
1746 gDataBuffer[24] = 0x3F; //Sectors per track
1747 gDataBuffer[25] = 0x00;
1748  
1749 gDataBuffer[26] = 0xFF; //Number of heads
1750 gDataBuffer[27] = 0x00;
1751  
1752 // Hidden sectors = sectors between the MBR and the boot sector
1753 gDataBuffer[28] = (BYTE)(disk->firsts & 0xFF);
1754 gDataBuffer[29] = (BYTE)((disk->firsts / 0x100) & 0xFF);
1755 gDataBuffer[30] = (BYTE)((disk->firsts / 0x10000) & 0xFF);
1756 gDataBuffer[31] = (BYTE)((disk->firsts / 0x1000000) & 0xFF);
1757  
1758 // Total Sectors = same as sectors in the partition from MBR
1759 gDataBuffer[32] = (BYTE)(secCount & 0xFF);
1760 gDataBuffer[33] = (BYTE)((secCount / 0x100) & 0xFF);
1761 gDataBuffer[34] = (BYTE)((secCount / 0x10000) & 0xFF);
1762 gDataBuffer[35] = (BYTE)((secCount / 0x1000000) & 0xFF);
1763  
1764 gDataBuffer[36] = fatsize & 0xFF; //Sectors per FAT
1765 gDataBuffer[37] = (fatsize >> 8) & 0xFF;
1766 gDataBuffer[38] = (fatsize >> 16) & 0xFF;
1767 gDataBuffer[39] = (fatsize >> 24) & 0xFF;
1768  
1769 gDataBuffer[40] = 0x00; //Active FAT
1770 gDataBuffer[41] = 0x00;
1771  
1772 gDataBuffer[42] = 0x00; //File System version
1773 gDataBuffer[43] = 0x00;
1774  
1775 gDataBuffer[44] = 0x02; //First cluster of the root directory
1776 gDataBuffer[45] = 0x00;
1777 gDataBuffer[46] = 0x00;
1778 gDataBuffer[47] = 0x00;
1779  
1780 gDataBuffer[48] = 0x01; //FSInfo
1781 gDataBuffer[49] = 0x00;
1782  
1783 gDataBuffer[50] = 0x00; //Backup Boot Sector
1784 gDataBuffer[51] = 0x00;
1785  
1786 gDataBuffer[52] = 0x00; //Reserved for future expansion
1787 gDataBuffer[53] = 0x00;
1788 gDataBuffer[54] = 0x00;
1789 gDataBuffer[55] = 0x00;
1790 gDataBuffer[56] = 0x00;
1791 gDataBuffer[57] = 0x00;
1792 gDataBuffer[58] = 0x00;
1793 gDataBuffer[59] = 0x00;
1794 gDataBuffer[60] = 0x00;
1795 gDataBuffer[61] = 0x00;
1796 gDataBuffer[62] = 0x00;
1797 gDataBuffer[63] = 0x00;
1798  
1799 gDataBuffer[64] = 0x00; // Physical drive number
1800  
1801 gDataBuffer[65] = 0x00; // Reserved (current head)
1802  
1803 gDataBuffer[66] = 0x29; // Signature code
1804  
1805 gDataBuffer[67] = (BYTE)(serialNumber & 0xFF);
1806 gDataBuffer[68] = (BYTE)((serialNumber / 0x100) & 0xFF);
1807 gDataBuffer[69] = (BYTE)((serialNumber / 0x10000) & 0xFF);
1808 gDataBuffer[70] = (BYTE)((serialNumber / 0x1000000) & 0xFF);
1809  
1810 // Volume ID
1811 if (volumeID != NULL)
1812 {
1813 for (Index = 0; (*(volumeID + Index) != 0) && (Index < 11); Index++)
1814 {
1815 gDataBuffer[Index + 71] = *(volumeID + Index);
1816 }
1817 while (Index < 11)
1818 {
1819 gDataBuffer[71 + Index++] = 0x20;
1820 }
1821 }
1822 else
1823 {
1824 for (Index = 0; Index < 11; Index++)
1825 {
1826 gDataBuffer[Index+71] = 0;
1827 }
1828 }
1829  
1830 gDataBuffer[82] = 'F';
1831 gDataBuffer[83] = 'A';
1832 gDataBuffer[84] = 'T';
1833 gDataBuffer[85] = '3';
1834 gDataBuffer[86] = '2';
1835 gDataBuffer[87] = ' ';
1836 gDataBuffer[88] = ' ';
1837 gDataBuffer[89] = ' ';
1838  
1839  
1840 }
1841  
1842 #ifdef __18CXX
1843 // C18 can't reference a value greater than 256
1844 // using an array name pointer
1845 *(dataBufferPointer + 510) = 0x55;
1846 *(dataBufferPointer + 511) = 0xAA;
1847 #else
1848 gDataBuffer[510] = 0x55;
1849 gDataBuffer[511] = 0xAA;
1850 #endif
1851  
1852 disk->root = disk->fat + (disk->fatcopy * disk->fatsize);
1853  
1854 if (MDD_SectorWrite (disk->firsts, gDataBuffer, FALSE) == FALSE)
1855 {
1856 FSerrno = CE_WRITE_ERROR;
1857 return EOF;
1858 }
1859  
1860 break;
1861 case 0:
1862 if (LoadBootSector (disk) != CE_GOOD)
1863 {
1864 FSerrno = CE_BADCACHEREAD;
1865 return EOF;
1866 }
1867 break;
1868 default:
1869 FSerrno = CE_INVALID_ARGUMENT;
1870 return EOF;
1871 }
1872  
1873 // Erase the FAT
1874 memset (gDataBuffer, 0x00, MEDIA_SECTOR_SIZE);
1875  
1876 if (disk->type == FAT32)
1877 {
1878 gDataBuffer[0] = 0xF8; //BPB_Media byte value in its low 8 bits, and all other bits are set to 1
1879 gDataBuffer[1] = 0xFF;
1880 gDataBuffer[2] = 0xFF;
1881 gDataBuffer[3] = 0xFF;
1882  
1883 gDataBuffer[4] = 0x00; //Disk is clean and no read/write errors were encountered
1884 gDataBuffer[5] = 0x00;
1885 gDataBuffer[6] = 0x00;
1886 gDataBuffer[7] = 0x0C;
1887  
1888 gDataBuffer[8] = 0xFF; //Root Directory EOF
1889 gDataBuffer[9] = 0xFF;
1890 gDataBuffer[10] = 0xFF;
1891 gDataBuffer[11] = 0xFF;
1892  
1893 for (j = disk->fatcopy - 1; j != 0xFFFF; j--)
1894 {
1895 if (MDD_SectorWrite (disk->fat + (j * disk->fatsize), gDataBuffer, FALSE) == FALSE)
1896 return EOF;
1897 }
1898  
1899 memset (gDataBuffer, 0x00, 12);
1900  
1901 for (Index = disk->fat + 1; Index < (disk->fat + disk->fatsize); Index++)
1902 {
1903 for (j = disk->fatcopy - 1; j != 0xFFFF; j--)
1904 {
1905 if (MDD_SectorWrite (Index + (j * disk->fatsize), gDataBuffer, FALSE) == FALSE)
1906 return EOF;
1907 }
1908 }
1909  
1910 // Erase the root directory
1911 for (Index = 1; Index < disk->SecPerClus; Index++)
1912 {
1913 if (MDD_SectorWrite (disk->root + Index, gDataBuffer, FALSE) == FALSE)
1914 return EOF;
1915 }
1916  
1917 if (volumeID != NULL)
1918 {
1919 // Create a drive name entry in the root dir
1920 Index = 0;
1921 while ((*(volumeID + Index) != 0) && (Index < 11))
1922 {
1923 gDataBuffer[Index] = *(volumeID + Index);
1924 Index++;
1925 }
1926 while (Index < 11)
1927 {
1928 gDataBuffer[Index++] = ' ';
1929 }
1930 gDataBuffer[11] = 0x08;
1931 gDataBuffer[17] = 0x11;
1932 gDataBuffer[19] = 0x11;
1933 gDataBuffer[23] = 0x11;
1934  
1935 if (MDD_SectorWrite (disk->root, gDataBuffer, FALSE) == FALSE)
1936 return EOF;
1937 }
1938 else
1939 {
1940 if (MDD_SectorWrite (disk->root, gDataBuffer, FALSE) == FALSE)
1941 return EOF;
1942 }
1943  
1944 return 0;
1945 }
1946 else
1947 {
1948 gDataBuffer[0] = 0xF8;
1949 gDataBuffer[1] = 0xFF;
1950 gDataBuffer[2] = 0xFF;
1951 if (disk->type == FAT16)
1952 gDataBuffer[3] = 0xFF;
1953  
1954 for (j = disk->fatcopy - 1; j != 0xFFFF; j--)
1955 {
1956 if (MDD_SectorWrite (disk->fat + (j * disk->fatsize), gDataBuffer, FALSE) == FALSE)
1957 return EOF;
1958 }
1959  
1960 memset (gDataBuffer, 0x00, 4);
1961  
1962 for (Index = disk->fat + 1; Index < (disk->fat + disk->fatsize); Index++)
1963 {
1964 for (j = disk->fatcopy - 1; j != 0xFFFF; j--)
1965 {
1966 if (MDD_SectorWrite (Index + (j * disk->fatsize), gDataBuffer, FALSE) == FALSE)
1967 return EOF;
1968 }
1969 }
1970  
1971 // Erase the root directory
1972 RootDirSectors = ((disk->maxroot * 32) + (disk->sectorSize - 1)) / disk->sectorSize;
1973  
1974 for (Index = 1; Index < RootDirSectors; Index++)
1975 {
1976 if (MDD_SectorWrite (disk->root + Index, gDataBuffer, FALSE) == FALSE)
1977 return EOF;
1978 }
1979  
1980 if (volumeID != NULL)
1981 {
1982 // Create a drive name entry in the root dir
1983 Index = 0;
1984 while ((*(volumeID + Index) != 0) && (Index < 11))
1985 {
1986 gDataBuffer[Index] = *(volumeID + Index);
1987 Index++;
1988 }
1989 while (Index < 11)
1990 {
1991 gDataBuffer[Index++] = ' ';
1992 }
1993 gDataBuffer[11] = 0x08;
1994 gDataBuffer[17] = 0x11;
1995 gDataBuffer[19] = 0x11;
1996 gDataBuffer[23] = 0x11;
1997  
1998 if (MDD_SectorWrite (disk->root, gDataBuffer, FALSE) == FALSE)
1999 return EOF;
2000 }
2001 else
2002 {
2003 if (MDD_SectorWrite (disk->root, gDataBuffer, FALSE) == FALSE)
2004 return EOF;
2005 }
2006  
2007 return 0;
2008 }
2009 }
2010 #endif
2011 #endif
2012  
2013  
2014 /*******************************************************
2015 Function:
2016 BYTE Write_File_Entry( FILEOBJ fo, WORD * curEntry)
2017 Summary:
2018 Write dir entry info into a specified entry
2019 Conditions:
2020 This function should not be called by the user.
2021 Input:
2022 fo - \File structure
2023 curEntry - Write destination
2024 Return Values:
2025 TRUE - Operation successful
2026 FALSE - Operation failed
2027 Side Effects:
2028 None
2029 Description:
2030 This function will calculate the sector of the
2031 directory (whose base sector is pointed to by the
2032 dirccls value in the FSFILE object 'fo') that contains
2033 a directory entry whose offset is indicated by the
2034 curEntry parameter. It will then write the data
2035 in the global data buffer (which should already
2036 contain the entries for that sector) to the device.
2037 Remarks:
2038 None
2039 *******************************************************/
2040  
2041 #ifdef ALLOW_WRITES
2042 BYTE Write_File_Entry( FILEOBJ fo, WORD * curEntry)
2043 {
2044 DISK *dsk;
2045 BYTE status;
2046 BYTE offset2;
2047 DWORD sector;
2048 DWORD ccls;
2049  
2050 dsk = fo->dsk;
2051  
2052 // get the cluster of this entry
2053 ccls = fo->dirccls;
2054  
2055 // figure out the offset from the base sector
2056 offset2 = (*curEntry / (dsk->sectorSize/32));
2057  
2058 /* Settings based on FAT type */
2059 switch (dsk->type)
2060 {
2061 #ifdef SUPPORT_FAT32 // If FAT32 supported.
2062 case FAT32:
2063 // Root is always cluster-based in FAT32
2064 offset2 = offset2 % (dsk->SecPerClus);
2065 break;
2066 #endif
2067 case FAT12:
2068 case FAT16:
2069 if(ccls != FatRootDirClusterValue)
2070 offset2 = offset2 % (dsk->SecPerClus);
2071 break;
2072 }
2073  
2074 sector = Cluster2Sector(dsk,ccls);
2075  
2076 // Now write it
2077 // "Offset" ensures writing of data belonging to a file entry only. Hence it doesn't change other file entries.
2078 if ( !MDD_SectorWrite( sector + offset2, dsk->buffer, FALSE))
2079 status = FALSE;
2080 else
2081 status = TRUE;
2082  
2083 return(status);
2084 } // Write_File_Entry
2085 #endif
2086  
2087  
2088 /**********************************************************
2089 Function:
2090 BYTE FAT_erase_cluster_chain (WORD cluster, DISK * dsk)
2091 Summary:
2092 Erase a chain of clusters
2093 Conditions:
2094 This function should not be called by the user.
2095 Input:
2096 cluster - The cluster number
2097 dsk - The disk structure
2098 Return Values:
2099 TRUE - Operation successful
2100 FALSE - Operation failed
2101 Side Effects:
2102 None
2103 Description:
2104 This function will parse through a cluster chain
2105 starting with the cluster pointed to by 'cluster' and
2106 mark all of the FAT entries as empty until the end of
2107 the chain has been reached or an error occurs.
2108 Remarks:
2109 None
2110 **********************************************************/
2111  
2112 #ifdef ALLOW_WRITES
2113 BYTE FAT_erase_cluster_chain (DWORD cluster, DISK * dsk)
2114 {
2115 DWORD c,c2,ClusterFailValue;
2116 enum _status {Good, Fail, Exit}status;
2117  
2118 status = Good;
2119  
2120 /* Settings based on FAT type */
2121 switch (dsk->type)
2122 {
2123  
2124 #ifdef SUPPORT_FAT32 // If FAT32 supported.
2125 case FAT32:
2126 ClusterFailValue = CLUSTER_FAIL_FAT32;
2127 c2 = LAST_CLUSTER_FAT32;
2128 break;
2129 #endif
2130 case FAT12:
2131 ClusterFailValue = CLUSTER_FAIL_FAT16; // FAT16 value itself
2132 c2 = LAST_CLUSTER_FAT12;
2133 break;
2134 case FAT16:
2135 default:
2136 ClusterFailValue = CLUSTER_FAIL_FAT16;
2137 c2 = LAST_CLUSTER_FAT16;
2138 break;
2139 }
2140  
2141 // Make sure there is actually a cluster assigned
2142 if(cluster == 0 || cluster == 1) // Cluster assigned can't be "0" and "1"
2143 {
2144 status = Exit;
2145 }
2146 else
2147 {
2148 while(status == Good)
2149 {
2150 // Get the FAT entry
2151 if((c = ReadFAT( dsk, cluster)) == ClusterFailValue)
2152 status = Fail;
2153 else
2154 {
2155 if(c == 0 || c == 1) // Cluster assigned can't be "0" and "1"
2156 {
2157 status = Exit;
2158 }
2159 else
2160 {
2161 // compare against max value of a cluster in FATxx
2162 // look for the last cluster in the chain
2163 if ( c >= c2)
2164 status = Exit;
2165  
2166 // Now erase this FAT entry
2167 if(WriteFAT(dsk, cluster, CLUSTER_EMPTY, FALSE) == ClusterFailValue)
2168 status = Fail;
2169  
2170 // now update what the current cluster is
2171 cluster = c;
2172 }
2173 }
2174 }// while status
2175 }// cluster == 0
2176  
2177 WriteFAT (dsk, 0, 0, TRUE);
2178  
2179 if(status == Exit)
2180 return(TRUE);
2181 else
2182 return(FALSE);
2183 } // Erase cluster
2184 #endif
2185  
2186 /**************************************************************************
2187 Function:
2188 DIRENTRY Cache_File_Entry( FILEOBJ fo, WORD * curEntry, BYTE ForceRead)
2189 Summary:
2190 Load a file entry
2191 Conditions:
2192 This function should not be called by the user.
2193 Input:
2194 fo - File information
2195 curEntry - Offset of the directory entry to load.
2196 ForceRead - Forces loading of a new sector of the directory.
2197 Return:
2198 DIRENTRY - Pointer to the directory entry that was loaded.
2199 Side Effects:
2200 Any unwritten data in the data buffer will be written to the device.
2201 Description:
2202 Load the sector containing the file entry pointed to by 'curEntry'
2203 from the directory pointed to by the variables in 'fo.'
2204 Remarks:
2205 Any modification of this function is extremely likely to
2206 break something.
2207 **************************************************************************/
2208  
2209 DIRENTRY Cache_File_Entry( FILEOBJ fo, WORD * curEntry, BYTE ForceRead)
2210 {
2211 DIRENTRY dir;
2212 DISK *dsk;
2213 DWORD sector;
2214 DWORD cluster, LastClusterLimit;
2215 DWORD ccls;
2216 BYTE offset2;
2217 BYTE numofclus;
2218  
2219 dsk = fo->dsk;
2220  
2221 // get the base sector of this directory
2222 cluster = fo->dirclus;
2223 ccls = fo->dirccls;
2224  
2225 // figure out the offset from the base sector
2226 offset2 = (*curEntry / (dsk->sectorSize/32));
2227  
2228 offset2 = offset2; // emulator issue
2229  
2230 /* Settings based on FAT type */
2231 switch (dsk->type)
2232 {
2233 #ifdef SUPPORT_FAT32 // If FAT32 supported.
2234 case FAT32:
2235 // the ROOT is always cluster based in FAT32
2236 /* In FAT32: There is no ROOT region. Root etries are made in DATA region only.
2237 Every cluster of DATA which is accupied by ROOT is tracked by FAT table/entry so the ROOT can grow
2238 to an amount which is restricted only by available free DATA region. */
2239 offset2 = offset2 % (dsk->SecPerClus); // figure out the offset
2240 LastClusterLimit = LAST_CLUSTER_FAT32;
2241 break;
2242 #endif
2243 case FAT12:
2244 case FAT16:
2245 default:
2246 // if its the root its not cluster based
2247 if(cluster != 0)
2248 offset2 = offset2 % (dsk->SecPerClus); // figure out the offset
2249 LastClusterLimit = LAST_CLUSTER_FAT16;
2250 break;
2251 }
2252  
2253 // check if a new sector of the root must be loaded
2254 if (ForceRead || (*curEntry & MASK_MAX_FILE_ENTRY_LIMIT_BITS) == 0) // only 16 entries per sector
2255 {
2256 // see if we have to load a new cluster
2257 if((offset2 == 0 && (*curEntry) >= DIRENTRIES_PER_SECTOR) || ForceRead)
2258 {
2259 if(cluster == 0)
2260 {
2261 ccls = 0;
2262 }
2263 else
2264 {
2265 // If ForceRead, read the number of sectors from 0
2266 if(ForceRead)
2267 numofclus = ((WORD)(*curEntry) / (WORD)(((WORD)DIRENTRIES_PER_SECTOR) * (WORD)dsk->SecPerClus));
2268 // Otherwise just read the next sector
2269 else
2270 numofclus = 1;
2271  
2272 // move to the correct cluster
2273 while(numofclus)
2274 {
2275 ccls = ReadFAT(dsk, ccls);
2276  
2277 if(ccls >= LastClusterLimit)
2278 break;
2279 else
2280 numofclus--;
2281 }
2282 }
2283 }
2284  
2285 // see if that we have a valid cluster number
2286 if(ccls < LastClusterLimit)
2287 {
2288 fo->dirccls = ccls; // write it back
2289  
2290 sector = Cluster2Sector(dsk,ccls);
2291  
2292 /* see if we are root and about to go pass our boundaries
2293 FAT32 stores the root directory in the Data Region along with files and other directories,
2294 allowing it to grow without such a restraint */
2295 if((ccls == FatRootDirClusterValue) && ((sector + offset2) >= dsk->data) && (FAT32 != dsk->type))
2296 {
2297 dir = ((DIRENTRY)NULL); // reached the end of the root
2298 }
2299 else
2300 {
2301 #ifdef ALLOW_WRITES
2302 if (gNeedDataWrite)
2303 if (flushData())
2304 return NULL;
2305 #endif
2306 gBufferOwner = NULL;
2307 gBufferZeroed = FALSE;
2308  
2309 if ( MDD_SectorRead( sector + offset2, dsk->buffer) != TRUE) // if FALSE: sector could not be read.
2310 {
2311 dir = ((DIRENTRY)NULL);
2312 }
2313 else // Sector has been read properly, Copy the root entry info of the file searched.
2314 {
2315 if(ForceRead) // Buffer holds all 16 root entry info. Point to the one required.
2316 dir = (DIRENTRY)((DIRENTRY)dsk->buffer) + ((*curEntry)%DIRENTRIES_PER_SECTOR);
2317 else
2318 dir = (DIRENTRY)dsk->buffer;
2319 }
2320 gLastDataSectorRead = 0xFFFFFFFF;
2321 }
2322 }
2323 else
2324 {
2325 nextClusterIsLast = TRUE;
2326 dir = ((DIRENTRY)NULL);
2327 }
2328 }
2329 else
2330 dir = (DIRENTRY)((DIRENTRY)dsk->buffer) + ((*curEntry)%DIRENTRIES_PER_SECTOR);
2331  
2332 return(dir);
2333 } // Cache_File_Entry
2334  
2335  
2336 /*************************************************************************
2337 Function:
2338 CETYPE CreateFileEntry(FILEOBJ fo, WORD *fHandle)
2339 Summary:
2340 Create a new file entry
2341 Conditions:
2342 Should not be called by the user.
2343 Input:
2344 fo - Pointer to file structure
2345 fHandle - Location to create file
2346 Return Values:
2347 CE_GOOD - File Creation successful
2348 CE_DIR_FULL - All root directory entries are taken
2349 CE_WRITE_ERROR - The head cluster of the file could not be created.
2350 Side Effects:
2351 Modifies the FSerrno variable.
2352 Description:
2353 With the data passed within fo, create a new file entry in the current
2354 directory. This function will first search for empty file entries.
2355 Once an empty entry is found, the entry will be populated with data
2356 for a file or directory entry. Finally, the first cluster of the
2357 new file will be located and allocated, and its value will be
2358 written into the file entry.
2359 Remarks:
2360 None
2361 *************************************************************************/
2362  
2363 #ifdef ALLOW_WRITES
2364 CETYPE CreateFileEntry(FILEOBJ fo, WORD *fHandle, BYTE mode)
2365 {
2366 BYTE index;
2367 CETYPE error = CE_GOOD;
2368 char name[11];
2369  
2370 FSerrno = CE_GOOD;
2371  
2372 for (index = 0; index < FILE_NAME_SIZE; index ++)
2373 {
2374 name[index] = fo->name[index];
2375 }
2376  
2377 *fHandle = 0;
2378  
2379 // figure out where to put this file in the directory stucture
2380 if(FindEmptyEntries(fo, fHandle))
2381 {
2382 // found the entry, now populate it
2383 if((error = PopulateEntries(fo, name ,fHandle, mode)) == CE_GOOD)
2384 {
2385 // if everything is ok, create a first cluster
2386 error = CreateFirstCluster(fo);
2387 }
2388 }
2389 else
2390 {
2391 error = CE_DIR_FULL;
2392 }
2393  
2394 FSerrno = error;
2395  
2396 return(error);
2397 }
2398 #endif
2399  
2400 /******************************************************
2401 Function:
2402 CETYPE CreateFirstCluster(FILEOBJ fo)
2403 Summary:
2404 Create the first cluster for a file
2405 Conditions:
2406 This function should not be called by the user.
2407 Input:
2408 fo - The file that contains the first cluster
2409 Return Values:
2410 CE_GOOD - First cluster created successfully
2411 CE_WRITE_ERROR - Cluster creation failed
2412 Side Effects:
2413 None
2414 Description:
2415 This function will find an unused cluster, link it to
2416 a file's directory entry, and write the entry back
2417 to the device.
2418 Remarks:
2419 None.
2420 ******************************************************/
2421  
2422 #ifdef ALLOW_WRITES
2423 CETYPE CreateFirstCluster(FILEOBJ fo)
2424 {
2425 CETYPE error;
2426 DWORD cluster,TempMsbCluster;
2427 WORD fHandle;
2428 DIRENTRY dir;
2429 fHandle = fo->entry;
2430  
2431 // Now create the first cluster (head cluster)
2432 if((error = FILECreateHeadCluster(fo,&cluster)) == CE_GOOD)
2433 {
2434 // load the file entry so the new cluster can be linked to it
2435 dir = LoadDirAttrib(fo, &fHandle);
2436  
2437 // Now update the new cluster
2438 dir->DIR_FstClusLO = (cluster & 0x0000FFFF);
2439  
2440  
2441 #ifdef SUPPORT_FAT32 // If FAT32 supported.
2442 // Get the higher part of cluster and store it in directory entry.
2443 TempMsbCluster = (cluster & 0x0FFF0000); // Since only 28 bits usedin FAT32. Mask the higher MSB nibble.
2444 TempMsbCluster = TempMsbCluster >> 16; // Get the date into Lsb place.
2445 dir->DIR_FstClusHI = TempMsbCluster;
2446 #else // If FAT32 support not enabled
2447 TempMsbCluster = 0; // Just to avoid compiler warnigng.
2448 dir->DIR_FstClusHI = 0;
2449 #endif
2450  
2451 // now write it
2452 if(Write_File_Entry(fo, &fHandle) != TRUE)
2453 error = CE_WRITE_ERROR;
2454 } // Create Cluster
2455  
2456 return(error);
2457 }// End of CreateFirstCluster
2458 #endif
2459  
2460 /**********************************************************
2461 Function:
2462 BYTE FindEmptyEntries(FILEOBJ fo, WORD *fHandle)
2463 Summary:
2464 Find an empty dir entry
2465 Conditions:
2466 This function should not be called by the user.
2467 Input:
2468 fo - Pointer to file structure
2469 fHandle - Start of entries
2470 Return Values:
2471 TRUE - One found
2472 FALSE - None found
2473 Side Effects:
2474 None
2475 Description:
2476 This function will cache directory entries, starting
2477 with the one pointed to by the fHandle argument. It will
2478 then search through the entries until an unused one
2479 is found. If the end of the cluster chain for the
2480 directory is reached, a new cluster will be allocated
2481 to the directory (unless it's a FAT12 or FAT16 root)
2482 and the first entry of the new cluster will be used.
2483 Remarks:
2484 None.
2485 **********************************************************/
2486  
2487 #ifdef ALLOW_WRITES
2488 BYTE FindEmptyEntries(FILEOBJ fo, WORD *fHandle)
2489 {
2490 BYTE status = NOT_FOUND;
2491 BYTE amountfound;
2492 BYTE a;
2493 WORD bHandle;
2494 DWORD b;
2495 DIRENTRY dir;
2496  
2497 fo->dirccls = fo->dirclus;
2498 if((dir = Cache_File_Entry( fo, fHandle, TRUE)) == NULL)
2499 {
2500 status = CE_BADCACHEREAD;
2501 }
2502 else
2503 {
2504 // while its still not found
2505 while(status == NOT_FOUND)
2506 {
2507 amountfound = 0;
2508 bHandle = *fHandle;
2509  
2510 // find (number) continuous entries
2511 do
2512 {
2513 // Get the entry
2514 dir = Cache_File_Entry( fo, fHandle, FALSE);
2515  
2516 // Read the first char of the file name
2517 if(dir != NULL) // Last entry of the cluster
2518 {
2519 a = dir->DIR_Name[0];
2520 }
2521 // increase number
2522 (*fHandle)++;
2523 }while((a == DIR_DEL || a == DIR_EMPTY) && (dir != (DIRENTRY)NULL) && (++amountfound < 1));
2524  
2525 // --- now why did we exit?
2526 if(dir == NULL) // Last entry of the cluster
2527 {
2528 //setup the current cluster
2529 b = fo->dirccls; // write it back
2530  
2531 // make sure we are not the root directory
2532 if(b == FatRootDirClusterValue)
2533 {
2534 if (fo->dsk->type != FAT32)
2535 status = NO_MORE;
2536 else
2537 {
2538 fo->ccls = b;
2539  
2540 if(FILEallocate_new_cluster(fo, 1) == CE_DISK_FULL)
2541 status = NO_MORE;
2542 else
2543 {
2544 *fHandle = bHandle;
2545 status = FOUND; // a new cluster will surely hold a new file name
2546 }
2547 }
2548 }
2549 else
2550 {
2551 fo->ccls = b;
2552  
2553 if(FILEallocate_new_cluster(fo, 1) == CE_DISK_FULL)
2554 status = NO_MORE;
2555 else
2556 {
2557 *fHandle = bHandle;
2558 status = FOUND; // a new cluster will surely hold a new file name
2559 }
2560 }
2561 }
2562 else
2563 {
2564 if(amountfound == 1)
2565 {
2566 status = FOUND;
2567 *fHandle = bHandle;
2568 }
2569 }
2570 }// while
2571  
2572 // copy the base handle over
2573 *fHandle = bHandle;
2574 }
2575  
2576 if(status == FOUND)
2577 return(TRUE);
2578 else
2579 return(FALSE);
2580 }
2581 #endif
2582  
2583 /**************************************************************************
2584 Function:
2585 BYTE PopulateEntries(FILEOBJ fo, char *name , WORD *fHandle)
2586 Summary:
2587 Populate a dir entry with data
2588 Conditions:
2589 Should not be called by the user.
2590 Input:
2591 fo - Pointer to file structure
2592 name - Name of the file
2593 fHandle - Location of the file
2594 Return Values:
2595 CE_GOOD - Population successful
2596 Side Effects:
2597 None
2598 Description:
2599 This function will write data into a new file entry. It will also
2600 load timestamp data (based on the method selected by the user) and
2601 update the timestamp variables.
2602 Remarks:
2603 None.
2604 **************************************************************************/
2605  
2606 #ifdef ALLOW_WRITES
2607 BYTE PopulateEntries(FILEOBJ fo, char *name , WORD *fHandle, BYTE mode)
2608 {
2609 BYTE error = CE_GOOD;
2610 DIRENTRY dir;
2611  
2612 fo->dirccls = fo->dirclus;
2613 dir = Cache_File_Entry( fo, fHandle, TRUE);
2614  
2615 if (dir == NULL)
2616 return CE_BADCACHEREAD;
2617  
2618 // copy the contents over
2619 strncpy(dir->DIR_Name,name,DIR_NAMECOMP);
2620  
2621 // setup no attributes
2622 if (mode == DIRECTORY)
2623 dir->DIR_Attr = ATTR_DIRECTORY;
2624 else
2625 dir->DIR_Attr = ATTR_ARCHIVE;
2626  
2627 dir->DIR_NTRes = 0x00; // nt reserved
2628 dir->DIR_FstClusHI = 0x0000; // high word of this enty's first cluster number
2629 dir->DIR_FstClusLO = 0x0000; // low word of this entry's first cluster number
2630 dir->DIR_FileSize = 0x0; // file size in DWORD
2631  
2632 // Timing information for uncontrolled clock mode
2633 #ifdef INCREMENTTIMESTAMP
2634 dir->DIR_CrtTimeTenth = 0xB2; // millisecond stamp
2635 dir->DIR_CrtTime = 0x7278; // time created
2636 dir->DIR_CrtDate = 0x32B0; // date created
2637 dir->DIR_LstAccDate = 0x32B0; // Last Access date
2638 dir->DIR_WrtTime = 0x7279; // last update time
2639 dir->DIR_WrtDate = 0x32B0; // last update date
2640 #endif
2641  
2642 #ifdef USEREALTIMECLOCK
2643 CacheTime();
2644 dir->DIR_CrtTimeTenth = gTimeCrtMS; // millisecond stamp
2645 dir->DIR_CrtTime = gTimeCrtTime; // time created //
2646 dir->DIR_CrtDate = gTimeCrtDate; // date created (1/1/2004)
2647 dir->DIR_LstAccDate = gTimeAccDate; // Last Access date
2648 dir->DIR_WrtTime = gTimeWrtTime; // last update time
2649 dir->DIR_WrtDate = gTimeWrtDate; // last update date
2650 #endif
2651  
2652 #ifdef USERDEFINEDCLOCK
2653 // The user will have set the time before this funciton is called
2654 dir->DIR_CrtTimeTenth = gTimeCrtMS;
2655 dir->DIR_CrtTime = gTimeCrtTime;
2656 dir->DIR_CrtDate = gTimeCrtDate;
2657 dir->DIR_LstAccDate = gTimeAccDate;
2658 dir->DIR_WrtTime = gTimeWrtTime;
2659 dir->DIR_WrtDate = gTimeWrtDate;
2660 #endif
2661  
2662 fo->size = dir->DIR_FileSize;
2663 fo->time = dir->DIR_CrtTime;
2664 fo->date = dir->DIR_CrtDate;
2665 fo->attributes = dir->DIR_Attr;
2666 fo->entry = *fHandle;
2667  
2668 // just write the last entry in
2669 if (Write_File_Entry(fo,fHandle) != TRUE)
2670 error = CE_WRITE_ERROR;
2671  
2672 return(error);
2673 }
2674  
2675 #ifdef USEREALTIMECLOCK
2676  
2677 /*************************************************************************
2678 Function:
2679 void CacheTime (void)
2680 Summary:
2681 Automatically store timestamp information from the RTCC
2682 Conditions:
2683 RTCC module enabled. Should not be called by the user.
2684 Return Values:
2685 None
2686 Side Effects:
2687 Modifies global timing variables
2688 Description:
2689 This function will automatically load information from an RTCC
2690 module and use it to update the global timing variables. These can
2691 then be used to update file timestamps.
2692 Remarks:
2693 None.
2694 *************************************************************************/
2695  
2696 void CacheTime (void)
2697 {
2698 WORD year, monthday, weekhour, minsec, c, result;
2699 BYTE ptr1, ptr0;
2700  
2701 #if defined (__PIC32MX__) // Added support for PIC32. -Bud (3/4/2008)
2702  
2703 unsigned int t0, t1;
2704 unsigned int d0, d1;
2705  
2706 do // Get the time
2707 {
2708 t0=RTCTIME;
2709 t1=RTCTIME;
2710 }while(t0!=t1);
2711  
2712 do // Get the date
2713 {
2714 d0=RTCDATE;
2715 d1=RTCDATE;
2716 }while(d0!=d1);
2717  
2718 // Put them in place.
2719 year = (WORD)(d0 >> 24);
2720 monthday = (WORD)(d0 >> 8);
2721 weekhour = (WORD)((d0 & 0x0F) << 8);
2722 weekhour |= (WORD)(t0 >> 24);
2723 minsec = (WORD)(t0 >> 8);
2724  
2725 #else
2726  
2727 if(RCFGCALbits.RTCPTR0)
2728 ptr0 = 1;
2729 else
2730 ptr0 = 0;
2731 if (RCFGCALbits.RTCPTR1)
2732 ptr1 = 1;
2733 else
2734 ptr1 = 0;
2735  
2736 RCFGCALbits.RTCPTR0 = 1;
2737 RCFGCALbits.RTCPTR1 = 1;
2738 year = RTCVAL;
2739 monthday = RTCVAL;
2740 weekhour = RTCVAL;
2741 minsec = RTCVAL;
2742  
2743 if (ptr0 == 1)
2744 RCFGCALbits.RTCPTR0 = 1;
2745  
2746 if (ptr1 == 1)
2747 RCFGCALbits.RTCPTR1 = 1;
2748  
2749 #endif
2750  
2751 c = 0;
2752 c += (year & 0x0F);
2753 c += ((year & 0xF0) >> 4) * 10;
2754 // c equals the last 2 digits of the year from 2000 to 2099
2755 // Add 20 to adjust it to FAT time (from 1980 to 2107)
2756 c += 20;
2757 // shift the result to bits
2758 result = c << 9;
2759  
2760 if ((monthday & 0x1000) == 0x1000)
2761 {
2762 c = 10;
2763 }
2764 else
2765 {
2766 c = 0;
2767 }
2768 c += ((monthday & 0x0F00) >> 8);
2769 c <<= 5;
2770 result |= c;
2771  
2772 c = (monthday & 0x00F0) >> 4;
2773 c *= 10;
2774 c += (monthday & 0x000F);
2775  
2776 result |= c;
2777  
2778 gTimeCrtDate = result;
2779 gTimeWrtDate = result;
2780 gTimeAccDate = result;
2781  
2782 c = ((weekhour & 0x00F0) >> 4) * 10;
2783 c += (weekhour & 0x000F);
2784 result = c << 11;
2785 c = ((minsec & 0xF000) >> 12) * 10;
2786 c += (minsec & 0x0F00) >> 8;
2787 result |= (c << 5);
2788 c = ((minsec & 0x00F0) >> 4) * 10;
2789 c += (minsec & 0x000F);
2790  
2791 // If seconds mod 2 is 1, add 1000 ms
2792 if (c % 2)
2793 gTimeCrtMS = 100;
2794 else
2795 gTimeCrtMS = 0;
2796  
2797 c >>= 1;
2798 result |= c;
2799  
2800 gTimeCrtTime = result;
2801 gTimeWrtTime = result;
2802 }
2803 #endif
2804  
2805 #ifdef USERDEFINEDCLOCK
2806  
2807 /***********************************************************************************************************
2808 Function:
2809 int SetClockVars (unsigned int year, unsigned char month, unsigned char day, unsigned char hour, unsigned char minute, unsigned char second)
2810 Summary:
2811 Manually set timestamp variables
2812 Conditions:
2813 USERDEFINEDCLOCK macro defined in FSconfig.h.
2814 Input:
2815 year - The year (1980\-2107)
2816 month - The month (1\-12)
2817 day - The day of the month (1\-31)
2818 hour - The hour (0\-23)
2819 minute - The minute (0\-59)
2820 second - The second (0\-59)
2821 Return Values:
2822 None
2823 Side Effects:
2824 Modifies global timing variables
2825 Description:
2826 Lets the user manually set the timing variables. The values passed in will be converted to the format
2827 used by the FAT timestamps.
2828 Remarks:
2829 Call this before creating a file or directory (set create time) and
2830 before closing a file (set last access time, last modified time)
2831 ***********************************************************************************************************/
2832  
2833 int SetClockVars (unsigned int year, unsigned char month, unsigned char day, unsigned char hour, unsigned char minute, unsigned char second)
2834 {
2835 unsigned int result;
2836  
2837 if ((year < 1980) || (year > 2107) || (month < 1) || (month > 12) ||
2838 (day < 1) || (day > 31) || (hour > 23) || (minute > 59) || (second > 59))
2839 {
2840 FSerrno = CE_INVALID_ARGUMENT;
2841 return -1;
2842 }
2843  
2844 result = (year - 1980) << 9;
2845 result |= (unsigned int)((unsigned int)month << 5);
2846 result |= (day);
2847  
2848 gTimeAccDate = result;
2849 gTimeCrtDate = result;
2850 gTimeWrtDate = result;
2851  
2852 result = ((unsigned int)hour << 11);
2853 result |= (unsigned int)((unsigned int)minute << 5);
2854 result |= (second/2);
2855  
2856 gTimeCrtTime = result;
2857 gTimeWrtTime = result;
2858  
2859 if (second % 2)
2860 gTimeCrtMS = 100;
2861 else
2862 gTimeCrtMS = 0;
2863  
2864 FSerrno = CE_GOOD;
2865 return 0;
2866 }
2867 #endif
2868  
2869 #endif
2870  
2871 /***********************************************************************
2872 Function:
2873 BYTE FILEallocate_new_cluster( FILEOBJ fo, BYTE mode)
2874 Summary;
2875 Allocate a new cluster to a file
2876 Conditions:
2877 Should not be called by the user.
2878 Input:
2879 fo - Pointer to file structure
2880 mode -
2881 - 0 - Allocate a cluster to a file
2882 - 1 - Allocate a cluster to a directory
2883 Return Values:
2884 CE_GOOD - Cluster allocated
2885 CE_DISK_FULL - No clusters available
2886 Side Effects:
2887 None
2888 Description:
2889 This function will find an empty cluster on the device using the
2890 FATfindEmptyCluster function. It will then mark it as the last
2891 cluster in the file in the FAT chain, and link the current last
2892 cluster of the passed file to the new cluster. If the new
2893 cluster is a directory cluster, it will be erased (so there are no
2894 extraneous directory entries). If it's allocated to a non-directory
2895 file, it doesn't need to be erased; extraneous data in the cluster
2896 will be unviewable because of the file size parameter.
2897 Remarks:
2898 None.
2899 ***********************************************************************/
2900  
2901 #ifdef ALLOW_WRITES
2902 BYTE FILEallocate_new_cluster( FILEOBJ fo, BYTE mode)
2903 {
2904 DISK * dsk;
2905 DWORD c,curcls;
2906  
2907 dsk = fo->dsk;
2908 c = fo->ccls;
2909  
2910 // find the next empty cluster
2911 c = FATfindEmptyCluster(fo);
2912 if (c == 0) // "0" is just an indication as Disk full in the fn "FATfindEmptyCluster()"
2913 return CE_DISK_FULL;
2914  
2915  
2916 // mark the cluster as taken, and last in chain
2917 if(dsk->type == FAT12)
2918 WriteFAT( dsk, c, LAST_CLUSTER_FAT12, FALSE);
2919 else if (dsk->type == FAT16)
2920 WriteFAT( dsk, c, LAST_CLUSTER_FAT16, FALSE);
2921  
2922 #ifdef SUPPORT_FAT32 // If FAT32 supported.
2923 else
2924 WriteFAT( dsk, c, LAST_CLUSTER_FAT32, FALSE);
2925 #endif
2926  
2927 // link current cluster to the new one
2928 curcls = fo->ccls;
2929  
2930 WriteFAT( dsk, curcls, c, FALSE);
2931  
2932 // update the FILE structure
2933 fo->ccls = c;
2934  
2935 // IF this is a dir, we need to erase the cluster
2936 // If it's a file, we can leave it- the file size
2937 // will limit the data we see to the data that's been
2938 // written
2939 if (mode == 1)
2940 return (EraseCluster(dsk, c));
2941 else
2942 return CE_GOOD;
2943  
2944 } // allocate new cluster
2945 #endif
2946  
2947 /***********************************************
2948 Function:
2949 DWORD FATfindEmptyCluster(FILEOBJ fo)
2950 Summary:
2951 Find the next available cluster on the device
2952 Conditions:
2953 This function should not be called by the
2954 user.
2955 Input:
2956 fo - Pointer to file structure
2957 Return Values:
2958 DWORD - Address of empty cluster
2959  
2960 Side Effects:
2961 None
2962 Description:
2963 This function will search through the FAT to
2964 find the next available cluster on the device.
2965 Remarks:
2966 Should not be called by user
2967 ***********************************************/
2968  
2969 #ifdef ALLOW_WRITES
2970 DWORD FATfindEmptyCluster(FILEOBJ fo)
2971 {
2972 DISK * disk;
2973 DWORD value = 0x0;
2974 DWORD c,curcls, EndClusterLimit, ClusterFailValue;
2975  
2976 disk = fo->dsk;
2977 c = fo->ccls;
2978  
2979 /* Settings based on FAT type */
2980 switch (disk->type)
2981 {
2982 #ifdef SUPPORT_FAT32 // If FAT32 supported.
2983 case FAT32:
2984 EndClusterLimit = END_CLUSTER_FAT32;
2985 ClusterFailValue = CLUSTER_FAIL_FAT32;
2986 break;
2987 #endif
2988 case FAT12:
2989 EndClusterLimit = END_CLUSTER_FAT12;
2990 ClusterFailValue = CLUSTER_FAIL_FAT16;
2991 break;
2992 case FAT16:
2993 default:
2994 EndClusterLimit = END_CLUSTER_FAT16;
2995 ClusterFailValue = CLUSTER_FAIL_FAT16;
2996 break;
2997 }
2998  
2999 // just in case
3000 if(c < 2)
3001 c = 2;
3002  
3003 curcls = c;
3004 ReadFAT(disk, c);
3005  
3006 // sequentially scan through the FAT looking for an empty cluster
3007 while(c)
3008 {
3009 // look at its value
3010 if ( (value = ReadFAT(disk, c)) == ClusterFailValue)
3011 {
3012 c = 0;
3013 break;
3014 }
3015  
3016 // check if empty cluster found
3017 if (value == CLUSTER_EMPTY)
3018 break;
3019  
3020 c++; // check next cluster in FAT
3021 // check if reached last cluster in FAT, re-start from top
3022 if (value == EndClusterLimit || c >= (disk->maxcls+2))
3023 c = 2;
3024  
3025 // check if full circle done, disk full
3026 if ( c == curcls)
3027 {
3028 c = 0;
3029 break;
3030 }
3031 } // scanning for an empty cluster
3032  
3033 return(c);
3034 }
3035 #endif
3036  
3037  
3038 /*********************************************************************************
3039 Function:
3040 void FSGetDiskProperties(FS_DISK_PROPERTIES* properties)
3041 Summary:
3042 Allows user to get the disk properties (size of disk, free space, etc)
3043 Conditions:
3044 1) ALLOW_GET_DISK_PROPERTIES must be defined in FSconfig.h
3045 2) a FS_DISK_PROPERTIES object must be created before the function is called
3046 3) the new_request member of the FS_DISK_PROPERTIES object must be set before
3047 calling the function for the first time. This will start a new search.
3048 4) this function should not be called while there is a file open. Close all
3049 files before calling this function.
3050 Input:
3051 properties - a pointer to a FS_DISK_PROPERTIES object where the results should
3052 be stored.
3053 Return Values:
3054 This function returns void. The properties_status of the previous call of this
3055 function is located in the properties.status field. This field has the
3056 following possible values:
3057  
3058 FS_GET_PROPERTIES_NO_ERRORS - operation completed without error. Results
3059 are in the properties object passed into the function.
3060 FS_GET_PROPERTIES_DISK_NOT_MOUNTED - there is no mounted disk. Results in
3061 properties object is not valid
3062 FS_GET_PROPERTIES_CLUSTER_FAILURE - there was a failure trying to read a
3063 cluster from the drive. The results in the properties object is a partial
3064 result up until the point of the failure.
3065 FS_GET_PROPERTIES_STILL_WORKING - the search for free sectors is still in
3066 process. Continue calling this function with the same properties pointer
3067 until either the function completes or until the partial results meets the
3068 application needs. The properties object contains the partial results of
3069 the search and can be used by the application.
3070 Side Effects:
3071 Can cause errors if called when files are open. Close all files before
3072 calling this function.
3073  
3074 Calling this function without setting the new_request member on the first
3075 call can result in undefined behavior and results.
3076  
3077 Calling this function after a result is returned other than
3078 FS_GET_PROPERTIES_STILL_WORKING can result in undefined behavior and results.
3079 Description:
3080 This function returns the information about the mounted drive. The results
3081 member of the properties object passed into the function is populated with
3082 the information about the drive.
3083  
3084 Before starting a new request, the new_request member of the properties
3085 input parameter should be set to TRUE. This will initiate a new search
3086 request.
3087  
3088 This function will return before the search is complete with partial results.
3089 All of the results except the free_clusters will be correct after the first
3090 call. The free_clusters will contain the number of free clusters found up
3091 until that point, thus the free_clusters result will continue to grow until
3092 the entire drive is searched. If an application only needs to know that a
3093 certain number of bytes is available and doesn't need to know the total free
3094 size, then this function can be called until the required free size is
3095 verified. To continue a search, pass a pointer to the same FS_DISK_PROPERTIES
3096 object that was passed in to create the search.
3097  
3098 A new search request sould be made once this function has returned a value
3099 other than FS_GET_PROPERTIES_STILL_WORKING. Continuing a completed search
3100 can result in undefined behavior or results.
3101  
3102 Typical Usage:
3103 <code>
3104 FS_DISK_PROPERTIES disk_properties;
3105  
3106 disk_properties.new_request = TRUE;
3107  
3108 do
3109 {
3110 FSGetDiskProperties(&disk_properties);
3111 } while (disk_properties.properties_status == FS_GET_PROPERTIES_STILL_WORKING);
3112 </code>
3113  
3114 results.disk_format - contains the format of the drive. Valid results are
3115 FAT12(1), FAT16(2), or FAT32(3).
3116  
3117 results.sector_size - the sector size of the mounted drive. Valid values are
3118 512, 1024, 2048, and 4096.
3119  
3120 results.sectors_per_cluster - the number sectors per cluster.
3121  
3122 results.total_clusters - the number of total clusters on the drive. This
3123 can be used to calculate the total disk size (total_clusters *
3124 sectors_per_cluster * sector_size = total size of drive in bytes)
3125  
3126 results.free_clusters - the number of free (unallocated) clusters on the drive.
3127 This can be used to calculate the total free disk size (free_clusters *
3128 sectors_per_cluster * sector_size = total size of drive in bytes)
3129  
3130 Remarks:
3131 PIC24F size estimates:
3132 Flash - 400 bytes (-Os setting)
3133  
3134 PIC24F speed estimates:
3135 Search takes approximately 7 seconds per Gigabyte of drive space. Speed
3136 will vary based on the number of sectors per cluster and the sector size.
3137 *********************************************************************************/
3138 #if defined(ALLOW_GET_DISK_PROPERTIES)
3139 void FSGetDiskProperties(FS_DISK_PROPERTIES* properties)
3140 {
3141 BYTE i;
3142 DWORD value = 0x0;
3143  
3144 if(properties->new_request == TRUE)
3145 {
3146 properties->disk = &gDiskData;
3147 properties->results.free_clusters = 0;
3148 properties->new_request = FALSE;
3149  
3150 if(properties->disk->mount != TRUE)
3151 {
3152 properties->properties_status = FS_GET_PROPERTIES_DISK_NOT_MOUNTED;
3153 return;
3154 }
3155  
3156 properties->properties_status = FS_GET_PROPERTIES_STILL_WORKING;
3157  
3158 properties->results.disk_format = properties->disk->type;
3159 properties->results.sector_size = properties->disk->sectorSize;
3160 properties->results.sectors_per_cluster = properties->disk->SecPerClus;
3161 properties->results.total_clusters = properties->disk->maxcls;
3162  
3163 /* Settings based on FAT type */
3164 switch (properties->disk->type)
3165 {
3166 #ifdef SUPPORT_FAT32 // If FAT32 supported.
3167 case FAT32:
3168 properties->private.EndClusterLimit = END_CLUSTER_FAT32;
3169 properties->private.ClusterFailValue = CLUSTER_FAIL_FAT32;
3170 break;
3171 #endif
3172 case FAT16:
3173 properties->private.EndClusterLimit = END_CLUSTER_FAT16;
3174 properties->private.ClusterFailValue = CLUSTER_FAIL_FAT16;
3175 break;
3176 case FAT12:
3177 properties->private.EndClusterLimit = END_CLUSTER_FAT12;
3178 properties->private.ClusterFailValue = CLUSTER_FAIL_FAT16;
3179 break;
3180 }
3181  
3182 properties->private.c = 2;
3183  
3184 properties->private.curcls = properties->private.c;
3185 ReadFAT(properties->disk, properties->private.c);
3186 }
3187  
3188 if(properties->disk == NULL)
3189 {
3190 properties->properties_status = FS_GET_PROPERTIES_DISK_NOT_MOUNTED;
3191 return;
3192 }
3193  
3194 if(properties->properties_status != FS_GET_PROPERTIES_STILL_WORKING)
3195 {
3196 return;
3197 }
3198  
3199 // sequentially scan through the FAT looking for an empty cluster
3200 for(i=0;i<255;i++)
3201 {
3202 // look at its value
3203 if ( (value = ReadFAT(properties->disk, properties->private.c)) == properties->private.ClusterFailValue)
3204 {
3205 properties->properties_status = FS_GET_PROPERTIES_CLUSTER_FAILURE;
3206 return;
3207 }
3208  
3209 // check if empty cluster found
3210 if (value == CLUSTER_EMPTY)
3211 {
3212 properties->results.free_clusters++;
3213 }
3214  
3215 properties->private.c++; // check next cluster in FAT
3216 // check if reached last cluster in FAT, re-start from top
3217 if (value == properties->private.EndClusterLimit || properties->private.c >= (properties->results.total_clusters + 2))
3218 properties->private.c = 2;
3219  
3220 // check if full circle done, disk full
3221 if ( properties->private.c == properties->private.curcls)
3222 {
3223 properties->properties_status = FS_GET_PROPERTIES_NO_ERRORS;
3224 return;
3225 }
3226 } // scanning for an empty cluster
3227  
3228 properties->properties_status = FS_GET_PROPERTIES_STILL_WORKING;
3229 return;
3230 }
3231 #endif
3232  
3233 /************************************************************
3234 Function:
3235 int FSfclose(FSFILE *fo)
3236 Summary:
3237 Update file information and free FSFILE objects
3238 Conditions:
3239 File opened
3240 Input:
3241 fo - Pointer to the file to close
3242 Return Values:
3243  
3244 EOF - Error closing the file
3245 Side Effects:
3246 The FSerrno variable will be changed.
3247 Description:
3248 This function will update the directory entry for the
3249 file pointed to by 'fo' with the information contained
3250 in 'fo,' including the new file size and attributes.
3251 Timestamp information will also be loaded based on the
3252 method selected by the user and written to the entry
3253 as the last modified time and date. The file entry will
3254 then be written to the device. Finally, the memory
3255 used for the specified file object will be freed from
3256 the dynamic heap or the array of FSFILE objects.
3257 Remarks:
3258 A function to flush data to the device without closing the
3259 file can be created by removing the portion of this
3260 function that frees the memory and the line that clears
3261 the write flag.
3262 ************************************************************/
3263  
3264 int FSfclose(FSFILE *fo)
3265 {
3266 WORD fHandle;
3267 #ifndef FS_DYNAMIC_MEM
3268 WORD fIndex;
3269 #endif
3270 int error = 72;
3271 #ifdef ALLOW_WRITES
3272 DIRENTRY dir;
3273 #endif
3274  
3275 FSerrno = CE_GOOD;
3276 fHandle = fo->entry;
3277  
3278 #ifdef ALLOW_WRITES
3279 if(fo->flags.write)
3280 {
3281 if (gNeedDataWrite)
3282 if (flushData())
3283 {
3284 FSerrno = CE_WRITE_ERROR;
3285 return EOF;
3286 }
3287  
3288 // Write the current FAT sector to the disk
3289 WriteFAT (fo->dsk, 0, 0, TRUE);
3290  
3291 // Get the file entry
3292 dir = LoadDirAttrib(fo, &fHandle);
3293  
3294 if (dir == NULL)
3295 {
3296 FSerrno = CE_BADCACHEREAD;
3297 error = EOF;
3298 return error;
3299 }
3300  
3301 // update the time
3302 #ifdef INCREMENTTIMESTAMP
3303 IncrementTimeStamp(dir);
3304 #elif defined USERDEFINEDCLOCK
3305 dir->DIR_WrtTime = gTimeWrtTime;
3306 dir->DIR_WrtDate = gTimeWrtDate;
3307 #elif defined USEREALTIMECLOCK
3308 CacheTime();
3309 dir->DIR_WrtTime = gTimeWrtTime;
3310 dir->DIR_WrtDate = gTimeWrtDate;
3311 #endif
3312  
3313 dir->DIR_FileSize = fo->size;
3314  
3315 dir->DIR_Attr = fo->attributes;
3316  
3317 // just write the last entry in
3318 if(Write_File_Entry(fo,&fHandle))
3319 error = 0;
3320 else
3321 {
3322 FSerrno = CE_WRITE_ERROR;
3323 error = EOF;
3324 }
3325  
3326 // it's now closed
3327 fo->flags.write = FALSE;
3328 }
3329 #endif
3330  
3331 #ifdef FS_DYNAMIC_MEM
3332 FS_free((unsigned char *)fo);
3333 #else
3334  
3335 for( fIndex = 0; fIndex < FS_MAX_FILES_OPEN; fIndex++ )
3336 {
3337 if( fo == &gFileArray[fIndex] )
3338 {
3339 gFileSlotOpen[fIndex] = TRUE;
3340 break;
3341 }
3342 }
3343 #endif
3344  
3345 // File opened in read mode
3346 if (error == 72)
3347 error = 0;
3348  
3349 return(error);
3350 } // FSfclose
3351  
3352  
3353  
3354  
3355 /*******************************************************
3356 Function:
3357 void IncrementTimeStamp(DIRENTRY dir)
3358 Summary:
3359 Automatically set the timestamp to "don't care" data
3360 Conditions:
3361 Should not be called by the user.
3362 Input:
3363 dir - Pointer to directory structure
3364 Return Values:
3365 None
3366 Side Effects:
3367 None
3368 Description:
3369 This function will increment the timestamp variable in
3370 the 'dir' directory entry. This is used for the
3371 don't-care timing method.
3372 Remarks:
3373 None
3374 *******************************************************/
3375 #ifdef INCREMENTTIMESTAMP
3376 void IncrementTimeStamp(DIRENTRY dir)
3377 {
3378 BYTE seconds;
3379 BYTE minutes;
3380 BYTE hours;
3381  
3382 BYTE day;
3383 BYTE month;
3384 BYTE year;
3385  
3386 seconds = (dir->DIR_WrtTime & 0x1f);
3387 minutes = ((dir->DIR_WrtTime & 0x07E0) >> 5);
3388 hours = ((dir->DIR_WrtTime & 0xF800) >> 11);
3389  
3390 day = (dir->DIR_WrtDate & 0x1f);
3391 month = ((dir->DIR_WrtDate & 0x01E0) >> 5);
3392 year = ((dir->DIR_WrtDate & 0xFE00) >> 9);
3393  
3394 if(seconds < 29)
3395 {
3396 // Increment number of seconds by 2
3397 // This clock method isn't intended to be accurate anyway
3398 seconds++;
3399 }
3400 else
3401 {
3402 seconds = 0x00;
3403  
3404 if(minutes < 59)
3405 {
3406 minutes++;
3407 }
3408 else
3409 {
3410 minutes = 0;
3411  
3412 if(hours < 23)
3413 {
3414 hours++;
3415 }
3416 else
3417 {
3418 hours = 0;
3419 if(day < 30)
3420 {
3421 day++;
3422 }
3423 else
3424 {
3425 day = 1;
3426  
3427 if(month < 12)
3428 {
3429 month++;
3430 }
3431 else
3432 {
3433 month = 1;
3434 // new year
3435 year++;
3436 // This is only valid until 2107
3437 }
3438 }
3439 }
3440 }
3441 }
3442  
3443 dir->DIR_WrtTime = (WORD)(seconds);
3444 dir->DIR_WrtTime |= ((WORD)(minutes) << 5);
3445 dir->DIR_WrtTime |= ((WORD)(hours) << 11);
3446  
3447 dir->DIR_WrtDate = (WORD)(day);
3448 dir->DIR_WrtDate |= ((WORD)(month) << 5);
3449 dir->DIR_WrtDate |= ((WORD)(year) << 9);
3450 }
3451 #endif
3452  
3453 /*****************************************************************
3454 Function:
3455 BYTE Fill_File_Object(FILEOBJ fo, WORD *fHandle)
3456 Summary:
3457 Fill a file object with specified dir entry data
3458 Conditions:
3459 This function should not be called by the user.
3460 Input:
3461 fo - Pointer to file structure
3462 fHandle - Passed member's location
3463 Return Values:
3464 FOUND - Operation successful
3465 NOT_FOUND - Operation failed
3466 Side Effects:
3467 None
3468 Description:
3469 This function will cache the sector of directory entries
3470 in the directory pointed to by the dirclus value in
3471 the FSFILE object 'fo' that contains the entry that
3472 corresponds to the fHandle offset. It will then copy
3473 the file information for that entry into the 'fo' FSFILE
3474 object.
3475 Remarks:
3476 None.
3477 *****************************************************************/
3478  
3479 BYTE Fill_File_Object(FILEOBJ fo, WORD *fHandle)
3480 {
3481 DIRENTRY dir;
3482 BYTE index, a;
3483 BYTE character;
3484 BYTE status;
3485 BYTE test = 0;
3486  
3487 // Get the entry
3488 if (((*fHandle & MASK_MAX_FILE_ENTRY_LIMIT_BITS) == 0) && (*fHandle != 0)) // 4-bit mask because 16-root entries max per sector
3489 {
3490 fo->dirccls = fo->dirclus;
3491 dir = Cache_File_Entry(fo, fHandle, TRUE);
3492 }
3493 else
3494 {
3495 dir = Cache_File_Entry (fo, fHandle, FALSE);
3496 }
3497  
3498  
3499 // Make sure there is a directory left
3500 if(dir == (DIRENTRY)NULL)
3501 {
3502 status = NO_MORE;
3503 }
3504 else
3505 {
3506 // Read the first char of the file name
3507 a = dir->DIR_Name[0];
3508  
3509 // Check for empty or deleted directory
3510 if ( a == DIR_DEL)
3511 {
3512 status = NOT_FOUND;
3513 }
3514 else if ( a == DIR_EMPTY)
3515 {
3516 status = NO_MORE;
3517 }
3518 else
3519 {
3520 // Get the attributes
3521 a = dir->DIR_Attr;
3522  
3523 // print the file name and extension
3524 for (index=0; index < DIR_NAMESIZE; index++)
3525 {
3526 character = dir->DIR_Name[index];
3527 character = (BYTE)toupper(character);
3528 fo->name[test++] = character;
3529 }
3530  
3531 // Get the attributes
3532 a = dir->DIR_Attr;
3533  
3534 // its possible to have an extension in a directory
3535 character = dir->DIR_Extension[0];
3536  
3537 // Get the file extension if its there
3538 for (index=0; index < DIR_EXTENSION; index++)
3539 {
3540 character = dir->DIR_Extension[index];
3541 character = (BYTE)toupper(character);
3542 fo->name[test++] = character;
3543 }
3544  
3545 // done and done with the name
3546 // fo->name[++test] = (BYTE)'\0';
3547  
3548 // Now store the identifier
3549 fo->entry = *fHandle;
3550  
3551 // see if we are still a good file
3552 a = dir->DIR_Name[0];
3553  
3554 if(a == DIR_DEL)
3555 status = NOT_FOUND;
3556 else
3557 status = FOUND;
3558  
3559 // Now store the size
3560 fo->size = (dir->DIR_FileSize);
3561  
3562 fo->cluster = GetFullClusterNumber(dir); // Get Complete Cluster number.
3563  
3564 /// -Get and store the attributes
3565 a = dir->DIR_Attr;
3566 fo->attributes = a;
3567  
3568 // get the date and time
3569 if ((a & ATTR_DIRECTORY) != 0)
3570 {
3571 fo->time = dir->DIR_CrtTime;
3572 fo->date = dir->DIR_CrtDate;
3573 }
3574 else
3575 {
3576 fo->time = dir->DIR_WrtTime;
3577 fo->date = dir->DIR_WrtDate;
3578 }
3579  
3580 }// deleted directory
3581 }// Ensure we are still good
3582 return(status);
3583 } // Fill_File_Object
3584  
3585  
3586 /************************************************************************
3587 Function:
3588 DIRENTRY LoadDirAttrib(FILEOBJ fo, WORD *fHandle)
3589 Summary:
3590 Load file information from a directory entry and cache the entry
3591 Conditions:
3592 This function should not be called by the user.
3593 Input:
3594 fo - Pointer to file structure
3595 fHandle - Information location
3596 Return Values:
3597 DIRENTRY - Pointer to the directory entry
3598 NULL - Directory entry could not be loaded
3599 Side Effects:
3600 None
3601 Description:
3602 This function will cache the sector of directory entries
3603 in the directory pointed to by the dirclus value in
3604 the FSFILE object 'fo' that contains the entry that
3605 corresponds to the fHandle offset. It will then return a pointer
3606 to the directory entry in the global data buffer.
3607 Remarks:
3608 None.
3609 ************************************************************************/
3610  
3611 DIRENTRY LoadDirAttrib(FILEOBJ fo, WORD *fHandle)
3612 {
3613 DIRENTRY dir;
3614 BYTE a;
3615  
3616 fo->dirccls = fo->dirclus;
3617 // Get the entry
3618 dir = Cache_File_Entry( fo, fHandle, TRUE);
3619 if (dir == NULL)
3620 return NULL;
3621  
3622 // Read the first char of the file name
3623 a = dir->DIR_Name[0];
3624  
3625 // Make sure there is a directory left
3626 if(a == DIR_EMPTY)
3627 dir = (DIRENTRY)NULL;
3628  
3629 if(dir != (DIRENTRY)NULL)
3630 {
3631 // Check for empty or deleted directory
3632 if ( a == DIR_DEL)
3633 dir = (DIRENTRY)NULL;
3634 else
3635 {
3636 // Get the attributes
3637 a = dir->DIR_Attr;
3638  
3639 // scan through all the long dir entries
3640 while(a == ATTR_LONG_NAME)
3641 {
3642 (*fHandle)++;
3643 dir = Cache_File_Entry( fo, fHandle, FALSE);
3644 if (dir == NULL)
3645 return NULL;
3646 a = dir->DIR_Attr;
3647 } // long file name while loop
3648 } // deleted dir
3649 }// Ensure we are still good
3650  
3651 return(dir);
3652 } // LoadDirAttrib
3653  
3654  
3655 /**************************************************************************
3656 Function:
3657 CETYPE FILEerase( FILEOBJ fo, WORD *fHandle, BYTE EraseClusters)
3658 Summary:
3659 Erase a file
3660 Conditions:
3661 This function should not be called by the user.
3662 Input:
3663 fo - Pointer to file structure
3664 fHandle - Location of file information
3665 EraseClusters - Remove cluster allocation from FAT?
3666 Return Values:
3667 CE_GOOD - File erased successfully
3668 CE_FILE_NOT_FOUND - Could not find the file on the card
3669 CE_ERASE_FAIL - Internal Card erase failed
3670 Side Effects:
3671 None
3672 Description:
3673 This function will cache the sector of directory entries in the directory
3674 pointed to by the dirclus value in the FSFILE object 'fo' that contains
3675 the entry that corresponds to the fHandle offset. It will then mark that
3676 entry as deleted. If the EraseClusters argument is TRUE, the chain of
3677 clusters for that file will be marked as unused in the FAT by the
3678 FAT_erase_cluster_chain function.
3679 Remarks:
3680 None.
3681 **************************************************************************/
3682  
3683 #ifdef ALLOW_WRITES
3684 CETYPE FILEerase( FILEOBJ fo, WORD *fHandle, BYTE EraseClusters)
3685 {
3686 DIRENTRY dir;
3687 BYTE a;
3688 CETYPE status = CE_GOOD;
3689 DWORD clus;
3690 DISK * disk;
3691  
3692 disk = fo->dsk;
3693  
3694 // reset the cluster
3695 clus = fo->dirclus;
3696 fo->dirccls = clus;
3697  
3698 // load the sector
3699 dir = Cache_File_Entry(fo, fHandle, TRUE);
3700 if (dir == NULL)
3701 {
3702 FSerrno = CE_ERASE_FAIL;
3703 return CE_BADCACHEREAD;
3704 }
3705  
3706 // Fill up the File Object with the information pointed to by fHandle
3707 a = dir->DIR_Name[0];
3708  
3709 // see if there is something in the dir
3710 if(dir == (DIRENTRY)NULL || a == DIR_EMPTY)
3711 {
3712 status = CE_FILE_NOT_FOUND;
3713 }
3714 else
3715 {
3716 // Check for empty or deleted directory
3717 if ( a == DIR_DEL)
3718 {
3719 status = CE_FILE_NOT_FOUND;
3720 }
3721 else
3722 {
3723 // Get the attributes
3724 a = dir->DIR_Attr;
3725  
3726 /* 8.3 File Name - entry*/
3727 dir->DIR_Name[0] = DIR_DEL; // mark as deleted
3728  
3729 // Get the starting cluster
3730 clus = GetFullClusterNumber(dir); // Get Complete Cluster number.
3731  
3732 // Now write it
3733 if(status != CE_GOOD || !(Write_File_Entry( fo, fHandle)))
3734 {
3735 status = CE_ERASE_FAIL;
3736 }
3737 else
3738 {
3739 if (clus != FatRootDirClusterValue) //
3740 {
3741 if(EraseClusters)
3742 {
3743 /* Now remove the cluster allocation from the FAT */
3744 status = ((FAT_erase_cluster_chain(clus, disk)) ? CE_GOOD : CE_ERASE_FAIL);
3745 }
3746 }
3747 }
3748 } // Not already deleted
3749 }// Not existant
3750  
3751 if (status == CE_GOOD)
3752 FSerrno = CE_GOOD;
3753 else
3754 FSerrno = CE_ERASE_FAIL;
3755  
3756 return (status);
3757 }
3758 #endif
3759  
3760 /***************************************************************
3761 Function:
3762 int FSrename (const rom char * fileName, FSFILE * fo)
3763 Summary:
3764 Change the name of a file or directory
3765 Conditions:
3766 File opened.
3767 Input:
3768 fileName - The new name of the file
3769 fo - The file to rename
3770 Return Values:
3771  
3772 EOF - File was not renamed
3773 Side Effects:
3774 The FSerrno variable will be changed.
3775 Description:
3776 The FSrename function will rename a file. First, it will
3777 search through the current working directory to ensure the
3778 specified new filename is not already in use. If it isn't,
3779 the new filename will be written to the file entry of the
3780 file pointed to by 'fo.'
3781 Remarks:
3782 None
3783 ***************************************************************/
3784  
3785 #ifdef ALLOW_WRITES
3786  
3787 int FSrename (const char * fileName, FSFILE * fo)
3788 {
3789 unsigned char j, k = 0;
3790 char string[12];
3791 WORD fHandle = 1, goodHandle;
3792 DIRENTRY dir;
3793  
3794 FSerrno = CE_GOOD;
3795  
3796 if (fo == NULL)
3797 {
3798 FSerrno = CE_FILENOTOPENED;
3799 return -1;
3800 }
3801 // If fo != NULL, rename the file
3802 if (FormatFileName (fileName, fo->name, 0) == FALSE)
3803 {
3804 FSerrno = CE_INVALID_FILENAME;
3805 return -1;
3806 }
3807 else
3808 {
3809 for (j = 0; j < 11; j++)
3810 {
3811 string[j] = fo->name[j];
3812 }
3813 goodHandle = fo->entry;
3814  
3815 fHandle = 0;
3816 fo->dirccls = fo->dirclus;
3817 dir = Cache_File_Entry (fo, &fHandle, TRUE);
3818 if (dir == NULL)
3819 {
3820 FSerrno = CE_BADCACHEREAD;
3821 return -1;
3822 }
3823 // Check if the file name is already used
3824 for (j = 0; j < 11; j++)
3825 {
3826 if (dir->DIR_Name[j] != string[j])
3827 k = 1;
3828 }
3829 if (k == 0)
3830 {
3831 FSerrno = CE_FILENAME_EXISTS;
3832 return -1;
3833 }
3834 else
3835 k = 0;
3836  
3837 nextClusterIsLast = FALSE;
3838 while (1)
3839 {
3840 // Look through the entries until we get to the end
3841 // to make sure the name isn't taken
3842 dir = Cache_File_Entry (fo, &fHandle, FALSE);
3843 if (dir == NULL)
3844 {
3845 if (nextClusterIsLast == TRUE)
3846 {
3847 break;
3848 }
3849 else
3850 {
3851 FSerrno = CE_BADCACHEREAD;
3852 return -1;
3853 }
3854 }
3855 if (dir->DIR_Name[0] == 0)
3856 break;
3857 for (j = 0; j < 11; j++)
3858 {
3859 if (dir->DIR_Name[j] != string[j])
3860 k = 1;
3861 }
3862 if (k == 0)
3863 {
3864 FSerrno = CE_FILENAME_EXISTS;
3865 return -1;
3866 }
3867 else
3868 k = 0;
3869 fHandle++;
3870 }
3871  
3872 fHandle = goodHandle;
3873 fo->dirccls = fo->dirclus;
3874  
3875 // Get the file entry
3876 dir = LoadDirAttrib(fo, &fHandle);
3877  
3878 if (dir == NULL)
3879 {
3880 FSerrno = CE_BADCACHEREAD;
3881 return -1;
3882 }
3883  
3884 for (j = 0; j < 11; j++)
3885 {
3886 dir->DIR_Name[j] = fo->name[j];
3887 }
3888  
3889 // just write the last entry in
3890 if(!Write_File_Entry(fo,&fHandle))
3891 {
3892 FSerrno = CE_WRITE_ERROR;
3893 return -1;
3894 }
3895 }
3896  
3897 return 0;
3898 }
3899  
3900 #endif // Allow writes
3901  
3902  
3903  
3904 /*********************************************************************
3905 Function:
3906 FSFILE * FSfopen (const char * fileName, const char *mode)
3907 Summary:
3908 Open a file
3909 Conditions:
3910 For read modes, file exists; FSInit performed
3911 Input:
3912 fileName - The name of the file to open
3913 mode -
3914 - WRITE - Create a new file or replace an existing file
3915 - READ - Read data from an existing file
3916 - APPEND - Append data to an existing file
3917 - WRITEPLUS - Create a new file or replace an existing file (reads also enabled)
3918 - READPLUS - Read data from an existing file (writes also enabled)
3919 - APPENDPLUS - Append data to an existing file (reads also enabled)
3920 Return Values:
3921 FSFILE * - The pointer to the file object
3922 NULL - The file could not be opened
3923 Side Effects:
3924 The FSerrno variable will be changed.
3925 Description:
3926 This function will open a file or directory. First, RAM in the
3927 dynamic heap or static array will be allocated to a new FSFILE object.
3928 Then, the specified file name will be formatted to ensure that it's
3929 in 8.3 format. Next, the FILEfind function will be used to search
3930 for the specified file name. If the name is found, one of three
3931 things will happen: if the file was opened in read mode, its file
3932 info will be loaded using the FILEopen function; if it was opened in
3933 write mode, it will be erased, and a new file will be constructed in
3934 its place; if it was opened in append mode, its file info will be
3935 loaded with FILEopen and the current location will be moved to the
3936 end of the file using the FSfseek function. If the file was not
3937 found by FILEfind, it will be created if the mode was specified as
3938 a write or append mode. In these cases, a pointer to the heap or
3939 static FSFILE object array will be returned. If the file was not
3940 found and the mode was specified as a read mode, the memory
3941 allocated to the file will be freed and the NULL pointer value
3942 will be returned.
3943 Remarks:
3944 None.
3945 *********************************************************************/
3946  
3947 FSFILE * FSfopen( const char * fileName, const char *mode )
3948 {
3949 FILEOBJ filePtr;
3950 #ifndef FS_DYNAMIC_MEM
3951 int fIndex;
3952 #endif
3953 BYTE ModeC;
3954 WORD fHandle;
3955 CETYPE final;
3956  
3957 #ifdef FS_DYNAMIC_MEM
3958 filePtr = (FILEOBJ) FS_malloc(sizeof(FSFILE));
3959 #else
3960  
3961 filePtr = NULL;
3962  
3963 //Pick available file structure
3964 for( fIndex = 0; fIndex < FS_MAX_FILES_OPEN; fIndex++ )
3965 {
3966 if( gFileSlotOpen[fIndex] ) //this slot is available
3967 {
3968 gFileSlotOpen[fIndex] = FALSE;
3969 filePtr = &gFileArray[fIndex];
3970 break;
3971 }
3972 }
3973  
3974 if( filePtr == NULL )
3975 {
3976 FSerrno = CE_TOO_MANY_FILES_OPEN;
3977 return NULL; //no file structure slot available
3978 }
3979 #endif
3980  
3981 //Format the source string.
3982 if( !FormatFileName(fileName, filePtr->name, 0) )
3983 {
3984 #ifdef FS_DYNAMIC_MEM
3985 FS_free( (unsigned char *)filePtr );
3986 #else
3987 gFileSlotOpen[fIndex] = TRUE; //put this slot back to the pool
3988 #endif
3989 FSerrno = CE_INVALID_FILENAME;
3990 return NULL; //bad filename
3991 }
3992  
3993 //Read the mode character
3994 ModeC = mode[0];
3995  
3996 filePtr->dsk = &gDiskData;
3997 filePtr->cluster = 0;
3998 filePtr->ccls = 0;
3999 filePtr->entry = 0;
4000 filePtr->attributes = ATTR_ARCHIVE;
4001  
4002 // start at the current directory
4003 #ifdef ALLOW_DIRS
4004 filePtr->dirclus = cwdptr->dirclus;
4005 filePtr->dirccls = cwdptr->dirccls;
4006 #else
4007 filePtr->dirclus = FatRootDirClusterValue;
4008 filePtr->dirccls = FatRootDirClusterValue;
4009 #endif
4010  
4011 // copy file object over
4012 FileObjectCopy(&gFileTemp, filePtr);
4013  
4014 // See if the file is found
4015 if(FILEfind (filePtr, &gFileTemp, LOOK_FOR_MATCHING_ENTRY, 0) == CE_GOOD)
4016 {
4017 // File is Found
4018 switch(ModeC)
4019 {
4020 #ifdef ALLOW_WRITES
4021 case 'w':
4022 case 'W':
4023 {
4024 // File exists, we want to create a new one, remove it first
4025 fHandle = filePtr->entry;
4026 final = FILEerase(filePtr, &fHandle, TRUE);
4027  
4028 if (final == CE_GOOD)
4029 {
4030 // now create a new one
4031 final = CreateFileEntry (filePtr, &fHandle, 0);
4032  
4033 if (final == CE_GOOD)
4034 {
4035 final = FILEopen (filePtr, &fHandle, 'w');
4036  
4037 if (filePtr->attributes & ATTR_DIRECTORY)
4038 {
4039 FSerrno = CE_INVALID_ARGUMENT;
4040 final = 0xFF;
4041 }
4042  
4043 if (final == CE_GOOD)
4044 {
4045 final = FSfseek (filePtr, 0, SEEK_END);
4046 if (mode[1] == '+')
4047 filePtr->flags.read = 1;
4048 }
4049 }
4050 }
4051 break;
4052 }
4053  
4054 case 'A':
4055 case 'a':
4056 {
4057 if(filePtr->size != 0)
4058 {
4059 fHandle = filePtr->entry;
4060  
4061 final = FILEopen (filePtr, &fHandle, 'w');
4062  
4063 if (filePtr->attributes & ATTR_DIRECTORY)
4064 {
4065 FSerrno = CE_INVALID_ARGUMENT;
4066 final = 0xFF;
4067 }
4068  
4069 if (final == CE_GOOD)
4070 {
4071 final = FSfseek (filePtr, 0, SEEK_END);
4072 if (final != CE_GOOD)
4073 FSerrno = CE_SEEK_ERROR;
4074 else
4075 ReadFAT (&gDiskData, filePtr->ccls);
4076 if (mode[1] == '+')
4077 filePtr->flags.read = 1;
4078 }
4079 }
4080 else
4081 {
4082 fHandle = filePtr->entry;
4083 final = FILEerase(filePtr, &fHandle, TRUE);
4084  
4085 if (final == CE_GOOD)
4086 {
4087 // now create a new one
4088 final = CreateFileEntry (filePtr, &fHandle, 0);
4089  
4090 if (final == CE_GOOD)
4091 {
4092 final = FILEopen (filePtr, &fHandle, 'w');
4093  
4094 if (filePtr->attributes & ATTR_DIRECTORY)
4095 {
4096 FSerrno = CE_INVALID_ARGUMENT;
4097 final = 0xFF;
4098 }
4099  
4100 if (final == CE_GOOD)
4101 {
4102 final = FSfseek (filePtr, 0, SEEK_END);
4103 if (final != CE_GOOD)
4104 FSerrno = CE_SEEK_ERROR;
4105 if (mode[1] == '+')
4106 filePtr->flags.read = 1;
4107 }
4108 }
4109 }
4110 }
4111 break;
4112 }
4113 #endif
4114 case 'R':
4115 case 'r':
4116 {
4117 fHandle = filePtr->entry;
4118  
4119 final = FILEopen (filePtr, &fHandle, 'r');
4120 #ifdef ALLOW_WRITES
4121 if ((mode[1] == '+') && !(filePtr->attributes & ATTR_DIRECTORY))
4122 filePtr->flags.write = 1;
4123 #endif
4124 break;
4125 }
4126  
4127 default:
4128 FSerrno = CE_INVALID_ARGUMENT;
4129 final = 0xFF;; //indicate error condition
4130 break;
4131 }
4132 }
4133 else
4134 {
4135 #ifdef ALLOW_WRITES
4136 // the file was not found, reset to the default asked
4137 FileObjectCopy(filePtr, &gFileTemp);
4138  
4139 // File is not Found
4140 if(ModeC == 'w' || ModeC == 'W' || ModeC == 'a' || ModeC == 'A')
4141 {
4142 // use the user requested name
4143 fHandle = 0;
4144 final = CreateFileEntry (filePtr, &fHandle, 0);
4145  
4146 if (final == CE_GOOD)
4147 {
4148 final = FILEopen (filePtr, &fHandle, 'w');
4149 if (filePtr->attributes & ATTR_DIRECTORY)
4150 {
4151 FSerrno = CE_INVALID_ARGUMENT;
4152 final = 0xFF;
4153 }
4154  
4155 if (final == CE_GOOD)
4156 {
4157 final = FSfseek (filePtr, 0, SEEK_END);
4158 if (final != CE_GOOD)
4159 FSerrno = CE_SEEK_ERROR;
4160 if (mode[1] == '+')
4161 filePtr->flags.read = 1;
4162 }
4163 }
4164 }
4165 else
4166 #endif
4167 final = CE_FILE_NOT_FOUND;
4168 }
4169  
4170 if (MDD_WriteProtectState())
4171 {
4172 filePtr->flags.write = 0;;
4173 }
4174  
4175 #ifdef FS_DYNAMIC_MEM
4176 if( final != CE_GOOD )
4177 {
4178 FS_free( (unsigned char *)filePtr );
4179 filePtr = NULL;
4180 }
4181 #else
4182 if( final != CE_GOOD )
4183 {
4184 gFileSlotOpen[fIndex] = TRUE; //put this slot back to the pool
4185 filePtr = NULL;
4186 }
4187 #endif
4188 else
4189 {
4190 FSerrno = CE_GOOD;
4191 }
4192  
4193 return filePtr;
4194 }
4195  
4196 /*******************************************************************
4197 Function:
4198 long FSftell (FSFILE * fo)
4199 Summary:
4200 Determine the current location in a file
4201 Conditions:
4202 File opened
4203 Input:
4204 fo - Pointer to file structure
4205 Return: Current location in the file
4206 Side Effects:
4207 The FSerrno variable will be changed
4208 Description:
4209 The FSftell function will return the current position in the
4210 file pointed to by 'fo' by returning the 'seek' variable in the
4211 FSFILE object, which is used to keep track of the absolute
4212 location of the current position in the file.
4213 Remarks:
4214 None
4215 *******************************************************************/
4216  
4217 long FSftell (FSFILE * fo)
4218 {
4219 FSerrno = CE_GOOD;
4220 return (fo->seek);
4221 }
4222  
4223  
4224 #ifdef ALLOW_WRITES
4225  
4226 /*********************************************************************
4227 Function:
4228 int FSremove (const char * fileName)
4229 Summary:
4230 Delete a file
4231 Conditions:
4232 File not opened, file exists
4233 Input:
4234 fileName - Name of the file to erase
4235 Return Values:
4236  
4237 EOF - File was not removed
4238 Side Effects:
4239 The FSerrno variable will be changed.
4240 Description:
4241 The FSremove function will attempt to find the specified file with
4242 the FILEfind function. If the file is found, it will be erased
4243 using the FILEerase function.
4244 Remarks:
4245 None
4246 **********************************************************************/
4247  
4248 int FSremove (const char * fileName)
4249 {
4250 FILEOBJ fo = &tempCWDobj;
4251 CETYPE result;
4252  
4253 FSerrno = CE_GOOD;
4254  
4255 if (MDD_WriteProtectState())
4256 {
4257 FSerrno = CE_WRITE_PROTECTED;
4258 return (-1);
4259 }
4260  
4261 //Format the source string
4262 if( !FormatFileName(fileName, fo->name, 0) )
4263 {
4264 FSerrno = CE_INVALID_FILENAME;
4265 return -1;
4266 }
4267  
4268 fo->dsk = &gDiskData;
4269 fo->cluster = 0;
4270 fo->ccls = 0;
4271 fo->entry = 0;
4272 fo->attributes = ATTR_ARCHIVE;
4273  
4274 #ifndef ALLOW_DIRS
4275 // start at the root directory
4276 fo->dirclus = FatRootDirClusterValue;
4277 fo->dirccls = FatRootDirClusterValue;
4278 #else
4279 fo->dirclus = cwdptr->dirclus;
4280 fo->dirccls = cwdptr->dirccls;
4281 #endif
4282  
4283 // copy file object over
4284 FileObjectCopy(&gFileTemp, fo);
4285  
4286 // See if the file is found
4287 result = FILEfind (fo, &gFileTemp, LOOK_FOR_MATCHING_ENTRY, 0);
4288  
4289 if (result != CE_GOOD)
4290 {
4291 FSerrno = CE_FILE_NOT_FOUND;
4292 return -1;
4293 }
4294  
4295 if (fo->attributes & ATTR_DIRECTORY)
4296 {
4297 FSerrno = CE_DELETE_DIR;
4298 return -1;
4299 }
4300  
4301 result = FILEerase(fo, &fo->entry, TRUE);
4302 if( result == CE_GOOD )
4303 return 0;
4304 else
4305 {
4306 FSerrno = CE_ERASE_FAIL;
4307 return -1;
4308 }
4309 }
4310 #endif
4311  
4312 /*********************************************************
4313 Function:
4314 void FSrewind (FSFILE * fo)
4315 Summary:
4316 Set the current position in a file to the beginning
4317 Conditions:
4318 File opened.
4319 Input:
4320 fo - Pointer to file structure
4321 Return Values:
4322 None
4323 Side Effects:
4324 None.
4325 Description:
4326 The FSrewind funciton will reset the position of the
4327 specified file to the beginning of the file. This
4328 functionality is faster than using FSfseek to reset
4329 the position in the file.
4330 Remarks:
4331 None.
4332 *********************************************************/
4333  
4334 void FSrewind (FSFILE * fo)
4335 {
4336 #ifdef ALLOW_WRITES
4337 if (gNeedDataWrite)
4338 flushData();
4339 #endif
4340 fo->seek = 0;
4341 fo->pos = 0;
4342 fo->sec = 0;
4343 fo->ccls = fo->cluster;
4344 gBufferOwner = NULL;
4345 return;
4346 }
4347  
4348 /**************************************************************************
4349 Function:
4350 int FSerror (void)
4351 Summary:
4352 Return an error code for the last function call
4353 Conditions:
4354 The return value depends on the last function called.
4355 Input:
4356 None
4357 Side Effects:
4358 None.
4359 Return Values:
4360 FSInit -
4361 - CE_GOOD – No Error
4362 - CE_INIT_ERROR – The physical media could not be initialized
4363 - CE_BAD_SECTOR_READ – The MBR or the boot sector could not be
4364 read correctly
4365 - CE_BAD_PARITION – The MBR signature code was incorrect.
4366 - CE_NOT_FORMATTED – The boot sector signature code was incorrect or
4367 indicates an invalid number of bytes per sector.
4368 - CE_UNSUPPORTED_SECTOR_SIZE - The number of bytes per sector is unsupported
4369 - CE_CARDFAT32 – The physical media is FAT32 type (only an error
4370 when FAT32 support is disabled).
4371 - CE_UNSUPPORTED_FS – The device is formatted with an unsupported file
4372 system (not FAT12 or 16).
4373 FSfopen -
4374 - CE_GOOD – No Error
4375 - CE_NOT_INIT – The device has not been initialized.
4376 - CE_TOO_MANY_FILES_OPEN – The function could not allocate any
4377 additional file information to the array
4378 of FSFILE structures or the heap.
4379 - CE_INVALID_FILENAME – The file name argument was invalid.
4380 - CE_INVALID_ARGUMENT – The user attempted to open a directory in a
4381 write mode or specified an invalid mode argument.
4382 - CE_FILE_NOT_FOUND – The specified file (which was to be opened in read
4383 mode) does not exist on the device.
4384 - CE_BADCACHEREAD – A read from the device failed.
4385 - CE_ERASE_FAIL – The existing file could not be erased (when opening
4386 a file in WRITE mode).
4387 - CE_DIR_FULL – The directory is full.
4388 - CE_DISK_FULL– The data memory section is full.
4389 - CE_WRITE_ERROR – A write to the device failed.
4390 - CE_SEEK_ERROR – The current position in the file could not be set to
4391 the end (when the file was opened in APPEND mode).
4392 FSfclose -
4393 - CE_GOOD – No Error
4394 - CE_WRITE_ERROR – The existing data in the data buffer or the new file
4395 entry information could not be written to the device.
4396 - CE_BADCACHEREAD – The file entry information could not be cached
4397 FSfread -
4398 - CE_GOOD – No Error
4399 - CE_WRITEONLY – The file was opened in a write-only mode.
4400 - CE_WRITE_ERROR – The existing data in the data buffer could not be
4401 written to the device.
4402 - CE_BAD_SECTOR_READ – The data sector could not be read.
4403 - CE_EOF – The end of the file was reached.
4404 - CE_COULD_NOT_GET_CLUSTER – Additional clusters in the file could not be loaded.
4405 FSfwrite -
4406 - CE_GOOD – No Error
4407 - CE_READONLY – The file was opened in a read-only mode.
4408 - CE_WRITE_PROTECTED – The device write-protect check function indicated
4409 that the device has been write-protected.
4410 - CE_WRITE_ERROR – There was an error writing data to the device.
4411 - CE_BADCACHEREAD – The data sector to be modified could not be read from
4412 the device.
4413 - CE_DISK_FULL – All data clusters on the device are in use.
4414 FSfseek -
4415 - CE_GOOD – No Error
4416 - CE_WRITE_ERROR – The existing data in the data buffer could not be
4417 written to the device.
4418 - CE_INVALID_ARGUMENT – The specified offset exceeds the size of the file.
4419 - CE_BADCACHEREAD – The sector that contains the new current position
4420 could not be loaded.
4421 - CE_COULD_NOT_GET_CLUSTER – Additional clusters in the file could not be
4422 loaded/allocated.
4423 FSftell -
4424 - CE_GOOD – No Error
4425 FSattrib -
4426 - CE_GOOD – No Error
4427 - CE_INVALID_ARGUMENT – The attribute argument was invalid.
4428 - CE_BADCACHEREAD – The existing file entry information could not be
4429 loaded.
4430 - CE_WRITE_ERROR – The file entry information could not be written to
4431 the device.
4432 FSrename -
4433 - CE_GOOD – No Error
4434 - CE_FILENOTOPENED – A null file pointer was passed into the function.
4435 - CE_INVALID_FILENAME – The file name passed into the function was invalid.
4436 - CE_BADCACHEREAD – A read from the device failed.
4437 - CE_FILENAME_EXISTS – A file with the specified name already exists.
4438 - CE_WRITE_ERROR – The new file entry data could not be written to the
4439 device.
4440 FSfeof -
4441 - CE_GOOD – No Error
4442 FSformat -
4443 - CE_GOOD – No Error
4444 - CE_INIT_ERROR – The device could not be initialized.
4445 - CE_BADCACHEREAD – The master boot record or boot sector could not be
4446 loaded successfully.
4447 - CE_INVALID_ARGUMENT – The user selected to create their own boot sector on
4448 a device that has no master boot record, or the mode
4449 argument was invalid.
4450 - CE_WRITE_ERROR – The updated MBR/Boot sector could not be written to
4451 the device.
4452 - CE_BAD_PARTITION – The calculated number of sectors per clusters was
4453 invalid.
4454 - CE_NONSUPPORTED_SIZE – The card has too many sectors to be formatted as
4455 FAT12 or FAT16.
4456 FSremove -
4457 - CE_GOOD – No Error
4458 - CE_WRITE_PROTECTED – The device write-protect check function indicated
4459 that the device has been write-protected.
4460 - CE_INVALID_FILENAME – The specified filename was invalid.
4461 - CE_FILE_NOT_FOUND – The specified file could not be found.
4462 - CE_ERASE_FAIL – The file could not be erased.
4463 FSchdir -
4464 - CE_GOOD – No Error
4465 - CE_INVALID_ARGUMENT – The path string was mis-formed or the user tried to
4466 change to a non-directory file.
4467 - CE_BADCACHEREAD – A directory entry could not be cached.
4468 - CE_DIR_NOT_FOUND – Could not find a directory in the path.
4469 FSgetcwd -
4470 - CE_GOOD – No Error
4471 - CE_INVALID_ARGUMENT – The user passed a 0-length buffer into the function.
4472 - CE_BADCACHEREAD – A directory entry could not be cached.
4473 - CE_BAD_SECTOR_READ – The function could not determine a previous directory
4474 of the current working directory.
4475 FSmkdir -
4476 - CE_GOOD – No Error
4477 - CE_WRITE_PROTECTED – The device write-protect check function indicated
4478 that the device has been write-protected.
4479 - CE_INVALID_ARGUMENT – The path string was mis-formed.
4480 - CE_BADCACHEREAD – Could not successfully change to a recently created
4481 directory to store its dir entry information, or
4482 could not cache directory entry information.
4483 - CE_INVALID_FILENAME – One or more of the directory names has an invalid
4484 format.
4485 - CE_WRITE_ERROR – The existing data in the data buffer could not be
4486 written to the device or the dot/dotdot entries could
4487 not be written to a newly created directory.
4488 - CE_DIR_FULL – There are no available dir entries in the CWD.
4489 - CE_DISK_FULL – There are no available clusters in the data region of
4490 the device.
4491 FSrmdir -
4492 - CE_GOOD – No Error
4493 - CE_DIR_NOT_FOUND – The directory specified could not be found or the
4494 function could not change to a subdirectory within
4495 the directory to be deleted (when recursive delete is
4496 enabled).
4497 - CE_INVALID_ARGUMENT – The user tried to remove the CWD or root directory.
4498 - CE_BADCACHEREAD – A directory entry could not be cached.
4499 - CE_DIR_NOT_EMPTY – The directory to be deleted was not empty and
4500 recursive subdirectory removal was disabled.
4501 - CE_ERASE_FAIL – The directory or one of the directories or files
4502 within it could not be deleted.
4503 - CE_BAD_SECTOR_READ – The function could not determine a previous directory
4504 of the CWD.
4505 SetClockVars -
4506 - CE_GOOD – No Error
4507 - CE_INVALID_ARGUMENT – The time values passed into the function were
4508 invalid.
4509 FindFirst -
4510 - CE_GOOD – No Error
4511 - CE_INVALID_FILENAME – The specified filename was invalid.
4512 - CE_FILE_NOT_FOUND – No file matching the specified criteria was found.
4513 - CE_BADCACHEREAD – The file information for the file that was found
4514 could not be cached.
4515 FindNext -
4516 - CE_GOOD – No Error
4517 - CE_NOT_INIT – The SearchRec object was not initialized by a call to
4518 FindFirst.
4519 - CE_INVALID_ARGUMENT – The SearchRec object was initialized in a different
4520 directory from the CWD.
4521 - CE_INVALID_FILENAME – The filename is invalid.
4522 - CE_FILE_NOT_FOUND – No file matching the specified criteria was found.
4523 FSfprintf -
4524 - CE_GOOD – No Error
4525 - CE_WRITE_ERROR – Characters could not be written to the file.
4526 Description:
4527 The FSerror function will return the FSerrno variable. This global
4528 variable will have been set to an error value during the last call of a
4529 library function.
4530 Remarks:
4531 None
4532 **************************************************************************/
4533  
4534 int FSerror (void)
4535 {
4536 return FSerrno;
4537 }
4538  
4539  
4540 /**************************************************************
4541 Function:
4542 void FileObjectCopy(FILEOBJ foDest,FILEOBJ foSource)
4543 Summary:
4544 Copy a file object
4545 Conditions:
4546 This function should not be called by the user.
4547 Input:
4548 foDest - The destination
4549 foSource - the source
4550 Return:
4551 None
4552 Side Effects:
4553 None
4554 Description:
4555 The FileObjectCopy function will make an exacy copy of
4556 a specified FSFILE object.
4557 Remarks:
4558 None
4559 **************************************************************/
4560  
4561 void FileObjectCopy(FILEOBJ foDest,FILEOBJ foSource)
4562 {
4563 BYTE size;
4564 BYTE *dest;
4565 BYTE *source;
4566 BYTE Index;
4567  
4568 dest = (BYTE *)foDest;
4569 source = (BYTE *)foSource;
4570  
4571 size = sizeof(FSFILE);
4572  
4573 for(Index=0;Index< size; Index++)
4574 {
4575 dest[Index] = source[Index];
4576 }
4577 }
4578  
4579 /*************************************************************************
4580 Function:
4581 CETYPE FILECreateHeadCluster( FILEOBJ fo, DWORD *cluster)
4582 Summary:
4583 Create the first cluster of a file
4584 Conditions:
4585 This function should not be called by the user.
4586 Input:
4587 fo - Pointer to file structure
4588 cluster - Cluster location
4589 Return Values:
4590 CE_GOOD - File closed successfully
4591 CE_WRITE_ERROR - Could not write to the sector
4592 CE_DISK_FULL - All clusters in partition are taken
4593 Side Effects:
4594 None
4595 Description:
4596 The FILECreateHeadCluster function will create the first cluster
4597 of a file. First, it will find an empty cluster with the
4598 FATfindEmptyCluster function and mark it as the last cluster in the
4599 file. It will then erase the cluster using the EraseCluster function.
4600 Remarks:
4601 None.
4602 *************************************************************************/
4603  
4604 #ifdef ALLOW_WRITES
4605 CETYPE FILECreateHeadCluster( FILEOBJ fo, DWORD *cluster)
4606 {
4607 DISK * disk;
4608 CETYPE error = CE_GOOD;
4609  
4610 disk = fo->dsk;
4611  
4612 // find the next empty cluster
4613 *cluster = FATfindEmptyCluster(fo);
4614  
4615 if(*cluster == 0) // "0" is just an indication as Disk full in the fn "FATfindEmptyCluster()"
4616 {
4617 error = CE_DISK_FULL;
4618 }
4619 else
4620 {
4621 // mark the cluster as taken, and last in chain
4622 if(disk->type == FAT12)
4623 {
4624 if(WriteFAT( disk, *cluster, LAST_CLUSTER_FAT12, FALSE) == CLUSTER_FAIL_FAT16)
4625 {
4626 error = CE_WRITE_ERROR;
4627 }
4628 }
4629 else if(disk->type == FAT16)
4630 {
4631 if(WriteFAT( disk, *cluster, LAST_CLUSTER_FAT16, FALSE) == CLUSTER_FAIL_FAT16)
4632 {
4633 error = CE_WRITE_ERROR;
4634 }
4635 }
4636  
4637 #ifdef SUPPORT_FAT32 // If FAT32 supported.
4638 else
4639 {
4640 if(WriteFAT( disk, *cluster, LAST_CLUSTER_FAT32, FALSE) == CLUSTER_FAIL_FAT32)
4641 {
4642 error = CE_WRITE_ERROR;
4643 }
4644 }
4645 #endif
4646  
4647 // lets erase this cluster
4648 if(error == CE_GOOD)
4649 {
4650 error = EraseCluster(disk,*cluster);
4651 }
4652 }
4653  
4654 return(error);
4655 } // allocate head cluster
4656 #endif
4657  
4658 /*************************************************************************
4659 Function:
4660 BYTE EraseCluster(DISK *disk, DWORD cluster)
4661 Summary:
4662 Erase a cluster
4663 Conditions:
4664 This function should not be called by the user.
4665 Input:
4666 dsk - Disk structure
4667 cluster - Cluster to be erased
4668 Return Values:
4669 CE_GOOD - File closed successfully
4670 CE_WRITE_ERROR - Could not write to the sector
4671 Side Effects:
4672 None
4673 Description:
4674 The EraseCluster function will write a 0 value into every byte of
4675 the specified cluster.
4676 Remarks:
4677 None.
4678 *************************************************************************/
4679  
4680 #ifdef ALLOW_WRITES
4681 BYTE EraseCluster(DISK *disk, DWORD cluster)
4682 {
4683 BYTE index;
4684 DWORD SectorAddress;
4685 BYTE error = CE_GOOD;
4686  
4687 SectorAddress = Cluster2Sector(disk,cluster);
4688 if (gNeedDataWrite)
4689 if (flushData())
4690 return CE_WRITE_ERROR;
4691  
4692 gBufferOwner = NULL;
4693  
4694 if (gBufferZeroed == FALSE)
4695 {
4696 // clear out the memory first
4697 memset(disk->buffer, 0x00, MEDIA_SECTOR_SIZE);
4698 gBufferZeroed = TRUE;
4699 }
4700  
4701 // Now clear them out
4702 for(index = 0; index < disk->SecPerClus && error == CE_GOOD; index++)
4703 {
4704 if (MDD_SectorWrite( SectorAddress++, disk->buffer, FALSE) != TRUE)
4705 error = CE_WRITE_ERROR;
4706 }
4707  
4708 return(error);
4709 }
4710 #endif
4711  
4712  
4713 #if defined (__C30__) || defined (__PIC32MX__)
4714  
4715 /***************************************************
4716 Function:
4717 BYTE ReadByte(BYTE * pBuffer, WORD index)
4718 Summary:
4719 Read a byte from a buffer
4720 Conditions:
4721 This function should not be called by the user.
4722 Input:
4723 pBuffer - pointer to a buffer to read from
4724 index - index in the buffer to read to
4725 Return:
4726 BYTE - the byte read
4727 Side Effects:
4728 None
4729 Description:
4730 Reads a byte from a buffer
4731 Remarks:
4732 None.
4733 ***************************************************/
4734  
4735 BYTE ReadByte( BYTE* pBuffer, WORD index )
4736 {
4737 return( pBuffer[index] );
4738 }
4739  
4740  
4741 /***************************************************
4742 Function:
4743 BYTE ReadWord(BYTE * pBuffer, WORD index)
4744 Summary:
4745 Read a 16-bit word from a buffer
4746 Conditions:
4747 This function should not be called by the user.
4748 Input:
4749 pBuffer - pointer to a buffer to read from
4750 index - index in the buffer to read to
4751 Return:
4752 WORD - the word read
4753 Side Effects:
4754 None
4755 Description:
4756 Reads a 16-bit word from a buffer
4757 Remarks:
4758 None.
4759 ***************************************************/
4760  
4761 WORD ReadWord( BYTE* pBuffer, WORD index )
4762 {
4763 BYTE loByte, hiByte;
4764 WORD res;
4765  
4766 loByte = pBuffer[index];
4767 hiByte = pBuffer[index+1];
4768 res = hiByte;
4769 res *= 0x100;
4770 res |= loByte;
4771 return( res );
4772 }
4773  
4774  
4775 /****************************************************
4776 Function:
4777 BYTE ReadDWord(BYTE * pBuffer, WORD index)
4778 Summary:
4779 Read a 32-bit double word from a buffer
4780 Conditions:
4781 This function should not be called by the user.
4782 Input:
4783 pBuffer - pointer to a buffer to read from
4784 index - index in the buffer to read to
4785 Return:
4786 DWORD - the double word read
4787 Side Effects:
4788 None
4789 Description:
4790 Reads a 32-bit double word from a buffer
4791 Remarks:
4792 None.
4793 ****************************************************/
4794  
4795 DWORD ReadDWord( BYTE* pBuffer, WORD index )
4796 {
4797 WORD loWord, hiWord;
4798 DWORD result;
4799  
4800 loWord = ReadWord( pBuffer, index );
4801 hiWord = ReadWord( pBuffer, index+2 );
4802  
4803 result = hiWord;
4804 result *= 0x10000;
4805 result |= loWord;
4806 return result;
4807 }
4808  
4809 #endif
4810  
4811  
4812  
4813 /****************************************************
4814 Function:
4815 DWORD Cluster2Sector(DISK * dsk, DWORD cluster)
4816 Summary:
4817 Convert a cluster number to the corresponding sector
4818 Conditions:
4819 This function should not be called by the user.
4820 Input:
4821 disk - Disk structure
4822 cluster - Cluster to be converted
4823 Return:
4824 sector - Sector that corresponds to given cluster
4825 Side Effects:
4826 None
4827 Description:
4828 The Cluster2Sector function will calculate the
4829 sector number that corresponds to the first sector
4830 of the cluster whose value was passed into the
4831 function.
4832 Remarks:
4833 None.
4834 ****************************************************/
4835  
4836 DWORD Cluster2Sector(DISK * dsk, DWORD cluster)
4837 {
4838 DWORD sector;
4839  
4840 /* Rt: Settings based on FAT type */
4841 switch (dsk->type)
4842 {
4843 #ifdef SUPPORT_FAT32 // If FAT32 supported.
4844 case FAT32:
4845 /* In FAT32, there is no separate ROOT region. It is as well stored in DATA region */
4846 sector = (((DWORD)cluster-2) * dsk->SecPerClus) + dsk->data;
4847 break;
4848 #endif
4849 case FAT12:
4850 case FAT16:
4851 default:
4852 // The root dir takes up cluster 0 and 1
4853 if(cluster == 0 ||cluster == 1)
4854 sector = dsk->root + cluster;
4855 else
4856 sector = (((DWORD)cluster-2) * dsk->SecPerClus) + dsk->data;
4857 break;
4858 }
4859  
4860 return(sector);
4861  
4862 }
4863  
4864  
4865 /***************************************************************************
4866 Function:
4867 int FSattrib (FSFILE * file, unsigned char attributes)
4868 Summary:
4869 Change the attributes of a file
4870 Conditions:
4871 File opened
4872 Input:
4873 file - Pointer to file structure
4874 attributes - The attributes to set for the file
4875 - Attribute - Value - Indications
4876 - ATTR_READ_ONLY - 0x01 - The read-only attribute
4877 - ATTR_HIDDEN - 0x02 - The hidden attribute
4878 - ATTR_SYSTEM - 0x04 - The system attribute
4879 - ATTR_ARCHIVE - 0x20 - The archive attribute
4880 Return Values:
4881  
4882 -1 - Attribute change was unsuccessful
4883 Side Effects:
4884 The FSerrno variable will be changed.
4885 Description:
4886 The FSattrib funciton will set the attributes of the specified file
4887 to the attributes passed in by the user. This function will load the
4888 file entry, replace the attributes with the ones specified, and write
4889 the attributes back. If the specified file is a directory, the
4890 directory attribute will be preserved.
4891 Remarks:
4892 None
4893 ***************************************************************************/
4894  
4895 #ifdef ALLOW_WRITES
4896 int FSattrib (FSFILE * file, unsigned char attributes)
4897 {
4898 WORD fHandle;
4899 DIRENTRY dir;
4900  
4901 FSerrno = CE_GOOD;
4902  
4903 // Check for valid attributes
4904 if ((attributes & ~0x27) != 0)
4905 {
4906 FSerrno = CE_INVALID_ARGUMENT;
4907 return -1;
4908 }
4909  
4910 fHandle = file->entry;
4911  
4912 file->dirccls = file->dirclus;
4913  
4914 // Get the file entry
4915 dir = LoadDirAttrib(file, &fHandle);
4916  
4917 if (dir == NULL)
4918 {
4919 FSerrno = CE_BADCACHEREAD;
4920 return -1;
4921 }
4922  
4923 // Ensure that we aren't trying to change the
4924 // attributes of a volume entry
4925 if (dir->DIR_Attr & ATTR_VOLUME)
4926 {
4927 FSerrno = CE_INVALID_ARGUMENT;
4928 return -1;
4929 }
4930  
4931 // Don't remove the directory attribute from DIR files
4932 if (file->attributes & ATTR_DIRECTORY)
4933 file->attributes = attributes | ATTR_DIRECTORY;
4934 else
4935 file->attributes = attributes;
4936  
4937 // just write the last entry in
4938 if(!Write_File_Entry(file,&fHandle))
4939 {
4940 FSerrno = CE_WRITE_ERROR;
4941 return -1;
4942 }
4943  
4944 return 0;
4945 }
4946 #endif
4947  
4948  
4949 /*********************************************************************************
4950 Function:
4951 size_t FSfwrite(const void *ptr, size_t size, size_t n, FSFILE *stream)
4952 Summary:
4953 Write data to a file
4954 Conditions:
4955 File opened in WRITE, APPEND, WRITE+, APPEND+, READ+ mode
4956 Input:
4957 ptr - Pointer to source buffer
4958 size - Size of units in bytes
4959 n - Number of units to transfer
4960 stream - Pointer to file structure
4961 Return:
4962 size_t - number of units written
4963 Side Effects:
4964 The FSerrno variable will be changed.
4965 Description:
4966 The FSfwrite function will write data to a file. First, the sector that
4967 corresponds to the current position in the file will be loaded (if it hasn't
4968 already been cached in the global data buffer). Data will then be written to
4969 the device from the specified buffer until the specified amount has been written.
4970 If the end of a cluster is reached, the next cluster will be loaded, unless
4971 the end-of-file flag for the specified file has been set. If it has, a new
4972 cluster will be allocated to the file. Finally, the new position and filezize
4973 will be stored in the FSFILE object. The parameters 'size' and 'n' indicate how
4974 much data to write. 'Size' refers to the size of one object to write (in bytes),
4975 and 'n' will refer to the number of these objects to write. The value returned
4976 will be equal to 'n' unless an error occured.
4977 Remarks:
4978 None.
4979 *********************************************************************************/
4980  
4981 #ifdef ALLOW_WRITES
4982 size_t FSfwrite(const void *ptr, size_t size, size_t n, FSFILE *stream)
4983 {
4984 DWORD count = size * n;
4985 BYTE * src = (BYTE *) ptr;
4986 DISK * dsk; // pointer to disk structure
4987 CETYPE error = CE_GOOD;
4988 WORD pos;
4989 DWORD l; // absolute lba of sector to load
4990 DWORD seek, filesize;
4991 WORD writeCount = 0;
4992  
4993 // see if the file was opened in a write mode
4994 if(!(stream->flags.write))
4995 {
4996 FSerrno = CE_READONLY;
4997 error = CE_WRITE_ERROR;
4998 return 0;
4999 }
5000  
5001 if (count == 0)
5002 return 0;
5003  
5004 if (MDD_WriteProtectState())
5005 {
5006 FSerrno = CE_WRITE_PROTECTED;
5007 error = CE_WRITE_PROTECTED;
5008 return 0;
5009 }
5010  
5011 gBufferZeroed = FALSE;
5012 dsk = stream->dsk;
5013 // get the stated position
5014 pos = stream->pos;
5015 seek = stream->seek;
5016 l = Cluster2Sector(dsk,stream->ccls);
5017 l += (WORD)stream->sec; // add the sector number to it
5018  
5019 // Check if the current stream was the last one to use the
5020 // buffer. If not, check if we need to write data from the
5021 // old stream
5022 if (gBufferOwner != stream)
5023 {
5024 if (gNeedDataWrite)
5025 {
5026 if (flushData())
5027 {
5028 FSerrno = CE_WRITE_ERROR;
5029 return 0;
5030 }
5031 }
5032 gBufferOwner = stream;
5033 }
5034 if (gLastDataSectorRead != l)
5035 {
5036 if (gNeedDataWrite)
5037 {
5038 if (flushData())
5039 {
5040 FSerrno = CE_WRITE_ERROR;
5041 return 0;
5042 }
5043 }
5044  
5045 gBufferZeroed = FALSE;
5046 if(!MDD_SectorRead( l, dsk->buffer) )
5047 {
5048 FSerrno = CE_BADCACHEREAD;
5049 error = CE_BAD_SECTOR_READ;
5050 }
5051 gLastDataSectorRead = l;
5052 }
5053 // exit loop if EOF reached
5054 filesize = stream->size;
5055  
5056 // Loop while writing bytes
5057 while (error == CE_GOOD && count > 0)
5058 {
5059 if( seek == filesize )
5060 stream->flags.FileWriteEOF = TRUE;
5061  
5062 // load a new sector if necessary, multiples of sector
5063 if (pos == dsk->sectorSize)
5064 {
5065 BYTE needRead = TRUE;
5066  
5067 if (gNeedDataWrite)
5068 if (flushData())
5069 {
5070 FSerrno = CE_WRITE_ERROR;
5071 return 0;
5072 }
5073  
5074 // reset position
5075 pos = 0;
5076  
5077 // point to the next sector
5078 stream->sec++;
5079  
5080 // get a new cluster if necessary
5081 if (stream->sec == dsk->SecPerClus)
5082 {
5083 stream->sec = 0;
5084  
5085 if(stream->flags.FileWriteEOF)
5086 {
5087 error = FILEallocate_new_cluster(stream, 0); // add new cluster to the file
5088 needRead = FALSE;
5089 }
5090 else
5091 error = FILEget_next_cluster( stream, 1);
5092 }
5093  
5094 if (error == CE_DISK_FULL)
5095 {
5096 FSerrno = CE_DISK_FULL;
5097 return 0;
5098 }
5099  
5100 if(error == CE_GOOD)
5101 {
5102 l = Cluster2Sector(dsk,stream->ccls);
5103 l += (WORD)stream->sec; // add the sector number to it
5104 gBufferOwner = stream;
5105 // If we just allocated a new cluster, then the cluster will
5106 // contain garbage data, so it doesn't matter what we write to it
5107 // Whatever is in the buffer will work fine
5108 if (needRead)
5109 {
5110 if( !MDD_SectorRead( l, dsk->buffer) )
5111 {
5112 FSerrno = CE_BADCACHEREAD;
5113 error = CE_BAD_SECTOR_READ;
5114 gLastDataSectorRead = 0xFFFFFFFF;
5115 return 0;
5116 }
5117 else
5118 {
5119 gLastDataSectorRead = l;
5120 }
5121 }
5122 else
5123 gLastDataSectorRead = l;
5124 }
5125 } // load new sector
5126  
5127 if(error == CE_GOOD)
5128 {
5129 // Write one byte at a time
5130 RAMwrite(dsk->buffer, pos++, *(char *)src);
5131 src = src + 1; // compiler bug
5132 seek++;
5133 count--;
5134 writeCount++;
5135 // now increment the size of the part
5136 if(stream->flags.FileWriteEOF)
5137 filesize++;
5138 gNeedDataWrite = TRUE;
5139 }
5140 } // while count
5141  
5142 // save off the positon
5143 stream->pos = pos;
5144  
5145 // save off the seek
5146 stream->seek = seek;
5147  
5148 // now the new size
5149 stream->size = filesize;
5150  
5151 return(writeCount / size);
5152 } // fwrite
5153 #endif
5154  
5155  
5156 /**********************************************************
5157 Function:
5158 BYTE flushData (void)
5159 Summary:
5160 Flush unwritten data to a file
5161 Conditions:
5162 File opened in a write mode, data needs to be written
5163 Return Values:
5164 CE_GOOD - Data was updated successfully
5165 CE_WRITE_ERROR - Data could not be updated
5166 Side Effects:
5167 None
5168 Description:
5169 The flushData function is called when it is necessary to
5170 read new data into the global data buffer and the
5171 gNeedDataWrite variable indicates that there is data
5172 in the buffer that hasn't been written to the device.
5173 The flushData function will write the data from the
5174 buffer into the current cluster of the FSFILE object
5175 that is stored in the gBufferOwner global variable.
5176 Remarks:
5177 None
5178 **********************************************************/
5179  
5180 #ifdef ALLOW_WRITES
5181 BYTE flushData (void)
5182 {
5183 DWORD l;
5184 DISK * dsk;
5185  
5186 // This will either be the pointer to the last file, or the handle
5187 FILEOBJ stream = gBufferOwner;
5188  
5189 dsk = stream->dsk;
5190  
5191 // figure out the lba
5192 l = Cluster2Sector(dsk,stream->ccls);
5193 l += (WORD)stream->sec; // add the sector number to it
5194  
5195 if(!MDD_SectorWrite( l, dsk->buffer, FALSE))
5196 {
5197 return CE_WRITE_ERROR;
5198 }
5199  
5200 gNeedDataWrite = FALSE;
5201  
5202 return CE_GOOD;
5203 }
5204 #endif
5205  
5206 /****************************************************
5207 Function:
5208 int FSfeof( FSFILE * stream )
5209 Summary:
5210 Indicate whether the current file position is at the end
5211 Conditions:
5212 File is open in a read mode
5213 Input:
5214 stream - Pointer to the target file
5215 Return Values:
5216 Non-Zero - EOF reached
5217  
5218 Side Effects:
5219 The FSerrno variable will be changed.
5220 Description:
5221 The FSfeof function will indicate that the end-of-
5222 file has been reached for the specified file by
5223 comparing the absolute location in the file to the
5224 size of the file.
5225 Remarks:
5226 None.
5227 ****************************************************/
5228  
5229 int FSfeof( FSFILE * stream )
5230 {
5231 FSerrno = CE_GOOD;
5232 return( stream->seek == stream->size );
5233 }
5234  
5235  
5236 /**************************************************************************
5237 Function:
5238 size_t FSfread(void *ptr, size_t size, size_t n, FSFILE *stream)
5239 Summary:
5240 Read data from a file
5241 Conditions:
5242 File is opened in a read mode
5243 Input:
5244 ptr - Destination buffer for read bytes
5245 size - Size of units in bytes
5246 n - Number of units to be read
5247 stream - File to be read from
5248 Return:
5249 size_t - number of units read
5250 Side Effects:
5251 The FSerrno variable will be changed.
5252 Description:
5253 The FSfread function will read data from the specified file. First,
5254 the appropriate sector of the file is loaded. Then, data is read into
5255 the specified buffer until the specified number of bytes have been read.
5256 When a cluster boundary is reached, a new cluster will be loaded. The
5257 parameters 'size' and 'n' indicate how much data to read. 'Size'
5258 refers to the size of one object to read (in bytes), and 'n' will refer
5259 to the number of these objects to read. The value returned will be equal
5260 to 'n' unless an error occured or the user tried to read beyond the end
5261 of the file.
5262 Remarks:
5263 None.
5264 **************************************************************************/
5265  
5266 size_t FSfread (void *ptr, size_t size, size_t n, FSFILE *stream)
5267 {
5268 DWORD len = size * n;
5269 BYTE *pointer = (BYTE *) ptr;
5270 DISK *dsk; // Disk structure
5271 DWORD seek, sec_sel;
5272 WORD pos; //position within sector
5273 CETYPE error = CE_GOOD;
5274 WORD readCount = 0;
5275  
5276 FSerrno = CE_GOOD;
5277  
5278 dsk = (DISK *)stream->dsk;
5279 pos = stream->pos;
5280 seek = stream->seek;
5281  
5282 if( !stream->flags.read )
5283 {
5284 FSerrno = CE_WRITEONLY;
5285 return 0; // CE_WRITEONLY
5286 }
5287  
5288 #ifdef ALLOW_WRITES
5289 if (gNeedDataWrite)
5290 if (flushData())
5291 {
5292 FSerrno = CE_WRITE_ERROR;
5293 return 0;
5294 }
5295 #endif
5296  
5297 // if it not my buffer, then get it from the disk.
5298 if( (gBufferOwner != stream) && (pos != dsk->sectorSize))
5299 {
5300 gBufferOwner = stream;
5301 sec_sel = Cluster2Sector(dsk,stream->ccls);
5302 sec_sel += (WORD)stream->sec; // add the sector number to it
5303  
5304 gBufferZeroed = FALSE;
5305 if( !MDD_SectorRead( sec_sel, dsk->buffer) )
5306 {
5307 FSerrno = CE_BAD_SECTOR_READ;
5308 error = CE_BAD_SECTOR_READ;
5309 return 0;
5310 }
5311 gLastDataSectorRead = sec_sel;
5312 }
5313  
5314 //loop reading (count) bytes
5315 while( len )
5316 {
5317 if( seek == stream->size )
5318 {
5319 FSerrno = CE_EOF;
5320 error = CE_EOF;
5321 break;
5322 }
5323  
5324 // In fopen, pos is init to 0 and the sect is loaded
5325 if( pos == dsk->sectorSize )
5326 {
5327 // reset position
5328 pos = 0;
5329 // point to the next sector
5330 stream->sec++;
5331  
5332 // get a new cluster if necessary
5333 if( stream->sec == dsk->SecPerClus )
5334 {
5335 stream->sec = 0;
5336 if( (error = FILEget_next_cluster( stream, 1)) != CE_GOOD )
5337 {
5338 FSerrno = CE_COULD_NOT_GET_CLUSTER;
5339 break;
5340 }
5341 }
5342  
5343 sec_sel = Cluster2Sector(dsk,stream->ccls);
5344 sec_sel += (WORD)stream->sec; // add the sector number to it
5345  
5346  
5347 gBufferOwner = stream;
5348 gBufferZeroed = FALSE;
5349 if( !MDD_SectorRead( sec_sel, dsk->buffer) )
5350 {
5351 FSerrno = CE_BAD_SECTOR_READ;
5352 error = CE_BAD_SECTOR_READ;
5353 break;
5354 }
5355 gLastDataSectorRead = sec_sel;
5356 }
5357  
5358 // copy one byte at a time
5359 *pointer = RAMread( dsk->buffer, pos++ );
5360 pointer++;
5361 seek++;
5362 readCount++;
5363 len--;
5364 }
5365  
5366 // save off the positon
5367 stream->pos = pos;
5368 // save off the seek
5369 stream->seek = seek;
5370  
5371 return(readCount / size);
5372 } // fread
5373  
5374  
5375 /***************************************************************************
5376 Function:
5377 BYTE FormatFileName( const char* fileName, char* fN2, BYTE mode )
5378 Summary:
5379 Format a file name into dir entry format
5380 Conditions:
5381 This function should not be called by the user.
5382 Input:
5383 fileName - The name to be formatted
5384 fN2 - The location the formatted name will be stored
5385 mode - Non-zero if parital string search chars are allowed
5386 Return Values:
5387 TRUE - Name formatted successfully
5388 FALSE - File name could not be formatted
5389 Side Effects:
5390 None
5391 Description:
5392 Format an 8.3 filename into FSFILE structure format. If filename is less
5393 than 8 chars, then it will be padded with spaces. If the extension name is
5394 fewer than 3 chars, then it will also be oadded with spaces. The
5395 ValidateChars function is used to ensure the characters in the specified
5396 filename are valid in this filesystem.
5397 Remarks:
5398 None.
5399 ***************************************************************************/
5400 BYTE FormatFileName( const char* fileName, char* fN2, BYTE mode)
5401 {
5402 char * pExt;
5403 WORD temp;
5404 char szName[15];
5405 BYTE count;
5406  
5407 for (count = 0; count < 11; count++)
5408 {
5409 *(fN2 + count) = ' '; // Load destination filename to be space intially.
5410 }
5411  
5412 // Make sure we dont have an empty string or a name with only
5413 // an extension
5414 if (fileName[0] == '.' || fileName[0] == 0)
5415 return FALSE;
5416  
5417 temp = strlen( fileName );
5418  
5419 if( temp <= TOTAL_FILE_SIZE ) // 8+3+1
5420 strcpy( szName, fileName ); // copy to RAM in case fileName is located in flash
5421 else
5422 return FALSE; //long file name
5423  
5424 // Make sure the characters are valid
5425 if ( !ValidateChars(szName, mode) )
5426 return FALSE;
5427  
5428 //Look for '.' in the szName
5429 if( (pExt = strchr( szName, '.' )) != 0 )
5430 {
5431 *pExt = 0; // Assigning NULL here makes the "szName" to be terminated and "pExt" pointer to hold only extn characters.
5432 pExt++; // now pointing to extension
5433  
5434 if( strlen( pExt ) > 3 ) // make sure the extension is 3 bytes or fewer
5435 return FALSE;
5436 }
5437  
5438 if( strlen(szName) > 8 )
5439 return FALSE;
5440  
5441 //copy file name
5442 for (count = 0; count < strlen(szName); count++)
5443 {
5444 *(fN2 + count) = * (szName + count); // Destination filename initially filled with SPACE. Now copy only available chars.
5445 }
5446  
5447 //copy extension
5448 if(pExt && *pExt )
5449 {
5450 for (count = 0; count < strlen (pExt); count++)
5451 {
5452 *(fN2 + count + 8) = *(pExt + count); // Copy the extn to 8th position onwards. Ex: "FILE .Tx "
5453 }
5454 }
5455  
5456 return TRUE;
5457 }
5458  
5459 #ifdef ALLOW_DIRS
5460  
5461 /*************************************************************************
5462 Function:
5463 BYTE FormatDirName (char * string, BYTE mode)
5464 Summary:
5465 Format a dir name into dir entry format
5466 Conditions:
5467 This function should not be called by the user.
5468 Input:
5469 string - The name to be formatted
5470 mode -
5471 - TRUE - Partial string search characters are allowed
5472 - FALSE - Partial string search characters are forbidden
5473 Return Values:
5474 TRUE - The name was formatted correctly
5475 FALSE - The name contained invalid characters
5476 Side Effects:
5477 None
5478 Description:
5479 Format an 8.3 filename into directory structure format. If the name is less
5480 than 8 chars, then it will be padded with spaces. If the extension name is
5481 fewer than 3 chars, then it will also be oadded with spaces. The
5482 ValidateChars function is used to ensure the characters in the specified
5483 directory name are valid in this filesystem.
5484 Remarks:
5485 None.
5486 *************************************************************************/
5487  
5488 BYTE FormatDirName (char * string, BYTE mode)
5489 {
5490 unsigned char i, j;
5491 char tempString [12];
5492  
5493 if (ValidateChars (string, mode) == FALSE)
5494 return FALSE;
5495  
5496 for (i = 0; (i < 8) && (*(string + i) != '.') && (*(string + i) != 0); i++)
5497 {
5498 tempString[i] = *(string + i);
5499 }
5500  
5501 j = i;
5502  
5503 while (i < 8)
5504 {
5505 tempString [i++] = 0x20;
5506 }
5507  
5508 if (*(string + j) == '.')
5509 {
5510 j++;
5511 while (*(string + j) != 0)
5512 {
5513 tempString[i++] = *(string + j++);
5514 }
5515 }
5516  
5517 while (i < 11)
5518 {
5519 tempString[i++] = 0x20;
5520 }
5521  
5522 tempString[11] = 0;
5523  
5524 // Forbidden
5525 if (tempString[0] == 0x20)
5526 {
5527 tempString[0] = '_';
5528 }
5529  
5530 for (i = 0; i < 12; i++)
5531 {
5532 *(string + i) = tempString[i];
5533 }
5534  
5535 return TRUE;
5536 }
5537 #endif
5538  
5539  
5540 /*************************************************************
5541 Function:
5542 BYTE ValidateChars( char * FileName, BYTE mode)
5543 Summary:
5544 Validate the characters in a given file name
5545 Conditions:
5546 This function should not be called by the user.
5547 Input:
5548 fileName - The name to be validated
5549 mode - Determines if partial string search is allowed
5550 Return Values:
5551 TRUE - Name was validated
5552 FALSE - File name was not valid
5553 Side Effects:
5554 None
5555 Description:
5556 The ValidateChars function will compare characters in a
5557 specified filename to determine if they're permissable
5558 in the FAT file system. Lower-case characters will be
5559 converted to upper-case. If the mode argument is specifed
5560 to be 'TRUE,' partial string search characters are allowed.
5561 Remarks:
5562 None.
5563 *************************************************************/
5564 BYTE ValidateChars( char * FileName , BYTE mode)
5565 {
5566 int StrSz, index;
5567 unsigned char radix = FALSE;
5568  
5569 StrSz = strlen(FileName);
5570  
5571 for( index = 0; index < StrSz; index++ )
5572 {
5573 if (((FileName[index] <= 0x20) && (FileName[index] != 0x05)) ||
5574 (FileName[index] == 0x22) || (FileName[index] == 0x2B) ||
5575 (FileName[index] == 0x2C) || (FileName[index] == 0x2F) ||
5576 (FileName[index] == 0x3A) || (FileName[index] == 0x3B) ||
5577 (FileName[index] == 0x3C) || (FileName[index] == 0x3D) ||
5578 (FileName[index] == 0x3E) || (FileName[index] == 0x5B) ||
5579 (FileName[index] == 0x5C) || (FileName[index] == 0x5D) ||
5580 (FileName[index] == 0x7C) || ((FileName[index] == 0x2E) && radix == TRUE))
5581 {
5582 return FALSE;
5583 }
5584 else
5585 {
5586 // Check for partial string search chars
5587 if (mode == FALSE)
5588 {
5589 if ((FileName[index] == '*') || (FileName[index] == '?'))
5590 return FALSE;
5591 }
5592 // only one radix ('.') character is allowed
5593 if (FileName[index] == 0x2E)
5594 {
5595 radix = TRUE;
5596 }
5597 // Convert lower-case to upper-case
5598 if ((FileName[index] >= 0x61) && (FileName[index] <= 0x7A))
5599 FileName[index] -= 0x20;
5600 }
5601 }
5602 return TRUE;
5603 }
5604  
5605  
5606 /**********************************************************************
5607 Function:
5608 int FSfseek(FSFILE *stream, long offset, int whence)
5609 Summary:
5610 Change the current position in a file
5611 Conditions:
5612 File opened
5613 Input:
5614 stream - Pointer to file structure
5615 offset - Offset from base location
5616 whence -
5617 - SEEK_SET - Seek from start of file
5618 - SEEK_CUR - Seek from current location
5619 - SEEK_END - Seek from end of file (subtract offset)
5620 Return Values:
5621  
5622 -1 - Operation unsuccesful
5623 Side Effects:
5624 The FSerrno variable will be changed.
5625 Description:
5626 The FSfseek function will change the current position in the file to
5627 one specified by the user. First, an absolute offset is calculated
5628 using the offset and base location passed in by the user. Then, the
5629 position variables are updated, and the sector number that corresponds
5630 to the new location. That sector is then loaded. If the offset
5631 falls exactly on a cluster boundary, a new cluster will be allocated
5632 to the file and the position will be set to the first byte of that
5633 cluster.
5634 Remarks:
5635 None
5636 **********************************************************************/
5637  
5638 int FSfseek(FSFILE *stream, long offset, int whence)
5639 {
5640 DWORD numsector, temp; // lba of first sector of first cluster
5641 DISK* dsk; // pointer to disk structure
5642 BYTE test;
5643 long offset2 = offset;
5644  
5645 dsk = stream->dsk;
5646  
5647 switch(whence)
5648 {
5649 case SEEK_CUR:
5650 // Apply the offset to the current position
5651 offset2 += stream->seek;
5652 break;
5653 case SEEK_END:
5654 // Apply the offset to the end of the file
5655 offset2 = stream->size - offset2;
5656 break;
5657 case SEEK_SET:
5658 // automatically there
5659 default:
5660 break;
5661 }
5662  
5663 #ifdef ALLOW_WRITES
5664 if (gNeedDataWrite)
5665 if (flushData())
5666 {
5667 FSerrno = CE_WRITE_ERROR;
5668 return EOF;
5669 }
5670 #endif
5671  
5672 // start from the beginning
5673 temp = stream->cluster;
5674 stream->ccls = temp;
5675  
5676 temp = stream->size;
5677  
5678 if (offset2 > temp)
5679 {
5680 FSerrno = CE_INVALID_ARGUMENT;
5681 return (-1); // past the limits
5682 }
5683 else
5684 {
5685 // if we are writing we are no longer at the end
5686 stream->flags.FileWriteEOF = FALSE;
5687  
5688 // set the new postion
5689 stream->seek = offset2;
5690  
5691 // figure out how many sectors
5692 numsector = offset2 / dsk->sectorSize;
5693  
5694 // figure out how many bytes off of the offset
5695 offset2 = offset2 - (numsector * dsk->sectorSize);
5696 stream->pos = offset2;
5697  
5698 // figure out how many clusters
5699 temp = numsector / dsk->SecPerClus;
5700  
5701 // figure out the stranded sectors
5702 numsector = numsector - (dsk->SecPerClus * temp);
5703 stream->sec = numsector;
5704  
5705 // if we are in the current cluster stay there
5706 if (temp > 0)
5707 {
5708 test = FILEget_next_cluster(stream, temp);
5709 if (test != CE_GOOD)
5710 {
5711 if (test == CE_FAT_EOF)
5712 {
5713 #ifdef ALLOW_WRITES
5714 if (stream->flags.write)
5715 {
5716 // load the previous cluster
5717 stream->ccls = stream->cluster;
5718 // Don't perform this operation if there's only one cluster
5719 if (temp != 1)
5720 test = FILEget_next_cluster(stream, temp - 1);
5721 if (FILEallocate_new_cluster(stream, 0) != CE_GOOD)
5722 {
5723 FSerrno = CE_COULD_NOT_GET_CLUSTER;
5724 return -1;
5725 }
5726 // sec and pos should already be zero
5727 }
5728 else
5729 {
5730 #endif
5731 stream->ccls = stream->cluster;
5732 test = FILEget_next_cluster(stream, temp - 1);
5733 if (test != CE_GOOD)
5734 {
5735 FSerrno = CE_COULD_NOT_GET_CLUSTER;
5736 return (-1);
5737 }
5738 stream->pos = dsk->sectorSize;
5739 stream->sec = dsk->SecPerClus - 1;
5740 #ifdef ALLOW_WRITES
5741 }
5742 #endif
5743 }
5744 else
5745 {
5746 FSerrno = CE_COULD_NOT_GET_CLUSTER;
5747 return (-1); // past the limits
5748 }
5749 }
5750 }
5751  
5752 // Determine the lba of the selected sector and load
5753 temp = Cluster2Sector(dsk,stream->ccls);
5754  
5755 // now the extra sectors
5756 numsector = stream->sec;
5757 temp += numsector;
5758  
5759 gBufferOwner = NULL;
5760 gBufferZeroed = FALSE;
5761 if( !MDD_SectorRead(temp, dsk->buffer) )
5762 {
5763 FSerrno = CE_BADCACHEREAD;
5764 return (-1); // Bad read
5765 }
5766 gLastDataSectorRead = temp;
5767 }
5768  
5769 FSerrno = CE_GOOD;
5770  
5771 return (0);
5772 }
5773  
5774  
5775 // FSfopenpgm, FSremovepgm, and FSrenamepgm will only work on PIC18s
5776 #ifdef __18CXX
5777 #ifdef ALLOW_PGMFUNCTIONS
5778  
5779 #ifdef ALLOW_WRITES
5780  
5781 /*****************************************************************
5782 Function:
5783 int FSrenamepgm(const rom char * fileName, FSFILE * fo)
5784 Summary:
5785 Rename a file named with a ROM string on PIC18
5786 Conditions:
5787 File opened.
5788 Input:
5789 fileName - The new name of the file (in ROM)
5790 fo - The file to rename
5791 Return Values:
5792  
5793 -1 - File could not be renamed
5794 Side Effects:
5795 The FSerrno variable will be changed.
5796 Description:
5797 The Fsrenamepgm function will copy the rom fileName specified
5798 by the user into a RAM array and pass that array into the
5799 FSrename function.
5800 Remarks:
5801 This function is for use with PIC18 when passing arguments in ROM.
5802 *****************************************************************/
5803  
5804 int FSrenamepgm (const rom char * fileName, FSFILE * fo)
5805 {
5806 char F[13];
5807 BYTE count;
5808  
5809 for (count = 0; count < 13; count++)
5810 {
5811 F[count] = *(fileName + count);
5812 }
5813  
5814 return FSrename (F, fo);
5815 }
5816 #endif
5817  
5818 /******************************************************************************
5819 Function:
5820 FSFILE * FSfopenpgm(const rom char * fileName, const rom char *mode)
5821 Summary:
5822 Open a file named with a ROM string on PIC18
5823 Conditions:
5824 For read modes, file exists; FSInit performed
5825 Input:
5826 fileName - The name of the file to be opened (ROM)
5827 mode - The mode the file will be opened in (ROM)
5828 Return Values:
5829 FSFILE * - A pointer to the file object
5830 NULL - File could not be opened
5831 Side Effects:
5832 The FSerrno variable will be changed.
5833 Description:
5834 The FSfopenpgm function will copy a PIC18 ROM fileName and mode argument
5835 into RAM arrays, and then pass those arrays to the FSfopen function.
5836 Remarks:
5837 This function is for use with PIC18 when passing arguments in ROM.
5838 ******************************************************************************/
5839  
5840  
5841 FSFILE * FSfopenpgm(const rom char * fileName, const rom char *mode)
5842 {
5843 char F[13];
5844 char M[2];
5845 BYTE count;
5846  
5847 for (count = 0; count < 13; count++)
5848 {
5849 F[count] = *(fileName + count);
5850 }
5851 for (count = 0; count < 2; count++)
5852 {
5853 M[count] = *(mode + count);
5854 }
5855  
5856 return FSfopen(F, M);
5857 }
5858  
5859 /*************************************************************
5860 Function:
5861 int FSremovepgm (const rom char * fileName)
5862 Summary:
5863 Delete a file named with a ROM string on PIC18
5864 Conditions:
5865 File not opened; file exists
5866 Input:
5867 fileName - The name of the file to be deleted (ROM)
5868 Return Values:
5869  
5870 -1 - File could not be removed
5871 Side Effects:
5872 The FSerrno variable will be changed.
5873 Description:
5874 The FSremovepgm function will copy a PIC18 ROM fileName argument
5875 into a RAM array, and then pass that array to the FSremove function.
5876 Remarks:
5877 This function is for use with PIC18 when passing arguments in ROM.
5878 *************************************************************/
5879 #ifdef ALLOW_WRITES
5880 int FSremovepgm (const rom char * fileName)
5881 {
5882 char F[13];
5883 BYTE count;
5884  
5885 *fileName;
5886 for(count = 0; count < sizeof(F); count++)
5887 {
5888 _asm TBLRDPOSTINC _endasm
5889 F[count] = TABLAT;
5890 }//end for(...)
5891  
5892 return FSremove (F);
5893 }
5894 #endif
5895  
5896 /**************************************************************************************
5897 Function:
5898 int FindFirstpgm (const char * fileName, unsigned int attr, SearchRec * rec)
5899 Summary:
5900 Find a file named with a ROM string on PIC18
5901 Conditions:
5902 None
5903 Input:
5904 fileName - The name of the file to be found (ROM)
5905 attr - The attributes of the file to be found
5906 rec - Pointer to a search record to store the file info in
5907 Return Values:
5908  
5909 -1 - No file matching the given parameters was found
5910 Side Effects:
5911 Search criteria from previous FindFirst call on passed SearchRec object will be lost.
5912 The FSerrno variable will be changed.
5913 Description:
5914 The FindFirstpgm function will copy a PIC18 ROM fileName argument
5915 into a RAM array, and then pass that array to the FindFirst function.
5916 Remarks:
5917 Call FindFirstpgm or FindFirst before calling FindNext.
5918 This function is for use with PIC18 when passing arguments in ROM.
5919 **************************************************************************************/
5920 #ifdef ALLOW_FILESEARCH
5921 int FindFirstpgm (const rom char * fileName, unsigned int attr, SearchRec * rec)
5922 {
5923 char F[13];
5924 BYTE count;
5925  
5926 *fileName;
5927 for(count = 0; count < sizeof(F); count++)
5928 {
5929 _asm TBLRDPOSTINC _endasm
5930 F[count] = TABLAT;
5931 }//end for
5932  
5933 return FindFirst (F,attr,rec);
5934 }
5935 #endif
5936 #endif
5937 #endif
5938  
5939  
5940 /***********************************************
5941 Function:
5942 DWORD ReadFAT (DISK *dsk, DWORD ccls)
5943 Summary:
5944 Read the next entry from the FAT
5945 Conditions:
5946 This function should not be called by the user.
5947 Input:
5948 dsk - The disk structure
5949 ccls - The current cluster
5950 Return:
5951 DWORD - The next cluster in a file chain
5952 Side Effects:
5953 None
5954 Description:
5955 The ReadFAT function will read the FAT and
5956 determine the next cluster value after the
5957 cluster specified by 'ccls.' Note that the
5958 FAT sector that is read is stored in the
5959 global FAT cache buffer.
5960 Remarks:
5961 None.
5962 ***********************************************/
5963  
5964 DWORD ReadFAT (DISK *dsk, DWORD ccls)
5965 {
5966 BYTE q;
5967 DWORD p, l; // "l" is the sector Address
5968 DWORD c = 0, d, ClusterFailValue,LastClusterLimit; // ClusterEntries
5969  
5970 gBufferZeroed = FALSE;
5971  
5972 /* Settings based on FAT type */
5973 switch (dsk->type)
5974 {
5975 #ifdef SUPPORT_FAT32 // If FAT32 supported.
5976 case FAT32:
5977 p = (DWORD)ccls * 4;
5978 q = 0; // "q" not used for FAT32, only initialized to remove a warning
5979 ClusterFailValue = CLUSTER_FAIL_FAT32;
5980 LastClusterLimit = LAST_CLUSTER_FAT32;
5981 break;
5982 #endif
5983 case FAT12:
5984 p = (DWORD) ccls *3; // Mulby1.5 to find cluster pos in FAT
5985 q = p&1;
5986 p >>= 1;
5987 ClusterFailValue = CLUSTER_FAIL_FAT16;
5988 LastClusterLimit = LAST_CLUSTER_FAT12;
5989 break;
5990 case FAT16:
5991 default:
5992 p = (DWORD)ccls *2; // Mulby 2 to find cluster pos in FAT
5993 q = 0; // "q" not used for FAT16, only initialized to remove a warning
5994 ClusterFailValue = CLUSTER_FAIL_FAT16;
5995 LastClusterLimit = LAST_CLUSTER_FAT16;
5996 break;
5997 }
5998  
5999 l = dsk->fat + (p / dsk->sectorSize); //
6000 p &= dsk->sectorSize - 1; // Restrict 'p' within the FATbuffer size
6001  
6002 // Check if the appropriate FAT sector is already loaded
6003 if (gLastFATSectorRead == l)
6004 {
6005 #ifdef SUPPORT_FAT32 // If FAT32 supported.
6006 if (dsk->type == FAT32)
6007 c = RAMreadD (gFATBuffer, p);
6008 else
6009 #endif
6010 if(dsk->type == FAT16)
6011 c = RAMreadW (gFATBuffer, p);
6012 else if(dsk->type == FAT12)
6013 {
6014 c = RAMread (gFATBuffer, p);
6015 if (q)
6016 {
6017 c >>= 4;
6018 }
6019 // Check if the MSB is across the sector boundry
6020 p = (p +1) & (dsk->sectorSize-1);
6021 if (p == 0)
6022 {
6023 // Start by writing the sector we just worked on to the card
6024 // if we need to
6025 #ifdef ALLOW_WRITES
6026 if (gNeedFATWrite)
6027 if(WriteFAT (dsk, 0, 0, TRUE))
6028 return ClusterFailValue;
6029 #endif
6030 if (!MDD_SectorRead (l+1, gFATBuffer))
6031 {
6032 gLastFATSectorRead = 0xFFFF;
6033 return ClusterFailValue;
6034 }
6035 else
6036 {
6037 gLastFATSectorRead = l +1;
6038 }
6039 }
6040 d = RAMread (gFATBuffer, p);
6041 if (q)
6042 {
6043 c += (d <<4);
6044 }
6045 else
6046 {
6047 c += ((d & 0x0F)<<8);
6048 }
6049 }
6050 }
6051 else
6052 {
6053 // If there's a currently open FAT sector,
6054 // write it back before reading into the buffer
6055 #ifdef ALLOW_WRITES
6056 if (gNeedFATWrite)
6057 {
6058 if(WriteFAT (dsk, 0, 0, TRUE))
6059 return ClusterFailValue;
6060 }
6061 #endif
6062 if (!MDD_SectorRead (l, gFATBuffer))
6063 {
6064 gLastFATSectorRead = 0xFFFF; // Note: It is Sector not Cluster.
6065 return ClusterFailValue;
6066 }
6067 else
6068 {
6069 gLastFATSectorRead = l;
6070  
6071 #ifdef SUPPORT_FAT32 // If FAT32 supported.
6072 if (dsk->type == FAT32)
6073 c = RAMreadD (gFATBuffer, p);
6074 else
6075 #endif
6076 if(dsk->type == FAT16)
6077 c = RAMreadW (gFATBuffer, p);
6078 else if (dsk->type == FAT12)
6079 {
6080 c = RAMread (gFATBuffer, p);
6081 if (q)
6082 {
6083 c >>= 4;
6084 }
6085 p = (p +1) & (dsk->sectorSize-1);
6086 d = RAMread (gFATBuffer, p);
6087 if (q)
6088 {
6089 c += (d <<4);
6090 }
6091 else
6092 {
6093 c += ((d & 0x0F)<<8);
6094 }
6095 }
6096 }
6097 }
6098  
6099 // Normalize it so 0xFFFF is an error
6100 if (c >= LastClusterLimit)
6101 c = LastClusterLimit;
6102  
6103 return c;
6104 } // ReadFAT
6105  
6106  
6107  
6108 /****************************************************************************
6109 Function:
6110 WORD WriteFAT (DISK *dsk, DWORD ccls, WORD value, BYTE forceWrite)
6111 Summary:
6112 Write an entry to the FAT
6113 Conditions:
6114 This function should not be called by the user.
6115 Input:
6116 dsk - The disk structure
6117 ccls - The current cluster
6118 value - The value to write in
6119 forceWrite - Force the function to write the current FAT sector
6120 Return:
6121  
6122 FAIL - The FAT could not be written
6123 Side Effects:
6124 None
6125 Description:
6126 The WriteFAT function writes an entry to the FAT. If the function
6127 is called and the 'forceWrite' argument is TRUE, the function will
6128 write the existing FAT data to the device. Otherwise, the function
6129 will replace a single entry in the FAT buffer (indicated by 'ccls')
6130 with a new value (indicated by 'value.')
6131 Remarks:
6132 None.
6133 ****************************************************************************/
6134  
6135 #ifdef ALLOW_WRITES
6136 DWORD WriteFAT (DISK *dsk, DWORD ccls, DWORD value, BYTE forceWrite)
6137 {
6138 BYTE i, q, c;
6139 DWORD p, li, l, ClusterFailValue;
6140  
6141 #ifdef SUPPORT_FAT32 // If FAT32 supported.
6142 if (dsk->type != FAT32 && dsk->type != FAT16 && dsk->type != FAT12)
6143 return CLUSTER_FAIL_FAT32;
6144 #else // If FAT32 support not enabled
6145 if (dsk->type != FAT16 && dsk->type != FAT12)
6146 return CLUSTER_FAIL_FAT16;
6147 #endif
6148  
6149 /* Settings based on FAT type */
6150 switch (dsk->type)
6151 {
6152 #ifdef SUPPORT_FAT32 // If FAT32 supported.
6153 case FAT32:
6154 ClusterFailValue = CLUSTER_FAIL_FAT32;
6155 break;
6156 #endif
6157 case FAT12:
6158 case FAT16:
6159 default:
6160 ClusterFailValue = CLUSTER_FAIL_FAT16;
6161 break;
6162 }
6163  
6164 gBufferZeroed = FALSE;
6165  
6166 // The only purpose for calling this function with forceWrite
6167 // is to write the current FAT sector to the card
6168 if (forceWrite)
6169 {
6170 for (i = 0, li = gLastFATSectorRead; i < dsk->fatcopy; i++, li += dsk->fatsize)
6171 {
6172 if (!MDD_SectorWrite (li, gFATBuffer, FALSE))
6173 {
6174 return ClusterFailValue;
6175 }
6176 }
6177  
6178 gNeedFATWrite = FALSE;
6179  
6180 return 0;
6181 }
6182  
6183 /* Settings based on FAT type */
6184 switch (dsk->type)
6185 {
6186 #ifdef SUPPORT_FAT32 // If FAT32 supported.
6187 case FAT32:
6188 p = (DWORD)ccls *4; // "p" is the position in "gFATBuffer" for corresponding cluster.
6189 q = 0; // "q" not used for FAT32, only initialized to remove a warning
6190 break;
6191 #endif
6192 case FAT12:
6193 p = (DWORD) ccls * 3; // "p" is the position in "gFATBuffer" for corresponding cluster.
6194 q = p & 1; // Odd or even?
6195 p >>= 1;
6196 break;
6197 case FAT16:
6198 default:
6199 p = (DWORD) ccls *2; // "p" is the position in "gFATBuffer" for corresponding cluster.
6200 q = 0; // "q" not used for FAT16, only initialized to remove a warning
6201 break;
6202 }
6203  
6204 l = dsk->fat + (p / dsk->sectorSize); //
6205 p &= dsk->sectorSize - 1; // Restrict 'p' within the FATbuffer size
6206  
6207 if (gLastFATSectorRead != l)
6208 {
6209 // If we are loading a new sector then write
6210 // the current one to the card if we need to
6211 if (gNeedFATWrite)
6212 {
6213 for (i = 0, li = gLastFATSectorRead; i < dsk->fatcopy; i++, li += dsk->fatsize)
6214 {
6215 if (!MDD_SectorWrite (li, gFATBuffer, FALSE))
6216 {
6217 return ClusterFailValue;
6218 }
6219 }
6220  
6221 gNeedFATWrite = FALSE;
6222 }
6223  
6224 // Load the new sector
6225 if (!MDD_SectorRead (l, gFATBuffer))
6226 {
6227 gLastFATSectorRead = 0xFFFF;
6228 return ClusterFailValue;
6229 }
6230 else
6231 {
6232 gLastFATSectorRead = l;
6233 }
6234 }
6235  
6236 #ifdef SUPPORT_FAT32 // If FAT32 supported.
6237 if (dsk->type == FAT32) // Refer page 16 of FAT requirement.
6238 {
6239 RAMwrite (gFATBuffer, p, ((value & 0x000000ff))); // lsb,1st byte of cluster value
6240 RAMwrite (gFATBuffer, p+1, ((value & 0x0000ff00) >> 8));
6241 RAMwrite (gFATBuffer, p+2, ((value & 0x00ff0000) >> 16));
6242 RAMwrite (gFATBuffer, p+3, ((value & 0x0f000000) >> 24)); // the MSB nibble is supposed to be "0" in FAT32. So mask it.
6243 }
6244 else
6245  
6246 #endif
6247 {
6248 if (dsk->type == FAT16)
6249 {
6250 RAMwrite (gFATBuffer, p, value); //lsB
6251 RAMwrite (gFATBuffer, p+1, ((value&0x0000ff00) >> 8)); // msB
6252 }
6253 else if (dsk->type == FAT12)
6254 {
6255 // Get the current byte from the FAT
6256 c = RAMread (gFATBuffer, p);
6257 if (q)
6258 {
6259 c = ((value & 0x0F) << 4) | ( c & 0x0F);
6260 }
6261 else
6262 {
6263 c = (value & 0xFF);
6264 }
6265 // Write in those bits
6266 RAMwrite (gFATBuffer, p, c);
6267  
6268 // FAT12 entries can cross sector boundaries
6269 // Check if we need to load a new sector
6270 p = (p +1) & (dsk->sectorSize-1);
6271 if (p == 0)
6272 {
6273 // call this function to update the FAT on the card
6274 if (WriteFAT (dsk, 0,0,TRUE))
6275 return ClusterFailValue;
6276  
6277 // Load the next sector
6278 if (!MDD_SectorRead (l +1, gFATBuffer))
6279 {
6280 gLastFATSectorRead = 0xFFFF;
6281 return ClusterFailValue;
6282 }
6283 else
6284 {
6285 gLastFATSectorRead = l + 1;
6286 }
6287 }
6288  
6289 // Get the second byte of the table entry
6290 c = RAMread (gFATBuffer, p);
6291 if (q)
6292 {
6293 c = (value >> 4);
6294 }
6295 else
6296 {
6297 c = ((value >> 8) & 0x0F) | (c & 0xF0);
6298 }
6299 RAMwrite (gFATBuffer, p, c);
6300 }
6301 }
6302 gNeedFATWrite = TRUE;
6303  
6304 return 0;
6305 }
6306 #endif
6307  
6308  
6309 #ifdef ALLOW_DIRS
6310  
6311 // This string is used by dir functions to hold dir names temporarily
6312 char defaultString [13];
6313  
6314  
6315  
6316 /**************************************************************************
6317 Function:
6318 int FSchdir (char * path)
6319 Summary:
6320 Change the current working directory
6321 Conditions:
6322 None
6323 Input:
6324 path - The path of the directory to change to.
6325 Return Values:
6326  
6327 EOF - The current working directory could not be changed
6328 Side Effects:
6329 The current working directory may be changed. The FSerrno variable will
6330 be changed.
6331 Description:
6332 The FSchdir function passes a RAM pointer to the path to the
6333 chdirhelper function.
6334 Remarks:
6335 None
6336 **************************************************************************/
6337  
6338 int FSchdir (char * path)
6339 {
6340 return chdirhelper (0, path, NULL);
6341 }
6342  
6343 /**************************************************************************
6344 Function:
6345 int FSchdirpgm (const rom char * path)
6346 Summary:
6347 Changed the CWD with a path in ROM on PIC18
6348 Conditions:
6349 None
6350 Input:
6351 path - The path of the directory to change to (ROM)
6352 Return Values:
6353  
6354 EOF - The current working directory could not be changed
6355 Side Effects:
6356 The current working directory may be changed. The FSerrno variable will
6357 be changed.
6358 Description:
6359 The FSchdirpgm function passes a PIC18 ROM path pointer to the
6360 chdirhelper function.
6361 Remarks:
6362 This function is for use with PIC18 when passing arguments in ROM
6363 **************************************************************************/
6364  
6365 #ifdef ALLOW_PGMFUNCTIONS
6366 int FSchdirpgm (const rom char * path)
6367 {
6368 return chdirhelper (1, NULL, path);
6369 }
6370 #endif
6371  
6372  
6373 /*************************************************************************
6374 Function:
6375 // PIC24/30/33/32
6376 int chdirhelper (BYTE mode, char * ramptr, char * romptr);
6377 // PIC18
6378 int chdirhelper (BYTE mode, char * ramptr, const rom char * romptr);
6379 Summary:
6380 Helper function for FSchdir
6381 Conditions:
6382 None
6383 Input:
6384 mode - Indicates which path pointer to use
6385 ramptr - Pointer to the path specified in RAM
6386 romptr - Pointer to the path specified in ROM
6387 Return Values:
6388  
6389 EOF - Directory could not be changed.
6390 Side Effects:
6391 The current working directory will be changed. The FSerrno variable
6392 will be changed. Any unwritten data in the data buffer will be written
6393 to the device.
6394 Description:
6395 This helper function is used by the FSchdir function. If the path
6396 argument is specified in ROM for PIC18 this function will be able to
6397 parse it correctly. The function will loop through a switch statement
6398 to process the tokens in the path string. Dot or dotdot entries are
6399 handled in the first case statement. A backslash character is handled
6400 in the second case statement (note that this case statement will only
6401 be used if backslash is the first character in the path; backslash
6402 token delimiters will automatically be skipped after each token in the
6403 path is processed). The third case statement will handle actual
6404 directory name strings.
6405 Remarks:
6406 None.
6407 *************************************************************************/
6408  
6409 #ifdef ALLOW_PGMFUNCTIONS
6410 int chdirhelper (BYTE mode, char * ramptr, const rom char * romptr)
6411 #else
6412 int chdirhelper (BYTE mode, char * ramptr, char * romptr)
6413 #endif
6414 {
6415 BYTE i, j;
6416 WORD curent = 1;
6417 DIRENTRY entry;
6418 char * temppath = ramptr;
6419 #ifdef ALLOW_PGMFUNCTIONS
6420 rom char * temppath2 = romptr;
6421 #endif
6422 FSFILE tempCWDobj2;
6423 char tempArray[12];
6424 FILEOBJ tempCWD = &tempCWDobj2;
6425 FileObjectCopy (tempCWD, cwdptr);
6426  
6427 FSerrno = CE_GOOD;
6428  
6429 // Check the first char of the path
6430 #ifdef ALLOW_PGMFUNCTIONS
6431 if (mode)
6432 i = *temppath2;
6433 else
6434 #endif
6435 i = *temppath;
6436 if (i == 0)
6437 {
6438 FSerrno = CE_INVALID_ARGUMENT;
6439 return -1;
6440 }
6441  
6442 while(1)
6443 {
6444 switch (i)
6445 {
6446 // First case: dot or dotdot entry
6447 case '.':
6448 // Move past the dot
6449 #ifdef ALLOW_PGMFUNCTIONS
6450 if (mode)
6451 {
6452 temppath2++;
6453 i = *temppath2;
6454 }
6455 else
6456 {
6457 #endif
6458 temppath++;
6459 i = *temppath;
6460 #ifdef ALLOW_PGMFUNCTIONS
6461 }
6462 #endif
6463 // Check if it's a dotdot entry
6464 if (i == '.')
6465 {
6466 // Increment the path variable
6467 #ifdef ALLOW_PGMFUNCTIONS
6468 if (mode)
6469 {
6470 temppath2++;
6471 i = *temppath2;
6472 }
6473 else
6474 {
6475 #endif
6476 temppath++;
6477 i = *temppath;
6478 #ifdef ALLOW_PGMFUNCTIONS
6479 }
6480 #endif
6481 // Check if we're in the root
6482 if (tempCWD->dirclus == FatRootDirClusterValue)
6483 {
6484 // Fails if there's a dotdot chdir from the root
6485 FSerrno = CE_INVALID_ARGUMENT;
6486 return -1;
6487 }
6488 else
6489 {
6490 // Cache the dotdot entry
6491 tempCWD->dirccls = tempCWD->dirclus;
6492 curent = 1;
6493 entry = Cache_File_Entry (tempCWD, &curent, TRUE);
6494 if (entry == NULL)
6495 {
6496 FSerrno = CE_BADCACHEREAD;
6497 return -1;
6498 }
6499  
6500 // Get the cluster
6501 tempCWD->dirclus = GetFullClusterNumber(entry); // Get Complete Cluster number.
6502 tempCWD->dirccls = tempCWD->dirclus;
6503  
6504 // If we changed to root, record the name
6505 if (tempCWD->dirclus == VALUE_DOTDOT_CLUSTER_VALUE_FOR_ROOT) // "0" is the value of Dotdot entry for Root in both FAT types.
6506 {
6507 tempCWD->name[0] = '\\';
6508 for (j = 1; j < 11; j++)
6509 {
6510 tempCWD->name[j] = 0x20;
6511 }
6512  
6513 /* While moving to Root, get the Root cluster value */
6514 tempCWD->dirccls = FatRootDirClusterValue;
6515 tempCWD->dirclus = FatRootDirClusterValue;
6516 }
6517 else
6518 {
6519 // Otherwise set the name to ..
6520 tempCWD->name[0] = '.';
6521 tempCWD->name[1] = '.';
6522 for (j = 2; j < 11; j++)
6523 {
6524 tempCWD->name[j] = 0x20;
6525 }
6526 }
6527 // Cache the dot entry
6528 curent = 0;
6529 if (Cache_File_Entry(tempCWD, &curent, TRUE) == NULL)
6530 {
6531 FSerrno = CE_BADCACHEREAD;
6532 return -1;
6533 }
6534 // Move past the next backslash, if necessary
6535 while (i == '\\')
6536 {
6537 #ifdef ALLOW_PGMFUNCTIONS
6538 if (mode)
6539 {
6540 temppath2++;
6541 i = *temppath2;
6542 }
6543 else
6544 {
6545 #endif
6546 temppath++;
6547 i = *temppath;
6548 #ifdef ALLOW_PGMFUNCTIONS
6549 }
6550 #endif
6551 }
6552 // Copy and return, if we're at the end
6553 if (i == 0)
6554 {
6555 FileObjectCopy (cwdptr, tempCWD);
6556 return 0;
6557 }
6558 }
6559 }
6560 else
6561 {
6562 // If we ended with a . entry,
6563 // just return what we have
6564 if (i == 0)
6565 {
6566 FileObjectCopy (cwdptr, tempCWD);
6567 return 0;
6568 }
6569 else
6570 {
6571 if (i == '\\')
6572 {
6573 while (i == '\\')
6574 {
6575 #ifdef ALLOW_PGMFUNCTIONS
6576 if (mode)
6577 {
6578 temppath2++;
6579 i = *temppath2;
6580 }
6581 else
6582 {
6583 #endif
6584 temppath++;
6585 i = *temppath;
6586 #ifdef ALLOW_PGMFUNCTIONS
6587 }
6588 #endif
6589 }
6590 if (i == 0)
6591 {
6592 FileObjectCopy (cwdptr, tempCWD);
6593 return 0;
6594 }
6595 }
6596 else
6597 {
6598 // Anything else after a dot doesn't make sense
6599 FSerrno = CE_INVALID_ARGUMENT;
6600 return -1;
6601 }
6602 }
6603 }
6604  
6605 break;
6606  
6607 // Second case: the first char is the root backslash
6608 // We will ONLY switch to this case if the first char
6609 // of the path is a backslash
6610 case '\\':
6611 // Increment pointer to second char
6612 #ifdef ALLOW_PGMFUNCTIONS
6613 if (mode)
6614 {
6615 temppath2++;
6616 i = *temppath2;
6617 }
6618 else
6619 {
6620 #endif
6621 temppath++;
6622 i = *temppath;
6623 #ifdef ALLOW_PGMFUNCTIONS
6624 }
6625 #endif
6626 // Can't start the path with multiple backslashes
6627 if (i == '\\')
6628 {
6629 FSerrno = CE_INVALID_ARGUMENT;
6630 return -1;
6631 }
6632  
6633 if (i == 0)
6634 {
6635 // The user is changing directory to
6636 // the root
6637 cwdptr->dirclus = FatRootDirClusterValue;
6638 cwdptr->dirccls = FatRootDirClusterValue;
6639 cwdptr->name[0] = '\\';
6640 for (j = 1; j < 11; j++)
6641 {
6642 cwdptr->name[j] = 0x20;
6643 }
6644 return 0;
6645 }
6646 else
6647 {
6648 // Our first char is the root dir switch
6649 tempCWD->dirclus = FatRootDirClusterValue;
6650 tempCWD->dirccls = FatRootDirClusterValue;
6651 tempCWD->name[0] = '\\';
6652 for (j = 1; j < 11; j++)
6653 {
6654 tempCWD->name[j] = 0x20;
6655 }
6656 }
6657 break;
6658  
6659 default:
6660 // We should be at the beginning of a string of letters/numbers
6661 j = 0;
6662 #ifdef ALLOW_PGMFUNCTIONS
6663 if (mode)
6664 {
6665 while ((i != 0) && (i != '\\') && (j < 12))
6666 {
6667 defaultString[j++] = i;
6668 i = *(++temppath2);
6669 }
6670 }
6671 else
6672 {
6673 #endif
6674 while ((i != 0) && (i != '\\') && (j < 12))
6675 {
6676 defaultString[j++] = i;
6677 i = *(++temppath);
6678 }
6679 #ifdef ALLOW_PGMFUNCTIONS
6680 }
6681 #endif
6682 // We got a whole 12 chars
6683 // There could be more- truncate it
6684 if (j == 12)
6685 {
6686 while ((i != 0) && (i != '\\'))
6687 {
6688 #ifdef ALLOW_PGMFUNCTIONS
6689 if (mode)
6690 {
6691 i = *(++temppath2);
6692 }
6693 else
6694 {
6695 #endif
6696 i = *(++temppath);
6697 #ifdef ALLOW_PGMFUNCTIONS
6698 }
6699 #endif
6700 }
6701 }
6702  
6703 defaultString[j] = 0;
6704  
6705 if (FormatDirName (defaultString, 0) == FALSE)
6706 return -1;
6707  
6708 for (j = 0; j < 11; j++)
6709 {
6710 tempArray[j] = tempCWD->name[j];
6711 tempCWD->name[j] = defaultString[j];
6712 }
6713  
6714 // copy file object over
6715 FileObjectCopy(&gFileTemp, tempCWD);
6716  
6717 // See if the directory is there
6718 if(FILEfind (&gFileTemp, tempCWD, LOOK_FOR_MATCHING_ENTRY, 0) != CE_GOOD)
6719 {
6720 // Couldn't find the DIR
6721 FSerrno = CE_DIR_NOT_FOUND;
6722 return -1;
6723 }
6724 else
6725 {
6726 // Found the file
6727 // Check to make sure it's actually a directory
6728 if (gFileTemp.attributes != ATTR_DIRECTORY)
6729 {
6730 FSerrno = CE_INVALID_ARGUMENT;
6731 return -1;
6732 }
6733  
6734 // Get the new name
6735 for (j = 0; j < 11; j++)
6736 {
6737 tempCWD->name[j] = gFileTemp.name[j];
6738 }
6739 tempCWD->dirclus = gFileTemp.cluster;
6740 tempCWD->dirccls = tempCWD->dirclus;
6741 }
6742  
6743 if (i == 0)
6744 {
6745 // If we're at the end of the string, we're done
6746 FileObjectCopy (cwdptr, tempCWD);
6747 return 0;
6748 }
6749 else
6750 {
6751 while (i == '\\')
6752 {
6753 // If we get to another backslash, increment past it
6754 #ifdef ALLOW_PGMFUNCTIONS
6755 if (mode)
6756 {
6757 temppath2++;
6758 i = *temppath2;
6759 }
6760 else
6761 {
6762 #endif
6763 temppath++;
6764 i = *temppath;
6765 #ifdef ALLOW_PGMFUNCTIONS
6766 }
6767 #endif
6768 if (i == 0)
6769 {
6770 FileObjectCopy (cwdptr, tempCWD);
6771 return 0;
6772 }
6773 }
6774 }
6775 break;
6776 }
6777 } // loop
6778 }
6779  
6780  
6781  
6782 // This string is used by FSgetcwd to return the cwd name if the path
6783 // passed into the function is NULL
6784 char defaultArray [10];
6785  
6786  
6787 /**************************************************************
6788 Function:
6789 char * FSgetcwd (char * path, int numchars)
6790 Summary:
6791 Get the current working directory name
6792 Conditions:
6793 None
6794 Input:
6795 path - Pointer to the array to return the cwd name in
6796 numchars - Number of chars in the path
6797 Return Values:
6798 char * - The cwd name string pointer (path or defaultArray)
6799 NULL - The current working directory name could not be loaded.
6800 Side Effects:
6801 The FSerrno variable will be changed
6802 Description:
6803 The FSgetcwd function will get the name of the current
6804 working directory and return it to the user. The name
6805 will be copied into the buffer pointed to by 'path,'
6806 starting at the root directory and copying as many chars
6807 as possible before the end of the buffer. The buffer
6808 size is indicated by the 'numchars' argument. The first
6809 thing this function will do is load the name of the current
6810 working directory, if it isn't already present. This could
6811 occur if the user switched to the dotdot entry of a
6812 subdirectory immediately before calling this function. The
6813 function will then copy the current working directory name
6814 into the buffer backwards, and insert a backslash character.
6815 Next, the function will continuously switch to the previous
6816 directories and copy their names backwards into the buffer
6817 until it reaches the root. If the buffer overflows, it
6818 will be treated as a circular buffer, and data will be
6819 copied over existing characters, starting at the beginning.
6820 Once the root directory is reached, the text in the buffer
6821 will be swapped, so that the buffer contains as much of the
6822 current working directory name as possible, starting at the
6823 root.
6824 Remarks:
6825 None
6826 **************************************************************/
6827 char * FSgetcwd (char * path, int numchars)
6828 {
6829 // If path is passed in as null, set up a default
6830 // array with 10 characters
6831 char totalchars = (path == NULL) ? 10 : numchars;
6832 char * returnPointer;
6833 char * bufferEnd;
6834 FILEOBJ tempCWD = &gFileTemp;
6835 BYTE bufferOverflow = FALSE;
6836 signed char j;
6837 DWORD curclus;
6838 WORD fHandle, tempindex;
6839 signed int i, index = 0;
6840 char aChar;
6841 DIRENTRY entry;
6842  
6843 FSerrno = CE_GOOD;
6844  
6845 // Set up the return value
6846 if (path == NULL)
6847 returnPointer = defaultArray;
6848 else
6849 {
6850 returnPointer = path;
6851 if (numchars == 0)
6852 {
6853 FSerrno = CE_INVALID_ARGUMENT;
6854 return NULL;
6855 }
6856 }
6857  
6858 bufferEnd = returnPointer + totalchars - 1;
6859  
6860 FileObjectCopy (tempCWD, cwdptr);
6861  
6862 if ((tempCWD->name[0] == '.') &&
6863 (tempCWD->name[1] == '.'))
6864 {
6865 // We last changed directory into a dotdot entry
6866 // Save the value of the current directory
6867 curclus = tempCWD->dirclus;
6868 // Put this dir's dotdot entry into the dirclus
6869 // Our cwd absolutely is not the root
6870 fHandle = 1;
6871 tempCWD->dirccls = tempCWD->dirclus;
6872 entry = Cache_File_Entry (tempCWD,&fHandle, TRUE);
6873 if (entry == NULL)
6874 {
6875 FSerrno = CE_BADCACHEREAD;
6876 return NULL;
6877 }
6878  
6879  
6880 // Get the cluster
6881 TempClusterCalc = GetFullClusterNumber(entry); // Get complete cluster number.
6882  
6883 // For FAT32, if the .. entry is 0, the cluster won't be 0
6884 #ifdef SUPPORT_FAT32
6885 if (TempClusterCalc == VALUE_DOTDOT_CLUSTER_VALUE_FOR_ROOT)
6886 {
6887 tempCWD->dirclus = FatRootDirClusterValue;
6888 }
6889 else
6890 #endif
6891 tempCWD->dirclus = TempClusterCalc;
6892  
6893 tempCWD->dirccls = tempCWD->dirclus;
6894  
6895 // Find the direntry for the entry we were just in
6896 fHandle = 0;
6897 entry = Cache_File_Entry (tempCWD, &fHandle, TRUE);
6898 if (entry == NULL)
6899 {
6900 FSerrno = CE_BADCACHEREAD;
6901 return NULL;
6902 }
6903  
6904 // Get the cluster
6905 TempClusterCalc = GetFullClusterNumber(entry); // Get complete cluster number.
6906  
6907 while ((TempClusterCalc != curclus) ||
6908 ((TempClusterCalc == curclus) &&
6909 (((unsigned char)entry->DIR_Name[0] == 0xE5) || (entry->DIR_Attr == ATTR_VOLUME) || (entry->DIR_Attr == ATTR_LONG_NAME))))
6910 {
6911 fHandle++;
6912 entry = Cache_File_Entry (tempCWD, &fHandle, FALSE);
6913 if (entry == NULL)
6914 {
6915 FSerrno = CE_BADCACHEREAD;
6916 return NULL;
6917 }
6918  
6919 // Get the cluster
6920 TempClusterCalc = GetFullClusterNumber(entry); // Get complete cluster number in a loop.
6921 }
6922 // We've found the entry for the dir we were in
6923 for (i = 0; i < 11; i++)
6924 {
6925 tempCWD->name[i] = entry->DIR_Name[i];
6926 cwdptr->name[i] = entry->DIR_Name[i];
6927 }
6928 // Reset our temp dir back to that cluster
6929 tempCWD->dirclus = curclus;
6930 tempCWD->dirccls = curclus;
6931 // This will set us at the cwd, but it will actually
6932 // have the name in the name field this time
6933 }
6934 // There's actually some kind of name value in the cwd
6935 if (tempCWD->name[0] == '\\')
6936 {
6937 // Easy, our CWD is the root
6938 *returnPointer = '\\';
6939 *(returnPointer + 1) = 0;
6940 return returnPointer;
6941 }
6942 else
6943 {
6944 // Loop until we get back to the root
6945 while (tempCWD->dirclus != FatRootDirClusterValue)
6946 {
6947 j = 10;
6948 while (tempCWD->name[j] == 0x20)
6949 j--;
6950 if (j >= 8)
6951 {
6952 while (j >= 8)
6953 {
6954 *(returnPointer + index++) = tempCWD->name[j--];
6955 // This is a circular buffer
6956 // Any unnecessary values will be overwritten
6957 if (index == totalchars)
6958 {
6959 index = 0;
6960 bufferOverflow = TRUE;
6961 }
6962 }
6963 *(returnPointer + index++) = '.';
6964 if (index == totalchars)
6965 {
6966 index = 0;
6967 bufferOverflow = TRUE;
6968 }
6969 }
6970  
6971 while (tempCWD->name[j] == 0x20)
6972 j--;
6973  
6974 while (j >= 0)
6975 {
6976 *(returnPointer + index++) = tempCWD->name[j--];
6977 // This is a circular buffer
6978 // Any unnecessary values will be overwritten
6979 if (index == totalchars)
6980 {
6981 index = 0;
6982 bufferOverflow = TRUE;
6983 }
6984 }
6985  
6986 // Put a backslash delimiter in front of the dir name
6987 *(returnPointer + index++) = '\\';
6988 if (index == totalchars)
6989 {
6990 index = 0;
6991 bufferOverflow = TRUE;
6992 }
6993  
6994 // Load the previous entry
6995 tempCWD->dirccls = tempCWD->dirclus;
6996 if (GetPreviousEntry (tempCWD))
6997 {
6998 FSerrno = CE_BAD_SECTOR_READ;
6999 return NULL;
7000 }
7001 }
7002 }
7003  
7004 // Point the index back at the last char in the string
7005 index--;
7006  
7007 i = 0;
7008 // Swap the chars in the buffer so they are in the right places
7009 if (bufferOverflow)
7010 {
7011 tempindex = index;
7012 // Swap the overflowed values in the buffer
7013 while ((index - i) > 0)
7014 {
7015 aChar = *(returnPointer + i);
7016 *(returnPointer + i) = * (returnPointer + index);
7017 *(returnPointer + index) = aChar;
7018 index--;
7019 i++;
7020 }
7021  
7022 // Point at the non-overflowed values
7023 i = tempindex + 1;
7024 index = bufferEnd - returnPointer;
7025  
7026 // Swap the non-overflowed values into the right places
7027 while ((index - i) > 0)
7028 {
7029 aChar = *(returnPointer + i);
7030 *(returnPointer + i) = * (returnPointer + index);
7031 *(returnPointer + index) = aChar;
7032 index--;
7033 i++;
7034 }
7035 // All the values should be in the right place now
7036 // Null-terminate the string
7037 *(bufferEnd) = 0;
7038 }
7039 else
7040 {
7041 // There was no overflow, just do one set of swaps
7042 tempindex = index;
7043 while ((index - i) > 0)
7044 {
7045 aChar = *(returnPointer + i);
7046 *(returnPointer + i) = * (returnPointer + index);
7047 *(returnPointer + index) = aChar;
7048 index--;
7049 i++;
7050 }
7051 *(returnPointer + tempindex + 1) = 0;
7052 }
7053  
7054 return returnPointer;
7055 }
7056  
7057  
7058 /**************************************************************************
7059 Function:
7060 void GetPreviousEntry (FSFILE * fo)
7061 Summary:
7062 Get the file entry info for the parent dir of the specified dir
7063 Conditions:
7064 Should not be called by the user.
7065 Input:
7066 fo - The file to get the previous entry of
7067 Return Values:
7068  
7069 -1 - The previous entry could not be retrieved
7070 Side Effects:
7071 None
7072 Description:
7073 The GetPreviousEntry function is used by the FSgetcwd function to
7074 load the previous (parent) directory. This function will load the
7075 parent directory and then search through the file entries in that
7076 directory for one that matches the cluster number of the original
7077 directory. When the matching entry is found, the name of the
7078 original directory is copied into the 'fo' FSFILE object.
7079 Remarks:
7080 None.
7081 **************************************************************************/
7082  
7083 BYTE GetPreviousEntry (FSFILE * fo)
7084 {
7085 BYTE i;
7086 WORD fHandle = 1;
7087 DWORD dirclus;
7088 DIRENTRY dirptr;
7089  
7090 // Load the previous entry
7091 dirptr = Cache_File_Entry (fo, &fHandle, TRUE);
7092 if (dirptr == NULL)
7093 return -1;
7094  
7095 // Get the cluster
7096 TempClusterCalc = GetFullClusterNumber(dirptr); // Get complete cluster number.
7097  
7098 if (TempClusterCalc == VALUE_DOTDOT_CLUSTER_VALUE_FOR_ROOT)
7099 {
7100 // The previous directory is the root
7101 fo->name[0] = '\\';
7102 for (i = 0; i < 11; i++)
7103 {
7104 fo->name[i] = 0x20;
7105 }
7106 fo->dirclus = FatRootDirClusterValue;
7107 fo->dirccls = FatRootDirClusterValue;
7108 }
7109 else
7110 {
7111 // Get the directory name
7112 // Save the previous cluster value
7113 // Get the cluster
7114  
7115 dirclus = TempClusterCalc;
7116 fo->dirclus = TempClusterCalc;
7117 fo->dirccls = TempClusterCalc;
7118  
7119  
7120 // Load the previous previous cluster
7121 dirptr = Cache_File_Entry (fo, &fHandle, TRUE);
7122 if (dirptr == NULL)
7123 return -1;
7124  
7125 // Get the cluster
7126 TempClusterCalc = GetFullClusterNumber(dirptr); // Get complete cluster number.
7127 #ifdef SUPPORT_FAT32
7128 // If we're using FAT32 and the previous previous cluster is the root, the
7129 // value in the dotdot entry will be 0, but the actual cluster won't
7130 if (TempClusterCalc == VALUE_DOTDOT_CLUSTER_VALUE_FOR_ROOT)
7131 {
7132 fo->dirclus = FatRootDirClusterValue;
7133 }
7134 else
7135 #endif
7136 fo->dirclus = TempClusterCalc;
7137  
7138 fo->dirccls = fo->dirclus;
7139  
7140 fHandle = 0;
7141 dirptr = Cache_File_Entry (fo, &fHandle, TRUE);
7142 if (dirptr == NULL)
7143 return -1;
7144 // Look through it until we get the name
7145 // of the previous cluster
7146 // Get the cluster
7147 TempClusterCalc = GetFullClusterNumber(dirptr); // Get complete cluster number.
7148 while ((TempClusterCalc != dirclus) ||
7149 ((TempClusterCalc == dirclus) &&
7150 (((unsigned char)dirptr->DIR_Name[0] == 0xE5) || (dirptr->DIR_Attr == ATTR_VOLUME) || (dirptr->DIR_Attr == ATTR_LONG_NAME))))
7151 {
7152 // Look through the entries until we get the
7153 // right one
7154 dirptr = Cache_File_Entry (fo, &fHandle, FALSE);
7155 if (dirptr == NULL)
7156 return -1;
7157 fHandle++;
7158  
7159 TempClusterCalc = GetFullClusterNumber(dirptr); // Get complete cluster number in a loop.
7160 }
7161  
7162 // The name should be in the entry now
7163 // Copy the actual directory location back
7164 for (i = 0; i < 11; i++)
7165 {
7166 fo->name[i] = dirptr->DIR_Name[i];
7167 }
7168 fo->dirclus = dirclus;
7169 fo->dirccls = dirclus;
7170 }
7171 return 0;
7172 }
7173  
7174  
7175 /**************************************************************************
7176 Function:
7177 int FSmkdir (char * path)
7178 Summary:
7179 Create a directory
7180 Conditions:
7181 None
7182 Input:
7183 path - The path of directories to create.
7184 Return Values:
7185  
7186 EOF - The specified directory could not be created
7187 Side Effects:
7188 Will create all non-existent directories in the path. The FSerrno
7189 variable will be changed.
7190 Description:
7191 The FSmkdir function passes a RAM pointer to the path to the
7192 mkdirhelper function.
7193 Remarks:
7194 None
7195 **************************************************************************/
7196  
7197 #ifdef ALLOW_WRITES
7198 int FSmkdir (char * path)
7199 {
7200 return mkdirhelper (0, path, NULL);
7201 }
7202  
7203 /**************************************************************************
7204 Function:
7205 int FSmkdirpgm (const rom char * path)
7206 Summary:
7207 Create a directory with a path in ROM on PIC18
7208 Conditions:
7209 None
7210 Input:
7211 path - The path of directories to create (ROM)
7212 Return Values:
7213  
7214 EOF - The specified directory could not be created
7215 Side Effects:
7216 Will create all non-existent directories in the path. The FSerrno
7217 variable will be changed.
7218 Description:
7219 The FSmkdirpgm function passes a PIC18 ROM path pointer to the
7220 mkdirhelper function.
7221 Remarks:
7222 This function is for use with PIC18 when passing arugments in ROM
7223 **************************************************************************/
7224  
7225 #ifdef ALLOW_PGMFUNCTIONS
7226 int FSmkdirpgm (const rom char * path)
7227 {
7228 return mkdirhelper (1, NULL, path);
7229 }
7230 #endif
7231  
7232  
7233 /*************************************************************************
7234 Function:
7235 // PIC24/30/33/32
7236 int mkdirhelper (BYTE mode, char * ramptr, char * romptr)
7237 // PIC18
7238 int mkdirhelper (BYTE mode, char * ramptr, const rom char * romptr)
7239 Summary:
7240 Helper function for FSmkdir
7241 Conditions:
7242 None
7243 Input:
7244 mode - Indicates which path pointer to use
7245 ramptr - Pointer to the path specified in RAM
7246 romptr - Pointer to the path specified in ROM
7247 Return Values:
7248  
7249 -1 - Directory could not be created
7250 Side Effects:
7251 Will create all non-existant directories in the path.
7252 The FSerrno variable will be changed.
7253 Description:
7254 This helper function is used by the FSchdir function. If the path
7255 argument is specified in ROM for PIC18 this function will be able
7256 to parse it correctly. This function will first scan through the path
7257 to ensure that any DIR names don't exceed 11 characters. It will then
7258 backup the current working directory and begin changing directories
7259 through the path until it reaches a directory than can't be changed to.
7260 It will then create the specified directory and change directories to
7261 the new directory. The function will continue creating and changing to
7262 directories until the end of the path is reached. The function will
7263 then restore the original current working directory.
7264 Remarks:
7265 None
7266 **************************************************************************/
7267  
7268 #ifdef ALLOW_PGMFUNCTIONS
7269 int mkdirhelper (BYTE mode, char * ramptr, const rom char * romptr)
7270 #else
7271 int mkdirhelper (BYTE mode, char * ramptr, char * romptr)
7272 #endif
7273 {
7274 BYTE i, j;
7275 char * temppath = ramptr;
7276 #ifdef ALLOW_PGMFUNCTIONS
7277 rom char * temppath2 = romptr;
7278 #endif
7279 char tempArray[13];
7280 FILEOBJ tempCWD = &tempCWDobj;
7281  
7282 #ifdef __18CXX
7283 char dotdotPath[] = "..";
7284 #endif
7285  
7286 FSerrno = CE_GOOD;
7287  
7288 if (MDD_WriteProtectState())
7289 {
7290 FSerrno = CE_WRITE_PROTECTED;
7291 return (-1);
7292 }
7293  
7294 #ifdef ALLOW_PGMFUNCTIONS
7295 if (mode == 1)
7296 {
7297 // Scan for too-long file names
7298 while (1)
7299 {
7300 i = 0;
7301 while((*temppath2 != 0) && (*temppath2 != '.')&& (*temppath2 != '\\'))
7302 {
7303 temppath2++;
7304 i++;
7305 }
7306 if (i > 8)
7307 {
7308 FSerrno = CE_INVALID_ARGUMENT;
7309 return -1;
7310 }
7311 if (*temppath2 == '.')
7312 {
7313 temppath2++;
7314 i = 0;
7315 while ((*temppath2 != 0) && (*temppath2 != '\\'))
7316 {
7317 temppath2++;
7318 i++;
7319 }
7320 if (i > 3)
7321 {
7322 FSerrno = CE_INVALID_ARGUMENT;
7323 return -1;
7324 }
7325 }
7326 while (*temppath2 == '\\')
7327 temppath2++;
7328 if (*temppath2 == 0)
7329 break;
7330 }
7331 }
7332 else
7333 #endif
7334 // Scan for too-long file names
7335 while (1)
7336 {
7337 i = 0;
7338 while((*temppath != 0) && (*temppath != '.')&& (*temppath != '\\'))
7339 {
7340 temppath++;
7341 i++;
7342 }
7343 if (i > 8)
7344 {
7345 FSerrno = CE_INVALID_ARGUMENT;
7346 return -1;
7347 }
7348 if (*temppath == '.')
7349 {
7350 temppath++;
7351 i = 0;
7352 while ((*temppath != 0) && (*temppath != '\\'))
7353 {
7354 temppath++;
7355 i++;
7356 }
7357 if (i > 3)
7358 {
7359 FSerrno = CE_INVALID_ARGUMENT;
7360 return -1;
7361 }
7362 }
7363 while (*temppath == '\\')
7364 temppath++;
7365 if (*temppath == 0)
7366 break;
7367 }
7368  
7369 temppath = ramptr;
7370 #ifdef ALLOW_PGMFUNCTIONS
7371 temppath2 = romptr;
7372 #endif
7373  
7374 // We're going to be moving the CWD
7375 // Back up the CWD
7376 FileObjectCopy (tempCWD, cwdptr);
7377  
7378 // get to the target directory
7379 while (1)
7380 {
7381 #ifdef ALLOW_PGMFUNCTIONS
7382 if (mode == 1)
7383 i = *temppath2;
7384 else
7385 #endif
7386 i = *temppath;
7387  
7388 if (i == '.')
7389 {
7390 #ifdef ALLOW_PGMFUNCTIONS
7391 if (mode == 1)
7392 {
7393 temppath2++;
7394 i = *temppath2;
7395 }
7396 else
7397 {
7398 #endif
7399 temppath++;
7400 i = *temppath;
7401 #ifdef ALLOW_PGMFUNCTIONS
7402 }
7403 #endif
7404  
7405 if ((i != '.') && (i != 0) && (i != '\\'))
7406 {
7407 FSerrno = CE_INVALID_ARGUMENT;
7408 return -1;
7409 }
7410  
7411 if (i == '.')
7412 {
7413 if (cwdptr->dirclus == FatRootDirClusterValue)
7414 {
7415 // If we try to change to the .. from the
7416 // root, operation fails
7417 FSerrno = CE_INVALID_ARGUMENT;
7418 return -1;
7419 }
7420 #ifdef ALLOW_PGMFUNCTIONS
7421 if (mode == 1)
7422 {
7423 temppath2++;
7424 i = *temppath2;
7425 }
7426 else
7427 {
7428 #endif
7429 temppath++;
7430 i = *temppath;
7431 #ifdef ALLOW_PGMFUNCTIONS
7432 }
7433 #endif
7434 if ((i != '\\') && (i != 0))
7435 {
7436 FSerrno = CE_INVALID_ARGUMENT;
7437 return -1;
7438 }
7439 // dotdot entry
7440 #ifndef __18CXX
7441 FSchdir ("..");
7442 #else
7443 FSchdir (dotdotPath);
7444 #endif
7445 }
7446 // Skip past any backslashes
7447 while (i == '\\')
7448 {
7449 #ifdef ALLOW_PGMFUNCTIONS
7450 if (mode == 1)
7451 {
7452 temppath2++;
7453 i = *temppath2;
7454 }
7455 else
7456 {
7457 #endif
7458 temppath++;
7459 i = *temppath;
7460 #ifdef ALLOW_PGMFUNCTIONS
7461 }
7462 #endif
7463 }
7464 if (i == 0)
7465 {
7466 // No point in creating a dot or dotdot entry directly
7467 FileObjectCopy (cwdptr, tempCWD);
7468 FSerrno = CE_INVALID_ARGUMENT;
7469 return -1;
7470 }
7471 }
7472 else
7473 {
7474 if (i == '\\')
7475 {
7476 // Start at the root
7477 cwdptr->dirclus = FatRootDirClusterValue;
7478 cwdptr->dirccls = FatRootDirClusterValue;
7479 cwdptr->name[0] = '\\';
7480 for (i = 1; i < 11; i++)
7481 {
7482 cwdptr->name[i] = 0x20;
7483 }
7484  
7485 #ifdef ALLOW_PGMFUNCTIONS
7486 if (mode == 1)
7487 {
7488 temppath2++;
7489 i = *temppath2;
7490 }
7491 else
7492 {
7493 #endif
7494 temppath++;
7495 i = *temppath;
7496 #ifdef ALLOW_PGMFUNCTIONS
7497 }
7498 #endif
7499 // If we just got two backslashes in a row at the
7500 // beginning of the path, the function fails
7501 if (i == '\\')
7502 {
7503 FileObjectCopy (cwdptr, tempCWD);
7504 FSerrno = CE_INVALID_ARGUMENT;
7505 return -1;
7506 }
7507 if (i == 0)
7508 {
7509 // We can't make the root dir
7510 FileObjectCopy (cwdptr, tempCWD);
7511 FSerrno = CE_INVALID_ARGUMENT;
7512 return -1;
7513 }
7514 }
7515 else
7516 {
7517 break;
7518 }
7519 }
7520 }
7521  
7522 tempArray[12] = 0;
7523 while (1)
7524 {
7525 while(1)
7526 {
7527 #ifdef ALLOW_PGMFUNCTIONS
7528 if (mode == 1)
7529 {
7530 // Change directories as specified
7531 i = *temppath2;
7532 j = 0;
7533 // Parse the next token
7534 while ((i != 0) && (i != '\\') && (j < 12))
7535 {
7536 tempArray[j++] = i;
7537 temppath2++;
7538 i = *temppath2;
7539 }
7540 }
7541 else
7542 {
7543 #endif
7544 // Change directories as specified
7545 i = *temppath;
7546 j = 0;
7547 // Parse the next token
7548 while ((i != 0) && (i != '\\') && (j < 12))
7549 {
7550 tempArray[j++] = i;
7551 temppath++;
7552 i = *temppath;
7553 }
7554 #ifdef ALLOW_PGMFUNCTIONS
7555 }
7556 #endif
7557 tempArray[j] = 0;
7558  
7559 if (tempArray[0] == '.')
7560 {
7561 if ((tempArray[1] != 0) && (tempArray[1] != '.'))
7562 {
7563 FileObjectCopy (cwdptr, tempCWD);
7564 FSerrno = CE_INVALID_ARGUMENT;
7565 return -1;
7566 }
7567 if ((tempArray[1] == '.') && (tempArray[2] != 0))
7568 {
7569 FileObjectCopy (cwdptr, tempCWD);
7570 FSerrno = CE_INVALID_ARGUMENT;
7571 return -1;
7572 }
7573 }
7574  
7575 // Try to change to it
7576 // If you can't we need to create it
7577 if (FSchdir (tempArray))
7578 {
7579 break;
7580 }
7581 else
7582 {
7583 // We changed into the directory
7584 while (i == '\\')
7585 {
7586 // Next char is a backslash
7587 // Move past it
7588 #ifdef ALLOW_PGMFUNCTIONS
7589 if (mode == 1)
7590 {
7591 temppath2++;
7592 i = *temppath2;
7593 }
7594 else
7595 {
7596 #endif
7597 temppath++;
7598 i = *temppath;
7599 #ifdef ALLOW_PGMFUNCTIONS
7600 }
7601 #endif
7602 }
7603 // If it's the last one, return success
7604 if (i == 0)
7605 {
7606 FileObjectCopy (cwdptr, tempCWD);
7607 return 0;
7608 }
7609 }
7610 }
7611  
7612 // Create a dir here
7613 if (!CreateDIR (tempArray))
7614 {
7615 FileObjectCopy (cwdptr, tempCWD);
7616 return -1;
7617 }
7618  
7619 // Try to change to that directory
7620 if (FSchdir (tempArray))
7621 {
7622 FileObjectCopy (cwdptr, tempCWD);
7623 FSerrno = CE_BADCACHEREAD;
7624 return -1;
7625 }
7626  
7627 #ifdef ALLOW_PGMFUNCTIONS
7628 if (mode == 1)
7629 {
7630 // Check for another backslash
7631 while (*temppath2 == '\\')
7632 {
7633 temppath2++;
7634 i = *temppath2;
7635 }
7636 }
7637 else
7638 {
7639 #endif
7640 while (*temppath == '\\')
7641 {
7642 temppath++;
7643 i = *temppath;
7644 }
7645 #ifdef ALLOW_PGMFUNCTIONS
7646 }
7647 #endif
7648  
7649 // Check to see if we're at the end of the path string
7650 if (i == 0)
7651 {
7652 // We already have one
7653 FileObjectCopy (cwdptr, tempCWD);
7654 return 0;
7655 }
7656 }
7657 }
7658  
7659  
7660 /**************************************************************************
7661 Function:
7662 int CreateDIR (char * path)
7663 Summary:
7664 FSmkdir helper function to create a directory
7665 Conditions:
7666 This function should not be called by the user.
7667 Input:
7668 path - The name of the dir to create
7669 Return Values:
7670 TRUE - Directory was created successfully
7671 FALSE - Directory could not be created.
7672 Side Effects:
7673 Any unwritten data in the data buffer or the FAT buffer will be written
7674 to the device.
7675 Description:
7676 The CreateDIR function is a helper function for the mkdirhelper
7677 function. The CreateDIR function will create a new file entry for
7678 a directory and assign a cluster to it. It will erase the cluster
7679 and write a dot and dotdot entry to it.
7680 Remarks:
7681 None.
7682 **************************************************************************/
7683  
7684 int CreateDIR (char * path)
7685 {
7686 FSFILE * dirEntryPtr = &gFileTemp;
7687 DIRENTRY dir;
7688 WORD handle = 0;
7689 DWORD dot, dotdot;
7690 BYTE i;
7691  
7692 for (i = 0; i < 12; i++)
7693 {
7694 defaultString[i] = *(path + i);
7695 }
7696  
7697 if (FormatDirName(defaultString, 0) == FALSE)
7698 {
7699 FSerrno = CE_INVALID_FILENAME;
7700 return FALSE;
7701 }
7702  
7703 // Copy name into file object
7704 for (i = 0; i < 11; i++)
7705 {
7706 dirEntryPtr->name[i] = defaultString[i];
7707 }
7708  
7709 dirEntryPtr->dirclus = cwdptr->dirclus;
7710 dirEntryPtr->dirccls = cwdptr->dirccls;
7711 dirEntryPtr->cluster = 0;
7712 dirEntryPtr->ccls = 0;
7713 dirEntryPtr->dsk = cwdptr->dsk;
7714  
7715 // Create a directory entry
7716 if(CreateFileEntry(dirEntryPtr, &handle, DIRECTORY) != CE_GOOD)
7717 {
7718 return FALSE;
7719 }
7720 else
7721 {
7722 if (gNeedFATWrite)
7723 if(WriteFAT (dirEntryPtr->dsk, 0, 0, TRUE))
7724 {
7725 FSerrno = CE_WRITE_ERROR;
7726 return FALSE;
7727 }
7728 // Zero that cluster
7729 if (dirEntryPtr->dirclus == FatRootDirClusterValue)
7730 dotdot = 0;
7731 else
7732 dotdot = dirEntryPtr->dirclus;
7733 dirEntryPtr->dirccls = dirEntryPtr->dirclus;
7734 dir = Cache_File_Entry(dirEntryPtr, &handle, TRUE);
7735 if (dir == NULL)
7736 {
7737 FSerrno = CE_BADCACHEREAD;
7738 return FALSE;
7739 }
7740  
7741 // Get the cluster
7742 dot = GetFullClusterNumber(dir); // Get complete cluster number.
7743  
7744 if (writeDotEntries (dirEntryPtr->dsk, dot, dotdot))
7745 return TRUE;
7746 else
7747 return FALSE;
7748  
7749 }
7750 }
7751  
7752  
7753 /***********************************************************************************
7754 Function:
7755 BYTE writeDotEntries (DISK * disk, DWORD dotAddress, DWORD dotdotAddress)
7756 Summary:
7757 Create dot and dotdot entries in a non-root directory
7758 Conditions:
7759 This function should not be called by the user.
7760 Input:
7761 disk - The global disk structure
7762 dotAddress - The cluster the current dir is in
7763 dotdotAddress - The cluster the previous directory was in
7764 Return Values:
7765 TRUE - The dot and dotdot entries were created
7766 FALSE - The dot and dotdot entries could not be created in the new directory
7767 Side Effects:
7768 None
7769 Description:
7770 The writeDotEntries function will create and write dot and dotdot entries
7771 to a newly created directory.
7772 Remarks:
7773 None.
7774 ***********************************************************************************/
7775  
7776 BYTE writeDotEntries (DISK * disk, DWORD dotAddress, DWORD dotdotAddress)
7777 {
7778 WORD i;
7779 WORD size;
7780 _DIRENTRY entry;
7781 DIRENTRY entryptr = &entry;
7782 DWORD sector;
7783  
7784 gBufferOwner = NULL;
7785  
7786 size = sizeof (_DIRENTRY);
7787  
7788 memset(disk->buffer, 0x00, MEDIA_SECTOR_SIZE);
7789  
7790 entry.DIR_Name[0] = '.';
7791  
7792 for (i = 1; i < 11; i++)
7793 {
7794 entry.DIR_Name[i] = 0x20;
7795 }
7796 entry.DIR_Attr = ATTR_DIRECTORY;
7797 entry.DIR_NTRes = 0x00;
7798  
7799 entry.DIR_FstClusLO = (WORD)(dotAddress & 0x0000FFFF); // Lower 16 bit address
7800  
7801 #ifdef SUPPORT_FAT32 // If FAT32 supported.
7802 entry.DIR_FstClusHI = (WORD)((dotAddress & 0x0FFF0000)>> 16); // Higher 16 bit address. FAT32 uses only 28 bits. Mask even higher nibble also.
7803 #else // If FAT32 support not enabled
7804 entry.DIR_FstClusHI = 0;
7805 #endif
7806  
7807 entry.DIR_FileSize = 0x00;
7808  
7809 // Times need to be the same as the times in the directory entry
7810  
7811 // Set dir date for uncontrolled clock source
7812 #ifdef INCREMENTTIMESTAMP
7813 entry.DIR_CrtTimeTenth = 0xB2;
7814 entry.DIR_CrtTime = 0x7278;
7815 entry.DIR_CrtDate = 0x32B0;
7816 entry.DIR_LstAccDate = 0x0000;
7817 entry.DIR_WrtTime = 0x0000;
7818 entry.DIR_WrtDate = 0x0000;
7819 #endif
7820  
7821 #ifdef USEREALTIMECLOCK
7822 entry.DIR_CrtTimeTenth = gTimeCrtMS; // millisecond stamp
7823 entry.DIR_CrtTime = gTimeCrtTime; // time created //
7824 entry.DIR_CrtDate = gTimeCrtDate; // date created (1/1/2004)
7825 entry.DIR_LstAccDate = 0x0000; // Last Access date
7826 entry.DIR_WrtTime = 0x0000; // last update time
7827 entry.DIR_WrtDate = 0x0000; // last update date
7828 #endif
7829  
7830 #ifdef USERDEFINEDCLOCK
7831 entry.DIR_CrtTimeTenth = gTimeCrtMS; // millisecond stamp
7832 entry.DIR_CrtTime = gTimeCrtTime; // time created //
7833 entry.DIR_CrtDate = gTimeCrtDate; // date created (1/1/2004)
7834 entry.DIR_LstAccDate = 0x0000; // Last Access date
7835 entry.DIR_WrtTime = 0x0000; // last update time
7836 entry.DIR_WrtDate = 0x0000; // last update date
7837 #endif
7838  
7839 for (i = 0; i < size; i++)
7840 {
7841 *(disk->buffer + i) = *((char *)entryptr + i);
7842 }
7843 entry.DIR_Name[1] = '.';
7844  
7845 entry.DIR_FstClusLO = (WORD)(dotdotAddress & 0x0000FFFF); // Lower 16 bit address
7846  
7847 #ifdef SUPPORT_FAT32 // If FAT32 supported.
7848 entry.DIR_FstClusHI = (WORD)((dotdotAddress & 0x0FFF0000)>> 16); // Higher 16 bit address. FAT32 uses only 28 bits. Mask even higher nibble also.
7849 #else // If FAT32 support not enabled
7850 entry.DIR_FstClusHI = 0;
7851 #endif
7852  
7853  
7854 for (i = 0; i < size; i++)
7855 {
7856 *(disk->buffer + i + size) = *((char *)entryptr + i);
7857 }
7858  
7859 sector = Cluster2Sector (disk, dotAddress);
7860  
7861 if (MDD_SectorWrite(sector, disk->buffer, FALSE) == FALSE)
7862 {
7863 FSerrno = CE_WRITE_ERROR;
7864 return FALSE;
7865 }
7866  
7867 return TRUE;
7868 }
7869  
7870 // This array is used to prevent a stack frame error
7871 #ifdef __18CXX
7872 char tempArray[13] = " ";
7873 #endif
7874  
7875  
7876 /**************************************************************************
7877 Function:
7878 int FSrmdir (char * path)
7879 Summary:
7880 Delete a directory
7881 Conditions:
7882 None
7883 Input:
7884 path - The path of the directory to remove
7885 rmsubdirs -
7886 - TRUE - All sub-dirs and files in the target dir will be removed
7887 - FALSE - FSrmdir will not remove non-empty directories
7888 Return Values:
7889  
7890 EOF - The specified directory could not be deleted
7891 Side Effects:
7892 The FSerrno variable will be changed.
7893 Description:
7894 The FSrmdir function passes a RAM pointer to the path to the
7895 rmdirhelper function.
7896 Remarks:
7897 None.
7898 **************************************************************************/
7899  
7900 int FSrmdir (char * path, unsigned char rmsubdirs)
7901 {
7902 return rmdirhelper (0, path, NULL, rmsubdirs);
7903 }
7904  
7905 /**************************************************************************
7906 Function:
7907 int FSrmdirpgm (const rom char * path)
7908 Summary:
7909 Delete a directory with a path in ROM on PIC18
7910 Conditions:
7911 None.
7912 Input:
7913 path - The path of the directory to remove (ROM)
7914 rmsubdirs -
7915 - TRUE - All sub-dirs and files in the target dir will be removed
7916 - FALSE - FSrmdir will not remove non-empty directories
7917 Return Values:
7918  
7919 EOF - The specified directory could not be deleted
7920 Side Effects:
7921 The FSerrno variable will be changed.
7922 Description:
7923 The FSrmdirpgm function passes a PIC18 ROM path pointer to the
7924 rmdirhelper function.
7925 Remarks:
7926 This function is for use with PIC18 when passing arguments in ROM.
7927 **************************************************************************/
7928  
7929 #ifdef ALLOW_PGMFUNCTIONS
7930 int FSrmdirpgm (const rom char * path, unsigned char rmsubdirs)
7931 {
7932 return rmdirhelper (1, NULL, path, rmsubdirs);
7933 }
7934 #endif
7935  
7936 /************************************************************************************************
7937 Function:
7938 // PIC24/30/33/32
7939 int rmdirhelper (BYTE mode, char * ramptr, char * romptr, unsigned char rmsubdirs)
7940 // PIC18
7941 int rmdirhelper (BYTE mode, char * ramptr, const rom char * romptr, unsigned char rmsubdirs)
7942 Summary:
7943 Helper function for FSrmdir
7944 Conditions:
7945 This function should not be called by the user.
7946 Input:
7947 path - The path of the dir to delete
7948 rmsubdirs -
7949 - TRUE - Remove all sub-directories and files in the directory
7950 - FALSE - Non-empty directories can not be removed
7951 Return Values:
7952  
7953 EOF - The specified directory could not be removed.
7954 Side Effects:
7955 The FSerrno variable will be changed.
7956 Description:
7957 This helper function is used by the FSmkdir function. If the path
7958 argument is specified in ROM for PIC18 this function will be able
7959 to parse it correctly. This function will first change to the
7960 specified directory. If the rmsubdirs argument is FALSE the function
7961 will search through the directory to ensure that it is empty and then
7962 remove it. If the rmsubdirs argument is TRUE the function will also
7963 search through the directory for subdirectories or files. When the
7964 function finds a file, the file will be erased. When the function
7965 finds a subdirectory, it will switch to the subdirectory and begin
7966 removing all of the files in that subdirectory. Once the subdirectory
7967 is empty, the function will switch back to the original directory.
7968 return to the original position in that directory, and continue removing
7969 files. Once the specified directory is empty, the function will
7970 change to the parent directory, search through it for the directory
7971 to remove, and then erase that directory.
7972 Remarks:
7973 None.
7974 ************************************************************************************************/
7975  
7976 #ifdef ALLOW_PGMFUNCTIONS
7977 int rmdirhelper (BYTE mode, char * ramptr, const rom char * romptr, unsigned char rmsubdirs)
7978 #else
7979 int rmdirhelper (BYTE mode, char * ramptr, char * romptr, unsigned char rmsubdirs)
7980 #endif
7981 {
7982 FILEOBJ tempCWD = &tempCWDobj;
7983 FILEOBJ fo = &gFileTemp;
7984 DIRENTRY entry;
7985 WORD handle = 0;
7986 WORD handle2;
7987 WORD subDirDepth;
7988 BYTE Index, Index2;
7989  
7990 #ifndef __18CXX
7991 char tempArray[13] = " ";
7992 #else
7993 char dotdotname[] = "..";
7994 #endif
7995  
7996 FSerrno = CE_GOOD;
7997  
7998 // Back up the current working directory
7999 FileObjectCopy (tempCWD, cwdptr);
8000  
8001 #ifdef ALLOW_PGMFUNCTIONS
8002 if (mode)
8003 {
8004 if (chdirhelper (1, NULL, romptr))
8005 {
8006 FSerrno = CE_DIR_NOT_FOUND;
8007 return -1;
8008 }
8009 }
8010 else
8011 {
8012 #endif
8013 if (FSchdir (ramptr))
8014 {
8015 FSerrno = CE_DIR_NOT_FOUND;
8016 return -1;
8017 }
8018 #ifdef ALLOW_PGMFUNCTIONS
8019 }
8020 #endif
8021  
8022 // Make sure we aren't trying to remove the root dir or the CWD
8023 if ((cwdptr->dirclus == FatRootDirClusterValue) || (cwdptr->dirclus == tempCWD->dirclus))
8024 {
8025 FileObjectCopy (cwdptr, tempCWD);
8026 FSerrno = CE_INVALID_ARGUMENT;
8027 return -1;
8028 }
8029  
8030 handle++;
8031 entry = Cache_File_Entry (cwdptr, &handle, TRUE);
8032  
8033 if (entry == NULL)
8034 {
8035 FileObjectCopy (cwdptr, tempCWD);
8036 FSerrno = CE_BADCACHEREAD;
8037 return -1;
8038 }
8039  
8040 handle++;
8041 entry = Cache_File_Entry (cwdptr, &handle, FALSE);
8042 if (entry == NULL)
8043 {
8044 FileObjectCopy (cwdptr, tempCWD);
8045 FSerrno = CE_BADCACHEREAD;
8046 return -1;
8047 }
8048 // Don't remove subdirectories and sub-files
8049 if (!rmsubdirs)
8050 {
8051 while (entry->DIR_Name[0] != 0)
8052 {
8053 if ((unsigned char)entry->DIR_Name[0] != 0xE5)
8054 {
8055 FileObjectCopy (cwdptr, tempCWD);
8056 FSerrno = CE_DIR_NOT_EMPTY;
8057 return -1;
8058 }
8059 handle++;
8060 entry = Cache_File_Entry (cwdptr, &handle, FALSE);
8061 if ((entry == NULL))
8062 {
8063 FileObjectCopy (cwdptr, tempCWD);
8064 FSerrno = CE_BADCACHEREAD;
8065 return -1;
8066 }
8067 }
8068 }
8069 else
8070 {
8071 // Do remove subdirectories and sub-files
8072 dirCleared = FALSE;
8073 subDirDepth = 0;
8074  
8075 while (!dirCleared)
8076 {
8077 if (entry->DIR_Name[0] != 0)
8078 {
8079 if (((unsigned char)entry->DIR_Name[0] != 0xE5) && (entry->DIR_Attr != ATTR_VOLUME) && (entry->DIR_Attr != ATTR_LONG_NAME))
8080 {
8081 if ((entry->DIR_Attr & ATTR_DIRECTORY) == ATTR_DIRECTORY)
8082 {
8083 // We have a directory
8084 subDirDepth++;
8085 for (Index = 0; (Index < DIR_NAMESIZE) && (entry->DIR_Name[Index] != 0x20); Index++)
8086 {
8087 tempArray[Index] = entry->DIR_Name[Index];
8088 }
8089 if (entry->DIR_Name[8] != 0x20)
8090 {
8091 tempArray[Index++] = '.';
8092 for (Index2 = 0; (Index2 < DIR_EXTENSION) && (entry->DIR_Name[Index2 + DIR_NAMESIZE] != 0x20); Index2++)
8093 {
8094 tempArray[Index++] = entry->DIR_Name[Index2 + DIR_NAMESIZE];
8095 }
8096 }
8097 tempArray[Index] = 0;
8098 // Change to the subdirectory
8099 if (FSchdir (tempArray))
8100 {
8101 FileObjectCopy (cwdptr, tempCWD);
8102 FSerrno = CE_DIR_NOT_FOUND;
8103 return -1;
8104 }
8105 else
8106 {
8107 // Make sure we're not trying to delete the CWD
8108 if (cwdptr->dirclus == tempCWD->dirclus)
8109 {
8110 FileObjectCopy (cwdptr, tempCWD);
8111 FSerrno = CE_INVALID_ARGUMENT;
8112 return -1;
8113 }
8114 }
8115 handle = 2;
8116 recache = TRUE;
8117 }
8118 else
8119 {
8120 memset (tempArray, 0x00, 12);
8121 for (Index = 0; Index < 11; Index++)
8122 {
8123 fo->name[Index] = entry->DIR_Name[Index];
8124 }
8125  
8126 fo->dsk = &gDiskData;
8127  
8128 fo->entry = handle;
8129 fo->dirclus = cwdptr->dirclus;
8130 fo->dirccls = cwdptr->dirccls;
8131 fo->cluster = 0;
8132 fo->ccls = 0;
8133  
8134 if (FILEerase(fo, &handle, TRUE))
8135 {
8136 FileObjectCopy (cwdptr, tempCWD);
8137 FSerrno = CE_ERASE_FAIL;
8138 return -1;
8139 }
8140 else
8141 {
8142 handle++;
8143 }
8144 } // Check to see if it's a DIR entry
8145 }// Check non-dir entry to see if its a valid file
8146 else
8147 {
8148 handle++;
8149 }
8150 if (recache)
8151 {
8152 recache = FALSE;
8153 cwdptr->dirccls = cwdptr->dirclus;
8154 entry = Cache_File_Entry (cwdptr, &handle, TRUE);
8155 }
8156 else
8157 {
8158 entry = Cache_File_Entry (cwdptr, &handle, FALSE);
8159 }
8160 if (entry == NULL)
8161 {
8162 FileObjectCopy (cwdptr, tempCWD);
8163 FSerrno = CE_BADCACHEREAD;
8164 return -1;
8165 }
8166 }
8167 else
8168 {
8169 // We have reached the end of the directory
8170 if (subDirDepth != 0)
8171 {
8172 handle2 = 0;
8173  
8174 cwdptr->dirccls = cwdptr->dirclus;
8175 entry = Cache_File_Entry (cwdptr, &handle2, TRUE);
8176 if (entry == NULL)
8177 {
8178 FileObjectCopy (cwdptr, tempCWD);
8179 FSerrno = CE_BADCACHEREAD;
8180 return -1;
8181 }
8182  
8183 // Get the cluster
8184 handle2 = GetFullClusterNumber(entry); // Get complete cluster number.
8185  
8186 #ifndef __18CXX
8187 if (FSchdir (".."))
8188 #else
8189 if (FSchdir (dotdotname))
8190 #endif
8191 {
8192 FileObjectCopy (cwdptr, tempCWD);
8193 FSerrno = CE_DIR_NOT_FOUND;
8194 return -1;
8195 }
8196 // Return to our previous position in this directory
8197 handle = 2;
8198 cwdptr->dirccls = cwdptr->dirclus;
8199 entry = Cache_File_Entry (cwdptr, &handle, TRUE);
8200 if (entry == NULL)
8201 {
8202 FileObjectCopy (cwdptr, tempCWD);
8203 FSerrno = CE_BADCACHEREAD;
8204 return -1;
8205 }
8206  
8207 // Get the cluster
8208 TempClusterCalc = GetFullClusterNumber(entry); // Get complete cluster number.
8209  
8210 while ((TempClusterCalc != handle2) ||
8211 ((TempClusterCalc == handle2) &&
8212 (((unsigned char)entry->DIR_Name[0] == 0xE5) || (entry->DIR_Attr == ATTR_VOLUME))))
8213 {
8214 handle++;
8215 entry = Cache_File_Entry (cwdptr, &handle, FALSE);
8216 if (entry == NULL)
8217 {
8218 FileObjectCopy (cwdptr, tempCWD);
8219 FSerrno = CE_BADCACHEREAD;
8220 return -1;
8221 }
8222 // Get the cluster
8223 TempClusterCalc = GetFullClusterNumber(entry); // Get complete cluster number in a loop.
8224 }
8225 // Erase the directory that we just cleared the subdirectories out of
8226 memset (tempArray, 0x00, 12);
8227 for (Index = 0; Index < 11; Index++)
8228 {
8229 tempArray[Index] = entry->DIR_Name[Index];
8230 }
8231 if (eraseDir (tempArray))
8232 {
8233 FileObjectCopy (cwdptr, tempCWD);
8234 FSerrno = CE_ERASE_FAIL;
8235 return -1;
8236 }
8237 else
8238 {
8239 handle++;
8240 cwdptr->dirccls = cwdptr->dirclus;
8241 entry = Cache_File_Entry (cwdptr, &handle, TRUE);
8242 if (entry == NULL)
8243 {
8244 FileObjectCopy (cwdptr, tempCWD);
8245 FSerrno = CE_BADCACHEREAD;
8246 return -1;
8247 }
8248 }
8249  
8250 // Decrease the subdirectory depth
8251 subDirDepth--;
8252 }
8253 else
8254 {
8255 dirCleared = TRUE;
8256 } // Check subdirectory depth
8257 } // Check until we get an empty entry
8258 } // Loop until the whole dir is cleared
8259 }
8260  
8261 // Cache the current directory name
8262 // tempArray is used so we don't disturb the
8263 // global getcwd buffer
8264 if (FSgetcwd (tempArray, 12) == NULL)
8265 {
8266 FileObjectCopy (cwdptr, tempCWD);
8267 return -1;
8268 }
8269  
8270 memset(tempArray, 0x00, 12);
8271  
8272 for (Index = 0; Index < 11; Index++)
8273 {
8274 tempArray[Index] = cwdptr->name[Index];
8275 }
8276  
8277 // If we're here, this directory is empty
8278 #ifndef __18CXX
8279 if (FSchdir (".."))
8280 #else
8281 if (FSchdir (dotdotname))
8282 #endif
8283 {
8284 FileObjectCopy (cwdptr, tempCWD);
8285 FSerrno = CE_DIR_NOT_FOUND;
8286 return -1;
8287 }
8288  
8289 if (eraseDir (tempArray))
8290 {
8291 FileObjectCopy (cwdptr, tempCWD);
8292 FSerrno = CE_ERASE_FAIL;
8293 return -1;
8294 }
8295 else
8296 {
8297 FileObjectCopy (cwdptr, tempCWD);
8298 return 0;
8299 }
8300 }
8301  
8302  
8303 /****************************************************************
8304 Function:
8305 int eraseDir (char * path)
8306 Summary:
8307 FSrmdir helper function to erase dirs
8308 Conditions:
8309 This function should not be called by the user.
8310 Input:
8311 path - The name of the directory to delete
8312 Return Values:
8313  
8314 -1 - Dir could not be deleted.
8315 Side Effects:
8316 None
8317 Description:
8318 The eraseDir function is a helper function for the rmdirhelper
8319 function. The eraseDir function will search for the
8320 directory that matches the specified path name and then erase
8321 it with the FILEerase function.
8322 Remarks:
8323 None.
8324 *****************************************************************/
8325  
8326 int eraseDir (char * path)
8327 {
8328 CETYPE result;
8329 BYTE Index;
8330 FSFILE tempCWDobj2;
8331  
8332 if (MDD_WriteProtectState())
8333 {
8334 return (-1);
8335 }
8336  
8337 // preserve CWD
8338 FileObjectCopy(&tempCWDobj2, cwdptr);
8339  
8340 for (Index = 0; Index <11; Index++)
8341 {
8342 cwdptr->name[Index] = *(path + Index);
8343 }
8344  
8345 // copy file object over
8346 FileObjectCopy(&gFileTemp, cwdptr);
8347  
8348 // See if the file is found
8349 result = FILEfind (cwdptr, &gFileTemp, LOOK_FOR_MATCHING_ENTRY, 0);
8350  
8351 if (result != CE_GOOD)
8352 {
8353 FileObjectCopy(cwdptr, &tempCWDobj2);
8354 return -1;
8355 }
8356 result = FILEerase(cwdptr, &cwdptr->entry, TRUE);
8357 if( result == CE_GOOD )
8358 {
8359 FileObjectCopy(cwdptr, &tempCWDobj2);
8360 return 0;
8361 }
8362 else
8363 {
8364 FileObjectCopy(cwdptr, &tempCWDobj2);
8365 return -1;
8366 }
8367 }
8368 #endif
8369  
8370  
8371  
8372 #endif
8373  
8374  
8375 #ifdef ALLOW_FILESEARCH
8376  
8377  
8378 /***********************************************************************************
8379 Function:
8380 int FindFirst (const char * fileName, unsigned int attr, SearchRec * rec)
8381 Summary:
8382 Initial search function
8383 Conditions:
8384 None
8385 Input:
8386 fileName - The name to search for
8387 - Parital string search characters
8388 - * - Indicates the rest of the filename or extension can vary (e.g. FILE.*)
8389 - ? - Indicates that one character in a filename can vary (e.g. F?LE.T?T)
8390 attr - The attributes that a found file may have
8391 - ATTR_READ_ONLY - File may be read only
8392 - ATTR_HIDDEN - File may be a hidden file
8393 - ATTR_SYSTEM - File may be a system file
8394 - ATTR_VOLUME - Entry may be a volume label
8395 - ATTR_DIRECTORY - File may be a directory
8396 - ATTR_ARCHIVE - File may have archive attribute
8397 - ATTR_MASK - All attributes
8398 rec - pointer to a structure to put the file information in
8399 Return Values:
8400  
8401 -1 - No file matching the specified criteria was found
8402 Side Effects:
8403 Search criteria from previous FindFirst call on passed SearchRec object
8404 will be lost. The FSerrno variable will be changed.
8405 Description:
8406 The FindFirst function will search for a file based on parameters passed in
8407 by the user. This function will use the FILEfind function to parse through
8408 the current working directory searching for entries that match the specified
8409 parameters. If a file is found, its parameters are copied into the SearchRec
8410 structure, as are the initial parameters passed in by the user and the position
8411 of the file entry in the current working directory.
8412 Remarks:
8413 Call FindFirst or FindFirstpgm before calling FindNext
8414 ***********************************************************************************/
8415  
8416 int FindFirst (const char * fileName, unsigned int attr, SearchRec * rec)
8417 {
8418 FSFILE f;
8419 FILEOBJ fo = &f;
8420 CETYPE result;
8421 WORD fHandle;
8422 BYTE j;
8423 BYTE Index;
8424  
8425 FSerrno = CE_GOOD;
8426  
8427 if( !FormatFileName(fileName, fo->name, 1) )
8428 {
8429 FSerrno = CE_INVALID_FILENAME;
8430 return -1;
8431 }
8432  
8433 rec->initialized = FALSE;
8434  
8435 for (Index = 0; (Index < 12) && (fileName[Index] != 0); Index++)
8436 {
8437 rec->searchname[Index] = fileName[Index];
8438 }
8439 rec->searchname[Index] = 0;
8440 rec->searchattr = attr;
8441 #ifdef ALLOW_DIRS
8442 rec->cwdclus = cwdptr->dirclus;
8443 #else
8444 rec->cwdclus = FatRootDirClusterValue;
8445 #endif
8446  
8447 fo->dsk = &gDiskData;
8448 fo->cluster = 0;
8449 fo->ccls = 0;
8450 fo->entry = 0;
8451 fo->attributes = attr;
8452  
8453 #ifndef ALLOW_DIRS
8454 // start at the root directory
8455 fo->dirclus = FatRootDirClusterValue;
8456 fo->dirccls = FatRootDirClusterValue;
8457 #else
8458 fo->dirclus = cwdptr->dirclus;
8459 fo->dirccls = cwdptr->dirccls;
8460 #endif
8461  
8462 // copy file object over
8463 FileObjectCopy(&gFileTemp, fo);
8464  
8465 // See if the file is found
8466 result = FILEfind (fo, &gFileTemp,LOOK_FOR_MATCHING_ENTRY, 1);
8467  
8468 if (result != CE_GOOD)
8469 {
8470 FSerrno = CE_FILE_NOT_FOUND;
8471 return -1;
8472 }
8473 else
8474 {
8475 fHandle = fo->entry;
8476 result = FILEopen (fo, &fHandle, 'r');
8477 }
8478 if (result == CE_GOOD)
8479 {
8480 // Copy as much name as there is
8481 if (fo->attributes != ATTR_VOLUME)
8482 {
8483 for (Index = 0, j = 0; (j < 8) && (fo->name[j] != 0x20); Index++, j++)
8484 {
8485 rec->filename[Index] = fo->name[j];
8486 }
8487 // Add the radix if its not a dir
8488 if ((fo->name[8] != ' ') || (fo->name[9] != ' ') || (fo->name[10] != ' '))
8489 rec->filename[Index++] = '.';
8490 // Move to the extension, even if there are more space chars
8491 for (j = 8; (j < 11) && (fo->name[j] != 0x20); Index++, j++)
8492 {
8493 rec->filename[Index] = fo->name[j];
8494 }
8495 // Null terminate it
8496 rec->filename[Index] = 0;
8497 }
8498 else
8499 {
8500 for (Index = 0; Index < DIR_NAMECOMP; Index++)
8501 {
8502 rec->filename[Index] = fo->name[Index];
8503 }
8504 rec->filename[Index] = 0;
8505 }
8506  
8507 rec->attributes = fo->attributes;
8508 rec->filesize = fo->size;
8509 rec->timestamp = (DWORD)((DWORD)fo->date << 16) + fo->time;
8510 rec->entry = fo->entry;
8511 rec->initialized = TRUE;
8512 return 0;
8513 }
8514 else
8515 {
8516 FSerrno = CE_BADCACHEREAD;
8517 return -1;
8518 }
8519 }
8520  
8521  
8522 /**********************************************************************
8523 Function:
8524 int FindNext (SearchRec * rec)
8525 Summary:
8526 Sequential search function
8527 Conditions:
8528 None
8529 Input:
8530 rec - The structure to store the file information in
8531 Return Values:
8532  
8533 -1 - No additional files matching the specified criteria were found
8534 Side Effects:
8535 The FSerrno variable will be changed.
8536 Description:
8537 The FindNext function performs the same function as the FindFirst
8538 funciton, except it does not copy any search parameters into the
8539 SearchRec structure (only info about found files) and it begins
8540 searching at the last directory entry offset at which a file was
8541 found, rather than at the beginning of the current working
8542 directory.
8543 Remarks:
8544 Call FindFirst or FindFirstpgm before calling this function
8545 **********************************************************************/
8546  
8547 int FindNext (SearchRec * rec)
8548 {
8549 FSFILE f;
8550 FILEOBJ fo = &f;
8551 CETYPE result;
8552 BYTE i, j;
8553  
8554 FSerrno = CE_GOOD;
8555  
8556 // Make sure we called FindFirst on this object
8557 if (rec->initialized == FALSE)
8558 {
8559 FSerrno = CE_NOT_INIT;
8560 return -1;
8561 }
8562  
8563 // Make sure we called FindFirst in the cwd
8564 #ifdef ALLOW_DIRS
8565 if (rec->cwdclus != cwdptr->dirclus)
8566 {
8567 FSerrno = CE_INVALID_ARGUMENT;
8568 return -1;
8569 }
8570 #endif
8571  
8572 if( !FormatFileName(rec->searchname, fo->name, 1) )
8573 {
8574 FSerrno = CE_INVALID_FILENAME;
8575 return -1;
8576 }
8577  
8578 /* Brn: Copy the formatted name to "fo" which is necesary before calling "FILEfind" function */
8579 //strcpy(fo->name,rec->searchname);
8580  
8581 fo->dsk = &gDiskData;
8582 fo->cluster = 0;
8583 fo->ccls = 0;
8584 fo->entry = rec->entry + 1;
8585 fo->attributes = rec->searchattr;
8586  
8587 #ifndef ALLOW_DIRS
8588 // start at the root directory
8589 fo->dirclus = FatRootDirClusterValue;
8590 fo->dirccls = FatRootDirClusterValue;
8591 #else
8592 fo->dirclus = cwdptr->dirclus;
8593 fo->dirccls = cwdptr->dirccls;
8594 #endif
8595  
8596 // copy file object over
8597 FileObjectCopy(&gFileTemp, fo);
8598  
8599 // See if the file is found
8600 result = FILEfind (fo, &gFileTemp,LOOK_FOR_MATCHING_ENTRY, 1);
8601  
8602 if (result != CE_GOOD)
8603 {
8604 FSerrno = CE_FILE_NOT_FOUND;
8605 return -1;
8606 }
8607 else
8608 {
8609 if (fo->attributes != ATTR_VOLUME)
8610 {
8611 for (i = 0, j = 0; (j < 8) && (fo->name[j] != 0x20); i++, j++)
8612 {
8613 rec->filename[i] = fo->name[j];
8614 }
8615 // Add the radix if its not a dir
8616 if ((fo->name[8] != ' ') || (fo->name[9] != ' ') || (fo->name[10] != ' '))
8617 rec->filename[i++] = '.';
8618 // Move to the extension, even if there are more space chars
8619 for (j = 8; (j < 11) && (fo->name[j] != 0x20); i++, j++)
8620 {
8621 rec->filename[i] = fo->name[j];
8622 }
8623 // Null terminate it
8624 rec->filename[i] = 0;
8625 }
8626 else
8627 {
8628 for (i = 0; i < DIR_NAMECOMP; i++)
8629 {
8630 rec->filename[i] = fo->name[i];
8631 }
8632 rec->filename[i] = 0;
8633 }
8634  
8635 rec->attributes = fo->attributes;
8636 rec->filesize = fo->size;
8637 rec->timestamp = (DWORD)((DWORD)fo->date << 16) + fo->time;
8638 rec->entry = fo->entry;
8639 return 0;
8640 }
8641 }
8642  
8643  
8644 #endif
8645  
8646  
8647  
8648 #ifdef ALLOW_FSFPRINTF
8649  
8650  
8651  
8652 /**********************************************************************
8653 Function:
8654 int FSputc (char c, FSFILE * file)
8655 Summary:
8656 FSfprintf helper function to write a char
8657 Conditions:
8658 This function should not be called by the user.
8659 Input:
8660 c - The character to write to the file.
8661 file - The file to write to.
8662 Return Values:
8663  
8664 EOF - The character was not written to the file.
8665 Side Effects:
8666 None
8667 Description:
8668 This is a helper function for FSfprintf. It will write one
8669 character to a file.
8670 Remarks:
8671 None
8672 **********************************************************************/
8673  
8674 int FSputc (char c, FSFILE * file)
8675 {
8676 if (FSfwrite ((void *)&c, 1, 1, file) != 1)
8677 return EOF;
8678 else
8679 return 0;
8680 }
8681  
8682  
8683 /**********************************************************************
8684 Function:
8685 int str_put_n_chars (FSFILE * handle, unsigned char n, char c)
8686 Summary:
8687 FSfprintf helper function to write a char multiple times
8688 Conditions:
8689 This function should not be called by the user.
8690 Input:
8691 handle - The file to write to.
8692 n - The number of times to write that character to a file.
8693 c - The character to write to the file.
8694 Return Values:
8695  
8696 EOF - The characters were not written to the file.
8697 Side Effects:
8698 None
8699 Description:
8700 This funciton is used by the FSfprintf function to write multiple
8701 instances of a single character to a file (for example, when
8702 padding a format specifier with leading spacez or zeros).
8703 Remarks:
8704 None.
8705 **********************************************************************/
8706  
8707  
8708 unsigned char str_put_n_chars (FSFILE * handle, unsigned char n, char c)
8709 {
8710 while (n--)
8711 if (FSputc (c, handle) == EOF)
8712 return 1;
8713 return 0;
8714 }
8715  
8716  
8717 /**********************************************************************
8718 Function:
8719 // PIC24/30/33/32
8720 int FSfprintf (FSFILE * fptr, const char * fmt, ...)
8721 // PIC18
8722 int FSfpritnf (FSFILE * fptr, const rom char * fmt, ...)
8723 Summary:
8724 Function to write formatted strings to a file
8725 Conditions:
8726 For PIC18, integer promotion must be enabled in the project build
8727 options menu. File opened in a write mode.
8728 Input:
8729 fptr - A pointer to the file to write to.
8730 fmt - A string of characters and format specifiers to write to
8731 the file
8732 ... - Additional arguments inserted in the string by format
8733 specifiers
8734 Returns:
8735 The number of characters written to the file
8736 Side Effects:
8737 The FSerrno variable will be changed.
8738 Description:
8739 Writes a specially formatted string to a file.
8740 Remarks:
8741 Consult AN1045 for a full description of how to use format
8742 specifiers.
8743 **********************************************************************/
8744  
8745 #ifdef __18CXX
8746 int FSfprintf (FSFILE *fptr, const rom char *fmt, ...)
8747 #else
8748 int FSfprintf (FSFILE *fptr, const char * fmt, ...)
8749 #endif
8750 {
8751 va_list ap;
8752 int n;
8753  
8754 va_start (ap, fmt);
8755 n = FSvfprintf (fptr, fmt, ap);
8756 va_end (ap);
8757 return n;
8758 }
8759  
8760  
8761 /**********************************************************************
8762 Function:
8763 // PIC24/30/33/32
8764 int FSvfprintf (FSFILE * handle, const char * formatString, va_list ap)
8765 // PIC18
8766 int FSvfpritnf (auto FSFILE * handle, auto const rom char * formatString, auto va_list ap)
8767 Summary:
8768 Helper function for FSfprintf
8769 Conditions:
8770 This function should not be called by the user.
8771 Input:
8772 handle - A pointer to the file to write to.
8773 formatString - A string of characters and format specifiers to write to
8774 the file
8775 ap - A structure pointing to the arguments on the stack
8776 Returns:
8777 The number of characters written to the file
8778 Side Effects:
8779 The FSerrno variable will be changed.
8780 Description:
8781 This helper function will access the elements passed to FSfprintf
8782 Remarks:
8783 Consult AN1045 for a full description of how to use format
8784 specifiers.
8785 **********************************************************************/
8786  
8787 #ifdef __18CXX
8788 int FSvfprintf (auto FSFILE *handle, auto const rom char * formatString, auto va_list ap)
8789 #else
8790 int FSvfprintf (FSFILE *handle, const char * formatString, va_list ap)
8791 #endif
8792 {
8793 unsigned char c;
8794 int count = 0;
8795  
8796 for (c = *formatString; c; c = *++formatString)
8797 {
8798 if (c == '%')
8799 {
8800 unsigned char flags = 0;
8801 unsigned char width = 0;
8802 unsigned char precision = 0;
8803 unsigned char have_precision = 0;
8804 unsigned char size = 0;
8805 #ifndef __18CXX
8806 unsigned char size2 = 0;
8807 #endif
8808 unsigned char space_cnt;
8809 unsigned char cval;
8810 #ifdef __18CXX
8811 unsigned long larg;
8812 far rom char * romstring;
8813 #else
8814 unsigned long long larg;
8815 #endif
8816 char * ramstring;
8817 int n;
8818  
8819 FSerrno = CE_GOOD;
8820  
8821 c = *++formatString;
8822  
8823 while (c == '-' || c == '+' || c == ' ' || c == '#'
8824 || c == '0')
8825 {
8826 switch (c)
8827 {
8828 case '-':
8829 flags |= _FLAG_MINUS;
8830 break;
8831 case '+':
8832 flags |= _FLAG_PLUS;
8833 break;
8834 case ' ':
8835 flags |= _FLAG_SPACE;
8836 break;
8837 case '#':
8838 flags |= _FLAG_OCTO;
8839 break;
8840 case '0':
8841 flags |= _FLAG_ZERO;
8842 break;
8843 }
8844 c = *++formatString;
8845 }
8846 /* the optional width field is next */
8847 if (c == '*')
8848 {
8849 n = va_arg (ap, int);
8850 if (n < 0)
8851 {
8852 flags |= _FLAG_MINUS;
8853 width = -n;
8854 }
8855 else
8856 width = n;
8857 c = *++formatString;
8858 }
8859 else
8860 {
8861 cval = 0;
8862 while ((unsigned char) isdigit (c))
8863 {
8864 cval = cval * 10 + c - '0';
8865 c = *++formatString;
8866 }
8867 width = cval;
8868 }
8869  
8870 /* if '-' is specified, '0' is ignored */
8871 if (flags & _FLAG_MINUS)
8872 flags &= ~_FLAG_ZERO;
8873  
8874 /* the optional precision field is next */
8875 if (c == '.')
8876 {
8877 c = *++formatString;
8878 if (c == '*')
8879 {
8880 n = va_arg (ap, int);
8881 if (n >= 0)
8882 {
8883 precision = n;
8884 have_precision = 1;
8885 }
8886 c = *++formatString;
8887 }
8888 else
8889 {
8890 cval = 0;
8891 while ((unsigned char) isdigit (c))
8892 {
8893 cval = cval * 10 + c - '0';
8894 c = *++formatString;
8895 }
8896 precision = cval;
8897 have_precision = 1;
8898 }
8899 }
8900  
8901 /* the optional 'h' specifier. since int and short int are
8902 the same size for MPLAB C18, this is a NOP for us. */
8903 if (c == 'h')
8904 {
8905 c = *++formatString;
8906 /* if 'c' is another 'h' character, this is an 'hh'
8907 specifier and the size is 8 bits */
8908 if (c == 'h')
8909 {
8910 size = _FMT_BYTE;
8911 c = *++formatString;
8912 }
8913 }
8914 else if (c == 't' || c == 'z')
8915 c = *++formatString;
8916 #ifdef __18CXX
8917 else if (c == 'H' || c == 'T' || c == 'Z')
8918 {
8919 size = _FMT_SHRTLONG;
8920 c = *++formatString;
8921 }
8922 else if (c == 'l' || c == 'j')
8923 #else
8924 else if (c == 'q' || c == 'j')
8925 {
8926 size = _FMT_LONGLONG;
8927 c = *++formatString;
8928 }
8929 else if (c == 'l')
8930 #endif
8931 {
8932 size = _FMT_LONG;
8933 c = *++formatString;
8934 }
8935  
8936 switch (c)
8937 {
8938 case '\0':
8939 /* this is undefined behaviour. we have a trailing '%' character
8940 in the string, perhaps with some flags, width, precision
8941 stuff as well, but no format specifier. We'll, arbitrarily,
8942 back up a character so that the loop will terminate
8943 properly when it loops back and we'll output a '%'
8944 character. */
8945 --formatString;
8946 /* fallthrough */
8947 case '%':
8948 if (FSputc ('%', handle) == EOF)
8949 {
8950 FSerrno = CE_WRITE_ERROR;
8951 return EOF;
8952 }
8953 ++count;
8954 break;
8955 case 'c':
8956 space_cnt = 0;
8957 if (width > 1)
8958 {
8959 space_cnt = width - 1;
8960 count += space_cnt;
8961 }
8962 if (space_cnt && !(flags & _FLAG_MINUS))
8963 {
8964 if (str_put_n_chars (handle, space_cnt, ' '))
8965 {
8966 FSerrno = CE_WRITE_ERROR;
8967 return EOF;
8968 }
8969 space_cnt = 0;
8970 }
8971 c = va_arg (ap, int);
8972 if (FSputc (c, handle) == EOF)
8973 {
8974 FSerrno = CE_WRITE_ERROR;
8975 return EOF;
8976 }
8977 ++count;
8978 if (str_put_n_chars (handle, space_cnt, ' '))
8979 {
8980 FSerrno = CE_WRITE_ERROR;
8981 return EOF;
8982 }
8983 break;
8984 case 'S':
8985 #ifdef __18CXX
8986 if (size == _FMT_SHRTLONG)
8987 romstring = va_arg (ap, rom far char *);
8988 else
8989 romstring = (far rom char*)va_arg (ap, rom near char *);
8990 n = strlenpgm (romstring);
8991 /* Normalize the width based on the length of the actual
8992 string and the precision. */
8993 if (have_precision && precision < (unsigned char) n)
8994 n = precision;
8995 if (width < (unsigned char) n)
8996 width = n;
8997 space_cnt = width - (unsigned char) n;
8998 count += space_cnt;
8999 /* we've already calculated the space count that the width
9000 will require. now we want the width field to have the
9001 number of character to display from the string itself,
9002 limited by the length of the actual string and the
9003 specified precision. */
9004 if (have_precision && precision < width)
9005 width = precision;
9006 /* if right justified, we print the spaces before the
9007 string */
9008 if (!(flags & _FLAG_MINUS))
9009 {
9010 if (str_put_n_chars (handle, space_cnt, ' '))
9011 {
9012 FSerrno = CE_WRITE_ERROR;
9013 return EOF;
9014 }
9015 space_cnt = 0;
9016 }
9017 cval = 0;
9018 for (c = *romstring; c && cval < width; c = *++romstring)
9019 {
9020 if (FSputc (c, handle) == EOF)
9021 {
9022 FSerrno = CE_WRITE_ERROR;
9023 return EOF;
9024 }
9025 ++count;
9026 ++cval;
9027 }
9028 /* If there are spaces left, it's left justified.
9029 Either way, calling the function unconditionally
9030 is smaller code. */
9031 if (str_put_n_chars (handle, space_cnt, ' '))
9032 {
9033 FSerrno = CE_WRITE_ERROR;
9034 return EOF;
9035 }
9036 break;
9037 #endif
9038 case 's':
9039 ramstring = va_arg (ap, char *);
9040 n = strlen (ramstring);
9041 /* Normalize the width based on the length of the actual
9042 string and the precision. */
9043 if (have_precision && precision < (unsigned char) n)
9044 n = precision;
9045 if (width < (unsigned char) n)
9046 width = n;
9047 space_cnt = width - (unsigned char) n;
9048 count += space_cnt;
9049 /* we've already calculated the space count that the width
9050 will require. now we want the width field to have the
9051 number of character to display from the string itself,
9052 limited by the length of the actual string and the
9053 specified precision. */
9054 if (have_precision && precision < width)
9055 width = precision;
9056 /* if right justified, we print the spaces before the string */
9057 if (!(flags & _FLAG_MINUS))
9058 {
9059 if (str_put_n_chars (handle, space_cnt, ' '))
9060 {
9061 FSerrno = CE_WRITE_ERROR;
9062 return EOF;
9063 }
9064 space_cnt = 0;
9065 }
9066 cval = 0;
9067 for (c = *ramstring; c && cval < width; c = *++ramstring)
9068 {
9069 if (FSputc (c, handle) == EOF)
9070 {
9071 FSerrno = CE_WRITE_ERROR;
9072 return EOF;
9073 }
9074 ++count;
9075 ++cval;
9076 }
9077 /* If there are spaces left, it's left justified.
9078 Either way, calling the function unconditionally
9079 is smaller code. */
9080 if (str_put_n_chars (handle, space_cnt, ' '))
9081 {
9082 FSerrno = CE_WRITE_ERROR;
9083 return EOF;
9084 }
9085 break;
9086 case 'd':
9087 case 'i':
9088 flags |= _FLAG_SIGNED;
9089 /* fall through */
9090 case 'o':
9091 case 'u':
9092 case 'x':
9093 case 'X':
9094 case 'b':
9095 case 'B':
9096 /* This is a bit of a trick. The 'l' and 'hh' size
9097 specifiers are valid only for the integer conversions,
9098 not the 'p' or 'P' conversions, and are ignored for the
9099 latter. By jumping over the additional size specifier
9100 checks here we get the best code size since we can
9101 limit the size checks in the remaining code. */
9102 if (size == _FMT_LONG)
9103 {
9104 if (flags & _FLAG_SIGNED)
9105 larg = va_arg (ap, long int);
9106 else
9107 larg = va_arg (ap, unsigned long int);
9108 goto _do_integer_conversion;
9109 }
9110 else if (size == _FMT_BYTE)
9111 {
9112 if (flags & _FLAG_SIGNED)
9113 larg = (signed char) va_arg (ap, int);
9114 else
9115 larg = (unsigned char) va_arg (ap, unsigned int);
9116 goto _do_integer_conversion;
9117 }
9118 #ifndef __18CXX
9119 else if (size == _FMT_LONGLONG)
9120 {
9121 if (flags & _FLAG_SIGNED)
9122 larg = (signed long long)va_arg (ap, long long);
9123 else
9124 larg = (unsigned long long) va_arg (ap, unsigned long long);
9125 goto _do_integer_conversion;
9126 }
9127 #endif
9128 /* fall trough */
9129 case 'p':
9130 case 'P':
9131 #ifdef __18CXX
9132 if (size == _FMT_SHRTLONG)
9133 {
9134 if (flags & _FLAG_SIGNED)
9135 larg = va_arg (ap, short long int);
9136 else
9137 larg = va_arg (ap, unsigned short long int);
9138 }
9139 else
9140 #endif
9141 if (flags & _FLAG_SIGNED)
9142 larg = va_arg (ap, int);
9143 else
9144 larg = va_arg (ap, unsigned int);
9145 _do_integer_conversion:
9146 /* default precision is 1 */
9147 if (!have_precision)
9148 precision = 1;
9149 {
9150 unsigned char digit_cnt = 0;
9151 unsigned char prefix_cnt = 0;
9152 unsigned char sign_char;
9153 /* A 32 bit number will require at most 32 digits in the
9154 string representation (binary format). */
9155 #ifdef __18CXX
9156 char buf[33];
9157 /* Start storing digits least-significant first */
9158 char *q = &buf[31];
9159 /* null terminate the string */
9160 buf[32] = '\0';
9161 #else
9162 char buf[65];
9163 char *q = &buf[63];
9164 buf[64] = '\0';
9165 #endif
9166 space_cnt = 0;
9167 size = 10;
9168  
9169 switch (c)
9170 {
9171 case 'b':
9172 case 'B':
9173 size = 2;
9174 #ifndef __18CXX
9175 size2 = 1;
9176 #endif
9177 break;
9178 case 'o':
9179 size = 8;
9180 #ifndef __18CXX
9181 size2 = 3;
9182 #endif
9183 break;
9184 case 'p':
9185 case 'P':
9186 /* from here on out, treat 'p' conversions just
9187 like 'x' conversions. */
9188 c += 'x' - 'p';
9189 /* fall through */
9190 case 'x':
9191 case 'X':
9192 size = 16;
9193 #ifndef __18CXX
9194 size2 = 4;
9195 #endif
9196 break;
9197 }// switch (c)
9198  
9199 /* if it's an unsigned conversion, we should ignore the
9200 ' ' and '+' flags */
9201 if (!(flags & _FLAG_SIGNED))
9202 flags &= ~(_FLAG_PLUS | _FLAG_SPACE);
9203  
9204 /* if it's a negative value, we need to negate the
9205 unsigned version before we convert to text. Using
9206 unsigned for this allows us to (ab)use the 2's
9207 complement system to avoid overflow and be able to
9208 adequately handle LONG_MIN.
9209  
9210 We'll figure out what sign character to print, if
9211 any, here as well. */
9212 #ifdef __18CXX
9213 if (flags & _FLAG_SIGNED && ((long) larg < 0))
9214 {
9215 larg = -(long) larg;
9216 #else
9217 if (flags & _FLAG_SIGNED && ((long long) larg < 0))
9218 {
9219 larg = -(long long) larg;
9220 #endif
9221 sign_char = '-';
9222 ++digit_cnt;
9223 }
9224 else if (flags & _FLAG_PLUS)
9225 {
9226 sign_char = '+';
9227 ++digit_cnt;
9228 }
9229 else if (flags & _FLAG_SPACE)
9230 {
9231 sign_char = ' ';
9232 ++digit_cnt;
9233 }
9234 else
9235 sign_char = '\0';
9236 /* get the digits for the actual number. If the
9237 precision is zero and the value is zero, the result
9238 is no characters. */
9239 if (precision || larg)
9240 {
9241 do
9242 {
9243 #ifdef __18CXX
9244 cval = s_digits[larg % size];
9245 if (c == 'X' && cval >= 'a')
9246 cval -= 'a' - 'A';
9247 larg /= size;
9248 #else
9249 // larg is congruent mod size2 to its lower 16 bits
9250 // for size2 = 2^n, 0 <= n <= 4
9251 if (size2 != 0)
9252 cval = s_digits[(unsigned int) larg % size];
9253 else
9254 cval = s_digits[larg % size];
9255 if (c == 'X' && cval >= 'a')
9256 cval -= 'a' - 'A';
9257 if (size2 != 0)
9258 larg = larg >> size2;
9259 else
9260 larg /= size;
9261 #endif
9262 *q-- = cval;
9263 ++digit_cnt;
9264 } while (larg);
9265 /* if the '#' flag was specified and we're dealing
9266 with an 'o', 'b', 'B', 'x', or 'X' conversion,
9267 we need a bit more. */
9268 if (flags & _FLAG_OCTO)
9269 {
9270 if (c == 'o')
9271 {
9272 /* per the standard, for octal, the '#' flag
9273 makes the precision be at least one more
9274 than the number of digits in the number */
9275 if (precision <= digit_cnt)
9276 precision = digit_cnt + 1;
9277 }
9278 else if (c == 'x' || c == 'X' || c == 'b' || c == 'B')
9279 prefix_cnt = 2;
9280 }
9281 }
9282 else
9283 digit_cnt = 0;
9284  
9285 /* The leading zero count depends on whether the '0'
9286 flag was specified or not. If it was not, then the
9287 count is the difference between the specified
9288 precision and the number of digits (including the
9289 sign character, if any) to be printed; otherwise,
9290 it's as if the precision were equal to the max of
9291 the specified precision and the field width. If a
9292 precision was specified, the '0' flag is ignored,
9293 however. */
9294 if ((flags & _FLAG_ZERO) && (width > precision)
9295 && !have_precision)
9296 precision = width;
9297 /* for the rest of the processing, precision contains
9298 the leading zero count for the conversion. */
9299 if (precision > digit_cnt)
9300 precision -= digit_cnt;
9301 else
9302 precision = 0;
9303 /* the space count is the difference between the field
9304 width and the digit count plus the leading zero
9305 count. If the width is less than the digit count
9306 plus the leading zero count, the space count is
9307 zero. */
9308 if (width > precision + digit_cnt + prefix_cnt)
9309 space_cnt = width - precision - digit_cnt - prefix_cnt;
9310  
9311 /* for output, we check the justification, if it's
9312 right justified and the space count is positive, we
9313 emit the space characters first. */
9314 if (!(flags & _FLAG_MINUS) && space_cnt)
9315 {
9316 if (str_put_n_chars (handle, space_cnt, ' '))
9317 {
9318 FSerrno = CE_WRITE_ERROR;
9319 return EOF;
9320 }
9321 count += space_cnt;
9322 space_cnt = 0;
9323 }
9324 /* if we have a sign character to print, that comes
9325 next */
9326 if (sign_char)
9327 if (FSputc (sign_char, handle) == EOF)
9328 {
9329 FSerrno = CE_WRITE_ERROR;
9330 return EOF;
9331 }
9332 /* if we have a prefix (0b, 0B, 0x or 0X), that's next */
9333 if (prefix_cnt)
9334 {
9335 if (FSputc ('0', handle) == EOF)
9336 {
9337 FSerrno = CE_WRITE_ERROR;
9338 return EOF;
9339 }
9340 if (FSputc (c, handle) == EOF)
9341 {
9342 FSerrno = CE_WRITE_ERROR;
9343 return EOF;
9344 }
9345 }
9346 /* if we have leading zeros, they follow. the prefix, if any
9347 is included in the number of digits when determining how
9348 many leading zeroes are needed. */
9349 // if (precision > prefix_cnt)
9350 // precision -= prefix_cnt;
9351 if (str_put_n_chars (handle, precision, '0'))
9352 {
9353 FSerrno = CE_WRITE_ERROR;
9354 return EOF;
9355 }
9356 /* print the actual number */
9357 for (cval = *++q; cval; cval = *++q)
9358 if (FSputc (cval, handle) == EOF)
9359 {
9360 FSerrno = CE_WRITE_ERROR;
9361 return EOF;
9362 }
9363 /* if there are any spaces left, they go to right-pad
9364 the field */
9365 if (str_put_n_chars (handle, space_cnt, ' '))
9366 {
9367 FSerrno = CE_WRITE_ERROR;
9368 return EOF;
9369 }
9370  
9371 count += precision + digit_cnt + space_cnt + prefix_cnt;
9372 }
9373 break;
9374 case 'n':
9375 switch (size)
9376 {
9377 case _FMT_LONG:
9378 *(long *) va_arg (ap, long *) = count;
9379 break;
9380 #ifdef __18CXX
9381 case _FMT_SHRTLONG:
9382 *(short long *) va_arg (ap, short long *) = count;
9383 break;
9384 #else
9385 case _FMT_LONGLONG:
9386 *(long long *) va_arg (ap, long long *) = count;
9387 break;
9388 #endif
9389 case _FMT_BYTE:
9390 *(signed char *) va_arg (ap, signed char *) = count;
9391 break;
9392 default:
9393 *(int *) va_arg (ap, int *) = count;
9394 break;
9395 }
9396 break;
9397 default:
9398 /* undefined behaviour. we do nothing */
9399 break;
9400 }
9401 }
9402 else
9403 {
9404 if (FSputc (c, handle) == EOF)
9405 {
9406 FSerrno = CE_WRITE_ERROR;
9407 return EOF;
9408 }
9409 ++count;
9410 }
9411 }
9412 return count;
9413 }
9414  
9415  
9416  
9417 #endif
9418  
9419  
9420  
9421  
{BLAME END}
{FOOTER START}

Powered by WebSVN v2.8.3