activity_menu_text_build.h 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. #pragma once
  2. #include "../../../middleware/content/packages/reader/reader.h"
  3. #include "../../../middleware/content/packages/tables/activity_presentation_table.h"
  4. namespace sunrise::client::content::activity {
  5. /** Runtime-only menu labels. Constants below are native localization identities, never text copies.
  6. */
  7. inline void build_menu_text(
  8. const middleware::content::packages::reader::Source& source,
  9. middleware::content::packages::reader::Scratch& scratch,
  10. std::span<const std::byte> globals,
  11. std::span<const middleware::content::packages::tables::activities::DisplayRefs> activityRefs,
  12. state::build_data::activities::MenuText& output) noexcept {
  13. namespace packages = middleware::content::packages;
  14. namespace catalog = state::build_data::activities;
  15. namespace tables = packages::tables;
  16. namespace parser = tables::activities;
  17. output = {};
  18. std::vector<std::byte> banks, container, language, bytes;
  19. tables::Array bankRows{};
  20. std::uint32_t tag{}, cls{};
  21. if (!tables::child_tag(globals, 72, tag)
  22. || !packages::reader::read_tag(source, scratch, tag, banks, cls) || cls != 0x80805F98
  23. || !tables::find_array_at(banks, 8, bankRows) || bankRows.count > 8192
  24. || bankRows.dataOffset > banks.size()
  25. || bankRows.count > (banks.size() - bankRows.dataOffset) / 8) {
  26. return;
  27. }
  28. const auto resolve = [&](parser::StringRef ref, catalog::NativeText& target) noexcept {
  29. target.bank = ref.bank;
  30. target.hash = ref.hash;
  31. std::uint32_t root{}, english{}, actual{};
  32. if (ref.bank >= bankRows.count
  33. || !parser::read(banks, bankRows.dataOffset + ref.bank * 8 + 4, root)
  34. || !packages::reader::read_tag(source, scratch, root, container, actual)
  35. || actual != 0x80809A88 || !parser::read(container, 24, english)
  36. || !packages::reader::read_tag(source, scratch, english, language, actual)
  37. || actual != 0x80809A8A) {
  38. return;
  39. }
  40. (void)parser::localized_string(container, language, ref.hash, target.text);
  41. };
  42. // Installed campaign-selection localization bank.
  43. constexpr std::array<std::uint32_t, 5> campaignHashes{
  44. 0x269A3283, 0x5E6C45BA, 0x99807D0D, 0xA5E7229E, 0xD07640BC};
  45. for (std::size_t i = 0; i < campaignHashes.size(); ++i) {
  46. resolve({2417, campaignHashes[i]}, output.content[i + 1]);
  47. }
  48. if (activityRefs.size() > 1) {
  49. resolve(activityRefs[1].title, output.content[6]);
  50. }
  51. // Seasons: the authored season row supplies its own bank/hash; no English matching or aliasing.
  52. tables::Array seasons{};
  53. if (tables::child_tag(globals, 54, tag)
  54. && packages::reader::read_tag(source, scratch, tag, bytes, cls) && cls == 0x8080311B
  55. && tables::find_array_at(bytes, 8, seasons) && seasons.count <= 64
  56. && seasons.dataOffset <= bytes.size()
  57. && seasons.count <= (bytes.size() - seasons.dataOffset) / 72) {
  58. // Season rows must match these identities before their labels are used.
  59. constexpr std::array<std::uint32_t, 7> identities{
  60. 0x854AC306, 0xAC5281E8, 0xFEDABB80, 0xD758957D, 0x77A58C71, 0xF088B659, 0x0ED0ED8B};
  61. for (std::size_t i = 0; i < identities.size(); ++i) {
  62. const auto row = i + 4;
  63. std::uint32_t identity{};
  64. parser::StringRef ref{};
  65. if (row < seasons.count
  66. && parser::read(bytes, seasons.dataOffset + row * 72 + 8, identity)
  67. && identity == identities[i]
  68. && parser::read(bytes, seasons.dataOffset + row * 72 + 36, ref)) {
  69. resolve(ref, output.content[i + 7]);
  70. }
  71. }
  72. }
  73. struct Event {
  74. std::size_t content;
  75. std::uint32_t tag, cls;
  76. std::size_t offset;
  77. };
  78. // These authored rows supply the event title references.
  79. constexpr std::array<Event, 4> events{{{14, 0x8161353B, 0x80805CE1, 132},
  80. {15, 0x816135B4, 0x80805CE1, 132},
  81. {19, 0x816134AB, 0x80805CE1, 132},
  82. {20, 0x81613CF8, 0x80802E11, 37676}}};
  83. for (const auto& event : events) {
  84. parser::StringRef ref{};
  85. if (packages::reader::read_tag(source, scratch, event.tag, bytes, cls) && cls == event.cls
  86. && parser::read(bytes, event.offset, ref)) {
  87. resolve(ref, output.content[event.content]);
  88. }
  89. }
  90. resolve({2095, 0xC019C7A5},
  91. output.content[16]); // Installed event title, distinct from its intro quest title.
  92. output.content[17] =
  93. output.content[14]; // Same native recurring-event label; classification IDs stay distinct.
  94. if (activityRefs.size() > 509) {
  95. resolve(activityRefs[509].type, output.content[21]);
  96. }
  97. if (activityRefs.size() > 299) {
  98. resolve(activityRefs[299].type, output.kinds[1]);
  99. resolve(activityRefs[229].type, output.kinds[2]);
  100. }
  101. // Director style string containers carry the native type label under one shared key.
  102. constexpr std::array<std::pair<std::size_t, std::uint32_t>, 4> styles{
  103. {{3, 0x8161305A}, {4, 0x816131E5}, {5, 0x8161311D}, {6, 0x816132B5}}};
  104. for (const auto [kind, style] : styles) {
  105. std::uint32_t root{}, english{};
  106. if (!packages::reader::read_tag(source, scratch, style, bytes, cls) || cls != 0x80802A38
  107. || !parser::read(bytes, 16, root)
  108. || !packages::reader::read_tag(source, scratch, root, container, cls)
  109. || cls != 0x80809A88 || !parser::read(container, 24, english)
  110. || !packages::reader::read_tag(source, scratch, english, language, cls)
  111. || cls != 0x80809A8A) {
  112. continue;
  113. }
  114. output.kinds[kind].bank = root;
  115. output.kinds[kind].hash = 0x8648376F;
  116. (void)parser::localized_string(container, language, 0x8648376F, output.kinds[kind].text);
  117. }
  118. }
  119. } // namespace sunrise::client::content::activity