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