diff options
author | Nebuleon Fumika | 2013-02-03 04:19:11 -0500 |
---|---|---|
committer | Nebuleon Fumika | 2013-02-03 04:19:11 -0500 |
commit | 8693ae1bd880a758eb2efec4fccd32f89593855d (patch) | |
tree | 660b7ceed1789fc904332b96a2a4006b0e4cc6b8 /sdk-modifications/libsrc/fs | |
parent | 02f8184fe07d99cceb85f4abd3ef0e5e3765b5ea (diff) | |
download | snesemu-8693ae1bd880a758eb2efec4fccd32f89593855d.tar.gz snesemu-8693ae1bd880a758eb2efec4fccd32f89593855d.tar.bz2 snesemu-8693ae1bd880a758eb2efec4fccd32f89593855d.zip |
Add SDK modifications by BassAceGold as of 2011-04-14, as well as modified DMA functions as of 2013-01-29.
Diffstat (limited to 'sdk-modifications/libsrc/fs')
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 |