Parcourir la source

Merge pull request #123 from bunnei/fs

Initial implementation of RomFS filesystem and fsp-srv
bunnei il y a 8 ans
Parent
commit
ab8525705b

+ 12 - 12
src/core/CMakeLists.txt

@@ -6,21 +6,17 @@ add_library(core STATIC
     core.h
     core_timing.cpp
     core_timing.h
-    file_sys/archive_backend.cpp
-    file_sys/archive_backend.h
-    file_sys/directory_backend.h
-    file_sys/disk_archive.cpp
-    file_sys/disk_archive.h
+    file_sys/directory.h
     file_sys/errors.h
-    file_sys/file_backend.h
-    file_sys/ivfc_archive.cpp
-    file_sys/ivfc_archive.h
+    file_sys/filesystem.cpp
+    file_sys/filesystem.h
     file_sys/path_parser.cpp
     file_sys/path_parser.h
-    file_sys/savedata_archive.cpp
-    file_sys/savedata_archive.h
-    file_sys/title_metadata.cpp
-    file_sys/title_metadata.h
+    file_sys/romfs_factory.cpp
+    file_sys/romfs_factory.h
+    file_sys/romfs_filesystem.cpp
+    file_sys/romfs_filesystem.h
+    file_sys/storage.h
     frontend/emu_window.cpp
     frontend/emu_window.h
     frontend/framebuffer_layout.cpp
@@ -101,6 +97,10 @@ add_library(core STATIC
     hle/service/audio/audio.h
     hle/service/audio/audout_u.cpp
     hle/service/audio/audout_u.h
+    hle/service/filesystem/filesystem.cpp
+    hle/service/filesystem/filesystem.h
+    hle/service/filesystem/fsp_srv.cpp
+    hle/service/filesystem/fsp_srv.h
     hle/service/hid/hid.cpp
     hle/service/hid/hid.h
     hle/service/lm/lm.cpp

+ 1 - 1
src/core/file_sys/directory_backend.h → src/core/file_sys/directory.h

@@ -1,4 +1,4 @@
-// Copyright 2014 Citra Emulator Project
+// Copyright 2018 yuzu emulator team
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 

+ 0 - 99
src/core/file_sys/disk_archive.cpp

@@ -1,99 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <cstdio>
-#include <memory>
-#include "common/common_types.h"
-#include "common/file_util.h"
-#include "common/logging/log.h"
-#include "core/file_sys/disk_archive.h"
-#include "core/file_sys/errors.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const {
-    if (!mode.read_flag)
-        return ERROR_INVALID_OPEN_FLAGS;
-
-    file->Seek(offset, SEEK_SET);
-    return MakeResult<size_t>(file->ReadBytes(buffer, length));
-}
-
-ResultVal<size_t> DiskFile::Write(const u64 offset, const size_t length, const bool flush,
-                                  const u8* buffer) const {
-    if (!mode.write_flag)
-        return ERROR_INVALID_OPEN_FLAGS;
-
-    file->Seek(offset, SEEK_SET);
-    size_t written = file->WriteBytes(buffer, length);
-    if (flush)
-        file->Flush();
-    return MakeResult<size_t>(written);
-}
-
-u64 DiskFile::GetSize() const {
-    return file->GetSize();
-}
-
-bool DiskFile::SetSize(const u64 size) const {
-    file->Resize(size);
-    file->Flush();
-    return true;
-}
-
-bool DiskFile::Close() const {
-    return file->Close();
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-DiskDirectory::DiskDirectory(const std::string& path) : directory() {
-    unsigned size = FileUtil::ScanDirectoryTree(path, directory);
-    directory.size = size;
-    directory.isDirectory = true;
-    children_iterator = directory.children.begin();
-}
-
-u32 DiskDirectory::Read(const u32 count, Entry* entries) {
-    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];
-
-        LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size,
-                  file.isDirectory);
-
-        // TODO(Link Mauve): use a proper conversion to UTF-16.
-        for (size_t j = 0; j < FILENAME_LENGTH; ++j) {
-            entry.filename[j] = filename[j];
-            if (!filename[j])
-                break;
-        }
-
-        FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
-
-        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;
-
-        ++entries_read;
-        ++children_iterator;
-    }
-    return entries_read;
-}
-
-} // namespace FileSys

+ 0 - 68
src/core/file_sys/disk_archive.h

