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.
master
Zach Hilman 2018-10-04 12:23:18 +07:00
parent 70bd2bb1d3
commit 110d578470
3 changed files with 51 additions and 16 deletions

@ -123,6 +123,22 @@ static std::string EscapeStringSequences(std::string in) {
return in; 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() { void IPSwitchCompiler::Parse() {
const auto bytes = patch_text->ReadAllBytes(); const auto bytes = patch_text->ReadAllBytes();
std::stringstream s; std::stringstream s;
@ -141,7 +157,17 @@ void IPSwitchCompiler::Parse() {
auto line = lines[i]; auto line = lines[i];
// Remove midline comments // Remove midline comments
const auto comment_index = line.find("//"); 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) { if (!StartsWith(line, "//") && comment_index != std::string::npos) {
last_comment = line.substr(comment_index + 2); last_comment = line.substr(comment_index + 2);
line = line.substr(0, comment_index); line = line.substr(0, comment_index);
@ -156,9 +182,6 @@ void IPSwitchCompiler::Parse() {
if (raw_build_id.size() != 0x40) if (raw_build_id.size() != 0x40)
raw_build_id.resize(0x40, '0'); raw_build_id.resize(0x40, '0');
nso_build_id = Common::HexStringToArray<0x20>(raw_build_id); nso_build_id = Common::HexStringToArray<0x20>(raw_build_id);
} else if (StartsWith(line, "@flag offset_shift ")) {
// Offset Shift Flag
offset_shift = std::stoll(line.substr(19), nullptr, 0);
} else if (StartsWith(line, "#")) { } else if (StartsWith(line, "#")) {
// Mandatory Comment // Mandatory Comment
LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Forced output comment: {}", LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Forced output comment: {}",
@ -170,15 +193,6 @@ void IPSwitchCompiler::Parse() {
continue; continue;
if (last_comment.find_first_not_of(' ') != 0) if (last_comment.find_first_not_of(' ') != 0)
last_comment = last_comment.substr(last_comment.find_first_not_of(' ')); last_comment = last_comment.substr(last_comment.find_first_not_of(' '));
} 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;
} else if (StartsWith(line, "@enabled") || StartsWith(line, "@disabled")) { } else if (StartsWith(line, "@enabled") || StartsWith(line, "@disabled")) {
// Start of patch // Start of patch
const auto enabled = StartsWith(line, "@enabled"); const auto enabled = StartsWith(line, "@enabled");
@ -195,6 +209,18 @@ void IPSwitchCompiler::Parse() {
break; break;
const auto patch_line = lines[++i]; 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 // 11 - 8 hex digit offset + space + minimum two digit overwrite val
if (patch_line.length() < 11) if (patch_line.length() < 11)
break; break;
@ -205,9 +231,15 @@ void IPSwitchCompiler::Parse() {
// 9 - first char of replacement val // 9 - first char of replacement val
if (patch_line[9] == '\"') { if (patch_line[9] == '\"') {
// string replacement // string replacement
const auto end_index = patch_line.find('\"', 10); auto end_index = patch_line.find('\"', 10);
if (end_index == std::string::npos || end_index < 10) if (end_index == std::string::npos || end_index < 10)
return; 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); auto value = patch_line.substr(10, end_index - 10);
value = EscapeStringSequences(value); value = EscapeStringSequences(value);
replace.reserve(value.size()); replace.reserve(value.size());
@ -226,10 +258,12 @@ void IPSwitchCompiler::Parse() {
patch_text->GetName(), offset, Common::HexVectorToString(replace)); patch_text->GetName(), offset, Common::HexVectorToString(replace));
} }
patch.records.emplace(offset, std::move(replace)); patch.records.insert_or_assign(offset, std::move(replace));
} }
patches.push_back(std::move(patch)); patches.push_back(std::move(patch));
} else if (StartsWith(line, "@")) {
ParseFlag(line);
} }
} }

@ -22,6 +22,7 @@ public:
VirtualFile Apply(const VirtualFile& in) const; VirtualFile Apply(const VirtualFile& in) const;
private: private:
void ParseFlag(const std::string& flag);
void Parse(); void Parse();
bool valid = false; bool valid = false;

@ -76,7 +76,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
static std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, static std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) { const std::string& build_id) {
std::vector<VirtualFile> out; std::vector<VirtualFile> out;
ips.reserve(patch_dirs.size()); out.reserve(patch_dirs.size());
for (const auto& subdir : patch_dirs) { for (const auto& subdir : patch_dirs) {
auto exefs_dir = subdir->GetSubdirectory("exefs"); auto exefs_dir = subdir->GetSubdirectory("exefs");
if (exefs_dir != nullptr) { if (exefs_dir != nullptr) {