aboutsummaryrefslogtreecommitdiff
path: root/sdk-modifications/libsrc/fs/file_allocation_table.c
diff options
context:
space:
mode:
Diffstat (limited to 'sdk-modifications/libsrc/fs/file_allocation_table.c')
-rw-r--r--sdk-modifications/libsrc/fs/file_allocation_table.c330
1 files changed, 330 insertions, 0 deletions
diff --git a/sdk-modifications/libsrc/fs/file_allocation_table.c b/sdk-modifications/libsrc/fs/file_allocation_table.c
new file mode 100644
index 0000000..f382b52
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/file_allocation_table.c
@@ -0,0 +1,330 @@
+/*
+ file_allocation_table.c
+ Reading, writing and manipulation of the FAT structure on
+ a FAT partition
+
+ Copyright (c) 2006 Michael "Chishm" Chisholm
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation and/or
+ other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ 2006-07-11 - Chishm
+ * Original release
+
+ 2006-07-11 - Chishm
+ * Made several fixes related to free clusters, thanks to Loopy
+
+ 2006-10-01 - Chishm
+ * Added _FAT_fat_linkFreeClusterCleared to clear a cluster when it is allocated
+*/
+
+
+#include "file_allocation_table.h"
+#include "partition.h"
+#include <string.h>
+
+/*
+Gets the cluster linked from input cluster
+*/
+u32 _FAT_fat_nextCluster(PARTITION* partition, u32 cluster)
+{
+ u32 nextCluster = CLUSTER_FREE;
+ u32 sector;
+ int offset;
+
+ switch (partition->filesysType)
+ {
+ case FS_UNKNOWN:
+ nextCluster = CLUSTER_FREE;
+ break;
+
+ case FS_FAT12:
+ sector = partition->fat.fatStart + (((cluster * 3) / 2) / BYTES_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTES_PER_READ;
+
+
+ _FAT_cache_readPartialSector (partition->cache, &nextCluster, sector, offset, sizeof(u8));
+
+ offset++;
+
+ if (offset >= BYTES_PER_READ) {
+ offset = 0;
+ sector++;
+ }
+
+ _FAT_cache_readPartialSector (partition->cache, ((u8*)&nextCluster) + sizeof(u8), sector, offset, sizeof(u8));
+
+ if (cluster & 0x01) {
+ nextCluster = nextCluster >> 4;
+ } else {
+ nextCluster &= 0x0FFF;
+ }
+
+ if (nextCluster >= 0x0FF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = partition->fat.fatStart + ((cluster << 1) / BYTES_PER_READ);
+ offset = (cluster % (BYTES_PER_READ >> 1)) << 1;
+
+ _FAT_cache_readPartialSector (partition->cache, &nextCluster, sector, offset, sizeof(u16));
+
+ if (nextCluster >= 0xFFF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+ break;
+
+ case FS_FAT32:
+ sector = partition->fat.fatStart + ((cluster << 2) / BYTES_PER_READ);
+ offset = (cluster % (BYTES_PER_READ >> 2)) << 2;
+
+ _FAT_cache_readPartialSector (partition->cache, &nextCluster, sector, offset, sizeof(u32));
+
+ if (nextCluster >= 0x0FFFFFF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+ break;
+
+ default:
+ nextCluster = CLUSTER_FREE;
+ break;
+ }
+
+ return nextCluster;
+}
+
+/*
+writes value into the correct offset within a partition's FAT, based
+on the cluster number.
+*/
+static bool _FAT_fat_writeFatEntry (PARTITION* partition, u32 cluster, u32 value) {
+ u32 sector;
+ int offset;
+ u8 oldValue;
+
+ if ((cluster < 0x0002) || (cluster > partition->fat.lastCluster))
+ {
+ return false;
+ }
+
+ switch (partition->filesysType)
+ {
+ case FS_UNKNOWN:
+ return false;
+ break;
+
+ case FS_FAT12:
+ sector = partition->fat.fatStart + (((cluster * 3) / 2) / BYTES_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTES_PER_READ;
+
+ if (cluster & 0x01) {
+
+ _FAT_cache_readPartialSector (partition->cache, &oldValue, sector, offset, sizeof(u8));
+
+ value = (value << 4) | (oldValue & 0x0F);
+
+ _FAT_cache_writePartialSector (partition->cache, &value, sector, offset, sizeof(u8));
+
+ offset++;
+ if (offset >= BYTES_PER_READ) {
+ offset = 0;
+ sector++;
+ }
+
+ _FAT_cache_writePartialSector (partition->cache, ((u8*)&value) + sizeof(u8), sector, offset, sizeof(u8));
+
+ } else {
+
+ _FAT_cache_writePartialSector (partition->cache, &value, sector, offset, sizeof(u8));
+
+ offset++;
+ if (offset >= BYTES_PER_READ) {
+ offset = 0;
+ sector++;
+ }
+
+ _FAT_cache_readPartialSector (partition->cache, &oldValue, sector, offset, sizeof(u8));
+
+ value = ((value >> 8) & 0x0F) | (oldValue & 0xF0);
+
+ _FAT_cache_writePartialSector (partition->cache, &value, sector, offset, sizeof(u8));
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = partition->fat.fatStart + ((cluster << 1) / BYTES_PER_READ);
+ offset = (cluster % (BYTES_PER_READ >> 1)) << 1;
+
+ _FAT_cache_writePartialSector (partition->cache, &value, sector, offset, sizeof(u16));
+
+ break;
+
+ case FS_FAT32:
+ sector = partition->fat.fatStart + ((cluster << 2) / BYTES_PER_READ);
+ offset = (cluster % (BYTES_PER_READ >> 2)) << 2;
+
+ _FAT_cache_writePartialSector (partition->cache, &value, sector, offset, sizeof(u32));
+
+ break;
+
+ default:
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+gets the first available free cluster, sets it
+to end of file, links the input cluster to it then returns the
+cluster number
+If an error occurs, return CLUSTER_FREE
+-----------------------------------------------------------------*/
+u32 _FAT_fat_linkFreeCluster(PARTITION* partition, u32 cluster) {
+ u32 firstFree;
+ u32 curLink;
+ u32 lastCluster;
+ bool loopedAroundFAT = false;
+
+ lastCluster = partition->fat.lastCluster;
+
+ if (cluster > lastCluster) {
+ return CLUSTER_FREE;
+ }
+
+ // Check if the cluster already has a link, and return it if so
+ curLink = _FAT_fat_nextCluster(partition, cluster);
+ if ((curLink >= CLUSTER_FIRST) && (curLink <= lastCluster)) {
+ return curLink; // Return the current link - don't allocate a new one
+ }
+
+ // Get a free cluster
+ firstFree = partition->fat.firstFree;
+ // Start at first valid cluster
+ if (firstFree < CLUSTER_FIRST) {
+ firstFree = CLUSTER_FIRST;
+ }
+
+ // Search until a free cluster is found
+ while (_FAT_fat_nextCluster(partition, firstFree) != CLUSTER_FREE) {
+ firstFree++;
+ if (firstFree > lastCluster) {
+ if (loopedAroundFAT) {
+ // If couldn't get a free cluster then return, saying this fact
+ partition->fat.firstFree = firstFree;
+ return CLUSTER_FREE;
+ } else {
+ // Try looping back to the beginning of the FAT
+ // This was suggested by loopy
+ firstFree = CLUSTER_FIRST;
+ loopedAroundFAT = true;
+ }
+ }
+ }
+ partition->fat.firstFree = firstFree;
+
+ if ((cluster >= CLUSTER_FIRST) && (cluster < lastCluster))
+ {
+ // Update the linked from FAT entry
+ _FAT_fat_writeFatEntry (partition, cluster, firstFree);
+ }
+ // Create the linked to FAT entry
+ _FAT_fat_writeFatEntry (partition, firstFree, CLUSTER_EOF);
+
+ return firstFree;
+}
+
+/*-----------------------------------------------------------------
+gets the first available free cluster, sets it
+to end of file, links the input cluster to it, clears the new
+cluster to 0 valued bytes, then returns the cluster number
+If an error occurs, return CLUSTER_FREE
+-----------------------------------------------------------------*/
+u32 _FAT_fat_linkFreeClusterCleared (PARTITION* partition, u32 cluster) {
+ u32 newCluster;
+ int i;
+ u8 emptySector[BYTES_PER_READ];
+
+ // Link the cluster
+ newCluster = _FAT_fat_linkFreeCluster(partition, cluster);
+ if (newCluster == CLUSTER_FREE) {
+ return CLUSTER_FREE;
+ }
+
+ // Clear all the sectors within the cluster
+ memset (emptySector, 0, BYTES_PER_READ);
+ for (i = 0; i < partition->sectorsPerCluster; i++) {
+ _FAT_disc_writeSectors (partition->disc,
+ _FAT_fat_clusterToSector (partition, newCluster) + i,
+ 1, emptySector);
+ }
+
+ return newCluster;
+}
+
+
+/*-----------------------------------------------------------------
+_FAT_fat_clearLinks
+frees any cluster used by a file
+-----------------------------------------------------------------*/
+bool _FAT_fat_clearLinks (PARTITION* partition, u32 cluster) {
+ u32 nextCluster;
+
+ if ((cluster < 0x0002) || (cluster > partition->fat.lastCluster))
+ return false;
+
+ // If this clears up more space in the FAT before the current free pointer, move it backwards
+ if (cluster < partition->fat.firstFree) {
+ partition->fat.firstFree = cluster;
+ }
+
+ while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE)) {
+ // Store next cluster before erasing the link
+ nextCluster = _FAT_fat_nextCluster (partition, cluster);
+
+ // Erase the link
+ _FAT_fat_writeFatEntry (partition, cluster, CLUSTER_FREE);
+
+ // Move onto next cluster
+ cluster = nextCluster;
+ }
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+_FAT_fat_lastCluster
+Trace the cluster links until the last one is found
+-----------------------------------------------------------------*/
+u32 _FAT_fat_lastCluster (PARTITION* partition, u32 cluster) {
+ while ((_FAT_fat_nextCluster(partition, cluster) != CLUSTER_FREE) && (_FAT_fat_nextCluster(partition, cluster) != CLUSTER_EOF)) {
+ cluster = _FAT_fat_nextCluster(partition, cluster);
+ }
+ return cluster;
+}