1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
|
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <span>
#include <string>
#include <unordered_map>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/unordered_map.hpp>
#include "common/common_types.h"
#include "core/file_sys/archive_backend.h"
#include "core/hle/result.h"
#include "core/hle/service/fs/directory.h"
#include "core/hle/service/fs/file.h"
/// The unique system identifier hash, also known as ID0
static constexpr char SYSTEM_ID[]{"00000000000000000000000000000000"};
/// The scrambled SD card CID, also known as ID1
static constexpr char SDCARD_ID[]{"00000000000000000000000000000000"};
namespace Loader {
class AppLoader;
}
namespace Core {
class System;
}
namespace Service::FS {
/// Supported archive types
enum class ArchiveIdCode : u32 {
SelfNCCH = 0x00000003,
SaveData = 0x00000004,
ExtSaveData = 0x00000006,
SharedExtSaveData = 0x00000007,
SystemSaveData = 0x00000008,
SDMC = 0x00000009,
SDMCWriteOnly = 0x0000000A,
BossExtSaveData = 0x12345678,
NCCH = 0x2345678A,
OtherSaveDataGeneral = 0x567890B2,
OtherSaveDataPermitted = 0x567890B4,
};
/// Media types for the archives
enum class MediaType : u32 { NAND = 0, SDMC = 1, GameCard = 2 };
MediaType GetMediaTypeFromPath(std::string_view path);
enum class SpecialContentType : u8 {
Update = 1,
Manual = 2,
DLPChild = 3,
};
typedef u64 ArchiveHandle;
struct ArchiveResource {
u32 sector_size_in_bytes;
u32 cluster_size_in_bytes;
u32 partition_capacity_in_clusters;
u32 free_space_in_clusters;
};
static_assert(sizeof(ArchiveResource) == 0x10, "ArchiveResource has incorrect size");
using FileSys::ArchiveBackend;
using FileSys::ArchiveFactory;
class ArchiveManager {
public:
explicit ArchiveManager(Core::System& system);
/**
* Opens an archive
* @param id_code IdCode of the archive to open
* @param archive_path Path to the archive, used with Binary paths
* @param program_id the program ID of the client that requests the operation
* @return Handle to the opened archive
*/
ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, const FileSys::Path& archive_path,
u64 program_id);
/**
* Closes an archive
* @param handle Handle to the archive to close
*/
Result CloseArchive(ArchiveHandle handle);
/**
* Open a File from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the File inside of the Archive
* @param mode Mode under which to open the File
* @return Pair containing the opened File object and the open delay
*/
std::pair<ResultVal<std::shared_ptr<File>>, std::chrono::nanoseconds> OpenFileFromArchive(
ArchiveHandle archive_handle, const FileSys::Path& path, FileSys::Mode mode);
/**
* Delete a File from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the File inside of the Archive
* @return Whether deletion succeeded
*/
Result DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
/**
* Rename a File between two Archives
* @param src_archive_handle Handle to the source Archive object
* @param src_path Path to the File inside of the source Archive
* @param dest_archive_handle Handle to the destination Archive object
* @param dest_path Path to the File inside of the destination Archive
* @return Whether rename succeeded
*/
Result RenameFileBetweenArchives(ArchiveHandle src_archive_handle,
const FileSys::Path& src_path,
ArchiveHandle dest_archive_handle,
const FileSys::Path& dest_path);
/**
* Delete a Directory from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the Directory inside of the Archive
* @return Whether deletion succeeded
*/
Result DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
/**
* Delete a Directory and anything under it from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the Directory inside of the Archive
* @return Whether deletion succeeded
*/
Result DeleteDirectoryRecursivelyFromArchive(ArchiveHandle archive_handle,
const FileSys::Path& path);
/**
* Create a File in an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the File inside of the Archive
* @param file_size The size of the new file, filled with zeroes
* @return File creation result code
*/
Result CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path,
u64 file_size);
/**
* Create a Directory from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the Directory inside of the Archive
* @return Whether creation of directory succeeded
*/
Result CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path);
/**
* Rename a Directory between two Archives
* @param src_archive_handle Handle to the source Archive object
* @param src_path Path to the Directory inside of the source Archive
* @param dest_archive_handle Handle to the destination Archive object
* @param dest_path Path to the Directory inside of the destination Archive
* @return Whether rename succeeded
*/
Result RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle,
const FileSys::Path& src_path,
ArchiveHandle dest_archive_handle,
const FileSys::Path& dest_path);
/**
* Open a Directory from an Archive
* @param archive_handle Handle to an open Archive object
* @param path Path to the Directory inside of the Archive
* @return The opened Directory object
*/
ResultVal<std::shared_ptr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle,
const FileSys::Path& path);
/**
* Get the free space in an Archive
* @param archive_handle Handle to an open Archive object
* @return The number of free bytes in the archive
*/
ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle);
/**
* Erases the contents of the physical folder that contains the archive
* identified by the specified id code and path
* @param id_code The id of the archive to format
* @param format_info Format information about the new archive
* @param path The path to the archive, if relevant.
* @param program_id the program ID of the client that requests the operation
* @return Result 0 on success or the corresponding code on error
*/
Result FormatArchive(ArchiveIdCode id_code, const FileSys::ArchiveFormatInfo& format_info,
const FileSys::Path& path, u64 program_id);
/**
* Retrieves the format info about the archive of the specified type and path.
* The format info is supplied by the client code when creating archives.
* @param id_code The id of the archive
* @param archive_path The path of the archive, if relevant
* @param program_id the program ID of the client that requests the operation
* @return The format info of the archive, or the corresponding error code if failed.
*/
ResultVal<FileSys::ArchiveFormatInfo> GetArchiveFormatInfo(ArchiveIdCode id_code,
const FileSys::Path& archive_path,
u64 program_id);
/**
* Creates a blank SharedExtSaveData archive for the specified extdata ID
* @param media_type The media type of the archive to create (NAND / SDMC)
* @param high The high word of the extdata id to create
* @param low The low word of the extdata id to create
* @param smdh_icon the SMDH icon for this ExtSaveData
* @param format_info Format information about the new archive
* @param program_id the program ID of the client that requests the operation
* @return Result 0 on success or the corresponding code on error
*/
Result CreateExtSaveData(MediaType media_type, u32 high, u32 low, std::span<const u8> smdh_icon,
const FileSys::ArchiveFormatInfo& format_info, u64 program_id);
/**
* Deletes the SharedExtSaveData archive for the specified extdata ID
* @param media_type The media type of the archive to delete (NAND / SDMC)
* @param high The high word of the extdata id to delete
* @param low The low word of the extdata id to delete
* @return Result 0 on success or the corresponding code on error
*/
Result DeleteExtSaveData(MediaType media_type, u32 high, u32 low);
/**
* Deletes the SystemSaveData archive folder for the specified save data id
* @param high The high word of the SystemSaveData archive to delete
* @param low The low word of the SystemSaveData archive to delete
* @return Result 0 on success or the corresponding code on error
*/
Result DeleteSystemSaveData(u32 high, u32 low);
/**
* Creates the SystemSaveData archive folder for the specified save data id
* @param high The high word of the SystemSaveData archive to create
* @param low The low word of the SystemSaveData archive to create
* @return Result 0 on success or the corresponding code on error
*/
Result CreateSystemSaveData(u32 high, u32 low);
/**
* Returns capacity and free space information about the given media type.
* @param media_type The media type to obtain the information about.
* @return The capacity information of the media type, or an error code if failed.
*/
ResultVal<ArchiveResource> GetArchiveResource(MediaType media_type) const;
/// Registers a new NCCH file with the SelfNCCH archive factory
void RegisterSelfNCCH(Loader::AppLoader& app_loader);
private:
Core::System& system;
/**
* Registers an Archive type, instances of which can later be opened using its IdCode.
* @param factory File system backend interface to the archive
* @param id_code Id code used to access this type of archive
*/
Result RegisterArchiveType(std::unique_ptr<FileSys::ArchiveFactory>&& factory,
ArchiveIdCode id_code);
/// Register all archive types
void RegisterArchiveTypes();
ArchiveBackend* GetArchive(ArchiveHandle handle);
/**
* Map of registered archives, identified by id code. Once an archive is registered here, it is
* never removed until UnregisterArchiveTypes is called.
*/
std::unordered_map<ArchiveIdCode, std::unique_ptr<ArchiveFactory>> id_code_map;
/**
* Map of active archive handles to archive objects
*/
std::unordered_map<ArchiveHandle, std::unique_ptr<ArchiveBackend>> handle_map;
ArchiveHandle next_handle = 1;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& id_code_map;
ar& handle_map;
ar& next_handle;
}
friend class boost::serialization::access;
};
} // namespace Service::FS
|