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/directory.c | 1005 +++++++++++++++++++++++++++++++ 1 file changed, 1005 insertions(+) create mode 100644 sdk-modifications/libsrc/fs/directory.c (limited to 'sdk-modifications/libsrc/fs/directory.c') 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; +} -- cgit v1.2.3 From d1a7bf5eb558e7db4a1a27e15ebedb02e6b7f804 Mon Sep 17 00:00:00 2001 From: Nebuleon Fumika Date: Mon, 4 Feb 2013 23:45:44 -0500 Subject: Fully integrate BassAceGold's libraries, finally. The README still states that 1.2 is required to overwrite 0.13's stuff; really, 0.13 is needed only for `gcc`. So the sequence goes 0.13's `gcc` -> 1.2 -> BassAceGold's libraries -> make `libds2a.a`. DMA function names changed to match BassAceGold's. --- sdk-modifications/libsrc/fs/directory.c | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 sdk-modifications/libsrc/fs/directory.c (limited to 'sdk-modifications/libsrc/fs/directory.c') diff --git a/sdk-modifications/libsrc/fs/directory.c b/sdk-modifications/libsrc/fs/directory.c old mode 100644 new mode 100755 -- cgit v1.2.3