Compare commits

..

12 Commits

Author SHA1 Message Date
Lioncash
6636f3ff47 patch_manager: Return a std::unique_ptr from ParseControlNCA() and GetControlMetadata() instead of a std::shared_ptr
Neither of these functions require the use of shared ownership of the
returned pointer. This makes it more difficult to create reference
cycles with, and makes the interface more generic, as std::shared_ptr
instances can be created from a std::unique_ptr, but the vice-versa
isn't possible. This also alters relevant functions to take NCA
arguments by const reference rather than a const reference to a
std::shared_ptr. These functions don't alter the ownership of the memory
used by the NCA instance, so we can make the interface more generic by
not assuming anything about the type of smart pointer the NCA is
contained within and make it the caller's responsibility to ensure the
supplied NCA is valid.
2018-10-09 14:38:03 -04:00
bunnei
561d79e034 Merge pull request #1423 from DarkLordZach/romfs-file-exts
fsmitm_romfsbuild: Add support for stubbing and IPS patches in LFS
2018-10-08 12:31:27 -04:00
bunnei
6b48ba5271 Merge pull request #1424 from DarkLordZach/ips-witch
ips_layer: Add support for IPSwitch executable patches
2018-10-08 12:30:33 -04:00
Zach Hilman
d0e6b93695 patch_manager: Avoid romfs_ext requirement for patching 2018-10-04 14:09:11 -04:00
Zach Hilman
c1e069c066 fsmitm_romfsbuild: Extract stubs and IPS to romfs_ext dir 2018-10-04 12:29:20 -04:00
Zach Hilman
bc4bec8a60 fsmitm_romfsbuild: Add support for stubbing and IPS patches in LFS 2018-10-04 12:29:14 -04:00
Zach Hilman
110d578470 ips_layer: Fix inaccuracies with comments and flags
Specifically bugs/crashes that arise when putting them in positions that are legal but not typical, such as midline, between patch data, or between patch records.
2018-10-04 12:23:27 -04:00
Zach Hilman
70bd2bb1d3 ips_layer: Deduplicate resource usage 2018-10-04 11:34:36 -04:00
Zach Hilman
9669cdb710 ips_layer: Add support for escape sequences and midline comments
More accurately follows IPSwitch specification.
2018-10-04 11:34:30 -04:00
Zach Hilman
8886f2e55e patch_manager: Add support for IPSwitch format patches 2018-10-04 11:34:06 -04:00
Zach Hilman
306739c2c4 ips_layer: Add IPSwitchCompiler to process IPSwitch format 2018-10-04 11:32:10 -04:00
Zach Hilman
f62227aa95 hex_util: Add HexVectorToString and HexStringToVector
Converts between bytes and strings when the size is not known at compile time.
2018-10-04 11:32:04 -04:00
16 changed files with 383 additions and 73 deletions

View File

@@ -18,6 +18,25 @@ u8 ToHexNibble(char c1) {
return 0;
}
std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
std::vector<u8> out(str.size() / 2);
if (little_endian) {
for (std::size_t i = str.size() - 2; i <= str.size(); i -= 2)
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
} else {
for (std::size_t i = 0; i < str.size(); i += 2)
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
}
return out;
}
std::string HexVectorToString(const std::vector<u8>& vector, bool upper) {
std::string out;
for (u8 c : vector)
out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
return out;
}
std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
if (len != 32) {
LOG_ERROR(Common,

View File

@@ -7,6 +7,7 @@
#include <array>
#include <cstddef>
#include <string>
#include <vector>
#include <fmt/format.h>
#include "common/common_types.h"
@@ -14,6 +15,8 @@ namespace Common {
u8 ToHexNibble(char c1);
std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
template <std::size_t Size, bool le = false>
std::array<u8, Size> HexStringToArray(std::string_view str) {
std::array<u8, Size> out{};
@@ -27,6 +30,8 @@ std::array<u8, Size> HexStringToArray(std::string_view str) {
return out;
}
std::string HexVectorToString(const std::vector<u8>& vector, bool upper = true);
template <std::size_t Size>
std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
std::string out;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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 (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)
ips.push_back(file);
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;
}
@@ -263,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");
@@ -301,23 +345,22 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
return out;
}
std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
const auto& installed{Service::FileSystem::GetUnionContents()};
const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
if (base_control_nca == nullptr)
return {};
return ParseControlNCA(base_control_nca);
return ParseControlNCA(*base_control_nca);
}
std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
const std::shared_ptr<NCA>& nca) const {
const auto base_romfs = nca->GetRomFS();
std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const {
const auto base_romfs = nca.GetRomFS();
if (base_romfs == nullptr)
return {};
const auto romfs = PatchRomFS(base_romfs, nca->GetBaseIVFCOffset(), ContentRecordType::Control);
const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control);
if (romfs == nullptr)
return {};
@@ -329,7 +372,7 @@ std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
if (nacp_file == nullptr)
nacp_file = extracted->GetFile("Control.nacp");
const auto nacp = nacp_file == nullptr ? nullptr : std::make_shared<NACP>(nacp_file);
auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file);
VirtualFile icon_file;
for (const auto& language : FileSys::LANGUAGE_NAMES) {
@@ -338,6 +381,6 @@ std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
break;
}
return {nacp, icon_file};
return {std::move(nacp), icon_file};
}
} // namespace FileSys

