Răsfoiți Sursa

Common: Add a helper function to generate a 8.3 filename from a long one.
Core: Fix the SDMC Directory implementation to make blargSnes work.

Emmanuel Gil Peyrot 11 ani în urmă
părinte
comite
fbd72fd6bf

+ 42 - 0
src/common/file_util.cpp

@@ -780,6 +780,48 @@ size_t ReadFileToString(bool text_file, const char *filename, std::string &str)
     return file.ReadArray(&str[0], str.size());
 }
 
+/**
+ * Splits the filename into 8.3 format
+ * Loosely implemented following https://en.wikipedia.org/wiki/8.3_filename
+ * @param filename The normal filename to use
+ * @param short_name A 9-char array in which the short name will be written
+ * @param extension A 4-char array in which the extension will be written
+ */
+void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
+                     std::array<char, 4>& extension) {
+    const std::string forbidden_characters = ".\"/\\[]:;=, ";
+
+    // On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces.
+    short_name = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'};
+    extension = {' ', ' ', ' ', '\0'};
+
+    std::string::size_type point = filename.rfind('.');
+    if (point == filename.size() - 1)
+        point = filename.rfind('.', point);
+
+    // Get short name.
+    int j = 0;
+    for (char letter : filename.substr(0, point)) {
+        if (forbidden_characters.find(letter, 0) != std::string::npos)
+            continue;
+        if (j == 8) {
+            // TODO(Link Mauve): also do that for filenames containing a space.
+            // TODO(Link Mauve): handle multiple files having the same short name.
+            short_name[6] = '~';
+            short_name[7] = '1';
+            break;
+        }
+        short_name[j++] = toupper(letter);
+    }
+
+    // Get extension.
+    if (point != std::string::npos) {
+        j = 0;
+        for (char letter : filename.substr(point + 1, 3))
+            extension[j++] = toupper(letter);
+    }
+}
+
 IOFile::IOFile()
     : m_file(NULL), m_good(true)
 {}

+ 11 - 0
src/common/file_util.h

@@ -4,6 +4,7 @@
 
 #pragma once
 
+#include <array>
 #include <fstream>
 #include <cstdio>
 #include <cstring>
@@ -131,6 +132,16 @@ std::string &GetExeDirectory();
 size_t WriteStringToFile(bool text_file, const std::string &str, const char *filename);
 size_t ReadFileToString(bool text_file, const char *filename, std::string &str);
 
+/**
+ * Splits the filename into 8.3 format
+ * Loosely implemented following https://en.wikipedia.org/wiki/8.3_filename
+ * @param filename The normal filename to use
+ * @param short_name A 9-char array in which the short name will be written
+ * @param extension A 4-char array in which the extension will be written
+ */
+void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
+                     std::array<char, 4>& extension);
+
 // simple wrapper for cstdlib file functions to
 // hopefully will make error checking easier
 // and make forgetting an fclose() harder

+ 2 - 2
src/core/file_sys/directory.h

@@ -19,9 +19,9 @@ namespace FileSys {
 const size_t FILENAME_LENGTH = 0x20C / 2;
 struct Entry {
     char16_t filename[FILENAME_LENGTH]; // Entry name (UTF-16, null-terminated)
-    char short_name[9]; // 8.3 file name ('longfilename' -> 'LONGFI~1', null-terminated)
+    std::array<char, 9> short_name; // 8.3 file name ('longfilename' -> 'LONGFI~1', null-terminated)
     char unknown1; // unknown (observed values: 0x0A, 0x70, 0xFD)
-    char extension[4]; // 8.3 file extension (set to spaces for directories, null-terminated)
+    std::array<char, 4> extension; // 8.3 file extension (set to spaces for directories, null-terminated)
     char unknown2; // unknown (always 0x01)
     char unknown3; // unknown (0x00 or 0x08)
     char is_directory; // directory flag

+ 19 - 24
src/core/file_sys/directory_sdmc.cpp

@@ -20,8 +20,8 @@ Directory_SDMC::Directory_SDMC(const Archive_SDMC* archive, const std::string& p
     // the root directory we set while opening the archive.
     // For example, opening /../../usr/bin can give the emulated program your installed programs.
     std::string absolute_path = archive->GetMountPoint() + path;
-    entry_count = FileUtil::ScanDirectoryTree(absolute_path, entry);
-    current_entry = 0;
+    FileUtil::ScanDirectoryTree(absolute_path, directory);
+    children_iterator = directory.children.begin();
 }
 
 Directory_SDMC::~Directory_SDMC() {
@@ -35,44 +35,39 @@ Directory_SDMC::~Directory_SDMC() {
  * @return Number of entries listed
  */
 u32 Directory_SDMC::Read(const u32 count, Entry* entries) {
-    u32 i;
-    for (i = 0; i < count && current_entry < entry_count; ++i) {
-        FileUtil::FSTEntry file = entry.children[current_entry];
-        std::string filename = file.virtualName;
-        WARN_LOG(FILESYS, "File %s: size=%d dir=%d", filename.c_str(), file.size, file.isDirectory);
+    u32 entries_read = 0;
+
+    while (entries_read < count && children_iterator != directory.children.cend()) {
+        const FileUtil::FSTEntry& file = *children_iterator;
+        const std::string& filename = file.virtualName;
+        Entry& entry = entries[entries_read];
 
-        Entry* entry = &entries[i];
+        WARN_LOG(FILESYS, "File %s: size=%d dir=%d", filename.c_str(), file.size, file.isDirectory);
 
         // TODO(Link Mauve): use a proper conversion to UTF-16.
         for (int j = 0; j < FILENAME_LENGTH; ++j) {
-            entry->filename[j] = filename[j];
+            entry.filename[j] = filename[j];
             if (!filename[j])
                 break;
         }
 
-        // Split the filename into 8.3 format.
-        // TODO(Link Mauve): move that to common, I guess, and make it more robust to long filenames.
-        std::string::size_type n = filename.rfind('.');
-        if (n == std::string::npos) {
-            strncpy(entry->short_name, filename.c_str(), 8);
-            memset(entry->extension, '\0', 3);
-        } else {
-            strncpy(entry->short_name, filename.substr(0, n).c_str(), 8);
-            strncpy(entry->extension, filename.substr(n + 1).c_str(), 8);
-        }
+        FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
 
-        entry->is_directory = file.isDirectory;
-        entry->file_size = file.size;
+        entry.is_directory = file.isDirectory;
+        entry.is_hidden = (filename[0] == '.');
+        entry.is_read_only = 0;
+        entry.file_size = file.size;
 
         // We emulate a SD card where the archive bit has never been cleared, as it would be on
         // most user SD cards.
         // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a
         // file bit.
-        entry->is_archive = !file.isDirectory;
+        entry.is_archive = !file.isDirectory;
 
-        ++current_entry;
+        ++entries_read;
+        ++children_iterator;
     }
-    return i;
+    return entries_read;
 }
 
 /**

+ 6 - 3
src/core/file_sys/directory_sdmc.h

@@ -37,9 +37,12 @@ public:
     bool Close() const override;
 
 private:
-    u32 entry_count;
-    u32 current_entry;
-    FileUtil::FSTEntry entry;
+    u32 total_entries_in_directory;
+    FileUtil::FSTEntry directory;
+
+    // We need to remember the last entry we returned, so a subsequent call to Read will continue
+    // from the next one.  This iterator will always point to the next unread entry.
+    std::vector<FileUtil::FSTEntry>::iterator children_iterator;
 };
 
 } // namespace FileSys