From 8693ae1bd880a758eb2efec4fccd32f89593855d Mon Sep 17 00:00:00 2001 From: Nebuleon Fumika Date: Sun, 3 Feb 2013 04:19:11 -0500 Subject: Add SDK modifications by BassAceGold as of 2011-04-14, as well as modified DMA functions as of 2013-01-29. --- sdk-modifications/libsrc/fs/bit_ops.h | 58 ++ sdk-modifications/libsrc/fs/cache.c | 273 ++++++ sdk-modifications/libsrc/fs/directory.c | 1005 ++++++++++++++++++++ sdk-modifications/libsrc/fs/directory.h | 180 ++++ sdk-modifications/libsrc/fs/disc_io/disc.c | 194 ++++ sdk-modifications/libsrc/fs/disc_io/disc.h | 128 +++ sdk-modifications/libsrc/fs/disc_io/disc_io.h | 83 ++ sdk-modifications/libsrc/fs/disc_io/io_ds2_mmcf.c | 13 + sdk-modifications/libsrc/fs/disc_io/io_ds2_mmcf.h | 63 ++ sdk-modifications/libsrc/fs/ds2_fcntl.c | 126 +++ sdk-modifications/libsrc/fs/ds2_fcntl.h | 51 + sdk-modifications/libsrc/fs/ds2_unistd.c | 18 + sdk-modifications/libsrc/fs/ds2_unistd.h | 22 + sdk-modifications/libsrc/fs/fat.h | 118 +++ sdk-modifications/libsrc/fs/fat_misc.c | 138 +++ sdk-modifications/libsrc/fs/fat_misc.h | 38 + sdk-modifications/libsrc/fs/fatdir.c | 672 +++++++++++++ sdk-modifications/libsrc/fs/fatdir.h | 85 ++ sdk-modifications/libsrc/fs/fatdir_ex.c | 183 ++++ sdk-modifications/libsrc/fs/fatdir_ex.h | 18 + sdk-modifications/libsrc/fs/fatfile.c | 889 +++++++++++++++++ sdk-modifications/libsrc/fs/fatfile.h | 89 ++ sdk-modifications/libsrc/fs/fatfile_ex.c | 47 + sdk-modifications/libsrc/fs/fatfile_ex.h | 19 + .../libsrc/fs/file_allocation_table.c | 330 +++++++ .../libsrc/fs/file_allocation_table.h | 64 ++ sdk-modifications/libsrc/fs/filetime.c | 146 +++ sdk-modifications/libsrc/fs/filetime.h | 44 + sdk-modifications/libsrc/fs/fs.mk | 24 + sdk-modifications/libsrc/fs/fs_api.c | 452 +++++++++ sdk-modifications/libsrc/fs/fs_api.h | 130 +++ sdk-modifications/libsrc/fs/fs_cache.h | 125 +++ sdk-modifications/libsrc/fs/fs_common.h | 129 +++ sdk-modifications/libsrc/fs/fs_unicode.c | 173 ++++ sdk-modifications/libsrc/fs/fs_unicode.h | 16 + sdk-modifications/libsrc/fs/libfat.c | 174 ++++ sdk-modifications/libsrc/fs/mem_allocate.h | 47 + sdk-modifications/libsrc/fs/partition.c | 501 ++++++++++ sdk-modifications/libsrc/fs/partition.h | 131 +++ 39 files changed, 6996 insertions(+) create mode 100644 sdk-modifications/libsrc/fs/bit_ops.h create mode 100644 sdk-modifications/libsrc/fs/cache.c create mode 100644 sdk-modifications/libsrc/fs/directory.c create mode 100644 sdk-modifications/libsrc/fs/directory.h create mode 100644 sdk-modifications/libsrc/fs/disc_io/disc.c create mode 100644 sdk-modifications/libsrc/fs/disc_io/disc.h create mode 100644 sdk-modifications/libsrc/fs/disc_io/disc_io.h create mode 100644 sdk-modifications/libsrc/fs/disc_io/io_ds2_mmcf.c create mode 100644 sdk-modifications/libsrc/fs/disc_io/io_ds2_mmcf.h create mode 100644 sdk-modifications/libsrc/fs/ds2_fcntl.c create mode 100644 sdk-modifications/libsrc/fs/ds2_fcntl.h create mode 100644 sdk-modifications/libsrc/fs/ds2_unistd.c create mode 100644 sdk-modifications/libsrc/fs/ds2_unistd.h create mode 100644 sdk-modifications/libsrc/fs/fat.h create mode 100644 sdk-modifications/libsrc/fs/fat_misc.c create mode 100644 sdk-modifications/libsrc/fs/fat_misc.h create mode 100644 sdk-modifications/libsrc/fs/fatdir.c create mode 100644 sdk-modifications/libsrc/fs/fatdir.h create mode 100644 sdk-modifications/libsrc/fs/fatdir_ex.c create mode 100644 sdk-modifications/libsrc/fs/fatdir_ex.h create mode 100644 sdk-modifications/libsrc/fs/fatfile.c create mode 100644 sdk-modifications/libsrc/fs/fatfile.h create mode 100644 sdk-modifications/libsrc/fs/fatfile_ex.c create mode 100644 sdk-modifications/libsrc/fs/fatfile_ex.h create mode 100644 sdk-modifications/libsrc/fs/file_allocation_table.c create mode 100644 sdk-modifications/libsrc/fs/file_allocation_table.h create mode 100644 sdk-modifications/libsrc/fs/filetime.c create mode 100644 sdk-modifications/libsrc/fs/filetime.h create mode 100644 sdk-modifications/libsrc/fs/fs.mk create mode 100644 sdk-modifications/libsrc/fs/fs_api.c create mode 100644 sdk-modifications/libsrc/fs/fs_api.h create mode 100644 sdk-modifications/libsrc/fs/fs_cache.h create mode 100644 sdk-modifications/libsrc/fs/fs_common.h create mode 100644 sdk-modifications/libsrc/fs/fs_unicode.c create mode 100644 sdk-modifications/libsrc/fs/fs_unicode.h create mode 100644 sdk-modifications/libsrc/fs/libfat.c create mode 100644 sdk-modifications/libsrc/fs/mem_allocate.h create mode 100644 sdk-modifications/libsrc/fs/partition.c create mode 100644 sdk-modifications/libsrc/fs/partition.h (limited to 'sdk-modifications/libsrc/fs') 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 + +#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 +#include + +#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 + +#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 +#include +#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 +#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 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 +#include +#include +#include +#include + +#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 +#include +//#include +#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 + +#include + +#include + +#include + +#include + + +#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 +#include +#include +#include +#include + +#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 +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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 +#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 + +/* +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 +#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 + +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 +#include +#include +#include +#include +#include +#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 +#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 +#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 +*/ + +/* + +*/ + +#include + +#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 +#include + +#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 -- cgit v1.2.3