mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-17 16:17:06 +02:00
EBX writer: rewritten arrays keep their own record hash
Each array record carries a hash the original files make specific to the field or element type; the writer used to give every array the hash of the first record of the same element type, so a rewrite changed hundreds of them in bam's level root. Arrays read from a document now keep their record's hash; only arrays with no source record borrow as before.
This commit is contained in:
1 parent
f22adf2348
commit
db0d1f348c
3 files changed
+28
-4
No files matched your search
@@ -1,6 +1,7 @@
|
||||
#include "ebx_document.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
@@ -356,7 +357,9 @@ void map_instances(Document& document,
|
||||
class ValueReader final {
|
||||
public:
|
||||
ValueReader(const std::span<const std::byte> bytes, Document& document)
|
||||
: bytes_(bytes), reader_(bytes), document_(document) {}
|
||||
: bytes_(bytes), reader_(bytes), document_(document) {
|
||||
for (const auto& record : document.arrays) arrayHashes_.emplace(record.offset, record.hash);
|
||||
}
|
||||
|
||||
void read_instances() {
|
||||
for (auto& instance : document_.instances) {
|
||||
@@ -423,7 +426,19 @@ private:
|
||||
}
|
||||
const auto position = start + field.dataOffset;
|
||||
if (position > document_.dataEnd) throw std::runtime_error("EBX field offset exceeds EBXD");
|
||||
output.fields.push_back({descriptorIndex, field.name, read_field(field, position)});
|
||||
auto value = read_field(field, position);
|
||||
if (field.category() == FieldCategory::array) {
|
||||
const auto* values = std::get_if<Value::Array>(&value.data);
|
||||
if (values && !values->empty()) {
|
||||
std::int32_t displacement{};
|
||||
std::memcpy(&displacement, bytes_.data() + position, sizeof(displacement));
|
||||
const auto target = static_cast<std::int64_t>(position) + displacement -
|
||||
static_cast<std::int64_t>(document_.dataStart);
|
||||
if (const auto found = arrayHashes_.find(target); found != arrayHashes_.end())
|
||||
value.arrayHash = found->second;
|
||||
}
|
||||
}
|
||||
output.fields.push_back({descriptorIndex, field.name, std::move(value)});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,6 +567,7 @@ private:
|
||||
}
|
||||
|
||||
std::span<const std::byte> bytes_;
|
||||
std::unordered_map<std::int64_t, std::uint32_t> arrayHashes_; // record offset -> its hash
|
||||
BinaryReader reader_;
|
||||
Document& document_;
|
||||
unsigned depth_{};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
@@ -149,6 +150,9 @@ struct Value {
|
||||
PointerReference, TypeReference, BoxedReference,
|
||||
std::shared_ptr<struct Object>, Array>;
|
||||
Storage data;
|
||||
// An array's own record hash, kept when the document is written again; a new array has none
|
||||
// and borrows one (ebx_writer.cpp, array_hash).
|
||||
std::optional<std::uint32_t> arrayHash;
|
||||
};
|
||||
|
||||
struct FieldValue {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <ranges>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
@@ -106,6 +107,7 @@ private:
|
||||
std::uint32_t slot{};
|
||||
const Value::Array* values{};
|
||||
std::size_t descriptor{};
|
||||
std::optional<std::uint32_t> hash;
|
||||
bool emitted{};
|
||||
};
|
||||
|
||||
@@ -208,7 +210,7 @@ private:
|
||||
static const Value::Array empty;
|
||||
const auto* array = value ? std::get_if<Value::Array>(&value->data) : nullptr;
|
||||
arraysPending_.push_back({checked_u32(position, "EBX array slot"), array ? array : &empty,
|
||||
descriptorIndex});
|
||||
descriptorIndex, value ? value->arrayHash : std::nullopt});
|
||||
} else if (field.type() == FieldType::structure) {
|
||||
if (field.classRef >= document_.types.size()) throw std::runtime_error("EBX struct type is invalid");
|
||||
const auto nestedStart = align_up(position, std::max<std::size_t>(document_.types[field.classRef].alignment, 1));
|
||||
@@ -505,8 +507,10 @@ private:
|
||||
write_scalar(field, elementStart, &value);
|
||||
}
|
||||
}
|
||||
// An array read from a record keeps that record's hash; a new one borrows (array_hash).
|
||||
const auto hash = arraysPending_[index].hash ? *arraysPending_[index].hash : array_hash(field);
|
||||
arrays_.push_back({checked_u32(data, "EBX array data"), checked_u32(values->size(), "EBX array count"),
|
||||
array_hash(field), static_cast<std::uint16_t>(field.encodedType << 1),
|
||||
hash, static_cast<std::uint16_t>(field.encodedType << 1),
|
||||
field.type() == FieldType::structure
|
||||
? fixup_slot_for_descriptor(field.classRef)
|
||||
: static_cast<std::uint16_t>(0xFFFF)});
|
||||
|
||||
Reference in new issue
Block a user