+/*
+ * Acess2 FAT12/16/32 Driver
+ * - By John Hodge (thePowersGang)
+ *
+ * fatio.c
+ * - FAT Manipulation and Cluster IO
+ */
+#define DEBUG 1
+#include <acess.h>
+#include <vfs.h>
+#include "common.h"
+
+// === CODE ===
+/**
+ * \fn Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)
+ * \brief Fetches a value from the FAT
+ */
+Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)
+{
+ Uint32 val = 0;
+ Uint32 ofs;
+ ENTER("pDisk xCluster", Disk, cluster);
+ Mutex_Acquire( &Disk->lFAT );
+ #if CACHE_FAT
+ if( Disk->ClusterCount <= giFAT_MaxCachedClusters )
+ {
+ val = Disk->FATCache[cluster];
+ if(Disk->type == FAT12 && val == EOC_FAT12) val = -1;
+ if(Disk->type == FAT16 && val == EOC_FAT16) val = -1;
+ if(Disk->type == FAT32 && val == EOC_FAT32) val = -1;
+ }
+ else
+ {
+ #endif
+ ofs = Disk->bootsect.resvSectCount*512;
+ if(Disk->type == FAT12) {
+ VFS_ReadAt(Disk->fileHandle, ofs+(cluster/2)*3, 3, &val);
+ val = (cluster & 1 ? val>>12 : val & 0xFFF);
+ if(val == EOC_FAT12) val = -1;
+ } else if(Disk->type == FAT16) {
+ VFS_ReadAt(Disk->fileHandle, ofs+cluster*2, 2, &val);
+ if(val == EOC_FAT16) val = -1;
+ } else {
+ VFS_ReadAt(Disk->fileHandle, ofs+cluster*4, 4, &val);
+ if(val == EOC_FAT32) val = -1;
+ }
+ #if CACHE_FAT
+ }
+ #endif /*CACHE_FAT*/
+ Mutex_Release( &Disk->lFAT );
+ LEAVE('x', val);
+ return val;
+}
+
+#if SUPPORT_WRITE
+/**
+ * \brief Allocate a new cluster
+ */
+Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous)
+{
+ Uint32 ret = -1, eoc;
+
+ switch(Disk->type)
+ {
+ case FAT12: eoc = EOC_FAT12; break;
+ case FAT16: eoc = EOC_FAT16; break;
+ case FAT32: eoc = EOC_FAT32; break;
+ default: return 0;
+ }
+
+ #if CACHE_FAT
+ if( Disk->ClusterCount <= giFAT_MaxCachedClusters )
+ {
+ int bFoundCluster = 0;
+ Mutex_Acquire(&Disk->lFAT);
+ if( Previous != -1 )
+ {
+ for(ret = Previous; ret < Disk->ClusterCount; ret++)
+ {
+ if(Disk->FATCache[ret] != 0) {
+ bFoundCluster = 1;
+ break;
+ }
+ }
+ }
+ if( !bFoundCluster )
+ {
+ for(ret = 0; ret < Previous; ret++)
+ {
+ if(Disk->FATCache[ret] == 0) {
+ bFoundCluster = 1;
+ break;
+ }
+ }
+ }
+
+ if(bFoundCluster)
+ {
+ Disk->FATCache[ret] = eoc;
+ if( Previous != -1 )
+ Disk->FATCache[Previous] = ret;
+ }
+ else
+ {
+ ret = 0;
+ }
+
+ Mutex_Release(&Disk->lFAT);
+ return ret;
+ }
+ else
+ {
+ #endif
+ Uint32 val = 0;
+ Uint32 base = Disk->bootsect.resvSectCount*512;
+ int block = 0, block_ofs = 0;
+ int first_block;
+ const int block_size = 512*3;
+ const int ents_per_block_12 = block_size * 2 / 3; // 1.5 bytes per entry
+// const int ents_per_block_16 = block_size / 2; // 2 bytes per entry
+// const int ents_per_block_32 = block_size / 4; // 4 bytes per entry
+ int block_count_12 = DivUp(Disk->ClusterCount, ents_per_block_12);
+ Uint8 sector_data[block_size+1];
+ sector_data[block_size] = 0;
+
+ Mutex_Acquire(&Disk->lFAT);
+ switch(Disk->type)
+ {
+ case FAT12:
+ if( Previous != -1 )
+ block = Previous / ents_per_block_12;
+ else
+ block = 0;
+ first_block = block;
+
+ // Search within the same block as the previous cluster first
+ do {
+ VFS_ReadAt(Disk->fileHandle, base + block, block_size, sector_data);
+ for( block_ofs = 0; block_ofs < ents_per_block_12; block_ofs ++ )
+ {
+ Uint32 *valptr = (void*)( sector_data + block_ofs / 2 * 3 );
+ int bitofs = 12 * (block_ofs % 2);
+// LOG("%i:%i - FAT Ent 0x%03x", block, block_ofs, (*valptr>>bitofs) & 0xFFF);
+ if( ((*valptr >> bitofs) & 0xFFF) == 0 ) {
+ // Found a free cluster
+ *valptr |= EOC_FAT12 << bitofs;
+ ret = block * ents_per_block_12 + block_ofs;
+ break;
+ }
+ }
+ // Check for early break from the above loop
+ if( block_ofs != ents_per_block_12 )
+ break;
+
+ // Next block please
+ block ++;
+ if( block == block_count_12 )
+ block = 0;
+ } while( block != first_block );
+
+ if( ret != 0 ) // TODO: Could cluster 0 be valid?
+ {
+ // Write back changes to this part of the FAT
+ VFS_WriteAt(Disk->fileHandle, base + block, block_size, sector_data);
+
+ // Note the new cluster in the chain
+ VFS_ReadAt(Disk->fileHandle, base + (Previous>>1)*3, 3, &val);
+ if( ret & 1 ) {
+ val &= 0x000FFF;
+ val |= ret << 12;
+ }
+ else {
+ val &= 0xFFF000;
+ val |= ret << 0;
+ }
+ VFS_WriteAt(Disk->fileHandle, base + (Previous>>1)*3, 3, &val);
+ }
+ break;
+ case FAT16:
+ Log_Warning("FAT", "TODO: Implement cluster allocation with FAT16");
+// VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret);
+// VFS_WriteAt(Disk->fileHandle, ofs+ret*2, 2, &eoc);
+ break;
+ case FAT32:
+ Log_Warning("FAT", "TODO: Implement cluster allocation with FAT32");
+// VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret);
+// VFS_WriteAt(Disk->fileHandle, ofs+ret*4, 4, &eoc);
+ break;
+ }
+ Mutex_Release(&Disk->lFAT);
+ return ret;
+ #if CACHE_FAT
+ }
+ #endif
+}
+
+/**
+ * \brief Free's a cluster
+ * \return The original contents of the cluster
+ */
+Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster)
+{
+ Uint32 ret;
+
+ Mutex_Acquire(&Disk->lFAT);
+ #if CACHE_FAT
+ if( Disk->ClusterCount <= giFAT_MaxCachedClusters )
+ {
+
+ ret = Disk->FATCache[Cluster];
+ Disk->FATCache[Cluster] = 0;
+ }
+ else
+ {
+ #endif
+ Uint32 val;
+ Uint32 ofs = Disk->bootsect.resvSectCount*512;
+ switch(Disk->type)
+ {
+ case FAT12:
+ VFS_ReadAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val);
+ if( Cluster & 1 ) {
+ ret = val & 0xFFF0000;
+ val &= 0xFFF;
+ }
+ else {
+ ret = val & 0xFFF;
+ val &= 0xFFF000;
+ }
+ VFS_WriteAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val);
+ break;
+ case FAT16:
+ VFS_ReadAt(Disk->fileHandle, ofs+Cluster*2, 2, &ret);
+ val = 0;
+ VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);
+ break;
+ case FAT32:
+ VFS_ReadAt(Disk->fileHandle, ofs+Cluster*4, 4, &ret);
+ val = 0;
+ VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);
+ break;
+ }
+ #if CACHE_FAT
+ }
+ #endif
+ Mutex_Release(&Disk->lFAT);
+ if(Disk->type == FAT12 && ret == EOC_FAT12) ret = -1;
+ if(Disk->type == FAT16 && ret == EOC_FAT16) ret = -1;
+ if(Disk->type == FAT32 && ret == EOC_FAT32) ret = -1;
+ return ret;
+}
+#endif
+
+/*
+ * ====================
+ * Cluster IO
+ * ====================
+ */
+/**
+ * \brief Read a cluster
+ * \param Disk Disk (Volume) to read from
+ * \param Length Length to read
+ * \param Buffer Destination for read data
+ */
+void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer)
+{
+ ENTER("pDisk xCluster iLength pBuffer", Disk, Cluster, Length, Buffer);
+ VFS_ReadAt(
+ Disk->fileHandle,
+ (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )
+ * Disk->bootsect.bps,
+ Length,
+ Buffer
+ );
+ LEAVE('-');
+}
+
+#if SUPPORT_WRITE
+/**
+ * \brief Write a cluster to disk
+ */
+void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, const void *Buffer)
+{
+ ENTER("pDisk xCluster pBuffer", Disk, Cluster, Buffer);
+ VFS_WriteAt(
+ Disk->fileHandle,
+ (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )
+ * Disk->bootsect.bps,
+ Disk->BytesPerCluster,
+ Buffer
+ );
+ LEAVE('-');
+}
+#endif