activity_sdk_squad_config_parse.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /** Parses authored type-1 spawner and type-66 rule configs into normalized squad facts. */
  2. #include <limits>
  3. #include <utility>
  4. #include "../../../middleware/content/packages/tables/authored_squad_reader.h"
  5. #include "activity_sdk_squad_inventory_internal.h"
  6. namespace sunrise::client::content::activity::sdk_generation::squad_inventory::detail {
  7. namespace {
  8. namespace tables = middleware::content::packages::tables;
  9. /** Reads one complete candidate row without trusting unbounded offset arithmetic. */
  10. [[nodiscard]] bool candidate_placement(std::span<const std::byte> blob,
  11. const tables::AuthoredSquadMember& member,
  12. std::size_t variant,
  13. std::uint64_t index,
  14. CandidateFact& output) noexcept {
  15. output = {};
  16. tables::AuthoredSquadCandidate source{};
  17. if (!tables::authored_squad_candidate_record_at(blob, member, variant, index, source)) {
  18. return false;
  19. }
  20. output.candidateDescriptorOffset = source.descriptorOffset;
  21. output.placementRelative = source.placementRelative;
  22. output.placementOffset = source.placementOffset;
  23. output.candidateTail = source.descriptorTail;
  24. if (!source.hasPlacement) {
  25. return true;
  26. }
  27. output.placedEntryClass = source.placementClass;
  28. output.actorDefinitionTag = source.classDefinitionTag;
  29. output.quaternionBits = source.rotationBits;
  30. output.positionBits = source.positionBits;
  31. output.uniformScaleBits = source.uniformScaleBits;
  32. output.nameHash = source.nameHash;
  33. output.placementFlagsRaw = source.placementFlagsRaw;
  34. output.placedEntryIdentity = source.placementIdentity;
  35. output.state = CandidateState::exactPlacement;
  36. return true;
  37. }
  38. /** Compares two parsed candidate lanes exactly. */
  39. [[nodiscard]] bool same_member(const MemberFact& left, const MemberFact& right) noexcept {
  40. if (left.memberKey != right.memberKey || left.reservedU32 != right.reservedU32) {
  41. return false;
  42. }
  43. for (std::size_t lane = 0; lane < left.candidates.size(); ++lane) {
  44. if (left.candidates[lane].size() != right.candidates[lane].size()) {
  45. return false;
  46. }
  47. for (std::size_t row = 0; row < left.candidates[lane].size(); ++row) {
  48. if (left.candidates[lane][row] != right.candidates[lane][row]) {
  49. return false;
  50. }
  51. }
  52. }
  53. return true;
  54. }
  55. } // namespace
  56. /** Compares repeated spawner definitions from different placed paths. */
  57. [[nodiscard]] bool same_spawner(const SpawnerFact& left, const SpawnerFact& right) noexcept {
  58. if (left.configTag != right.configTag || left.rawReference98 != right.rawReference98
  59. || left.rawReferenceA0 != right.rawReferenceA0 || left.complete != right.complete
  60. || left.primaryComponentOffset != right.primaryComponentOffset
  61. || left.secondaryComponentOffset != right.secondaryComponentOffset
  62. || left.primaryComponentClass != right.primaryComponentClass
  63. || left.secondaryComponentClass != right.secondaryComponentClass
  64. || left.members.size() != right.members.size()
  65. || left.hasInlinePointSet != right.hasInlinePointSet
  66. || left.inlinePointSetOffset != right.inlinePointSetOffset
  67. || left.inlinePlacementComponentOffset != right.inlinePlacementComponentOffset
  68. || left.inlineInitialPointIndex != right.inlineInitialPointIndex
  69. || left.inlinePoints != right.inlinePoints
  70. || left.inlinePlacement != right.inlinePlacement) {
  71. return false;
  72. }
  73. for (std::size_t index = 0; index < left.members.size(); ++index) {
  74. if (!same_member(left.members[index], right.members[index])) {
  75. return false;
  76. }
  77. }
  78. return true;
  79. }
  80. /** Compares repeated spawn-rule definitions from different placed paths. */
  81. [[nodiscard]] bool same_rule(const RuleFact& left, const RuleFact& right) noexcept {
  82. if (left.configTag != right.configTag || left.complete != right.complete
  83. || left.primaryComponentOffset != right.primaryComponentOffset
  84. || left.secondaryComponentOffset != right.secondaryComponentOffset
  85. || left.primaryComponentClass != right.primaryComponentClass
  86. || left.secondaryComponentClass != right.secondaryComponentClass
  87. || left.inlineForm != right.inlineForm || left.points.size() != right.points.size()) {
  88. return false;
  89. }
  90. for (std::size_t index = 0; index < left.points.size(); ++index) {
  91. if (left.points[index] != right.points[index]) {
  92. return false;
  93. }
  94. }
  95. return true;
  96. }
  97. /** Parses one exact authored type-1 config. */
  98. [[nodiscard]] bool
  99. parse_spawner(std::uint32_t tag, std::span<const std::byte> blob, SpawnerFact& output) {
  100. output = {};
  101. tables::AuthoredSquadSpawner source{};
  102. if (!tables::authored_squad_spawner(blob, tag, source)
  103. || source.members.count >= format::kAbsentIndex) {
  104. return false;
  105. }
  106. try {
  107. output.configTag = tag;
  108. output.rawReference98 = source.rawReference98;
  109. output.rawReferenceA0 = source.rawReferenceA0;
  110. output.primaryComponentOffset = source.components.primaryOffset;
  111. output.secondaryComponentOffset = source.components.secondaryOffset;
  112. output.primaryComponentClass = source.components.primaryClass;
  113. output.secondaryComponentClass = source.components.secondaryClass;
  114. output.members.reserve(static_cast<std::size_t>(source.members.count));
  115. for (std::uint64_t memberIndex = 0; memberIndex < source.members.count; ++memberIndex) {
  116. tables::AuthoredSquadMember member{};
  117. if (!tables::authored_squad_member_at(blob, source, memberIndex, member)) {
  118. return false;
  119. }
  120. MemberFact row{};
  121. row.memberKey = member.key;
  122. row.reservedU32 = member.reserved;
  123. for (std::size_t lane = 0; lane < member.candidates.size(); ++lane) {
  124. const tables::Array& candidates = member.candidates[lane];
  125. if (candidates.count > (std::numeric_limits<std::uint16_t>::max)()) {
  126. return false;
  127. }
  128. row.candidates[lane].reserve(static_cast<std::size_t>(candidates.count));
  129. for (std::uint64_t candidateIndex = 0; candidateIndex < candidates.count;
  130. ++candidateIndex) {
  131. CandidateFact candidate{};
  132. if (!candidate_placement(blob, member, lane, candidateIndex, candidate)) {
  133. return false;
  134. }
  135. row.candidates[lane].push_back(candidate);
  136. }
  137. }
  138. output.members.push_back(std::move(row));
  139. }
  140. bool inlinePresent = false;
  141. tables::AuthoredSquadInlinePointSet inlineSet{};
  142. if (!tables::authored_squad_inline_point_set(blob, tag, source, inlinePresent, inlineSet)) {
  143. return false;
  144. }
  145. if (inlinePresent) {
  146. if (inlineSet.points.count >= format::kAbsentIndex) {
  147. return false;
  148. }
  149. output.hasInlinePointSet = true;
  150. output.inlinePointSetOffset = inlineSet.pointSetOffset;
  151. output.inlinePlacementComponentOffset = inlineSet.placementComponentOffset;
  152. output.inlineInitialPointIndex = inlineSet.initialPointIndex;
  153. tables::AuthoredSquadRule pointOwner{};
  154. pointOwner.points = inlineSet.points;
  155. output.inlinePoints.reserve(static_cast<std::size_t>(inlineSet.points.count));
  156. for (std::uint64_t index = 0; index < inlineSet.points.count; ++index) {
  157. tables::AuthoredSquadRulePoint point{};
  158. if (!tables::authored_squad_rule_point_at(blob, pointOwner, index, point)) {
  159. return false;
  160. }
  161. RulePointFact row{};
  162. row.placedEntryIdentity = point.placementIdentity;
  163. row.rowOffset = point.rowOffset;
  164. row.rawTail = point.rawTail;
  165. output.inlinePoints.push_back(row);
  166. }
  167. const tables::AuthoredSquadCandidate& placement = inlineSet.placement;
  168. CandidateFact& row = output.inlinePlacement;
  169. row.placementOffset = placement.placementOffset;
  170. row.placedEntryClass = placement.placementClass;
  171. row.actorDefinitionTag = placement.classDefinitionTag;
  172. row.quaternionBits = placement.rotationBits;
  173. row.positionBits = placement.positionBits;
  174. row.uniformScaleBits = placement.uniformScaleBits;
  175. row.nameHash = placement.nameHash;
  176. row.placementFlagsRaw = placement.placementFlagsRaw;
  177. row.placedEntryIdentity = placement.placementIdentity;
  178. row.state = CandidateState::exactPlacement;
  179. }
  180. output.complete = true;
  181. return true;
  182. } catch (...) {
  183. output = {};
  184. return false;
  185. }
  186. }
  187. /** Builds the rule row a spawner's own point set stands for. */
  188. [[nodiscard]] RuleFact inline_rule(const SpawnerFact& spawner) {
  189. RuleFact rule{};
  190. rule.configTag = spawner.configTag;
  191. rule.points = spawner.inlinePoints;
  192. rule.complete = true;
  193. rule.primaryComponentOffset = spawner.inlinePointSetOffset;
  194. rule.secondaryComponentOffset = spawner.inlinePlacementComponentOffset;
  195. rule.primaryComponentClass = tables::kAuthoredSquadInlinePointSetClass;
  196. rule.secondaryComponentClass = tables::kAuthoredSquadInlinePlacementClass;
  197. rule.inlineForm = true;
  198. return rule;
  199. }
  200. /** Parses one exact authored type-66 config. */
  201. [[nodiscard]] bool
  202. parse_rule(std::uint32_t tag, std::span<const std::byte> blob, RuleFact& output) {
  203. output = {};
  204. tables::AuthoredSquadRule source{};
  205. if (!tables::authored_squad_rule(blob, tag, source)
  206. || source.points.count >= format::kAbsentIndex) {
  207. return false;
  208. }
  209. try {
  210. output.configTag = tag;
  211. output.primaryComponentOffset = source.components.primaryOffset;
  212. output.secondaryComponentOffset = source.components.secondaryOffset;
  213. output.primaryComponentClass = source.components.primaryClass;
  214. output.secondaryComponentClass = source.components.secondaryClass;
  215. output.points.reserve(static_cast<std::size_t>(source.points.count));
  216. for (std::uint64_t index = 0; index < source.points.count; ++index) {
  217. tables::AuthoredSquadRulePoint point{};
  218. if (!tables::authored_squad_rule_point_at(blob, source, index, point)) {
  219. return false;
  220. }
  221. RulePointFact row{};
  222. row.placedEntryIdentity = point.placementIdentity;
  223. row.rowOffset = point.rowOffset;
  224. row.rawTail = point.rawTail;
  225. output.points.push_back(row);
  226. }
  227. output.complete = true;
  228. return true;
  229. } catch (...) {
  230. output = {};
  231. return false;
  232. }
  233. }
  234. } // namespace sunrise::client::content::activity::sdk_generation::squad_inventory::detail