aboutsummaryrefslogtreecommitdiff
path: root/sdk-modifications/libsrc/fs
diff options
context:
space:
mode:
Diffstat (limited to 'sdk-modifications/libsrc/fs')
-rw-r--r--sdk-modifications/libsrc/fs/bit_ops.h58
-rw-r--r--sdk-modifications/libsrc/fs/cache.c273
-rw-r--r--sdk-modifications/libsrc/fs/directory.c1005
-rw-r--r--sdk-modifications/libsrc/fs/directory.h180
-rw-r--r--sdk-modifications/libsrc/fs/disc_io/disc.c194
-rw-r--r--sdk-modifications/libsrc/fs/disc_io/disc.h128
-rw-r--r--sdk-modifications/libsrc/fs/disc_io/disc_io.h83
-rw-r--r--sdk-modifications/libsrc/fs/disc_io/io_ds2_mmcf.c13
-rw-r--r--sdk-modifications/libsrc/fs/disc_io/io_ds2_mmcf.h63
-rw-r--r--sdk-modifications/libsrc/fs/ds2_fcntl.c126
-rw-r--r--sdk-modifications/libsrc/fs/ds2_fcntl.h51
-rw-r--r--sdk-modifications/libsrc/fs/ds2_unistd.c18
-rw-r--r--sdk-modifications/libsrc/fs/ds2_unistd.h22
-rw-r--r--sdk-modifications/libsrc/fs/fat.h118
-rw-r--r--sdk-modifications/libsrc/fs/fat_misc.c138
-rw-r--r--sdk-modifications/libsrc/fs/fat_misc.h38
-rw-r--r--sdk-modifications/libsrc/fs/fatdir.c672
-rw-r--r--sdk-modifications/libsrc/fs/fatdir.h85
-rw-r--r--sdk-modifications/libsrc/fs/fatdir_ex.c183
-rw-r--r--sdk-modifications/libsrc/fs/fatdir_ex.h18
-rw-r--r--sdk-modifications/libsrc/fs/fatfile.c889
-rw-r--r--sdk-modifications/libsrc/fs/fatfile.h89
-rw-r--r--sdk-modifications/libsrc/fs/fatfile_ex.c47
-rw-r--r--sdk-modifications/libsrc/fs/fatfile_ex.h19
-rw-r--r--sdk-modifications/libsrc/fs/file_allocation_table.c330
-rw-r--r--sdk-modifications/libsrc/fs/file_allocation_table.h64
-rw-r--r--sdk-modifications/libsrc/fs/filetime.c146
-rw-r--r--sdk-modifications/libsrc/fs/filetime.h44
-rw-r--r--sdk-modifications/libsrc/fs/fs.mk24
-rw-r--r--sdk-modifications/libsrc/fs/fs_api.c452
-rw-r--r--sdk-modifications/libsrc/fs/fs_api.h130
-rw-r--r--sdk-modifications/libsrc/fs/fs_cache.h125
-rw-r--r--sdk-modifications/libsrc/fs/fs_common.h129
-rw-r--r--sdk-modifications/libsrc/fs/fs_unicode.c173
-rw-r--r--sdk-modifications/libsrc/fs/fs_unicode.h16
-rw-r--r--sdk-modifications/libsrc/fs/libfat.c174
-rw-r--r--sdk-modifications/libsrc/fs/mem_allocate.h47
-rw-r--r--sdk-modifications/libsrc/fs/partition.c501
-rw-r--r--sdk-modifications/libsrc/fs/partition.h131
39 files changed, 6996 insertions, 0 deletions
diff --git a/sdk-modifications/libsrc/fs/bit_ops.h b/sdk-modifications/libsrc/fs/bit_ops.h
new file mode 100644
index 0000000..f823db7
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/bit_ops.h
@@ -0,0 +1,58 @@
+/*
+ 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.
+
+ 2006-07-11 - Chishm
+ * Original release
+*/
+
+#ifndef _BIT_OPS_H
+#define _BIT_OPS_H
+
+/*-----------------------------------------------------------------
+Functions to deal with little endian values stored in u8 arrays
+-----------------------------------------------------------------*/
+static inline u16 u8array_to_u16 (const u8* item, int offset) {
+ return ( item[offset] | (item[offset + 1] << 8));
+}
+
+static inline u32 u8array_to_u32 (const u8* item, int offset) {
+ return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24));
+}
+
+static inline void u16_to_u8array (u8* item, int offset, u16 value) {
+ item[offset] = (u8)value;
+ item[offset + 1] = (u8)(value >> 8);
+}
+
+static inline void u32_to_u8array (u8* item, int offset, u32 value) {
+ item[offset] = (u8)value;
+ item[offset + 1] = (u8)(value >> 8);
+ item[offset + 2] = (u8)(value >> 16);
+ item[offset + 3] = (u8)(value >> 24);
+}
+
+#endif // _BIT_OPS_H
diff --git a/sdk-modifications/libsrc/fs/cache.c b/sdk-modifications/libsrc/fs/cache.c
new file mode 100644
index 0000000..2c66e1f
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/cache.c
@@ -0,0 +1,273 @@
+/*
+ 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 <string.h>
+
+#include "fs_common.h"
+#include "fs_cache.h"
+#include "disc_io/disc.h"
+
+#include "mem_allocate.h"
+
+#define CACHE_FREE 0xFFFFFFFF
+
+CACHE* _FAT_cache_constructor (u32 numberOfPages, const IO_INTERFACE* discInterface) {
+ CACHE* cache;
+ u32 i;
+ CACHE_ENTRY* cacheEntries;
+
+ if (numberOfPages < 2) {
+ numberOfPages = 2;
+ }
+
+ cache = (CACHE*) _FAT_mem_allocate (sizeof(CACHE));
+ if (cache == NULL) {
+ return NULL;
+ }
+
+ cache->disc = discInterface;
+ cache->numberOfPages = numberOfPages;
+
+
+ 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].dirty = 0;
+ }
+
+ cache->cacheEntries = cacheEntries;
+
+ cache->pages = (u8*) _FAT_mem_allocate ( CACHE_PAGE_SIZE * numberOfPages);
+ if (cache->pages == NULL) {
+ _FAT_mem_free (cache->cacheEntries);
+ _FAT_mem_free (cache);
+ return NULL;
+ }
+
+ return cache;
+}
+
+void _FAT_cache_destructor (CACHE* cache) {
+ // Clear out cache before destroying it
+ _FAT_cache_flush(cache);
+
+ // Free memory in reverse allocation order
+ _FAT_mem_free (cache->pages);
+ _FAT_mem_free (cache->cacheEntries);
+ _FAT_mem_free (cache);
+
+ return;
+}
+
+/*
+Retrieve a sector's page from the cache. If it is not found in the cache,
+load it into the cache and return the page it was loaded to.
+Return CACHE_FREE on error.
+*/
+static u32 _FAT_cache_getSector (CACHE* cache, u32 sector) {
+ u32 i;
+ CACHE_ENTRY* cacheEntries = cache->cacheEntries;
+ u32 numberOfPages = cache->numberOfPages;
+
+ u32 leastUsed = 0;
+ u32 lowestCount = 0xFFFFFFFF;
+
+ for (i = 0; (i < numberOfPages) && (cacheEntries[i].sector != sector); i++) {
+ // While searching for the desired sector, also search for the leased used page
+ if ( (cacheEntries[i].sector == CACHE_FREE) || (cacheEntries[i].count < lowestCount) ) {
+ leastUsed = i;
+ lowestCount = cacheEntries[i].count;
+ }
+ }
+
+ // If it found the sector in the cache, return it
+ if ((i < numberOfPages) && (cacheEntries[i].sector == sector)) {
+ // Increment usage counter
+ cacheEntries[i].count += 1;
+ return i;
+ }
+
+ // If it didn't, replace the least used cache page with the desired sector
+ if ((cacheEntries[leastUsed].sector != CACHE_FREE) && (0xC33CA55A == cacheEntries[leastUsed].dirty)) {
+ // Write the page back to disc if it has been written to
+ if (!_FAT_disc_writeSectors (cache->disc, cacheEntries[leastUsed].sector, 1, cache->pages + CACHE_PAGE_SIZE * leastUsed)) {
+ return CACHE_FREE;
+ }
+ cacheEntries[leastUsed].dirty = 0;
+ }
+
+ // Load the new sector into the cache
+ if (!_FAT_disc_readSectors (cache->disc, sector, 1, cache->pages + CACHE_PAGE_SIZE * leastUsed)) {
+ return CACHE_FREE;
+ }
+ cacheEntries[leastUsed].sector = sector;
+ // Increment the usage count, don't reset it
+ // This creates a paging policy of least used PAGE, not sector
+ cacheEntries[leastUsed].count += 1;
+ return leastUsed;
+}
+
+/*
+Reads some data from a cache page, determined by the sector number
+*/
+bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, u32 sector, u32 offset, u32 size) {
+ u32 page;
+
+ if (offset + size > BYTES_PER_READ) {
+ return false;
+ }
+
+ page = _FAT_cache_getSector (cache, sector);
+ if (page == CACHE_FREE) {
+ return false;
+ }
+
+ memcpy (buffer, cache->pages + (CACHE_PAGE_SIZE * page) + offset, size);
+ 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, u32 sector, u32 offset, u32 size) {
+ u32 page;
+
+ if (offset + size > BYTES_PER_READ) {
+ return false;
+ }
+
+ page = _FAT_cache_getSector (cache, sector);
+ if (page == CACHE_FREE) {
+ return false;
+ }
+
+ memcpy (cache->pages + (CACHE_PAGE_SIZE * page) + offset, buffer, size);
+ cache->cacheEntries[page].dirty = 0xC33CA55A;
+
+ return true;
+}
+
+/*
+some where call _FAT_cache_writePartialSector to cache sector m , but later, another
+place(in fwrite function) directly write data to sector m, in this case, need to
+cancel the dirty state of sector m
+*/
+void _FAT_cache_writePartialSector_check (CACHE* cache, u32 sector, u32 num, const void* buffer)
+{
+ u32 i, m;
+ CACHE_ENTRY* cacheEntries = cache->cacheEntries;
+ u32 numberOfPages = cache->numberOfPages;
+
+ m = 0;
+ while(m++ < num)
+ {
+ for (i = 0; i < numberOfPages; i++)
+ {
+ if(cacheEntries[i].sector == sector)
+ break;
+ }
+
+ if(i >= numberOfPages)
+ return;
+
+ //cache the data
+ memcpy (cache->pages + (CACHE_PAGE_SIZE * i), buffer, CACHE_PAGE_SIZE);
+ //cancel the dirty state
+ cache->cacheEntries[i].dirty = 0;
+
+ sector += 1;
+ buffer += CACHE_PAGE_SIZE;
+ }
+}
+
+/*
+Writes some data to a cache page, zeroing out the page first
+*/
+bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, u32 sector, u32 offset, u32 size) {
+ u32 page;
+
+ if (offset + size > BYTES_PER_READ) {
+ return false;
+ }
+
+ page = _FAT_cache_getSector (cache, sector);
+ if (page == CACHE_FREE) {
+ return false;
+ }
+
+ memset (cache->pages + (CACHE_PAGE_SIZE * page), 0, CACHE_PAGE_SIZE);
+ memcpy (cache->pages + (CACHE_PAGE_SIZE * page) + offset, buffer, size);
+ cache->cacheEntries[page].dirty = 0xC33CA55A;
+
+ return true;
+}
+
+
+/*
+Flushes all dirty pages to disc, clearing the dirty flag.
+Also resets all pages' page count to 0.
+*/
+bool _FAT_cache_flush (CACHE* cache) {
+ u32 i;
+
+ for (i = 0; i < cache->numberOfPages; i++) {
+ if (0xC33CA55A == cache->cacheEntries[i].dirty) {
+ if (!_FAT_disc_writeSectors (cache->disc, cache->cacheEntries[i].sector, 1, cache->pages + CACHE_PAGE_SIZE * i)) {
+ return CACHE_FREE;
+ }
+ }
+ cache->cacheEntries[i].count = 0;
+ cache->cacheEntries[i].dirty = 0;
+ }
+
+ _FAT_disc_clearStatus( cache->disc );
+ return true;
+}
+
+void _FAT_cache_invalidate (CACHE* cache) {
+ int i;
+ for (i = 0; i < cache->numberOfPages; i++) {
+ cache->cacheEntries[i].sector = CACHE_FREE;
+ cache->cacheEntries[i].count = 0;
+ cache->cacheEntries[i].dirty = 0;
+ }
+}
+
diff --git a/sdk-modifications/libsrc/fs/directory.c b/sdk-modifications/libsrc/fs/directory.c
new file mode 100644
index 0000000..b30cccc
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/directory.c
@@ -0,0 +1,1005 @@
+/*
+ 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.
+
+ 2006-08-14 - Chishm
+ * entryFromPath correctly finds "" and "." now
+
+ 2006-08-17 - Chishm
+ * entryFromPath doesn't look for "" anymore - use "." to refer to the current directory
+
+ 2006-08-19 - Chishm
+ * Fixed entryFromPath bug when looking for "." in root directory
+
+ 2006-10-01 - Chishm
+ * Now clears the whole new cluster when linking in more clusters for a directory
+
+ 2006-10-28 - Chishm
+ * stat returns the hostType for the st_dev value
+*/
+//version 1.1
+//Note: fix bug in _FAT_directory_isValidAlias()
+
+#include <string.h>
+#include <ctype.h>
+
+#include "directory.h"
+#include "fs_common.h"
+#include "partition.h"
+#include "file_allocation_table.h"
+#include "bit_ops.h"
+#include "filetime.h"
+#include "fs_unicode.h"
+
+// Directory entry codes
+#define DIR_ENTRY_LAST 0x00
+#define DIR_ENTRY_FREE 0xE5
+
+
+// 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
+};
+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 char* strupr(char* a)
+{
+ char *str;
+
+ str = a;
+ while(*str)
+ {
+ if(*str >= 'a' && *str <= 'z') *str -= 0x20;
+ str += 1;
+ }
+
+ return a;
+}
+
+static char* strlwr(char *a){
+ char *str;
+
+ str = a;
+ while(*str)
+ {
+ if((*str) >= 'A' && (*str) <= 'Z') (*str) = ((*str) -'A' + 'a');
+ str++;
+ }
+ return a;
+}
+
+bool _FAT_directory_isValidLfn (const char* name) {
+ u32 i;
+ u32 nameLength;
+ // Make sure the name is short enough to be valid
+ if ( strnlen(name, MAX_FILENAME_LENGTH) >= MAX_FILENAME_LENGTH) {
+ return false;
+ }
+ // Make sure it doesn't contain any invalid characters
+ if (strpbrk (name, "\\/:*?\"<>|") != NULL) {
+ return false;
+ }
+
+ nameLength = strnlen(name, MAX_FILENAME_LENGTH);
+ // Make sure the name doesn't contain any control codes
+ for (i = 0; i < nameLength; i++) {
+ if ((unsigned char)name[i] < 0x20) {
+ return false;
+ }
+ }
+ // Otherwise it is valid
+ return true;
+}
+bool _FAT_directory_isValidAlias (const char* name) {
+ return false;//disables this function to preserve file name casing
+
+ u32 i;
+ u32 nameLength;
+ const char* dot;
+
+ // Make sure the name is short enough to be valid
+ if ( strnlen(name, MAX_ALIAS_LENGTH) >= MAX_ALIAS_LENGTH) {
+ return false;
+ }
+ // Make sure it doesn't contain any invalid characters
+ if (strpbrk (name, "\\/:;*?\"<>|&+,=[]") != NULL) {
+ return false;
+ }
+
+ //
+ if (strpbrk (name, " ") != NULL) {
+ return false;
+ }
+
+ nameLength = strnlen(name, MAX_ALIAS_LENGTH);
+ // Make sure the name doesn't contain any control codes
+ //if name isn't all capitals, then it is not a valid short name
+ for (i = 0; i < nameLength; i++) {
+ if (name[i] < 0x5A && name[i]!=0x20) {
+ return false;
+ }
+ }
+
+ dot = strchr ( name, '.');
+ // Make sure there is only one '.'
+ if ((dot != NULL) && (strrchr ( name, '.') != dot)) {
+ return false;
+ }
+ // If there is a '.':
+ if (dot != NULL) {
+ // Make sure the filename portion is 1-8 characters long
+ if (((dot - 1 - name) > 8) || ((dot - 1 - name) < 1)) {
+ return false;
+ }
+ // Make sure the extension is 1-3 characters long, if it exists
+ if ((strnlen(dot + 1, MAX_ALIAS_LENGTH) > 3) || (strnlen(dot + 1, MAX_ALIAS_LENGTH) < 1)) {
+ return false;
+ }
+ } else {
+ // Make sure the entire file name is 1-8 characters long
+ if ((nameLength > 8) || (nameLength < 1)) {
+ return false;
+ }
+ }
+
+ // Since we made it through all those tests, it must be valid
+ return true;
+}
+
+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');
+}
+
+u32 _FAT_directory_entryGetCluster (const u8* entryData) {
+ return u8array_to_u16(entryData,DIR_ENTRY_cluster) | (u8array_to_u16(entryData, DIR_ENTRY_clusterHigh) << 16);
+}
+
+static bool _FAT_directory_incrementDirEntryPosition (PARTITION* partition, DIR_ENTRY_POSITION* entryPosition, bool extendDirectory) {
+ DIR_ENTRY_POSITION position;
+ position = *entryPosition;
+ u32 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 (tempCluster == CLUSTER_FREE) {
+ 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;
+
+ u8 entryData[0x20];
+
+ bool notFound, found;
+ u32 maxSectors;
+ int lfnPos;
+ u8 lfnChkSum, chkSum;
+ char* filename;
+ u16 unicodeFilename[256];
+ 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;
+ filename = entry->d_name;
+ //unicodeFilename = entry->unicodeFilename;
+ memset( unicodeFilename, 0, 512 );
+
+ // Can only be FAT16_ROOT_DIR_CLUSTER if it is the root directory on a FAT12 or FAT16 partition
+ if (entryStart.cluster == FAT16_ROOT_DIR_CLUSTER) {
+ maxSectors = partition->dataStart - partition->rootDirStart;
+ } else {
+ maxSectors = partition->sectorsPerCluster;
+ }
+
+ 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;
+ //filename[(entryData[LFN_offset_ordinal] & ~LFN_END) * 13] = '\0'; // Set end of lfn to null character
+ unicodeFilename[(entryData[LFN_offset_ordinal] & ~LFN_END) * 13] = 0x0000;
+ lfnChkSum = entryData[LFN_offset_checkSum];
+ } if (lfnChkSum != entryData[LFN_offset_checkSum]) {
+ lfnExists = false;
+ }
+ //unicodeFilename[0] = 0x0000;
+ if (lfnExists) {
+ lfnPos = ((entryData[LFN_offset_ordinal] & ~LFN_END) - 1) * 13;
+ for (i = 0; i < 13; i++) {
+ unicodeFilename[lfnPos + i] = u8array_to_u16( entryData, LFN_offset_table[i]);
+ // | entryData[LFN_offset_table[i]+1]<<8; // modify this for unicode support;
+ }
+ }
+ } 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;
+ //filename[0] = '\0';
+ //unicodeFilename[0] = 0x0000; // move this line to below(1)
+ }
+ }
+ //short name
+ if (!lfnExists) {
+ entryStart = entryEnd;
+ unicodeFilename[0] = 0x0000; // (1)make sure clear previous search junk
+ // get alias anyway
+ _FAT_directory_entryGetAlias (entryData, filename);
+ strlwr(filename);//convert to lowercase characters
+ }
+ //long name
+ else
+ {
+ _FAT_unicode16_to_utf8 (unicodeFilename, 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, u32 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->d_name, '\0', MAX_FILENAME_LENGTH);
+ entry->d_name[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;
+ DIR_ENTRY_POSITION entryEnd;
+ entryStart = entry->dataStart;
+ entryEnd = entry->dataEnd;
+ bool entryStillValid;
+ bool finished;
+
+ int i;
+ int lfnPos;
+
+ u8 entryData[DIR_ENTRY_DATA_SIZE];
+
+ memset (entry->d_name, '\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;
+ for (i = 0; i < 13; i++) {
+ entry->d_name[lfnPos + i] = entryData[LFN_offset_table[i]]; // modify this for unicode support;
+ }
+ }
+ }
+
+ 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->d_name)) {
+ 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;
+
+ //size_t uniDirnameLength;
+ //u16 uniPath[MAX_FILENAME_LENGTH];
+ //const u16 * uniPathPosition;
+ //const u16 * uniNextPathPosition;
+
+ u32 dirCluster;
+ bool foundFile;
+
+ //char alias[MAX_ALIAS_LENGTH];
+
+ bool found, notFound;
+
+ //_FAT_utf8_to_unicode16( path, uniPath );
+ pathPosition = path;
+ //uniPathPosition = uniPath;
+
+ 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++;
+ }
+
+ //while (uniPathPosition[0] == (unsigned short)DIR_SEPARATOR) {
+ // uniPathPosition++;
+ //}
+
+ 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) && (strncasecmp(".", pathPosition, 2) == 0)) {
+ if ((dirCluster == partition->rootDirCluster) && strlen(pathPosition) == 1 && (strcasecmp(".", 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);
+ //uniNextPathPosition = _unistrchr( uniPathPosition, (unsigned short)DIR_SEPARATOR );
+ if (nextPathPosition != NULL) {
+ dirnameLength = nextPathPosition - pathPosition;
+ } else {
+ dirnameLength = strlen(pathPosition);
+ }
+ //if (uniNextPathPosition != 0x0000) {
+ // uniDirnameLength = uniNextPathPosition - uniPathPosition;
+ //} else {
+ // uniDirnameLength = _unistrnlen(uniPathPosition, MAX_FILENAME_LENGTH);
+ //}
+
+ if (dirnameLength > MAX_FILENAME_LENGTH) {
+ // The path is too long to bother with
+ return false;
+ }
+ //if( uniDirnameLength > MAX_FILENAME_LENGTH ) {
+ // 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 ((uniDirnameLength == _unistrnlen(entry->unicodeFilename, MAX_FILENAME_LENGTH))
+ // && (_unistrncmp(entry->unicodeFilename, uniPathPosition, uniDirnameLength) == 0)) {
+ // found = true;
+ //}
+
+ //if(!strncasecmp(entry->d_name, pathPosition, dirnameLength))
+ if(strlen(entry->d_name) == dirnameLength && !strncasecmp(entry->d_name, pathPosition, dirnameLength))
+ found = true;
+
+ // Check if the alias matches
+ //_FAT_directory_entryGetAlias (entry->entryData, alias);
+ //if ((dirnameLength == strnlen(alias, MAX_ALIAS_LENGTH))
+ // && (strncasecmp(alias, pathPosition, 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 (entry->entryData);
+ pathPosition = nextPathPosition;
+ //uniPathPosition = uniNextPathPosition;
+ // Consume separator(s)
+ while (pathPosition[0] == DIR_SEPARATOR) {
+ pathPosition++;
+ }
+ //while (uniPathPosition[0] == (unsigned short)DIR_SEPARATOR) {
+ // uniPathPosition++;
+ //}
+ // The requested directory was found
+ if (pathPosition >= pathEnd) {
+ found = true;
+ } else {
+ found = false;
+ }
+ }
+ }
+
+ if (found && !notFound) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry) {
+ DIR_ENTRY_POSITION entryStart;
+ DIR_ENTRY_POSITION entryEnd;
+ entryStart = entry->dataStart;
+ entryEnd = entry->dataEnd;
+ bool entryStillValid;
+ bool finished;
+
+ u8 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, u32 dirCluster, u32 size) {
+ DIR_ENTRY_POSITION gapStart;
+ DIR_ENTRY_POSITION gapEnd;
+
+ u8 entryData[DIR_ENTRY_DATA_SIZE];
+
+ u32 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, u32 dirCluster) {
+ DIR_ENTRY tempEntry;
+ bool foundFile;
+ //char alias[MAX_ALIAS_LENGTH];
+ u32 dirnameLength;
+ //u16 unicodeName[MAX_FILENAME_LENGTH];
+
+ dirnameLength = strnlen(name, MAX_FILENAME_LENGTH);
+
+ if (dirnameLength >= MAX_FILENAME_LENGTH) {
+ return false;
+ }
+
+ //_FAT_utf8_to_unicode16( name, unicodeName );
+
+ // 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 == _unistrnlen(tempEntry.unicodeFilename, MAX_FILENAME_LENGTH))*/
+ // //&& (strcasecmp(tempEntry.filename, name) == 0)) {
+ // /*&&*/ (_unistrncmp( unicodeName, tempEntry.unicodeFilename, MAX_FILENAME_LENGTH ) == 0 ))
+ //{
+ // return true;
+ //}
+ //if(!strncasecmp(name, tempEntry.d_name, dirnameLength))
+ if(!strcasecmp(name, tempEntry.d_name))
+ return true;
+
+ // Check if the alias matches
+ //_FAT_directory_entryGetAlias (tempEntry.entryData, alias);
+ //if ((dirnameLength == strnlen(alias, MAX_ALIAS_LENGTH))
+ // && (strcasecmp(alias, name) == 0)) {
+ // return true;
+ //}
+ foundFile = _FAT_directory_getNextEntry (partition, &tempEntry);
+ }
+ return false;
+}
+
+
+
+bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, u32 dirCluster) {
+ u32 entrySize;
+ u8 lfnEntry[DIR_ENTRY_DATA_SIZE];
+ s32 i,j; // Must be signed for use when decrementing in for loop
+ char *tmpCharPtr;
+ DIR_ENTRY_POSITION curEntryPos;
+ bool entryStillValid;
+ u8 aliasCheckSum = 0;
+ char alias [MAX_ALIAS_LENGTH];
+ u16 unicodeFilename[256];
+
+ // Make sure the filename is not 0 length
+ if (strnlen (entry->d_name, MAX_FILENAME_LENGTH) < 1) {
+ return false;
+ }
+ // Make sure the filename is at least a valid LFN
+ if ( !(_FAT_directory_isValidLfn (entry->d_name))) {
+ return false;
+ }
+
+ // Remove trailing spaces
+ for (i = strlen (entry->d_name) - 1; (i > 0) && (entry->d_name[i] == ' '); --i) {
+ entry->d_name[i] = '\0';
+ }
+ // Remove leading spaces
+ for (i = 0; (i < strlen (entry->d_name)) && (entry->d_name[i] == ' '); ++i) ;
+ if (i > 0) {
+ memmove (entry->d_name, entry->d_name + i, strlen (entry->d_name + i));
+ }
+
+ // Remove junk in filename
+ i = strlen (entry->d_name);
+ memset (entry->d_name + i, '\0', MAX_FILENAME_LENGTH - i);
+
+ // Make sure the entry doesn't already exist
+ if (_FAT_directory_entryExists (partition, entry->d_name, dirCluster)) {
+ return false;
+ }
+
+ // Clear out alias, so we can generate a new one
+ memset (entry->entryData, ' ', 11);
+
+ if ( strncmp(entry->d_name, ".", MAX_FILENAME_LENGTH) == 0) {
+ // "." entry
+ entry->entryData[0] = '.';
+ entrySize = 1;
+ } else if ( strncmp(entry->d_name, "..", MAX_FILENAME_LENGTH) == 0) {
+ // ".." entry
+ entry->entryData[0] = '.';
+ entry->entryData[1] = '.';
+ entrySize = 1;
+ }else if (_FAT_directory_isValidAlias (entry->d_name)) {
+ entrySize = 1;
+ // Copy into alias
+ for (i = 0, j = 0; (j < 8) && (entry->d_name[i] != '.') && (entry->d_name[i] != '\0'); i++, j++) {
+ entry->entryData[j] = entry->d_name[i];
+ }
+ while (j < 8) {
+ entry->entryData[j] = ' ';
+ ++ j;
+ }
+ if (entry->d_name[i] == '.') {
+ // Copy extension
+ ++ i;
+ while ((entry->d_name[i] != '\0') && (j < 11)) {
+ entry->entryData[j] = entry->d_name[i];
+ ++ i;
+ ++ j;
+ }
+ }
+ while (j < 11) {
+ entry->entryData[j] = ' ';
+ ++ j;
+ }
+ // Short filename
+ strupr (entry->d_name);
+ }else {
+ // Long filename needed
+ //memset( entry->unicodeFilename, 0, 512 );
+ //_FAT_utf8_to_unicode16( (const char*)entry->d_name, entry->unicodeFilename );
+ memset( unicodeFilename, 0, 512 );
+ _FAT_utf8_to_unicode16( (const char*)entry->d_name, unicodeFilename );
+
+ //entrySize = ((strnlen (entry->filename, MAX_FILENAME_LENGTH) + LFN_ENTRY_LENGTH - 1) / LFN_ENTRY_LENGTH) + 1;
+ //entrySize = ((_unistrnlen( entry->unicodeFilename, MAX_FILENAME_LENGTH ) + LFN_ENTRY_LENGTH - 1) / LFN_ENTRY_LENGTH) + 1;
+ entrySize = ((_unistrnlen( unicodeFilename, MAX_FILENAME_LENGTH ) + LFN_ENTRY_LENGTH - 1) / LFN_ENTRY_LENGTH) + 1;
+
+ // Generate alias
+ tmpCharPtr = strrchr (entry->d_name, '.');
+ if (tmpCharPtr == NULL) {
+ tmpCharPtr = strrchr (entry->d_name, '\0');
+ }
+ for (i = 0, j = 0; (j < 6) && (entry->d_name + i < tmpCharPtr); i++) {
+ if ( _uniisalnum((u8)(entry->d_name[i]))) {
+ alias[j] = entry->d_name[i];
+ ++ j;
+ }
+ }
+ while (j < 8) {
+ alias[j] = '_';
+ ++ j;
+ }
+ tmpCharPtr = strrchr (entry->d_name, '.');
+ if (tmpCharPtr != NULL) {
+ alias[8] = '.';
+ // Copy extension
+ while ((tmpCharPtr != '\0') && (j < 12)) {
+ alias[j] = tmpCharPtr[0];
+ ++ tmpCharPtr;
+ ++ j;
+ }
+ alias[j] = '\0';
+ } else {
+ for (j = 8; j < MAX_ALIAS_LENGTH; j++) {
+ alias[j] = '\0';
+ }
+ }
+
+ // Get a valid tail number
+ alias[5] = '~';
+ i = 0;
+ do {
+ i++;
+ alias[6] = '0' + ((i / 10) % 10); // 10's digit
+ alias[7] = '0' + (i % 10); // 1's digit
+ } while (_FAT_directory_entryExists (partition, alias, dirCluster) && (i < 100));
+ if (i == 100) {
+ // Couldn't get a tail number
+ return false;
+ }
+
+ // Make it upper case
+ strupr (alias);
+
+ // Now copy it into the directory entry data
+ memcpy (entry->entryData, alias, 8);
+ memcpy (entry->entryData + 8, alias + 9, 3);
+ for (i = 0; i < 10; i++) {
+ if (entry->entryData[i] < 0x20) {
+ // Replace null and control characters with spaces
+ entry->entryData[i] = 0x20;
+ }
+ }
+ // Generate alias checksum
+ for (i=0; i < 11; 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;
+
+ 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) | (i == entrySize ? LFN_END : 0);
+ for (j = 0; j < 13; j++) {
+ //if (entry->unicodeFilename[(i - 2) * 13 + j] == '\0') {
+ if (unicodeFilename[(i - 2) * 13 + j] == '\0') {
+ //if ((j > 1) && (entry->unicodeFilename[(i - 2) * 13 + (j-1)] == '\0')) {
+ if ((j > 1) && (unicodeFilename[(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], entry->unicodeFilename[(i - 2) * 13 + j]);
+ u16_to_u8array (lfnEntry, LFN_offset_table[j], unicodeFilename[(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 (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(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) |
+ (_FAT_directory_isHidden (entry) ? S_IHIDDEN : 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 = _FAT_directory_isHidden (entry);
+ 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/sdk-modifications/libsrc/fs/directory.h b/sdk-modifications/libsrc/fs/directory.h
new file mode 100644
index 0000000..654c5d2
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/directory.h
@@ -0,0 +1,180 @@
+/*
+ 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.
+
+ 2006-07-11 - Chishm
+ * Original release
+*/
+
+#ifndef _DIRECTORY_H
+#define _DIRECTORY_H
+
+#include <sys/stat.h>
+
+#include "fs_common.h"
+#include "partition.h"
+
+#define DIR_ENTRY_DATA_SIZE 0x20
+#define MAX_FILENAME_LENGTH 256
+#define MAX_ALIAS_LENGTH 13
+#define LFN_ENTRY_LENGTH 13
+#define FAT16_ROOT_DIR_CLUSTER 0
+
+#define DIR_SEPARATOR '/'
+
+// File attributes
+#define S_IHIDDEN 0x00200000
+
+
+#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 {
+ u32 cluster;
+ u32 sector;
+ s32 offset;
+} DIR_ENTRY_POSITION;
+
+typedef struct {
+ u8 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 d_name[MAX_FILENAME_LENGTH + MAX_FILENAME_LENGTH*2]; //Store name string using UTF8 coding
+ //u16 unicodeFilename[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->d_name[0] == '.') && ((entry->d_name[1] == '\0') ||
+ ((entry->d_name[1] == '.') && entry->d_name[2] == '\0')));
+}
+
+static inline bool _FAT_directory_isHidden (DIR_ENTRY* entry) {
+ return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_HID) != 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, u32 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, u32 dirCluster);
+
+/*
+Get the start cluster of a file from it's entry data
+*/
+u32 _FAT_directory_entryGetCluster (const u8* 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/sdk-modifications/libsrc/fs/disc_io/disc.c b/sdk-modifications/libsrc/fs/disc_io/disc.c
new file mode 100644
index 0000000..8b50953
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/disc_io/disc.c
@@ -0,0 +1,194 @@
+/*
+
+ disc.c
+
+ uniformed io-interface to work with Chishm's FAT library
+
+ Written by MightyMax
+
+
+ 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.
+
+ 2005-11-06 - Chishm
+ * Added WAIT_CR modifications for NDS
+
+ 2006-02-03 www.neoflash.com
+ * Added SUPPORT_* defines, comment out any of the SUPPORT_* defines in disc_io.h to remove support
+ for the given interface and stop code being linked to the binary
+
+ * Added support for MK2 MMC interface
+
+ * Added disc_Cache* functions
+
+ 2006-02-05 - Chishm
+ * Added Supercard SD support
+
+ 2006-02-26 - Cytex
+ * Added EFA2 support
+
+ 2006-05-18 - Chishm
+ * Rewritten for FATlib disc.c
+
+ 2006-06-19 - Chishm
+ * Changed read and write interface to accept a u32 instead of a u8 for the number of sectors
+
+ 2006-07-11 - Chishm
+ * Removed disc_Cache* functions, since there is now a proper unified cache
+ * Removed SUPPORT_* defines
+ * Rewrote device detection functions
+ * First libfat release
+
+ 2006-07-25 - Chishm
+ * Changed IO_INTERFACEs to const
+
+ 2006-08-02 - Chishm
+ * Added NinjaDS
+*/
+
+#include "disc.h"
+#include "disc_io.h"
+
+// Include known io-interfaces:
+//#include "io_dldi.h"
+//#include "io_mpcf.h"
+//#include "io_m3cf.h"
+//#include "io_m3sd.h"
+//#include "io_sccf.h"
+//#include "io_scsd.h"
+//#include "io_fcsr.h"
+//#include "io_nmmc.h"
+//#include "io_efa2.h"
+//#include "io_mmcf.h"
+//#include "io_njsd.h"
+//#include "io_acekard.h"
+//#include "io_rpg_nand.h"
+//#include "io_rpg_sd.h"
+//#include "iointerface_sc.h"
+#include "io_ds2_mmcf.h"
+
+const IO_INTERFACE* ioInterfaces[] = {
+ //&_io_dldi,
+ //&_io_fcsr,
+#ifdef NDS
+ // Place Slot 1 (DS Card) interfaces here
+ //&_io_rpg_sd, //&_io_rpg_nand, /*&_io_acekard, */
+ // Place Slot 2 (GBA Cart) interfaces here
+ //&_io_scsd, &_io_mpcf, &_io_m3cf, &_io_sccf, &_io_m3sd, &_io_fcsr
+ // Experimental Slot 2 interfaces
+ //, &_io_mmcf, &_io_efa2
+ &_io_ds2_mmcf,
+#endif
+};
+
+/*
+
+ Hardware level disc funtions
+
+*/
+
+
+const IO_INTERFACE* _FAT_disc_gbaSlotFindInterface (void)
+{
+ // If running on an NDS, make sure the correct CPU can access
+ // the GBA cart. First implemented by SaTa.
+#ifdef NDS
+ #ifdef ARM9
+ REG_EXMEMCNT &= ~ARM7_OWNS_ROM;
+ #endif
+ #ifdef ARM7
+ REG_EXMEMCNT |= ARM7_OWNS_ROM;
+ #endif
+#endif
+
+ int i;
+
+ for (i = 0; i < (sizeof(ioInterfaces) / sizeof(IO_INTERFACE*)); i++) {
+ if ((ioInterfaces[i]->features & FEATURE_SLOT_GBA) && (ioInterfaces[i]->fn_startup())) {
+ return ioInterfaces[i];
+ }
+ }
+ return NULL;
+}
+
+#ifdef NDS
+/*
+ * Check the DS card slot for a valid memory card interface
+ * If an interface is found, it is set as the default interace
+ * and it returns true. Otherwise the default interface is left
+ * untouched and it returns false.
+ */
+const IO_INTERFACE* _FAT_disc_dsSlotFindInterface (void)
+{
+#ifdef ARM9
+ REG_EXMEMCNT &= ~ARM7_OWNS_CARD;
+#endif
+#ifdef ARM7
+ REG_EXMEMCNT |= ARM7_OWNS_CARD;
+#endif
+ int i;
+
+ for (i = 0; i < (sizeof(ioInterfaces) / sizeof(IO_INTERFACE*)); i++) {
+ if ((ioInterfaces[i]->features & FEATURE_SLOT_NDS) && (ioInterfaces[i]->fn_startup())) {
+ return ioInterfaces[i];
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+/*
+ * When running on an NDS, check the either slot for a valid memory
+ * card interface.
+ * When running on a GBA, call _FAT_disc_gbaSlotFindInterface
+ * If an interface is found, it is set as the default interace
+ * and it returns true. Otherwise the default interface is left
+ * untouched and it returns false.
+ */
+#ifdef NDS
+const IO_INTERFACE* _FAT_disc_findInterface (void)
+{
+#ifdef ARM9
+ REG_EXMEMCNT &= ~(ARM7_OWNS_CARD | ARM7_OWNS_ROM);
+#endif
+#ifdef ARM7
+ REG_EXMEMCNT |= (ARM7_OWNS_CARD | ARM7_OWNS_ROM);
+#endif
+
+ int i;
+
+ for (i = 0; i < (sizeof(ioInterfaces) / sizeof(IO_INTERFACE*)); i++) {
+ if (ioInterfaces[i]->fn_startup()) {
+ return ioInterfaces[i];
+ }
+ }
+
+ return NULL;
+}
+#else
+const IO_INTERFACE* _FAT_disc_findInterface (void)
+{
+ return _FAT_disc_gbaSlotFindInterface();
+}
+#endif
diff --git a/sdk-modifications/libsrc/fs/disc_io/disc.h b/sdk-modifications/libsrc/fs/disc_io/disc.h
new file mode 100644
index 0000000..2129359
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/disc_io/disc.h
@@ -0,0 +1,128 @@
+/*
+ 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.
+
+ 2006-07-11 - Chishm
+ * Original release
+
+*/
+#ifndef _DISC_H
+#define _DISC_H
+
+#include "../fs_common.h"
+#include "disc_io.h"
+
+/*
+Search for a block based device in the GBA slot.
+Return a pointer to a usable interface if one is found,
+NULL if not.
+*/
+extern const IO_INTERFACE* _FAT_disc_gbaSlotFindInterface (void);
+
+/*
+Search for a block based device in the DS slot.
+Return a pointer to a usable interface if one is found,
+NULL if not.
+*/
+#ifdef NDS
+extern const IO_INTERFACE* _FAT_disc_dsSlotFindInterface (void);
+#endif
+
+/*
+Search for a block based device in the both slots.
+Return a pointer to a usable interface if one is found,
+NULL if not.
+*/
+extern const IO_INTERFACE* _FAT_disc_findInterface (void);
+
+/*
+Check if a disc is inserted
+Return true if a disc is inserted and ready, false otherwise
+*/
+static inline bool _FAT_disc_isInserted (const IO_INTERFACE* disc) {
+ return disc->fn_isInserted();
+}
+
+/*
+Read numSectors sectors from a disc, starting at sector.
+numSectors is between 1 and 256
+sector is from 0 to 2^28
+buffer is a pointer to the memory to fill
+*/
+//return non-zero is sucess
+static inline bool _FAT_disc_readSectors (const IO_INTERFACE* disc, unsigned long sector, unsigned long numSectors, void* buffer) {
+ return disc->fn_readSectors (sector, numSectors, buffer);
+}
+
+/*
+Write numSectors sectors to a disc, starting at sector.
+numSectors is between 1 and 256
+sector is from 0 to 2^28
+buffer is a pointer to the memory to read from
+*/
+//return non-zero is sucess
+static inline bool _FAT_disc_writeSectors (const IO_INTERFACE* disc, unsigned long sector, unsigned long numSectors, const void* buffer) {
+ return disc->fn_writeSectors (sector, numSectors, buffer);
+}
+
+/*
+Reset the card back to a ready state
+*/
+static inline bool _FAT_disc_clearStatus (const IO_INTERFACE* disc) {
+ return disc->fn_clearStatus();
+}
+
+/*
+Initialise the disc to a state ready for data reading or writing
+*/
+static inline bool _FAT_disc_startup (const IO_INTERFACE* disc) {
+ return disc->fn_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 IO_INTERFACE* disc) {
+ return disc->fn_shutdown();
+}
+
+/*
+Return a 32 bit value unique to each type of interface
+*/
+static inline unsigned long _FAT_disc_hostType (const IO_INTERFACE* disc) {
+ return disc->ioType;
+}
+
+/*
+Return a 32 bit value that specifies the capabilities of the disc
+*/
+static inline unsigned long _FAT_disc_features (const IO_INTERFACE* disc) {
+ return disc->features;
+}
+
+#endif // _DISC_H
diff --git a/sdk-modifications/libsrc/fs/disc_io/disc_io.h b/sdk-modifications/libsrc/fs/disc_io/disc_io.h
new file mode 100644
index 0000000..6ac0c31
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/disc_io/disc_io.h
@@ -0,0 +1,83 @@
+/*
+ disc_io.h
+ Interface template for low level disc functions.
+
+ Copyright (c) 2006 Michael "Chishm" Chisholm
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation and/or
+ other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ 2006-07-11 - Chishm
+ * Original release
+
+ 2006-07-16 - Chishm
+ * Renamed _CF_USE_DMA to _IO_USE_DMA
+ * Renamed _CF_ALLOW_UNALIGNED to _IO_ALLOW_UNALIGNED
+*/
+
+#ifndef _DISC_IO_H
+#define _DISC_IO_H
+
+#include "../fs_common.h"
+
+//----------------------------------------------------------------------
+// Customisable features
+
+// Use DMA to read the card, remove this line to use normal reads/writes
+// #define _IO_USE_DMA
+
+// Allow buffers not alligned to 16 bits when reading files.
+// Note that this will slow down access speed, so only use if you have to.
+// It is also incompatible with DMA
+//#define _IO_ALLOW_UNALIGNED
+
+#define _IO_USE_DMA
+
+#if defined _IO_USE_DMA && defined _IO_ALLOW_UNALIGNED
+ #error You can't use both DMA and unaligned memory
+#endif
+
+#define FEATURE_MEDIUM_CANREAD 0x00000001
+#define FEATURE_MEDIUM_CANWRITE 0x00000002
+#define FEATURE_SLOT_GBA 0x00000010
+#define FEATURE_SLOT_NDS 0x00000020
+
+typedef bool (* FN_MEDIUM_STARTUP)(void) ;
+typedef bool (* FN_MEDIUM_ISINSERTED)(void) ;
+typedef bool (* FN_MEDIUM_READSECTORS)(unsigned long sector, unsigned long numSectors, void* buffer) ;
+typedef bool (* FN_MEDIUM_WRITESECTORS)(unsigned long sector, unsigned long numSectors, const void* buffer) ;
+typedef bool (* FN_MEDIUM_CLEARSTATUS)(void) ;
+typedef bool (* FN_MEDIUM_SHUTDOWN)(void) ;
+
+struct IO_INTERFACE_STRUCT {
+ unsigned long ioType ;
+ unsigned long features ;
+ FN_MEDIUM_STARTUP fn_startup ;
+ FN_MEDIUM_ISINSERTED fn_isInserted ;
+ FN_MEDIUM_READSECTORS fn_readSectors ;
+ FN_MEDIUM_WRITESECTORS fn_writeSectors ;
+ FN_MEDIUM_CLEARSTATUS fn_clearStatus ;
+ FN_MEDIUM_SHUTDOWN fn_shutdown ;
+} ;
+
+typedef struct IO_INTERFACE_STRUCT IO_INTERFACE ;
+
+#endif // define _DISC_IO_H
diff --git a/sdk-modifications/libsrc/fs/disc_io/io_ds2_mmcf.c b/sdk-modifications/libsrc/fs/disc_io/io_ds2_mmcf.c
new file mode 100644
index 0000000..c432d84
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/disc_io/io_ds2_mmcf.c
@@ -0,0 +1,13 @@
+//io_ds2_mmcf.c
+#include "io_ds2_mmcf.h"
+
+const IO_INTERFACE _io_ds2_mmcf = {
+ DEVICE_TYPE_DS2_MMCF,
+ FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE,
+ (FN_MEDIUM_STARTUP)&_MMC_StartUp,
+ (FN_MEDIUM_ISINSERTED)&_MMC_IsInserted,
+ (FN_MEDIUM_READSECTORS)&_MMC_ReadSectors,
+ (FN_MEDIUM_WRITESECTORS)&_MMC_WriteSectors,
+ (FN_MEDIUM_CLEARSTATUS)&_MMC_ClearStatus,
+ (FN_MEDIUM_SHUTDOWN)&_MMC_ShutDown
+};
diff --git a/sdk-modifications/libsrc/fs/disc_io/io_ds2_mmcf.h b/sdk-modifications/libsrc/fs/disc_io/io_ds2_mmcf.h
new file mode 100644
index 0000000..334a9bc
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/disc_io/io_ds2_mmcf.h
@@ -0,0 +1,63 @@
+#ifndef __IO_DS2_H__
+#define __IO_DS2_H__
+
+// 'DS2F'
+#define DEVICE_TYPE_DS2_MMCF 0x46434D4D
+
+#include "disc_io.h"
+#include "ds2_mmc_api.h"
+
+// export interface
+extern const IO_INTERFACE _io_ds2_mmcf ;
+
+/* initialize MMC/SD card */
+static inline bool _MMC_StartUp(void)
+{
+ return MMC_Initialize();
+}
+
+/* read multi blocks from MMC/SD card */
+/* read a single block from MMC/SD card */
+static inline bool _MMC_ReadSectors(u32 sector, u32 numSectors, void* buffer)
+{
+ int flag;
+
+ if(numSectors > 1)
+ flag= MMC_ReadMultiBlock(sector, numSectors, (unsigned char*)buffer);
+ else
+ flag= MMC_ReadBlock(sector, (unsigned char*)buffer);
+ return (flag==MMC_NO_ERROR);
+}
+
+/* write multi blocks from MMC/SD card */
+/* write a single block from MMC/SD card */
+static inline bool _MMC_WriteSectors(u32 sector, u32 numSectors, const void* buffer)
+{
+ int flag;
+
+ if(numSectors > 1)
+ flag= MMC_WriteMultiBlock(sector, numSectors, (unsigned char*)buffer);
+ else
+ flag= MMC_WriteBlock(sector, (unsigned char*)buffer);
+
+ return (flag==MMC_NO_ERROR);
+}
+
+static inline bool _MMC_ClearStatus(void)
+{
+ return true;
+}
+
+static inline bool _MMC_ShutDown(void)
+{
+ return true;
+}
+
+static inline bool _MMC_IsInserted(void)
+{
+ return true;
+}
+
+
+#endif //__IO_DS2_H__
+
diff --git a/sdk-modifications/libsrc/fs/ds2_fcntl.c b/sdk-modifications/libsrc/fs/ds2_fcntl.c
new file mode 100644
index 0000000..0fc00d7
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/ds2_fcntl.c
@@ -0,0 +1,126 @@
+#include <stdarg.h>
+#include <errno.h>
+#include "ds2_fcntl.h"
+#include "fs_api.h"
+#include "ds2_malloc.h"
+
+
+/*typedef struct {
+ FILE_STRUCT* stream;
+ int flags;
+} _fd_t;
+
+_fd_t* _fd_list = NULL;
+int _fd_count = 0;*/
+
+int open(const char* path, int oflag, ... ) {
+ if(oflag & _O_UNSUPPORTED) {
+ errno = EINVAL;
+ return -1;
+ }
+ // TODO - Deal more correctly with certain flags.
+ FILE_STRUCT* tempFile = fat_fopen(path, "rb");
+ if(oflag & O_CREAT) {
+ if(tempFile == NULL)
+ tempFile = fat_fopen(path, "wb");
+ if(tempFile == NULL)
+ return -1;
+ } else if(tempFile == NULL) {
+ return -1;
+ } else if(oflag & O_TRUNC) {
+ tempFile = fat_fopen(path, "wb");
+ if(tempFile == NULL)
+ return -1;
+ }
+ fat_fclose(tempFile);
+
+ char tempMode[16];
+ if((oflag & 0x3) == O_RDONLY) {
+ sprintf(tempMode, "rb");
+ } else if((oflag & 0x3) == O_WRONLY) {
+ if(oflag & O_APPEND)
+ sprintf(tempMode, "ab");
+ else
+ sprintf(tempMode, "wb");
+ } else if((oflag & 0x3) == O_RDWR) {
+ if(oflag & O_APPEND)
+ sprintf(tempMode, "ab+");
+ else
+ sprintf(tempMode, "rb+");
+ }
+
+ tempFile = fat_fopen(path, tempMode);
+ if(tempFile == NULL)
+ return -1;
+
+ return tempFile -> fd;
+}
+
+int fcntl(int fildes, int cmd, ...) {
+ /*if((fildes < 0) || (fildes >= _fd_count) || (_fd_list[fildes].stream == NULL)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if((cmd <= 0) || (cmd > _F_LAST)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ va_list ap;
+
+ int arg;
+ void* flock;
+ switch(cmd) {
+ case F_SETFD:
+ case F_SETOWN:
+ va_start(ap, cmd);
+ arg = va_arg(ap, int);
+ va_end(ap);
+ break;
+ case F_GETLK:
+ case F_SETLK:
+ va_start(ap, cmd);
+ flock = va_arg(ap, void*);
+ va_end(ap);
+ break;
+ }
+
+ switch(cmd) {
+ case F_DUPFD: // Duplicate file descriptors not supported.
+ errno = EINVAL;
+ return -1;
+ case F_GETFD:
+ return (_fd_list[fildes].flags & _F_FILE_DESC);
+ case F_SETFD:
+ arg &= _F_FILE_DESC;
+ _fd_list[fildes].flags &= ~_F_FILE_DESC;
+ _fd_list[fildes].flags |= arg;
+ return 0;
+ case F_GETFL:
+ return (_fd_list[fildes].flags & (O_ACCMODE | _O_FILE_STATUS));
+ case F_SETFL:
+ arg &= (O_ACCMODE | _O_FILE_STATUS);
+ if(arg & _O_UNSUPPORTED) {
+ errno = EINVAL;
+ return -1;
+ }
+ if((arg & O_ACCMODE) != (_fd_list[fildes].flags & O_ACCMODE)) {
+ errno = EINVAL;
+ return -1;
+ }
+ _fd_list[fildes].flags &= ~(O_ACCMODE | _O_FILE_STATUS);
+ _fd_list[fildes].flags |= arg;
+ return 0;
+ case F_GETOWN:
+ case F_SETOWN:
+ errno = -1;
+ return -1;
+ case F_GETLK:
+ case F_SETLK:
+ case F_SETLKW:
+ return -1;
+ }
+
+ errno = EINVAL;*/
+ return -1;
+}
diff --git a/sdk-modifications/libsrc/fs/ds2_fcntl.h b/sdk-modifications/libsrc/fs/ds2_fcntl.h
new file mode 100644
index 0000000..2c69221
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/ds2_fcntl.h
@@ -0,0 +1,51 @@
+#ifndef __libc_fcntl_h__
+#define __libc_fcntl_h__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define O_RDONLY (1 << 0)
+#define O_WRONLY (2 << 0)
+#define O_RDWR (3 << 0)
+#define O_APPEND (1 << 2)
+#define O_CREAT (1 << 3)
+#define O_DSYNC (1 << 4)
+#define O_EXCL (1 << 5)
+#define O_NOCTTY (1 << 6)
+#define O_NONBLOCK (1 << 7)
+#define O_RSYNC (1 << 8)
+#define O_SYNC (1 << 9)
+#define O_TRUNC (1 << 10)
+#define O_CREATE O_CREAT
+
+#define O_ACCMODE 0x3
+
+#define F_CLOEXEC (1 << 11)
+
+#define F_DUPFD 1
+#define F_GETFD 2
+#define F_SETFD 3
+#define F_GETFL 4
+#define F_SETFL 5
+#define F_GETLK 6
+#define F_SETLK 7
+#define F_SETLKW 8
+#define F_GETOWN 9
+#define F_SETOWN 10
+#define _F_LAST F_SETOWN
+
+#define _F_FILE_DESC (F_CLOEXEC)
+#define _O_FILE_CREATE (O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC)
+#define _O_FILE_STATUS (O_APPEND | O_DSYNC | O_NONBLOCK | O_RSYNC | O_SYNC)
+#define _O_UNSUPPORTED (0)
+
+extern int open(const char* path, int oflag, ...);
+extern int fcntl(int fildes, int cmd, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sdk-modifications/libsrc/fs/ds2_unistd.c b/sdk-modifications/libsrc/fs/ds2_unistd.c
new file mode 100644
index 0000000..8625eba
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/ds2_unistd.c
@@ -0,0 +1,18 @@
+#include "ds2_unistd.h"
+#include "fs_api.h"
+
+int close(int fildes) {
+ return _FAT_close_r (&__REENT, fildes);
+}
+
+int lseek(int fildes, int offset, int whence) {
+ return _FAT_seek_r (&__REENT, fildes, (int)offset, whence);
+}
+
+int read(int fildes, void* buf, size_t len) {
+ return _FAT_read_r (&__REENT, fildes, (char*)buf, len);
+}
+
+int write(int fildes, const void* buf, size_t len) {
+ return _FAT_write_r (&__REENT, fildes, (const char*)buf, len);
+}
diff --git a/sdk-modifications/libsrc/fs/ds2_unistd.h b/sdk-modifications/libsrc/fs/ds2_unistd.h
new file mode 100644
index 0000000..f5efdb1
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/ds2_unistd.h
@@ -0,0 +1,22 @@
+#ifndef __libc_unistd_h__
+#define __libc_unistd_h__
+
+#include "fs_common.h"
+#include "fatfile.h"
+#include "fs_api.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern int close(int fildes);
+extern int lseek(int fildes, int offset, int whence);
+extern int read(int fildes, void* buf, size_t len);
+extern int write(int fildes, const void* buf, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sdk-modifications/libsrc/fs/fat.h b/sdk-modifications/libsrc/fs/fat.h
new file mode 100644
index 0000000..3ddbac3
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fat.h
@@ -0,0 +1,118 @@
+/*
+ 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.
+
+ 2006-07-11 - Chishm
+ * Original release
+
+ 2006-07-14
+ * fatInitialise renamed to fatInit
+
+ 2006-07-16 - Chishm
+ * Added fatInitDefault
+*/
+
+
+#ifndef _LIBFAT_H
+#define _LIBFAT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// When compiling for NDS, make sure NDS is defined
+#ifndef NDS
+ #define NDS
+#endif
+
+#include <nds/jtypes.h>
+#include "partition.h"
+#include "directory.h"
+#include "file_allocation_table.h"
+#include "unicode/unicode.h"
+#include "fatdir_ex.h"
+#include "fatfile_ex.h"
+
+//typedef enum {PI_DEFAULT, PI_SLOT_1, PI_SLOT_2, PI_CUSTOM} PARTITION_INTERFACE;
+
+struct IO_INTERFACE_STRUCT ;
+
+/*
+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
+*/
+bool fatInit (u32 cacheSize, bool setAsDefaultDevice);
+
+/*
+Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system.
+*/
+bool fatInitDefault (void);
+
+/*
+Special initialize for RPG card
+*/
+bool fatInitRPG (void);
+
+/*
+Mount the device specified by partitionNumber
+PD_DEFAULT is not allowed, use _FAT_partition_setDefaultDevice
+PD_CUSTOM is not allowed, use _FAT_partition_mountCustomDevice
+*/
+bool fatMountNormalInterface (PARTITION_INTERFACE partitionNumber, u32 cacheSize);
+
+/*
+Mount a partition on a custom device
+*/
+bool fatMountCustomInterface (struct IO_INTERFACE_STRUCT* device, u32 cacheSize);
+
+/*
+Unmount the partition specified by partitionNumber
+If there are open files, it will fail
+*/
+bool fatUnmount (PARTITION_INTERFACE partitionNumber);
+
+
+/*
+Forcibly unmount the partition specified by partitionNumber
+Any open files on the partition will become invalid
+The cache will be invalidated, and any unflushed writes will be lost
+*/
+bool fatUnsafeUnmount (PARTITION_INTERFACE partitionNumber);
+
+/*
+Set the default device for access by fat: and fat0:
+PD_DEFAULT is unallowed.
+Doesn't do anything useful on GBA, since there is only one device
+*/
+bool fatSetDefaultInterface (PARTITION_INTERFACE partitionNumber);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LIBFAT_H
diff --git a/sdk-modifications/libsrc/fs/fat_misc.c b/sdk-modifications/libsrc/fs/fat_misc.c
new file mode 100644
index 0000000..c0f62ac
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fat_misc.c
@@ -0,0 +1,138 @@
+//fat_misc.c
+//v1.0
+
+#include "fat_misc.h"
+#include "fs_api.h"
+
+static unsigned int _usedSecNums;
+
+static int strFindFromEnd( char *str,char strValue )
+{
+ int pos = 0,i = 0,strNum = 0;
+ while(1)
+ {
+ if( (*str)!=0 )
+ {
+ strNum++;
+ str++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ pos = strNum;
+ for( i=0;i<strNum;i++ )
+ {
+ str--;
+ pos--;
+ if( (*str) == strValue||(*str) == ':' )
+ {
+ return pos;
+ }
+ if( (*str)== 0 )
+ {
+ return -1;
+ }
+ }
+ return -1;
+}
+
+int getDirSize( const char * path, int includeSubdirs, unsigned int * dirSize )
+{
+ char dirPath[MAX_FILENAME_LENGTH];
+ unsigned int size = 0;
+ if( "" == path ){
+ return false;
+ }
+
+ memset( dirPath,0,MAX_FILENAME_LENGTH );
+ strcpy( dirPath,path );
+
+ if( dirPath[strlen(dirPath)-1] != '/' )
+ dirPath[strlen(dirPath)] = '/';
+ if( strlen(dirPath) > MAX_FILENAME_LENGTH )
+ return false;
+
+ DIR_STATE_STRUCT *dir; dir = fat_opendir((const char*)dirPath);
+ if (dir == NULL)
+ return false;
+ struct stat stat_buf;
+ DIR_ENTRY *currentEntry;
+ char* filename;
+
+ while(fat_readdir_ex(dir, &stat_buf) != NULL)
+ {
+ filename = currentEntry->d_name;
+
+ if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
+ continue;
+
+ if (!(stat_buf.st_mode & S_IFDIR)) {
+ size += (stat_buf.st_size+511)/512;
+ _usedSecNums +=(stat_buf.st_size+511)/512;
+ }
+ else if (includeSubdirs)
+ {
+ // calculate the size recursively
+ unsigned int subDirSize = 0;
+ char dirPathBuffer[MAX_FILENAME_LENGTH];
+
+ memset( dirPathBuffer,0,MAX_FILENAME_LENGTH );
+ strcpy( dirPathBuffer,dirPath );
+ memset( dirPath,0,MAX_FILENAME_LENGTH );
+ sprintf( dirPath,"%s%s",dirPathBuffer,filename );
+ int succ = getDirSize( dirPath, includeSubdirs, &subDirSize );
+ if( succ ) {
+ size += (subDirSize+511)/512;
+ _usedSecNums +=(subDirSize+511)/512;
+ }
+ memset( dirPath,0,MAX_FILENAME_LENGTH );
+ strcpy( dirPath,dirPathBuffer );
+ }
+ }
+
+ fat_closedir(dir);
+
+ *dirSize = size;
+ return true;
+}
+
+int fat_getDiskTotalSpace( char * diskName, unsigned int * diskSpace )
+{
+ if( !strcmp("",diskName) )
+ return false;
+
+ unsigned int len = strlen(diskName);
+ if( *(diskName+len-1) != '/' ){
+ *(diskName+len) = '/';
+ }
+
+ PARTITION * diskPartition = _FAT_partition_getPartitionFromPath( diskName );
+ if( NULL == diskPartition )
+ return false;
+
+ *diskSpace = (unsigned int)diskPartition->numberOfSectors;
+ return true;
+}
+
+int fat_getDiskSpaceInfo( char * diskName, unsigned int * total, unsigned int * used, unsigned int * freeSpace )
+{
+ _usedSecNums = 0;
+
+ if( !strcmp("",diskName) )
+ return -1;
+ if( !fat_getDiskTotalSpace(diskName, total) )
+ return -1;
+ if( !getDirSize(diskName, true, used) )
+ return -1;
+
+ *used = _usedSecNums;
+ if( *total <= *used ){
+ *freeSpace = 0;
+ }else{
+ *freeSpace = *total - *used;
+ }
+
+ return 0;
+}
diff --git a/sdk-modifications/libsrc/fs/fat_misc.h b/sdk-modifications/libsrc/fs/fat_misc.h
new file mode 100644
index 0000000..05db0bb
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fat_misc.h
@@ -0,0 +1,38 @@
+/*--------------------------------------------------------------------------------
+Copyright (C) 2007 Acekard, www.acekard.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+---------------------------------------------------------------------------------*/
+#ifndef __FAT_MISC_H__
+#define __FAT_MISC_H__
+
+#include "fs_common.h"
+#include "directory.h"
+ typedef enum _SRC_FILE_MODE
+{
+ SFM_COPY = 0,
+ SFM_CUT = 1
+}SRC_FILE_MODE;
+
+
+extern bool fat_getDiskSpaceInfo( char * diskName, unsigned int *total, unsigned int *used, unsigned int *freeSpace );
+
+#endif //__FAT_MISC_H__
diff --git a/sdk-modifications/libsrc/fs/fatdir.c b/sdk-modifications/libsrc/fs/fatdir.c
new file mode 100644
index 0000000..dc9b914
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fatdir.c
@@ -0,0 +1,672 @@
+/*
+ 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.
+
+ 2006-08-13 - Chishm
+ * Moved all externally visible directory related functions to fatdir
+ * Added _FAT_mkdir_r
+
+ 2006-08-14 - Chishm
+ * Added directory iterator functions
+
+ 2006-08-19 - Chishm
+ * Updated dirnext return values to return correctly
+
+ 2006-10-01 - Chishm
+ * Now clears the whole cluster when creating a new directory, bug found by Hermes
+
+ 2007-01-10 - Chishm
+ * Updated directory iterator functions for DevkitPro r20
+*/
+
+/*
+ 2010.06.30
+ * Modify _FAT_diropen_r,
+*/
+
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/dir.h>
+
+#include "fatdir.h"
+
+#include "fs_cache.h"
+#include "file_allocation_table.h"
+#include "partition.h"
+#include "directory.h"
+#include "bit_ops.h"
+#include "filetime.h"
+#include "fs_unicode.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;
+ }
+
+ // Search for the file on the disc
+ if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) {
+ r->_errno = ENOENT;
+ return -1;
+ }
+
+ // Fill in the stat struct
+ _FAT_directory_entryStat (partition, &dirEntry, st);
+
+ return 0;
+}
+
+int _FAT_getshortname_r (struct _reent *r, const char *name, char *outName) {
+ PARTITION* partition = NULL;
+ DIR_ENTRY DirEntry;
+ u32 dirCluster;
+
+ // Get the partition this directory is on
+ partition = _FAT_partition_getPartitionFromPath (name);
+
+ if (partition == NULL) {
+ r->_errno = ENODEV;
+ return 1;
+ }
+
+ // Move the path pointer to the start of the actual path
+ if (strchr (name, ':') != NULL) {
+ name = strchr (name, ':') + 1;
+ }
+ if (strchr (name, ':') != NULL) {
+ r->_errno = EINVAL;
+ return 1;
+ }
+
+ // Search for the file on the disc
+ if (!_FAT_directory_entryFromPath (partition, &DirEntry, name, NULL)) {
+ r->_errno = ENOENT;
+ return 1;
+ }
+
+ strcpy(outName, DirEntry.entryData);
+ return 1;
+}
+
+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;
+ u32 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;
+ }
+
+ // Search for the file on the disc
+ if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) {
+ r->_errno = ENOENT;
+ return -1;
+ }
+
+ cluster = _FAT_directory_entryGetCluster (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
+ r->_errno = EPERM;
+ return -1;
+ }
+ nextEntry = _FAT_directory_getNextEntry (partition, &dirContents);
+ }
+ }
+
+ if (cluster != CLUSTER_FREE) {
+ // 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;
+ }
+
+ 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;
+ }
+
+ // Set the default device to match this one
+ if (!_FAT_partition_setDefaultPartition (partition)) {
+ r->_errno = ENOENT;
+ return -1;
+ }
+
+ // Try changing directory
+ if (_FAT_directory_chdir (partition, path)) {
+ // Successful
+ return 0;
+ } else {
+ // Failed
+ 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;
+ u32 dirCluster;
+
+ // Get the partition this directory is on
+ partition = _FAT_partition_getPartitionFromPath (oldName);
+
+ if (partition == NULL) {
+ r->_errno = ENODEV;
+ return -1;
+ }
+
+ // Make sure the same partition is used for the old and new names
+ if (partition != _FAT_partition_getPartitionFromPath (newName)) {
+ r->_errno = EXDEV;
+ 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 (oldName, ':') != NULL) {
+ oldName = strchr (oldName, ':') + 1;
+ }
+ if (strchr (oldName, ':') != NULL) {
+ r->_errno = EINVAL;
+ return -1;
+ }
+ if (strchr (newName, ':') != NULL) {
+ newName = strchr (newName, ':') + 1;
+ }
+ if (strchr (newName, ':') != NULL) {
+ r->_errno = EINVAL;
+ return -1;
+ }
+
+ // Search for the file on the disc
+ if (!_FAT_directory_entryFromPath (partition, &oldDirEntry, oldName, NULL)) {
+ 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)) {
+ 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)) {
+ r->_errno = ENOTDIR;
+ return -1;
+ }
+ dirCluster = _FAT_directory_entryGetCluster (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.d_name, pathEnd, MAX_FILENAME_LENGTH - 1);
+
+ // Write the new entry
+ if (!_FAT_directory_addEntry (partition, &newDirEntry, dirCluster)) {
+ r->_errno = ENOSPC;
+ return -1;
+ }
+
+ // Remove the old entry
+ if (!_FAT_directory_removeEntry (partition, &oldDirEntry)) {
+ r->_errno = EIO;
+ return -1;
+ }
+
+ // Flush any sectors in the disc cache
+ if (!_FAT_cache_flush (partition->cache)) {
+ r->_errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+int _FAT_hideAttrib_r (struct _reent *r, const char *name, u8 hide) {
+ PARTITION* partition = NULL;
+ DIR_ENTRY oldDirEntry;
+ DIR_ENTRY newDirEntry;
+ u32 dirCluster;
+
+ // Get the partition this directory is on
+ partition = _FAT_partition_getPartitionFromPath (name);
+
+ 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 (name, ':') != NULL) {
+ name = strchr (name, ':') + 1;
+ }
+ if (strchr (name, ':') != NULL) {
+ r->_errno = EINVAL;
+ return -1;
+ }
+
+ // Search for the file on the disc
+ if (!_FAT_directory_entryFromPath (partition, &oldDirEntry, name, NULL)) {
+ r->_errno = ENOENT;
+ return -1;
+ }
+
+ // Create the new file entry
+ // Get the directory it has to go in
+ memcpy (&newDirEntry, &oldDirEntry, sizeof(DIR_ENTRY));
+
+ // No path was specified
+ dirCluster = partition->cwdCluster;
+
+ // Remove the old entry hopefully doesn't screw things up...
+ if (!_FAT_directory_removeEntry (partition, &oldDirEntry)) {
+ r->_errno = EIO;
+ return -1;
+ }
+
+ // Set the new attribute
+ if(hide)
+ newDirEntry.entryData[DIR_ENTRY_attributes] |= ATTRIB_HID;
+ else
+ newDirEntry.entryData[DIR_ENTRY_attributes] &= ~ATTRIB_HID;
+
+ // Write the new entry
+ if (!_FAT_directory_addEntry (partition, &newDirEntry, dirCluster)) {
+ r->_errno = ENOSPC;
+ return -1;
+ }
+
+ // Flush any sectors in the disc cache
+ if (!_FAT_cache_flush (partition->cache)) {
+ r->_errno = EIO;
+ return -1;
+ }
+
+ 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;
+ u32 parentCluster, dirCluster;
+ u8 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;
+ }
+
+ // Search for the file/directory on the disc
+ fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL);
+
+ // Make sure it doesn't exist
+ if (fileExists) {
+ r->_errno = EEXIST;
+ return -1;
+ }
+
+ if (partition->readOnly) {
+ // We can't write to a read-only partition
+ 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)) {
+ r->_errno = ENOTDIR;
+ return -1;
+ }
+ parentCluster = _FAT_directory_entryGetCluster (dirEntry.entryData);
+ // Move the pathEnd past the last DIR_SEPARATOR
+ pathEnd += 1;
+ }
+
+ // Create the entry data
+ strncpy (dirEntry.d_name, 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());
+
+ // 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 (dirCluster == CLUSTER_FREE) {
+ // No space left on disc for the cluster
+ 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)) {
+ 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
+ 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)) {
+ r->_errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+DIR_STATE_STRUCT* _FAT_diropen_r(struct _reent *r, DIR_STATE_STRUCT *dirState, const char *path) {
+ DIR_ENTRY dirEntry;
+ DIR_STATE_STRUCT* state = dirState;
+ bool fileExists;
+ const char* root_path= "/";
+
+ 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;
+ }
+
+ if('\0' == path[0]) path = root_path;
+
+ // Get the start cluster of the directory
+ fileExists = _FAT_directory_entryFromPath (state->partition, &dirEntry, path, NULL);
+ if (!fileExists) {
+ r->_errno = ENOENT;
+ return NULL;
+ }
+
+ // Make sure it is a directory
+ if (! _FAT_directory_isDirectory (&dirEntry)) {
+ r->_errno = ENOTDIR;
+ return NULL;
+ }
+
+ // Save the start cluster for use when resetting the directory data
+ state->startCluster = _FAT_directory_entryGetCluster (dirEntry.entryData);
+
+ //Entry location pointer
+ state->posEntry = 0;
+
+// // Get the first entry for use with a call to dirnext
+// state->validEntry =
+// _FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster);
+ state->validEntry = false;
+
+ // We are now using this entry
+ state->inUse = true;
+ return state;
+}
+
+int _FAT_dirreset_r (struct _reent *r, DIR_STATE_STRUCT *dirState) {
+ DIR_STATE_STRUCT* state = dirState;
+
+ // Make sure we are still using this entry
+ if (!state->inUse) {
+ 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);
+
+ state->posEntry = 0;
+
+ return 0;
+}
+
+int _FAT_dirnext_r (struct _reent *r, DIR_STATE_STRUCT *dirState, struct stat *filestat) {
+ DIR_STATE_STRUCT* state = dirState;
+
+ // Make sure we are still using this entry
+ if (!state->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+
+ // Make sure there is another file to report on
+// if (!state->validEntry) {
+// r->_errno = ENOENT;
+// return -1;
+// }
+
+ if(0 == state->posEntry) {
+ state->validEntry = _FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster);
+ }
+ else
+ state->validEntry = _FAT_directory_getNextEntry (state->partition, &(state->currentEntry));
+
+ state->posEntry += 1;
+
+ if(!state->validEntry)
+ {
+ r->_errno = ENOENT;
+ return -1;
+ }
+
+ if (filestat != NULL) {
+ _FAT_directory_entryStat (state->partition, &(state->currentEntry), filestat);
+ }
+
+ return 0;
+/*
+ // Get the filename
+ strncpy (filename, state->currentEntry.d_name, 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));
+
+ return 0;
+*/
+}
+
+int _FAT_dirclose_r (struct _reent *r, DIR_STATE_STRUCT *dirState) {
+ DIR_STATE_STRUCT* state = dirState;
+
+ // We are no longer using this entry
+ state->inUse = false;
+
+ return 0;
+}
diff --git a/sdk-modifications/libsrc/fs/fatdir.h b/sdk-modifications/libsrc/fs/fatdir.h
new file mode 100644
index 0000000..7c419ee
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fatdir.h
@@ -0,0 +1,85 @@
+/*
+ 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.
+
+ 2006-08-13 - Chishm
+ * Moved all externally visible directory related functions to fatdir
+ * Added _FAT_mkdir_r
+
+ 2006-08-14 - Chishm
+ * Added directory iterator functions
+
+ 2007-01-10 - Chishm
+ * Updated directory iterator functions for DevkitPro r20
+*/
+
+
+#ifndef _FATDIR_H
+#define _FATDIR_H
+
+//#include <sys/reent.h>
+#include <sys/stat.h>
+//#include <sys/iosupport.h>
+#include "fs_common.h"
+#include "directory.h"
+
+typedef struct {
+ PARTITION* partition;
+ DIR_ENTRY currentEntry;
+ u32 startCluster;
+ u32 posEntry;
+ 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_getshortname_r (struct _reent *r, const char *name, char *outName);
+
+extern int _FAT_hideAttrib_r (struct _reent *r, const char *name, u8 hide);
+
+extern int _FAT_mkdir_r (struct _reent *r, const char *path, int mode);
+
+/*
+Directory iterator functions
+*/
+extern DIR_STATE_STRUCT* _FAT_diropen_r(struct _reent *r, DIR_STATE_STRUCT *dirState, const char *path);
+extern int _FAT_dirreset_r (struct _reent *r, DIR_STATE_STRUCT *dirState);
+extern int _FAT_dirnext_r (struct _reent *r, DIR_STATE_STRUCT *dirState, struct stat *filestat);
+extern int _FAT_dirclose_r (struct _reent *r, DIR_STATE_STRUCT *dirState);
+
+
+#endif // _FATDIR_H
diff --git a/sdk-modifications/libsrc/fs/fatdir_ex.c b/sdk-modifications/libsrc/fs/fatdir_ex.c
new file mode 100644
index 0000000..b773255
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fatdir_ex.c
@@ -0,0 +1,183 @@
+#include <string.h>
+
+#include <errno.h>
+
+#include <ctype.h>
+
+#include <unistd.h>
+
+#include <sys/dir.h>
+
+
+#include "fatdir.h"
+
+#include "fatdir_ex.h"
+
+
+#include "cache.h"
+
+#include "file_allocation_table.h"
+
+#include "partition.h"
+
+#include "directory.h"
+
+#include "bit_ops.h"
+
+#include "filetime.h"
+
+#include "unicode/unicode.h"
+
+int dirnextl (DIR_ITER *dirState, char *filename, char *longFilename, struct stat *filestat)
+{
+ DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct);
+
+
+ // Make sure we are still using this entry
+
+ if (!state->inUse) {
+
+ errno = EBADF;
+ return -1;
+
+ }
+
+
+ // Make sure there is another file to report on
+
+ if (! state->validEntry) {
+ errno = ENOENT;
+
+ return -1;
+
+ }
+
+
+ // Get the filename
+
+ strncpy (filename, state->currentEntry.d_name, MAX_FILENAME_LENGTH);
+
+ // Get long filename
+ _FAT_unicode_unicode_to_local( state->currentEntry.unicodeFilename, (u8 *)longFilename );
+
+
+ // 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));
+
+
+ return 0;
+}
+
+int renamex( const char *oldName, const char *newName )
+{
+ PARTITION* partition = NULL;
+ DIR_ENTRY oldDirEntry;
+ DIR_ENTRY newDirEntry;
+ const char *pathEnd;
+ u32 dirCluster;
+
+ // Get the partition this directory is on
+ partition = _FAT_partition_getPartitionFromPath (oldName);
+
+ if (partition == NULL) {
+ errno = ENODEV;
+ return -1;
+ }
+
+ // Make sure the same partition is used for the old and new names
+ if (partition != _FAT_partition_getPartitionFromPath (newName)) {
+ errno = EXDEV;
+ return -1;
+ }
+
+ // Make sure we aren't trying to write to a read-only disc
+ if (partition->readOnly) {
+ 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) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (strchr (newName, ':') != NULL) {
+ newName = strchr (newName, ':') + 1;
+ }
+ if (strchr (newName, ':') != NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ // Search for the file on the disc
+ if (!_FAT_directory_entryFromPath (partition, &oldDirEntry, oldName, NULL)) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ // Make sure there is no existing file / directory with the new name
+ if (_FAT_directory_entryFromPath (partition, &newDirEntry, newName, NULL)) {
+ 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)) {
+ errno = ENOTDIR;
+ return -1;
+ }
+ dirCluster = _FAT_directory_entryGetCluster (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.d_name, pathEnd, MAX_FILENAME_LENGTH - 1);
+
+ // Write the new entry
+ if (!_FAT_directory_addEntry (partition, &newDirEntry, dirCluster)) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ // Remove the old entry
+ if (!_FAT_directory_removeEntry (partition, &oldDirEntry)) {
+ errno = EIO;
+ return -1;
+ }
+
+ // Flush any sectors in the disc cache
+ if (!_FAT_cache_flush (partition->cache)) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/sdk-modifications/libsrc/fs/fatdir_ex.h b/sdk-modifications/libsrc/fs/fatdir_ex.h
new file mode 100644
index 0000000..fbac46f
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fatdir_ex.h
@@ -0,0 +1,18 @@
+#ifndef _FATDIR_EX_H_
+#define _FATDIR_EX_H_
+
+#include "fatdir.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int dirnextl (DIR_ITER *dirState, char *filename, char *longFilename, struct stat *filestat);
+int renamex( const char *oldName, const char *newName );
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif//_FATDIR_EX_H_
diff --git a/sdk-modifications/libsrc/fs/fatfile.c b/sdk-modifications/libsrc/fs/fatfile.c
new file mode 100644
index 0000000..304da34
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fatfile.c
@@ -0,0 +1,889 @@
+/*
+ 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.
+
+ 2006-07-11 - Chishm
+ * Original release
+
+ 2006-07-17 - Chishm
+ * Made all path inputs const char*
+ * Added _FAT_rename_r
+
+ 2006-08-02 - Chishm
+ * Fixed _FAT_seek_r
+
+ 2006-08-13 - Chishm
+ * Moved all externally visible directory related functions to fatdir
+*/
+//version 1.12
+
+#include "fatfile.h"
+
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include "fs_cache.h"
+#include "file_allocation_table.h"
+#include "bit_ops.h"
+#include "filetime.h"
+
+
+int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags) {
+ PARTITION* partition = NULL;
+ bool fileExists;
+ DIR_ENTRY dirEntry;
+ const char* pathEnd;
+ u32 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
+ 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) {
+ r->_errno = EEXIST;
+ return -1;
+ }
+
+ // It should not be a directory if we're openning a file,
+ if (fileExists && _FAT_directory_isDirectory(&dirEntry)) {
+ r->_errno = EISDIR;
+ return -1;
+ }
+ // 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
+ 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)) {
+ r->_errno = ENOTDIR;
+ return -1;
+ }
+ dirCluster = _FAT_directory_entryGetCluster (dirEntry.entryData);
+ // Move the pathEnd past the last DIR_SEPARATOR
+ pathEnd += 1;
+ }
+ // Create the entry data
+ strncpy (dirEntry.d_name, 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)) {
+ r->_errno = ENOSPC;
+ return -1;
+ }
+ } else {
+ // file doesn't exist, and we aren't creating it
+ 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)) {
+ r->_errno = EROFS;
+ return -1;
+ }
+
+ // Associate this file with a particular partition
+ file->partition = partition;
+ file->startCluster = _FAT_directory_entryGetCluster (dirEntry.entryData);
+
+ // Truncate the file if requested
+ if ((flags & O_TRUNC) && file->write && (file->startCluster != 0)) {
+ _FAT_fat_clearLinks (partition, file->startCluster);
+ file->startCluster = 0;
+ file->filesize = 0;
+ }
+
+ // Get a new cluster for the file if required
+ if (file->startCluster == 0) {
+ file->startCluster = _FAT_fat_linkFreeCluster (partition, CLUSTER_FREE);
+ }
+
+ // 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;
+
+ file->currentPosition = 0;
+
+ file->rwPosition.cluster = file->startCluster;
+ file->rwPosition.sector = 0;
+ file->rwPosition.byte = 0;
+
+
+ if (flags & O_APPEND) {
+ file->append = true;
+ 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;
+ file->appendPosition = file->rwPosition;
+ }
+
+ file->inUse = true;
+
+ partition->openFileCount += 1;
+
+ return (int) file;
+}
+
+int _FAT_close_r (struct _reent *r, int fd) {
+ FILE_STRUCT* file = (FILE_STRUCT*) fd;
+ u8 dirEntryData[DIR_ENTRY_DATA_SIZE];
+
+ if (!file->inUse) {
+ r->_errno = EBADF;
+ return -1;
+ }
+ if (file->write) {
+ // 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());
+
+ // 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)) {
+ r->_errno = EIO;
+ return -1;
+ }
+ }
+
+ file->inUse = false;
+ file->partition->openFileCount -= 1;
+
+ return 0;
+}
+
+int _FAT_read_r (struct _reent *r, int fd, char *ptr, int len) {
+ FILE_STRUCT* file = (FILE_STRUCT*) fd;
+ PARTITION* partition;
+ CACHE* cache;
+
+ FILE_POSITION position;
+ u32 tempNextCluster;
+
+ int tempVar;
+
+ u32 remain;
+
+ bool flagNoError = true;
+
+ // Make sure we can actually read from the file
+ if ((file == NULL) || !file->inUse || !file->read) {
+ r->_errno = EBADF;
+ return 0;
+ }
+
+ // Don't try to read if the read pointer is past the end of file
+ if (file->currentPosition >= file->filesize) {
+ 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; //Assume len > 0
+
+ position = file->rwPosition;
+
+ partition = file->partition;
+ cache = file->partition->cache;
+
+ // Move onto next cluster if needed
+ if (position.sector >= partition->sectorsPerCluster) {
+ tempNextCluster = _FAT_fat_nextCluster(partition, position.cluster);
+ if (tempNextCluster == CLUSTER_EOF) {
+ position.sector = partition->sectorsPerCluster;
+ } else if (tempNextCluster == CLUSTER_FREE) {
+ r->_errno = EIO;
+ return 0;
+ } else {
+ position.sector = 0;
+ position.cluster = tempNextCluster;
+ }
+ }
+
+ // Align to sector
+ tempVar = BYTES_PER_READ - position.byte;
+ if (tempVar > remain) {
+ tempVar = remain;
+ }
+
+ if (tempVar < BYTES_PER_READ)
+ {
+ _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
+ //remaining more than 1 sector and read part of cluster
+ if((remain >= BYTES_PER_READ) && (position.sector < partition->sectorsPerCluster))
+ {
+ // tempVar is number of sectors to write
+ tempVar = remain / BYTES_PER_READ;
+ if(tempVar > (partition->sectorsPerCluster - position.sector))
+ tempVar = partition->sectorsPerCluster - position.sector;
+
+ // read several sectors from disk
+ _FAT_disc_readSectors (partition->disc, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector,
+ tempVar, ptr);
+
+ 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 ((remain>0) && (position.sector >= partition->sectorsPerCluster)) {
+ tempNextCluster = _FAT_fat_nextCluster(partition, position.cluster);
+ if (tempNextCluster == CLUSTER_EOF) {
+ position.sector = partition->sectorsPerCluster;
+ } else if (tempNextCluster == CLUSTER_FREE) {
+ r->_errno = EIO;
+ flagNoError = false;
+ } else {
+ position.sector = 0;
+ position.cluster = tempNextCluster;
+ }
+ }
+
+ // Read in whole clusters
+ while ((remain >= partition->bytesPerCluster) && flagNoError) {
+ _FAT_disc_readSectors (partition->disc, _FAT_fat_clusterToSector (partition, position.cluster), partition->sectorsPerCluster, ptr);
+ ptr += partition->bytesPerCluster;
+ remain -= partition->bytesPerCluster;
+
+ // Advance to next cluster
+ tempNextCluster = _FAT_fat_nextCluster(partition, position.cluster);
+ if ((remain == 0) && (tempNextCluster == CLUSTER_EOF)) {
+ position.sector = partition->sectorsPerCluster;
+ } else if (tempNextCluster == CLUSTER_FREE) {
+ r->_errno = EIO;
+ flagNoError = false;
+ } else {
+ position.sector = 0;
+ position.cluster = tempNextCluster;
+ }
+ }
+
+ // Read remaining sectors
+ tempVar = remain / BYTES_PER_READ; // Number of sectors left
+ if ((tempVar > 0) && flagNoError) {
+ _FAT_disc_readSectors (partition->disc, _FAT_fat_clusterToSector (partition, position.cluster),
+ tempVar, ptr);
+ 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;
+
+ return len;
+}
+
+/*
+Extend a file so that the size is the same as the rwPosition
+*/
+static bool file_extend_r (struct _reent *r, FILE_STRUCT* file) {
+ PARTITION* partition = file->partition;
+ CACHE* cache = file->partition->cache;
+
+ FILE_POSITION position;
+
+ u32 remain;
+
+ u8 zeroBuffer [BYTES_PER_READ] = {0};
+
+ u32 tempNextCluster;
+
+ position.byte = file->filesize % BYTES_PER_READ;
+
+ u32 partCluster = file->filesize % partition->bytesPerCluster;
+ if(0 == partCluster && 0 != file->filesize) //integer cluster
+ partCluster = partition->bytesPerCluster;
+ //position.sector = (file->filesize % partition->bytesPerCluster) / BYTES_PER_READ;
+ position.sector = partCluster / BYTES_PER_READ;
+
+ //This function index the last Cluster currently used, when file size equal Cluster size, will cause problem
+ position.cluster = _FAT_fat_lastCluster (partition, file->startCluster);
+
+ remain = file->currentPosition - file->filesize;
+
+ // Move onto next cluster if needed
+ if (position.sector >= partition->sectorsPerCluster) {
+ //Because _FAT_fat_lastCluster point to the last cluster currently used, so
+ //position.sector == partition->sectorsPerCluster means, the last cluster all used
+ //move to next cluster
+ position.sector = 0;
+ 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 (tempNextCluster == CLUSTER_FREE) {
+ // Couldn't get a cluster, so abort
+ r->_errno = ENOSPC;
+ return false;
+ } else {
+ position.cluster = tempNextCluster;
+ }
+ }
+
+ // Only need to clear to the end of the sector
+ if (remain + position.byte < BYTES_PER_READ) {
+ _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;
+ 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 (tempNextCluster == CLUSTER_FREE) {
+ // Couldn't get a cluster, so abort
+ r->_errno = ENOSPC;
+ return false;
+ } else {
+ position.cluster = tempNextCluster;
+ }
+ }
+
+ _FAT_disc_writeSectors (partition->disc,
+ _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 1, zeroBuffer);
+ //cancel the dirty state of cahced sctor, not necessary here, the sector haven't allocated yet
+ //_FAT_cache_writePartialSector_check(cache,
+ // _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 1, zeroBuffer);
+
+ remain -= BYTES_PER_READ;
+ position.sector ++;
+ }
+
+ if (position.sector >= partition->sectorsPerCluster) {
+ position.sector = 0;
+ 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 (tempNextCluster == CLUSTER_FREE) {
+ // Couldn't get a cluster, so abort
+ r->_errno = ENOSPC;
+ return false;
+ } else {
+ position.cluster = tempNextCluster;
+ }
+ }
+
+ 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;
+}
+
+
+int _FAT_write_r (struct _reent *r,int fd, const char *ptr, int len) {
+ FILE_STRUCT* file = (FILE_STRUCT*) fd;
+
+ PARTITION* partition;
+ CACHE* cache;
+
+ FILE_POSITION position;
+ u32 tempNextCluster;
+
+ int tempVar;
+
+ u32 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;
+ remain = len;
+
+ 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,
+ // the extended area set to zero
+ if (file->currentPosition > file->filesize) { //This may accure when fseek called before
+ if (!file_extend_r (r, file)) { //the function have done Cluster moving
+ return 0;
+ }
+ }
+
+ // 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
+ if (position.sector >= partition->sectorsPerCluster) {
+ position.sector = 0;
+ 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 (tempNextCluster == CLUSTER_FREE) {
+ // Couldn't get a cluster, so abort
+ r->_errno = ENOSPC;
+ flagNoError = false;
+ } else {
+ position.cluster = tempNextCluster;
+ }
+ }
+
+ // Align to sector
+ if(flagNoError && (position.byte > 0))
+ {
+ tempVar = BYTES_PER_READ - position.byte;
+ if (tempVar > remain) {
+ tempVar = remain;
+ }
+
+ // 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 ++; //here may be needed move to next cluster
+ }
+ }
+
+ // Align to cluster
+ //remaining more than 1 sector and used part of cluster
+ if(flagNoError && (remain >= BYTES_PER_READ) && (position.sector < partition->sectorsPerCluster))
+ {
+ // tempVar is number of sectors to write
+ tempVar = remain / BYTES_PER_READ;
+ if(tempVar > (partition->sectorsPerCluster - position.sector))
+ tempVar = partition->sectorsPerCluster - position.sector;
+
+ // write several sectors to disk
+ _FAT_disc_writeSectors (partition->disc,
+ _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, tempVar, ptr);
+ //cancel the dirty state of cached sctor
+ _FAT_cache_writePartialSector_check(cache,
+ _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, tempVar, ptr);
+
+ ptr += tempVar * BYTES_PER_READ;
+ remain -= tempVar * BYTES_PER_READ;
+ position.sector += tempVar;
+ }
+
+ // Move onto next cluster if needed
+ if (flagNoError && (position.sector >= partition->sectorsPerCluster) && (remain > 0)) {
+ position.sector = 0;
+ 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 (tempNextCluster == CLUSTER_FREE) {
+ // Couldn't get a cluster, so abort
+ r->_errno = ENOSPC;
+ flagNoError = false;
+ } else {
+ position.cluster = tempNextCluster;
+ }
+ }
+
+ // Write whole clusters
+ while ((remain >= partition->bytesPerCluster) && flagNoError) {
+ _FAT_disc_writeSectors (partition->disc, _FAT_fat_clusterToSector(partition, position.cluster),
+ partition->sectorsPerCluster, ptr);
+ //cancel the dirty state of cahced sctor
+ _FAT_cache_writePartialSector_check(cache, _FAT_fat_clusterToSector(partition, position.cluster),
+ partition->sectorsPerCluster, ptr);
+
+ ptr += partition->bytesPerCluster;
+ remain -= partition->bytesPerCluster;
+ if (remain > 0) {
+ 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 (tempNextCluster == CLUSTER_FREE) {
+ // Couldn't get a cluster, so abort
+ r->_errno = ENOSPC;
+ flagNoError = false;
+ } else {
+ position.cluster = tempNextCluster;
+ }
+ } else {
+ // Allocate a new cluster when next writing the file
+ position.sector = partition->sectorsPerCluster;
+ }
+ }
+
+ // Write remaining sectors
+ // Number of sectors left
+ tempVar = remain / BYTES_PER_READ;
+ if ((tempVar > 0) && flagNoError) {
+ _FAT_disc_writeSectors (partition->disc, _FAT_fat_clusterToSector (partition, position.cluster),
+ tempVar, ptr);
+ //cancel the dirty state of cahced sctor
+ _FAT_cache_writePartialSector_check(cache, _FAT_fat_clusterToSector (partition, position.cluster),
+ tempVar, ptr);
+
+ ptr += tempVar * BYTES_PER_READ;
+ remain -= tempVar * BYTES_PER_READ;
+ position.sector += tempVar;
+ }
+
+ // Last remaining partial sector
+ if ((remain > 0) && flagNoError) {
+ if (flagAppending) {
+ //allocated new sector, erasing remaing unused part
+ //Actually, no necessary. when using fwrite to increase the file size, valid data area end at currentPosition
+ //when using fseek to increase file size, valid data area end at rwPosition, the increased zero area will valid
+ //at next fwrite, fread before this can not read out the increased zero area, the file size also not changed
+ //before next fwrite called
+ _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 read is the originally requested amount minus stuff remaining
+ len = len - remain;
+
+ // Update file information
+ 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;
+ }
+ }
+
+ return len;
+}
+
+#include "ds2io.h"
+int _FAT_seek_r (struct _reent *r, int fd, int pos, int dir) {
+ FILE_STRUCT* file = (FILE_STRUCT*) fd;
+
+ PARTITION* partition;
+
+ u32 cluster, nextCluster;
+ int clusCount;
+ int position;
+
+ if ((file == NULL) || (file->inUse == false)) {
+ // invalid file
+ r->_errno = EBADF;
+ return -1;
+ }
+
+ partition = file->partition;
+
+ switch (dir) {
+ case SEEK_SET:
+ position = pos;
+ break;
+ case SEEK_CUR:
+ position = file->currentPosition + pos;
+ break;
+ case SEEK_END:
+ position = file->filesize + pos;
+ break;
+ default:
+ r->_errno = EINVAL;
+ return -1;
+ }
+
+ if ((pos > 0) && (position < 0)) {
+ r->_errno = EOVERFLOW;
+ return -1;
+ }
+
+ if (position < 0) {
+ r->_errno = EINVAL;
+ return -1;
+ }
+
+ // Only change the read/write position if it is within or at the bounds of the current filesize
+ if (file->filesize >= position) {
+
+ // Calculate where the correct cluster is
+ if (position >= file->currentPosition) {
+ clusCount = (position / partition->bytesPerCluster) - (file->currentPosition / partition->bytesPerCluster);
+ cluster = file->rwPosition.cluster;
+ //When last reading, it stop at the cluster boundary,
+ //file->rwPosition.sector == partition->bytesPerCluster, but
+ //file->rwPosition.cluster did not move to next cluster
+ if(file->rwPosition.sector >= partition->sectorsPerCluster)
+ clusCount += 1;
+ } else {
+ clusCount = position / partition->bytesPerCluster;
+ cluster = file->startCluster;
+ }
+
+ // 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 the file is being written to
+ if ((clusCount > 0) && (file->write || file->append)) {
+ // Set flag to allocate a new cluster
+ file->rwPosition.sector = partition->sectorsPerCluster;
+ file->rwPosition.byte = 0;
+ }
+
+ file->rwPosition.cluster = cluster;
+ }
+
+ // Save position
+ file->currentPosition = position;
+
+ 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;
+
+ // Get the file's entry data
+ fileEntry.dataStart = file->dirEntryStart;
+ fileEntry.dataEnd = file->dirEntryEnd;
+
+ if (!_FAT_directory_entryFromPosition (partition, &fileEntry)) {
+ 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
+
+ return 0;
+}
+
diff --git a/sdk-modifications/libsrc/fs/fatfile.h b/sdk-modifications/libsrc/fs/fatfile.h
new file mode 100644
index 0000000..4869562
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fatfile.h
@@ -0,0 +1,89 @@
+/*
+ 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.
+
+ 2006-07-11 - Chishm
+ * Original release
+
+ 2006-07-17 - Chishm
+ * Made all path inputs const char*
+ * Added _FAT_rename_r
+
+ 2006-07-24 - Chishm
+ * Removed padding workaround from FILE_STRUCT
+
+ 2006-08-13 - Chishm
+ * Moved all externally visible directory related functions to fatdir
+*/
+
+
+#ifndef _FATFILE_H
+#define _FATFILE_H
+
+//#include <sys/reent.h>
+#include <sys/stat.h>
+
+#include "fs_common.h"
+#include "partition.h"
+#include "directory.h"
+
+typedef struct {
+ u32 cluster;
+ u32 sector;
+ s32 byte;
+} FILE_POSITION;
+
+typedef struct {
+ int fd;
+ u32 filesize;
+ u32 startCluster;
+ u32 currentPosition;
+ FILE_POSITION rwPosition;
+ FILE_POSITION appendPosition;
+ bool read;
+ bool write;
+ bool append;
+ bool inUse;
+ PARTITION* partition;
+ 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
+} FILE_STRUCT;
+
+extern int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags);
+
+extern int _FAT_close_r (struct _reent *r, int fd);
+
+extern int _FAT_write_r (struct _reent *r,int fd, const char *ptr, int len);
+
+extern int _FAT_read_r (struct _reent *r, int fd, char *ptr, int len);
+
+extern int _FAT_seek_r (struct _reent *r, int fd,int pos, int dir);
+
+extern int _FAT_fstat_r (struct _reent *r, int fd, struct stat *st);
+
+#endif // _FATFILE_H
diff --git a/sdk-modifications/libsrc/fs/fatfile_ex.c b/sdk-modifications/libsrc/fs/fatfile_ex.c
new file mode 100644
index 0000000..aa23fb3
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fatfile_ex.c
@@ -0,0 +1,47 @@
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <iorpg.h>
+
+#include "cache.h"
+#include "file_allocation_table.h"
+#include "bit_ops.h"
+#include "filetime.h"
+#include "fatfile.h"
+
+// origin fread/fwrite do not take advantage of continuous read/write function of SD/NAND,
+// it looks like the third parameter to _FAT_read_r()/_FAT_write_r() is a fixed '2'
+// so they are much slower than these two....
+
+// due to behavior of _FAT_read_r()/_FAT_write_r(),
+// FAT should have a at least 8192 cluster size to get better performance,
+// 16K or larger cluster size can get the best performance.
+
+
+
+int freadex( void * buffer, int _size, int _n, FILE * f )
+{
+ if( 0 == _n )
+ return 0;
+ dbg_printf("freadx %d\n", _n );
+ struct _reent r;
+ int ret = _FAT_read_r( &r, (int)f->_file + 8, buffer, _size * _n );
+ errno = r._errno;
+ return ret;
+}
+
+
+int fwriteex( const void * buffer, int _size, int _n, FILE * f )
+{
+ if( 0 == _n )
+ return 0;
+ dbg_printf("fwritex new %d\n", _n );
+ struct _reent r;
+ int ret = _FAT_write_r( &r, (int)f->_file + 8, buffer, _size * _n );
+ errno = r._errno;
+ return ret;
+}
+
diff --git a/sdk-modifications/libsrc/fs/fatfile_ex.h b/sdk-modifications/libsrc/fs/fatfile_ex.h
new file mode 100644
index 0000000..c434b63
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fatfile_ex.h
@@ -0,0 +1,19 @@
+#ifndef _FATFILE_EX_H_
+#define _FATFILE_EX_H_
+
+#include <stdio.h>
+#include "fatfile.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int freadex( void * buffer, int _size, int _n, FILE * f );
+int fwritex( const void * buffer, int _size, int _n, FILE * f );
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif//_FATFILE_EX_H_
diff --git a/sdk-modifications/libsrc/fs/file_allocation_table.c b/sdk-modifications/libsrc/fs/file_allocation_table.c
new file mode 100644
index 0000000..f382b52
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/file_allocation_table.c
@@ -0,0 +1,330 @@
+/*
+ file_allocation_table.c
+ Reading, writing and manipulation of the FAT structure on
+ a FAT partition
+
+ Copyright (c) 2006 Michael "Chishm" Chisholm
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation and/or
+ other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ 2006-07-11 - Chishm
+ * Original release
+
+ 2006-07-11 - Chishm
+ * Made several fixes related to free clusters, thanks to Loopy
+
+ 2006-10-01 - Chishm
+ * Added _FAT_fat_linkFreeClusterCleared to clear a cluster when it is allocated
+*/
+
+
+#include "file_allocation_table.h"
+#include "partition.h"
+#include <string.h>
+
+/*
+Gets the cluster linked from input cluster
+*/
+u32 _FAT_fat_nextCluster(PARTITION* partition, u32 cluster)
+{
+ u32 nextCluster = CLUSTER_FREE;
+ u32 sector;
+ int offset;
+
+ switch (partition->filesysType)
+ {
+ case FS_UNKNOWN:
+ nextCluster = CLUSTER_FREE;
+ break;
+
+ case FS_FAT12:
+ sector = partition->fat.fatStart + (((cluster * 3) / 2) / BYTES_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTES_PER_READ;
+
+
+ _FAT_cache_readPartialSector (partition->cache, &nextCluster, sector, offset, sizeof(u8));
+
+ offset++;
+
+ if (offset >= BYTES_PER_READ) {
+ offset = 0;
+ sector++;
+ }
+
+ _FAT_cache_readPartialSector (partition->cache, ((u8*)&nextCluster) + sizeof(u8), sector, offset, sizeof(u8));
+
+ if (cluster & 0x01) {
+ nextCluster = nextCluster >> 4;
+ } else {
+ nextCluster &= 0x0FFF;
+ }
+
+ if (nextCluster >= 0x0FF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = partition->fat.fatStart + ((cluster << 1) / BYTES_PER_READ);
+ offset = (cluster % (BYTES_PER_READ >> 1)) << 1;
+
+ _FAT_cache_readPartialSector (partition->cache, &nextCluster, sector, offset, sizeof(u16));
+
+ if (nextCluster >= 0xFFF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+ break;
+
+ case FS_FAT32:
+ sector = partition->fat.fatStart + ((cluster << 2) / BYTES_PER_READ);
+ offset = (cluster % (BYTES_PER_READ >> 2)) << 2;
+
+ _FAT_cache_readPartialSector (partition->cache, &nextCluster, sector, offset, sizeof(u32));
+
+ if (nextCluster >= 0x0FFFFFF7)
+ {
+ nextCluster = CLUSTER_EOF;
+ }
+ break;
+
+ default:
+ nextCluster = CLUSTER_FREE;
+ break;
+ }
+
+ return nextCluster;
+}
+
+/*
+writes value into the correct offset within a partition's FAT, based
+on the cluster number.
+*/
+static bool _FAT_fat_writeFatEntry (PARTITION* partition, u32 cluster, u32 value) {
+ u32 sector;
+ int offset;
+ u8 oldValue;
+
+ if ((cluster < 0x0002) || (cluster > partition->fat.lastCluster))
+ {
+ return false;
+ }
+
+ switch (partition->filesysType)
+ {
+ case FS_UNKNOWN:
+ return false;
+ break;
+
+ case FS_FAT12:
+ sector = partition->fat.fatStart + (((cluster * 3) / 2) / BYTES_PER_READ);
+ offset = ((cluster * 3) / 2) % BYTES_PER_READ;
+
+ if (cluster & 0x01) {
+
+ _FAT_cache_readPartialSector (partition->cache, &oldValue, sector, offset, sizeof(u8));
+
+ value = (value << 4) | (oldValue & 0x0F);
+
+ _FAT_cache_writePartialSector (partition->cache, &value, sector, offset, sizeof(u8));
+
+ offset++;
+ if (offset >= BYTES_PER_READ) {
+ offset = 0;
+ sector++;
+ }
+
+ _FAT_cache_writePartialSector (partition->cache, ((u8*)&value) + sizeof(u8), sector, offset, sizeof(u8));
+
+ } else {
+
+ _FAT_cache_writePartialSector (partition->cache, &value, sector, offset, sizeof(u8));
+
+ offset++;
+ if (offset >= BYTES_PER_READ) {
+ offset = 0;
+ sector++;
+ }
+
+ _FAT_cache_readPartialSector (partition->cache, &oldValue, sector, offset, sizeof(u8));
+
+ value = ((value >> 8) & 0x0F) | (oldValue & 0xF0);
+
+ _FAT_cache_writePartialSector (partition->cache, &value, sector, offset, sizeof(u8));
+ }
+
+ break;
+
+ case FS_FAT16:
+ sector = partition->fat.fatStart + ((cluster << 1) / BYTES_PER_READ);
+ offset = (cluster % (BYTES_PER_READ >> 1)) << 1;
+
+ _FAT_cache_writePartialSector (partition->cache, &value, sector, offset, sizeof(u16));
+
+ break;
+
+ case FS_FAT32:
+ sector = partition->fat.fatStart + ((cluster << 2) / BYTES_PER_READ);
+ offset = (cluster % (BYTES_PER_READ >> 2)) << 2;
+
+ _FAT_cache_writePartialSector (partition->cache, &value, sector, offset, sizeof(u32));
+
+ break;
+
+ default:
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+gets the first available free cluster, sets it
+to end of file, links the input cluster to it then returns the
+cluster number
+If an error occurs, return CLUSTER_FREE
+-----------------------------------------------------------------*/
+u32 _FAT_fat_linkFreeCluster(PARTITION* partition, u32 cluster) {
+ u32 firstFree;
+ u32 curLink;
+ u32 lastCluster;
+ bool loopedAroundFAT = false;
+
+ lastCluster = partition->fat.lastCluster;
+
+ if (cluster > lastCluster) {
+ return CLUSTER_FREE;
+ }
+
+ // Check if the cluster already has a link, and return it if so
+ curLink = _FAT_fat_nextCluster(partition, cluster);
+ if ((curLink >= CLUSTER_FIRST) && (curLink <= lastCluster)) {
+ return curLink; // Return the current link - don't allocate a new one
+ }
+
+ // Get a free cluster
+ firstFree = partition->fat.firstFree;
+ // Start at first valid cluster
+ if (firstFree < CLUSTER_FIRST) {
+ firstFree = CLUSTER_FIRST;
+ }
+
+ // Search until a free cluster is found
+ while (_FAT_fat_nextCluster(partition, firstFree) != CLUSTER_FREE) {
+ firstFree++;
+ if (firstFree > lastCluster) {
+ if (loopedAroundFAT) {
+ // If couldn't get a free cluster then return, saying this fact
+ partition->fat.firstFree = firstFree;
+ return CLUSTER_FREE;
+ } else {
+ // Try looping back to the beginning of the FAT
+ // This was suggested by loopy
+ firstFree = CLUSTER_FIRST;
+ loopedAroundFAT = true;
+ }
+ }
+ }
+ partition->fat.firstFree = firstFree;
+
+ if ((cluster >= CLUSTER_FIRST) && (cluster < lastCluster))
+ {
+ // Update the linked from FAT entry
+ _FAT_fat_writeFatEntry (partition, cluster, firstFree);
+ }
+ // Create the linked to FAT entry
+ _FAT_fat_writeFatEntry (partition, firstFree, CLUSTER_EOF);
+
+ return firstFree;
+}
+
+/*-----------------------------------------------------------------
+gets the first available free cluster, sets it
+to end of file, links the input cluster to it, clears the new
+cluster to 0 valued bytes, then returns the cluster number
+If an error occurs, return CLUSTER_FREE
+-----------------------------------------------------------------*/
+u32 _FAT_fat_linkFreeClusterCleared (PARTITION* partition, u32 cluster) {
+ u32 newCluster;
+ int i;
+ u8 emptySector[BYTES_PER_READ];
+
+ // Link the cluster
+ newCluster = _FAT_fat_linkFreeCluster(partition, cluster);
+ if (newCluster == CLUSTER_FREE) {
+ return CLUSTER_FREE;
+ }
+
+ // Clear all the sectors within the cluster
+ memset (emptySector, 0, BYTES_PER_READ);
+ for (i = 0; i < partition->sectorsPerCluster; i++) {
+ _FAT_disc_writeSectors (partition->disc,
+ _FAT_fat_clusterToSector (partition, newCluster) + i,
+ 1, emptySector);
+ }
+
+ return newCluster;
+}
+
+
+/*-----------------------------------------------------------------
+_FAT_fat_clearLinks
+frees any cluster used by a file
+-----------------------------------------------------------------*/
+bool _FAT_fat_clearLinks (PARTITION* partition, u32 cluster) {
+ u32 nextCluster;
+
+ if ((cluster < 0x0002) || (cluster > partition->fat.lastCluster))
+ return false;
+
+ // If this clears up more space in the FAT before the current free pointer, move it backwards
+ if (cluster < partition->fat.firstFree) {
+ partition->fat.firstFree = cluster;
+ }
+
+ while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE)) {
+ // Store next cluster before erasing the link
+ nextCluster = _FAT_fat_nextCluster (partition, cluster);
+
+ // Erase the link
+ _FAT_fat_writeFatEntry (partition, cluster, CLUSTER_FREE);
+
+ // Move onto next cluster
+ cluster = nextCluster;
+ }
+
+ return true;
+}
+
+/*-----------------------------------------------------------------
+_FAT_fat_lastCluster
+Trace the cluster links until the last one is found
+-----------------------------------------------------------------*/
+u32 _FAT_fat_lastCluster (PARTITION* partition, u32 cluster) {
+ while ((_FAT_fat_nextCluster(partition, cluster) != CLUSTER_FREE) && (_FAT_fat_nextCluster(partition, cluster) != CLUSTER_EOF)) {
+ cluster = _FAT_fat_nextCluster(partition, cluster);
+ }
+ return cluster;
+}
diff --git a/sdk-modifications/libsrc/fs/file_allocation_table.h b/sdk-modifications/libsrc/fs/file_allocation_table.h
new file mode 100644
index 0000000..4fb4149
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/file_allocation_table.h
@@ -0,0 +1,64 @@
+/*
+ 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.
+
+ 2006-07-11 - Chishm
+ * Original release
+
+ 2006-10-01 - Chishm
+ * Added _FAT_fat_linkFreeClusterCleared to clear a cluster when it is allocated
+*/
+
+#ifndef _FAT_H
+#define _FAT_H
+
+#include "fs_common.h"
+#include "partition.h"
+
+#define CLUSTER_EOF_16 0xFFFF
+#define CLUSTER_EOF 0x0FFFFFFF
+#define CLUSTER_FREE 0x0000
+#define CLUSTER_FIRST 0x0002
+
+#define CLUSTERS_PER_FAT12 4085
+#define CLUSTERS_PER_FAT16 65525
+
+
+u32 _FAT_fat_nextCluster(PARTITION* partition, u32 cluster);
+
+u32 _FAT_fat_linkFreeCluster(PARTITION* partition, u32 cluster);
+u32 _FAT_fat_linkFreeClusterCleared (PARTITION* partition, u32 cluster);
+
+bool _FAT_fat_clearLinks (PARTITION* partition, u32 cluster);
+
+u32 _FAT_fat_lastCluster (PARTITION* partition, u32 cluster);
+
+static inline u32 _FAT_fat_clusterToSector (PARTITION* partition, u32 cluster) {
+ return (cluster >= 2) ? ((cluster - 2) * partition->sectorsPerCluster) + partition->dataStart : partition->rootDirStart;
+}
+
+#endif // _FAT_H
diff --git a/sdk-modifications/libsrc/fs/filetime.c b/sdk-modifications/libsrc/fs/filetime.c
new file mode 100644
index 0000000..f2de6ef
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/filetime.c
@@ -0,0 +1,146 @@
+/*
+ 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.
+
+ 2006-07-11 - Chishm
+ * Original release
+
+ 2006-09-30 - Chishm
+ * Validity checks performed on the time supplied by the IPC
+ * Cleaned up magic numbers
+
+ 2006-10-01 - Chishm
+ * Fixed incorrect use of bitwise-or instead of logical-or
+*/
+
+
+#include "filetime.h"
+
+#ifdef NDS
+//#include <nds/ipc.h>
+#include "ds2io.h"
+#endif
+
+#define HOUR_PM_INDICATOR 40
+
+#define MAX_HOUR 23
+#define MAX_MINUTE 59
+#define MAX_SECOND 59
+
+#define MAX_YEAR 99
+#define MIN_YEAR 6 // The date is invalid if it's before this year
+#define MAX_MONTH 12
+#define MIN_MONTH 1
+#define MAX_DAY 31
+#define MIN_DAY 1
+
+// Second values are averages, so time value won't be 100% accurate,
+// but should be within the correct month.
+#define SECONDS_PER_MINUTE 60
+#define SECONDS_PER_HOUR 3600
+#define SECONDS_PER_DAY 86400
+#define SECONDS_PER_MONTH 2629743
+#define SECONDS_PER_YEAR 31556926
+
+u16 _FAT_filetime_getTimeFromRTC (void) {
+#ifdef NDS
+ int hour, minute, second;
+ struct rtc time;
+
+ ds2_getTime(&time);
+
+ hour = (time.hours >= HOUR_PM_INDICATOR ? time.hours - HOUR_PM_INDICATOR : time.hours);
+ minute = time.minutes;
+ second = time.seconds;
+
+ // Check that the values are all in range.
+ // If they are not, return 0 (no timestamp)
+ if ((hour < 0) || (hour > MAX_HOUR)) return 0;
+ if ((minute < 0) || (minute > MAX_MINUTE)) return 0;
+ if ((second < 0) || (second > MAX_SECOND)) return 0;
+
+ return (
+ ((hour & 0x1F) << 11) |
+ ((minute & 0x3F) << 5) |
+ ((second >> 1) & 0x1F)
+ );
+#else
+ return 0;
+#endif
+}
+
+
+u16 _FAT_filetime_getDateFromRTC (void) {
+#ifdef NDS
+ int year, month, day;
+ struct rtc time;
+
+ ds2_getTime(&time);
+
+ year = time.year;
+ month = time.month;
+ day = time.day;
+
+ if ((year < MIN_YEAR) || (year > MAX_YEAR)) return 0;
+ if ((month < MIN_MONTH) || (month > MAX_MONTH)) return 0;
+ if ((day < MIN_DAY) || (day > MAX_DAY)) return 0;
+
+ return (
+ (((year + 20) & 0x7F) <<9) | // Adjust for MS-FAT base year (1980 vs 2000 for DS clock)
+ ((month & 0xF) << 5) |
+ (day & 0x1F)
+ );
+#else
+ return 0;
+#endif
+}
+
+time_t _FAT_filetime_to_time_t (u16 time, u16 date) {
+ int hour, minute, second;
+ int day, month, year;
+
+ time_t result;
+
+ hour = time >> 11;
+ minute = (time >> 5) & 0x3F;
+ second = (time & 0x1F) << 1;
+
+ day = date & 0x1F;
+ month = (date >> 5) & 0x0F;
+ year = date >> 9;
+
+ // Second values are averages, so time value won't be 100% accurate,
+ // but should be within the correct month.
+ result = second
+ + minute * SECONDS_PER_MINUTE
+ + hour * SECONDS_PER_HOUR
+ + day * SECONDS_PER_DAY
+ + month * SECONDS_PER_MONTH
+ + year * SECONDS_PER_YEAR
+ ;
+
+ return result;
+}
diff --git a/sdk-modifications/libsrc/fs/filetime.h b/sdk-modifications/libsrc/fs/filetime.h
new file mode 100644
index 0000000..fa651a7
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/filetime.h
@@ -0,0 +1,44 @@
+/*
+ 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.
+
+ 2006-07-11 - Chishm
+ * Original release
+*/
+
+#ifndef _FILETIME_H
+#define _FILETIME_H
+
+#include "fs_common.h"
+#include <sys/types.h>
+
+u16 _FAT_filetime_getTimeFromRTC (void);
+u16 _FAT_filetime_getDateFromRTC (void);
+
+time_t _FAT_filetime_to_time_t (u16 time, u16 date);
+
+
+#endif // _FILETIME_H
diff --git a/sdk-modifications/libsrc/fs/fs.mk b/sdk-modifications/libsrc/fs/fs.mk
new file mode 100644
index 0000000..9b17c48
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fs.mk
@@ -0,0 +1,24 @@
+#fs.mk
+
+SRC += $(FS_DIR)/cache.c \
+ $(FS_DIR)/directory.c \
+ $(FS_DIR)/fatdir.c \
+ $(FS_DIR)/fatfile.c \
+ $(FS_DIR)/file_allocation_table.c \
+ $(FS_DIR)/filetime.c \
+ $(FS_DIR)/fs_api.c \
+ $(FS_DIR)/fs_unicode.c \
+ $(FS_DIR)/libfat.c \
+ $(FS_DIR)/partition.c \
+ $(FS_DIR)/fat_misc.c \
+ $(FS_DIR)/disc_io/disc.c \
+ $(FS_DIR)/disc_io/io_ds2_mmcf.c \
+ $(FS_DIR)/ds2_fcntl.c \
+ $(FS_DIR)/ds2_unistd.c \
+
+SSRC +=
+
+INC += -I$(FS_DIR) -I$(FS_DIR)/disc_io
+
+CFLAGS += -DNDS
+
diff --git a/sdk-modifications/libsrc/fs/fs_api.c b/sdk-modifications/libsrc/fs/fs_api.c
new file mode 100644
index 0000000..b23a450
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fs_api.c
@@ -0,0 +1,452 @@
+//fs_api.c
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/stat.h>
+#include "fs_common.h"
+#include "fatfile.h"
+#include "fatdir.h"
+
+typedef unsigned int size_t;
+typedef unsigned int mode_t;
+
+typedef struct {
+ const char* mode; /* mode string */
+ unsigned int flag; /* mode flag */
+// unsigned char mode_r; /* READ */
+// unsigned char mode_w; /* WRITE */
+// unsigned char mode_a; /* APPEND */
+// unsigned char mode_c; /* CREATE */
+} _fat_mode_type;
+
+static const _fat_mode_type _fat_valid_mode[] = {
+ { "r" , O_RDONLY },
+ { "w" , O_WRONLY | O_TRUNC | O_CREAT },
+ { "a" , O_WRONLY | O_APPEND | O_CREAT },
+ { "rb" , O_RDONLY },
+ { "wb" , O_WRONLY | O_TRUNC | O_CREAT },
+ { "ab" , O_WRONLY | O_APPEND | O_CREAT },
+ { "r+" , O_RDWR },
+ { "w+" , O_RDWR | O_TRUNC | O_CREAT },
+ { "a+" , O_RDWR | O_APPEND | O_CREAT },
+ { "r+b" , O_RDWR },
+ { "rb+" , O_RDWR },
+ { "w+b" , O_RDWR | O_TRUNC | O_CREAT },
+ { "wb+" , O_RDWR | O_TRUNC | O_CREAT },
+ { "a+b" , O_RDWR | O_APPEND | O_CREAT },
+ { "ab+" , O_RDWR | O_APPEND | O_CREAT }
+};
+
+#define MAX_OPEN_FILE 16
+#define MAX_OPEN_DIR 32
+#define MAX_PWD_LEN 512
+#define MAX_STDIO_BUF_SIZE 2048
+
+#define FAT_VALID_MODE_NUM (sizeof(_fat_valid_mode)/sizeof(_fat_mode_type))
+
+static FILE_STRUCT __FILEs[MAX_OPEN_FILE];
+struct _reent __REENT;
+static DIR_STATE_STRUCT __DIRs[MAX_OPEN_DIR];
+static char __PWD[MAX_PWD_LEN];
+
+int fat_init(void)
+{
+ int i, flag;
+
+ for(i= 0; i < MAX_OPEN_FILE; i++)
+ __FILEs[i].inUse = false;
+
+ for(i= 0; i < MAX_OPEN_DIR; i++)
+ __DIRs[i].inUse = false;
+
+ flag = -1;
+ if(_FAT_Init() == true) //success
+ flag = 0;
+
+ return flag;
+}
+
+FILE_STRUCT* fat_fopen(const char *file, const char *mode)
+{
+ int i, k;
+ int fd;
+ FILE_STRUCT* fp;
+
+ for(i= 0; i < MAX_OPEN_FILE; i++)
+ if(false == __FILEs[i].inUse) break;
+
+ if(i >= MAX_OPEN_FILE)
+ {
+ __REENT._errno = EMFILE; /* Too many open files */
+ return NULL;
+ }
+
+ for(k = 0; k < FAT_VALID_MODE_NUM; k++)
+ if(!strcasecmp(_fat_valid_mode[k].mode, mode)) break;
+
+ if(k >= FAT_VALID_MODE_NUM)
+ {
+ __REENT._errno = EINVAL;
+ return NULL;
+ }
+
+ fd = _FAT_open_r(&__REENT, (void*)&__FILEs[i], file, _fat_valid_mode[k].flag);
+ if(-1 == fd) return NULL;
+
+ fp = &__FILEs[i];
+ fp -> fd = fd;
+
+ return fp;
+}
+
+size_t fat_fread(void *buf, size_t size, size_t count, FILE_STRUCT *fp)
+{
+ if(0 == size || 0 == count)
+ return 0;
+
+ int len = _FAT_read_r(&__REENT, fp->fd, (char*)buf, size*count);
+ len /= size;
+ return len;
+}
+
+size_t fat_fwrite(const void *buf, size_t size, size_t count, FILE_STRUCT *fp)
+{
+ if(0 == size || 0 == count)
+ return 0;
+
+ int len = _FAT_write_r(&__REENT, fp->fd, (const char*)buf, size*count);
+ len /= size;
+
+ return len;
+}
+
+int fat_fclose(FILE_STRUCT *fp)
+{
+ return( _FAT_close_r(&__REENT, fp->fd) );
+}
+
+int fat_fseek(FILE_STRUCT *fp, long offset, int whence)
+{
+ int flag;
+
+ //When success, _FAT_seek_r return file position pointer
+ flag = _FAT_seek_r (&__REENT, fp->fd, (int)offset, whence);
+ if(flag > 0) flag = 0;
+
+ return flag;
+}
+
+long fat_ftell(FILE_STRUCT *fp)
+{
+ return( (long)fp->currentPosition );
+}
+
+int fat_feof(FILE_STRUCT *fp)
+{
+ int result;
+
+ result = 0;
+ if((fp->currentPosition +1) >= (fp->filesize))
+ result = -1;
+
+ return result;
+}
+
+int fat_ferror(FILE_STRUCT *fp)
+{
+ return( __REENT._errno );
+}
+
+void fat_clearerr(FILE_STRUCT *fp)
+{
+ fp = fp;
+
+ __REENT._errno = 0;
+}
+
+int fat_fflush(FILE_STRUCT *fp)
+{
+ return(_FAT_cache_flush(fp->partition->cache));
+}
+
+int fat_fgetc(FILE_STRUCT *fp)
+{
+ char ch;
+ int result;
+
+ result = _FAT_read_r(&__REENT, fp->fd, &ch, 1);
+ if(0 == result)
+ return EOF;
+
+ return ( (int)ch );
+}
+
+char* fat_fgets(char* buf, int n, FILE_STRUCT* fp)
+{
+ int m;
+ char *s;
+
+ buf[0] = '\0';
+ if(n <= 1) return buf;
+
+ n -= 1;
+ m = _FAT_read_r(&__REENT, fp->fd, buf, n);
+ if(0 == m) return NULL;
+
+ buf[m] = '\0';
+ s = strchr((const char*)buf, 0x0A);
+
+ if(NULL != s)
+ {
+ *(++s)= '\0';
+ m -= s - buf;
+
+ //fix reading pointer
+ _FAT_seek_r (&__REENT, fp->fd, -m, SEEK_CUR);
+ }
+ else if(m == n)
+ {
+ if(0x0D == buf[n-1]) //0x0D,0x0A
+ {
+ buf[n-1] = '\0';
+ _FAT_seek_r (&__REENT, fp->fd, -1, SEEK_CUR);
+ }
+ }
+
+ return buf;
+}
+
+int fat_fputc(int ch, FILE_STRUCT *fp)
+{
+ return( _FAT_write_r(&__REENT, fp->fd, (const char*)&ch, 1) );
+}
+
+int fat_fputs(const char *s, FILE_STRUCT *fp)
+{
+ unsigned int len;
+
+ len = strlen(s);
+ return( _FAT_write_r(&__REENT, fp->fd, s, len) );
+}
+
+int fat_remove(const char *filename)
+{
+ return( _FAT_unlink_r (&__REENT, (const char*)filename) );
+}
+
+int fat_rename(const char *oldName, const char *newName)
+{
+ return( _FAT_rename_r(&__REENT, oldName, newName) );
+}
+
+int fat_setHidden(const char *name, unsigned char hide){
+ return(_FAT_hideAttrib_r (&__REENT, name, hide));
+}
+
+#define S_ISHID(st_mode) ((st_mode & S_IHIDDEN) != 0)
+int fat_isHidden(struct stat *st){
+ return S_ISHID(st->st_mode);
+}
+
+int fat_getShortName(const char *fullName, char *outName){
+ return(_FAT_getshortname_r (&__REENT, fullName, outName));
+}
+
+
+void fat_rewind(FILE_STRUCT *fp)
+{
+ _FAT_seek_r (&__REENT, fp->fd, 0, SEEK_SET);
+}
+
+int fat_fstat(int fildes, struct stat *buf)
+{
+ return( _FAT_fstat_r (&__REENT, fildes, buf) );
+}
+
+//int fat_fprintf(FILE_STRUCT *fp, const char *format, ...)
+int fat_fprintf(void* fp, const char *format, ...)
+{
+ int ret;
+ va_list ap;
+ char buf[MAX_STDIO_BUF_SIZE];
+
+ if(NULL == fp)
+ {
+ __REENT._errno = EINVAL;
+ return -1;
+ }
+
+ //FIXME: stderr, stdout
+ if((void*)stderr == fp) return 0;
+ if((void*)stdout == fp) return 0;
+
+ memset(buf, 0, MAX_STDIO_BUF_SIZE);
+
+ va_start (ap, format);
+ ret = vsnprintf (buf, MAX_STDIO_BUF_SIZE, format, ap);
+ va_end (ap);
+
+ //if output string too long, it will not put out to file
+ if(ret >= MAX_STDIO_BUF_SIZE)
+ return -1;
+
+ return( _FAT_write_r(&__REENT, ((FILE_STRUCT*)fp)->fd, (const char*)buf, strlen(buf)) );
+}
+
+int fat_fscanf(FILE_STRUCT *fp, const char *format, ...)
+{
+ int ret;
+ va_list ap;
+ char buf[MAX_STDIO_BUF_SIZE];
+ char *pt;
+
+ if(NULL == fp)
+ {
+ __REENT._errno = EINVAL;
+ return -1;
+ }
+
+ pt = fat_fgets(buf, MAX_STDIO_BUF_SIZE, fp);
+ if(NULL == pt)
+ return -1;
+
+ va_start (ap, format);
+ ret = vsscanf (buf, format, ap);
+
+ return ret;
+}
+
+DIR_STATE_STRUCT* fat_opendir(const char *name)
+{
+ int i;
+
+ for(i = 0; i < MAX_OPEN_DIR; i++)
+ {
+ if(false == __DIRs[i].inUse) break;
+ }
+
+ if(i>= MAX_OPEN_DIR)
+ {
+ __REENT._errno = EMFILE; /* Too many open files */
+ return NULL;
+ }
+
+ return( _FAT_diropen_r(&__REENT, &__DIRs[i], name) );
+}
+
+DIR_ENTRY* fat_readdir(DIR_STATE_STRUCT *dirp)
+{
+ int isValid;
+
+ isValid = _FAT_dirnext_r (&__REENT, dirp, NULL);
+ if(0 != isValid)
+ return NULL;
+
+ return( &(dirp->currentEntry) );
+}
+
+long int fat_telldir(DIR_STATE_STRUCT *dirp)
+{
+ return( dirp->posEntry );
+}
+
+void fat_seekdir(DIR_STATE_STRUCT *dirp, long int loc)
+{
+ if (!dirp->inUse) {
+ __REENT._errno = EBADF;
+ return;
+ }
+
+ if(0 == loc)
+ {
+ dirp->posEntry = 0;
+ }
+ else if(loc > 0)
+ {
+ while(dirp->posEntry < loc)
+ {
+ dirp->validEntry = _FAT_directory_getNextEntry (dirp->partition, &(dirp->currentEntry));
+ dirp->posEntry += 1;
+
+ if(!dirp->validEntry) break;
+ }
+ }
+
+ return;
+}
+
+int fat_closedir(DIR_STATE_STRUCT *dirp)
+{
+ return(_FAT_dirclose_r (&__REENT, dirp));
+}
+
+int fat_chdir(const char *path)
+{
+ int ret;
+ char *pt;
+
+ ret = _FAT_chdir_r (&__REENT, path);
+ if(0 != ret) return -1;
+
+ pt = strchr(path, ':');
+ if(pt == NULL)
+ strcat(__PWD, path);
+ else
+ strcpy(__PWD, path);
+
+ pt = strchr(__PWD, '\0');
+ while(pt-- != __PWD)
+ {
+ if(pt[0] != DIR_SEPARATOR) break;
+ }
+
+ pt[1] = DIR_SEPARATOR;
+ pt[2] = '\0';
+
+ return 0;
+}
+
+char* fat_getcwd(char *buf, size_t size)
+{
+ int len;
+
+ len = strlen(__PWD);
+ if(len >= size)
+ {
+ __REENT._errno = ERANGE;
+ return NULL;
+ }
+
+ strcpy(buf, __PWD);
+ return buf;
+}
+
+int fat_mkdir(const char *path, mode_t mode)
+{
+ return( _FAT_mkdir_r(&__REENT, path, mode) );
+}
+
+int fat_rmdir(const char *path)
+{
+ return( _FAT_unlink_r(&__REENT, path) );
+}
+
+int fat_lstat(const char *path, struct stat *buf)
+{
+ return( _FAT_stat_r (&__REENT, path, buf) );
+}
+
+DIR_ENTRY* fat_readdir_ex(DIR_STATE_STRUCT *dirp, struct stat *statbuf)
+{
+ int isValid;
+
+ isValid = _FAT_dirnext_r (&__REENT, dirp, statbuf);
+ if(0 != isValid)
+ return NULL;
+
+ return( &(dirp->currentEntry) );
+}
+
diff --git a/sdk-modifications/libsrc/fs/fs_api.h b/sdk-modifications/libsrc/fs/fs_api.h
new file mode 100644
index 0000000..fa4ae07
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fs_api.h
@@ -0,0 +1,130 @@
+#ifndef __FS_API_H__
+#define __FS_API_H__
+//v1.0
+
+#include "sys/stat.h"
+#include "fatfile.h"
+#include "fatdir.h"
+
+extern struct _reent __REENT;
+typedef unsigned int mode_t;
+
+extern int fat_init(void);
+
+extern FILE_STRUCT* fat_fopen(const char *file, const char *mode);
+
+extern size_t fat_fread(void *buf, size_t size, size_t count, FILE_STRUCT *fp);
+
+extern size_t fat_fwrite(const void *buf, size_t size, size_t count, FILE_STRUCT *fp);
+
+extern int fat_fclose(FILE_STRUCT *fp);
+
+extern int fat_fseek(FILE_STRUCT *fp, long offset, int whence);
+
+extern long fat_ftell(FILE_STRUCT *fp);
+
+extern int fat_feof(FILE_STRUCT *fp);
+
+extern int fat_ferror(FILE_STRUCT *fp);
+
+extern void fat_clearerr(FILE_STRUCT *fp);
+
+extern int fat_fflush(FILE_STRUCT *fp);
+
+extern int fat_fgetc(FILE_STRUCT *fp);
+
+extern char* fat_fgets(char *buf, int n, FILE_STRUCT *fp);
+
+extern int fat_fputc(int ch, FILE_STRUCT *fp);
+
+extern int fat_fputs(const char *s, FILE_STRUCT *fp);
+
+extern int fat_remove(const char *filename);
+
+extern int fat_rename(const char *oldName, const char *newName);
+
+extern int fat_setHidden(const char *name, unsigned char hide);
+
+extern int fat_isHidden(struct stat *st);
+
+extern int fat_getShortName(const char *fullName, char *outName);
+
+extern void fat_rewind(FILE_STRUCT *fp);
+
+extern int fat_fstat(int fildes, struct stat *buf);
+
+extern int fat_fprintf(void* fp, const char *format, ...);
+
+extern int fat_fscanf(FILE_STRUCT *fp, const char *format, ...);
+
+extern DIR_STATE_STRUCT* fat_opendir(const char *name);
+
+extern DIR_ENTRY* fat_readdir(DIR_STATE_STRUCT *dirp);
+
+extern long fat_telldir(DIR_STATE_STRUCT *dirp);
+
+extern void fat_seekdir(DIR_STATE_STRUCT *dirp, long int loc);
+
+extern int fat_closedir(DIR_STATE_STRUCT *dirp);
+
+extern int fat_chdir(const char *path);
+
+extern char* fat_getcwd(char *buf, size_t size);
+
+extern int fat_mkdir(const char *path, mode_t mode);
+
+extern int fat_rmdir(const char *path);
+
+extern int fat_lstat(const char *path, struct stat *buf);
+
+extern DIR_ENTRY* fat_readdir_ex(DIR_STATE_STRUCT *dirp, struct stat *statbuf);
+
+//#define S_ISDIR(st) (st.st_mode & S_IFDIR)
+
+#define FILE FILE_STRUCT
+#define fopen fat_fopen
+#define fread fat_fread
+#define fwrite fat_fwrite
+#define fclose fat_fclose
+#define fseek fat_fseek
+#define ftell fat_ftell
+#define feof fat_feof
+#define ferror fat_ferror
+#define fclearerr fat_clearerr
+#define fflush fat_fflush
+#define fgetc fat_fgetc
+#define fgets fat_fgets
+#define fputc fat_fputc
+#define fputs fat_fputs
+#define fprintf fat_fprintf
+#define fscanf fat_fscanf
+#define remove fat_remove
+#define fhidden fat_isHidden
+
+#define DIR DIR_STATE_STRUCT
+#define dirent DIR_ENTRY
+#define opendir fat_opendir
+#define readdir fat_readdir
+#define telldir fat_telldir
+#define seekdir fat_seekdir
+#define closedir fat_closedir
+#define chdir fat_chdir
+#define getcwd fat_getcwd
+#define mkdir fat_mkdir
+#define rmdir fat_rmdir
+
+#define lstat fat_lstat
+#define fstat fat_fstat
+
+#define S_ISHID(st_mode) ((st_mode & S_IHIDDEN) != 0)
+
+//the extended version of readdir_ex
+#define readdir_ex fat_readdir_ex
+
+#define MAX_PATH 512
+#define MAX_FILE 512
+
+//Misc function
+extern bool fat_getDiskSpaceInfo( char * diskName, unsigned int *total, unsigned int *used, unsigned int *freeSpace );
+
+#endif //__FS_API_H__
diff --git a/sdk-modifications/libsrc/fs/fs_cache.h b/sdk-modifications/libsrc/fs/fs_cache.h
new file mode 100644
index 0000000..1abae66
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fs_cache.h
@@ -0,0 +1,125 @@
+/*
+ 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.
+
+ 2006-07-11 - Chishm
+ * Original release
+*/
+
+#ifndef _CACHE_H
+#define _CACHE_H
+
+#include "fs_common.h"
+#include "disc_io/disc_io.h"
+
+#define CACHE_PAGE_SIZE BYTES_PER_READ
+
+typedef struct {
+ u32 sector;
+ u32 count;
+ bool dirty;
+} CACHE_ENTRY;
+
+typedef struct {
+ const IO_INTERFACE* disc;
+ u32 numberOfPages;
+ CACHE_ENTRY* cacheEntries;
+ u8* pages;
+} 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, u32 sector, u32 offset, u32 size);
+
+/*
+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 reading from
+size is the amount of data to read
+Precondition: offset + size <= BYTES_PER_READ
+*/
+bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, u32 sector, u32 offset, u32 size);
+
+/*
+some where call _FAT_cache_writePartialSector to cache sector m , but later, another
+place(in fwrite function) directly write data to sector m, in this case, need to
+cancel the dirty state of sector m
+*/
+void _FAT_cache_writePartialSector_check (CACHE* cache, u32 sector, u32 num, const void* buffer);
+
+/*
+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 reading from
+size is the amount of data to read
+Precondition: offset + size <= BYTES_PER_READ
+*/
+bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, u32 sector, u32 offset, u32 size);
+
+/*
+Read a full sector from the cache
+*/
+static inline bool _FAT_cache_readSector (CACHE* cache, void* buffer, u32 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, u32 sector) {
+ return _FAT_cache_writePartialSector (cache, buffer, sector, 0, BYTES_PER_READ);
+}
+
+/*
+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 (u32 numberOfPages, const IO_INTERFACE* discInterface);
+
+void _FAT_cache_destructor (CACHE* cache);
+
+#endif // _CACHE_H
diff --git a/sdk-modifications/libsrc/fs/fs_common.h b/sdk-modifications/libsrc/fs/fs_common.h
new file mode 100644
index 0000000..ce1cfff
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fs_common.h
@@ -0,0 +1,129 @@
+/*
+ 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.
+
+ 2006-07-11 - Chishm
+ * Original release
+*/
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+// When compiling for NDS, make sure NDS is defined
+#ifndef NDS
+ #if defined ARM9 || defined ARM7
+ #define NDS
+ #endif
+#endif
+
+#if 0
+#ifdef NDS
+ #include <nds/jtypes.h>
+#else
+ #include "gba_types.h"
+#endif
+#endif
+
+#define BYTES_PER_READ 512
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+#ifndef bool
+#define bool int
+#endif
+
+#ifndef false
+#define false 0
+#endif
+
+#ifndef true
+#define true 1
+#endif
+
+#ifndef u8
+#define u8 unsigned char
+#endif
+
+#ifndef u16
+#define u16 unsigned short
+#endif
+
+#ifndef u32
+#define u32 unsigned long
+#endif
+
+#ifndef s32
+#define s32 long
+#endif
+
+struct _reent
+{
+ /* FILE is a big struct and may change over time. To try to achieve binary
+ compatibility with future versions, put stdin,stdout,stderr here.
+ These are pointers into member __sf defined below. */
+// __FILE *_stdin, *_stdout, *_stderr; /* XXX */
+
+ int _errno; /* local copy of errno */
+
+// int _inc; /* used by tmpnam */
+
+// char *_emergency;
+
+// int __sdidinit; /* 1 means stdio has been init'd */
+
+// int _current_category; /* unused */
+// _CONST char *_current_locale; /* unused */
+
+// struct _mprec *_mp;
+
+// void _EXFNPTR(__cleanup, (struct _reent *));
+
+// int _gamma_signgam;
+
+ /* used by some fp conversion routines */
+// int _cvtlen; /* should be size_t */
+// char *_cvtbuf;
+
+// struct _rand48 *_r48;
+// struct __tm *_localtime_buf;
+// char *_asctime_buf;
+
+ /* signal info */
+// void (**(_sig_func))(int);
+
+ /* atexit stuff */
+// struct _atexit *_atexit;
+// struct _atexit _atexit0;
+
+// struct _glue __sglue; /* root of glue chain */
+// __FILE *__sf; /* file descriptors */
+// struct _misc_reent *_misc; /* strtok, multibyte states */
+// char *_signal_buf; /* strsignal */
+};
+
+#endif // _COMMON_H
diff --git a/sdk-modifications/libsrc/fs/fs_unicode.c b/sdk-modifications/libsrc/fs/fs_unicode.c
new file mode 100644
index 0000000..2f1c1ed
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fs_unicode.c
@@ -0,0 +1,173 @@
+//fs_unicode.c
+
+#include <string.h>
+#include "fs_common.h"
+
+//void _FAT_unicode_init_default() // ANSI CODE PAGE
+//{
+// _L2UTable = NULL;
+// _U2LTable = NULL;
+// _ankTable = NULL;
+//}
+
+static inline const char* _FAT_utf8decode(const char* utf8, u16 *ucs)
+{
+ unsigned char c = *utf8++;
+ unsigned long code;
+ int tail = 0;
+
+ if ((c <= 0x7f) || (c >= 0xc2)) {
+ /* Start of new character. */
+ if (c < 0x80) { /* U-00000000 - U-0000007F, 1 byte */
+ code = c;
+ } else if (c < 0xe0) { /* U-00000080 - U-000007FF, 2 bytes */
+ tail = 1;
+ code = c & 0x1f;
+ } else if (c < 0xf0) { /* U-00000800 - U-0000FFFF, 3 bytes */
+ tail = 2;
+ code = c & 0x0f;
+ } else if (c < 0xf5) { /* U-00010000 - U-001FFFFF, 4 bytes */
+ tail = 3;
+ code = c & 0x07;
+ } else {
+ /* Invalid size. */
+ code = 0;
+ }
+
+ while (tail-- && ((c = *utf8++) != 0)) {
+ if ((c & 0xc0) == 0x80) {
+ /* Valid continuation character. */
+ code = (code << 6) | (c & 0x3f);
+
+ } else {
+ /* Invalid continuation char */
+ code = 0xfffd;
+ utf8--;
+ break;
+ }
+ }
+ } else {
+ /* Invalid UTF-8 char */
+ code = 0;
+ }
+ /* currently we don't support chars above U-FFFF */
+ *ucs = (code < 0x10000) ? code : 0;
+ return utf8;
+}
+
+void _FAT_utf8_to_unicode16( const char* src, unsigned short* dest )
+{
+ while('\0' != *src)
+ {
+ src = _FAT_utf8decode(src, dest++);
+ }
+
+ *dest = '\0';
+}
+
+#if 0
+static inline int _FAT_utf8coding(const u16* ucs, char* dest)
+{
+ int len= 0;
+
+ if (*ucs < 0x80) //one byte
+ {
+ dest[len++] = *ucs;
+ }
+ else if (*ucs < 0x800) //two bytes
+ {
+ dest[len++] = 0xC0 | (*ucs >> 6 & 0x1F);
+ dest[len++] = 0x80 | (*ucs & 0x3F);
+ }
+ else //if(*ucs < 0x10000) //three bytes
+ {
+ dest[len++] = 0xE0 | (*ucs >> 12);
+ dest[len++] = 0x80 | (*ucs >>6 & 0x3F);
+ dest[len++] = 0x80 | (*ucs &0x3F);
+ }
+
+ return len;
+}
+#endif
+
+void _FAT_unicode16_to_utf8( const u16* src, char* dest)
+{
+ int len=0;
+ while(*src)
+ {
+ if (*src < 0x80) //1 byte
+ {
+ dest[len++] = *src;
+ }
+ else if (*src < 0x800) //2 bytes
+ {
+ dest[len++] = 0xC0 | (*src >> 6 & 0x1F);
+ dest[len++] = 0x80 | (*src & 0x3F);
+ }
+ else //if(*src < 0x10000) //3 bytes
+ {
+ dest[len++] = 0xE0 | (*src >> 12);
+ dest[len++] = 0x80 | (*src >>6 & 0x3F);
+ dest[len++] = 0x80 | (*src &0x3F);
+ }
+ src ++;
+ }
+ dest[len] = 0;
+}
+
+u32 _unistrnlen( const u16* unistr, u32 maxlen )
+{
+ const u16 * pstr = NULL;
+ u32 len = 0;
+ if( NULL == unistr )
+ return 0;
+
+ if( 0 == maxlen )
+ return 0;
+
+ pstr = unistr;
+
+ while( len < maxlen && *pstr != 0x0000 )
+ {
+ ++len;
+ ++pstr;
+ }
+ return len;
+}
+
+int _unistrncmp( const u16* src, const u16* dest, u32 maxlen )
+{
+ if( NULL == src || NULL == dest )
+ {
+ if( src == dest ) return 0;
+ return (NULL == src ) ? -1 : 1;
+ }
+
+ while( *src == *dest && maxlen && *src != 0x0000 && *dest != 0x0000 )
+ {
+ ++src;
+ ++dest;
+ --maxlen;
+ }
+ if( 0 == maxlen ) return 0;
+
+ return *src > *dest ? 1 : -1;
+}
+
+const u16 * _unistrchr( const u16 * str, u16 unichar )
+{
+ if( NULL == str )
+ return NULL;
+
+ while( *str != unichar && *str != 0x0000 )
+ {
+ ++str;
+ }
+ return str;
+}
+
+int _uniisalnum( u8 ch )
+{
+ return isalnum( ch );
+}
+
diff --git a/sdk-modifications/libsrc/fs/fs_unicode.h b/sdk-modifications/libsrc/fs/fs_unicode.h
new file mode 100644
index 0000000..45e9d0a
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/fs_unicode.h
@@ -0,0 +1,16 @@
+#ifndef __FS_UNICODE_H__
+#define __FS_UNICODE_H__
+
+extern void _FAT_utf8_to_unicode16( const char* src, u16* dest );
+
+extern void _FAT_unicode16_to_utf8( const u16* src, char* dest);
+
+extern u32 _unistrnlen( const u16* unistr, u32 maxlen );
+
+extern int _unistrncmp( const u16 * src, const u16 * dest, u32 maxlen );
+
+extern const u16 * _unistrchr( const u16 * str, u16 unichar );
+
+int _uniisalnum( u8 ch );
+
+#endif //__FS_UNICODE_H__
diff --git a/sdk-modifications/libsrc/fs/libfat.c b/sdk-modifications/libsrc/fs/libfat.c
new file mode 100644
index 0000000..1956d13
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/libfat.c
@@ -0,0 +1,174 @@
+/*
+ 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.
+
+ 2006-07-11 - Chishm
+ * Original release
+
+ 2006-08-13 - Chishm
+ * Moved all externally visible directory related functions to fatdir
+
+ 2006-08-14 - Chishm
+ * Added extended devoptab_t functions
+
+ 2007-01-10 - Chishm
+ * fatInit now sets the correct path when setAsDefaultDevice
+
+ 2007-01-11 - Chishm
+ * Added missing #include <unistd.h>
+*/
+
+/*
+
+*/
+
+#include <unistd.h>
+
+#include "fs_common.h"
+#include "partition.h"
+#include "fatfile.h"
+#include "fatdir.h"
+#include "fs_unicode.h"
+#include "io_ds2_mmcf.h"
+
+#define GBA_DEFAULT_CACHE_PAGES 2
+#define NDS_DEFAULT_CACHE_PAGES 8
+
+#if 0
+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
+};
+#endif
+
+#if 0
+bool fatInit (u32 cacheSize, bool setAsDefaultDevice)
+{
+#ifdef NDS
+ bool slot1Device, slot2Device;
+
+ // add unicode suppport
+ //file name string coding are set to UTF8, other coding needed conveted to UTF8 by the caller
+ //_FAT_unicode_init_default();
+
+ // Try mounting both slots
+ slot1Device = _FAT_partition_mount (PI_SLOT_1, cacheSize);
+ slot2Device = _FAT_partition_mount (PI_SLOT_2, cacheSize);
+
+ // Choose the default device
+ if (slot1Device) {
+ _FAT_partition_setDefaultInterface (PI_SLOT_1);
+ } else if (slot2Device) {
+ _FAT_partition_setDefaultInterface (PI_SLOT_2);
+ } else {
+ return false;
+ }
+
+#else // not defined NDS
+ bool cartSlotDevice;
+
+ cartSlotDevice = _FAT_partition_mount (PI_CART_SLOT, cacheSize);
+
+ if (cartSlotDevice) {
+ _FAT_partition_setDefaultInterface (PI_CART_SLOT);
+ } else {
+ return false;
+ }
+
+#endif // defined NDS
+
+ AddDevice (&dotab_fat);
+
+ if (setAsDefaultDevice) {
+ chdir ("fat:/");
+ }
+
+ return true;
+}
+#endif
+
+#if 0
+bool fatInitDefault (void) {
+#ifdef NDS
+ return fatInit (NDS_DEFAULT_CACHE_PAGES, true);
+#else
+ return fatInit (GBA_DEFAULT_CACHE_PAGES, true);
+#endif
+}
+#endif
+
+bool _FAT_Init(void)
+{
+ // Try mounting sd/mmc
+ bool sdOK = false;
+ if( _io_ds2_mmcf.fn_startup() == 0) //NO ERROR
+ sdOK = _FAT_partition_freeMount( PI_DEFAULT, &_io_ds2_mmcf, 8);
+ else
+ return false;
+
+ if(sdOK == true)
+ fat_chdir ("fat:/");
+
+ return sdOK;
+}
+
+bool fatMountNormalInterface (PARTITION_INTERFACE partitionNumber, u32 cacheSize) {
+ return _FAT_partition_mount (partitionNumber, cacheSize);
+}
+
+bool fatMountCustomInterface (const IO_INTERFACE* device, u32 cacheSize) {
+ return _FAT_partition_mountCustomInterface (device, cacheSize);
+}
+
+bool fatUnmount (PARTITION_INTERFACE partitionNumber) {
+ return _FAT_partition_unmount (partitionNumber);
+}
+
+
+bool fatUnsafeUnmount (PARTITION_INTERFACE partitionNumber) {
+ return _FAT_partition_unsafeUnmount (partitionNumber);
+}
+
+bool fatSetDefaultInterface (PARTITION_INTERFACE partitionNumber) {
+ return _FAT_partition_setDefaultInterface (partitionNumber);
+}
diff --git a/sdk-modifications/libsrc/fs/mem_allocate.h b/sdk-modifications/libsrc/fs/mem_allocate.h
new file mode 100644
index 0000000..7bbe8de
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/mem_allocate.h
@@ -0,0 +1,47 @@
+/*
+ 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.
+
+ 2006-07-11 - Chishm
+ * Original release
+*/
+
+#ifndef _MEM_ALLOCATE_H
+#define _MEM_ALLOCATE_H
+
+#include "ds2_malloc.h"
+
+static inline void* _FAT_mem_allocate (size_t size) {
+ return ((void*)malloc (size));
+}
+
+static inline void _FAT_mem_free (void* mem) {
+ return (free ((void*)mem));
+}
+
+#endif // _MEM_ALLOCATE_H
diff --git a/sdk-modifications/libsrc/fs/partition.c b/sdk-modifications/libsrc/fs/partition.c
new file mode 100644
index 0000000..1791b40
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/partition.c
@@ -0,0 +1,501 @@
+/*
+ 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.
+
+ 2006-07-11 - Chishm
+ * Original release
+
+ 2006-08-10 - Chishm
+ * Fixed problem when openning files starting with "fat"
+
+ 2006-10-28 - Chishm
+ * _partitions changed to _FAT_partitions to maintain the same style of naming as the functions
+*/
+//version 1.1
+//Note: fix bug in _FAT_partition_setDefaultPartition() function
+
+#include "partition.h"
+#include "bit_ops.h"
+#include "file_allocation_table.h"
+#include "directory.h"
+
+#include <string.h>
+#include <ctype.h>
+
+#include "mem_allocate.h"
+
+/*
+This device name, as known by DevKitPro
+*/
+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
+};
+
+
+#ifdef NDS
+#define MAXIMUM_PARTITIONS 4
+PARTITION* _FAT_partitions[MAXIMUM_PARTITIONS] = {NULL};
+#else // not defined NDS
+#define MAXIMUM_PARTITIONS 1
+PARTITION* _FAT_partitions[MAXIMUM_PARTITIONS] = {NULL};
+#endif // defined NDS
+
+// Use a single static buffer for the partitions
+
+
+static PARTITION* _FAT_partition_constructor ( const IO_INTERFACE* disc, u32 cacheSize) {
+ PARTITION* partition;
+ int i;
+ u32 bootSector;
+ u8 sectorBuffer[BYTES_PER_READ] = {0};
+
+ // Read first sector of disc
+ if ( !_FAT_disc_readSectors (disc, 0, 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;
+ }
+
+ // Check if there is a FAT string, which indicates this is a boot sector
+ if ((sectorBuffer[0x36] == 'F') && (sectorBuffer[0x37] == 'A') && (sectorBuffer[0x38] == 'T')) {
+ bootSector = 0;
+ } else if ((sectorBuffer[0x52] == 'F') && (sectorBuffer[0x53] == 'A') && (sectorBuffer[0x54] == 'T')) {
+ // Check for FAT32
+ bootSector = 0;
+ } else {
+ // This is an MBR
+ // Find first valid partition from MBR
+ // First check for an active partition
+ for (i=0x1BE; (i < 0x1FE) && (sectorBuffer[i] != 0x80); i+= 0x10);
+ // If it didn't find an active partition, search for any valid partition
+ if (i == 0x1FE) {
+ for (i=0x1BE; (i < 0x1FE) && (sectorBuffer[i+0x04] == 0x00); i+= 0x10);
+ }
+
+ // Go to first valid partition
+ if ( i != 0x1FE) {
+ // Make sure it found a partition
+ bootSector = u8array_to_u32(sectorBuffer, 0x8 + i);
+ } else {
+ bootSector = 0; // No partition found, assume this is a MBR free disk
+ }
+ }
+
+ // Read in boot sector
+ if ( !_FAT_disc_readSectors (disc, bootSector, 1, sectorBuffer)) {
+ return NULL;
+ }
+
+ partition = (PARTITION*) _FAT_mem_allocate (sizeof(PARTITION));
+ if (partition == NULL) {
+ return NULL;
+ }
+
+ // 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 = bootSector + 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 = (partition->numberOfSectors - partition->dataStart) * partition->bytesPerSector;
+
+ // Store info about FAT
+ partition->fat.lastCluster = (partition->numberOfSectors - partition->dataStart) / partition->sectorsPerCluster;
+ partition->fat.firstFree = CLUSTER_FIRST;
+
+ if (partition->fat.lastCluster < CLUSTERS_PER_FAT12) {
+ partition->filesysType = FS_FAT12; // FAT12 volume
+ } else if (partition->fat.lastCluster < 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, partition->disc);
+
+ // 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;
+
+ return partition;
+}
+
+static void _FAT_partition_destructor (PARTITION* partition) {
+ _FAT_cache_destructor (partition->cache);
+ _FAT_disc_shutdown (partition->disc);
+ _FAT_mem_free (partition);
+}
+
+bool _FAT_partition_mount (PARTITION_INTERFACE partitionNumber, u32 cacheSize)
+{
+#ifdef NDS
+ int i;
+ const IO_INTERFACE* disc = NULL;
+
+ if (_FAT_partitions[partitionNumber] != NULL) {
+ return false;
+ }
+
+ switch (partitionNumber) {
+ case PI_SLOT_1:
+ // Mount the disc in slot 1
+ disc = _FAT_disc_dsSlotFindInterface ();
+ break;
+ case PI_SLOT_2:
+ // Mount the disc in slot 2
+ disc = _FAT_disc_gbaSlotFindInterface ();
+ break;
+ case PI_DEFAULT:
+ case PI_CUSTOM:
+ default:
+ // Anything else has to be handled specially
+ return false;
+ break;
+ }
+
+ if (disc == NULL) {
+ return false;
+ }
+
+ // See if that disc is already in use, if so, then just copy the partition pointer
+ for (i = 0; i < MAXIMUM_PARTITIONS; i++) {
+ if ((_FAT_partitions[i] != NULL) && (_FAT_partitions[i]->disc == disc)) {
+ _FAT_partitions[partitionNumber] = _FAT_partitions[i];
+ return true;
+ }
+ }
+
+ _FAT_partitions[partitionNumber] = _FAT_partition_constructor (disc, cacheSize);
+
+ if (_FAT_partitions[partitionNumber] == NULL) {
+ return false;
+ }
+
+#else // not defined NDS
+ const IO_INTERFACE* disc = NULL;
+
+ if (_FAT_partitions[partitionNumber] != NULL) {
+ return false;
+ }
+
+ // Only ever one partition on GBA
+ disc = _FAT_disc_gbaSlotFindInterface ();
+ _FAT_partitions[partitionNumber] = _FAT_partition_constructor (disc, cacheSize);
+
+#endif // defined NDS
+
+ return true;
+}
+
+bool _FAT_partition_mountCustomInterface (const IO_INTERFACE* device, u32 cacheSize) {
+#ifdef NDS
+ int i;
+
+ if (_FAT_partitions[PI_CUSTOM] != NULL) {
+ return false;
+ }
+
+ if (device == NULL) {
+ return false;
+ }
+
+ // See if that disc is already in use, if so, then just copy the partition pointer
+ for (i = 0; i < MAXIMUM_PARTITIONS; i++) {
+ if ((_FAT_partitions[i] != NULL) && (_FAT_partitions[i]->disc == device)) {
+ _FAT_partitions[PI_CUSTOM] = _FAT_partitions[i];
+ return true;
+ }
+ }
+
+ _FAT_partitions[PI_CUSTOM] = _FAT_partition_constructor (device, cacheSize);
+
+ if (_FAT_partitions[PI_CUSTOM] == NULL) {
+ return false;
+ }
+
+#else // not defined NDS
+ if (_FAT_partitions[PI_CART_SLOT] != NULL) {
+ return false;
+ }
+
+ if (device == NULL) {
+ return false;
+ }
+
+ // Only ever one partition on GBA
+ _FAT_partitions[PI_CART_SLOT] = _FAT_partition_constructor (device, cacheSize);
+
+#endif // defined NDS
+
+ return true;
+}
+
+bool _FAT_partition_setDefaultInterface (PARTITION_INTERFACE partitionNumber)
+{
+#ifdef NDS // Can only set the default partition when there is more than 1, so doesn't apply to GBA
+ if ((partitionNumber < 1) || (partitionNumber >= MAXIMUM_PARTITIONS)) {
+ return false;
+ }
+
+ if (_FAT_partitions[partitionNumber] == NULL) {
+ return false;
+ }
+
+ _FAT_partitions[PI_DEFAULT] = _FAT_partitions[partitionNumber];
+#endif
+ return true;
+}
+
+bool _FAT_partition_setDefaultPartition (PARTITION* partition) {
+#ifdef NDS // Can only set the default partition when there is more than 1, so doesn't apply to GBA
+ int i;
+
+ if (partition == NULL) {
+ return false;
+ }
+
+ if(_FAT_partitions[PI_DEFAULT] == partition) //Already is the default partition
+ return true;
+
+ // Ensure that this device is already in the list
+ for (i = 1; i < MAXIMUM_PARTITIONS; i++) {
+ if (_FAT_partitions[i] == partition) {
+ break;
+ }
+ }
+
+ // It wasn't in the list, so fail
+ if (i == MAXIMUM_PARTITIONS) {
+ return false;
+ }
+
+ // Change the default partition / device to this one
+ _FAT_partitions[PI_DEFAULT] = partition;
+
+#endif
+ return true;
+}
+
+bool _FAT_partition_unmount (PARTITION_INTERFACE partitionNumber) {
+ int i;
+ PARTITION* partition = _FAT_partitions[partitionNumber];
+
+ if (partition == NULL) {
+ return false;
+ }
+
+ if (partition->openFileCount > 0) {
+ // There are still open files that need closing
+ return false;
+ }
+
+ // Remove all references to this partition
+ for (i = 0; i < MAXIMUM_PARTITIONS; i++) {
+ if (_FAT_partitions[i] == partition) {
+ _FAT_partitions[i] = NULL;
+ }
+ }
+
+ _FAT_partition_destructor (partition);
+ return true;
+}
+
+bool _FAT_partition_unsafeUnmount (PARTITION_INTERFACE partitionNumber) {
+ int i;
+ PARTITION* partition = _FAT_partitions[partitionNumber];
+
+ if (partition == NULL) {
+ return false;
+ }
+
+ // Remove all references to this partition
+ for (i = 0; i < MAXIMUM_PARTITIONS; i++) {
+ if (_FAT_partitions[i] == partition) {
+ _FAT_partitions[i] = NULL;
+ }
+ }
+
+ _FAT_cache_invalidate (partition->cache);
+ _FAT_partition_destructor (partition);
+ return true;
+}
+
+PARTITION* _FAT_partition_getPartitionFromPath (const char* path) {
+#ifdef NDS
+ int namelen;
+ int partitionNumber;
+
+ // Device name extraction code taken from DevKitPro
+ namelen = strlen(DEVICE_NAME);
+ if (strchr (path, ':') == NULL) {
+ // No device specified
+ partitionNumber = PI_DEFAULT;
+ } else if( strncmp(DEVICE_NAME, path, namelen) == 0 ) {
+ if ( path[namelen] == ':' ) {
+ // Only the device name is specified
+ partitionNumber = PI_DEFAULT;
+ } else if (isdigit(path[namelen]) && path[namelen+1] ==':' ) {
+ // Device name and number specified
+ partitionNumber = path[namelen] - '0';
+ } else {
+ // Incorrect device name
+ return NULL;
+ }
+ } else {
+ // Incorrect device name
+ return NULL;
+ }
+
+ if ((partitionNumber < 0) || (partitionNumber >= MAXIMUM_PARTITIONS)) {
+ return NULL;
+ }
+
+ return _FAT_partitions[partitionNumber];
+#else // not defined NDS
+ // Only one possible partition on GBA
+ return _FAT_partitions[PI_CART_SLOT];
+#endif // defined NDS
+}
+
+bool _FAT_partition_freeMount( int partitionNumber, const IO_INTERFACE* device, u32 cacheSize) {
+
+ if( partitionNumber < 0 || partitionNumber > 3 )
+ return false;
+ int i;
+
+ if (device == NULL) {
+ return false;
+ }
+
+ if (_FAT_partitions[partitionNumber] != NULL) {
+ if(_FAT_partitions[partitionNumber] -> disc == device)
+ return true;
+
+ return false;
+ }
+
+ // See if that disc is already in use, if so, then just copy the partition pointer
+ for (i = 0; i < MAXIMUM_PARTITIONS; i++) {
+ if ((_FAT_partitions[i] != NULL) && (_FAT_partitions[i]->disc == device)) {
+ _FAT_partitions[partitionNumber] = _FAT_partitions[i];
+ return true;
+ }
+ }
+
+ _FAT_partitions[partitionNumber] = _FAT_partition_constructor (device, cacheSize);
+
+ if (_FAT_partitions[partitionNumber] == NULL) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/sdk-modifications/libsrc/fs/partition.h b/sdk-modifications/libsrc/fs/partition.h
new file mode 100644
index 0000000..0777b54
--- /dev/null
+++ b/sdk-modifications/libsrc/fs/partition.h
@@ -0,0 +1,131 @@
+/*
+ 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.
+
+ 2006-07-11 - Chishm
+ * Original release
+*/
+
+#ifndef _PARTITION_H
+#define _PARTITION_H
+
+#include "fs_common.h"
+
+#include "disc_io/disc.h"
+#include "fs_cache.h"
+
+// Device name
+extern const char* DEVICE_NAME;
+
+// Filesystem type
+typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE;
+
+#ifdef NDS
+typedef enum {PI_DEFAULT, PI_SLOT_1, PI_SLOT_2, PI_CUSTOM} PARTITION_INTERFACE;
+#else
+typedef enum {PI_CART_SLOT} PARTITION_INTERFACE;
+#endif
+
+typedef struct {
+ u32 fatStart;
+ u32 sectorsPerFat;
+ u32 lastCluster;
+ u32 firstFree;
+} FAT;
+
+typedef struct {
+ const IO_INTERFACE* disc;
+ CACHE* cache;
+ // Info about the partition
+ bool readOnly; // If this is set, then do not try writing to the disc
+ FS_TYPE filesysType;
+ u32 totalSize;
+ u32 rootDirStart;
+ u32 rootDirCluster;
+ u32 numberOfSectors;
+ u32 dataStart;
+ u32 bytesPerSector;
+ u32 sectorsPerCluster;
+ u32 bytesPerCluster;
+ FAT fat;
+ // Values that may change after construction
+ u32 cwdCluster; // Current working directory cluser
+ u32 openFileCount;
+} PARTITION;
+
+/*
+Mount the device specified by partitionDevice
+PD_DEFAULT is not allowed, use _FAT_partition_setDefaultDevice
+PD_CUSTOM is not allowed, use _FAT_partition_mountCustomDevice
+*/
+bool _FAT_partition_mount (PARTITION_INTERFACE partitionNumber, u32 cacheSize);
+
+/*
+Mount a partition on a custom device
+*/
+bool _FAT_partition_mountCustomInterface (const IO_INTERFACE* device, u32 cacheSize);
+
+
+/*
+Free Mount a partition on a custom device
+*/
+bool _FAT_partition_freeMount( int partitionNumber, const IO_INTERFACE* device, u32 cacheSize);
+
+
+/*
+Unmount the partition specified by partitionNumber
+If there are open files, it will fail
+*/
+bool _FAT_partition_unmount (PARTITION_INTERFACE partitionNumber);
+
+/*
+Forcibly unmount the partition specified by partitionNumber
+Any open files on the partition will become invalid
+The cache will be invalidated, and any unflushed writes will be lost
+*/
+bool _FAT_partition_unsafeUnmount (PARTITION_INTERFACE partitionNumber);
+
+/*
+Set the default device for access by fat: and fat0:,
+based on the device number
+*/
+bool _FAT_partition_setDefaultInterface (PARTITION_INTERFACE partitionNumber);
+
+/*
+Set the default device for access by fat: and fat0:,
+based on the partition pointer
+*/
+bool _FAT_partition_setDefaultPartition (PARTITION* partition);
+
+/*
+Return the partition specified in a path
+For instance, "fat0:", "fat:", "/" and "fat:/" will all
+return the default partition
+*/
+PARTITION* _FAT_partition_getPartitionFromPath (const char* path);
+
+#endif // _PARTITION_H