activity_sdk_dialogue_group_index.cpp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. #include "activity_sdk_dialogue_group_index.h"
  2. #include <cmath>
  3. #include <cstring>
  4. #include <limits>
  5. #include "../../../state/activity_sdk/format.h"
  6. namespace sunrise::client::content::activity::sdk_generation::dialogue_group_index {
  7. namespace {
  8. // The bank stores a counted definition array at +8 with an eight-byte row stride.
  9. constexpr std::size_t kDefinitionsField = 8U;
  10. constexpr std::size_t kDefinitionStride = 8U;
  11. constexpr std::size_t kArrayDataOffset = 16U;
  12. // An empty authored name uses the FNV-1 basis.
  13. constexpr std::uint32_t kAbsentDefinitionHash = 0x811C9DC5U;
  14. template <typename Value>
  15. [[nodiscard]] bool
  16. read_value(std::span<const std::byte> bytes, std::size_t offset, Value& output) noexcept {
  17. output = {};
  18. if (offset > bytes.size() || sizeof output > bytes.size() - offset) {
  19. return false;
  20. }
  21. std::memcpy(&output, bytes.data() + offset, sizeof output);
  22. return true;
  23. }
  24. /** Applies one signed blob-relative offset. @return False when the result leaves the blob. */
  25. [[nodiscard]] bool
  26. add_relative(std::size_t member, std::int64_t relative, std::size_t& target) noexcept {
  27. if (relative >= 0) {
  28. const auto distance = static_cast<std::uint64_t>(relative);
  29. if (distance > (std::numeric_limits<std::size_t>::max)() - member) {
  30. return false;
  31. }
  32. target = member + static_cast<std::size_t>(distance);
  33. return true;
  34. }
  35. const auto distance = static_cast<std::uint64_t>(-(relative + 1)) + 1U;
  36. if (distance > member) {
  37. return false;
  38. }
  39. target = member - static_cast<std::size_t>(distance);
  40. return true;
  41. }
  42. } // namespace
  43. /**
  44. * Reads cue windows in bank order.
  45. * @param bytes Authored dialogue bank.
  46. * @param output Receives every row; cleared on failure.
  47. * @return False for an invalid window or array.
  48. */
  49. bool definitions(std::span<const std::byte> bytes, std::vector<Definition>& output) noexcept {
  50. namespace format = state::activity_sdk::format;
  51. output.clear();
  52. std::uint64_t count = 0;
  53. std::int64_t relative = 0;
  54. std::size_t header = 0;
  55. std::uint64_t repeated = 0;
  56. std::uint32_t rowClass = 0;
  57. if (!read_value(bytes, kDefinitionsField, count) || count > format::kDialogueMaximumCueCount
  58. || !read_value(bytes, kDefinitionsField + sizeof count, relative)
  59. || !add_relative(kDefinitionsField + sizeof count, relative, header)
  60. || !read_value(bytes, header, repeated) || repeated != count
  61. || !read_value(bytes, header + sizeof repeated, rowClass)
  62. || rowClass != format::kDialogueDefinitionArrayClass || header > bytes.size()
  63. || kArrayDataOffset > bytes.size() - header
  64. || count > (bytes.size() - header - kArrayDataOffset) / kDefinitionStride) {
  65. return false;
  66. }
  67. try {
  68. output.reserve(static_cast<std::size_t>(count));
  69. for (std::size_t ordinal = 0; ordinal < count; ++ordinal) {
  70. const std::size_t offset = header + kArrayDataOffset + ordinal * kDefinitionStride;
  71. Definition row{};
  72. if (!read_value(bytes, offset, row.hash)
  73. || !read_value(bytes, offset + sizeof row.hash, row.authoredWindowSeconds)
  74. || row.hash == 0 || row.hash == kAbsentDefinitionHash
  75. || !std::isfinite(row.authoredWindowSeconds) || row.authoredWindowSeconds < 0.0F) {
  76. output.clear();
  77. return false;
  78. }
  79. output.push_back(row);
  80. }
  81. return true;
  82. } catch (...) {
  83. output.clear();
  84. return false;
  85. }
  86. }
  87. } // namespace sunrise::client::content::activity::sdk_generation::dialogue_group_index