Add BPS patch utility functions and integrate into CLI handlers
This commit is contained in:
@@ -14,6 +14,11 @@ set(
|
|||||||
app/emu/snes.cc
|
app/emu/snes.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(
|
||||||
|
YAZE_UTIL_SRC
|
||||||
|
util/bps.cc
|
||||||
|
)
|
||||||
|
|
||||||
set(YAZE_RESOURCE_FILES
|
set(YAZE_RESOURCE_FILES
|
||||||
${CMAKE_SOURCE_DIR}/assets/layouts/overworld.zeml
|
${CMAKE_SOURCE_DIR}/assets/layouts/overworld.zeml
|
||||||
${CMAKE_SOURCE_DIR}/assets/font/Karla-Regular.ttf
|
${CMAKE_SOURCE_DIR}/assets/font/Karla-Regular.ttf
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ if (APPLE)
|
|||||||
${YAZE_APP_EDITOR_SRC}
|
${YAZE_APP_EDITOR_SRC}
|
||||||
${YAZE_APP_GFX_SRC}
|
${YAZE_APP_GFX_SRC}
|
||||||
${YAZE_APP_ZELDA3_SRC}
|
${YAZE_APP_ZELDA3_SRC}
|
||||||
|
${YAZE_UTIL_SRC}
|
||||||
${YAZE_GUI_SRC}
|
${YAZE_GUI_SRC}
|
||||||
${IMGUI_SRC}
|
${IMGUI_SRC}
|
||||||
# Bundled Resources
|
# Bundled Resources
|
||||||
@@ -30,6 +31,7 @@ else()
|
|||||||
${YAZE_APP_EDITOR_SRC}
|
${YAZE_APP_EDITOR_SRC}
|
||||||
${YAZE_APP_GFX_SRC}
|
${YAZE_APP_GFX_SRC}
|
||||||
${YAZE_APP_ZELDA3_SRC}
|
${YAZE_APP_ZELDA3_SRC}
|
||||||
|
${YAZE_UTIL_SRC}
|
||||||
${YAZE_GUI_SRC}
|
${YAZE_GUI_SRC}
|
||||||
${IMGUI_SRC}
|
${IMGUI_SRC}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#include <zlib.h>
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -15,37 +13,6 @@ namespace core {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void encode(uint64_t data, std::vector<uint8_t> &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<uint8_t> &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<uint8_t> &data) {
|
|
||||||
uint32_t crc = ::crc32(0L, Z_NULL, 0);
|
|
||||||
return ::crc32(crc, data.data(), data.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// "load little endian value at the given byte offset and shift to get its
|
// "load little endian value at the given byte offset and shift to get its
|
||||||
// value relative to the base offset (powers of 256, essentially)"
|
// value relative to the base offset (powers of 256, essentially)"
|
||||||
unsigned ldle(uint8_t const *const p_arr, unsigned const p_index) {
|
unsigned ldle(uint8_t const *const p_arr, unsigned const p_index) {
|
||||||
@@ -197,164 +164,5 @@ uint16_t ldle16b_i(uint8_t const *const p_arr, size_t const p_index) {
|
|||||||
return ldle16b(p_arr + (2 * p_index));
|
return ldle16b(p_arr + (2 * p_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateBpsPatch(const std::vector<uint8_t> &source,
|
|
||||||
const std::vector<uint8_t> &target,
|
|
||||||
std::vector<uint8_t> &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 source_offset = 0;
|
|
||||||
size_t target_offset = 0;
|
|
||||||
int64_t source_rel_offset = 0;
|
|
||||||
int64_t target_rel_offset = 0;
|
|
||||||
|
|
||||||
while (target_offset < target.size()) {
|
|
||||||
if (source_offset < source.size() &&
|
|
||||||
source[source_offset] == target[target_offset]) {
|
|
||||||
size_t length = 0;
|
|
||||||
while (source_offset + length < source.size() &&
|
|
||||||
target_offset + length < target.size() &&
|
|
||||||
source[source_offset + length] == target[target_offset + length]) {
|
|
||||||
length++;
|
|
||||||
}
|
|
||||||
encode((length - 1) << 2 | 0, patch); // SourceRead
|
|
||||||
source_offset += length;
|
|
||||||
target_offset += length;
|
|
||||||
} else {
|
|
||||||
size_t length = 0;
|
|
||||||
while (
|
|
||||||
target_offset + length < target.size() &&
|
|
||||||
(source_offset + length >= source.size() ||
|
|
||||||
source[source_offset + length] != target[target_offset + length])) {
|
|
||||||
length++;
|
|
||||||
}
|
|
||||||
if (length > 0) {
|
|
||||||
encode((length - 1) << 2 | 1, patch); // TargetRead
|
|
||||||
for (size_t i = 0; i < length; i++) {
|
|
||||||
patch.push_back(target[target_offset + i]);
|
|
||||||
}
|
|
||||||
target_offset += length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SourceCopy
|
|
||||||
if (source_offset < source.size()) {
|
|
||||||
size_t length = 0;
|
|
||||||
int64_t offset = source_offset - source_rel_offset;
|
|
||||||
while (source_offset + length < source.size() &&
|
|
||||||
target_offset + length < target.size() &&
|
|
||||||
source[source_offset + length] == target[target_offset + length]) {
|
|
||||||
length++;
|
|
||||||
}
|
|
||||||
if (length > 0) {
|
|
||||||
encode((length - 1) << 2 | 2, patch);
|
|
||||||
encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
|
|
||||||
source_offset += length;
|
|
||||||
target_offset += length;
|
|
||||||
source_rel_offset = source_offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TargetCopy
|
|
||||||
if (target_offset > 0) {
|
|
||||||
size_t length = 0;
|
|
||||||
int64_t offset = target_offset - target_rel_offset;
|
|
||||||
while (target_offset + length < target.size() &&
|
|
||||||
target[target_offset - 1] == target[target_offset + length]) {
|
|
||||||
length++;
|
|
||||||
}
|
|
||||||
if (length > 0) {
|
|
||||||
encode((length - 1) << 2 | 3, patch);
|
|
||||||
encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
|
|
||||||
target_offset += length;
|
|
||||||
target_rel_offset = target_offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
patch.resize(patch.size() + 12); // Make space for the checksums
|
|
||||||
uint32_t source_checksum = crc32(source);
|
|
||||||
uint32_t target_checksum = crc32(target);
|
|
||||||
uint32_t patch_checksum = crc32(patch);
|
|
||||||
|
|
||||||
memcpy(patch.data() + patch.size() - 12, &source_checksum, sizeof(uint32_t));
|
|
||||||
memcpy(patch.data() + patch.size() - 8, &target_checksum, sizeof(uint32_t));
|
|
||||||
memcpy(patch.data() + patch.size() - 4, &patch_checksum, sizeof(uint32_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ApplyBpsPatch(const std::vector<uint8_t> &source,
|
|
||||||
const std::vector<uint8_t> &patch,
|
|
||||||
std::vector<uint8_t> &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 patch_offset = 4;
|
|
||||||
uint64_t target_size = decode(patch, patch_offset);
|
|
||||||
uint64_t metadata_size = decode(patch, patch_offset);
|
|
||||||
patch_offset += metadata_size;
|
|
||||||
|
|
||||||
target.resize(target_size);
|
|
||||||
size_t source_offset = 0;
|
|
||||||
size_t target_offset = 0;
|
|
||||||
int64_t source_rel_offset = 0;
|
|
||||||
int64_t target_rel_offset = 0;
|
|
||||||
|
|
||||||
while (patch_offset < patch.size() - 12) {
|
|
||||||
uint64_t data = decode(patch, patch_offset);
|
|
||||||
uint64_t command = data & 3;
|
|
||||||
uint64_t length = (data >> 2) + 1;
|
|
||||||
|
|
||||||
switch (command) {
|
|
||||||
case 0: // SourceRead
|
|
||||||
while (length--) {
|
|
||||||
target[target_offset++] = source[source_offset++];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 1: // TargetRead
|
|
||||||
while (length--) {
|
|
||||||
target[target_offset++] = patch[patch_offset++];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 2: // SourceCopy
|
|
||||||
{
|
|
||||||
int64_t offsetData = decode(patch, patch_offset);
|
|
||||||
source_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
|
|
||||||
while (length--) {
|
|
||||||
target[target_offset++] = source[source_rel_offset++];
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case 3: // TargetCopy
|
|
||||||
{
|
|
||||||
uint64_t offsetData = decode(patch, patch_offset);
|
|
||||||
target_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
|
|
||||||
while (length--) {
|
|
||||||
target[target_offset++] = target[target_rel_offset++];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("Invalid patch command");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t source_checksum;
|
|
||||||
uint32_t target_checksum;
|
|
||||||
uint32_t patch_checksum;
|
|
||||||
memcpy(&source_checksum, patch.data() + patch.size() - 12, sizeof(uint32_t));
|
|
||||||
memcpy(&target_checksum, patch.data() + patch.size() - 8, sizeof(uint32_t));
|
|
||||||
memcpy(&patch_checksum, patch.data() + patch.size() - 4, sizeof(uint32_t));
|
|
||||||
|
|
||||||
if (source_checksum != crc32(source) || target_checksum != crc32(target) ||
|
|
||||||
patch_checksum !=
|
|
||||||
crc32(std::vector<uint8_t>(patch.begin(), patch.end() - 4))) {
|
|
||||||
throw std::runtime_error("Checksum mismatch");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace core
|
} // namespace core
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "absl/strings/str_format.h"
|
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
|
#include "absl/strings/str_format.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
|
|
||||||
@@ -180,9 +180,9 @@ static void logf(const absl::FormatSpec<Args...> &format, const Args &...args) {
|
|||||||
|
|
||||||
std::time_t now_tt = std::chrono::system_clock::to_time_t(timestamp);
|
std::time_t now_tt = std::chrono::system_clock::to_time_t(timestamp);
|
||||||
std::tm tm = *std::localtime(&now_tt);
|
std::tm tm = *std::localtime(&now_tt);
|
||||||
message = absl::StrCat("[", tm.tm_hour, ":", tm.tm_min, ":", tm.tm_sec, "] ",
|
message = absl::StrCat("[", tm.tm_hour, ":", tm.tm_min, ":", tm.tm_sec, "] ",
|
||||||
message, "\n");
|
message, "\n");
|
||||||
|
|
||||||
if (ExperimentFlags::get().kLogToConsole) {
|
if (ExperimentFlags::get().kLogToConsole) {
|
||||||
std::cout << message;
|
std::cout << message;
|
||||||
}
|
}
|
||||||
@@ -252,14 +252,6 @@ struct FolderItem {
|
|||||||
|
|
||||||
typedef struct FolderItem FolderItem;
|
typedef struct FolderItem FolderItem;
|
||||||
|
|
||||||
void CreateBpsPatch(const std::vector<uint8_t> &source,
|
|
||||||
const std::vector<uint8_t> &target,
|
|
||||||
std::vector<uint8_t> &patch);
|
|
||||||
|
|
||||||
void ApplyBpsPatch(const std::vector<uint8_t> &source,
|
|
||||||
const std::vector<uint8_t> &patch,
|
|
||||||
std::vector<uint8_t> &target);
|
|
||||||
|
|
||||||
} // namespace core
|
} // namespace core
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ add_executable(
|
|||||||
${YAZE_APP_EDITOR_SRC}
|
${YAZE_APP_EDITOR_SRC}
|
||||||
${YAZE_APP_GFX_SRC}
|
${YAZE_APP_GFX_SRC}
|
||||||
${YAZE_APP_ZELDA3_SRC}
|
${YAZE_APP_ZELDA3_SRC}
|
||||||
|
${YAZE_UTIL_SRC}
|
||||||
${YAZE_GUI_SRC}
|
${YAZE_GUI_SRC}
|
||||||
${IMGUI_SRC}
|
${IMGUI_SRC}
|
||||||
)
|
)
|
||||||
@@ -35,4 +36,8 @@ target_link_libraries(
|
|||||||
${CMAKE_DL_LIBS}
|
${CMAKE_DL_LIBS}
|
||||||
ImGui
|
ImGui
|
||||||
ImGuiTestEngine
|
ImGuiTestEngine
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
target_link_libraries(yaze_emu PUBLIC ${COCOA_LIBRARY})
|
||||||
|
endif()
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include "asar-dll-bindings/c/asar.h"
|
#include "asar-dll-bindings/c/asar.h"
|
||||||
|
|
||||||
|
#include "util/bps.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace cli {
|
namespace cli {
|
||||||
|
|
||||||
@@ -17,7 +19,7 @@ absl::Status ApplyPatch::handle(const std::vector<std::string>& arg_vec) {
|
|||||||
|
|
||||||
// Apply patch
|
// Apply patch
|
||||||
std::vector<uint8_t> patched;
|
std::vector<uint8_t> patched;
|
||||||
core::ApplyBpsPatch(source, patch, patched);
|
util::ApplyBpsPatch(source, patch, patched);
|
||||||
|
|
||||||
// Save patched file
|
// Save patched file
|
||||||
std::ofstream patched_rom("patched.sfc", std::ios::binary);
|
std::ofstream patched_rom("patched.sfc", std::ios::binary);
|
||||||
@@ -50,7 +52,7 @@ absl::Status CreatePatch::handle(const std::vector<std::string>& arg_vec) {
|
|||||||
std::vector<uint8_t> target;
|
std::vector<uint8_t> target;
|
||||||
std::vector<uint8_t> patch;
|
std::vector<uint8_t> patch;
|
||||||
// Create patch
|
// Create patch
|
||||||
core::CreateBpsPatch(source, target, patch);
|
util::CreateBpsPatch(source, target, patch);
|
||||||
|
|
||||||
// Save patch to file
|
// Save patch to file
|
||||||
// std::ofstream patchFile("patch.bps", ios::binary);
|
// std::ofstream patchFile("patch.bps", ios::binary);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <ftxui/dom/elements.hpp>
|
#include <ftxui/dom/elements.hpp>
|
||||||
#include <ftxui/screen/screen.hpp>
|
#include <ftxui/screen/screen.hpp>
|
||||||
|
|
||||||
#include "app/core/common.h"
|
#include "util/bps.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace cli {
|
namespace cli {
|
||||||
@@ -56,10 +56,10 @@ void ApplyBpsPatchComponent(ftxui::ScreenInteractive &screen) {
|
|||||||
|
|
||||||
// Button to apply the patch.
|
// Button to apply the patch.
|
||||||
auto apply_button = Button("Apply Patch", [&] {
|
auto apply_button = Button("Apply Patch", [&] {
|
||||||
std::vector<uint8_t> source;
|
std::vector<uint8_t> source = app_context.rom.vector();
|
||||||
auto source_contents = core::LoadFile(base_file);
|
// auto source_contents = core::LoadFile(base_file);
|
||||||
std::copy(source_contents.begin(), source_contents.end(),
|
// std::copy(source_contents.begin(), source_contents.end(),
|
||||||
std::back_inserter(source));
|
// std::back_inserter(source));
|
||||||
std::vector<uint8_t> patch;
|
std::vector<uint8_t> patch;
|
||||||
auto patch_contents = core::LoadFile(patch_file);
|
auto patch_contents = core::LoadFile(patch_file);
|
||||||
std::copy(patch_contents.begin(), patch_contents.end(),
|
std::copy(patch_contents.begin(), patch_contents.end(),
|
||||||
@@ -67,7 +67,7 @@ void ApplyBpsPatchComponent(ftxui::ScreenInteractive &screen) {
|
|||||||
std::vector<uint8_t> patched;
|
std::vector<uint8_t> patched;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
core::ApplyBpsPatch(source, patch, patched);
|
util::ApplyBpsPatch(source, patch, patched);
|
||||||
} catch (const std::runtime_error &e) {
|
} catch (const std::runtime_error &e) {
|
||||||
app_context.error_message = e.what();
|
app_context.error_message = e.what();
|
||||||
SwitchComponents(screen, LayoutID::kError);
|
SwitchComponents(screen, LayoutID::kError);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ add_executable(
|
|||||||
${YAZE_APP_EMU_SRC}
|
${YAZE_APP_EMU_SRC}
|
||||||
${YAZE_APP_GFX_SRC}
|
${YAZE_APP_GFX_SRC}
|
||||||
${YAZE_APP_ZELDA3_SRC}
|
${YAZE_APP_ZELDA3_SRC}
|
||||||
|
${YAZE_UTIL_SRC}
|
||||||
${YAZE_GUI_SRC}
|
${YAZE_GUI_SRC}
|
||||||
${IMGUI_SRC}
|
${IMGUI_SRC}
|
||||||
${ASAR_STATIC_SRC}
|
${ASAR_STATIC_SRC}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ add_executable(
|
|||||||
${YAZE_APP_GFX_SRC}
|
${YAZE_APP_GFX_SRC}
|
||||||
${YAZE_APP_ZELDA3_SRC}
|
${YAZE_APP_ZELDA3_SRC}
|
||||||
${YAZE_APP_EDITOR_SRC}
|
${YAZE_APP_EDITOR_SRC}
|
||||||
|
${YAZE_UTIL_SRC}
|
||||||
${YAZE_GUI_SRC}
|
${YAZE_GUI_SRC}
|
||||||
${IMGUI_SRC}
|
${IMGUI_SRC}
|
||||||
${IMGUI_TEST_ENGINE_SOURCES}
|
${IMGUI_TEST_ENGINE_SOURCES}
|
||||||
|
|||||||
207
src/util/bps.cc
Normal file
207
src/util/bps.cc
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
|
||||||
|
#include "bps.h"
|
||||||
|
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
uint32_t crc32(const std::vector<uint8_t> &data) {
|
||||||
|
uint32_t crc = ::crc32(0L, Z_NULL, 0);
|
||||||
|
return ::crc32(crc, data.data(), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void encode(uint64_t data, std::vector<uint8_t> &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<uint8_t> &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
|
||||||
|
|
||||||
|
void CreateBpsPatch(const std::vector<uint8_t> &source,
|
||||||
|
const std::vector<uint8_t> &target,
|
||||||
|
std::vector<uint8_t> &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 source_offset = 0;
|
||||||
|
size_t target_offset = 0;
|
||||||
|
int64_t source_rel_offset = 0;
|
||||||
|
int64_t target_rel_offset = 0;
|
||||||
|
|
||||||
|
while (target_offset < target.size()) {
|
||||||
|
if (source_offset < source.size() &&
|
||||||
|
source[source_offset] == target[target_offset]) {
|
||||||
|
size_t length = 0;
|
||||||
|
while (source_offset + length < source.size() &&
|
||||||
|
target_offset + length < target.size() &&
|
||||||
|
source[source_offset + length] == target[target_offset + length]) {
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
encode((length - 1) << 2 | 0, patch); // SourceRead
|
||||||
|
source_offset += length;
|
||||||
|
target_offset += length;
|
||||||
|
} else {
|
||||||
|
size_t length = 0;
|
||||||
|
while (
|
||||||
|
target_offset + length < target.size() &&
|
||||||
|
(source_offset + length >= source.size() ||
|
||||||
|
source[source_offset + length] != target[target_offset + length])) {
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
if (length > 0) {
|
||||||
|
encode((length - 1) << 2 | 1, patch); // TargetRead
|
||||||
|
for (size_t i = 0; i < length; i++) {
|
||||||
|
patch.push_back(target[target_offset + i]);
|
||||||
|
}
|
||||||
|
target_offset += length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SourceCopy
|
||||||
|
if (source_offset < source.size()) {
|
||||||
|
size_t length = 0;
|
||||||
|
int64_t offset = source_offset - source_rel_offset;
|
||||||
|
while (source_offset + length < source.size() &&
|
||||||
|
target_offset + length < target.size() &&
|
||||||
|
source[source_offset + length] == target[target_offset + length]) {
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
if (length > 0) {
|
||||||
|
encode((length - 1) << 2 | 2, patch);
|
||||||
|
encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
|
||||||
|
source_offset += length;
|
||||||
|
target_offset += length;
|
||||||
|
source_rel_offset = source_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TargetCopy
|
||||||
|
if (target_offset > 0) {
|
||||||
|
size_t length = 0;
|
||||||
|
int64_t offset = target_offset - target_rel_offset;
|
||||||
|
while (target_offset + length < target.size() &&
|
||||||
|
target[target_offset - 1] == target[target_offset + length]) {
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
if (length > 0) {
|
||||||
|
encode((length - 1) << 2 | 3, patch);
|
||||||
|
encode((offset < 0 ? 1 : 0) | (abs(offset) << 1), patch);
|
||||||
|
target_offset += length;
|
||||||
|
target_rel_offset = target_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
patch.resize(patch.size() + 12); // Make space for the checksums
|
||||||
|
uint32_t source_checksum = crc32(source);
|
||||||
|
uint32_t target_checksum = crc32(target);
|
||||||
|
uint32_t patch_checksum = crc32(patch);
|
||||||
|
|
||||||
|
memcpy(patch.data() + patch.size() - 12, &source_checksum, sizeof(uint32_t));
|
||||||
|
memcpy(patch.data() + patch.size() - 8, &target_checksum, sizeof(uint32_t));
|
||||||
|
memcpy(patch.data() + patch.size() - 4, &patch_checksum, sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyBpsPatch(const std::vector<uint8_t> &source,
|
||||||
|
const std::vector<uint8_t> &patch,
|
||||||
|
std::vector<uint8_t> &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 patch_offset = 4;
|
||||||
|
uint64_t target_size = decode(patch, patch_offset);
|
||||||
|
uint64_t metadata_size = decode(patch, patch_offset);
|
||||||
|
patch_offset += metadata_size;
|
||||||
|
|
||||||
|
target.resize(target_size);
|
||||||
|
size_t source_offset = 0;
|
||||||
|
size_t target_offset = 0;
|
||||||
|
int64_t source_rel_offset = 0;
|
||||||
|
int64_t target_rel_offset = 0;
|
||||||
|
|
||||||
|
while (patch_offset < patch.size() - 12) {
|
||||||
|
uint64_t data = decode(patch, patch_offset);
|
||||||
|
uint64_t command = data & 3;
|
||||||
|
uint64_t length = (data >> 2) + 1;
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case 0: // SourceRead
|
||||||
|
while (length--) {
|
||||||
|
target[target_offset++] = source[source_offset++];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1: // TargetRead
|
||||||
|
while (length--) {
|
||||||
|
target[target_offset++] = patch[patch_offset++];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: // SourceCopy
|
||||||
|
{
|
||||||
|
int64_t offsetData = decode(patch, patch_offset);
|
||||||
|
source_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
|
||||||
|
while (length--) {
|
||||||
|
target[target_offset++] = source[source_rel_offset++];
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case 3: // TargetCopy
|
||||||
|
{
|
||||||
|
uint64_t offsetData = decode(patch, patch_offset);
|
||||||
|
target_rel_offset += (offsetData & 1 ? -1 : +1) * (offsetData >> 1);
|
||||||
|
while (length--) {
|
||||||
|
target[target_offset++] = target[target_rel_offset++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Invalid patch command");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t source_checksum;
|
||||||
|
uint32_t target_checksum;
|
||||||
|
uint32_t patch_checksum;
|
||||||
|
memcpy(&source_checksum, patch.data() + patch.size() - 12, sizeof(uint32_t));
|
||||||
|
memcpy(&target_checksum, patch.data() + patch.size() - 8, sizeof(uint32_t));
|
||||||
|
memcpy(&patch_checksum, patch.data() + patch.size() - 4, sizeof(uint32_t));
|
||||||
|
|
||||||
|
if (source_checksum != crc32(source) || target_checksum != crc32(target) ||
|
||||||
|
patch_checksum !=
|
||||||
|
crc32(std::vector<uint8_t>(patch.begin(), patch.end() - 4))) {
|
||||||
|
throw std::runtime_error("Checksum mismatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace yaze
|
||||||
21
src/util/bps.h
Normal file
21
src/util/bps.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef YAZE_UTIL_BPS_H
|
||||||
|
#define YAZE_UTIL_BPS_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
void CreateBpsPatch(const std::vector<uint8_t> &source,
|
||||||
|
const std::vector<uint8_t> &target,
|
||||||
|
std::vector<uint8_t> &patch);
|
||||||
|
|
||||||
|
void ApplyBpsPatch(const std::vector<uint8_t> &source,
|
||||||
|
const std::vector<uint8_t> &patch,
|
||||||
|
std::vector<uint8_t> &target);
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
|
#endif // YAZE_UTIL_BPS_H
|
||||||
Reference in New Issue
Block a user