activity_sdk_catalog.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976
  1. #include <Windows.h>
  2. #include <algorithm>
  3. #include <atomic>
  4. #include "internal.h"
  5. #include "runtime.h"
  6. namespace sunrise::state::activity_sdk {
  7. namespace {
  8. std::atomic<Snapshot> g_snapshot{};
  9. std::atomic<Status> g_status{Status::notReady};
  10. template <typename Value>
  11. [[nodiscard]] std::span<const Value>
  12. rows(const format::Header* header, const std::byte* view, format::SectionIndex index) noexcept {
  13. if (header == nullptr || view == nullptr) {
  14. return {};
  15. }
  16. const format::Section& section = header->sections[static_cast<std::size_t>(index)];
  17. return {reinterpret_cast<const Value*>(view + section.offset), section.count};
  18. }
  19. /** Returns one validated child range only for a parent from the same mapping. */
  20. template <typename Parent, typename Child>
  21. [[nodiscard]] std::span<const Child> children(std::span<const Parent> parents,
  22. const Parent& parent,
  23. std::span<const Child> values,
  24. format::Range range) noexcept {
  25. const std::size_t first = range.first;
  26. const std::size_t count = range.count;
  27. if (!owns(parents, parent) || first > values.size() || count > values.size() - first) {
  28. return {};
  29. }
  30. return values.subspan(first, count);
  31. }
  32. } // namespace
  33. /** Releases the mapped view after the last immutable snapshot owner leaves. */
  34. Catalog::~Catalog() noexcept {
  35. if (view_ != nullptr) {
  36. UnmapViewOfFile(view_);
  37. }
  38. if (mapping_ != nullptr) {
  39. CloseHandle(static_cast<HANDLE>(mapping_));
  40. }
  41. if (file_ != nullptr && file_ != INVALID_HANDLE_VALUE) {
  42. CloseHandle(static_cast<HANDLE>(file_));
  43. }
  44. }
  45. std::span<const std::byte> Catalog::sdk_build_sha256() const noexcept {
  46. return header_ != nullptr ? std::span<const std::byte>(header_->sdkBuildSha256)
  47. : std::span<const std::byte>{};
  48. }
  49. std::span<const std::byte> Catalog::payload_sha256() const noexcept {
  50. return header_ != nullptr ? std::span<const std::byte>(header_->payloadSha256)
  51. : std::span<const std::byte>{};
  52. }
  53. std::span<const std::byte> Catalog::content_key_sha256() const noexcept {
  54. return header_ != nullptr ? std::span<const std::byte>(header_->contentKeySha256)
  55. : std::span<const std::byte>{};
  56. }
  57. std::span<const std::byte> Catalog::logical_ir_sha256() const noexcept {
  58. return header_ != nullptr ? std::span<const std::byte>(header_->logicalIrSha256)
  59. : std::span<const std::byte>{};
  60. }
  61. std::wstring_view Catalog::artifact_directory() const noexcept {
  62. return {artifactDirectory_.chars.data(), artifactDirectory_.length};
  63. }
  64. std::span<const std::byte> Catalog::string_bytes() const noexcept {
  65. return rows<std::byte>(header_, view_, format::SectionIndex::strings);
  66. }
  67. /** Resolves only refs already admitted by validation and stays safe for defensive callers. */
  68. std::string_view Catalog::string(format::StringRef reference) const noexcept {
  69. const auto values = string_bytes();
  70. if (reference.offset == format::kAbsentIndex) {
  71. return {};
  72. }
  73. const std::size_t first = reference.offset;
  74. const std::size_t length = reference.length;
  75. if (first > values.size() || length > values.size() - first) {
  76. return {};
  77. }
  78. return {reinterpret_cast<const char*>(values.data() + first), length};
  79. }
  80. std::span<const format::Activity> Catalog::activities() const noexcept {
  81. return rows<format::Activity>(header_, view_, format::SectionIndex::activities);
  82. }
  83. std::span<const format::Scenario> Catalog::scenarios() const noexcept {
  84. return rows<format::Scenario>(header_, view_, format::SectionIndex::scenarios);
  85. }
  86. std::span<const format::Bubble> Catalog::bubbles() const noexcept {
  87. return rows<format::Bubble>(header_, view_, format::SectionIndex::bubbles);
  88. }
  89. std::span<const format::State> Catalog::states() const noexcept {
  90. return rows<format::State>(header_, view_, format::SectionIndex::states);
  91. }
  92. std::span<const format::Object> Catalog::objects() const noexcept {
  93. return rows<format::Object>(header_, view_, format::SectionIndex::objects);
  94. }
  95. std::span<const format::Occurrence> Catalog::occurrences() const noexcept {
  96. return rows<format::Occurrence>(header_, view_, format::SectionIndex::occurrences);
  97. }
  98. std::span<const format::Slot> Catalog::slots() const noexcept {
  99. return rows<format::Slot>(header_, view_, format::SectionIndex::slots);
  100. }
  101. std::span<const format::Text> Catalog::texts() const noexcept {
  102. return rows<format::Text>(header_, view_, format::SectionIndex::texts);
  103. }
  104. std::span<const format::Capability> Catalog::capabilities() const noexcept {
  105. return rows<format::Capability>(header_, view_, format::SectionIndex::capabilities);
  106. }
  107. std::span<const format::Gate> Catalog::gates() const noexcept {
  108. return rows<format::Gate>(header_, view_, format::SectionIndex::gates);
  109. }
  110. std::span<const format::Refusal> Catalog::refusals() const noexcept {
  111. return rows<format::Refusal>(header_, view_, format::SectionIndex::refusals);
  112. }
  113. std::span<const format::ActorClass> Catalog::actor_classes() const noexcept {
  114. return rows<format::ActorClass>(header_, view_, format::SectionIndex::actorClasses);
  115. }
  116. std::span<const format::RsatDescriptor> Catalog::rsat_descriptors() const noexcept {
  117. return rows<format::RsatDescriptor>(header_, view_, format::SectionIndex::rsatDescriptors);
  118. }
  119. std::span<const format::RsatSchema> Catalog::rsat_schemas() const noexcept {
  120. return rows<format::RsatSchema>(header_, view_, format::SectionIndex::rsatSchemas);
  121. }
  122. std::span<const format::RsatField> Catalog::rsat_fields() const noexcept {
  123. return rows<format::RsatField>(header_, view_, format::SectionIndex::rsatFields);
  124. }
  125. std::span<const format::Squad> Catalog::squads() const noexcept {
  126. return rows<format::Squad>(header_, view_, format::SectionIndex::squads);
  127. }
  128. std::span<const format::SquadMember> Catalog::squad_members() const noexcept {
  129. return rows<format::SquadMember>(header_, view_, format::SectionIndex::squadMembers);
  130. }
  131. std::span<const format::SquadAnchor> Catalog::squad_anchors() const noexcept {
  132. return rows<format::SquadAnchor>(header_, view_, format::SectionIndex::squadAnchors);
  133. }
  134. std::span<const format::AuthoredSceneResource> Catalog::authored_scene_resources() const noexcept {
  135. return rows<format::AuthoredSceneResource>(
  136. header_, view_, format::SectionIndex::authoredSceneResources);
  137. }
  138. std::span<const format::AuthoredSceneSquadEdge>
  139. Catalog::authored_scene_squad_edges() const noexcept {
  140. return rows<format::AuthoredSceneSquadEdge>(
  141. header_, view_, format::SectionIndex::authoredSceneSquadEdges);
  142. }
  143. std::span<const format::TaskTarget> Catalog::task_targets() const noexcept {
  144. return rows<format::TaskTarget>(header_, view_, format::SectionIndex::taskTargets);
  145. }
  146. std::span<const format::DialogueCueText> Catalog::dialogue_cue_texts() const noexcept {
  147. return rows<format::DialogueCueText>(header_, view_, format::SectionIndex::dialogueCueTexts);
  148. }
  149. std::span<const format::DialogueCue> Catalog::dialogue_cues() const noexcept {
  150. return rows<format::DialogueCue>(header_, view_, format::SectionIndex::dialogueCues);
  151. }
  152. std::span<const format::ActorAbility> Catalog::actor_abilities() const noexcept {
  153. return rows<format::ActorAbility>(header_, view_, format::SectionIndex::actorAbilities);
  154. }
  155. std::span<const format::ActorAbilityTarget> Catalog::actor_ability_targets() const noexcept {
  156. return rows<format::ActorAbilityTarget>(
  157. header_, view_, format::SectionIndex::actorAbilityTargets);
  158. }
  159. std::span<const format::CombatObjectiveGroup> Catalog::combat_objective_groups() const noexcept {
  160. return rows<format::CombatObjectiveGroup>(
  161. header_, view_, format::SectionIndex::combatObjectiveGroups);
  162. }
  163. std::span<const format::DirectiveElement> Catalog::directive_elements() const noexcept {
  164. return rows<format::DirectiveElement>(header_, view_, format::SectionIndex::directiveElements);
  165. }
  166. std::span<const format::ActivityBindingTag> Catalog::activity_binding_tags() const noexcept {
  167. return rows<format::ActivityBindingTag>(
  168. header_, view_, format::SectionIndex::activityBindingTags);
  169. }
  170. std::span<const format::ActivityBindingLocator>
  171. Catalog::activity_binding_locators() const noexcept {
  172. return rows<format::ActivityBindingLocator>(
  173. header_, view_, format::SectionIndex::activityBindingLocators);
  174. }
  175. std::span<const format::BehaviorProgram> Catalog::behavior_programs() const noexcept {
  176. return rows<format::BehaviorProgram>(header_, view_, format::SectionIndex::behaviorPrograms);
  177. }
  178. std::span<const format::BehaviorInput> Catalog::behavior_inputs() const noexcept {
  179. return rows<format::BehaviorInput>(header_, view_, format::SectionIndex::behaviorInputs);
  180. }
  181. std::span<const format::BehaviorChannelWrite> Catalog::behavior_channel_writes() const noexcept {
  182. return rows<format::BehaviorChannelWrite>(
  183. header_, view_, format::SectionIndex::behaviorChannelWrites);
  184. }
  185. std::span<const format::BehaviorOwner> Catalog::behavior_owners() const noexcept {
  186. return rows<format::BehaviorOwner>(header_, view_, format::SectionIndex::behaviorOwners);
  187. }
  188. std::span<const format::BehaviorActivityBinding>
  189. Catalog::behavior_activity_bindings() const noexcept {
  190. return rows<format::BehaviorActivityBinding>(
  191. header_, view_, format::SectionIndex::behaviorActivityBindings);
  192. }
  193. std::span<const format::ActorMessageSchema> Catalog::actor_message_schemas() const noexcept {
  194. return rows<format::ActorMessageSchema>(
  195. header_, view_, format::SectionIndex::actorMessageSchemas);
  196. }
  197. std::span<const format::ActorCommandDefinition>
  198. Catalog::actor_command_definitions() const noexcept {
  199. return rows<format::ActorCommandDefinition>(
  200. header_, view_, format::SectionIndex::actorCommandDefinitions);
  201. }
  202. std::span<const format::ActorBehaviorProfile> Catalog::actor_behavior_profiles() const noexcept {
  203. return rows<format::ActorBehaviorProfile>(
  204. header_, view_, format::SectionIndex::actorBehaviorProfiles);
  205. }
  206. std::span<const format::SimulationEventDefinition>
  207. Catalog::simulation_event_definitions() const noexcept {
  208. return rows<format::SimulationEventDefinition>(
  209. header_, view_, format::SectionIndex::simulationEventDefinitions);
  210. }
  211. std::span<const format::RuntimeSchema> Catalog::runtime_schemas() const noexcept {
  212. return rows<format::RuntimeSchema>(header_, view_, format::SectionIndex::runtimeSchemas);
  213. }
  214. std::span<const format::RuntimeField> Catalog::runtime_fields() const noexcept {
  215. return rows<format::RuntimeField>(header_, view_, format::SectionIndex::runtimeFields);
  216. }
  217. std::span<const format::RuntimeTypeDefinition> Catalog::runtime_type_definitions() const noexcept {
  218. return rows<format::RuntimeTypeDefinition>(
  219. header_, view_, format::SectionIndex::runtimeTypeDefinitions);
  220. }
  221. std::span<const format::SobjectRsat> Catalog::sobject_rsats() const noexcept {
  222. return rows<format::SobjectRsat>(header_, view_, format::SectionIndex::sobjectRsats);
  223. }
  224. std::span<const format::SobjectRsatDescriptor> Catalog::sobject_rsat_descriptors() const noexcept {
  225. return rows<format::SobjectRsatDescriptor>(
  226. header_, view_, format::SectionIndex::sobjectRsatDescriptors);
  227. }
  228. std::span<const format::EntityTypeDefinition> Catalog::entity_type_definitions() const noexcept {
  229. return rows<format::EntityTypeDefinition>(
  230. header_, view_, format::SectionIndex::entityTypeDefinitions);
  231. }
  232. std::span<const format::SobjectRsatFieldBinding>
  233. Catalog::sobject_rsat_field_bindings() const noexcept {
  234. return rows<format::SobjectRsatFieldBinding>(
  235. header_, view_, format::SectionIndex::sobjectRsatFieldBindings);
  236. }
  237. std::span<const format::ActorSequenceTable> Catalog::actor_sequence_tables() const noexcept {
  238. return rows<format::ActorSequenceTable>(
  239. header_, view_, format::SectionIndex::actorSequenceTables);
  240. }
  241. std::span<const format::ActorSequenceEntry> Catalog::actor_sequence_entries() const noexcept {
  242. return rows<format::ActorSequenceEntry>(
  243. header_, view_, format::SectionIndex::actorSequenceEntries);
  244. }
  245. std::span<const format::ActorSequenceBinding> Catalog::actor_sequence_bindings() const noexcept {
  246. return rows<format::ActorSequenceBinding>(
  247. header_, view_, format::SectionIndex::actorSequenceBindings);
  248. }
  249. std::span<const format::AuthoredSceneEventKey> Catalog::authored_scene_event_keys() const noexcept {
  250. return rows<format::AuthoredSceneEventKey>(
  251. header_, view_, format::SectionIndex::authoredSceneEventKeys);
  252. }
  253. std::span<const format::ActorStateName> Catalog::actor_state_names() const noexcept {
  254. return rows<format::ActorStateName>(header_, view_, format::SectionIndex::actorStateNames);
  255. }
  256. /** Atomically publishes a catalog-authorized module-relative pack or its refusal status. */
  257. void initialize(void* module, const ExpectedIdentity& expected) noexcept {
  258. std::shared_ptr<Catalog> pending;
  259. Status result = Status::catalogInvalid;
  260. if (load(module, expected, pending, result)) {
  261. g_snapshot.store(Snapshot(std::move(pending)), std::memory_order_release);
  262. g_status.store(Status::ready, std::memory_order_release);
  263. return;
  264. }
  265. g_snapshot.store({}, std::memory_order_release);
  266. g_status.store(result, std::memory_order_release);
  267. }
  268. /** Keeps the published mapping when a catalog-authorized replacement cannot be authenticated. */
  269. bool reload(void* module, const ExpectedIdentity& expected) noexcept {
  270. std::shared_ptr<Catalog> pending;
  271. Status result = Status::catalogInvalid;
  272. if (!load(module, expected, pending, result)) {
  273. return false;
  274. }
  275. g_snapshot.store(Snapshot(std::move(pending)), std::memory_order_release);
  276. g_status.store(Status::ready, std::memory_order_release);
  277. return true;
  278. }
  279. #if defined(SUNRISE_ACTIVITY_SDK_TESTING)
  280. /** Loads the compile-pinned canonical regression fixture in test binaries only. */
  281. void initialize(void* module) noexcept {
  282. const ExpectedIdentity expected{format::kExpectedSdkBuildDigest,
  283. format::kExpectedPayloadDigest,
  284. format::kExpectedContentKeyDigest,
  285. format::kExpectedLogicalIrDigest};
  286. initialize(module, expected);
  287. }
  288. /** Reloads the compile-pinned canonical regression fixture in test binaries only. */
  289. bool reload(void* module) noexcept {
  290. const ExpectedIdentity expected{format::kExpectedSdkBuildDigest,
  291. format::kExpectedPayloadDigest,
  292. format::kExpectedContentKeyDigest,
  293. format::kExpectedLogicalIrDigest};
  294. return reload(module, expected);
  295. }
  296. #endif
  297. void shutdown() noexcept {
  298. g_snapshot.store({}, std::memory_order_release);
  299. g_status.store(Status::notReady, std::memory_order_release);
  300. }
  301. Status status() noexcept {
  302. return g_status.load(std::memory_order_acquire);
  303. }
  304. /** Maps every public status to stable machine-readable panel text. */
  305. const char* status_name(Status value) noexcept {
  306. switch (value) {
  307. case Status::notReady:
  308. return "not_ready";
  309. case Status::ready:
  310. return "ready";
  311. case Status::missing:
  312. return "missing";
  313. case Status::wrongSdkBuild:
  314. return "wrong_sdk_build";
  315. case Status::catalogInvalid:
  316. return "catalog_invalid";
  317. case Status::missingClient:
  318. return "missing_client";
  319. case Status::ambiguousClient:
  320. return "ambiguous_client";
  321. case Status::staleSession:
  322. return "stale_session";
  323. case Status::wrongActivity:
  324. return "wrong_activity";
  325. case Status::activityJoinNotExact:
  326. return "activity_join_not_exact";
  327. case Status::missingScenarioLink:
  328. return "missing_scenario_link";
  329. case Status::staleActivityClient:
  330. return "stale_activity_client";
  331. }
  332. return "catalog_invalid";
  333. }
  334. /** Maps every mission-plan result to stable machine-readable diagnostic text. */
  335. const char* status_name(MissionSeedStatus value) noexcept {
  336. switch (value) {
  337. case MissionSeedStatus::ready:
  338. return "ready";
  339. case MissionSeedStatus::invalidView:
  340. return "invalid_view";
  341. case MissionSeedStatus::invalidSliceSet:
  342. return "invalid_slice_set";
  343. case MissionSeedStatus::missingInitialState:
  344. return "missing_initial_state";
  345. case MissionSeedStatus::ambiguousInitialState:
  346. return "ambiguous_initial_state";
  347. case MissionSeedStatus::invalidOccurrence:
  348. return "invalid_occurrence";
  349. case MissionSeedStatus::schemaJoinNotExact:
  350. return "schema_join_not_exact";
  351. case MissionSeedStatus::invalidRosterGroup:
  352. return "invalid_roster_group";
  353. case MissionSeedStatus::rosterKeyConflict:
  354. return "roster_key_conflict";
  355. case MissionSeedStatus::groupCapacityExceeded:
  356. return "group_capacity_exceeded";
  357. }
  358. return "invalid_view";
  359. }
  360. /** Maps every authored-scene seed result to stable machine-readable diagnostic text. */
  361. const char* status_name(AuthoredSceneSeedStatus value) noexcept {
  362. switch (value) {
  363. case AuthoredSceneSeedStatus::ready:
  364. return "ready";
  365. case AuthoredSceneSeedStatus::invalidObject:
  366. return "invalid_object";
  367. case AuthoredSceneSeedStatus::schemaMismatch:
  368. return "schema_mismatch";
  369. case AuthoredSceneSeedStatus::missingResource:
  370. return "missing_resource";
  371. case AuthoredSceneSeedStatus::ambiguousResource:
  372. return "ambiguous_resource";
  373. case AuthoredSceneSeedStatus::capacityExceeded:
  374. return "capacity_exceeded";
  375. }
  376. return "invalid_object";
  377. }
  378. Snapshot snapshot() noexcept {
  379. return g_snapshot.load(std::memory_order_acquire);
  380. }
  381. /** Fills the compiled communication route for one message id built into the executable. */
  382. bool executable_communication_route(
  383. std::uint32_t messageId,
  384. middleware::bap::activity_message::wire_schema::communication::ActivityCommunicationRoute&
  385. output) noexcept {
  386. namespace communication = middleware::bap::activity_message::wire_schema::communication;
  387. output = {};
  388. const communication::ActivityCommunicationRoute* const route =
  389. communication::find_route(messageId);
  390. if (route == nullptr) {
  391. return false;
  392. }
  393. output = *route;
  394. return true;
  395. }
  396. const format::Activity* bound_activity(const BoundView& view) noexcept {
  397. if (view.catalog == nullptr || view.activityRow >= view.catalog->activities().size()) {
  398. return nullptr;
  399. }
  400. return &view.catalog->activities()[view.activityRow];
  401. }
  402. const format::Scenario* bound_scenario(const BoundView& view) noexcept {
  403. if (view.catalog == nullptr || view.scenarioRow >= view.catalog->scenarios().size()) {
  404. return nullptr;
  405. }
  406. return &view.catalog->scenarios()[view.scenarioRow];
  407. }
  408. std::span<const format::Text> activity_aliases(const Catalog& catalog,
  409. const format::Activity& activity) noexcept {
  410. return children(catalog.activities(), activity, catalog.texts(), activity.aliases);
  411. }
  412. std::span<const format::Capability>
  413. activity_capabilities(const Catalog& catalog, const format::Activity& activity) noexcept {
  414. return children(catalog.activities(), activity, catalog.capabilities(), activity.capabilities);
  415. }
  416. std::span<const format::ActivityBindingTag>
  417. activity_root_candidate_tags(const Catalog& catalog, const format::Activity& activity) noexcept {
  418. return children(catalog.activities(),
  419. activity,
  420. catalog.activity_binding_tags(),
  421. activity.activityRootCandidateTags);
  422. }
  423. std::span<const format::ActivityBindingTag>
  424. activity_scenario_name_candidate_tags(const Catalog& catalog,
  425. const format::Activity& activity) noexcept {
  426. return children(catalog.activities(),
  427. activity,
  428. catalog.activity_binding_tags(),
  429. activity.scenarioNameCandidateTags);
  430. }
  431. std::span<const format::ActivityBindingTag>
  432. activity_evidence_root_tags(const Catalog& catalog, const format::Activity& activity) noexcept {
  433. return children(
  434. catalog.activities(), activity, catalog.activity_binding_tags(), activity.evidenceRootTags);
  435. }
  436. std::span<const format::ActivityBindingLocator>
  437. activity_binding_locators(const Catalog& catalog, const format::Activity& activity) noexcept {
  438. return children(catalog.activities(),
  439. activity,
  440. catalog.activity_binding_locators(),
  441. activity.bindingLocators);
  442. }
  443. /** Finds the canonical contiguous host-capability suffix without allocating a filtered copy. */
  444. std::span<const format::Capability> host_capabilities(const Catalog& catalog) noexcept {
  445. const auto values = catalog.capabilities();
  446. for (std::size_t index = 0; index < values.size(); ++index) {
  447. if (values[index].subjectKind == static_cast<std::uint32_t>(format::SubjectKind::hostApi)) {
  448. return values.subspan(index);
  449. }
  450. }
  451. return {};
  452. }
  453. std::span<const format::Bubble> scenario_bubbles(const Catalog& catalog,
  454. const format::Scenario& scenario) noexcept {
  455. return children(catalog.scenarios(), scenario, catalog.bubbles(), scenario.bubbles);
  456. }
  457. std::span<const format::State> scenario_states(const Catalog& catalog,
  458. const format::Scenario& scenario) noexcept {
  459. return children(catalog.scenarios(), scenario, catalog.states(), scenario.states);
  460. }
  461. std::span<const format::Occurrence>
  462. scenario_occurrences(const Catalog& catalog, const format::Scenario& scenario) noexcept {
  463. return children(catalog.scenarios(), scenario, catalog.occurrences(), scenario.occurrences);
  464. }
  465. /** Relies on scenario-index ordering to return one contiguous zero-copy squad range. */
  466. std::span<const format::Squad> scenario_squads(const Catalog& catalog,
  467. const format::Scenario& scenario) noexcept {
  468. const auto scenarios = catalog.scenarios();
  469. if (!owns(scenarios, scenario)) {
  470. return {};
  471. }
  472. const auto values = catalog.squads();
  473. const std::uint32_t scenarioIndex = static_cast<std::uint32_t>(&scenario - scenarios.data());
  474. const auto first = std::lower_bound(
  475. values.begin(), values.end(), scenarioIndex, [](const format::Squad& row, auto index) {
  476. return row.scenarioIndex < index;
  477. });
  478. const auto last = std::upper_bound(
  479. first, values.end(), scenarioIndex, [](auto index, const format::Squad& row) {
  480. return index < row.scenarioIndex;
  481. });
  482. const std::size_t offset = static_cast<std::size_t>(first - values.begin());
  483. return values.subspan(offset, static_cast<std::size_t>(last - first));
  484. }
  485. std::span<const format::State> bubble_states(const Catalog& catalog,
  486. const format::Bubble& bubble) noexcept {
  487. return children(catalog.bubbles(), bubble, catalog.states(), bubble.states);
  488. }
  489. std::span<const format::Slot> object_slots(const Catalog& catalog,
  490. const format::Object& object) noexcept {
  491. return children(catalog.objects(), object, catalog.slots(), object.slots);
  492. }
  493. std::span<const format::Text> slot_aliases(const Catalog& catalog,
  494. const format::Slot& slot) noexcept {
  495. return children(catalog.slots(), slot, catalog.texts(), slot.aliases);
  496. }
  497. std::span<const format::Capability> slot_capabilities(const Catalog& catalog,
  498. const format::Slot& slot) noexcept {
  499. return children(catalog.slots(), slot, catalog.capabilities(), slot.capabilities);
  500. }
  501. std::span<const format::Gate> capability_gates(const Catalog& catalog,
  502. const format::Capability& capability) noexcept {
  503. return children(catalog.capabilities(), capability, catalog.gates(), capability.gates);
  504. }
  505. std::span<const format::Refusal>
  506. capability_refusals(const Catalog& catalog, const format::Capability& capability) noexcept {
  507. return children(catalog.capabilities(), capability, catalog.refusals(), capability.refusals);
  508. }
  509. std::span<const format::Text> refusal_reason_codes(const Catalog& catalog,
  510. const format::Refusal& refusal) noexcept {
  511. return children(catalog.refusals(), refusal, catalog.texts(), refusal.reasonCodes);
  512. }
  513. std::span<const format::RsatDescriptor>
  514. actor_class_descriptors(const Catalog& catalog, const format::ActorClass& actorClass) noexcept {
  515. return children(
  516. catalog.actor_classes(), actorClass, catalog.rsat_descriptors(), actorClass.descriptors);
  517. }
  518. std::span<const format::RsatField> rsat_schema_fields(const Catalog& catalog,
  519. const format::RsatSchema& schema) noexcept {
  520. return children(catalog.rsat_schemas(), schema, catalog.rsat_fields(), schema.fields);
  521. }
  522. const format::RsatSchema*
  523. rsat_descriptor_schema(const Catalog& catalog, const format::RsatDescriptor& descriptor) noexcept {
  524. if (!owns(catalog.rsat_descriptors(), descriptor)
  525. || descriptor.schemaIndex >= catalog.rsat_schemas().size()) {
  526. return nullptr;
  527. }
  528. return &catalog.rsat_schemas()[descriptor.schemaIndex];
  529. }
  530. /** @return The actor command with this name, or null when no row carries it. */
  531. const format::ActorCommandDefinition* actor_command_by_name(const Catalog& catalog,
  532. std::string_view name) noexcept {
  533. if (name.empty()) {
  534. return nullptr;
  535. }
  536. for (const format::ActorCommandDefinition& row : catalog.actor_command_definitions()) {
  537. if (catalog.string(row.name) == name) {
  538. return &row;
  539. }
  540. }
  541. return nullptr;
  542. }
  543. /** @return The actor message schema with this name, or null when no row carries it. */
  544. const format::ActorMessageSchema* actor_message_schema_by_name(const Catalog& catalog,
  545. std::string_view name) noexcept {
  546. if (name.empty()) {
  547. return nullptr;
  548. }
  549. for (const format::ActorMessageSchema& row : catalog.actor_message_schemas()) {
  550. if (catalog.string(row.name) == name) {
  551. return &row;
  552. }
  553. }
  554. return nullptr;
  555. }
  556. /** @return The profile row of this actor class, or null when the two rows disagree. */
  557. const format::ActorBehaviorProfile*
  558. actor_behavior_profile(const Catalog& catalog, const format::ActorClass& actorClass) noexcept {
  559. const auto actors = catalog.actor_classes();
  560. if (!owns(actors, actorClass)) {
  561. return nullptr;
  562. }
  563. const std::size_t index = static_cast<std::size_t>(&actorClass - actors.data());
  564. const auto profiles = catalog.actor_behavior_profiles();
  565. if (index >= profiles.size() || profiles[index].actorClassIndex != index) {
  566. return nullptr;
  567. }
  568. return &profiles[index];
  569. }
  570. /** @return The simulation event with this name, or null when no row carries it. */
  571. const format::SimulationEventDefinition* simulation_event_by_name(const Catalog& catalog,
  572. std::string_view name) noexcept {
  573. if (name.empty()) {
  574. return nullptr;
  575. }
  576. for (const format::SimulationEventDefinition& row : catalog.simulation_event_definitions()) {
  577. if (catalog.string(row.name) == name) {
  578. return &row;
  579. }
  580. }
  581. return nullptr;
  582. }
  583. const format::RuntimeSchema* runtime_schema_by_handle(const Catalog& catalog,
  584. std::uint32_t handle) noexcept {
  585. for (const format::RuntimeSchema& row : catalog.runtime_schemas()) {
  586. if (row.handle == handle) {
  587. return &row;
  588. }
  589. }
  590. return nullptr;
  591. }
  592. std::span<const format::RuntimeField>
  593. runtime_schema_fields(const Catalog& catalog, const format::RuntimeSchema& schema) noexcept {
  594. return children(catalog.runtime_schemas(), schema, catalog.runtime_fields(), schema.fields);
  595. }
  596. /** @return The type this codec family gives that code, or null when no row declares it. */
  597. const format::RuntimeTypeDefinition* runtime_type_by_code(const Catalog& catalog,
  598. format::RuntimeCodecFamily codecFamily,
  599. std::uint32_t typeCode) noexcept {
  600. for (const format::RuntimeTypeDefinition& row : catalog.runtime_type_definitions()) {
  601. if ((row.codecFamilies & static_cast<std::uint32_t>(codecFamily)) != 0
  602. && row.typeCode == typeCode) {
  603. return &row;
  604. }
  605. }
  606. return nullptr;
  607. }
  608. const format::SobjectRsat* sobject_rsat_by_tag(const Catalog& catalog,
  609. std::uint32_t rsatTag) noexcept {
  610. const auto rows = catalog.sobject_rsats();
  611. const auto found =
  612. std::lower_bound(rows.begin(), rows.end(), rsatTag, [](const auto& row, auto tag) {
  613. return row.rsatTag < tag;
  614. });
  615. return found != rows.end() && found->rsatTag == rsatTag ? &*found : nullptr;
  616. }
  617. std::span<const format::SobjectRsatDescriptor>
  618. sobject_rsat_descriptors(const Catalog& catalog, const format::SobjectRsat& rsat) noexcept {
  619. return children(
  620. catalog.sobject_rsats(), rsat, catalog.sobject_rsat_descriptors(), rsat.descriptors);
  621. }
  622. /** @return The entity type with this generated name, or null when no row carries it. */
  623. const format::EntityTypeDefinition* entity_type_by_name(const Catalog& catalog,
  624. std::string_view name) noexcept {
  625. if (name.empty()) {
  626. return nullptr;
  627. }
  628. for (const format::EntityTypeDefinition& row : catalog.entity_type_definitions()) {
  629. if (catalog.string(row.name) == name) {
  630. return &row;
  631. }
  632. }
  633. return nullptr;
  634. }
  635. const format::EntityTypeDefinition* entity_type_by_value(const Catalog& catalog,
  636. std::uint32_t entityType) noexcept {
  637. for (const format::EntityTypeDefinition& row : catalog.entity_type_definitions()) {
  638. if (row.entityType == entityType) {
  639. return &row;
  640. }
  641. }
  642. return nullptr;
  643. }
  644. /** @return The binding row of this RSAT field, or null when the two rows disagree. */
  645. const format::SobjectRsatFieldBinding*
  646. sobject_rsat_field_binding(const Catalog& catalog, const format::RsatField& field) noexcept {
  647. const auto fields = catalog.rsat_fields();
  648. if (!owns(fields, field)) {
  649. return nullptr;
  650. }
  651. const std::size_t index = static_cast<std::size_t>(&field - fields.data());
  652. const auto bindings = catalog.sobject_rsat_field_bindings();
  653. return index < bindings.size() && bindings[index].rsatFieldIndex == index ? &bindings[index]
  654. : nullptr;
  655. }
  656. std::span<const format::SquadMember> squad_members(const Catalog& catalog,
  657. const format::Squad& squad) noexcept {
  658. return children(catalog.squads(), squad, catalog.squad_members(), squad.members);
  659. }
  660. std::span<const format::SquadAnchor> squad_anchors(const Catalog& catalog,
  661. const format::Squad& squad) noexcept {
  662. return children(catalog.squads(), squad, catalog.squad_anchors(), squad.anchors);
  663. }
  664. /** Relies on slot-index ordering to return one contiguous zero-copy resource range. */
  665. std::span<const format::AuthoredSceneResource>
  666. slot_authored_scene_resources(const Catalog& catalog, const format::Slot& slot) noexcept {
  667. const auto slots = catalog.slots();
  668. if (!owns(slots, slot)) {
  669. return {};
  670. }
  671. const auto values = catalog.authored_scene_resources();
  672. const std::uint32_t slotIndex = static_cast<std::uint32_t>(&slot - slots.data());
  673. const auto first =
  674. std::lower_bound(values.begin(), values.end(), slotIndex, [](const auto& row, auto index) {
  675. return row.slotIndex < index;
  676. });
  677. const auto last =
  678. std::upper_bound(first, values.end(), slotIndex, [](auto index, const auto& row) {
  679. return index < row.slotIndex;
  680. });
  681. return values.subspan(static_cast<std::size_t>(first - values.begin()),
  682. static_cast<std::size_t>(last - first));
  683. }
  684. /** Relies on slot ordering to return one contiguous zero-copy element range. */
  685. std::span<const format::DirectiveElement>
  686. slot_directive_elements(const Catalog& catalog, const format::Slot& slot) noexcept {
  687. const auto slots = catalog.slots();
  688. if (!owns(slots, slot)) {
  689. return {};
  690. }
  691. const auto values = catalog.directive_elements();
  692. const std::uint32_t slotIndex = static_cast<std::uint32_t>(&slot - slots.data());
  693. const auto first =
  694. std::lower_bound(values.begin(), values.end(), slotIndex, [](const auto& row, auto index) {
  695. return row.slotIndex < index;
  696. });
  697. const auto last =
  698. std::upper_bound(first, values.end(), slotIndex, [](auto index, const auto& row) {
  699. return index < row.slotIndex;
  700. });
  701. return values.subspan(static_cast<std::size_t>(first - values.begin()),
  702. static_cast<std::size_t>(last - first));
  703. }
  704. /** Relies on scene-slot ordering to return one contiguous zero-copy key range. */
  705. std::span<const format::AuthoredSceneEventKey>
  706. slot_authored_scene_event_keys(const Catalog& catalog, const format::Slot& slot) noexcept {
  707. const auto slots = catalog.slots();
  708. if (!owns(slots, slot)) {
  709. return {};
  710. }
  711. const auto values = catalog.authored_scene_event_keys();
  712. const std::uint32_t slotIndex = static_cast<std::uint32_t>(&slot - slots.data());
  713. const auto first =
  714. std::lower_bound(values.begin(), values.end(), slotIndex, [](const auto& row, auto index) {
  715. return row.sceneSlotIndex < index;
  716. });
  717. const auto last =
  718. std::upper_bound(first, values.end(), slotIndex, [](auto index, const auto& row) {
  719. return index < row.sceneSlotIndex;
  720. });
  721. return values.subspan(static_cast<std::size_t>(first - values.begin()),
  722. static_cast<std::size_t>(last - first));
  723. }
  724. /** Relies on scene-slot ordering to return one contiguous zero-copy edge range. */
  725. std::span<const format::AuthoredSceneSquadEdge>
  726. slot_authored_scene_squad_edges(const Catalog& catalog, const format::Slot& slot) noexcept {
  727. const auto slots = catalog.slots();
  728. if (!owns(slots, slot)) {
  729. return {};
  730. }
  731. const auto values = catalog.authored_scene_squad_edges();
  732. const std::uint32_t slotIndex = static_cast<std::uint32_t>(&slot - slots.data());
  733. const auto first =
  734. std::lower_bound(values.begin(), values.end(), slotIndex, [](const auto& row, auto index) {
  735. return row.sceneSlotIndex < index;
  736. });
  737. const auto last =
  738. std::upper_bound(first, values.end(), slotIndex, [](auto index, const auto& row) {
  739. return index < row.sceneSlotIndex;
  740. });
  741. return values.subspan(static_cast<std::size_t>(first - values.begin()),
  742. static_cast<std::size_t>(last - first));
  743. }
  744. const format::Slot*
  745. authored_scene_linked_squad_slot(const Catalog& catalog,
  746. const format::AuthoredSceneSquadEdge& edge) noexcept {
  747. if (!owns(catalog.authored_scene_squad_edges(), edge)
  748. || edge.squadSlotIndex >= catalog.slots().size()) {
  749. return nullptr;
  750. }
  751. return &catalog.slots()[edge.squadSlotIndex];
  752. }
  753. /** Relies on actor-class ordering to return one contiguous zero-copy state-name range. */
  754. std::span<const format::ActorStateName>
  755. actor_class_state_names(const Catalog& catalog, std::uint32_t actorClassIndex) noexcept {
  756. const auto values = catalog.actor_state_names();
  757. const auto first = std::lower_bound(
  758. values.begin(), values.end(), actorClassIndex, [](const auto& row, auto index) {
  759. return row.actorClassIndex < index;
  760. });
  761. const auto last =
  762. std::upper_bound(first, values.end(), actorClassIndex, [](auto index, const auto& row) {
  763. return index < row.actorClassIndex;
  764. });
  765. return values.subspan(static_cast<std::size_t>(first - values.begin()),
  766. static_cast<std::size_t>(last - first));
  767. }
  768. /** Copies one performance slot's state name hashes. @return How many were written. */
  769. std::size_t performance_state_names(const Catalog& catalog,
  770. const format::Scenario& scenario,
  771. std::uint32_t slotRow,
  772. std::span<std::uint32_t> output) noexcept {
  773. const auto slots = catalog.slots();
  774. if (slotRow >= slots.size() || output.empty()) {
  775. return 0;
  776. }
  777. const format::Slot& slot = slots[slotRow];
  778. if (slot.slotType != format::kPerformanceSlotType
  779. || slot.componentClass != format::kPerformanceComponentClass
  780. || slot.authSchema != format::kPerformanceAuthSchema
  781. || (slot.flags & format::kSlotSchemaJoinExact) == 0) {
  782. return 0;
  783. }
  784. // A sensor drives exactly one squad. Two edges would leave the target ambiguous.
  785. std::uint32_t squadSlot = format::kAbsentIndex;
  786. std::size_t edges = 0;
  787. for (const format::AuthoredSceneSquadEdge& edge :
  788. slot_authored_scene_squad_edges(catalog, slot)) {
  789. if ((edge.flags & format::kAuthoredSceneSquadPerformanceTargetExact) != 0) {
  790. squadSlot = edge.squadSlotIndex;
  791. ++edges;
  792. }
  793. }
  794. if (edges != 1) {
  795. return 0;
  796. }
  797. std::size_t written = 0;
  798. for (const format::Squad& squad : scenario_squads(catalog, scenario)) {
  799. if (squad.slotIndex != squadSlot) {
  800. continue;
  801. }
  802. for (const format::SquadMember& member : squad_members(catalog, squad)) {
  803. for (const format::ActorStateName& name :
  804. actor_class_state_names(catalog, member.actorClassIndex)) {
  805. if (std::find(output.begin(),
  806. output.begin() + static_cast<std::ptrdiff_t>(written),
  807. name.nameHash)
  808. != output.begin() + static_cast<std::ptrdiff_t>(written)) {
  809. continue;
  810. }
  811. if (written == output.size()) {
  812. return written;
  813. }
  814. output[written++] = name.nameHash;
  815. }
  816. }
  817. }
  818. return written;
  819. }
  820. /** Relies on task-slot ordering to return one contiguous zero-copy target range. */
  821. std::span<const format::TaskTarget> slot_task_targets(const Catalog& catalog,
  822. const format::Slot& slot) noexcept {
  823. const auto slots = catalog.slots();
  824. if (!owns(slots, slot)) {
  825. return {};
  826. }
  827. const auto values = catalog.task_targets();
  828. const std::uint32_t slotIndex = static_cast<std::uint32_t>(&slot - slots.data());
  829. const auto first =
  830. std::lower_bound(values.begin(), values.end(), slotIndex, [](const auto& row, auto index) {
  831. return row.taskSlotIndex < index;
  832. });
  833. const auto last =
  834. std::upper_bound(first, values.end(), slotIndex, [](auto index, const auto& row) {
  835. return index < row.taskSlotIndex;
  836. });
  837. return values.subspan(static_cast<std::size_t>(first - values.begin()),
  838. static_cast<std::size_t>(last - first));
  839. }
  840. /** @return The objective slot one task target names, or null when it names none. */
  841. const format::Slot* task_linked_objective_slot(const Catalog& catalog,
  842. const format::TaskTarget& target) noexcept {
  843. const auto targets = catalog.task_targets();
  844. const auto slots = catalog.slots();
  845. if (!owns(targets, target) || target.objectiveSlotIndex >= slots.size()) {
  846. return nullptr;
  847. }
  848. const format::Slot& slot = slots[target.objectiveSlotIndex];
  849. return slot.slotType == format::kObjectiveSlotType
  850. && slot.componentClass == format::kObjectiveComponentClass
  851. && slot.senseSchema == format::kObjectiveSenseSchema
  852. && slot.authSchema == format::kObjectiveAuthSchema
  853. ? &slot
  854. : nullptr;
  855. }
  856. } // namespace sunrise::state::activity_sdk