summaryrefslogtreecommitdiff
path: root/src/core/file_sys
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/file_sys')
-rw-r--r--src/core/file_sys/archive_extsavedata.cpp245
-rw-r--r--src/core/file_sys/archive_extsavedata.h89
-rw-r--r--src/core/file_sys/archive_ncch.cpp114
-rw-r--r--src/core/file_sys/archive_ncch.h34
-rw-r--r--src/core/file_sys/archive_other_savedata.cpp145
-rw-r--r--src/core/file_sys/archive_other_savedata.h52
-rw-r--r--src/core/file_sys/archive_savedata.cpp33
-rw-r--r--src/core/file_sys/archive_savedata.h33
-rw-r--r--src/core/file_sys/archive_sdmc.cpp379
-rw-r--r--src/core/file_sys/archive_sdmc.h66
-rw-r--r--src/core/file_sys/archive_sdmcwriteonly.cpp70
-rw-r--r--src/core/file_sys/archive_sdmcwriteonly.h57
-rw-r--r--src/core/file_sys/archive_selfncch.cpp297
-rw-r--r--src/core/file_sys/archive_selfncch.h54
-rw-r--r--src/core/file_sys/archive_source_sd_savedata.cpp97
-rw-r--r--src/core/file_sys/archive_source_sd_savedata.h32
-rw-r--r--src/core/file_sys/archive_systemsavedata.cpp77
-rw-r--r--src/core/file_sys/archive_systemsavedata.h61
18 files changed, 0 insertions, 1935 deletions
diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp
deleted file mode 100644
index 4867c9d17..000000000
--- a/src/core/file_sys/archive_extsavedata.cpp
+++ /dev/null
@@ -1,245 +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 <memory>
7#include <vector>
8#include "common/common_types.h"
9#include "common/file_util.h"
10#include "common/logging/log.h"
11#include "common/string_util.h"
12#include "core/file_sys/archive_extsavedata.h"
13#include "core/file_sys/disk_archive.h"
14#include "core/file_sys/errors.h"
15#include "core/file_sys/path_parser.h"
16#include "core/file_sys/savedata_archive.h"
17#include "core/hle/service/fs/archive.h"
18
19////////////////////////////////////////////////////////////////////////////////////////////////////
20// FileSys namespace
21
22namespace FileSys {
23
24/**
25 * A modified version of DiskFile for fixed-size file used by ExtSaveData
26 * The file size can't be changed by SetSize or Write.
27 */
28class FixSizeDiskFile : public DiskFile {
29public:
30 FixSizeDiskFile(FileUtil::IOFile&& file, const Mode& mode) : DiskFile(std::move(file), mode) {
31 size = GetSize();
32 }
33
34 bool SetSize(u64 size) const override {
35 return false;
36 }
37
38 ResultVal<size_t> Write(u64 offset, size_t length, bool flush,
39 const u8* buffer) const override {
40 if (offset > size) {
41 return ERR_WRITE_BEYOND_END;
42 } else if (offset == size) {
43 return MakeResult<size_t>(0);
44 }
45
46 if (offset + length > size) {
47 length = size - offset;
48 }
49
50 return DiskFile::Write(offset, length, flush, buffer);
51 }
52
53private:
54 u64 size{};
55};
56
57/**
58 * Archive backend for general extsave data archive type.
59 * The behaviour of ExtSaveDataArchive is almost the same as SaveDataArchive, except for
60 * - file size can't be changed once created (thus creating zero-size file and openning with create
61 * flag are prohibited);
62 * - always open a file with read+write permission.
63 */
64class ExtSaveDataArchive : public SaveDataArchive {
65public:
66 explicit ExtSaveDataArchive(const std::string& mount_point) : SaveDataArchive(mount_point) {}
67
68 std::string GetName() const override {
69 return "ExtSaveDataArchive: " + mount_point;
70 }
71
72 ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
73 const Mode& mode) const override {
74 LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
75
76 const PathParser path_parser(path);
77
78 if (!path_parser.IsValid()) {
79 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
80 return ERROR_INVALID_PATH;
81 }
82
83 if (mode.hex == 0) {
84 LOG_ERROR(Service_FS, "Empty open mode");
85 return ERROR_UNSUPPORTED_OPEN_FLAGS;
86 }
87
88 if (mode.create_flag) {
89 LOG_ERROR(Service_FS, "Create flag is not supported");
90 return ERROR_UNSUPPORTED_OPEN_FLAGS;
91 }
92
93 const auto full_path = path_parser.BuildHostPath(mount_point);
94
95 switch (path_parser.GetHostStatus(mount_point)) {
96 case PathParser::InvalidMountPoint:
97 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
98 return ERROR_FILE_NOT_FOUND;
99 case PathParser::PathNotFound:
100 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
101 return ERROR_PATH_NOT_FOUND;
102 case PathParser::FileInPath:
103 case PathParser::DirectoryFound:
104 LOG_ERROR(Service_FS, "Unexpected file or directory in %s", full_path.c_str());
105 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
106 case PathParser::NotFound:
107 LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
108 return ERROR_FILE_NOT_FOUND;
109 case PathParser::FileFound:
110 break; // Expected 'success' case
111 }
112
113 FileUtil::IOFile file(full_path, "r+b");
114 if (!file.IsOpen()) {
115 LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str());
116 return ERROR_FILE_NOT_FOUND;
117 }
118
119 Mode rwmode;
120 rwmode.write_flag.Assign(1);
121 rwmode.read_flag.Assign(1);
122 auto disk_file = std::make_unique<FixSizeDiskFile>(std::move(file), rwmode);
123 return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
124 }
125
126 ResultCode CreateFile(const Path& path, u64 size) const override {
127 if (size == 0) {
128 LOG_ERROR(Service_FS, "Zero-size file is not supported");
129 return ERROR_UNSUPPORTED_OPEN_FLAGS;
130 }
131 return SaveDataArchive::CreateFile(path, size);
132 }
133};
134
135std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path) {
136 std::vector<u8> vec_data = path.AsBinary();
137 const u32* data = reinterpret_cast<const u32*>(vec_data.data());
138 u32 save_low = data[1];
139 u32 save_high = data[2];
140 return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_high, save_low);
141}
142
143std::string GetExtDataContainerPath(const std::string& mount_point, bool shared) {
144 if (shared)
145 return Common::StringFromFormat("%sdata/%s/extdata/", mount_point.c_str(), SYSTEM_ID);
146
147 return Common::StringFromFormat("%sNintendo 3DS/%s/%s/extdata/", mount_point.c_str(), SYSTEM_ID,
148 SDCARD_ID);
149}
150
151Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low) {
152 std::vector<u8> binary_path;
153 binary_path.reserve(12);
154
155 // Append each word byte by byte
156
157 // The first word is the media type
158 for (unsigned i = 0; i < 4; ++i)
159 binary_path.push_back((media_type >> (8 * i)) & 0xFF);
160
161 // Next is the low word
162 for (unsigned i = 0; i < 4; ++i)
163 binary_path.push_back((low >> (8 * i)) & 0xFF);
164
165 // Next is the high word
166 for (unsigned i = 0; i < 4; ++i)
167 binary_path.push_back((high >> (8 * i)) & 0xFF);
168
169 return {binary_path};
170}
171
172ArchiveFactory_ExtSaveData::ArchiveFactory_ExtSaveData(const std::string& mount_location,
173 bool shared)
174 : shared(shared), mount_point(GetExtDataContainerPath(mount_location, shared)) {
175 LOG_DEBUG(Service_FS, "Directory %s set as base for ExtSaveData.", mount_point.c_str());
176}
177
178bool ArchiveFactory_ExtSaveData::Initialize() {
179 if (!FileUtil::CreateFullPath(mount_point)) {
180 LOG_ERROR(Service_FS, "Unable to create ExtSaveData base path.");
181 return false;
182 }
183
184 return true;
185}
186
187ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(const Path& path) {
188 std::string fullpath = GetExtSaveDataPath(mount_point, path) + "user/";
189 if (!FileUtil::Exists(fullpath)) {
190 // TODO(Subv): Verify the archive behavior of SharedExtSaveData compared to ExtSaveData.
191 // ExtSaveData seems to return FS_NotFound (120) when the archive doesn't exist.
192 if (!shared) {
193 return ERR_NOT_FOUND_INVALID_STATE;
194 } else {
195 return ERR_NOT_FORMATTED;
196 }
197 }
198 auto archive = std::make_unique<ExtSaveDataArchive>(fullpath);
199 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
200}
201
202ResultCode ArchiveFactory_ExtSaveData::Format(const Path& path,
203 const FileSys::ArchiveFormatInfo& format_info) {
204 // These folders are always created with the ExtSaveData
205 std::string user_path = GetExtSaveDataPath(mount_point, path) + "user/";
206 std::string boss_path = GetExtSaveDataPath(mount_point, path) + "boss/";
207 FileUtil::CreateFullPath(user_path);
208 FileUtil::CreateFullPath(boss_path);
209
210 // Write the format metadata
211 std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata";
212 FileUtil::IOFile file(metadata_path, "wb");
213
214 if (!file.IsOpen()) {
215 // TODO(Subv): Find the correct error code
216 return ResultCode(-1);
217 }
218
219 file.WriteBytes(&format_info, sizeof(format_info));
220 return RESULT_SUCCESS;
221}
222
223ResultVal<ArchiveFormatInfo> ArchiveFactory_ExtSaveData::GetFormatInfo(const Path& path) const {
224 std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata";
225 FileUtil::IOFile file(metadata_path, "rb");
226
227 if (!file.IsOpen()) {
228 LOG_ERROR(Service_FS, "Could not open metadata information for archive");
229 // TODO(Subv): Verify error code
230 return ERR_NOT_FORMATTED;
231 }
232
233 ArchiveFormatInfo info = {};
234 file.ReadBytes(&info, sizeof(info));
235 return MakeResult<ArchiveFormatInfo>(info);
236}
237
238void ArchiveFactory_ExtSaveData::WriteIcon(const Path& path, const u8* icon_data,
239 size_t icon_size) {
240 std::string game_path = FileSys::GetExtSaveDataPath(GetMountPoint(), path);
241 FileUtil::IOFile icon_file(game_path + "icon", "wb");
242 icon_file.WriteBytes(icon_data, icon_size);
243}
244
245} // namespace FileSys
diff --git a/src/core/file_sys/archive_extsavedata.h b/src/core/file_sys/archive_extsavedata.h
deleted file mode 100644
index f705ade1c..000000000
--- a/src/core/file_sys/archive_extsavedata.h
+++ /dev/null
@@ -1,89 +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 <memory>
8#include <string>
9#include "common/common_types.h"
10#include "core/file_sys/archive_backend.h"
11#include "core/hle/result.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace
15
16namespace FileSys {
17
18/// File system interface to the ExtSaveData archive
19class ArchiveFactory_ExtSaveData final : public ArchiveFactory {
20public:
21 ArchiveFactory_ExtSaveData(const std::string& mount_point, bool shared);
22
23 /**
24 * Initialize the archive.
25 * @return true if it initialized successfully
26 */
27 bool Initialize();
28
29 std::string GetName() const override {
30 return "ExtSaveData";
31 }
32
33 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
34 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
35 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
36
37 const std::string& GetMountPoint() const {
38 return mount_point;
39 }
40
41 /**
42 * Writes the SMDH icon of the ExtSaveData to file
43 * @param path Path of this ExtSaveData
44 * @param icon_data Binary data of the icon
45 * @param icon_size Size of the icon data
46 */
47 void WriteIcon(const Path& path, const u8* icon_data, size_t icon_size);
48
49private:
50 bool shared; ///< Whether this archive represents an ExtSaveData archive or a SharedExtSaveData
51 /// archive
52
53 /**
54 * This holds the full directory path for this archive, it is only set after a successful call
55 * to Open, this is formed as `<base extsavedatapath>/<type>/<high>/<low>`.
56 * See GetExtSaveDataPath for the code that extracts this data from an archive path.
57 */
58 std::string mount_point;
59};
60
61/**
62 * Constructs a path to the concrete ExtData archive in the host filesystem based on the
63 * input Path and base mount point.
64 * @param mount_point The base mount point of the ExtSaveData archives.
65 * @param path The path that identifies the requested concrete ExtSaveData archive.
66 * @returns The complete path to the specified extdata archive in the host filesystem
67 */
68std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path);
69
70/**
71 * Constructs a path to the base folder to hold concrete ExtSaveData archives in the host file
72 * system.
73 * @param mount_point The base folder where this folder resides, ie. SDMC or NAND.
74 * @param shared Whether this ExtSaveData container is for SharedExtSaveDatas or not.
75 * @returns The path to the base ExtSaveData archives' folder in the host file system
76 */
77std::string GetExtDataContainerPath(const std::string& mount_point, bool shared);
78
79/**
80 * Constructs a FileSys::Path object that refers to the ExtData archive identified by
81 * the specified media type, high save id and low save id.
82 * @param media_type The media type where the archive is located (NAND / SDMC)
83 * @param high The high word of the save id for the archive
84 * @param low The low word of the save id for the archive
85 * @returns A FileSys::Path to the wanted archive
86 */
87Path ConstructExtDataBinaryPath(u32 media_type, u32 high, u32 low);
88
89} // namespace FileSys
diff --git a/src/core/file_sys/archive_ncch.cpp b/src/core/file_sys/archive_ncch.cpp
deleted file mode 100644
index e8c5be983..000000000
--- a/src/core/file_sys/archive_ncch.cpp
+++ /dev/null
@@ -1,114 +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 <memory>
7#include <vector>
8#include "common/common_types.h"
9#include "common/file_util.h"
10#include "common/logging/log.h"
11#include "common/string_util.h"
12#include "core/core.h"
13#include "core/file_sys/archive_ncch.h"
14#include "core/file_sys/errors.h"
15#include "core/file_sys/ivfc_archive.h"
16#include "core/file_sys/ncch_container.h"
17#include "core/file_sys/title_metadata.h"
18#include "core/hle/service/fs/archive.h"
19#include "core/loader/loader.h"
20
21////////////////////////////////////////////////////////////////////////////////////////////////////
22// FileSys namespace
23
24namespace FileSys {
25
26static std::string GetNCCHContainerPath(const std::string& nand_directory) {
27 return Common::StringFromFormat("%s%s/title/", nand_directory.c_str(), SYSTEM_ID);
28}
29
30static std::string GetNCCHPath(const std::string& mount_point, u32 high, u32 low) {
31 u32 content_id = 0;
32
33 // TODO(shinyquagsire23): Title database should be doing this path lookup
34 std::string content_path =
35 Common::StringFromFormat("%s%08x/%08x/content/", mount_point.c_str(), high, low);
36 std::string tmd_path = content_path + "00000000.tmd";
37 TitleMetadata tmd(tmd_path);
38 if (tmd.Load() == Loader::ResultStatus::Success) {
39 content_id = tmd.GetBootContentID();
40 }
41
42 return Common::StringFromFormat("%s%08x.app", content_path.c_str(), content_id);
43}
44
45ArchiveFactory_NCCH::ArchiveFactory_NCCH(const std::string& nand_directory)
46 : mount_point(GetNCCHContainerPath(nand_directory)) {}
47
48ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_NCCH::Open(const Path& path) {
49 auto vec = path.AsBinary();
50 const u32* data = reinterpret_cast<u32*>(vec.data());
51 u32 high = data[1];
52 u32 low = data[0];
53 std::string file_path = GetNCCHPath(mount_point, high, low);
54
55 std::shared_ptr<FileUtil::IOFile> romfs_file;
56 u64 romfs_offset = 0;
57 u64 romfs_size = 0;
58 auto ncch_container = NCCHContainer(file_path);
59
60 if (ncch_container.ReadRomFS(romfs_file, romfs_offset, romfs_size) !=
61 Loader::ResultStatus::Success) {
62 // High Title ID of the archive: The category (https://3dbrew.org/wiki/Title_list).
63 constexpr u32 shared_data_archive = 0x0004009B;
64 constexpr u32 system_data_archive = 0x000400DB;
65
66 // Low Title IDs.
67 constexpr u32 mii_data = 0x00010202;
68 constexpr u32 region_manifest = 0x00010402;
69 constexpr u32 ng_word_list = 0x00010302;
70
71 LOG_DEBUG(Service_FS, "Full Path: %s. Category: 0x%X. Path: 0x%X.", path.DebugStr().c_str(),
72 high, low);
73
74 if (high == shared_data_archive) {
75 if (low == mii_data) {
76 LOG_ERROR(Service_FS, "Failed to get a handle for shared data archive: Mii data. ");
77 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles,
78 "Mii data");
79 } else if (low == region_manifest) {
80 LOG_ERROR(Service_FS,
81 "Failed to get a handle for shared data archive: region manifest.");
82 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles,
83 "Region manifest");
84 }
85 } else if (high == system_data_archive) {
86 if (low == ng_word_list) {
87 LOG_ERROR(Service_FS,
88 "Failed to get a handle for system data archive: NG bad word list.");
89 Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles,
90 "NG bad word list");
91 }
92 }
93 return ERROR_NOT_FOUND;
94 }
95
96 auto archive = std::make_unique<IVFCArchive>(romfs_file, romfs_offset, romfs_size);
97 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
98}
99
100ResultCode ArchiveFactory_NCCH::Format(const Path& path,
101 const FileSys::ArchiveFormatInfo& format_info) {
102 LOG_ERROR(Service_FS, "Attempted to format a NCCH archive.");
103 // TODO: Verify error code
104 return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported,
105 ErrorLevel::Permanent);
106}
107
108ResultVal<ArchiveFormatInfo> ArchiveFactory_NCCH::GetFormatInfo(const Path& path) const {
109 // TODO(Subv): Implement
110 LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
111 return ResultCode(-1);
112}
113
114} // namespace FileSys
diff --git a/src/core/file_sys/archive_ncch.h b/src/core/file_sys/archive_ncch.h
deleted file mode 100644
index 753b91f96..000000000
--- a/src/core/file_sys/archive_ncch.h
+++ /dev/null
@@ -1,34 +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 <memory>
8#include <string>
9#include "core/file_sys/archive_backend.h"
10#include "core/hle/result.h"
11
12////////////////////////////////////////////////////////////////////////////////////////////////////
13// FileSys namespace
14
15namespace FileSys {
16
17/// File system interface to the NCCH archive
18class ArchiveFactory_NCCH final : public ArchiveFactory {
19public:
20 explicit ArchiveFactory_NCCH(const std::string& mount_point);
21
22 std::string GetName() const override {
23 return "NCCH";
24 }
25
26 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
27 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
28 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
29
30private:
31 std::string mount_point;
32};
33
34} // namespace FileSys
diff --git a/src/core/file_sys/archive_other_savedata.cpp b/src/core/file_sys/archive_other_savedata.cpp
deleted file mode 100644
index d3cf080da..000000000
--- a/src/core/file_sys/archive_other_savedata.cpp
+++ /dev/null
@@ -1,145 +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 <tuple>
6#include "core/file_sys/archive_other_savedata.h"
7#include "core/file_sys/errors.h"
8#include "core/hle/kernel/process.h"
9#include "core/hle/service/fs/archive.h"
10
11////////////////////////////////////////////////////////////////////////////////////////////////////
12// FileSys namespace
13
14namespace FileSys {
15
16// TODO(wwylele): The storage info in exheader should be checked before accessing these archives
17
18using Service::FS::MediaType;
19
20namespace {
21
22template <typename T>
23ResultVal<std::tuple<MediaType, u64>> ParsePath(const Path& path, T program_id_reader) {
24 if (path.GetType() != Binary) {
25 LOG_ERROR(Service_FS, "Wrong path type %d", static_cast<int>(path.GetType()));
26 return ERROR_INVALID_PATH;
27 }
28
29 std::vector<u8> vec_data = path.AsBinary();
30
31 if (vec_data.size() != 12) {
32 LOG_ERROR(Service_FS, "Wrong path length %zu", vec_data.size());
33 return ERROR_INVALID_PATH;
34 }
35
36 const u32* data = reinterpret_cast<const u32*>(vec_data.data());
37 auto media_type = static_cast<MediaType>(data[0]);
38
39 if (media_type != MediaType::SDMC && media_type != MediaType::GameCard) {
40 LOG_ERROR(Service_FS, "Unsupported media type %u", static_cast<u32>(media_type));
41
42 // Note: this is strange, but the error code was verified with a real 3DS
43 return ERROR_UNSUPPORTED_OPEN_FLAGS;
44 }
45
46 return MakeResult<std::tuple<MediaType, u64>>(media_type, program_id_reader(data));
47}
48
49ResultVal<std::tuple<MediaType, u64>> ParsePathPermitted(const Path& path) {
50 return ParsePath(path,
51 [](const u32* data) -> u64 { return (data[1] << 8) | 0x0004000000000000ULL; });
52}
53
54ResultVal<std::tuple<MediaType, u64>> ParsePathGeneral(const Path& path) {
55 return ParsePath(
56 path, [](const u32* data) -> u64 { return data[1] | (static_cast<u64>(data[2]) << 32); });
57}
58
59} // namespace
60
61ArchiveFactory_OtherSaveDataPermitted::ArchiveFactory_OtherSaveDataPermitted(
62 std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata)
63 : sd_savedata_source(sd_savedata) {}
64
65ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_OtherSaveDataPermitted::Open(
66 const Path& path) {
67 MediaType media_type;
68 u64 program_id;
69 CASCADE_RESULT(std::tie(media_type, program_id), ParsePathPermitted(path));
70
71 if (media_type == MediaType::GameCard) {
72 LOG_WARNING(Service_FS, "(stubbed) Unimplemented media type GameCard");
73 return ERROR_GAMECARD_NOT_INSERTED;
74 }
75
76 return sd_savedata_source->Open(program_id);
77}
78
79ResultCode ArchiveFactory_OtherSaveDataPermitted::Format(
80 const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
81 LOG_ERROR(Service_FS, "Attempted to format a OtherSaveDataPermitted archive.");
82 return ERROR_INVALID_PATH;
83}
84
85ResultVal<ArchiveFormatInfo> ArchiveFactory_OtherSaveDataPermitted::GetFormatInfo(
86 const Path& path) const {
87 MediaType media_type;
88 u64 program_id;
89 CASCADE_RESULT(std::tie(media_type, program_id), ParsePathPermitted(path));
90
91 if (media_type == MediaType::GameCard) {
92 LOG_WARNING(Service_FS, "(stubbed) Unimplemented media type GameCard");
93 return ERROR_GAMECARD_NOT_INSERTED;
94 }
95
96 return sd_savedata_source->GetFormatInfo(program_id);
97}
98
99ArchiveFactory_OtherSaveDataGeneral::ArchiveFactory_OtherSaveDataGeneral(
100 std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata)
101 : sd_savedata_source(sd_savedata) {}
102
103ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_OtherSaveDataGeneral::Open(
104 const Path& path) {
105 MediaType media_type;
106 u64 program_id;
107 CASCADE_RESULT(std::tie(media_type, program_id), ParsePathGeneral(path));
108
109 if (media_type == MediaType::GameCard) {
110 LOG_WARNING(Service_FS, "(stubbed) Unimplemented media type GameCard");
111 return ERROR_GAMECARD_NOT_INSERTED;
112 }
113
114 return sd_savedata_source->Open(program_id);
115}
116
117ResultCode ArchiveFactory_OtherSaveDataGeneral::Format(
118 const Path& path, const FileSys::ArchiveFormatInfo& format_info) {
119 MediaType media_type;
120 u64 program_id;
121 CASCADE_RESULT(std::tie(media_type, program_id), ParsePathGeneral(path));
122
123 if (media_type == MediaType::GameCard) {
124 LOG_WARNING(Service_FS, "(stubbed) Unimplemented media type GameCard");
125 return ERROR_GAMECARD_NOT_INSERTED;
126 }
127
128 return sd_savedata_source->Format(program_id, format_info);
129}
130
131ResultVal<ArchiveFormatInfo> ArchiveFactory_OtherSaveDataGeneral::GetFormatInfo(
132 const Path& path) const {
133 MediaType media_type;
134 u64 program_id;
135 CASCADE_RESULT(std::tie(media_type, program_id), ParsePathGeneral(path));
136
137 if (media_type == MediaType::GameCard) {
138 LOG_WARNING(Service_FS, "(stubbed) Unimplemented media type GameCard");
139 return ERROR_GAMECARD_NOT_INSERTED;
140 }
141
142 return sd_savedata_source->GetFormatInfo(program_id);
143}
144
145} // namespace FileSys
diff --git a/src/core/file_sys/archive_other_savedata.h b/src/core/file_sys/archive_other_savedata.h
deleted file mode 100644
index d80725158..000000000
--- a/src/core/file_sys/archive_other_savedata.h
+++ /dev/null
@@ -1,52 +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 "core/file_sys/archive_source_sd_savedata.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// FileSys namespace
11
12namespace FileSys {
13
14/// File system interface to the OtherSaveDataPermitted archive
15class ArchiveFactory_OtherSaveDataPermitted final : public ArchiveFactory {
16public:
17 explicit ArchiveFactory_OtherSaveDataPermitted(
18 std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source);
19
20 std::string GetName() const override {
21 return "OtherSaveDataPermitted";
22 }
23
24 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
25 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
26 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
27
28private:
29 std::string mount_point;
30 std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
31};
32
33/// File system interface to the OtherSaveDataGeneral archive
34class ArchiveFactory_OtherSaveDataGeneral final : public ArchiveFactory {
35public:
36 explicit ArchiveFactory_OtherSaveDataGeneral(
37 std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source);
38
39 std::string GetName() const override {
40 return "OtherSaveDataGeneral";
41 }
42
43 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
44 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
45 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
46
47private:
48 std::string mount_point;
49 std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
50};
51
52} // namespace FileSys
diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp
deleted file mode 100644
index 67076c73f..000000000
--- a/src/core/file_sys/archive_savedata.cpp
+++ /dev/null
@@ -1,33 +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 "core/file_sys/archive_savedata.h"
6#include "core/hle/kernel/process.h"
7
8////////////////////////////////////////////////////////////////////////////////////////////////////
9// FileSys namespace
10
11namespace FileSys {
12
13ArchiveFactory_SaveData::ArchiveFactory_SaveData(
14 std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata)
15 : sd_savedata_source(sd_savedata) {}
16
17ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const Path& path) {
18 UNIMPLEMENTED();
19 return {}; //sd_savedata_source->Open(Kernel::g_current_process->codeset->program_id);
20}
21
22ResultCode ArchiveFactory_SaveData::Format(const Path& path,
23 const FileSys::ArchiveFormatInfo& format_info) {
24 UNIMPLEMENTED();
25 return RESULT_SUCCESS; //sd_savedata_source->Format(Kernel::g_current_process->codeset->program_id, format_info);
26}
27
28ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveData::GetFormatInfo(const Path& path) const {
29 UNIMPLEMENTED();
30 return {}; //sd_savedata_source->GetFormatInfo(Kernel::g_current_process->codeset->program_id);
31}
32
33} // namespace FileSys
diff --git a/src/core/file_sys/archive_savedata.h b/src/core/file_sys/archive_savedata.h
deleted file mode 100644
index 41aa6f189..000000000
--- a/src/core/file_sys/archive_savedata.h
+++ /dev/null
@@ -1,33 +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 "core/file_sys/archive_source_sd_savedata.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// FileSys namespace
11
12namespace FileSys {
13
14/// File system interface to the SaveData archive
15class ArchiveFactory_SaveData final : public ArchiveFactory {
16public:
17 explicit ArchiveFactory_SaveData(std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source);
18
19 std::string GetName() const override {
20 return "SaveData";
21 }
22
23 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
24 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
25
26 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
27
28private:
29 std::string mount_point;
30 std::shared_ptr<ArchiveSource_SDSaveData> sd_savedata_source;
31};
32
33} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp
deleted file mode 100644
index fe3dce5d4..000000000
--- a/src/core/file_sys/archive_sdmc.cpp
+++ /dev/null
@@ -1,379 +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 <memory>
7#include "common/file_util.h"
8#include "common/logging/log.h"
9#include "core/file_sys/archive_sdmc.h"
10#include "core/file_sys/disk_archive.h"
11#include "core/file_sys/errors.h"
12#include "core/file_sys/path_parser.h"
13#include "core/settings.h"
14
15////////////////////////////////////////////////////////////////////////////////////////////////////
16// FileSys namespace
17
18namespace FileSys {
19
20ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFile(const Path& path,
21 const Mode& mode) const {
22 Mode modified_mode;
23 modified_mode.hex = mode.hex;
24
25 // SDMC archive always opens a file with at least read permission
26 modified_mode.read_flag.Assign(1);
27
28 return OpenFileBase(path, modified_mode);
29}
30
31ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFileBase(const Path& path,
32 const Mode& mode) const {
33 LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
34
35 const PathParser path_parser(path);
36
37 if (!path_parser.IsValid()) {
38 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
39 return ERROR_INVALID_PATH;
40 }
41
42 if (mode.hex == 0) {
43 LOG_ERROR(Service_FS, "Empty open mode");
44 return ERROR_INVALID_OPEN_FLAGS;
45 }
46
47 if (mode.create_flag && !mode.write_flag) {
48 LOG_ERROR(Service_FS, "Create flag set but write flag not set");
49 return ERROR_INVALID_OPEN_FLAGS;
50 }
51
52 const auto full_path = path_parser.BuildHostPath(mount_point);
53
54 switch (path_parser.GetHostStatus(mount_point)) {
55 case PathParser::InvalidMountPoint:
56 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
57 return ERROR_NOT_FOUND;
58 case PathParser::PathNotFound:
59 case PathParser::FileInPath:
60 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
61 return ERROR_NOT_FOUND;
62 case PathParser::DirectoryFound:
63 LOG_ERROR(Service_FS, "%s is not a file", full_path.c_str());
64 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
65 case PathParser::NotFound:
66 if (!mode.create_flag) {
67 LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.",
68 full_path.c_str());
69 return ERROR_NOT_FOUND;
70 } else {
71 // Create the file
72 FileUtil::CreateEmptyFile(full_path);
73 }
74 break;
75 case PathParser::FileFound:
76 break; // Expected 'success' case
77 }
78
79 FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
80 if (!file.IsOpen()) {
81 LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str());
82 return ERROR_NOT_FOUND;
83 }
84
85 auto disk_file = std::make_unique<DiskFile>(std::move(file), mode);
86 return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
87}
88
89ResultCode SDMCArchive::DeleteFile(const Path& path) const {
90 const PathParser path_parser(path);
91
92 if (!path_parser.IsValid()) {
93 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
94 return ERROR_INVALID_PATH;
95 }
96
97 const auto full_path = path_parser.BuildHostPath(mount_point);
98
99 switch (path_parser.GetHostStatus(mount_point)) {
100 case PathParser::InvalidMountPoint:
101 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
102 return ERROR_NOT_FOUND;
103 case PathParser::PathNotFound:
104 case PathParser::FileInPath:
105 case PathParser::NotFound:
106 LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
107 return ERROR_NOT_FOUND;
108 case PathParser::DirectoryFound:
109 LOG_ERROR(Service_FS, "%s is not a file", full_path.c_str());
110 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
111 case PathParser::FileFound:
112 break; // Expected 'success' case
113 }
114
115 if (FileUtil::Delete(full_path)) {
116 return RESULT_SUCCESS;
117 }
118
119 LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str());
120 return ERROR_NOT_FOUND;
121}
122
123ResultCode SDMCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
124 const PathParser path_parser_src(src_path);
125
126 // TODO: Verify these return codes with HW
127 if (!path_parser_src.IsValid()) {
128 LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str());
129 return ERROR_INVALID_PATH;
130 }
131
132 const PathParser path_parser_dest(dest_path);
133
134 if (!path_parser_dest.IsValid()) {
135 LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str());
136 return ERROR_INVALID_PATH;
137 }
138
139 const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
140 const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
141
142 if (FileUtil::Rename(src_path_full, dest_path_full)) {
143 return RESULT_SUCCESS;
144 }
145
146 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
147 // exist or similar. Verify.
148 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
149 ErrorSummary::NothingHappened, ErrorLevel::Status);
150}
151
152template <typename T>
153static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mount_point,
154 T deleter) {
155 const PathParser path_parser(path);
156
157 if (!path_parser.IsValid()) {
158 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
159 return ERROR_INVALID_PATH;
160 }
161
162 if (path_parser.IsRootDirectory())
163 return ERROR_NOT_FOUND;
164
165 const auto full_path = path_parser.BuildHostPath(mount_point);
166
167 switch (path_parser.GetHostStatus(mount_point)) {
168 case PathParser::InvalidMountPoint:
169 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
170 return ERROR_NOT_FOUND;
171 case PathParser::PathNotFound:
172 case PathParser::NotFound:
173 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
174 return ERROR_NOT_FOUND;
175 case PathParser::FileInPath:
176 case PathParser::FileFound:
177 LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
178 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
179 case PathParser::DirectoryFound:
180 break; // Expected 'success' case
181 }
182
183 if (deleter(full_path)) {
184 return RESULT_SUCCESS;
185 }
186
187 LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str());
188 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
189}
190
191ResultCode SDMCArchive::DeleteDirectory(const Path& path) const {
192 return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
193}
194
195ResultCode SDMCArchive::DeleteDirectoryRecursively(const Path& path) const {
196 return DeleteDirectoryHelper(
197 path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
198}
199
200ResultCode SDMCArchive::CreateFile(const FileSys::Path& path, u64 size) const {
201 const PathParser path_parser(path);
202
203 if (!path_parser.IsValid()) {
204 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
205 return ERROR_INVALID_PATH;
206 }
207
208 const auto full_path = path_parser.BuildHostPath(mount_point);
209
210 switch (path_parser.GetHostStatus(mount_point)) {
211 case PathParser::InvalidMountPoint:
212 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
213 return ERROR_NOT_FOUND;
214 case PathParser::PathNotFound:
215 case PathParser::FileInPath:
216 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
217 return ERROR_NOT_FOUND;
218 case PathParser::DirectoryFound:
219 LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
220 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
221 case PathParser::FileFound:
222 LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
223 return ERROR_ALREADY_EXISTS;
224 case PathParser::NotFound:
225 break; // Expected 'success' case
226 }
227
228 if (size == 0) {
229 FileUtil::CreateEmptyFile(full_path);
230 return RESULT_SUCCESS;
231 }
232
233 FileUtil::IOFile file(full_path, "wb");
234 // Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
235 // We do this by seeking to the right size, then writing a single null byte.
236 if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
237 return RESULT_SUCCESS;
238 }
239
240 LOG_ERROR(Service_FS, "Too large file");
241 return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource,
242 ErrorLevel::Info);
243}
244
245ResultCode SDMCArchive::CreateDirectory(const Path& path) const {
246 const PathParser path_parser(path);
247
248 if (!path_parser.IsValid()) {
249 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
250 return ERROR_INVALID_PATH;
251 }
252
253 const auto full_path = path_parser.BuildHostPath(mount_point);
254
255 switch (path_parser.GetHostStatus(mount_point)) {
256 case PathParser::InvalidMountPoint:
257 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
258 return ERROR_NOT_FOUND;
259 case PathParser::PathNotFound:
260 case PathParser::FileInPath:
261 LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
262 return ERROR_NOT_FOUND;
263 case PathParser::DirectoryFound:
264 case PathParser::FileFound:
265 LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
266 return ERROR_ALREADY_EXISTS;
267 case PathParser::NotFound:
268 break; // Expected 'success' case
269 }
270
271 if (FileUtil::CreateDir(mount_point + path.AsString())) {
272 return RESULT_SUCCESS;
273 }
274
275 LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", mount_point.c_str());
276 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, ErrorSummary::Canceled,
277 ErrorLevel::Status);
278}
279
280ResultCode SDMCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
281 const PathParser path_parser_src(src_path);
282
283 // TODO: Verify these return codes with HW
284 if (!path_parser_src.IsValid()) {
285 LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str());
286 return ERROR_INVALID_PATH;
287 }
288
289 const PathParser path_parser_dest(dest_path);
290
291 if (!path_parser_dest.IsValid()) {
292 LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str());
293 return ERROR_INVALID_PATH;
294 }
295
296 const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
297 const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
298
299 if (FileUtil::Rename(src_path_full, dest_path_full)) {
300 return RESULT_SUCCESS;
301 }
302
303 // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't
304 // exist or similar. Verify.
305 return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description
306 ErrorSummary::NothingHappened, ErrorLevel::Status);
307}
308
309ResultVal<std::unique_ptr<DirectoryBackend>> SDMCArchive::OpenDirectory(const Path& path) const {
310 const PathParser path_parser(path);
311
312 if (!path_parser.IsValid()) {
313 LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
314 return ERROR_INVALID_PATH;
315 }
316
317 const auto full_path = path_parser.BuildHostPath(mount_point);
318
319 switch (path_parser.GetHostStatus(mount_point)) {
320 case PathParser::InvalidMountPoint:
321 LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
322 return ERROR_NOT_FOUND;
323 case PathParser::PathNotFound:
324 case PathParser::NotFound:
325 case PathParser::FileFound:
326 LOG_ERROR(Service_FS, "%s not found", full_path.c_str());
327 return ERROR_NOT_FOUND;
328 case PathParser::FileInPath:
329 LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
330 return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC;
331 case PathParser::DirectoryFound:
332 break; // Expected 'success' case
333 }
334
335 auto directory = std::make_unique<DiskDirectory>(full_path);
336 return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
337}
338
339u64 SDMCArchive::GetFreeBytes() const {
340 // TODO: Stubbed to return 1GiB
341 return 1024 * 1024 * 1024;
342}
343
344ArchiveFactory_SDMC::ArchiveFactory_SDMC(const std::string& sdmc_directory)
345 : sdmc_directory(sdmc_directory) {
346 LOG_DEBUG(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str());
347}
348
349bool ArchiveFactory_SDMC::Initialize() {
350 if (!Settings::values.use_virtual_sd) {
351 LOG_WARNING(Service_FS, "SDMC disabled by config.");
352 return false;
353 }
354
355 if (!FileUtil::CreateFullPath(sdmc_directory)) {
356 LOG_ERROR(Service_FS, "Unable to create SDMC path.");
357 return false;
358 }
359
360 return true;
361}
362
363ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) {
364 auto archive = std::make_unique<SDMCArchive>(sdmc_directory);
365 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
366}
367
368ResultCode ArchiveFactory_SDMC::Format(const Path& path,
369 const FileSys::ArchiveFormatInfo& format_info) {
370 // This is kind of an undesirable operation, so let's just ignore it. :)
371 return RESULT_SUCCESS;
372}
373
374ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMC::GetFormatInfo(const Path& path) const {
375 // TODO(Subv): Implement
376 LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
377 return ResultCode(-1);
378}
379} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h
deleted file mode 100644
index f6c70bfcc..000000000
--- a/src/core/file_sys/archive_sdmc.h
+++ /dev/null
@@ -1,66 +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 <memory>
8#include <string>
9#include "core/file_sys/archive_backend.h"
10#include "core/hle/result.h"
11
12////////////////////////////////////////////////////////////////////////////////////////////////////
13// FileSys namespace
14
15namespace FileSys {
16
17/// Archive backend for SDMC archive
18class SDMCArchive : public ArchiveBackend {
19public:
20 explicit SDMCArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
21
22 std::string GetName() const override {
23 return "SDMCArchive: " + mount_point;
24 }
25
26 ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
27 const Mode& mode) const override;
28 ResultCode DeleteFile(const Path& path) const override;
29 ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
30 ResultCode DeleteDirectory(const Path& path) const override;
31 ResultCode DeleteDirectoryRecursively(const Path& path) const override;
32 ResultCode CreateFile(const Path& path, u64 size) const override;
33 ResultCode CreateDirectory(const Path& path) const override;
34 ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
35 ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
36 u64 GetFreeBytes() const override;
37
38protected:
39 ResultVal<std::unique_ptr<FileBackend>> OpenFileBase(const Path& path, const Mode& mode) const;
40 std::string mount_point;
41};
42
43/// File system interface to the SDMC archive
44class ArchiveFactory_SDMC final : public ArchiveFactory {
45public:
46 explicit ArchiveFactory_SDMC(const std::string& mount_point);
47
48 /**
49 * Initialize the archive.
50 * @return true if it initialized successfully
51 */
52 bool Initialize();
53
54 std::string GetName() const override {
55 return "SDMC";
56 }
57
58 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
59 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
60 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
61
62private:
63 std::string sdmc_directory;
64};
65
66} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmcwriteonly.cpp b/src/core/file_sys/archive_sdmcwriteonly.cpp
deleted file mode 100644
index 244aef48a..000000000
--- a/src/core/file_sys/archive_sdmcwriteonly.cpp
+++ /dev/null
@@ -1,70 +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 <memory>
6#include "common/file_util.h"
7#include "core/file_sys/archive_sdmcwriteonly.h"
8#include "core/file_sys/directory_backend.h"
9#include "core/file_sys/errors.h"
10#include "core/file_sys/file_backend.h"
11#include "core/settings.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace
15
16namespace FileSys {
17
18ResultVal<std::unique_ptr<FileBackend>> SDMCWriteOnlyArchive::OpenFile(const Path& path,
19 const Mode& mode) const {
20 if (mode.read_flag) {
21 LOG_ERROR(Service_FS, "Read flag is not supported");
22 return ERROR_INVALID_READ_FLAG;
23 }
24 return SDMCArchive::OpenFileBase(path, mode);
25}
26
27ResultVal<std::unique_ptr<DirectoryBackend>> SDMCWriteOnlyArchive::OpenDirectory(
28 const Path& path) const {
29 LOG_ERROR(Service_FS, "Not supported");
30 return ERROR_UNSUPPORTED_OPEN_FLAGS;
31}
32
33ArchiveFactory_SDMCWriteOnly::ArchiveFactory_SDMCWriteOnly(const std::string& mount_point)
34 : sdmc_directory(mount_point) {
35 LOG_DEBUG(Service_FS, "Directory %s set as SDMCWriteOnly.", sdmc_directory.c_str());
36}
37
38bool ArchiveFactory_SDMCWriteOnly::Initialize() {
39 if (!Settings::values.use_virtual_sd) {
40 LOG_WARNING(Service_FS, "SDMC disabled by config.");
41 return false;
42 }
43
44 if (!FileUtil::CreateFullPath(sdmc_directory)) {
45 LOG_ERROR(Service_FS, "Unable to create SDMC path.");
46 return false;
47 }
48
49 return true;
50}
51
52ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMCWriteOnly::Open(const Path& path) {
53 auto archive = std::make_unique<SDMCWriteOnlyArchive>(sdmc_directory);
54 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
55}
56
57ResultCode ArchiveFactory_SDMCWriteOnly::Format(const Path& path,
58 const FileSys::ArchiveFormatInfo& format_info) {
59 // TODO(wwylele): hwtest this
60 LOG_ERROR(Service_FS, "Attempted to format a SDMC write-only archive.");
61 return ResultCode(-1);
62}
63
64ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMCWriteOnly::GetFormatInfo(const Path& path) const {
65 // TODO(Subv): Implement
66 LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
67 return ResultCode(-1);
68}
69
70} // namespace FileSys
diff --git a/src/core/file_sys/archive_sdmcwriteonly.h b/src/core/file_sys/archive_sdmcwriteonly.h
deleted file mode 100644
index 9cd38d96f..000000000
--- a/src/core/file_sys/archive_sdmcwriteonly.h
+++ /dev/null
@@ -1,57 +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 "core/file_sys/archive_sdmc.h"
8
9////////////////////////////////////////////////////////////////////////////////////////////////////
10// FileSys namespace
11
12namespace FileSys {
13
14/**
15 * Archive backend for SDMC write-only archive.
16 * The behaviour of SDMCWriteOnlyArchive is almost the same as SDMCArchive, except for
17 * - OpenDirectory is unsupported;
18 * - OpenFile with read flag is unsupported.
19 */
20class SDMCWriteOnlyArchive : public SDMCArchive {
21public:
22 explicit SDMCWriteOnlyArchive(const std::string& mount_point) : SDMCArchive(mount_point) {}
23
24 std::string GetName() const override {
25 return "SDMCWriteOnlyArchive: " + mount_point;
26 }
27
28 ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
29 const Mode& mode) const override;
30
31 ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
32};
33
34/// File system interface to the SDMC write-only archive
35class ArchiveFactory_SDMCWriteOnly final : public ArchiveFactory {
36public:
37 explicit ArchiveFactory_SDMCWriteOnly(const std::string& mount_point);
38
39 /**
40 * Initialize the archive.
41 * @return true if it initialized successfully
42 */
43 bool Initialize();
44
45 std::string GetName() const override {
46 return "SDMCWriteOnly";
47 }
48
49 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
50 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
51 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
52
53private:
54 std::string sdmc_directory;
55};
56
57} // namespace FileSys
diff --git a/src/core/file_sys/archive_selfncch.cpp b/src/core/file_sys/archive_selfncch.cpp
deleted file mode 100644
index 3222000cf..000000000
--- a/src/core/file_sys/archive_selfncch.cpp
+++ /dev/null
@@ -1,297 +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 <array>
6#include <cinttypes>
7#include "common/common_types.h"
8#include "common/logging/log.h"
9#include "common/swap.h"
10#include "core/file_sys/archive_selfncch.h"
11#include "core/file_sys/errors.h"
12#include "core/file_sys/ivfc_archive.h"
13#include "core/hle/kernel/process.h"
14
15////////////////////////////////////////////////////////////////////////////////////////////////////
16// FileSys namespace
17
18namespace FileSys {
19
20enum class SelfNCCHFilePathType : u32 {
21 RomFS = 0,
22 Code = 1, // This is not supported by SelfNCCHArchive but by archive 0x2345678E
23 ExeFS = 2,
24 UpdateRomFS = 5, // This is presumably for accessing the RomFS of the update patch.
25};
26
27struct SelfNCCHFilePath {
28 u32_le type;
29 std::array<char, 8> exefs_filename;
30};
31static_assert(sizeof(SelfNCCHFilePath) == 12, "NCCHFilePath has wrong size!");
32
33// A read-only file created from a block of data. It only allows you to read the entire file at
34// once, in a single read operation.
35class ExeFSSectionFile final : public FileBackend {
36public:
37 explicit ExeFSSectionFile(std::shared_ptr<std::vector<u8>> data_) : data(std::move(data_)) {}
38
39 ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override {
40 if (offset != 0) {
41 LOG_ERROR(Service_FS, "offset must be zero!");
42 return ERROR_UNSUPPORTED_OPEN_FLAGS;
43 }
44
45 if (length != data->size()) {
46 LOG_ERROR(Service_FS, "size must match the file size!");
47 return ERROR_INCORRECT_EXEFS_READ_SIZE;
48 }
49
50 std::memcpy(buffer, data->data(), data->size());
51 return MakeResult<size_t>(data->size());
52 }
53
54 ResultVal<size_t> Write(u64 offset, size_t length, bool flush,
55 const u8* buffer) const override {
56 LOG_ERROR(Service_FS, "The file is read-only!");
57 return ERROR_UNSUPPORTED_OPEN_FLAGS;
58 }
59
60 u64 GetSize() const override {
61 return data->size();
62 }
63
64 bool SetSize(u64 size) const override {
65 return false;
66 }
67
68 bool Close() const override {
69 return true;
70 }
71
72 void Flush() const override {}
73
74private:
75 std::shared_ptr<std::vector<u8>> data;
76};
77
78// SelfNCCHArchive represents the running application itself. From this archive the application can
79// open RomFS and ExeFS, excluding the .code section.
80class SelfNCCHArchive final : public ArchiveBackend {
81public:
82 explicit SelfNCCHArchive(const NCCHData& ncch_data_) : ncch_data(ncch_data_) {}
83
84 std::string GetName() const override {
85 return "SelfNCCHArchive";
86 }
87
88 ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode&) const override {
89 // Note: SelfNCCHArchive doesn't check the open mode.
90
91 if (path.GetType() != LowPathType::Binary) {
92 LOG_ERROR(Service_FS, "Path need to be Binary");
93 return ERROR_INVALID_PATH;
94 }
95
96 std::vector<u8> binary = path.AsBinary();
97 if (binary.size() != sizeof(SelfNCCHFilePath)) {
98 LOG_ERROR(Service_FS, "Wrong path size %zu", binary.size());
99 return ERROR_INVALID_PATH;
100 }
101
102 SelfNCCHFilePath file_path;
103 std::memcpy(&file_path, binary.data(), sizeof(SelfNCCHFilePath));
104
105 switch (static_cast<SelfNCCHFilePathType>(file_path.type)) {
106 case SelfNCCHFilePathType::UpdateRomFS:
107 return OpenUpdateRomFS();
108
109 case SelfNCCHFilePathType::RomFS:
110 return OpenRomFS();
111
112 case SelfNCCHFilePathType::Code:
113 LOG_ERROR(Service_FS, "Reading the code section is not supported!");
114 return ERROR_COMMAND_NOT_ALLOWED;
115
116 case SelfNCCHFilePathType::ExeFS: {
117 const auto& raw = file_path.exefs_filename;
118 auto end = std::find(raw.begin(), raw.end(), '\0');
119 std::string filename(raw.begin(), end);
120 return OpenExeFS(filename);
121 }
122 default:
123 LOG_ERROR(Service_FS, "Unknown file type %u!", static_cast<u32>(file_path.type));
124 return ERROR_INVALID_PATH;
125 }
126 }
127
128 ResultCode DeleteFile(const Path& path) const override {
129 LOG_ERROR(Service_FS, "Unsupported");
130 return ERROR_UNSUPPORTED_OPEN_FLAGS;
131 }
132
133 ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override {
134 LOG_ERROR(Service_FS, "Unsupported");
135 return ERROR_UNSUPPORTED_OPEN_FLAGS;
136 }
137
138 ResultCode DeleteDirectory(const Path& path) const override {
139 LOG_ERROR(Service_FS, "Unsupported");
140 return ERROR_UNSUPPORTED_OPEN_FLAGS;
141 }
142
143 ResultCode DeleteDirectoryRecursively(const Path& path) const override {
144 LOG_ERROR(Service_FS, "Unsupported");
145 return ERROR_UNSUPPORTED_OPEN_FLAGS;
146 }
147
148 ResultCode CreateFile(const Path& path, u64 size) const override {
149 LOG_ERROR(Service_FS, "Unsupported");
150 return ERROR_UNSUPPORTED_OPEN_FLAGS;
151 }
152
153 ResultCode CreateDirectory(const Path& path) const override {
154 LOG_ERROR(Service_FS, "Unsupported");
155 return ERROR_UNSUPPORTED_OPEN_FLAGS;
156 }
157
158 ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override {
159 LOG_ERROR(Service_FS, "Unsupported");
160 return ERROR_UNSUPPORTED_OPEN_FLAGS;
161 }
162
163 ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override {
164 LOG_ERROR(Service_FS, "Unsupported");
165 return ERROR_UNSUPPORTED_OPEN_FLAGS;
166 }
167
168 u64 GetFreeBytes() const override {
169 return 0;
170 }
171
172private:
173 ResultVal<std::unique_ptr<FileBackend>> OpenRomFS() const {
174 if (ncch_data.romfs_file) {
175 return MakeResult<std::unique_ptr<FileBackend>>(std::make_unique<IVFCFile>(
176 ncch_data.romfs_file, ncch_data.romfs_offset, ncch_data.romfs_size));
177 } else {
178 LOG_INFO(Service_FS, "Unable to read RomFS");
179 return ERROR_ROMFS_NOT_FOUND;
180 }
181 }
182
183 ResultVal<std::unique_ptr<FileBackend>> OpenUpdateRomFS() const {
184 if (ncch_data.update_romfs_file) {
185 return MakeResult<std::unique_ptr<FileBackend>>(std::make_unique<IVFCFile>(
186 ncch_data.update_romfs_file, ncch_data.update_romfs_offset,
187 ncch_data.update_romfs_size));
188 } else {
189 LOG_INFO(Service_FS, "Unable to read update RomFS");
190 return ERROR_ROMFS_NOT_FOUND;
191 }
192 }
193
194 ResultVal<std::unique_ptr<FileBackend>> OpenExeFS(const std::string& filename) const {
195 if (filename == "icon") {
196 if (ncch_data.icon) {
197 return MakeResult<std::unique_ptr<FileBackend>>(
198 std::make_unique<ExeFSSectionFile>(ncch_data.icon));
199 }
200
201 LOG_WARNING(Service_FS, "Unable to read icon");
202 return ERROR_EXEFS_SECTION_NOT_FOUND;
203 }
204
205 if (filename == "logo") {
206 if (ncch_data.logo) {
207 return MakeResult<std::unique_ptr<FileBackend>>(
208 std::make_unique<ExeFSSectionFile>(ncch_data.logo));
209 }
210
211 LOG_WARNING(Service_FS, "Unable to read logo");
212 return ERROR_EXEFS_SECTION_NOT_FOUND;
213 }
214
215 if (filename == "banner") {
216 if (ncch_data.banner) {
217 return MakeResult<std::unique_ptr<FileBackend>>(
218 std::make_unique<ExeFSSectionFile>(ncch_data.banner));
219 }
220
221 LOG_WARNING(Service_FS, "Unable to read banner");
222 return ERROR_EXEFS_SECTION_NOT_FOUND;
223 }
224
225 LOG_ERROR(Service_FS, "Unknown ExeFS section %s!", filename.c_str());
226 return ERROR_INVALID_PATH;
227 }
228
229 NCCHData ncch_data;
230};
231
232void ArchiveFactory_SelfNCCH::Register(Loader::AppLoader& app_loader) {
233 u64 program_id = 0;
234 if (app_loader.ReadProgramId(program_id) != Loader::ResultStatus::Success) {
235 LOG_WARNING(
236 Service_FS,
237 "Could not read program id when registering with SelfNCCH, this might be a 3dsx file");
238 }
239
240 LOG_DEBUG(Service_FS, "Registering program %016" PRIX64 " with the SelfNCCH archive factory",
241 program_id);
242
243 if (ncch_data.find(program_id) != ncch_data.end()) {
244 LOG_WARNING(Service_FS, "Registering program %016" PRIX64
245 " with SelfNCCH will override existing mapping",
246 program_id);
247 }
248
249 NCCHData& data = ncch_data[program_id];
250
251 std::shared_ptr<FileUtil::IOFile> romfs_file_;
252 if (Loader::ResultStatus::Success ==
253 app_loader.ReadRomFS(romfs_file_, data.romfs_offset, data.romfs_size)) {
254
255 data.romfs_file = std::move(romfs_file_);
256 }
257
258 std::shared_ptr<FileUtil::IOFile> update_romfs_file;
259 if (Loader::ResultStatus::Success ==
260 app_loader.ReadUpdateRomFS(update_romfs_file, data.update_romfs_offset,
261 data.update_romfs_size)) {
262
263 data.update_romfs_file = std::move(update_romfs_file);
264 }
265
266 std::vector<u8> buffer;
267
268 if (Loader::ResultStatus::Success == app_loader.ReadIcon(buffer))
269 data.icon = std::make_shared<std::vector<u8>>(std::move(buffer));
270
271 buffer.clear();
272 if (Loader::ResultStatus::Success == app_loader.ReadLogo(buffer))
273 data.logo = std::make_shared<std::vector<u8>>(std::move(buffer));
274
275 buffer.clear();
276 if (Loader::ResultStatus::Success == app_loader.ReadBanner(buffer))
277 data.banner = std::make_shared<std::vector<u8>>(std::move(buffer));
278}
279
280ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path) {
281 //auto archive = std::make_unique<SelfNCCHArchive>(
282 // ncch_data[Kernel::g_current_process->codeset->program_id]);
283 //return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
284 return {};
285}
286
287ResultCode ArchiveFactory_SelfNCCH::Format(const Path&, const FileSys::ArchiveFormatInfo&) {
288 LOG_ERROR(Service_FS, "Attempted to format a SelfNCCH archive.");
289 return ERROR_INVALID_PATH;
290}
291
292ResultVal<ArchiveFormatInfo> ArchiveFactory_SelfNCCH::GetFormatInfo(const Path&) const {
293 LOG_ERROR(Service_FS, "Attempted to get format info of a SelfNCCH archive");
294 return ERROR_INVALID_PATH;
295}
296
297} // namespace FileSys
diff --git a/src/core/file_sys/archive_selfncch.h b/src/core/file_sys/archive_selfncch.h
deleted file mode 100644
index 0d6d6766e..000000000
--- a/src/core/file_sys/archive_selfncch.h
+++ /dev/null
@@ -1,54 +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 <memory>
8#include <string>
9#include <unordered_map>
10#include <vector>
11#include "common/common_types.h"
12#include "core/file_sys/archive_backend.h"
13#include "core/hle/result.h"
14#include "core/loader/loader.h"
15
16////////////////////////////////////////////////////////////////////////////////////////////////////
17// FileSys namespace
18
19namespace FileSys {
20
21struct NCCHData {
22 std::shared_ptr<std::vector<u8>> icon;
23 std::shared_ptr<std::vector<u8>> logo;
24 std::shared_ptr<std::vector<u8>> banner;
25 std::shared_ptr<FileUtil::IOFile> romfs_file;
26 u64 romfs_offset = 0;
27 u64 romfs_size = 0;
28
29 std::shared_ptr<FileUtil::IOFile> update_romfs_file;
30 u64 update_romfs_offset = 0;
31 u64 update_romfs_size = 0;
32};
33
34/// File system interface to the SelfNCCH archive
35class ArchiveFactory_SelfNCCH final : public ArchiveFactory {
36public:
37 ArchiveFactory_SelfNCCH() = default;
38
39 /// Registers a loaded application so that we can open its SelfNCCH archive when requested.
40 void Register(Loader::AppLoader& app_loader);
41
42 std::string GetName() const override {
43 return "SelfNCCH";
44 }
45 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
46 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
47 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
48
49private:
50 /// Mapping of ProgramId -> NCCHData
51 std::unordered_map<u64, NCCHData> ncch_data;
52};
53
54} // namespace FileSys
diff --git a/src/core/file_sys/archive_source_sd_savedata.cpp b/src/core/file_sys/archive_source_sd_savedata.cpp
deleted file mode 100644
index a7e331724..000000000
--- a/src/core/file_sys/archive_source_sd_savedata.cpp
+++ /dev/null
@@ -1,97 +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 "common/logging/log.h"
7#include "common/string_util.h"
8#include "core/file_sys/archive_source_sd_savedata.h"
9#include "core/file_sys/errors.h"
10#include "core/file_sys/savedata_archive.h"
11#include "core/hle/service/fs/archive.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace
15
16namespace FileSys {
17
18namespace {
19
20std::string GetSaveDataContainerPath(const std::string& sdmc_directory) {
21 return Common::StringFromFormat("%sNintendo 3DS/%s/%s/title/", sdmc_directory.c_str(),
22 SYSTEM_ID, SDCARD_ID);
23}
24
25std::string GetSaveDataPath(const std::string& mount_location, u64 program_id) {
26 u32 high = static_cast<u32>(program_id >> 32);
27 u32 low = static_cast<u32>(program_id & 0xFFFFFFFF);
28 return Common::StringFromFormat("%s%08x/%08x/data/00000001/", mount_location.c_str(), high,
29 low);
30}
31
32std::string GetSaveDataMetadataPath(const std::string& mount_location, u64 program_id) {
33 u32 high = static_cast<u32>(program_id >> 32);
34 u32 low = static_cast<u32>(program_id & 0xFFFFFFFF);
35 return Common::StringFromFormat("%s%08x/%08x/data/00000001.metadata", mount_location.c_str(),
36 high, low);
37}
38
39} // namespace
40
41ArchiveSource_SDSaveData::ArchiveSource_SDSaveData(const std::string& sdmc_directory)
42 : mount_point(GetSaveDataContainerPath(sdmc_directory)) {
43 LOG_DEBUG(Service_FS, "Directory %s set as SaveData.", mount_point.c_str());
44}
45
46ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveSource_SDSaveData::Open(u64 program_id) {
47 std::string concrete_mount_point = GetSaveDataPath(mount_point, program_id);
48 if (!FileUtil::Exists(concrete_mount_point)) {
49 // When a SaveData archive is created for the first time, it is not yet formatted and the
50 // save file/directory structure expected by the game has not yet been initialized.
51 // Returning the NotFormatted error code will signal the game to provision the SaveData
52 // archive with the files and folders that it expects.
53 return ERR_NOT_FORMATTED;
54 }
55
56 auto archive = std::make_unique<SaveDataArchive>(std::move(concrete_mount_point));
57 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
58}
59
60ResultCode ArchiveSource_SDSaveData::Format(u64 program_id,
61 const FileSys::ArchiveFormatInfo& format_info) {
62 std::string concrete_mount_point = GetSaveDataPath(mount_point, program_id);
63 FileUtil::DeleteDirRecursively(concrete_mount_point);
64 FileUtil::CreateFullPath(concrete_mount_point);
65
66 // Write the format metadata
67 std::string metadata_path = GetSaveDataMetadataPath(mount_point, program_id);
68 FileUtil::IOFile file(metadata_path, "wb");
69
70 if (file.IsOpen()) {
71 file.WriteBytes(&format_info, sizeof(format_info));
72 return RESULT_SUCCESS;
73 }
74 return RESULT_SUCCESS;
75}
76
77ResultVal<ArchiveFormatInfo> ArchiveSource_SDSaveData::GetFormatInfo(u64 program_id) const {
78 std::string metadata_path = GetSaveDataMetadataPath(mount_point, program_id);
79 FileUtil::IOFile file(metadata_path, "rb");
80
81 if (!file.IsOpen()) {
82 LOG_ERROR(Service_FS, "Could not open metadata information for archive");
83 // TODO(Subv): Verify error code
84 return ERR_NOT_FORMATTED;
85 }
86
87 ArchiveFormatInfo info = {};
88 file.ReadBytes(&info, sizeof(info));
89 return MakeResult<ArchiveFormatInfo>(info);
90}
91
92std::string ArchiveSource_SDSaveData::GetSaveDataPathFor(const std::string& mount_point,
93 u64 program_id) {
94 return GetSaveDataPath(GetSaveDataContainerPath(mount_point), program_id);
95}
96
97} // namespace FileSys
diff --git a/src/core/file_sys/archive_source_sd_savedata.h b/src/core/file_sys/archive_source_sd_savedata.h
deleted file mode 100644
index b5fe43cc1..000000000
--- a/src/core/file_sys/archive_source_sd_savedata.h
+++ /dev/null
@@ -1,32 +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 <memory>
8#include <string>
9#include "core/file_sys/archive_backend.h"
10#include "core/hle/result.h"
11
12////////////////////////////////////////////////////////////////////////////////////////////////////
13// FileSys namespace
14
15namespace FileSys {
16
17/// A common source of SD save data archive
18class ArchiveSource_SDSaveData {
19public:
20 explicit ArchiveSource_SDSaveData(const std::string& mount_point);
21
22 ResultVal<std::unique_ptr<ArchiveBackend>> Open(u64 program_id);
23 ResultCode Format(u64 program_id, const FileSys::ArchiveFormatInfo& format_info);
24 ResultVal<ArchiveFormatInfo> GetFormatInfo(u64 program_id) const;
25
26 static std::string GetSaveDataPathFor(const std::string& mount_point, u64 program_id);
27
28private:
29 std::string mount_point;
30};
31
32} // namespace FileSys
diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp
deleted file mode 100644
index 81423bffd..000000000
--- a/src/core/file_sys/archive_systemsavedata.cpp
+++ /dev/null
@@ -1,77 +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 <memory>
7#include <vector>
8#include "common/common_types.h"
9#include "common/file_util.h"
10#include "common/string_util.h"
11#include "core/file_sys/archive_systemsavedata.h"
12#include "core/file_sys/errors.h"
13#include "core/file_sys/savedata_archive.h"
14#include "core/hle/service/fs/archive.h"
15
16////////////////////////////////////////////////////////////////////////////////////////////////////
17// FileSys namespace
18
19namespace FileSys {
20
21std::string GetSystemSaveDataPath(const std::string& mount_point, const Path& path) {
22 std::vector<u8> vec_data = path.AsBinary();
23 const u32* data = reinterpret_cast<const u32*>(vec_data.data());
24 u32 save_low = data[1];
25 u32 save_high = data[0];
26 return Common::StringFromFormat("%s%08X/%08X/", mount_point.c_str(), save_low, save_high);
27}
28
29std::string GetSystemSaveDataContainerPath(const std::string& mount_point) {
30 return Common::StringFromFormat("%sdata/%s/sysdata/", mount_point.c_str(), SYSTEM_ID);
31}
32
33Path ConstructSystemSaveDataBinaryPath(u32 high, u32 low) {
34 std::vector<u8> binary_path;
35 binary_path.reserve(8);
36
37 // Append each word byte by byte
38
39 // First is the high word
40 for (unsigned i = 0; i < 4; ++i)
41 binary_path.push_back((high >> (8 * i)) & 0xFF);
42
43 // Next is the low word
44 for (unsigned i = 0; i < 4; ++i)
45 binary_path.push_back((low >> (8 * i)) & 0xFF);
46
47 return {binary_path};
48}
49
50ArchiveFactory_SystemSaveData::ArchiveFactory_SystemSaveData(const std::string& nand_path)
51 : base_path(GetSystemSaveDataContainerPath(nand_path)) {}
52
53ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(const Path& path) {
54 std::string fullpath = GetSystemSaveDataPath(base_path, path);
55 if (!FileUtil::Exists(fullpath)) {
56 // TODO(Subv): Check error code, this one is probably wrong
57 return ERR_NOT_FORMATTED;
58 }
59 auto archive = std::make_unique<SaveDataArchive>(fullpath);
60 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
61}
62
63ResultCode ArchiveFactory_SystemSaveData::Format(const Path& path,
64 const FileSys::ArchiveFormatInfo& format_info) {
65 std::string fullpath = GetSystemSaveDataPath(base_path, path);
66 FileUtil::DeleteDirRecursively(fullpath);
67 FileUtil::CreateFullPath(fullpath);
68 return RESULT_SUCCESS;
69}
70
71ResultVal<ArchiveFormatInfo> ArchiveFactory_SystemSaveData::GetFormatInfo(const Path& path) const {
72 // TODO(Subv): Implement
73 LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
74 return ResultCode(-1);
75}
76
77} // namespace FileSys
diff --git a/src/core/file_sys/archive_systemsavedata.h b/src/core/file_sys/archive_systemsavedata.h
deleted file mode 100644
index 52eb6c630..000000000
--- a/src/core/file_sys/archive_systemsavedata.h
+++ /dev/null
@@ -1,61 +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 <memory>
8#include <string>
9#include "common/common_types.h"
10#include "core/file_sys/archive_backend.h"
11#include "core/hle/result.h"
12
13////////////////////////////////////////////////////////////////////////////////////////////////////
14// FileSys namespace
15
16namespace FileSys {
17
18/// File system interface to the SystemSaveData archive
19class ArchiveFactory_SystemSaveData final : public ArchiveFactory {
20public:
21 explicit ArchiveFactory_SystemSaveData(const std::string& mount_point);
22
23 ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override;
24 ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override;
25 ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
26
27 std::string GetName() const override {
28 return "SystemSaveData";
29 }
30
31private:
32 std::string base_path;
33};
34
35/**
36 * Constructs a path to the concrete SystemSaveData archive in the host filesystem based on the
37 * input Path and base mount point.
38 * @param mount_point The base mount point of the SystemSaveData archives.
39 * @param path The path that identifies the requested concrete SystemSaveData archive.
40 * @returns The complete path to the specified SystemSaveData archive in the host filesystem
41 */
42std::string GetSystemSaveDataPath(const std::string& mount_point, const Path& path);
43
44/**
45 * Constructs a path to the base folder to hold concrete SystemSaveData archives in the host file
46 * system.
47 * @param mount_point The base folder where this folder resides, ie. SDMC or NAND.
48 * @returns The path to the base SystemSaveData archives' folder in the host file system
49 */
50std::string GetSystemSaveDataContainerPath(const std::string& mount_point);
51
52/**
53 * Constructs a FileSys::Path object that refers to the SystemSaveData archive identified by
54 * the specified high save id and low save id.
55 * @param high The high word of the save id for the archive
56 * @param low The low word of the save id for the archive
57 * @returns A FileSys::Path to the wanted archive
58 */
59Path ConstructSystemSaveDataBinaryPath(u32 high, u32 low);
60
61} // namespace FileSys