@@ -1,68 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <cstddef>
-#include <memory>
-#include <string>
-#include <vector>
-#include "common/common_types.h"
-#include "common/file_util.h"
-#include "core/file_sys/archive_backend.h"
-#include "core/file_sys/directory_backend.h"
-#include "core/file_sys/file_backend.h"
-#include "core/hle/result.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-class DiskFile : public FileBackend {
-public:
-    DiskFile(FileUtil::IOFile&& file_, const Mode& mode_)
-        : file(new FileUtil::IOFile(std::move(file_))) {
-        mode.hex = mode_.hex;
-    }
-
-    ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
-    ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
-    u64 GetSize() const override;
-    bool SetSize(u64 size) const override;
-    bool Close() const override;
-
-    void Flush() const override {
-        file->Flush();
-    }
-
-protected:
-    Mode mode;
-    std::unique_ptr<FileUtil::IOFile> file;
-};
-
-class DiskDirectory : public DirectoryBackend {
-public:
-    DiskDirectory(const std::string& path);
-
-    ~DiskDirectory() override {
-        Close();
-    }
-
-    u32 Read(const u32 count, Entry* entries) override;
-
-    bool Close() const override {
-        return true;
-    }
-
-protected:
-    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

+ 2 - 2
src/core/file_sys/archive_backend.cpp → src/core/file_sys/filesystem.cpp

@@ -1,4 +1,4 @@
-// Copyright 2015 Citra Emulator Project
+// Copyright 2018 yuzu emulator team
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
@@ -7,7 +7,7 @@
 #include <sstream>
 #include "common/logging/log.h"
 #include "common/string_util.h"
-#include "core/file_sys/archive_backend.h"
+#include "core/file_sys/filesystem.h"
 #include "core/memory.h"
 
 namespace FileSys {

+ 29 - 29
src/core/file_sys/archive_backend.h → src/core/file_sys/filesystem.h

@@ -1,4 +1,4 @@
-// Copyright 2014 Citra Emulator Project
+// Copyright 2018 yuzu emulator team
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
@@ -15,7 +15,7 @@
 
 namespace FileSys {
 
-class FileBackend;
+class StorageBackend;
 class DirectoryBackend;
 
 // Path string type
@@ -71,9 +71,9 @@ struct ArchiveFormatInfo {
 };
 static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD");
 
-class ArchiveBackend : NonCopyable {
+class FileSystemBackend : NonCopyable {
 public:
-    virtual ~ArchiveBackend() {}
+    virtual ~FileSystemBackend() {}
 
     /**
      * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
@@ -81,13 +81,12 @@ public:
     virtual std::string GetName() const = 0;
 
     /**
-     * Open a file specified by its path, using the specified mode
-     * @param path Path relative to the archive
-     * @param mode Mode to open the file with
-     * @return Opened file, or error code
+     * Create a file specified by its path
+     * @param path Path relative to the Archive
+     * @param size The size of the new file, filled with zeroes
+     * @return Result of the operation
      */
-    virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
-                                                             const Mode& mode) const = 0;
+    virtual ResultCode CreateFile(const Path& path, u64 size) const = 0;
 
     /**
      * Delete a file specified by its path
@@ -97,12 +96,11 @@ public:
     virtual ResultCode DeleteFile(const Path& path) const = 0;
 
     /**
-     * Rename a File specified by its path
-     * @param src_path Source path relative to the archive
-     * @param dest_path Destination path relative to the archive
+     * Create a directory specified by its path
+     * @param path Path relative to the archive
      * @return Result of the operation
      */
-    virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0;
+    virtual ResultCode CreateDirectory(const Path& path) const = 0;
 
     /**
      * Delete a directory specified by its path
@@ -119,19 +117,12 @@ public:
     virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0;
 
     /**
-     * Create a file specified by its path
-     * @param path Path relative to the Archive
-     * @param size The size of the new file, filled with zeroes
-     * @return Result of the operation
-     */
-    virtual ResultCode CreateFile(const Path& path, u64 size) const = 0;
-
-    /**
-     * Create a directory specified by its path
-     * @param path Path relative to the archive
+     * Rename a File specified by its path
+     * @param src_path Source path relative to the archive
+     * @param dest_path Destination path relative to the archive
      * @return Result of the operation
      */
-    virtual ResultCode CreateDirectory(const Path& path) const = 0;
+    virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0;
 
     /**
      * Rename a Directory specified by its path
@@ -141,6 +132,15 @@ public:
      */
     virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0;
 
+    /**
+     * Open a file specified by its path, using the specified mode
+     * @param path Path relative to the archive
+     * @param mode Mode to open the file with
+     * @return Opened file, or error code
+     */
+    virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const Path& path,
+                                                                const Mode& mode) const = 0;
+
     /**
      * Open a directory specified by its path
      * @param path Path relative to the archive
@@ -152,12 +152,12 @@ public:
      * Get the free space
      * @return The number of free bytes in the archive
      */
-    virtual u64 GetFreeBytes() const = 0;
+    virtual u64 GetFreeSpaceSize() const = 0;
 };
 
-class ArchiveFactory : NonCopyable {
+class FileSystemFactory : NonCopyable {
 public:
-    virtual ~ArchiveFactory() {}
+    virtual ~FileSystemFactory() {}
 
     /**
      * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
@@ -169,7 +169,7 @@ public:
      * @param path Path to the archive
      * @return An ArchiveBackend corresponding operating specified archive path.
      */
-    virtual ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) = 0;
+    virtual ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) = 0;
 
     /**
      * Deletes the archive contents and then re-creates the base folder

+ 1 - 1
src/core/file_sys/path_parser.h

@@ -6,7 +6,7 @@
 
 #include <string>
 #include <vector>
-#include "core/file_sys/archive_backend.h"
+#include "core/file_sys/filesystem.h"
 
 namespace FileSys {
 

+ 38 - 0
src/core/file_sys/romfs_factory.cpp

@@ -0,0 +1,38 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <memory>
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "core/file_sys/romfs_factory.h"
+#include "core/file_sys/romfs_filesystem.h"
+
+namespace FileSys {
+
+RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) {
+    // Load the RomFS from the app
+    if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) {
+        LOG_ERROR(Service_FS, "Unable to read RomFS!");
+    }
+}
+
+ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& path) {
+    auto archive = std::make_unique<RomFS_FileSystem>(romfs_file, data_offset, data_size);
+    return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
+}
+
+ResultCode RomFS_Factory::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
+    LOG_ERROR(Service_FS, "Unimplemented Format archive %s", GetName().c_str());
+    // TODO(bunnei): Find the right error code for this
+    return ResultCode(-1);
+}
+
+ResultVal<ArchiveFormatInfo> RomFS_Factory::GetFormatInfo(const Path& path) const {
+    LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
+    // TODO(bunnei): Find the right error code for this
+    return ResultCode(-1);
+}
+
+} // namespace FileSys

+ 35 - 0
src/core/file_sys/romfs_factory.h

@@ -0,0 +1,35 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <vector>
+#include "common/common_types.h"
+#include "core/file_sys/filesystem.h"
+#include "core/hle/result.h"
+#include "core/loader/loader.h"
+
+namespace FileSys {
+
+/// File system interface to the RomFS archive
+class RomFS_Factory final : public FileSystemFactory {
+public:
+    explicit RomFS_Factory(Loader::AppLoader& app_loader);
+
+    std::string GetName() const override {
+        return "ArchiveFactory_RomFS";
+    }
+    ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
+    ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
+    ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
+
+private:
+    std::shared_ptr<FileUtil::IOFile> romfs_file;
+    u64 data_offset;
+    u64 data_size;
+};
+
+} // namespace FileSys

+ 34 - 38
src/core/file_sys/ivfc_archive.cpp → src/core/file_sys/romfs_filesystem.cpp

@@ -1,4 +1,4 @@
-// Copyright 2014 Citra Emulator Project
+// Copyright 2018 yuzu emulator team
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
@@ -6,84 +6,80 @@
 #include <memory>
 #include "common/common_types.h"
 #include "common/logging/log.h"
-#include "core/file_sys/ivfc_archive.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
+#include "core/file_sys/romfs_filesystem.h"
 
 namespace FileSys {
 
-std::string IVFCArchive::GetName() const {
-    return "IVFC";
+std::string RomFS_FileSystem::GetName() const {
+    return "RomFS";
 }
 
-ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path,
-                                                              const Mode& mode) const {
-    return MakeResult<std::unique_ptr<FileBackend>>(
-        std::make_unique<IVFCFile>(romfs_file, data_offset, data_size));
+ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const Path& path,
+                                                                      const Mode& mode) const {
+    return MakeResult<std::unique_ptr<StorageBackend>>(
+        std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size));
 }
 
-ResultCode IVFCArchive::DeleteFile(const Path& path) const {
-    LOG_CRITICAL(Service_FS, "Attempted to delete a file from an IVFC archive (%s).",
+ResultCode RomFS_FileSystem::DeleteFile(const Path& path) const {
+    LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive (%s).",
                  GetName().c_str());
     // TODO(bunnei): Use correct error code
     return ResultCode(-1);
 }
 
-ResultCode IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
-    LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).",
+ResultCode RomFS_FileSystem::RenameFile(const Path& src_path, const Path& dest_path) const {
+    LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive (%s).",
                  GetName().c_str());
     // TODO(wwylele): Use correct error code
     return ResultCode(-1);
 }
 
-ResultCode IVFCArchive::DeleteDirectory(const Path& path) const {
-    LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).",
+ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const {
+    LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive (%s).",
                  GetName().c_str());
     // TODO(wwylele): Use correct error code
     return ResultCode(-1);
 }
 
-ResultCode IVFCArchive::DeleteDirectoryRecursively(const Path& path) const {
-    LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).",
+ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const {
+    LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive (%s).",
                  GetName().c_str());
     // TODO(wwylele): Use correct error code
     return ResultCode(-1);
 }
 
-ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const {
-    LOG_CRITICAL(Service_FS, "Attempted to create a file in an IVFC archive (%s).",
+ResultCode RomFS_FileSystem::CreateFile(const Path& path, u64 size) const {
+    LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive (%s).",
                  GetName().c_str());
     // TODO(bunnei): Use correct error code
     return ResultCode(-1);
 }
 
-ResultCode IVFCArchive::CreateDirectory(const Path& path) const {
-    LOG_CRITICAL(Service_FS, "Attempted to create a directory in an IVFC archive (%s).",
+ResultCode RomFS_FileSystem::CreateDirectory(const Path& path) const {
+    LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive (%s).",
                  GetName().c_str());
     // TODO(wwylele): Use correct error code
     return ResultCode(-1);
 }
 
-ResultCode IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
-    LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).",
+ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const {
+    LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive (%s).",
                  GetName().c_str());
     // TODO(wwylele): Use correct error code
     return ResultCode(-1);
 }
 
-ResultVal<std::unique_ptr<DirectoryBackend>> IVFCArchive::OpenDirectory(const Path& path) const {
-    return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<IVFCDirectory>());
+ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory(
+    const Path& path) const {
+    return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>());
 }
 
-u64 IVFCArchive::GetFreeBytes() const {
-    LOG_WARNING(Service_FS, "Attempted to get the free space in an IVFC archive");
+u64 RomFS_FileSystem::GetFreeSpaceSize() const {
+    LOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive");
     return 0;
 }
 
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-ResultVal<size_t> IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const {
+ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
     LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length);
     romfs_file->Seek(data_offset + offset, SEEK_SET);
     size_t read_length = (size_t)std::min((u64)length, data_size - offset);
@@ -91,19 +87,19 @@ ResultVal<size_t> IVFCFile::Read(const u64 offset, const size_t length, u8* buff
     return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length));
 }
 
-ResultVal<size_t> IVFCFile::Write(const u64 offset, const size_t length, const bool flush,
-                                  const u8* buffer) const {
-    LOG_ERROR(Service_FS, "Attempted to write to IVFC file");
+ResultVal<size_t> RomFS_Storage::Write(const u64 offset, const size_t length, const bool flush,
+                                       const u8* buffer) const {
+    LOG_ERROR(Service_FS, "Attempted to write to ROMFS file");
     // TODO(Subv): Find error code
     return MakeResult<size_t>(0);
 }
 
-u64 IVFCFile::GetSize() const {
+u64 RomFS_Storage::GetSize() const {
     return data_size;
 }
 
-bool IVFCFile::SetSize(const u64 size) const {
-    LOG_ERROR(Service_FS, "Attempted to set the size of an IVFC file");
+bool RomFS_Storage::SetSize(const u64 size) const {
+    LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file");
     return false;
 }
 

+ 15 - 18
src/core/file_sys/ivfc_archive.h → src/core/file_sys/romfs_filesystem.h

@@ -1,4 +1,4 @@
-// Copyright 2014 Citra Emulator Project
+// Copyright 2018 yuzu emulator team
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
@@ -10,30 +10,27 @@
 #include <vector>
 #include "common/common_types.h"
 #include "common/file_util.h"
-#include "core/file_sys/archive_backend.h"
-#include "core/file_sys/directory_backend.h"
-#include "core/file_sys/file_backend.h"
+#include "core/file_sys/directory.h"
+#include "core/file_sys/filesystem.h"
+#include "core/file_sys/storage.h"
 #include "core/hle/result.h"
 
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
 namespace FileSys {
 
 /**
- * Helper which implements an interface to deal with IVFC images used in some archives
- * This should be subclassed by concrete archive types, which will provide the
- * input data (load the raw IVFC archive) and override any required methods
+ * Helper which implements an interface to deal with Switch .istorage ROMFS images used in some
+ * archives This should be subclassed by concrete archive types, which will provide the input data
+ * (load the raw ROMFS archive) and override any required methods
  */
-class IVFCArchive : public ArchiveBackend {
+class RomFS_FileSystem : public FileSystemBackend {
 public:
-    IVFCArchive(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
+    RomFS_FileSystem(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
         : romfs_file(file), data_offset(offset), data_size(size) {}
 
     std::string GetName() const override;
 
-    ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
-                                                     const Mode& mode) const override;
+    ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const Path& path,
+                                                        const Mode& mode) const override;
     ResultCode DeleteFile(const Path& path) const override;
     ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
     ResultCode DeleteDirectory(const Path& path) const override;
@@ -42,7 +39,7 @@ public:
     ResultCode CreateDirectory(const Path& path) const override;
     ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
     ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
-    u64 GetFreeBytes() const override;
+    u64 GetFreeSpaceSize() const override;
 
 protected:
     std::shared_ptr<FileUtil::IOFile> romfs_file;
@@ -50,9 +47,9 @@ protected:
     u64 data_size;
 };
 
-class IVFCFile : public FileBackend {
+class RomFS_Storage : public StorageBackend {
 public:
-    IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
+    RomFS_Storage(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
         : romfs_file(file), data_offset(offset), data_size(size) {}
 
     ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
@@ -70,7 +67,7 @@ private:
     u64 data_size;
 };
 
-class IVFCDirectory : public DirectoryBackend {
+class ROMFSDirectory : public DirectoryBackend {
 public:
     u32 Read(const u32 count, Entry* entries) override {
         return 0;

+ 0 - 330
src/core/file_sys/savedata_archive.cpp

@@ -1,330 +0,0 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/file_util.h"
-#include "core/file_sys/disk_archive.h"
-#include "core/file_sys/errors.h"
-#include "core/file_sys/path_parser.h"
-#include "core/file_sys/savedata_archive.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& path,
-                                                                  const Mode& mode) const {
-    LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
-
-    const PathParser path_parser(path);
-
-    if (!path_parser.IsValid()) {
-        LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
-        return ERROR_INVALID_PATH;
-    }
-
-    if (mode.hex == 0) {
-        LOG_ERROR(Service_FS, "Empty open mode");
-        return ERROR_UNSUPPORTED_OPEN_FLAGS;
-    }
-
-    if (mode.create_flag && !mode.write_flag) {
-        LOG_ERROR(Service_FS, "Create flag set but write flag not set");
-        return ERROR_UNSUPPORTED_OPEN_FLAGS;
-    }
-
-    const auto full_path = path_parser.BuildHostPath(mount_point);
-
-    switch (path_parser.GetHostStatus(mount_point)) {
-    case PathParser::InvalidMountPoint:
-        LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
-        return ERROR_FILE_NOT_FOUND;
-    case PathParser::PathNotFound:
-        LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
-        return ERROR_PATH_NOT_FOUND;
-    case PathParser::FileInPath:
-    case PathParser::DirectoryFound:
-        LOG_ERROR(Service_FS, "Unexpected file or directory in %s", full_path.c_str());
-        return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
-    case PathParser::NotFound:
-        if (!mode.create_flag) {
-            LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.",
-                      full_path.c_str());
-            return ERROR_FILE_NOT_FOUND;
-        } else {
-            // Create the file
-            FileUtil::CreateEmptyFile(full_path);
-        }
-        break;
-    case PathParser::FileFound:
-        break; // Expected 'success' case
-    }
-
-    FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
-    if (!file.IsOpen()) {
-        LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str());
-        return ERROR_FILE_NOT_FOUND;
-    }
-
-    auto disk_file = std::make_unique<DiskFile>(std::move(file), mode);
-    return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
-}
-
-ResultCode SaveDataArchive::DeleteFile(const Path& path) const {
-    const PathParser path_parser(path);
-
-    if (!path_parser.IsValid()) {
-        LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
-        return ERROR_INVALID_PATH;
-    }
-
-    const auto full_path = path_parser.BuildHostPath(mount_point);
-
-    switch (path_parser.GetHostStatus(mount_point)) {
-    case PathParser::InvalidMountPoint:
-        LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
-        return ERROR_FILE_NOT_FOUND;
-    case PathParser::PathNotFound:
-        LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
-        return ERROR_PATH_NOT_FOUND;
-    case PathParser::FileInPath:
-    case PathParser::DirectoryFound:
-    case PathParser::NotFound:
-        LOG_ERROR(Service_FS, "File not found %s", full_path.c_str());
-        return ERROR_FILE_NOT_FOUND;
-    case PathParser::FileFound:
-        break; // Expected 'success' case
-    }
-
-    if (FileUtil::Delete(full_path)) {
-        return RESULT_SUCCESS;
-    }
-
-    LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str());
-    return ERROR_FILE_NOT_FOUND;
-}
-
-ResultCode SaveDataArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
-    const PathParser path_parser_src(src_path);
-
-    // TODO: Verify these return codes with HW
-    if (!path_parser_src.IsValid()) {
-        LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str());
-        return ERROR_INVALID_PATH;
-    }
-
-    const PathParser path_parser_dest(dest_path);
-
-    if (!path_parser_dest.IsValid()) {
-        LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str());
-        return ERROR_INVALID_PATH;
-    }
-
-    const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
-    const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
-
-    if (FileUtil::Rename(src_path_full, dest_path_full)) {
-        return RESULT_SUCCESS;
-    }
-
-    // TODO(bunnei): Use correct error code
-    return ResultCode(-1);
-}
-
-template <typename T>
-static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mount_point,
-                                        T deleter) {
-    const PathParser path_parser(path);
-
-    if (!path_parser.IsValid()) {
-        LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
-        return ERROR_INVALID_PATH;
-    }
-
-    if (path_parser.IsRootDirectory())
-        return ERROR_DIRECTORY_NOT_EMPTY;
-
-    const auto full_path = path_parser.BuildHostPath(mount_point);
-
-    switch (path_parser.GetHostStatus(mount_point)) {
-    case PathParser::InvalidMountPoint:
-        LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
-        return ERROR_PATH_NOT_FOUND;
-    case PathParser::PathNotFound:
-    case PathParser::NotFound:
-        LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
-        return ERROR_PATH_NOT_FOUND;
-    case PathParser::FileInPath:
-    case PathParser::FileFound:
-        LOG_ERROR(Service_FS, "Unexpected file or directory %s", full_path.c_str());
-        return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
-    case PathParser::DirectoryFound:
-        break; // Expected 'success' case
-    }
-
-    if (deleter(full_path)) {
-        return RESULT_SUCCESS;
-    }
-
-    LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str());
-    return ERROR_DIRECTORY_NOT_EMPTY;
-}
-
-ResultCode SaveDataArchive::DeleteDirectory(const Path& path) const {
-    return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
-}
-
-ResultCode SaveDataArchive::DeleteDirectoryRecursively(const Path& path) const {
-    return DeleteDirectoryHelper(
-        path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
-}
-
-ResultCode SaveDataArchive::CreateFile(const FileSys::Path& path, u64 size) const {
-    const PathParser path_parser(path);
-
-    if (!path_parser.IsValid()) {
-        LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
-        return ERROR_INVALID_PATH;
-    }
-
-    const auto full_path = path_parser.BuildHostPath(mount_point);
-
-    switch (path_parser.GetHostStatus(mount_point)) {
-    case PathParser::InvalidMountPoint:
-        LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
-        return ERROR_FILE_NOT_FOUND;
-    case PathParser::PathNotFound:
-        LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
-        return ERROR_PATH_NOT_FOUND;
-    case PathParser::FileInPath:
-        LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
-        return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
-    case PathParser::DirectoryFound:
-    case PathParser::FileFound:
-        LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
-        return ERROR_FILE_ALREADY_EXISTS;
-    case PathParser::NotFound:
-        break; // Expected 'success' case
-    }
-
-    if (size == 0) {
-        FileUtil::CreateEmptyFile(full_path);
-        return RESULT_SUCCESS;
-    }
-
-    FileUtil::IOFile file(full_path, "wb");
-    // Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
-    // We do this by seeking to the right size, then writing a single null byte.
-    if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
-        return RESULT_SUCCESS;
-    }
-
-    LOG_ERROR(Service_FS, "Too large file");
-
-    // TODO(bunnei): Use correct error code
-    return ResultCode(-1);
-}
-
-ResultCode SaveDataArchive::CreateDirectory(const Path& path) const {
-    const PathParser path_parser(path);
-
-    if (!path_parser.IsValid()) {
-        LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
-        return ERROR_INVALID_PATH;
-    }
-
-    const auto full_path = path_parser.BuildHostPath(mount_point);
-
-    switch (path_parser.GetHostStatus(mount_point)) {
-    case PathParser::InvalidMountPoint:
-        LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
-        return ERROR_FILE_NOT_FOUND;
-    case PathParser::PathNotFound:
-        LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
-        return ERROR_PATH_NOT_FOUND;
-    case PathParser::FileInPath:
-        LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
-        return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
-    case PathParser::DirectoryFound:
-    case PathParser::FileFound:
-        LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
-        return ERROR_DIRECTORY_ALREADY_EXISTS;
-    case PathParser::NotFound:
-        break; // Expected 'success' case
-    }
-
-    if (FileUtil::CreateDir(mount_point + path.AsString())) {
-        return RESULT_SUCCESS;
-    }
-
-    LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", mount_point.c_str());
-
-    // TODO(bunnei): Use correct error code
-    return ResultCode(-1);
-}
-
-ResultCode SaveDataArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
-    const PathParser path_parser_src(src_path);
-
-    // TODO: Verify these return codes with HW
-    if (!path_parser_src.IsValid()) {
-        LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str());
-        return ERROR_INVALID_PATH;
-    }
-
-    const PathParser path_parser_dest(dest_path);
-
-    if (!path_parser_dest.IsValid()) {
-        LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str());
-        return ERROR_INVALID_PATH;
-    }
-
-    const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
-    const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
-
-    if (FileUtil::Rename(src_path_full, dest_path_full)) {
-        return RESULT_SUCCESS;
-    }
-
-    // TODO(bunnei): Use correct error code
-    return ResultCode(-1);
-}
-
-ResultVal<std::unique_ptr<DirectoryBackend>> SaveDataArchive::OpenDirectory(
-    const Path& path) const {
-    const PathParser path_parser(path);
-
-    if (!path_parser.IsValid()) {
-        LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
-        return ERROR_INVALID_PATH;
-    }
-
-    const auto full_path = path_parser.BuildHostPath(mount_point);
-
-    switch (path_parser.GetHostStatus(mount_point)) {
-    case PathParser::InvalidMountPoint:
-        LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
-        return ERROR_FILE_NOT_FOUND;
-    case PathParser::PathNotFound:
-    case PathParser::NotFound:
-        LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
-        return ERROR_PATH_NOT_FOUND;
-    case PathParser::FileInPath:
-    case PathParser::FileFound:
-        LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
-        return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
-    case PathParser::DirectoryFound:
-        break; // Expected 'success' case
-    }
-
-    auto directory = std::make_unique<DiskDirectory>(full_path);
-    return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
-}
-
-u64 SaveDataArchive::GetFreeBytes() const {
-    // TODO: Stubbed to return 1GiB
-    return 1024 * 1024 * 1024;
-}
-
-} // namespace FileSys

