summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt24
-rw-r--r--src/core/file_sys/directory.h (renamed from src/core/file_sys/directory_backend.h)2
-rw-r--r--src/core/file_sys/disk_archive.cpp99
-rw-r--r--src/core/file_sys/disk_archive.h68
-rw-r--r--src/core/file_sys/filesystem.cpp (renamed from src/core/file_sys/archive_backend.cpp)4
-rw-r--r--src/core/file_sys/filesystem.h (renamed from src/core/file_sys/archive_backend.h)58
-rw-r--r--src/core/file_sys/path_parser.h2
-rw-r--r--src/core/file_sys/romfs_factory.cpp38
-rw-r--r--src/core/file_sys/romfs_factory.h35
-rw-r--r--src/core/file_sys/romfs_filesystem.cpp (renamed from src/core/file_sys/ivfc_archive.cpp)72
-rw-r--r--src/core/file_sys/romfs_filesystem.h (renamed from src/core/file_sys/ivfc_archive.h)33
-rw-r--r--src/core/file_sys/savedata_archive.cpp330
-rw-r--r--src/core/file_sys/savedata_archive.h43
-rw-r--r--src/core/file_sys/storage.h (renamed from src/core/file_sys/file_backend.h)27
-rw-r--r--src/core/file_sys/title_metadata.cpp163
-rw-r--r--src/core/file_sys/title_metadata.h126
-rw-r--r--src/core/hle/ipc_helpers.h5
-rw-r--r--src/core/hle/result.h2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp54
-rw-r--r--src/core/hle/service/filesystem/filesystem.h50
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp138
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h34
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp69
-rw-r--r--src/core/loader/deconstructed_rom_directory.h4
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
17namespace FileSys {
18
19ResultVal<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
27ResultVal<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
39u64 DiskFile::GetSize() const {
40 return file->GetSize();
41}
42
43bool DiskFile::SetSize(const u64 size) const {
44 file->Resize(size);
45 file->Flush();
46 return true;
47}
48
49bool DiskFile::Close() const {
50 return file->Close();
51}
52
53////////////////////////////////////////////////////////////////////////////////////////////////////
54
55DiskDirectory::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
62u32 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
21namespace FileSys {
22
23class DiskFile : public FileBackend {
24public:
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
40protected:
41 Mode mode;
42 std::unique_ptr<FileUtil::IOFile> file;
43};
44
45class DiskDirectory : public DirectoryBackend {
46public:
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
59protected:
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
13namespace FileSys { 13namespace 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
16namespace FileSys { 16namespace FileSys {
17 17
18class FileBackend; 18class StorageBackend;
19class DirectoryBackend; 19class DirectoryBackend;
20 20
21// Path string type 21// Path string type
@@ -71,9 +71,9 @@ struct ArchiveFormatInfo {
71}; 71};
72static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD"); 72static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD");
73 73
74class ArchiveBackend : NonCopyable { 74class FileSystemBackend : NonCopyable {
75public: 75public:
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
158class ArchiveFactory : NonCopyable { 158class FileSystemFactory : NonCopyable {
159public: 159public:
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
11namespace FileSys { 11namespace 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
12namespace FileSys {
13
14RomFS_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
21ResultVal<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
26ResultCode 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
32ResultVal<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
15namespace FileSys {
16
17/// File system interface to the RomFS archive
18class RomFS_Factory final : public FileSystemFactory {
19public:
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
29private:
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
14namespace FileSys { 11namespace FileSys {
15 12
16std::string IVFCArchive::GetName() const { 13std::string RomFS_FileSystem::GetName() const {
17 return "IVFC"; 14 return "RomFS";
18} 15}
19 16
20ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, 17ResultVal<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
26ResultCode IVFCArchive::DeleteFile(const Path& path) const { 23ResultCode 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
33ResultCode IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { 30ResultCode 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
40ResultCode IVFCArchive::DeleteDirectory(const Path& path) const { 37ResultCode 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
47ResultCode IVFCArchive::DeleteDirectoryRecursively(const Path& path) const { 44ResultCode 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
54ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const { 51ResultCode 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
61ResultCode IVFCArchive::CreateDirectory(const Path& path) const { 58ResultCode 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
68ResultCode IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { 65ResultCode 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
75ResultVal<std::unique_ptr<DirectoryBackend>> IVFCArchive::OpenDirectory(const Path& path) const { 72ResultVal<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
79u64 IVFCArchive::GetFreeBytes() const { 77u64 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//////////////////////////////////////////////////////////////////////////////////////////////////// 82ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
85
86ResultVal<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
94ResultVal<size_t> IVFCFile::Write(const u64 offset, const size_t length, const bool flush, 90ResultVal<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
101u64 IVFCFile::GetSize() const { 97u64 RomFS_Storage::GetSize() const {
102 return data_size; 98 return data_size;
103} 99}
104 100
105bool IVFCFile::SetSize(const u64 size) const { 101bool 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
21namespace FileSys { 18namespace 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 */
28class IVFCArchive : public ArchiveBackend { 25class RomFS_FileSystem : public FileSystemBackend {
29public: 26public:
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
47protected: 44protected:
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
53class IVFCFile : public FileBackend { 50class RomFS_Storage : public StorageBackend {
54public: 51public:
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
73class IVFCDirectory : public DirectoryBackend { 70class ROMFSDirectory : public DirectoryBackend {
74public: 71public:
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
14namespace FileSys {
15
16ResultVal<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
74ResultCode 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
108ResultCode 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
135template <typename T>
136static 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
174ResultCode SaveDataArchive::DeleteDirectory(const Path& path) const {
175 return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
176}
177
178ResultCode SaveDataArchive::DeleteDirectoryRecursively(const Path& path) const {
179 return DeleteDirectoryHelper(
180 path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
181}
182
183ResultCode 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
229ResultCode 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
267ResultCode 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
294ResultVal<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
325u64 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
16namespace FileSys {
17
18/// Archive backend for general save data archive type (SaveData and SystemSaveData)
19class SaveDataArchive : public ArchiveBackend {
20public:
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
39protected:
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
14namespace FileSys { 11namespace FileSys {
15 12
16class FileBackend : NonCopyable { 13class StorageBackend : NonCopyable {
17public: 14public:
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
15namespace FileSys {
16
17static 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
33Loader::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
70Loader::ResultStatus TitleMetadata::Save() {
71 UNIMPLEMENTED();
72 return Loader::ResultStatus::Success;
73}
74
75u64 TitleMetadata::GetTitleID() const {
76 return tmd_body.title_id;
77}
78
79u32 TitleMetadata::GetTitleType() const {
80 return tmd_body.title_type;
81}
82
83u16 TitleMetadata::GetTitleVersion() const {
84 return tmd_body.title_version;
85}
86
87u64 TitleMetadata::GetSystemVersion() const {
88 return tmd_body.system_version;
89}
90
91size_t TitleMetadata::GetContentCount() const {
92 return tmd_chunks.size();
93}
94
95u32 TitleMetadata::GetBootContentID() const {
96 return tmd_chunks[TMDContentIndex::Main].id;
97}
98
99u32 TitleMetadata::GetManualContentID() const {
100 return tmd_chunks[TMDContentIndex::Manual].id;
101}
102
103u32 TitleMetadata::GetDLPContentID() const {
104 return tmd_chunks[TMDContentIndex::DLP].id;
105}
106
107void TitleMetadata::SetTitleID(u64 title_id) {
108 tmd_body.title_id = title_id;
109}
110
111void TitleMetadata::SetTitleType(u32 type) {
112 tmd_body.title_type = type;
113}
114
115void TitleMetadata::SetTitleVersion(u16 version) {
116 tmd_body.title_version = version;
117}
118
119void TitleMetadata::SetSystemVersion(u64 version) {
120 tmd_body.system_version = version;
121}
122
123void TitleMetadata::AddContentChunk(const ContentChunk& chunk) {
124 tmd_chunks.push_back(chunk);
125}
126
127void 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
13namespace Loader {
14enum class ResultStatus;
15}
16
17////////////////////////////////////////////////////////////////////////////////////////////////////
18// FileSys namespace
19
20namespace FileSys {
21
22enum TMDSignatureType : u32 {
23 Rsa4096Sha1 = 0x10000,
24 Rsa2048Sha1 = 0x10001,
25 EllipticSha1 = 0x10002,
26 Rsa4096Sha256 = 0x10003,
27 Rsa2048Sha256 = 0x10004,
28 EcdsaSha256 = 0x10005
29};
30
31enum 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 */
44class TitleMetadata {
45public:
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
115private:
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
307template <> 307template <>
308inline s64 RequestParser::Pop() {
309 return static_cast<s64>(Pop<u64>());
310}
311
312template <>
308inline bool RequestParser::Pop() { 313inline 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 @@
19enum class ErrorDescription : u32 { 19enum 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
10namespace Service {
11namespace 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 */
17static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map;
18
19ResultCode 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
31ResultVal<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
44void UnregisterFileSystems() {
45 filesystem_map.clear();
46}
47
48void 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
11namespace FileSys {
12class FileSystemBackend;
13class FileSystemFactory;
14class Path;
15} // namespace FileSys
16
17namespace Service {
18
19namespace SM {
20class ServiceManager;
21} // namespace SM
22
23namespace FileSystem {
24
25/// Supported FileSystem types
26enum 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 */
35ResultCode 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 */
43ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type,
44 FileSys::Path& path);
45
46/// Registers all Filesystem services with the specified service manager.
47void 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
15namespace Service {
16namespace FileSystem {
17
18class IStorage final : public ServiceFramework<IStorage> {
19public:
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
29private:
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
70FSP_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
80void 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
91void 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
98void 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
106void 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
132void 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
10namespace FileSys {
11class FileSystemBackend;
12}
13
14namespace Service {
15namespace FileSystem {
16
17class FSP_SRV final : public ServiceFramework<FSP_SRV> {
18public:
19 explicit FSP_SRV();
20 ~FSP_SRV() = default;
21
22private:
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
16namespace Loader { 18namespace Loader {
17 19
20static 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
18AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file, 48AppLoader_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
144ResultStatus 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
38private: 41private:
42 std::string filepath_romfs;
39 std::string filepath; 43 std::string filepath;
40}; 44};
41 45