#include #include "definition_index_table.h" #include "items.h" namespace sunrise::middleware::content::packages::tables::items { namespace { /** The art block declares its gear art definition index here. */ constexpr std::size_t kGearArtIndexOffset = 88; /** One art row is 4 bytes: signed class, one pad byte, then its art index. */ constexpr std::size_t kArtRowStride = 4; constexpr std::size_t kArtRowValueOffset = 2; /** Material override arrays sit at these art-block offsets, one per stage. */ constexpr std::size_t kMaterialStageOffsets[]{40, 56, 72}; /** One material override row is 4 bytes: a signed key, one pad byte, then a value. */ constexpr std::size_t kMaterialRowStride = 4; /** A material override row carries its value after its key and one pad byte. */ constexpr std::size_t kMaterialRowValueOffset = 2; /** The stat block holds the sandbox perk array descriptor at this offset inside itself. */ constexpr std::size_t kSandboxPerkDescriptor = 16; /** One sandbox perk entry is 24 bytes and begins with its perk index. */ constexpr std::size_t kSandboxPerkStride = 24; /** All bits set marks a declared-empty sandbox perk entry. */ constexpr std::uint16_t kUnavailableSandboxPerk = 0xFFFF; /** @param blob Source bytes. @param offset Field offset. @param value Receives the field. */ template [[nodiscard]] bool read(std::span blob, std::size_t offset, Value& value) noexcept { if (offset > blob.size() || blob.size() - offset < sizeof value) { return false; } std::memcpy(&value, blob.data() + offset, sizeof value); return true; } /** * Turns a self-relative block offset a definition declares into a blob offset. * @param definition Whole item definition bytes. * @param member Offset of the self-relative field. * @param block Receives the block offset when the field names one. * @return True when the field is present, nonzero and lands inside the blob. */ [[nodiscard]] bool resolve_block(std::span definition, std::size_t member, std::size_t& block) noexcept { std::int64_t relative = 0; if (!read(definition, member, relative) || relative == 0) { return false; } const std::int64_t resolved = static_cast(member) + relative; if (resolved < 0 || static_cast(resolved) >= definition.size()) { return false; } block = static_cast(resolved); return true; } /** * Reads the gear-art index and every class-qualified arrangement the art block declares. * @param definition Whole item definition bytes. * @param art Art block offset. * @param row Receives both art indices. */ void read_art(std::span definition, std::size_t art, Row& row) noexcept { (void)read(definition, art + kGearArtIndexOffset, row.gearArtIndex); Array rows{}; if (!find_array_at(definition, art, rows) || rows.elementClass != kArtRowClass) { return; } for (std::uint64_t index = 0; index < rows.count; ++index) { const std::size_t at = rows.dataOffset + static_cast(index) * kArtRowStride; std::int8_t characterClass = -1; std::uint16_t arrangement = kUnavailableArtIndex; if (!read(definition, at, characterClass) || !read(definition, at + kArtRowValueOffset, arrangement)) { return; } const std::size_t slot = characterClass == -1 ? 0 : static_cast(characterClass) + 1U; if (slot < kArtClassCapacity && row.artArrangementIndices[slot] == kUnavailableArtIndex) { row.artArrangementIndices[slot] = arrangement; } } } /** * Reads every material override row the art block's three stage arrays declare. * Stage order is kept because the consumer folds base and plug rows stage by stage. A row whose * key is empty is not a row. * @param definition Whole item definition bytes. * @param art Art block offset. * @param row Receives the override rows in stage order. */ void read_material_overrides(std::span definition, std::size_t art, Row& row) noexcept { for (std::size_t stage = 0; stage < std::size(kMaterialStageOffsets); ++stage) { Array overrides{}; if (!find_array_at(definition, art + kMaterialStageOffsets[stage], overrides) || overrides.elementClass != kMaterialOverrideClass) { continue; } for (std::uint64_t entry = 0; entry < overrides.count; ++entry) { if (row.renderOverrideCount >= kRenderOverrideCapacity) { return; } const std::size_t at = overrides.dataOffset + static_cast(entry) * kMaterialRowStride; std::int8_t key = kEmptyOverrideKey; std::uint16_t value = 0; if (!read(definition, at, key) || !read(definition, at + kMaterialRowValueOffset, value)) { return; } if (key == kEmptyOverrideKey) { continue; } row.renderOverrides[row.renderOverrideCount++] = { static_cast(stage), key, value}; } } } /** * Reads the finished sandbox perk indices the stat block's second array declares. * These are rows of the sandbox perk table, not item indices. The character record wants that * form. * @param definition Whole item definition bytes. * @param row Receives the declared perk indices. */ void read_sandbox_perks(std::span definition, Row& row) noexcept { std::size_t block = 0; Array perks{}; if (!resolve_block(definition, kStatBlockOffset, block) || !find_array_at(definition, block + kSandboxPerkDescriptor, perks) || perks.elementClass != kSandboxPerkClass) { return; } for (std::uint64_t entry = 0; entry < perks.count; ++entry) { if (row.sandboxPerkCount >= kSandboxPerkCapacity) { return; } std::uint16_t perk = kUnavailableSandboxPerk; if (!read(definition, perks.dataOffset + static_cast(entry) * kSandboxPerkStride, perk)) { return; } if (perk != kUnavailableSandboxPerk) { row.sandboxPerks[row.sandboxPerkCount++] = perk; } } } } // namespace /** Reads the art indices, material override rows and sandbox perks one definition declares. */ void read_appearance(std::span definition, Row& row) noexcept { row.gearArtIndex = kUnavailableArtIndex; std::fill(std::begin(row.artArrangementIndices), std::end(row.artArrangementIndices), kUnavailableArtIndex); row.sandboxPerkCount = 0; row.renderOverrideCount = 0; std::size_t art = 0; // A definition with no art block draws nothing and declares no material overrides. Subclasses // and emblems take this path, and both art indices stay at their empty sentinel. if (resolve_block(definition, kArtBlockOffset, art)) { read_art(definition, art, row); read_material_overrides(definition, art, row); } read_sandbox_perks(definition, row); } } // namespace sunrise::middleware::content::packages::tables::items