+ 0 - 43
src/core/file_sys/savedata_archive.h

@@ -1,43 +0,0 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <string>
-#include "core/file_sys/archive_backend.h"
-#include "core/file_sys/directory_backend.h"
-#include "core/file_sys/file_backend.h"
-#include "core/hle/result.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-/// Archive backend for general save data archive type (SaveData and SystemSaveData)
-class SaveDataArchive : public ArchiveBackend {
-public:
-    explicit SaveDataArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
-
-    std::string GetName() const override {
-        return "SaveDataArchive: " + mount_point;
-    }
-
-    ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
-                                                     const Mode& mode) const override;
-    ResultCode DeleteFile(const Path& path) const override;
-    ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
-    ResultCode DeleteDirectory(const Path& path) const override;
-    ResultCode DeleteDirectoryRecursively(const Path& path) const override;
-    ResultCode CreateFile(const Path& path, u64 size) const override;
-    ResultCode CreateDirectory(const Path& path) const override;
-    ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
-    ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
-    u64 GetFreeBytes() const override;
-
-protected:
-    std::string mount_point;
-};
-
-} // namespace FileSys

+ 12 - 15
src/core/file_sys/file_backend.h → src/core/file_sys/storage.h

@@ -1,4 +1,4 @@
-// Copyright 2014 Citra Emulator Project
+// Copyright 2018 yuzu emulator team
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
@@ -8,15 +8,12 @@
 #include "common/common_types.h"
 #include "core/hle/result.h"
 
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
 namespace FileSys {
 
-class FileBackend : NonCopyable {
+class StorageBackend : NonCopyable {
 public:
-    FileBackend() {}
-    virtual ~FileBackend() {}
+    StorageBackend() {}
+    virtual ~StorageBackend() {}
 
     /**
      * Read data from the file
@@ -39,10 +36,9 @@ public:
                                     const u8* buffer) const = 0;
 
     /**
-     * Get the size of the file in bytes
-     * @return Size of the file in bytes
+     * Flushes the file
      */
-    virtual u64 GetSize() const = 0;
+    virtual void Flush() const = 0;
 
     /**
      * Set the size of the file in bytes
@@ -52,15 +48,16 @@ public:
     virtual bool SetSize(u64 size) const = 0;
 
     /**
-     * Close the file
-     * @return true if the file closed correctly
+     * Get the size of the file in bytes
+     * @return Size of the file in bytes
      */
-    virtual bool Close() const = 0;
+    virtual u64 GetSize() const = 0;
 
     /**
-     * Flushes the file
+     * Close the file
+     * @return true if the file closed correctly
      */
-    virtual void Flush() const = 0;
+    virtual bool Close() const = 0;
 };
 
 } // namespace FileSys

+ 0 - 163
src/core/file_sys/title_metadata.cpp

@@ -1,163 +0,0 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <cinttypes>
-#include "common/alignment.h"
-#include "common/file_util.h"
-#include "common/logging/log.h"
-#include "core/file_sys/title_metadata.h"
-#include "core/loader/loader.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-static u32 GetSignatureSize(u32 signature_type) {
-    switch (signature_type) {
-    case Rsa4096Sha1:
-    case Rsa4096Sha256:
-        return 0x200;
-
-    case Rsa2048Sha1:
-    case Rsa2048Sha256:
-        return 0x100;
-
-    case EllipticSha1:
-    case EcdsaSha256:
-        return 0x3C;
-    }
-}
-
-Loader::ResultStatus TitleMetadata::Load() {
-    FileUtil::IOFile file(filepath, "rb");
-    if (!file.IsOpen())
-        return Loader::ResultStatus::Error;
-
-    if (!file.ReadBytes(&signature_type, sizeof(u32_be)))
-        return Loader::ResultStatus::Error;
-
-    // Signature lengths are variable, and the body follows the signature
-    u32 signature_size = GetSignatureSize(signature_type);
-
-    tmd_signature.resize(signature_size);
-    if (!file.ReadBytes(&tmd_signature[0], signature_size))
-        return Loader::ResultStatus::Error;
-
-    // The TMD body start position is rounded to the nearest 0x40 after the signature
-    size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40);
-    file.Seek(body_start, SEEK_SET);
-
-    // Read our TMD body, then load the amount of ContentChunks specified
-    if (file.ReadBytes(&tmd_body, sizeof(TitleMetadata::Body)) != sizeof(TitleMetadata::Body))
-        return Loader::ResultStatus::Error;
-
-    for (u16 i = 0; i < tmd_body.content_count; i++) {
-        ContentChunk chunk;
-        if (file.ReadBytes(&chunk, sizeof(ContentChunk)) == sizeof(ContentChunk)) {
-            tmd_chunks.push_back(chunk);
-        } else {
-            LOG_ERROR(Service_FS, "Malformed TMD %s, failed to load content chunk index %u!",
-                      filepath.c_str(), i);
-            return Loader::ResultStatus::ErrorInvalidFormat;
-        }
-    }
-
-    return Loader::ResultStatus::Success;
-}
-
-Loader::ResultStatus TitleMetadata::Save() {
-    UNIMPLEMENTED();
-    return Loader::ResultStatus::Success;
-}
-
-u64 TitleMetadata::GetTitleID() const {
-    return tmd_body.title_id;
-}
-
-u32 TitleMetadata::GetTitleType() const {
-    return tmd_body.title_type;
-}
-
-u16 TitleMetadata::GetTitleVersion() const {
-    return tmd_body.title_version;
-}
-
-u64 TitleMetadata::GetSystemVersion() const {
-    return tmd_body.system_version;
-}
-
-size_t TitleMetadata::GetContentCount() const {
-    return tmd_chunks.size();
-}
-
-u32 TitleMetadata::GetBootContentID() const {
-    return tmd_chunks[TMDContentIndex::Main].id;
-}
-
-u32 TitleMetadata::GetManualContentID() const {
-    return tmd_chunks[TMDContentIndex::Manual].id;
-}
-
-u32 TitleMetadata::GetDLPContentID() const {
-    return tmd_chunks[TMDContentIndex::DLP].id;
-}
-
-void TitleMetadata::SetTitleID(u64 title_id) {
-    tmd_body.title_id = title_id;
-}
-
-void TitleMetadata::SetTitleType(u32 type) {
-    tmd_body.title_type = type;
-}
-
-void TitleMetadata::SetTitleVersion(u16 version) {
-    tmd_body.title_version = version;
-}
-
-void TitleMetadata::SetSystemVersion(u64 version) {
-    tmd_body.system_version = version;
-}
-
-void TitleMetadata::AddContentChunk(const ContentChunk& chunk) {
-    tmd_chunks.push_back(chunk);
-}
-
-void TitleMetadata::Print() const {
-    LOG_DEBUG(Service_FS, "%s - %u chunks", filepath.c_str(),
-              static_cast<u32>(tmd_body.content_count));
-
-    // Content info describes ranges of content chunks
-    LOG_DEBUG(Service_FS, "Content info:");
-    for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) {
-        if (tmd_body.contentinfo[i].command_count == 0)
-            break;
-
-        LOG_DEBUG(Service_FS, "    Index %04X, Command Count %04X",
-                  static_cast<u32>(tmd_body.contentinfo[i].index),
-                  static_cast<u32>(tmd_body.contentinfo[i].command_count));
-    }
-
-    // For each content info, print their content chunk range
-    for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) {
-        u16 index = static_cast<u16>(tmd_body.contentinfo[i].index);
-        u16 count = static_cast<u16>(tmd_body.contentinfo[i].command_count);
-
-        if (count == 0)
-            continue;
-
-        LOG_DEBUG(Service_FS, "Content chunks for content info index %zu:", i);
-        for (u16 j = index; j < index + count; j++) {
-            // Don't attempt to print content we don't have
-            if (j > tmd_body.content_count)
-                break;
-
-            const ContentChunk& chunk = tmd_chunks[j];
-            LOG_DEBUG(Service_FS, "    ID %08X, Index %04X, Type %04x, Size %016" PRIX64,
-                      static_cast<u32>(chunk.id), static_cast<u32>(chunk.index),
-                      static_cast<u32>(chunk.type), static_cast<u64>(chunk.size));
-        }
-    }
-}
-} // namespace FileSys

