item_appearance_reader.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. #include <cstring>
  2. #include "definition_index_table.h"
  3. #include "items.h"
  4. namespace sunrise::middleware::content::packages::tables::items {
  5. namespace {
  6. /** The art block declares its gear art definition index here. */
  7. constexpr std::size_t kGearArtIndexOffset = 88;
  8. /** One art row is 4 bytes: signed class, one pad byte, then its art index. */
  9. constexpr std::size_t kArtRowStride = 4;
  10. constexpr std::size_t kArtRowValueOffset = 2;
  11. /** Material override arrays sit at these art-block offsets, one per stage. */
  12. constexpr std::size_t kMaterialStageOffsets[]{40, 56, 72};
  13. /** One material override row is 4 bytes: a signed key, one pad byte, then a value. */
  14. constexpr std::size_t kMaterialRowStride = 4;
  15. /** A material override row carries its value after its key and one pad byte. */
  16. constexpr std::size_t kMaterialRowValueOffset = 2;
  17. /** The stat block holds the sandbox perk array descriptor at this offset inside itself. */
  18. constexpr std::size_t kSandboxPerkDescriptor = 16;
  19. /** One sandbox perk entry is 24 bytes and begins with its perk index. */
  20. constexpr std::size_t kSandboxPerkStride = 24;
  21. /** All bits set marks a declared-empty sandbox perk entry. */
  22. constexpr std::uint16_t kUnavailableSandboxPerk = 0xFFFF;
  23. /** @param blob Source bytes. @param offset Field offset. @param value Receives the field. */
  24. template <typename Value>
  25. [[nodiscard]] bool
  26. read(std::span<const std::byte> blob, std::size_t offset, Value& value) noexcept {
  27. if (offset > blob.size() || blob.size() - offset < sizeof value) {
  28. return false;
  29. }
  30. std::memcpy(&value, blob.data() + offset, sizeof value);
  31. return true;
  32. }
  33. /**
  34. * Turns a self-relative block offset a definition declares into a blob offset.
  35. * @param definition Whole item definition bytes.
  36. * @param member Offset of the self-relative field.
  37. * @param block Receives the block offset when the field names one.
  38. * @return True when the field is present, nonzero and lands inside the blob.
  39. */
  40. [[nodiscard]] bool resolve_block(std::span<const std::byte> definition,
  41. std::size_t member,
  42. std::size_t& block) noexcept {
  43. std::int64_t relative = 0;
  44. if (!read(definition, member, relative) || relative == 0) {
  45. return false;
  46. }
  47. const std::int64_t resolved = static_cast<std::int64_t>(member) + relative;
  48. if (resolved < 0 || static_cast<std::uint64_t>(resolved) >= definition.size()) {
  49. return false;
  50. }
  51. block = static_cast<std::size_t>(resolved);
  52. return true;
  53. }
  54. /**
  55. * Reads the gear-art index and every class-qualified arrangement the art block declares.
  56. * @param definition Whole item definition bytes.
  57. * @param art Art block offset.
  58. * @param row Receives both art indices.
  59. */
  60. void read_art(std::span<const std::byte> definition, std::size_t art, Row& row) noexcept {
  61. (void)read(definition, art + kGearArtIndexOffset, row.gearArtIndex);
  62. Array rows{};
  63. if (!find_array_at(definition, art, rows) || rows.elementClass != kArtRowClass) {
  64. return;
  65. }
  66. for (std::uint64_t index = 0; index < rows.count; ++index) {
  67. const std::size_t at = rows.dataOffset + static_cast<std::size_t>(index) * kArtRowStride;
  68. std::int8_t characterClass = -1;
  69. std::uint16_t arrangement = kUnavailableArtIndex;
  70. if (!read(definition, at, characterClass)
  71. || !read(definition, at + kArtRowValueOffset, arrangement)) {
  72. return;
  73. }
  74. const std::size_t slot =
  75. characterClass == -1 ? 0 : static_cast<std::size_t>(characterClass) + 1U;
  76. if (slot < kArtClassCapacity && row.artArrangementIndices[slot] == kUnavailableArtIndex) {
  77. row.artArrangementIndices[slot] = arrangement;
  78. }
  79. }
  80. }
  81. /**
  82. * Reads every material override row the art block's three stage arrays declare.
  83. * Stage order is kept because the consumer folds base and plug rows stage by stage. A row whose
  84. * key is empty is not a row.
  85. * @param definition Whole item definition bytes.
  86. * @param art Art block offset.
  87. * @param row Receives the override rows in stage order.
  88. */
  89. void read_material_overrides(std::span<const std::byte> definition,
  90. std::size_t art,
  91. Row& row) noexcept {
  92. for (std::size_t stage = 0; stage < std::size(kMaterialStageOffsets); ++stage) {
  93. Array overrides{};
  94. if (!find_array_at(definition, art + kMaterialStageOffsets[stage], overrides)
  95. || overrides.elementClass != kMaterialOverrideClass) {
  96. continue;
  97. }
  98. for (std::uint64_t entry = 0; entry < overrides.count; ++entry) {
  99. if (row.renderOverrideCount >= kRenderOverrideCapacity) {
  100. return;
  101. }
  102. const std::size_t at =
  103. overrides.dataOffset + static_cast<std::size_t>(entry) * kMaterialRowStride;
  104. std::int8_t key = kEmptyOverrideKey;
  105. std::uint16_t value = 0;
  106. if (!read(definition, at, key)
  107. || !read(definition, at + kMaterialRowValueOffset, value)) {
  108. return;
  109. }
  110. if (key == kEmptyOverrideKey) {
  111. continue;
  112. }
  113. row.renderOverrides[row.renderOverrideCount++] = {
  114. static_cast<std::uint8_t>(stage), key, value};
  115. }
  116. }
  117. }
  118. /**
  119. * Reads the finished sandbox perk indices the stat block's second array declares.
  120. * These are rows of the sandbox perk table, not item indices. The character record wants that
  121. * form.
  122. * @param definition Whole item definition bytes.
  123. * @param row Receives the declared perk indices.
  124. */
  125. void read_sandbox_perks(std::span<const std::byte> definition, Row& row) noexcept {
  126. std::size_t block = 0;
  127. Array perks{};
  128. if (!resolve_block(definition, kStatBlockOffset, block)
  129. || !find_array_at(definition, block + kSandboxPerkDescriptor, perks)
  130. || perks.elementClass != kSandboxPerkClass) {
  131. return;
  132. }
  133. for (std::uint64_t entry = 0; entry < perks.count; ++entry) {
  134. if (row.sandboxPerkCount >= kSandboxPerkCapacity) {
  135. return;
  136. }
  137. std::uint16_t perk = kUnavailableSandboxPerk;
  138. if (!read(definition,
  139. perks.dataOffset + static_cast<std::size_t>(entry) * kSandboxPerkStride,
  140. perk)) {
  141. return;
  142. }
  143. if (perk != kUnavailableSandboxPerk) {
  144. row.sandboxPerks[row.sandboxPerkCount++] = perk;
  145. }
  146. }
  147. }
  148. } // namespace
  149. /** Reads the art indices, material override rows and sandbox perks one definition declares. */
  150. void read_appearance(std::span<const std::byte> definition, Row& row) noexcept {
  151. row.gearArtIndex = kUnavailableArtIndex;
  152. std::fill(std::begin(row.artArrangementIndices),
  153. std::end(row.artArrangementIndices),
  154. kUnavailableArtIndex);
  155. row.sandboxPerkCount = 0;
  156. row.renderOverrideCount = 0;
  157. std::size_t art = 0;
  158. // A definition with no art block draws nothing and declares no material overrides. Subclasses
  159. // and emblems take this path, and both art indices stay at their empty sentinel.
  160. if (resolve_block(definition, kArtBlockOffset, art)) {
  161. read_art(definition, art, row);
  162. read_material_overrides(definition, art, row);
  163. }
  164. read_sandbox_perks(definition, row);
  165. }
  166. } // namespace sunrise::middleware::content::packages::tables::items