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/archives.h"
8 #include "common/common_types.h"
9 #include "common/logging/log.h"
10 #include "common/swap.h"
11 #include "core/core.h"
12 #include "core/file_sys/archive_selfncch.h"
13 #include "core/file_sys/errors.h"
14 #include "core/file_sys/ivfc_archive.h"
15 #include "core/hle/kernel/process.h"
16
17 ////////////////////////////////////////////////////////////////////////////////////////////////////
18 // FileSys namespace
19
20 SERIALIZE_EXPORT_IMPL(FileSys::ArchiveFactory_SelfNCCH)
21
22 namespace FileSys {
23
24 enum class SelfNCCHFilePathType : u32 {
25 RomFS = 0,
26 Code = 1, // This is not supported by SelfNCCHArchive but by archive 0x2345678E
27 ExeFS = 2,
28 UpdateRomFS = 5, // This is presumably for accessing the RomFS of the update patch.
29 };
30
31 struct SelfNCCHFilePath {
32 enum_le<SelfNCCHFilePathType> type;
33 std::array<char, 8> exefs_filename;
34 };
35 static_assert(sizeof(SelfNCCHFilePath) == 12, "NCCHFilePath has wrong size!");
36
37 // A read-only file created from a block of data. It only allows you to read the entire file at
38 // once, in a single read operation.
39 class ExeFSSectionFile final : public FileBackend {
40 public:
ExeFSSectionFile(std::shared_ptr<std::vector<u8>> data_)41 explicit ExeFSSectionFile(std::shared_ptr<std::vector<u8>> data_) : data(std::move(data_)) {}
42
Read(u64 offset,std::size_t length,u8 * buffer) const43 ResultVal<std::size_t> Read(u64 offset, std::size_t length, u8* buffer) const override {
44 if (offset != 0) {
45 LOG_ERROR(Service_FS, "offset must be zero!");
46 return ERROR_UNSUPPORTED_OPEN_FLAGS;
47 }
48
49 if (length != data->size()) {
50 LOG_ERROR(Service_FS, "size must match the file size!");
51 return ERROR_INCORRECT_EXEFS_READ_SIZE;
52 }
53
54 std::memcpy(buffer, data->data(), data->size());
55 return MakeResult<std::size_t>(data->size());
56 }
57
Write(u64 offset,std::size_t length,bool flush,const u8 * buffer)58 ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush,
59 const u8* buffer) override {
60 LOG_ERROR(Service_FS, "The file is read-only!");
61 return ERROR_UNSUPPORTED_OPEN_FLAGS;
62 }
63
GetSize() const64 u64 GetSize() const override {
65 return data->size();
66 }
67
SetSize(u64 size) const68 bool SetSize(u64 size) const override {
69 return false;
70 }
71
Close() const72 bool Close() const override {
73 return true;
74 }
75
Flush() const76 void Flush() const override {}
77
78 private:
79 std::shared_ptr<std::vector<u8>> data;
80
81 ExeFSSectionFile() = default;
82
83 template <class Archive>
serialize(Archive & ar,const unsigned int)84 void serialize(Archive& ar, const unsigned int) {
85 ar& boost::serialization::base_object<FileBackend>(*this);
86 ar& data;
87 }
88 friend class boost::serialization::access;
89 };
90
91 // SelfNCCHArchive represents the running application itself. From this archive the application can
92 // open RomFS and ExeFS, excluding the .code section.
93 class SelfNCCHArchive final : public ArchiveBackend {
94 public:
SelfNCCHArchive(const NCCHData & ncch_data_)95 explicit SelfNCCHArchive(const NCCHData& ncch_data_) : ncch_data(ncch_data_) {}
96
GetName() const97 std::string GetName() const override {
98 return "SelfNCCHArchive";
99 }
100
OpenFile(const Path & path,const Mode &) const101 ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, const Mode&) const override {
102 // Note: SelfNCCHArchive doesn't check the open mode.
103
104 if (path.GetType() != LowPathType::Binary) {
105 LOG_ERROR(Service_FS, "Path need to be Binary");
106 return ERROR_INVALID_PATH;
107 }
108
109 std::vector<u8> binary = path.AsBinary();
110 if (binary.size() != sizeof(SelfNCCHFilePath)) {
111 LOG_ERROR(Service_FS, "Wrong path size {}", binary.size());
112 return ERROR_INVALID_PATH;
113 }
114
115 SelfNCCHFilePath file_path;
116 std::memcpy(&file_path, binary.data(), sizeof(SelfNCCHFilePath));
117
118 switch (file_path.type) {
119 case SelfNCCHFilePathType::UpdateRomFS:
120 return OpenUpdateRomFS();
121
122 case SelfNCCHFilePathType::RomFS:
123 return OpenRomFS();
124
125 case SelfNCCHFilePathType::Code:
126 LOG_ERROR(Service_FS, "Reading the code section is not supported!");
127 return ERROR_COMMAND_NOT_ALLOWED;
128
129 case SelfNCCHFilePathType::ExeFS: {
130 const auto& raw = file_path.exefs_filename;
131 auto end = std::find(raw.begin(), raw.end(), '\0');
132 std::string filename(raw.begin(), end);
133 return OpenExeFS(filename);
134 }
135 default:
136 LOG_ERROR(Service_FS, "Unknown file type {}!", file_path.type);
137 return ERROR_INVALID_PATH;
138 }
139 }
140
DeleteFile(const Path & path) const141 ResultCode DeleteFile(const Path& path) const override {
142 LOG_ERROR(Service_FS, "Unsupported");
143 return ERROR_UNSUPPORTED_OPEN_FLAGS;
144 }
145
RenameFile(const Path & src_path,const Path & dest_path) const146 ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override {
147 LOG_ERROR(Service_FS, "Unsupported");
148 return ERROR_UNSUPPORTED_OPEN_FLAGS;
149 }
150
DeleteDirectory(const Path & path) const151 ResultCode DeleteDirectory(const Path& path) const override {
152 LOG_ERROR(Service_FS, "Unsupported");
153 return ERROR_UNSUPPORTED_OPEN_FLAGS;
154 }
155
DeleteDirectoryRecursively(const Path & path) const156 ResultCode DeleteDirectoryRecursively(const Path& path) const override {
157 LOG_ERROR(Service_FS, "Unsupported");
158 return ERROR_UNSUPPORTED_OPEN_FLAGS;
159 }
160
CreateFile(const Path & path,u64 size) const161 ResultCode CreateFile(const Path& path, u64 size) const override {
162 LOG_ERROR(Service_FS, "Unsupported");
163 return ERROR_UNSUPPORTED_OPEN_FLAGS;
164 }
165
CreateDirectory(const Path & path) const166 ResultCode CreateDirectory(const Path& path) const override {
167 LOG_ERROR(Service_FS, "Unsupported");
168 return ERROR_UNSUPPORTED_OPEN_FLAGS;
169 }
170
RenameDirectory(const Path & src_path,const Path & dest_path) const171 ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override {
172 LOG_ERROR(Service_FS, "Unsupported");
173 return ERROR_UNSUPPORTED_OPEN_FLAGS;
174 }
175
OpenDirectory(const Path & path) const176 ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override {
177 LOG_ERROR(Service_FS, "Unsupported");
178 return ERROR_UNSUPPORTED_OPEN_FLAGS;
179 }
180
GetFreeBytes() const181 u64 GetFreeBytes() const override {
182 return 0;
183 }
184
185 private:
OpenRomFS() const186 ResultVal<std::unique_ptr<FileBackend>> OpenRomFS() const {
187 if (ncch_data.romfs_file) {
188 std::unique_ptr<DelayGenerator> delay_generator =
189 std::make_unique<RomFSDelayGenerator>();
190 return MakeResult<std::unique_ptr<FileBackend>>(
191 std::make_unique<IVFCFile>(ncch_data.romfs_file, std::move(delay_generator)));
192 } else {
193 LOG_INFO(Service_FS, "Unable to read RomFS");
194 return ERROR_ROMFS_NOT_FOUND;
195 }
196 }
197
OpenUpdateRomFS() const198 ResultVal<std::unique_ptr<FileBackend>> OpenUpdateRomFS() const {
199 if (ncch_data.update_romfs_file) {
200 std::unique_ptr<DelayGenerator> delay_generator =
201 std::make_unique<RomFSDelayGenerator>();
202 return MakeResult<std::unique_ptr<FileBackend>>(std::make_unique<IVFCFile>(
203 ncch_data.update_romfs_file, std::move(delay_generator)));
204 } else {
205 LOG_INFO(Service_FS, "Unable to read update RomFS");
206 return ERROR_ROMFS_NOT_FOUND;
207 }
208 }
209
OpenExeFS(const std::string & filename) const210 ResultVal<std::unique_ptr<FileBackend>> OpenExeFS(const std::string& filename) const {
211 if (filename == "icon") {
212 if (ncch_data.icon) {
213 return MakeResult<std::unique_ptr<FileBackend>>(
214 std::make_unique<ExeFSSectionFile>(ncch_data.icon));
215 }
216
217 LOG_WARNING(Service_FS, "Unable to read icon");
218 return ERROR_EXEFS_SECTION_NOT_FOUND;
219 }
220
221 if (filename == "logo") {
222 if (ncch_data.logo) {
223 return MakeResult<std::unique_ptr<FileBackend>>(
224 std::make_unique<ExeFSSectionFile>(ncch_data.logo));
225 }
226
227 LOG_WARNING(Service_FS, "Unable to read logo");
228 return ERROR_EXEFS_SECTION_NOT_FOUND;
229 }
230
231 if (filename == "banner") {
232 if (ncch_data.banner) {
233 return MakeResult<std::unique_ptr<FileBackend>>(
234 std::make_unique<ExeFSSectionFile>(ncch_data.banner));
235 }
236
237 LOG_WARNING(Service_FS, "Unable to read banner");
238 return ERROR_EXEFS_SECTION_NOT_FOUND;
239 }
240
241 LOG_ERROR(Service_FS, "Unknown ExeFS section {}!", filename);
242 return ERROR_INVALID_PATH;
243 }
244
245 NCCHData ncch_data;
246
247 SelfNCCHArchive() = default;
248
249 template <class Archive>
serialize(Archive & ar,const unsigned int)250 void serialize(Archive& ar, const unsigned int) {
251 ar& boost::serialization::base_object<ArchiveBackend>(*this);
252 ar& ncch_data;
253 }
254 friend class boost::serialization::access;
255 };
256
Register(Loader::AppLoader & app_loader)257 void ArchiveFactory_SelfNCCH::Register(Loader::AppLoader& app_loader) {
258 u64 program_id = 0;
259 if (app_loader.ReadProgramId(program_id) != Loader::ResultStatus::Success) {
260 LOG_WARNING(
261 Service_FS,
262 "Could not read program id when registering with SelfNCCH, this might be a 3dsx file");
263 }
264
265 LOG_DEBUG(Service_FS, "Registering program {:016X} with the SelfNCCH archive factory",
266 program_id);
267
268 if (ncch_data.find(program_id) != ncch_data.end()) {
269 LOG_WARNING(Service_FS,
270 "Registering program {:016X} with SelfNCCH will override existing mapping",
271 program_id);
272 }
273
274 NCCHData& data = ncch_data[program_id];
275
276 std::shared_ptr<RomFSReader> romfs_file_;
277 if (Loader::ResultStatus::Success == app_loader.ReadRomFS(romfs_file_)) {
278
279 data.romfs_file = std::move(romfs_file_);
280 }
281
282 std::shared_ptr<RomFSReader> update_romfs_file;
283 if (Loader::ResultStatus::Success == app_loader.ReadUpdateRomFS(update_romfs_file)) {
284
285 data.update_romfs_file = std::move(update_romfs_file);
286 }
287
288 std::vector<u8> buffer;
289
290 if (Loader::ResultStatus::Success == app_loader.ReadIcon(buffer))
291 data.icon = std::make_shared<std::vector<u8>>(std::move(buffer));
292
293 buffer.clear();
294 if (Loader::ResultStatus::Success == app_loader.ReadLogo(buffer))
295 data.logo = std::make_shared<std::vector<u8>>(std::move(buffer));
296
297 buffer.clear();
298 if (Loader::ResultStatus::Success == app_loader.ReadBanner(buffer))
299 data.banner = std::make_shared<std::vector<u8>>(std::move(buffer));
300 }
301
Open(const Path & path,u64 program_id)302 ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SelfNCCH::Open(const Path& path,
303 u64 program_id) {
304 auto archive = std::make_unique<SelfNCCHArchive>(ncch_data[program_id]);
305 return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive));
306 }
307
Format(const Path &,const FileSys::ArchiveFormatInfo &,u64 program_id)308 ResultCode ArchiveFactory_SelfNCCH::Format(const Path&, const FileSys::ArchiveFormatInfo&,
309 u64 program_id) {
310 LOG_ERROR(Service_FS, "Attempted to format a SelfNCCH archive.");
311 return ERROR_INVALID_PATH;
312 }
313
GetFormatInfo(const Path &,u64 program_id) const314 ResultVal<ArchiveFormatInfo> ArchiveFactory_SelfNCCH::GetFormatInfo(const Path&,
315 u64 program_id) const {
316 LOG_ERROR(Service_FS, "Attempted to get format info of a SelfNCCH archive");
317 return ERROR_INVALID_PATH;
318 }
319
320 } // namespace FileSys
321
322 SERIALIZE_EXPORT_IMPL(FileSys::ExeFSSectionFile)
323 SERIALIZE_EXPORT_IMPL(FileSys::SelfNCCHArchive)
324