+ 0 - 126
src/core/file_sys/title_metadata.h

@@ -1,126 +0,0 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <array>
-#include <string>
-#include <vector>
-#include "common/common_types.h"
-#include "common/swap.h"
-
-namespace Loader {
-enum class ResultStatus;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// FileSys namespace
-
-namespace FileSys {
-
-enum TMDSignatureType : u32 {
-    Rsa4096Sha1 = 0x10000,
-    Rsa2048Sha1 = 0x10001,
-    EllipticSha1 = 0x10002,
-    Rsa4096Sha256 = 0x10003,
-    Rsa2048Sha256 = 0x10004,
-    EcdsaSha256 = 0x10005
-};
-
-enum TMDContentTypeFlag : u16 {
-    Encrypted = 1 << 1,
-    Disc = 1 << 2,
-    CFM = 1 << 3,
-    Optional = 1 << 14,
-    Shared = 1 << 15
-};
-
-/**
- * Helper which implements an interface to read and write Title Metadata (TMD) files.
- * If a file path is provided and the file exists, it can be parsed and used, otherwise
- * it must be created. The TMD file can then be interpreted, modified and/or saved.
- */
-class TitleMetadata {
-public:
-    struct ContentChunk {
-        u32_be id;
-        u16_be index;
-        u16_be type;
-        u64_be size;
-        std::array<u8, 0x20> hash;
-    };
-
-    static_assert(sizeof(ContentChunk) == 0x30, "TMD ContentChunk structure size is wrong");
-
-    struct ContentInfo {
-        u16_be index;
-        u16_be command_count;
-        std::array<u8, 0x20> hash;
-    };
-
-    static_assert(sizeof(ContentInfo) == 0x24, "TMD ContentInfo structure size is wrong");
-
-#pragma pack(push, 1)
-
-    struct Body {
-        std::array<u8, 0x40> issuer;
-        u8 version;
-        u8 ca_crl_version;
-        u8 signer_crl_version;
-        u8 reserved;
-        u64_be system_version;
-        u64_be title_id;
-        u32_be title_type;
-        u16_be group_id;
-        u32_be savedata_size;
-        u32_be srl_private_savedata_size;
-        std::array<u8, 4> reserved_2;
-        u8 srl_flag;
-        std::array<u8, 0x31> reserved_3;
-        u32_be access_rights;
-        u16_be title_version;
-        u16_be content_count;
-        u16_be boot_content;
-        std::array<u8, 2> reserved_4;
-        std::array<u8, 0x20> contentinfo_hash;
-        std::array<ContentInfo, 64> contentinfo;
-    };
-
-    static_assert(sizeof(Body) == 0x9C4, "TMD body structure size is wrong");
-
-#pragma pack(pop)
-
-    explicit TitleMetadata(std::string& path) : filepath(std::move(path)) {}
-    Loader::ResultStatus Load();
-    Loader::ResultStatus Save();
-
-    u64 GetTitleID() const;
-    u32 GetTitleType() const;
-    u16 GetTitleVersion() const;
-    u64 GetSystemVersion() const;
-    size_t GetContentCount() const;
-    u32 GetBootContentID() const;
-    u32 GetManualContentID() const;
-    u32 GetDLPContentID() const;
-
-    void SetTitleID(u64 title_id);
-    void SetTitleType(u32 type);
-    void SetTitleVersion(u16 version);
-    void SetSystemVersion(u64 version);
-    void AddContentChunk(const ContentChunk& chunk);
-
-    void Print() const;
-
-private:
-    enum TMDContentIndex { Main = 0, Manual = 1, DLP = 2 };
-
-    Body tmd_body;
-    u32_be signature_type;
-    std::vector<u8> tmd_signature;
-    std::vector<ContentChunk> tmd_chunks;
-
-    std::string filepath;
-};
-
-} // namespace FileSys

+ 5 - 0
src/core/hle/ipc_helpers.h

@@ -304,6 +304,11 @@ inline u64 RequestParser::Pop() {
     return msw << 32 | lsw;
 }
 
+template <>
+inline s64 RequestParser::Pop() {
+    return static_cast<s64>(Pop<u64>());
+}
+
 template <>
 inline bool RequestParser::Pop() {
     return Pop<u8>() != 0;

+ 2 - 0
src/core/hle/result.h

@@ -19,6 +19,8 @@
 enum class ErrorDescription : u32 {
     Success = 0,
     RemoteProcessDead = 301,
+    InvalidOffset = 6061,
+    InvalidLength = 6062,
 };
 
 /**

+ 54 - 0
src/core/hle/service/filesystem/filesystem.cpp

@@ -0,0 +1,54 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <boost/container/flat_map.hpp>
+#include "core/file_sys/filesystem.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/filesystem/fsp_srv.h"
+
+namespace Service {
+namespace FileSystem {
+
+/**
+ * Map of registered file systems, identified by type. Once an file system is registered here, it
+ * is never removed until UnregisterFileSystems is called.
+ */
+static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map;
+
+ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type) {
+    auto result = filesystem_map.emplace(type, std::move(factory));
+
+    bool inserted = result.second;
+    ASSERT_MSG(inserted, "Tried to register more than one system with same id code");
+
+    auto& filesystem = result.first->second;
+    LOG_DEBUG(Service_FS, "Registered file system %s with id code 0x%08X",
+              filesystem->GetName().c_str(), static_cast<u32>(type));
+    return RESULT_SUCCESS;
+}
+
+ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
+                                                                      FileSys::Path& path) {
+    LOG_TRACE(Service_FS, "Opening FileSystem with type=%d", type);
+
+    auto itr = filesystem_map.find(type);
+    if (itr == filesystem_map.end()) {
+        // TODO(bunnei): Find a better error code for this
+        return ResultCode(-1);
+    }
+
+    return itr->second->Open(path);
+}
+
+void UnregisterFileSystems() {
+    filesystem_map.clear();
+}
+
+void InstallInterfaces(SM::ServiceManager& service_manager) {
+    UnregisterFileSystems();
+    std::make_shared<FSP_SRV>()->InstallAsService(service_manager);
+}
+
+} // namespace FileSystem
+} // namespace Service

