diff options
| author | 2018-01-21 16:15:15 -0500 | |
|---|---|---|
| committer | 2018-01-21 16:15:15 -0500 | |
| commit | ab8525705bbd0ae62b567f301281ca516e7c5320 (patch) | |
| tree | 88be854c845cb826366acca96e07ccc62cc96695 | |
| parent | Merge pull request #129 from Rozelette/master (diff) | |
| parent | file_sys: Clang format fixes. (diff) | |
| download | yuzu-ab8525705bbd0ae62b567f301281ca516e7c5320.tar.gz yuzu-ab8525705bbd0ae62b567f301281ca516e7c5320.tar.xz yuzu-ab8525705bbd0ae62b567f301281ca516e7c5320.zip | |
Merge pull request #123 from bunnei/fs
Initial implementation of RomFS filesystem and fsp-srv
25 files changed, 535 insertions, 947 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7153c4f3f..433e7e596 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -6,21 +6,17 @@ add_library(core STATIC | |||
| 6 | core.h | 6 | core.h |
| 7 | core_timing.cpp | 7 | core_timing.cpp |
| 8 | core_timing.h | 8 | core_timing.h |
| 9 | file_sys/archive_backend.cpp | 9 | file_sys/directory.h |
| 10 | file_sys/archive_backend.h | ||
| 11 | file_sys/directory_backend.h | ||
| 12 | file_sys/disk_archive.cpp | ||
| 13 | file_sys/disk_archive.h | ||
| 14 | file_sys/errors.h | 10 | file_sys/errors.h |
| 15 | file_sys/file_backend.h | 11 | file_sys/filesystem.cpp |
| 16 | file_sys/ivfc_archive.cpp | 12 | file_sys/filesystem.h |
| 17 | file_sys/ivfc_archive.h | ||
| 18 | file_sys/path_parser.cpp | 13 | file_sys/path_parser.cpp |
| 19 | file_sys/path_parser.h | 14 | file_sys/path_parser.h |
| 20 | file_sys/savedata_archive.cpp | 15 | file_sys/romfs_factory.cpp |
| 21 | file_sys/savedata_archive.h | 16 | file_sys/romfs_factory.h |
| 22 | file_sys/title_metadata.cpp | 17 | file_sys/romfs_filesystem.cpp |
| 23 | file_sys/title_metadata.h | 18 | file_sys/romfs_filesystem.h |
| 19 | file_sys/storage.h | ||
| 24 | frontend/emu_window.cpp | 20 | frontend/emu_window.cpp |
| 25 | frontend/emu_window.h | 21 | frontend/emu_window.h |
| 26 | frontend/framebuffer_layout.cpp | 22 | frontend/framebuffer_layout.cpp |
| @@ -101,6 +97,10 @@ add_library(core STATIC | |||
| 101 | hle/service/audio/audio.h | 97 | hle/service/audio/audio.h |
| 102 | hle/service/audio/audout_u.cpp | 98 | hle/service/audio/audout_u.cpp |
| 103 | hle/service/audio/audout_u.h | 99 | hle/service/audio/audout_u.h |
| 100 | hle/service/filesystem/filesystem.cpp | ||
| 101 | hle/service/filesystem/filesystem.h | ||
| 102 | hle/service/filesystem/fsp_srv.cpp | ||
| 103 | hle/service/filesystem/fsp_srv.h | ||
| 104 | hle/service/hid/hid.cpp | 104 | hle/service/hid/hid.cpp |
| 105 | hle/service/hid/hid.h | 105 | hle/service/hid/hid.h |
| 106 | hle/service/lm/lm.cpp | 106 | hle/service/lm/lm.cpp |
diff --git a/src/core/file_sys/directory_backend.h b/src/core/file_sys/directory.h index 0c93f2074..5a40bf472 100644 --- a/src/core/file_sys/directory_backend.h +++ b/src/core/file_sys/directory.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2018 yuzu emulator team |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp deleted file mode 100644 index 98d80aabc..000000000 --- a/src/core/file_sys/disk_archive.cpp +++ /dev/null | |||
| @@ -1,99 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <cstdio> | ||
| 7 | #include <memory> | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "common/file_util.h" | ||
| 10 | #include "common/logging/log.h" | ||
| 11 | #include "core/file_sys/disk_archive.h" | ||
| 12 | #include "core/file_sys/errors.h" | ||
| 13 | |||
| 14 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 15 | // FileSys namespace | ||
| 16 | |||
| 17 | namespace FileSys { | ||
| 18 | |||
| 19 | ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const { | ||
| 20 | if (!mode.read_flag) | ||
| 21 | return ERROR_INVALID_OPEN_FLAGS; | ||
| 22 | |||
| 23 | file->Seek(offset, SEEK_SET); | ||
| 24 | return MakeResult<size_t>(file->ReadBytes(buffer, length)); | ||
| 25 | } | ||
| 26 | |||
| 27 | ResultVal<size_t> DiskFile::Write(const u64 offset, const size_t length, const bool flush, | ||
| 28 | const u8* buffer) const { | ||
| 29 | if (!mode.write_flag) | ||
| 30 | return ERROR_INVALID_OPEN_FLAGS; | ||
| 31 | |||
| 32 | file->Seek(offset, SEEK_SET); | ||
| 33 | size_t written = file->WriteBytes(buffer, length); | ||
| 34 | if (flush) | ||
| 35 | file->Flush(); | ||
| 36 | return MakeResult<size_t>(written); | ||
| 37 | } | ||
| 38 | |||
| 39 | u64 DiskFile::GetSize() const { | ||
| 40 | return file->GetSize(); | ||
| 41 | } | ||
| 42 | |||
| 43 | bool DiskFile::SetSize(const u64 size) const { | ||
| 44 | file->Resize(size); | ||
| 45 | file->Flush(); | ||
| 46 | return true; | ||
| 47 | } | ||
| 48 | |||
| 49 | bool DiskFile::Close() const { | ||
| 50 | return file->Close(); | ||
| 51 | } | ||
| 52 | |||
| 53 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 54 | |||
| 55 | DiskDirectory::DiskDirectory(const std::string& path) : directory() { | ||
| 56 | unsigned size = FileUtil::ScanDirectoryTree(path, directory); | ||
| 57 | directory.size = size; | ||
| 58 | directory.isDirectory = true; | ||
| 59 | children_iterator = directory.children.begin(); | ||
| 60 | } | ||
| 61 | |||
| 62 | u32 DiskDirectory::Read(const u32 count, Entry* entries) { | ||
| 63 | u32 entries_read = 0; | ||
| 64 | |||
| 65 | while (entries_read < count && children_iterator != directory.children.cend()) { | ||
| 66 | const FileUtil::FSTEntry& file = *children_iterator; | ||
| 67 | const std::string& filename = file.virtualName; | ||
| 68 | Entry& entry = entries[entries_read]; | ||
| 69 | |||
| 70 | LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, | ||
| 71 | file.isDirectory); | ||
| 72 | |||
| 73 | // TODO(Link Mauve): use a proper conversion to UTF-16. | ||
| 74 | for (size_t j = 0; j < FILENAME_LENGTH; ++j) { | ||
| 75 | entry.filename[j] = filename[j]; | ||
| 76 | if (!filename[j]) | ||
| 77 | break; | ||
| 78 | } | ||
| 79 | |||
| 80 | FileUtil::SplitFilename83(filename, entry.short_name, entry.extension); | ||
| 81 | |||
| 82 | entry.is_directory = file.isDirectory; | ||
| 83 | entry.is_hidden = (filename[0] == '.'); | ||
| 84 | entry.is_read_only = 0; | ||
| 85 | entry.file_size = file.size; | ||
| 86 | |||
| 87 | // We emulate a SD card where the archive bit has never been cleared, as it would be on | ||
| 88 | // most user SD cards. | ||
| 89 | // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a | ||
| 90 | // file bit. | ||
| 91 | entry.is_archive = !file.isDirectory; | ||
| 92 | |||
| 93 | ++entries_read; | ||
| 94 | ++children_iterator; | ||
| 95 | } | ||
| 96 | return entries_read; | ||
| 97 | } | ||
| 98 | |||
| 99 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h deleted file mode 100644 index eb9166df6..000000000 --- a/src/core/file_sys/disk_archive.h +++ /dev/null | |||
| @@ -1,68 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | #include <memory> | ||
| 9 | #include <string> | ||
| 10 | #include <vector> | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/file_util.h" | ||
| 13 | #include "core/file_sys/archive_backend.h" | ||
| 14 | #include "core/file_sys/directory_backend.h" | ||
| 15 | #include "core/file_sys/file_backend.h" | ||
| 16 | #include "core/hle/result.h" | ||
| 17 | |||
| 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 19 | // FileSys namespace | ||
| 20 | |||
| 21 | namespace FileSys { | ||
| 22 | |||
| 23 | class DiskFile : public FileBackend { | ||
| 24 | public: | ||
| 25 | DiskFile(FileUtil::IOFile&& file_, const Mode& mode_) | ||
| 26 | : file(new FileUtil::IOFile(std::move(file_))) { | ||
| 27 | mode.hex = mode_.hex; | ||
| 28 | } | ||
| 29 | |||
| 30 | ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; | ||
| 31 | ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; | ||
| 32 | u64 GetSize() const override; | ||
| 33 | bool SetSize(u64 size) const override; | ||
| 34 | bool Close() const override; | ||
| 35 | |||
| 36 | void Flush() const override { | ||
| 37 | file->Flush(); | ||
| 38 | } | ||
| 39 | |||
| 40 | protected: | ||
| 41 | Mode mode; | ||
| 42 | std::unique_ptr<FileUtil::IOFile> file; | ||
| 43 | }; | ||
| 44 | |||
| 45 | class DiskDirectory : public DirectoryBackend { | ||
| 46 | public: | ||
| 47 | DiskDirectory(const std::string& path); | ||
| 48 | |||
| 49 | ~DiskDirectory() override { | ||
| 50 | Close(); | ||
| 51 | } | ||
| 52 | |||
| 53 | u32 Read(const u32 count, Entry* entries) override; | ||
| 54 | |||
| 55 | bool Close() const override { | ||
| 56 | return true; | ||
| 57 | } | ||
| 58 | |||
| 59 | protected: | ||
| 60 | u32 total_entries_in_directory; | ||
| 61 | FileUtil::FSTEntry directory; | ||
| 62 | |||
| 63 | // We need to remember the last entry we returned, so a subsequent call to Read will continue | ||
| 64 | // from the next one. This iterator will always point to the next unread entry. | ||
| 65 | std::vector<FileUtil::FSTEntry>::iterator children_iterator; | ||
| 66 | }; | ||
| 67 | |||
| 68 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/archive_backend.cpp b/src/core/file_sys/filesystem.cpp index fc472b44f..82fdb3c46 100644 --- a/src/core/file_sys/archive_backend.cpp +++ b/src/core/file_sys/filesystem.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | 1 | // Copyright 2018 yuzu emulator team |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include <sstream> | 7 | #include <sstream> |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "common/string_util.h" | 9 | #include "common/string_util.h" |
| 10 | #include "core/file_sys/archive_backend.h" | 10 | #include "core/file_sys/filesystem.h" |
| 11 | #include "core/memory.h" | 11 | #include "core/memory.h" |
| 12 | 12 | ||
| 13 | namespace FileSys { | 13 | namespace FileSys { |
diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/filesystem.h index 58f6c150c..02705506b 100644 --- a/src/core/file_sys/archive_backend.h +++ b/src/core/file_sys/filesystem.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2018 yuzu emulator team |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| @@ -15,7 +15,7 @@ | |||
| 15 | 15 | ||
| 16 | namespace FileSys { | 16 | namespace FileSys { |
| 17 | 17 | ||
| 18 | class FileBackend; | 18 | class StorageBackend; |
| 19 | class DirectoryBackend; | 19 | class DirectoryBackend; |
| 20 | 20 | ||
| 21 | // Path string type | 21 | // Path string type |
| @@ -71,9 +71,9 @@ struct ArchiveFormatInfo { | |||
| 71 | }; | 71 | }; |
| 72 | static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD"); | 72 | static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD"); |
| 73 | 73 | ||
| 74 | class ArchiveBackend : NonCopyable { | 74 | class FileSystemBackend : NonCopyable { |
| 75 | public: | 75 | public: |
| 76 | virtual ~ArchiveBackend() {} | 76 | virtual ~FileSystemBackend() {} |
| 77 | 77 | ||
| 78 | /** | 78 | /** |
| 79 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | 79 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) |
| @@ -81,13 +81,12 @@ public: | |||
| 81 | virtual std::string GetName() const = 0; | 81 | virtual std::string GetName() const = 0; |
| 82 | 82 | ||
| 83 | /** | 83 | /** |
| 84 | * Open a file specified by its path, using the specified mode | 84 | * Create a file specified by its path |
| 85 | * @param path Path relative to the archive | 85 | * @param path Path relative to the Archive |
| 86 | * @param mode Mode to open the file with | 86 | * @param size The size of the new file, filled with zeroes |
| 87 | * @return Opened file, or error code | 87 | * @return Result of the operation |
| 88 | */ | 88 | */ |
| 89 | virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, | 89 | virtual ResultCode CreateFile(const Path& path, u64 size) const = 0; |
| 90 | const Mode& mode) const = 0; | ||
| 91 | 90 | ||
| 92 | /** | 91 | /** |
| 93 | * Delete a file specified by its path | 92 | * Delete a file specified by its path |
| @@ -97,12 +96,11 @@ public: | |||
| 97 | virtual ResultCode DeleteFile(const Path& path) const = 0; | 96 | virtual ResultCode DeleteFile(const Path& path) const = 0; |
| 98 | 97 | ||
| 99 | /** | 98 | /** |
| 100 | * Rename a File specified by its path | 99 | * Create a directory specified by its path |
| 101 | * @param src_path Source path relative to the archive | 100 | * @param path Path relative to the archive |
| 102 | * @param dest_path Destination path relative to the archive | ||
| 103 | * @return Result of the operation | 101 | * @return Result of the operation |
| 104 | */ | 102 | */ |
| 105 | virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0; | 103 | virtual ResultCode CreateDirectory(const Path& path) const = 0; |
| 106 | 104 | ||
| 107 | /** | 105 | /** |
| 108 | * Delete a directory specified by its path | 106 | * Delete a directory specified by its path |
| @@ -119,19 +117,12 @@ public: | |||
| 119 | virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0; | 117 | virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0; |
| 120 | 118 | ||
| 121 | /** | 119 | /** |
| 122 | * Create a file specified by its path | 120 | * Rename a File specified by its path |
| 123 | * @param path Path relative to the Archive | 121 | * @param src_path Source path relative to the archive |
| 124 | * @param size The size of the new file, filled with zeroes | 122 | * @param dest_path Destination path relative to the archive |
| 125 | * @return Result of the operation | ||
| 126 | */ | ||
| 127 | virtual ResultCode CreateFile(const Path& path, u64 size) const = 0; | ||
| 128 | |||
| 129 | /** | ||
| 130 | * Create a directory specified by its path | ||
| 131 | * @param path Path relative to the archive | ||
| 132 | * @return Result of the operation | 123 | * @return Result of the operation |
| 133 | */ | 124 | */ |
| 134 | virtual ResultCode CreateDirectory(const Path& path) const = 0; | 125 | virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0; |
| 135 | 126 | ||
| 136 | /** | 127 | /** |
| 137 | * Rename a Directory specified by its path | 128 | * Rename a Directory specified by its path |
| @@ -142,6 +133,15 @@ public: | |||
| 142 | virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0; | 133 | virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0; |
| 143 | 134 | ||
| 144 | /** | 135 | /** |
| 136 | * Open a file specified by its path, using the specified mode | ||
| 137 | * @param path Path relative to the archive | ||
| 138 | * @param mode Mode to open the file with | ||
| 139 | * @return Opened file, or error code | ||
| 140 | */ | ||
| 141 | virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const Path& path, | ||
| 142 | const Mode& mode) const = 0; | ||
| 143 | |||
| 144 | /** | ||
| 145 | * Open a directory specified by its path | 145 | * Open a directory specified by its path |
| 146 | * @param path Path relative to the archive | 146 | * @param path Path relative to the archive |
| 147 | * @return Opened directory, or error code | 147 | * @return Opened directory, or error code |
| @@ -152,12 +152,12 @@ public: | |||
| 152 | * Get the free space | 152 | * Get the free space |
| 153 | * @return The number of free bytes in the archive | 153 | * @return The number of free bytes in the archive |
| 154 | */ | 154 | */ |
| 155 | virtual u64 GetFreeBytes() const = 0; | 155 | virtual u64 GetFreeSpaceSize() const = 0; |
| 156 | }; | 156 | }; |
| 157 | 157 | ||
| 158 | class ArchiveFactory : NonCopyable { | 158 | class FileSystemFactory : NonCopyable { |
| 159 | public: | 159 | public: |
| 160 | virtual ~ArchiveFactory() {} | 160 | virtual ~FileSystemFactory() {} |
| 161 | 161 | ||
| 162 | /** | 162 | /** |
| 163 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | 163 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) |
| @@ -169,7 +169,7 @@ public: | |||
| 169 | * @param path Path to the archive | 169 | * @param path Path to the archive |
| 170 | * @return An ArchiveBackend corresponding operating specified archive path. | 170 | * @return An ArchiveBackend corresponding operating specified archive path. |
| 171 | */ | 171 | */ |
| 172 | virtual ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) = 0; | 172 | virtual ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) = 0; |
| 173 | 173 | ||
| 174 | /** | 174 | /** |
| 175 | * Deletes the archive contents and then re-creates the base folder | 175 | * Deletes the archive contents and then re-creates the base folder |
diff --git a/src/core/file_sys/path_parser.h b/src/core/file_sys/path_parser.h index b9f52f65d..184f59d55 100644 --- a/src/core/file_sys/path_parser.h +++ b/src/core/file_sys/path_parser.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | #include "core/file_sys/archive_backend.h" | 9 | #include "core/file_sys/filesystem.h" |
| 10 | 10 | ||
| 11 | namespace FileSys { | 11 | namespace FileSys { |
| 12 | 12 | ||
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp new file mode 100644 index 000000000..e0de49f05 --- /dev/null +++ b/src/core/file_sys/romfs_factory.cpp | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <memory> | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/file_sys/romfs_factory.h" | ||
| 10 | #include "core/file_sys/romfs_filesystem.h" | ||
| 11 | |||
| 12 | namespace FileSys { | ||
| 13 | |||
| 14 | RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) { | ||
| 15 | // Load the RomFS from the app | ||
| 16 | if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) { | ||
| 17 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& path) { | ||
| 22 | auto archive = std::make_unique<RomFS_FileSystem>(romfs_file, data_offset, data_size); | ||
| 23 | return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); | ||
| 24 | } | ||
| 25 | |||
| 26 | ResultCode RomFS_Factory::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) { | ||
| 27 | LOG_ERROR(Service_FS, "Unimplemented Format archive %s", GetName().c_str()); | ||
| 28 | // TODO(bunnei): Find the right error code for this | ||
| 29 | return ResultCode(-1); | ||
| 30 | } | ||
| 31 | |||
| 32 | ResultVal<ArchiveFormatInfo> RomFS_Factory::GetFormatInfo(const Path& path) const { | ||
| 33 | LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str()); | ||
| 34 | // TODO(bunnei): Find the right error code for this | ||
| 35 | return ResultCode(-1); | ||
| 36 | } | ||
| 37 | |||
| 38 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h new file mode 100644 index 000000000..10ea13966 --- /dev/null +++ b/src/core/file_sys/romfs_factory.h | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "core/file_sys/filesystem.h" | ||
| 12 | #include "core/hle/result.h" | ||
| 13 | #include "core/loader/loader.h" | ||
| 14 | |||
| 15 | namespace FileSys { | ||
| 16 | |||
| 17 | /// File system interface to the RomFS archive | ||
| 18 | class RomFS_Factory final : public FileSystemFactory { | ||
| 19 | public: | ||
| 20 | explicit RomFS_Factory(Loader::AppLoader& app_loader); | ||
| 21 | |||
| 22 | std::string GetName() const override { | ||
| 23 | return "ArchiveFactory_RomFS"; | ||
| 24 | } | ||
| 25 | ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override; | ||
| 26 | ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override; | ||
| 27 | ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; | ||
| 28 | |||
| 29 | private: | ||
| 30 | std::shared_ptr<FileUtil::IOFile> romfs_file; | ||
| 31 | u64 data_offset; | ||
| 32 | u64 data_size; | ||
| 33 | }; | ||
| 34 | |||
| 35 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/romfs_filesystem.cpp index b3c3f2c6f..ca1463d7c 100644 --- a/src/core/file_sys/ivfc_archive.cpp +++ b/src/core/file_sys/romfs_filesystem.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2018 yuzu emulator team |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| @@ -6,84 +6,80 @@ | |||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "core/file_sys/ivfc_archive.h" | 9 | #include "core/file_sys/romfs_filesystem.h" |
| 10 | |||
| 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 12 | // FileSys namespace | ||
| 13 | 10 | ||
| 14 | namespace FileSys { | 11 | namespace FileSys { |
| 15 | 12 | ||
| 16 | std::string IVFCArchive::GetName() const { | 13 | std::string RomFS_FileSystem::GetName() const { |
| 17 | return "IVFC"; | 14 | return "RomFS"; |
| 18 | } | 15 | } |
| 19 | 16 | ||
| 20 | ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, | 17 | ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const Path& path, |
| 21 | const Mode& mode) const { | 18 | const Mode& mode) const { |
| 22 | return MakeResult<std::unique_ptr<FileBackend>>( | 19 | return MakeResult<std::unique_ptr<StorageBackend>>( |
| 23 | std::make_unique<IVFCFile>(romfs_file, data_offset, data_size)); | 20 | std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size)); |
| 24 | } | 21 | } |
| 25 | 22 | ||
| 26 | ResultCode IVFCArchive::DeleteFile(const Path& path) const { | 23 | ResultCode RomFS_FileSystem::DeleteFile(const Path& path) const { |
| 27 | LOG_CRITICAL(Service_FS, "Attempted to delete a file from an IVFC archive (%s).", | 24 | LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive (%s).", |
| 28 | GetName().c_str()); | 25 | GetName().c_str()); |
| 29 | // TODO(bunnei): Use correct error code | 26 | // TODO(bunnei): Use correct error code |
| 30 | return ResultCode(-1); | 27 | return ResultCode(-1); |
| 31 | } | 28 | } |
| 32 | 29 | ||
| 33 | ResultCode IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { | 30 | ResultCode RomFS_FileSystem::RenameFile(const Path& src_path, const Path& dest_path) const { |
| 34 | LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).", | 31 | LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive (%s).", |
| 35 | GetName().c_str()); | 32 | GetName().c_str()); |
| 36 | // TODO(wwylele): Use correct error code | 33 | // TODO(wwylele): Use correct error code |
| 37 | return ResultCode(-1); | 34 | return ResultCode(-1); |
| 38 | } | 35 | } |
| 39 | 36 | ||
| 40 | ResultCode IVFCArchive::DeleteDirectory(const Path& path) const { | 37 | ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const { |
| 41 | LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).", | 38 | LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive (%s).", |
| 42 | GetName().c_str()); | 39 | GetName().c_str()); |
| 43 | // TODO(wwylele): Use correct error code | 40 | // TODO(wwylele): Use correct error code |
| 44 | return ResultCode(-1); | 41 | return ResultCode(-1); |
| 45 | } | 42 | } |
| 46 | 43 | ||
| 47 | ResultCode IVFCArchive::DeleteDirectoryRecursively(const Path& path) const { | 44 | ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const { |
| 48 | LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).", | 45 | LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive (%s).", |
| 49 | GetName().c_str()); | 46 | GetName().c_str()); |
| 50 | // TODO(wwylele): Use correct error code | 47 | // TODO(wwylele): Use correct error code |
| 51 | return ResultCode(-1); | 48 | return ResultCode(-1); |
| 52 | } | 49 | } |
| 53 | 50 | ||
| 54 | ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const { | 51 | ResultCode RomFS_FileSystem::CreateFile(const Path& path, u64 size) const { |
| 55 | LOG_CRITICAL(Service_FS, "Attempted to create a file in an IVFC archive (%s).", | 52 | LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive (%s).", |
| 56 | GetName().c_str()); | 53 | GetName().c_str()); |
| 57 | // TODO(bunnei): Use correct error code | 54 | // TODO(bunnei): Use correct error code |
| 58 | return ResultCode(-1); | 55 | return ResultCode(-1); |
| 59 | } | 56 | } |
| 60 | 57 | ||
| 61 | ResultCode IVFCArchive::CreateDirectory(const Path& path) const { | 58 | ResultCode RomFS_FileSystem::CreateDirectory(const Path& path) const { |
| 62 | LOG_CRITICAL(Service_FS, "Attempted to create a directory in an IVFC archive (%s).", | 59 | LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive (%s).", |
| 63 | GetName().c_str()); | 60 | GetName().c_str()); |
| 64 | // TODO(wwylele): Use correct error code | 61 | // TODO(wwylele): Use correct error code |
| 65 | return ResultCode(-1); | 62 | return ResultCode(-1); |
| 66 | } | 63 | } |
| 67 | 64 | ||
| 68 | ResultCode IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { | 65 | ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { |
| 69 | LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).", | 66 | LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive (%s).", |
| 70 | GetName().c_str()); | 67 | GetName().c_str()); |
| 71 | // TODO(wwylele): Use correct error code | 68 | // TODO(wwylele): Use correct error code |
| 72 | return ResultCode(-1); | 69 | return ResultCode(-1); |
| 73 | } | 70 | } |
| 74 | 71 | ||
| 75 | ResultVal<std::unique_ptr<DirectoryBackend>> IVFCArchive::OpenDirectory(const Path& path) const { | 72 | ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory( |
| 76 | return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<IVFCDirectory>()); | 73 | const Path& path) const { |
| 74 | return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>()); | ||
| 77 | } | 75 | } |
| 78 | 76 | ||
| 79 | u64 IVFCArchive::GetFreeBytes() const { | 77 | u64 RomFS_FileSystem::GetFreeSpaceSize() const { |
| 80 | LOG_WARNING(Service_FS, "Attempted to get the free space in an IVFC archive"); | 78 | LOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive"); |
| 81 | return 0; | 79 | return 0; |
| 82 | } | 80 | } |
| 83 | 81 | ||
| 84 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 82 | ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { |
| 85 | |||
| 86 | ResultVal<size_t> IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const { | ||
| 87 | LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length); | 83 | LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length); |
| 88 | romfs_file->Seek(data_offset + offset, SEEK_SET); | 84 | romfs_file->Seek(data_offset + offset, SEEK_SET); |
| 89 | size_t read_length = (size_t)std::min((u64)length, data_size - offset); | 85 | 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 | |||
| 91 | return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length)); | 87 | return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length)); |
| 92 | } | 88 | } |
| 93 | 89 | ||
| 94 | ResultVal<size_t> IVFCFile::Write(const u64 offset, const size_t length, const bool flush, | 90 | ResultVal<size_t> RomFS_Storage::Write(const u64 offset, const size_t length, const bool flush, |
| 95 | const u8* buffer) const { | 91 | const u8* buffer) const { |
| 96 | LOG_ERROR(Service_FS, "Attempted to write to IVFC file"); | 92 | LOG_ERROR(Service_FS, "Attempted to write to ROMFS file"); |
| 97 | // TODO(Subv): Find error code | 93 | // TODO(Subv): Find error code |
| 98 | return MakeResult<size_t>(0); | 94 | return MakeResult<size_t>(0); |
| 99 | } | 95 | } |
| 100 | 96 | ||
| 101 | u64 IVFCFile::GetSize() const { | 97 | u64 RomFS_Storage::GetSize() const { |
| 102 | return data_size; | 98 | return data_size; |
| 103 | } | 99 | } |
| 104 | 100 | ||
| 105 | bool IVFCFile::SetSize(const u64 size) const { | 101 | bool RomFS_Storage::SetSize(const u64 size) const { |
| 106 | LOG_ERROR(Service_FS, "Attempted to set the size of an IVFC file"); | 102 | LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file"); |
| 107 | return false; | 103 | return false; |
| 108 | } | 104 | } |
| 109 | 105 | ||
diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/romfs_filesystem.h index e6fbdfb1f..900ea567a 100644 --- a/src/core/file_sys/ivfc_archive.h +++ b/src/core/file_sys/romfs_filesystem.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2018 yuzu emulator team |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| @@ -10,30 +10,27 @@ | |||
| 10 | #include <vector> | 10 | #include <vector> |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/file_util.h" | 12 | #include "common/file_util.h" |
| 13 | #include "core/file_sys/archive_backend.h" | 13 | #include "core/file_sys/directory.h" |
| 14 | #include "core/file_sys/directory_backend.h" | 14 | #include "core/file_sys/filesystem.h" |
| 15 | #include "core/file_sys/file_backend.h" | 15 | #include "core/file_sys/storage.h" |
| 16 | #include "core/hle/result.h" | 16 | #include "core/hle/result.h" |
| 17 | 17 | ||
| 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 19 | // FileSys namespace | ||
| 20 | |||
| 21 | namespace FileSys { | 18 | namespace FileSys { |
| 22 | 19 | ||
| 23 | /** | 20 | /** |
| 24 | * Helper which implements an interface to deal with IVFC images used in some archives | 21 | * Helper which implements an interface to deal with Switch .istorage ROMFS images used in some |
| 25 | * This should be subclassed by concrete archive types, which will provide the | 22 | * archives This should be subclassed by concrete archive types, which will provide the input data |
| 26 | * input data (load the raw IVFC archive) and override any required methods | 23 | * (load the raw ROMFS archive) and override any required methods |
| 27 | */ | 24 | */ |
| 28 | class IVFCArchive : public ArchiveBackend { | 25 | class RomFS_FileSystem : public FileSystemBackend { |
| 29 | public: | 26 | public: |
| 30 | IVFCArchive(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) | 27 | RomFS_FileSystem(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) |
| 31 | : romfs_file(file), data_offset(offset), data_size(size) {} | 28 | : romfs_file(file), data_offset(offset), data_size(size) {} |
| 32 | 29 | ||
| 33 | std::string GetName() const override; | 30 | std::string GetName() const override; |
| 34 | 31 | ||
| 35 | ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, | 32 | ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const Path& path, |
| 36 | const Mode& mode) const override; | 33 | const Mode& mode) const override; |
| 37 | ResultCode DeleteFile(const Path& path) const override; | 34 | ResultCode DeleteFile(const Path& path) const override; |
| 38 | ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override; | 35 | ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override; |
| 39 | ResultCode DeleteDirectory(const Path& path) const override; | 36 | ResultCode DeleteDirectory(const Path& path) const override; |
| @@ -42,7 +39,7 @@ public: | |||
| 42 | ResultCode CreateDirectory(const Path& path) const override; | 39 | ResultCode CreateDirectory(const Path& path) const override; |
| 43 | ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; | 40 | ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; |
| 44 | ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override; | 41 | ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override; |
| 45 | u64 GetFreeBytes() const override; | 42 | u64 GetFreeSpaceSize() const override; |
| 46 | 43 | ||
| 47 | protected: | 44 | protected: |
| 48 | std::shared_ptr<FileUtil::IOFile> romfs_file; | 45 | std::shared_ptr<FileUtil::IOFile> romfs_file; |
| @@ -50,9 +47,9 @@ protected: | |||
| 50 | u64 data_size; | 47 | u64 data_size; |
| 51 | }; | 48 | }; |
| 52 | 49 | ||
| 53 | class IVFCFile : public FileBackend { | 50 | class RomFS_Storage : public StorageBackend { |
| 54 | public: | 51 | public: |
| 55 | IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) | 52 | RomFS_Storage(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) |
| 56 | : romfs_file(file), data_offset(offset), data_size(size) {} | 53 | : romfs_file(file), data_offset(offset), data_size(size) {} |
| 57 | 54 | ||
| 58 | ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; | 55 | ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; |
| @@ -70,7 +67,7 @@ private: | |||
| 70 | u64 data_size; | 67 | u64 data_size; |
| 71 | }; | 68 | }; |
| 72 | 69 | ||
| 73 | class IVFCDirectory : public DirectoryBackend { | 70 | class ROMFSDirectory : public DirectoryBackend { |
| 74 | public: | 71 | public: |
| 75 | u32 Read(const u32 count, Entry* entries) override { | 72 | u32 Read(const u32 count, Entry* entries) override { |
| 76 | return 0; | 73 | return 0; |
diff --git a/src/core/file_sys/savedata_archive.cpp b/src/core/file_sys/savedata_archive.cpp deleted file mode 100644 index d7b012f6e..000000000 --- a/src/core/file_sys/savedata_archive.cpp +++ /dev/null | |||
| @@ -1,330 +0,0 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/file_util.h" | ||
| 6 | #include "core/file_sys/disk_archive.h" | ||
| 7 | #include "core/file_sys/errors.h" | ||
| 8 | #include "core/file_sys/path_parser.h" | ||
| 9 | #include "core/file_sys/savedata_archive.h" | ||
| 10 | |||
| 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 12 | // FileSys namespace | ||
| 13 | |||
| 14 | namespace FileSys { | ||
| 15 | |||
| 16 | ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& path, | ||
| 17 | const Mode& mode) const { | ||
| 18 | LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); | ||
| 19 | |||
| 20 | const PathParser path_parser(path); | ||
| 21 | |||
| 22 | if (!path_parser.IsValid()) { | ||
| 23 | LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); | ||
| 24 | return ERROR_INVALID_PATH; | ||
| 25 | } | ||
| 26 | |||
| 27 | if (mode.hex == 0) { | ||
| 28 | LOG_ERROR(Service_FS, "Empty open mode"); | ||
| 29 | return ERROR_UNSUPPORTED_OPEN_FLAGS; | ||
| 30 | } | ||
| 31 | |||
| 32 | if (mode.create_flag && !mode.write_flag) { | ||
| 33 | LOG_ERROR(Service_FS, "Create flag set but write flag not set"); | ||
| 34 | return ERROR_UNSUPPORTED_OPEN_FLAGS; | ||
| 35 | } | ||
| 36 | |||
| 37 | const auto full_path = path_parser.BuildHostPath(mount_point); | ||
| 38 | |||
| 39 | switch (path_parser.GetHostStatus(mount_point)) { | ||
| 40 | case PathParser::InvalidMountPoint: | ||
| 41 | LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); | ||
| 42 | return ERROR_FILE_NOT_FOUND; | ||
| 43 | case PathParser::PathNotFound: | ||
| 44 | LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); | ||
| 45 | return ERROR_PATH_NOT_FOUND; | ||
| 46 | case PathParser::FileInPath: | ||
| 47 | case PathParser::DirectoryFound: | ||
| 48 | LOG_ERROR(Service_FS, "Unexpected file or directory in %s", full_path.c_str()); | ||
| 49 | return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; | ||
| 50 | case PathParser::NotFound: | ||
| 51 | if (!mode.create_flag) { | ||
| 52 | LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", | ||
| 53 | full_path.c_str()); | ||
| 54 | return ERROR_FILE_NOT_FOUND; | ||
| 55 | } else { | ||
| 56 | // Create the file | ||
| 57 | FileUtil::CreateEmptyFile(full_path); | ||
| 58 | } | ||
| 59 | break; | ||
| 60 | case PathParser::FileFound: | ||
| 61 | break; // Expected 'success' case | ||
| 62 | } | ||
| 63 | |||
| 64 | FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb"); | ||
| 65 | if (!file.IsOpen()) { | ||
| 66 | LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str()); | ||
| 67 | return ERROR_FILE_NOT_FOUND; | ||
| 68 | } | ||
| 69 | |||
| 70 | auto disk_file = std::make_unique<DiskFile>(std::move(file), mode); | ||
| 71 | return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file)); | ||
| 72 | } | ||
| 73 | |||
| 74 | ResultCode SaveDataArchive::DeleteFile(const Path& path) const { | ||
| 75 | const PathParser path_parser(path); | ||
| 76 | |||
| 77 | if (!path_parser.IsValid()) { | ||
| 78 | LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); | ||
| 79 | return ERROR_INVALID_PATH; | ||
| 80 | } | ||
| 81 | |||
| 82 | const auto full_path = path_parser.BuildHostPath(mount_point); | ||
| 83 | |||
| 84 | switch (path_parser.GetHostStatus(mount_point)) { | ||
| 85 | case PathParser::InvalidMountPoint: | ||
| 86 | LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); | ||
| 87 | return ERROR_FILE_NOT_FOUND; | ||
| 88 | case PathParser::PathNotFound: | ||
| 89 | LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); | ||
| 90 | return ERROR_PATH_NOT_FOUND; | ||
| 91 | case PathParser::FileInPath: | ||
| 92 | case PathParser::DirectoryFound: | ||
| 93 | case PathParser::NotFound: | ||
| 94 | LOG_ERROR(Service_FS, "File not found %s", full_path.c_str()); | ||
| 95 | return ERROR_FILE_NOT_FOUND; | ||
| 96 | case PathParser::FileFound: | ||
| 97 | break; // Expected 'success' case | ||
| 98 | } | ||
| 99 | |||
| 100 | if (FileUtil::Delete(full_path)) { | ||
| 101 | return RESULT_SUCCESS; | ||
| 102 | } | ||
| 103 | |||
| 104 | LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str()); | ||
| 105 | return ERROR_FILE_NOT_FOUND; | ||
| 106 | } | ||
| 107 | |||
| 108 | ResultCode SaveDataArchive::RenameFile(const Path& src_path, const Path& dest_path) const { | ||
| 109 | const PathParser path_parser_src(src_path); | ||
| 110 | |||
| 111 | // TODO: Verify these return codes with HW | ||
| 112 | if (!path_parser_src.IsValid()) { | ||
| 113 | LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str()); | ||
| 114 | return ERROR_INVALID_PATH; | ||
| 115 | } | ||
| 116 | |||
| 117 | const PathParser path_parser_dest(dest_path); | ||
| 118 | |||
| 119 | if (!path_parser_dest.IsValid()) { | ||
| 120 | LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str()); | ||
| 121 | return ERROR_INVALID_PATH; | ||
| 122 | } | ||
| 123 | |||
| 124 | const auto src_path_full = path_parser_src.BuildHostPath(mount_point); | ||
| 125 | const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point); | ||
| 126 | |||
| 127 | if (FileUtil::Rename(src_path_full, dest_path_full)) { | ||
| 128 | return RESULT_SUCCESS; | ||
| 129 | } | ||
| 130 | |||
| 131 | // TODO(bunnei): Use correct error code | ||
| 132 | return ResultCode(-1); | ||
| 133 | } | ||
| 134 | |||
| 135 | template <typename T> | ||
| 136 | static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mount_point, | ||
| 137 | T deleter) { | ||
| 138 | const PathParser path_parser(path); | ||
| 139 | |||
| 140 | if (!path_parser.IsValid()) { | ||
| 141 | LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); | ||
| 142 | return ERROR_INVALID_PATH; | ||
| 143 | } | ||
| 144 | |||
| 145 | if (path_parser.IsRootDirectory()) | ||
| 146 | return ERROR_DIRECTORY_NOT_EMPTY; | ||
| 147 | |||
| 148 | const auto full_path = path_parser.BuildHostPath(mount_point); | ||
| 149 | |||
| 150 | switch (path_parser.GetHostStatus(mount_point)) { | ||
| 151 | case PathParser::InvalidMountPoint: | ||
| 152 | LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); | ||
| 153 | return ERROR_PATH_NOT_FOUND; | ||
| 154 | case PathParser::PathNotFound: | ||
| 155 | case PathParser::NotFound: | ||
| 156 | LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); | ||
| 157 | return ERROR_PATH_NOT_FOUND; | ||
| 158 | case PathParser::FileInPath: | ||
| 159 | case PathParser::FileFound: | ||
| 160 | LOG_ERROR(Service_FS, "Unexpected file or directory %s", full_path.c_str()); | ||
| 161 | return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; | ||
| 162 | case PathParser::DirectoryFound: | ||
| 163 | break; // Expected 'success' case | ||
| 164 | } | ||
| 165 | |||
| 166 | if (deleter(full_path)) { | ||
| 167 | return RESULT_SUCCESS; | ||
| 168 | } | ||
| 169 | |||
| 170 | LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str()); | ||
| 171 | return ERROR_DIRECTORY_NOT_EMPTY; | ||
| 172 | } | ||
| 173 | |||
| 174 | ResultCode SaveDataArchive::DeleteDirectory(const Path& path) const { | ||
| 175 | return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir); | ||
| 176 | } | ||
| 177 | |||
| 178 | ResultCode SaveDataArchive::DeleteDirectoryRecursively(const Path& path) const { | ||
| 179 | return DeleteDirectoryHelper( | ||
| 180 | path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); }); | ||
| 181 | } | ||
| 182 | |||
| 183 | ResultCode SaveDataArchive::CreateFile(const FileSys::Path& path, u64 size) const { | ||
| 184 | const PathParser path_parser(path); | ||
| 185 | |||
| 186 | if (!path_parser.IsValid()) { | ||
| 187 | LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); | ||
| 188 | return ERROR_INVALID_PATH; | ||
| 189 | } | ||
| 190 | |||
| 191 | const auto full_path = path_parser.BuildHostPath(mount_point); | ||
| 192 | |||
| 193 | switch (path_parser.GetHostStatus(mount_point)) { | ||
| 194 | case PathParser::InvalidMountPoint: | ||
| 195 | LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); | ||
| 196 | return ERROR_FILE_NOT_FOUND; | ||
| 197 | case PathParser::PathNotFound: | ||
| 198 | LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); | ||
| 199 | return ERROR_PATH_NOT_FOUND; | ||
| 200 | case PathParser::FileInPath: | ||
| 201 | LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str()); | ||
| 202 | return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; | ||
| 203 | case PathParser::DirectoryFound: | ||
| 204 | case PathParser::FileFound: | ||
| 205 | LOG_ERROR(Service_FS, "%s already exists", full_path.c_str()); | ||
| 206 | return ERROR_FILE_ALREADY_EXISTS; | ||
| 207 | case PathParser::NotFound: | ||
| 208 | break; // Expected 'success' case | ||
| 209 | } | ||
| 210 | |||
| 211 | if (size == 0) { | ||
| 212 | FileUtil::CreateEmptyFile(full_path); | ||
| 213 | return RESULT_SUCCESS; | ||
| 214 | } | ||
| 215 | |||
| 216 | FileUtil::IOFile file(full_path, "wb"); | ||
| 217 | // Creates a sparse file (or a normal file on filesystems without the concept of sparse files) | ||
| 218 | // We do this by seeking to the right size, then writing a single null byte. | ||
| 219 | if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) { | ||
| 220 | return RESULT_SUCCESS; | ||
| 221 | } | ||
| 222 | |||
| 223 | LOG_ERROR(Service_FS, "Too large file"); | ||
| 224 | |||
| 225 | // TODO(bunnei): Use correct error code | ||
| 226 | return ResultCode(-1); | ||
| 227 | } | ||
| 228 | |||
| 229 | ResultCode SaveDataArchive::CreateDirectory(const Path& path) const { | ||
| 230 | const PathParser path_parser(path); | ||
| 231 | |||
| 232 | if (!path_parser.IsValid()) { | ||
| 233 | LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); | ||
| 234 | return ERROR_INVALID_PATH; | ||
| 235 | } | ||
| 236 | |||
| 237 | const auto full_path = path_parser.BuildHostPath(mount_point); | ||
| 238 | |||
| 239 | switch (path_parser.GetHostStatus(mount_point)) { | ||
| 240 | case PathParser::InvalidMountPoint: | ||
| 241 | LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); | ||
| 242 | return ERROR_FILE_NOT_FOUND; | ||
| 243 | case PathParser::PathNotFound: | ||
| 244 | LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); | ||
| 245 | return ERROR_PATH_NOT_FOUND; | ||
| 246 | case PathParser::FileInPath: | ||
| 247 | LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str()); | ||
| 248 | return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; | ||
| 249 | case PathParser::DirectoryFound: | ||
| 250 | case PathParser::FileFound: | ||
| 251 | LOG_ERROR(Service_FS, "%s already exists", full_path.c_str()); | ||
| 252 | return ERROR_DIRECTORY_ALREADY_EXISTS; | ||
| 253 | case PathParser::NotFound: | ||
| 254 | break; // Expected 'success' case | ||
| 255 | } | ||
| 256 | |||
| 257 | if (FileUtil::CreateDir(mount_point + path.AsString())) { | ||
| 258 | return RESULT_SUCCESS; | ||
| 259 | } | ||
| 260 | |||
| 261 | LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", mount_point.c_str()); | ||
| 262 | |||
| 263 | // TODO(bunnei): Use correct error code | ||
| 264 | return ResultCode(-1); | ||
| 265 | } | ||
| 266 | |||
| 267 | ResultCode SaveDataArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { | ||
| 268 | const PathParser path_parser_src(src_path); | ||
| 269 | |||
| 270 | // TODO: Verify these return codes with HW | ||
| 271 | if (!path_parser_src.IsValid()) { | ||
| 272 | LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str()); | ||
| 273 | return ERROR_INVALID_PATH; | ||
| 274 | } | ||
| 275 | |||
| 276 | const PathParser path_parser_dest(dest_path); | ||
| 277 | |||
| 278 | if (!path_parser_dest.IsValid()) { | ||
| 279 | LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str()); | ||
| 280 | return ERROR_INVALID_PATH; | ||
| 281 | } | ||
| 282 | |||
| 283 | const auto src_path_full = path_parser_src.BuildHostPath(mount_point); | ||
| 284 | const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point); | ||
| 285 | |||
| 286 | if (FileUtil::Rename(src_path_full, dest_path_full)) { | ||
| 287 | return RESULT_SUCCESS; | ||
| 288 | } | ||
| 289 | |||
| 290 | // TODO(bunnei): Use correct error code | ||
| 291 | return ResultCode(-1); | ||
| 292 | } | ||
| 293 | |||
| 294 | ResultVal<std::unique_ptr<DirectoryBackend>> SaveDataArchive::OpenDirectory( | ||
| 295 | const Path& path) const { | ||
| 296 | const PathParser path_parser(path); | ||
| 297 | |||
| 298 | if (!path_parser.IsValid()) { | ||
| 299 | LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); | ||
| 300 | return ERROR_INVALID_PATH; | ||
| 301 | } | ||
| 302 | |||
| 303 | const auto full_path = path_parser.BuildHostPath(mount_point); | ||
| 304 | |||
| 305 | switch (path_parser.GetHostStatus(mount_point)) { | ||
| 306 | case PathParser::InvalidMountPoint: | ||
| 307 | LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); | ||
| 308 | return ERROR_FILE_NOT_FOUND; | ||
| 309 | case PathParser::PathNotFound: | ||
| 310 | case PathParser::NotFound: | ||
| 311 | LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); | ||
| 312 | return ERROR_PATH_NOT_FOUND; | ||
| 313 | case PathParser::FileInPath: | ||
| 314 | case PathParser::FileFound: | ||
| 315 | LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str()); | ||
| 316 | return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; | ||
| 317 | case PathParser::DirectoryFound: | ||
| 318 | break; // Expected 'success' case | ||
| 319 | } | ||
| 320 | |||
| 321 | auto directory = std::make_unique<DiskDirectory>(full_path); | ||
| 322 | return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory)); | ||
| 323 | } | ||
| 324 | |||
| 325 | u64 SaveDataArchive::GetFreeBytes() const { | ||
| 326 | // TODO: Stubbed to return 1GiB | ||
| 327 | return 1024 * 1024 * 1024; | ||
| 328 | } | ||
| 329 | |||
| 330 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/savedata_archive.h b/src/core/file_sys/savedata_archive.h deleted file mode 100644 index 176d35710..000000000 --- a/src/core/file_sys/savedata_archive.h +++ /dev/null | |||
| @@ -1,43 +0,0 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <string> | ||
| 8 | #include "core/file_sys/archive_backend.h" | ||
| 9 | #include "core/file_sys/directory_backend.h" | ||
| 10 | #include "core/file_sys/file_backend.h" | ||
| 11 | #include "core/hle/result.h" | ||
| 12 | |||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 14 | // FileSys namespace | ||
| 15 | |||
| 16 | namespace FileSys { | ||
| 17 | |||
| 18 | /// Archive backend for general save data archive type (SaveData and SystemSaveData) | ||
| 19 | class SaveDataArchive : public ArchiveBackend { | ||
| 20 | public: | ||
| 21 | explicit SaveDataArchive(const std::string& mount_point_) : mount_point(mount_point_) {} | ||
| 22 | |||
| 23 | std::string GetName() const override { | ||
| 24 | return "SaveDataArchive: " + mount_point; | ||
| 25 | } | ||
| 26 | |||
| 27 | ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, | ||
| 28 | const Mode& mode) const override; | ||
| 29 | ResultCode DeleteFile(const Path& path) const override; | ||
| 30 | ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override; | ||
| 31 | ResultCode DeleteDirectory(const Path& path) const override; | ||
| 32 | ResultCode DeleteDirectoryRecursively(const Path& path) const override; | ||
| 33 | ResultCode CreateFile(const Path& path, u64 size) const override; | ||
| 34 | ResultCode CreateDirectory(const Path& path) const override; | ||
| 35 | ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; | ||
| 36 | ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override; | ||
| 37 | u64 GetFreeBytes() const override; | ||
| 38 | |||
| 39 | protected: | ||
| 40 | std::string mount_point; | ||
| 41 | }; | ||
| 42 | |||
| 43 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/file_backend.h b/src/core/file_sys/storage.h index 5e7c2bab4..2a6811831 100644 --- a/src/core/file_sys/file_backend.h +++ b/src/core/file_sys/storage.h | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | 1 | // Copyright 2018 yuzu emulator team |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| @@ -8,15 +8,12 @@ | |||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/hle/result.h" | 9 | #include "core/hle/result.h" |
| 10 | 10 | ||
| 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 12 | // FileSys namespace | ||
| 13 | |||
| 14 | namespace FileSys { | 11 | namespace FileSys { |
| 15 | 12 | ||
| 16 | class FileBackend : NonCopyable { | 13 | class StorageBackend : NonCopyable { |
| 17 | public: | 14 | public: |
| 18 | FileBackend() {} | 15 | StorageBackend() {} |
| 19 | virtual ~FileBackend() {} | 16 | virtual ~StorageBackend() {} |
| 20 | 17 | ||
| 21 | /** | 18 | /** |
| 22 | * Read data from the file | 19 | * Read data from the file |
| @@ -39,10 +36,9 @@ public: | |||
| 39 | const u8* buffer) const = 0; | 36 | const u8* buffer) const = 0; |
| 40 | 37 | ||
| 41 | /** | 38 | /** |
| 42 | * Get the size of the file in bytes | 39 | * Flushes the file |
| 43 | * @return Size of the file in bytes | ||
| 44 | */ | 40 | */ |
| 45 | virtual u64 GetSize() const = 0; | 41 | virtual void Flush() const = 0; |
| 46 | 42 | ||
| 47 | /** | 43 | /** |
| 48 | * Set the size of the file in bytes | 44 | * Set the size of the file in bytes |
| @@ -52,15 +48,16 @@ public: | |||
| 52 | virtual bool SetSize(u64 size) const = 0; | 48 | virtual bool SetSize(u64 size) const = 0; |
| 53 | 49 | ||
| 54 | /** | 50 | /** |
| 55 | * Close the file | 51 | * Get the size of the file in bytes |
| 56 | * @return true if the file closed correctly | 52 | * @return Size of the file in bytes |
| 57 | */ | 53 | */ |
| 58 | virtual bool Close() const = 0; | 54 | virtual u64 GetSize() const = 0; |
| 59 | 55 | ||
| 60 | /** | 56 | /** |
| 61 | * Flushes the file | 57 | * Close the file |
| 58 | * @return true if the file closed correctly | ||
| 62 | */ | 59 | */ |
| 63 | virtual void Flush() const = 0; | 60 | virtual bool Close() const = 0; |
| 64 | }; | 61 | }; |
| 65 | 62 | ||
| 66 | } // namespace FileSys | 63 | } // namespace FileSys |
diff --git a/src/core/file_sys/title_metadata.cpp b/src/core/file_sys/title_metadata.cpp deleted file mode 100644 index e29ba6064..000000000 --- a/src/core/file_sys/title_metadata.cpp +++ /dev/null | |||
| @@ -1,163 +0,0 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cinttypes> | ||
| 6 | #include "common/alignment.h" | ||
| 7 | #include "common/file_util.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/file_sys/title_metadata.h" | ||
| 10 | #include "core/loader/loader.h" | ||
| 11 | |||
| 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 13 | // FileSys namespace | ||
| 14 | |||
| 15 | namespace FileSys { | ||
| 16 | |||
| 17 | static u32 GetSignatureSize(u32 signature_type) { | ||
| 18 | switch (signature_type) { | ||
| 19 | case Rsa4096Sha1: | ||
| 20 | case Rsa4096Sha256: | ||
| 21 | return 0x200; | ||
| 22 | |||
| 23 | case Rsa2048Sha1: | ||
| 24 | case Rsa2048Sha256: | ||
| 25 | return 0x100; | ||
| 26 | |||
| 27 | case EllipticSha1: | ||
| 28 | case EcdsaSha256: | ||
| 29 | return 0x3C; | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | Loader::ResultStatus TitleMetadata::Load() { | ||
| 34 | FileUtil::IOFile file(filepath, "rb"); | ||
| 35 | if (!file.IsOpen()) | ||
| 36 | return Loader::ResultStatus::Error; | ||
| 37 | |||
| 38 | if (!file.ReadBytes(&signature_type, sizeof(u32_be))) | ||
| 39 | return Loader::ResultStatus::Error; | ||
| 40 | |||
| 41 | // Signature lengths are variable, and the body follows the signature | ||
| 42 | u32 signature_size = GetSignatureSize(signature_type); | ||
| 43 | |||
| 44 | tmd_signature.resize(signature_size); | ||
| 45 | if (!file.ReadBytes(&tmd_signature[0], signature_size)) | ||
| 46 | return Loader::ResultStatus::Error; | ||
| 47 | |||
| 48 | // The TMD body start position is rounded to the nearest 0x40 after the signature | ||
| 49 | size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40); | ||
| 50 | file.Seek(body_start, SEEK_SET); | ||
| 51 | |||
| 52 | // Read our TMD body, then load the amount of ContentChunks specified | ||
| 53 | if (file.ReadBytes(&tmd_body, sizeof(TitleMetadata::Body)) != sizeof(TitleMetadata::Body)) | ||
| 54 | return Loader::ResultStatus::Error; | ||
| 55 | |||
| 56 | for (u16 i = 0; i < tmd_body.content_count; i++) { | ||
| 57 | ContentChunk chunk; | ||
| 58 | if (file.ReadBytes(&chunk, sizeof(ContentChunk)) == sizeof(ContentChunk)) { | ||
| 59 | tmd_chunks.push_back(chunk); | ||
| 60 | } else { | ||
| 61 | LOG_ERROR(Service_FS, "Malformed TMD %s, failed to load content chunk index %u!", | ||
| 62 | filepath.c_str(), i); | ||
| 63 | return Loader::ResultStatus::ErrorInvalidFormat; | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | return Loader::ResultStatus::Success; | ||
| 68 | } | ||
| 69 | |||
| 70 | Loader::ResultStatus TitleMetadata::Save() { | ||
| 71 | UNIMPLEMENTED(); | ||
| 72 | return Loader::ResultStatus::Success; | ||
| 73 | } | ||
| 74 | |||
| 75 | u64 TitleMetadata::GetTitleID() const { | ||
| 76 | return tmd_body.title_id; | ||
| 77 | } | ||
| 78 | |||
| 79 | u32 TitleMetadata::GetTitleType() const { | ||
| 80 | return tmd_body.title_type; | ||
| 81 | } | ||
| 82 | |||
| 83 | u16 TitleMetadata::GetTitleVersion() const { | ||
| 84 | return tmd_body.title_version; | ||
| 85 | } | ||
| 86 | |||
| 87 | u64 TitleMetadata::GetSystemVersion() const { | ||
| 88 | return tmd_body.system_version; | ||
| 89 | } | ||
| 90 | |||
| 91 | size_t TitleMetadata::GetContentCount() const { | ||
| 92 | return tmd_chunks.size(); | ||
| 93 | } | ||
| 94 | |||
| 95 | u32 TitleMetadata::GetBootContentID() const { | ||
| 96 | return tmd_chunks[TMDContentIndex::Main].id; | ||
| 97 | } | ||
| 98 | |||
| 99 | u32 TitleMetadata::GetManualContentID() const { | ||
| 100 | return tmd_chunks[TMDContentIndex::Manual].id; | ||
| 101 | } | ||
| 102 | |||
| 103 | u32 TitleMetadata::GetDLPContentID() const { | ||
| 104 | return tmd_chunks[TMDContentIndex::DLP].id; | ||
| 105 | } | ||
| 106 | |||
| 107 | void TitleMetadata::SetTitleID(u64 title_id) { | ||
| 108 | tmd_body.title_id = title_id; | ||
| 109 | } | ||
| 110 | |||
| 111 | void TitleMetadata::SetTitleType(u32 type) { | ||
| 112 | tmd_body.title_type = type; | ||
| 113 | } | ||
| 114 | |||
| 115 | void TitleMetadata::SetTitleVersion(u16 version) { | ||
| 116 | tmd_body.title_version = version; | ||
| 117 | } | ||
| 118 | |||
| 119 | void TitleMetadata::SetSystemVersion(u64 version) { | ||
| 120 | tmd_body.system_version = version; | ||
| 121 | } | ||
| 122 | |||
| 123 | void TitleMetadata::AddContentChunk(const ContentChunk& chunk) { | ||
| 124 | tmd_chunks.push_back(chunk); | ||
| 125 | } | ||
| 126 | |||
| 127 | void TitleMetadata::Print() const { | ||
| 128 | LOG_DEBUG(Service_FS, "%s - %u chunks", filepath.c_str(), | ||
| 129 | static_cast<u32>(tmd_body.content_count)); | ||
| 130 | |||
| 131 | // Content info describes ranges of content chunks | ||
| 132 | LOG_DEBUG(Service_FS, "Content info:"); | ||
| 133 | for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) { | ||
| 134 | if (tmd_body.contentinfo[i].command_count == 0) | ||
| 135 | break; | ||
| 136 | |||
| 137 | LOG_DEBUG(Service_FS, " Index %04X, Command Count %04X", | ||
| 138 | static_cast<u32>(tmd_body.contentinfo[i].index), | ||
| 139 | static_cast<u32>(tmd_body.contentinfo[i].command_count)); | ||
| 140 | } | ||
| 141 | |||
| 142 | // For each content info, print their content chunk range | ||
| 143 | for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) { | ||
| 144 | u16 index = static_cast<u16>(tmd_body.contentinfo[i].index); | ||
| 145 | u16 count = static_cast<u16>(tmd_body.contentinfo[i].command_count); | ||
| 146 | |||
| 147 | if (count == 0) | ||
| 148 | continue; | ||
| 149 | |||
| 150 | LOG_DEBUG(Service_FS, "Content chunks for content info index %zu:", i); | ||
| 151 | for (u16 j = index; j < index + count; j++) { | ||
| 152 | // Don't attempt to print content we don't have | ||
| 153 | if (j > tmd_body.content_count) | ||
| 154 | break; | ||
| 155 | |||
| 156 | const ContentChunk& chunk = tmd_chunks[j]; | ||
| 157 | LOG_DEBUG(Service_FS, " ID %08X, Index %04X, Type %04x, Size %016" PRIX64, | ||
| 158 | static_cast<u32>(chunk.id), static_cast<u32>(chunk.index), | ||
| 159 | static_cast<u32>(chunk.type), static_cast<u64>(chunk.size)); | ||
| 160 | } | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/title_metadata.h b/src/core/file_sys/title_metadata.h deleted file mode 100644 index a4c7d1089..000000000 --- a/src/core/file_sys/title_metadata.h +++ /dev/null | |||
| @@ -1,126 +0,0 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <string> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "common/swap.h" | ||
| 12 | |||
| 13 | namespace Loader { | ||
| 14 | enum class ResultStatus; | ||
| 15 | } | ||
| 16 | |||
| 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 18 | // FileSys namespace | ||
| 19 | |||
| 20 | namespace FileSys { | ||
| 21 | |||
| 22 | enum TMDSignatureType : u32 { | ||
| 23 | Rsa4096Sha1 = 0x10000, | ||
| 24 | Rsa2048Sha1 = 0x10001, | ||
| 25 | EllipticSha1 = 0x10002, | ||
| 26 | Rsa4096Sha256 = 0x10003, | ||
| 27 | Rsa2048Sha256 = 0x10004, | ||
| 28 | EcdsaSha256 = 0x10005 | ||
| 29 | }; | ||
| 30 | |||
| 31 | enum TMDContentTypeFlag : u16 { | ||
| 32 | Encrypted = 1 << 1, | ||
| 33 | Disc = 1 << 2, | ||
| 34 | CFM = 1 << 3, | ||
| 35 | Optional = 1 << 14, | ||
| 36 | Shared = 1 << 15 | ||
| 37 | }; | ||
| 38 | |||
| 39 | /** | ||
| 40 | * Helper which implements an interface to read and write Title Metadata (TMD) files. | ||
| 41 | * If a file path is provided and the file exists, it can be parsed and used, otherwise | ||
| 42 | * it must be created. The TMD file can then be interpreted, modified and/or saved. | ||
| 43 | */ | ||
| 44 | class TitleMetadata { | ||
| 45 | public: | ||
| 46 | struct ContentChunk { | ||
| 47 | u32_be id; | ||
| 48 | u16_be index; | ||
| 49 | u16_be type; | ||
| 50 | u64_be size; | ||
| 51 | std::array<u8, 0x20> hash; | ||
| 52 | }; | ||
| 53 | |||
| 54 | static_assert(sizeof(ContentChunk) == 0x30, "TMD ContentChunk structure size is wrong"); | ||
| 55 | |||
| 56 | struct ContentInfo { | ||
| 57 | u16_be index; | ||
| 58 | u16_be command_count; | ||
| 59 | std::array<u8, 0x20> hash; | ||
| 60 | }; | ||
| 61 | |||
| 62 | static_assert(sizeof(ContentInfo) == 0x24, "TMD ContentInfo structure size is wrong"); | ||
| 63 | |||
| 64 | #pragma pack(push, 1) | ||
| 65 | |||
| 66 | struct Body { | ||
| 67 | std::array<u8, 0x40> issuer; | ||
| 68 | u8 version; | ||
| 69 | u8 ca_crl_version; | ||
| 70 | u8 signer_crl_version; | ||
| 71 | u8 reserved; | ||
| 72 | u64_be system_version; | ||
| 73 | u64_be title_id; | ||
| 74 | u32_be title_type; | ||
| 75 | u16_be group_id; | ||
| 76 | u32_be savedata_size; | ||
| 77 | u32_be srl_private_savedata_size; | ||
| 78 | std::array<u8, 4> reserved_2; | ||
| 79 | u8 srl_flag; | ||
| 80 | std::array<u8, 0x31> reserved_3; | ||
| 81 | u32_be access_rights; | ||
| 82 | u16_be title_version; | ||
| 83 | u16_be content_count; | ||
| 84 | u16_be boot_content; | ||
| 85 | std::array<u8, 2> reserved_4; | ||
| 86 | std::array<u8, 0x20> contentinfo_hash; | ||
| 87 | std::array<ContentInfo, 64> contentinfo; | ||
| 88 | }; | ||
| 89 | |||
| 90 | static_assert(sizeof(Body) == 0x9C4, "TMD body structure size is wrong"); | ||
| 91 | |||
| 92 | #pragma pack(pop) | ||
| 93 | |||
| 94 | explicit TitleMetadata(std::string& path) : filepath(std::move(path)) {} | ||
| 95 | Loader::ResultStatus Load(); | ||
| 96 | Loader::ResultStatus Save(); | ||
| 97 | |||
| 98 | u64 GetTitleID() const; | ||
| 99 | u32 GetTitleType() const; | ||
| 100 | u16 GetTitleVersion() const; | ||
| 101 | u64 GetSystemVersion() const; | ||
| 102 | size_t GetContentCount() const; | ||
| 103 | u32 GetBootContentID() const; | ||
| 104 | u32 GetManualContentID() const; | ||
| 105 | u32 GetDLPContentID() const; | ||
| 106 | |||
| 107 | void SetTitleID(u64 title_id); | ||
| 108 | void SetTitleType(u32 type); | ||
| 109 | void SetTitleVersion(u16 version); | ||
| 110 | void SetSystemVersion(u64 version); | ||
| 111 | void AddContentChunk(const ContentChunk& chunk); | ||
| 112 | |||
| 113 | void Print() const; | ||
| 114 | |||
| 115 | private: | ||
| 116 | enum TMDContentIndex { Main = 0, Manual = 1, DLP = 2 }; | ||
| 117 | |||
| 118 | Body tmd_body; | ||
| 119 | u32_be signature_type; | ||
| 120 | std::vector<u8> tmd_signature; | ||
| 121 | std::vector<ContentChunk> tmd_chunks; | ||
| 122 | |||
| 123 | std::string filepath; | ||
| 124 | }; | ||
| 125 | |||
| 126 | } // namespace FileSys | ||
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 4c9b0de28..a27cfbc2d 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h | |||
| @@ -305,6 +305,11 @@ inline u64 RequestParser::Pop() { | |||
| 305 | } | 305 | } |
| 306 | 306 | ||
| 307 | template <> | 307 | template <> |
| 308 | inline s64 RequestParser::Pop() { | ||
| 309 | return static_cast<s64>(Pop<u64>()); | ||
| 310 | } | ||
| 311 | |||
| 312 | template <> | ||
| 308 | inline bool RequestParser::Pop() { | 313 | inline bool RequestParser::Pop() { |
| 309 | return Pop<u8>() != 0; | 314 | return Pop<u8>() != 0; |
| 310 | } | 315 | } |
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 10ddc4feb..656e1b4a7 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -19,6 +19,8 @@ | |||
| 19 | enum class ErrorDescription : u32 { | 19 | enum class ErrorDescription : u32 { |
| 20 | Success = 0, | 20 | Success = 0, |
| 21 | RemoteProcessDead = 301, | 21 | RemoteProcessDead = 301, |
| 22 | InvalidOffset = 6061, | ||
| 23 | InvalidLength = 6062, | ||
| 22 | }; | 24 | }; |
| 23 | 25 | ||
| 24 | /** | 26 | /** |
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp new file mode 100644 index 000000000..4b47548fd --- /dev/null +++ b/src/core/hle/service/filesystem/filesystem.cpp | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <boost/container/flat_map.hpp> | ||
| 6 | #include "core/file_sys/filesystem.h" | ||
| 7 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 8 | #include "core/hle/service/filesystem/fsp_srv.h" | ||
| 9 | |||
| 10 | namespace Service { | ||
| 11 | namespace FileSystem { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Map of registered file systems, identified by type. Once an file system is registered here, it | ||
| 15 | * is never removed until UnregisterFileSystems is called. | ||
| 16 | */ | ||
| 17 | static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map; | ||
| 18 | |||
| 19 | ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type) { | ||
| 20 | auto result = filesystem_map.emplace(type, std::move(factory)); | ||
| 21 | |||
| 22 | bool inserted = result.second; | ||
| 23 | ASSERT_MSG(inserted, "Tried to register more than one system with same id code"); | ||
| 24 | |||
| 25 | auto& filesystem = result.first->second; | ||
| 26 | LOG_DEBUG(Service_FS, "Registered file system %s with id code 0x%08X", | ||
| 27 | filesystem->GetName().c_str(), static_cast<u32>(type)); | ||
| 28 | return RESULT_SUCCESS; | ||
| 29 | } | ||
| 30 | |||
| 31 | ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, | ||
| 32 | FileSys::Path& path) { | ||
| 33 | LOG_TRACE(Service_FS, "Opening FileSystem with type=%d", type); | ||
| 34 | |||
| 35 | auto itr = filesystem_map.find(type); | ||
| 36 | if (itr == filesystem_map.end()) { | ||
| 37 | // TODO(bunnei): Find a better error code for this | ||
| 38 | return ResultCode(-1); | ||
| 39 | } | ||
| 40 | |||
| 41 | return itr->second->Open(path); | ||
| 42 | } | ||
| 43 | |||
| 44 | void UnregisterFileSystems() { | ||
| 45 | filesystem_map.clear(); | ||
| 46 | } | ||
| 47 | |||
| 48 | void InstallInterfaces(SM::ServiceManager& service_manager) { | ||
| 49 | UnregisterFileSystems(); | ||
| 50 | std::make_shared<FSP_SRV>()->InstallAsService(service_manager); | ||
| 51 | } | ||
| 52 | |||
| 53 | } // namespace FileSystem | ||
| 54 | } // namespace Service | ||
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h new file mode 100644 index 000000000..a674c9493 --- /dev/null +++ b/src/core/hle/service/filesystem/filesystem.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "core/hle/result.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | class FileSystemBackend; | ||
| 13 | class FileSystemFactory; | ||
| 14 | class Path; | ||
| 15 | } // namespace FileSys | ||
| 16 | |||
| 17 | namespace Service { | ||
| 18 | |||
| 19 | namespace SM { | ||
| 20 | class ServiceManager; | ||
| 21 | } // namespace SM | ||
| 22 | |||
| 23 | namespace FileSystem { | ||
| 24 | |||
| 25 | /// Supported FileSystem types | ||
| 26 | enum class Type { | ||
| 27 | RomFS = 1, | ||
| 28 | }; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * Registers a FileSystem, instances of which can later be opened using its IdCode. | ||
| 32 | * @param factory FileSystem backend interface to use | ||
| 33 | * @param type Type used to access this type of FileSystem | ||
| 34 | */ | ||
| 35 | ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type); | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Opens a file system | ||
| 39 | * @param type Type of the file system to open | ||
| 40 | * @param path Path to the file system, used with Binary paths | ||
| 41 | * @return FileSys::FileSystemBackend interface to the file system | ||
| 42 | */ | ||
| 43 | ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, | ||
| 44 | FileSys::Path& path); | ||
| 45 | |||
| 46 | /// Registers all Filesystem services with the specified service manager. | ||
| 47 | void InstallInterfaces(SM::ServiceManager& service_manager); | ||
| 48 | |||
| 49 | } // namespace FileSystem | ||
| 50 | } // namespace Service | ||
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp new file mode 100644 index 000000000..ef1915e5a --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/core.h" | ||
| 7 | #include "core/file_sys/filesystem.h" | ||
| 8 | #include "core/file_sys/storage.h" | ||
| 9 | #include "core/hle/ipc_helpers.h" | ||
| 10 | #include "core/hle/kernel/client_port.h" | ||
| 11 | #include "core/hle/kernel/client_session.h" | ||
| 12 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 13 | #include "core/hle/service/filesystem/fsp_srv.h" | ||
| 14 | |||
| 15 | namespace Service { | ||
| 16 | namespace FileSystem { | ||
| 17 | |||
| 18 | class IStorage final : public ServiceFramework<IStorage> { | ||
| 19 | public: | ||
| 20 | IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend) | ||
| 21 | : ServiceFramework("IStorage"), backend(std::move(backend)) { | ||
| 22 | static const FunctionInfo functions[] = { | ||
| 23 | {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, | ||
| 24 | {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, | ||
| 25 | }; | ||
| 26 | RegisterHandlers(functions); | ||
| 27 | } | ||
| 28 | |||
| 29 | private: | ||
| 30 | std::unique_ptr<FileSys::StorageBackend> backend; | ||
| 31 | |||
| 32 | void Read(Kernel::HLERequestContext& ctx) { | ||
| 33 | IPC::RequestParser rp{ctx}; | ||
| 34 | const s64 offset = rp.Pop<s64>(); | ||
| 35 | const s64 length = rp.Pop<s64>(); | ||
| 36 | const auto& descriptor = ctx.BufferDescriptorB()[0]; | ||
| 37 | |||
| 38 | LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length); | ||
| 39 | |||
| 40 | // Error checking | ||
| 41 | ASSERT_MSG(length == descriptor.Size(), "unexpected size difference"); | ||
| 42 | if (length < 0) { | ||
| 43 | IPC::RequestBuilder rb{ctx, 2}; | ||
| 44 | rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); | ||
| 45 | return; | ||
| 46 | } | ||
| 47 | if (offset < 0) { | ||
| 48 | IPC::RequestBuilder rb{ctx, 2}; | ||
| 49 | rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); | ||
| 50 | return; | ||
| 51 | } | ||
| 52 | |||
| 53 | // Read the data from the Storage backend | ||
| 54 | std::vector<u8> output(length); | ||
| 55 | ResultVal<size_t> res = backend->Read(offset, length, output.data()); | ||
| 56 | if (res.Failed()) { | ||
| 57 | IPC::RequestBuilder rb{ctx, 2}; | ||
| 58 | rb.Push(res.Code()); | ||
| 59 | return; | ||
| 60 | } | ||
| 61 | |||
| 62 | // Write the data to memory | ||
| 63 | Memory::WriteBlock(descriptor.Address(), output.data(), descriptor.Size()); | ||
| 64 | |||
| 65 | IPC::RequestBuilder rb{ctx, 2}; | ||
| 66 | rb.Push(RESULT_SUCCESS); | ||
| 67 | } | ||
| 68 | }; | ||
| 69 | |||
| 70 | FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | ||
| 71 | static const FunctionInfo functions[] = { | ||
| 72 | {1, &FSP_SRV::Initalize, "Initalize"}, | ||
| 73 | {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, | ||
| 74 | {203, &FSP_SRV::OpenRomStorage, "OpenRomStorage"}, | ||
| 75 | {1005, &FSP_SRV::GetGlobalAccessLogMode, "GetGlobalAccessLogMode"}, | ||
| 76 | }; | ||
| 77 | RegisterHandlers(functions); | ||
| 78 | } | ||
| 79 | |||
| 80 | void FSP_SRV::TryLoadRomFS() { | ||
| 81 | if (romfs) { | ||
| 82 | return; | ||
| 83 | } | ||
| 84 | FileSys::Path unused; | ||
| 85 | auto res = OpenFileSystem(Type::RomFS, unused); | ||
| 86 | if (res.Succeeded()) { | ||
| 87 | romfs = std::move(res.Unwrap()); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | void FSP_SRV::Initalize(Kernel::HLERequestContext& ctx) { | ||
| 92 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 93 | |||
| 94 | IPC::RequestBuilder rb{ctx, 2}; | ||
| 95 | rb.Push(RESULT_SUCCESS); | ||
| 96 | } | ||
| 97 | |||
| 98 | void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | ||
| 99 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 100 | |||
| 101 | IPC::RequestBuilder rb{ctx, 4}; | ||
| 102 | rb.Push(RESULT_SUCCESS); | ||
| 103 | rb.Push<u32>(5); | ||
| 104 | } | ||
| 105 | |||
| 106 | void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { | ||
| 107 | LOG_DEBUG(Service_FS, "called"); | ||
| 108 | |||
| 109 | TryLoadRomFS(); | ||
| 110 | if (!romfs) { | ||
| 111 | // TODO (bunnei): Find the right error code to use here | ||
| 112 | LOG_CRITICAL(Service_FS, "no file system interface available!"); | ||
| 113 | IPC::RequestBuilder rb{ctx, 2}; | ||
| 114 | rb.Push(ResultCode(-1)); | ||
| 115 | return; | ||
| 116 | } | ||
| 117 | |||
| 118 | // Attempt to open a StorageBackend interface to the RomFS | ||
| 119 | auto storage = romfs->OpenFile({}, {}); | ||
| 120 | if (storage.Failed()) { | ||
| 121 | LOG_CRITICAL(Service_FS, "no storage interface available!"); | ||
| 122 | IPC::RequestBuilder rb{ctx, 2}; | ||
| 123 | rb.Push(storage.Code()); | ||
| 124 | return; | ||
| 125 | } | ||
| 126 | |||
| 127 | IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; | ||
| 128 | rb.Push(RESULT_SUCCESS); | ||
| 129 | rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap())); | ||
| 130 | } | ||
| 131 | |||
| 132 | void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { | ||
| 133 | LOG_WARNING(Service_FS, "(STUBBED) called, using OpenDataStorageByCurrentProcess"); | ||
| 134 | OpenDataStorageByCurrentProcess(ctx); | ||
| 135 | } | ||
| 136 | |||
| 137 | } // namespace FileSystem | ||
| 138 | } // namespace Service | ||
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h new file mode 100644 index 000000000..15be8edc1 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_srv.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include "core/hle/service/service.h" | ||
| 9 | |||
| 10 | namespace FileSys { | ||
| 11 | class FileSystemBackend; | ||
| 12 | } | ||
| 13 | |||
| 14 | namespace Service { | ||
| 15 | namespace FileSystem { | ||
| 16 | |||
| 17 | class FSP_SRV final : public ServiceFramework<FSP_SRV> { | ||
| 18 | public: | ||
| 19 | explicit FSP_SRV(); | ||
| 20 | ~FSP_SRV() = default; | ||
| 21 | |||
| 22 | private: | ||
| 23 | void TryLoadRomFS(); | ||
| 24 | |||
| 25 | void Initalize(Kernel::HLERequestContext& ctx); | ||
| 26 | void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); | ||
| 27 | void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); | ||
| 28 | void OpenRomStorage(Kernel::HLERequestContext& ctx); | ||
| 29 | |||
| 30 | std::unique_ptr<FileSys::FileSystemBackend> romfs; | ||
| 31 | }; | ||
| 32 | |||
| 33 | } // namespace FileSystem | ||
| 34 | } // namespace Service | ||
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 19213a2f4..3f5ce56c6 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "core/hle/service/aoc/aoc_u.h" | 19 | #include "core/hle/service/aoc/aoc_u.h" |
| 20 | #include "core/hle/service/apm/apm.h" | 20 | #include "core/hle/service/apm/apm.h" |
| 21 | #include "core/hle/service/audio/audio.h" | 21 | #include "core/hle/service/audio/audio.h" |
| 22 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 22 | #include "core/hle/service/hid/hid.h" | 23 | #include "core/hle/service/hid/hid.h" |
| 23 | #include "core/hle/service/lm/lm.h" | 24 | #include "core/hle/service/lm/lm.h" |
| 24 | #include "core/hle/service/nvdrv/nvdrv.h" | 25 | #include "core/hle/service/nvdrv/nvdrv.h" |
| @@ -172,6 +173,7 @@ void Init() { | |||
| 172 | AOC::InstallInterfaces(*SM::g_service_manager); | 173 | AOC::InstallInterfaces(*SM::g_service_manager); |
| 173 | APM::InstallInterfaces(*SM::g_service_manager); | 174 | APM::InstallInterfaces(*SM::g_service_manager); |
| 174 | Audio::InstallInterfaces(*SM::g_service_manager); | 175 | Audio::InstallInterfaces(*SM::g_service_manager); |
| 176 | FileSystem::InstallInterfaces(*SM::g_service_manager); | ||
| 175 | HID::InstallInterfaces(*SM::g_service_manager); | 177 | HID::InstallInterfaces(*SM::g_service_manager); |
| 176 | LM::InstallInterfaces(*SM::g_service_manager); | 178 | LM::InstallInterfaces(*SM::g_service_manager); |
| 177 | Nvidia::InstallInterfaces(*SM::g_service_manager); | 179 | Nvidia::InstallInterfaces(*SM::g_service_manager); |
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 4bee5fb86..37030683b 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -7,14 +7,44 @@ | |||
| 7 | #include "common/file_util.h" | 7 | #include "common/file_util.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "common/string_util.h" | 9 | #include "common/string_util.h" |
| 10 | #include "core/file_sys/romfs_factory.h" | ||
| 10 | #include "core/hle/kernel/process.h" | 11 | #include "core/hle/kernel/process.h" |
| 11 | #include "core/hle/kernel/resource_limit.h" | 12 | #include "core/hle/kernel/resource_limit.h" |
| 13 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 12 | #include "core/loader/deconstructed_rom_directory.h" | 14 | #include "core/loader/deconstructed_rom_directory.h" |
| 13 | #include "core/loader/nso.h" | 15 | #include "core/loader/nso.h" |
| 14 | #include "core/memory.h" | 16 | #include "core/memory.h" |
| 15 | 17 | ||
| 16 | namespace Loader { | 18 | namespace Loader { |
| 17 | 19 | ||
| 20 | static std::string FindRomFS(const std::string& directory) { | ||
| 21 | std::string filepath_romfs; | ||
| 22 | const auto callback = [&filepath_romfs](unsigned*, const std::string& directory, | ||
| 23 | const std::string& virtual_name) -> bool { | ||
| 24 | const std::string physical_name = directory + virtual_name; | ||
| 25 | if (FileUtil::IsDirectory(physical_name)) { | ||
| 26 | // Skip directories | ||
| 27 | return true; | ||
| 28 | } | ||
| 29 | |||
| 30 | // Verify extension | ||
| 31 | const std::string extension = physical_name.substr(physical_name.find_last_of(".") + 1); | ||
| 32 | if (Common::ToLower(extension) != "istorage") { | ||
| 33 | return true; | ||
| 34 | } | ||
| 35 | |||
| 36 | // Found it - we are done | ||
| 37 | filepath_romfs = std::move(physical_name); | ||
| 38 | return false; | ||
| 39 | }; | ||
| 40 | |||
| 41 | // Search the specified directory recursively, looking for the first .istorage file, which will | ||
| 42 | // be used for the RomFS | ||
| 43 | FileUtil::ForeachDirectoryEntry(nullptr, directory, callback); | ||
| 44 | |||
| 45 | return filepath_romfs; | ||
| 46 | } | ||
| 47 | |||
| 18 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file, | 48 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file, |
| 19 | std::string filepath) | 49 | std::string filepath) |
| 20 | : AppLoader(std::move(file)), filepath(std::move(filepath)) {} | 50 | : AppLoader(std::move(file)), filepath(std::move(filepath)) {} |
| @@ -79,10 +109,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 79 | 109 | ||
| 80 | // Load NSO modules | 110 | // Load NSO modules |
| 81 | VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; | 111 | VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; |
| 112 | const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP; | ||
| 82 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", | 113 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", |
| 83 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { | 114 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { |
| 84 | const std::string path = | 115 | const std::string path = directory + DIR_SEP + module; |
| 85 | filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP + module; | ||
| 86 | const VAddr load_addr = next_load_addr; | 116 | const VAddr load_addr = next_load_addr; |
| 87 | next_load_addr = AppLoader_NSO::LoadModule(path, load_addr); | 117 | next_load_addr = AppLoader_NSO::LoadModule(path, load_addr); |
| 88 | if (next_load_addr) { | 118 | if (next_load_addr) { |
| @@ -98,8 +128,43 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 98 | Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); | 128 | Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); |
| 99 | process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE); | 129 | process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE); |
| 100 | 130 | ||
| 131 | // Find the RomFS by searching for a ".istorage" file in this directory | ||
| 132 | filepath_romfs = FindRomFS(directory); | ||
| 133 | |||
| 134 | // Register the RomFS if a ".istorage" file was found | ||
| 135 | if (!filepath_romfs.empty()) { | ||
| 136 | Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this), | ||
| 137 | Service::FileSystem::Type::RomFS); | ||
| 138 | } | ||
| 139 | |||
| 101 | is_loaded = true; | 140 | is_loaded = true; |
| 102 | return ResultStatus::Success; | 141 | return ResultStatus::Success; |
| 103 | } | 142 | } |
| 104 | 143 | ||
| 144 | ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS( | ||
| 145 | std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { | ||
| 146 | |||
| 147 | if (filepath_romfs.empty()) { | ||
| 148 | LOG_DEBUG(Loader, "No RomFS available"); | ||
| 149 | return ResultStatus::ErrorNotUsed; | ||
| 150 | } | ||
| 151 | |||
| 152 | // We reopen the file, to allow its position to be independent | ||
| 153 | romfs_file = std::make_shared<FileUtil::IOFile>(filepath_romfs, "rb"); | ||
| 154 | if (!romfs_file->IsOpen()) { | ||
| 155 | return ResultStatus::Error; | ||
| 156 | } | ||
| 157 | |||
| 158 | offset = 0; | ||
| 159 | size = romfs_file->GetSize(); | ||
| 160 | |||
| 161 | LOG_DEBUG(Loader, "RomFS offset: 0x%08X", offset); | ||
| 162 | LOG_DEBUG(Loader, "RomFS size: 0x%08X", size); | ||
| 163 | |||
| 164 | // Reset read pointer | ||
| 165 | file.Seek(0, SEEK_SET); | ||
| 166 | |||
| 167 | return ResultStatus::Success; | ||
| 168 | } | ||
| 169 | |||
| 105 | } // namespace Loader | 170 | } // namespace Loader |
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index 162541d54..26493de5e 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h | |||
| @@ -35,7 +35,11 @@ public: | |||
| 35 | 35 | ||
| 36 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 36 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |
| 37 | 37 | ||
| 38 | ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, | ||
| 39 | u64& size) override; | ||
| 40 | |||
| 38 | private: | 41 | private: |
| 42 | std::string filepath_romfs; | ||
| 39 | std::string filepath; | 43 | std::string filepath; |
| 40 | }; | 44 | }; |
| 41 | 45 | ||