file_sys: Move IPS patching code into separate source file

In anticipation of a new BPS patcher.
This commit is contained in:
Léo Lam 2019-11-24 20:32:10 +01:00
parent 020cd56ad8
commit 1377be9902
6 changed files with 78 additions and 52 deletions

View file

@ -74,6 +74,8 @@ add_library(core STATIC
file_sys/ivfc_archive.h file_sys/ivfc_archive.h
file_sys/ncch_container.cpp file_sys/ncch_container.cpp
file_sys/ncch_container.h file_sys/ncch_container.h
file_sys/patch.cpp
file_sys/patch.h
file_sys/path_parser.cpp file_sys/path_parser.cpp
file_sys/path_parser.h file_sys/path_parser.h
file_sys/romfs_reader.cpp file_sys/romfs_reader.cpp

View file

@ -12,6 +12,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/ncch_container.h" #include "core/file_sys/ncch_container.h"
#include "core/file_sys/patch.h"
#include "core/file_sys/seed_db.h" #include "core/file_sys/seed_db.h"
#include "core/hw/aes/key.h" #include "core/hw/aes/key.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
@ -24,53 +25,6 @@ namespace FileSys {
static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs
static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes) static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
/**
* Attempts to patch a buffer using an IPS
* @param ips Vector of the patches to apply
* @param buffer Vector to patch data into
*/
static void ApplyIPS(std::vector<u8>& ips, std::vector<u8>& buffer) {
u32 cursor = 5;
u32 patch_length = ips.size() - 3;
std::string ips_header(ips.begin(), ips.begin() + 5);
if (ips_header != "PATCH") {
LOG_INFO(Service_FS, "Attempted to load invalid IPS");
return;
}
while (cursor < patch_length) {
std::string eof_check(ips.begin() + cursor, ips.begin() + cursor + 3);
if (eof_check == "EOF")
return;
u32 offset = ips[cursor] << 16 | ips[cursor + 1] << 8 | ips[cursor + 2];
std::size_t length = ips[cursor + 3] << 8 | ips[cursor + 4];
// check for an rle record
if (length == 0) {
length = ips[cursor + 5] << 8 | ips[cursor + 6];
if (buffer.size() < offset + length)
return;
for (u32 i = 0; i < length; ++i)
buffer[offset + i] = ips[cursor + 7];
cursor += 8;
continue;
}
if (buffer.size() < offset + length)
return;
std::memcpy(&buffer[offset], &ips[cursor + 5], length);
cursor += length + 5;
}
}
/** /**
* Get the decompressed size of an LZSS compressed ExeFS file * Get the decompressed size of an LZSS compressed ExeFS file
* @param buffer Buffer of compressed file * @param buffer Buffer of compressed file
@ -553,7 +507,7 @@ Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vect
return Loader::ResultStatus::ErrorNotUsed; return Loader::ResultStatus::ErrorNotUsed;
} }
bool NCCHContainer::ApplyIPSPatch(std::vector<u8>& code) const { bool NCCHContainer::ApplyCodePatch(std::vector<u8>& code) const {
const std::string override_ips = filepath + ".exefsdir/code.ips"; const std::string override_ips = filepath + ".exefsdir/code.ips";
FileUtil::IOFile ips_file{override_ips, "rb"}; FileUtil::IOFile ips_file{override_ips, "rb"};
@ -565,7 +519,7 @@ bool NCCHContainer::ApplyIPSPatch(std::vector<u8>& code) const {
return false; return false;
LOG_INFO(Service_FS, "File {} patching code.bin", override_ips); LOG_INFO(Service_FS, "File {} patching code.bin", override_ips);
ApplyIPS(ips, code); Patch::ApplyIpsPatch(ips, code);
return true; return true;
} }

View file

@ -272,11 +272,11 @@ public:
Loader::ResultStatus ReadExtdataId(u64& extdata_id); Loader::ResultStatus ReadExtdataId(u64& extdata_id);
/** /**
* Apply an IPS patch for .code (if it exists). * Apply a patch for .code (if it exists).
* This should only be called after allocating .bss. * This should only be called after allocating .bss.
* @return bool true if a patch was applied, false otherwise * @return bool true if a patch was applied, false otherwise
*/ */
bool ApplyIPSPatch(std::vector<u8>& code) const; bool ApplyCodePatch(std::vector<u8>& code) const;
/** /**
* Checks whether the NCCH container contains an ExeFS * Checks whether the NCCH container contains an ExeFS

View file

@ -0,0 +1,55 @@
// Copyright 2019 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <string>
#include "common/logging/log.h"
#include "core/file_sys/patch.h"
namespace FileSys::Patch {
bool ApplyIpsPatch(const std::vector<u8>& ips, std::vector<u8>& buffer) {
u32 cursor = 5;
u32 patch_length = ips.size() - 3;
std::string ips_header(ips.begin(), ips.begin() + 5);
if (ips_header != "PATCH") {
LOG_INFO(Service_FS, "Attempted to load invalid IPS");
return false;
}
while (cursor < patch_length) {
std::string eof_check(ips.begin() + cursor, ips.begin() + cursor + 3);
if (eof_check == "EOF")
return false;
u32 offset = ips[cursor] << 16 | ips[cursor + 1] << 8 | ips[cursor + 2];
std::size_t length = ips[cursor + 3] << 8 | ips[cursor + 4];
// check for an rle record
if (length == 0) {
length = ips[cursor + 5] << 8 | ips[cursor + 6];
if (buffer.size() < offset + length)
return false;
for (u32 i = 0; i < length; ++i)
buffer[offset + i] = ips[cursor + 7];
cursor += 8;
continue;
}
if (buffer.size() < offset + length)
return false;
std::memcpy(&buffer[offset], &ips[cursor + 5], length);
cursor += length + 5;
}
return true;
}
} // namespace FileSys::Patch

15
src/core/file_sys/patch.h Normal file
View file

@ -0,0 +1,15 @@
// Copyright 2019 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "common/common_types.h"
namespace FileSys::Patch {
bool ApplyIpsPatch(const std::vector<u8>& patch, std::vector<u8>& buffer);
} // namespace FileSys::Patch

View file

@ -101,7 +101,7 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process)
bss_page_size; bss_page_size;
// Apply any IPS patch now that the entire codeset (including .bss) has been allocated // Apply any IPS patch now that the entire codeset (including .bss) has been allocated
overlay_ncch->ApplyIPSPatch(code); overlay_ncch->ApplyCodePatch(code);
codeset->entrypoint = codeset->CodeSegment().addr; codeset->entrypoint = codeset->CodeSegment().addr;
codeset->memory = std::move(code); codeset->memory = std::move(code);