package_collectible_build.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. #include <array>
  2. #include <cstdio>
  3. #include "../../../../core/logging/log.h"
  4. #include "../../../../state/build_data/sobjects/sobject_catalog.h"
  5. #include <cstring>
  6. #include <limits>
  7. #include "../../../../state/build_data/runtime.h"
  8. #include "internal.h"
  9. namespace sunrise::client::content::items::packages {
  10. /** Reads the fixed collectible table and publishes its native-index item links. */
  11. bool build_collectibles(const reader::Source& source,
  12. Storage& storage,
  13. std::span<const std::byte> root,
  14. std::uint64_t itemDefinitionCount) noexcept {
  15. namespace domain = state::build_data::collectibles;
  16. // The definition table an incident target names. Read here because this pass already holds an
  17. // open source, and because a collectible picked up in the world arrives as an incident: without
  18. // this table its target is a bare number.
  19. if (state::build_data::sobjects::count() == 0) {
  20. namespace sobjects = state::build_data::sobjects;
  21. // Geometry is documented and asserted rather than divided out: count at +112, rows at +128,
  22. // forty bytes each, name hash at +0, selector group at +32, ordinal at +34, type at +36.
  23. constexpr std::size_t kCountOffset = 112;
  24. constexpr std::size_t kRowBase = 128;
  25. constexpr std::size_t kRowStride = 40;
  26. for (const std::uint32_t tag : {0x81327CD4U, 0x80B9E5BFU}) {
  27. std::vector<std::byte> blob{};
  28. if (!reader::read_tag(source, storage.scratch, tag, blob)
  29. || blob.size() < kRowBase) {
  30. continue;
  31. }
  32. std::uint64_t rowCount = 0;
  33. std::memcpy(&rowCount, blob.data() + kCountOffset, sizeof rowCount);
  34. if (rowCount == 0 || rowCount > sobjects::kDefinitionCapacity
  35. || kRowBase + static_cast<std::size_t>(rowCount) * kRowStride > blob.size()) {
  36. continue;
  37. }
  38. std::vector<sobjects::Definition> rows(static_cast<std::size_t>(rowCount));
  39. for (std::size_t row = 0; row < rows.size(); ++row) {
  40. const std::size_t at = kRowBase + row * kRowStride;
  41. std::memcpy(&rows[row].nameHash, blob.data() + at, sizeof(std::uint32_t));
  42. std::memcpy(&rows[row].selectorGroup, blob.data() + at + 32, sizeof(std::uint16_t));
  43. std::memcpy(&rows[row].nodeOrdinal, blob.data() + at + 34, sizeof(std::uint16_t));
  44. std::memcpy(&rows[row].typeCode, blob.data() + at + 36, sizeof(std::int32_t));
  45. }
  46. (void)sobjects::replace(std::span<const sobjects::Definition>{rows});
  47. break;
  48. }
  49. }
  50. if (state::build_data::collectible_definitions_ready()) {
  51. return true;
  52. }
  53. if (itemDefinitionCount == 0
  54. || itemDefinitionCount > state::build_data::items::kDefinitionCapacity) {
  55. return false;
  56. }
  57. std::uint32_t requirementTableTag = 0;
  58. std::uint32_t requirementTableClass = 0;
  59. tables::Array requirementSets{};
  60. if (!tables::slot_tag(root, tables::kMaterialRequirementTableSlot, requirementTableTag)
  61. || requirementTableTag == 0
  62. || tables::package_of(requirementTableTag) == tables::kAbsentPackageId
  63. || !reader::read_tag(
  64. source, storage.scratch, requirementTableTag, storage.definition, requirementTableClass)
  65. || requirementTableClass != tables::kMaterialRequirementTableClass
  66. || !tables::find_array_at(std::span<const std::byte>{storage.definition},
  67. tables::kTableArrayDescriptor,
  68. requirementSets)
  69. || requirementSets.elementClass != tables::kMaterialRequirementSetRowClass
  70. || requirementSets.count == 0
  71. || requirementSets.count > (std::numeric_limits<std::uint16_t>::max)()) {
  72. return false;
  73. }
  74. const std::span<const std::byte> requirementTable{storage.definition};
  75. if (requirementSets.dataOffset > requirementTable.size()
  76. || requirementSets.count > (requirementTable.size() - requirementSets.dataOffset)
  77. / tables::kMaterialRequirementSetRowStride) {
  78. return false;
  79. }
  80. std::uint32_t tableTag = 0;
  81. std::uint32_t tableClass = 0;
  82. tables::Array rows{};
  83. if (!tables::slot_tag(root, tables::kCollectibleTableSlot, tableTag) || tableTag == 0
  84. || tables::package_of(tableTag) == tables::kAbsentPackageId
  85. || !reader::read_tag(source, storage.scratch, tableTag, storage.child, tableClass)
  86. || tableClass != tables::kCollectibleTableClass
  87. || !tables::find_array_at(
  88. std::span<const std::byte>{storage.child}, tables::kTableArrayDescriptor, rows)
  89. || rows.elementClass != tables::kCollectibleRowClass || rows.count == 0
  90. || rows.count > storage.collectibleRows.size()) {
  91. return false;
  92. }
  93. const std::span<const std::byte> table{storage.child};
  94. if (rows.dataOffset > table.size()
  95. || rows.count > (table.size() - rows.dataOffset) / tables::kCollectibleRowStride) {
  96. return false;
  97. }
  98. for (std::uint64_t row = 0; row < rows.count; ++row) {
  99. const std::size_t at =
  100. rows.dataOffset + static_cast<std::size_t>(row) * tables::kCollectibleRowStride;
  101. std::uint32_t collectibleHash = 0;
  102. std::uint16_t itemDefinitionIndex = domain::kUnavailableItemDefinitionIndex;
  103. std::uint16_t requirementSetIndex = domain::kUnavailableMaterialRequirementSetIndex;
  104. std::memcpy(&collectibleHash,
  105. table.data() + at + tables::kCollectibleHashOffset,
  106. sizeof collectibleHash);
  107. std::memcpy(&itemDefinitionIndex,
  108. table.data() + at + tables::kCollectibleItemIndexOffset,
  109. sizeof itemDefinitionIndex);
  110. std::memcpy(&requirementSetIndex,
  111. table.data() + at + tables::kCollectibleMaterialRequirementIndexOffset,
  112. sizeof requirementSetIndex);
  113. if (itemDefinitionIndex != domain::kUnavailableItemDefinitionIndex
  114. && itemDefinitionIndex >= itemDefinitionCount) {
  115. return false;
  116. }
  117. domain::Definition& output = storage.collectibleRows[static_cast<std::size_t>(row)];
  118. output = {};
  119. output.collectibleHash = collectibleHash;
  120. output.collectibleIndex = static_cast<std::uint16_t>(row);
  121. output.itemDefinitionIndex = itemDefinitionIndex;
  122. if (requirementSetIndex == domain::kUnavailableMaterialRequirementSetIndex) {
  123. continue;
  124. }
  125. if (requirementSetIndex >= requirementSets.count) {
  126. return false;
  127. }
  128. const std::size_t setAt = requirementSets.dataOffset
  129. + static_cast<std::size_t>(requirementSetIndex)
  130. * tables::kMaterialRequirementSetRowStride;
  131. std::uint32_t requirementSetHash = 0;
  132. std::int64_t descriptorRelative = 0;
  133. std::memcpy(&requirementSetHash,
  134. requirementTable.data() + setAt + tables::kMaterialRequirementSetHashOffset,
  135. sizeof requirementSetHash);
  136. std::memcpy(&descriptorRelative,
  137. requirementTable.data() + setAt
  138. + tables::kMaterialRequirementSetArrayPointerOffset,
  139. sizeof descriptorRelative);
  140. const std::size_t pointerAt = setAt + tables::kMaterialRequirementSetArrayPointerOffset;
  141. if (requirementSetHash == 0 || descriptorRelative < -static_cast<std::int64_t>(pointerAt)
  142. || descriptorRelative
  143. > static_cast<std::int64_t>(requirementTable.size() - pointerAt)) {
  144. return false;
  145. }
  146. const auto descriptorSigned = static_cast<std::int64_t>(pointerAt) + descriptorRelative;
  147. if (descriptorSigned < 0) {
  148. return false;
  149. }
  150. tables::Array requirements{};
  151. if (!tables::find_array_at(
  152. requirementTable, static_cast<std::size_t>(descriptorSigned), requirements)
  153. || requirements.elementClass != tables::kMaterialRequirementRowClass
  154. || requirements.count == 0 || requirements.count > output.materialRequirements.size()
  155. || requirements.dataOffset > requirementTable.size()
  156. || requirements.count > (requirementTable.size() - requirements.dataOffset)
  157. / tables::kMaterialRequirementRowStride) {
  158. return false;
  159. }
  160. output.materialRequirementSetHash = requirementSetHash;
  161. output.materialRequirementSetIndex = requirementSetIndex;
  162. output.materialRequirementCount = static_cast<std::uint8_t>(requirements.count);
  163. for (std::size_t requirement = 0; requirement < requirements.count; ++requirement) {
  164. const std::size_t requirementAt =
  165. requirements.dataOffset + requirement * tables::kMaterialRequirementRowStride;
  166. std::uint32_t nativeItemIndex = 0;
  167. std::uint32_t quantity = 0;
  168. std::uint8_t deleteOnAction = 0;
  169. std::uint8_t omitFromRequirements = 0;
  170. std::uint16_t sentinel = 0;
  171. std::memcpy(&nativeItemIndex,
  172. requirementTable.data() + requirementAt
  173. + tables::kMaterialRequirementItemIndexOffset,
  174. sizeof nativeItemIndex);
  175. std::memcpy(&quantity,
  176. requirementTable.data() + requirementAt
  177. + tables::kMaterialRequirementQuantityOffset,
  178. sizeof quantity);
  179. std::memcpy(&deleteOnAction,
  180. requirementTable.data() + requirementAt
  181. + tables::kMaterialRequirementDeleteOffset,
  182. sizeof deleteOnAction);
  183. std::memcpy(&omitFromRequirements,
  184. requirementTable.data() + requirementAt
  185. + tables::kMaterialRequirementOmitOffset,
  186. sizeof omitFromRequirements);
  187. std::memcpy(&sentinel,
  188. requirementTable.data() + requirementAt
  189. + tables::kMaterialRequirementSentinelOffset,
  190. sizeof sentinel);
  191. if (nativeItemIndex >= itemDefinitionCount
  192. || nativeItemIndex > (std::numeric_limits<std::uint16_t>::max)() || quantity == 0
  193. || deleteOnAction > 1 || omitFromRequirements > 1 || sentinel != 0xFFFFU) {
  194. return false;
  195. }
  196. output.materialRequirements[requirement] = {
  197. quantity,
  198. static_cast<std::uint16_t>(nativeItemIndex),
  199. deleteOnAction != 0,
  200. omitFromRequirements != 0,
  201. };
  202. }
  203. }
  204. return state::build_data::publish_collectible_definitions(
  205. std::span(storage.collectibleRows).first(static_cast<std::size_t>(rows.count)));
  206. }
  207. } // namespace sunrise::client::content::items::packages