+ 50 - 0
src/core/hle/service/filesystem/filesystem.h

@@ -0,0 +1,50 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include "common/common_types.h"
+#include "core/hle/result.h"
+
+namespace FileSys {
+class FileSystemBackend;
+class FileSystemFactory;
+class Path;
+} // namespace FileSys
+
+namespace Service {
+
+namespace SM {
+class ServiceManager;
+} // namespace SM
+
+namespace FileSystem {
+
+/// Supported FileSystem types
+enum class Type {
+    RomFS = 1,
+};
+
+/**
+ * Registers a FileSystem, instances of which can later be opened using its IdCode.
+ * @param factory FileSystem backend interface to use
+ * @param type Type used to access this type of FileSystem
+ */
+ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type);
+
+/**
+ * Opens a file system
+ * @param type Type of the file system to open
+ * @param path Path to the file system, used with Binary paths
+ * @return FileSys::FileSystemBackend interface to the file system
+ */
+ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
+                                                                      FileSys::Path& path);
+
+/// Registers all Filesystem services with the specified service manager.
+void InstallInterfaces(SM::ServiceManager& service_manager);
+
+} // namespace FileSystem
+} // namespace Service

+ 138 - 0
src/core/hle/service/filesystem/fsp_srv.cpp

@@ -0,0 +1,138 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/file_sys/filesystem.h"
+#include "core/file_sys/storage.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/client_port.h"
+#include "core/hle/kernel/client_session.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/filesystem/fsp_srv.h"
+
+namespace Service {
+namespace FileSystem {
+
+class IStorage final : public ServiceFramework<IStorage> {
+public:
+    IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend)
+        : ServiceFramework("IStorage"), backend(std::move(backend)) {
+        static const FunctionInfo functions[] = {
+            {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"},   {2, nullptr, "Flush"},
+            {3, nullptr, "SetSize"},      {4, nullptr, "GetSize"},
+        };
+        RegisterHandlers(functions);
+    }
+
+private:
+    std::unique_ptr<FileSys::StorageBackend> backend;
+
+    void Read(Kernel::HLERequestContext& ctx) {
+        IPC::RequestParser rp{ctx};
+        const s64 offset = rp.Pop<s64>();
+        const s64 length = rp.Pop<s64>();
+        const auto& descriptor = ctx.BufferDescriptorB()[0];
+
+        LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length);
+
+        // Error checking
+        ASSERT_MSG(length == descriptor.Size(), "unexpected size difference");
+        if (length < 0) {
+            IPC::RequestBuilder rb{ctx, 2};
+            rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
+            return;
+        }
+        if (offset < 0) {
+            IPC::RequestBuilder rb{ctx, 2};
+            rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
+            return;
+        }
+
+        // Read the data from the Storage backend
+        std::vector<u8> output(length);
+        ResultVal<size_t> res = backend->Read(offset, length, output.data());
+        if (res.Failed()) {
+            IPC::RequestBuilder rb{ctx, 2};
+            rb.Push(res.Code());
+            return;
+        }
+
+        // Write the data to memory
+        Memory::WriteBlock(descriptor.Address(), output.data(), descriptor.Size());
+
+        IPC::RequestBuilder rb{ctx, 2};
+        rb.Push(RESULT_SUCCESS);
+    }
+};
+
+FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
+    static const FunctionInfo functions[] = {
+        {1, &FSP_SRV::Initalize, "Initalize"},
+        {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"},
+        {203, &FSP_SRV::OpenRomStorage, "OpenRomStorage"},
+        {1005, &FSP_SRV::GetGlobalAccessLogMode, "GetGlobalAccessLogMode"},
+    };
+    RegisterHandlers(functions);
+}
+
+void FSP_SRV::TryLoadRomFS() {
+    if (romfs) {
+        return;
+    }
+    FileSys::Path unused;
+    auto res = OpenFileSystem(Type::RomFS, unused);
+    if (res.Succeeded()) {
+        romfs = std::move(res.Unwrap());
+    }
+}
+
+void FSP_SRV::Initalize(Kernel::HLERequestContext& ctx) {
+    LOG_WARNING(Service_FS, "(STUBBED) called");
+
+    IPC::RequestBuilder rb{ctx, 2};
+    rb.Push(RESULT_SUCCESS);
+}
+
+void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
+    LOG_WARNING(Service_FS, "(STUBBED) called");
+
+    IPC::RequestBuilder rb{ctx, 4};
+    rb.Push(RESULT_SUCCESS);
+    rb.Push<u32>(5);
+}
+
+void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
+    LOG_DEBUG(Service_FS, "called");
+
+    TryLoadRomFS();
+    if (!romfs) {
+        // TODO (bunnei): Find the right error code to use here
+        LOG_CRITICAL(Service_FS, "no file system interface available!");
+        IPC::RequestBuilder rb{ctx, 2};
+        rb.Push(ResultCode(-1));
+        return;
+    }
+
+    // Attempt to open a StorageBackend interface to the RomFS
+    auto storage = romfs->OpenFile({}, {});
+    if (storage.Failed()) {
+        LOG_CRITICAL(Service_FS, "no storage interface available!");
+        IPC::RequestBuilder rb{ctx, 2};
+        rb.Push(storage.Code());
+        return;
+    }
+
+    IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
+    rb.Push(RESULT_SUCCESS);
+    rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap()));
+}
+
+void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
+    LOG_WARNING(Service_FS, "(STUBBED) called, using OpenDataStorageByCurrentProcess");
+    OpenDataStorageByCurrentProcess(ctx);
+}
+
+} // namespace FileSystem
+} // namespace Service

