diff --git a/src/core/file_sys/ncch_container.cpp b/src/core/file_sys/ncch_container.cpp index d913c86240..de767e8409 100644 --- a/src/core/file_sys/ncch_container.cpp +++ b/src/core/file_sys/ncch_container.cpp @@ -24,6 +24,53 @@ namespace FileSys { static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs 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& ips, std::vector& 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 * @param buffer Buffer of compressed file @@ -483,6 +530,21 @@ Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vect dec.ProcessData(&buffer[0], &buffer[0], section.size); } } + + std::string override_ips = filepath + ".exefsdir/code.ips"; + + if (FileUtil::Exists(override_ips) && strcmp(name, ".code") == 0) { + FileUtil::IOFile ips_file(override_ips, "rb"); + std::size_t ips_file_size = ips_file.GetSize(); + std::vector ips(ips_file_size); + + if (ips_file.IsOpen() && + ips_file.ReadBytes(&ips[0], ips_file_size) == ips_file_size) { + LOG_INFO(Service_FS, "File {} patching code.bin", override_ips); + ApplyIPS(ips, buffer); + } + } + return Loader::ResultStatus::Success; } }