cache_payload_reader.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. #include "cache_payload_reader.h"
  2. #include <algorithm>
  3. #include <limits>
  4. #include "../records/codec.h"
  5. #include "../records/validation.h"
  6. namespace sunrise::state::build_data::cache::read {
  7. namespace {
  8. /**
  9. * Adds one record array to the running cache size without unsigned overflow.
  10. * @param count Number of records.
  11. * @param stride Packed record size in bytes.
  12. * @param size Running file size.
  13. * @return True when the multiply and the add both fit.
  14. */
  15. [[nodiscard]] bool
  16. add_records(std::size_t count, std::size_t stride, std::uint64_t& size) noexcept {
  17. /** Cache offsets use the full unsigned 64-bit Windows file-size range. */
  18. constexpr std::uint64_t kMaximum = (std::numeric_limits<std::uint64_t>::max)();
  19. if (count > kMaximum / stride) {
  20. return false;
  21. }
  22. const std::uint64_t bytes = count * stride;
  23. if (bytes > kMaximum - size) {
  24. return false;
  25. }
  26. size += bytes;
  27. return true;
  28. }
  29. /**
  30. * Reads and decodes one record array, and extends the shared checksum.
  31. * @tparam Record Packed disk record type.
  32. * @tparam Value Runtime row type the codec overload picks.
  33. * @param file Open sequential cache handle.
  34. * @param output Span of rows to fill.
  35. * @param checksum Running payload checksum.
  36. * @return True when every packed row is complete and in its standard form.
  37. */
  38. template <typename Record, typename Value>
  39. [[nodiscard]] bool
  40. read_domain(HANDLE file, std::span<Value> output, std::uint64_t& checksum) noexcept {
  41. for (Value& value : output) {
  42. Record record{};
  43. if (!read_value(file, record) || !records::decode(record, value)) {
  44. return false;
  45. }
  46. checksum = records::checksum_value(checksum, record);
  47. }
  48. return true;
  49. }
  50. } // namespace
  51. /** Clears every output span so a failed read cannot expose partial records. */
  52. void clear(records::MutableDomains output) noexcept {
  53. if (output.constants != nullptr) {
  54. *output.constants = {};
  55. }
  56. std::fill(output.named.begin(), output.named.end(), content::Definition{});
  57. std::fill(output.items.begin(), output.items.end(), items::Definition{});
  58. std::fill(output.collectibles.begin(), output.collectibles.end(), collectibles::Definition{});
  59. std::fill(output.materialRequirementSets.begin(),
  60. output.materialRequirementSets.end(),
  61. material_requirements::Definition{});
  62. std::fill(output.itemDetails.begin(), output.itemDetails.end(), items::details::Definition{});
  63. std::fill(
  64. output.socketPlugRules.begin(), output.socketPlugRules.end(), items::socket_plugs::Rule{});
  65. std::fill(
  66. output.socketPlugPools.begin(), output.socketPlugPools.end(), items::socket_plugs::Pool{});
  67. std::fill(output.socketPlugMembers.begin(),
  68. output.socketPlugMembers.end(),
  69. items::socket_plugs::Member{});
  70. std::fill(output.inventoryBuckets.begin(),
  71. output.inventoryBuckets.end(),
  72. inventory::buckets::Descriptor{});
  73. std::fill(output.socketEntryLists.begin(),
  74. output.socketEntryLists.end(),
  75. socket_entry_lists::Definition{});
  76. std::fill(output.socketEntryTables.begin(),
  77. output.socketEntryTables.end(),
  78. socket_entry_lists::EntryTable{});
  79. std::fill(output.abilityBuckets.begin(), output.abilityBuckets.end(), abilities::Definition{});
  80. std::fill(output.progressions.begin(), output.progressions.end(), progressions::Definition{});
  81. std::fill(output.records.begin(), output.records.end(), build_data::records::Definition{});
  82. std::fill(output.nodes.begin(), output.nodes.end(), nodes::Definition{});
  83. std::fill(output.sobjects.begin(), output.sobjects.end(), sobjects::Definition{});
  84. std::fill(output.scenarios.begin(), output.scenarios.end(), scenarios::Definition{});
  85. std::fill(output.rosterGroups.begin(), output.rosterGroups.end(), scenarios::RosterGroup{});
  86. std::fill(output.spawnStems.begin(), output.spawnStems.end(), spawn_sets::Stem{});
  87. std::fill(output.spawnNameHashes.begin(), output.spawnNameHashes.end(), spawn_sets::NameHash{});
  88. std::fill(output.spawnPoints.begin(), output.spawnPoints.end(), spawn_sets::Point{});
  89. std::fill(output.hashNames.begin(), output.hashNames.end(), hash_names::Name{});
  90. std::fill(output.vendorIndex.begin(), output.vendorIndex.end(), vendors::IndexEntry{});
  91. std::fill(
  92. output.vendorDefinitions.begin(), output.vendorDefinitions.end(), vendors::Definition{});
  93. std::fill(output.vendorSaleRows.begin(), output.vendorSaleRows.end(), vendors::SaleRow{});
  94. std::fill(output.vendorInstalledRows.begin(),
  95. output.vendorInstalledRows.end(),
  96. vendors::InstalledRow{});
  97. }
  98. /** Computes the exact file size for every record array. */
  99. bool expected_size(const records::DomainCounts& counts, std::uint64_t& size) noexcept {
  100. size = sizeof(records::Header);
  101. return add_records(counts.named, sizeof(records::NamedRecord), size)
  102. && add_records(counts.items, sizeof(records::ItemRecord), size)
  103. && add_records(counts.collectibles, sizeof(records::CollectibleRecord), size)
  104. && add_records(
  105. counts.materialRequirementSets, sizeof(records::MaterialRequirementSetRecord), size)
  106. && add_records(counts.itemDetails, sizeof(records::ItemDetailRecord), size)
  107. && add_records(counts.socketPlugRules, sizeof(records::SocketPlugRuleRecord), size)
  108. && add_records(counts.socketPlugPools, sizeof(records::SocketPlugPoolRecord), size)
  109. && add_records(counts.socketPlugMembers, sizeof(records::SocketPlugMemberRecord), size)
  110. && add_records(counts.inventoryBuckets, sizeof(records::InventoryBucketRecord), size)
  111. && add_records(counts.socketEntryLists, sizeof(records::SocketEntryListRecord), size)
  112. && add_records(counts.socketEntryTables, sizeof(records::SocketEntryTableRecord), size)
  113. && add_records(counts.abilityBuckets, sizeof(records::AbilityBucketRecord), size)
  114. && add_records(counts.progressions, sizeof(records::ProgressionRecord), size)
  115. && add_records(counts.records, sizeof(records::RecordDefinitionRecord), size)
  116. && add_records(counts.nodes, sizeof(records::NodeDefinitionRecord), size)
  117. && add_records(counts.sobjects, sizeof(records::SObjectDefinitionRecord), size)
  118. && add_records(counts.scenarios, sizeof(records::ScenarioRecord), size)
  119. && add_records(counts.rosterGroups, sizeof(records::RosterGroupRecord), size)
  120. && add_records(counts.spawnStems, sizeof(records::SpawnStemRecord), size)
  121. && add_records(counts.spawnNameHashes, sizeof(records::SpawnNameHashRecord), size)
  122. && add_records(counts.spawnPoints, sizeof(records::SpawnPointRecord), size)
  123. && add_records(counts.hashNames, sizeof(records::HashNameRecord), size)
  124. && add_records(counts.vendorIndex, sizeof(records::VendorIndexRecord), size)
  125. && add_records(counts.vendorDefinitions, sizeof(records::VendorDefinitionRecord), size)
  126. && add_records(counts.vendorSaleRows, sizeof(records::VendorSaleRowRecord), size)
  127. && add_records(
  128. counts.vendorInstalledRows, sizeof(records::VendorInstalledRowRecord), size);
  129. }
  130. /** Reads every payload array and checks the decoded domains as one transaction. */
  131. bool read_payload(HANDLE file,
  132. const records::InvestmentConstants& constants,
  133. const records::DomainCounts& counts,
  134. records::MutableDomains output,
  135. std::uint64_t& checksum) noexcept {
  136. checksum = records::checksum_value(records::kChecksumOffsetBasis, constants);
  137. bool valid =
  138. read_domain<records::NamedRecord>(file, output.named.first(counts.named), checksum);
  139. valid =
  140. valid && read_domain<records::ItemRecord>(file, output.items.first(counts.items), checksum);
  141. valid = valid
  142. && read_domain<records::CollectibleRecord>(
  143. file, output.collectibles.first(counts.collectibles), checksum);
  144. valid =
  145. valid
  146. && read_domain<records::MaterialRequirementSetRecord>(
  147. file, output.materialRequirementSets.first(counts.materialRequirementSets), checksum);
  148. valid = valid
  149. && read_domain<records::ItemDetailRecord>(
  150. file, output.itemDetails.first(counts.itemDetails), checksum);
  151. valid = valid
  152. && read_domain<records::SocketPlugRuleRecord>(
  153. file, output.socketPlugRules.first(counts.socketPlugRules), checksum);
  154. valid = valid
  155. && read_domain<records::SocketPlugPoolRecord>(
  156. file, output.socketPlugPools.first(counts.socketPlugPools), checksum);
  157. valid = valid
  158. && read_domain<records::SocketPlugMemberRecord>(
  159. file, output.socketPlugMembers.first(counts.socketPlugMembers), checksum);
  160. valid = valid
  161. && read_domain<records::InventoryBucketRecord>(
  162. file, output.inventoryBuckets.first(counts.inventoryBuckets), checksum);
  163. valid = valid
  164. && read_domain<records::SocketEntryListRecord>(
  165. file, output.socketEntryLists.first(counts.socketEntryLists), checksum);
  166. valid = valid
  167. && read_domain<records::SocketEntryTableRecord>(
  168. file, output.socketEntryTables.first(counts.socketEntryTables), checksum);
  169. valid = valid
  170. && read_domain<records::AbilityBucketRecord>(
  171. file, output.abilityBuckets.first(counts.abilityBuckets), checksum);
  172. valid = valid
  173. && read_domain<records::ProgressionRecord>(
  174. file, output.progressions.first(counts.progressions), checksum);
  175. valid = valid
  176. && read_domain<records::RecordDefinitionRecord>(
  177. file, output.records.first(counts.records), checksum);
  178. valid = valid
  179. && read_domain<records::NodeDefinitionRecord>(
  180. file, output.nodes.first(counts.nodes), checksum);
  181. valid = valid
  182. && read_domain<records::SObjectDefinitionRecord>(
  183. file, output.sobjects.first(counts.sobjects), checksum);
  184. valid = valid
  185. && read_domain<records::ScenarioRecord>(
  186. file, output.scenarios.first(counts.scenarios), checksum);
  187. valid = valid
  188. && read_domain<records::RosterGroupRecord>(
  189. file, output.rosterGroups.first(counts.rosterGroups), checksum);
  190. valid = valid
  191. && read_domain<records::SpawnStemRecord>(
  192. file, output.spawnStems.first(counts.spawnStems), checksum);
  193. valid = valid
  194. && read_domain<records::SpawnNameHashRecord>(
  195. file, output.spawnNameHashes.first(counts.spawnNameHashes), checksum);
  196. valid = valid
  197. && read_domain<records::SpawnPointRecord>(
  198. file, output.spawnPoints.first(counts.spawnPoints), checksum);
  199. valid = valid
  200. && read_domain<records::HashNameRecord>(
  201. file, output.hashNames.first(counts.hashNames), checksum);
  202. valid = valid
  203. && read_domain<records::VendorIndexRecord>(
  204. file, output.vendorIndex.first(counts.vendorIndex), checksum);
  205. valid = valid
  206. && read_domain<records::VendorDefinitionRecord>(
  207. file, output.vendorDefinitions.first(counts.vendorDefinitions), checksum);
  208. valid = valid
  209. && read_domain<records::VendorSaleRowRecord>(
  210. file, output.vendorSaleRows.first(counts.vendorSaleRows), checksum);
  211. valid = valid
  212. && read_domain<records::VendorInstalledRowRecord>(
  213. file, output.vendorInstalledRows.first(counts.vendorInstalledRows), checksum);
  214. if (!valid) {
  215. return false;
  216. }
  217. return records::valid_domains({
  218. constants,
  219. output.named.first(counts.named),
  220. output.items.first(counts.items),
  221. output.collectibles.first(counts.collectibles),
  222. output.materialRequirementSets.first(counts.materialRequirementSets),
  223. output.itemDetails.first(counts.itemDetails),
  224. output.socketPlugRules.first(counts.socketPlugRules),
  225. output.socketPlugPools.first(counts.socketPlugPools),
  226. output.socketPlugMembers.first(counts.socketPlugMembers),
  227. output.inventoryBuckets.first(counts.inventoryBuckets),
  228. output.socketEntryLists.first(counts.socketEntryLists),
  229. output.socketEntryTables.first(counts.socketEntryTables),
  230. output.abilityBuckets.first(counts.abilityBuckets),
  231. output.progressions.first(counts.progressions),
  232. output.records.first(counts.records),
  233. output.nodes.first(counts.nodes),
  234. output.sobjects.first(counts.sobjects),
  235. output.scenarios.first(counts.scenarios),
  236. output.rosterGroups.first(counts.rosterGroups),
  237. output.spawnStems.first(counts.spawnStems),
  238. output.spawnNameHashes.first(counts.spawnNameHashes),
  239. output.spawnPoints.first(counts.spawnPoints),
  240. output.hashNames.first(counts.hashNames),
  241. output.vendorIndex.first(counts.vendorIndex),
  242. output.vendorDefinitions.first(counts.vendorDefinitions),
  243. output.vendorSaleRows.first(counts.vendorSaleRows),
  244. output.vendorInstalledRows.first(counts.vendorInstalledRows),
  245. });
  246. }
  247. } // namespace sunrise::state::build_data::cache::read