3 * Copyright (C) 1999, 2000 Brian Palmer <brianp@sginet.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 DWORD nBytesPerSector
; // Bytes per sector
29 DWORD nSectorsPerCluster
; // Number of sectors in a cluster
30 DWORD nReservedSectors
; // Reserved sectors, usually 1 (the bootsector)
31 DWORD nNumberOfFATs
; // Number of FAT tables
32 DWORD nRootDirEntries
; // Number of root directory entries (fat12/16)
33 DWORD nTotalSectors16
; // Number of total sectors on the drive, 16-bit
34 DWORD nSectorsPerFAT16
; // Sectors per FAT table (fat12/16)
35 DWORD nSectorsPerTrack
; // Number of sectors in a track
36 DWORD nNumberOfHeads
; // Number of heads on the disk
37 DWORD nHiddenSectors
; // Hidden sectors (sectors before the partition start like the partition table)
38 DWORD nTotalSectors32
; // Number of total sectors on the drive, 32-bit
40 DWORD nSectorsPerFAT32
; // Sectors per FAT table (fat32)
41 DWORD nExtendedFlags
; // Extended flags (fat32)
42 DWORD nFileSystemVersion
; // File system version (fat32)
43 DWORD nRootDirStartCluster
; // Starting cluster of the root directory (fat32)
45 DWORD nRootDirSectorStart
; // Starting sector of the root directory (fat12/16)
46 DWORD nDataSectorStart
; // Starting sector of the data area
47 DWORD nSectorsPerFAT
; // Sectors per FAT table
48 DWORD nRootDirSectors
; // Number of sectors of the root directory (fat32)
49 DWORD nTotalSectors
; // Total sectors on the drive
50 DWORD nNumberOfClusters
; // Number of clusters on the drive
52 BOOL
FATReadRootDirectoryEntry(int nDirEntry
, void *pDirEntryBuf
)
54 DWORD nDirEntrySector
;
55 int nOffsetWithinSector
;
57 nDirEntrySector
= nRootDirSectorStart
+ ((nDirEntry
* 32) / nBytesPerSector
);
59 if (!ReadOneSector(nDirEntrySector
))
62 nOffsetWithinSector
= (nDirEntry
* 32) % nBytesPerSector
;
64 memcpy(pDirEntryBuf
, SectorBuffer
+ nOffsetWithinSector
, 32);
66 if (*((char *)pDirEntryBuf
) == 0x05)
67 *((char *)pDirEntryBuf
) = 0xE5;
72 BOOL
FATReadDirectoryEntry(DWORD nDirStartCluster
, int nDirEntry
, void *pDirEntryBuf
)
74 DWORD nRealDirCluster
;
75 int nSectorWithinCluster
;
76 int nOffsetWithinSector
;
80 i
= (nDirEntry
* 32) / (nSectorsPerCluster
* nBytesPerSector
);
81 //if ((nDirEntry * 32) % (nSectorsPerCluster * nBytesPerSector))
84 for (nRealDirCluster
= nDirStartCluster
; i
; i
--)
85 nRealDirCluster
= FATGetFATEntry(nRealDirCluster
);
87 nSectorWithinCluster
= ((nDirEntry
* 32) % (nSectorsPerCluster
* nBytesPerSector
)) / nBytesPerSector
;
89 nSectorToRead
= ((nRealDirCluster
- 2) * nSectorsPerCluster
) + nDataSectorStart
+ nSectorWithinCluster
;
91 if (!ReadOneSector(nSectorToRead
))
94 nOffsetWithinSector
= (nDirEntry
* 32) % nBytesPerSector
;
96 memcpy(pDirEntryBuf
, SectorBuffer
+ nOffsetWithinSector
, 32);
98 if (*((char *)pDirEntryBuf
) == 0x05)
99 *((char *)pDirEntryBuf
) = 0xE5;
106 * This function searches the file system for the
107 * specified filename and fills in a FAT_STRUCT structure
108 * with info describing the file, etc. returns true
109 * if the file exists or false otherwise
111 BOOL
FATLookupFile(char *file
, PFAT_STRUCT pFatStruct
)
118 FAT_STRUCT fatstruct
;
122 memset(pFatStruct
, 0, sizeof(FAT_STRUCT
));
124 // Check and see if the first character is '\' and remove it if so
128 // Figure out how many sub-directories we are nested in
129 numparts
= FATGetNumPathParts(file
);
131 // Loop once for each part
132 for (i
=0; i
<numparts
; i
++)
134 // Make filename compatible with MSDOS dir entry
135 if (!FATGetFirstNameFromPath(filename
, file
))
137 // Advance to the next part of the path
138 for (; (*file
!= '\\') && (*file
!= '\0'); file
++);
141 // If we didn't find the correct sub-directory the fail
142 if ((i
!= 0) && !bFound
)
147 // Check if we are pulling from the root directory of a fat12/fat16 disk
148 if ((i
== 0) && ((nFATType
== FAT12
) || (nFATType
== FAT16
)))
149 nNumDirEntries
= nRootDirEntries
;
150 else if ((i
== 0) && (nFATType
== FAT32
))
152 cluster
= nRootDirStartCluster
;
153 fatstruct
.dwSize
= nSectorsPerCluster
* nBytesPerSector
;
154 while((cluster
= FATGetFATEntry(cluster
)) < 0x0FFFFFF8)
155 fatstruct
.dwSize
+= nSectorsPerCluster
* nBytesPerSector
;
157 fatstruct
.dwStartCluster
= nRootDirStartCluster
;
158 nNumDirEntries
= fatstruct
.dwSize
/ 32;
161 nNumDirEntries
= fatstruct
.dwSize
/ 32;
163 // Loop through each directory entry
164 for (j
=0; j
<nNumDirEntries
; j
++)
167 if ((i
== 0) && ((nFATType
== FAT12
) || (nFATType
== FAT16
)))
169 if (!FATReadRootDirectoryEntry(j
, direntry
))
174 if (!FATReadDirectoryEntry(fatstruct
.dwStartCluster
, j
, direntry
))
178 if (memcmp(direntry
, filename
, 11) == 0)
180 fatstruct
.dwStartCluster
= 0;
181 fatstruct
.dwCurrentCluster
= 0;
182 memcpy(&fatstruct
.dwStartCluster
, direntry
+ 26, sizeof(WORD
));
183 memcpy(&fatstruct
.dwCurrentCluster
, direntry
+ 20, sizeof(WORD
));
184 fatstruct
.dwStartCluster
+= (fatstruct
.dwCurrentCluster
* 0x10000);
186 if (direntry
[11] & ATTR_DIRECTORY
)
188 fatstruct
.dwSize
= nSectorsPerCluster
* nBytesPerSector
;
189 cluster
= fatstruct
.dwStartCluster
;
193 while((cluster
= FATGetFATEntry(cluster
)) < 0xFF8)
194 fatstruct
.dwSize
+= nSectorsPerCluster
* nBytesPerSector
;
197 while((cluster
= FATGetFATEntry(cluster
)) < 0xFFF8)
198 fatstruct
.dwSize
+= nSectorsPerCluster
* nBytesPerSector
;
201 while((cluster
= FATGetFATEntry(cluster
)) < 0x0FFFFFF8)
202 fatstruct
.dwSize
+= nSectorsPerCluster
* nBytesPerSector
;
207 // If we have more parts to go and this isn't a directory then fail
208 if ((i
< (numparts
-1)) && !(direntry
[11] & ATTR_DIRECTORY
))
211 // If this is supposed to be a file and it is a directory then fail
212 if ((i
== (numparts
-1)) && (direntry
[11] & ATTR_DIRECTORY
))
224 memcpy(&pFatStruct
->dwStartCluster
, direntry
+ 26, sizeof(WORD
));
225 memcpy(&pFatStruct
->dwCurrentCluster
, direntry
+ 20, sizeof(WORD
));
226 pFatStruct
->dwStartCluster
+= (pFatStruct
->dwCurrentCluster
* 0x10000);
227 pFatStruct
->dwCurrentCluster
= pFatStruct
->dwStartCluster
;
229 memcpy(&pFatStruct
->dwSize
, direntry
+ 28, sizeof(DWORD
));
230 pFatStruct
->dwCurrentReadOffset
= 0;
236 * FATGetNumPathParts()
237 * This function parses a path in the form of dir1\dir2\file1.ext
238 * and returns the number of parts it has (i.e. 3 - dir1,dir2,file1.ext)
240 int FATGetNumPathParts(char *name
)
244 for(i
=0,num
=0; i
<(int)strlen(name
); i
++)
255 * FATGetFirstNameFromPath()
256 * This function parses a path in the form of dir1\dir2\file1.ext
257 * and puts the first name of the path (e.g. "dir1") in buffer
258 * compatible with the MSDOS directory structure
260 BOOL
FATGetFirstNameFromPath(char *buffer
, char *name
)
265 // Copy all the characters up to the end of the
266 // string or until we hit a '\' character
267 // and put them in temp
268 for(i
=0; i
<(int)strlen(name
); i
++)
277 // If the filename is too long then fail
278 if(strlen(temp
) > 12)
281 FATParseFileName(buffer
, temp
);
288 * This function parses "name" which is in the form of file.ext
289 * and puts it in "buffer" in the form of "FILE EXT" which
290 * is compatible with the MSDOS directory structure
292 void FATParseFileName(char *buffer
, char *name
)
300 buffer
[i
++] = (name
[j
] && (name
[j
] != '.')) ? toupper(name
[j
++]) : ' ';
306 buffer
[i
++] = name
[j
] ? toupper(name
[j
++]) : ' ';
313 * returns the FAT entry for a given cluster number
315 DWORD
FATGetFATEntry(DWORD nCluster
)
320 int ThisFATEntOffset
;
327 FATOffset
= nCluster
+ (nCluster
/ 2);
328 ThisFATSecNum
= (FATOffset
/ nBytesPerSector
);
329 ThisFATEntOffset
= (FATOffset
% nBytesPerSector
);
330 fat
= *((WORD
*) (pFileSysData
+ (ThisFATSecNum
* nBytesPerSector
) + ThisFATEntOffset
));
331 if (nCluster
& 0x0001)
332 fat
= fat
>> 4; /* Cluster number is ODD */
334 fat
= fat
& 0x0FFF; /* Cluster number is EVEN */
339 FATOffset
= (nCluster
* 2);
340 //ThisFATSecNum = nReservedSectors + (FATOffset / nBytesPerSector);
341 //ThisFATEntOffset = (FATOffset % nBytesPerSector);
343 //if (!ReadOneSector(ThisFATSecNum))
346 //fat = *((WORD *) &SectorBuffer[ThisFATEntOffset]);
347 fat
= *((WORD
*) (pFileSysData
+ FATOffset
));
352 //if (!ReadOneSector(ThisFATSecNum))
355 //fat = *((DWORD *) &SectorBuffer[ThisFATEntOffset]) & 0x0FFFFFFF;
358 // This code manages the fat32 fat table entry cache
359 // The cache is at address FILESYSADDR which is 128k in size
360 // The first two sectors will contain an array of DWORDs that
361 // Specify what fat sector is cached. The first two DWORDs
363 FATOffset
= (nCluster
* 4);
364 ThisFATSecNum
= nReservedSectors
+ (FATOffset
/ nBytesPerSector
);
365 ThisFATEntOffset
= (FATOffset
% nBytesPerSector
);
367 // Now we go through our cache and see if we already have the sector cached
369 for (Idx
=2; Idx
<256; Idx
++)
371 if (((int*)pFat32FATCacheIndex
)[Idx
] == ThisFATSecNum
)
381 fat
= (*((DWORD
*) (pFat32FATCacheIndex
+
382 (Idx
* nBytesPerSector
) + ThisFATEntOffset
))) & 0x0FFFFFFF;
386 if (!ReadOneSector(ThisFATSecNum
))
389 // Move each sector down in the cache to make room for new sector
390 for (Idx
=255; Idx
>2; Idx
--)
392 memcpy(pFat32FATCacheIndex
+ (Idx
* nBytesPerSector
), pFat32FATCacheIndex
+ ((Idx
- 1) * nBytesPerSector
), nBytesPerSector
);
393 ((int*)pFat32FATCacheIndex
)[Idx
] = ((int*)pFat32FATCacheIndex
)[Idx
- 1];
396 // Insert it into the cache
397 memcpy(pFat32FATCacheIndex
+ (2 * nBytesPerSector
), SectorBuffer
, nBytesPerSector
);
398 ((int*)pFat32FATCacheIndex
)[2] = ThisFATSecNum
;
401 fat
= (*((DWORD
*) (pFat32FATCacheIndex
+
402 (2 * nBytesPerSector
) + ThisFATEntOffset
))) & 0x0FFFFFFF;
414 * Tries to open the file 'name' and returns true or false
415 * for success and failure respectively
417 BOOL
FATOpenFile(char *szFileName
, PFAT_STRUCT pFatStruct
)
419 if(!FATLookupFile(szFileName
, pFatStruct
))
422 /* Fill in file control information */
423 pFatStruct
->dwCurrentCluster
= pFatStruct
->dwStartCluster
;
424 pFatStruct
->dwCurrentReadOffset
= 0;
431 * Reads the specified cluster into memory
432 * and returns the number of bytes read
434 int FATReadCluster(DWORD nCluster
, char *cBuffer
)
438 nStartSector
= ((nCluster
- 2) * nSectorsPerCluster
) + nDataSectorStart
;
440 ReadMultipleSectors(nStartSector
, nSectorsPerCluster
, cBuffer
);
442 return (nSectorsPerCluster
* nBytesPerSector
);
447 * Reads nNumBytes from open file and
448 * returns the number of bytes read
450 int FATRead(PFAT_STRUCT pFatStruct
, int nNumBytes
, char *cBuffer
)
452 int nSectorWithinCluster
;
453 int nOffsetWithinSector
;
454 int nOffsetWithinCluster
;
458 // If all the data is read return zero
459 if (pFatStruct
->dwCurrentReadOffset
>= pFatStruct
->dwSize
)
462 // If they are trying to read more than there is to read
463 // then adjust the amount to read
464 if ((pFatStruct
->dwCurrentReadOffset
+ nNumBytes
) > pFatStruct
->dwSize
)
465 nNumBytes
= pFatStruct
->dwSize
- pFatStruct
->dwCurrentReadOffset
;
469 // Check and see if the read offset is aligned to a cluster boundary
470 // if so great, if not then read the rest of the current cluster
471 if ((pFatStruct
->dwCurrentReadOffset
% (nSectorsPerCluster
* nBytesPerSector
)) != 0)
473 nSectorWithinCluster
= ((pFatStruct
->dwCurrentReadOffset
/ nBytesPerSector
) % nSectorsPerCluster
);
474 nOffsetWithinSector
= (pFatStruct
->dwCurrentReadOffset
% nBytesPerSector
);
475 nOffsetWithinCluster
= (pFatStruct
->dwCurrentReadOffset
% (nSectorsPerCluster
* nBytesPerSector
));
477 // Read the cluster into the scratch area
478 FATReadCluster(pFatStruct
->dwCurrentCluster
, (char *)FATCLUSTERBUF
);
480 nNum
= (nSectorsPerCluster
* nBytesPerSector
) - (pFatStruct
->dwCurrentReadOffset
% (nSectorsPerCluster
* nBytesPerSector
));
481 if (nNumBytes
>= nNum
)
483 memcpy(cBuffer
, (char *)(FATCLUSTERBUF
+ nOffsetWithinCluster
), nNum
);
486 pFatStruct
->dwCurrentReadOffset
+= nNum
;
487 pFatStruct
->dwCurrentCluster
= FATGetFATEntry(pFatStruct
->dwCurrentCluster
);
492 memcpy(cBuffer
, (char *)(FATCLUSTERBUF
+ nOffsetWithinCluster
), nNumBytes
);
493 nBytesRead
+= nNumBytes
;
494 cBuffer
+= nNumBytes
;
495 pFatStruct
->dwCurrentReadOffset
+= nNumBytes
;
496 nNumBytes
-= nNumBytes
;
501 // Read the cluster into the scratch area
502 FATReadCluster(pFatStruct
->dwCurrentCluster
, (char *)FATCLUSTERBUF
);
504 nNum
= (nSectorsPerCluster
* nBytesPerSector
);
505 if (nNumBytes
>= nNum
)
507 memcpy(cBuffer
, (char *)(FATCLUSTERBUF
), nNum
);
510 pFatStruct
->dwCurrentReadOffset
+= nNum
;
511 pFatStruct
->dwCurrentCluster
= FATGetFATEntry(pFatStruct
->dwCurrentCluster
);
516 memcpy(cBuffer
, (char *)(FATCLUSTERBUF
), nNumBytes
);
517 nBytesRead
+= nNumBytes
;
518 cBuffer
+= nNumBytes
;
519 pFatStruct
->dwCurrentReadOffset
+= nNumBytes
;
520 nNumBytes
-= nNumBytes
;
528 int FATfseek(PFAT_STRUCT pFatStruct
, DWORD offset
)
533 numclusters
= offset
/ (nSectorsPerCluster
* nBytesPerSector
);
534 for (cluster
=pFatStruct
->dwStartCluster
; numclusters
> 0; numclusters
--)
535 cluster
= FATGetFATEntry(cluster
);
537 pFatStruct
->dwCurrentCluster
= cluster
;
538 pFatStruct
->dwCurrentReadOffset
= offset
;