1 /*
2 gba_nds_fat.c
3 By chishm (Michael Chisholm)
4
5 Routines for reading a compact flash card
6 using the GBA Movie Player or M3.
7
8 Some FAT routines are based on those in fat.c, which
9 is part of avrlib by Pascal Stang.
10
11 This software is completely free. No warranty is provided.
12 If you use it, please give me credit and email me about your
13 project at chishm@hotmail.com
14
15 See gba_nds_fat.txt for help and license details.
16 */
17
18 //---------------------------------------------------------------
19 // Includes
20
21 // Allow use of stuff in <time.h>
22 #define FORBIDDEN_SYMBOL_EXCEPTION_time_h
23
24 #include "gba_nds_fat.h"
25 #include "disc_io.h"
26 #include <string.h>
27 #ifdef NDS
28 // #include <nds/ipc.h> // Time on the NDS
29 #include <NDS/scummvm_ipc.h>
30 #endif
31 //----------------------------------------------------------------
32 // Data types
33 #ifndef NULL
34 #define NULL 0
35 #endif
36
37 //----------------------------------------------------------------
38 // NDS memory access control register
39 #ifdef NDS
40 #ifndef WAIT_CR
41 #define WAIT_CR (*(vu16*)0x04000204)
42 #endif
43 #endif
44
45 //---------------------------------------------------------------
46 // Appropriate placement of CF functions and data
47 #ifdef NDS
48 #define _VARS_IN_RAM
49 #else
50 #define _VARS_IN_RAM __attribute__ ((section (".sbss")))
51 #endif
52
53
54 //-----------------------------------------------------------------
55 // FAT constants
56 #define CLUSTER_EOF_16 0xFFFF
57 #define CLUSTER_EOF 0x0FFFFFFF
58 #define CLUSTER_FREE 0x0000
59 #define CLUSTER_FIRST 0x0002
60
61 #define FILE_LAST 0x00
62 #define FILE_FREE 0xE5
63
64 #define FAT16_ROOT_DIR_CLUSTER 0x00
65
66
67 //-----------------------------------------------------------------
68 // long file name constants
69 #define LFN_END 0x40
70 #define LFN_DEL 0x80
71
72 //-----------------------------------------------------------------
73 // Data Structures
74
75 // Take care of packing for GCC - it doesn't obey pragma pack()
76 // properly for ARM targets.
77 #ifdef __GNUC__
78 #define __PACKED __attribute__ ((__packed__))
79 #else
80 #define __PACKED
81 #pragma pack(1)
82 #endif
83
84 // Boot Sector - must be packed
85 typedef struct
86 {
87 u8 jmpBoot[3];
88 u8 OEMName[8];
89 // BIOS Parameter Block
90 u16 bytesPerSector;
91 u8 sectorsPerCluster;
92 u16 reservedSectors;
93 u8 numFATs;
94 u16 rootEntries;
95 u16 numSectorsSmall;
96 u8 mediaDesc;
97 u16 sectorsPerFAT;
98 u16 sectorsPerTrk;
99 u16 numHeads;
100 u32 numHiddenSectors;
101 u32 numSectors;
102 union // Different types of extended BIOS Parameter Block for FAT16 and FAT32
103 {
104 struct
105 {
106 // Ext BIOS Parameter Block for FAT16
107 u8 driveNumber;
108 u8 reserved1;
109 u8 extBootSig;
110 u32 volumeID;
111 u8 volumeLabel[11];
112 u8 fileSysType[8];
113 // Bootcode
114 u8 bootCode[448];
115 } __PACKED fat16;
116 struct
117 {
118 // FAT32 extended block
119 u32 sectorsPerFAT32;
120 u16 extFlags;
121 u16 fsVer;
122 u32 rootClus;
123 u16 fsInfo;
124 u16 bkBootSec;
125 u8 reserved[12];
126 // Ext BIOS Parameter Block for FAT16
127 u8 driveNumber;
128 u8 reserved1;
129 u8 extBootSig;
130 u32 volumeID;
131 u8 volumeLabel[11];
132 u8 fileSysType[8];
133 // Bootcode
134 u8 bootCode[420];
135 } __PACKED fat32;
136 } __PACKED extBlock;
137
138 u16 bootSig;
139
140 } __PACKED BOOT_SEC;
141
142 // Directory entry - must be packed
143 typedef struct
144 {
145 u8 name[8];
146 u8 ext[3];
147 u8 attrib;
148 u8 reserved;
149 u8 cTime_ms;
150 u16 cTime;
151 u16 cDate;
152 u16 aDate;
153 u16 startClusterHigh;
154 u16 mTime;
155 u16 mDate;
156 u16 startCluster;
157 u32 fileSize;
158 } __PACKED DIR_ENT;
159
160 // Long file name directory entry - must be packed
161 typedef struct
162 {
163 u8 ordinal; // Position within LFN
164 u16 char0;
165 u16 char1;
166 u16 char2;
167 u16 char3;
168 u16 char4;
169 u8 flag; // Should be equal to ATTRIB_LFN
170 u8 reserved1; // Always 0x00
171 u8 checkSum; // Checksum of short file name (alias)
172 u16 char5;
173 u16 char6;
174 u16 char7;
175 u16 char8;
176 u16 char9;
177 u16 char10;
178 u16 reserved2; // Always 0x0000
179 u16 char11;
180 u16 char12;
181 } __PACKED DIR_ENT_LFN;
182
183 const char lfn_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};
184
185 // End of packed structs
186 #ifdef __PACKED
187 #undef __PACKED
188 #endif
189 #ifndef __GNUC__
190 #pragma pack()
191 #endif
192
193 //-----------------------------------------------------------------
194 // Global Variables
195
196 // _VARS_IN_RAM variables are stored in the largest section of WRAM
197 // available: IWRAM on NDS ARM7, EWRAM on NDS ARM9 and GBA
198
199 // Files
200 FAT_FILE openFiles[MAX_FILES_OPEN] __attribute__((section(".itcm")));
201 //_VARS_IN_RAM
202
203 // Long File names
204 _VARS_IN_RAM char lfnName[MAX_FILENAME_LENGTH];
205 bool lfnExists;
206
207 // Locations on card
208 int filesysRootDir;
209 int filesysRootDirClus;
210 int filesysFAT;
211 int filesysSecPerFAT;
212 int filesysNumSec;
213 int filesysData;
214 int filesysBytePerSec;
215 int filesysSecPerClus;
216 int filesysBytePerClus;
217
218 FS_TYPE filesysType = FS_UNKNOWN;
219 u32 filesysTotalSize;
220
221 // Info about FAT
222 u32 fatLastCluster;
223 u32 fatFirstFree;
224
225 // fatBuffer used to reduce wear on the CF card from multiple writes
226 _VARS_IN_RAM char fatBuffer[BYTE_PER_READ];
227 u32 fatBufferCurSector;
228
229 // Current working directory
230 u32 curWorkDirCluster;
231
232 // Position of the directory entry last retreived with FAT_GetDirEntry
233 u32 wrkDirCluster;
234 int wrkDirSector;
235 int wrkDirOffset;
236
237 // Global sector buffer to save on stack space
238 _VARS_IN_RAM unsigned char globalBuffer[BYTE_PER_READ];
239
240 //-----------------------------------------------------------------
241 // Functions contained in this file - predeclarations
242 char ucase (char character);
243 u16 getRTCtoFileTime (void);
244 u16 getRTCtoFileDate (void);
245
246 bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry);
247 bool FAT_ClearLinks (u32 cluster);
248 DIR_ENT FAT_DirEntFromPath (const char* path);
249 u32 FAT_FirstFreeCluster(void);
250 DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin);
251 u32 FAT_LinkFreeCluster(u32 cluster);
252 u32 FAT_NextCluster(u32 cluster);
253 bool FAT_WriteFatEntry (u32 cluster, u32 value);
254 bool FAT_GetFilename (DIR_ENT dirEntry, char* alias);
255
256 bool FAT_InitFiles (void);
257 bool FAT_FreeFiles (void);
258 int FAT_remove (const char* path);
259 bool FAT_chdir (const char* path);
260 FILE_TYPE FAT_FindFirstFile (char* filename);
261 FILE_TYPE FAT_FindNextFile (char* filename);
262 FILE_TYPE FAT_FileExists (const char* filename);
263 bool FAT_GetAlias (char* alias);
264 bool FAT_GetLongFilename (char* filename);
265 u32 FAT_GetFileSize (void);
266 u32 FAT_GetFileCluster (void);
267
268 FAT_FILE* FAT_fopen(const char* path, const char* mode);
269 bool FAT_fclose (FAT_FILE* file);
270 bool FAT_feof(FAT_FILE* file);
271 int FAT_fseek(FAT_FILE* file, s32 offset, int origin);
272 u32 FAT_ftell (FAT_FILE* file);
273 u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file);
274 u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file);
275 char FAT_fgetc (FAT_FILE* file);
276 char FAT_fputc (char c, FAT_FILE* file);
277
278 /*-----------------------------------------------------------------
279 ucase
280 Returns the uppercase version of the given char
281 char IN: a character
282 char return OUT: uppercase version of character
283 -----------------------------------------------------------------*/
ucase(char character)284 char ucase (char character)
285 {
286 if ((character > 0x60) && (character < 0x7B))
287 character = character - 0x20;
288 return (character);
289 }
290
291
292 /*-----------------------------------------------------------------
293 getRTCtoFileTime and getRTCtoFileDate
294 Returns the time / date in Dir Entry styled format
295 u16 return OUT: time / date in Dir Entry styled format
296 -----------------------------------------------------------------*/
getRTCtoFileTime(void)297 u16 getRTCtoFileTime (void)
298 {
299 #ifdef NDS
300 return (
301 ( ( (IPC->rtc.hours > 11 ? IPC->rtc.hours - 40 : IPC->rtc.hours) & 0x1F) << 11) |
302 ( (IPC->rtc.minutes & 0x3F) << 5) |
303 ( (IPC->rtc.seconds >> 1) & 0x1F) );
304 #else
305 return 0;
306 #endif
307 }
308
getRTCtoFileDate(void)309 u16 getRTCtoFileDate (void)
310 {
311 #ifdef NDS
312 return (
313 ( ((IPC->rtc.year + 20) & 0x7F) <<9) |
314 ( (IPC->rtc.month & 0xF) << 5) |
315 (IPC->rtc.day & 0x1F) );
316 #else
317 return 0;
318 #endif
319 }
320
321
322 /*-----------------------------------------------------------------
323 Disc level FAT routines
324 -----------------------------------------------------------------*/
325 #define FAT_ClustToSect(m) \
326 (((m-2) * filesysSecPerClus) + filesysData)
327
328 /*-----------------------------------------------------------------
329 FAT_NextCluster
330 Internal function - gets the cluster linked from input cluster
331 -----------------------------------------------------------------*/
FAT_NextCluster(u32 cluster)332 u32 FAT_NextCluster(u32 cluster)
333 {
334 u32 nextCluster = CLUSTER_FREE;
335 u32 sector;
336 int offset;
337
338 switch (filesysType)
339 {
340 case FS_UNKNOWN:
341 nextCluster = CLUSTER_FREE;
342 break;
343
344 case FS_FAT12:
345 sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
346 offset = ((cluster * 3) / 2) % BYTE_PER_READ;
347
348 // If FAT buffer contains wrong sector
349 if (sector != fatBufferCurSector)
350 {
351 // Load correct sector to buffer
352 fatBufferCurSector = sector;
353 disc_ReadSector(fatBufferCurSector, fatBuffer);
354 }
355
356 nextCluster = ((u8*)fatBuffer)[offset];
357 offset++;
358
359 if (offset >= BYTE_PER_READ) {
360 offset = 0;
361 fatBufferCurSector++;
362 disc_ReadSector(fatBufferCurSector, fatBuffer);
363 }
364
365 nextCluster |= (((u8*)fatBuffer)[offset]) << 8;
366
367 if (cluster & 0x01) {
368 nextCluster = nextCluster >> 4;
369 } else {
370 nextCluster &= 0x0FFF;
371 }
372
373 if (nextCluster >= 0x0FF7)
374 {
375 nextCluster = CLUSTER_EOF;
376 }
377
378 break;
379
380 case FS_FAT16:
381 sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
382 offset = cluster % (BYTE_PER_READ >> 1);
383
384 // If FAT buffer contains wrong sector
385 if (sector != fatBufferCurSector)
386 {
387 // Load correct sector to buffer
388 fatBufferCurSector = sector;
389 disc_ReadSector(fatBufferCurSector, fatBuffer);
390 }
391
392 // read the nextCluster value
393 nextCluster = ((u16*)fatBuffer)[offset];
394
395 if (nextCluster >= 0xFFF7)
396 {
397 nextCluster = CLUSTER_EOF;
398 }
399 break;
400
401 case FS_FAT32:
402 sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
403 offset = cluster % (BYTE_PER_READ >> 2);
404
405 // If FAT buffer contains wrong sector
406 if (sector != fatBufferCurSector)
407 {
408 // Load correct sector to buffer
409 fatBufferCurSector = sector;
410 disc_ReadSector(fatBufferCurSector, fatBuffer);
411 }
412
413 // read the nextCluster value
414 nextCluster = (((u32*)fatBuffer)[offset]) & 0x0FFFFFFF;
415
416 if (nextCluster >= 0x0FFFFFF7)
417 {
418 nextCluster = CLUSTER_EOF;
419 }
420 break;
421
422 default:
423 nextCluster = CLUSTER_FREE;
424 break;
425 }
426
427 return nextCluster;
428 }
429
430 #ifdef CAN_WRITE_TO_DISC
431 /*-----------------------------------------------------------------
432 FAT_WriteFatEntry
433 Internal function - writes FAT information about a cluster
434 -----------------------------------------------------------------*/
FAT_WriteFatEntry(u32 cluster,u32 value)435 bool FAT_WriteFatEntry (u32 cluster, u32 value)
436 {
437 u32 sector;
438 int offset;
439
440 if ((cluster < 0x0002) || (cluster > fatLastCluster))
441 {
442 return false;
443 }
444
445 switch (filesysType)
446 {
447 case FS_UNKNOWN:
448 return false;
449 break;
450
451 case FS_FAT12:
452 sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
453 offset = ((cluster * 3) / 2) % BYTE_PER_READ;
454
455 // If FAT buffer contains wrong sector
456 if (sector != fatBufferCurSector)
457 {
458 // Load correct sector to buffer
459 fatBufferCurSector = sector;
460 disc_ReadSector(fatBufferCurSector, fatBuffer);
461 }
462
463 if (cluster & 0x01) {
464
465 ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4);
466
467 offset++;
468 if (offset >= BYTE_PER_READ) {
469 offset = 0;
470 // write the buffer back to disc
471 disc_WriteSector(fatBufferCurSector, fatBuffer);
472 // read the next sector
473 fatBufferCurSector++;
474 disc_ReadSector(fatBufferCurSector, fatBuffer);
475 }
476
477 ((u8*)fatBuffer)[offset] = (value & 0x0FF0) >> 4;
478
479 } else {
480
481 ((u8*)fatBuffer)[offset] = value & 0xFF;
482
483 offset++;
484 if (offset >= BYTE_PER_READ) {
485 offset = 0;
486 // write the buffer back to disc
487 disc_WriteSector(fatBufferCurSector, fatBuffer);
488 // read the next sector
489 fatBufferCurSector++;
490 disc_ReadSector(fatBufferCurSector, fatBuffer);
491 }
492
493 ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F);
494 }
495
496 break;
497
498 case FS_FAT16:
499 sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
500 offset = cluster % (BYTE_PER_READ >> 1);
501
502 // If FAT buffer contains wrong sector
503 if (sector != fatBufferCurSector)
504 {
505 // Load correct sector to buffer
506 fatBufferCurSector = sector;
507 disc_ReadSector(fatBufferCurSector, fatBuffer);
508 }
509
510 // write the value to the FAT buffer
511 ((u16*)fatBuffer)[offset] = (value & 0xFFFF);
512
513 break;
514
515 case FS_FAT32:
516 sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
517 offset = cluster % (BYTE_PER_READ >> 2);
518
519 // If FAT buffer contains wrong sector
520 if (sector != fatBufferCurSector)
521 {
522 // Load correct sector to buffer
523 fatBufferCurSector = sector;
524 disc_ReadSector(fatBufferCurSector, fatBuffer);
525 }
526
527 // write the value to the FAT buffer
528 (((u32*)fatBuffer)[offset]) = value;
529
530 break;
531
532 default:
533 return false;
534 break;
535 }
536
537 // write the buffer back to disc
538 disc_WriteSector(fatBufferCurSector, fatBuffer);
539
540 return true;
541 }
542 #endif
543
544 #ifdef CAN_WRITE_TO_DISC
545 /*-----------------------------------------------------------------
546 FAT_ReadWriteFatEntryBuffered
547 Internal function - writes FAT information about a cluster to a
548 buffer that should then be flushed to disc using
549 FAT_WriteFatEntryFlushBuffer()
550 Call FAT_WriteFatEntry first so as not to ruin the disc.
551 Also returns the entry being replaced
552 -----------------------------------------------------------------*/
FAT_ReadWriteFatEntryBuffered(u32 cluster,u32 value)553 u32 FAT_ReadWriteFatEntryBuffered (u32 cluster, u32 value)
554 {
555 u32 sector;
556 int offset;
557 u32 oldValue;
558
559 if ((cluster < 0x0002) || (cluster > fatLastCluster))
560 return CLUSTER_FREE;
561
562
563 switch (filesysType)
564 {
565 case FS_UNKNOWN:
566 oldValue = CLUSTER_FREE;
567 break;
568
569 case FS_FAT12:
570 sector = filesysFAT + (((cluster * 3) / 2) / BYTE_PER_READ);
571 offset = ((cluster * 3) / 2) % BYTE_PER_READ;
572
573 // If FAT buffer contains wrong sector
574 if (sector != fatBufferCurSector)
575 {
576 // write the old buffer to disc
577 if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
578 disc_WriteSector(fatBufferCurSector, fatBuffer);
579 // Load correct sector to buffer
580 fatBufferCurSector = sector;
581 disc_ReadSector(fatBufferCurSector, fatBuffer);
582 }
583
584 if (cluster & 0x01) {
585
586 oldValue = (((u8*)fatBuffer)[offset] & 0xF0) >> 4;
587 ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0x0F) | ((value & 0x0F) << 4);
588
589 offset++;
590 if (offset >= BYTE_PER_READ) {
591 offset = 0;
592 // write the buffer back to disc
593 disc_WriteSector(fatBufferCurSector, fatBuffer);
594 // read the next sector
595 fatBufferCurSector++;
596 disc_ReadSector(fatBufferCurSector, fatBuffer);
597 }
598
599 oldValue |= ((((u8*)fatBuffer)[offset]) << 4) & 0x0FF0;
600 ((u8*)fatBuffer)[offset] = (value & 0x0FF0) >> 4;
601
602 } else {
603
604 oldValue = ((u8*)fatBuffer)[offset] & 0xFF;
605 ((u8*)fatBuffer)[offset] = value & 0xFF;
606
607 offset++;
608 if (offset >= BYTE_PER_READ) {
609 offset = 0;
610 // write the buffer back to disc
611 disc_WriteSector(fatBufferCurSector, fatBuffer);
612 // read the next sector
613 fatBufferCurSector++;
614 disc_ReadSector(fatBufferCurSector, fatBuffer);
615 }
616
617 oldValue |= (((u8*)fatBuffer)[offset] & 0x0F) << 8;
618 ((u8*)fatBuffer)[offset] = (((u8*)fatBuffer)[offset] & 0xF0) | ((value >> 8) & 0x0F);
619 }
620
621 if (oldValue >= 0x0FF7)
622 {
623 oldValue = CLUSTER_EOF;
624 }
625
626 break;
627
628 case FS_FAT16:
629 sector = filesysFAT + ((cluster << 1) / BYTE_PER_READ);
630 offset = cluster % (BYTE_PER_READ >> 1);
631
632 // If FAT buffer contains wrong sector
633 if (sector != fatBufferCurSector)
634 {
635 // write the old buffer to disc
636 if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
637 disc_WriteSector(fatBufferCurSector, fatBuffer);
638 // Load correct sector to buffer
639 fatBufferCurSector = sector;
640 disc_ReadSector(fatBufferCurSector, fatBuffer);
641 }
642
643 // write the value to the FAT buffer
644 oldValue = ((u16*)fatBuffer)[offset];
645 ((u16*)fatBuffer)[offset] = value;
646
647 if (oldValue >= 0xFFF7)
648 {
649 oldValue = CLUSTER_EOF;
650 }
651
652 break;
653
654 case FS_FAT32:
655 sector = filesysFAT + ((cluster << 2) / BYTE_PER_READ);
656 offset = cluster % (BYTE_PER_READ >> 2);
657
658 // If FAT buffer contains wrong sector
659 if (sector != fatBufferCurSector)
660 {
661 // write the old buffer to disc
662 if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
663 disc_WriteSector(fatBufferCurSector, fatBuffer);
664 // Load correct sector to buffer
665 fatBufferCurSector = sector;
666 disc_ReadSector(fatBufferCurSector, fatBuffer);
667 }
668
669 // write the value to the FAT buffer
670 oldValue = ((u32*)fatBuffer)[offset];
671 ((u32*)fatBuffer)[offset] = value;
672
673 if (oldValue >= 0x0FFFFFF7)
674 {
675 oldValue = CLUSTER_EOF;
676 }
677
678 break;
679
680 default:
681 oldValue = CLUSTER_FREE;
682 break;
683 }
684
685 return oldValue;
686 }
687 #endif
688
689 #ifdef CAN_WRITE_TO_DISC
690 /*-----------------------------------------------------------------
691 FAT_WriteFatEntryFlushBuffer
692 Flush the FAT buffer back to the disc
693 -----------------------------------------------------------------*/
FAT_WriteFatEntryFlushBuffer(void)694 bool FAT_WriteFatEntryFlushBuffer (void)
695 {
696 // write the buffer disc
697 if ((fatBufferCurSector >= filesysFAT) && (fatBufferCurSector < (filesysFAT + filesysSecPerFAT)))
698 {
699 disc_WriteSector(fatBufferCurSector, fatBuffer);
700 return true;
701 } else {
702 return false;
703 }
704 }
705 #endif
706
707 #ifdef CAN_WRITE_TO_DISC
708 /*-----------------------------------------------------------------
709 FAT_FirstFreeCluster
710 Internal function - gets the first available free cluster
711 -----------------------------------------------------------------*/
FAT_FirstFreeCluster(void)712 u32 FAT_FirstFreeCluster(void)
713 {
714 // Start at first valid cluster
715 if (fatFirstFree < CLUSTER_FIRST)
716 fatFirstFree = CLUSTER_FIRST;
717
718 while ((FAT_NextCluster(fatFirstFree) != CLUSTER_FREE) && (fatFirstFree <= fatLastCluster))
719 {
720 fatFirstFree++;
721 }
722 if (fatFirstFree > fatLastCluster)
723 {
724 return CLUSTER_EOF;
725 }
726 return fatFirstFree;
727 }
728 #endif
729
730 #ifdef CAN_WRITE_TO_DISC
731 /*-----------------------------------------------------------------
732 FAT_LinkFreeCluster
733 Internal function - gets the first available free cluster, sets it
734 to end of file, links the input cluster to it then returns the
735 cluster number
736 -----------------------------------------------------------------*/
FAT_LinkFreeCluster(u32 cluster)737 u32 FAT_LinkFreeCluster(u32 cluster)
738 {
739 u32 firstFree;
740 u32 curLink;
741
742 if (cluster > fatLastCluster)
743 {
744 return CLUSTER_FREE;
745 }
746
747 // Check if the cluster already has a link, and return it if so
748 curLink = FAT_NextCluster (cluster);
749 if ((curLink >= CLUSTER_FIRST) && (curLink < fatLastCluster))
750 {
751 return curLink; // Return the current link - don't allocate a new one
752 }
753
754 // Get a free cluster
755 firstFree = FAT_FirstFreeCluster();
756
757 // If couldn't get a free cluster then return
758 if (firstFree == CLUSTER_EOF)
759 {
760 return CLUSTER_FREE;
761 }
762
763 if ((cluster >= CLUSTER_FIRST) && (cluster < fatLastCluster))
764 {
765 // Update the linked from FAT entry
766 FAT_WriteFatEntry (cluster, firstFree);
767 }
768 // Create the linked to FAT entry
769 FAT_WriteFatEntry (firstFree, CLUSTER_EOF);
770
771 return firstFree;
772 }
773 #endif
774
775
776 #ifdef CAN_WRITE_TO_DISC
777 /*-----------------------------------------------------------------
778 FAT_ClearLinks
779 Internal function - frees any cluster used by a file
780 -----------------------------------------------------------------*/
FAT_ClearLinks(u32 cluster)781 bool FAT_ClearLinks (u32 cluster)
782 {
783 u32 nextCluster;
784
785 if ((cluster < 0x0002) || (cluster > fatLastCluster))
786 return false;
787
788 // Store next cluster before erasing the link
789 nextCluster = FAT_NextCluster (cluster);
790
791 // Erase the link
792 FAT_WriteFatEntry (cluster, CLUSTER_FREE);
793
794 // Move onto next cluster
795 cluster = nextCluster;
796
797 while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE))
798 {
799 cluster = FAT_ReadWriteFatEntryBuffered (cluster, CLUSTER_FREE);
800 }
801
802 // Flush fat write buffer
803 FAT_WriteFatEntryFlushBuffer ();
804
805 return true;
806 }
807 #endif
808
809
810 /*-----------------------------------------------------------------
811 FAT_InitFiles
812 Reads the FAT information from the CF card.
813 You need to call this before reading any files.
814 bool return OUT: true if successful.
815 -----------------------------------------------------------------*/
FAT_InitFiles(void)816 bool FAT_InitFiles (void)
817 {
818 int i;
819 int bootSector;
820 BOOT_SEC* bootSec;
821
822 if (!disc_Init())
823 {
824 return (false);
825 }
826 // Read first sector of CF card
827 if ( !disc_ReadSector(0, globalBuffer)) {
828 return false;
829 }
830
831
832 // Make sure it is a valid MBR or boot sector
833 /* if ( (globalBuffer[0x1FE] != 0x55) || (globalBuffer[0x1FF] != 0xAA)) {
834 return false;
835 }*/
836
837
838
839 // Check if there is a FAT string, which indicates this is a boot sector
840 if ((globalBuffer[0x36] == 'F') && (globalBuffer[0x37] == 'A') && (globalBuffer[0x38] == 'T'))
841 {
842 bootSector = 0;
843 }
844 // Check for FAT32
845 else if ((globalBuffer[0x52] == 'F') && (globalBuffer[0x53] == 'A') && (globalBuffer[0x54] == 'T'))
846 {
847 bootSector = 0;
848 }
849 else // This is an MBR
850 {
851 // Find first valid partition from MBR
852 // First check for an active partition
853 for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i] != 0x80); i+= 0x10);
854 // If it didn't find an active partition, search for any valid partition
855 if (i == 0x1FE)
856 for (i=0x1BE; (i < 0x1FE) && (globalBuffer[i+0x04] == 0x00); i+= 0x10);
857
858 // Go to first valid partition
859 if ( i != 0x1FE) // Make sure it found a partition
860 {
861 bootSector = globalBuffer[0x8 + i] + (globalBuffer[0x9 + i] << 8) + (globalBuffer[0xA + i] << 16) + ((globalBuffer[0xB + i] << 24) & 0x0F);
862 } else {
863 bootSector = 0; // No partition found, assume this is a MBR free disk
864 }
865 }
866
867 // Read in boot sector
868 bootSec = (BOOT_SEC*) globalBuffer;
869 if (!disc_ReadSector (bootSector, bootSec)) {
870 return false;
871 }
872
873 // Store required information about the file system
874 if (bootSec->sectorsPerFAT != 0)
875 {
876 filesysSecPerFAT = bootSec->sectorsPerFAT;
877 }
878 else
879 {
880 filesysSecPerFAT = bootSec->extBlock.fat32.sectorsPerFAT32;
881 }
882
883 if (bootSec->numSectorsSmall != 0)
884 {
885 filesysNumSec = bootSec->numSectorsSmall;
886 }
887 else
888 {
889 filesysNumSec = bootSec->numSectors;
890 }
891
892 filesysBytePerSec = BYTE_PER_READ; // Sector size is redefined to be 512 bytes
893 filesysSecPerClus = bootSec->sectorsPerCluster * bootSec->bytesPerSector / BYTE_PER_READ;
894 filesysBytePerClus = filesysBytePerSec * filesysSecPerClus;
895 filesysFAT = bootSector + bootSec->reservedSectors;
896
897 filesysRootDir = filesysFAT + (bootSec->numFATs * filesysSecPerFAT);
898 filesysData = filesysRootDir + ((bootSec->rootEntries * sizeof(DIR_ENT)) / filesysBytePerSec);
899
900 filesysTotalSize = (filesysNumSec - filesysData) * filesysBytePerSec;
901
902 // Store info about FAT
903 fatLastCluster = (filesysNumSec - filesysData) / bootSec->sectorsPerCluster;
904 fatFirstFree = CLUSTER_FIRST;
905 fatBufferCurSector = 0;
906 disc_ReadSector(fatBufferCurSector, fatBuffer);
907
908 if (fatLastCluster < 4085)
909 {
910 filesysType = FS_FAT12; // FAT12 volume - unsupported
911 }
912 else if (fatLastCluster < 65525)
913 {
914 filesysType = FS_FAT16; // FAT16 volume
915 }
916 else
917 {
918 filesysType = FS_FAT32; // FAT32 volume
919 }
920
921 if (filesysType != FS_FAT32)
922 {
923 filesysRootDirClus = FAT16_ROOT_DIR_CLUSTER;
924 }
925 else // Set up for the FAT32 way
926 {
927 filesysRootDirClus = bootSec->extBlock.fat32.rootClus;
928 // Check if FAT mirroring is enabled
929 if (!(bootSec->extBlock.fat32.extFlags & 0x80))
930 {
931 // Use the active FAT
932 filesysFAT = filesysFAT + ( filesysSecPerFAT * (bootSec->extBlock.fat32.extFlags & 0x0F));
933 }
934 }
935
936 // Set current directory to the root
937 curWorkDirCluster = filesysRootDirClus;
938 wrkDirCluster = filesysRootDirClus;
939 wrkDirSector = 0;
940 wrkDirOffset = 0;
941
942 // Set all files to free
943 for (i=0; i < MAX_FILES_OPEN; i++)
944 {
945 openFiles[i].inUse = false;
946 }
947
948 // No long filenames so far
949 lfnExists = false;
950 for (i = 0; i < MAX_FILENAME_LENGTH; i++)
951 {
952 lfnName[i] = '\0';
953 }
954
955 return (true);
956 }
957
958 /*-----------------------------------------------------------------
959 FAT_FreeFiles
960 Closes all open files then resets the CF card.
961 Call this before exiting back to the GBAMP
962 bool return OUT: true if successful.
963 -----------------------------------------------------------------*/
FAT_FreeFiles(void)964 bool FAT_FreeFiles (void)
965 {
966 int i;
967
968 // Close all open files
969 for (i=0; i < MAX_FILES_OPEN; i++)
970 {
971 if (openFiles[i].inUse == true)
972 {
973 FAT_fclose(&openFiles[i]);
974 }
975 }
976
977 // Flush any sectors in disc cache
978 disc_CacheFlush();
979
980 // Clear card status
981 disc_ClearStatus();
982
983 // Return status of card
984 return disc_IsInserted();
985 }
986
987
988 /*-----------------------------------------------------------------
989 FAT_GetDirEntry
990 Return the file info structure of the next valid file entry
991 u32 dirCluster: IN cluster of subdirectory table
992 int entry: IN the desired file entry
993 int origin IN: relative position of the entry
994 DIR_ENT return OUT: desired dirEntry. First char will be FILE_FREE if
995 the entry does not exist.
996 -----------------------------------------------------------------*/
FAT_GetDirEntry(u32 dirCluster,int entry,int origin)997 DIR_ENT FAT_GetDirEntry ( u32 dirCluster, int entry, int origin)
998 {
999 DIR_ENT dir;
1000 DIR_ENT_LFN lfn;
1001 int firstSector = 0;
1002 bool notFound = false;
1003 bool found = false;
1004 int maxSectors;
1005 int lfnPos, aliasPos;
1006 u8 lfnChkSum, chkSum;
1007
1008 int i;
1009
1010 dir.name[0] = FILE_FREE; // default to no file found
1011 dir.attrib = 0x00;
1012
1013 // Check if fat has been initialized
1014 if (filesysBytePerSec == 0)
1015 {
1016 return (dir);
1017 }
1018
1019 switch (origin)
1020 {
1021 case SEEK_SET:
1022 wrkDirCluster = dirCluster;
1023 wrkDirSector = 0;
1024 wrkDirOffset = -1;
1025 break;
1026 case SEEK_CUR: // Don't change anything
1027 break;
1028 case SEEK_END: // Find entry signifying end of directory
1029 // Subtraction will never reach 0, so it keeps going
1030 // until reaches end of directory
1031 wrkDirCluster = dirCluster;
1032 wrkDirSector = 0;
1033 wrkDirOffset = -1;
1034 entry = -1;
1035 break;
1036 default:
1037 return dir;
1038 }
1039
1040 lfnChkSum = 0;
1041 maxSectors = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus);
1042
1043 // Scan Dir for correct entry
1044 firstSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster));
1045 disc_ReadSector (firstSector + wrkDirSector, globalBuffer);
1046 found = false;
1047 notFound = false;
1048 do {
1049 wrkDirOffset++;
1050 if (wrkDirOffset == BYTE_PER_READ / sizeof (DIR_ENT))
1051 {
1052 wrkDirOffset = 0;
1053 wrkDirSector++;
1054 if ((wrkDirSector == filesysSecPerClus) && (wrkDirCluster != FAT16_ROOT_DIR_CLUSTER))
1055 {
1056 wrkDirSector = 0;
1057 wrkDirCluster = FAT_NextCluster(wrkDirCluster);
1058 if (wrkDirCluster == CLUSTER_EOF)
1059 {
1060 notFound = true;
1061 }
1062 firstSector = FAT_ClustToSect(wrkDirCluster);
1063 }
1064 else if ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER) && (wrkDirSector == (filesysData - filesysRootDir)))
1065 {
1066 notFound = true; // Got to end of root dir
1067 }
1068 disc_ReadSector (firstSector + wrkDirSector, globalBuffer);
1069 }
1070 dir = ((DIR_ENT*) globalBuffer)[wrkDirOffset];
1071 if ((dir.name[0] != FILE_FREE) && (dir.name[0] > 0x20) && ((dir.attrib & ATTRIB_VOL) != ATTRIB_VOL))
1072 {
1073 entry--;
1074 if (lfnExists)
1075 {
1076 // Calculate file checksum
1077 chkSum = 0;
1078 for (aliasPos=0; aliasPos < 11; aliasPos++)
1079 {
1080 // NOTE: The operation is an unsigned char rotate right
1081 chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + (aliasPos < 8 ? dir.name[aliasPos] : dir.ext[aliasPos - 8]);
1082 }
1083 if (chkSum != lfnChkSum)
1084 {
1085 lfnExists = false;
1086 lfnName[0] = '\0';
1087 }
1088 }
1089 if (entry == 0)
1090 {
1091 if (!lfnExists)
1092 {
1093 FAT_GetFilename (dir, lfnName);
1094 }
1095 found = true;
1096 }
1097 }
1098 else if (dir.name[0] == FILE_LAST)
1099 {
1100 if (origin == SEEK_END)
1101 {
1102 found = true;
1103 }
1104 else
1105 {
1106 notFound = true;
1107 }
1108 }
1109 else if (dir.attrib == ATTRIB_LFN)
1110 {
1111 lfn = ((DIR_ENT_LFN*) globalBuffer)[wrkDirOffset];
1112 if (lfn.ordinal & LFN_DEL)
1113 {
1114 lfnExists = false;
1115 }
1116 else if (lfn.ordinal & LFN_END) // Last part of LFN, make sure it isn't deleted (Thanks MoonLight)
1117 {
1118 lfnExists = true;
1119 lfnName[(lfn.ordinal & ~LFN_END) * 13] = '\0'; // Set end of lfn to null character
1120 lfnChkSum = lfn.checkSum;
1121 }
1122 if (lfnChkSum != lfn.checkSum)
1123 {
1124 lfnExists = false;
1125 }
1126 if (lfnExists)
1127 {
1128 lfnPos = ((lfn.ordinal & ~LFN_END) - 1) * 13;
1129 for (i = 0; i < 13; i++) {
1130 lfnName[lfnPos + i] = ((u8*)&lfn)[(int)(lfn_offset_table[i])] /* | ((u8*)&lfn)[(int)(lfn_offset_table[i]) + 1] include this for unicode support*/;
1131 }
1132 }
1133 }
1134 } while (!found && !notFound);
1135
1136 // If no file is found, return FILE_FREE
1137 if (notFound)
1138 {
1139 dir.name[0] = FILE_FREE;
1140 }
1141
1142 return (dir);
1143 }
1144
1145
1146 /*-----------------------------------------------------------------
1147 FAT_GetLongFilename
1148 Get the long name of the last file or directory retrived with
1149 GetDirEntry. Also works for FindFirstFile and FindNextFile.
1150 If a long name doesn't exist, it returns the short name
1151 instead.
1152 char* filename: OUT will be filled with the filename, should be at
1153 least 256 bytes long
1154 bool return OUT: return true if successful
1155 -----------------------------------------------------------------*/
FAT_GetLongFilename(char * filename)1156 bool FAT_GetLongFilename (char* filename)
1157 {
1158 if (filename == NULL)
1159 return false;
1160
1161 strncpy (filename, lfnName, MAX_FILENAME_LENGTH - 1);
1162 filename[MAX_FILENAME_LENGTH - 1] = '\0';
1163
1164 return true;
1165 }
1166
1167
1168 /*-----------------------------------------------------------------
1169 FAT_GetFilename
1170 Get the alias (short name) of the file or directory stored in
1171 dirEntry
1172 DIR_ENT dirEntry: IN a valid directory table entry
1173 char* alias OUT: will be filled with the alias (short filename),
1174 should be at least 13 bytes long
1175 bool return OUT: return true if successful
1176 -----------------------------------------------------------------*/
FAT_GetFilename(DIR_ENT dirEntry,char * alias)1177 bool FAT_GetFilename (DIR_ENT dirEntry, char* alias)
1178 {
1179 int i=0;
1180 int j=0;
1181
1182 alias[0] = '\0';
1183 if (dirEntry.name[0] != FILE_FREE)
1184 {
1185 if (dirEntry.name[0] == '.')
1186 {
1187 alias[0] = '.';
1188 if (dirEntry.name[1] == '.')
1189 {
1190 alias[1] = '.';
1191 alias[2] = '\0';
1192 }
1193 else
1194 {
1195 alias[1] = '\0';
1196 }
1197 }
1198 else
1199 {
1200 // Copy the filename from the dirEntry to the string
1201 for (i = 0; (i < 8) && (dirEntry.name[i] != ' '); i++)
1202 {
1203 alias[i] = dirEntry.name[i];
1204 }
1205 // Copy the extension from the dirEntry to the string
1206 if (dirEntry.ext[0] != ' ')
1207 {
1208 alias[i++] = '.';
1209 for ( j = 0; (j < 3) && (dirEntry.ext[j] != ' '); j++)
1210 {
1211 alias[i++] = dirEntry.ext[j];
1212 }
1213 }
1214 alias[i] = '\0';
1215 }
1216 }
1217
1218 return (alias[0] != '\0');
1219 }
1220
1221 /*-----------------------------------------------------------------
1222 FAT_GetAlias
1223 Get the alias (short name) of the last file or directory entry read
1224 using GetDirEntry. Works for FindFirstFile and FindNextFile
1225 char* alias OUT: will be filled with the alias (short filename),
1226 should be at least 13 bytes long
1227 bool return OUT: return true if successful
1228 -----------------------------------------------------------------*/
FAT_GetAlias(char * alias)1229 bool FAT_GetAlias (char* alias)
1230 {
1231 if (alias == NULL)
1232 {
1233 return false;
1234 }
1235 // Read in the last accessed directory entry
1236 disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
1237
1238 return FAT_GetFilename (((DIR_ENT*)globalBuffer)[wrkDirOffset], alias);
1239 }
1240
1241 /*-----------------------------------------------------------------
1242 FAT_GetFileSize
1243 Get the file size of the last file found or openned.
1244 This idea is based on a modification by MoonLight
1245 u32 return OUT: the file size
1246 -----------------------------------------------------------------*/
FAT_GetFileSize(void)1247 u32 FAT_GetFileSize (void)
1248 {
1249 // Read in the last accessed directory entry
1250 disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
1251
1252 return ((DIR_ENT*)globalBuffer)[wrkDirOffset].fileSize;
1253 }
1254
1255 /*-----------------------------------------------------------------
1256 FAT_GetFileCluster
1257 Get the first cluster of the last file found or openned.
1258 u32 return OUT: the file start cluster
1259 -----------------------------------------------------------------*/
FAT_GetFileCluster(void)1260 u32 FAT_GetFileCluster (void)
1261 {
1262 // Read in the last accessed directory entry
1263 disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
1264
1265 return (((DIR_ENT*)globalBuffer)[wrkDirOffset].startCluster) | (((DIR_ENT*)globalBuffer)[wrkDirOffset].startClusterHigh << 16);
1266 }
1267
1268 /*-----------------------------------------------------------------
1269 FAT_GetFileAttributes
1270 Get the attributes of the last file found or openned.
1271 u8 return OUT: the file's attributes
1272 -----------------------------------------------------------------*/
FAT_GetFileAttributes(void)1273 u8 FAT_GetFileAttributes (void)
1274 {
1275 // Read in the last accessed directory entry
1276 disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
1277
1278 return ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib;
1279 }
1280
1281 #ifdef CAN_WRITE_TO_DISC
1282 /*-----------------------------------------------------------------
1283 FAT_SetFileAttributes
1284 Set the attributes of a file.
1285 const char* filename IN: The name and path of the file to modify
1286 u8 attributes IN: The attribute values to assign
1287 u8 mask IN: Detemines which attributes are changed
1288 u8 return OUT: the file's new attributes
1289 -----------------------------------------------------------------*/
FAT_SetFileAttributes(const char * filename,u8 attributes,u8 mask)1290 u8 FAT_SetFileAttributes (const char* filename, u8 attributes, u8 mask)
1291 {
1292 // Get the file
1293 if (!FAT_FileExists(filename)) {
1294 return 0xff;
1295 }
1296
1297 // Read in the last accessed directory entry
1298 disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
1299
1300 ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib = (((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib & ~(mask & 0x27)) | (attributes & 0x27); // 0x27 is he settable attributes
1301
1302 disc_WriteSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
1303
1304 return ((DIR_ENT*)globalBuffer)[wrkDirOffset].attrib;
1305 }
1306 #endif
1307
1308 #ifdef FILE_TIME_SUPPORT
FAT_FileTimeToCTime(u16 fileTime,u16 fileDate)1309 time_t FAT_FileTimeToCTime (u16 fileTime, u16 fileDate)
1310 {
1311 struct tm timeInfo;
1312
1313 timeInfo.tm_year = (fileDate >> 9) + 80; // years since midnight January 1970
1314 timeInfo.tm_mon = ((fileDate >> 5) & 0xf) - 1; // Months since january
1315 timeInfo.tm_mday = fileDate & 0x1f; // Day of the month
1316
1317 timeInfo.tm_hour = fileTime >> 11; // hours past midnight
1318 timeInfo.tm_min = (fileTime >> 5) & 0x3f; // minutes past the hour
1319 timeInfo.tm_sec = (fileTime & 0x1f) * 2; // seconds past the minute
1320
1321 return mktime(&timeInfo);
1322 }
1323
1324 /*-----------------------------------------------------------------
1325 FAT_GetFileCreationTime
1326 Get the creation time of the last file found or openned.
1327 time_t return OUT: the file's creation time
1328 -----------------------------------------------------------------*/
FAT_GetFileCreationTime(void)1329 time_t FAT_GetFileCreationTime (void)
1330 {
1331 // Read in the last accessed directory entry
1332 disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
1333
1334 return FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].cTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].cDate);
1335 }
1336
1337 /*-----------------------------------------------------------------
1338 FAT_GetFileLastWriteTime
1339 Get the creation time of the last file found or openned.
1340 time_t return OUT: the file's creation time
1341 -----------------------------------------------------------------*/
FAT_GetFileLastWriteTime(void)1342 time_t FAT_GetFileLastWriteTime (void)
1343 {
1344 // Read in the last accessed directory entry
1345 disc_ReadSector ((wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector, globalBuffer);
1346
1347 return FAT_FileTimeToCTime(((DIR_ENT*)globalBuffer)[wrkDirOffset].mTime, ((DIR_ENT*)globalBuffer)[wrkDirOffset].mDate);
1348 }
1349 #endif
1350
1351 /*-----------------------------------------------------------------
1352 FAT_DirEntFromPath
1353 Finds the directory entry for a file or directory from a path
1354 Path separator is a forward slash /
1355 const char* path: IN null terminated string of path.
1356 DIR_ENT return OUT: dirEntry of found file. First char will be FILE_FREE
1357 if the file was not found
1358 -----------------------------------------------------------------*/
FAT_DirEntFromPath(const char * path)1359 DIR_ENT FAT_DirEntFromPath (const char* path)
1360 {
1361 int pathPos;
1362 char name[MAX_FILENAME_LENGTH];
1363 char alias[13];
1364 int namePos;
1365 bool found, notFound;
1366 DIR_ENT dirEntry;
1367 u32 dirCluster;
1368 bool flagLFN, dotSeen;
1369 // Start at beginning of path
1370 pathPos = 0;
1371
1372 #ifdef DS_BUILD_F
1373 // Problems with Kyrandia doing a load of path lookups are reduced by this hack.
1374 if (strstr(path, ".voc") || strstr(path, ".voc"))
1375 {
1376 dirEntry.name[0] = FILE_FREE;
1377 dirEntry.attrib = 0x00;
1378 return dirEntry;
1379 }
1380 #endif
1381
1382 if (path[pathPos] == '/')
1383 {
1384 dirCluster = filesysRootDirClus; // Start at root directory
1385 }
1386 else
1387 {
1388 dirCluster = curWorkDirCluster; // Start at current working dir
1389 }
1390
1391 // Eat any slash /
1392 while ((path[pathPos] == '/') && (path[pathPos] != '\0'))
1393 {
1394 pathPos++;
1395 }
1396
1397 // Search until can't continue
1398 found = false;
1399 notFound = false;
1400 while (!notFound && !found)
1401 {
1402 flagLFN = false;
1403 // Copy name from path
1404 namePos = 0;
1405 if ((path[pathPos] == '.') && ((path[pathPos + 1] == '\0') || (path[pathPos + 1] == '/'))) {
1406 // Dot entry
1407 name[namePos++] = '.';
1408 pathPos++;
1409 } else if ((path[pathPos] == '.') && (path[pathPos + 1] == '.') && ((path[pathPos + 2] == '\0') || (path[pathPos + 2] == '/'))){
1410 // Double dot entry
1411 name[namePos++] = '.';
1412 pathPos++;
1413 name[namePos++] = '.';
1414 pathPos++;
1415 } else {
1416 // Copy name from path
1417 if (path[pathPos] == '.') {
1418 flagLFN = true;
1419 }
1420 dotSeen = false;
1421 while ((namePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0') && (path[pathPos] != '/'))
1422 {
1423 name[namePos] = ucase(path[pathPos]);
1424 if ((name[namePos] <= ' ') || ((name[namePos] >= ':') && (name[namePos] <= '?'))) // Invalid character
1425 {
1426 flagLFN = true;
1427 }
1428 if (name[namePos] == '.') {
1429 if (!dotSeen) {
1430 dotSeen = true;
1431 } else {
1432 flagLFN = true;
1433 }
1434 }
1435 namePos++;
1436 pathPos++;
1437 }
1438 // Check if a long filename was specified
1439 if (namePos > 12)
1440 {
1441 flagLFN = true;
1442 }
1443 }
1444
1445 // Add end of string char
1446 name[namePos] = '\0';
1447
1448 // Move through path to correct place
1449 while ((path[pathPos] != '/') && (path[pathPos] != '\0'))
1450 pathPos++;
1451 // Eat any slash /
1452 while ((path[pathPos] == '/') && (path[pathPos] != '\0'))
1453 {
1454 pathPos++;
1455 }
1456
1457 // Search current Dir for correct entry
1458 dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_SET);
1459 while ( !found && !notFound)
1460 {
1461 // Match filename
1462 found = true;
1463 for (namePos = 0; (namePos < MAX_FILENAME_LENGTH-1) && found && (name[namePos] != '\0') && (lfnName[namePos] != '\0'); namePos++)
1464 {
1465 if (name[namePos] != ucase(lfnName[namePos]))
1466 {
1467 found = false;
1468 }
1469 }
1470 if ((name[namePos] == '\0') != (lfnName[namePos] == '\0'))
1471 {
1472 found = false;
1473 }
1474
1475 // Check against alias as well.
1476 if (!found)
1477 {
1478 FAT_GetFilename(dirEntry, alias);
1479 found = true;
1480 for (namePos = 0; (namePos < (sizeof(alias)/sizeof(alias[0]))-1) && found && (name[namePos] != '\0') && (alias[namePos] != '\0'); namePos++)
1481 {
1482 if (name[namePos] != ucase(alias[namePos]))
1483 {
1484 found = false;
1485 }
1486 }
1487 if ((name[namePos] == '\0') != (alias[namePos] == '\0'))
1488 {
1489 found = false;
1490 }
1491 }
1492
1493 if (dirEntry.name[0] == FILE_FREE)
1494 // Couldn't find specified file
1495 {
1496 found = false;
1497 notFound = true;
1498 }
1499 if (!found && !notFound)
1500 {
1501 dirEntry = FAT_GetDirEntry (dirCluster, 1, SEEK_CUR);
1502 }
1503 }
1504
1505 if (found && ((dirEntry.attrib & ATTRIB_DIR) == ATTRIB_DIR) && (path[pathPos] != '\0'))
1506 // It has found a directory from within the path that needs to be followed
1507 {
1508 found = false;
1509 dirCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
1510 }
1511 }
1512
1513 if (notFound)
1514 {
1515 dirEntry.name[0] = FILE_FREE;
1516 dirEntry.attrib = 0x00;
1517 }
1518
1519 return (dirEntry);
1520 }
1521
1522
1523 #ifdef CAN_WRITE_TO_DISC
1524 /*-----------------------------------------------------------------
1525 FAT_AddDirEntry
1526 Creates a new dir entry for a file
1527 Path separator is a forward slash /
1528 const char* path: IN null terminated string of path to file.
1529 DIR_ENT newDirEntry IN: The directory entry to use.
1530 int file IN: The file being added (optional, use -1 if not used)
1531 bool return OUT: true if successful
1532 -----------------------------------------------------------------*/
FAT_AddDirEntry(const char * path,DIR_ENT newDirEntry)1533 bool FAT_AddDirEntry (const char* path, DIR_ENT newDirEntry)
1534 {
1535 char filename[MAX_FILENAME_LENGTH];
1536 int filePos, pathPos, aliasPos;
1537 char tempChar;
1538 bool flagLFN, dotSeen;
1539 char fileAlias[13] = {0};
1540 int tailNum;
1541
1542 unsigned char chkSum = 0;
1543
1544 u32 oldWorkDirCluster;
1545
1546 DIR_ENT* dirEntries = (DIR_ENT*)globalBuffer;
1547 u32 dirCluster;
1548 int secOffset;
1549 int entryOffset;
1550 int maxSectors;
1551 u32 firstSector;
1552
1553 DIR_ENT_LFN lfnEntry;
1554 int lfnPos = 0;
1555
1556 int dirEntryLength = 0;
1557 int dirEntryRemain = 0;
1558 u32 tempDirCluster;
1559 int tempSecOffset;
1560 int tempEntryOffset;
1561 bool dirEndFlag = false;
1562
1563 int i;
1564
1565 // Store current working directory
1566 oldWorkDirCluster = curWorkDirCluster;
1567
1568 // Find filename within path and change to correct directory
1569 if (path[0] == '/')
1570 {
1571 curWorkDirCluster = filesysRootDirClus;
1572 }
1573
1574 pathPos = 0;
1575 filePos = 0;
1576 flagLFN = false;
1577
1578 while (path[pathPos + filePos] != '\0')
1579 {
1580 if (path[pathPos + filePos] == '/')
1581 {
1582 filename[filePos] = '\0';
1583 if (FAT_chdir(filename) == false)
1584 {
1585 curWorkDirCluster = oldWorkDirCluster;
1586 return false; // Couldn't change directory
1587 }
1588 pathPos += filePos + 1;
1589 filePos = 0;
1590 }
1591 filename[filePos] = path[pathPos + filePos];
1592 filePos++;
1593 }
1594
1595 // Skip over last slashes
1596 while (path[pathPos] == '/')
1597 pathPos++;
1598
1599 // Check if the filename has a leading "."
1600 // If so, it is an LFN
1601 if (path[pathPos] == '.') {
1602 flagLFN = true;
1603 }
1604
1605 // Copy name from path
1606 filePos = 0;
1607 dotSeen = false;
1608
1609 while ((filePos < MAX_FILENAME_LENGTH - 1) && (path[pathPos] != '\0'))
1610 {
1611 filename[filePos] = path[pathPos];
1612 if ((filename[filePos] <= ' ') || ((filename[filePos] >= ':') && (filename[filePos] <= '?'))) // Invalid character
1613 {
1614 flagLFN = true;
1615 }
1616 if (filename[filePos] == '.') {
1617 if (!dotSeen) {
1618 dotSeen = true;
1619 } else {
1620 flagLFN = true;
1621 }
1622 }
1623 filePos++;
1624 pathPos++;
1625 if ((filePos > 8) && !dotSeen) {
1626 flagLFN = true;
1627 }
1628 }
1629
1630 if (filePos == 0) // No filename
1631 {
1632 return false;
1633 }
1634
1635 // Check if a long filename was specified
1636 if (filePos > 12)
1637 {
1638 flagLFN = true;
1639 }
1640
1641 // Check if extension is > 3 characters long
1642 if (!flagLFN && (strrchr (filename, '.') != NULL) && (strlen(strrchr(filename, '.')) > 4)) {
1643 flagLFN = true;
1644 }
1645
1646 lfnPos = (filePos - 1) / 13;
1647
1648 // Add end of string char
1649 filename[filePos++] = '\0';
1650 // Clear remaining chars
1651 while (filePos < MAX_FILENAME_LENGTH)
1652 filename[filePos++] = 0x01; // Set for LFN compatibility
1653
1654
1655 if (flagLFN)
1656 {
1657 // Generate short filename - always a 2 digit number for tail
1658 // Get first 5 chars of alias from LFN
1659 aliasPos = 0;
1660 filePos = 0;
1661 if (filename[filePos] == '.') {
1662 filePos++;
1663 }
1664 for ( ; (aliasPos < 5) && (filename[filePos] != '\0') && (filename[filePos] != '.') ; filePos++)
1665 {
1666 tempChar = ucase(filename[filePos]);
1667 if (((tempChar > ' ' && tempChar < ':') || tempChar > '?') && tempChar != '.')
1668 fileAlias[aliasPos++] = tempChar;
1669 }
1670 // Pad Alias with underscores
1671 while (aliasPos < 5)
1672 fileAlias[aliasPos++] = '_';
1673
1674 fileAlias[5] = '~';
1675 fileAlias[8] = '.';
1676 fileAlias[9] = ' ';
1677 fileAlias[10] = ' ';
1678 fileAlias[11] = ' ';
1679 if (strchr (filename, '.') != NULL) {
1680 while(filename[filePos] != '\0')
1681 {
1682 filePos++;
1683 if (filename[filePos] == '.')
1684 {
1685 pathPos = filePos;
1686 }
1687 }
1688 filePos = pathPos + 1; //pathPos is used as a temporary variable
1689 // Copy first 3 characters of extension
1690 for (aliasPos = 9; (aliasPos < 12) && (filename[filePos] != '\0'); filePos++)
1691 {
1692 tempChar = ucase(filename[filePos]);
1693 if ((tempChar > ' ' && tempChar < ':') || tempChar > '?')
1694 fileAlias[aliasPos++] = tempChar;
1695 }
1696 } else {
1697 aliasPos = 9;
1698 }
1699
1700 // Pad Alias extension with spaces
1701 while (aliasPos < 12)
1702 fileAlias[aliasPos++] = ' ';
1703
1704 fileAlias[12] = '\0';
1705
1706
1707 // Get a valid tail number
1708 tailNum = 0;
1709 do {
1710 tailNum++;
1711 fileAlias[6] = 0x30 + ((tailNum / 10) % 10); // 10's digit
1712 fileAlias[7] = 0x30 + (tailNum % 10); // 1's digit
1713 } while ((FAT_DirEntFromPath(fileAlias).name[0] != FILE_FREE) && (tailNum < 100));
1714
1715 if (tailNum < 100) // Found an alias not being used
1716 {
1717 // Calculate file checksum
1718 chkSum = 0;
1719 for (aliasPos=0; aliasPos < 12; aliasPos++)
1720 {
1721 // Skip '.'
1722 if (fileAlias[aliasPos] == '.')
1723 aliasPos++;
1724 // NOTE: The operation is an unsigned char rotate right
1725 chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + fileAlias[aliasPos];
1726 }
1727 }
1728 else // Couldn't find a valid alias
1729 {
1730 return false;
1731 }
1732
1733 dirEntryLength = lfnPos + 2;
1734 }
1735 else // Its not a long file name
1736 {
1737 // Just copy alias straight from filename
1738 for (aliasPos = 0; aliasPos < 13; aliasPos++)
1739 {
1740 tempChar = ucase(filename[aliasPos]);
1741 if ((tempChar > ' ' && tempChar < ':') || tempChar > '?')
1742 fileAlias[aliasPos] = tempChar;
1743 }
1744 fileAlias[12] = '\0';
1745
1746 lfnPos = -1;
1747
1748 dirEntryLength = 1;
1749 }
1750
1751 // Change dirEntry name to match alias
1752 for (aliasPos = 0; ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0') && (aliasPos < 8)); aliasPos++)
1753 {
1754 newDirEntry.name[aliasPos] = fileAlias[aliasPos];
1755 }
1756 while (aliasPos < 8)
1757 {
1758 newDirEntry.name[aliasPos++] = ' ';
1759 }
1760 aliasPos = 0;
1761 while ((fileAlias[aliasPos] != '.') && (fileAlias[aliasPos] != '\0'))
1762 aliasPos++;
1763 filePos = 0;
1764 while (( filePos < 3 ) && (fileAlias[aliasPos] != '\0'))
1765 {
1766 tempChar = fileAlias[aliasPos++];
1767 if ((tempChar > ' ' && tempChar < ':' && tempChar!='.') || tempChar > '?')
1768 newDirEntry.ext[filePos++] = tempChar;
1769 }
1770 while (filePos < 3)
1771 {
1772 newDirEntry.ext[filePos++] = ' ';
1773 }
1774
1775 // Scan Dir for free entry
1776 dirCluster = curWorkDirCluster;
1777 secOffset = 0;
1778 entryOffset = 0;
1779 maxSectors = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? (filesysData - filesysRootDir) : filesysSecPerClus);
1780 firstSector = (dirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(dirCluster));
1781 disc_ReadSector (firstSector + secOffset, dirEntries);
1782
1783 dirEntryRemain = dirEntryLength;
1784 tempDirCluster = dirCluster;
1785 tempSecOffset = secOffset;
1786 tempEntryOffset = entryOffset;
1787
1788 // Search for a large enough space to fit in new directory entry
1789 while ((dirEntries[entryOffset].name[0] != FILE_LAST) && (dirEntryRemain > 0))
1790 {
1791
1792 entryOffset++;
1793
1794 if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT))
1795 {
1796 entryOffset = 0;
1797 secOffset++;
1798 if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER))
1799 {
1800 secOffset = 0;
1801 if (FAT_NextCluster(dirCluster) == CLUSTER_EOF)
1802 {
1803 dirCluster = FAT_LinkFreeCluster(dirCluster);
1804 dirEntries[0].name[0] = FILE_LAST;
1805 }
1806 else
1807 {
1808 dirCluster = FAT_NextCluster(dirCluster);
1809 }
1810 firstSector = FAT_ClustToSect(dirCluster);
1811 }
1812 else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir)))
1813 {
1814 return false; // Got to end of root dir - can't fit in more files
1815 }
1816 disc_ReadSector (firstSector + secOffset, dirEntries);
1817 }
1818
1819 if ((dirEntries[entryOffset].name[0] == FILE_FREE) || (dirEntries[entryOffset].name[0] == FILE_LAST) )
1820 {
1821 dirEntryRemain--;
1822 } else {
1823 dirEntryRemain = dirEntryLength;
1824 tempDirCluster = dirCluster;
1825 tempSecOffset = secOffset;
1826 tempEntryOffset = entryOffset;
1827 }
1828 }
1829
1830 // Modifying the last directory is a special case - have to erase following entries
1831 if (dirEntries[entryOffset].name[0] == FILE_LAST)
1832 {
1833 dirEndFlag = true;
1834 }
1835
1836 // Recall last used entry
1837 dirCluster = tempDirCluster;
1838 secOffset = tempSecOffset;
1839 entryOffset = tempEntryOffset;
1840 dirEntryRemain = dirEntryLength;
1841
1842 // Re-read in first sector that will be written to
1843 if (dirEndFlag && (entryOffset == 0)) {
1844 memset (dirEntries, FILE_LAST, BYTE_PER_READ);
1845 } else {
1846 disc_ReadSector (firstSector + secOffset, dirEntries);
1847 }
1848
1849 // Add new directory entry
1850 while (dirEntryRemain > 0)
1851 {
1852 // Move to next entry, first pass advances from last used entry
1853 entryOffset++;
1854 if (entryOffset == BYTE_PER_READ / sizeof (DIR_ENT))
1855 {
1856 // Write out the current sector if we need to
1857 entryOffset = 0;
1858 if (dirEntryRemain < dirEntryLength) // Don't write out sector on first pass
1859 {
1860 disc_WriteSector (firstSector + secOffset, dirEntries);
1861 }
1862 secOffset++;
1863 if ((secOffset == filesysSecPerClus) && (dirCluster != FAT16_ROOT_DIR_CLUSTER))
1864 {
1865 secOffset = 0;
1866 if (FAT_NextCluster(dirCluster) == CLUSTER_EOF)
1867 {
1868 dirCluster = FAT_LinkFreeCluster(dirCluster);
1869 dirEntries[0].name[0] = FILE_LAST;
1870 }
1871 else
1872 {
1873 dirCluster = FAT_NextCluster(dirCluster);
1874 }
1875 firstSector = FAT_ClustToSect(dirCluster);
1876 }
1877 else if ((dirCluster == FAT16_ROOT_DIR_CLUSTER) && (secOffset == (filesysData - filesysRootDir)))
1878 {
1879 return false; // Got to end of root dir - can't fit in more files
1880 }
1881 if (dirEndFlag)
1882 {
1883 memset (dirEntries, FILE_LAST, BYTE_PER_READ);
1884 } else {
1885 disc_ReadSector (firstSector + secOffset, dirEntries);
1886 }
1887 }
1888
1889 // Generate LFN entries
1890 if (lfnPos >= 0)
1891 {
1892 lfnEntry.ordinal = (lfnPos + 1) | (dirEntryRemain == dirEntryLength ? LFN_END : 0);
1893 for (i = 0; i < 13; i++) {
1894 if (filename [lfnPos * 13 + i] == 0x01) {
1895 ((u8*)&lfnEntry)[(int)lfn_offset_table[i]] = 0xff;
1896 ((u8*)&lfnEntry)[(int)(lfn_offset_table[i]) + 1] = 0xff;
1897 } else {
1898 ((u8*)&lfnEntry)[(int)lfn_offset_table[i]] = filename [lfnPos * 13 + i];
1899 ((u8*)&lfnEntry)[(int)(lfn_offset_table[i]) + 1] = 0x00;
1900 }
1901 }
1902
1903 lfnEntry.checkSum = chkSum;
1904 lfnEntry.flag = ATTRIB_LFN;
1905 lfnEntry.reserved1 = 0;
1906 lfnEntry.reserved2 = 0;
1907
1908 *((DIR_ENT_LFN*)&dirEntries[entryOffset]) = lfnEntry;
1909 lfnPos --;
1910 lfnEntry.ordinal = 0;
1911 } // end writing long filename entries
1912 else
1913 {
1914 dirEntries[entryOffset] = newDirEntry;
1915 if (dirEndFlag && (entryOffset < (BYTE_PER_READ / sizeof (DIR_ENT))) )
1916 dirEntries[entryOffset+1].name[0] = FILE_LAST;
1917 }
1918
1919 dirEntryRemain--;
1920 }
1921
1922 // Write directory back to disk
1923 disc_WriteSector (firstSector + secOffset, dirEntries);
1924
1925 // Change back to Working DIR
1926 curWorkDirCluster = oldWorkDirCluster;
1927
1928 return true;
1929 }
1930 #endif
1931
1932 /*-----------------------------------------------------------------
1933 FAT_FindNextFile
1934 Gets the name of the next directory entry
1935 (can be a file or subdirectory)
1936 char* filename: OUT filename, must be at least 13 chars long
1937 FILE_TYPE return: OUT returns FT_NONE if failed,
1938 FT_FILE if it found a file and FT_DIR if it found a directory
1939 -----------------------------------------------------------------*/
FAT_FindNextFile(char * filename)1940 FILE_TYPE FAT_FindNextFile(char* filename)
1941 {
1942 // Get the next directory entry
1943 DIR_ENT file;
1944 file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_CUR);
1945
1946 if (file.name[0] == FILE_FREE)
1947 {
1948 return FT_NONE; // Did not find a file
1949 }
1950
1951 // Get the filename
1952 if (filename != NULL)
1953 FAT_GetFilename (file, filename);
1954
1955 if ((file.attrib & ATTRIB_DIR) != 0)
1956 {
1957 return FT_DIR; // Found a directory
1958 }
1959 else
1960 {
1961 return FT_FILE; // Found a file
1962 }
1963 }
1964
1965 /*-----------------------------------------------------------------
1966 FAT_FindFirstFile
1967 Gets the name of the first directory entry and resets the count
1968 (can be a file or subdirectory)
1969 char* filename: OUT filename, must be at least 13 chars long
1970 FILE_TYPE return: OUT returns FT_NONE if failed,
1971 FT_FILE if it found a file and FT_DIR if it found a directory
1972 -----------------------------------------------------------------*/
FAT_FindFirstFile(char * filename)1973 FILE_TYPE FAT_FindFirstFile(char* filename)
1974 {
1975 // Get the first directory entry
1976 DIR_ENT file;
1977 file = FAT_GetDirEntry (curWorkDirCluster, 1, SEEK_SET);
1978
1979 if (file.name[0] == FILE_FREE)
1980 {
1981 return FT_NONE; // Did not find a file
1982 }
1983
1984 // Get the filename
1985 if (filename != NULL)
1986 FAT_GetFilename (file, filename);
1987
1988 if ((file.attrib & ATTRIB_DIR) != 0)
1989 {
1990 return FT_DIR; // Found a directory
1991 }
1992 else
1993 {
1994 return FT_FILE; // Found a file
1995 }
1996 }
1997
1998 /*-----------------------------------------------------------------
1999 FAT_FindFirstFileLFN
2000 Gets the long file name of the first directory entry and resets
2001 the count (can be a file or subdirectory)
2002 char* lfn: OUT long file name, must be at least 256 chars long
2003 FILE_TYPE return: OUT returns FT_NONE if failed,
2004 FT_FILE if it found a file and FT_DIR if it found a directory
2005 -----------------------------------------------------------------*/
FAT_FindFirstFileLFN(char * lfn)2006 FILE_TYPE FAT_FindFirstFileLFN(char* lfn)
2007 {
2008 FILE_TYPE type;
2009 type = FAT_FindFirstFile(NULL);
2010 FAT_GetLongFilename (lfn);
2011 return type;
2012 }
2013
2014 /*-----------------------------------------------------------------
2015 FAT_FindNextFileLFN
2016 Gets the long file name of the next directory entry
2017 (can be a file or subdirectory)
2018 char* lfn: OUT long file name, must be at least 256 chars long
2019 FILE_TYPE return: OUT returns FT_NONE if failed,
2020 FT_FILE if it found a file and FT_DIR if it found a directory
2021 -----------------------------------------------------------------*/
FAT_FindNextFileLFN(char * lfn)2022 FILE_TYPE FAT_FindNextFileLFN(char* lfn)
2023 {
2024 FILE_TYPE type;
2025 type = FAT_FindNextFile(NULL);
2026 FAT_GetLongFilename (lfn);
2027 return type;
2028 }
2029
2030
2031 /*-----------------------------------------------------------------
2032 FAT_FileExists
2033 Returns the type of file
2034 char* filename: IN filename of the file to look for
2035 FILE_TYPE return: OUT returns FT_NONE if there is now file with
2036 that name, FT_FILE if it is a file and FT_DIR if it is a directory
2037 -----------------------------------------------------------------*/
FAT_FileExists(const char * filename)2038 FILE_TYPE FAT_FileExists(const char* filename)
2039 {
2040 DIR_ENT dirEntry;
2041 // Get the dirEntry for the path specified
2042 dirEntry = FAT_DirEntFromPath (filename);
2043
2044 if (dirEntry.name[0] == FILE_FREE)
2045 {
2046 return FT_NONE;
2047 }
2048 else if (dirEntry.attrib & ATTRIB_DIR)
2049 {
2050 return FT_DIR;
2051 }
2052 else
2053 {
2054 return FT_FILE;
2055 }
2056 }
2057
2058 /*-----------------------------------------------------------------
2059 FAT_GetFileSystemType
2060 FS_TYPE return: OUT returns the current file system type
2061 -----------------------------------------------------------------*/
FAT_GetFileSystemType(void)2062 FS_TYPE FAT_GetFileSystemType (void)
2063 {
2064 return filesysType;
2065 }
2066
2067 /*-----------------------------------------------------------------
2068 FAT_GetFileSystemTotalSize
2069 u32 return: OUT returns the total disk space (used + free)
2070 -----------------------------------------------------------------*/
FAT_GetFileSystemTotalSize(void)2071 u32 FAT_GetFileSystemTotalSize (void)
2072 {
2073 return filesysTotalSize;
2074 }
2075
2076
2077
2078 /*-----------------------------------------------------------------
2079 FAT_chdir
2080 Changes the current working directory
2081 const char* path: IN null terminated string of directory separated by
2082 forward slashes, / is root
2083 bool return: OUT returns true if successful
2084 -----------------------------------------------------------------*/
FAT_chdir(const char * path)2085 bool FAT_chdir (const char* path)
2086 {
2087 DIR_ENT dir;
2088 if (path[0] == '/' && path[1] == '\0')
2089 {
2090 curWorkDirCluster = filesysRootDirClus;
2091 return true;
2092 }
2093 if (path[0] == '\0') // Return true if changing relative to nothing
2094 {
2095 return true;
2096 }
2097
2098 dir = FAT_DirEntFromPath (path);
2099
2100 if (((dir.attrib & ATTRIB_DIR) == ATTRIB_DIR) && (dir.name[0] != FILE_FREE))
2101 {
2102 // Change directory
2103 curWorkDirCluster = dir.startCluster | (dir.startClusterHigh << 16);
2104
2105 // Move to correct cluster for root directory
2106 if (curWorkDirCluster == FAT16_ROOT_DIR_CLUSTER)
2107 {
2108 curWorkDirCluster = filesysRootDirClus;
2109 }
2110
2111 // Reset file position in directory
2112 wrkDirCluster = curWorkDirCluster;
2113 wrkDirSector = 0;
2114 wrkDirOffset = -1;
2115 return true;
2116 }
2117 else
2118 {
2119 // Couldn't change directory - wrong path specified
2120 return false;
2121 }
2122 }
2123
2124 /*-----------------------------------------------------------------
2125 FAT_fopen(filename, mode)
2126 Opens a file
2127 const char* path: IN null terminated string of filename and path
2128 separated by forward slashes, / is root
2129 const char* mode: IN mode to open file in
2130 Supported modes: "r", "r+", "w", "w+", "a", "a+", don't use
2131 "b" or "t" in any mode, as all files are openned in binary mode
2132 FAT_FILE* return: OUT handle to open file, returns NULL if the file
2133 couldn't be openned
2134 -----------------------------------------------------------------*/
FAT_fopen(const char * path,const char * mode)2135 FAT_FILE* FAT_fopen(const char* path, const char* mode)
2136 {
2137 int fileNum;
2138 FAT_FILE* file;
2139 DIR_ENT dirEntry;
2140 #ifdef CAN_WRITE_TO_DISC
2141 u32 startCluster;
2142 int clusCount;
2143 #endif
2144
2145 char* pchTemp;
2146 // Check that a valid mode was specified
2147 pchTemp = strpbrk ( mode, "rRwWaA" );
2148 if (pchTemp == NULL)
2149 {
2150 return NULL;
2151 }
2152 if (strpbrk ( pchTemp+1, "rRwWaA" ) != NULL)
2153 {
2154 return NULL;
2155 }
2156
2157 // Get the dirEntry for the path specified
2158 dirEntry = FAT_DirEntFromPath (path);
2159
2160 // Check that it is not a directory
2161 if (dirEntry.attrib & ATTRIB_DIR)
2162 {
2163 return NULL;
2164 }
2165
2166 #ifdef CAN_WRITE_TO_DISC
2167 // Check that it is not a read only file being openned in a writing mode
2168 if ( (strpbrk(mode, "wWaA+") != NULL) && (dirEntry.attrib & ATTRIB_RO))
2169 {
2170 return NULL;
2171 }
2172 #else
2173 if ( (strpbrk(mode, "wWaA+") != NULL))
2174 {
2175 return NULL;
2176 }
2177 #endif
2178
2179 // Find a free file buffer
2180 for (fileNum = 0; (fileNum < MAX_FILES_OPEN) && (openFiles[fileNum].inUse == true); fileNum++);
2181
2182 if (fileNum == MAX_FILES_OPEN) // No free files
2183 {
2184 return NULL;
2185 }
2186
2187 file = &openFiles[fileNum];
2188 // Remember where directory entry was
2189 file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector;
2190 file->dirEntOffset = wrkDirOffset;
2191
2192 if ( strpbrk(mode, "rR") != NULL ) //(ucase(mode[0]) == 'R')
2193 {
2194 if (dirEntry.name[0] == FILE_FREE) // File must exist
2195 {
2196 return NULL;
2197 }
2198
2199 file->read = true;
2200 #ifdef CAN_WRITE_TO_DISC
2201 file->write = ( strchr(mode, '+') != NULL ); //(mode[1] == '+');
2202 #else
2203 file->write = false;
2204 #endif
2205 file->append = false;
2206
2207 // Store information about position within the file, for use
2208 // by FAT_fread, FAT_fseek, etc.
2209 file->firstCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
2210
2211 #ifdef CAN_WRITE_TO_DISC
2212 // Check if file is openned for random. If it is, and currently has no cluster, one must be
2213 // assigned to it.
2214 if (file->write && file->firstCluster == CLUSTER_FREE)
2215 {
2216 file->firstCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
2217 if (file->firstCluster == CLUSTER_FREE) // Couldn't get a free cluster
2218 {
2219 return NULL;
2220 }
2221
2222 // Store cluster position into the directory entry
2223 dirEntry.startCluster = (file->firstCluster & 0xFFFF);
2224 dirEntry.startClusterHigh = ((file->firstCluster >> 16) & 0xFFFF);
2225 disc_ReadSector (file->dirEntSector, globalBuffer);
2226 ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry;
2227 disc_WriteSector (file->dirEntSector, globalBuffer);
2228 }
2229 #endif
2230
2231 file->length = dirEntry.fileSize;
2232 file->curPos = 0;
2233 file->curClus = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
2234 file->curSect = 0;
2235 file->curByte = 0;
2236
2237 // Not appending
2238 file->appByte = 0;
2239 file->appClus = 0;
2240 file->appSect = 0;
2241
2242 disc_ReadSector( FAT_ClustToSect( file->curClus), file->readBuffer);
2243 file->inUse = true; // We're using this file now
2244
2245 return file;
2246 } // mode "r"
2247
2248 #ifdef CAN_WRITE_TO_DISC
2249 if ( strpbrk(mode, "wW") != NULL ) // (ucase(mode[0]) == 'W')
2250 {
2251 if (dirEntry.name[0] == FILE_FREE) // Create file if it doesn't exist
2252 {
2253 dirEntry.attrib = ATTRIB_ARCH;
2254 dirEntry.reserved = 0;
2255
2256 // Time and date set to system time and date
2257 dirEntry.cTime_ms = 0;
2258 dirEntry.cTime = getRTCtoFileTime();
2259 dirEntry.cDate = getRTCtoFileDate();
2260 dirEntry.aDate = getRTCtoFileDate();
2261 dirEntry.mTime = getRTCtoFileTime();
2262 dirEntry.mDate = getRTCtoFileDate();
2263 }
2264 else // Already a file entry
2265 {
2266 // Free any clusters used
2267 FAT_ClearLinks (dirEntry.startCluster | (dirEntry.startClusterHigh << 16));
2268 }
2269
2270 // Get a cluster to use
2271 startCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
2272 if (startCluster == CLUSTER_FREE) // Couldn't get a free cluster
2273 {
2274 return NULL;
2275 }
2276
2277 // Store cluster position into the directory entry
2278 dirEntry.startCluster = (startCluster & 0xFFFF);
2279 dirEntry.startClusterHigh = ((startCluster >> 16) & 0xFFFF);
2280
2281 // The file has no data in it - its over written so should be empty
2282 dirEntry.fileSize = 0;
2283
2284 if (dirEntry.name[0] == FILE_FREE) // No file
2285 {
2286 // Have to create a new entry
2287 if(!FAT_AddDirEntry (path, dirEntry))
2288 {
2289 return NULL;
2290 }
2291 // Get the newly created dirEntry
2292 dirEntry = FAT_DirEntFromPath (path);
2293
2294 // Remember where directory entry was
2295 file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector;
2296 file->dirEntOffset = wrkDirOffset;
2297 }
2298 else // Already a file
2299 {
2300 // Just modify the old entry
2301 disc_ReadSector (file->dirEntSector, globalBuffer);
2302 ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry;
2303 disc_WriteSector (file->dirEntSector, globalBuffer);
2304 }
2305
2306
2307 // Now that file is created, open it
2308 file->read = ( strchr(mode, '+') != NULL ); //(mode[1] == '+');
2309 file->write = true;
2310 file->append = false;
2311
2312 // Store information about position within the file, for use
2313 // by FAT_fread, FAT_fseek, etc.
2314 file->firstCluster = startCluster;
2315 file->length = 0; // Should always have 0 bytes if openning in "w" mode
2316 file->curPos = 0;
2317 file->curClus = startCluster;
2318 file->curSect = 0;
2319 file->curByte = 0;
2320
2321 // Not appending
2322 file->appByte = 0;
2323 file->appClus = 0;
2324 file->appSect = 0;
2325
2326 // Empty file, so empty read buffer
2327 memset (file->readBuffer, 0, BYTE_PER_READ);
2328 file->inUse = true; // We're using this file now
2329
2330 return file;
2331 }
2332
2333 if ( strpbrk(mode, "aA") != NULL ) // (ucase(mode[0]) == 'A')
2334 {
2335 if (dirEntry.name[0] == FILE_FREE) // Create file if it doesn't exist
2336 {
2337 dirEntry.attrib = ATTRIB_ARCH;
2338 dirEntry.reserved = 0;
2339
2340 // Time and date set to system time and date
2341 dirEntry.cTime_ms = 0;
2342 dirEntry.cTime = getRTCtoFileTime();
2343 dirEntry.cDate = getRTCtoFileDate();
2344 dirEntry.aDate = getRTCtoFileDate();
2345 dirEntry.mTime = getRTCtoFileTime();
2346 dirEntry.mDate = getRTCtoFileDate();
2347
2348 // The file has no data in it
2349 dirEntry.fileSize = 0;
2350
2351 // Get a cluster to use
2352 startCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
2353 if (startCluster == CLUSTER_FREE) // Couldn't get a free cluster
2354 {
2355 return NULL;
2356 }
2357 dirEntry.startCluster = (startCluster & 0xFFFF);
2358 dirEntry.startClusterHigh = ((startCluster >> 16) & 0xFFFF);
2359
2360 if(!FAT_AddDirEntry (path, dirEntry))
2361 return NULL;
2362
2363 // Get the newly created dirEntry
2364 dirEntry = FAT_DirEntFromPath (path);
2365
2366 // Store append cluster
2367 file->appClus = startCluster;
2368
2369 // Remember where directory entry was
2370 file->dirEntSector = (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector;
2371 file->dirEntOffset = wrkDirOffset;
2372 }
2373 else // File already exists - reuse the old directory entry
2374 {
2375 startCluster = dirEntry.startCluster | (dirEntry.startClusterHigh << 16);
2376 // If it currently has no cluster, one must be assigned to it.
2377 if (startCluster == CLUSTER_FREE)
2378 {
2379 file->firstCluster = FAT_LinkFreeCluster (CLUSTER_FREE);
2380 if (file->firstCluster == CLUSTER_FREE) // Couldn't get a free cluster
2381 {
2382 return NULL;
2383 }
2384
2385 // Store cluster position into the directory entry
2386 dirEntry.startCluster = (file->firstCluster & 0xFFFF);
2387 dirEntry.startClusterHigh = ((file->firstCluster >> 16) & 0xFFFF);
2388 disc_ReadSector (file->dirEntSector, globalBuffer);
2389 ((DIR_ENT*) globalBuffer)[file->dirEntOffset] = dirEntry;
2390 disc_WriteSector (file->dirEntSector, globalBuffer);
2391
2392 // Store append cluster
2393 file->appClus = startCluster;
2394
2395 } else {
2396
2397 // Follow cluster list until last one is found
2398 clusCount = dirEntry.fileSize / filesysBytePerClus;
2399 file->appClus = startCluster;
2400 while ((clusCount--) && (FAT_NextCluster (file->appClus) != CLUSTER_FREE) && (FAT_NextCluster (file->appClus) != CLUSTER_EOF))
2401 {
2402 file->appClus = FAT_NextCluster (file->appClus);
2403 }
2404 if (clusCount >= 0) // Check if ran out of clusters
2405 {
2406 // Set flag to allocate new cluster when needed
2407 file->appSect = filesysSecPerClus;
2408 file->appByte = 0;
2409 }
2410 }
2411 }
2412
2413 // Now that file is created, open it
2414 file->read = ( strchr(mode, '+') != NULL );
2415 file->write = false;
2416 file->append = true;
2417
2418 // Calculate the sector and byte of the current position,
2419 // and store them
2420 file->appSect = (dirEntry.fileSize % filesysBytePerClus) / BYTE_PER_READ;
2421 file->appByte = dirEntry.fileSize % BYTE_PER_READ;
2422
2423 // Store information about position within the file, for use
2424 // by FAT_fread, FAT_fseek, etc.
2425 file->firstCluster = startCluster;
2426 file->length = dirEntry.fileSize;
2427 file->curPos = dirEntry.fileSize;
2428 file->curClus = file->appClus;
2429 file->curSect = file->appSect;
2430 file->curByte = file->appByte;
2431
2432 // Read into buffer
2433 disc_ReadSector( FAT_ClustToSect(file->curClus) + file->curSect, file->readBuffer);
2434 file->inUse = true; // We're using this file now
2435 return file;
2436 }
2437 #endif
2438
2439 // Can only reach here if a bad mode was specified
2440 return NULL;
2441 }
2442
2443 /*-----------------------------------------------------------------
2444 FAT_fclose(file)
2445 Closes a file
2446 FAT_FILE* file: IN handle of the file to close
2447 bool return OUT: true if successful, false if not
2448 -----------------------------------------------------------------*/
FAT_fclose(FAT_FILE * file)2449 bool FAT_fclose (FAT_FILE* file)
2450 {
2451 // Clear memory used by file information
2452 if ((file != NULL) && (file->inUse == true))
2453 {
2454 #ifdef CAN_WRITE_TO_DISC
2455 if (file->write || file->append)
2456 {
2457 // Write new length, time and date back to directory entry
2458 disc_ReadSector (file->dirEntSector, globalBuffer);
2459
2460 ((DIR_ENT*)globalBuffer)[file->dirEntOffset].fileSize = file->length;
2461 ((DIR_ENT*)globalBuffer)[file->dirEntOffset].mTime = getRTCtoFileTime();
2462 ((DIR_ENT*)globalBuffer)[file->dirEntOffset].mDate = getRTCtoFileDate();
2463 ((DIR_ENT*)globalBuffer)[file->dirEntOffset].aDate = getRTCtoFileDate();
2464
2465 disc_WriteSector (file->dirEntSector, globalBuffer);
2466
2467 // Flush any sectors in disc cache
2468 disc_CacheFlush();
2469 }
2470 #endif
2471 file->inUse = false;
2472 return true;
2473 }
2474 else
2475 {
2476 return false;
2477 }
2478 }
2479
2480 /*-----------------------------------------------------------------
2481 FAT_ftell(file)
2482 Returns the current position in a file
2483 FAT_FILE* file: IN handle of an open file
2484 u32 OUT: Current position
2485 -----------------------------------------------------------------*/
FAT_ftell(FAT_FILE * file)2486 u32 FAT_ftell (FAT_FILE* file)
2487 {
2488 // Return the position as specified in the FAT_FILE structure
2489 if ((file != NULL) && (file->inUse == true))
2490 {
2491 return file->curPos;
2492 }
2493 else
2494 {
2495 // Return -1 if no file was given
2496 return -1;
2497 }
2498 }
2499
2500 /*-----------------------------------------------------------------
2501 FAT_fseek(file, offset, origin)
2502 Seeks to specified byte position in file
2503 FAT_FILE* file: IN handle of an open file
2504 s32 offset IN: position to seek to, relative to origin
2505 int origin IN: origin to seek from
2506 int OUT: Returns 0 if successful, -1 if not
2507 -----------------------------------------------------------------*/
FAT_fseek(FAT_FILE * file,s32 offset,int origin)2508 int FAT_fseek(FAT_FILE* file, s32 offset, int origin)
2509 {
2510 u32 cluster, nextCluster;
2511 int clusCount;
2512 u32 position;
2513 u32 curPos;
2514
2515
2516 if ((file == NULL) || (file->inUse == false)) // invalid file
2517 {
2518 return -1;
2519 }
2520
2521 // Can't seek in append only mode
2522 if (!file->read && !file->write)
2523 {
2524 return -1;
2525 }
2526
2527 curPos = file->curPos;
2528
2529 switch (origin)
2530 {
2531 case SEEK_SET:
2532 if (offset >= 0)
2533 {
2534 position = offset;
2535 } else {
2536 // Tried to seek before start of file
2537 position = 0;
2538 }
2539 break;
2540 case SEEK_CUR:
2541 if (offset >= 0)
2542 {
2543 position = curPos + offset;
2544 }
2545 else if ( (u32)(offset * -1) >= curPos )
2546 {
2547 // Tried to seek before start of file
2548 position = 0;
2549 }
2550 else
2551 {
2552 // Using u32 to maintain 32 bits of accuracy
2553 position = curPos - (u32)(offset * -1);
2554 }
2555 break;
2556 case SEEK_END:
2557 if (offset >= 0)
2558 {
2559 // Seeking to end of file
2560 position = file->length; // Fixed thanks to MoonLight
2561 }
2562 else if ( (u32)(offset * -1) >= file->length )
2563 {
2564 // Tried to seek before start of file
2565 position = 0;
2566 }
2567 else
2568 {
2569 // Using u32 to maintain 32 bits of accuracy
2570 position = file->length - (u32)(offset * -1);
2571 }
2572 break;
2573 default:
2574 return -1;
2575 }
2576
2577 if (position > file->length)
2578 {
2579 // Tried to go past end of file
2580 position = file->length;
2581 }
2582
2583 // Save position
2584 file->curPos = position;
2585
2586
2587 // Calculate where the correct cluster is
2588 if (position > curPos)
2589 {
2590 clusCount = (position - curPos + (file->curSect * filesysBytePerSec) + file->curByte) / filesysBytePerClus; // Fixed thanks to AgentQ
2591 cluster = file->curClus;
2592 } else {
2593 clusCount = position / filesysBytePerClus;
2594 cluster = file->firstCluster;
2595 }
2596
2597 // Calculate the sector and byte of the current position,
2598 // and store them
2599 file->curSect = (position % filesysBytePerClus) / BYTE_PER_READ;
2600 file->curByte = position % BYTE_PER_READ;
2601
2602 // Follow cluster list until desired one is found
2603 if (clusCount > 0) // Only look at next cluster if need to
2604 {
2605 nextCluster = FAT_NextCluster (cluster);
2606 } else {
2607 nextCluster = cluster;
2608 }
2609 while ((clusCount--) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF))
2610 {
2611 cluster = nextCluster;
2612 nextCluster = FAT_NextCluster (cluster);
2613 }
2614 // Check if ran out of clusters, and the file is being written to
2615 if ((clusCount >= 0) && (file->write || file->append))
2616 {
2617 // Set flag to allocate a new cluster
2618 file->curSect = filesysSecPerClus;
2619 file->curByte = 0;
2620 }
2621 file->curClus = cluster;
2622
2623 // Reload sector buffer for new position in file, if it is a different sector
2624 if ((curPos ^ position) >= BYTE_PER_READ)
2625 {
2626 disc_ReadSector( file->curSect + FAT_ClustToSect(file->curClus), file->readBuffer);
2627 }
2628
2629 return 0;
2630
2631 }
2632
2633 /*-----------------------------------------------------------------
2634 FAT_fread(buffer, size, count, file)
2635 Reads in size * count bytes into buffer from file, starting
2636 from current position. It then sets the current position to the
2637 byte after the last byte read. If it reaches the end of file
2638 before filling the buffer then it stops reading.
2639 void* buffer OUT: Pointer to buffer to fill. Should be at least as
2640 big as the number of bytes required
2641 u32 size IN: size of each item to read
2642 u32 count IN: number of items to read
2643 FAT_FILE* file IN: Handle of an open file
2644 u32 OUT: returns the actual number of bytes read
2645 -----------------------------------------------------------------*/
FAT_fread(void * buffer,u32 size,u32 count,FAT_FILE * file)2646 u32 FAT_fread (void* buffer, u32 size, u32 count, FAT_FILE* file)
2647 {
2648 int curByte;
2649 int curSect;
2650 u32 curClus;
2651 u32 tempNextCluster;
2652
2653 int tempVar;
2654
2655 char* data = (char*)buffer;
2656
2657 u32 length = size * count;
2658 u32 remain;
2659
2660 bool flagNoError = true;
2661
2662 // Can't read non-existant files
2663 if ((file == NULL) || (file->inUse == false) || size == 0 || count == 0 || buffer == NULL)
2664 return 0;
2665
2666 // Can only read files openned for reading
2667 if (!file->read)
2668 return 0;
2669
2670 // Don't read past end of file
2671 if (length + file->curPos > file->length)
2672 length = file->length - file->curPos;
2673
2674 remain = length;
2675
2676 curByte = file->curByte;
2677 curSect = file->curSect;
2678 curClus = file->curClus;
2679
2680 // Align to sector
2681 tempVar = BYTE_PER_READ - curByte;
2682 if (tempVar > remain)
2683 tempVar = remain;
2684
2685 if ((tempVar < BYTE_PER_READ) && flagNoError)
2686 {
2687 memcpy(data, &(file->readBuffer[curByte]), tempVar);
2688 remain -= tempVar;
2689 data += tempVar;
2690
2691 curByte += tempVar;
2692 if (curByte >= BYTE_PER_READ)
2693 {
2694 curByte = 0;
2695 curSect++;
2696 }
2697 }
2698
2699 // align to cluster
2700 // tempVar is number of sectors to read
2701 if (remain > (filesysSecPerClus - curSect) * BYTE_PER_READ)
2702 {
2703 tempVar = filesysSecPerClus - curSect;
2704 } else {
2705 tempVar = remain / BYTE_PER_READ;
2706 }
2707
2708 if ((tempVar > 0) && flagNoError)
2709 {
2710 disc_ReadSectors ( curSect + FAT_ClustToSect(curClus), tempVar, data);
2711 data += tempVar * BYTE_PER_READ;
2712 remain -= tempVar * BYTE_PER_READ;
2713
2714 curSect += tempVar;
2715 }
2716
2717 // Move onto next cluster
2718 // It should get to here without reading anything if a cluster is due to be allocated
2719 if (curSect >= filesysSecPerClus)
2720 {
2721 tempNextCluster = FAT_NextCluster(curClus);
2722 if ((remain == 0) && (tempNextCluster == CLUSTER_EOF))
2723 {
2724 curSect = filesysSecPerClus;
2725 } else {
2726 curSect = 0;
2727 curClus = tempNextCluster;
2728 if (curClus == CLUSTER_FREE)
2729 {
2730 flagNoError = false;
2731 }
2732 }
2733 }
2734
2735 // Read in whole clusters
2736 while ((remain >= filesysBytePerClus) && flagNoError)
2737 {
2738 disc_ReadSectors (FAT_ClustToSect(curClus), filesysSecPerClus, data);
2739 data += filesysBytePerClus;
2740 remain -= filesysBytePerClus;
2741
2742 // Advance to next cluster
2743 tempNextCluster = FAT_NextCluster(curClus);
2744 if ((remain == 0) && (tempNextCluster == CLUSTER_EOF))
2745 {
2746 curSect = filesysSecPerClus;
2747 } else {
2748 curSect = 0;
2749 curClus = tempNextCluster;
2750 if (curClus == CLUSTER_FREE)
2751 {
2752 flagNoError = false;
2753 }
2754 }
2755 }
2756
2757 // Read remaining sectors
2758 tempVar = remain / BYTE_PER_READ; // Number of sectors left
2759 if ((tempVar > 0) && flagNoError)
2760 {
2761 disc_ReadSectors (FAT_ClustToSect(curClus), tempVar, data);
2762 data += tempVar * BYTE_PER_READ;
2763 remain -= tempVar * BYTE_PER_READ;
2764 curSect += tempVar;
2765 }
2766
2767 // Last remaining sector
2768 // Check if sector wanted is different to the one started with
2769 if ( ((file->curByte + length) >= BYTE_PER_READ) && flagNoError)
2770 {
2771 disc_ReadSector( curSect + FAT_ClustToSect( curClus), file->readBuffer);
2772 if (remain > 0)
2773 {
2774 memcpy(data, file->readBuffer, remain);
2775 curByte += remain;
2776 remain = 0;
2777 }
2778 }
2779
2780 // Length read is the wanted length minus the stuff not read
2781 length = length - remain;
2782
2783 // Update file information
2784 file->curByte = curByte;
2785 file->curSect = curSect;
2786 file->curClus = curClus;
2787 file->curPos = file->curPos + length;
2788 return length;
2789 }
2790
2791 #ifdef CAN_WRITE_TO_DISC
2792 /*-----------------------------------------------------------------
2793 FAT_fwrite(buffer, size, count, file)
2794 Writes size * count bytes into file from buffer, starting
2795 from current position. It then sets the current position to the
2796 byte after the last byte written. If the file was openned in
2797 append mode it always writes to the end of the file.
2798 const void* buffer IN: Pointer to buffer containing data. Should be
2799 at least as big as the number of bytes to be written.
2800 u32 size IN: size of each item to write
2801 u32 count IN: number of items to write
2802 FAT_FILE* file IN: Handle of an open file
2803 u32 OUT: returns the actual number of bytes written
2804 -----------------------------------------------------------------*/
FAT_fwrite(const void * buffer,u32 size,u32 count,FAT_FILE * file)2805 u32 FAT_fwrite (const void* buffer, u32 size, u32 count, FAT_FILE* file)
2806 {
2807 int curByte;
2808 int curSect;
2809 u32 curClus;
2810
2811 u32 tempNextCluster;
2812 int tempVar;
2813 u32 length = size * count;
2814 u32 remain = length;
2815 char* data = (char*)buffer;
2816
2817 char* writeBuffer;
2818
2819 bool flagNoError = true;
2820 bool flagAppending = false;
2821
2822 if ((file == NULL) || (file->inUse == false) || length == 0 || buffer == NULL)
2823 return 0;
2824
2825 if (file->write)
2826 {
2827 // Write at current read pointer
2828 curByte = file->curByte;
2829 curSect = file->curSect;
2830 curClus = file->curClus;
2831
2832 // Use read buffer as write buffer
2833 writeBuffer = file->readBuffer;
2834
2835 // If it is writing past the current end of file, set appending flag
2836 if (length + file->curPos > file->length)
2837 {
2838 flagAppending = true;
2839 }
2840 }
2841 else if (file->append)
2842 {
2843 // Write at end of file
2844 curByte = file->appByte;
2845 curSect = file->appSect;
2846 curClus = file->appClus;
2847 flagAppending = true;
2848
2849 // Use global buffer as write buffer, don't touch read buffer
2850 writeBuffer = (char*)globalBuffer;
2851 disc_ReadSector(curSect + FAT_ClustToSect(curClus), writeBuffer);
2852 }
2853 else
2854 {
2855 return 0;
2856 }
2857
2858 // Move onto next cluster if needed
2859 if (curSect >= filesysSecPerClus)
2860 {
2861 curSect = 0;
2862 tempNextCluster = FAT_NextCluster(curClus);
2863 if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE))
2864 {
2865 // Ran out of clusters so get a new one
2866 curClus = FAT_LinkFreeCluster(curClus);
2867 if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort
2868 {
2869 flagNoError = false;
2870 }
2871 memset(writeBuffer, 0, BYTE_PER_READ);
2872 } else {
2873 curClus = tempNextCluster;
2874 disc_ReadSector( FAT_ClustToSect( curClus), writeBuffer);
2875 }
2876 }
2877
2878 // Align to sector
2879 tempVar = BYTE_PER_READ - curByte;
2880 if (tempVar > remain)
2881 tempVar = remain;
2882
2883 if ((tempVar < BYTE_PER_READ) && flagNoError)
2884 {
2885 memcpy(&(writeBuffer[curByte]), data, tempVar);
2886 remain -= tempVar;
2887 data += tempVar;
2888 curByte += tempVar;
2889
2890 // Write buffer back to disk
2891 disc_WriteSector (curSect + FAT_ClustToSect(curClus), writeBuffer);
2892
2893 // Move onto next sector
2894 if (curByte >= BYTE_PER_READ)
2895 {
2896 curByte = 0;
2897 curSect++;
2898 }
2899 }
2900
2901 // Align to cluster
2902 // tempVar is number of sectors to write
2903 if (remain > (filesysSecPerClus - curSect) * BYTE_PER_READ)
2904 {
2905 tempVar = filesysSecPerClus - curSect;
2906 } else {
2907 tempVar = remain / BYTE_PER_READ;
2908 }
2909
2910 if ((tempVar > 0) && flagNoError)
2911 {
2912 disc_WriteSectors ( curSect + FAT_ClustToSect(curClus), tempVar, data);
2913 data += tempVar * BYTE_PER_READ;
2914 remain -= tempVar * BYTE_PER_READ;
2915 curSect += tempVar;
2916 }
2917
2918 if (((curSect >= filesysSecPerClus) && flagNoError) && (remain > 0))
2919 {
2920 curSect = 0;
2921 tempNextCluster = FAT_NextCluster(curClus);
2922 if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE))
2923 {
2924 // Ran out of clusters so get a new one
2925 curClus = FAT_LinkFreeCluster(curClus);
2926 if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort
2927 {
2928 flagNoError = false;
2929 }
2930 } else {
2931 curClus = tempNextCluster;
2932 }
2933 }
2934
2935 // Write whole clusters
2936 while ((remain >= filesysBytePerClus) && flagNoError)
2937 {
2938 disc_WriteSectors (FAT_ClustToSect(curClus), filesysSecPerClus, data);
2939 data += filesysBytePerClus;
2940 remain -= filesysBytePerClus;
2941 if (remain > 0)
2942 {
2943 tempNextCluster = FAT_NextCluster(curClus);
2944 if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE))
2945 {
2946 // Ran out of clusters so get a new one
2947 curClus = FAT_LinkFreeCluster(curClus);
2948 if (curClus == CLUSTER_FREE) // Couldn't get a cluster, so abort
2949 {
2950 flagNoError = false;
2951 break;
2952 }
2953 } else {
2954 curClus = tempNextCluster;
2955 }
2956 } else {
2957 // Allocate a new cluster when next writing the file
2958 curSect = filesysSecPerClus;
2959 }
2960 }
2961
2962 // Write remaining sectors
2963 tempVar = remain / BYTE_PER_READ; // Number of sectors left
2964 if ((tempVar > 0) && flagNoError)
2965 {
2966 disc_WriteSectors (FAT_ClustToSect(curClus), tempVar, data);
2967 data += tempVar * BYTE_PER_READ;
2968 remain -= tempVar * BYTE_PER_READ;
2969 curSect += tempVar;
2970 }
2971
2972 // Last remaining sector
2973 // Check if sector wanted is different to the one started with
2974 if ( (( (file->append ? file->appByte : file->curByte) + length) >= BYTE_PER_READ) && flagNoError)
2975 {
2976 if (flagAppending)
2977 {
2978 // Zero sector before using it
2979 memset (writeBuffer, 0, BYTE_PER_READ);
2980 } else {
2981 // Modify existing sector
2982 disc_ReadSector( curSect + FAT_ClustToSect( curClus), writeBuffer);
2983 }
2984 if (remain > 0) {
2985 memcpy(writeBuffer, data, remain);
2986 curByte += remain;
2987 remain = 0;
2988 disc_WriteSector( curSect + FAT_ClustToSect( curClus), writeBuffer);
2989 }
2990 }
2991
2992 // Amount read is the originally requested amount minus stuff remaining
2993 length = length - remain;
2994
2995 // Update file information
2996 if (file->write) // Writing also shifts the read pointer
2997 {
2998 file->curByte = curByte;
2999 file->curSect = curSect;
3000 file->curClus = curClus;
3001 file->curPos = file->curPos + length;
3002 if (file->length < file->curPos)
3003 {
3004 file->length = file->curPos;
3005 }
3006 }
3007 else if (file->append) // Appending doesn't affect the read pointer
3008 {
3009 file->appByte = curByte;
3010 file->appSect = curSect;
3011 file->appClus = curClus;
3012 file->length = file->length + length;
3013 }
3014
3015 return length;
3016 }
3017 #endif
3018
3019
3020 /*-----------------------------------------------------------------
3021 FAT_feof(file)
3022 Returns true if the end of file has been reached
3023 FAT_FILE* file IN: Handle of an open file
3024 bool return OUT: true if EOF, false if not
3025 -----------------------------------------------------------------*/
FAT_feof(FAT_FILE * file)3026 bool FAT_feof(FAT_FILE* file)
3027 {
3028 if ((file == NULL) || (file->inUse == false))
3029 return true; // Return eof on invalid files
3030
3031 return (file->length == file->curPos);
3032 }
3033
3034
3035 #ifdef CAN_WRITE_TO_DISC
3036 /*-----------------------------------------------------------------
3037 FAT_remove (path)
3038 Deletes the file or empty directory sepecified in path
3039 const char* path IN: Path of item to delete
3040 int return OUT: zero if successful, non-zero if not
3041 -----------------------------------------------------------------*/
FAT_remove(const char * path)3042 int FAT_remove (const char* path)
3043 {
3044 DIR_ENT dirEntry;
3045 u32 oldWorkDirCluster;
3046 char checkFilename[13];
3047 FILE_TYPE checkFiletype;
3048
3049 dirEntry = FAT_DirEntFromPath (path);
3050
3051 if (dirEntry.name[0] == FILE_FREE)
3052 {
3053 return -1;
3054 }
3055
3056 // Only delete directories if the directory is entry
3057 if (dirEntry.attrib & ATTRIB_DIR)
3058 {
3059 // Change to the directory temporarily
3060 oldWorkDirCluster = curWorkDirCluster;
3061 FAT_chdir(path);
3062
3063 // Search for files or directories, excluding the . and .. entries
3064 checkFiletype = FAT_FindFirstFile (checkFilename);
3065 while ((checkFilename[0] == '.') && (checkFiletype != FT_NONE))
3066 {
3067 checkFiletype = FAT_FindNextFile (checkFilename);
3068 }
3069
3070 // Change back to working directory
3071 curWorkDirCluster = oldWorkDirCluster;
3072
3073 // Check that the directory is empty
3074 if (checkFiletype != FT_NONE)
3075 {
3076 // Directory isn't empty
3077 return -1;
3078 }
3079 }
3080
3081 // Refresh directory information
3082 dirEntry = FAT_DirEntFromPath (path);
3083
3084 // Free any clusters used
3085 FAT_ClearLinks (dirEntry.startCluster | (dirEntry.startClusterHigh << 16));
3086
3087 // Remove Directory entry
3088 disc_ReadSector ( (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector , globalBuffer);
3089 ((DIR_ENT*)globalBuffer)[wrkDirOffset].name[0] = FILE_FREE;
3090 disc_WriteSector ( (wrkDirCluster == FAT16_ROOT_DIR_CLUSTER ? filesysRootDir : FAT_ClustToSect(wrkDirCluster)) + wrkDirSector , globalBuffer);
3091
3092 // Flush any sectors in disc cache
3093 disc_CacheFlush();
3094
3095 return 0;
3096 }
3097 #endif
3098
3099 #ifdef CAN_WRITE_TO_DISC
3100 /*-----------------------------------------------------------------
3101 FAT_mkdir (path)
3102 Makes a new directory, so long as no other directory or file has
3103 the same name.
3104 const char* path IN: Path and filename of directory to make
3105 int return OUT: zero if successful, non-zero if not
3106 -----------------------------------------------------------------*/
FAT_mkdir(const char * path)3107 int FAT_mkdir (const char* path)
3108 {
3109 u32 newDirCluster;
3110 u32 parentDirCluster;
3111 DIR_ENT dirEntry;
3112 DIR_ENT* entries = (DIR_ENT*)globalBuffer;
3113 int i;
3114
3115 int pathPos, filePos;
3116 char pathname[MAX_FILENAME_LENGTH];
3117 u32 oldDirCluster;
3118
3119 if (FAT_FileExists(path) != FT_NONE)
3120 {
3121 return -1; // File or directory exists with that name
3122 }
3123
3124 // Find filename within path and change to that directory
3125 oldDirCluster = curWorkDirCluster;
3126 if (path[0] == '/')
3127 {
3128 curWorkDirCluster = filesysRootDirClus;
3129 }
3130
3131 pathPos = 0;
3132 filePos = 0;
3133
3134 while (path[pathPos + filePos] != '\0')
3135 {
3136 if (path[pathPos + filePos] == '/')
3137 {
3138 pathname[filePos] = '\0';
3139 if (FAT_chdir(pathname) == false)
3140 {
3141 curWorkDirCluster = oldDirCluster;
3142 return -1; // Couldn't change directory
3143 }
3144 pathPos += filePos + 1;
3145 filePos = 0;
3146 }
3147 pathname[filePos] = path[pathPos + filePos];
3148 filePos++;
3149 }
3150
3151 // Now grab the parent directory's cluster
3152 parentDirCluster = curWorkDirCluster;
3153 curWorkDirCluster = oldDirCluster;
3154
3155 // Get a new cluster for the file
3156 newDirCluster = FAT_LinkFreeCluster(CLUSTER_FREE);
3157
3158 if (newDirCluster == CLUSTER_FREE)
3159 {
3160 return -1; // Couldn't get a new cluster for the directory
3161 }
3162 // Fill in directory entry's information
3163 dirEntry.attrib = ATTRIB_DIR;
3164 dirEntry.reserved = 0;
3165 // Time and date set to system time and date
3166 dirEntry.cTime_ms = 0;
3167 dirEntry.cTime = getRTCtoFileTime();
3168 dirEntry.cDate = getRTCtoFileDate();
3169 dirEntry.aDate = getRTCtoFileDate();
3170 dirEntry.mTime = getRTCtoFileTime();
3171 dirEntry.mDate = getRTCtoFileDate();
3172 // Store cluster position into the directory entry
3173 dirEntry.startCluster = (newDirCluster & 0xFFFF);
3174 dirEntry.startClusterHigh = ((newDirCluster >> 16) & 0xFFFF);
3175 // The file has no data in it - its over written so should be empty
3176 dirEntry.fileSize = 0;
3177
3178 if (FAT_AddDirEntry (path, dirEntry) == false)
3179 {
3180 return -1; // Couldn't add the directory entry
3181 }
3182
3183 // Create the new directory itself
3184 memset(entries, FILE_LAST, BYTE_PER_READ);
3185
3186 // Create . directory entry
3187 dirEntry.name[0] = '.';
3188 // Fill name and extension with spaces
3189 for (i = 1; i < (sizeof(dirEntry.name)/sizeof(dirEntry.name[0])); i++)
3190 {
3191 dirEntry.name[i] = ' ';
3192 }
3193
3194 memcpy(entries, &dirEntry, sizeof(dirEntry));
3195
3196 // Create .. directory entry
3197 dirEntry.name[1] = '.';
3198 dirEntry.startCluster = (parentDirCluster & 0xFFFF);
3199 dirEntry.startClusterHigh = ((parentDirCluster >> 16) & 0xFFFF);
3200
3201 memcpy(&entries[1], &dirEntry, sizeof(dirEntry));
3202
3203 // Write entry to disc
3204 disc_WriteSector(FAT_ClustToSect(newDirCluster), entries);
3205
3206 // Flush any sectors in disc cache
3207 disc_CacheFlush();
3208 return 0;
3209 }
3210 #endif
3211
3212 /*-----------------------------------------------------------------
3213 FAT_fgetc (handle)
3214 Gets the next character in the file
3215 FAT_FILE* file IN: Handle of open file
3216 bool return OUT: character if successful, EOF if not
3217 -----------------------------------------------------------------*/
FAT_fgetc(FAT_FILE * file)3218 char FAT_fgetc (FAT_FILE* file)
3219 {
3220 char c;
3221 return (FAT_fread(&c, 1, 1, file) == 1) ? c : EOF;
3222 }
3223
3224 #ifdef CAN_WRITE_TO_DISC
3225 /*-----------------------------------------------------------------
3226 FAT_fputc (character, handle)
3227 Writes the given character into the file
3228 char c IN: Character to be written
3229 FAT_FILE* file IN: Handle of open file
3230 bool return OUT: character if successful, EOF if not
3231 -----------------------------------------------------------------*/
FAT_fputc(char c,FAT_FILE * file)3232 char FAT_fputc (char c, FAT_FILE* file)
3233 {
3234 return (FAT_fwrite(&c, 1, 1, file) == 1) ? c : EOF;
3235 }
3236 #endif
3237
3238 /*-----------------------------------------------------------------
3239 FAT_fgets (char *tgtBuffer, int num, FAT_FILE* file)
3240 Gets a up to num bytes from file, stopping at the first
3241 newline.
3242
3243 CAUTION: does not do strictly streaming. I.e. it's
3244 reading more then needed bytes and seeking back.
3245 shouldn't matter for random access
3246
3247 char *tgtBuffer OUT: buffer to write to
3248 int num IN: size of target buffer
3249 FAT_FILE* file IN: Handle of open file
3250 bool return OUT: character if successful, EOF if not
3251
3252 Written by MightyMax
3253 Modified by Chishm - 2005-11-17
3254 * Added check for unix style text files
3255 * Removed seek when no newline is found, since it isn't necessary
3256 -------------------------------------------------------------------*/
FAT_fgets(char * tgtBuffer,int num,FAT_FILE * file)3257 char *FAT_fgets(char *tgtBuffer, int num, FAT_FILE* file)
3258 {
3259 u32 curPos;
3260 u32 readLength;
3261 char *returnChar;
3262
3263 // invalid filehandle
3264 if (file == NULL)
3265 {
3266 return NULL ;
3267 }
3268
3269 // end of file
3270 if (FAT_feof(file)==true)
3271 {
3272 return NULL ;
3273 }
3274
3275 // save current position
3276 curPos = FAT_ftell(file);
3277
3278 // read the full buffer (max string chars is num-1 and one end of string \0
3279 readLength = FAT_fread(tgtBuffer,1,num-1,file) ;
3280
3281 // mark least possible end of string
3282 tgtBuffer[readLength] = '\0' ;
3283
3284 if (readLength==0) {
3285 // return error
3286 return NULL ;
3287 }
3288
3289 // get position of first return '\r'
3290 returnChar = strchr(tgtBuffer,'\r');
3291
3292 // if no return is found, search for a newline
3293 if (returnChar == NULL)
3294 {
3295 returnChar = strchr(tgtBuffer,'\n');
3296 }
3297
3298 // Mark the return, if existant, as end of line/string
3299 if (returnChar!=NULL) {
3300 *returnChar++ = 0 ;
3301 if (*returnChar=='\n') { // catch newline too when jumping over the end
3302 // return to location after \r\n (strlen+2)
3303 FAT_fseek(file,curPos+strlen(tgtBuffer)+2,SEEK_SET) ;
3304 return tgtBuffer ;
3305 } else {
3306 // return to location after \r (strlen+1)
3307 FAT_fseek(file,curPos+strlen(tgtBuffer)+1,SEEK_SET) ;
3308 return tgtBuffer ;
3309 }
3310 }
3311
3312 return tgtBuffer ;
3313 }
3314
3315 #ifdef CAN_WRITE_TO_DISC
3316 /*-----------------------------------------------------------------
3317 FAT_fputs (const char *string, FAT_FILE* file)
3318 Writes string to file, excluding end of string character
3319 const char *string IN: string to write
3320 FAT_FILE* file IN: Handle of open file
3321 bool return OUT: number of characters written if successful,
3322 EOF if not
3323
3324 Written by MightyMax
3325 Modified by Chishm - 2005-11-17
3326 * Uses FAT_FILE instead of int
3327 * writtenBytes is now u32 instead of int
3328 -------------------------------------------------------------------*/
FAT_fputs(const char * string,FAT_FILE * file)3329 int FAT_fputs (const char *string, FAT_FILE* file)
3330 {
3331 u32 writtenBytes;
3332 // save string except end of string '\0'
3333 writtenBytes = FAT_fwrite((void *)string, 1, strlen(string), file);
3334
3335 // check if we had an error
3336 if (writtenBytes != strlen(string))
3337 {
3338 // return EOF error
3339 return EOF;
3340 }
3341
3342 // return the charcount written
3343 return writtenBytes ;
3344 }
3345 #endif
3346