View File

@@ -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.
@@ -56,11 +57,10 @@ public:
// 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.
std::pair<std::shared_ptr<NACP>, VirtualFile> GetControlMetadata() const;
std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const;
// Version of GetControlMetadata that takes an arbitrary NCA
std::pair<std::shared_ptr<NACP>, VirtualFile> ParseControlNCA(
const std::shared_ptr<NCA>& nca) const;
std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const;
private:
u64 title_id;

View File

@@ -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());
}

View File

@@ -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

View File

@@ -301,28 +301,13 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
return Mutex::Release(mutex_addr);
}
struct BreakReason {
union {
u64 raw;
BitField<31, 1, u64> dont_kill_application;
};
};
/// Break program execution
static void Break(u64 reason, u64 info1, u64 info2) {
BreakReason break_reason{reason};
if (break_reason.dont_kill_application) {
LOG_ERROR(
Debug_Emulated,
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
} else {
LOG_CRITICAL(
Debug_Emulated,
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
ASSERT(false);
}
LOG_CRITICAL(
Debug_Emulated,
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
ASSERT(false);
}
/// Used to output a message on a debug hardware unit - does nothing on a retail unit

View File

@@ -35,7 +35,7 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
return;
std::tie(nacp_file, icon_file) =
FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(control_nca);
FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
}
AppLoader_NSP::~AppLoader_NSP() = default;

View File

@@ -49,7 +49,7 @@ private:
std::unique_ptr<AppLoader> secondary_loader;
FileSys::VirtualFile icon_file;
std::shared_ptr<FileSys::NACP> nacp_file;
std::unique_ptr<FileSys::NACP> nacp_file;
u64 title_id;
};

View File

@@ -30,7 +30,7 @@ AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
return;
std::tie(nacp_file, icon_file) =
FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(control_nca);
FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(*control_nca);
}
AppLoader_XCI::~AppLoader_XCI() = default;

View File

@@ -49,7 +49,7 @@ private:
std::unique_ptr<AppLoader_NCA> nca_loader;
FileSys::VirtualFile icon_file;
std::shared_ptr<FileSys::NACP> nacp_file;
std::unique_ptr<FileSys::NACP> nacp_file;
};
} // namespace Loader

View File

@@ -27,9 +27,8 @@
#include "yuzu/ui_settings.h"
namespace {
void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager,
const std::shared_ptr<FileSys::NCA>& nca, std::vector<u8>& icon,
std::string& name) {
void GetMetadataFromControlNCA(const FileSys::PatchManager& patch_manager, const FileSys::NCA& nca,
std::vector<u8>& icon, std::string& name) {
auto [nacp, icon_file] = patch_manager.ParseControlNCA(nca);
if (icon_file != nullptr)
icon = icon_file->ReadAllBytes();
@@ -110,7 +109,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
const FileSys::PatchManager patch{program_id};
const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);
if (control != nullptr)
GetMetadataFromControlNCA(patch, control, icon, name);
GetMetadataFromControlNCA(patch, *control, icon, name);
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
@@ -197,8 +196,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
res2 == Loader::ResultStatus::Success) {
// Use from metadata pool.
if (nca_control_map.find(program_id) != nca_control_map.end()) {
const auto nca = nca_control_map[program_id];
GetMetadataFromControlNCA(patch, nca, icon, name);
const auto& nca = nca_control_map[program_id];
GetMetadataFromControlNCA(patch, *nca, icon, name);
}
}