activity_sdk_generation_shard.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. #include <algorithm>
  2. #include <array>
  3. #include <cstdio>
  4. #include <cwchar>
  5. #include <span>
  6. #include "../../../state/activity_sdk/generated_world/store.h"
  7. #include "activity_sdk_generation_worker_internal.h"
  8. namespace sunrise::client::content::activity::sdk_generation::worker_internal {
  9. namespace shard_store = state::activity_sdk::generated_world::store;
  10. // Shards are published with the pack extension.
  11. constexpr std::wstring_view kShardExtension = L".pack";
  12. /** Formats an immutable shard path from its scenario and payload identities. */
  13. [[nodiscard]] bool shard_path(const std::wstring& directory,
  14. std::uint32_t scenarioTag,
  15. const generated::Digest& digest,
  16. std::wstring& output) noexcept {
  17. // A shard leaf spells the digest in lowercase hex.
  18. static constexpr wchar_t kDigits[] = L"0123456789abcdef";
  19. std::array<wchar_t, 10> tag{};
  20. const int length =
  21. std::swprintf(tag.data(), tag.size(), L"%08X-", static_cast<unsigned>(scenarioTag));
  22. if (length != 9 || directory.empty()) {
  23. return false;
  24. }
  25. try {
  26. output = directory;
  27. output.push_back(L'\\');
  28. output.append(tag.data(), static_cast<std::size_t>(length));
  29. for (const std::byte byte : digest) {
  30. const unsigned value = std::to_integer<unsigned>(byte);
  31. output.push_back(kDigits[(value >> 4U) & 0xFU]);
  32. output.push_back(kDigits[value & 0xFU]);
  33. }
  34. output.append(kShardExtension);
  35. } catch (...) {
  36. output.clear();
  37. return false;
  38. }
  39. return true;
  40. }
  41. /** Reopens one legacy cache row through the same authenticated full-only gate. */
  42. [[nodiscard]] bool load_record(const std::wstring& scenarioDirectory,
  43. const generated::Digest& sourceFingerprint,
  44. const Scenario& scenario,
  45. const manifest::Record& record,
  46. catalog::Snapshot& output) noexcept {
  47. std::shared_ptr<const catalog::Snapshot> loaded;
  48. shard_store::RecordLoadStatus status = shard_store::RecordLoadStatus::invalid;
  49. if (!shard_store::load_record(scenarioDirectory,
  50. scenario.tag,
  51. std::string_view(scenario.name.data(), scenario.nameLength),
  52. sourceFingerprint,
  53. record,
  54. loaded,
  55. status)
  56. || status != shard_store::RecordLoadStatus::loaded || loaded == nullptr) {
  57. return false;
  58. }
  59. try {
  60. output = *loaded;
  61. } catch (...) {
  62. output = {};
  63. return false;
  64. }
  65. return true;
  66. }
  67. /** Reopens one generator-owned row only when every full extraction family is present. */
  68. [[nodiscard]] bool load_full_record(const std::wstring& scenarioDirectory,
  69. const generated::Digest& sourceFingerprint,
  70. const Scenario& scenario,
  71. const manifest::Record& record,
  72. std::shared_ptr<const catalog::Snapshot>& output) noexcept {
  73. shard_store::RecordLoadStatus status = shard_store::RecordLoadStatus::invalid;
  74. return shard_store::load_record(scenarioDirectory,
  75. scenario.tag,
  76. std::string_view(scenario.name.data(), scenario.nameLength),
  77. sourceFingerprint,
  78. record,
  79. output,
  80. status)
  81. && status == shard_store::RecordLoadStatus::loaded;
  82. }
  83. /** Finds one sorted manifest record by scenario tag. */
  84. [[nodiscard]] const manifest::Record* find_record(const manifest::Catalog& catalog,
  85. std::uint32_t scenarioTag) noexcept {
  86. const auto found = std::lower_bound(
  87. catalog.records.begin(),
  88. catalog.records.end(),
  89. scenarioTag,
  90. [](const manifest::Record& row, std::uint32_t tag) { return row.scenarioTag < tag; });
  91. return found != catalog.records.end() && found->scenarioTag == scenarioTag ? &*found : nullptr;
  92. }
  93. /** Accepts the requested name or this generator's exact unnamed-scenario fallback. */
  94. [[nodiscard]] bool record_name_matches(const manifest::Record& record,
  95. std::uint32_t scenarioTag,
  96. std::string_view requestedName) noexcept {
  97. const std::string_view recordName(record.scenarioName.data(), record.scenarioNameLength);
  98. if (recordName == requestedName) {
  99. return true;
  100. }
  101. std::array<char, 32> synthetic{};
  102. const int length = std::snprintf(
  103. synthetic.data(), synthetic.size(), "scenario_%08X", static_cast<unsigned>(scenarioTag));
  104. return length > 0 && static_cast<std::size_t>(length) < synthetic.size()
  105. && recordName == std::string_view(synthetic.data(), static_cast<std::size_t>(length));
  106. }
  107. } // namespace sunrise::client::content::activity::sdk_generation::worker_internal