#include "attribute_data.h" #include #include #include namespace fmt { namespace { constexpr char kHeader[] = "AttributeData"; constexpr std::size_t kHeaderSize = sizeof(kHeader); // include trailing NUL constexpr std::uint32_t kMaxRecords = 1u << 20; constexpr std::uint32_t kMaxHeightVertices = 1u << 24; struct Reader { const std::uint8_t *data = nullptr; std::size_t size = 0; std::size_t offset = 0; std::string *error = nullptr; bool fail(const std::string &message) { if (error) *error = message + " at offset " + std::to_string(offset); return false; } bool require(std::size_t count) { if (count > size || offset > size - count) return fail("AttributeData truncated"); return true; } template bool read(T &value) { if (!require(sizeof(T))) return false; std::memcpy(&value, data + offset, sizeof(T)); offset += sizeof(T); return true; } bool read_bytes(void *dst, std::size_t count) { if (!require(count)) return false; std::memcpy(dst, data + offset, count); offset += count; return true; } }; std::size_t dimension_count(std::uint32_t type) { switch (static_cast(type)) { case AttributeCollisionType::Plane: case AttributeCollisionType::Cylinder: return 2; case AttributeCollisionType::Box: case AttributeCollisionType::Sphere: case AttributeCollisionType::Aabb: case AttributeCollisionType::Obb: return type == static_cast(AttributeCollisionType::Sphere) ? 1 : 3; default: return 0; } } std::string fixed_name(const char (&raw)[32]) { const void *nul = std::memchr(raw, '\0', 32); const auto count = nul ? static_cast(nul) - raw : 32; return std::string(raw, static_cast(count)); } } // namespace bool parse_attribute_data(const std::uint8_t *data, std::size_t size, AttributeData &out, std::string *err) { out = AttributeData{}; if (data == nullptr) return err ? ((*err = "AttributeData buffer is null"), false) : false; Reader r{data, size, 0, err}; char header[kHeaderSize] = {}; if (!r.read_bytes(header, kHeaderSize)) return false; if (std::memcmp(header, kHeader, kHeaderSize) != 0) return r.fail("AttributeData bad header"); std::uint32_t collision_count = 0; std::uint32_t height_count = 0; if (!r.read(collision_count) || !r.read(height_count)) return false; if (collision_count > kMaxRecords || height_count > kMaxRecords) return r.fail("AttributeData record count is unreasonable"); out.collisions.reserve(collision_count); for (std::uint32_t i = 0; i < collision_count; ++i) { AttributeCollision collision; if (!r.read(collision.type)) return false; const std::size_t dims = dimension_count(collision.type); if (dims == 0) return r.fail("AttributeData unknown collision type"); char raw_name[32] = {}; if (!r.read_bytes(raw_name, sizeof(raw_name)) || !r.read_bytes(collision.position.data(), sizeof(float) * 3) || !r.read_bytes(collision.dimensions.data(), sizeof(float) * dims) || !r.read_bytes(collision.quaternion.data(), sizeof(float) * 4)) return false; collision.name = fixed_name(raw_name); out.collisions.push_back(std::move(collision)); } out.heights.reserve(height_count); for (std::uint32_t i = 0; i < height_count; ++i) { AttributeHeight height; char raw_name[32] = {}; std::uint32_t vertex_count = 0; if (!r.read_bytes(raw_name, sizeof(raw_name)) || !r.read(vertex_count)) return false; if (vertex_count > kMaxHeightVertices) return r.fail("AttributeData height vertex count is unreasonable"); if (vertex_count > 0 && vertex_count > (std::numeric_limits::max() / 12)) return r.fail("AttributeData height size overflows"); if (!r.require(static_cast(vertex_count) * 12)) return false; height.name = fixed_name(raw_name); height.vertices.resize(vertex_count); for (auto &vertex : height.vertices) { if (!r.read_bytes(vertex.data(), sizeof(float) * 3)) return false; } out.heights.push_back(std::move(height)); } if (r.offset != size) return r.fail("AttributeData trailing bytes"); return true; } bool parse_attribute_data_file(const std::string &path, AttributeData &out, std::string *err) { std::ifstream file(path, std::ios::binary | std::ios::ate); if (!file) { if (err) *err = "cannot open AttributeData: " + path; return false; } const std::streamoff end = file.tellg(); if (end < 0) { if (err) *err = "cannot stat AttributeData: " + path; return false; } std::vector bytes(static_cast(end)); file.seekg(0, std::ios::beg); if (!bytes.empty() && !file.read(reinterpret_cast(bytes.data()), static_cast(bytes.size()))) { if (err) *err = "cannot read AttributeData: " + path; return false; } return parse_attribute_data(bytes.data(), bytes.size(), out, err); } } // namespace fmt