#include #include #include "../../../../state/build_data/runtime.h" #include "internal.h" namespace sunrise::client::content::items::packages { /** Reads the fixed collectible table and publishes its native-index item links. */ bool build_collectibles(const reader::Source& source, Storage& storage, std::span root, std::uint64_t itemDefinitionCount) noexcept { namespace domain = state::build_data::collectibles; if (state::build_data::collectible_definitions_ready()) { return true; } if (itemDefinitionCount == 0 || itemDefinitionCount > state::build_data::items::kDefinitionCapacity) { return false; } std::uint32_t requirementTableTag = 0; std::uint32_t requirementTableClass = 0; tables::Array requirementSets{}; if (!tables::slot_tag(root, tables::kMaterialRequirementTableSlot, requirementTableTag) || requirementTableTag == 0 || tables::package_of(requirementTableTag) == tables::kAbsentPackageId || !reader::read_tag( source, storage.scratch, requirementTableTag, storage.definition, requirementTableClass) || requirementTableClass != tables::kMaterialRequirementTableClass || !tables::find_array_at(std::span{storage.definition}, tables::kTableArrayDescriptor, requirementSets) || requirementSets.elementClass != tables::kMaterialRequirementSetRowClass || requirementSets.count == 0 || requirementSets.count > (std::numeric_limits::max)()) { return false; } const std::span requirementTable{storage.definition}; if (requirementSets.dataOffset > requirementTable.size() || requirementSets.count > (requirementTable.size() - requirementSets.dataOffset) / tables::kMaterialRequirementSetRowStride) { return false; } std::uint32_t tableTag = 0; std::uint32_t tableClass = 0; tables::Array rows{}; if (!tables::slot_tag(root, tables::kCollectibleTableSlot, tableTag) || tableTag == 0 || tables::package_of(tableTag) == tables::kAbsentPackageId || !reader::read_tag(source, storage.scratch, tableTag, storage.child, tableClass) || tableClass != tables::kCollectibleTableClass || !tables::find_array_at( std::span{storage.child}, tables::kTableArrayDescriptor, rows) || rows.elementClass != tables::kCollectibleRowClass || rows.count == 0 || rows.count > storage.collectibleRows.size()) { return false; } const std::span table{storage.child}; if (rows.dataOffset > table.size() || rows.count > (table.size() - rows.dataOffset) / tables::kCollectibleRowStride) { return false; } for (std::uint64_t row = 0; row < rows.count; ++row) { const std::size_t at = rows.dataOffset + static_cast(row) * tables::kCollectibleRowStride; std::uint32_t collectibleHash = 0; std::uint16_t itemDefinitionIndex = domain::kUnavailableItemDefinitionIndex; std::uint16_t requirementSetIndex = domain::kUnavailableMaterialRequirementSetIndex; std::memcpy(&collectibleHash, table.data() + at + tables::kCollectibleHashOffset, sizeof collectibleHash); std::memcpy(&itemDefinitionIndex, table.data() + at + tables::kCollectibleItemIndexOffset, sizeof itemDefinitionIndex); std::memcpy(&requirementSetIndex, table.data() + at + tables::kCollectibleMaterialRequirementIndexOffset, sizeof requirementSetIndex); if (itemDefinitionIndex != domain::kUnavailableItemDefinitionIndex && itemDefinitionIndex >= itemDefinitionCount) { return false; } domain::Definition& output = storage.collectibleRows[static_cast(row)]; output = {}; output.collectibleHash = collectibleHash; output.collectibleIndex = static_cast(row); output.itemDefinitionIndex = itemDefinitionIndex; if (requirementSetIndex == domain::kUnavailableMaterialRequirementSetIndex) { continue; } if (requirementSetIndex >= requirementSets.count) { return false; } const std::size_t setAt = requirementSets.dataOffset + static_cast(requirementSetIndex) * tables::kMaterialRequirementSetRowStride; std::uint32_t requirementSetHash = 0; std::int64_t descriptorRelative = 0; std::memcpy(&requirementSetHash, requirementTable.data() + setAt + tables::kMaterialRequirementSetHashOffset, sizeof requirementSetHash); std::memcpy(&descriptorRelative, requirementTable.data() + setAt + tables::kMaterialRequirementSetArrayPointerOffset, sizeof descriptorRelative); const std::size_t pointerAt = setAt + tables::kMaterialRequirementSetArrayPointerOffset; if (requirementSetHash == 0 || descriptorRelative < -static_cast(pointerAt) || descriptorRelative > static_cast(requirementTable.size() - pointerAt)) { return false; } const auto descriptorSigned = static_cast(pointerAt) + descriptorRelative; if (descriptorSigned < 0) { return false; } tables::Array requirements{}; if (!tables::find_array_at( requirementTable, static_cast(descriptorSigned), requirements) || requirements.elementClass != tables::kMaterialRequirementRowClass || requirements.count == 0 || requirements.count > output.materialRequirements.size() || requirements.dataOffset > requirementTable.size() || requirements.count > (requirementTable.size() - requirements.dataOffset) / tables::kMaterialRequirementRowStride) { return false; } output.materialRequirementSetHash = requirementSetHash; output.materialRequirementSetIndex = requirementSetIndex; output.materialRequirementCount = static_cast(requirements.count); for (std::size_t requirement = 0; requirement < requirements.count; ++requirement) { const std::size_t requirementAt = requirements.dataOffset + requirement * tables::kMaterialRequirementRowStride; std::uint32_t nativeItemIndex = 0; std::uint32_t quantity = 0; std::uint8_t deleteOnAction = 0; std::uint8_t omitFromRequirements = 0; std::uint16_t sentinel = 0; std::memcpy(&nativeItemIndex, requirementTable.data() + requirementAt + tables::kMaterialRequirementItemIndexOffset, sizeof nativeItemIndex); std::memcpy(&quantity, requirementTable.data() + requirementAt + tables::kMaterialRequirementQuantityOffset, sizeof quantity); std::memcpy(&deleteOnAction, requirementTable.data() + requirementAt + tables::kMaterialRequirementDeleteOffset, sizeof deleteOnAction); std::memcpy(&omitFromRequirements, requirementTable.data() + requirementAt + tables::kMaterialRequirementOmitOffset, sizeof omitFromRequirements); std::memcpy(&sentinel, requirementTable.data() + requirementAt + tables::kMaterialRequirementSentinelOffset, sizeof sentinel); if (nativeItemIndex >= itemDefinitionCount || nativeItemIndex > (std::numeric_limits::max)() || quantity == 0 || deleteOnAction > 1 || omitFromRequirements > 1 || sentinel != 0xFFFFU) { return false; } output.materialRequirements[requirement] = { quantity, static_cast(nativeItemIndex), deleteOnAction != 0, omitFromRequirements != 0, }; } } return state::build_data::publish_collectible_definitions( std::span(storage.collectibleRows).first(static_cast(rows.count))); } } // namespace sunrise::client::content::items::packages