diff options
Diffstat (limited to 'src/core')
32 files changed, 547 insertions, 58 deletions
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp index 2a913ce82..47b7526c7 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.cpp +++ b/src/core/file_sys/fsmitm_romfsbuild.cpp @@ -26,6 +26,7 @@ #include "common/alignment.h" #include "common/assert.h" #include "core/file_sys/fsmitm_romfsbuild.h" +#include "core/file_sys/ips_layer.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_vector.h" @@ -123,7 +124,7 @@ static u64 romfs_get_hash_table_count(u64 num_entries) { return count; } -void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, +void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext, std::shared_ptr<RomFSBuildDirectoryContext> parent) { std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs; @@ -144,6 +145,9 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); child->path = parent->path + "/" + kv.first; + if (ext != nullptr && ext->GetFileRelative(child->path + ".stub") != nullptr) + continue; + // Sanity check on path_len ASSERT(child->path_len < FS_MAX_PATH); @@ -157,11 +161,24 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); child->path = parent->path + "/" + kv.first; + if (ext != nullptr && ext->GetFileRelative(child->path + ".stub") != nullptr) + continue; + // Sanity check on path_len ASSERT(child->path_len < FS_MAX_PATH); child->source = root_romfs->GetFileRelative(child->path); + if (ext != nullptr) { + const auto ips = ext->GetFileRelative(child->path + ".ips"); + + if (ips != nullptr) { + auto patched = PatchIPS(child->source, ips); + if (patched != nullptr) + child->source = std::move(patched); + } + } + child->size = child->source->GetSize(); AddFile(parent, child); @@ -169,7 +186,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, } for (auto& child : child_dirs) { - this->VisitDirectory(root_romfs, child); + this->VisitDirectory(root_romfs, ext, child); } } @@ -208,14 +225,15 @@ bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> pare return true; } -RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_)) { +RomFSBuildContext::RomFSBuildContext(VirtualDir base_, VirtualDir ext_) + : base(std::move(base_)), ext(std::move(ext_)) { root = std::make_shared<RomFSBuildDirectoryContext>(); root->path = "\0"; directories.emplace(root->path, root); num_dirs = 1; dir_table_size = 0x18; - VisitDirectory(base, root); + VisitDirectory(base, ext, root); } RomFSBuildContext::~RomFSBuildContext() = default; diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h index b0c3c123b..3d377b0af 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.h +++ b/src/core/file_sys/fsmitm_romfsbuild.h @@ -40,7 +40,7 @@ struct RomFSFileEntry; class RomFSBuildContext { public: - explicit RomFSBuildContext(VirtualDir base); + explicit RomFSBuildContext(VirtualDir base, VirtualDir ext = nullptr); ~RomFSBuildContext(); // This finalizes the context. @@ -48,6 +48,7 @@ public: private: VirtualDir base; + VirtualDir ext; std::shared_ptr<RomFSBuildDirectoryContext> root; std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories; std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files; @@ -59,7 +60,8 @@ private: u64 file_hash_table_size = 0; u64 file_partition_size = 0; - void VisitDirectory(VirtualDir filesys, std::shared_ptr<RomFSBuildDirectoryContext> parent); + void VisitDirectory(VirtualDir filesys, VirtualDir ext, + std::shared_ptr<RomFSBuildDirectoryContext> parent); bool AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx); diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index df933ee36..0cadbc375 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp @@ -2,7 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <sstream> #include "common/assert.h" +#include "common/hex_util.h" #include "common/swap.h" #include "core/file_sys/ips_layer.h" #include "core/file_sys/vfs_vector.h" @@ -15,6 +17,12 @@ enum class IPSFileType { Error, }; +constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{ + std::pair{"\\a", "\a"}, {"\\b", "\b"}, {"\\f", "\f"}, {"\\n", "\n"}, + {"\\r", "\r"}, {"\\t", "\t"}, {"\\v", "\v"}, {"\\\\", "\\"}, + {"\\\'", "\'"}, {"\\\"", "\""}, {"\\\?", "\?"}, +}; + static IPSFileType IdentifyMagic(const std::vector<u8>& magic) { if (magic.size() != 5) return IPSFileType::Error; @@ -85,4 +93,205 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); } +IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) { + Parse(); +} + +IPSwitchCompiler::~IPSwitchCompiler() = default; + +std::array<u8, 32> IPSwitchCompiler::GetBuildID() const { + return nso_build_id; +} + +bool IPSwitchCompiler::IsValid() const { + return valid; +} + +static bool StartsWith(std::string_view base, std::string_view check) { + return base.size() >= check.size() && base.substr(0, check.size()) == check; +} + +static std::string EscapeStringSequences(std::string in) { + for (const auto& seq : ESCAPE_CHARACTER_MAP) { + for (auto index = in.find(seq.first); index != std::string::npos; + index = in.find(seq.first, index)) { + in.replace(index, std::strlen(seq.first), seq.second); + index += std::strlen(seq.second); + } + } + + return in; +} + +void IPSwitchCompiler::ParseFlag(const std::string& line) { + if (StartsWith(line, "@flag offset_shift ")) { + // Offset Shift Flag + offset_shift = std::stoll(line.substr(19), nullptr, 0); + } else if (StartsWith(line, "@little-endian")) { + // Set values to read as little endian + is_little_endian = true; + } else if (StartsWith(line, "@big-endian")) { + // Set values to read as big endian + is_little_endian = false; + } else if (StartsWith(line, "@flag print_values")) { + // Force printing of applied values + print_values = true; + } +} + +void IPSwitchCompiler::Parse() { + const auto bytes = patch_text->ReadAllBytes(); + std::stringstream s; + s.write(reinterpret_cast<const char*>(bytes.data()), bytes.size()); + + std::vector<std::string> lines; + std::string stream_line; + while (std::getline(s, stream_line)) { + // Remove a trailing \r + if (!stream_line.empty() && stream_line.back() == '\r') + stream_line.pop_back(); + lines.push_back(std::move(stream_line)); + } + + for (std::size_t i = 0; i < lines.size(); ++i) { + auto line = lines[i]; + + // Remove midline comments + std::size_t comment_index = std::string::npos; + bool within_string = false; + for (std::size_t k = 0; k < line.size(); ++k) { + if (line[k] == '\"' && (k > 0 && line[k - 1] != '\\')) { + within_string = !within_string; + } else if (line[k] == '\\' && (k < line.size() - 1 && line[k + 1] == '\\')) { + comment_index = k; + break; + } + } + + if (!StartsWith(line, "//") && comment_index != std::string::npos) { + last_comment = line.substr(comment_index + 2); + line = line.substr(0, comment_index); + } + + if (StartsWith(line, "@stop")) { + // Force stop + break; + } else if (StartsWith(line, "@nsobid-")) { + // NSO Build ID Specifier + auto raw_build_id = line.substr(8); + if (raw_build_id.size() != 0x40) + raw_build_id.resize(0x40, '0'); + nso_build_id = Common::HexStringToArray<0x20>(raw_build_id); + } else if (StartsWith(line, "#")) { + // Mandatory Comment + LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Forced output comment: {}", + patch_text->GetName(), line.substr(1)); + } else if (StartsWith(line, "//")) { + // Normal Comment + last_comment = line.substr(2); + if (last_comment.find_first_not_of(' ') == std::string::npos) + continue; + if (last_comment.find_first_not_of(' ') != 0) + last_comment = last_comment.substr(last_comment.find_first_not_of(' ')); + } else if (StartsWith(line, "@enabled") || StartsWith(line, "@disabled")) { + // Start of patch + const auto enabled = StartsWith(line, "@enabled"); + if (i == 0) + return; + LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Parsing patch '{}' ({})", + patch_text->GetName(), last_comment, line.substr(1)); + + IPSwitchPatch patch{last_comment, enabled, {}}; + + // Read rest of patch + while (true) { + if (i + 1 >= lines.size()) + break; + const auto patch_line = lines[++i]; + + // Start of new patch + if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) { + --i; + break; + } + + // Check for a flag + if (StartsWith(patch_line, "@")) { + ParseFlag(patch_line); + continue; + } + + // 11 - 8 hex digit offset + space + minimum two digit overwrite val + if (patch_line.length() < 11) + break; + auto offset = std::stoul(patch_line.substr(0, 8), nullptr, 16); + offset += offset_shift; + + std::vector<u8> replace; + // 9 - first char of replacement val + if (patch_line[9] == '\"') { + // string replacement + auto end_index = patch_line.find('\"', 10); + if (end_index == std::string::npos || end_index < 10) + return; + while (patch_line[end_index - 1] == '\\') { + end_index = patch_line.find('\"', end_index + 1); + if (end_index == std::string::npos || end_index < 10) + return; + } + + auto value = patch_line.substr(10, end_index - 10); + value = EscapeStringSequences(value); + replace.reserve(value.size()); + std::copy(value.begin(), value.end(), std::back_inserter(replace)); + } else { + // hex replacement + const auto value = patch_line.substr(9); + replace.reserve(value.size() / 2); + replace = Common::HexStringToVector(value, is_little_endian); + } + + if (print_values) { + LOG_INFO(Loader, + "[IPSwitchCompiler ('{}')] - Patching value at offset 0x{:08X} " + "with byte string '{}'", + patch_text->GetName(), offset, Common::HexVectorToString(replace)); + } + + patch.records.insert_or_assign(offset, std::move(replace)); + } + + patches.push_back(std::move(patch)); + } else if (StartsWith(line, "@")) { + ParseFlag(line); + } + } + + valid = true; +} + +VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const { + if (in == nullptr || !valid) + return nullptr; + + auto in_data = in->ReadAllBytes(); + + for (const auto& patch : patches) { + if (!patch.enabled) + continue; + + for (const auto& record : patch.records) { + if (record.first >= in_data.size()) + continue; + auto replace_size = record.second.size(); + if (record.first + replace_size > in_data.size()) + replace_size = in_data.size() - record.first; + for (std::size_t i = 0; i < replace_size; ++i) + in_data[i + record.first] = record.second[i]; + } + } + + return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); +} + } // namespace FileSys diff --git a/src/core/file_sys/ips_layer.h b/src/core/file_sys/ips_layer.h index 81c163494..57da00da8 100644 --- a/src/core/file_sys/ips_layer.h +++ b/src/core/file_sys/ips_layer.h @@ -12,4 +12,34 @@ namespace FileSys { VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips); +class IPSwitchCompiler { +public: + explicit IPSwitchCompiler(VirtualFile patch_text); + ~IPSwitchCompiler(); + + std::array<u8, 0x20> GetBuildID() const; + bool IsValid() const; + VirtualFile Apply(const VirtualFile& in) const; + +private: + void ParseFlag(const std::string& flag); + void Parse(); + + bool valid = false; + + struct IPSwitchPatch { + std::string name; + bool enabled; + std::map<u32, std::vector<u8>> records; + }; + + VirtualFile patch_text; + std::vector<IPSwitchPatch> patches; + std::array<u8, 0x20> nso_build_id{}; + bool is_little_endian = false; + s64 offset_shift = 0; + bool print_values = false; + std::string last_comment = ""; +}; + } // namespace FileSys diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 539698f6e..b14d7cb0a 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -73,27 +73,38 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { return exefs; } -static std::vector<VirtualFile> CollectIPSPatches(const std::vector<VirtualDir>& patch_dirs, - const std::string& build_id) { - std::vector<VirtualFile> ips; - ips.reserve(patch_dirs.size()); +static std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, + const std::string& build_id) { + std::vector<VirtualFile> out; + out.reserve(patch_dirs.size()); for (const auto& subdir : patch_dirs) { auto exefs_dir = subdir->GetSubdirectory("exefs"); if (exefs_dir != nullptr) { for (const auto& file : exefs_dir->GetFiles()) { - if (file->GetExtension() != "ips") - continue; - auto name = file->GetName(); - const auto p1 = name.substr(0, name.find('.')); - const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1); - - if (build_id == this_build_id) - ips.push_back(file); + if (file->GetExtension() == "ips") { + auto name = file->GetName(); + const auto p1 = name.substr(0, name.find('.')); + const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1); + + if (build_id == this_build_id) + out.push_back(file); + } else if (file->GetExtension() == "pchtxt") { + IPSwitchCompiler compiler{file}; + if (!compiler.IsValid()) + continue; + + auto this_build_id = Common::HexArrayToString(compiler.GetBuildID()); + this_build_id = + this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1); + + if (build_id == this_build_id) + out.push_back(file); + } } } } - return ips; + return out; } std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { @@ -115,15 +126,24 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { auto patch_dirs = load_dir->GetSubdirectories(); std::sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); - const auto ips = CollectIPSPatches(patch_dirs, build_id); + const auto patches = CollectPatches(patch_dirs, build_id); auto out = nso; - for (const auto& ips_file : ips) { - LOG_INFO(Loader, " - Appling IPS patch from mod \"{}\"", - ips_file->GetContainingDirectory()->GetParentDirectory()->GetName()); - const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), ips_file); - if (patched != nullptr) - out = patched->ReadAllBytes(); + for (const auto& patch_file : patches) { + if (patch_file->GetExtension() == "ips") { + LOG_INFO(Loader, " - Applying IPS patch from mod \"{}\"", + patch_file->GetContainingDirectory()->GetParentDirectory()->GetName()); + const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), patch_file); + if (patched != nullptr) + out = patched->ReadAllBytes(); + } else if (patch_file->GetExtension() == "pchtxt") { + LOG_INFO(Loader, " - Applying IPSwitch patch from mod \"{}\"", + patch_file->GetContainingDirectory()->GetParentDirectory()->GetName()); + const IPSwitchCompiler compiler{patch_file}; + const auto patched = compiler.Apply(std::make_shared<VectorVfsFile>(out)); + if (patched != nullptr) + out = patched->ReadAllBytes(); + } } if (out.size() < 0x100) @@ -143,7 +163,7 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { std::sort(patch_dirs.begin(), patch_dirs.end(), [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); - return !CollectIPSPatches(patch_dirs, build_id).empty(); + return !CollectPatches(patch_dirs, build_id).empty(); } static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { @@ -162,11 +182,17 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); std::vector<VirtualDir> layers; + std::vector<VirtualDir> layers_ext; layers.reserve(patch_dirs.size() + 1); + layers_ext.reserve(patch_dirs.size() + 1); for (const auto& subdir : patch_dirs) { auto romfs_dir = subdir->GetSubdirectory("romfs"); if (romfs_dir != nullptr) layers.push_back(std::move(romfs_dir)); + + auto ext_dir = subdir->GetSubdirectory("romfs_ext"); + if (ext_dir != nullptr) + layers_ext.push_back(std::move(ext_dir)); } layers.push_back(std::move(extracted)); @@ -175,7 +201,9 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t return; } - auto packed = CreateRomFS(std::move(layered)); + auto layered_ext = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers_ext)); + + auto packed = CreateRomFS(std::move(layered), std::move(layered_ext)); if (packed == nullptr) { return; } @@ -184,8 +212,8 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t romfs = std::move(packed); } -VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, - ContentRecordType type) const { +VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type, + VirtualFile update_raw) const { LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id, static_cast<u8>(type)); @@ -205,6 +233,13 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); romfs = new_nca->GetRomFS(); } + } else if (update_raw != nullptr) { + const auto new_nca = std::make_shared<NCA>(update_raw, romfs, ivfc_offset); + if (new_nca->GetStatus() == Loader::ResultStatus::Success && + new_nca->GetRomFS() != nullptr) { + LOG_INFO(Loader, " RomFS: Update (PACKED) applied successfully"); + romfs = new_nca->GetRomFS(); + } } // LayeredFS @@ -224,7 +259,8 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) { return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); } -std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames() const { +std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames( + VirtualFile update_raw) const { std::map<std::string, std::string, std::less<>> out; const auto installed = Service::FileSystem::GetUnionContents(); @@ -245,6 +281,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam "Update", FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements)); } + } else if (update_raw != nullptr) { + out.insert_or_assign("Update", "PACKED"); } } @@ -253,8 +291,24 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam if (mod_dir != nullptr && mod_dir->GetSize() > 0) { for (const auto& mod : mod_dir->GetSubdirectories()) { std::string types; - if (IsDirValidAndNonEmpty(mod->GetSubdirectory("exefs"))) - AppendCommaIfNotEmpty(types, "IPS"); + + const auto exefs_dir = mod->GetSubdirectory("exefs"); + if (IsDirValidAndNonEmpty(exefs_dir)) { + bool ips = false; + bool ipswitch = false; + + for (const auto& file : exefs_dir->GetFiles()) { + if (file->GetExtension() == "ips") + ips = true; + else if (file->GetExtension() == "pchtxt") + ipswitch = true; + } + + if (ips) + AppendCommaIfNotEmpty(types, "IPS"); + if (ipswitch) + AppendCommaIfNotEmpty(types, "IPSwitch"); + } if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) AppendCommaIfNotEmpty(types, "LayeredFS"); diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 6a864ec43..eb6fc4607 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -36,6 +36,7 @@ public: // Currently tracked NSO patches: // - IPS + // - IPSwitch std::vector<u8> PatchNSO(const std::vector<u8>& nso) const; // Checks to see if PatchNSO() will have any effect given the NSO's build ID. @@ -46,11 +47,13 @@ public: // - Game Updates // - LayeredFS VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, - ContentRecordType type = ContentRecordType::Program) const; + ContentRecordType type = ContentRecordType::Program, + VirtualFile update_raw = nullptr) const; // Returns a vector of pairs between patch names and patch versions. // i.e. Update 3.2.2 will return {"Update", "3.2.2"} - std::map<std::string, std::string, std::less<>> GetPatchVersionNames() const; + std::map<std::string, std::string, std::less<>> GetPatchVersionNames( + VirtualFile update_raw = nullptr) const; // Given title_id of the program, attempts to get the control data of the update and parse it, // falling back to the base control data. diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index 5910f7046..81e1f66ac 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp @@ -129,11 +129,11 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) { return out; } -VirtualFile CreateRomFS(VirtualDir dir) { +VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) { if (dir == nullptr) return nullptr; - RomFSBuildContext ctx{dir}; + RomFSBuildContext ctx{dir, ext}; return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName()); } diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h index ecd1eb725..0ec404731 100644 --- a/src/core/file_sys/romfs.h +++ b/src/core/file_sys/romfs.h @@ -44,6 +44,6 @@ VirtualDir ExtractRomFS(VirtualFile file, // Converts a VFS filesystem into a RomFS binary // Returns nullptr on failure -VirtualFile CreateRomFS(VirtualDir dir); +VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext = nullptr); } // namespace FileSys diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index 4994c2532..0b645b106 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp @@ -30,12 +30,17 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) { RomFSFactory::~RomFSFactory() = default; +void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) { + this->update_raw = std::move(update_raw); +} + ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { if (!updatable) return MakeResult<VirtualFile>(file); const PatchManager patch_manager(Core::CurrentProcess()->GetTitleID()); - return MakeResult<VirtualFile>(patch_manager.PatchRomFS(file, ivfc_offset)); + return MakeResult<VirtualFile>( + patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw)); } ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) { diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h index 2cace8180..7724c0b23 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h @@ -32,11 +32,13 @@ public: explicit RomFSFactory(Loader::AppLoader& app_loader); ~RomFSFactory(); + void SetPackedUpdate(VirtualFile update_raw); ResultVal<VirtualFile> OpenCurrentProcess(); ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type); private: VirtualFile file; + VirtualFile update_raw; bool updatable; u64 ivfc_offset; }; diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index 09bf077cd..ab5dc900c 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -259,8 +259,11 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { auto next_nca = std::make_shared<NCA>(next_file); if (next_nca->GetType() == NCAContentType::Program) program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); - if (next_nca->GetStatus() == Loader::ResultStatus::Success) + if (next_nca->GetStatus() == Loader::ResultStatus::Success || + (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && + (cnmt.GetTitleID() & 0x800) != 0)) { ncas_title[rec.type] = std::move(next_nca); + } } break; diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index 873d6c516..d4c91d529 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -17,6 +17,10 @@ namespace Kernel { ClientPort::ClientPort(KernelCore& kernel) : Object{kernel} {} ClientPort::~ClientPort() = default; +SharedPtr<ServerPort> ClientPort::GetServerPort() const { + return server_port; +} + ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { // Note: Threads do not wait for the server endpoint to call // AcceptSession before returning from this call. diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h index f3dfebbb1..6cd607206 100644 --- a/src/core/hle/kernel/client_port.h +++ b/src/core/hle/kernel/client_port.h @@ -30,6 +30,8 @@ public: return HANDLE_TYPE; } + SharedPtr<ServerPort> GetServerPort() const; + /** * Creates a new Session pair, adds the created ServerSession to the associated ServerPort's * list of pending sessions, and signals the ServerPort, causing any threads diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 62fb51349..e52f8245f 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -11,6 +11,7 @@ #include "common/common_types.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/wait_object.h" +#include "core/hle/result.h" namespace Kernel { diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 79580bcd9..0ecfb5af1 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -61,13 +61,13 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs AOC_U::~AOC_U() = default; void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { - IPC::ResponseBuilder rb{ctx, 4}; + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); - rb.Push<u32>(std::count_if(add_on_content.begin(), add_on_content.end(), [¤t](u64 tid) { - return (tid & DLC_BASE_TITLE_ID_MASK) == current; - })); + rb.Push<u32>(static_cast<u32>( + std::count_if(add_on_content.begin(), add_on_content.end(), + [¤t](u64 tid) { return (tid & DLC_BASE_TITLE_ID_MASK) == current; }))); } void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { @@ -91,7 +91,7 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { return; } - count = std::min<size_t>(out.size() - offset, count); + count = static_cast<u32>(std::min<size_t>(out.size() - offset, count)); std::rotate(out.begin(), out.begin() + offset, out.end()); out.resize(count); diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index aed2abb71..439e62d27 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -264,6 +264,15 @@ ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { return RESULT_SUCCESS; } +void SetPackedUpdate(FileSys::VirtualFile update_raw) { + LOG_TRACE(Service_FS, "Setting packed update for romfs"); + + if (romfs_factory == nullptr) + return; + + romfs_factory->SetPackedUpdate(std::move(update_raw)); +} + ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() { LOG_TRACE(Service_FS, "Opening RomFS for current process"); diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 7039a2247..53b01bb01 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -39,6 +39,7 @@ ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); +void SetPackedUpdate(FileSys::VirtualFile update_raw); ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess(); ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index d8b8037a8..7555bbe7d 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -157,7 +157,12 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); const auto itr = buffer_mappings.find(params.offset); - ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping"); + if (itr == buffer_mappings.end()) { + LOG_WARNING(Service_NVDRV, "Tried to unmap an invalid offset 0x{:X}", params.offset); + // Hardware tests shows that unmapping an already unmapped buffer always returns successful + // and doesn't fail. + return 0; + } auto& system_instance = Core::System::GetInstance(); diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index da2c51082..4f8145dda 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -6,9 +6,12 @@ #include <memory> #include <string> +#include <type_traits> #include <unordered_map> +#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/object.h" +#include "core/hle/kernel/server_port.h" #include "core/hle/result.h" #include "core/hle/service/service.h" @@ -48,6 +51,22 @@ public: ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> GetServicePort(const std::string& name); ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ConnectToService(const std::string& name); + template <typename T> + std::shared_ptr<T> GetService(const std::string& service_name) const { + static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>, + "Not a base of ServiceFrameworkBase"); + auto service = registered_services.find(service_name); + if (service == registered_services.end()) { + LOG_DEBUG(Service, "Can't find service: {}", service_name); + return nullptr; + } + auto port = service->second->GetServerPort(); + if (port == nullptr) { + return nullptr; + } + return std::static_pointer_cast<T>(port->hle_handler); + } + void InvokeControlRequest(Kernel::HLERequestContext& context); private: diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 9a86e5824..951fd8257 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <cinttypes> +#include <cstring> #include "common/common_funcs.h" #include "common/file_util.h" #include "common/logging/log.h" @@ -140,7 +141,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) const FileSys::VirtualFile module_file = dir->GetFile(module); if (module_file != nullptr) { const VAddr load_addr = next_load_addr; - next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr, pm); + next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr, + std::strcmp(module, "rtld") == 0, pm); LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); // Register module with GDBStub GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index f2a183ba1..91659ec17 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) { return "unknown"; } -constexpr std::array<const char*, 58> RESULT_MESSAGES{ +constexpr std::array<const char*, 59> RESULT_MESSAGES{ "The operation completed successfully.", "The loader requested to load is already loaded.", "The operation is not implemented.", @@ -152,6 +152,7 @@ constexpr std::array<const char*, 58> RESULT_MESSAGES{ "The BKTR-type NCA has a bad Relocation bucket.", "The BKTR-type NCA has a bad Subsection bucket.", "The BKTR-type NCA is missing the base RomFS.", + "The NSP or XCI does not contain an update in addition to the base game.", }; std::ostream& operator<<(std::ostream& os, ResultStatus status) { diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 20e66109b..0e0333db5 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -114,6 +114,7 @@ enum class ResultStatus : u16 { ErrorBadRelocationBuckets, ErrorBadSubsectionBuckets, ErrorMissingBKTRBaseRomFS, + ErrorNoPackedUpdate, }; std::ostream& operator<<(std::ostream& os, ResultStatus status); @@ -196,10 +197,19 @@ public: /** * Get the RomFS of the application * Since the RomFS can be huge, we return a file reference instead of copying to a buffer - * @param dir The directory containing the RomFS + * @param file The directory containing the RomFS * @return ResultStatus result of function */ - virtual ResultStatus ReadRomFS(FileSys::VirtualFile& dir) { + virtual ResultStatus ReadRomFS(FileSys::VirtualFile& file) { + return ResultStatus::ErrorNotImplemented; + } + + /** + * Get the raw update of the application, should it come packed with one + * @param file The raw update NCA file (Program-type + * @return ResultStatus result of function + */ + virtual ResultStatus ReadUpdateRaw(FileSys::VirtualFile& file) { return ResultStatus::ErrorNotImplemented; } diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp index 073fb9d2f..42f4a777b 100644 --- a/src/core/loader/nax.cpp +++ b/src/core/loader/nax.cpp @@ -72,6 +72,10 @@ ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) { return nca_loader->ReadRomFS(dir); } +u64 AppLoader_NAX::ReadRomFSIVFCOffset() const { + return nca_loader->ReadRomFSIVFCOffset(); +} + ResultStatus AppLoader_NAX::ReadProgramId(u64& out_program_id) { return nca_loader->ReadProgramId(out_program_id); } diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h index fc3c01876..b4d93bd01 100644 --- a/src/core/loader/nax.h +++ b/src/core/loader/nax.h @@ -36,6 +36,7 @@ public: ResultStatus Load(Kernel::Process& process) override; ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; + u64 ReadRomFSIVFCOffset() const override; ResultStatus ReadProgramId(u64& out_program_id) override; private: diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index c10f826a4..25dd3f04e 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -18,7 +18,9 @@ #include "core/hle/kernel/process.h" #include "core/hle/kernel/vm_manager.h" #include "core/loader/nro.h" +#include "core/loader/nso.h" #include "core/memory.h" +#include "core/settings.h" namespace Loader { @@ -150,6 +152,19 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { codeset->segments[i].size = PageAlignSize(nro_header.segments[i].size); } + if (!Settings::values.program_args.empty()) { + const auto arg_data = Settings::values.program_args; + codeset->DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; + NSOArgumentHeader args_header{ + NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; + const auto end_offset = program_image.size(); + program_image.resize(static_cast<u32>(program_image.size()) + + NSO_ARGUMENT_DATA_ALLOCATION_SIZE); + std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader)); + std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(), + arg_data.size()); + } + // Read MOD header ModHeader mod_header{}; // Default .bss to NRO header bss size if MOD0 section doesn't exist diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 2186b02af..28c6dd9b7 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -17,6 +17,7 @@ #include "core/hle/kernel/vm_manager.h" #include "core/loader/nso.h" #include "core/memory.h" +#include "core/settings.h" namespace Loader { @@ -94,6 +95,7 @@ static constexpr u32 PageAlignSize(u32 size) { } VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, + bool should_pass_arguments, boost::optional<FileSys::PatchManager> pm) { if (file == nullptr) return {}; @@ -125,6 +127,19 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size())); } + if (should_pass_arguments && !Settings::values.program_args.empty()) { + const auto arg_data = Settings::values.program_args; + codeset->DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; + NSOArgumentHeader args_header{ + NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; + const auto end_offset = program_image.size(); + program_image.resize(static_cast<u32>(program_image.size()) + + NSO_ARGUMENT_DATA_ALLOCATION_SIZE); + std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader)); + std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(), + arg_data.size()); + } + // MOD header pointer is at .text offset + 4 u32 module_offset; std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); @@ -172,7 +187,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) { // Load module const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); - LoadModule(file, base_address); + LoadModule(file, base_address, true); LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 05353d4d9..70ab3b718 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -11,6 +11,15 @@ namespace Loader { +constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000; + +struct NSOArgumentHeader { + u32_le allocated_size; + u32_le actual_size; + INSERT_PADDING_BYTES(0x18); +}; +static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size."); + /// Loads an NSO file class AppLoader_NSO final : public AppLoader, Linker { public: @@ -27,7 +36,7 @@ public: return IdentifyType(file); } - static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, + static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, bool should_pass_arguments, boost::optional<FileSys::PatchManager> pm = boost::none); ResultStatus Load(Kernel::Process& process) override; diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index b7ba77ef4..5534ce01c 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -10,8 +10,10 @@ #include "core/file_sys/control_metadata.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h" #include "core/file_sys/submission_package.h" #include "core/hle/kernel/process.h" +#include "core/hle/service/filesystem/filesystem.h" #include "core/loader/deconstructed_rom_directory.h" #include "core/loader/nca.h" #include "core/loader/nsp.h" @@ -91,13 +93,39 @@ ResultStatus AppLoader_NSP::Load(Kernel::Process& process) { if (result != ResultStatus::Success) return result; + FileSys::VirtualFile update_raw; + if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) + Service::FileSystem::SetPackedUpdate(std::move(update_raw)); + is_loaded = true; return ResultStatus::Success; } -ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& dir) { - return secondary_loader->ReadRomFS(dir); +ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& file) { + return secondary_loader->ReadRomFS(file); +} + +u64 AppLoader_NSP::ReadRomFSIVFCOffset() const { + return secondary_loader->ReadRomFSIVFCOffset(); +} + +ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& file) { + if (nsp->IsExtractedType()) + return ResultStatus::ErrorNoPackedUpdate; + + const auto read = + nsp->GetNCAFile(FileSys::GetUpdateTitleID(title_id), FileSys::ContentRecordType::Program); + + if (read == nullptr) + return ResultStatus::ErrorNoPackedUpdate; + const auto nca_test = std::make_shared<FileSys::NCA>(read); + + if (nca_test->GetStatus() != ResultStatus::ErrorMissingBKTRBaseRomFS) + return nca_test->GetStatus(); + + file = read; + return ResultStatus::Success; } ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) { diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index eac9b819a..b006594a6 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h @@ -37,7 +37,9 @@ public: ResultStatus Load(Kernel::Process& process) override; - ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; + ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; + u64 ReadRomFSIVFCOffset() const override; + ResultStatus ReadUpdateRaw(FileSys::VirtualFile& file) override; ResultStatus ReadProgramId(u64& out_program_id) override; ResultStatus ReadIcon(std::vector<u8>& buffer) override; ResultStatus ReadTitle(std::string& title) override; diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index eda67a8c8..ee5452eb9 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp @@ -9,7 +9,11 @@ #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h" +#include "core/file_sys/romfs.h" +#include "core/file_sys/submission_package.h" #include "core/hle/kernel/process.h" +#include "core/hle/service/filesystem/filesystem.h" #include "core/loader/nca.h" #include "core/loader/xci.h" @@ -63,13 +67,41 @@ ResultStatus AppLoader_XCI::Load(Kernel::Process& process) { if (result != ResultStatus::Success) return result; + FileSys::VirtualFile update_raw; + if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) + Service::FileSystem::SetPackedUpdate(std::move(update_raw)); + is_loaded = true; return ResultStatus::Success; } -ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& dir) { - return nca_loader->ReadRomFS(dir); +ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& file) { + return nca_loader->ReadRomFS(file); +} + +u64 AppLoader_XCI::ReadRomFSIVFCOffset() const { + return nca_loader->ReadRomFSIVFCOffset(); +} + +ResultStatus AppLoader_XCI::ReadUpdateRaw(FileSys::VirtualFile& file) { + u64 program_id{}; + nca_loader->ReadProgramId(program_id); + if (program_id == 0) + return ResultStatus::ErrorXCIMissingProgramNCA; + + const auto read = xci->GetSecurePartitionNSP()->GetNCAFile( + FileSys::GetUpdateTitleID(program_id), FileSys::ContentRecordType::Program); + + if (read == nullptr) + return ResultStatus::ErrorNoPackedUpdate; + const auto nca_test = std::make_shared<FileSys::NCA>(read); + + if (nca_test->GetStatus() != ResultStatus::ErrorMissingBKTRBaseRomFS) + return nca_test->GetStatus(); + + file = read; + return ResultStatus::Success; } ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) { diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index 17e47b658..770ed1437 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h @@ -37,7 +37,9 @@ public: ResultStatus Load(Kernel::Process& process) override; - ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; + ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; + u64 ReadRomFSIVFCOffset() const override; + ResultStatus ReadUpdateRaw(FileSys::VirtualFile& file) override; ResultStatus ReadProgramId(u64& out_program_id) override; ResultStatus ReadIcon(std::vector<u8>& buffer) override; ResultStatus ReadTitle(std::string& title) override; diff --git a/src/core/settings.h b/src/core/settings.h index 1808f5937..83b9a04c8 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -155,6 +155,7 @@ struct Values { // Debugging bool use_gdbstub; u16 gdbstub_port; + std::string program_args; // WebService bool enable_telemetry; |
