summaryrefslogtreecommitdiff
path: root/src/core/loader
diff options
context:
space:
mode:
authorGravatar Zach Hilman2018-07-18 21:07:11 -0400
committerGravatar bunnei2018-07-18 18:07:11 -0700
commit29aff8d5ab46c8d0199aa4bfa7eeff5d4fa2d7ef (patch)
tree3202e2ce55ab6387a4ca366a509eccdd963434c3 /src/core/loader
parentMerge pull request #683 from DarkLordZach/touch (diff)
downloadyuzu-29aff8d5ab46c8d0199aa4bfa7eeff5d4fa2d7ef.tar.gz
yuzu-29aff8d5ab46c8d0199aa4bfa7eeff5d4fa2d7ef.tar.xz
yuzu-29aff8d5ab46c8d0199aa4bfa7eeff5d4fa2d7ef.zip
Virtual Filesystem 2: Electric Boogaloo (#676)
* Virtual Filesystem * Fix delete bug and documentate * Review fixes + other stuff * Fix puyo regression
Diffstat (limited to 'src/core/loader')
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp105
-rw-r--r--src/core/loader/deconstructed_rom_directory.h15
-rw-r--r--src/core/loader/elf.cpp24
-rw-r--r--src/core/loader/elf.h12
-rw-r--r--src/core/loader/loader.cpp63
-rw-r--r--src/core/loader/loader.h30
-rw-r--r--src/core/loader/nca.cpp248
-rw-r--r--src/core/loader/nca.h19
-rw-r--r--src/core/loader/nro.cpp32
-rw-r--r--src/core/loader/nro.h13
-rw-r--r--src/core/loader/nso.cpp93
-rw-r--r--src/core/loader/nso.h17
12 files changed, 146 insertions, 525 deletions
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 0b11bf4f3..19b8667ba 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -4,11 +4,10 @@
4 4
5#include <cinttypes> 5#include <cinttypes>
6#include "common/common_funcs.h" 6#include "common/common_funcs.h"
7#include "common/common_paths.h"
8#include "common/file_util.h" 7#include "common/file_util.h"
9#include "common/logging/log.h" 8#include "common/logging/log.h"
10#include "common/string_util.h" 9#include "common/string_util.h"
11#include "core/file_sys/romfs_factory.h" 10#include "core/file_sys/content_archive.h"
12#include "core/gdbstub/gdbstub.h" 11#include "core/gdbstub/gdbstub.h"
13#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
14#include "core/hle/kernel/resource_limit.h" 13#include "core/hle/kernel/resource_limit.h"
@@ -47,55 +46,11 @@ static std::string FindRomFS(const std::string& directory) {
47 return filepath_romfs; 46 return filepath_romfs;
48} 47}
49 48
50AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file, 49AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file)
51 std::string filepath) 50 : AppLoader(std::move(file)) {}
52 : AppLoader(std::move(file)), filepath(std::move(filepath)) {}
53
54FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& file,
55 const std::string& filepath) {
56 bool is_main_found{};
57 bool is_npdm_found{};
58 bool is_rtld_found{};
59 bool is_sdk_found{};
60
61 const auto callback = [&](unsigned* num_entries_out, const std::string& directory,
62 const std::string& virtual_name) -> bool {
63 // Skip directories
64 std::string physical_name = directory + virtual_name;
65 if (FileUtil::IsDirectory(physical_name)) {
66 return true;
67 }
68
69 // Verify filename
70 if (Common::ToLower(virtual_name) == "main") {
71 is_main_found = true;
72 } else if (Common::ToLower(virtual_name) == "main.npdm") {
73 is_npdm_found = true;
74 return true;
75 } else if (Common::ToLower(virtual_name) == "rtld") {
76 is_rtld_found = true;
77 } else if (Common::ToLower(virtual_name) == "sdk") {
78 is_sdk_found = true;
79 } else {
80 // Continue searching
81 return true;
82 }
83
84 // Verify file is an NSO
85 FileUtil::IOFile file(physical_name, "rb");
86 if (AppLoader_NSO::IdentifyType(file, physical_name) != FileType::NSO) {
87 return false;
88 }
89
90 // We are done if we've found and verified all required NSOs
91 return !(is_main_found && is_npdm_found && is_rtld_found && is_sdk_found);
92 };
93 51
94 // Search the directory recursively, looking for the required modules 52FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) {
95 const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP; 53 if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) {
96 FileUtil::ForeachDirectoryEntry(nullptr, directory, callback);
97
98 if (is_main_found && is_npdm_found && is_rtld_found && is_sdk_found) {
99 return FileType::DeconstructedRomDirectory; 54 return FileType::DeconstructedRomDirectory;
100 } 55 }
101 56
@@ -107,14 +62,13 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
107 if (is_loaded) { 62 if (is_loaded) {
108 return ResultStatus::ErrorAlreadyLoaded; 63 return ResultStatus::ErrorAlreadyLoaded;
109 } 64 }
110 if (!file.IsOpen()) {
111 return ResultStatus::Error;
112 }
113 65
114 const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP; 66 const FileSys::VirtualDir dir = file->GetContainingDirectory();
115 const std::string npdm_path = directory + DIR_SEP + "main.npdm"; 67 const FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
68 if (npdm == nullptr)
69 return ResultStatus::ErrorInvalidFormat;
116 70
117 ResultStatus result = metadata.Load(npdm_path); 71 ResultStatus result = metadata.Load(npdm);
118 if (result != ResultStatus::Success) { 72 if (result != ResultStatus::Success) {
119 return result; 73 return result;
120 } 74 }
@@ -129,9 +83,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
129 VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; 83 VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
130 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", 84 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
131 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { 85 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
132 const std::string path = directory + DIR_SEP + module;
133 const VAddr load_addr = next_load_addr; 86 const VAddr load_addr = next_load_addr;
134 next_load_addr = AppLoader_NSO::LoadModule(path, load_addr); 87 const FileSys::VirtualFile module_file = dir->GetFile(module);
88 if (module_file != nullptr)
89 next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr);
135 if (next_load_addr) { 90 if (next_load_addr) {
136 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); 91 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
137 // Register module with GDBStub 92 // Register module with GDBStub
@@ -150,10 +105,15 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
150 metadata.GetMainThreadStackSize()); 105 metadata.GetMainThreadStackSize());
151 106
152 // Find the RomFS by searching for a ".romfs" file in this directory 107 // Find the RomFS by searching for a ".romfs" file in this directory
153 filepath_romfs = FindRomFS(directory); 108 const auto& files = dir->GetFiles();
109 const auto romfs_iter =
110 std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
111 return file->GetName().find(".romfs") != std::string::npos;
112 });
154 113
155 // Register the RomFS if a ".romfs" file was found 114 // Register the RomFS if a ".romfs" file was found
156 if (!filepath_romfs.empty()) { 115 if (romfs_iter != files.end() && *romfs_iter != nullptr) {
116 romfs = *romfs_iter;
157 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); 117 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
158 } 118 }
159 119
@@ -161,29 +121,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
161 return ResultStatus::Success; 121 return ResultStatus::Success;
162} 122}
163 123
164ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS( 124ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) {
165 std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { 125 if (romfs == nullptr)
166
167 if (filepath_romfs.empty()) {
168 LOG_DEBUG(Loader, "No RomFS available");
169 return ResultStatus::ErrorNotUsed; 126 return ResultStatus::ErrorNotUsed;
170 } 127 dir = romfs;
171
172 // We reopen the file, to allow its position to be independent
173 romfs_file = std::make_shared<FileUtil::IOFile>(filepath_romfs, "rb");
174 if (!romfs_file->IsOpen()) {
175 return ResultStatus::Error;
176 }
177
178 offset = 0;
179 size = romfs_file->GetSize();
180
181 LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset);
182 LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size);
183
184 // Reset read pointer
185 file.Seek(0, SEEK_SET);
186
187 return ResultStatus::Success; 128 return ResultStatus::Success;
188} 129}
189 130
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 23295d911..982a037f7 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -20,29 +20,26 @@ namespace Loader {
20 */ 20 */
21class AppLoader_DeconstructedRomDirectory final : public AppLoader { 21class AppLoader_DeconstructedRomDirectory final : public AppLoader {
22public: 22public:
23 AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file, std::string filepath); 23 explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file);
24 24
25 /** 25 /**
26 * Returns the type of the file 26 * Returns the type of the file
27 * @param file FileUtil::IOFile open file 27 * @param file std::shared_ptr<VfsFile> open file
28 * @param filepath Path of the file that we are opening.
29 * @return FileType found, or FileType::Error if this loader doesn't know it 28 * @return FileType found, or FileType::Error if this loader doesn't know it
30 */ 29 */
31 static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); 30 static FileType IdentifyType(const FileSys::VirtualFile& file);
32 31
33 FileType GetFileType() override { 32 FileType GetFileType() override {
34 return IdentifyType(file, filepath); 33 return IdentifyType(file);
35 } 34 }
36 35
37 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; 36 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
38 37
39 ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, 38 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
40 u64& size) override;
41 39
42private: 40private:
43 std::string filepath_romfs;
44 std::string filepath;
45 FileSys::ProgramMetadata metadata; 41 FileSys::ProgramMetadata metadata;
42 FileSys::VirtualFile romfs;
46}; 43};
47 44
48} // namespace Loader 45} // namespace Loader
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index b69e5c6ef..4bfd5f536 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -365,20 +365,17 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const
365 365
366namespace Loader { 366namespace Loader {
367 367
368AppLoader_ELF::AppLoader_ELF(FileUtil::IOFile&& file, std::string filename) 368AppLoader_ELF::AppLoader_ELF(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
369 : AppLoader(std::move(file)), filename(std::move(filename)) {}
370 369
371FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file, const std::string&) { 370FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) {
372 static constexpr u16 ELF_MACHINE_ARM{0x28}; 371 static constexpr u16 ELF_MACHINE_ARM{0x28};
373 372
374 u32 magic = 0; 373 u32 magic = 0;
375 file.Seek(0, SEEK_SET); 374 if (4 != file->ReadObject(&magic))
376 if (1 != file.ReadArray<u32>(&magic, 1))
377 return FileType::Error; 375 return FileType::Error;
378 376
379 u16 machine = 0; 377 u16 machine = 0;
380 file.Seek(18, SEEK_SET); 378 if (2 != file->ReadObject(&machine, 18))
381 if (1 != file.ReadArray<u16>(&machine, 1))
382 return FileType::Error; 379 return FileType::Error;
383 380
384 if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine) 381 if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine)
@@ -391,20 +388,13 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
391 if (is_loaded) 388 if (is_loaded)
392 return ResultStatus::ErrorAlreadyLoaded; 389 return ResultStatus::ErrorAlreadyLoaded;
393 390
394 if (!file.IsOpen()) 391 std::vector<u8> buffer = file->ReadAllBytes();
395 return ResultStatus::Error; 392 if (buffer.size() != file->GetSize())
396
397 // Reset read pointer in case this file has been read before.
398 file.Seek(0, SEEK_SET);
399
400 size_t size = file.GetSize();
401 std::unique_ptr<u8[]> buffer(new u8[size]);
402 if (file.ReadBytes(&buffer[0], size) != size)
403 return ResultStatus::Error; 393 return ResultStatus::Error;
404 394
405 ElfReader elf_reader(&buffer[0]); 395 ElfReader elf_reader(&buffer[0]);
406 SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); 396 SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR);
407 codeset->name = filename; 397 codeset->name = file->GetName();
408 398
409 process->LoadModule(codeset, codeset->entrypoint); 399 process->LoadModule(codeset, codeset->entrypoint);
410 process->svc_access_mask.set(); 400 process->svc_access_mask.set();
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index ee741a789..b8fb982d0 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -16,24 +16,20 @@ namespace Loader {
16/// Loads an ELF/AXF file 16/// Loads an ELF/AXF file
17class AppLoader_ELF final : public AppLoader { 17class AppLoader_ELF final : public AppLoader {
18public: 18public:
19 AppLoader_ELF(FileUtil::IOFile&& file, std::string filename); 19 explicit AppLoader_ELF(FileSys::VirtualFile file);
20 20
21 /** 21 /**
22 * Returns the type of the file 22 * Returns the type of the file
23 * @param file FileUtil::IOFile open file 23 * @param file std::shared_ptr<VfsFile> open file
24 * @param filepath Path of the file that we are opening.
25 * @return FileType found, or FileType::Error if this loader doesn't know it 24 * @return FileType found, or FileType::Error if this loader doesn't know it
26 */ 25 */
27 static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); 26 static FileType IdentifyType(const FileSys::VirtualFile& file);
28 27
29 FileType GetFileType() override { 28 FileType GetFileType() override {
30 return IdentifyType(file, filename); 29 return IdentifyType(file);
31 } 30 }
32 31
33 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; 32 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
34
35private:
36 std::string filename;
37}; 33};
38 34
39} // namespace Loader 35} // namespace Loader
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 8831d8e83..1574345a1 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -6,6 +6,7 @@
6#include <string> 6#include <string>
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/string_util.h" 8#include "common/string_util.h"
9#include "core/file_sys/vfs_real.h"
9#include "core/hle/kernel/process.h" 10#include "core/hle/kernel/process.h"
10#include "core/loader/deconstructed_rom_directory.h" 11#include "core/loader/deconstructed_rom_directory.h"
11#include "core/loader/elf.h" 12#include "core/loader/elf.h"
@@ -21,11 +22,11 @@ const std::initializer_list<Kernel::AddressMapping> default_address_mappings = {
21 {0x1F000000, 0x600000, false}, // entire VRAM 22 {0x1F000000, 0x600000, false}, // entire VRAM
22}; 23};
23 24
24FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath) { 25FileType IdentifyFile(FileSys::VirtualFile file) {
25 FileType type; 26 FileType type;
26 27
27#define CHECK_TYPE(loader) \ 28#define CHECK_TYPE(loader) \
28 type = AppLoader_##loader::IdentifyType(file, filepath); \ 29 type = AppLoader_##loader::IdentifyType(file); \
29 if (FileType::Error != type) \ 30 if (FileType::Error != type) \
30 return type; 31 return type;
31 32
@@ -41,25 +42,22 @@ FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath) {
41} 42}
42 43
43FileType IdentifyFile(const std::string& file_name) { 44FileType IdentifyFile(const std::string& file_name) {
44 FileUtil::IOFile file(file_name, "rb"); 45 return IdentifyFile(FileSys::VirtualFile(std::make_shared<FileSys::RealVfsFile>(file_name)));
45 if (!file.IsOpen()) {
46 LOG_ERROR(Loader, "Failed to load file {}", file_name);
47 return FileType::Unknown;
48 }
49
50 return IdentifyFile(file, file_name);
51} 46}
52 47
53FileType GuessFromExtension(const std::string& extension_) { 48FileType GuessFromFilename(const std::string& name) {
54 std::string extension = Common::ToLower(extension_); 49 if (name == "main")
50 return FileType::DeconstructedRomDirectory;
55 51
56 if (extension == ".elf") 52 const std::string extension = Common::ToLower(FileUtil::GetExtensionFromFilename(name));
53
54 if (extension == "elf")
57 return FileType::ELF; 55 return FileType::ELF;
58 else if (extension == ".nro") 56 if (extension == "nro")
59 return FileType::NRO; 57 return FileType::NRO;
60 else if (extension == ".nso") 58 if (extension == "nso")
61 return FileType::NSO; 59 return FileType::NSO;
62 else if (extension == ".nca") 60 if (extension == "nca")
63 return FileType::NCA; 61 return FileType::NCA;
64 62
65 return FileType::Unknown; 63 return FileType::Unknown;
@@ -93,58 +91,47 @@ const char* GetFileTypeString(FileType type) {
93 * @param filepath the file full path (with name) 91 * @param filepath the file full path (with name)
94 * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type 92 * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
95 */ 93 */
96static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileType type, 94static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileType type) {
97 const std::string& filename,
98 const std::string& filepath) {
99 switch (type) { 95 switch (type) {
100 96
101 // Standard ELF file format. 97 // Standard ELF file format.
102 case FileType::ELF: 98 case FileType::ELF:
103 return std::make_unique<AppLoader_ELF>(std::move(file), filename); 99 return std::make_unique<AppLoader_ELF>(std::move(file));
104 100
105 // NX NSO file format. 101 // NX NSO file format.
106 case FileType::NSO: 102 case FileType::NSO:
107 return std::make_unique<AppLoader_NSO>(std::move(file), filepath); 103 return std::make_unique<AppLoader_NSO>(std::move(file));
108 104
109 // NX NRO file format. 105 // NX NRO file format.
110 case FileType::NRO: 106 case FileType::NRO:
111 return std::make_unique<AppLoader_NRO>(std::move(file), filepath); 107 return std::make_unique<AppLoader_NRO>(std::move(file));
112 108
113 // NX NCA file format. 109 // NX NCA file format.
114 case FileType::NCA: 110 case FileType::NCA:
115 return std::make_unique<AppLoader_NCA>(std::move(file), filepath); 111 return std::make_unique<AppLoader_NCA>(std::move(file));
116 112
117 // NX deconstructed ROM directory. 113 // NX deconstructed ROM directory.
118 case FileType::DeconstructedRomDirectory: 114 case FileType::DeconstructedRomDirectory:
119 return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file), filepath); 115 return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file));
120 116
121 default: 117 default:
122 return nullptr; 118 return nullptr;
123 } 119 }
124} 120}
125 121
126std::unique_ptr<AppLoader> GetLoader(const std::string& filename) { 122std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file) {
127 FileUtil::IOFile file(filename, "rb"); 123 FileType type = IdentifyFile(file);
128 if (!file.IsOpen()) { 124 FileType filename_type = GuessFromFilename(file->GetName());
129 LOG_ERROR(Loader, "Failed to load file {}", filename);
130 return nullptr;
131 }
132
133 std::string filename_filename, filename_extension;
134 Common::SplitPath(filename, nullptr, &filename_filename, &filename_extension);
135
136 FileType type = IdentifyFile(file, filename);
137 FileType filename_type = GuessFromExtension(filename_extension);
138 125
139 if (type != filename_type) { 126 if (type != filename_type) {
140 LOG_WARNING(Loader, "File {} has a different type than its extension.", filename); 127 LOG_WARNING(Loader, "File {} has a different type than its extension.", file->GetName());
141 if (FileType::Unknown == type) 128 if (FileType::Unknown == type)
142 type = filename_type; 129 type = filename_type;
143 } 130 }
144 131
145 LOG_DEBUG(Loader, "Loading file {} as {}...", filename, GetFileTypeString(type)); 132 LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
146 133
147 return GetFileLoader(std::move(file), type, filename_filename, filename); 134 return GetFileLoader(std::move(file), type);
148} 135}
149 136
150} // namespace Loader 137} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index b76f7b13d..1da9e8099 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -13,6 +13,7 @@
13#include <boost/optional.hpp> 13#include <boost/optional.hpp>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/file_util.h" 15#include "common/file_util.h"
16#include "core/file_sys/vfs.h"
16#include "core/hle/kernel/kernel.h" 17#include "core/hle/kernel/kernel.h"
17 18
18namespace Kernel { 19namespace Kernel {
@@ -36,10 +37,9 @@ enum class FileType {
36/** 37/**
37 * Identifies the type of a bootable file based on the magic value in its header. 38 * Identifies the type of a bootable file based on the magic value in its header.
38 * @param file open file 39 * @param file open file
39 * @param filepath Path of the file that we are opening.
40 * @return FileType of file 40 * @return FileType of file
41 */ 41 */
42FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath); 42FileType IdentifyFile(FileSys::VirtualFile file);
43 43
44/** 44/**
45 * Identifies the type of a bootable file based on the magic value in its header. 45 * Identifies the type of a bootable file based on the magic value in its header.
@@ -50,12 +50,12 @@ FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath);
50FileType IdentifyFile(const std::string& file_name); 50FileType IdentifyFile(const std::string& file_name);
51 51
52/** 52/**
53 * Guess the type of a bootable file from its extension 53 * Guess the type of a bootable file from its name
54 * @param extension String extension of bootable file 54 * @param name String name of bootable file
55 * @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine 55 * @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine
56 * a filetype, and will never return FileType::Error. 56 * a filetype, and will never return FileType::Error.
57 */ 57 */
58FileType GuessFromExtension(const std::string& extension); 58FileType GuessFromFilename(const std::string& name);
59 59
60/** 60/**
61 * Convert a FileType into a string which can be displayed to the user. 61 * Convert a FileType into a string which can be displayed to the user.
@@ -79,7 +79,7 @@ enum class ResultStatus {
79/// Interface for loading an application 79/// Interface for loading an application
80class AppLoader : NonCopyable { 80class AppLoader : NonCopyable {
81public: 81public:
82 AppLoader(FileUtil::IOFile&& file) : file(std::move(file)) {} 82 AppLoader(FileSys::VirtualFile file) : file(std::move(file)) {}
83 virtual ~AppLoader() {} 83 virtual ~AppLoader() {}
84 84
85 /** 85 /**
@@ -154,26 +154,20 @@ public:
154 /** 154 /**
155 * Get the RomFS of the application 155 * Get the RomFS of the application
156 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer 156 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer
157 * @param romfs_file The file containing the RomFS 157 * @param file The file containing the RomFS
158 * @param offset The offset the romfs begins on
159 * @param size The size of the romfs
160 * @return ResultStatus result of function 158 * @return ResultStatus result of function
161 */ 159 */
162 virtual ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, 160 virtual ResultStatus ReadRomFS(FileSys::VirtualFile& dir) {
163 u64& size) {
164 return ResultStatus::ErrorNotImplemented; 161 return ResultStatus::ErrorNotImplemented;
165 } 162 }
166 163
167 /** 164 /**
168 * Get the update RomFS of the application 165 * Get the update RomFS of the application
169 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer 166 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer
170 * @param romfs_file The file containing the RomFS 167 * @param file The file containing the RomFS
171 * @param offset The offset the romfs begins on
172 * @param size The size of the romfs
173 * @return ResultStatus result of function 168 * @return ResultStatus result of function
174 */ 169 */
175 virtual ResultStatus ReadUpdateRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, 170 virtual ResultStatus ReadUpdateRomFS(FileSys::VirtualFile& file) {
176 u64& size) {
177 return ResultStatus::ErrorNotImplemented; 171 return ResultStatus::ErrorNotImplemented;
178 } 172 }
179 173
@@ -187,7 +181,7 @@ public:
187 } 181 }
188 182
189protected: 183protected:
190 FileUtil::IOFile file; 184 FileSys::VirtualFile file;
191 bool is_loaded = false; 185 bool is_loaded = false;
192}; 186};
193 187
@@ -202,6 +196,6 @@ extern const std::initializer_list<Kernel::AddressMapping> default_address_mappi
202 * @param filename String filename of bootable file 196 * @param filename String filename of bootable file
203 * @return best loader for this file 197 * @return best loader for this file
204 */ 198 */
205std::unique_ptr<AppLoader> GetLoader(const std::string& filename); 199std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file);
206 200
207} // namespace Loader 201} // namespace Loader
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index b463f369c..e73b253b2 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -4,14 +4,13 @@
4 4
5#include <vector> 5#include <vector>
6 6
7#include "common/common_funcs.h"
8#include "common/file_util.h" 7#include "common/file_util.h"
9#include "common/logging/log.h" 8#include "common/logging/log.h"
10#include "common/string_util.h" 9#include "common/string_util.h"
11#include "common/swap.h" 10#include "common/swap.h"
12#include "core/core.h" 11#include "core/core.h"
12#include "core/file_sys/content_archive.h"
13#include "core/file_sys/program_metadata.h" 13#include "core/file_sys/program_metadata.h"
14#include "core/file_sys/romfs_factory.h"
15#include "core/gdbstub/gdbstub.h" 14#include "core/gdbstub/gdbstub.h"
16#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
17#include "core/hle/kernel/resource_limit.h" 16#include "core/hle/kernel/resource_limit.h"
@@ -22,208 +21,15 @@
22 21
23namespace Loader { 22namespace Loader {
24 23
25// Media offsets in headers are stored divided by 512. Mult. by this to get real offset. 24AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file) : AppLoader(file) {}
26constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200;
27
28constexpr u64 SECTION_HEADER_SIZE = 0x200;
29constexpr u64 SECTION_HEADER_OFFSET = 0x400;
30
31enum class NcaContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 };
32
33enum class NcaSectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 };
34
35struct NcaSectionTableEntry {
36 u32_le media_offset;
37 u32_le media_end_offset;
38 INSERT_PADDING_BYTES(0x8);
39};
40static_assert(sizeof(NcaSectionTableEntry) == 0x10, "NcaSectionTableEntry has incorrect size.");
41
42struct NcaHeader {
43 std::array<u8, 0x100> rsa_signature_1;
44 std::array<u8, 0x100> rsa_signature_2;
45 u32_le magic;
46 u8 is_system;
47 NcaContentType content_type;
48 u8 crypto_type;
49 u8 key_index;
50 u64_le size;
51 u64_le title_id;
52 INSERT_PADDING_BYTES(0x4);
53 u32_le sdk_version;
54 u8 crypto_type_2;
55 INSERT_PADDING_BYTES(15);
56 std::array<u8, 0x10> rights_id;
57 std::array<NcaSectionTableEntry, 0x4> section_tables;
58 std::array<std::array<u8, 0x20>, 0x4> hash_tables;
59 std::array<std::array<u8, 0x10>, 0x4> key_area;
60 INSERT_PADDING_BYTES(0xC0);
61};
62static_assert(sizeof(NcaHeader) == 0x400, "NcaHeader has incorrect size.");
63
64struct NcaSectionHeaderBlock {
65 INSERT_PADDING_BYTES(3);
66 NcaSectionFilesystemType filesystem_type;
67 u8 crypto_type;
68 INSERT_PADDING_BYTES(3);
69};
70static_assert(sizeof(NcaSectionHeaderBlock) == 0x8, "NcaSectionHeaderBlock has incorrect size.");
71
72struct Pfs0Superblock {
73 NcaSectionHeaderBlock header_block;
74 std::array<u8, 0x20> hash;
75 u32_le size;
76 INSERT_PADDING_BYTES(4);
77 u64_le hash_table_offset;
78 u64_le hash_table_size;
79 u64_le pfs0_header_offset;
80 u64_le pfs0_size;
81 INSERT_PADDING_BYTES(432);
82};
83static_assert(sizeof(Pfs0Superblock) == 0x200, "Pfs0Superblock has incorrect size.");
84
85static bool IsValidNca(const NcaHeader& header) {
86 return header.magic == Common::MakeMagic('N', 'C', 'A', '2') ||
87 header.magic == Common::MakeMagic('N', 'C', 'A', '3');
88}
89
90// TODO(DarkLordZach): Add support for encrypted.
91class Nca final {
92 std::vector<FileSys::PartitionFilesystem> pfs;
93 std::vector<u64> pfs_offset;
94
95 u64 romfs_offset = 0;
96 u64 romfs_size = 0;
97
98 boost::optional<u8> exefs_id = boost::none;
99
100 FileUtil::IOFile file;
101 std::string path;
102
103 u64 GetExeFsFileOffset(const std::string& file_name) const;
104 u64 GetExeFsFileSize(const std::string& file_name) const;
105
106public:
107 ResultStatus Load(FileUtil::IOFile&& file, std::string path);
108
109 FileSys::PartitionFilesystem GetPfs(u8 id) const;
110
111 u64 GetRomFsOffset() const;
112 u64 GetRomFsSize() const;
113
114 std::vector<u8> GetExeFsFile(const std::string& file_name);
115};
116
117static bool IsPfsExeFs(const FileSys::PartitionFilesystem& pfs) {
118 // According to switchbrew, an exefs must only contain these two files:
119 return pfs.GetFileSize("main") > 0 && pfs.GetFileSize("main.npdm") > 0;
120}
121
122ResultStatus Nca::Load(FileUtil::IOFile&& in_file, std::string in_path) {
123 file = std::move(in_file);
124 path = in_path;
125 file.Seek(0, SEEK_SET);
126 std::array<u8, sizeof(NcaHeader)> header_array{};
127 if (sizeof(NcaHeader) != file.ReadBytes(header_array.data(), sizeof(NcaHeader)))
128 LOG_CRITICAL(Loader, "File reader errored out during header read.");
129
130 NcaHeader header{};
131 std::memcpy(&header, header_array.data(), sizeof(NcaHeader));
132 if (!IsValidNca(header))
133 return ResultStatus::ErrorInvalidFormat;
134
135 int number_sections =
136 std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
137 [](NcaSectionTableEntry entry) { return entry.media_offset > 0; });
138
139 for (int i = 0; i < number_sections; ++i) {
140 // Seek to beginning of this section.
141 file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET);
142 std::array<u8, sizeof(NcaSectionHeaderBlock)> array{};
143 if (sizeof(NcaSectionHeaderBlock) !=
144 file.ReadBytes(array.data(), sizeof(NcaSectionHeaderBlock)))
145 LOG_CRITICAL(Loader, "File reader errored out during header read.");
146
147 NcaSectionHeaderBlock block{};
148 std::memcpy(&block, array.data(), sizeof(NcaSectionHeaderBlock));
149
150 if (block.filesystem_type == NcaSectionFilesystemType::ROMFS) {
151 romfs_offset = header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
152 romfs_size =
153 header.section_tables[i].media_end_offset * MEDIA_OFFSET_MULTIPLIER - romfs_offset;
154 } else if (block.filesystem_type == NcaSectionFilesystemType::PFS0) {
155 Pfs0Superblock sb{};
156 // Seek back to beginning of this section.
157 file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET);
158 if (sizeof(Pfs0Superblock) != file.ReadBytes(&sb, sizeof(Pfs0Superblock)))
159 LOG_CRITICAL(Loader, "File reader errored out during header read.");
160
161 u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
162 MEDIA_OFFSET_MULTIPLIER) +
163 sb.pfs0_header_offset;
164 FileSys::PartitionFilesystem npfs{};
165 ResultStatus status = npfs.Load(path, offset);
166
167 if (status == ResultStatus::Success) {
168 pfs.emplace_back(std::move(npfs));
169 pfs_offset.emplace_back(offset);
170 }
171 }
172 }
173
174 for (size_t i = 0; i < pfs.size(); ++i) {
175 if (IsPfsExeFs(pfs[i]))
176 exefs_id = i;
177 }
178
179 return ResultStatus::Success;
180}
181
182FileSys::PartitionFilesystem Nca::GetPfs(u8 id) const {
183 return pfs[id];
184}
185
186u64 Nca::GetExeFsFileOffset(const std::string& file_name) const {
187 if (exefs_id == boost::none)
188 return 0;
189 return pfs[*exefs_id].GetFileOffset(file_name) + pfs_offset[*exefs_id];
190}
191
192u64 Nca::GetExeFsFileSize(const std::string& file_name) const {
193 if (exefs_id == boost::none)
194 return 0;
195 return pfs[*exefs_id].GetFileSize(file_name);
196}
197
198u64 Nca::GetRomFsOffset() const {
199 return romfs_offset;
200}
201
202u64 Nca::GetRomFsSize() const {
203 return romfs_size;
204}
205
206std::vector<u8> Nca::GetExeFsFile(const std::string& file_name) {
207 std::vector<u8> out(GetExeFsFileSize(file_name));
208 file.Seek(GetExeFsFileOffset(file_name), SEEK_SET);
209 file.ReadBytes(out.data(), GetExeFsFileSize(file_name));
210 return out;
211}
212
213AppLoader_NCA::AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath)
214 : AppLoader(std::move(file)), filepath(std::move(filepath)) {}
215
216FileType AppLoader_NCA::IdentifyType(FileUtil::IOFile& file, const std::string&) {
217 file.Seek(0, SEEK_SET);
218 std::array<u8, 0x400> header_enc_array{};
219 if (0x400 != file.ReadBytes(header_enc_array.data(), 0x400))
220 return FileType::Error;
221 25
26FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
222 // TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support. 27 // TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support.
223 NcaHeader header{}; 28 FileSys::NCAHeader header{};
224 std::memcpy(&header, header_enc_array.data(), sizeof(NcaHeader)); 29 if (sizeof(FileSys::NCAHeader) != file->ReadObject(&header))
30 return FileType::Error;
225 31
226 if (IsValidNca(header) && header.content_type == NcaContentType::Program) 32 if (IsValidNCA(header) && header.content_type == FileSys::NCAContentType::Program)
227 return FileType::NCA; 33 return FileType::NCA;
228 34
229 return FileType::Error; 35 return FileType::Error;
@@ -233,17 +39,22 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
233 if (is_loaded) { 39 if (is_loaded) {
234 return ResultStatus::ErrorAlreadyLoaded; 40 return ResultStatus::ErrorAlreadyLoaded;
235 } 41 }
236 if (!file.IsOpen()) {
237 return ResultStatus::Error;
238 }
239 42
240 nca = std::make_unique<Nca>(); 43 nca = std::make_unique<FileSys::NCA>(file);
241 ResultStatus result = nca->Load(std::move(file), filepath); 44 ResultStatus result = nca->GetStatus();
242 if (result != ResultStatus::Success) { 45 if (result != ResultStatus::Success) {
243 return result; 46 return result;
244 } 47 }
245 48
246 result = metadata.Load(nca->GetExeFsFile("main.npdm")); 49 if (nca->GetType() != FileSys::NCAContentType::Program)
50 return ResultStatus::ErrorInvalidFormat;
51
52 auto exefs = nca->GetExeFS();
53
54 if (exefs == nullptr)
55 return ResultStatus::ErrorInvalidFormat;
56
57 result = metadata.Load(exefs->GetFile("main.npdm"));
247 if (result != ResultStatus::Success) { 58 if (result != ResultStatus::Success) {
248 return result; 59 return result;
249 } 60 }
@@ -258,7 +69,8 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
258 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", 69 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
259 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { 70 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
260 const VAddr load_addr = next_load_addr; 71 const VAddr load_addr = next_load_addr;
261 next_load_addr = AppLoader_NSO::LoadModule(module, nca->GetExeFsFile(module), load_addr); 72
73 next_load_addr = AppLoader_NSO::LoadModule(exefs->GetFile(module), load_addr);
262 if (next_load_addr) { 74 if (next_load_addr) {
263 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); 75 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
264 // Register module with GDBStub 76 // Register module with GDBStub
@@ -276,28 +88,18 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
276 process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(), 88 process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
277 metadata.GetMainThreadStackSize()); 89 metadata.GetMainThreadStackSize());
278 90
279 if (nca->GetRomFsSize() > 0) 91 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0)
280 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); 92 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
281 93
282 is_loaded = true; 94 is_loaded = true;
95
283 return ResultStatus::Success; 96 return ResultStatus::Success;
284} 97}
285 98
286ResultStatus AppLoader_NCA::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, 99ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
287 u64& size) { 100 if (nca == nullptr || nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0)
288 if (nca->GetRomFsSize() == 0) {
289 LOG_DEBUG(Loader, "No RomFS available");
290 return ResultStatus::ErrorNotUsed; 101 return ResultStatus::ErrorNotUsed;
291 } 102 dir = nca->GetRomFS();
292
293 romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb");
294
295 offset = nca->GetRomFsOffset();
296 size = nca->GetRomFsSize();
297
298 LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset);
299 LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size);
300
301 return ResultStatus::Success; 103 return ResultStatus::Success;
302} 104}
303 105
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index 3b6c451d0..52c95953a 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -6,44 +6,39 @@
6 6
7#include <string> 7#include <string>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/file_sys/partition_filesystem.h" 9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/program_metadata.h" 10#include "core/file_sys/program_metadata.h"
11#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
12#include "core/loader/loader.h" 12#include "core/loader/loader.h"
13 13
14namespace Loader { 14namespace Loader {
15 15
16class Nca;
17
18/// Loads an NCA file 16/// Loads an NCA file
19class AppLoader_NCA final : public AppLoader { 17class AppLoader_NCA final : public AppLoader {
20public: 18public:
21 AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath); 19 explicit AppLoader_NCA(FileSys::VirtualFile file);
22 20
23 /** 21 /**
24 * Returns the type of the file 22 * Returns the type of the file
25 * @param file FileUtil::IOFile open file 23 * @param file std::shared_ptr<VfsFile> open file
26 * @param filepath Path of the file that we are opening.
27 * @return FileType found, or FileType::Error if this loader doesn't know it 24 * @return FileType found, or FileType::Error if this loader doesn't know it
28 */ 25 */
29 static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); 26 static FileType IdentifyType(const FileSys::VirtualFile& file);
30 27
31 FileType GetFileType() override { 28 FileType GetFileType() override {
32 return IdentifyType(file, filepath); 29 return IdentifyType(file);
33 } 30 }
34 31
35 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; 32 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
36 33
37 ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, 34 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
38 u64& size) override;
39 35
40 ~AppLoader_NCA(); 36 ~AppLoader_NCA();
41 37
42private: 38private:
43 std::string filepath;
44 FileSys::ProgramMetadata metadata; 39 FileSys::ProgramMetadata metadata;
45 40
46 std::unique_ptr<Nca> nca; 41 std::unique_ptr<FileSys::NCA> nca;
47}; 42};
48 43
49} // namespace Loader 44} // namespace Loader
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 4d7c69a22..a007d3e6e 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -48,14 +48,12 @@ struct ModHeader {
48}; 48};
49static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); 49static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
50 50
51AppLoader_NRO::AppLoader_NRO(FileUtil::IOFile&& file, std::string filepath) 51AppLoader_NRO::AppLoader_NRO(FileSys::VirtualFile file) : AppLoader(file) {}
52 : AppLoader(std::move(file)), filepath(std::move(filepath)) {}
53 52
54FileType AppLoader_NRO::IdentifyType(FileUtil::IOFile& file, const std::string&) { 53FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& file) {
55 // Read NSO header 54 // Read NSO header
56 NroHeader nro_header{}; 55 NroHeader nro_header{};
57 file.Seek(0, SEEK_SET); 56 if (sizeof(NroHeader) != file->ReadObject(&nro_header)) {
58 if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) {
59 return FileType::Error; 57 return FileType::Error;
60 } 58 }
61 if (nro_header.magic == Common::MakeMagic('N', 'R', 'O', '0')) { 59 if (nro_header.magic == Common::MakeMagic('N', 'R', 'O', '0')) {
@@ -68,16 +66,10 @@ static constexpr u32 PageAlignSize(u32 size) {
68 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 66 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
69} 67}
70 68
71bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) { 69bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
72 FileUtil::IOFile file(path, "rb");
73 if (!file.IsOpen()) {
74 return {};
75 }
76
77 // Read NSO header 70 // Read NSO header
78 NroHeader nro_header{}; 71 NroHeader nro_header{};
79 file.Seek(0, SEEK_SET); 72 if (sizeof(NroHeader) != file->ReadObject(&nro_header)) {
80 if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) {
81 return {}; 73 return {};
82 } 74 }
83 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { 75 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
@@ -86,10 +78,9 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) {
86 78
87 // Build program image 79 // Build program image
88 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); 80 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
89 std::vector<u8> program_image; 81 std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size));
90 program_image.resize(PageAlignSize(nro_header.file_size)); 82 if (program_image.size() != PageAlignSize(nro_header.file_size))
91 file.Seek(0, SEEK_SET); 83 return {};
92 file.ReadBytes(program_image.data(), nro_header.file_size);
93 84
94 for (int i = 0; i < nro_header.segments.size(); ++i) { 85 for (int i = 0; i < nro_header.segments.size(); ++i) {
95 codeset->segments[i].addr = nro_header.segments[i].offset; 86 codeset->segments[i].addr = nro_header.segments[i].offset;
@@ -112,7 +103,7 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) {
112 program_image.resize(static_cast<u32>(program_image.size()) + bss_size); 103 program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
113 104
114 // Load codeset for current process 105 // Load codeset for current process
115 codeset->name = path; 106 codeset->name = file->GetName();
116 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 107 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
117 Core::CurrentProcess()->LoadModule(codeset, load_base); 108 Core::CurrentProcess()->LoadModule(codeset, load_base);
118 109
@@ -126,14 +117,11 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
126 if (is_loaded) { 117 if (is_loaded) {
127 return ResultStatus::ErrorAlreadyLoaded; 118 return ResultStatus::ErrorAlreadyLoaded;
128 } 119 }
129 if (!file.IsOpen()) {
130 return ResultStatus::Error;
131 }
132 120
133 // Load NRO 121 // Load NRO
134 static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; 122 static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR};
135 123
136 if (!LoadNro(filepath, base_addr)) { 124 if (!LoadNro(file, base_addr)) {
137 return ResultStatus::ErrorInvalidFormat; 125 return ResultStatus::ErrorInvalidFormat;
138 } 126 }
139 127
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 599adb253..2c03d06bb 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -15,26 +15,23 @@ namespace Loader {
15/// Loads an NRO file 15/// Loads an NRO file
16class AppLoader_NRO final : public AppLoader, Linker { 16class AppLoader_NRO final : public AppLoader, Linker {
17public: 17public:
18 AppLoader_NRO(FileUtil::IOFile&& file, std::string filepath); 18 AppLoader_NRO(FileSys::VirtualFile file);
19 19
20 /** 20 /**
21 * Returns the type of the file 21 * Returns the type of the file
22 * @param file FileUtil::IOFile open file 22 * @param file std::shared_ptr<VfsFile> open file
23 * @param filepath Path of the file that we are opening.
24 * @return FileType found, or FileType::Error if this loader doesn't know it 23 * @return FileType found, or FileType::Error if this loader doesn't know it
25 */ 24 */
26 static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); 25 static FileType IdentifyType(const FileSys::VirtualFile& file);
27 26
28 FileType GetFileType() override { 27 FileType GetFileType() override {
29 return IdentifyType(file, filepath); 28 return IdentifyType(file);
30 } 29 }
31 30
32 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; 31 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
33 32
34private: 33private:
35 bool LoadNro(const std::string& path, VAddr load_base); 34 bool LoadNro(FileSys::VirtualFile file, VAddr load_base);
36
37 std::string filepath;
38}; 35};
39 36
40} // namespace Loader 37} // namespace Loader
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 7b3d6b837..2beb85fbf 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -38,6 +38,7 @@ struct NsoHeader {
38 std::array<u32_le, 3> segments_compressed_size; 38 std::array<u32_le, 3> segments_compressed_size;
39}; 39};
40static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size."); 40static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
41static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable.");
41 42
42struct ModHeader { 43struct ModHeader {
43 u32_le magic; 44 u32_le magic;
@@ -50,15 +51,11 @@ struct ModHeader {
50}; 51};
51static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); 52static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
52 53
53AppLoader_NSO::AppLoader_NSO(FileUtil::IOFile&& file, std::string filepath) 54AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
54 : AppLoader(std::move(file)), filepath(std::move(filepath)) {}
55 55
56FileType AppLoader_NSO::IdentifyType(FileUtil::IOFile& file, const std::string&) { 56FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
57 u32 magic = 0; 57 u32 magic = 0;
58 file.Seek(0, SEEK_SET); 58 file->ReadObject(&magic);
59 if (1 != file.ReadArray<u32>(&magic, 1)) {
60 return FileType::Error;
61 }
62 59
63 if (Common::MakeMagic('N', 'S', 'O', '0') == magic) { 60 if (Common::MakeMagic('N', 'S', 'O', '0') == magic) {
64 return FileType::NSO; 61 return FileType::NSO;
@@ -99,13 +96,16 @@ static constexpr u32 PageAlignSize(u32 size) {
99 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 96 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
100} 97}
101 98
102VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>& file_data, 99VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) {
103 VAddr load_base) { 100 if (file == nullptr)
104 if (file_data.size() < sizeof(NsoHeader))
105 return {}; 101 return {};
106 102
107 NsoHeader nso_header; 103 if (file->GetSize() < sizeof(NsoHeader))
108 std::memcpy(&nso_header, file_data.data(), sizeof(NsoHeader)); 104 return {};
105
106 NsoHeader nso_header{};
107 if (sizeof(NsoHeader) != file->ReadObject(&nso_header))
108 return {};
109 109
110 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) 110 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
111 return {}; 111 return {};
@@ -114,9 +114,8 @@ VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>&
114 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); 114 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
115 std::vector<u8> program_image; 115 std::vector<u8> program_image;
116 for (int i = 0; i < nso_header.segments.size(); ++i) { 116 for (int i = 0; i < nso_header.segments.size(); ++i) {
117 std::vector<u8> compressed_data(nso_header.segments_compressed_size[i]); 117 const std::vector<u8> compressed_data =
118 for (auto j = 0; j < nso_header.segments_compressed_size[i]; ++j) 118 file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
119 compressed_data[j] = file_data[nso_header.segments[i].offset + j];
120 std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]); 119 std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]);
121 program_image.resize(nso_header.segments[i].location); 120 program_image.resize(nso_header.segments[i].location);
122 program_image.insert(program_image.end(), data.begin(), data.end()); 121 program_image.insert(program_image.end(), data.begin(), data.end());
@@ -144,7 +143,7 @@ VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>&
144 program_image.resize(image_size); 143 program_image.resize(image_size);
145 144
146 // Load codeset for current process 145 // Load codeset for current process
147 codeset->name = name; 146 codeset->name = file->GetName();
148 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); 147 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
149 Core::CurrentProcess()->LoadModule(codeset, load_base); 148 Core::CurrentProcess()->LoadModule(codeset, load_base);
150 149
@@ -154,72 +153,14 @@ VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>&
154 return load_base + image_size; 153 return load_base + image_size;
155} 154}
156 155
157VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) {
158 FileUtil::IOFile file(path, "rb");
159 if (!file.IsOpen()) {
160 return {};
161 }
162
163 // Read NSO header
164 NsoHeader nso_header{};
165 file.Seek(0, SEEK_SET);
166 if (sizeof(NsoHeader) != file.ReadBytes(&nso_header, sizeof(NsoHeader))) {
167 return {};
168 }
169 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
170 return {};
171 }
172
173 // Build program image
174 Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create("");
175 std::vector<u8> program_image;
176 for (int i = 0; i < nso_header.segments.size(); ++i) {
177 std::vector<u8> data =
178 ReadSegment(file, nso_header.segments[i], nso_header.segments_compressed_size[i]);
179 program_image.resize(nso_header.segments[i].location);
180 program_image.insert(program_image.end(), data.begin(), data.end());
181 codeset->segments[i].addr = nso_header.segments[i].location;
182 codeset->segments[i].offset = nso_header.segments[i].location;
183 codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
184 }
185
186 // MOD header pointer is at .text offset + 4
187 u32 module_offset;
188 std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
189
190 // Read MOD header
191 ModHeader mod_header{};
192 // Default .bss to size in segment header if MOD0 section doesn't exist
193 u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
194 std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader));
195 const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
196 if (has_mod_header) {
197 // Resize program image to include .bss section and page align each section
198 bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
199 }
200 codeset->data.size += bss_size;
201 const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)};
202 program_image.resize(image_size);
203
204 // Load codeset for current process
205 codeset->name = path;
206 codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
207 Core::CurrentProcess()->LoadModule(codeset, load_base);
208
209 return load_base + image_size;
210}
211
212ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { 156ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
213 if (is_loaded) { 157 if (is_loaded) {
214 return ResultStatus::ErrorAlreadyLoaded; 158 return ResultStatus::ErrorAlreadyLoaded;
215 } 159 }
216 if (!file.IsOpen()) {
217 return ResultStatus::Error;
218 }
219 160
220 // Load module 161 // Load module
221 LoadModule(filepath, Memory::PROCESS_IMAGE_VADDR); 162 LoadModule(file, Memory::PROCESS_IMAGE_VADDR);
222 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", filepath, Memory::PROCESS_IMAGE_VADDR); 163 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), Memory::PROCESS_IMAGE_VADDR);
223 164
224 process->svc_access_mask.set(); 165 process->svc_access_mask.set();
225 process->address_mappings = default_address_mappings; 166 process->address_mappings = default_address_mappings;
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 386f4d39a..3f7567500 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -15,29 +15,22 @@ namespace Loader {
15/// Loads an NSO file 15/// Loads an NSO file
16class AppLoader_NSO final : public AppLoader, Linker { 16class AppLoader_NSO final : public AppLoader, Linker {
17public: 17public:
18 AppLoader_NSO(FileUtil::IOFile&& file, std::string filepath); 18 explicit AppLoader_NSO(FileSys::VirtualFile file);
19 19
20 /** 20 /**
21 * Returns the type of the file 21 * Returns the type of the file
22 * @param file FileUtil::IOFile open file 22 * @param file std::shared_ptr<VfsFile> open file
23 * @param filepath Path of the file that we are opening.
24 * @return FileType found, or FileType::Error if this loader doesn't know it 23 * @return FileType found, or FileType::Error if this loader doesn't know it
25 */ 24 */
26 static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); 25 static FileType IdentifyType(const FileSys::VirtualFile& file);
27 26
28 FileType GetFileType() override { 27 FileType GetFileType() override {
29 return IdentifyType(file, filepath); 28 return IdentifyType(file);
30 } 29 }
31 30
32 static VAddr LoadModule(const std::string& name, const std::vector<u8>& file_data, 31 static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base);
33 VAddr load_base);
34
35 static VAddr LoadModule(const std::string& path, VAddr load_base);
36 32
37 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; 33 ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
38
39private:
40 std::string filepath;
41}; 34};
42 35
43} // namespace Loader 36} // namespace Loader