+ 34 - 0
src/core/hle/service/filesystem/fsp_srv.h

@@ -0,0 +1,34 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include "core/hle/service/service.h"
+
+namespace FileSys {
+class FileSystemBackend;
+}
+
+namespace Service {
+namespace FileSystem {
+
+class FSP_SRV final : public ServiceFramework<FSP_SRV> {
+public:
+    explicit FSP_SRV();
+    ~FSP_SRV() = default;
+
+private:
+    void TryLoadRomFS();
+
+    void Initalize(Kernel::HLERequestContext& ctx);
+    void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
+    void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
+    void OpenRomStorage(Kernel::HLERequestContext& ctx);
+
+    std::unique_ptr<FileSys::FileSystemBackend> romfs;
+};
+
+} // namespace FileSystem
+} // namespace Service

+ 2 - 0
src/core/hle/service/service.cpp

@@ -19,6 +19,7 @@
 #include "core/hle/service/aoc/aoc_u.h"
 #include "core/hle/service/apm/apm.h"
 #include "core/hle/service/audio/audio.h"
+#include "core/hle/service/filesystem/filesystem.h"
 #include "core/hle/service/hid/hid.h"
 #include "core/hle/service/lm/lm.h"
 #include "core/hle/service/nvdrv/nvdrv.h"
