From 626c79ea2d7aae320722bc5c91389f49c15cbc49 Mon Sep 17 00:00:00 2001
From: dimok321 <15055714+dimok789@users.noreply.github.com>
Date: Sun, 15 Nov 2009 21:30:44 +0000
Subject: [PATCH] *Added the custom libfat source to our source so you don't
need to replace the original one in libogc *Added NewSuperMarioBrosPatch for
NTSC Version
---
Makefile | 4 +-
gui.pnproj | 2 +-
gui.pnps | 2 +-
source/banner/banner.c | 252 ++--
source/banner/openingbnr.c | 14 +-
source/cheats/cheatmenu.cpp | 6 +-
source/fatmounter.c | 8 +-
source/libfat/bit_ops.h | 57 +
source/libfat/cache.c | 366 ++++++
source/libfat/cache.h | 130 ++
source/libfat/common.h | 48 +
source/libfat/directory.c | 1075 ++++++++++++++++
source/libfat/directory.h | 173 +++
source/libfat/disc_fat.c | 67 +
source/libfat/disc_fat.h | 110 ++
source/libfat/fat.h | 80 ++
source/libfat/fatdir.c | 610 +++++++++
source/libfat/fatdir.h | 73 ++
source/libfat/fatfile.c | 1131 +++++++++++++++++
source/libfat/fatfile.h | 105 ++
source/libfat/file_allocation_table.c | 383 ++++++
source/libfat/file_allocation_table.h | 70 +
source/libfat/filetime.c | 107 ++
source/libfat/filetime.h | 41 +
source/libfat/libfat.c | 197 +++
source/libfat/lock.h | 87 ++
source/libfat/mem_allocate.h | 49 +
source/libfat/partition.c | 312 +++++
source/libfat/partition.h | 88 ++
source/listfiles.c | 2 +-
source/main.cpp | 2 +-
source/prompts/PromptWindows.cpp | 2 +-
source/prompts/PromptWindows.h | 2 +-
source/settings/Settings.cpp | 2 +-
source/usbloader/apploader.c | 20 +-
.../{partition.c => partition_usbloader.c} | 380 +++---
.../{partition.h => partition_usbloader.h} | 0
source/usbloader/wbfs.c | 2 +-
source/usbloader/wbfs_fat.c | 2 +-
source/wad/title.h | 4 +-
40 files changed, 5721 insertions(+), 344 deletions(-)
create mode 100644 source/libfat/bit_ops.h
create mode 100644 source/libfat/cache.c
create mode 100644 source/libfat/cache.h
create mode 100644 source/libfat/common.h
create mode 100644 source/libfat/directory.c
create mode 100644 source/libfat/directory.h
create mode 100644 source/libfat/disc_fat.c
create mode 100644 source/libfat/disc_fat.h
create mode 100644 source/libfat/fat.h
create mode 100644 source/libfat/fatdir.c
create mode 100644 source/libfat/fatdir.h
create mode 100644 source/libfat/fatfile.c
create mode 100644 source/libfat/fatfile.h
create mode 100644 source/libfat/file_allocation_table.c
create mode 100644 source/libfat/file_allocation_table.h
create mode 100644 source/libfat/filetime.c
create mode 100644 source/libfat/filetime.h
create mode 100644 source/libfat/libfat.c
create mode 100644 source/libfat/lock.h
create mode 100644 source/libfat/mem_allocate.h
create mode 100644 source/libfat/partition.c
create mode 100644 source/libfat/partition.h
rename source/usbloader/{partition.c => partition_usbloader.c} (95%)
rename source/usbloader/{partition.h => partition_usbloader.h} (100%)
diff --git a/Makefile b/Makefile
index d676a032..0b623e39 100644
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,7 @@ SOURCES := source source/libwiigui source/images source/fonts source/sounds \
source/libwbfs source/unzip source/language source/mload source/patches \
source/usbloader source/xml source/network source/settings source/prompts \
source/ramdisk source/wad source/banner source/cheats source/homebrewboot \
- source/themes source/menu
+ source/themes source/menu source/libfat
DATA := data
INCLUDES := source
@@ -35,7 +35,7 @@ LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map,--section-start,.init=0x80B00
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
-LIBS := -lfat -lpngu -lpng -lm -lz -lwiiuse -lbte -lasnd -logc -lfreetype -ltremor -lmad -lmxml -ljpeg
+LIBS := -lpngu -lpng -lm -lz -lwiiuse -lbte -lasnd -logc -lfreetype -ltremor -lmad -lmxml -ljpeg
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
diff --git a/gui.pnproj b/gui.pnproj
index bf0ccfb3..0b9dce41 100644
--- a/gui.pnproj
+++ b/gui.pnproj
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/gui.pnps b/gui.pnps
index 094633ff..faba12af 100644
--- a/gui.pnps
+++ b/gui.pnps
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/source/banner/banner.c b/source/banner/banner.c
index 5ac1fbcd..62340b83 100644
--- a/source/banner/banner.c
+++ b/source/banner/banner.c
@@ -1,127 +1,127 @@
-/****************************************************************************
- * USB Loader GX Team
- * banner.c
- *
- * Dump opening.bnr thanks to Wiipower
- ***************************************************************************/
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "fatmounter.h"
+/****************************************************************************
+ * USB Loader GX Team
+ * banner.c
+ *
+ * Dump opening.bnr thanks to Wiipower
+ ***************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "libfat/fat.h"
+#include "fatmounter.h"
#include "usbloader/wdvd.h"
-#include "usbloader/disc.h"
-#include "banner.h"
-#include "patches/fst.h"
-#include "usbloader/fstfile.h"
-
-s32 dump_banner(const u8* discid,const char * dest)
-{
- // Mount the disc
- //Disc_SetWBFS(1, (u8*)discid);
- Disc_SetUSB(discid);
-
- Disc_Open();
-
- u64 offset;
- s32 ret;
-
- ret = __Disc_FindPartition(&offset);
- if (ret < 0)
- return ret;
-
- ret = WDVD_OpenPartition(offset);
-
- if (ret < 0) {
- //printf("ERROR: OpenPartition(0x%llx) %d\n", offset, ret);
- return ret;
- }
-
- // Read where to find the fst.bin
- u32 *buffer = memalign(32, 0x20);
-
- if (buffer == NULL)
- {
- //Out of memory
- return -1;
- }
-
- ret = WDVD_Read(buffer, 0x20, 0x420);
- if (ret < 0)
- return ret;
-
- // Read fst.bin
- void *fstbuffer = memalign(32, buffer[2]*4);
- FST_ENTRY *fst = (FST_ENTRY *)fstbuffer;
-
- if (fst == NULL)
- {
- //Out of memory
- free(buffer);
- return -1;
- }
-
- ret = WDVD_Read(fstbuffer, buffer[2]*4, buffer[1]*4);
- if (ret < 0)
- return ret;
-
- free(buffer);
-
- // Search the fst.bin
- u32 count = fst[0].filelen;
- int i;
- u32 index = 0;
-
- for (i=1;i
#include
#include
-#include
+#include "libfat/fat.h"
#include "MD5.h"
#include "banner.h"
@@ -115,7 +115,7 @@ typedef struct
u8 zeroes[16];
} U8_archive_header;
-static int write_file(void* data, size_t size, char* name)
+static int write_file(void* data, size_t size, char* name)
{
size_t written=0;
FILE *out;
@@ -126,7 +126,7 @@ static int write_file(void* data, size_t size, char* name)
fclose(out);
}
return (written == size) ? 1 : -1;
-}
+}
u8* decompress_lz77(u8 *data, size_t data_size, size_t* decompressed_size)
{
@@ -376,7 +376,7 @@ void do_U8_archivebanner(FILE *fp)
fread(string_table, 1, rest_size, fp);
for (i = 0; i < num_nodes; i++) {
- U8_node* node = &nodes[i];
+ U8_node* node = &nodes[i];
u16 type = be16((u8*)&node->type);
u16 name_offset = be16((u8*)&node->name_offset);
u32 my_data_offset = be32((u8*)&node->data_offset);
@@ -499,7 +499,7 @@ int unpackBanner(const u8 *gameid, int what, const char *outdir)
size_t size;
u8 *data;
fseek(fp, 0, SEEK_END);
- size = ftell(fp);
+ size = ftell(fp);
if(!size)
{
ret = -1;
@@ -512,14 +512,14 @@ int unpackBanner(const u8 *gameid, int what, const char *outdir)
ret = -1;
goto error;
}
- if(fread(data, 1, size, fp) != size)
+ if(fread(data, 1, size, fp) != size)
{
ret = -1;
goto error;
}
ret = write_file(data, size, path);
}
-error: fclose(fp);
+error: fclose(fp);
}
ramdiskUnmount("BANNER");
error2:
diff --git a/source/cheats/cheatmenu.cpp b/source/cheats/cheatmenu.cpp
index 64e40fa8..3e751079 100644
--- a/source/cheats/cheatmenu.cpp
+++ b/source/cheats/cheatmenu.cpp
@@ -1,12 +1,12 @@
#include
#include
-#include
+#include "libfat/fat.h"
#include "libwiigui/gui.h"
#include "libwiigui/gui_customoptionbrowser.h"
#include "prompts/PromptWindows.h"
#include "language/gettext.h"
-#include "fatmounter.h"
+#include "fatmounter.h"
#include "listfiles.h"
#include "menu.h"
#include "filelist.h"
@@ -136,7 +136,7 @@ int CheatMenu(const char * gameID) {
}
if (x == 0) {
WindowPrompt(tr("Error"),tr("No cheats were selected"),tr("OK"));
- } else {
+ } else {
subfoldercreate(Settings.Cheatcodespath);
string chtpath = Settings.Cheatcodespath;
string gctfname = chtpath + c.getGameID() + ".gct";
diff --git a/source/fatmounter.c b/source/fatmounter.c
index fc9b4e2f..64cca03b 100644
--- a/source/fatmounter.c
+++ b/source/fatmounter.c
@@ -1,4 +1,3 @@
-#include
#include
#include
#include
@@ -8,6 +7,7 @@
#include "usbloader/sdhc.h"
#include "usbloader/usbstorage.h"
+#include "libfat/fat.h"
//these are the only stable and speed is good
#define CACHE 32
@@ -41,7 +41,7 @@ int USBDevice_Init() {
return -1;
}
}
-
+
fat_usb_mount = 1;
fat_usb_sec = _FAT_startSector;
return 0;
@@ -67,7 +67,7 @@ int WBFSDevice_Init(u32 sector) {
return -1;
}
}
-
+
fat_wbfs_mount = 1;
fat_wbfs_sec = _FAT_startSector;
if (sector && fat_wbfs_sec != sector) {
@@ -112,7 +112,7 @@ int SDCard_Init() {
void SDCard_deInit() {
//closing all open Files write back the cache and then shutdown em!
fatUnmount("SD:/");
-
+
fat_sd_mount = MOUNT_NONE;
fat_sd_sec = 0;
}
diff --git a/source/libfat/bit_ops.h b/source/libfat/bit_ops.h
new file mode 100644
index 00000000..8a5fb3e1
--- /dev/null
+++ b/source/libfat/bit_ops.h
@@ -0,0 +1,57 @@
+/*
+ bit_ops.h
+ Functions for dealing with conversion of data between types
+
+ 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.
+*/
+
+#ifndef __BIT_OPS_H
+#define __BIT_OPS_H
+
+#include
+
+/*-----------------------------------------------------------------
+Functions to deal with little endian values stored in uint8_t arrays
+-----------------------------------------------------------------*/
+static inline uint16_t u8array_to_u16 (const uint8_t* item, int offset) {
+ return ( item[offset] | (item[offset + 1] << 8));
+}
+
+static inline uint32_t u8array_to_u32 (const uint8_t* item, int offset) {
+ return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24));
+}
+
+static inline void u16_to_u8array (uint8_t* item, int offset, uint16_t value) {
+ item[offset] = (uint8_t) value;
+ item[offset + 1] = (uint8_t)(value >> 8);
+}
+
+static inline void u32_to_u8array (uint8_t* item, int offset, uint32_t value) {
+ item[offset] = (uint8_t) value;
+ item[offset + 1] = (uint8_t)(value >> 8);
+ item[offset + 2] = (uint8_t)(value >> 16);
+ item[offset + 3] = (uint8_t)(value >> 24);
+}
+
+#endif // _BIT_OPS_H
diff --git a/source/libfat/cache.c b/source/libfat/cache.c
new file mode 100644
index 00000000..94633f55
--- /dev/null
+++ b/source/libfat/cache.c
@@ -0,0 +1,366 @@
+/*
+ cache.c
+ The cache is not visible to the user. It should be flushed
+ when any file is closed or changes are made to the filesystem.
+
+ This cache implements a least-used-page replacement policy. This will
+ distribute sectors evenly over the pages, so if less than the maximum
+ pages are used at once, they should all eventually remain in the cache.
+ This also has the benefit of throwing out old sectors, so as not to keep
+ too many stale pages around.
+
+ 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.
+*/
+
+#include
+#include
+
+#include "common.h"
+#include "cache.h"
+#include "disc_fat.h"
+
+#include "mem_allocate.h"
+#include "bit_ops.h"
+#include "file_allocation_table.h"
+
+#define CACHE_FREE UINT_MAX
+
+CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition) {
+ CACHE* cache;
+ unsigned int i;
+ CACHE_ENTRY* cacheEntries;
+
+ if (numberOfPages < 2) {
+ numberOfPages = 2;
+ }
+
+ if (sectorsPerPage < 8) {
+ sectorsPerPage = 8;
+ }
+
+ cache = (CACHE*) _FAT_mem_allocate (sizeof(CACHE));
+ if (cache == NULL) {
+ return NULL;
+ }
+
+ cache->disc = discInterface;
+ cache->endOfPartition = endOfPartition;
+ cache->numberOfPages = numberOfPages;
+ cache->sectorsPerPage = sectorsPerPage;
+
+
+ cacheEntries = (CACHE_ENTRY*) _FAT_mem_allocate ( sizeof(CACHE_ENTRY) * numberOfPages);
+ if (cacheEntries == NULL) {
+ _FAT_mem_free (cache);
+ return NULL;
+ }
+
+ for (i = 0; i < numberOfPages; i++) {
+ cacheEntries[i].sector = CACHE_FREE;
+ cacheEntries[i].count = 0;
+ cacheEntries[i].last_access = 0;
+ cacheEntries[i].dirty = false;
+ cacheEntries[i].cache = (uint8_t*) _FAT_mem_align ( sectorsPerPage * BYTES_PER_READ );
+ }
+
+ cache->cacheEntries = cacheEntries;
+
+ return cache;
+}
+
+void _FAT_cache_destructor (CACHE* cache) {
+ unsigned int i;
+ // Clear out cache before destroying it
+ _FAT_cache_flush(cache);
+
+ // Free memory in reverse allocation order
+ for (i = 0; i < cache->numberOfPages; i++) {
+ _FAT_mem_free (cache->cacheEntries[i].cache);
+ }
+ _FAT_mem_free (cache->cacheEntries);
+ _FAT_mem_free (cache);
+}
+
+
+static u32 accessCounter = 0;
+
+static u32 accessTime(){
+ accessCounter++;
+ return accessCounter;
+}
+
+
+static CACHE_ENTRY* _FAT_cache_getPage(CACHE *cache,sec_t sector)
+{
+ unsigned int i;
+ CACHE_ENTRY* cacheEntries = cache->cacheEntries;
+ unsigned int numberOfPages = cache->numberOfPages;
+ unsigned int sectorsPerPage = cache->sectorsPerPage;
+
+ bool foundFree = false;
+ unsigned int oldUsed = 0;
+ unsigned int oldAccess = UINT_MAX;
+
+ for(i=0;i=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) {
+ cacheEntries[i].last_access = accessTime();
+ return &(cacheEntries[i]);
+ }
+
+ if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_accessdisc,cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL;
+ cacheEntries[oldUsed].dirty = false;
+ }
+
+ sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size
+ sec_t next_page = sector + sectorsPerPage;
+ if(next_page > cache->endOfPartition) next_page = cache->endOfPartition;
+
+ if(!_FAT_disc_readSectors(cache->disc,sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL;
+
+ cacheEntries[oldUsed].sector = sector;
+ cacheEntries[oldUsed].count = next_page-sector;
+ cacheEntries[oldUsed].last_access = accessTime();
+
+ return &(cacheEntries[oldUsed]);
+}
+
+bool _FAT_cache_readSectors(CACHE *cache,sec_t sector,sec_t numSectors,void *buffer)
+{
+ sec_t sec;
+ sec_t secs_to_read;
+ CACHE_ENTRY *entry;
+ uint8_t *dest = buffer;
+
+ while(numSectors>0) {
+ entry = _FAT_cache_getPage(cache,sector);
+ if(entry==NULL) return false;
+
+ sec = sector - entry->sector;
+ secs_to_read = entry->count - sec;
+ if(secs_to_read>numSectors) secs_to_read = numSectors;
+
+ memcpy(dest,entry->cache + (sec*BYTES_PER_READ),(secs_to_read*BYTES_PER_READ));
+
+ dest += (secs_to_read*BYTES_PER_READ);
+ sector += secs_to_read;
+ numSectors -= secs_to_read;
+ }
+
+ return true;
+}
+
+/*
+Reads some data from a cache page, determined by the sector number
+*/
+bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size)
+{
+ sec_t sec;
+ CACHE_ENTRY *entry;
+
+ if (offset + size > BYTES_PER_READ) return false;
+
+ entry = _FAT_cache_getPage(cache,sector);
+ if(entry==NULL) return false;
+
+ sec = sector - entry->sector;
+ memcpy(buffer,entry->cache + ((sec*BYTES_PER_READ) + offset),size);
+
+ return true;
+}
+
+bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) {
+ uint8_t buf[4];
+ if (!_FAT_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false;
+
+ switch(num_bytes) {
+ case 1: *value = buf[0]; break;
+ case 2: *value = u8array_to_u16(buf,0); break;
+ case 4: *value = u8array_to_u32(buf,0); break;
+ default: return false;
+ }
+ return true;
+}
+
+/*
+Writes some data to a cache page, making sure it is loaded into memory first.
+*/
+bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size)
+{
+ sec_t sec;
+ CACHE_ENTRY *entry;
+
+ if (offset + size > BYTES_PER_READ) return false;
+
+ entry = _FAT_cache_getPage(cache,sector);
+ if(entry==NULL) return false;
+
+ sec = sector - entry->sector;
+ memcpy(entry->cache + ((sec*BYTES_PER_READ) + offset),buffer,size);
+
+ entry->dirty = true;
+ return true;
+}
+
+bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) {
+ uint8_t buf[4] = {0, 0, 0, 0};
+
+ switch(size) {
+ case 1: buf[0] = value; break;
+ case 2: u16_to_u8array(buf, 0, value); break;
+ case 4: u32_to_u8array(buf, 0, value); break;
+ default: return false;
+ }
+
+ return _FAT_cache_writePartialSector(cache, buf, sector, offset, size);
+}
+
+/*
+Writes some data to a cache page, zeroing out the page first
+*/
+bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size)
+{
+ sec_t sec;
+ CACHE_ENTRY *entry;
+
+ if (offset + size > BYTES_PER_READ) return false;
+
+ entry = _FAT_cache_getPage(cache,sector);
+ if(entry==NULL) return false;
+
+ sec = sector - entry->sector;
+ memset(entry->cache + (sec*BYTES_PER_READ),0,BYTES_PER_READ);
+ memcpy(entry->cache + ((sec*BYTES_PER_READ) + offset),buffer,size);
+
+ entry->dirty = true;
+ return true;
+}
+
+
+static CACHE_ENTRY* _FAT_cache_findPage(CACHE *cache, sec_t sector, sec_t count) {
+
+ unsigned int i;
+ CACHE_ENTRY* cacheEntries = cache->cacheEntries;
+ unsigned int numberOfPages = cache->numberOfPages;
+ CACHE_ENTRY *entry = NULL;
+ sec_t lowest = UINT_MAX;
+
+ for(i=0;i cacheEntries[i].sector) {
+ intersect = sector - cacheEntries[i].sector < cacheEntries[i].count;
+ } else {
+ intersect = cacheEntries[i].sector - sector < count;
+ }
+
+ if ( intersect && (cacheEntries[i].sector < lowest)) {
+ lowest = cacheEntries[i].sector;
+ entry = &cacheEntries[i];
+ }
+ }
+ }
+
+ return entry;
+}
+
+bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer)
+{
+ sec_t sec;
+ sec_t secs_to_write;
+ CACHE_ENTRY* entry;
+ const uint8_t *src = buffer;
+
+ while(numSectors>0)
+ {
+ entry = _FAT_cache_findPage(cache,sector,numSectors);
+
+ if(entry!=NULL) {
+
+ if ( entry->sector > sector) {
+
+ secs_to_write = entry->sector - sector;
+
+ _FAT_disc_writeSectors(cache->disc,sector,secs_to_write,src);
+ src += (secs_to_write*BYTES_PER_READ);
+ sector += secs_to_write;
+ numSectors -= secs_to_write;
+ }
+
+ sec = sector - entry->sector;
+ secs_to_write = entry->count - sec;
+
+ if(secs_to_write>numSectors) secs_to_write = numSectors;
+
+ memcpy(entry->cache + (sec*BYTES_PER_READ),src,(secs_to_write*BYTES_PER_READ));
+
+ src += (secs_to_write*BYTES_PER_READ);
+ sector += secs_to_write;
+ numSectors -= secs_to_write;
+
+ entry->dirty = true;
+
+ } else {
+ _FAT_disc_writeSectors(cache->disc,sector,numSectors,src);
+ numSectors=0;
+ }
+ }
+ return true;
+}
+
+/*
+Flushes all dirty pages to disc, clearing the dirty flag.
+*/
+bool _FAT_cache_flush (CACHE* cache) {
+ unsigned int i;
+
+ for (i = 0; i < cache->numberOfPages; i++) {
+ if (cache->cacheEntries[i].dirty) {
+ if (!_FAT_disc_writeSectors (cache->disc, cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) {
+ return false;
+ }
+ }
+ cache->cacheEntries[i].dirty = false;
+ }
+
+ return true;
+}
+
+void _FAT_cache_invalidate (CACHE* cache) {
+ unsigned int i;
+ _FAT_cache_flush(cache);
+ for (i = 0; i < cache->numberOfPages; i++) {
+ cache->cacheEntries[i].sector = CACHE_FREE;
+ cache->cacheEntries[i].last_access = 0;
+ cache->cacheEntries[i].count = 0;
+ cache->cacheEntries[i].dirty = false;
+ }
+}
diff --git a/source/libfat/cache.h b/source/libfat/cache.h
new file mode 100644
index 00000000..ce754c6d
--- /dev/null
+++ b/source/libfat/cache.h
@@ -0,0 +1,130 @@
+/*
+ cache.h
+ The cache is not visible to the user. It should be flushed
+ when any file is closed or changes are made to the filesystem.
+
+ This cache implements a least-used-page replacement policy. This will
+ distribute sectors evenly over the pages, so if less than the maximum
+ pages are used at once, they should all eventually remain in the cache.
+ This also has the benefit of throwing out old sectors, so as not to keep
+ too many stale pages around.
+
+ 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.
+*/
+
+#ifndef __CACHE_H
+#define __CACHE_H
+
+#include "common.h"
+#include "disc_fat.h"
+
+#define PAGE_SECTORS 64
+#define CACHE_PAGE_SIZE (BYTES_PER_READ * PAGE_SECTORS)
+
+typedef struct {
+ sec_t sector;
+ unsigned int count;
+ unsigned int last_access;
+ bool dirty;
+ uint8_t* cache;
+} CACHE_ENTRY;
+
+typedef struct {
+ const DISC_INTERFACE* disc;
+ sec_t endOfPartition;
+ unsigned int numberOfPages;
+ unsigned int sectorsPerPage;
+ CACHE_ENTRY* cacheEntries;
+} CACHE;
+
+/*
+Read data from a sector in the cache
+If the sector is not in the cache, it will be swapped in
+offset is the position to start reading from
+size is the amount of data to read
+Precondition: offset + size <= BYTES_PER_READ
+*/
+bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size);
+
+bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes);
+
+/*
+Write data to a sector in the cache
+If the sector is not in the cache, it will be swapped in.
+When the sector is swapped out, the data will be written to the disc
+offset is the position to start writing to
+size is the amount of data to write
+Precondition: offset + size <= BYTES_PER_READ
+*/
+bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size);
+
+bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes);
+
+/*
+Write data to a sector in the cache, zeroing the sector first
+If the sector is not in the cache, it will be swapped in.
+When the sector is swapped out, the data will be written to the disc
+offset is the position to start writing to
+size is the amount of data to write
+Precondition: offset + size <= BYTES_PER_READ
+*/
+bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size);
+
+/*
+Read several sectors from the cache
+*/
+bool _FAT_cache_readSectors (CACHE* cache, sec_t sector, sec_t numSectors, void* buffer);
+
+/*
+Read a full sector from the cache
+*/
+static inline bool _FAT_cache_readSector (CACHE* cache, void* buffer, sec_t sector) {
+ return _FAT_cache_readPartialSector (cache, buffer, sector, 0, BYTES_PER_READ);
+}
+
+/*
+Write a full sector to the cache
+*/
+static inline bool _FAT_cache_writeSector (CACHE* cache, const void* buffer, sec_t sector) {
+ return _FAT_cache_writePartialSector (cache, buffer, sector, 0, BYTES_PER_READ);
+}
+
+bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer);
+
+/*
+Write any dirty sectors back to disc and clear out the contents of the cache
+*/
+bool _FAT_cache_flush (CACHE* cache);
+
+/*
+Clear out the contents of the cache without writing any dirty sectors first
+*/
+void _FAT_cache_invalidate (CACHE* cache);
+
+CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition);
+
+void _FAT_cache_destructor (CACHE* cache);
+
+#endif // _CACHE_H
+
diff --git a/source/libfat/common.h b/source/libfat/common.h
new file mode 100644
index 00000000..85d15db9
--- /dev/null
+++ b/source/libfat/common.h
@@ -0,0 +1,48 @@
+/*
+ common.h
+ Common definitions and included files for the FATlib
+
+ 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.
+*/
+
+#ifndef __COMMON_H
+#define __COMMON_H
+
+#define BYTES_PER_READ 512
+#include "fat.h"
+#include
+#include
+
+
+// Platform specific includes
+#include
+#include
+#include
+// Platform specific options
+#define DEFAULT_CACHE_PAGES 4
+#define DEFAULT_SECTORS_PAGE 64
+#define USE_LWP_LOCK
+#define USE_RTC_TIME
+
+#endif // _COMMON_H
diff --git a/source/libfat/directory.c b/source/libfat/directory.c
new file mode 100644
index 00000000..279a361d
--- /dev/null
+++ b/source/libfat/directory.c
@@ -0,0 +1,1075 @@
+/*
+ directory.c
+ Reading, writing and manipulation of the directory 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.
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "directory.h"
+#include "common.h"
+#include "partition.h"
+#include "file_allocation_table.h"
+#include "bit_ops.h"
+#include "filetime.h"
+
+// Directory entry codes
+#define DIR_ENTRY_LAST 0x00
+#define DIR_ENTRY_FREE 0xE5
+
+#define LAST_LFN_POS (19*13)
+#define LAST_LFN_POS_CORRECTION (MAX_LFN_LENGTH-15)
+
+typedef unsigned short ucs2_t;
+
+// Long file name directory entry
+enum LFN_offset {
+ LFN_offset_ordinal = 0x00, // Position within LFN
+ LFN_offset_char0 = 0x01,
+ LFN_offset_char1 = 0x03,
+ LFN_offset_char2 = 0x05,
+ LFN_offset_char3 = 0x07,
+ LFN_offset_char4 = 0x09,
+ LFN_offset_flag = 0x0B, // Should be equal to ATTRIB_LFN
+ LFN_offset_reserved1 = 0x0C, // Always 0x00
+ LFN_offset_checkSum = 0x0D, // Checksum of short file name (alias)
+ LFN_offset_char5 = 0x0E,
+ LFN_offset_char6 = 0x10,
+ LFN_offset_char7 = 0x12,
+ LFN_offset_char8 = 0x14,
+ LFN_offset_char9 = 0x16,
+ LFN_offset_char10 = 0x18,
+ LFN_offset_reserved2 = 0x1A, // Always 0x0000
+ LFN_offset_char11 = 0x1C,
+ LFN_offset_char12 = 0x1E
+};
+static const int LFN_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};
+
+#define LFN_END 0x40
+#define LFN_DEL 0x80
+
+static const char ILLEGAL_ALIAS_CHARACTERS[] = "\\/:;*?\"<>|&+,=[] ";
+static const char ILLEGAL_LFN_CHARACTERS[] = "\\/:*?\"<>|";
+
+/*
+Returns number of UCS-2 characters needed to encode an LFN
+Returns -1 if it is an invalid LFN
+*/
+#define ABOVE_UCS_RANGE 0xF0
+static int _FAT_directory_lfnLength (const char* name) {
+ unsigned int i;
+ size_t nameLength;
+ int ucsLength;
+ const char* tempName = name;
+
+ nameLength = strnlen(name, MAX_FILENAME_LENGTH);
+ // Make sure the name is short enough to be valid
+ if ( nameLength >= MAX_FILENAME_LENGTH) {
+ return -1;
+ }
+ // Make sure it doesn't contain any invalid characters
+ if (strpbrk (name, ILLEGAL_LFN_CHARACTERS) != NULL) {
+ return -1;
+ }
+ // Make sure the name doesn't contain any control codes or codes not representable in UCS-2
+ for (i = 0; i < nameLength; i++) {
+ if (name[i] < 0x20 || name[i] >= ABOVE_UCS_RANGE) {
+ return -1;
+ }
+ }
+ // Convert to UCS-2 and get the resulting length
+ ucsLength = mbsrtowcs(NULL, &tempName, MAX_LFN_LENGTH, NULL);
+ if (ucsLength < 0 || ucsLength >= MAX_LFN_LENGTH) {
+ return -1;
+ }
+
+ // Otherwise it is valid
+ return ucsLength;
+}
+
+/*
+Convert a multibyte encoded string into a NUL-terminated UCS-2 string, storing at most len characters
+return number of characters stored
+*/
+static size_t _FAT_directory_mbstoucs2 (ucs2_t* dst, const char* src, size_t len) {
+ mbstate_t ps = {0};
+ wchar_t tempChar;
+ int bytes;
+ size_t count = 0;
+
+ while (count < len-1 && src != '\0') {
+ bytes = mbrtowc (&tempChar, src, MB_CUR_MAX, &ps);
+ if (bytes > 0) {
+ *dst = (ucs2_t)tempChar;
+ src += bytes;
+ dst++;
+ count++;
+ } else if (bytes == 0) {
+ break;
+ } else {
+ return -1;
+ }
+ }
+ *dst = '\0';
+
+ return count;
+}
+
+/*
+Convert a UCS-2 string into a NUL-terminated multibyte string, storing at most len chars
+return number of chars stored, or (size_t)-1 on error
+*/
+static size_t _FAT_directory_ucs2tombs (char* dst, const ucs2_t* src, size_t len) {
+ mbstate_t ps = {0};
+ size_t count = 0;
+ int bytes;
+ char buff[MB_CUR_MAX];
+ int i;
+
+ while (count < len - 1 && *src != '\0') {
+ bytes = wcrtomb (buff, *src, &ps);
+ if (bytes < 0) {
+ return -1;
+ }
+ if (count + bytes < len && bytes > 0) {
+ for (i = 0; i < bytes; i++) {
+ *dst++ = buff[i];
+ }
+ src++;
+ count += bytes;
+ } else {
+ break;
+ }
+ }
+ *dst = L'\0';
+
+ return count;
+}
+
+/*
+Case-independent comparison of two multibyte encoded strings
+*/
+static int _FAT_directory_mbsncasecmp (const char* s1, const char* s2, size_t len1) {
+ wchar_t wc1, wc2;
+ mbstate_t ps1 = {0};
+ mbstate_t ps2 = {0};
+ size_t b1 = 0;
+ size_t b2 = 0;
+
+ if (len1 == 0) {
+ return 0;
+ }
+
+ do {
+ s1 += b1;
+ s2 += b2;
+ b1 = mbrtowc(&wc1, s1, MB_CUR_MAX, &ps1);
+ b2 = mbrtowc(&wc2, s2, MB_CUR_MAX, &ps2);
+ if ((int)b1 < 0 || (int)b2 < 0) {
+ break;
+ }
+ len1 -= b1;
+ } while (len1 > 0 && towlower(wc1) == towlower(wc2) && wc1 != 0);
+
+ return towlower(wc1) - towlower(wc2);
+}
+
+
+static bool _FAT_directory_entryGetAlias (const u8* entryData, char* destName) {
+ int i=0;
+ int j=0;
+
+ destName[0] = '\0';
+ if (entryData[0] != DIR_ENTRY_FREE) {
+ if (entryData[0] == '.') {
+ destName[0] = '.';
+ if (entryData[1] == '.') {
+ destName[1] = '.';
+ destName[2] = '\0';
+ } else {
+ destName[1] = '\0';
+ }
+ } else {
+ // Copy the filename from the dirEntry to the string
+ for (i = 0; (i < 8) && (entryData[DIR_ENTRY_name + i] != ' '); i++) {
+ destName[i] = entryData[DIR_ENTRY_name + i];
+ }
+ // Copy the extension from the dirEntry to the string
+ if (entryData[DIR_ENTRY_extension] != ' ') {
+ destName[i++] = '.';
+ for ( j = 0; (j < 3) && (entryData[DIR_ENTRY_extension + j] != ' '); j++) {
+ destName[i++] = entryData[DIR_ENTRY_extension + j];
+ }
+ }
+ destName[i] = '\0';
+ }
+ }
+
+ return (destName[0] != '\0');
+}
+
+uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData) {
+ if (partition->filesysType == FS_FAT32) {
+ // Only use high 16 bits of start cluster when we are certain they are correctly defined
+ return u8array_to_u16(entryData,DIR_ENTRY_cluster) | (u8array_to_u16(entryData, DIR_ENTRY_clusterHigh) << 16);
+ } else {
+ return u8array_to_u16(entryData,DIR_ENTRY_cluster);
+ }
+}
+
+static bool _FAT_directory_incrementDirEntryPosition (PARTITION* partition, DIR_ENTRY_POSITION* entryPosition, bool extendDirectory) {
+ DIR_ENTRY_POSITION position = *entryPosition;
+ uint32_t tempCluster;
+
+ // Increment offset, wrapping at the end of a sector
+ ++ position.offset;
+ if (position.offset == BYTES_PER_READ / DIR_ENTRY_DATA_SIZE) {
+ position.offset = 0;
+ // Increment sector when wrapping
+ ++ position.sector;
+ // But wrap at the end of a cluster
+ if ((position.sector == partition->sectorsPerCluster) && (position.cluster != FAT16_ROOT_DIR_CLUSTER)) {
+ position.sector = 0;
+ // Move onto the next cluster, making sure there is another cluster to go to
+ tempCluster = _FAT_fat_nextCluster(partition, position.cluster);
+ if (tempCluster == CLUSTER_EOF) {
+ if (extendDirectory) {
+ tempCluster = _FAT_fat_linkFreeClusterCleared (partition, position.cluster);
+ if (!_FAT_fat_isValidCluster(partition, tempCluster)) {
+ return false; // This will only happen if the disc is full
+ }
+ } else {
+ return false; // Got to the end of the directory, not extending it
+ }
+ }
+ position.cluster = tempCluster;
+ } else if ((position.cluster == FAT16_ROOT_DIR_CLUSTER) && (position.sector == (partition->dataStart - partition->rootDirStart))) {
+ return false; // Got to end of root directory, can't extend it
+ }
+ }
+ *entryPosition = position;
+ return true;
+}
+
+bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry) {
+ DIR_ENTRY_POSITION entryStart;
+ DIR_ENTRY_POSITION entryEnd;
+ uint8_t entryData[0x20];
+ ucs2_t lfn[MAX_LFN_LENGTH];
+ bool notFound, found;
+ int lfnPos;
+ uint8_t lfnChkSum, chkSum;
+ bool lfnExists;
+ int i;
+
+ lfnChkSum = 0;
+
+ entryStart = entry->dataEnd;
+
+ // Make sure we are using the correct root directory, in case of FAT32
+ if (entryStart.cluster == FAT16_ROOT_DIR_CLUSTER) {
+ entryStart.cluster = partition->rootDirCluster;
+ }
+
+ entryEnd = entryStart;
+
+ lfnExists = false;
+
+ found = false;
+ notFound = false;
+
+ while (!found && !notFound) {
+ if (_FAT_directory_incrementDirEntryPosition (partition, &entryEnd, false) == false) {
+ notFound = true;
+ }
+
+ _FAT_cache_readPartialSector (partition->cache, entryData,
+ _FAT_fat_clusterToSector(partition, entryEnd.cluster) + entryEnd.sector,
+ entryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
+
+ if (entryData[DIR_ENTRY_attributes] == ATTRIB_LFN) {
+ // It's an LFN
+ if (entryData[LFN_offset_ordinal] & LFN_DEL) {
+ lfnExists = false;
+ } else if (entryData[LFN_offset_ordinal] & LFN_END) {
+ // Last part of LFN, make sure it isn't deleted using previous if(Thanks MoonLight)
+ entryStart = entryEnd; // This is the start of a directory entry
+ lfnExists = true;
+ lfnPos = (entryData[LFN_offset_ordinal] & ~LFN_END) * 13;
+ if (lfnPos > MAX_LFN_LENGTH - 1) {
+ lfnPos = MAX_LFN_LENGTH - 1;
+ }
+ lfn[lfnPos] = '\0'; // Set end of lfn to null character
+ lfnChkSum = entryData[LFN_offset_checkSum];
+ } if (lfnChkSum != entryData[LFN_offset_checkSum]) {
+ lfnExists = false;
+ }
+ if (lfnExists) {
+ lfnPos = ((entryData[LFN_offset_ordinal] & ~LFN_END) - 1) * 13;
+ if (lfnPos > LAST_LFN_POS) {
+ // Force it within the buffer. Will corrupt the filename but prevent buffer overflows
+ lfnPos = LAST_LFN_POS;
+ }
+ for (i = 0; i < 13; i++) {
+ lfn[lfnPos + i] = entryData[LFN_offset_table[i]] | (entryData[LFN_offset_table[i]+1] << 8);
+ }
+ }
+ } else if (entryData[DIR_ENTRY_attributes] & ATTRIB_VOL) {
+ // This is a volume name, don't bother with it
+ } else if (entryData[0] == DIR_ENTRY_LAST) {
+ notFound = true;
+ } else if ((entryData[0] != DIR_ENTRY_FREE) && (entryData[0] > 0x20) && !(entryData[DIR_ENTRY_attributes] & ATTRIB_VOL)) {
+ if (lfnExists) {
+ // Calculate file checksum
+ chkSum = 0;
+ for (i=0; i < 11; i++) {
+ // NOTE: The operation is an unsigned char rotate right
+ chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + entryData[i];
+ }
+ if (chkSum != lfnChkSum) {
+ lfnExists = false;
+ entry->filename[0] = '\0';
+ }
+ }
+
+ if (lfnExists) {
+ if (_FAT_directory_ucs2tombs (entry->filename, lfn, MAX_FILENAME_LENGTH) == (size_t)-1) {
+ // Failed to convert the file name to UTF-8. Maybe the wrong locale is set?
+ return false;
+ }
+ } else {
+ entryStart = entryEnd;
+ _FAT_directory_entryGetAlias (entryData, entry->filename);
+ }
+ found = true;
+ }
+ }
+
+ // If no file is found, return false
+ if (notFound) {
+ return false;
+ } else {
+ // Fill in the directory entry struct
+ entry->dataStart = entryStart;
+ entry->dataEnd = entryEnd;
+ memcpy (entry->entryData, entryData, DIR_ENTRY_DATA_SIZE);
+ return true;
+ }
+}
+
+bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster) {
+ entry->dataStart.cluster = dirCluster;
+ entry->dataStart.sector = 0;
+ entry->dataStart.offset = -1; // Start before the beginning of the directory
+
+ entry->dataEnd = entry->dataStart;
+
+ return _FAT_directory_getNextEntry (partition, entry);
+}
+
+bool _FAT_directory_getRootEntry (PARTITION* partition, DIR_ENTRY* entry) {
+ entry->dataStart.cluster = 0;
+ entry->dataStart.sector = 0;
+ entry->dataStart.offset = 0;
+
+ entry->dataEnd = entry->dataStart;
+
+ memset (entry->filename, '\0', MAX_FILENAME_LENGTH);
+ entry->filename[0] = '.';
+
+ memset (entry->entryData, 0, DIR_ENTRY_DATA_SIZE);
+ memset (entry->entryData, ' ', 11);
+ entry->entryData[0] = '.';
+
+ entry->entryData[DIR_ENTRY_attributes] = ATTRIB_DIR;
+
+ u16_to_u8array (entry->entryData, DIR_ENTRY_cluster, partition->rootDirCluster);
+ u16_to_u8array (entry->entryData, DIR_ENTRY_clusterHigh, partition->rootDirCluster >> 16);
+
+ return true;
+}
+
+bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry) {
+ DIR_ENTRY_POSITION entryStart = entry->dataStart;
+ DIR_ENTRY_POSITION entryEnd = entry->dataEnd;
+ bool entryStillValid;
+ bool finished;
+ ucs2_t lfn[MAX_LFN_LENGTH];
+ int i;
+ int lfnPos;
+ uint8_t entryData[DIR_ENTRY_DATA_SIZE];
+
+ memset (entry->filename, '\0', MAX_FILENAME_LENGTH);
+
+ // Create an empty directory entry to overwrite the old ones with
+ for ( entryStillValid = true, finished = false;
+ entryStillValid && !finished;
+ entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &entryStart, false))
+ {
+ _FAT_cache_readPartialSector (partition->cache, entryData,
+ _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector,
+ entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
+
+ if ((entryStart.cluster == entryEnd.cluster)
+ && (entryStart.sector == entryEnd.sector)
+ && (entryStart.offset == entryEnd.offset)) {
+ // Copy the entry data and stop, since this is the last section of the directory entry
+ memcpy (entry->entryData, entryData, DIR_ENTRY_DATA_SIZE);
+ finished = true;
+ } else {
+ // Copy the long file name data
+ lfnPos = ((entryData[LFN_offset_ordinal] & ~LFN_END) - 1) * 13;
+ if (lfnPos > LAST_LFN_POS) {
+ lfnPos = LAST_LFN_POS_CORRECTION;
+ }
+ for (i = 0; i < 13; i++) {
+ lfn[lfnPos + i] = entryData[LFN_offset_table[i]] | (entryData[LFN_offset_table[i]+1] << 8);
+ }
+ }
+ }
+
+ if (!entryStillValid) {
+ return false;
+ }
+
+ if ((entryStart.cluster == entryEnd.cluster)
+ && (entryStart.sector == entryEnd.sector)
+ && (entryStart.offset == entryEnd.offset)) {
+ // Since the entry doesn't have a long file name, extract the short filename
+ if (!_FAT_directory_entryGetAlias (entry->entryData, entry->filename)) {
+ return false;
+ }
+ } else {
+ // Encode the long file name into a multibyte string
+ if (_FAT_directory_ucs2tombs (entry->filename, lfn, MAX_FILENAME_LENGTH) == (size_t)-1) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd) {
+ size_t dirnameLength;
+ const char* pathPosition;
+ const char* nextPathPosition;
+ uint32_t dirCluster;
+ bool foundFile;
+ char alias[MAX_ALIAS_LENGTH];
+ bool found, notFound;
+
+ pathPosition = path;
+
+ found = false;
+ notFound = false;
+
+ if (pathEnd == NULL) {
+ // Set pathEnd to the end of the path string
+ pathEnd = strchr (path, '\0');
+ }
+
+ if (pathPosition[0] == DIR_SEPARATOR) {
+ // Start at root directory
+ dirCluster = partition->rootDirCluster;
+ // Consume separator(s)
+ while (pathPosition[0] == DIR_SEPARATOR) {
+ pathPosition++;
+ }
+ // If the path is only specifying a directory in the form of "/" return it
+ if (pathPosition >= pathEnd) {
+ _FAT_directory_getRootEntry (partition, entry);
+ found = true;
+ }
+ } else {
+ // Start in current working directory
+ dirCluster = partition->cwdCluster;
+ }
+
+ // If the path is only specifying a directory in the form "."
+ // and this is the root directory, return it
+ if ((dirCluster == partition->rootDirCluster) && (strcmp(".", pathPosition) == 0)) {
+ _FAT_directory_getRootEntry (partition, entry);
+ found = true;
+ }
+
+ while (!found && !notFound) {
+ // Get the name of the next required subdirectory within the path
+ nextPathPosition = strchr (pathPosition, DIR_SEPARATOR);
+ if (nextPathPosition != NULL) {
+ dirnameLength = nextPathPosition - pathPosition;
+ } else {
+ dirnameLength = strlen(pathPosition);
+ }
+
+ if (dirnameLength > MAX_FILENAME_LENGTH) {
+ // The path is too long to bother with
+ return false;
+ }
+
+ // Look for the directory within the path
+ foundFile = _FAT_directory_getFirstEntry (partition, entry, dirCluster);
+
+ while (foundFile && !found && !notFound) { // It hasn't already found the file
+ // Check if the filename matches
+ if ((dirnameLength == strnlen(entry->filename, MAX_FILENAME_LENGTH))
+ && (_FAT_directory_mbsncasecmp(pathPosition, entry->filename, dirnameLength) == 0)) {
+ found = true;
+ }
+
+ // Check if the alias matches
+ _FAT_directory_entryGetAlias (entry->entryData, alias);
+ if ((dirnameLength == strnlen(alias, MAX_ALIAS_LENGTH))
+ && (strncasecmp(pathPosition, alias, dirnameLength) == 0)) {
+ found = true;
+ }
+
+ if (found && !(entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) && (nextPathPosition != NULL)) {
+ // Make sure that we aren't trying to follow a file instead of a directory in the path
+ found = false;
+ }
+
+ if (!found) {
+ foundFile = _FAT_directory_getNextEntry (partition, entry);
+ }
+ }
+
+ if (!foundFile) {
+ // Check that the search didn't get to the end of the directory
+ notFound = true;
+ found = false;
+ } else if ((nextPathPosition == NULL) || (nextPathPosition >= pathEnd)) {
+ // Check that we reached the end of the path
+ found = true;
+ } else if (entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) {
+ dirCluster = _FAT_directory_entryGetCluster (partition, entry->entryData);
+ pathPosition = nextPathPosition;
+ // Consume separator(s)
+ while (pathPosition[0] == DIR_SEPARATOR) {
+ pathPosition++;
+ }
+ // The requested directory was found
+ if (pathPosition >= pathEnd) {
+ found = true;
+ } else {
+ found = false;
+ }
+ }
+ }
+
+ if (found && !notFound) {
+ if (partition->filesysType == FS_FAT32 && (entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) &&
+ _FAT_directory_entryGetCluster (partition, entry->entryData) == CLUSTER_ROOT)
+ {
+ // On FAT32 it should specify an actual cluster for the root entry,
+ // not cluster 0 as on FAT16
+ _FAT_directory_getRootEntry (partition, entry);
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry) {
+ DIR_ENTRY_POSITION entryStart = entry->dataStart;
+ DIR_ENTRY_POSITION entryEnd = entry->dataEnd;
+ bool entryStillValid;
+ bool finished;
+ uint8_t entryData[DIR_ENTRY_DATA_SIZE];
+
+ // Create an empty directory entry to overwrite the old ones with
+ for ( entryStillValid = true, finished = false;
+ entryStillValid && !finished;
+ entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &entryStart, false))
+ {
+ _FAT_cache_readPartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
+ entryData[0] = DIR_ENTRY_FREE;
+ _FAT_cache_writePartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
+ if ((entryStart.cluster == entryEnd.cluster) && (entryStart.sector == entryEnd.sector) && (entryStart.offset == entryEnd.offset)) {
+ finished = true;
+ }
+ }
+
+ if (!entryStillValid) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool _FAT_directory_findEntryGap (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster, size_t size) {
+ DIR_ENTRY_POSITION gapStart;
+ DIR_ENTRY_POSITION gapEnd;
+ uint8_t entryData[DIR_ENTRY_DATA_SIZE];
+ size_t dirEntryRemain;
+ bool endOfDirectory, entryStillValid;
+
+ // Scan Dir for free entry
+ gapEnd.offset = 0;
+ gapEnd.sector = 0;
+ gapEnd.cluster = dirCluster;
+
+ gapStart = gapEnd;
+
+ entryStillValid = true;
+ dirEntryRemain = size;
+ endOfDirectory = false;
+
+ while (entryStillValid && !endOfDirectory && (dirEntryRemain > 0)) {
+ _FAT_cache_readPartialSector (partition->cache, entryData,
+ _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector,
+ gapEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
+ if (entryData[0] == DIR_ENTRY_LAST) {
+ gapStart = gapEnd;
+ -- dirEntryRemain;
+ endOfDirectory = true;
+ } else if (entryData[0] == DIR_ENTRY_FREE) {
+ if (dirEntryRemain == size) {
+ gapStart = gapEnd;
+ }
+ -- dirEntryRemain;
+ } else {
+ dirEntryRemain = size;
+ }
+
+ if (!endOfDirectory && (dirEntryRemain > 0)) {
+ entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &gapEnd, true);
+ }
+ }
+
+ // Make sure the scanning didn't fail
+ if (!entryStillValid) {
+ return false;
+ }
+
+ // Save the start entry, since we know it is valid
+ entry->dataStart = gapStart;
+
+ if (endOfDirectory) {
+ memset (entryData, DIR_ENTRY_LAST, DIR_ENTRY_DATA_SIZE);
+ dirEntryRemain += 1; // Increase by one to take account of End Of Directory Marker
+ while ((dirEntryRemain > 0) && entryStillValid) {
+ // Get the gapEnd before incrementing it, so the second to last one is saved
+ entry->dataEnd = gapEnd;
+ // Increment gapEnd, moving onto the next entry
+ entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &gapEnd, true);
+ -- dirEntryRemain;
+ // Fill the entry with blanks
+ _FAT_cache_writePartialSector (partition->cache, entryData,
+ _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector,
+ gapEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
+ }
+ if (!entryStillValid) {
+ return false;
+ }
+ } else {
+ entry->dataEnd = gapEnd;
+ }
+
+ return true;
+}
+
+static bool _FAT_directory_entryExists (PARTITION* partition, const char* name, uint32_t dirCluster) {
+ DIR_ENTRY tempEntry;
+ bool foundFile;
+ char alias[MAX_ALIAS_LENGTH];
+ size_t dirnameLength;
+
+ dirnameLength = strnlen(name, MAX_FILENAME_LENGTH);
+
+ if (dirnameLength >= MAX_FILENAME_LENGTH) {
+ return false;
+ }
+
+ // Make sure the entry doesn't already exist
+ foundFile = _FAT_directory_getFirstEntry (partition, &tempEntry, dirCluster);
+
+ while (foundFile) { // It hasn't already found the file
+ // Check if the filename matches
+ if ((dirnameLength == strnlen(tempEntry.filename, MAX_FILENAME_LENGTH))
+ && (_FAT_directory_mbsncasecmp(name, tempEntry.filename, dirnameLength) == 0)) {
+ return true;
+ }
+
+ // Check if the alias matches
+ _FAT_directory_entryGetAlias (tempEntry.entryData, alias);
+ if ((strncasecmp(name, alias, MAX_ALIAS_LENGTH) == 0)) {
+ return true;
+ }
+ foundFile = _FAT_directory_getNextEntry (partition, &tempEntry);
+ }
+ return false;
+}
+
+/*
+Creates an alias for a long file name. If the alias is not an exact match for the
+filename, it returns the number of characters in the alias. If the two names match,
+it returns 0. If there was an error, it returns -1.
+*/
+static int _FAT_directory_createAlias (char* alias, const char* lfn) {
+ bool lossyConversion = false; // Set when the alias had to be modified to be valid
+ int lfnPos = 0;
+ int aliasPos = 0;
+ wchar_t lfnChar;
+ int oemChar;
+ mbstate_t ps = {0};
+ int bytesUsed = 0;
+ const char* lfnExt;
+ int aliasExtLen;
+
+ // Strip leading periods
+ while (lfn[lfnPos] == '.') {
+ lfnPos ++;
+ lossyConversion = true;
+ }
+
+ // Primary portion of alias
+ while (aliasPos < 8 && lfn[lfnPos] != '.' && lfn[lfnPos] != '\0') {
+ bytesUsed = mbrtowc(&lfnChar, lfn + lfnPos, MAX_FILENAME_LENGTH - lfnPos, &ps);
+ if (bytesUsed < 0) {
+ return -1;
+ }
+ oemChar = wctob(towupper((wint_t)lfnChar));
+ if (wctob((wint_t)lfnChar) != oemChar) {
+ // Case of letter was changed
+ lossyConversion = true;
+ }
+ if (oemChar == ' ') {
+ // Skip spaces in filename
+ lossyConversion = true;
+ lfnPos += bytesUsed;
+ continue;
+ }
+ if (oemChar == EOF) {
+ oemChar = '_'; // Replace unconvertable characters with underscores
+ lossyConversion = true;
+ }
+ if (strchr (ILLEGAL_ALIAS_CHARACTERS, oemChar) != NULL) {
+ // Invalid Alias character
+ oemChar = '_'; // Replace illegal characters with underscores
+ lossyConversion = true;
+ }
+
+ alias[aliasPos] = (char)oemChar;
+ aliasPos++;
+ lfnPos += bytesUsed;
+ }
+
+ if (lfn[lfnPos] != '.' && lfn[lfnPos] != '\0') {
+ // Name was more than 8 characters long
+ lossyConversion = true;
+ }
+
+ // Alias extension
+ lfnExt = strrchr (lfn, '.');
+ if (lfnExt != NULL && lfnExt != strchr (lfn, '.')) {
+ // More than one period in name
+ lossyConversion = true;
+ }
+ if (lfnExt != NULL && lfnExt[1] != '\0') {
+ lfnExt++;
+ alias[aliasPos] = '.';
+ aliasPos++;
+ memset (&ps, 0, sizeof(ps));
+ for (aliasExtLen = 0; aliasExtLen < MAX_ALIAS_EXT_LENGTH && *lfnExt != '\0'; aliasExtLen++) {
+ bytesUsed = mbrtowc(&lfnChar, lfnExt, MAX_FILENAME_LENGTH - lfnPos, &ps);
+ if (bytesUsed < 0) {
+ return -1;
+ }
+ oemChar = wctob(towupper((wint_t)lfnChar));
+ if (wctob((wint_t)lfnChar) != oemChar) {
+ // Case of letter was changed
+ lossyConversion = true;
+ }
+ if (oemChar == ' ') {
+ // Skip spaces in alias
+ lossyConversion = true;
+ lfnExt += bytesUsed;
+ continue;
+ }
+ if (oemChar == EOF) {
+ oemChar = '_'; // Replace unconvertable characters with underscores
+ lossyConversion = true;
+ }
+ if (strchr (ILLEGAL_ALIAS_CHARACTERS, oemChar) != NULL) {
+ // Invalid Alias character
+ oemChar = '_'; // Replace illegal characters with underscores
+ lossyConversion = true;
+ }
+
+ alias[aliasPos] = (char)oemChar;
+ aliasPos++;
+ lfnExt += bytesUsed;
+ }
+ if (*lfnExt != '\0') {
+ // Extension was more than 3 characters long
+ lossyConversion = true;
+ }
+ }
+
+ alias[aliasPos] = '\0';
+ if (lossyConversion) {
+ return aliasPos;
+ } else {
+ return 0;
+ }
+}
+
+bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster) {
+ size_t entrySize;
+ uint8_t lfnEntry[DIR_ENTRY_DATA_SIZE];
+ int i,j; // Must be signed for use when decrementing in for loop
+ char *tmpCharPtr;
+ DIR_ENTRY_POSITION curEntryPos;
+ bool entryStillValid;
+ uint8_t aliasCheckSum = 0;
+ char alias [MAX_ALIAS_LENGTH];
+ int aliasLen;
+ int lfnLen;
+
+ // Make sure the filename is not 0 length
+ if (strnlen (entry->filename, MAX_FILENAME_LENGTH) < 1) {
+ return false;
+ }
+
+ // Make sure the filename is at least a valid LFN
+ lfnLen = _FAT_directory_lfnLength (entry->filename);
+ if (lfnLen < 0) {
+ return false;
+ }
+
+ // Remove trailing spaces
+ for (i = strlen (entry->filename) - 1; (i > 0) && (entry->filename[i] == ' '); --i) {
+ entry->filename[i] = '\0';
+ }
+ // Remove leading spaces
+ for (i = 0; (i < (int)strlen (entry->filename)) && (entry->filename[i] == ' '); ++i) ;
+ if (i > 0) {
+ memmove (entry->filename, entry->filename + i, strlen (entry->filename + i));
+ }
+
+ // Remove junk in filename
+ i = strlen (entry->filename);
+ memset (entry->filename + i, '\0', MAX_FILENAME_LENGTH - i);
+
+ // Make sure the entry doesn't already exist
+ if (_FAT_directory_entryExists (partition, entry->filename, dirCluster)) {
+ return false;
+ }
+
+ // Clear out alias, so we can generate a new one
+ memset (entry->entryData, ' ', 11);
+
+ if ( strncmp(entry->filename, ".", MAX_FILENAME_LENGTH) == 0) {
+ // "." entry
+ entry->entryData[0] = '.';
+ entrySize = 1;
+ } else if ( strncmp(entry->filename, "..", MAX_FILENAME_LENGTH) == 0) {
+ // ".." entry
+ entry->entryData[0] = '.';
+ entry->entryData[1] = '.';
+ entrySize = 1;
+ } else {
+ // Normal file name
+ aliasLen = _FAT_directory_createAlias (alias, entry->filename);
+ if (aliasLen < 0) {
+ return false;
+ } else if (aliasLen == 0) {
+ // It's a normal short filename
+ entrySize = 1;
+ } else {
+ // It's a long filename with an alias
+ entrySize = ((lfnLen + LFN_ENTRY_LENGTH - 1) / LFN_ENTRY_LENGTH) + 1;
+
+ // Generate full alias for all cases except when the alias is simply an upper case version of the LFN
+ // and there isn't already a file with that name
+ if (strncasecmp (alias, entry->filename, MAX_ALIAS_LENGTH) != 0 ||
+ _FAT_directory_entryExists (partition, alias, dirCluster))
+ {
+ // expand primary part to 8 characters long by padding the end with underscores
+ i = MAX_ALIAS_PRI_LENGTH - 1;
+ // Move extension to last 3 characters
+ while (alias[i] != '.' && i > 0) i--;
+ if (i > 0) {
+ j = MAX_ALIAS_LENGTH - MAX_ALIAS_EXT_LENGTH - 2; // 1 char for '.', one for NUL, 3 for extension
+ memmove (alias + j, alias + i, strlen(alias) - i);
+ // Pad primary component
+ memset (alias + i, '_', j - i);
+ alias[MAX_ALIAS_LENGTH-1]=0;
+ }
+
+ // Generate numeric tail
+ for (i = 1; i <= MAX_NUMERIC_TAIL; i++) {
+ j = i;
+ tmpCharPtr = alias + MAX_ALIAS_PRI_LENGTH - 1;
+ while (j > 0) {
+ *tmpCharPtr = '0' + (j % 10); // ASCII numeric value
+ tmpCharPtr--;
+ j /= 10;
+ }
+ *tmpCharPtr = '~';
+ if (!_FAT_directory_entryExists (partition, alias, dirCluster)) {
+ break;
+ }
+ }
+ if (i > MAX_NUMERIC_TAIL) {
+ // Couldn't get a valid alias
+ return false;
+ }
+ }
+ }
+
+ // Copy alias or short file name into directory entry data
+ for (i = 0, j = 0; (j < 8) && (alias[i] != '.') && (alias[i] != '\0'); i++, j++) {
+ entry->entryData[j] = alias[i];
+ }
+ while (j < 8) {
+ entry->entryData[j] = ' ';
+ ++ j;
+ }
+ if (alias[i] == '.') {
+ // Copy extension
+ ++ i;
+ while ((alias[i] != '\0') && (j < 11)) {
+ entry->entryData[j] = alias[i];
+ ++ i;
+ ++ j;
+ }
+ }
+ while (j < 11) {
+ entry->entryData[j] = ' ';
+ ++ j;
+ }
+
+ // Generate alias checksum
+ for (i=0; i < ALIAS_ENTRY_LENGTH; i++) {
+ // NOTE: The operation is an unsigned char rotate right
+ aliasCheckSum = ((aliasCheckSum & 1) ? 0x80 : 0) + (aliasCheckSum >> 1) + entry->entryData[i];
+ }
+ }
+
+ // Find or create space for the entry
+ if (_FAT_directory_findEntryGap (partition, entry, dirCluster, entrySize) == false) {
+ return false;
+ }
+
+ // Write out directory entry
+ curEntryPos = entry->dataStart;
+
+ {
+ // lfn is only pushed onto the stack here, reducing overall stack usage
+ ucs2_t lfn[MAX_LFN_LENGTH] = {0};
+ _FAT_directory_mbstoucs2 (lfn, entry->filename, MAX_LFN_LENGTH);
+
+ for (entryStillValid = true, i = entrySize; entryStillValid && i > 0;
+ entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &curEntryPos, false), -- i )
+ {
+ if (i > 1) {
+ // Long filename entry
+ lfnEntry[LFN_offset_ordinal] = (i - 1) | ((size_t)i == entrySize ? LFN_END : 0);
+ for (j = 0; j < 13; j++) {
+ if (lfn [(i - 2) * 13 + j] == '\0') {
+ if ((j > 1) && (lfn [(i - 2) * 13 + (j-1)] == '\0')) {
+ u16_to_u8array (lfnEntry, LFN_offset_table[j], 0xffff); // Padding
+ } else {
+ u16_to_u8array (lfnEntry, LFN_offset_table[j], 0x0000); // Terminating null character
+ }
+ } else {
+ u16_to_u8array (lfnEntry, LFN_offset_table[j], lfn [(i - 2) * 13 + j]);
+ }
+ }
+
+ lfnEntry[LFN_offset_checkSum] = aliasCheckSum;
+ lfnEntry[LFN_offset_flag] = ATTRIB_LFN;
+ lfnEntry[LFN_offset_reserved1] = 0;
+ u16_to_u8array (lfnEntry, LFN_offset_reserved2, 0);
+ _FAT_cache_writePartialSector (partition->cache, lfnEntry, _FAT_fat_clusterToSector(partition, curEntryPos.cluster) + curEntryPos.sector, curEntryPos.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
+ } else {
+ // Alias & file data
+ _FAT_cache_writePartialSector (partition->cache, entry->entryData, _FAT_fat_clusterToSector(partition, curEntryPos.cluster) + curEntryPos.sector, curEntryPos.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool _FAT_directory_chdir (PARTITION* partition, const char* path) {
+ DIR_ENTRY entry;
+
+ if (!_FAT_directory_entryFromPath (partition, &entry, path, NULL)) {
+ return false;
+ }
+
+ if (!(entry.entryData[DIR_ENTRY_attributes] & ATTRIB_DIR)) {
+ return false;
+ }
+
+ partition->cwdCluster = _FAT_directory_entryGetCluster (partition, entry.entryData);
+
+ return true;
+}
+
+void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st) {
+ // Fill in the stat struct
+ // Some of the values are faked for the sake of compatibility
+ st->st_dev = _FAT_disc_hostType(partition->disc); // The device is the 32bit ioType value
+ st->st_ino = (ino_t)(_FAT_directory_entryGetCluster(partition, entry->entryData)); // The file serial number is the start cluster
+ st->st_mode = (_FAT_directory_isDirectory(entry) ? S_IFDIR : S_IFREG) |
+ (S_IRUSR | S_IRGRP | S_IROTH) |
+ (_FAT_directory_isWritable (entry) ? (S_IWUSR | S_IWGRP | S_IWOTH) : 0); // Mode bits based on dirEntry ATTRIB byte
+ st->st_nlink = 1; // Always one hard link on a FAT file
+ st->st_uid = 1; // Faked for FAT
+ st->st_gid = 2; // Faked for FAT
+ st->st_rdev = st->st_dev;
+ st->st_size = u8array_to_u32 (entry->entryData, DIR_ENTRY_fileSize); // File size
+ st->st_atime = _FAT_filetime_to_time_t (
+ 0,
+ u8array_to_u16 (entry->entryData, DIR_ENTRY_aDate)
+ );
+ st->st_spare1 = 0;
+ st->st_mtime = _FAT_filetime_to_time_t (
+ u8array_to_u16 (entry->entryData, DIR_ENTRY_mTime),
+ u8array_to_u16 (entry->entryData, DIR_ENTRY_mDate)
+ );
+ st->st_spare2 = 0;
+ st->st_ctime = _FAT_filetime_to_time_t (
+ u8array_to_u16 (entry->entryData, DIR_ENTRY_cTime),
+ u8array_to_u16 (entry->entryData, DIR_ENTRY_cDate)
+ );
+ st->st_spare3 = 0;
+ st->st_blksize = BYTES_PER_READ; // Prefered file I/O block size
+ st->st_blocks = (st->st_size + BYTES_PER_READ - 1) / BYTES_PER_READ; // File size in blocks
+ st->st_spare4[0] = 0;
+ st->st_spare4[1] = 0;
+}
diff --git a/source/libfat/directory.h b/source/libfat/directory.h
new file mode 100644
index 00000000..02bd82ff
--- /dev/null
+++ b/source/libfat/directory.h
@@ -0,0 +1,173 @@
+/*
+ directory.h
+ Reading, writing and manipulation of the directory 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.
+*/
+
+#ifndef __DIRECTORY_H
+#define __DIRECTORY_H
+
+#include
+
+#include "common.h"
+#include "partition.h"
+
+#define DIR_ENTRY_DATA_SIZE 0x20
+#define MAX_LFN_LENGTH 256
+#define MAX_FILENAME_LENGTH 768 // 256 UCS-2 characters encoded into UTF-8 can use up to 768 UTF-8 chars
+#define MAX_ALIAS_LENGTH 13
+#define LFN_ENTRY_LENGTH 13
+#define ALIAS_ENTRY_LENGTH 11
+#define MAX_ALIAS_EXT_LENGTH 3
+#define MAX_ALIAS_PRI_LENGTH 8
+#define MAX_NUMERIC_TAIL 999999
+#define FAT16_ROOT_DIR_CLUSTER 0
+
+#define DIR_SEPARATOR '/'
+
+// File attributes
+#define ATTRIB_ARCH 0x20 // Archive
+#define ATTRIB_DIR 0x10 // Directory
+#define ATTRIB_LFN 0x0F // Long file name
+#define ATTRIB_VOL 0x08 // Volume
+#define ATTRIB_SYS 0x04 // System
+#define ATTRIB_HID 0x02 // Hidden
+#define ATTRIB_RO 0x01 // Read only
+
+typedef enum {FT_DIRECTORY, FT_FILE} FILE_TYPE;
+
+typedef struct {
+ uint32_t cluster;
+ sec_t sector;
+ int32_t offset;
+} DIR_ENTRY_POSITION;
+
+typedef struct {
+ uint8_t entryData[DIR_ENTRY_DATA_SIZE];
+ DIR_ENTRY_POSITION dataStart; // Points to the start of the LFN entries of a file, or the alias for no LFN
+ DIR_ENTRY_POSITION dataEnd; // Always points to the file/directory's alias entry
+ char filename[MAX_FILENAME_LENGTH];
+} DIR_ENTRY;
+
+// Directory entry offsets
+enum DIR_ENTRY_offset {
+ DIR_ENTRY_name = 0x00,
+ DIR_ENTRY_extension = 0x08,
+ DIR_ENTRY_attributes = 0x0B,
+ DIR_ENTRY_reserved = 0x0C,
+ DIR_ENTRY_cTime_ms = 0x0D,
+ DIR_ENTRY_cTime = 0x0E,
+ DIR_ENTRY_cDate = 0x10,
+ DIR_ENTRY_aDate = 0x12,
+ DIR_ENTRY_clusterHigh = 0x14,
+ DIR_ENTRY_mTime = 0x16,
+ DIR_ENTRY_mDate = 0x18,
+ DIR_ENTRY_cluster = 0x1A,
+ DIR_ENTRY_fileSize = 0x1C
+};
+
+/*
+Returns true if the file specified by entry is a directory
+*/
+static inline bool _FAT_directory_isDirectory (DIR_ENTRY* entry) {
+ return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) != 0);
+}
+
+static inline bool _FAT_directory_isWritable (DIR_ENTRY* entry) {
+ return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_RO) == 0);
+}
+
+static inline bool _FAT_directory_isDot (DIR_ENTRY* entry) {
+ return ((entry->filename[0] == '.') && ((entry->filename[1] == '\0') ||
+ ((entry->filename[1] == '.') && entry->filename[2] == '\0')));
+}
+
+/*
+Reads the first directory entry from the directory starting at dirCluster
+Places result in entry
+entry will be destroyed even if no directory entry is found
+Returns true on success, false on failure
+*/
+bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster);
+
+/*
+Reads the next directory entry after the one already pointed to by entry
+Places result in entry
+entry will be destroyed even if no directory entry is found
+Returns true on success, false on failure
+*/
+bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry);
+
+/*
+Gets the directory entry corrsponding to the supplied path
+entry will be destroyed even if no directory entry is found
+pathEnd specifies the end of the path string, for cutting strings short if needed
+ specify NULL to use the full length of path
+ pathEnd is only a suggestion, and the path string will be searched up until the next PATH_SEPARATOR
+ after pathEND.
+Returns true on success, false on failure
+*/
+bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd);
+
+/*
+Changes the current directory to the one specified by path
+Returns true on success, false on failure
+*/
+bool _FAT_directory_chdir (PARTITION* partition, const char* path);
+
+/*
+Removes the directory entry specified by entry
+Assumes that entry is valid
+Returns true on success, false on failure
+*/
+bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry);
+
+/*
+Add a directory entry to the directory specified by dirCluster
+The fileData, dataStart and dataEnd elements of the DIR_ENTRY struct are
+updated with the new directory entry position and alias.
+Returns true on success, false on failure
+*/
+bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster);
+
+/*
+Get the start cluster of a file from it's entry data
+*/
+uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData);
+
+/*
+Fill in the file name and entry data of DIR_ENTRY* entry.
+Assumes that the entry's dataStart and dataEnd are correct
+Returns true on success, false on failure
+*/
+bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry);
+
+/*
+Fill in a stat struct based on a file entry
+*/
+void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st);
+
+#endif // _DIRECTORY_H
diff --git a/source/libfat/disc_fat.c b/source/libfat/disc_fat.c
new file mode 100644
index 00000000..f6753d7a
--- /dev/null
+++ b/source/libfat/disc_fat.c
@@ -0,0 +1,67 @@
+/*
+ disc.c
+ Interface to the low level disc functions. Used by the higher level
+ file system code.
+
+ Copyright (c) 2008 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.
+*/
+
+#include "disc_fat.h"
+
+/*
+The list of interfaces consists of a series of name/interface pairs.
+The interface is returned via a simple function. This allows for
+platforms where the interface has to be "assembled" before it can
+be used, like DLDI on the NDS. For cases where a simple struct
+is available, wrapper functions are used.
+The list is terminated by a NULL/NULL entry.
+*/
+
+/* ====================== Wii ====================== */
+#include
+#include
+#include
+
+static const DISC_INTERFACE* get_io_wiisd (void) {
+ return &__io_wiisd;
+}
+static const DISC_INTERFACE* get_io_usbstorage (void) {
+ return &__io_usbstorage;
+}
+
+static const DISC_INTERFACE* get_io_gcsda (void) {
+ return &__io_gcsda;
+}
+static const DISC_INTERFACE* get_io_gcsdb (void) {
+ return &__io_gcsdb;
+}
+
+const INTERFACE_ID _FAT_disc_interfaces[] = {
+ {"sd", get_io_wiisd},
+ {"usb", get_io_usbstorage},
+ {"carda", get_io_gcsda},
+ {"cardb", get_io_gcsdb},
+ {NULL, NULL}
+};
+
diff --git a/source/libfat/disc_fat.h b/source/libfat/disc_fat.h
new file mode 100644
index 00000000..14a323b2
--- /dev/null
+++ b/source/libfat/disc_fat.h
@@ -0,0 +1,110 @@
+/*
+ disc.h
+ Interface to the low level disc functions. Used by the higher level
+ file system code.
+
+ 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.
+*/
+#ifndef __DISC_H
+#define __DISC_H
+
+#include "common.h"
+
+/*
+A list of all default devices to try at startup,
+terminated by a {NULL,NULL} entry.
+*/
+typedef struct {
+ const char* name;
+ const DISC_INTERFACE* (*getInterface)(void);
+} INTERFACE_ID;
+extern const INTERFACE_ID _FAT_disc_interfaces[];
+
+/*
+Check if a disc is inserted
+Return true if a disc is inserted and ready, false otherwise
+*/
+static inline bool _FAT_disc_isInserted (const DISC_INTERFACE* disc) {
+ return disc->isInserted();
+}
+
+/*
+Read numSectors sectors from a disc, starting at sector.
+numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined,
+else it is at least 1
+sector is 0 or greater
+buffer is a pointer to the memory to fill
+*/
+static inline bool _FAT_disc_readSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, void* buffer) {
+ return disc->readSectors (sector, numSectors, buffer);
+}
+
+/*
+Write numSectors sectors to a disc, starting at sector.
+numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined,
+else it is at least 1
+sector is 0 or greater
+buffer is a pointer to the memory to read from
+*/
+static inline bool _FAT_disc_writeSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, const void* buffer) {
+ return disc->writeSectors (sector, numSectors, buffer);
+}
+
+/*
+Reset the card back to a ready state
+*/
+static inline bool _FAT_disc_clearStatus (const DISC_INTERFACE* disc) {
+ return disc->clearStatus();
+}
+
+/*
+Initialise the disc to a state ready for data reading or writing
+*/
+static inline bool _FAT_disc_startup (const DISC_INTERFACE* disc) {
+ return disc->startup();
+}
+
+/*
+Put the disc in a state ready for power down.
+Complete any pending writes and disable the disc if necessary
+*/
+static inline bool _FAT_disc_shutdown (const DISC_INTERFACE* disc) {
+ return disc->shutdown();
+}
+
+/*
+Return a 32 bit value unique to each type of interface
+*/
+static inline uint32_t _FAT_disc_hostType (const DISC_INTERFACE* disc) {
+ return disc->ioType;
+}
+
+/*
+Return a 32 bit value that specifies the capabilities of the disc
+*/
+static inline uint32_t _FAT_disc_features (const DISC_INTERFACE* disc) {
+ return disc->features;
+}
+
+#endif // _DISC_H
diff --git a/source/libfat/fat.h b/source/libfat/fat.h
new file mode 100644
index 00000000..d7b11176
--- /dev/null
+++ b/source/libfat/fat.h
@@ -0,0 +1,80 @@
+/*
+ fat.h
+ Simple functionality for startup, mounting and unmounting of FAT-based devices.
+
+ 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.
+*/
+
+
+#ifndef _LIBFAT_H
+#define _LIBFAT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include
+#include
+
+/*
+Initialise any inserted block-devices.
+Add the fat device driver to the devoptab, making it available for standard file functions.
+cacheSize: The number of pages to allocate for each inserted block-device
+setAsDefaultDevice: if true, make this the default device driver for file operations
+*/
+extern bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice);
+
+/*
+Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system.
+*/
+extern bool fatInitDefault (void);
+
+/*
+Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
+You can then access the filesystem using "name:/".
+This will mount the active partition or the first valid partition on the disc,
+and will use a cache size optimized for the host system.
+*/
+extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface);
+
+/*
+Mount the device pointed to by interface, and set up a devoptab entry for it as "name:".
+You can then access the filesystem using "name:/".
+If startSector = 0, it will mount the active partition of the first valid partition on
+the disc. Otherwise it will try to mount the partition starting at startSector.
+cacheSize specifies the number of pages to allocate for the cache.
+This will not startup the disc, so you need to call interface->startup(); first.
+*/
+extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage);
+/*
+Unmount the partition specified by name.
+If there are open files, it will attempt to synchronise them to disc.
+*/
+extern void fatUnmount (const char* name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LIBFAT_H
diff --git a/source/libfat/fatdir.c b/source/libfat/fatdir.c
new file mode 100644
index 00000000..55faa0b9
--- /dev/null
+++ b/source/libfat/fatdir.c
@@ -0,0 +1,610 @@
+/*
+ fatdir.c
+
+ Functions used by the newlib disc stubs to interface with
+ this library
+
+ 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.
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+#include "fatdir.h"
+
+#include "cache.h"
+#include "file_allocation_table.h"
+#include "partition.h"
+#include "directory.h"
+#include "bit_ops.h"
+#include "filetime.h"
+#include "lock.h"
+
+
+int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st) {
+ PARTITION* partition = NULL;
+ DIR_ENTRY dirEntry;
+
+ // Get the partition this file is on
+ partition = _FAT_partition_getPartitionFromPath (path);
+ if (partition == NULL) {
+ r->_errno = ENODEV;
+ return -1;
+ }
+
+ // Move the path pointer to the start of the actual path
+ if (strchr (path, ':') != NULL) {
+ path = strchr (path, ':') + 1;
+ }
+ if (strchr (path, ':') != NULL) {
+ r->_errno = EINVAL;
+ return -1;
+ }
+
+ _FAT_lock(&partition->lock);
+
+ // Search for the file on the disc
+ if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = ENOENT;
+ return -1;
+ }
+
+ // Fill in the stat struct
+ _FAT_directory_entryStat (partition, &dirEntry, st);
+
+ _FAT_unlock(&partition->lock);
+ return 0;
+}
+
+int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink) {
+ r->_errno = ENOTSUP;
+ return -1;
+}
+
+int _FAT_unlink_r (struct _reent *r, const char *path) {
+ PARTITION* partition = NULL;
+ DIR_ENTRY dirEntry;
+ DIR_ENTRY dirContents;
+ uint32_t cluster;
+ bool nextEntry;
+ bool errorOccured = false;
+
+ // Get the partition this directory is on
+ partition = _FAT_partition_getPartitionFromPath (path);
+ if (partition == NULL) {
+ r->_errno = ENODEV;
+ return -1;
+ }
+
+ // Make sure we aren't trying to write to a read-only disc
+ if (partition->readOnly) {
+ r->_errno = EROFS;
+ return -1;
+ }
+
+ // Move the path pointer to the start of the actual path
+ if (strchr (path, ':') != NULL) {
+ path = strchr (path, ':') + 1;
+ }
+ if (strchr (path, ':') != NULL) {
+ r->_errno = EINVAL;
+ return -1;
+ }
+
+ _FAT_lock(&partition->lock);
+
+ // Search for the file on the disc
+ if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = ENOENT;
+ return -1;
+ }
+
+ cluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData);
+
+
+ // If this is a directory, make sure it is empty
+ if (_FAT_directory_isDirectory (&dirEntry)) {
+ nextEntry = _FAT_directory_getFirstEntry (partition, &dirContents, cluster);
+
+ while (nextEntry) {
+ if (!_FAT_directory_isDot (&dirContents)) {
+ // The directory had something in it that isn't a reference to itself or it's parent
+ _FAT_unlock(&partition->lock);
+ r->_errno = EPERM;
+ return -1;
+ }
+ nextEntry = _FAT_directory_getNextEntry (partition, &dirContents);
+ }
+ }
+
+ if (_FAT_fat_isValidCluster(partition, cluster)) {
+ // Remove the cluster chain for this file
+ if (!_FAT_fat_clearLinks (partition, cluster)) {
+ r->_errno = EIO;
+ errorOccured = true;
+ }
+ }
+
+ // Remove the directory entry for this file
+ if (!_FAT_directory_removeEntry (partition, &dirEntry)) {
+ r->_errno = EIO;
+ errorOccured = true;
+ }
+
+ // Flush any sectors in the disc cache
+ if (!_FAT_cache_flush(partition->cache)) {
+ r->_errno = EIO;
+ errorOccured = true;
+ }
+
+ _FAT_unlock(&partition->lock);
+ if (errorOccured) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+int _FAT_chdir_r (struct _reent *r, const char *path) {
+ PARTITION* partition = NULL;
+
+ // Get the partition this directory is on
+ partition = _FAT_partition_getPartitionFromPath (path);
+ if (partition == NULL) {
+ r->_errno = ENODEV;
+ return -1;
+ }
+
+ // Move the path pointer to the start of the actual path
+ if (strchr (path, ':') != NULL) {
+ path = strchr (path, ':') + 1;
+ }
+ if (strchr (path, ':') != NULL) {
+ r->_errno = EINVAL;
+ return -1;
+ }
+
+ _FAT_lock(&partition->lock);
+
+ // Try changing directory
+ if (_FAT_directory_chdir (partition, path)) {
+ // Successful
+ _FAT_unlock(&partition->lock);
+ return 0;
+ } else {
+ // Failed
+ _FAT_unlock(&partition->lock);
+ r->_errno = ENOTDIR;
+ return -1;
+ }
+}
+
+int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName) {
+ PARTITION* partition = NULL;
+ DIR_ENTRY oldDirEntry;
+ DIR_ENTRY newDirEntry;
+ const char *pathEnd;
+ uint32_t dirCluster;
+
+ // Get the partition this directory is on
+ partition = _FAT_partition_getPartitionFromPath (oldName);
+ if (partition == NULL) {
+ r->_errno = ENODEV;
+ return -1;
+ }
+
+ _FAT_lock(&partition->lock);
+
+ // Make sure the same partition is used for the old and new names
+ if (partition != _FAT_partition_getPartitionFromPath (newName)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EXDEV;
+ return -1;
+ }
+
+ // Make sure we aren't trying to write to a read-only disc
+ if (partition->readOnly) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EROFS;
+ return -1;
+ }
+
+ // Move the path pointer to the start of the actual path
+ if (strchr (oldName, ':') != NULL) {
+ oldName = strchr (oldName, ':') + 1;
+ }
+ if (strchr (oldName, ':') != NULL) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EINVAL;
+ return -1;
+ }
+ if (strchr (newName, ':') != NULL) {
+ newName = strchr (newName, ':') + 1;
+ }
+ if (strchr (newName, ':') != NULL) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EINVAL;
+ return -1;
+ }
+
+ // Search for the file on the disc
+ if (!_FAT_directory_entryFromPath (partition, &oldDirEntry, oldName, NULL)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = ENOENT;
+ return -1;
+ }
+
+ // Make sure there is no existing file / directory with the new name
+ if (_FAT_directory_entryFromPath (partition, &newDirEntry, newName, NULL)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EEXIST;
+ return -1;
+ }
+
+ // Create the new file entry
+ // Get the directory it has to go in
+ pathEnd = strrchr (newName, DIR_SEPARATOR);
+ if (pathEnd == NULL) {
+ // No path was specified
+ dirCluster = partition->cwdCluster;
+ pathEnd = newName;
+ } else {
+ // Path was specified -- get the right dirCluster
+ // Recycling newDirEntry, since it needs to be recreated anyway
+ if (!_FAT_directory_entryFromPath (partition, &newDirEntry, newName, pathEnd) ||
+ !_FAT_directory_isDirectory(&newDirEntry)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = ENOTDIR;
+ return -1;
+ }
+ dirCluster = _FAT_directory_entryGetCluster (partition, newDirEntry.entryData);
+ // Move the pathEnd past the last DIR_SEPARATOR
+ pathEnd += 1;
+ }
+
+ // Copy the entry data
+ memcpy (&newDirEntry, &oldDirEntry, sizeof(DIR_ENTRY));
+
+ // Set the new name
+ strncpy (newDirEntry.filename, pathEnd, MAX_FILENAME_LENGTH - 1);
+
+ // Write the new entry
+ if (!_FAT_directory_addEntry (partition, &newDirEntry, dirCluster)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = ENOSPC;
+ return -1;
+ }
+
+ // Remove the old entry
+ if (!_FAT_directory_removeEntry (partition, &oldDirEntry)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EIO;
+ return -1;
+ }
+
+ // Flush any sectors in the disc cache
+ if (!_FAT_cache_flush (partition->cache)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EIO;
+ return -1;
+ }
+
+ _FAT_unlock(&partition->lock);
+ return 0;
+}
+
+int _FAT_mkdir_r (struct _reent *r, const char *path, int mode) {
+ PARTITION* partition = NULL;
+ bool fileExists;
+ DIR_ENTRY dirEntry;
+ const char* pathEnd;
+ uint32_t parentCluster, dirCluster;
+ uint8_t newEntryData[DIR_ENTRY_DATA_SIZE];
+
+ partition = _FAT_partition_getPartitionFromPath (path);
+ if (partition == NULL) {
+ r->_errno = ENODEV;
+ return -1;
+ }
+
+ // Move the path pointer to the start of the actual path
+ if (strchr (path, ':') != NULL) {
+ path = strchr (path, ':') + 1;
+ }
+ if (strchr (path, ':') != NULL) {
+ r->_errno = EINVAL;
+ return -1;
+ }
+
+ _FAT_lock(&partition->lock);
+
+ // Search for the file/directory on the disc
+ fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL);
+
+ // Make sure it doesn't exist
+ if (fileExists) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EEXIST;
+ return -1;
+ }
+
+ if (partition->readOnly) {
+ // We can't write to a read-only partition
+ _FAT_unlock(&partition->lock);
+ r->_errno = EROFS;
+ return -1;
+ }
+
+ // Get the directory it has to go in
+ pathEnd = strrchr (path, DIR_SEPARATOR);
+ if (pathEnd == NULL) {
+ // No path was specified
+ parentCluster = partition->cwdCluster;
+ pathEnd = path;
+ } else {
+ // Path was specified -- get the right parentCluster
+ // Recycling dirEntry, since it needs to be recreated anyway
+ if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, pathEnd) ||
+ !_FAT_directory_isDirectory(&dirEntry)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = ENOTDIR;
+ return -1;
+ }
+ parentCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData);
+ // Move the pathEnd past the last DIR_SEPARATOR
+ pathEnd += 1;
+ }
+ // Create the entry data
+ strncpy (dirEntry.filename, pathEnd, MAX_FILENAME_LENGTH - 1);
+ memset (dirEntry.entryData, 0, DIR_ENTRY_DATA_SIZE);
+
+ // Set the creation time and date
+ dirEntry.entryData[DIR_ENTRY_cTime_ms] = 0;
+ u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cTime, _FAT_filetime_getTimeFromRTC());
+ u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cDate, _FAT_filetime_getDateFromRTC());
+ u16_to_u8array (dirEntry.entryData, DIR_ENTRY_mTime, _FAT_filetime_getTimeFromRTC());
+ u16_to_u8array (dirEntry.entryData, DIR_ENTRY_mDate, _FAT_filetime_getDateFromRTC());
+ u16_to_u8array (dirEntry.entryData, DIR_ENTRY_aDate, _FAT_filetime_getDateFromRTC());
+
+ // Set the directory attribute
+ dirEntry.entryData[DIR_ENTRY_attributes] = ATTRIB_DIR;
+
+ // Get a cluster for the new directory
+ dirCluster = _FAT_fat_linkFreeClusterCleared (partition, CLUSTER_FREE);
+ if (!_FAT_fat_isValidCluster(partition, dirCluster)) {
+ // No space left on disc for the cluster
+ _FAT_unlock(&partition->lock);
+ r->_errno = ENOSPC;
+ return -1;
+ }
+ u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cluster, dirCluster);
+ u16_to_u8array (dirEntry.entryData, DIR_ENTRY_clusterHigh, dirCluster >> 16);
+
+ // Write the new directory's entry to it's parent
+ if (!_FAT_directory_addEntry (partition, &dirEntry, parentCluster)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = ENOSPC;
+ return -1;
+ }
+
+ // Create the dot entry within the directory
+ memset (newEntryData, 0, DIR_ENTRY_DATA_SIZE);
+ memset (newEntryData, ' ', 11);
+ newEntryData[DIR_ENTRY_name] = '.';
+ newEntryData[DIR_ENTRY_attributes] = ATTRIB_DIR;
+ u16_to_u8array (newEntryData, DIR_ENTRY_cluster, dirCluster);
+ u16_to_u8array (newEntryData, DIR_ENTRY_clusterHigh, dirCluster >> 16);
+
+ // Write it to the directory, erasing that sector in the process
+ _FAT_cache_eraseWritePartialSector ( partition->cache, newEntryData,
+ _FAT_fat_clusterToSector (partition, dirCluster), 0, DIR_ENTRY_DATA_SIZE);
+
+
+ // Create the double dot entry within the directory
+
+ // if ParentDir == Rootdir then ".."" always link to Cluster 0
+ if(parentCluster == partition->rootDirCluster)
+ parentCluster = FAT16_ROOT_DIR_CLUSTER;
+
+ newEntryData[DIR_ENTRY_name + 1] = '.';
+ u16_to_u8array (newEntryData, DIR_ENTRY_cluster, parentCluster);
+ u16_to_u8array (newEntryData, DIR_ENTRY_clusterHigh, parentCluster >> 16);
+
+ // Write it to the directory
+ _FAT_cache_writePartialSector ( partition->cache, newEntryData,
+ _FAT_fat_clusterToSector (partition, dirCluster), DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
+
+ // Flush any sectors in the disc cache
+ if (!_FAT_cache_flush(partition->cache)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EIO;
+ return -1;
+ }
+
+ _FAT_unlock(&partition->lock);
+ return 0;
+}
+
+int _FAT_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf)
+{
+ PARTITION* partition = NULL;
+ unsigned int freeClusterCount;
+
+ // Get the partition of the requested path
+ partition = _FAT_partition_getPartitionFromPath (path);
+ if (partition == NULL) {
+ r->_errno = ENODEV;
+ return -1;
+ }
+
+ _FAT_lock(&partition->lock);
+
+ freeClusterCount = _FAT_fat_freeClusterCount (partition);
+
+ // FAT clusters = POSIX blocks
+ buf->f_bsize = partition->bytesPerCluster; // File system block size.
+ buf->f_frsize = partition->bytesPerCluster; // Fundamental file system block size.
+
+ buf->f_blocks = partition->fat.lastCluster - CLUSTER_FIRST + 1; // Total number of blocks on file system in units of f_frsize.
+ buf->f_bfree = freeClusterCount; // Total number of free blocks.
+ buf->f_bavail = freeClusterCount; // Number of free blocks available to non-privileged process.
+
+ // Treat requests for info on inodes as clusters
+ buf->f_files = partition->fat.lastCluster - CLUSTER_FIRST + 1; // Total number of file serial numbers.
+ buf->f_ffree = freeClusterCount; // Total number of free file serial numbers.
+ buf->f_favail = freeClusterCount; // Number of file serial numbers available to non-privileged process.
+
+ // File system ID. 32bit ioType value
+ buf->f_fsid = _FAT_disc_hostType(partition->disc);
+
+ // Bit mask of f_flag values.
+ buf->f_flag = ST_NOSUID /* No support for ST_ISUID and ST_ISGID file mode bits */
+ | (partition->readOnly ? ST_RDONLY /* Read only file system */ : 0 ) ;
+ // Maximum filename length.
+ buf->f_namemax = MAX_FILENAME_LENGTH;
+
+ _FAT_unlock(&partition->lock);
+ return 0;
+}
+
+DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path) {
+ DIR_ENTRY dirEntry;
+ DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
+ bool fileExists;
+
+ state->partition = _FAT_partition_getPartitionFromPath (path);
+ if (state->partition == NULL) {
+ r->_errno = ENODEV;
+ return NULL;
+ }
+
+ // Move the path pointer to the start of the actual path
+ if (strchr (path, ':') != NULL) {
+ path = strchr (path, ':') + 1;
+ }
+ if (strchr (path, ':') != NULL) {
+ r->_errno = EINVAL;
+ return NULL;
+ }
+
+ _FAT_lock(&state->partition->lock);
+
+ // Get the start cluster of the directory
+ fileExists = _FAT_directory_entryFromPath (state->partition, &dirEntry, path, NULL);
+
+ if (!fileExists) {
+ _FAT_unlock(&state->partition->lock);
+ r->_errno = ENOENT;
+ return NULL;
+ }
+
+ // Make sure it is a directory
+ if (! _FAT_directory_isDirectory (&dirEntry)) {
+ _FAT_unlock(&state->partition->lock);
+ r->_errno = ENOTDIR;
+ return NULL;
+ }
+
+ // Save the start cluster for use when resetting the directory data
+ state->startCluster = _FAT_directory_entryGetCluster (state->partition, dirEntry.entryData);
+
+ // Get the first entry for use with a call to dirnext
+ state->validEntry =
+ _FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster);
+
+ // We are now using this entry
+ state->inUse = true;
+ _FAT_unlock(&state->partition->lock);
+ return (DIR_ITER*) state;
+}
+
+int _FAT_dirreset_r (struct _reent *r, DIR_ITER *dirState) {
+ DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
+
+ _FAT_lock(&state->partition->lock);
+
+ // Make sure we are still using this entry
+ if (!state->inUse) {
+ _FAT_unlock(&state->partition->lock);
+ r->_errno = EBADF;
+ return -1;
+ }
+
+ // Get the first entry for use with a call to dirnext
+ state->validEntry =
+ _FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster);
+
+ _FAT_unlock(&state->partition->lock);
+ return 0;
+}
+
+int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) {
+ DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
+
+ _FAT_lock(&state->partition->lock);
+
+ // Make sure we are still using this entry
+ if (!state->inUse) {
+ _FAT_unlock(&state->partition->lock);
+ r->_errno = EBADF;
+ return -1;
+ }
+
+ // Make sure there is another file to report on
+ if (! state->validEntry) {
+ _FAT_unlock(&state->partition->lock);
+ r->_errno = ENOENT;
+ return -1;
+ }
+
+ // Get the filename
+ strncpy (filename, state->currentEntry.filename, MAX_FILENAME_LENGTH);
+ // Get the stats, if requested
+ if (filestat != NULL) {
+ _FAT_directory_entryStat (state->partition, &(state->currentEntry), filestat);
+ }
+
+ // Look for the next entry for use next time
+ state->validEntry =
+ _FAT_directory_getNextEntry (state->partition, &(state->currentEntry));
+
+ _FAT_unlock(&state->partition->lock);
+ return 0;
+}
+
+int _FAT_dirclose_r (struct _reent *r, DIR_ITER *dirState) {
+ DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
+
+ // We are no longer using this entry
+ _FAT_lock(&state->partition->lock);
+ state->inUse = false;
+ _FAT_unlock(&state->partition->lock);
+
+ return 0;
+}
diff --git a/source/libfat/fatdir.h b/source/libfat/fatdir.h
new file mode 100644
index 00000000..1b47a914
--- /dev/null
+++ b/source/libfat/fatdir.h
@@ -0,0 +1,73 @@
+/*
+ fatdir.h
+
+ Functions used by the newlib disc stubs to interface with
+ this library
+
+ 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.
+*/
+
+
+#ifndef __FATDIR_H
+#define __FATDIR_H
+
+#include
+#include
+#include
+#include
+#include "common.h"
+#include "directory.h"
+
+typedef struct {
+ PARTITION* partition;
+ DIR_ENTRY currentEntry;
+ uint32_t startCluster;
+ bool inUse;
+ bool validEntry;
+} DIR_STATE_STRUCT;
+
+extern int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st);
+
+extern int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink);
+
+extern int _FAT_unlink_r (struct _reent *r, const char *name);
+
+extern int _FAT_chdir_r (struct _reent *r, const char *name);
+
+extern int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName);
+
+extern int _FAT_mkdir_r (struct _reent *r, const char *path, int mode);
+
+extern int _FAT_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf);
+
+/*
+Directory iterator functions
+*/
+extern DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path);
+extern int _FAT_dirreset_r (struct _reent *r, DIR_ITER *dirState);
+extern int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
+extern int _FAT_dirclose_r (struct _reent *r, DIR_ITER *dirState);
+
+
+#endif // _FATDIR_H
diff --git a/source/libfat/fatfile.c b/source/libfat/fatfile.c
new file mode 100644
index 00000000..23d44ddc
--- /dev/null
+++ b/source/libfat/fatfile.c
@@ -0,0 +1,1131 @@
+/*
+ fatfile.c
+
+ Functions used by the newlib disc stubs to interface with
+ this library
+
+ 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.
+
+ 2009-10-23 oggzee: fixes for cluster aligned file size (write, truncate, seek)
+*/
+
+
+#include "fatfile.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include "cache.h"
+#include "file_allocation_table.h"
+#include "bit_ops.h"
+#include "filetime.h"
+#include "lock.h"
+
+int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) {
+ PARTITION* partition = NULL;
+ bool fileExists;
+ DIR_ENTRY dirEntry;
+ const char* pathEnd;
+ uint32_t dirCluster;
+ FILE_STRUCT* file = (FILE_STRUCT*) fileStruct;
+ partition = _FAT_partition_getPartitionFromPath (path);
+
+ if (partition == NULL) {
+ r->_errno = ENODEV;
+ return -1;
+ }
+
+ // Move the path pointer to the start of the actual path
+ if (strchr (path, ':') != NULL) {
+ path = strchr (path, ':') + 1;
+ }
+ if (strchr (path, ':') != NULL) {
+ r->_errno = EINVAL;
+ return -1;
+ }
+
+ // Determine which mode the file is openned for
+ if ((flags & 0x03) == O_RDONLY) {
+ // Open the file for read-only access
+ file->read = true;
+ file->write = false;
+ file->append = false;
+ } else if ((flags & 0x03) == O_WRONLY) {
+ // Open file for write only access
+ file->read = false;
+ file->write = true;
+ file->append = false;
+ } else if ((flags & 0x03) == O_RDWR) {
+ // Open file for read/write access
+ file->read = true;
+ file->write = true;
+ file->append = false;
+ } else {
+ r->_errno = EACCES;
+ return -1;
+ }
+
+ // Make sure we aren't trying to write to a read-only disc
+ if (file->write && partition->readOnly) {
+ r->_errno = EROFS;
+ return -1;
+ }
+
+ // Search for the file on the disc
+ _FAT_lock(&partition->lock);
+ fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL);
+
+ // The file shouldn't exist if we are trying to create it
+ if ((flags & O_CREAT) && (flags & O_EXCL) && fileExists) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EEXIST;
+ return -1;
+ }
+
+ // It should not be a directory if we're openning a file,
+ if (fileExists && _FAT_directory_isDirectory(&dirEntry)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EISDIR;
+ return -1;
+ }
+
+ // We haven't modified the file yet
+ file->modified = false;
+
+ // If the file doesn't exist, create it if we're allowed to
+ if (!fileExists) {
+ if (flags & O_CREAT) {
+ if (partition->readOnly) {
+ // We can't write to a read-only partition
+ _FAT_unlock(&partition->lock);
+ r->_errno = EROFS;
+ return -1;
+ }
+ // Create the file
+ // Get the directory it has to go in
+ pathEnd = strrchr (path, DIR_SEPARATOR);
+ if (pathEnd == NULL) {
+ // No path was specified
+ dirCluster = partition->cwdCluster;
+ pathEnd = path;
+ } else {
+ // Path was specified -- get the right dirCluster
+ // Recycling dirEntry, since it needs to be recreated anyway
+ if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, pathEnd) ||
+ !_FAT_directory_isDirectory(&dirEntry)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = ENOTDIR;
+ return -1;
+ }
+ dirCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData);
+ // Move the pathEnd past the last DIR_SEPARATOR
+ pathEnd += 1;
+ }
+ // Create the entry data
+ strncpy (dirEntry.filename, pathEnd, MAX_FILENAME_LENGTH - 1);
+ memset (dirEntry.entryData, 0, DIR_ENTRY_DATA_SIZE);
+
+ // Set the creation time and date
+ dirEntry.entryData[DIR_ENTRY_cTime_ms] = 0;
+ u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cTime, _FAT_filetime_getTimeFromRTC());
+ u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cDate, _FAT_filetime_getDateFromRTC());
+
+ if (!_FAT_directory_addEntry (partition, &dirEntry, dirCluster)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = ENOSPC;
+ return -1;
+ }
+
+ // File entry is modified
+ file->modified = true;
+ } else {
+ // file doesn't exist, and we aren't creating it
+ _FAT_unlock(&partition->lock);
+ r->_errno = ENOENT;
+ return -1;
+ }
+ }
+
+ file->filesize = u8array_to_u32 (dirEntry.entryData, DIR_ENTRY_fileSize);
+
+ /* Allow LARGEFILEs with undefined results
+ // Make sure that the file size can fit in the available space
+ if (!(flags & O_LARGEFILE) && (file->filesize >= (1<<31))) {
+ r->_errno = EFBIG;
+ return -1;
+ }
+ */
+
+ // Make sure we aren't trying to write to a read-only file
+ if (file->write && !_FAT_directory_isWritable(&dirEntry)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EROFS;
+ return -1;
+ }
+
+ // Associate this file with a particular partition
+ file->partition = partition;
+
+ file->startCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData);
+
+ // Truncate the file if requested
+ if ((flags & O_TRUNC) && file->write && (file->startCluster != 0)) {
+ _FAT_fat_clearLinks (partition, file->startCluster);
+ file->startCluster = CLUSTER_FREE;
+ file->filesize = 0;
+ // File is modified since we just cut it all off
+ file->modified = true;
+ }
+
+ // Remember the position of this file's directory entry
+ file->dirEntryStart = dirEntry.dataStart; // Points to the start of the LFN entries of a file, or the alias for no LFN
+ file->dirEntryEnd = dirEntry.dataEnd;
+
+ // Reset read/write pointer
+ file->currentPosition = 0;
+ file->rwPosition.cluster = file->startCluster;
+ file->rwPosition.sector = 0;
+ file->rwPosition.byte = 0;
+
+ if (flags & O_APPEND) {
+ file->append = true;
+
+ // Set append pointer to the end of the file
+ file->appendPosition.cluster = _FAT_fat_lastCluster (partition, file->startCluster);
+ file->appendPosition.sector = (file->filesize % partition->bytesPerCluster) / BYTES_PER_READ;
+ file->appendPosition.byte = file->filesize % BYTES_PER_READ;
+
+ // Check if the end of the file is on the end of a cluster
+ if ( (file->filesize > 0) && ((file->filesize % partition->bytesPerCluster)==0) ){
+ // Set flag to allocate a new cluster
+ file->appendPosition.sector = partition->sectorsPerCluster;
+ file->appendPosition.byte = 0;
+ }
+ } else {
+ file->append = false;
+ // Use something sane for the append pointer, so the whole file struct contains known values
+ file->appendPosition = file->rwPosition;
+ }
+
+ file->inUse = true;
+
+ // Insert this file into the double-linked list of open files
+ partition->openFileCount += 1;
+ if (partition->firstOpenFile) {
+ file->nextOpenFile = partition->firstOpenFile;
+ partition->firstOpenFile->prevOpenFile = file;
+ } else {
+ file->nextOpenFile = NULL;
+ }
+ file->prevOpenFile = NULL;
+ partition->firstOpenFile = file;
+
+ _FAT_unlock(&partition->lock);
+
+ return (int) file;
+}
+
+/*
+Synchronizes the file data to disc.
+Does no locking of its own -- lock the partition before calling.
+Returns 0 on success, an error code on failure.
+*/
+int _FAT_syncToDisc (FILE_STRUCT* file) {
+ uint8_t dirEntryData[DIR_ENTRY_DATA_SIZE];
+
+ if (!file || !file->inUse) {
+ return EBADF;
+ }
+
+ if (file->write && file->modified) {
+ // Load the old entry
+ _FAT_cache_readPartialSector (file->partition->cache, dirEntryData,
+ _FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector,
+ file->dirEntryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
+
+ // Write new data to the directory entry
+ // File size
+ u32_to_u8array (dirEntryData, DIR_ENTRY_fileSize, file->filesize);
+
+ // Start cluster
+ u16_to_u8array (dirEntryData, DIR_ENTRY_cluster, file->startCluster);
+ u16_to_u8array (dirEntryData, DIR_ENTRY_clusterHigh, file->startCluster >> 16);
+
+ // Modification time and date
+ u16_to_u8array (dirEntryData, DIR_ENTRY_mTime, _FAT_filetime_getTimeFromRTC());
+ u16_to_u8array (dirEntryData, DIR_ENTRY_mDate, _FAT_filetime_getDateFromRTC());
+
+ // Access date
+ u16_to_u8array (dirEntryData, DIR_ENTRY_aDate, _FAT_filetime_getDateFromRTC());
+
+ // Set archive attribute
+ dirEntryData[DIR_ENTRY_attributes] |= ATTRIB_ARCH;
+
+ // Write the new entry
+ _FAT_cache_writePartialSector (file->partition->cache, dirEntryData,
+ _FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector,
+ file->dirEntryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE);
+
+ // Flush any sectors in the disc cache
+ if (!_FAT_cache_flush(file->partition->cache)) {
+ return EIO;
+ }
+ }
+
+ file->modified = false;
+
+ return 0;
+}
+
+
+int _FAT_close_r (struct _reent *r, int fd) {
+ FILE_STRUCT* file = (FILE_STRUCT*) fd;
+ int ret = 0;
+
+ if (!file->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+
+ _FAT_lock(&file->partition->lock);
+
+ if (file->write) {
+ ret = _FAT_syncToDisc (file);
+ if (ret != 0) {
+ r->_errno = ret;
+ ret = -1;
+ }
+ }
+
+ file->inUse = false;
+
+ // Remove this file from the double-linked list of open files
+ file->partition->openFileCount -= 1;
+ if (file->nextOpenFile) {
+ file->nextOpenFile->prevOpenFile = file->prevOpenFile;
+ }
+ if (file->prevOpenFile) {
+ file->prevOpenFile->nextOpenFile = file->nextOpenFile;
+ } else {
+ file->partition->firstOpenFile = file->nextOpenFile;
+ }
+
+ _FAT_unlock(&file->partition->lock);
+
+ return ret;
+}
+
+ssize_t _FAT_read_r (struct _reent *r, int fd, char *ptr, size_t len) {
+ FILE_STRUCT* file = (FILE_STRUCT*) fd;
+ PARTITION* partition;
+ CACHE* cache;
+ FILE_POSITION position;
+ uint32_t tempNextCluster;
+ unsigned int tempVar;
+ size_t remain;
+ bool flagNoError = true;
+
+ // Short circuit cases where len is 0 (or less)
+ if (len <= 0) {
+ return 0;
+ }
+
+ // Make sure we can actually read from the file
+ if ((file == NULL) || !file->inUse || !file->read) {
+ r->_errno = EBADF;
+ return -1;
+ }
+
+ partition = file->partition;
+ _FAT_lock(&partition->lock);
+
+ // Don't try to read if the read pointer is past the end of file
+ if (file->currentPosition >= file->filesize || file->startCluster == CLUSTER_FREE) {
+ r->_errno = EOVERFLOW;
+ _FAT_unlock(&partition->lock);
+ return 0;
+ }
+
+ // Don't read past end of file
+ if (len + file->currentPosition > file->filesize) {
+ r->_errno = EOVERFLOW;
+ len = file->filesize - file->currentPosition;
+ }
+
+ remain = len;
+ position = file->rwPosition;
+ cache = file->partition->cache;
+
+ // Align to sector
+ tempVar = BYTES_PER_READ - position.byte;
+ if (tempVar > remain) {
+ tempVar = remain;
+ }
+
+ if ((tempVar < BYTES_PER_READ) && flagNoError)
+ {
+ _FAT_cache_readPartialSector ( cache, ptr, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector,
+ position.byte, tempVar);
+
+ remain -= tempVar;
+ ptr += tempVar;
+
+ position.byte += tempVar;
+ if (position.byte >= BYTES_PER_READ) {
+ position.byte = 0;
+ position.sector++;
+ }
+ }
+
+ // align to cluster
+ // tempVar is number of sectors to read
+ if (remain > (partition->sectorsPerCluster - position.sector) * BYTES_PER_READ) {
+ tempVar = partition->sectorsPerCluster - position.sector;
+ } else {
+ tempVar = remain / BYTES_PER_READ;
+ }
+
+ if ((tempVar > 0) && flagNoError) {
+ if (! _FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector,
+ tempVar, ptr))
+ {
+ flagNoError = false;
+ r->_errno = EIO;
+ } else {
+ ptr += tempVar * BYTES_PER_READ;
+ remain -= tempVar * BYTES_PER_READ;
+ position.sector += tempVar;
+ }
+ }
+
+ // Move onto next cluster
+ // It should get to here without reading anything if a cluster is due to be allocated
+ if ((position.sector >= partition->sectorsPerCluster) && flagNoError) {
+ tempNextCluster = _FAT_fat_nextCluster(partition, position.cluster);
+ if ((remain == 0) && (tempNextCluster == CLUSTER_EOF)) {
+ position.sector = partition->sectorsPerCluster;
+ } else if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
+ r->_errno = EIO;
+ flagNoError = false;
+ } else {
+ position.sector = 0;
+ position.cluster = tempNextCluster;
+ }
+ }
+
+ // Read in whole clusters, contiguous blocks at a time
+ while ((remain >= partition->bytesPerCluster) && flagNoError) {
+ uint32_t chunkEnd;
+ uint32_t nextChunkStart = position.cluster;
+ size_t chunkSize = 0;
+
+ do {
+ chunkEnd = nextChunkStart;
+ nextChunkStart = _FAT_fat_nextCluster (partition, chunkEnd);
+ chunkSize += partition->bytesPerCluster;
+ } while ((nextChunkStart == chunkEnd + 1) &&
+#ifdef LIMIT_SECTORS
+ (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * BYTES_PER_READ) &&
+#endif
+ (chunkSize + partition->bytesPerCluster <= remain));
+
+ if (!_FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster),
+ chunkSize / BYTES_PER_READ, ptr))
+ {
+ flagNoError = false;
+ r->_errno = EIO;
+ break;
+ }
+ ptr += chunkSize;
+ remain -= chunkSize;
+
+ // Advance to next cluster
+ if ((remain == 0) && (nextChunkStart == CLUSTER_EOF)) {
+ position.sector = partition->sectorsPerCluster;
+ position.cluster = chunkEnd;
+ } else if (!_FAT_fat_isValidCluster(partition, nextChunkStart)) {
+ r->_errno = EIO;
+ flagNoError = false;
+ } else {
+ position.sector = 0;
+ position.cluster = nextChunkStart;
+ }
+ }
+
+ // Read remaining sectors
+ tempVar = remain / BYTES_PER_READ; // Number of sectors left
+ if ((tempVar > 0) && flagNoError) {
+ if (!_FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster),
+ tempVar, ptr))
+ {
+ flagNoError = false;
+ r->_errno = EIO;
+ } else {
+ ptr += tempVar * BYTES_PER_READ;
+ remain -= tempVar * BYTES_PER_READ;
+ position.sector += tempVar;
+ }
+ }
+
+ // Last remaining sector
+ // Check if anything is left
+ if ((remain > 0) && flagNoError) {
+ _FAT_cache_readPartialSector ( cache, ptr,
+ _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain);
+ position.byte += remain;
+ remain = 0;
+ }
+
+ // Length read is the wanted length minus the stuff not read
+ len = len - remain;
+
+ // Update file information
+ file->rwPosition = position;
+ file->currentPosition += len;
+
+ _FAT_unlock(&partition->lock);
+ return len;
+}
+
+// if current position is on the cluster border and more data has to be written
+// then get next cluster or allocate next cluster
+// this solves the over-allocation problems when file size is aligned to cluster size
+// return true on succes, false on error
+static bool _FAT_check_position_for_next_cluster(struct _reent *r,
+ FILE_POSITION *position, PARTITION* partition, size_t remain, bool *flagNoError)
+{
+ uint32_t tempNextCluster;
+ // do nothing if no more data to write
+ if (remain == 0) return true;
+ if (flagNoError && *flagNoError == false) return false;
+ if ((remain < 0) || (position->sector > partition->sectorsPerCluster)) {
+ // invalid arguments - internal error
+ r->_errno = EINVAL;
+ goto err;
+ }
+ if (position->sector == partition->sectorsPerCluster) {
+ // need to advance to next cluster
+ tempNextCluster = _FAT_fat_nextCluster(partition, position->cluster);
+ if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) {
+ // Ran out of clusters so get a new one
+ tempNextCluster = _FAT_fat_linkFreeCluster(partition, position->cluster);
+ }
+ if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
+ // Couldn't get a cluster, so abort
+ r->_errno = ENOSPC;
+ goto err;
+ }
+ position->sector = 0;
+ position->cluster = tempNextCluster;
+ }
+ return true;
+err:
+ if (flagNoError) *flagNoError = false;
+ return false;
+}
+
+/*
+Extend a file so that the size is the same as the rwPosition
+*/
+static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) {
+ PARTITION* partition = file->partition;
+ CACHE* cache = file->partition->cache;
+ FILE_POSITION position;
+ uint8_t zeroBuffer [BYTES_PER_READ] = {0};
+ uint32_t remain;
+ uint32_t tempNextCluster;
+ unsigned int sector;
+
+ position.byte = file->filesize % BYTES_PER_READ;
+ position.sector = (file->filesize % partition->bytesPerCluster) / BYTES_PER_READ;
+ // It is assumed that there is always a startCluster
+ // This will be true when _FAT_file_extend_r is called from _FAT_write_r
+ position.cluster = _FAT_fat_lastCluster (partition, file->startCluster);
+
+ remain = file->currentPosition - file->filesize;
+
+ if ((remain > 0) && (file->filesize > 0) && (position.sector == 0) && (position.byte == 0)) {
+ // Get a new cluster on the edge of a cluster boundary
+ tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster);
+ if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
+ // Couldn't get a cluster, so abort
+ r->_errno = ENOSPC;
+ return false;
+ }
+ position.cluster = tempNextCluster;
+ position.sector = 0;
+ }
+
+ if (remain + position.byte < BYTES_PER_READ) {
+ // Only need to clear to the end of the sector
+ _FAT_cache_writePartialSector (cache, zeroBuffer,
+ _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, remain);
+ position.byte += remain;
+ } else {
+ if (position.byte > 0) {
+ _FAT_cache_writePartialSector (cache, zeroBuffer,
+ _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte,
+ BYTES_PER_READ - position.byte);
+ remain -= (BYTES_PER_READ - position.byte);
+ position.byte = 0;
+ position.sector ++;
+ }
+
+ while (remain >= BYTES_PER_READ) {
+ if (position.sector >= partition->sectorsPerCluster) {
+ position.sector = 0;
+ // Ran out of clusters so get a new one
+ tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster);
+ if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
+ // Couldn't get a cluster, so abort
+ r->_errno = ENOSPC;
+ return false;
+ }
+ position.cluster = tempNextCluster;
+ }
+
+ sector = _FAT_fat_clusterToSector (partition, position.cluster) + position.sector;
+ _FAT_cache_writeSectors (cache, sector, 1, zeroBuffer);
+
+ remain -= BYTES_PER_READ;
+ position.sector ++;
+ }
+
+ if (!_FAT_check_position_for_next_cluster(r, &position, partition, remain, NULL)) {
+ // error already marked
+ return false;
+ }
+
+ if (remain > 0) {
+ _FAT_cache_writePartialSector (cache, zeroBuffer,
+ _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain);
+ position.byte = remain;
+ }
+ }
+
+ file->rwPosition = position;
+ file->filesize = file->currentPosition;
+ return true;
+}
+
+ssize_t _FAT_write_r (struct _reent *r, int fd, const char *ptr, size_t len) {
+ FILE_STRUCT* file = (FILE_STRUCT*) fd;
+ PARTITION* partition;
+ CACHE* cache;
+ FILE_POSITION position;
+ uint32_t tempNextCluster;
+ unsigned int tempVar;
+ size_t remain;
+ bool flagNoError = true;
+ bool flagAppending = false;
+
+ // Make sure we can actually write to the file
+ if ((file == NULL) || !file->inUse || !file->write) {
+ r->_errno = EBADF;
+ return -1;
+ }
+
+ partition = file->partition;
+ cache = file->partition->cache;
+ _FAT_lock(&partition->lock);
+
+ // Only write up to the maximum file size, taking into account wrap-around of ints
+ if (remain + file->filesize > FILE_MAX_SIZE || len + file->filesize < file->filesize) {
+ len = FILE_MAX_SIZE - file->filesize;
+ }
+ remain = len;
+
+ // Short circuit cases where len is 0 (or less)
+ if (len <= 0) {
+ _FAT_unlock(&partition->lock);
+ return 0;
+ }
+
+ // Get a new cluster for the start of the file if required
+ if (file->startCluster == CLUSTER_FREE) {
+ tempNextCluster = _FAT_fat_linkFreeCluster (partition, CLUSTER_FREE);
+ if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
+ // Couldn't get a cluster, so abort immediately
+ _FAT_unlock(&partition->lock);
+ r->_errno = ENOSPC;
+ return -1;
+ }
+ file->startCluster = tempNextCluster;
+
+ // Appending starts at the begining for a 0 byte file
+ file->appendPosition.cluster = file->startCluster;
+ file->appendPosition.sector = 0;
+ file->appendPosition.byte = 0;
+
+ file->rwPosition.cluster = file->startCluster;
+ file->rwPosition.sector = 0;
+ file->rwPosition.byte = 0;
+ }
+
+ if (file->append) {
+ position = file->appendPosition;
+ flagAppending = true;
+ } else {
+ // If the write pointer is past the end of the file, extend the file to that size
+ if (file->currentPosition > file->filesize) {
+ if (!_FAT_file_extend_r (r, file)) {
+ _FAT_unlock(&partition->lock);
+ return -1;
+ }
+ }
+
+ // Write at current read pointer
+ position = file->rwPosition;
+
+ // If it is writing past the current end of file, set appending flag
+ if (len + file->currentPosition > file->filesize) {
+ flagAppending = true;
+ }
+ }
+
+ // Move onto next cluster if needed
+ _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError);
+
+ // Align to sector
+ tempVar = BYTES_PER_READ - position.byte;
+ if (tempVar > remain) {
+ tempVar = remain;
+ }
+
+ if ((tempVar < BYTES_PER_READ) && flagNoError) {
+ // Write partial sector to disk
+ _FAT_cache_writePartialSector (cache, ptr,
+ _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, tempVar);
+
+ remain -= tempVar;
+ ptr += tempVar;
+ position.byte += tempVar;
+
+
+ // Move onto next sector
+ if (position.byte >= BYTES_PER_READ) {
+ position.byte = 0;
+ position.sector ++;
+ }
+ }
+
+ // Align to cluster
+ // tempVar is number of sectors to write
+ if (remain > (partition->sectorsPerCluster - position.sector) * BYTES_PER_READ) {
+ tempVar = partition->sectorsPerCluster - position.sector;
+ } else {
+ tempVar = remain / BYTES_PER_READ;
+ }
+
+ if ((tempVar > 0 && tempVar < partition->sectorsPerCluster) && flagNoError) {
+ if (!_FAT_cache_writeSectors (cache,
+ _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, tempVar, ptr))
+ {
+ flagNoError = false;
+ r->_errno = EIO;
+ } else {
+ ptr += tempVar * BYTES_PER_READ;
+ remain -= tempVar * BYTES_PER_READ;
+ position.sector += tempVar;
+ }
+ }
+
+ // Write whole clusters
+ while ((remain >= partition->bytesPerCluster) && flagNoError) {
+ // allocate next cluster
+ _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError);
+ if (!flagNoError) break;
+ // set indexes to the current position
+ uint32_t chunkEnd = position.cluster;
+ uint32_t nextChunkStart = position.cluster;
+ size_t chunkSize = partition->bytesPerCluster;
+ FILE_POSITION next_position = position;
+
+ // group consecutive clusters
+ while (flagNoError &&
+#ifdef LIMIT_SECTORS
+ (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * BYTES_PER_READ) &&
+#endif
+ (chunkSize + partition->bytesPerCluster < remain))
+ {
+ // pretend to use up all sectors in next_position
+ next_position.sector = partition->sectorsPerCluster;
+ // get or allocate next cluster
+ _FAT_check_position_for_next_cluster(r, &next_position, partition,
+ remain - chunkSize, &flagNoError);
+ if (!flagNoError) break; // exit loop on error
+ nextChunkStart = next_position.cluster;
+ if (nextChunkStart != chunkEnd + 1) break; // exit loop if not consecutive
+ chunkEnd = nextChunkStart;
+ chunkSize += partition->bytesPerCluster;
+ }
+
+ if ( !_FAT_cache_writeSectors (cache,
+ _FAT_fat_clusterToSector(partition, position.cluster), chunkSize / BYTES_PER_READ, ptr))
+ {
+ flagNoError = false;
+ r->_errno = EIO;
+ break;
+ }
+ ptr += chunkSize;
+ remain -= chunkSize;
+
+ if ((chunkEnd != nextChunkStart) && _FAT_fat_isValidCluster(partition, nextChunkStart)) {
+ // new cluster is already allocated (because it was not consecutive)
+ position.cluster = nextChunkStart;
+ position.sector = 0;
+ } else {
+ // Allocate a new cluster when next writing the file
+ position.cluster = chunkEnd;
+ position.sector = partition->sectorsPerCluster;
+ }
+ }
+
+ // allocate next cluster if needed
+ _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError);
+
+ // Write remaining sectors
+ tempVar = remain / BYTES_PER_READ; // Number of sectors left
+ if ((tempVar > 0) && flagNoError) {
+ if (!_FAT_cache_writeSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), tempVar, ptr))
+ {
+ flagNoError = false;
+ r->_errno = EIO;
+ } else {
+ ptr += tempVar * BYTES_PER_READ;
+ remain -= tempVar * BYTES_PER_READ;
+ position.sector += tempVar;
+ }
+ }
+
+ // Last remaining sector
+ if ((remain > 0) && flagNoError) {
+ if (flagAppending) {
+ _FAT_cache_eraseWritePartialSector ( cache, ptr,
+ _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain);
+ } else {
+ _FAT_cache_writePartialSector ( cache, ptr,
+ _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain);
+ }
+ position.byte += remain;
+ remain = 0;
+ }
+
+
+ // Amount written is the originally requested amount minus stuff remaining
+ len = len - remain;
+
+ // Update file information
+ file->modified = true;
+ if (file->append) {
+ // Appending doesn't affect the read pointer
+ file->appendPosition = position;
+ file->filesize += len;
+ } else {
+ // Writing also shifts the read pointer
+ file->rwPosition = position;
+ file->currentPosition += len;
+ if (file->filesize < file->currentPosition) {
+ file->filesize = file->currentPosition;
+ }
+ }
+ _FAT_unlock(&partition->lock);
+
+ return len;
+}
+
+
+off_t _FAT_seek_r (struct _reent *r, int fd, off_t pos, int dir) {
+ FILE_STRUCT* file = (FILE_STRUCT*) fd;
+ PARTITION* partition;
+ uint32_t cluster, nextCluster;
+ int clusCount;
+ off_t newPosition;
+ uint32_t position;
+
+ if ((file == NULL) || (file->inUse == false)) {
+ // invalid file
+ r->_errno = EBADF;
+ return -1;
+ }
+
+ partition = file->partition;
+ _FAT_lock(&partition->lock);
+
+ switch (dir) {
+ case SEEK_SET:
+ newPosition = pos;
+ break;
+ case SEEK_CUR:
+ newPosition = (off_t)file->currentPosition + pos;
+ break;
+ case SEEK_END:
+ newPosition = (off_t)file->filesize + pos;
+ break;
+ default:
+ _FAT_unlock(&partition->lock);
+ r->_errno = EINVAL;
+ return -1;
+ }
+
+ if ((pos > 0) && (newPosition < 0)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EOVERFLOW;
+ return -1;
+ }
+
+ // newPosition can only be larger than the FILE_MAX_SIZE on platforms where
+ // off_t is larger than 32 bits.
+ if (newPosition < 0 || ((sizeof(newPosition) > 4) && newPosition > (off_t)FILE_MAX_SIZE)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EINVAL;
+ return -1;
+ }
+
+ position = (uint32_t)newPosition;
+
+ // Only change the read/write position if it is within the bounds of the current filesize,
+ // or at the very edge of the file
+ if (position <= file->filesize && file->startCluster != CLUSTER_FREE) {
+ // Calculate where the correct cluster is
+ // how many clusters from start of file
+ clusCount = position / partition->bytesPerCluster;
+ cluster = file->startCluster;
+ if (position >= file->currentPosition) {
+ // start from current cluster
+ int currentCount = file->currentPosition / partition->bytesPerCluster;
+ if (file->rwPosition.sector == partition->sectorsPerCluster) {
+ currentCount--;
+ }
+ clusCount -= currentCount;
+ cluster = file->rwPosition.cluster;
+ }
+ // Calculate the sector and byte of the current position,
+ // and store them
+ file->rwPosition.sector = (position % partition->bytesPerCluster) / BYTES_PER_READ;
+ file->rwPosition.byte = position % BYTES_PER_READ;
+
+ nextCluster = _FAT_fat_nextCluster (partition, cluster);
+ while ((clusCount > 0) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) {
+ clusCount--;
+ cluster = nextCluster;
+ nextCluster = _FAT_fat_nextCluster (partition, cluster);
+ }
+
+ // Check if ran out of clusters and it needs to allocate a new one
+ if (clusCount > 0) {
+ if ((clusCount == 1) && (file->filesize == position) && (file->rwPosition.sector == 0)) {
+ // Set flag to allocate a new cluster
+ file->rwPosition.sector = partition->sectorsPerCluster;
+ file->rwPosition.byte = 0;
+ } else {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EINVAL;
+ return -1;
+ }
+ }
+
+ file->rwPosition.cluster = cluster;
+ }
+
+ // Save position
+ file->currentPosition = position;
+
+ _FAT_unlock(&partition->lock);
+ return position;
+}
+
+
+
+int _FAT_fstat_r (struct _reent *r, int fd, struct stat *st) {
+ FILE_STRUCT* file = (FILE_STRUCT*) fd;
+ PARTITION* partition;
+ DIR_ENTRY fileEntry;
+
+ if ((file == NULL) || (file->inUse == false)) {
+ // invalid file
+ r->_errno = EBADF;
+ return -1;
+ }
+
+ partition = file->partition;
+ _FAT_lock(&partition->lock);
+
+ // Get the file's entry data
+ fileEntry.dataStart = file->dirEntryStart;
+ fileEntry.dataEnd = file->dirEntryEnd;
+
+ if (!_FAT_directory_entryFromPosition (partition, &fileEntry)) {
+ _FAT_unlock(&partition->lock);
+ r->_errno = EIO;
+ return -1;
+ }
+
+ // Fill in the stat struct
+ _FAT_directory_entryStat (partition, &fileEntry, st);
+
+ // Fix stats that have changed since the file was openned
+ st->st_ino = (ino_t)(file->startCluster); // The file serial number is the start cluster
+ st->st_size = file->filesize; // File size
+
+ _FAT_unlock(&partition->lock);
+ return 0;
+}
+
+int _FAT_ftruncate_r (struct _reent *r, int fd, off_t len) {
+ FILE_STRUCT* file = (FILE_STRUCT*) fd;
+ PARTITION* partition;
+ int ret=0;
+ uint32_t newSize = (uint32_t)len;
+
+ if (len < 0) {
+ // Trying to truncate to a negative size
+ r->_errno = EINVAL;
+ return -1;
+ }
+
+ if ((sizeof(len) > 4) && len > (off_t)FILE_MAX_SIZE) {
+ // Trying to extend the file beyond what FAT supports
+ r->_errno = EFBIG;
+ return -1;
+ }
+
+ if (!file || !file->inUse) {
+ // invalid file
+ r->_errno = EBADF;
+ return -1;
+ }
+
+ if (!file->write) {
+ // Read-only file
+ r->_errno = EINVAL;
+ return -1;
+ }
+
+ partition = file->partition;
+ _FAT_lock(&partition->lock);
+
+ if (newSize > file->filesize) {
+ // Expanding the file
+ FILE_POSITION savedPosition;
+ uint32_t savedOffset;
+ // Get a new cluster for the start of the file if required
+ if (file->startCluster == CLUSTER_FREE) {
+ uint32_t tempNextCluster = _FAT_fat_linkFreeCluster (partition, CLUSTER_FREE);
+ if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) {
+ // Couldn't get a cluster, so abort immediately
+ _FAT_unlock(&partition->lock);
+ r->_errno = ENOSPC;
+ return -1;
+ }
+ file->startCluster = tempNextCluster;
+
+ file->rwPosition.cluster = file->startCluster;
+ file->rwPosition.sector = 0;
+ file->rwPosition.byte = 0;
+ }
+ // Save the read/write pointer
+ savedPosition = file->rwPosition;
+ savedOffset = file->currentPosition;
+ // Set the position to the new size
+ file->currentPosition = newSize;
+ // Extend the file to the new position
+ if (!_FAT_file_extend_r (r, file)) {
+ ret = -1;
+ }
+ // Set the append position to the new rwPointer
+ if (file->append) {
+ file->appendPosition = file->rwPosition;
+ }
+ // Restore the old rwPointer;
+ file->rwPosition = savedPosition;
+ file->currentPosition = savedOffset;
+ } else if (newSize < file->filesize){
+ // Shrinking the file
+ if (len == 0) {
+ // Cutting the file down to nothing, clear all clusters used
+ _FAT_fat_clearLinks (partition, file->startCluster);
+ file->startCluster = CLUSTER_FREE;
+
+ file->appendPosition.cluster = CLUSTER_FREE;
+ file->appendPosition.sector = 0;
+ file->appendPosition.byte = 0;
+ } else {
+ // Trimming the file down to the required size
+ unsigned int chainLength;
+ uint32_t lastCluster;
+
+ // Drop the unneeded end of the cluster chain.
+ // If the end falls on a cluster boundary, drop that cluster too,
+ // then set a flag to allocate a cluster as needed
+ chainLength = ((newSize-1) / partition->bytesPerCluster) + 1;
+ lastCluster = _FAT_fat_trimChain (partition, file->startCluster, chainLength);
+
+ if (file->append) {
+ file->appendPosition.byte = newSize % BYTES_PER_READ;
+ // Does the end of the file fall on the edge of a cluster?
+ if (newSize % partition->bytesPerCluster == 0) {
+ // Set a flag to allocate a new cluster
+ file->appendPosition.sector = partition->sectorsPerCluster;
+ } else {
+ file->appendPosition.sector = (newSize % partition->bytesPerCluster) / BYTES_PER_READ;
+ }
+ file->appendPosition.cluster = lastCluster;
+ }
+ }
+ } else {
+ // Truncating to same length, so don't do anything
+ }
+
+ file->filesize = newSize;
+ file->modified = true;
+
+ _FAT_unlock(&partition->lock);
+ return ret;
+}
+
+int _FAT_fsync_r (struct _reent *r, int fd) {
+ FILE_STRUCT* file = (FILE_STRUCT*) fd;
+ int ret = 0;
+
+ if (!file->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+
+ _FAT_lock(&file->partition->lock);
+
+ ret = _FAT_syncToDisc (file);
+ if (ret != 0) {
+ r->_errno = ret;
+ ret = -1;
+ }
+
+ _FAT_unlock(&file->partition->lock);
+
+ return ret;
+}
diff --git a/source/libfat/fatfile.h b/source/libfat/fatfile.h
new file mode 100644
index 00000000..f53600af
--- /dev/null
+++ b/source/libfat/fatfile.h
@@ -0,0 +1,105 @@
+/*
+ fatfile.h
+
+ Functions used by the newlib disc stubs to interface with
+ this library
+
+ 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.
+*/
+
+
+#ifndef __FATFILE_H
+#define __FATFILE_H
+
+#include
+#include
+
+#include "common.h"
+#include "partition.h"
+#include "directory.h"
+
+#define FILE_MAX_SIZE ((uint32_t)0xFFFFFFFF) // 4GiB - 1B
+
+typedef struct {
+ u32 cluster;
+ sec_t sector;
+ s32 byte;
+} FILE_POSITION;
+
+struct _FILE_STRUCT;
+
+struct _FILE_STRUCT {
+ uint32_t filesize;
+ uint32_t startCluster;
+ uint32_t currentPosition;
+ FILE_POSITION rwPosition;
+ FILE_POSITION appendPosition;
+ DIR_ENTRY_POSITION dirEntryStart; // Points to the start of the LFN entries of a file, or the alias for no LFN
+ DIR_ENTRY_POSITION dirEntryEnd; // Always points to the file's alias entry
+ PARTITION* partition;
+ struct _FILE_STRUCT* prevOpenFile; // The previous entry in a double-linked list of open files
+ struct _FILE_STRUCT* nextOpenFile; // The next entry in a double-linked list of open files
+ bool read;
+ bool write;
+ bool append;
+ bool inUse;
+ bool modified;
+};
+
+typedef struct _FILE_STRUCT FILE_STRUCT;
+
+extern int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
+
+extern int _FAT_close_r (struct _reent *r, int fd);
+
+extern ssize_t _FAT_write_r (struct _reent *r,int fd, const char *ptr, size_t len);
+
+extern ssize_t _FAT_read_r (struct _reent *r, int fd, char *ptr, size_t len);
+
+extern off_t _FAT_seek_r (struct _reent *r, int fd, off_t pos, int dir);
+
+extern int _FAT_fstat_r (struct _reent *r, int fd, struct stat *st);
+
+extern int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st);
+
+extern int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink);
+
+extern int _FAT_unlink_r (struct _reent *r, const char *name);
+
+extern int _FAT_chdir_r (struct _reent *r, const char *name);
+
+extern int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName);
+
+extern int _FAT_ftruncate_r (struct _reent *r, int fd, off_t len);
+
+extern int _FAT_fsync_r (struct _reent *r, int fd);
+
+/*
+Synchronizes the file data to disc.
+Does no locking of its own -- lock the partition before calling.
+Returns 0 on success, an error code on failure.
+*/
+extern int _FAT_syncToDisc (FILE_STRUCT* file);
+
+#endif // _FATFILE_H
diff --git a/source/libfat/file_allocation_table.c b/source/libfat/file_allocation_table.c
new file mode 100644
index 00000000..639a6374
--- /dev/null
+++ b/source/libfat/file_allocation_table.c
@@ -0,0 +1,383 @@
+/*
+ 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.
+*/
+
+
+#include "file_allocation_table.h"
+#include "partition.h"
+#include
+
+/*
+Gets the cluster linked from input cluster
+*/
+uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster)
+{
+ uint32_t nextCluster = CLUSTER_FREE;
+ sec_t sector;
+ int offset;
+
+ if (cluster == CLUSTER_FREE) {
+ return CLUSTER_FREE;
+ }
+
+ switch (partition->filesysType)
+ {
+ case FS_UNKNOWN:
+ return CLUSTER_ERROR;
+ break;
+
+ case FS_FAT12:
+ {
+ u32 nextCluster_h;
+ sector = partition->fat.fatStart + (((cluster * 3) / 2) / BYTES_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTES_PER_READ;
+
+
+ _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u8));
+
+ offset++;
+
+ if (offset >= BYTES_PER_READ) {
+ offset = 0;
+ sector++;
+ }
+ nextCluster_h = 0;
+
+ _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster_h, sector, offset, sizeof(u8));
+ nextCluster |= (nextCluster_h << 8);
+
+ 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_readLittleEndianValue (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_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u32));
+
+ if (nextCluster >= 0x0FFFFFF7) {
+ nextCluster = CLUSTER_EOF;
+ }
+ break;
+
+ default:
+ return CLUSTER_ERROR;
+ 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, uint32_t cluster, uint32_t value) {
+ sec_t sector;
+ int offset;
+ uint32_t oldValue;
+
+ if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */))
+ {
+ 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_readLittleEndianValue (partition->cache, &oldValue, sector, offset, sizeof(u8));
+
+ value = (value << 4) | (oldValue & 0x0F);
+
+ _FAT_cache_writeLittleEndianValue (partition->cache, value & 0xFF, sector, offset, sizeof(u8));
+
+ offset++;
+ if (offset >= BYTES_PER_READ) {
+ offset = 0;
+ sector++;
+ }
+
+ _FAT_cache_writeLittleEndianValue (partition->cache, (value >> 8) & 0xFF, sector, offset, sizeof(u8));
+
+ } else {
+
+ _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u8));
+
+ offset++;
+ if (offset >= BYTES_PER_READ) {
+ offset = 0;
+ sector++;
+ }
+
+ _FAT_cache_readLittleEndianValue (partition->cache, &oldValue, sector, offset, sizeof(u8));
+
+ value = ((value >> 8) & 0x0F) | (oldValue & 0xF0);
+
+ _FAT_cache_writeLittleEndianValue (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_writeLittleEndianValue (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_writeLittleEndianValue (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_ERROR
+-----------------------------------------------------------------*/
+uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster) {
+ uint32_t firstFree;
+ uint32_t curLink;
+ uint32_t lastCluster;
+ bool loopedAroundFAT = false;
+
+ lastCluster = partition->fat.lastCluster;
+
+ if (cluster > lastCluster) {
+ return CLUSTER_ERROR;
+ }
+
+ // 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 an error
+ partition->fat.firstFree = firstFree;
+ return CLUSTER_ERROR;
+ } 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_ERROR
+-----------------------------------------------------------------*/
+uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster) {
+ uint32_t newCluster;
+ uint32_t i;
+ uint8_t emptySector[BYTES_PER_READ];
+
+ // Link the cluster
+ newCluster = _FAT_fat_linkFreeCluster(partition, cluster);
+
+ if (newCluster == CLUSTER_FREE || newCluster == CLUSTER_ERROR) {
+ return CLUSTER_ERROR;
+ }
+
+ // Clear all the sectors within the cluster
+ memset (emptySector, 0, BYTES_PER_READ);
+ for (i = 0; i < partition->sectorsPerCluster; i++) {
+ _FAT_cache_writeSectors (partition->cache,
+ _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, uint32_t cluster) {
+ uint32_t nextCluster;
+
+ if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */))
+ 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) && (cluster != CLUSTER_ERROR)) {
+ // 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_trimChain
+Drop all clusters past the chainLength.
+If chainLength is 0, all clusters are dropped.
+If chainLength is 1, the first cluster is kept and the rest are
+dropped, and so on.
+Return the last cluster left in the chain.
+-----------------------------------------------------------------*/
+uint32_t _FAT_fat_trimChain (PARTITION* partition, uint32_t startCluster, unsigned int chainLength) {
+ uint32_t nextCluster;
+
+ if (chainLength == 0) {
+ // Drop the entire chain
+ _FAT_fat_clearLinks (partition, startCluster);
+ return CLUSTER_FREE;
+ } else {
+ // Find the last cluster in the chain, and the one after it
+ chainLength--;
+ nextCluster = _FAT_fat_nextCluster (partition, startCluster);
+ while ((chainLength > 0) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) {
+ chainLength--;
+ startCluster = nextCluster;
+ nextCluster = _FAT_fat_nextCluster (partition, startCluster);
+ }
+
+ // Drop all clusters after the last in the chain
+ if (nextCluster != CLUSTER_FREE && nextCluster != CLUSTER_EOF) {
+ _FAT_fat_clearLinks (partition, nextCluster);
+ }
+
+ // Mark the last cluster in the chain as the end of the file
+ _FAT_fat_writeFatEntry (partition, startCluster, CLUSTER_EOF);
+
+ return startCluster;
+ }
+}
+
+/*-----------------------------------------------------------------
+_FAT_fat_lastCluster
+Trace the cluster links until the last one is found
+-----------------------------------------------------------------*/
+uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster) {
+ while ((_FAT_fat_nextCluster(partition, cluster) != CLUSTER_FREE) && (_FAT_fat_nextCluster(partition, cluster) != CLUSTER_EOF)) {
+ cluster = _FAT_fat_nextCluster(partition, cluster);
+ }
+ return cluster;
+}
+
+/*-----------------------------------------------------------------
+_FAT_fat_freeClusterCount
+Return the number of free clusters available
+-----------------------------------------------------------------*/
+unsigned int _FAT_fat_freeClusterCount (PARTITION* partition) {
+ unsigned int count = 0;
+ uint32_t curCluster;
+
+ for (curCluster = CLUSTER_FIRST; curCluster <= partition->fat.lastCluster; curCluster++) {
+ if (_FAT_fat_nextCluster(partition, curCluster) == CLUSTER_FREE) {
+ count++;
+ }
+ }
+
+ return count;
+}
+
diff --git a/source/libfat/file_allocation_table.h b/source/libfat/file_allocation_table.h
new file mode 100644
index 00000000..de500496
--- /dev/null
+++ b/source/libfat/file_allocation_table.h
@@ -0,0 +1,70 @@
+/*
+ file_allocation_table.h
+ 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.
+*/
+
+#ifndef __FAT_H
+#define __FAT_H
+
+#include "common.h"
+#include "partition.h"
+
+#define CLUSTER_EOF_16 0xFFFF
+#define CLUSTER_EOF 0x0FFFFFFF
+#define CLUSTER_FREE 0x00000000
+#define CLUSTER_ROOT 0x00000000
+#define CLUSTER_FIRST 0x00000002
+#define CLUSTER_ERROR 0xFFFFFFFF
+
+#define CLUSTERS_PER_FAT12 4085
+#define CLUSTERS_PER_FAT16 65525
+
+
+uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster);
+
+uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster);
+uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster);
+
+bool _FAT_fat_clearLinks (PARTITION* partition, uint32_t cluster);
+
+uint32_t _FAT_fat_trimChain (PARTITION* partition, uint32_t startCluster, unsigned int chainLength);
+
+uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster);
+
+unsigned int _FAT_fat_freeClusterCount (PARTITION* partition);
+
+static inline sec_t _FAT_fat_clusterToSector (PARTITION* partition, uint32_t cluster) {
+ return (cluster >= CLUSTER_FIRST) ?
+ ((cluster - CLUSTER_FIRST) * (sec_t)partition->sectorsPerCluster) + partition->dataStart :
+ partition->rootDirStart;
+}
+
+static inline bool _FAT_fat_isValidCluster (PARTITION* partition, uint32_t cluster) {
+ return (cluster >= CLUSTER_FIRST) && (cluster <= partition->fat.lastCluster /* This will catch CLUSTER_ERROR */);
+}
+
+#endif // _FAT_H
diff --git a/source/libfat/filetime.c b/source/libfat/filetime.c
new file mode 100644
index 00000000..d297bf64
--- /dev/null
+++ b/source/libfat/filetime.c
@@ -0,0 +1,107 @@
+/*
+ filetime.c
+ Conversion of file time and date values to various other types
+
+ 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.
+*/
+
+
+#include
+#include "filetime.h"
+#include "common.h"
+
+#define MAX_HOUR 23
+#define MAX_MINUTE 59
+#define MAX_SECOND 59
+
+#define MAX_MONTH 11
+#define MIN_MONTH 0
+#define MAX_DAY 31
+#define MIN_DAY 1
+
+uint16_t _FAT_filetime_getTimeFromRTC (void) {
+#ifdef USE_RTC_TIME
+ struct tm timeParts;
+ time_t epochTime;
+
+ if (time(&epochTime) == (time_t)-1) {
+ return 0;
+ }
+ localtime_r(&epochTime, &timeParts);
+
+ // Check that the values are all in range.
+ // If they are not, return 0 (no timestamp)
+ if ((timeParts.tm_hour < 0) || (timeParts.tm_hour > MAX_HOUR)) return 0;
+ if ((timeParts.tm_min < 0) || (timeParts.tm_min > MAX_MINUTE)) return 0;
+ if ((timeParts.tm_sec < 0) || (timeParts.tm_sec > MAX_SECOND)) return 0;
+
+ return (
+ ((timeParts.tm_hour & 0x1F) << 11) |
+ ((timeParts.tm_min & 0x3F) << 5) |
+ ((timeParts.tm_sec >> 1) & 0x1F)
+ );
+#else
+ return 0;
+#endif
+}
+
+
+uint16_t _FAT_filetime_getDateFromRTC (void) {
+#ifdef USE_RTC_TIME
+ struct tm timeParts;
+ time_t epochTime;
+
+ if (time(&epochTime) == (time_t)-1) {
+ return 0;
+ }
+ localtime_r(&epochTime, &timeParts);
+
+ if ((timeParts.tm_mon < MIN_MONTH) || (timeParts.tm_mon > MAX_MONTH)) return 0;
+ if ((timeParts.tm_mday < MIN_DAY) || (timeParts.tm_mday > MAX_DAY)) return 0;
+
+ return (
+ (((timeParts.tm_year - 80) & 0x7F) <<9) | // Adjust for MS-FAT base year (1980 vs 1900 for tm_year)
+ (((timeParts.tm_mon + 1) & 0xF) << 5) |
+ (timeParts.tm_mday & 0x1F)
+ );
+#else
+ return 0;
+#endif
+}
+
+time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d) {
+ struct tm timeParts;
+
+ timeParts.tm_hour = t >> 11;
+ timeParts.tm_min = (t >> 5) & 0x3F;
+ timeParts.tm_sec = (t & 0x1F) << 1;
+
+ timeParts.tm_mday = d & 0x1F;
+ timeParts.tm_mon = ((d >> 5) & 0x0F) - 1;
+ timeParts.tm_year = (d >> 9) + 80;
+
+ timeParts.tm_isdst = 0;
+
+ return mktime(&timeParts);
+}
diff --git a/source/libfat/filetime.h b/source/libfat/filetime.h
new file mode 100644
index 00000000..8ffd539a
--- /dev/null
+++ b/source/libfat/filetime.h
@@ -0,0 +1,41 @@
+/*
+ filetime.h
+ Conversion of file time and date values to various other types
+
+ 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.
+*/
+
+#ifndef __FILETIME_H
+#define __FILETIME_H
+
+#include "common.h"
+#include
+
+uint16_t _FAT_filetime_getTimeFromRTC (void);
+uint16_t _FAT_filetime_getDateFromRTC (void);
+
+time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d);
+
+
+#endif // _FILETIME_H
diff --git a/source/libfat/libfat.c b/source/libfat/libfat.c
new file mode 100644
index 00000000..0b3cbd06
--- /dev/null
+++ b/source/libfat/libfat.c
@@ -0,0 +1,197 @@
+/*
+ libfat.c
+ Simple functionality for startup, mounting and unmounting of FAT-based devices.
+
+ 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.
+*/
+
+#include
+#include
+#include
+
+#include "common.h"
+#include "partition.h"
+#include "fatfile.h"
+#include "fatdir.h"
+#include "lock.h"
+#include "mem_allocate.h"
+#include "disc_fat.h"
+
+static const devoptab_t dotab_fat = {
+ "fat",
+ sizeof (FILE_STRUCT),
+ _FAT_open_r,
+ _FAT_close_r,
+ _FAT_write_r,
+ _FAT_read_r,
+ _FAT_seek_r,
+ _FAT_fstat_r,
+ _FAT_stat_r,
+ _FAT_link_r,
+ _FAT_unlink_r,
+ _FAT_chdir_r,
+ _FAT_rename_r,
+ _FAT_mkdir_r,
+ sizeof (DIR_STATE_STRUCT),
+ _FAT_diropen_r,
+ _FAT_dirreset_r,
+ _FAT_dirnext_r,
+ _FAT_dirclose_r,
+ _FAT_statvfs_r,
+ _FAT_ftruncate_r,
+ _FAT_fsync_r,
+ NULL /* Device data */
+};
+
+bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage) {
+ PARTITION* partition;
+ devoptab_t* devops;
+ char* nameCopy;
+
+ if(!interface->startup())
+ return false;
+
+ if(!interface->isInserted()) {
+ interface->shutdown();
+ return false;
+ }
+
+ devops = _FAT_mem_allocate (sizeof(devoptab_t) + strlen(name) + 1);
+ if (!devops) {
+ interface->shutdown();
+ return false;
+ }
+ // Use the space allocated at the end of the devoptab struct for storing the name
+ nameCopy = (char*)(devops+1);
+
+ // Initialize the file system
+ partition = _FAT_partition_constructor (interface, cacheSize, SectorsPerPage, startSector);
+ if (!partition) {
+ _FAT_mem_free (devops);
+ interface->shutdown();
+ return false;
+ }
+
+ // Add an entry for this device to the devoptab table
+ memcpy (devops, &dotab_fat, sizeof(dotab_fat));
+ strcpy (nameCopy, name);
+ devops->name = nameCopy;
+ devops->deviceData = partition;
+
+ AddDevice (devops);
+
+ return true;
+}
+
+bool fatMountSimple (const char* name, const DISC_INTERFACE* interface) {
+ return fatMount (name, interface, 0, DEFAULT_CACHE_PAGES, DEFAULT_SECTORS_PAGE);
+}
+
+void fatUnmount (const char* name) {
+ devoptab_t *devops;
+ PARTITION* partition;
+ const DISC_INTERFACE *disc;
+
+ devops = (devoptab_t*)GetDeviceOpTab (name);
+ if (!devops) {
+ return;
+ }
+
+ // Perform a quick check to make sure we're dealing with a libfat controlled device
+ if (devops->open_r != dotab_fat.open_r) {
+ return;
+ }
+
+ if (RemoveDevice (name) == -1) {
+ return;
+ }
+
+ partition = (PARTITION*)devops->deviceData;
+ disc = partition->disc;
+ _FAT_partition_destructor (partition);
+ _FAT_mem_free (devops);
+ disc->shutdown();
+}
+
+bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice) {
+ int i;
+ int defaultDevice = -1;
+ const DISC_INTERFACE *disc;
+
+ for (i = 0;
+ _FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL;
+ i++)
+ {
+ disc = _FAT_disc_interfaces[i].getInterface();
+ if (fatMount (_FAT_disc_interfaces[i].name, disc, 0, cacheSize, DEFAULT_SECTORS_PAGE)) {
+ // The first device to successfully mount is set as the default
+ if (defaultDevice < 0) {
+ defaultDevice = i;
+ }
+ }
+ }
+
+ if (defaultDevice < 0) {
+ // None of our devices mounted
+ return false;
+ }
+
+ if (setAsDefaultDevice) {
+ char filePath[MAXPATHLEN * 2];
+ strcpy (filePath, _FAT_disc_interfaces[defaultDevice].name);
+ strcat (filePath, ":/");
+#ifdef ARGV_MAGIC
+ if ( __system_argv->argvMagic == ARGV_MAGIC && __system_argv->argc >= 1 && strrchr( __system_argv->argv[0], '/' )!=NULL ) {
+ // Check the app's path against each of our mounted devices, to see
+ // if we can support it. If so, change to that path.
+ for (i = 0;
+ _FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL;
+ i++)
+ {
+ if ( !strncasecmp( __system_argv->argv[0], _FAT_disc_interfaces[i].name,
+ strlen(_FAT_disc_interfaces[i].name)))
+ {
+ char *lastSlash;
+ strcpy(filePath, __system_argv->argv[0]);
+ lastSlash = strrchr( filePath, '/' );
+
+ if ( NULL != lastSlash) {
+ if ( *(lastSlash - 1) == ':') lastSlash++;
+ *lastSlash = 0;
+ }
+ }
+ }
+ }
+#endif
+ chdir (filePath);
+ }
+
+ return true;
+}
+
+bool fatInitDefault (void) {
+ return fatInit (DEFAULT_CACHE_PAGES, true);
+}
+
+
diff --git a/source/libfat/lock.h b/source/libfat/lock.h
new file mode 100644
index 00000000..73b8902b
--- /dev/null
+++ b/source/libfat/lock.h
@@ -0,0 +1,87 @@
+/*
+ lock.h
+
+ Copyright (c) 2008 Sven Peter
+
+ 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.
+
+*/
+
+#ifndef __LOCK_H
+#define __LOCK_H
+
+#include "common.h"
+
+#ifdef USE_LWP_LOCK
+
+static inline void _FAT_lock_init(mutex_t *mutex)
+{
+ LWP_MutexInit(mutex, false);
+}
+
+static inline void _FAT_lock_deinit(mutex_t *mutex)
+{
+ LWP_MutexDestroy(*mutex);
+}
+
+static inline void _FAT_lock(mutex_t *mutex)
+{
+ LWP_MutexLock(*mutex);
+}
+
+static inline void _FAT_unlock(mutex_t *mutex)
+{
+ LWP_MutexUnlock(*mutex);
+}
+
+#else
+
+// We still need a blank lock type
+#ifndef mutex_t
+typedef int mutex_t;
+#endif
+
+static inline void _FAT_lock_init(mutex_t *mutex)
+{
+ return;
+}
+
+static inline void _FAT_lock_deinit(mutex_t *mutex)
+{
+ return;
+}
+
+static inline void _FAT_lock(mutex_t *mutex)
+{
+ return;
+}
+
+static inline void _FAT_unlock(mutex_t *mutex)
+{
+ return;
+}
+
+#endif // USE_LWP_LOCK
+
+
+#endif // _LOCK_H
+
diff --git a/source/libfat/mem_allocate.h b/source/libfat/mem_allocate.h
new file mode 100644
index 00000000..2d38f9ce
--- /dev/null
+++ b/source/libfat/mem_allocate.h
@@ -0,0 +1,49 @@
+/*
+ mem_allocate.h
+ Memory allocation and destruction calls
+ Replace these calls with custom allocators if
+ malloc is unavailable
+
+ 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.
+*/
+
+#ifndef __MEM_ALLOCATE_H_
+#define __MEM_ALLOCATE_H_
+
+#include
+
+static inline void* _FAT_mem_allocate (size_t size) {
+ return malloc (size);
+}
+
+static inline void* _FAT_mem_align (size_t size) {
+
+ return memalign (32, size);
+}
+
+static inline void _FAT_mem_free (void* mem) {
+ free (mem);
+}
+
+#endif // _MEM_ALLOCATE_H
diff --git a/source/libfat/partition.c b/source/libfat/partition.c
new file mode 100644
index 00000000..955468de
--- /dev/null
+++ b/source/libfat/partition.c
@@ -0,0 +1,312 @@
+/*
+ partition.c
+ Functions for mounting and dismounting partitions
+ on various block devices.
+
+ 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.
+*/
+
+#include "partition.h"
+#include "bit_ops.h"
+#include "file_allocation_table.h"
+#include "directory.h"
+#include "mem_allocate.h"
+#include "fatfile.h"
+
+#include
+#include
+#include
+
+sec_t _FAT_startSector;
+
+/*
+This device name, as known by devkitPro toolchains
+*/
+const char* DEVICE_NAME = "fat";
+
+/*
+Data offsets
+*/
+
+// BIOS Parameter Block offsets
+enum BPB {
+ BPB_jmpBoot = 0x00,
+ BPB_OEMName = 0x03,
+ // BIOS Parameter Block
+ BPB_bytesPerSector = 0x0B,
+ BPB_sectorsPerCluster = 0x0D,
+ BPB_reservedSectors = 0x0E,
+ BPB_numFATs = 0x10,
+ BPB_rootEntries = 0x11,
+ BPB_numSectorsSmall = 0x13,
+ BPB_mediaDesc = 0x15,
+ BPB_sectorsPerFAT = 0x16,
+ BPB_sectorsPerTrk = 0x18,
+ BPB_numHeads = 0x1A,
+ BPB_numHiddenSectors = 0x1C,
+ BPB_numSectors = 0x20,
+ // Ext BIOS Parameter Block for FAT16
+ BPB_FAT16_driveNumber = 0x24,
+ BPB_FAT16_reserved1 = 0x25,
+ BPB_FAT16_extBootSig = 0x26,
+ BPB_FAT16_volumeID = 0x27,
+ BPB_FAT16_volumeLabel = 0x2B,
+ BPB_FAT16_fileSysType = 0x36,
+ // Bootcode
+ BPB_FAT16_bootCode = 0x3E,
+ // FAT32 extended block
+ BPB_FAT32_sectorsPerFAT32 = 0x24,
+ BPB_FAT32_extFlags = 0x28,
+ BPB_FAT32_fsVer = 0x2A,
+ BPB_FAT32_rootClus = 0x2C,
+ BPB_FAT32_fsInfo = 0x30,
+ BPB_FAT32_bkBootSec = 0x32,
+ // Ext BIOS Parameter Block for FAT32
+ BPB_FAT32_driveNumber = 0x40,
+ BPB_FAT32_reserved1 = 0x41,
+ BPB_FAT32_extBootSig = 0x42,
+ BPB_FAT32_volumeID = 0x43,
+ BPB_FAT32_volumeLabel = 0x47,
+ BPB_FAT32_fileSysType = 0x52,
+ // Bootcode
+ BPB_FAT32_bootCode = 0x5A,
+ BPB_bootSig_55 = 0x1FE,
+ BPB_bootSig_AA = 0x1FF
+};
+
+static const char FAT_SIG[3] = {'F', 'A', 'T'};
+
+
+sec_t FindFirstValidPartition(const DISC_INTERFACE* disc)
+{
+ uint8_t part_table[16*4];
+ uint8_t *ptr;
+ int i;
+
+ uint8_t sectorBuffer[BYTES_PER_READ] = {0};
+
+ // Read first sector of disc
+ if (!_FAT_disc_readSectors (disc, 0, 1, sectorBuffer)) {
+ return 0;
+ }
+
+ memcpy(part_table,sectorBuffer+0x1BE,16*4);
+ ptr = part_table;
+
+ for(i=0;i<4;i++,ptr+=16) {
+ sec_t part_lba = u8array_to_u32(ptr, 0x8);
+
+ if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) ||
+ !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
+ return part_lba;
+ }
+
+ if(ptr[4]==0) continue;
+
+ if(ptr[4]==0x0F) {
+ sec_t part_lba2=part_lba;
+ sec_t next_lba2=0;
+ int n;
+
+ for(n=0;n<8;n++) // max 8 logic partitions
+ {
+ if(!_FAT_disc_readSectors (disc, part_lba+next_lba2, 1, sectorBuffer)) return 0;
+
+ part_lba2 = part_lba + next_lba2 + u8array_to_u32(sectorBuffer, 0x1C6) ;
+ next_lba2 = u8array_to_u32(sectorBuffer, 0x1D6);
+
+ if(!_FAT_disc_readSectors (disc, part_lba2, 1, sectorBuffer)) return 0;
+
+ if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) ||
+ !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
+ {
+ return part_lba2;
+ }
+
+ if(next_lba2==0) break;
+ }
+ } else {
+ if(!_FAT_disc_readSectors (disc, part_lba, 1, sectorBuffer)) return 0;
+ if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) ||
+ !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
+ return part_lba;
+ }
+ }
+ }
+ return 0;
+}
+
+PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t sectorsPerPage, sec_t startSector) {
+ PARTITION* partition;
+ uint8_t sectorBuffer[BYTES_PER_READ] = {0};
+
+ // Read first sector of disc
+ if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) {
+ return NULL;
+ }
+
+ // Make sure it is a valid MBR or boot sector
+ if ( (sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA)) {
+ return NULL;
+ }
+
+ if (startSector != 0) {
+ // We're told where to start the partition, so just accept it
+ } else if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
+ // Check if there is a FAT string, which indicates this is a boot sector
+ startSector = 0;
+ } else if (!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
+ // Check for FAT32
+ startSector = 0;
+ } else {
+ startSector = FindFirstValidPartition(disc);
+ if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) {
+ return NULL;
+ }
+ }
+
+ // Now verify that this is indeed a FAT partition
+ if (memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) &&
+ memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
+ {
+ return NULL;
+ }
+
+ // check again for the last two cases to make sure that we really have a FAT filesystem here
+ // and won't corrupt any data
+ if(memcmp(sectorBuffer + BPB_FAT16_fileSysType, "FAT", 3) != 0 && memcmp(sectorBuffer + BPB_FAT32_fileSysType, "FAT32", 5) != 0)
+ {
+ return NULL;
+ }
+
+ partition = (PARTITION*) _FAT_mem_allocate (sizeof(PARTITION));
+ if (partition == NULL) {
+ return NULL;
+ }
+
+ _FAT_startSector = startSector;
+
+ // Init the partition lock
+ _FAT_lock_init(&partition->lock);
+
+ // Set partition's disc interface
+ partition->disc = disc;
+
+ // Store required information about the file system
+ partition->fat.sectorsPerFat = u8array_to_u16(sectorBuffer, BPB_sectorsPerFAT);
+ if (partition->fat.sectorsPerFat == 0) {
+ partition->fat.sectorsPerFat = u8array_to_u32( sectorBuffer, BPB_FAT32_sectorsPerFAT32);
+ }
+
+ partition->numberOfSectors = u8array_to_u16( sectorBuffer, BPB_numSectorsSmall);
+ if (partition->numberOfSectors == 0) {
+ partition->numberOfSectors = u8array_to_u32( sectorBuffer, BPB_numSectors);
+ }
+
+ partition->bytesPerSector = BYTES_PER_READ; // Sector size is redefined to be 512 bytes
+ partition->sectorsPerCluster = sectorBuffer[BPB_sectorsPerCluster] * u8array_to_u16(sectorBuffer, BPB_bytesPerSector) / BYTES_PER_READ;
+ partition->bytesPerCluster = partition->bytesPerSector * partition->sectorsPerCluster;
+ partition->fat.fatStart = startSector + u8array_to_u16(sectorBuffer, BPB_reservedSectors);
+
+ partition->rootDirStart = partition->fat.fatStart + (sectorBuffer[BPB_numFATs] * partition->fat.sectorsPerFat);
+ partition->dataStart = partition->rootDirStart +
+ (( u8array_to_u16(sectorBuffer, BPB_rootEntries) * DIR_ENTRY_DATA_SIZE) / partition->bytesPerSector);
+
+ partition->totalSize = ((uint64_t)partition->numberOfSectors - (partition->dataStart - startSector)) * (uint64_t)partition->bytesPerSector;
+
+ // Store info about FAT
+ uint32_t clusterCount = (partition->numberOfSectors - (uint32_t)(partition->dataStart - startSector)) / partition->sectorsPerCluster;
+ partition->fat.lastCluster = clusterCount + CLUSTER_FIRST - 1;
+ partition->fat.firstFree = CLUSTER_FIRST;
+
+ if (clusterCount < CLUSTERS_PER_FAT12) {
+ partition->filesysType = FS_FAT12; // FAT12 volume
+ } else if (clusterCount < CLUSTERS_PER_FAT16) {
+ partition->filesysType = FS_FAT16; // FAT16 volume
+ } else {
+ partition->filesysType = FS_FAT32; // FAT32 volume
+ }
+
+ if (partition->filesysType != FS_FAT32) {
+ partition->rootDirCluster = FAT16_ROOT_DIR_CLUSTER;
+ } else {
+ // Set up for the FAT32 way
+ partition->rootDirCluster = u8array_to_u32(sectorBuffer, BPB_FAT32_rootClus);
+ // Check if FAT mirroring is enabled
+ if (!(sectorBuffer[BPB_FAT32_extFlags] & 0x80)) {
+ // Use the active FAT
+ partition->fat.fatStart = partition->fat.fatStart + ( partition->fat.sectorsPerFat * (sectorBuffer[BPB_FAT32_extFlags] & 0x0F));
+ }
+ }
+
+ // Create a cache to use
+ partition->cache = _FAT_cache_constructor (cacheSize, sectorsPerPage, partition->disc, startSector+partition->numberOfSectors);
+
+ // Set current directory to the root
+ partition->cwdCluster = partition->rootDirCluster;
+
+ // Check if this disc is writable, and set the readOnly property appropriately
+ partition->readOnly = !(_FAT_disc_features(disc) & FEATURE_MEDIUM_CANWRITE);
+
+ // There are currently no open files on this partition
+ partition->openFileCount = 0;
+ partition->firstOpenFile = NULL;
+
+ return partition;
+}
+
+void _FAT_partition_destructor (PARTITION* partition) {
+ FILE_STRUCT* nextFile;
+
+ _FAT_lock(&partition->lock);
+
+ // Synchronize open files
+ nextFile = partition->firstOpenFile;
+ while (nextFile) {
+ _FAT_syncToDisc (nextFile);
+ nextFile = nextFile->nextOpenFile;
+ }
+
+ // Free memory used by the cache, writing it to disc at the same time
+ _FAT_cache_destructor (partition->cache);
+
+ // Unlock the partition and destroy the lock
+ _FAT_unlock(&partition->lock);
+ _FAT_lock_deinit(&partition->lock);
+
+ // Free memory used by the partition
+ _FAT_mem_free (partition);
+}
+
+PARTITION* _FAT_partition_getPartitionFromPath (const char* path) {
+ const devoptab_t *devops;
+
+ devops = GetDeviceOpTab (path);
+
+ if (!devops) {
+ return NULL;
+ }
+
+ return (PARTITION*)devops->deviceData;
+}
diff --git a/source/libfat/partition.h b/source/libfat/partition.h
new file mode 100644
index 00000000..7cfb5f91
--- /dev/null
+++ b/source/libfat/partition.h
@@ -0,0 +1,88 @@
+/*
+ partition.h
+ Functions for mounting and dismounting partitions
+ on various block devices.
+
+ 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.
+*/
+
+#ifndef __PARTITION_H
+#define __PARTITION_H
+
+#include "common.h"
+#include "cache.h"
+#include "lock.h"
+
+// Device name
+extern const char* DEVICE_NAME;
+
+// Filesystem type
+typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE;
+
+typedef struct {
+ sec_t fatStart;
+ uint32_t sectorsPerFat;
+ uint32_t lastCluster;
+ uint32_t firstFree;
+} FAT;
+
+typedef struct {
+ const DISC_INTERFACE* disc;
+ CACHE* cache;
+ // Info about the partition
+ FS_TYPE filesysType;
+ uint64_t totalSize;
+ sec_t rootDirStart;
+ uint32_t rootDirCluster;
+ uint32_t numberOfSectors;
+ sec_t dataStart;
+ uint32_t bytesPerSector;
+ uint32_t sectorsPerCluster;
+ uint32_t bytesPerCluster;
+ FAT fat;
+ // Values that may change after construction
+ uint32_t cwdCluster; // Current working directory cluster
+ int openFileCount;
+ struct _FILE_STRUCT* firstOpenFile; // The start of a linked list of files
+ mutex_t lock; // A lock for partition operations
+ bool readOnly; // If this is set, then do not try writing to the disc
+} PARTITION;
+
+/*
+Mount the supplied device and return a pointer to the struct necessary to use it
+*/
+PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t SectorsPerPage, sec_t startSector);
+
+/*
+Dismount the device and free all structures used.
+Will also attempt to synchronise all open files to disc.
+*/
+void _FAT_partition_destructor (PARTITION* partition);
+
+/*
+Return the partition specified in a path, as taken from the devoptab.
+*/
+PARTITION* _FAT_partition_getPartitionFromPath (const char* path);
+
+#endif // _PARTITION_H
diff --git a/source/listfiles.c b/source/listfiles.c
index 5f831d67..8537a789 100644
--- a/source/listfiles.c
+++ b/source/listfiles.c
@@ -2,12 +2,12 @@
#include
#include
#include
-#include
#include
#include
#include
#include "listfiles.h"
+#include "libfat/fat.h"
static char alldirfiles[300][70];
diff --git a/source/main.cpp b/source/main.cpp
index 6d51c940..3ec2450d 100644
--- a/source/main.cpp
+++ b/source/main.cpp
@@ -36,7 +36,7 @@
#include "fat.h"
#include "gecko.h"
#include "svnrev.h"
-#include "usbloader/partition.h"
+#include "usbloader/partition_usbloader.h"
#include "usbloader/usbstorage.h"
extern bool geckoinit;
diff --git a/source/prompts/PromptWindows.cpp b/source/prompts/PromptWindows.cpp
index afbe4cfa..b78c636c 100644
--- a/source/prompts/PromptWindows.cpp
+++ b/source/prompts/PromptWindows.cpp
@@ -9,7 +9,7 @@
#include "usbloader/wbfs.h"
#include "usbloader/wdvd.h"
-#include "usbloader/partition.h"
+#include "usbloader/partition_usbloader.h"
#include "usbloader/usbstorage.h"
#include "usbloader/getentries.h"
#include "language/gettext.h"
diff --git a/source/prompts/PromptWindows.h b/source/prompts/PromptWindows.h
index ce22deae..587196d3 100644
--- a/source/prompts/PromptWindows.h
+++ b/source/prompts/PromptWindows.h
@@ -8,7 +8,7 @@
#ifndef _PROMPTWINDOWS_H_
#define _PROMPTWINDOWS_H_
-#include "usbloader/partition.h"
+#include "usbloader/partition_usbloader.h"
#define NOTFULLCHANNEL
int WindowPrompt(const char *title, const char *msg = NULL, const char *btn1Label = NULL,
diff --git a/source/settings/Settings.cpp b/source/settings/Settings.cpp
index b9d0ee89..523cb5a8 100644
--- a/source/settings/Settings.cpp
+++ b/source/settings/Settings.cpp
@@ -17,7 +17,7 @@
#include "listfiles.h"
#include "sys.h"
#include "cfg.h"
-#include "usbloader/partition.h"
+#include "usbloader/partition_usbloader.h"
#include "usbloader/utils.h"
#define MAXOPTIONS 13
diff --git a/source/usbloader/apploader.c b/source/usbloader/apploader.c
index 84c63abc..174f1b5d 100644
--- a/source/usbloader/apploader.c
+++ b/source/usbloader/apploader.c
@@ -256,7 +256,25 @@ void PretendThereIsADiscInTheDrive(void *buffer, u32 len)
/** Thanks to WiiPower **/
bool NewSuperMarioBrosPatch(void *Address, int Size)
{
- if (memcmp("SMN", (char *)0x80000000, 3) == 0)
+ if (memcmp("SMNE", (char *)0x80000000, 4) == 0)
+ {
+ u8 SearchPattern[32] = { 0x94, 0x21, 0xFF, 0xD0, 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x34, 0x39, 0x61, 0x00, 0x30, 0x48, 0x12, 0xD7, 0x89, 0x7C, 0x7B, 0x1B, 0x78, 0x7C, 0x9C, 0x23, 0x78, 0x7C, 0xBD, 0x2B, 0x78 };
+ u8 PatchData[32] = { 0x4E, 0x80, 0x00, 0x20, 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x34, 0x39, 0x61, 0x00, 0x30, 0x48, 0x12, 0xD7, 0x89, 0x7C, 0x7B, 0x1B, 0x78, 0x7C, 0x9C, 0x23, 0x78, 0x7C, 0xBD, 0x2B, 0x78 };
+
+ void *Addr = Address;
+ void *Addr_end = Address+Size;
+
+ while(Addr <= Addr_end-sizeof(SearchPattern))
+ {
+ if(memcmp(Addr, SearchPattern, sizeof(SearchPattern))==0)
+ {
+ memcpy(Addr,PatchData,sizeof(PatchData));
+ return true;
+ }
+ Addr += 4;
+ }
+ }
+ else if (memcmp("SMN", (char *)0x80000000, 3) == 0)
{
u8 SearchPattern[32] = { 0x94, 0x21, 0xFF, 0xD0, 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x34, 0x39, 0x61, 0x00, 0x30, 0x48, 0x12, 0xD9, 0x39, 0x7C, 0x7B, 0x1B, 0x78, 0x7C, 0x9C, 0x23, 0x78, 0x7C, 0xBD, 0x2B, 0x78 };
u8 PatchData[32] = { 0x4E, 0x80, 0x00, 0x20, 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x34, 0x39, 0x61, 0x00, 0x30, 0x48, 0x12, 0xD9, 0x39, 0x7C, 0x7B, 0x1B, 0x78, 0x7C, 0x9C, 0x23, 0x78, 0x7C, 0xBD, 0x2B, 0x78 };
diff --git a/source/usbloader/partition.c b/source/usbloader/partition_usbloader.c
similarity index 95%
rename from source/usbloader/partition.c
rename to source/usbloader/partition_usbloader.c
index 58233185..8ccccd2b 100644
--- a/source/usbloader/partition.c
+++ b/source/usbloader/partition_usbloader.c
@@ -1,190 +1,190 @@
-#include
-#include
-#include
-
-#include "partition.h"
-#include "usbstorage.h"
-#include "sdhc.h"
-#include "utils.h"
-#include "libwbfs/libwbfs.h"
-#include "wbfs.h"
-
-/* 'partition table' structure */
-typedef struct {
- /* Zero bytes */
- u8 padding[446];
-
- /* Partition table entries */
- partitionEntry entries[MAX_PARTITIONS];
-} ATTRIBUTE_PACKED partitionTable;
-
-s32 Partition_GetEntries(partitionEntry *outbuf, u32 *outval) {
- static partitionTable table ATTRIBUTE_ALIGN(32);
-
- u32 cnt, sector_size;
- s32 ret;
-
- /* Get sector size */
- ret = USBStorage_GetCapacity(§or_size);
- if (ret < 0)
- return ret;
-
- /* Read partition table */
- ret = USBStorage_ReadSectors(0, 1, &table);
- if (ret < 0)
- return ret;
-
- /* Swap endianess */
- for (cnt = 0; cnt < 4; cnt++) {
- partitionEntry *entry = &table.entries[cnt];
-
- entry->sector = swap32(entry->sector);
- entry->size = swap32(entry->size);
- }
-
- /* Set partition entries */
- memcpy(outbuf, table.entries, sizeof(table.entries));
-
- /* Set sector size */
- *outval = sector_size;
-
- return 0;
-}
-
-s32 Partition_GetEntriesEx(partitionEntry *outbuf, u32 *outval, int *num)
-{
- static partitionTable table ATTRIBUTE_ALIGN(32);
- partitionEntry *entry;
-
- u32 i, sector_size;
- s32 ret;
- int maxpart = *num;
-
- // Get sector size
- ret = USBStorage_GetCapacity(§or_size);
- if (ret == 0) return -1;
-
- u32 ext = 0;
- u32 next = 0;
-
- // Read partition table
- ret = USBStorage_ReadSectors(0, 1, &table);
- if (!ret) return -1;
- /* Swap endianess */
- for (i = 0; i < 4; i++) {
- entry = &table.entries[i];
- entry->sector = swap32(entry->sector);
- entry->size = swap32(entry->size);
- if (!ext && entry->type == 0x0f) ext = entry->sector;
- }
- /* Set partition entries */
- memcpy(outbuf, table.entries, sizeof(table.entries));
- /* Set sector size */
- *outval = sector_size;
- // num primary
- *num = 4;
-
- next = ext;
- // scan extended partition for logical
- if (ext) for(i=0; isector = swap32(entry->sector);
- entry->size = swap32(entry->size);
- if (entry->type && entry->size && entry->sector) {
- // rebase to abolute address
- entry->sector += next;
- // add logical
- memcpy(&outbuf[*num], entry, sizeof(*entry));
- (*num)++;
- // get next
- entry++;
- if (entry->type && entry->size && entry->sector) {
- next = ext + swap32(entry->sector);
- } else {
- break;
- }
- }
-
- }
-
- return 0;
-}
-
-char* part_type_data(int type)
-{
- switch (type) {
- case 0x01: return "FAT12";
- case 0x04: return "FAT16";
- case 0x06: return "FAT16"; //+
- case 0x07: return "NTFS";
- case 0x0b: return "FAT32";
- case 0x0c: return "FAT32";
- case 0x0e: return "FAT16";
- case 0x82: return "LxSWP";
- case 0x83: return "LINUX";
- case 0x8e: return "LxLVM";
- case 0xa8: return "OSX";
- case 0xab: return "OSXBT";
- case 0xaf: return "OSXHF";
- case 0xe8: return "LUKS";
- }
- return NULL;
-}
-
-int get_fs_type(char *buf)
-{
- // WBFS
- wbfs_head_t *head = (wbfs_head_t *)buf;
- if (head->magic == wbfs_htonl(WBFS_MAGIC)) return FS_TYPE_WBFS;
- // 55AA
- if (buf[0x1FE] == 0x55 && buf[0x1FF] == 0xAA) {
- // FAT
- if (memcmp(buf+0x36,"FAT",3) == 0) return FS_TYPE_FAT16;
- if (memcmp(buf+0x52,"FAT",3) == 0) return FS_TYPE_FAT32;
- // NTFS
- if (memcmp(buf+0x03,"NTFS",4) == 0) return FS_TYPE_NTFS;
- }
- return FS_TYPE_UNK;
-}
-
-bool is_type_fat(int type)
-{
- return (type == FS_TYPE_FAT16 || type == FS_TYPE_FAT32);
-}
-
-s32 Partition_GetList(PartList *plist)
-{
- partitionEntry *entry = NULL;
- PartInfo *pinfo = NULL;
- int i, ret;
-
- memset(plist, 0, sizeof(PartList));
-
- // Get partition entries
- plist->num = MAX_PARTITIONS_EX;
- ret = Partition_GetEntriesEx(plist->pentry, &plist->sector_size, &plist->num);
- if (ret < 0) {
- return -1;
- }
- char buf[plist->sector_size];
-
- // scan partitions for filesystem type
- for (i = 0; i < plist->num; i++) {
- pinfo = &plist->pinfo[i];
- entry = &plist->pentry[i];
- if (!entry->size) continue;
- if (!part_type_data(entry->type)) continue;
- if (!USBStorage_ReadSectors(entry->sector, 1, buf)) continue;
- pinfo->fs_type = get_fs_type(buf);
- if (pinfo->fs_type == FS_TYPE_WBFS) {
- plist->wbfs_n++;
- pinfo->wbfs_i = plist->wbfs_n;
- } else if (is_type_fat(pinfo->fs_type)) {
- plist->fat_n++;
- pinfo->fat_i = plist->fat_n;
- }
- }
- return 0;
-}
+#include
+#include
+#include
+
+#include "partition_usbloader.h"
+#include "usbstorage.h"
+#include "sdhc.h"
+#include "utils.h"
+#include "libwbfs/libwbfs.h"
+#include "wbfs.h"
+
+/* 'partition table' structure */
+typedef struct {
+ /* Zero bytes */
+ u8 padding[446];
+
+ /* Partition table entries */
+ partitionEntry entries[MAX_PARTITIONS];
+} ATTRIBUTE_PACKED partitionTable;
+
+s32 Partition_GetEntries(partitionEntry *outbuf, u32 *outval) {
+ static partitionTable table ATTRIBUTE_ALIGN(32);
+
+ u32 cnt, sector_size;
+ s32 ret;
+
+ /* Get sector size */
+ ret = USBStorage_GetCapacity(§or_size);
+ if (ret < 0)
+ return ret;
+
+ /* Read partition table */
+ ret = USBStorage_ReadSectors(0, 1, &table);
+ if (ret < 0)
+ return ret;
+
+ /* Swap endianess */
+ for (cnt = 0; cnt < 4; cnt++) {
+ partitionEntry *entry = &table.entries[cnt];
+
+ entry->sector = swap32(entry->sector);
+ entry->size = swap32(entry->size);
+ }
+
+ /* Set partition entries */
+ memcpy(outbuf, table.entries, sizeof(table.entries));
+
+ /* Set sector size */
+ *outval = sector_size;
+
+ return 0;
+}
+
+s32 Partition_GetEntriesEx(partitionEntry *outbuf, u32 *outval, int *num)
+{
+ static partitionTable table ATTRIBUTE_ALIGN(32);
+ partitionEntry *entry;
+
+ u32 i, sector_size;
+ s32 ret;
+ int maxpart = *num;
+
+ // Get sector size
+ ret = USBStorage_GetCapacity(§or_size);
+ if (ret == 0) return -1;
+
+ u32 ext = 0;
+ u32 next = 0;
+
+ // Read partition table
+ ret = USBStorage_ReadSectors(0, 1, &table);
+ if (!ret) return -1;
+ /* Swap endianess */
+ for (i = 0; i < 4; i++) {
+ entry = &table.entries[i];
+ entry->sector = swap32(entry->sector);
+ entry->size = swap32(entry->size);
+ if (!ext && entry->type == 0x0f) ext = entry->sector;
+ }
+ /* Set partition entries */
+ memcpy(outbuf, table.entries, sizeof(table.entries));
+ /* Set sector size */
+ *outval = sector_size;
+ // num primary
+ *num = 4;
+
+ next = ext;
+ // scan extended partition for logical
+ if (ext) for(i=0; isector = swap32(entry->sector);
+ entry->size = swap32(entry->size);
+ if (entry->type && entry->size && entry->sector) {
+ // rebase to abolute address
+ entry->sector += next;
+ // add logical
+ memcpy(&outbuf[*num], entry, sizeof(*entry));
+ (*num)++;
+ // get next
+ entry++;
+ if (entry->type && entry->size && entry->sector) {
+ next = ext + swap32(entry->sector);
+ } else {
+ break;
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+char* part_type_data(int type)
+{
+ switch (type) {
+ case 0x01: return "FAT12";
+ case 0x04: return "FAT16";
+ case 0x06: return "FAT16"; //+
+ case 0x07: return "NTFS";
+ case 0x0b: return "FAT32";
+ case 0x0c: return "FAT32";
+ case 0x0e: return "FAT16";
+ case 0x82: return "LxSWP";
+ case 0x83: return "LINUX";
+ case 0x8e: return "LxLVM";
+ case 0xa8: return "OSX";
+ case 0xab: return "OSXBT";
+ case 0xaf: return "OSXHF";
+ case 0xe8: return "LUKS";
+ }
+ return NULL;
+}
+
+int get_fs_type(char *buf)
+{
+ // WBFS
+ wbfs_head_t *head = (wbfs_head_t *)buf;
+ if (head->magic == wbfs_htonl(WBFS_MAGIC)) return FS_TYPE_WBFS;
+ // 55AA
+ if (buf[0x1FE] == 0x55 && buf[0x1FF] == 0xAA) {
+ // FAT
+ if (memcmp(buf+0x36,"FAT",3) == 0) return FS_TYPE_FAT16;
+ if (memcmp(buf+0x52,"FAT",3) == 0) return FS_TYPE_FAT32;
+ // NTFS
+ if (memcmp(buf+0x03,"NTFS",4) == 0) return FS_TYPE_NTFS;
+ }
+ return FS_TYPE_UNK;
+}
+
+bool is_type_fat(int type)
+{
+ return (type == FS_TYPE_FAT16 || type == FS_TYPE_FAT32);
+}
+
+s32 Partition_GetList(PartList *plist)
+{
+ partitionEntry *entry = NULL;
+ PartInfo *pinfo = NULL;
+ int i, ret;
+
+ memset(plist, 0, sizeof(PartList));
+
+ // Get partition entries
+ plist->num = MAX_PARTITIONS_EX;
+ ret = Partition_GetEntriesEx(plist->pentry, &plist->sector_size, &plist->num);
+ if (ret < 0) {
+ return -1;
+ }
+ char buf[plist->sector_size];
+
+ // scan partitions for filesystem type
+ for (i = 0; i < plist->num; i++) {
+ pinfo = &plist->pinfo[i];
+ entry = &plist->pentry[i];
+ if (!entry->size) continue;
+ if (!part_type_data(entry->type)) continue;
+ if (!USBStorage_ReadSectors(entry->sector, 1, buf)) continue;
+ pinfo->fs_type = get_fs_type(buf);
+ if (pinfo->fs_type == FS_TYPE_WBFS) {
+ plist->wbfs_n++;
+ pinfo->wbfs_i = plist->wbfs_n;
+ } else if (is_type_fat(pinfo->fs_type)) {
+ plist->fat_n++;
+ pinfo->fat_i = plist->fat_n;
+ }
+ }
+ return 0;
+}
diff --git a/source/usbloader/partition.h b/source/usbloader/partition_usbloader.h
similarity index 100%
rename from source/usbloader/partition.h
rename to source/usbloader/partition_usbloader.h
diff --git a/source/usbloader/wbfs.c b/source/usbloader/wbfs.c
index f1938ac5..c6ff999f 100644
--- a/source/usbloader/wbfs.c
+++ b/source/usbloader/wbfs.c
@@ -12,7 +12,7 @@
#include "wbfs.h"
#include "wbfs_fat.h"
#include "fatmounter.h"
-#include "partition.h"
+#include "partition_usbloader.h"
#include "libwbfs/libwbfs.h"
diff --git a/source/usbloader/wbfs_fat.c b/source/usbloader/wbfs_fat.c
index c1475d3a..62ea2756 100644
--- a/source/usbloader/wbfs_fat.c
+++ b/source/usbloader/wbfs_fat.c
@@ -18,7 +18,7 @@
#include "wdvd.h"
#include "splits.h"
#include "fat.h"
-#include "partition.h"
+#include "partition_usbloader.h"
#include "wpad.h"
#include "wbfs_fat.h"
#include "disc.h"
diff --git a/source/wad/title.h b/source/wad/title.h
index acde9a2c..7667256f 100644
--- a/source/wad/title.h
+++ b/source/wad/title.h
@@ -9,7 +9,7 @@ Copyright (C) 2008 tona and/or waninkoko
#include
#include
#include
-#include
+#include "libfat/fat.h"
// Turn upper and lower into a full title ID
#define TITLE_ID(x,y) (((u64)(x) << 32) | (y))
@@ -60,7 +60,7 @@ extern "C" {
char *__getTitleName(u64 titleid, int language);
s32 Uninstall_FromTitle(const u64 tid);
-
+
//check for a game save present on nand based on game ID
int CheckForSave(const char *gameID);