| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- #include <algorithm>
- #include <cstring>
- #include "definition_index_table.h"
- #include "items.h"
- namespace sunrise::middleware::content::packages::tables::items {
- namespace {
- /** The signed stack limit comes before the bucket id. */
- constexpr std::size_t kMaxStackSizeOffset = 180;
- /** A nonzero predicate byte marks an instanced definition. */
- constexpr std::size_t kInstancedOffset = 187;
- /** The equipment block is self-relative from this offset, zero when absent. */
- constexpr std::size_t kEquipmentBlockOffset = 16;
- /** The equipment block stores its signed slot id here. */
- constexpr std::size_t kEquipmentSlotOffset = 24;
- /** The socket entry list block is self-relative from this offset, zero when absent. */
- constexpr std::size_t kSocketEntryListBlockOffset = 128;
- /** A present block declares 12 bytes. A shorter tail is not a block. */
- constexpr std::size_t kSocketEntryListBlockSize = 12;
- /** Equipment slots run from 0 through 19. */
- constexpr std::int32_t kEquipmentSlotCount = 20;
- /** A socket entry stores its type index first and its initial plug next. */
- constexpr std::size_t kSocketTypeOffset = 0;
- constexpr std::size_t kSocketPlugOffset = 2;
- /** Fixed fields end after the instanced predicate. */
- constexpr std::size_t kFixedFieldEnd = kInstancedOffset + 1;
- /** Optional plug category used to expand three native reusable plug families. */
- constexpr std::size_t kPlugCategoryOffset = 392;
- /** Embedded reusable-list array descriptor inside one 80-byte ordinary socket entry. */
- constexpr std::size_t kEmbeddedPlugListOffset = 64;
- /** Reusable and randomized shared plug-set row indices inside one socket entry. */
- constexpr std::size_t kReusablePlugSetIndexOffset = 12;
- constexpr std::size_t kRandomizedPlugSetIndexOffset = 32;
- /** One shared plug-set table row is 24 bytes and carries its member descriptor at byte 8. */
- constexpr std::size_t kPlugSetRowStride = 24;
- constexpr std::size_t kPlugSetMemberDescriptorOffset = 8;
- /** Both embedded and shared plug member rows name an item index first and occupy 32 bytes. */
- constexpr std::size_t kPlugMemberStride = 32;
- constexpr std::size_t kPlugMemberIndexOffset = 0;
- /** Native arrays use 16-bit definition indices even though their serialized field is 32-bit. */
- constexpr std::uint64_t kMaximumPlugMemberCount = 65535;
- /** @param blob Source bytes. @param offset Field offset. @param value Receives the field. */
- template <typename Value>
- [[nodiscard]] bool
- read(std::span<const std::byte> 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;
- }
- /**
- * Reads the socket entry list index a definition declares.
- * @param definition Whole item definition bytes.
- * @param row Receives list presence and index.
- */
- void read_socket_entry_list(std::span<const std::byte> definition, Row& row) noexcept {
- row.hasSocketEntryList = false;
- row.socketEntryListIndex = 0;
- std::int64_t relative = 0;
- if (!read(definition, kSocketEntryListBlockOffset, relative) || relative == 0) {
- return;
- }
- const std::int64_t block = static_cast<std::int64_t>(kSocketEntryListBlockOffset) + relative;
- if (block < 0) {
- return;
- }
- const auto blockOffset = static_cast<std::uint64_t>(block);
- if (blockOffset + kSocketEntryListBlockSize > definition.size()) {
- return;
- }
- std::uint16_t index = 0;
- if (read(definition, static_cast<std::size_t>(block), index)) {
- row.hasSocketEntryList = true;
- row.socketEntryListIndex = index;
- }
- }
- /**
- * Reads the optional equipment slot a definition declares.
- * @param definition Whole item definition bytes.
- * @param slot Receives the slot when the block is present and readable.
- * @param raw Receives the unvalidated declared value, or -1 when the block is absent.
- */
- void read_equipment_slot(std::span<const std::byte> definition,
- std::optional<std::int8_t>& slot,
- std::int32_t& raw) noexcept {
- slot.reset();
- raw = -1;
- std::int64_t relative = 0;
- if (!read(definition, kEquipmentBlockOffset, relative) || relative == 0) {
- return;
- }
- const std::int64_t block = static_cast<std::int64_t>(kEquipmentBlockOffset) + relative;
- if (block < 0) {
- return;
- }
- // A value outside the equippable range marks an item that takes no slot.
- std::int32_t value = 0;
- if (read(definition, static_cast<std::size_t>(block) + kEquipmentSlotOffset, value)
- && value >= 0 && value < static_cast<std::int32_t>(kEquipmentSlotCount)) {
- slot = static_cast<std::int8_t>(value);
- }
- }
- /**
- * Reads the ordinary socket lanes a definition declares.
- * The lane's type index picks the overflow-hash priority the character record applies, so it is
- * read from the same entry as the plug rather than worked out later.
- * @param definition Whole item definition bytes.
- * @param row Receives socket presence, count, types and initial plugs.
- */
- void read_sockets(std::span<const std::byte> definition, Row& row) noexcept {
- row.hasSockets = false;
- row.socketCount = 0;
- std::fill(std::begin(row.initialPlugs), std::end(row.initialPlugs), kUnavailablePlug);
- std::fill(std::begin(row.socketTypes), std::end(row.socketTypes), kUnavailableSocketType);
- std::int64_t relative = 0;
- if (!read(definition, kSocketBlockOffset, relative) || relative == 0) {
- return;
- }
- const std::int64_t block = static_cast<std::int64_t>(kSocketBlockOffset) + relative;
- if (block < 0 || static_cast<std::uint64_t>(block) >= definition.size()) {
- return;
- }
- Array array{};
- if (!find_array_at(definition, static_cast<std::size_t>(block), array)
- || array.elementClass != kOrdinarySocketClass) {
- return;
- }
- row.hasSockets = true;
- const auto lanes =
- static_cast<std::size_t>((std::min)(array.count, std::uint64_t{kSocketCapacity}));
- for (std::size_t lane = 0; lane < lanes; ++lane) {
- const std::size_t entry = array.dataOffset + lane * kSocketEntryStride;
- std::uint16_t plug = kUnavailablePlug;
- std::uint16_t type = kUnavailableSocketType;
- if (!read(definition, entry + kSocketPlugOffset, plug)
- || !read(definition, entry + kSocketTypeOffset, type)) {
- return;
- }
- row.initialPlugs[lane] = plug;
- row.socketTypes[lane] = type;
- row.socketCount = static_cast<std::uint8_t>(lane + 1);
- }
- }
- /** Walks one checked array of 32-byte plug rows. */
- [[nodiscard]] bool visit_plug_array(std::span<const std::byte> blob,
- const Array& array,
- AllowedPlugVisitor visitor,
- void* context) noexcept {
- if (array.count > kMaximumPlugMemberCount || array.dataOffset > blob.size()
- || array.count > (blob.size() - array.dataOffset) / kPlugMemberStride) {
- return false;
- }
- for (std::uint64_t index = 0; index < array.count; ++index) {
- std::uint32_t itemDefinitionIndex = 0;
- const std::size_t row =
- array.dataOffset + static_cast<std::size_t>(index) * kPlugMemberStride;
- if (!read(blob, row + kPlugMemberIndexOffset, itemDefinitionIndex)
- || !visitor(context, itemDefinitionIndex)) {
- return false;
- }
- }
- return true;
- }
- /** Walks one reusable or randomized shared plug-set row, when the socket declares it. */
- [[nodiscard]] bool visit_shared_plug_set(std::span<const std::byte> definition,
- std::size_t socketEntry,
- std::size_t setIndexOffset,
- std::span<const std::byte> plugSetTable,
- const Array& sets,
- AllowedPlugVisitor visitor,
- void* context) noexcept {
- std::uint16_t setIndex = kUnavailablePlug;
- if (!read(definition, socketEntry + setIndexOffset, setIndex)) {
- return false;
- }
- if (setIndex == kUnavailablePlug) {
- return true;
- }
- if (setIndex >= sets.count || sets.dataOffset > plugSetTable.size()
- || sets.count > (plugSetTable.size() - sets.dataOffset) / kPlugSetRowStride) {
- return false;
- }
- const std::size_t descriptor = sets.dataOffset
- + static_cast<std::size_t>(setIndex) * kPlugSetRowStride
- + kPlugSetMemberDescriptorOffset;
- std::uint64_t memberCount = 0;
- if (!read(plugSetTable, descriptor, memberCount)) {
- return false;
- }
- if (memberCount == 0) {
- return true;
- }
- Array members{};
- return find_array_at(plugSetTable, descriptor, members)
- && visit_plug_array(plugSetTable, members, visitor, context);
- }
- /** The block header carries its own self-relative pointer to the entries at byte 8. */
- constexpr std::size_t kStatDataMember = 8;
- /** One stat entry is 40 blob bytes. */
- constexpr std::size_t kStatEntryStride = 40;
- /** A stat entry names its stat table row at byte 16. */
- constexpr std::size_t kStatEntryRow = 16;
- /** A stat entry carries its signed contribution at byte 20. */
- constexpr std::size_t kStatEntryValue = 20;
- /**
- * Reads the per-item stat contributions declared inside the definition blob.
- * Two self-relative hops: the definition points at a header, the header points at the entries.
- * Both are blob offsets and are valid only against the serialized definition.
- * @param definition Whole item definition bytes.
- * @param row Receives the declared stat rows and values.
- */
- void read_stats(std::span<const std::byte> definition, Row& row) noexcept {
- row.statCount = 0;
- std::int64_t blockRelative = 0;
- if (!read(definition, kStatBlockOffset, blockRelative)) {
- return;
- }
- const auto header = static_cast<std::int64_t>(kStatBlockOffset) + blockRelative;
- if (header < 0 || static_cast<std::size_t>(header) + kStatDataMember > definition.size()) {
- return;
- }
- std::uint64_t count = 0;
- std::int64_t dataRelative = 0;
- if (!read(definition, static_cast<std::size_t>(header), count) || count == 0
- || count > kStatCapacity
- || !read(definition, static_cast<std::size_t>(header) + kStatDataMember, dataRelative)) {
- return;
- }
- const std::int64_t data = header + static_cast<std::int64_t>(kStatDataMember) + dataRelative;
- if (data < 0 || static_cast<std::size_t>(data) + kStatEntryStride * count > definition.size()) {
- return;
- }
- for (std::uint64_t entry = 0; entry < count; ++entry) {
- const auto base = static_cast<std::size_t>(data) + kStatEntryStride * entry;
- if (!read(definition, base + kStatEntryRow, row.statRows[entry])
- || !read(definition, base + kStatEntryValue, row.statValues[entry])) {
- row.statCount = 0;
- return;
- }
- }
- row.statCount = static_cast<std::uint8_t>(count);
- }
- } // namespace
- /** Reads the fixed fields of one item definition blob. */
- bool read_definition(std::span<const std::byte> definition, Row& row) noexcept {
- const std::uint32_t hash = row.definitionHash;
- const std::uint16_t index = row.definitionIndex;
- row = {};
- row.definitionHash = hash;
- row.definitionIndex = index;
- row.insertionMaterialRequirementSetIndex = kUnavailableMaterialRequirementSetIndex;
- row.enabledMaterialRequirementSetIndex = kUnavailableMaterialRequirementSetIndex;
- std::fill(std::begin(row.initialPlugs), std::end(row.initialPlugs), kUnavailablePlug);
- std::fill(std::begin(row.socketTypes), std::end(row.socketTypes), kUnavailableSocketType);
- if (definition.size() < kFixedFieldEnd) {
- return false;
- }
- std::uint8_t instanced = 0;
- if (!read(definition, kBucketIdOffset, row.bucketId)
- || !read(definition, kMaxStackSizeOffset, row.maxStackSize)
- || !read(definition, kInstancedOffset, instanced)) {
- return false;
- }
- row.instanced = instanced != 0;
- // Short legacy definitions simply do not declare a plug category.
- (void)read(definition, kPlugCategoryOffset, row.plugCategoryHash);
- (void)read(definition,
- kInsertionMaterialRequirementSetIndexOffset,
- row.insertionMaterialRequirementSetIndex);
- (void)read(definition,
- kEnabledMaterialRequirementSetIndexOffset,
- row.enabledMaterialRequirementSetIndex);
- read_stats(definition, row);
- read_appearance(definition, row);
- read_socket_entry_list(definition, row);
- read_equipment_slot(definition, row.equipmentSlot, row.rawEquipmentSlot);
- read_sockets(definition, row);
- return true;
- }
- /** Visits every list-backed allowed plug for one exact ordinary socket lane. */
- bool visit_allowed_plugs(std::span<const std::byte> definition,
- std::span<const std::byte> plugSetTable,
- std::uint8_t lane,
- AllowedPlugVisitor visitor,
- void* context) noexcept {
- if (visitor == nullptr || lane >= kSocketCapacity) {
- return false;
- }
- std::int64_t socketBlockRelative = 0;
- if (!read(definition, kSocketBlockOffset, socketBlockRelative) || socketBlockRelative == 0) {
- return false;
- }
- const std::int64_t socketBlock =
- static_cast<std::int64_t>(kSocketBlockOffset) + socketBlockRelative;
- if (socketBlock < 0 || static_cast<std::uint64_t>(socketBlock) >= definition.size()) {
- return false;
- }
- Array sockets{};
- if (!find_array_at(definition, static_cast<std::size_t>(socketBlock), sockets)
- || sockets.elementClass != kOrdinarySocketClass || lane >= sockets.count
- || sockets.dataOffset > definition.size()
- || sockets.count > (definition.size() - sockets.dataOffset) / kSocketEntryStride) {
- return false;
- }
- Array sets{};
- if (!find_array_at(plugSetTable, kTableArrayDescriptor, sets)) {
- return false;
- }
- const std::size_t socketEntry =
- sockets.dataOffset + static_cast<std::size_t>(lane) * kSocketEntryStride;
- Array embedded{};
- std::uint64_t embeddedCount = 0;
- if (!read(definition, socketEntry + kEmbeddedPlugListOffset, embeddedCount)
- || (embeddedCount != 0
- && (!find_array_at(definition, socketEntry + kEmbeddedPlugListOffset, embedded)
- || !visit_plug_array(definition, embedded, visitor, context)))) {
- return false;
- }
- return visit_shared_plug_set(definition,
- socketEntry,
- kReusablePlugSetIndexOffset,
- plugSetTable,
- sets,
- visitor,
- context)
- && visit_shared_plug_set(definition,
- socketEntry,
- kRandomizedPlugSetIndexOffset,
- plugSetTable,
- sets,
- visitor,
- context);
- }
- } // namespace sunrise::middleware::content::packages::tables::items
|