@@ -172,6 +173,7 @@ void Init() {
     AOC::InstallInterfaces(*SM::g_service_manager);
     APM::InstallInterfaces(*SM::g_service_manager);
     Audio::InstallInterfaces(*SM::g_service_manager);
+    FileSystem::InstallInterfaces(*SM::g_service_manager);
     HID::InstallInterfaces(*SM::g_service_manager);
     LM::InstallInterfaces(*SM::g_service_manager);
     Nvidia::InstallInterfaces(*SM::g_service_manager);

+ 67 - 2
src/core/loader/deconstructed_rom_directory.cpp

@@ -7,14 +7,44 @@
 #include "common/file_util.h"
 #include "common/logging/log.h"
 #include "common/string_util.h"
+#include "core/file_sys/romfs_factory.h"
 #include "core/hle/kernel/process.h"
 #include "core/hle/kernel/resource_limit.h"
+#include "core/hle/service/filesystem/filesystem.h"
 #include "core/loader/deconstructed_rom_directory.h"
 #include "core/loader/nso.h"
 #include "core/memory.h"
 
 namespace Loader {
 
+static std::string FindRomFS(const std::string& directory) {
+    std::string filepath_romfs;
+    const auto callback = [&filepath_romfs](unsigned*, const std::string& directory,
+                                            const std::string& virtual_name) -> bool {
+        const std::string physical_name = directory + virtual_name;
+        if (FileUtil::IsDirectory(physical_name)) {
+            // Skip directories
+            return true;
+        }
+
+        // Verify extension
+        const std::string extension = physical_name.substr(physical_name.find_last_of(".") + 1);
+        if (Common::ToLower(extension) != "istorage") {
+            return true;
+        }
+
+        // Found it - we are done
+        filepath_romfs = std::move(physical_name);
+        return false;
+    };
+
+    // Search the specified directory recursively, looking for the first .istorage file, which will
+    // be used for the RomFS
+    FileUtil::ForeachDirectoryEntry(nullptr, directory, callback);
+
+    return filepath_romfs;
+}
+
 AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file,
                                                                          std::string filepath)
     : AppLoader(std::move(file)), filepath(std::move(filepath)) {}
@@ -79,10 +109,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
 
     // Load NSO modules
     VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
+    const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP;
     for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
                                "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
-        const std::string path =
-            filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP + module;
+        const std::string path = directory + DIR_SEP + module;
         const VAddr load_addr = next_load_addr;
         next_load_addr = AppLoader_NSO::LoadModule(path, load_addr);
         if (next_load_addr) {
@@ -98,8 +128,43 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
         Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
     process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE);
 
+    // Find the RomFS by searching for a ".istorage" file in this directory
+    filepath_romfs = FindRomFS(directory);
+
+    // Register the RomFS if a ".istorage" file was found
+    if (!filepath_romfs.empty()) {
+        Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this),
+                                                Service::FileSystem::Type::RomFS);
+    }
+
     is_loaded = true;
     return ResultStatus::Success;
 }
 
+ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(
+    std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) {
+
+    if (filepath_romfs.empty()) {
+        LOG_DEBUG(Loader, "No RomFS available");
+        return ResultStatus::ErrorNotUsed;
+    }
+
+    // We reopen the file, to allow its position to be independent
+    romfs_file = std::make_shared<FileUtil::IOFile>(filepath_romfs, "rb");
+    if (!romfs_file->IsOpen()) {
+        return ResultStatus::Error;
+    }
+
+    offset = 0;
+    size = romfs_file->GetSize();
+
+    LOG_DEBUG(Loader, "RomFS offset:           0x%08X", offset);
+    LOG_DEBUG(Loader, "RomFS size:             0x%08X", size);
+
+    // Reset read pointer
+    file.Seek(0, SEEK_SET);
+
+    return ResultStatus::Success;
+}
+
 } // namespace Loader

+ 4 - 0
src/core/loader/deconstructed_rom_directory.h

@@ -35,7 +35,11 @@ public:
 
     ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
 
+    ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
+                           u64& size) override;
+
 private:
+    std::string filepath_romfs;
     std::string filepath;
 };