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