From 3a57264b673ca2a551492b1aee1a56740a7955b8 Mon Sep 17 00:00:00 2001 From: scawful Date: Tue, 6 Aug 2024 02:06:07 -0400 Subject: [PATCH] move bps patch to core/common --- src/app/core/common.cc | 204 ++++++++++++++++++++++++++++++++++++- src/app/core/common.h | 14 ++- src/cli/command_handler.h | 25 +++-- src/cli/patch.cc | 205 -------------------------------------- src/cli/patch.h | 29 ------ 5 files changed, 226 insertions(+), 251 deletions(-) delete mode 100644 src/cli/patch.cc delete mode 100644 src/cli/patch.h diff --git a/src/app/core/common.cc b/src/app/core/common.cc index d751994d..4de7edcd 100644 --- a/src/app/core/common.cc +++ b/src/app/core/common.cc @@ -1,20 +1,55 @@ #include "common.h" -#include "imgui/imgui.h" +#include #include #include +#include +#include #include +#include #include #include #include +#include #include "absl/strings/str_format.h" +#include "imgui/imgui.h" namespace yaze { namespace app { namespace core { +namespace { + +void encode(uint64_t data, std::vector &output) { + while (true) { + uint8_t x = data & 0x7f; + data >>= 7; + if (data == 0) { + output.push_back(0x80 | x); + break; + } + output.push_back(x); + data--; + } +} + +uint64_t decode(const std::vector &input, size_t &offset) { + uint64_t data = 0; + uint64_t shift = 1; + while (true) { + uint8_t x = input[offset++]; + data += (x & 0x7f) * shift; + if (x & 0x80) break; + shift <<= 7; + data += shift; + } + return data; +} + +} // namespace + std::shared_ptr ExperimentFlags::flags_; std::string UppercaseHexByte(uint8_t byte, bool leading) { @@ -175,13 +210,178 @@ uint16_t ldle16b_i(uint8_t const *const p_arr, size_t const p_index) { std::stack ImGuiIdIssuer::idStack; uint32_t Get24LocalFromPC(uint8_t *data, int addr, bool pc) { - uint32_t ret = (PcToSnes(addr) & 0xFF0000) | (data[addr + 1] << 8) | data[addr]; + uint32_t ret = + (PcToSnes(addr) & 0xFF0000) | (data[addr + 1] << 8) | data[addr]; if (pc) { return SnesToPc(ret); } return ret; } +uint32_t crc32(const std::vector &data) { + uint32_t crc = ::crc32(0L, Z_NULL, 0); + return ::crc32(crc, data.data(), data.size()); +} + +void CreateBpsPatch(const std::vector &source, + const std::vector &target, + std::vector &patch) { + patch.clear(); + patch.insert(patch.end(), {'B', 'P', 'S', '1'}); + + encode(source.size(), patch); + encode(target.size(), patch); + encode(0, patch); // No metadata + + size_t sourceOffset = 0; + size_t targetOffset = 0; + int64_t sourceRelOffset = 0; + int64_t targetRelOffset = 0; + + while (targetOffset < target.size()) { + if (sourceOffset < source.size() && + source[sourceOffset] == target[targetOffset]) { + size_t length = 0; + while (sourceOffset + length < source.size() && + targetOffset + length < target.size() && + source[sourceOffset + length] == target[targetOffset + length]) { + length++; + } + encode((length - 1) << 2 | 0, patch); // SourceRead + sourceOffset += length; + targetOffset += length; + } else { + size_t length = 0; + while (targetOffset + length < target.size() && + (sourceOffset + length >= source.size() || + source[sourceOffset + length] != target[targetOffset + length])) { + length++; + } + if (length > 0) { + encode((length - 1) << 2 | 1, patch); // TargetRead + for (size_t i = 0; i < length; i++) { + patch.push_back(target[targetOffset + i]); + } + targetOffset += length; + } + } + + // SourceCopy + if (sourceOffset < source.size()) { + size_t length = 0; + int64_t offset = sourceOffset - sourceRelOffset; + while (sourceOffset + length < source.size() && + targetOffset + length < target.size() && + source[sourceOffset + length] == target[targetOffset + length]) { + length++; + } + if (length > 0) { + encode((length - 1) << 2 | 2, patch); + encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch); + sourceOffset += length; + targetOffset += length; + sourceRelOffset = sourceOffset; + } + } + + // TargetCopy + if (targetOffset > 0) { + size_t length = 0; + int64_t offset = targetOffset - targetRelOffset; + while (targetOffset + length < target.size() && + target[targetOffset - 1] == target[targetOffset + length]) { + length++; + } + if (length > 0) { + encode((length - 1) << 2 | 3, patch); + encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch); + targetOffset += length; + targetRelOffset = targetOffset; + } + } + } + + patch.resize(patch.size() + 12); // Make space for the checksums + uint32_t sourceChecksum = crc32(source); + uint32_t targetChecksum = crc32(target); + uint32_t patchChecksum = crc32(patch); + + memcpy(patch.data() + patch.size() - 12, &sourceChecksum, sizeof(uint32_t)); + memcpy(patch.data() + patch.size() - 8, &targetChecksum, sizeof(uint32_t)); + memcpy(patch.data() + patch.size() - 4, &patchChecksum, sizeof(uint32_t)); +} + +void ApplyBpsPatch(const std::vector &source, + const std::vector &patch, + std::vector &target) { + if (patch.size() < 4 || patch[0] != 'B' || patch[1] != 'P' || + patch[2] != 'S' || patch[3] != '1') { + throw std::runtime_error("Invalid patch format"); + } + + size_t patchOffset = 4; + uint64_t sourceSize = decode(patch, patchOffset); + uint64_t targetSize = decode(patch, patchOffset); + uint64_t metadataSize = decode(patch, patchOffset); + patchOffset += metadataSize; + + target.resize(targetSize); + size_t sourceOffset = 0; + size_t targetOffset = 0; + int64_t sourceRelOffset = 0; + int64_t targetRelOffset = 0; + + while (patchOffset < patch.size() - 12) { + uint64_t data = decode(patch, patchOffset); + uint64_t command = data & 3; + uint64_t length = (data >> 2) + 1; + + switch (command) { + case 0: // SourceRead + while (length--) { + target[targetOffset++] = source[sourceOffset++]; + } + break; + case 1: // TargetRead + while (length--) { + target[targetOffset++] = patch[patchOffset++]; + } + break; + case 2: // SourceCopy + { + int64_t offsetData = decode(patch, patchOffset); + sourceRelOffset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1); + while (length--) { + target[targetOffset++] = source[sourceRelOffset++]; + } + } break; + case 3: // TargetCopy + { + uint64_t offsetData = decode(patch, patchOffset); + targetRelOffset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1); + while (length--) { + target[targetOffset++] = target[targetRelOffset++]; + } + } + default: + throw std::runtime_error("Invalid patch command"); + } + } + + uint32_t sourceChecksum; + uint32_t targetChecksum; + uint32_t patchChecksum; + memcpy(&sourceChecksum, patch.data() + patch.size() - 12, sizeof(uint32_t)); + memcpy(&targetChecksum, patch.data() + patch.size() - 8, sizeof(uint32_t)); + memcpy(&patchChecksum, patch.data() + patch.size() - 4, sizeof(uint32_t)); + + if (sourceChecksum != crc32(source) || targetChecksum != crc32(target) || + patchChecksum != + crc32(std::vector(patch.begin(), patch.end() - 4))) { + throw std::runtime_error("Checksum mismatch"); + } +} + } // namespace core } // namespace app } // namespace yaze diff --git a/src/app/core/common.h b/src/app/core/common.h index 0fd56f7b..e79bc60e 100644 --- a/src/app/core/common.h +++ b/src/app/core/common.h @@ -1,8 +1,6 @@ #ifndef YAZE_CORE_COMMON_H #define YAZE_CORE_COMMON_H -#include "imgui/imgui.h" - #include #include #include @@ -12,6 +10,8 @@ #include #include +#include "imgui/imgui.h" + namespace yaze { namespace app { @@ -227,6 +227,16 @@ typedef struct FolderItem FolderItem; uint32_t Get24LocalFromPC(uint8_t *data, int addr, bool pc = true); +uint32_t crc32(const std::vector &data); + +void CreateBpsPatch(const std::vector &source, + const std::vector &target, + std::vector &patch); + +void ApplyBpsPatch(const std::vector &source, + const std::vector &patch, + std::vector &target); + } // namespace core } // namespace app } // namespace yaze diff --git a/src/cli/command_handler.h b/src/cli/command_handler.h index 13a837ef..22a62076 100644 --- a/src/cli/command_handler.h +++ b/src/cli/command_handler.h @@ -1,28 +1,27 @@ #ifndef YAZE_CLI_COMMAND_HANDLER_H #define YAZE_CLI_COMMAND_HANDLER_H -#include // for uint8_t, uint32_t -#include // for operator<<, string, ostream, basic_... -#include // for make_shared, shared_ptr +#include +#include +#include #include -#include // for char_traits, basic_string, hash +#include #include -#include // for unordered_map -#include // for vector, vector<>::value_type +#include +#include -#include "absl/status/status.h" // for OkStatus, Status +#include "absl/status/status.h" #include "absl/strings/str_cat.h" -#include "app/core/common.h" // for PcToSnes, SnesToPc -#include "app/core/constants.h" // for RETURN_IF_ERROR +#include "app/core/common.h" +#include "app/core/constants.h" #include "app/emu/snes.h" #include "app/gfx/bitmap.h" #include "app/gfx/compression.h" #include "app/gfx/snes_palette.h" #include "app/gfx/snes_tile.h" #include "app/gui/canvas.h" -#include "app/rom.h" // for Rom +#include "app/rom.h" #include "app/zelda3/overworld/overworld.h" -#include "cli/patch.h" // for ApplyBpsPatch, CreateBpsPatch namespace yaze { namespace cli { @@ -76,7 +75,7 @@ class ApplyPatch : public CommandHandler { // Apply patch std::vector patched; - ApplyBpsPatch(source, patch, patched); + core::ApplyBpsPatch(source, patch, patched); // Save patched file std::ofstream patched_rom("patched.sfc", std::ios::binary); @@ -93,7 +92,7 @@ class CreatePatch : public CommandHandler { std::vector target; std::vector patch; // Create patch - CreateBpsPatch(source, target, patch); + core::CreateBpsPatch(source, target, patch); // Save patch to file // std::ofstream patchFile("patch.bps", ios::binary); diff --git a/src/cli/patch.cc b/src/cli/patch.cc deleted file mode 100644 index 1b6945c8..00000000 --- a/src/cli/patch.cc +++ /dev/null @@ -1,205 +0,0 @@ -#include "cli/patch.h" - -#include - -#include -#include -#include -#include -#include - -namespace yaze { -namespace cli { - -void encode(uint64_t data, std::vector& output) { - while (true) { - uint8_t x = data & 0x7f; - data >>= 7; - if (data == 0) { - output.push_back(0x80 | x); - break; - } - output.push_back(x); - data--; - } -} - -uint64_t decode(const std::vector& input, size_t& offset) { - uint64_t data = 0; - uint64_t shift = 1; - while (true) { - uint8_t x = input[offset++]; - data += (x & 0x7f) * shift; - if (x & 0x80) break; - shift <<= 7; - data += shift; - } - return data; -} - -uint32_t crc32(const std::vector& data) { - uint32_t crc = ::crc32(0L, Z_NULL, 0); - return ::crc32(crc, data.data(), data.size()); -} - -void CreateBpsPatch(const std::vector& source, - const std::vector& target, - std::vector& patch) { - patch.clear(); - patch.insert(patch.end(), {'B', 'P', 'S', '1'}); - - encode(source.size(), patch); - encode(target.size(), patch); - encode(0, patch); // No metadata - - size_t sourceOffset = 0; - size_t targetOffset = 0; - int64_t sourceRelOffset = 0; - int64_t targetRelOffset = 0; - - while (targetOffset < target.size()) { - if (sourceOffset < source.size() && - source[sourceOffset] == target[targetOffset]) { - size_t length = 0; - while (sourceOffset + length < source.size() && - targetOffset + length < target.size() && - source[sourceOffset + length] == target[targetOffset + length]) { - length++; - } - encode((length - 1) << 2 | 0, patch); // SourceRead - sourceOffset += length; - targetOffset += length; - } else { - size_t length = 0; - while (targetOffset + length < target.size() && - (sourceOffset + length >= source.size() || - source[sourceOffset + length] != target[targetOffset + length])) { - length++; - } - if (length > 0) { - encode((length - 1) << 2 | 1, patch); // TargetRead - for (size_t i = 0; i < length; i++) { - patch.push_back(target[targetOffset + i]); - } - targetOffset += length; - } - } - - // SourceCopy - if (sourceOffset < source.size()) { - size_t length = 0; - int64_t offset = sourceOffset - sourceRelOffset; - while (sourceOffset + length < source.size() && - targetOffset + length < target.size() && - source[sourceOffset + length] == target[targetOffset + length]) { - length++; - } - if (length > 0) { - encode((length - 1) << 2 | 2, patch); - encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch); - sourceOffset += length; - targetOffset += length; - sourceRelOffset = sourceOffset; - } - } - - // TargetCopy - if (targetOffset > 0) { - size_t length = 0; - int64_t offset = targetOffset - targetRelOffset; - while (targetOffset + length < target.size() && - target[targetOffset - 1] == target[targetOffset + length]) { - length++; - } - if (length > 0) { - encode((length - 1) << 2 | 3, patch); - encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch); - targetOffset += length; - targetRelOffset = targetOffset; - } - } - } - - patch.resize(patch.size() + 12); // Make space for the checksums - uint32_t sourceChecksum = crc32(source); - uint32_t targetChecksum = crc32(target); - uint32_t patchChecksum = crc32(patch); - - memcpy(patch.data() + patch.size() - 12, &sourceChecksum, sizeof(uint32_t)); - memcpy(patch.data() + patch.size() - 8, &targetChecksum, sizeof(uint32_t)); - memcpy(patch.data() + patch.size() - 4, &patchChecksum, sizeof(uint32_t)); -} - -void ApplyBpsPatch(const std::vector& source, - const std::vector& patch, - std::vector& target) { - if (patch.size() < 4 || patch[0] != 'B' || patch[1] != 'P' || - patch[2] != 'S' || patch[3] != '1') { - throw std::runtime_error("Invalid patch format"); - } - - size_t patchOffset = 4; - uint64_t sourceSize = decode(patch, patchOffset); - uint64_t targetSize = decode(patch, patchOffset); - uint64_t metadataSize = decode(patch, patchOffset); - patchOffset += metadataSize; - - target.resize(targetSize); - size_t sourceOffset = 0; - size_t targetOffset = 0; - int64_t sourceRelOffset = 0; - int64_t targetRelOffset = 0; - - while (patchOffset < patch.size() - 12) { - uint64_t data = decode(patch, patchOffset); - uint64_t command = data & 3; - uint64_t length = (data >> 2) + 1; - - switch (command) { - case 0: // SourceRead - while (length--) { - target[targetOffset++] = source[sourceOffset++]; - } - break; - case 1: // TargetRead - while (length--) { - target[targetOffset++] = patch[patchOffset++]; - } - break; - case 2: // SourceCopy - { - int64_t offsetData = decode(patch, patchOffset); - sourceRelOffset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1); - while (length--) { - target[targetOffset++] = source[sourceRelOffset++]; - } - } break; - case 3: // TargetCopy - { - uint64_t offsetData = decode(patch, patchOffset); - targetRelOffset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1); - while (length--) { - target[targetOffset++] = target[targetRelOffset++]; - } - } - default: - throw std::runtime_error("Invalid patch command"); - } - } - - uint32_t sourceChecksum; - uint32_t targetChecksum; - uint32_t patchChecksum; - memcpy(&sourceChecksum, patch.data() + patch.size() - 12, sizeof(uint32_t)); - memcpy(&targetChecksum, patch.data() + patch.size() - 8, sizeof(uint32_t)); - memcpy(&patchChecksum, patch.data() + patch.size() - 4, sizeof(uint32_t)); - - if (sourceChecksum != crc32(source) || targetChecksum != crc32(target) || - patchChecksum != - crc32(std::vector(patch.begin(), patch.end() - 4))) { - throw std::runtime_error("Checksum mismatch"); - } -} - -} // namespace cli -} // namespace yaze \ No newline at end of file diff --git a/src/cli/patch.h b/src/cli/patch.h deleted file mode 100644 index c9595be7..00000000 --- a/src/cli/patch.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef YAZE_CLI_PATCH_H -#define YAZE_CLI_PATCH_H - -#include -#include -#include -#include -#include - -namespace yaze { -namespace cli { - -void encode(uint64_t data, std::vector& output); - -uint64_t decode(const std::vector& input, size_t& offset); - -uint32_t crc32(const std::vector& data); - -void CreateBpsPatch(const std::vector& source, - const std::vector& target, - std::vector& patch); - -void ApplyBpsPatch(const std::vector& source, - const std::vector& patch, - std::vector& target); - -} // namespace cli -} // namespace yaze -#endif \ No newline at end of file