瀏覽代碼

Grant lore collectibles their correct triumph, claimable and persisted

A pickup granted a chapter by guessing: one book per activity from a
hand-written two-entry table, whose Menagerie entry held 0x811C9DC5 --
the FNV-1a offset basis, meaning "bubble unset", so it matched every
instanced activity and swallowed unrelated pickups.

Replace that with a generated bubble -> candidate record table, and grant
the first record in a bubble's bucket the account does not already hold.
Instanced activities carry no bubble at all and are recognised as such
rather than through the sentinel.

The grant then had to survive being made. A record reads claimable when
its objective value equals its completionValue while its completion flag
stays clear; the flag encodes redeemed state only -- all four values of
the 2-bit field were measured and none means claimable, so kFlagAvailable
is removed. The objective slot each record owns is derived as

    slot = 2746 + sum(objectiveCount, or 2 if none) over earlier rows

verified against thirteen points measured in game, six of them predicted
blind. Records with no objective still reserve two slots, which is what
defeated every simpler model. Claimable state now persists beside claims
in its own file, so a relaunch no longer forgets what was collected and
hand out the same chapter again.

Book bars counted claimed children through the node's own value slot,
which resolves for only some books; sixteen were skipped entirely and
never written. Count from the records instead and write the index the
parent record's own expression names. Eighteen books name a value there;
twelve name a flag and remain unresolved.

Also drops three TEMPORARY probes, a stale debug loop, and two vcxproj
entries referencing files that do not exist.

Value slot tables documented in Sunrise-docs/Value_Slots.
Millie 2 周之前
父節點
當前提交
5447cd42d7

+ 3 - 1
Sunrise/src/client/content/items/packages/package_collectible_build.cpp

@@ -23,7 +23,8 @@ bool build_collectibles(const reader::Source& source,
     if (state::build_data::sobjects::count() == 0) {
         namespace sobjects = state::build_data::sobjects;
         // Geometry is documented and asserted rather than divided out: count at +112, rows at +128,
-        // forty bytes each, name hash at +0, selector group at +32, ordinal at +34, type at +36.
+        // forty bytes each, name hash at +0, lane 4 at +16, selector group at +32, ordinal at +34,
+        // type at +36.
         constexpr std::size_t kCountOffset = 112;
         constexpr std::size_t kRowBase = 128;
         constexpr std::size_t kRowStride = 40;
@@ -43,6 +44,7 @@ bool build_collectibles(const reader::Source& source,
             for (std::size_t row = 0; row < rows.size(); ++row) {
                 const std::size_t at = kRowBase + row * kRowStride;
                 std::memcpy(&rows[row].nameHash, blob.data() + at, sizeof(std::uint32_t));
+                std::memcpy(&rows[row].lane4, blob.data() + at + 16, sizeof(std::uint32_t));
                 std::memcpy(&rows[row].selectorGroup, blob.data() + at + 32, sizeof(std::uint16_t));
                 std::memcpy(&rows[row].nodeOrdinal, blob.data() + at + 34, sizeof(std::uint16_t));
                 std::memcpy(&rows[row].typeCode, blob.data() + at + 36, sizeof(std::int32_t));

+ 148 - 14
Sunrise/src/client/content/items/packages/package_node_build.cpp

@@ -1,7 +1,8 @@
+#include <algorithm>
 #include <array>
 #include <cstdio>
-#include <cstring>
 #include <unordered_map>
+#include <vector>
 
 #include "../../../../core/logging/log.h"
 #include "../../../../state/build_data/runtime.h"
@@ -23,8 +24,6 @@ void report(const char* stage, unsigned long long detail) noexcept {
     }
 }
 
-
-
 } // namespace
 
 /**
@@ -156,6 +155,8 @@ bool build_nodes(const reader::Source& source,
 
     const std::span<const std::byte> table{blob};
     std::size_t driving = 0;
+
+    // Pass 1: resolve every node's own value slot and children.
     for (std::uint64_t row = 0; row < rows.count; ++row) {
         const std::size_t at =
             rows.dataOffset + static_cast<std::size_t>(row) * tables::kNodeRowStride;
@@ -168,22 +169,83 @@ bool build_nodes(const reader::Source& source,
         const bool named =
             tables::expression_value_slot(table, at, tables::kNodeExpressionFieldPrimary, slot)
             || tables::expression_value_slot(table, at, tables::kNodeExpressionFieldAlternate, slot);
+        // A lore book whose expression parses as neither a value read nor a flag test is
+        // indistinguishable from one whose slot simply is not addressable, so the raw instruction
+        // stream is reported for the lore range rather than inferred. Sixteen books resolve no
+        // slot at all and their bars can never move until that is understood.
+        if (domain::lore_category(static_cast<std::uint16_t>(row))) {
+            // Every 8-byte-aligned field in the row is tried, not just the two the resolver
+            // knows: sixteen books carry a flag test where the others carry a value read, so the
+            // value expression that drives their bar must sit in a field nobody has looked at.
+            for (std::size_t field = 0; field + 16 <= tables::kNodeRowStride; field += 8) {
+                std::int16_t probe = 0;
+                if (!tables::expression_value_slot(table, at, field, probe)) {
+                    continue;
+                }
+                std::int64_t count = 0;
+                std::int64_t relative = 0;
+                if (at + field + 16 > table.size()) {
+                    continue;
+                }
+                std::memcpy(&count, table.data() + at + field, sizeof count);
+                std::memcpy(&relative, table.data() + at + field + 8, sizeof relative);
+                std::array<char, 200> head{};
+                int used = std::snprintf(head.data(), head.size(),
+                                         "ev=nodes stage=expr node=%llu field=%zu count=%lld ops=",
+                                         static_cast<unsigned long long>(row), field,
+                                         static_cast<long long>(count));
+                const std::size_t pointerAt = at + field + 8;
+                const std::int64_t target = static_cast<std::int64_t>(pointerAt) + relative
+                                            + static_cast<std::int64_t>(tables::kHeaderSkip);
+                if (count >= 1 && count <= tables::kNodeExpressionCapacity && target >= 0
+                    && static_cast<std::size_t>(target)
+                               + static_cast<std::size_t>(count) * tables::kUnlockInstructionStride
+                           <= table.size()) {
+                    const auto base = static_cast<std::size_t>(target);
+                    for (std::int64_t index = 0; index < count && index < 6; ++index) {
+                        std::uint32_t instruction = 0;
+                        std::uint32_t operand = 0;
+                        const std::size_t insAt =
+                            base + static_cast<std::size_t>(index) * tables::kUnlockInstructionStride;
+                        std::memcpy(&instruction, table.data() + insAt, sizeof instruction);
+                        std::memcpy(&operand, table.data() + insAt + 4, sizeof operand);
+                        used += std::snprintf(head.data() + used,
+                                              head.size() - static_cast<std::size_t>(used),
+                                              "%u:%u ", instruction, operand);
+                    }
+                } else {
+                    used += std::snprintf(head.data() + used,
+                                          head.size() - static_cast<std::size_t>(used), "unparsed");
+                }
+                if (used > 0) {
+                    core::log::write(core::log::Channel::client, core::log::Level::info,
+                                     {head.data(), static_cast<std::size_t>(used)});
+                }
+            }
+        }
         if (named) {
+            definition.valueSlot = slot;
             const auto found = indexBySlot.find(slot);
             if (found != indexBySlot.end()) {
                 definition.valueIndex = found->second;
             }
-            // The parent record's own bar reads the next slot up. Resolve it through the mapping
-            // table rather than adding one to the index: rows happen to run in slot order around
-            // here, but nothing guarantees that.
-            const auto characterSlot = characterValueIndexBySlot.find(slot);
-            if (characterSlot != characterValueIndexBySlot.end()) {
-                definition.characterValueIndex = characterSlot->second;
-            }
-            const auto parent = indexBySlot.find(
-                static_cast<std::int16_t>(slot + tables::kNodeParentSlotStep));
-            if (parent != indexBySlot.end()) {
-                definition.parentValueIndex = parent->second;
+            // The same expression may resolve in the character scope: one lore book's bar reads a
+            // slot only the character table carries, and its parent has to be fed there too.
+            std::int16_t characterSlot = 0;
+            if (tables::expression_value_slot(table,
+                                              at,
+                                              tables::kNodeExpressionFieldPrimary,
+                                              characterSlot)
+                || tables::expression_value_slot(table,
+                                                 at,
+                                                 tables::kNodeExpressionFieldAlternate,
+                                                 characterSlot)) {
+                definition.characterValueSlot = characterSlot;
+                const auto characterResolved =
+                    characterValueIndexBySlot.find(characterSlot);
+                if (characterResolved != characterValueIndexBySlot.end()) {
+                    definition.characterValueIndex = characterResolved->second;
+                }
             }
         }
 
@@ -238,7 +300,79 @@ bool build_nodes(const reader::Source& source,
         }
         ++count;
     }
+
+    // Pass 2: assign each lore book's parent-record bar slot from the shipped allocation.
+    //
+    // The naive rule (parent = category slot + 1) holds only when the slot above a category was
+    // free at allocation time. Categories were handed out in contiguous runs, and a run's parent
+    // slots were deferred to immediately after the run, assigned in reverse category order: the
+    // run's first book takes the last parent slot, its last book takes the first. Verified against
+    // four independent in-game marker readings; see kNodeParentSlotStep in definition_index_table.h.
+    //
+    // Both scopes are walked. Most books are account-scoped; one reads its category from the
+    // character table, and its parent sits in that scope too.
+    {
+        struct BookSlot {
+            std::int32_t slot;
+            std::size_t row;
+        };
+        std::vector<BookSlot> accountBooks;
+        std::vector<BookSlot> characterBooks;
+        for (std::size_t row = 0; row < count; ++row) {
+            const auto& d = output[row];
+            if (!domain::lore_category(d.definitionIndex)) {
+                continue;
+            }
+            if (d.valueSlot >= 0) {
+                accountBooks.push_back({d.valueSlot, row});
+            }
+            if (d.characterValueSlot >= 0) {
+                characterBooks.push_back({d.characterValueSlot, row});
+            }
+        }
+
+        auto assignRuns = [&](std::vector<BookSlot>& books,
+                              const std::unordered_map<std::int16_t, std::uint16_t>& indexBySlot,
+                              auto memberSetter) {
+            std::sort(books.begin(), books.end(),
+                      [](const BookSlot& a, const BookSlot& b) { return a.slot < b.slot; });
+            std::size_t i = 0;
+            while (i < books.size()) {
+                std::size_t j = i;
+                while (j + 1 < books.size() && books[j + 1].slot == books[j].slot + 1) {
+                    ++j;
+                }
+                const std::size_t runLength = j - i + 1;
+                const std::int32_t runEnd = books[j].slot;
+                for (std::size_t k = 0; k < runLength; ++k) {
+                    // Reverse order within the run: book k gets the slot after the run counting
+                    // back from its end.
+                    const std::int32_t parentSlot =
+                        runEnd + static_cast<std::int32_t>(runLength - k);
+                    const auto found = indexBySlot.find(static_cast<std::int16_t>(parentSlot));
+                    if (found != indexBySlot.end()) {
+                        memberSetter(output[books[i + k].row], found->second);
+                    }
+                }
+                i = j + 1;
+            }
+        };
+
+        assignRuns(accountBooks,
+                   indexBySlot,
+                   [](domain::Definition& d, std::uint16_t index) {
+                       d.parentValueIndex = index;
+                   });
+        assignRuns(characterBooks,
+                   characterValueIndexBySlot,
+                   [](domain::Definition& d, std::uint16_t index) {
+                       d.parentCharacterValueIndex = index;
+                   });
+    }
+
     report("ok", static_cast<unsigned long long>(driving));
+
+
     return count != 0;
 }
 

+ 36 - 16
Sunrise/src/client/content/items/packages/package_record_build.cpp

@@ -162,27 +162,47 @@ bool build_records(const reader::Source& source,
             && static_cast<std::size_t>(categorySlot) < kSlotSpace) {
             definition.categoryValueIndex = valueIndexBySlot[static_cast<std::size_t>(categorySlot)];
         }
+        // A book's parent triumph shows a progress bar, and the slot that bar reads has never
+        // been identified -- only the category field is parsed. Every 8-byte-aligned field of the
+        // row is reported for the lore books' parent records (loreRow 0xFFFF), with both the raw
+        // slot each names and the bank index it maps to, so the bar's source can be read off
+        // rather than guessed.
+        if (definition.loreRow == 0xFFFFU && !valueIndexBySlot.empty()) {
+            for (std::size_t field = 0; field + 16 <= tables::kRecordRowStride; field += 8) {
+                std::int16_t raw = 0;
+                const bool isValue = tables::expression_value_slot(
+                    std::span<const std::byte>{blob}, at, field, raw);
+                std::int16_t rawFlag = 0;
+                const bool isFlag = tables::expression_flag_slot(
+                    std::span<const std::byte>{blob}, at, field, rawFlag);
+                if (!isValue && !isFlag) {
+                    continue;
+                }
+                const std::int16_t named = isValue ? raw : rawFlag;
+                int mapped = -1;
+                if (addressable_slot(named) && static_cast<std::size_t>(named) < kSlotSpace) {
+                    mapped = isValue ? static_cast<int>(valueIndexBySlot[
+                                           static_cast<std::size_t>(named)])
+                                     : static_cast<int>(indexBySlot[
+                                           static_cast<std::size_t>(named)]);
+                }
+                std::array<char, 160> line{};
+                const int told = std::snprintf(
+                    line.data(), line.size(),
+                    "ev=records stage=parent_expr row=%llu field=%zu kind=%s raw=%d mapped=%d",
+                    static_cast<unsigned long long>(row), field, isValue ? "value" : "flag",
+                    static_cast<int>(named), mapped);
+                if (told > 0) {
+                    core::log::write(core::log::Channel::client, core::log::Level::info,
+                                     {line.data(), static_cast<std::size_t>(told)});
+                }
+            }
+        }
         if (addressable_slot(slot) && static_cast<std::size_t>(slot) < kSlotSpace) {
             definition.completionFlagIndex = indexBySlot[static_cast<std::size_t>(slot)];
         }
         ++count;
     }
-    // TEMPORARY: what score a lore record actually carries in this build. The published manifest
-    // shows ScoreValue 0 for Confessions Entry I, which does not match the game, so read the rows
-    // rather than trust either. 1707 is the book's parent triumph and 1708 to 1716 its chapters.
-    for (std::uint64_t row = 1707; row <= 1716 && row < count; ++row) {
-        std::array<char, 180> line{};
-        const int told = std::snprintf(
-            line.data(), line.size(), "ev=lorescore record=%llu score=%u lore_row=%u flag=%u",
-            static_cast<unsigned long long>(row), static_cast<unsigned>(output[row].scoreValue),
-            static_cast<unsigned>(output[row].loreRow),
-            static_cast<unsigned>(output[row].completionFlagIndex));
-        if (told > 0) {
-            core::log::write(core::log::Channel::client, core::log::Level::info,
-                             {line.data(), static_cast<std::size_t>(told)});
-        }
-    }
-
     report("ok", static_cast<unsigned long long>(count));
     return count != 0;
 }

+ 53 - 0
Sunrise/src/middleware/bap/activity_message/activity_incident_parser.cpp

@@ -7,7 +7,9 @@
  */
 
 #include <algorithm>
+#include <bit>
 #include <climits>
+#include <cmath>
 
 #include "../../encoding/bit_reader.h"
 #include "../../encoding/byte_order.h"
@@ -16,6 +18,15 @@
 namespace sunrise::middleware::bap::activity_message::incident {
 namespace {
 
+/** Only this header shape has a validated payload layout for the decode below. */
+inline constexpr std::uint32_t kPositionHeaderBits = 29;
+/** Bit offset of the position triple, MSB-first from the start of the raw message body. */
+inline constexpr std::size_t kPositionBitOffset = 518;
+/** Three consecutive 32-bit fields: x, y, z. */
+inline constexpr std::size_t kPositionBitWidth = 96;
+/** A decoded axis magnitude at or above this is treated as garbage rather than a real position. */
+inline constexpr float kPositionSanityLimit = 100'000.0F;
+
 /** @return True when one target index is safe to hand to the Client's table lookup. */
 [[nodiscard]] bool target_allowed(std::uint32_t target, Verdict& verdict) noexcept {
     if (target > kTargetMaximum) {
@@ -29,6 +40,39 @@ namespace {
     return true;
 }
 
+/** @return True when a decoded axis is finite and inside the sanity bound. */
+[[nodiscard]] bool position_axis_sane(float value) noexcept {
+    return std::isfinite(value) && std::fabs(value) < kPositionSanityLimit;
+}
+
+/**
+ * Decodes the validated position triple directly from the raw message body, independent of the
+ * header reader's cursor. Only called once the headerBits == 29 shape and buffer length are
+ * confirmed by the caller.
+ * @param body The exact span validate() was given.
+ * @param parsed Receives x/y/z and hasPosition when the read and sanity gate both succeed.
+ */
+void decode_position(std::span<const std::byte> body, Incident& parsed) noexcept {
+    encoding::bits::Reader positionReader(body);
+    std::uint64_t xWord = 0;
+    std::uint64_t yWord = 0;
+    std::uint64_t zWord = 0;
+    if (!positionReader.skip(kPositionBitOffset) || !positionReader.read(32, xWord)
+        || !positionReader.read(32, yWord) || !positionReader.read(32, zWord)) {
+        return;
+    }
+    const float x = std::bit_cast<float>(static_cast<std::uint32_t>(xWord));
+    const float y = std::bit_cast<float>(static_cast<std::uint32_t>(yWord));
+    const float z = std::bit_cast<float>(static_cast<std::uint32_t>(zWord));
+    if (!position_axis_sane(x) || !position_axis_sane(y) || !position_axis_sane(z)) {
+        return;
+    }
+    parsed.x = x;
+    parsed.y = y;
+    parsed.z = z;
+    parsed.hasPosition = true;
+}
+
 } // namespace
 
 /** @return A short stable name for one verdict, for the log line. */
@@ -124,6 +168,15 @@ Verdict validate(std::span<const std::byte> payload, Incident& parsed) noexcept
     if (parsed.payloadLength > kPayloadMaximum) {
         return Verdict::payloadTooLong;
     }
+    // The header is everything decoded up to here; the payload bytes read below are opaque.
+    parsed.headerBits = static_cast<std::uint32_t>(payload.size() * encoding::kBitsPerByte
+                                                    - reader.remaining_bits());
+    // The position triple is only validated for this one header shape, and only when the body is
+    // long enough to actually hold bits 518..613.
+    if (parsed.headerBits == kPositionHeaderBits
+        && payload.size() * encoding::kBitsPerByte >= kPositionBitOffset + kPositionBitWidth) {
+        decode_position(payload, parsed);
+    }
     for (std::uint32_t index = 0; index < parsed.payloadLength; ++index) {
         if (!reader.read(CHAR_BIT, field)) {
             return Verdict::truncated;

+ 15 - 0
Sunrise/src/middleware/bap/activity_message/incident.h

@@ -75,12 +75,27 @@ struct Incident {
     std::uint32_t optionalWordB{};
     /** Bits the body used. Below the payload's own bit count means trailing padding. */
     std::uint32_t consumedBits{};
+    /**
+     * Bit cursor position where the decoded header ends, i.e. immediately before the opaque
+     * payload bytes. Also the shape gate for the decoded position below: only the headerBits == 29
+     * message shape has a validated field layout inside the opaque payload.
+     */
+    std::uint32_t headerBits{};
+    /**
+     * World position, decoded only for the one message shape whose payload layout is validated
+     * (headerBits == 29). Meaningless unless hasPosition is set.
+     */
+    float x{};
+    float y{};
+    float z{};
     /** Set when a compressed selector is present. Its bytes stay opaque. */
     bool hasCompressedSelector{};
     /** Set when the two optional words are present. */
     bool hasOptionalBlock{};
     /** Set when the payload length and its bytes were reached and checked. */
     bool hasPayload{};
+    /** Set when x/y/z were decoded from the validated shape and passed the sanity gate. */
+    bool hasPosition{};
 };
 
 /** @return A short stable name for one verdict, for the log line. */

+ 10 - 1
Sunrise/src/middleware/content/packages/tables/definition_index_table.h

@@ -95,7 +95,16 @@ inline constexpr std::size_t kNodeExpressionFieldAlternate = 48;
 /** Records a node owns, four bytes each as a row then a gate. */
 inline constexpr std::size_t kNodeChildRecordField = 136;
 inline constexpr std::size_t kNodeChildRecordStride = 4;
-/** A category's parent record reads the slot immediately above the category's own. */
+/**
+ * How a lore book's parent-record bar slot relates to the category's own.
+ *
+ * The naive rule — parent = category slot + 1 — holds only when the slot above a category is free.
+ * Categories were allocated in contiguous runs, so for those books slot+1 is the next book's
+ * category. The shipped allocation instead defers every run's parent slots to immediately after
+ * the run, assigning them in reverse category order: the first book of a run gets the last parent
+ * slot and vice versa. Verified against four independent in-game marker readings (Pigeon 7060->7061,
+ * Exegete 7284->7285 isolated; Aunor -> 5230 and Ecdysis -> 5229 inside the 5222-5227 run).
+ */
 inline constexpr std::int32_t kNodeParentSlotStep = 1;
 /** Array descriptor of the character object's flag mapping table, sized to that bank. */
 inline constexpr std::size_t kCharacterFlagMapDescriptor = 40;

+ 3 - 0
Sunrise/src/middleware/datagen/family4/account/account_encoder.cpp

@@ -140,6 +140,9 @@ bool encode(const state::AccountState& state, std::span<std::byte> output) noexc
     // A node's progress bar reads a value slot and shows whatever it holds, so the claimed children
     // have to be counted into it here or the bar never moves.
     (void)state::record_claims::apply_node_progress(object.objectiveValues);
+    // A record reads claimable when its objective equals completionValue and its flag is clear --
+    // the flag alone can never carry that state, so its objective value(s) are written here instead.
+    (void)state::record_claims::apply_claimable_objectives(object.objectiveValues);
 
     for (layout::CharacterUnlockBlock& block : object.characterUnlocks) {
         block.flags = unlocks.characterFlags;

+ 161 - 34
Sunrise/src/server/bap/encrypted/activity_message/receipts/activity_message_receipts.cpp

@@ -6,8 +6,6 @@
 
 #include "../../../../../state/activity/current_activity.h"
 #include "../../../../../state/lore/lore_grant.h"
-#include <vector>
-#include <algorithm>
 #include "../../../../../state/build_data/collectibles/collectible_catalog.h"
 #include "../../../../../state/build_data/sobjects/sobject_catalog.h"
 #include "../../../../bap/internal.h"
@@ -15,6 +13,7 @@
 
 #include <array>
 #include <cstdarg>
+#include <cstddef>
 #include <cstdint>
 #include <cstdio>
 
@@ -294,26 +293,30 @@ Framed frame_incident(const message::Request& request) noexcept {
     const bool accepted = verdict == incident::Verdict::accepted;
     report(accepted ? core::log::Level::debug : core::log::Level::warn,
            "ev=activity stage=incident result=%s target=%u extra=%u selector=%u "
-           "optional=%u payload=%u",
+           "optional=%u payload=%u hdrbits=%u",
            incident::verdict_name(verdict),
            parsed.primaryTarget,
            parsed.extraTargetCount,
            parsed.selectorLength,
            static_cast<unsigned>(parsed.hasOptionalBlock),
-           parsed.payloadLength);
+           parsed.payloadLength,
+           parsed.headerBits);
     // Resolve the target against the definition table it indexes, so an incident says what it is
     // rather than carrying a bare number. Nothing acts on it yet; this is what acting on it needs.
     if (accepted) {
         state::build_data::sobjects::Definition definition{};
-        if (state::build_data::sobjects::find(
-                static_cast<std::uint16_t>(parsed.primaryTarget), definition)) {
+        const bool sobjectFound = state::build_data::sobjects::find(
+            static_cast<std::uint16_t>(parsed.primaryTarget), definition);
+        if (sobjectFound) {
             report(core::log::Level::info,
-                   "ev=activity stage=sobject target=%u hash=0x%08X type=%d group=%u ordinal=%u",
+                   "ev=activity stage=sobject target=%u hash=0x%08X type=%d group=%u ordinal=%u "
+                   "lane4=0x%08X",
                    parsed.primaryTarget,
                    definition.nameHash,
                    definition.typeCode,
                    static_cast<unsigned>(definition.selectorGroup),
-                   static_cast<unsigned>(definition.nodeOrdinal));
+                   static_cast<unsigned>(definition.nodeOrdinal),
+                   definition.lane4);
         } else {
             report(core::log::Level::warn,
                    "ev=activity stage=sobject target=%u result=unresolved rows=%zu",
@@ -321,6 +324,41 @@ Framed frame_incident(const message::Request& request) noexcept {
                    state::build_data::sobjects::count());
         }
 
+        // The decoded world position, only present for the one validated message shape.
+        if (parsed.hasPosition) {
+            report(core::log::Level::info,
+                   "ev=activity stage=incident_pos x=%.4f y=%.4f z=%.4f",
+                   static_cast<double>(parsed.x),
+                   static_cast<double>(parsed.y),
+                   static_cast<double>(parsed.z));
+        }
+
+        // The extra targets, which have never been looked at. They are sobject indices too -- the
+        // parser range-checks them the same way -- and the primary target has turned out to name
+        // only the kind of object, not the instance: one pickup in the Menagerie and another on the
+        // Tangled Shore both reported target 3539. So if anything in the incident distinguishes one
+        // vase from another, this is where it can still be. Logged rather than acted on, because a
+        // guess about which extra means what is exactly the mistake this project keeps paying for.
+        for (std::uint32_t extra = 0; extra < parsed.extraTargetCount; ++extra) {
+            const std::uint32_t target = parsed.extraTargets[extra];
+            state::build_data::sobjects::Definition row{};
+            if (state::build_data::sobjects::find(static_cast<std::uint16_t>(target), row)) {
+                report(core::log::Level::info,
+                       "ev=activity stage=sobject_extra slot=%u target=%u hash=0x%08X type=%d "
+                       "lane4=0x%08X",
+                       extra,
+                       target,
+                       row.nameHash,
+                       row.typeCode,
+                       row.lane4);
+            } else {
+                report(core::log::Level::info,
+                       "ev=activity stage=sobject_extra slot=%u target=%u result=unresolved",
+                       extra,
+                       target);
+            }
+        }
+
         // The type payload, which is what the schema describes. The parser separates it from
         // the envelope; reading the whole message body instead puts every field at an offset the
         // schema does not predict.
@@ -345,10 +383,12 @@ Framed frame_incident(const message::Request& request) noexcept {
             const std::uint64_t sequence = take(32);
             const std::uint64_t kindRaw = take(3);
             const std::uint64_t identity = take(64);
-            // Which book. The payload does not name the chapter it granted -- its only
-            // per-object content is a position -- so the reward is chosen here instead. The bubble
-            // does say which activity the pickup happened in, and an activity's pickups feed one
-            // book, which is a far smaller association than one entry per object in the world.
+            // Which reward. Lane 4 of the target's sobject row names it exactly when the type code
+            // resolves one -- a type-10 row's low half is the record itself, no guessing needed --
+            // so that is tried first. Only when it does not resolve does the bubble heuristic below
+            // run: the payload does not name the chapter it granted -- its only per-object content
+            // is a position -- and the bubble at least says which activity the pickup happened in,
+            // which is a far smaller association (one book per activity) than one entry per object.
             //
             // The bubble sits at bit 126, the first of three consecutive u32 fields of the nested
             // block. That position is measured: the same hash appears at 126, 158 and 190, and the
@@ -361,34 +401,121 @@ Framed frame_incident(const message::Request& request) noexcept {
             // The activity selection already named the bubble once, so it is read from there and
             // the payload is not decoded at all.
             constexpr std::array<std::int32_t, 2> kPickupTypeCodes{2, 10};
-            state::build_data::sobjects::Definition row{};
             bool pickupType = false;
-            if (state::build_data::sobjects::find(static_cast<std::uint16_t>(parsed.primaryTarget),
-                                                  row)) {
+            if (sobjectFound) {
                 for (const std::int32_t code : kPickupTypeCodes) {
-                    pickupType = pickupType || row.typeCode == code;
+                    pickupType = pickupType || definition.typeCode == code;
                 }
             }
             if (pickupType) {
-                const std::uint32_t bubble = state::activity::current_bubble();
-                const std::uint16_t node = state::lore::book_for_bubble(bubble);
-                const state::lore::GrantOutcome outcome = state::lore::grant_next_chapter(node);
-                if (outcome == state::lore::GrantOutcome::granted) {
-                    // Nothing else will stage an account image for this peer: a pickup is not a web
-                    // service transaction and has no response to carry the change back. Arm every
-                    // peer, the origin included, so the chapter appears without a relaunch and a
-                    // second pickup in the same run sees the first one already held.
-                    bap::arm_account_resync_everywhere();
+                bool resolvedExactly = false;
+                if (definition.typeCode == 10) {
+                    // typeCode 10's lane-4 low half is a DestinyRecordDefinition row. 360 of 807
+                    // type-10 rows carry one that resolves; the rest fall through to the fallback
+                    // below exactly as they did before this path existed.
+                    const std::uint16_t recordRow = definition.recordRow();
+                    const state::lore::GrantOutcome outcome = state::lore::grant_record(recordRow);
+                    // A row whose lane 4 does not name a chapter is not an exact resolution at
+                    // all, only a number that fell inside the record table, so it falls back.
+                    resolvedExactly = outcome != state::lore::GrantOutcome::recordNotFound
+                                      && outcome != state::lore::GrantOutcome::notAChapter;
+                    if (resolvedExactly && outcome == state::lore::GrantOutcome::granted) {
+                        // Nothing else will stage an account image for this peer: a pickup is not a
+                        // web service transaction and has no response to carry the change back. Arm
+                        // every peer, the origin included, so the record appears without a relaunch.
+                        bap::arm_account_resync_everywhere();
+                    }
+                    if (resolvedExactly) {
+                        report(outcome == state::lore::GrantOutcome::granted
+                                   ? core::log::Level::info
+                                   : core::log::Level::warn,
+                               "ev=activity stage=lore path=exact type=10 target=%u lane4=0x%08X "
+                               "record=%u result=%s",
+                               parsed.primaryTarget,
+                               definition.lane4,
+                               static_cast<unsigned>(recordRow),
+                               state::lore::grant_outcome_name(outcome));
+                    }
+                } else if (definition.typeCode == 2) {
+                    // typeCode 2's lane-4 high half is a DestinyCollectibleDefinition row and
+                    // resolves 709 of 713 times, but nothing in this file's scope owns the
+                    // account-side collections write: record_claims only knows how to claim a
+                    // record's completion flag, and inventing a second mechanism here is exactly
+                    // what this pass was told not to do.
+                    // TODO(lore): grant the item behind collectibleRow once a collections claim
+                    // path exists. Until then this always falls back to the bubble table below.
+                    const std::uint16_t collectibleRow = definition.collectibleRow();
+                    state::build_data::collectibles::Definition collectible{};
+                    const bool collectibleResolves = state::build_data::collectibles::find(
+                        collectibleRow, collectible);
+                    report(core::log::Level::warn,
+                           "ev=activity stage=lore path=exact type=2 target=%u lane4=0x%08X "
+                           "collectible=%u resolves=%d result=not_implemented",
+                           parsed.primaryTarget,
+                           definition.lane4,
+                           static_cast<unsigned>(collectibleRow),
+                           collectibleResolves ? 1 : 0);
+                }
+                if (!resolvedExactly) {
+                    const std::uint32_t bubble = state::activity::current_bubble();
+                    state::lore::GrantOutcome outcome;
+                    const char* path;
+                    std::size_t bucketSize = 0;
+                    std::uint16_t node = state::lore::kNoBook;
+
+                    if (bubble == state::lore::kBubbleUnsetSentinel) {
+                        // caluseum_experience is instanced and so carries no real bubble at all --
+                        // every one of its pickups reports the FNV-1a offset basis here. That makes
+                        // this Confessions by construction, not a bubble_record_table lookup, so the
+                        // sentinel is recognised directly rather than ever being handed to the table.
+                        node = state::lore::kConfessionsNode;
+                        outcome = state::lore::grant_next_chapter(node);
+                        path = "instanced";
+                    } else {
+                        outcome = state::lore::grant_from_bubble_table(bubble, bucketSize);
+                        path = "bubble_table";
+                        if (outcome == state::lore::GrantOutcome::bubbleTableExhausted) {
+                            // Every candidate this bubble's bucket names is already held. Logged
+                            // distinctly here so an exhausted bucket reads differently in the logs
+                            // from a bubble the table never covered, then fall through below rather
+                            // than granting nothing silently.
+                            report(core::log::Level::warn,
+                                   "ev=activity stage=lore path=bubble_table bubble=0x%08X "
+                                   "bucket=%zu result=%s",
+                                   bubble, bucketSize, state::lore::grant_outcome_name(outcome));
+                        }
+                        if (outcome != state::lore::GrantOutcome::granted
+                            && outcome != state::lore::GrantOutcome::refused) {
+                            // Not in the table, or the table's own candidates are exhausted: fall
+                            // through to the same book path this always used. The generated table is
+                            // built from public map data that never recorded every bubble hosting a
+                            // pickup, so a bubble missing from it is a gap in that data rather than a
+                            // place nothing is collectable -- walk whatever book is known for it and
+                            // grant an arbitrary chapter rather than granting nothing.
+                            node = state::lore::legacy_book_for_bubble(bubble);
+                            outcome = state::lore::grant_next_chapter(node);
+                            path = "fallback";
+                        }
+                    }
+
+                    if (outcome == state::lore::GrantOutcome::granted) {
+                        // Nothing else will stage an account image for this peer: a pickup is not a web
+                        // service transaction and has no response to carry the change back. Arm every
+                        // peer, the origin included, so the chapter appears without a relaunch and a
+                        // second pickup in the same run sees the first one already held.
+                        bap::arm_account_resync_everywhere();
+                    }
+                    report(outcome == state::lore::GrantOutcome::granted ? core::log::Level::info
+                                                                         : core::log::Level::warn,
+                           "ev=activity stage=lore path=%s bubble=0x%08X node=%u bucket=%zu "
+                           "result=%s record=%u item=%d",
+                           path, bubble, static_cast<unsigned>(node), bucketSize,
+                           state::lore::grant_outcome_name(outcome),
+                           static_cast<unsigned>(outcome == state::lore::GrantOutcome::granted
+                                                     ? state::lore::last_granted_record()
+                                                     : 0),
+                           state::lore::last_item_granted() ? 1 : 0);
                 }
-                report(outcome == state::lore::GrantOutcome::granted ? core::log::Level::info
-                                                                     : core::log::Level::warn,
-                       "ev=activity stage=lore bubble=0x%08X node=%u result=%s record=%u item=%d",
-                       bubble, static_cast<unsigned>(node),
-                       state::lore::grant_outcome_name(outcome),
-                       static_cast<unsigned>(outcome == state::lore::GrantOutcome::granted
-                                                 ? state::lore::last_granted_record()
-                                                 : 0),
-                       state::lore::last_item_granted() ? 1 : 0);
             }
 
             report(core::log::Level::info,

+ 11 - 5
Sunrise/src/state/build_data/cache/records/cache_investment_records.cpp

@@ -83,9 +83,11 @@ bool encode(const build_data::records::Definition& value,
             RecordDefinitionRecord& record) noexcept {
     record = {
         value.definitionIndex,
+        value.definitionHash,
         value.completionFlagIndex,
+        value.loreRow,
         value.scoreValue,
-        kReservedFieldValue,
+        value.categoryValueIndex,
     };
     return true;
 }
@@ -94,10 +96,14 @@ bool encode(const build_data::records::Definition& value,
 bool decode(const RecordDefinitionRecord& record,
             build_data::records::Definition& value) noexcept {
     value = {};
-    if (record.reserved != kReservedFieldValue) {
-        return false;
-    }
-    value = {record.definitionIndex, record.completionFlagIndex, record.scoreValue};
+    // Assigned by name, never positionally: this row outlived two field additions to Definition,
+    // and a positional list silently filled the new fields with the wrong disk values.
+    value.definitionIndex = record.definitionIndex;
+    value.definitionHash = record.definitionHash;
+    value.completionFlagIndex = record.completionFlagIndex;
+    value.loreRow = record.loreRow;
+    value.scoreValue = record.scoreValue;
+    value.categoryValueIndex = record.categoryValueIndex;
     return true;
 }
 

+ 19 - 5
Sunrise/src/state/build_data/cache/records/format.h

@@ -29,8 +29,13 @@ inline constexpr std::array<char, 8> kCacheMagic{'S', 'U', 'N', 'R', 'I', 'S', '
  * Current build-data cache format. An older cache is rebuilt rather than read.
  * Bump it when a stored shape changes, and when the extraction filling it changes what it writes.
  * A cached row survives a code change, so a corrected walk keeps publishing the old rows.
+ *
+ * 46: records::Definition gained definitionHash (record-reward lookup). The old cache's record
+ *     rows are four bytes short per row; reading them as the new shape shifts every later field,
+ *     which surfaced as claims resolving to the score value instead of the flag index. Bumped so
+ *     the stale shape is rejected and rebuilt instead of misread.
  */
-inline constexpr std::uint32_t kCacheFormatVersion = 45;
+inline constexpr std::uint32_t kCacheFormatVersion = 46;
 /** Signed -1 on disk means there is no equipment slot. */
 inline constexpr std::int8_t kAbsentEquipmentSlot = -1;
 /** The standard 64-bit FNV-1a offset basis starts the payload checksum. */
@@ -238,13 +243,21 @@ struct ProgressionRecord {
     std::uint8_t reserved{};
 };
 
-/** Disk form of one record and the account flag bank row its claim sets. */
+/** Disk form of one record and the account flag bank row its claim sets.
+ *
+ * Carries every runtime field. It did not always: loreRow, categoryValueIndex and definitionHash
+ * were added to records::Definition after this row was laid out, and the positional decode then
+ * filled completionFlagIndex from the disk scoreValue - claims resolved to the score (50/100) as
+ * their flag, which looked like claiming doing nothing at all. The row is now the full shape and
+ * the codec assigns by name; kCacheFormatVersion bump rejects rows written before that.
+ */
 struct RecordDefinitionRecord {
     std::uint16_t definitionIndex{};
+    std::uint32_t definitionHash{};
     std::uint16_t completionFlagIndex{};
+    std::uint16_t loreRow{};
     std::uint16_t scoreValue{};
-    /** Must be zero, so the packed record row always matches. */
-    std::uint16_t reserved{};
+    std::uint16_t categoryValueIndex{};
 };
 
 /** Disk form of one dense socket-entry-list definition. */
@@ -470,7 +483,8 @@ static_assert(sizeof(RosterGroupRecord)
                      + 2 * scenarios::kRosterSlotCapacity * sizeof(std::uint8_t)
                      + scenarios::kRosterSlotCapacity * sizeof(std::uint16_t));
 static_assert(sizeof(ProgressionRecord) == sizeof(std::uint16_t) + 2 * sizeof(std::uint8_t));
-static_assert(sizeof(RecordDefinitionRecord) == 4 * sizeof(std::uint16_t));
+static_assert(sizeof(RecordDefinitionRecord)
+              == sizeof(std::uint16_t) + sizeof(std::uint32_t) + 4 * sizeof(std::uint16_t));
 static_assert(sizeof(AbilityBucketRecord)
               == sizeof(std::uint16_t) + 6 * sizeof(std::uint8_t)
                      + 2 * abilities::kBucketCapacity * sizeof(std::uint8_t)

+ 20 - 0
Sunrise/src/state/build_data/nodes/definition.h

@@ -49,6 +49,21 @@ struct Definition {
     std::uint16_t definitionIndex{};
     /** Account value bank mapping row, or kUnavailableValueIndex when no slot is addressable. */
     std::uint16_t valueIndex{kUnavailableValueIndex};
+    /**
+     * The raw account-scoped value SLOT this node's expression names, before mapping.
+     *
+     * Parent-slot assignment needs the slot itself, not the mapped row: the shipped allocation
+     * groups books by contiguous slot runs, and the run shape is invisible through indices alone.
+     */
+    std::int16_t valueSlot{-1};
+    /**
+     * The raw character-scoped value SLOT this node's expression names, or -1.
+     *
+     * One lore book's bar reads a slot only the character table carries. Its parent needs the same
+     * run-aware assignment as account-scoped books, keyed by this slot's place in the allocation
+     * sequence.
+     */
+    std::int16_t characterValueSlot{-1};
     /**
      * Account value index of the parent record's own bar, one slot above the node's.
      *
@@ -56,6 +71,11 @@ struct Definition {
      * the chapters. They are separate slots and need separate counts.
      */
     std::uint16_t parentValueIndex{kUnavailableValueIndex};
+    /**
+     * Character value index of the parent record's own bar for a character-scoped book, or
+     * kUnavailableValueIndex.
+     */
+    std::uint16_t parentCharacterValueIndex{kUnavailableValueIndex};
     /**
      * Account flag index this node's own gate reads, or kUnavailableFlagIndex.
      *

+ 19 - 0
Sunrise/src/state/build_data/sobjects/sobject_catalog.h

@@ -33,6 +33,25 @@ struct Definition {
     std::uint16_t selectorGroup{};
     /** Position in that group's node table. */
     std::uint16_t nodeOrdinal{};
+    /**
+     * Row offset +16, "lane 4": a packed pair of u16s whose meaning is chosen by typeCode, not
+     * fixed. On typeCode 10 (dead ghosts and similar) the low half is a DestinyRecordDefinition row
+     * index -- 807 rows carry this type, 360 of them resolving. On typeCode 2 (lore vases and
+     * similar) the high half is a DestinyCollectibleDefinition row index -- 713 rows carry this
+     * type, 709 resolving. Kept raw and read through the accessors below so a caller cannot reach
+     * into the wrong half without first deciding, from typeCode, which one applies.
+     */
+    std::uint32_t lane4{};
+
+    /** @return Lane 4's low half, the record row a typeCode-10 sobject names. */
+    [[nodiscard]] constexpr std::uint16_t recordRow() const noexcept {
+        return static_cast<std::uint16_t>(lane4 & 0xFFFFU);
+    }
+
+    /** @return Lane 4's high half, the collectible row a typeCode-2 sobject names. */
+    [[nodiscard]] constexpr std::uint16_t collectibleRow() const noexcept {
+        return static_cast<std::uint16_t>(lane4 >> 16U);
+    }
 };
 
 /** Clears every generated row. */

+ 247 - 0
Sunrise/src/state/lore/bubble_record_table.h

@@ -0,0 +1,247 @@
+// GENERATED FILE -- do not hand-edit.
+//
+// Produced by gen_bubble_table.py from bubble_record_map.json. To change an entry, fix
+// the map data and regenerate:
+//   python3 collectible-triumph-map/gen_bubble_table.py
+#pragma once
+
+#include <array>
+#include <cstdint>
+#include <span>
+
+namespace sunrise::state::lore::bubble_record_table {
+
+/**
+ * bubble -> candidate record rows, joined from the 86657-era manifest.
+ *
+ * Each bucket is one bubble a lore-collectible pickup can report, holding the record rows
+ * (build row indices into the record table, not definition hashes) that bubble's pickups can
+ * complete. Where a bucket holds more than one row, a pickup grants the first the account
+ * does not already hold -- see grant_from_bubble_table in lore_grant.h.
+ *
+ * 59 bubbles, generated from bubble_record_map.json's non-empty buckets. The FNV-1a
+ * offset basis (0x811C9DC5) is not a real bubble and never appears here --
+ * see kConfessionsNode in lore_grant.h for the instanced-activity case that used to be
+ * aliased onto it.
+ */
+
+namespace detail {
+
+// bubble 0x09711AEB, destination 308080871, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows0 = {819};
+// bubble 0x09777230, destination 308080871, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows1 = {800};
+// bubble 0x0E5486FF, destination 290444260, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows2 = {1834};
+// bubble 0x109F7CF6, destination 2779202173, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows3 = {778, 40};
+// bubble 0x110EFBDC, destination 290444260, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows4 = {1849, 1829};
+// bubble 0x18E267F9, destination 290444260, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows5 = {1826, 1827};
+// bubble 0x1CFBD6EB, destination 1199524104, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows6 = {748};
+// bubble 0x257C904B, destination 359854275, 3 candidate row(s).
+inline constexpr std::array<std::uint16_t, 3> kRows7 = {790, 803, 744};
+// bubble 0x272010E9, destination 1199524104, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows8 = {806};
+// bubble 0x29A640EF, destination 1199524104, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows9 = {749};
+// bubble 0x29B8886A, destination 359854275, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows10 = {788, 755};
+// bubble 0x2A2301B8, destination 2779202173, 3 candidate row(s).
+inline constexpr std::array<std::uint16_t, 3> kRows11 = {785, 783, 40};
+// bubble 0x2DCABF42, destination 2779202173, 4 candidate row(s).
+inline constexpr std::array<std::uint16_t, 4> kRows12 = {787, 772, 765, 43};
+// bubble 0x31256234, destination 359854275, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows13 = {824};
+// bubble 0x31943967, destination 359854275, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows14 = {791};
+// bubble 0x3453E759, destination 290444260, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows15 = {1828};
+// bubble 0x37A08717, destination 126924919, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows16 = {813};
+// bubble 0x3976EE58, destination 2388758973, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows17 = {811};
+// bubble 0x3C18FBCE, destination 290444260, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows18 = {1831, 1842};
+// bubble 0x3CB36B81, destination 1993421442, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows19 = {798, 815};
+// bubble 0x41006024, destination 359854275, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows20 = {821};
+// bubble 0x410C4F97, destination 2779202173, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows21 = {784, 42};
+// bubble 0x447E4EB5, destination 290444260, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows22 = {1832, 1844};
+// bubble 0x4AC36E96, destination 308080871, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows23 = {820, 750};
+// bubble 0x4CF9B596, destination 2388758973, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows24 = {810};
+// bubble 0x5A95C41A, destination 359854275, 5 candidate row(s).
+inline constexpr std::array<std::uint16_t, 5> kRows25 = {786, 802, 742, 741, 740};
+// bubble 0x5E856821, destination 2779202173, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows26 = {761, 41};
+// bubble 0x64BFD67C, destination 359854275, 3 candidate row(s).
+inline constexpr std::array<std::uint16_t, 3> kRows27 = {792, 805, 746};
+// bubble 0x6D64AA42, destination 290444260, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows28 = {1833, 1846};
+// bubble 0x6DDAC118, destination 2388758973, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows29 = {809};
+// bubble 0x75A68449, destination 290444260, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows30 = {1830};
+// bubble 0x773F859E, destination 2779202173, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows31 = {48};
+// bubble 0x7B3F5526, destination 359854275, 4 candidate row(s).
+inline constexpr std::array<std::uint16_t, 4> kRows32 = {793, 823, 757, 756};
+// bubble 0x7EE2011A, destination 359854275, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows33 = {789, 804};
+// bubble 0x83682E45, destination 290444260, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows34 = {1843};
+// bubble 0x85B17A04, destination 2218917881, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows35 = {818};
+// bubble 0x890FA3E0, destination 359854275, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows36 = {822, 754};
+// bubble 0x9F2597D3, destination 126924919, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows37 = {814};
+// bubble 0xA4ADFE84, destination 2779202173, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows38 = {780, 781};
+// bubble 0xA7CF4C10, destination 2218917881, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows39 = {797};
+// bubble 0xAA40DF1C, destination 2779202173, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows40 = {48};
+// bubble 0xADE65E0A, destination 1199524104, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows41 = {808};
+// bubble 0xB76C4F9A, destination 1199524104, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows42 = {794};
+// bubble 0xB76C4F9B, destination 1199524104, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows43 = {807, 747};
+// bubble 0xB9E2BD60, destination 2779202173, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows44 = {48};
+// bubble 0xBEE4E974, destination 2779202173, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows45 = {48};
+// bubble 0xC1FAAA4C, destination 126924919, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows46 = {812};
+// bubble 0xC64457D2, destination 290444260, 3 candidate row(s).
+inline constexpr std::array<std::uint16_t, 3> kRows47 = {1841, 1822, 1823};
+// bubble 0xC65A2A44, destination 2388758973, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows48 = {795};
+// bubble 0xD1CF7944, destination 359854275, 2 candidate row(s).
+inline constexpr std::array<std::uint16_t, 2> kRows49 = {753, 752};
+// bubble 0xD1EF1EF5, destination 2779202173, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows50 = {779};
+// bubble 0xEF2B6D69, destination 2218917881, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows51 = {816};
+// bubble 0xEF2B6D6A, destination 2218917881, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows52 = {817};
+// bubble 0xEFEF8119, destination 290444260, 3 candidate row(s).
+inline constexpr std::array<std::uint16_t, 3> kRows53 = {1847, 1824, 1825};
+// bubble 0xF1E4371C, destination 2779202173, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows54 = {782};
+// bubble 0xF25CEA8D, destination 290444260, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows55 = {1845};
+// bubble 0xF68613C1, destination 126924919, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows56 = {796};
+// bubble 0xFA122719, destination 290444260, 4 candidate row(s).
+inline constexpr std::array<std::uint16_t, 4> kRows57 = {1850, 1822, 1820, 1821};
+// bubble 0xFA9B272B, destination 308080871, 1 candidate row(s).
+inline constexpr std::array<std::uint16_t, 1> kRows58 = {751};
+
+/** One bubble's candidate record rows, in table order. */
+struct Bucket {
+    std::uint32_t bubbleHash;
+    std::span<const std::uint16_t> rows;
+};
+
+inline constexpr std::array<Bucket, 59> kBuckets{{
+    {0x09711AEBU, kRows0},
+    {0x09777230U, kRows1},
+    {0x0E5486FFU, kRows2},
+    {0x109F7CF6U, kRows3},
+    {0x110EFBDCU, kRows4},
+    {0x18E267F9U, kRows5},
+    {0x1CFBD6EBU, kRows6},
+    {0x257C904BU, kRows7},
+    {0x272010E9U, kRows8},
+    {0x29A640EFU, kRows9},
+    {0x29B8886AU, kRows10},
+    {0x2A2301B8U, kRows11},
+    {0x2DCABF42U, kRows12},
+    {0x31256234U, kRows13},
+    {0x31943967U, kRows14},
+    {0x3453E759U, kRows15},
+    {0x37A08717U, kRows16},
+    {0x3976EE58U, kRows17},
+    {0x3C18FBCEU, kRows18},
+    {0x3CB36B81U, kRows19},
+    {0x41006024U, kRows20},
+    {0x410C4F97U, kRows21},
+    {0x447E4EB5U, kRows22},
+    {0x4AC36E96U, kRows23},
+    {0x4CF9B596U, kRows24},
+    {0x5A95C41AU, kRows25},
+    {0x5E856821U, kRows26},
+    {0x64BFD67CU, kRows27},
+    {0x6D64AA42U, kRows28},
+    {0x6DDAC118U, kRows29},
+    {0x75A68449U, kRows30},
+    {0x773F859EU, kRows31},
+    {0x7B3F5526U, kRows32},
+    {0x7EE2011AU, kRows33},
+    {0x83682E45U, kRows34},
+    {0x85B17A04U, kRows35},
+    {0x890FA3E0U, kRows36},
+    {0x9F2597D3U, kRows37},
+    {0xA4ADFE84U, kRows38},
+    {0xA7CF4C10U, kRows39},
+    {0xAA40DF1CU, kRows40},
+    {0xADE65E0AU, kRows41},
+    {0xB76C4F9AU, kRows42},
+    {0xB76C4F9BU, kRows43},
+    {0xB9E2BD60U, kRows44},
+    {0xBEE4E974U, kRows45},
+    {0xC1FAAA4CU, kRows46},
+    {0xC64457D2U, kRows47},
+    {0xC65A2A44U, kRows48},
+    {0xD1CF7944U, kRows49},
+    {0xD1EF1EF5U, kRows50},
+    {0xEF2B6D69U, kRows51},
+    {0xEF2B6D6AU, kRows52},
+    {0xEFEF8119U, kRows53},
+    {0xF1E4371CU, kRows54},
+    {0xF25CEA8DU, kRows55},
+    {0xF68613C1U, kRows56},
+    {0xFA122719U, kRows57},
+    {0xFA9B272BU, kRows58},
+}};
+
+/** @return True if no bucket names the FNV-1a offset basis as a bubble. */
+[[nodiscard]] constexpr bool none_is_unset_sentinel() noexcept {
+    for (const Bucket& bucket : kBuckets) {
+        if (bucket.bubbleHash == 0x811C9DC5U) {
+            return false;
+        }
+    }
+    return true;
+}
+static_assert(none_is_unset_sentinel(),
+              "generated bubble table must never contain the FNV-1a offset basis -- it is not "
+              "a real bubble, see lore_grant.h's instanced-activity case instead");
+
+} // namespace detail
+
+/**
+ * Looks up one bubble's candidate record rows.
+ * @param bubble Bubble hash an incident reported.
+ * @return The bucket's rows in table order, or an empty span when the bubble is not mapped.
+ */
+[[nodiscard]] constexpr std::span<const std::uint16_t> records_for_bubble(
+    std::uint32_t bubble) noexcept {
+    for (const detail::Bucket& bucket : detail::kBuckets) {
+        if (bucket.bubbleHash == bubble) {
+            return bucket.rows;
+        }
+    }
+    return {};
+}
+
+} // namespace sunrise::state::lore::bubble_record_table

+ 106 - 50
Sunrise/src/state/lore/lore_grant.cpp

@@ -1,6 +1,5 @@
 #include "lore_grant.h"
 
-#include <array>
 #include <atomic>
 #include <cstddef>
 #include <span>
@@ -12,43 +11,40 @@
 #include "../build_data/records/definition.h"
 #include "../build_data/records/record_catalog.h"
 #include "../build_data/records/record_persistence.h"
-#include "../build_data/collectibles/collectible_catalog.h"
-#include "../build_data/inventory/buckets/definition.h"
-#include "../build_data/items/details/definition.h"
-#include "../runtime/runtime.h"
 #include "../build_data/runtime.h"
 #include "../record_claims/record_claims.h"
+#include "bubble_record_table.h"
 
 namespace sunrise::state::lore {
 namespace {
 
+std::atomic<std::uint16_t> g_lastGranted{0};
+std::atomic<bool> g_lastItemGranted{false};
+
 /**
- * Which book an activity's pickups feed.
+ * Publishes the node and record tables once, so a warm start does not run either path empty.
  *
- * One entry per activity, not one per object. This is the whole of the authored data the chosen
- * design needs, and it is the part that would be replaced if Bungie's own object-to-reward mapping
- * were ever reproduced from activity and spawn data.
- *
- * Only the Menagerie is known so far, measured from its pickups: the bubble its incidents carry
- * against the book those pickups fill.
+ * On a warm start the package pass is skipped, so neither table is published until something asks
+ * for it. Both grant paths need the record table and the book path also needs the node table, so
+ * both are brought up together here; a caller that only needs records still pays for nodes once,
+ * which costs nothing once the tables are up and is simpler than tracking each domain apart.
+ * @return True once both tables are known published, this call or an earlier one.
  */
-struct BubbleBook {
-    std::uint32_t bubble;
-    std::uint16_t node;
-};
-
-constexpr std::array<BubbleBook, 2> kBubbleBooks{{
-    // caluseum_experience, whose vases fill Confessions. Node 838 confirmed against the published
-    // manifest: nine chapters, Entry I on record 1708 carrying lore hash 0x58C9C088.
-    {0x811C9DC5U, 838U},
-    // The destination whose dead ghosts fill Ghost Stories. Node 817 identified by child count --
-    // the manifest lists 23 records and only two books here have 23 chapters -- and by section,
-    // since Ghost Stories sits under The Light and the other candidate, node 847, does not.
-    {0x5FE28198U, 817U},
-}};
-
-std::atomic<std::uint16_t> g_lastGranted{0};
-std::atomic<bool> g_lastItemGranted{false};
+bool ensure_tables_published() noexcept {
+    namespace nodes = build_data::nodes;
+    namespace records = build_data::records;
+    static std::atomic<bool> published{false};
+    if (published.load(std::memory_order_relaxed)) {
+        return true;
+    }
+    const bool haveNodes = nodes::count() != 0 || nodes::load_and_publish();
+    const bool haveRecords = records::count() != 0 || records::load_and_publish();
+    if (haveNodes && haveRecords) {
+        published.store(true, std::memory_order_relaxed);
+        return true;
+    }
+    return false;
+}
 
 } // namespace
 
@@ -73,20 +69,20 @@ const char* grant_outcome_name(GrantOutcome outcome) noexcept {
         return "book_complete";
     case GrantOutcome::refused:
         return "refused";
+    case GrantOutcome::recordNotFound:
+        return "record_not_found";
+    case GrantOutcome::noFlag:
+        return "no_flag";
+    case GrantOutcome::alreadyHeld:
+        return "already_held";
+    case GrantOutcome::notAChapter:
+        return "not_a_chapter";
+    case GrantOutcome::bubbleTableExhausted:
+        return "bubble_table_exhausted";
     }
     return "unknown";
 }
 
-/** @return The presentation node of the book that activity's pickups feed. */
-std::uint16_t book_for_bubble(std::uint32_t bubble) noexcept {
-    for (const BubbleBook& entry : kBubbleBooks) {
-        if (entry.bubble == bubble) {
-            return entry.node;
-        }
-    }
-    return kNoBook;
-}
-
 /** Grants the next chapter of one book that the account does not already hold. */
 GrantOutcome grant_next_chapter(std::uint16_t node) noexcept {
     if (node == kNoBook) {
@@ -95,17 +91,7 @@ GrantOutcome grant_next_chapter(std::uint16_t node) noexcept {
 
     namespace nodes = build_data::nodes;
     namespace records = build_data::records;
-    // On a warm start the package pass is skipped, so the node table is never published and every
-    // book looks empty. The table is kept in its own file for exactly this; publishing it here
-    // costs nothing once it has succeeded.
-    static std::atomic<bool> published{false};
-    if (!published.load(std::memory_order_relaxed)) {
-        const bool haveNodes = nodes::count() != 0 || nodes::load_and_publish();
-        const bool haveRecords = records::count() != 0 || records::load_and_publish();
-        if (haveNodes && haveRecords) {
-            published.store(true, std::memory_order_relaxed);
-        }
-    }
+    (void)ensure_tables_published();
     std::vector<nodes::Definition> rows(nodes::kDefinitionCapacity);
     std::size_t count = 0;
     if (!nodes::snapshot(std::span<nodes::Definition>{rows}, count) || count == 0) {
@@ -162,6 +148,76 @@ GrantOutcome grant_next_chapter(std::uint16_t node) noexcept {
     return sawRecord ? GrantOutcome::noChapters : GrantOutcome::noRecords;
 }
 
+/** Grants one record's completion directly, by the row an sobject's lane 4 names. */
+GrantOutcome grant_record(std::uint16_t definitionIndex) noexcept {
+    namespace records = build_data::records;
+    (void)ensure_tables_published();
+
+    records::Definition record{};
+    if (!records::find(definitionIndex, record)) {
+        return GrantOutcome::recordNotFound;
+    }
+    if (record.completionFlagIndex == records::kUnavailableFlagIndex) {
+        return GrantOutcome::noFlag;
+    }
+    // Same rule as grant_next_chapter: a record naming no lore row is not a chapter. Here it also
+    // means lane 4 did not really name a record -- see GrantOutcome::notAChapter -- so the caller
+    // is told to fall back rather than granting whatever triumph the number happened to land on.
+    if (record.loreRow == records::kUnavailableLoreRow) {
+        return GrantOutcome::notAChapter;
+    }
+    // Same rule as grant_next_chapter: finding lore completes a chapter, claiming it is the
+    // player's own act, so a record already claimed or already offered claimable is left alone
+    // rather than reported as this pickup's doing.
+    if (record_claims::claimed(record.completionFlagIndex)
+        || record_claims::claimable(record.completionFlagIndex)) {
+        return GrantOutcome::alreadyHeld;
+    }
+    if (!record_claims::mark_claimable(record.completionFlagIndex)) {
+        return GrantOutcome::refused;
+    }
+    g_lastGranted.store(definitionIndex, std::memory_order_relaxed);
+    return GrantOutcome::granted;
+}
+
+std::uint16_t legacy_book_for_bubble(std::uint32_t bubble) noexcept {
+    // Thieves' Landing. Its checklist entries are Region Chests, so the Ghost Lore join that built
+    // bubble_record_table.h never produced a bucket for it, but a pickup here does grant a Ghost
+    // Stories chapter -- observed granting record 802 before the table replaced kBubbleBooks.
+    constexpr std::uint32_t kThievesLandingBubble = 0x5FE28198U;
+
+    if (bubble == kThievesLandingBubble) {
+        return kGhostStoriesNode;
+    }
+    return kNoBook;
+}
+
+/**
+ * Grants the first record in one bubble's generated candidate bucket that the account does not
+ * already hold, walking the bucket in table order.
+ */
+GrantOutcome grant_from_bubble_table(std::uint32_t bubble, std::size_t& bucketSize) noexcept {
+    const std::span<const std::uint16_t> rows = bubble_record_table::records_for_bubble(bubble);
+    bucketSize = rows.size();
+    if (rows.empty()) {
+        return GrantOutcome::unknownBook;
+    }
+
+    // Each row is tried through grant_record, which is the one place that already knows how to
+    // check and write a claim -- there is no second mechanism to maintain here. A row this bucket
+    // names but that is already held is exactly the case a later pickup in the same bubble is
+    // meant to skip past, so the walk continues; a row that fails to resolve at all would be a
+    // data problem in the generated table, and is skipped rather than aborting the whole pickup.
+    // Only a claim-store refusal stops the walk outright, the same as grant_next_chapter.
+    for (const std::uint16_t row : rows) {
+        const GrantOutcome outcome = grant_record(row);
+        if (outcome == GrantOutcome::granted || outcome == GrantOutcome::refused) {
+            return outcome;
+        }
+    }
+    return GrantOutcome::bubbleTableExhausted;
+}
+
 /** @return True when the last grant also gave the collectible's item. */
 bool last_item_granted() noexcept {
     return g_lastItemGranted.load(std::memory_order_relaxed);

+ 104 - 6
Sunrise/src/state/lore/lore_grant.h

@@ -1,5 +1,6 @@
 #pragma once
 
+#include <cstddef>
 #include <cstdint>
 
 namespace sunrise::state::lore {
@@ -12,14 +13,60 @@ namespace sunrise::state::lore {
  * while a different object differs only across the position vector. Reproducing Bungie's choice of
  * chapter would mean mapping every object in the world to a reward, from activity and spawn data.
  *
- * So the chapter is chosen here instead. A pickup grants the next chapter of its book that the
- * account does not already hold, which is what a player observes anyway: collect, and the book
- * fills in. The association needed is one book per activity rather than one reward per object.
+ * So the chapter is chosen here instead. A pickup grants the next chapter its bubble has candidates
+ * for that the account does not already hold, which is what a player observes anyway: collect, and
+ * the book fills in. The association needed is bubble -> candidate chapters (bubble_record_table,
+ * generated from the manifest) rather than one reward per object -- except for Confessions, whose
+ * caluseum_experience activity is instanced and so carries no bubble at all; see kConfessionsNode.
  */
 
 /** A node index that names no book. */
 inline constexpr std::uint16_t kNoBook = 0xFFFFU;
 
+/**
+ * The FNV-1a 32-bit offset basis -- the hash of the empty string.
+ *
+ * Every instanced activity reports this value when its bubble field is otherwise unset. It is not
+ * an authored bubble, is confirmed absent from the whole manifest, and must never be looked up in
+ * bubble_record_table (which asserts as much at compile time). A bubble equal to this is the
+ * instanced-activity case: see kConfessionsNode.
+ */
+inline constexpr std::uint32_t kBubbleUnsetSentinel = 0x811C9DC5U;
+
+/**
+ * Presentation node of Confessions, the caluseum_experience book whose vases feed it.
+ *
+ * Confessions has no bubble: caluseum_experience is instanced, so every one of its pickups reports
+ * 0x811C9DC5, the FNV-1a 32-bit offset basis every instanced activity carries when its bubble field
+ * is otherwise unset -- not an authored identifier for this or any other place. It is therefore
+ * never looked up in the generated bubble table (see bubble_record_table.h, which asserts the
+ * sentinel absent); the caller recognises the sentinel directly and grants against this node
+ * instead, the same ordered walk grant_next_chapter always did here. Node 838 confirmed against the
+ * published manifest: nine chapters, Entry I on record 1708 carrying lore hash 0x58C9C088.
+ */
+inline constexpr std::uint16_t kConfessionsNode = 838U;
+
+/**
+ * Presentation node of Ghost Stories. Confirmed against the published manifest: 24 chapters.
+ *
+ * Ghost Stories is granted by Dead Ghosts, which are scattered across the whole game rather than
+ * gathered into one destination, so the generated bubble table does not cover every bubble that
+ * hosts one -- the public map data it is built from simply never recorded them. Bubbles known to
+ * host a pickup but absent from the table fall back to an ordered walk of this node, which is what
+ * the retired kBubbleBooks did for them. It grants the right book and an arbitrary chapter of it,
+ * which beats granting nothing; see legacy_book_for_bubble.
+ */
+inline constexpr std::uint16_t kGhostStoriesNode = 817U;
+
+/**
+ * Book to walk for a bubble the generated table does not cover, or kNoBook when none is known.
+ *
+ * This is deliberately a short hand-maintained list and not a second generated table: it exists
+ * only to stop a pickup in a known-good bubble granting nothing while that bubble is missing from
+ * bubble_record_map.json. Entries should be deleted as the generated table grows to cover them.
+ */
+[[nodiscard]] std::uint16_t legacy_book_for_bubble(std::uint32_t bubble) noexcept;
+
 /** Why one pickup granted nothing. */
 enum class GrantOutcome : std::uint8_t {
     granted,
@@ -39,16 +86,53 @@ enum class GrantOutcome : std::uint8_t {
     bookComplete,
     /** The claim store refused the write. */
     refused,
+    /** The named record row does not exist, or the record table is not published. */
+    recordNotFound,
+    /** The record exists but names no completion flag, so it cannot be claimed. */
+    noFlag,
+    /** The record is already claimed or already marked claimable. */
+    alreadyHeld,
+    /**
+     * The named record is not a lore chapter, so this pickup is not what completes it.
+     *
+     * A type-10 row's lane 4 resolves into the record table for 360 of 807 rows, but only 102 of
+     * those name a record that carries a lore row. The rest land on unrelated triumphs -- Season of
+     * Dawn, Titan Strength, Reward: Jumpship -- because a small u16 falls inside a 2242 row table
+     * often enough to look like an index without being one. Granting those would hand out a
+     * visibly wrong triumph, so they are refused here and left to the bubble table / fallback.
+     */
+    notAChapter,
+    /**
+     * The bubble resolved to a bucket in the generated table, but every candidate row in it is
+     * already claimed or claimable. The caller's cue to fall through to the fallback path rather
+     * than report a silent no-op.
+     */
+    bubbleTableExhausted,
 };
 
 /** @return A short name for the outcome, for logs. */
 [[nodiscard]] const char* grant_outcome_name(GrantOutcome outcome) noexcept;
 
 /**
- * @param bubble Bubble hash the incident carried.
- * @return The presentation node of the book that activity's pickups feed, or kNoBook.
+ * Grants the first record in one bubble's generated candidate bucket that the account does not
+ * already hold, walking the bucket in table order.
+ *
+ * Replaces the old kBubbleBooks heuristic (one book per activity, guessed at) with the generated
+ * bubble_record_table (one set of candidate chapters per bubble, joined from the manifest). Where a
+ * bucket holds several rows, successive pickups in that bubble hand out successive chapters -- this
+ * does not claim to identify the exact physical object, only to walk the same small set a player
+ * would see filling in as they collect.
+ *
+ * @param bubble Bubble hash the incident carried. Never pass kBubbleUnsetSentinel here -- the
+ *        caller must recognise that case itself and use kConfessionsNode with grant_next_chapter
+ *        instead; bubble_record_table asserts the sentinel is not one of its entries.
+ * @param bucketSize Out: number of candidate rows the bubble's bucket held, 0 when the bubble is not
+ *        in the table. For logs.
+ * @return granted; unknownBook when the bubble is not in the generated table; bubbleTableExhausted
+ *         when it is but every candidate is already held; or whatever grant_record returned for the
+ *         row that stopped the walk (recordNotFound, noFlag, notAChapter, refused).
  */
-[[nodiscard]] std::uint16_t book_for_bubble(std::uint32_t bubble) noexcept;
+[[nodiscard]] GrantOutcome grant_from_bubble_table(std::uint32_t bubble, std::size_t& bucketSize) noexcept;
 
 /**
  * Grants the next chapter of one book that the account does not already hold.
@@ -59,6 +143,20 @@ enum class GrantOutcome : std::uint8_t {
  */
 [[nodiscard]] GrantOutcome grant_next_chapter(std::uint16_t node) noexcept;
 
+/**
+ * Grants one record's completion directly, by the row an sobject's lane 4 names.
+ *
+ * Performs the same write grant_next_chapter does -- record_claims::mark_claimable on the record's
+ * completion flag -- but on a row named exactly rather than one found by walking a book's children.
+ * This is the path a type-10 incident takes when its target's lane 4 resolves a record on its own,
+ * which makes guessing at the book unnecessary.
+ * @param definitionIndex Native record row, from an sobject's lane 4 low half.
+ * @return What happened. recordNotFound means the row itself does not resolve -- the caller's cue
+ *         to fall back to the bubble table / instanced case instead of treating this as a completed
+ *         attempt.
+ */
+[[nodiscard]] GrantOutcome grant_record(std::uint16_t definitionIndex) noexcept;
+
 /** @return The record row the last successful grant claimed. Only meaningful after `granted`. */
 [[nodiscard]] std::uint16_t last_granted_record() noexcept;
 

+ 4653 - 0
Sunrise/src/state/record_claims/objective_slot_table.h

@@ -0,0 +1,4653 @@
+// GENERATED FILE -- do not hand-edit.
+//
+// Produced by gen_objective_slots.py from record_objective_slots.json. To change an entry,
+// fix the map data and regenerate:
+//   python3 collectible-triumph-map/gen_objective_slots.py
+#pragma once
+
+#include <array>
+#include <cstdint>
+
+namespace sunrise::state::record_claims::objective_slot_table {
+
+/** One objective's account value-bank slot and the value it must hold to read complete. */
+struct ObjectiveSlot {
+    std::uint16_t slot;
+    std::int32_t completionValue;
+};
+
+/** One record's completion-flag index and the run of objective slots it owns. */
+struct RecordEntry {
+    std::uint16_t flagIndex;
+    std::uint16_t firstObjective;
+    std::uint8_t objectiveCount;
+};
+
+/**
+ * flagIndex -> objective slot(s), joined from record_objective_slots.json.
+ *
+ * A record's objective value(s) live in unlocks::Table::objectiveValues, a bank separate
+ * from the completion-flag bank the flagIndex addresses. Slots were derived as
+ * 2746 + sum(objectiveCount, or 2 if the record has none) over every earlier record row, and
+ * verified against seven measured anchors spanning rows 705..1905. A multi-objective record's
+ * slots are consecutive, referenced here as a run into kObjectives rather than repeated inline.
+ *
+ * 2048 records carry at least one objective; the rest reserve no slot and are not in
+ * this table. completionValue is copied from the source record verbatim, never assumed to be
+ * 1 -- 103 lore records score above 1 (up to 25) and would read permanently
+ * incomplete if under-written.
+ */
+
+inline constexpr std::size_t kObjectiveCount = 2553;
+inline constexpr std::size_t kRecordCount = 2048;
+
+/** Every authored objective slot across every mapped record, in record (flagIndex) order. */
+inline constexpr std::array<ObjectiveSlot, kObjectiveCount> kObjectives{{
+    {2746, 14},
+    {2747, 1},
+    {2748, 1},
+    {2749, 1},
+    {2750, 1},
+    {2751, 1},
+    {2752, 1},
+    {2753, 1},
+    {2754, 1},
+    {2755, 1},
+    {2756, 1},
+    {2757, 1},
+    {2758, 1},
+    {2759, 1},
+    {2760, 1},
+    {2761, 13},
+    {2762, 1},
+    {2763, 1},
+    {2764, 1},
+    {2765, 1},
+    {2766, 1},
+    {2767, 1},
+    {2768, 1},
+    {2769, 1},
+    {2770, 1},
+    {2771, 1},
+    {2772, 1},
+    {2773, 1},
+    {2774, 1},
+    {2775, 10},
+    {2776, 1},
+    {2777, 1},
+    {2778, 1},
+    {2779, 1},
+    {2780, 1},
+    {2781, 1},
+    {2782, 1},
+    {2783, 1},
+    {2784, 1},
+    {2785, 1},
+    {2786, 5},
+    {2787, 2},
+    {2788, 5},
+    {2789, 5},
+    {2790, 9},
+    {2791, 3},
+    {2792, 3},
+    {2793, 3},
+    {2794, 5},
+    {2795, 11},
+    {2796, 1},
+    {2797, 2},
+    {2798, 3},
+    {2799, 4},
+    {2800, 5},
+    {2801, 6},
+    {2802, 7},
+    {2803, 8},
+    {2804, 9},
+    {2805, 10},
+    {2806, 11},
+    {2807, 10},
+    {2808, 1},
+    {2809, 1},
+    {2810, 1},
+    {2811, 1},
+    {2812, 1},
+    {2813, 1},
+    {2814, 1},
+    {2815, 1},
+    {2816, 1},
+    {2817, 1},
+    {2818, 20},
+    {2819, 1},
+    {2820, 1},
+    {2821, 1},
+    {2822, 1},
+    {2823, 1},
+    {2824, 1},
+    {2825, 1},
+    {2826, 1},
+    {2827, 1},
+    {2828, 1},
+    {2829, 1},
+    {2830, 1},
+    {2831, 1},
+    {2832, 1},
+    {2833, 1},
+    {2834, 1},
+    {2835, 1},
+    {2836, 1},
+    {2837, 1},
+    {2838, 1},
+    {2839, 13},
+    {2840, 1},
+    {2841, 1},
+    {2842, 1},
+    {2843, 1},
+    {2844, 1},
+    {2845, 1},
+    {2846, 1},
+    {2847, 1},
+    {2848, 1},
+    {2849, 1},
+    {2850, 1},
+    {2851, 1},
+    {2852, 1},
+    {2853, 10},
+    {2854, 1},
+    {2855, 1},
+    {2856, 1},
+    {2857, 1},
+    {2858, 1},
+    {2859, 1},
+    {2860, 1},
+    {2861, 1},
+    {2862, 1},
+    {2863, 1},
+    {2864, 11},
+    {2865, 1},
+    {2866, 1},
+    {2867, 1},
+    {2868, 1},
+    {2869, 1},
+    {2870, 1},
+    {2871, 1},
+    {2872, 1},
+    {2873, 1},
+    {2874, 1},
+    {2875, 1},
+    {2876, 10},
+    {2877, 1},
+    {2878, 1},
+    {2879, 1},
+    {2880, 1},
+    {2881, 1},
+    {2882, 1},
+    {2883, 1},
+    {2884, 1},
+    {2885, 1},
+    {2886, 1},
+    {2887, 10},
+    {2888, 1},
+    {2889, 1},
+    {2890, 1},
+    {2891, 1},
+    {2892, 1},
+    {2893, 1},
+    {2894, 1},
+    {2895, 1},
+    {2896, 1},
+    {2897, 1},
+    {2898, 10},
+    {2899, 1},
+    {2900, 1},
+    {2901, 1},
+    {2902, 1},
+    {2903, 1},
+    {2904, 1},
+    {2905, 1},
+    {2906, 1},
+    {2907, 1},
+    {2908, 1},
+    {2909, 10},
+    {2910, 1},
+    {2911, 1},
+    {2912, 1},
+    {2913, 1},
+    {2914, 1},
+    {2915, 1},
+    {2916, 1},
+    {2917, 1},
+    {2918, 1},
+    {2919, 1},
+    {2920, 50},
+    {2921, 50},
+    {2922, 1},
+    {2923, 1},
+    {2926, 50},
+    {2927, 50},
+    {2928, 50},
+    {2929, 1},
+    {2930, 4},
+    {2931, 1},
+    {2932, 50},
+    {2933, 50},
+    {2934, 7},
+    {2935, 1},
+    {2936, 18},
+    {2937, 1},
+    {2938, 1},
+    {2939, 1},
+    {2940, 1},
+    {2941, 1},
+    {2942, 1},
+    {2943, 1},
+    {2944, 1},
+    {2945, 1},
+    {2946, 1},
+    {2947, 1},
+    {2948, 1},
+    {2949, 1},
+    {2950, 1},
+    {2951, 1},
+    {2952, 1},
+    {2953, 1},
+    {2954, 1},
+    {2955, 1},
+    {2956, 1},
+    {2957, 1},
+    {2958, 250},
+    {2959, 400},
+    {2960, 1},
+    {2961, 1},
+    {2962, 1},
+    {2963, 1},
+    {2964, 1},
+    {2965, 1},
+    {2966, 1},
+    {2967, 1},
+    {2968, 1},
+    {2969, 1},
+    {2970, 1},
+    {2971, 1},
+    {2972, 1},
+    {2973, 1},
+    {2974, 1},
+    {2975, 1},
+    {2976, 1},
+    {2977, 1},
+    {2978, 22},
+    {2979, 11},
+    {2982, 10},
+    {2983, 10},
+    {2984, 10},
+    {2985, 1},
+    {2986, 16},
+    {2987, 1},
+    {2988, 1},
+    {2989, 1},
+    {2990, 1},
+    {2991, 1},
+    {2992, 1},
+    {2993, 17},
+    {2994, 23},
+    {2995, 1},
+    {2996, 1},
+    {2997, 1},
+    {2998, 1},
+    {2999, 1},
+    {3000, 1},
+    {3001, 1},
+    {3002, 1},
+    {3003, 29},
+    {3004, 29},
+    {3005, 29},
+    {3006, 1},
+    {3007, 15},
+    {3008, 15},
+    {3009, 15},
+    {3010, 1},
+    {3011, 18},
+    {3012, 1},
+    {3013, 18},
+    {3014, 18},
+    {3015, 1},
+    {3016, 1},
+    {3017, 1},
+    {3018, 17},
+    {3019, 1},
+    {3020, 13},
+    {3021, 2},
+    {3022, 500},
+    {3023, 2},
+    {3024, 500},
+    {3025, 2},
+    {3026, 500},
+    {3027, 2},
+    {3028, 300},
+    {3029, 2},
+    {3030, 500},
+    {3031, 2},
+    {3032, 300},
+    {3033, 2},
+    {3034, 500},
+    {3035, 2},
+    {3036, 5},
+    {3037, 2},
+    {3038, 1000},
+    {3039, 1000},
+    {3040, 2000},
+    {3041, 1},
+    {3042, 500},
+    {3043, 2},
+    {3044, 50},
+    {3045, 350},
+    {3046, 2},
+    {3047, 250},
+    {3048, 2},
+    {3049, 500},
+    {3050, 500},
+    {3051, 2},
+    {3052, 250},
+    {3053, 5},
+    {3054, 2},
+    {3055, 1000},
+    {3056, 2},
+    {3057, 200},
+    {3058, 2},
+    {3059, 500},
+    {3060, 2},
+    {3061, 250},
+    {3062, 2},
+    {3063, 500},
+    {3064, 2},
+    {3065, 300},
+    {3066, 500},
+    {3067, 500},
+    {3068, 500},
+    {3069, 500},
+    {3070, 500},
+    {3071, 700},
+    {3072, 400},
+    {3073, 250},
+    {3074, 400},
+    {3075, 2},
+    {3076, 500},
+    {3077, 1000},
+    {3078, 2500},
+    {3079, 2},
+    {3080, 2},
+    {3081, 500},
+    {3082, 1000},
+    {3083, 2},
+    {3084, 500},
+    {3085, 1000},
+    {3086, 2},
+    {3087, 1000},
+    {3088, 2},
+    {3089, 150},
+    {3090, 150},
+    {3091, 150},
+    {3092, 2},
+    {3093, 500},
+    {3094, 50},
+    {3095, 2},
+    {3096, 1000},
+    {3097, 2},
+    {3098, 500},
+    {3099, 2},
+    {3100, 1},
+    {3101, 1},
+    {3102, 2},
+    {3103, 500},
+    {3104, 50},
+    {3105, 500},
+    {3106, 700},
+    {3107, 700},
+    {3108, 700},
+    {3109, 700},
+    {3110, 400},
+    {3111, 5000},
+    {3112, 400},
+    {3113, 1},
+    {3114, 23},
+    {3115, 25},
+    {3116, 1},
+    {3117, 1},
+    {3118, 1},
+    {3119, 10},
+    {3120, 16},
+    {3121, 14},
+    {3122, 28},
+    {3123, 7},
+    {3124, 5},
+    {3125, 6},
+    {3126, 1},
+    {3127, 1},
+    {3128, 6},
+    {3129, 6},
+    {3130, 28},
+    {3131, 21},
+    {3132, 5},
+    {3133, 1},
+    {3134, 1},
+    {3135, 6},
+    {3136, 1},
+    {3137, 1},
+    {3138, 1},
+    {3139, 1},
+    {3140, 1},
+    {3141, 1},
+    {3142, 1},
+    {3143, 1},
+    {3144, 1},
+    {3145, 1},
+    {3146, 1},
+    {3147, 1},
+    {3148, 1},
+    {3149, 1},
+    {3150, 1},
+    {3151, 1},
+    {3152, 1},
+    {3153, 1},
+    {3154, 1},
+    {3155, 1},
+    {3156, 1},
+    {3157, 1},
+    {3158, 1},
+    {3159, 1},
+    {3160, 1},
+    {3161, 1},
+    {3162, 1},
+    {3165, 1},
+    {3166, 1},
+    {3167, 1},
+    {3168, 1},
+    {3169, 1},
+    {3170, 1},
+    {3171, 1},
+    {3208, 1},
+    {3209, 1},
+    {3210, 1},
+    {3211, 1},
+    {3212, 1},
+    {3217, 1},
+    {3218, 1},
+    {3219, 1},
+    {3220, 1},
+    {3221, 1},
+    {3222, 1},
+    {3259, 1},
+    {3260, 1},
+    {3261, 1},
+    {3262, 1},
+    {3263, 1},
+    {3268, 1},
+    {3269, 1},
+    {3270, 1},
+    {3271, 1},
+    {3272, 1},
+    {3273, 1},
+    {3310, 1},
+    {3311, 1},
+    {3316, 5},
+    {3317, 1},
+    {3318, 1},
+    {3319, 1},
+    {3320, 1},
+    {3321, 1},
+    {3322, 1},
+    {3323, 1},
+    {3324, 1},
+    {3325, 1},
+    {3326, 1},
+    {3329, 1},
+    {3332, 5},
+    {3333, 1},
+    {3338, 1},
+    {3339, 1},
+    {3342, 1},
+    {3343, 1},
+    {3348, 1},
+    {3349, 1},
+    {3350, 1},
+    {3351, 1},
+    {3356, 1},
+    {3359, 1},
+    {3366, 1},
+    {3367, 1},
+    {3368, 5},
+    {3371, 1},
+    {3372, 1},
+    {3375, 5},
+    {3376, 1},
+    {3377, 1},
+    {3378, 1},
+    {3379, 1},
+    {3380, 1},
+    {3381, 1},
+    {3386, 5},
+    {3387, 1},
+    {3388, 1},
+    {3389, 1},
+    {3390, 1},
+    {3391, 1},
+    {3392, 1},
+    {3393, 1},
+    {3394, 1},
+    {3395, 1},
+    {3396, 1},
+    {3397, 1},
+    {3398, 1},
+    {3399, 1},
+    {3400, 1},
+    {3401, 1},
+    {3402, 1},
+    {3403, 1},
+    {3404, 1},
+    {3405, 1},
+    {3414, 1},
+    {3415, 5},
+    {3416, 1},
+    {3417, 1},
+    {3418, 1},
+    {3419, 1},
+    {3420, 1},
+    {3421, 1},
+    {3424, 5},
+    {3425, 1},
+    {3428, 1},
+    {3429, 1},
+    {3430, 1},
+    {3431, 3},
+    {3434, 1},
+    {3435, 1},
+    {3436, 1},
+    {3437, 1},
+    {3442, 2},
+    {3447, 1},
+    {3456, 3},
+    {3457, 1},
+    {3458, 1},
+    {3459, 1},
+    {3460, 1},
+    {3461, 1},
+    {3462, 1},
+    {3463, 1},
+    {3464, 1},
+    {3465, 1},
+    {3466, 1},
+    {3467, 3},
+    {3468, 1},
+    {3471, 1},
+    {3472, 1},
+    {3473, 1},
+    {3474, 1},
+    {3475, 1},
+    {3476, 1},
+    {3477, 6},
+    {3478, 1},
+    {3479, 1},
+    {3480, 1},
+    {3481, 7},
+    {3482, 1},
+    {3483, 1},
+    {3484, 1},
+    {3485, 1},
+    {3486, 1},
+    {3487, 1},
+    {3488, 1},
+    {3489, 1},
+    {3490, 1},
+    {3491, 1},
+    {3492, 1},
+    {3493, 1},
+    {3494, 1},
+    {3495, 1},
+    {3496, 1},
+    {3497, 1},
+    {3498, 1},
+    {3499, 1},
+    {3500, 16},
+    {3503, 40},
+    {3508, 1},
+    {3509, 1},
+    {3510, 1},
+    {3511, 1},
+    {3512, 1},
+    {3513, 1},
+    {3514, 1},
+    {3515, 1},
+    {3516, 1},
+    {3517, 8},
+    {3518, 1},
+    {3519, 1},
+    {3520, 1},
+    {3521, 1},
+    {3522, 1},
+    {3523, 1},
+    {3524, 1},
+    {3529, 1},
+    {3530, 1},
+    {3531, 1},
+    {3532, 1},
+    {3533, 1},
+    {3544, 1},
+    {3545, 1},
+    {3546, 1},
+    {3547, 1},
+    {3548, 1},
+    {3549, 1},
+    {3550, 1},
+    {3551, 1},
+    {3552, 1},
+    {3553, 1},
+    {3554, 7},
+    {3555, 1},
+    {3556, 9},
+    {3557, 1},
+    {3558, 1},
+    {3559, 1},
+    {3560, 1},
+    {3561, 40},
+    {3562, 100},
+    {3563, 1},
+    {3564, 5},
+    {3565, 5},
+    {3566, 5},
+    {3573, 1000},
+    {3580, 1},
+    {3581, 1},
+    {3582, 1},
+    {3583, 5},
+    {3588, 1},
+    {3589, 1},
+    {3590, 100},
+    {3591, 1},
+    {3594, 1},
+    {3597, 10},
+    {3600, 1},
+    {3601, 1},
+    {3602, 1},
+    {3603, 1},
+    {3604, 1},
+    {3611, 100},
+    {3612, 100},
+    {3613, 100},
+    {3614, 100},
+    {3615, 100},
+    {3616, 100},
+    {3617, 100},
+    {3618, 100},
+    {3619, 100},
+    {3620, 100},
+    {3621, 100},
+    {3622, 100},
+    {3623, 100},
+    {3624, 100},
+    {3625, 1},
+    {3634, 1},
+    {3635, 1},
+    {3636, 1},
+    {3637, 1},
+    {3638, 1},
+    {3639, 1},
+    {3640, 1},
+    {3641, 1},
+    {3642, 1},
+    {3643, 1},
+    {3646, 1},
+    {3647, 1},
+    {3648, 1},
+    {3649, 1},
+    {3650, 1},
+    {3651, 1},
+    {3652, 1},
+    {3653, 1},
+    {3654, 100},
+    {3655, 2},
+    {3656, 1000},
+    {3657, 1},
+    {3658, 1},
+    {3659, 1},
+    {3660, 1},
+    {3661, 1},
+    {3662, 1},
+    {3663, 3},
+    {3664, 3},
+    {3665, 3},
+    {3666, 5},
+    {3667, 3},
+    {3668, 5},
+    {3669, 5},
+    {3670, 10},
+    {3671, 3},
+    {3672, 1},
+    {3673, 10},
+    {3674, 3},
+    {3675, 1},
+    {3676, 3},
+    {3677, 5},
+    {3678, 5},
+    {3679, 10},
+    {3680, 3},
+    {3681, 5},
+    {3682, 3},
+    {3683, 10},
+    {3684, 5},
+    {3685, 3},
+    {3692, 5},
+    {3693, 1},
+    {3694, 1},
+    {3695, 1},
+    {3696, 1},
+    {3697, 1},
+    {3698, 1},
+    {3699, 1},
+    {3700, 1},
+    {3701, 1},
+    {3702, 1},
+    {3703, 1},
+    {3704, 1},
+    {3705, 1},
+    {3706, 1},
+    {3707, 1},
+    {3708, 1},
+    {3709, 1},
+    {3710, 20},
+    {3711, 1},
+    {3712, 1},
+    {3713, 1},
+    {3714, 1},
+    {3715, 1},
+    {3716, 1},
+    {3717, 1},
+    {3718, 1},
+    {3719, 1},
+    {3720, 1},
+    {3721, 1},
+    {3722, 1},
+    {3723, 1},
+    {3724, 1},
+    {3725, 1},
+    {3726, 1},
+    {3727, 1},
+    {3728, 1},
+    {3729, 1},
+    {3730, 1},
+    {3731, 25},
+    {3732, 1},
+    {3733, 1},
+    {3734, 1},
+    {3735, 1},
+    {3736, 1},
+    {3737, 1},
+    {3738, 1},
+    {3739, 1},
+    {3740, 1},
+    {3741, 1},
+    {3742, 1},
+    {3743, 1},
+    {3744, 1},
+    {3745, 1},
+    {3746, 1},
+    {3747, 1},
+    {3748, 25},
+    {3749, 25},
+    {3750, 23},
+    {3751, 1},
+    {3752, 1},
+    {3753, 1},
+    {3754, 1},
+    {3755, 1},
+    {3756, 1},
+    {3757, 1},
+    {3758, 1},
+    {3759, 1},
+    {3760, 1},
+    {3761, 1},
+    {3762, 1},
+    {3763, 1},
+    {3764, 1},
+    {3765, 1},
+    {3766, 1},
+    {3767, 1},
+    {3768, 1},
+    {3769, 1},
+    {3770, 1},
+    {3771, 1},
+    {3772, 1},
+    {3773, 1},
+    {3774, 23},
+    {3775, 1},
+    {3776, 1},
+    {3777, 1},
+    {3778, 1},
+    {3779, 1},
+    {3780, 1},
+    {3781, 1},
+    {3782, 1},
+    {3783, 1},
+    {3784, 1},
+    {3785, 1},
+    {3786, 1},
+    {3787, 1},
+    {3788, 1},
+    {3789, 1},
+    {3790, 1},
+    {3791, 1},
+    {3792, 1},
+    {3793, 1},
+    {3794, 1},
+    {3795, 1},
+    {3796, 1},
+    {3797, 1},
+    {3798, 1},
+    {3799, 1},
+    {3800, 1},
+    {3801, 1},
+    {3802, 1},
+    {3803, 1},
+    {3804, 1},
+    {3805, 1},
+    {3806, 1},
+    {3807, 1},
+    {3808, 1},
+    {3809, 1},
+    {3810, 1},
+    {3811, 1},
+    {3812, 1},
+    {3813, 15},
+    {3814, 1},
+    {3815, 1},
+    {3816, 1},
+    {3817, 1},
+    {3818, 1},
+    {3819, 1},
+    {3820, 1},
+    {3821, 1},
+    {3822, 1},
+    {3823, 1},
+    {3824, 1},
+    {3825, 1},
+    {3826, 1},
+    {3827, 1},
+    {3828, 1},
+    {3829, 1},
+    {3830, 1},
+    {3831, 1},
+    {3832, 1},
+    {3833, 1},
+    {3834, 1},
+    {3835, 1},
+    {3836, 1},
+    {3837, 1},
+    {3842, 10},
+    {3843, 5},
+    {3844, 5},
+    {3845, 5},
+    {3848, 1},
+    {3849, 250},
+    {3850, 3},
+    {3851, 250},
+    {3852, 7},
+    {3853, 250},
+    {3854, 7},
+    {3855, 250},
+    {3856, 7},
+    {3857, 250},
+    {3858, 7},
+    {3859, 250},
+    {3860, 7},
+    {3861, 1},
+    {3862, 100},
+    {3863, 5},
+    {3864, 1},
+    {3865, 1},
+    {3866, 5},
+    {3867, 3},
+    {3868, 5},
+    {3869, 3},
+    {3870, 5},
+    {3871, 100},
+    {3872, 1},
+    {3873, 1},
+    {3874, 750},
+    {3875, 75},
+    {3876, 25},
+    {3877, 1},
+    {3878, 50},
+    {3879, 3},
+    {3880, 5},
+    {3881, 1},
+    {3882, 50},
+    {3883, 3},
+    {3884, 5},
+    {3885, 1},
+    {3886, 50},
+    {3887, 3},
+    {3888, 5},
+    {3889, 1},
+    {3890, 50},
+    {3891, 3},
+    {3892, 5},
+    {3893, 1},
+    {3894, 50},
+    {3895, 3},
+    {3896, 5},
+    {3897, 1},
+    {3898, 50},
+    {3899, 3},
+    {3900, 5},
+    {3901, 1},
+    {3902, 50},
+    {3903, 3},
+    {3904, 5},
+    {3905, 1},
+    {3906, 50},
+    {3907, 3},
+    {3908, 1},
+    {3909, 50},
+    {3910, 3},
+    {3911, 1},
+    {3912, 3},
+    {3913, 1},
+    {3914, 3},
+    {3915, 1},
+    {3916, 5},
+    {3917, 1},
+    {3918, 1},
+    {3919, 10},
+    {3920, 5},
+    {3921, 3},
+    {3922, 5},
+    {3923, 5},
+    {3924, 5},
+    {3925, 1},
+    {3926, 1},
+    {3927, 5},
+    {3928, 1},
+    {3929, 1},
+    {3930, 5},
+    {3931, 1},
+    {3932, 1},
+    {3933, 5},
+    {3934, 1},
+    {3935, 1},
+    {3936, 5},
+    {3937, 1},
+    {3938, 1},
+    {3939, 5},
+    {3940, 1},
+    {3941, 1},
+    {3942, 5},
+    {3943, 1},
+    {3944, 1},
+    {3945, 5},
+    {3946, 1},
+    {3947, 1},
+    {3948, 5},
+    {3949, 1},
+    {3950, 1},
+    {3951, 15},
+    {3958, 10},
+    {3959, 10},
+    {3960, 10},
+    {3971, 1},
+    {3972, 2500},
+    {3973, 500},
+    {3974, 15},
+    {3975, 15},
+    {3976, 15},
+    {3977, 1},
+    {3978, 5},
+    {3979, 50},
+    {3980, 15},
+    {3981, 1},
+    {3982, 5},
+    {3983, 50},
+    {3984, 15},
+    {3985, 1},
+    {3986, 5},
+    {3987, 50},
+    {3988, 15},
+    {3989, 1},
+    {3990, 5},
+    {3991, 50},
+    {3992, 15},
+    {3993, 1},
+    {3994, 5},
+    {3995, 50},
+    {3996, 15},
+    {3997, 10},
+    {3998, 1},
+    {3999, 50},
+    {4000, 500},
+    {4001, 100},
+    {4002, 100},
+    {4003, 100},
+    {4004, 250},
+    {4005, 25},
+    {4006, 25},
+    {4007, 25},
+    {4008, 50},
+    {4009, 500},
+    {4010, 100},
+    {4011, 100},
+    {4012, 100},
+    {4013, 250},
+    {4014, 25},
+    {4015, 25},
+    {4016, 25},
+    {4017, 50},
+    {4018, 500},
+    {4019, 100},
+    {4020, 100},
+    {4021, 100},
+    {4022, 250},
+    {4023, 25},
+    {4024, 25},
+    {4025, 25},
+    {4026, 1},
+    {4027, 50},
+    {4028, 250},
+    {4029, 50},
+    {4030, 1},
+    {4031, 50},
+    {4032, 250},
+    {4033, 50},
+    {4034, 1},
+    {4035, 50},
+    {4036, 250},
+    {4037, 50},
+    {4038, 1},
+    {4039, 50},
+    {4040, 250},
+    {4041, 100},
+    {4042, 50},
+    {4043, 1},
+    {4044, 50},
+    {4045, 250},
+    {4046, 100},
+    {4047, 50},
+    {4048, 1},
+    {4049, 50},
+    {4050, 250},
+    {4051, 100},
+    {4052, 50},
+    {4053, 50},
+    {4054, 25},
+    {4055, 100},
+    {4056, 100},
+    {4057, 100},
+    {4058, 1},
+    {4059, 100},
+    {4060, 1},
+    {4061, 100},
+    {4062, 1},
+    {4063, 100},
+    {4064, 25},
+    {4065, 1},
+    {4066, 100},
+    {4067, 25},
+    {4068, 1},
+    {4069, 100},
+    {4070, 1},
+    {4071, 100},
+    {4072, 1},
+    {4073, 100},
+    {4074, 1},
+    {4075, 100},
+    {4076, 25},
+    {4077, 1},
+    {4078, 100},
+    {4079, 25},
+    {4080, 1},
+    {4081, 50},
+    {4082, 50},
+    {4083, 50},
+    {4084, 1},
+    {4085, 100},
+    {4086, 100},
+    {4087, 1},
+    {4088, 100},
+    {4089, 1},
+    {4090, 50},
+    {4091, 1},
+    {4092, 100},
+    {4093, 1},
+    {4094, 15},
+    {4095, 25},
+    {4096, 15},
+    {4097, 10},
+    {4098, 25},
+    {4099, 15},
+    {4100, 100},
+    {4101, 50},
+    {4102, 20},
+    {4103, 25},
+    {4104, 100},
+    {4105, 250},
+    {4106, 50},
+    {4107, 50},
+    {4108, 15},
+    {4109, 10},
+    {4110, 5},
+    {4111, 5},
+    {4112, 15},
+    {4113, 25},
+    {4114, 100},
+    {4115, 100},
+    {4116, 15},
+    {4117, 15},
+    {4118, 75},
+    {4119, 25},
+    {4120, 15},
+    {4121, 25},
+    {4122, 50},
+    {4123, 100},
+    {4124, 25},
+    {4125, 25},
+    {4126, 25},
+    {4127, 25},
+    {4128, 100},
+    {4129, 25},
+    {4130, 15},
+    {4131, 15},
+    {4132, 100},
+    {4133, 25},
+    {4134, 10},
+    {4135, 5},
+    {4136, 20},
+    {4137, 100},
+    {4138, 50},
+    {4139, 25},
+    {4140, 25},
+    {4141, 50},
+    {4142, 25},
+    {4143, 15},
+    {4144, 50},
+    {4145, 5},
+    {4146, 10},
+    {4153, 25},
+    {4154, 1},
+    {4155, 1},
+    {4156, 1},
+    {4157, 1},
+    {4158, 1},
+    {4159, 1},
+    {4160, 1},
+    {4161, 1},
+    {4162, 1},
+    {4163, 1},
+    {4164, 1},
+    {4165, 1},
+    {4166, 1},
+    {4167, 1},
+    {4168, 1},
+    {4169, 1},
+    {4170, 1},
+    {4171, 1},
+    {4172, 1},
+    {4173, 1},
+    {4174, 1},
+    {4175, 1},
+    {4176, 1},
+    {4177, 1},
+    {4178, 1},
+    {4179, 1},
+    {4180, 1},
+    {4181, 1},
+    {4182, 1},
+    {4183, 1},
+    {4184, 1},
+    {4185, 1},
+    {4186, 1},
+    {4187, 1},
+    {4188, 1},
+    {4189, 1},
+    {4190, 1},
+    {4191, 1},
+    {4192, 1},
+    {4193, 1},
+    {4194, 1},
+    {4195, 1},
+    {4196, 1},
+    {4197, 1},
+    {4198, 1},
+    {4199, 1},
+    {4200, 1},
+    {4201, 1},
+    {4202, 1},
+    {4203, 1},
+    {4204, 1},
+    {4205, 1},
+    {4206, 1},
+    {4207, 1},
+    {4208, 1},
+    {4209, 1},
+    {4210, 1},
+    {4211, 1},
+    {4212, 1},
+    {4213, 1},
+    {4214, 1},
+    {4215, 1},
+    {4216, 1},
+    {4217, 1},
+    {4218, 1},
+    {4219, 1},
+    {4220, 1},
+    {4221, 1},
+    {4222, 1},
+    {4223, 1},
+    {4224, 1},
+    {4225, 1},
+    {4226, 1},
+    {4227, 1},
+    {4228, 1},
+    {4229, 1},
+    {4230, 1},
+    {4231, 1},
+    {4232, 1},
+    {4233, 1},
+    {4234, 1},
+    {4235, 1},
+    {4236, 1},
+    {4237, 1},
+    {4238, 1},
+    {4239, 1},
+    {4240, 1},
+    {4241, 1},
+    {4242, 1},
+    {4243, 1},
+    {4244, 1},
+    {4245, 1},
+    {4246, 1},
+    {4247, 1},
+    {4248, 1},
+    {4249, 1},
+    {4250, 1},
+    {4251, 1},
+    {4252, 1},
+    {4253, 1},
+    {4254, 1},
+    {4255, 1},
+    {4256, 1},
+    {4257, 1},
+    {4258, 1},
+    {4259, 1},
+    {4260, 1},
+    {4261, 1},
+    {4262, 1},
+    {4263, 1},
+    {4264, 1},
+    {4265, 1},
+    {4266, 1},
+    {4267, 1},
+    {4268, 1},
+    {4269, 1},
+    {4270, 1},
+    {4271, 1},
+    {4272, 1},
+    {4273, 1},
+    {4274, 1},
+    {4275, 1},
+    {4276, 1},
+    {4277, 1},
+    {4278, 1},
+    {4279, 1},
+    {4280, 1},
+    {4281, 1},
+    {4282, 1},
+    {4283, 1},
+    {4284, 1},
+    {4285, 1},
+    {4286, 1},
+    {4287, 1},
+    {4288, 1},
+    {4289, 1},
+    {4290, 1},
+    {4291, 1},
+    {4292, 1},
+    {4293, 1},
+    {4294, 1},
+    {4295, 1},
+    {4296, 1},
+    {4297, 1},
+    {4298, 1},
+    {4299, 1},
+    {4300, 1},
+    {4301, 1},
+    {4302, 1},
+    {4303, 1},
+    {4304, 1},
+    {4305, 1},
+    {4306, 1},
+    {4307, 1},
+    {4308, 1},
+    {4309, 1},
+    {4310, 1},
+    {4311, 1},
+    {4312, 1},
+    {4313, 1},
+    {4314, 1},
+    {4315, 1},
+    {4316, 1},
+    {4317, 1},
+    {4318, 1},
+    {4319, 1},
+    {4320, 1},
+    {4321, 1},
+    {4322, 1},
+    {4323, 1},
+    {4324, 1},
+    {4325, 1},
+    {4326, 1},
+    {4327, 1},
+    {4328, 1},
+    {4329, 1},
+    {4330, 1},
+    {4331, 1},
+    {4332, 1},
+    {4333, 1},
+    {4334, 1},
+    {4335, 1},
+    {4336, 1},
+    {4337, 1},
+    {4338, 1},
+    {4339, 1},
+    {4340, 1},
+    {4341, 1},
+    {4342, 1},
+    {4343, 1},
+    {4344, 1},
+    {4345, 1},
+    {4346, 1},
+    {4347, 1},
+    {4348, 1},
+    {4349, 1},
+    {4350, 1},
+    {4351, 1},
+    {4352, 1},
+    {4353, 1},
+    {4354, 1},
+    {4355, 1},
+    {4356, 1},
+    {4357, 1},
+    {4358, 1},
+    {4359, 1},
+    {4360, 1},
+    {4361, 1},
+    {4362, 1},
+    {4363, 1},
+    {4364, 1},
+    {4365, 1},
+    {4366, 1},
+    {4367, 1},
+    {4368, 1},
+    {4369, 1},
+    {4370, 1},
+    {4371, 1},
+    {4372, 1},
+    {4373, 1},
+    {4374, 1},
+    {4375, 1},
+    {4376, 1},
+    {4377, 1},
+    {4378, 10},
+    {4379, 10},
+    {4380, 5},
+    {4381, 1},
+    {4382, 1},
+    {4383, 1},
+    {4384, 1},
+    {4385, 1},
+    {4386, 1},
+    {4387, 1},
+    {4388, 1},
+    {4389, 1},
+    {4390, 1},
+    {4391, 1},
+    {4392, 1},
+    {4393, 1},
+    {4394, 1},
+    {4395, 1},
+    {4396, 1},
+    {4397, 1},
+    {4398, 1},
+    {4399, 1},
+    {4400, 1},
+    {4401, 1},
+    {4402, 1},
+    {4403, 1},
+    {4404, 1},
+    {4405, 1},
+    {4406, 1},
+    {4407, 1},
+    {4408, 1},
+    {4409, 1},
+    {4410, 1},
+    {4411, 1},
+    {4412, 1},
+    {4413, 1},
+    {4414, 1},
+    {4415, 1},
+    {4416, 1},
+    {4417, 1000},
+    {4418, 500},
+    {4419, 75},
+    {4420, 40},
+    {4421, 2000},
+    {4422, 7},
+    {4423, 25},
+    {4434, 1},
+    {4435, 1},
+    {4436, 1},
+    {4437, 1},
+    {4438, 1},
+    {4439, 1},
+    {4440, 1},
+    {4441, 1},
+    {4442, 1},
+    {4443, 1},
+    {4444, 1},
+    {4445, 1},
+    {4446, 1},
+    {4447, 1},
+    {4448, 1},
+    {4449, 1},
+    {4450, 1},
+    {4451, 1},
+    {4452, 1},
+    {4453, 1},
+    {4454, 1},
+    {4455, 1},
+    {4456, 1},
+    {4457, 1},
+    {4458, 1},
+    {4459, 1},
+    {4460, 1},
+    {4461, 1},
+    {4462, 1},
+    {4463, 1},
+    {4464, 1},
+    {4465, 1},
+    {4466, 1},
+    {4467, 1},
+    {4468, 1},
+    {4469, 1},
+    {4470, 1},
+    {4471, 1},
+    {4472, 1},
+    {4473, 1},
+    {4474, 1},
+    {4475, 1},
+    {4476, 1},
+    {4477, 1},
+    {4478, 1},
+    {4479, 1},
+    {4480, 1},
+    {4481, 1},
+    {4482, 1},
+    {4483, 1},
+    {4484, 1},
+    {4485, 1},
+    {4486, 1},
+    {4487, 1},
+    {4488, 1},
+    {4489, 1},
+    {4490, 1},
+    {4491, 1},
+    {4492, 1},
+    {4493, 1},
+    {4494, 1},
+    {4495, 1},
+    {4496, 1},
+    {4497, 1},
+    {4498, 1},
+    {4499, 1},
+    {4500, 1},
+    {4501, 1},
+    {4502, 1},
+    {4503, 1},
+    {4504, 1},
+    {4505, 1},
+    {4506, 1},
+    {4507, 1},
+    {4508, 1},
+    {4509, 1},
+    {4510, 1},
+    {4511, 1},
+    {4512, 1},
+    {4513, 1},
+    {4514, 1},
+    {4515, 1},
+    {4516, 1},
+    {4517, 1},
+    {4518, 1},
+    {4519, 1},
+    {4520, 1},
+    {4521, 1},
+    {4522, 1},
+    {4523, 1},
+    {4524, 1},
+    {4525, 1},
+    {4536, 12},
+    {4565, 1},
+    {4566, 1},
+    {4567, 1},
+    {4568, 1},
+    {4569, 1},
+    {4570, 1},
+    {4571, 1},
+    {4572, 1},
+    {4573, 1},
+    {4574, 1},
+    {4575, 1},
+    {4576, 1},
+    {4577, 1},
+    {4578, 1},
+    {4579, 1},
+    {4580, 1},
+    {4581, 1},
+    {4582, 1},
+    {4587, 1},
+    {4588, 1},
+    {4589, 1},
+    {4590, 5},
+    {4591, 1},
+    {4592, 1},
+    {4593, 1},
+    {4594, 1},
+    {4595, 1},
+    {4596, 1},
+    {4597, 1},
+    {4598, 1},
+    {4599, 1},
+    {4600, 1},
+    {4601, 1},
+    {4602, 1},
+    {4603, 1},
+    {4604, 1},
+    {4605, 1},
+    {4606, 1},
+    {4607, 1},
+    {4608, 1},
+    {4609, 1},
+    {4610, 1},
+    {4611, 1},
+    {4612, 1},
+    {4613, 1},
+    {4614, 1},
+    {4615, 1},
+    {4616, 10},
+    {4617, 25},
+    {4618, 9},
+    {4619, 1},
+    {4620, 1},
+    {4621, 1},
+    {4622, 1},
+    {4623, 1},
+    {4624, 1},
+    {4625, 1},
+    {4626, 1},
+    {4627, 1},
+    {4628, 6},
+    {4629, 1},
+    {4630, 1},
+    {4631, 1},
+    {4632, 1},
+    {4633, 1},
+    {4634, 1},
+    {4635, 18},
+    {4636, 1},
+    {4637, 1},
+    {4638, 1},
+    {4639, 1},
+    {4640, 1},
+    {4641, 1},
+    {4642, 1},
+    {4643, 1},
+    {4644, 1},
+    {4645, 1},
+    {4646, 1},
+    {4647, 1},
+    {4648, 1},
+    {4649, 1},
+    {4650, 1},
+    {4651, 1},
+    {4652, 1},
+    {4653, 17},
+    {4654, 1},
+    {4655, 1},
+    {4656, 1},
+    {4657, 1},
+    {4658, 3},
+    {4659, 500},
+    {4660, 500},
+    {4661, 1},
+    {4662, 1},
+    {4663, 1},
+    {4664, 1},
+    {4665, 4},
+    {4666, 500},
+    {4667, 500},
+    {4668, 250},
+    {4669, 150},
+    {4670, 3},
+    {4671, 3},
+    {4672, 1},
+    {4673, 1},
+    {4674, 1},
+    {4675, 1},
+    {4676, 1},
+    {4677, 1},
+    {4678, 1},
+    {4679, 1},
+    {4680, 1},
+    {4681, 1},
+    {4682, 1},
+    {4683, 1},
+    {4684, 1},
+    {4685, 1},
+    {4686, 1},
+    {4687, 1},
+    {4688, 1},
+    {4689, 1},
+    {4690, 1},
+    {4691, 1},
+    {4692, 8},
+    {4693, 1},
+    {4694, 1},
+    {4695, 40},
+    {4696, 3},
+    {4697, 1},
+    {4698, 9},
+    {4699, 1},
+    {4700, 1},
+    {4701, 1},
+    {4702, 1},
+    {4703, 1},
+    {4704, 22},
+    {4705, 11},
+    {4706, 77},
+    {4707, 77},
+    {4708, 77},
+    {4709, 2},
+    {4710, 11},
+    {4711, 1},
+    {4712, 2},
+    {4713, 2},
+    {4714, 1},
+    {4715, 100},
+    {4716, 30},
+    {4717, 6},
+    {4718, 1},
+    {4719, 1},
+    {4720, 1},
+    {4721, 1},
+    {4722, 1},
+    {4723, 1},
+    {4724, 1},
+    {4725, 1},
+    {4726, 1},
+    {4727, 1},
+    {4734, 75},
+    {4735, 350},
+    {4736, 1},
+    {4737, 1},
+    {4738, 1},
+    {4739, 1},
+    {4740, 1},
+    {4741, 1},
+    {4742, 40},
+    {4743, 5000},
+    {4744, 2500},
+    {4745, 50},
+    {4746, 50},
+    {4747, 40},
+    {4748, 5000},
+    {4749, 2500},
+    {4750, 50},
+    {4751, 50},
+    {4752, 40},
+    {4753, 5000},
+    {4754, 2500},
+    {4755, 50},
+    {4756, 50},
+    {4757, 3},
+    {4758, 1},
+    {4769, 10},
+    {4770, 25},
+    {4771, 25},
+    {4772, 25},
+    {4773, 25},
+    {4774, 100},
+    {4775, 10},
+    {4776, 1000},
+    {4777, 100},
+    {4778, 40},
+    {4779, 15},
+    {4780, 20},
+    {4781, 1000},
+    {4782, 50},
+    {4783, 100},
+    {4784, 10},
+    {4785, 20},
+    {4786, 40},
+    {4787, 200},
+    {4788, 150},
+    {4789, 20},
+    {4790, 1},
+    {4791, 3},
+    {4792, 1},
+    {4793, 1},
+    {4794, 1},
+    {4795, 10},
+    {4796, 10},
+    {4797, 1},
+    {4798, 1},
+    {4799, 10},
+    {4800, 10},
+    {4801, 10},
+    {4802, 10},
+    {4803, 5},
+    {4804, 1},
+    {4805, 10},
+    {4806, 1},
+    {4807, 20},
+    {4808, 1},
+    {4809, 10},
+    {4810, 2},
+    {4811, 3},
+    {4812, 1},
+    {4813, 1},
+    {4814, 1},
+    {4815, 4},
+    {4816, 4},
+    {4817, 1000},
+    {4818, 250},
+    {4819, 150},
+    {4820, 1},
+    {4821, 1},
+    {4822, 1},
+    {4823, 25},
+    {4824, 50},
+    {4825, 1},
+    {4826, 1},
+    {4827, 1},
+    {4828, 1},
+    {4829, 9},
+    {4830, 1},
+    {4831, 2},
+    {4832, 3},
+    {4833, 4},
+    {4834, 5},
+    {4835, 6},
+    {4836, 7},
+    {4837, 8},
+    {4838, 9},
+    {4839, 9},
+    {4840, 1},
+    {4841, 1},
+    {4842, 1},
+    {4843, 1},
+    {4844, 1},
+    {4845, 1},
+    {4846, 1},
+    {4847, 1},
+    {4848, 1},
+    {4849, 9},
+    {4850, 1},
+    {4851, 2},
+    {4852, 3},
+    {4853, 4},
+    {4854, 5},
+    {4855, 6},
+    {4856, 7},
+    {4857, 8},
+    {4858, 9},
+    {4859, 9},
+    {4860, 1},
+    {4861, 2},
+    {4862, 3},
+    {4863, 4},
+    {4864, 5},
+    {4865, 6},
+    {4866, 7},
+    {4867, 8},
+    {4868, 9},
+    {4869, 9},
+    {4870, 1},
+    {4871, 2},
+    {4872, 3},
+    {4873, 4},
+    {4874, 5},
+    {4875, 6},
+    {4876, 7},
+    {4877, 8},
+    {4878, 9},
+    {4879, 15},
+    {4880, 1},
+    {4881, 1},
+    {4882, 1},
+    {4883, 1},
+    {4884, 1},
+    {4885, 1},
+    {4886, 1},
+    {4887, 1},
+    {4888, 1},
+    {4889, 1},
+    {4890, 1},
+    {4891, 1},
+    {4892, 1},
+    {4893, 1},
+    {4894, 1},
+    {4895, 11},
+    {4896, 7},
+    {4897, 50},
+    {4898, 20},
+    {4899, 200},
+    {4900, 200},
+    {4901, 1},
+    {4902, 20},
+    {4903, 150},
+    {4904, 100},
+    {4905, 100},
+    {4906, 50},
+    {4907, 50},
+    {4908, 80},
+    {4909, 1},
+    {4910, 1},
+    {4911, 8000},
+    {4912, 1},
+    {4913, 2500000},
+    {4914, 900},
+    {4915, 100},
+    {4916, 240},
+    {4917, 400},
+    {4918, 500},
+    {4919, 40},
+    {4920, 40},
+    {4921, 24},
+    {4922, 32},
+    {4923, 40000},
+    {4924, 1},
+    {4925, 1},
+    {4926, 1},
+    {4927, 1},
+    {4928, 1},
+    {4929, 1},
+    {4930, 1},
+    {4931, 1},
+    {4932, 1},
+    {4933, 1},
+    {4934, 50},
+    {4935, 1},
+    {4936, 1},
+    {4937, 1},
+    {4938, 1},
+    {4939, 1},
+    {4940, 1},
+    {4941, 1},
+    {4942, 1},
+    {4943, 1},
+    {4944, 1},
+    {4945, 1},
+    {4946, 1},
+    {4947, 16},
+    {4948, 1},
+    {4949, 1},
+    {4950, 1},
+    {4951, 1},
+    {4952, 1},
+    {4953, 1},
+    {4954, 1},
+    {4955, 1},
+    {4956, 1},
+    {4957, 1},
+    {4958, 1},
+    {4959, 200},
+    {4960, 1},
+    {4961, 1},
+    {4962, 7},
+    {4963, 1},
+    {4964, 1},
+    {4965, 1},
+    {4966, 1},
+    {4967, 1},
+    {4968, 1},
+    {4969, 1},
+    {4970, 9},
+    {4971, 1},
+    {4972, 1},
+    {4973, 1},
+    {4974, 1},
+    {4975, 1},
+    {4976, 1},
+    {4977, 1},
+    {4978, 1},
+    {4979, 1},
+    {4980, 9},
+    {4981, 1},
+    {4982, 1},
+    {4983, 1},
+    {4984, 1},
+    {4985, 1},
+    {4986, 1},
+    {4987, 1},
+    {4988, 1},
+    {4989, 1},
+    {4990, 15},
+    {4991, 1},
+    {4992, 1},
+    {4993, 1},
+    {4994, 1},
+    {4995, 1},
+    {4996, 1},
+    {4997, 1},
+    {4998, 1},
+    {4999, 1},
+    {5000, 1},
+    {5001, 1},
+    {5002, 1},
+    {5003, 1},
+    {5004, 1},
+    {5005, 1},
+    {5006, 1},
+    {5007, 5},
+    {5008, 10},
+    {5009, 15},
+    {5010, 1},
+    {5011, 1},
+    {5012, 1},
+    {5013, 25},
+    {5014, 1},
+    {5015, 1},
+    {5016, 1},
+    {5017, 50},
+    {5018, 10},
+    {5019, 1},
+    {5020, 1},
+    {5021, 30000},
+    {5022, 1000},
+    {5023, 10},
+    {5024, 1},
+    {5025, 1},
+    {5026, 1},
+    {5027, 1},
+    {5028, 1},
+    {5029, 1},
+    {5030, 1},
+    {5031, 1},
+    {5032, 1},
+    {5033, 1},
+    {5034, 1},
+    {5035, 1},
+    {5036, 1},
+    {5037, 1},
+    {5038, 1},
+    {5039, 1},
+    {5040, 1},
+    {5041, 1},
+    {5042, 1},
+    {5043, 1},
+    {5044, 1},
+    {5045, 1},
+    {5046, 1},
+    {5047, 1},
+    {5048, 1},
+    {5049, 5},
+    {5050, 1},
+    {5051, 1},
+    {5052, 1},
+    {5053, 1},
+    {5054, 1},
+    {5055, 1},
+    {5056, 1},
+    {5057, 10},
+    {5058, 25},
+    {5059, 250},
+    {5060, 150},
+    {5061, 3},
+    {5062, 3},
+    {5063, 1},
+    {5064, 20},
+    {5065, 1},
+    {5066, 1},
+    {5067, 1},
+    {5068, 1},
+    {5069, 1},
+    {5070, 12},
+    {5071, 1},
+    {5072, 1},
+    {5073, 9},
+    {5074, 1},
+    {5075, 1},
+    {5076, 1},
+    {5077, 1},
+    {5078, 9},
+    {5079, 10},
+    {5080, 8},
+    {5081, 1},
+    {5082, 1},
+    {5083, 1},
+    {5084, 1},
+    {5085, 1},
+    {5086, 1},
+    {5087, 1},
+    {5088, 1},
+    {5089, 1},
+    {5090, 1},
+    {5091, 1},
+    {5096, 1},
+    {5097, 1},
+    {5098, 25},
+    {5099, 50},
+    {5100, 75},
+    {5101, 50},
+    {5102, 50},
+    {5103, 15},
+    {5104, 100},
+    {5105, 11},
+    {5106, 1},
+    {5107, 2},
+    {5108, 3},
+    {5109, 4},
+    {5110, 5},
+    {5111, 6},
+    {5112, 7},
+    {5113, 8},
+    {5114, 9},
+    {5115, 10},
+    {5116, 11},
+    {5117, 9},
+    {5118, 1},
+    {5119, 1},
+    {5120, 1},
+    {5121, 1},
+    {5122, 1},
+    {5123, 1},
+    {5124, 1},
+    {5125, 1},
+    {5126, 1},
+    {5127, 20},
+    {5128, 1},
+    {5129, 1},
+    {5130, 1},
+    {5131, 1},
+    {5132, 1},
+    {5133, 1},
+    {5134, 1},
+    {5135, 1},
+    {5136, 1},
+    {5137, 1},
+    {5138, 1},
+    {5139, 1},
+    {5140, 1},
+    {5141, 1},
+    {5142, 1},
+    {5143, 1},
+    {5144, 1},
+    {5145, 1},
+    {5146, 1},
+    {5147, 1},
+    {5148, 10},
+    {5149, 1},
+    {5150, 1},
+    {5151, 1},
+    {5152, 1},
+    {5153, 1},
+    {5154, 1},
+    {5155, 1},
+    {5156, 1},
+    {5157, 1},
+    {5158, 1},
+    {5159, 14},
+    {5160, 1},
+    {5161, 1},
+    {5162, 1},
+    {5163, 1},
+    {5164, 1},
+    {5165, 1},
+    {5166, 1},
+    {5167, 1},
+    {5168, 1},
+    {5169, 1},
+    {5170, 1},
+    {5171, 1},
+    {5172, 1},
+    {5173, 1},
+    {5174, 9},
+    {5175, 1},
+    {5176, 1},
+    {5177, 1},
+    {5178, 1},
+    {5179, 1},
+    {5180, 1},
+    {5181, 1},
+    {5182, 1},
+    {5183, 1},
+    {5184, 20},
+    {5185, 1},
+    {5186, 1},
+    {5187, 1},
+    {5188, 1},
+    {5189, 1},
+    {5190, 1},
+    {5191, 1},
+    {5192, 1},
+    {5193, 1},
+    {5194, 1},
+    {5195, 1},
+    {5196, 1},
+    {5197, 1},
+    {5198, 1},
+    {5199, 1},
+    {5200, 1},
+    {5201, 1},
+    {5202, 1},
+    {5203, 1},
+    {5204, 1},
+    {5205, 7},
+    {5206, 75},
+    {5207, 1},
+    {5208, 5},
+    {5209, 100},
+    {5210, 1000},
+    {5211, 40},
+    {5212, 1},
+    {5213, 1},
+    {5214, 1},
+    {5215, 1},
+    {5216, 1},
+    {5217, 1},
+    {5218, 10},
+    {5219, 1},
+    {5220, 1},
+    {5221, 1},
+    {5222, 1},
+    {5223, 1},
+    {5224, 1},
+    {5225, 1},
+    {5226, 1},
+    {5227, 1},
+    {5228, 1},
+    {5229, 1},
+    {5230, 1},
+    {5231, 1},
+    {5232, 1},
+    {5233, 1},
+    {5234, 1},
+    {5235, 1},
+    {5236, 1},
+    {5237, 1},
+    {5238, 1},
+    {5239, 1},
+    {5240, 1},
+    {5241, 1},
+    {5242, 1},
+    {5243, 1},
+    {5244, 1},
+    {5245, 1},
+    {5246, 1},
+    {5247, 1},
+    {5248, 1},
+    {5249, 10},
+    {5250, 25},
+    {5251, 1},
+    {5252, 1},
+    {5253, 1},
+    {5254, 1},
+    {5255, 250},
+    {5256, 150},
+    {5257, 3},
+    {5258, 3},
+    {5263, 100},
+    {5264, 3},
+    {5265, 3},
+    {5266, 4},
+    {5267, 1},
+    {5268, 3},
+    {5269, 4},
+    {5270, 500},
+    {5271, 500},
+    {5272, 500},
+    {5273, 500},
+    {5274, 500},
+    {5275, 200},
+    {5276, 150},
+    {5277, 200},
+    {5278, 100},
+    {5279, 100},
+    {5280, 100},
+    {5281, 100},
+    {5282, 100},
+    {5283, 10},
+    {5284, 300},
+    {5285, 400},
+    {5286, 300},
+    {5287, 300},
+    {5288, 400},
+    {5289, 300},
+    {5290, 1},
+    {5291, 1},
+    {5292, 1},
+    {5293, 125},
+    {5294, 75},
+    {5295, 3},
+    {5296, 1},
+    {5297, 10},
+    {5298, 25},
+    {5299, 50},
+    {5300, 100},
+    {5301, 1},
+    {5314, 40},
+    {5315, 4},
+    {5316, 1},
+    {5317, 1},
+    {5318, 1},
+    {5319, 1},
+    {5320, 1},
+    {5321, 1},
+    {5322, 1},
+    {5323, 1},
+    {5324, 1},
+    {5325, 1},
+    {5326, 1},
+    {5327, 1},
+    {5330, 1},
+    {5331, 1},
+    {5332, 1},
+    {5337, 1},
+    {5338, 19},
+    {5339, 1},
+    {5340, 1},
+    {5341, 1},
+    {5342, 1},
+    {5343, 1},
+    {5344, 1},
+    {5345, 1},
+    {5346, 1},
+    {5347, 1},
+    {5348, 1},
+    {5349, 1},
+    {5350, 1},
+    {5351, 1},
+    {5352, 1},
+    {5353, 1},
+    {5354, 1},
+    {5355, 1},
+    {5356, 1},
+    {5357, 1},
+    {5358, 9},
+    {5359, 1},
+    {5360, 2},
+    {5361, 3},
+    {5362, 4},
+    {5363, 5},
+    {5364, 6},
+    {5365, 7},
+    {5366, 8},
+    {5367, 9},
+    {5368, 250},
+    {5369, 150},
+    {5370, 3},
+    {5371, 3},
+    {5372, 10},
+    {5373, 25},
+    {5374, 11},
+    {5375, 7},
+    {5376, 5000},
+    {5377, 400},
+    {5378, 500},
+    {5379, 600},
+    {5380, 1000},
+    {5381, 30},
+    {5382, 75},
+    {5383, 125},
+    {5384, 400},
+    {5385, 1},
+    {5386, 1},
+    {5387, 1},
+    {5388, 1},
+    {5389, 1},
+    {5390, 20},
+    {5391, 10},
+    {5392, 25},
+    {5397, 1},
+    {5398, 1},
+    {5399, 1},
+    {5400, 1},
+    {5401, 1},
+    {5402, 1},
+    {5403, 1},
+    {5404, 1},
+    {5405, 1},
+    {5406, 3},
+    {5407, 1},
+    {5408, 1},
+    {5409, 1},
+    {5410, 1},
+    {5411, 1},
+    {5412, 1},
+    {5413, 1},
+    {5414, 1},
+    {5415, 1},
+    {5416, 1},
+    {5417, 1},
+    {5418, 1},
+    {5419, 1},
+    {5420, 1},
+    {5421, 1},
+    {5422, 10},
+    {5423, 4},
+    {5424, 1},
+    {5425, 1},
+    {5426, 1},
+    {5427, 1},
+    {5428, 1},
+    {5429, 1},
+    {5430, 1},
+    {5431, 1},
+    {5432, 1},
+    {5433, 1},
+    {5434, 1},
+    {5435, 1},
+    {5436, 1},
+    {5437, 1},
+    {5438, 1},
+    {5439, 1},
+    {5440, 1},
+    {5441, 1},
+    {5442, 1},
+    {5443, 1},
+    {5444, 1},
+    {5445, 1},
+    {5446, 1},
+    {5447, 1},
+    {5448, 1},
+    {5449, 1},
+    {5450, 9},
+    {5451, 1},
+    {5452, 2},
+    {5453, 3},
+    {5454, 4},
+    {5455, 5},
+    {5456, 6},
+    {5457, 7},
+    {5458, 8},
+    {5459, 9},
+    {5460, 13},
+    {5461, 1},
+    {5462, 2},
+    {5463, 3},
+    {5464, 4},
+    {5465, 5},
+    {5466, 6},
+    {5467, 7},
+    {5468, 8},
+    {5469, 9},
+    {5470, 10},
+    {5471, 11},
+    {5472, 12},
+    {5473, 13},
+    {5474, 250},
+    {5475, 150},
+    {5476, 3},
+    {5477, 3},
+    {5478, 10},
+    {5479, 25},
+    {5480, 1},
+    {5481, 1},
+    {5482, 1},
+    {5483, 1},
+    {5484, 12},
+    {5489, 1},
+    {5490, 12},
+    {5493, 1},
+    {5494, 1},
+    {5495, 1},
+    {5496, 1},
+    {5497, 1},
+    {5498, 1},
+    {5499, 1},
+    {5500, 1},
+    {5501, 1},
+    {5502, 25},
+    {5503, 50},
+    {5504, 1},
+    {5505, 1},
+    {5506, 1},
+    {5507, 1},
+    {5508, 1},
+    {5509, 1},
+    {5510, 1},
+    {5511, 1},
+    {5512, 1},
+    {5513, 1},
+    {5514, 1},
+    {5515, 1},
+    {5516, 1},
+    {5517, 1},
+    {5518, 1},
+    {5519, 1},
+    {5520, 1},
+    {5521, 1},
+    {5522, 1},
+    {5523, 1},
+    {5524, 1},
+    {5525, 1},
+    {5526, 1},
+    {5527, 1},
+    {5528, 1},
+    {5529, 1},
+    {5530, 1},
+    {5531, 10},
+    {5532, 4},
+    {5533, 1},
+    {5534, 1},
+    {5535, 1},
+    {5536, 1},
+    {5537, 1},
+    {5538, 1},
+    {5539, 1},
+    {5540, 1},
+    {5541, 1},
+    {5542, 1},
+    {5543, 1},
+    {5544, 1},
+    {5545, 1},
+    {5546, 1},
+    {5547, 1},
+    {5548, 1},
+    {5549, 1},
+    {5550, 1},
+    {5551, 1},
+    {5552, 1},
+    {5553, 1},
+    {5554, 1},
+    {5555, 1},
+    {5556, 1},
+    {5557, 1},
+    {5558, 1},
+    {5559, 1},
+    {5560, 5},
+    {5561, 10},
+    {5562, 15},
+    {5563, 1},
+    {5564, 1},
+    {5565, 1},
+    {5566, 1},
+    {5567, 1},
+    {5568, 1},
+    {5569, 1},
+    {5570, 1},
+    {5571, 1},
+    {5572, 1},
+    {5573, 12500},
+    {5574, 50000},
+    {5575, 15},
+    {5576, 10},
+    {5577, 20},
+    {5578, 20},
+    {5579, 7},
+    {5580, 3},
+    {5581, 100},
+    {5582, 1},
+    {5583, 1},
+    {5584, 1},
+    {5585, 1},
+    {5586, 20},
+    {5587, 1},
+    {5588, 1},
+    {5589, 1},
+    {5590, 7},
+    {5591, 15},
+    {5592, 30},
+    {5593, 45},
+    {5594, 5},
+    {5595, 1},
+    {5596, 1},
+    {5597, 1},
+    {5598, 1},
+    {5599, 14},
+    {5600, 1},
+    {5601, 1},
+    {5602, 1},
+    {5603, 1},
+    {5604, 1},
+    {5605, 1},
+    {5606, 1},
+    {5607, 1},
+    {5608, 1},
+    {5609, 1},
+    {5610, 1},
+    {5611, 1},
+    {5612, 1},
+    {5613, 1},
+    {5614, 12},
+    {5615, 1},
+    {5616, 2},
+    {5617, 3},
+    {5618, 4},
+    {5619, 5},
+    {5620, 6},
+    {5621, 7},
+    {5622, 8},
+    {5623, 9},
+    {5624, 10},
+    {5625, 11},
+    {5626, 12},
+    {5627, 15},
+    {5628, 15},
+    {5629, 15},
+    {5630, 11},
+    {5631, 11},
+    {5632, 11},
+    {5633, 20},
+    {5634, 20},
+    {5635, 20},
+    {5636, 25},
+    {5637, 25},
+    {5638, 25},
+    {5639, 31},
+    {5640, 31},
+    {5641, 31},
+    {5642, 36},
+    {5643, 36},
+    {5644, 36},
+    {5645, 25},
+    {5646, 25},
+    {5647, 25},
+    {5648, 39},
+    {5649, 39},
+    {5650, 39},
+    {5651, 19},
+    {5652, 19},
+    {5653, 19},
+    {5654, 30},
+    {5655, 30},
+    {5656, 30},
+    {5657, 23},
+    {5658, 23},
+    {5659, 23},
+    {5660, 24},
+    {5661, 24},
+    {5662, 24},
+    {5663, 20},
+    {5664, 20},
+    {5665, 20},
+    {5666, 1},
+    {5667, 1},
+    {5668, 1},
+    {5669, 29},
+    {5670, 29},
+    {5671, 29},
+    {5672, 15},
+    {5673, 15},
+    {5674, 15},
+    {5675, 18},
+    {5676, 18},
+    {5677, 18},
+    {5678, 19},
+    {5679, 19},
+    {5680, 19},
+    {5681, 19},
+    {5682, 19},
+    {5683, 19},
+    {5684, 15},
+    {5685, 15},
+    {5686, 15},
+}};
+
+/** flagIndex -> objective run. Sorted by flagIndex so record_claims can binary-search it. */
+inline constexpr std::array<RecordEntry, kRecordCount> kRecords{{
+    {8923, 0, 1},
+    {8924, 1, 1},
+    {8925, 2, 1},
+    {8926, 3, 1},
+    {8927, 4, 1},
+    {8928, 5, 1},
+    {8929, 6, 1},
+    {8930, 7, 1},
+    {8931, 8, 1},
+    {8932, 9, 1},
+    {8933, 10, 1},
+    {8934, 11, 1},
+    {8935, 12, 1},
+    {8936, 13, 1},
+    {8937, 14, 1},
+    {8938, 15, 1},
+    {8939, 16, 1},
+    {8940, 17, 1},
+    {8941, 18, 1},
+    {8942, 19, 1},
+    {8943, 20, 1},
+    {8944, 21, 1},
+    {8945, 22, 1},
+    {8946, 23, 1},
+    {8947, 24, 1},
+    {8948, 25, 1},
+    {8949, 26, 1},
+    {8950, 27, 1},
+    {8951, 28, 1},
+    {8952, 29, 1},
+    {8953, 30, 1},
+    {8954, 31, 1},
+    {8955, 32, 1},
+    {8956, 33, 1},
+    {8957, 34, 1},
+    {8958, 35, 1},
+    {8959, 36, 1},
+    {8960, 37, 1},
+    {8961, 38, 1},
+    {8962, 39, 1},
+    {8963, 40, 1},
+    {8964, 41, 1},
+    {8965, 42, 1},
+    {8966, 43, 1},
+    {8967, 44, 1},
+    {8968, 45, 1},
+    {8969, 46, 1},
+    {8970, 47, 1},
+    {8971, 48, 1},
+    {8972, 49, 1},
+    {8973, 50, 1},
+    {8974, 51, 1},
+    {8975, 52, 1},
+    {8976, 53, 1},
+    {8977, 54, 1},
+    {8978, 55, 1},
+    {8979, 56, 1},
+    {8980, 57, 1},
+    {8981, 58, 1},
+    {8982, 59, 1},
+    {8983, 60, 1},
+    {8984, 61, 1},
+    {8985, 62, 1},
+    {8986, 63, 1},
+    {8987, 64, 1},
+    {8988, 65, 1},
+    {8989, 66, 1},
+    {8990, 67, 1},
+    {8991, 68, 1},
+    {8992, 69, 1},
+    {8993, 70, 1},
+    {8994, 71, 1},
+    {8995, 72, 1},
+    {8996, 73, 1},
+    {8997, 74, 1},
+    {8998, 75, 1},
+    {8999, 76, 1},
+    {9000, 77, 1},
+    {9001, 78, 1},
+    {9002, 79, 1},
+    {9003, 80, 1},
+    {9004, 81, 1},
+    {9005, 82, 1},
+    {9006, 83, 1},
+    {9007, 84, 1},
+    {9008, 85, 1},
+    {9009, 86, 1},
+    {9010, 87, 1},
+    {9011, 88, 1},
+    {9012, 89, 1},
+    {9013, 90, 1},
+    {9014, 91, 1},
+    {9015, 92, 1},
+    {9016, 93, 1},
+    {9017, 94, 1},
+    {9018, 95, 1},
+    {9019, 96, 1},
+    {9020, 97, 1},
+    {9021, 98, 1},
+    {9022, 99, 1},
+    {9023, 100, 1},
+    {9024, 101, 1},
+    {9025, 102, 1},
+    {9026, 103, 1},
+    {9027, 104, 1},
+    {9028, 105, 1},
+    {9029, 106, 1},
+    {9030, 107, 1},
+    {9031, 108, 1},
+    {9032, 109, 1},
+    {9033, 110, 1},
+    {9034, 111, 1},
+    {9035, 112, 1},
+    {9036, 113, 1},
+    {9037, 114, 1},
+    {9038, 115, 1},
+    {9039, 116, 1},
+    {9040, 117, 1},
+    {9041, 118, 1},
+    {9042, 119, 1},
+    {9043, 120, 1},
+    {9044, 121, 1},
+    {9045, 122, 1},
+    {9046, 123, 1},
+    {9047, 124, 1},
+    {9048, 125, 1},
+    {9049, 126, 1},
+    {9050, 127, 1},
+    {9051, 128, 1},
+    {9052, 129, 1},
+    {9053, 130, 1},
+    {9054, 131, 1},
+    {9055, 132, 1},
+    {9056, 133, 1},
+    {9057, 134, 1},
+    {9058, 135, 1},
+    {9059, 136, 1},
+    {9060, 137, 1},
+    {9061, 138, 1},
+    {9062, 139, 1},
+    {9063, 140, 1},
+    {9064, 141, 1},
+    {9065, 142, 1},
+    {9066, 143, 1},
+    {9067, 144, 1},
+    {9068, 145, 1},
+    {9069, 146, 1},
+    {9070, 147, 1},
+    {9071, 148, 1},
+    {9072, 149, 1},
+    {9073, 150, 1},
+    {9074, 151, 1},
+    {9075, 152, 1},
+    {9076, 153, 1},
+    {9077, 154, 1},
+    {9078, 155, 1},
+    {9079, 156, 1},
+    {9080, 157, 1},
+    {9081, 158, 1},
+    {9082, 159, 1},
+    {9083, 160, 1},
+    {9084, 161, 1},
+    {9085, 162, 1},
+    {9086, 163, 1},
+    {9087, 164, 1},
+    {9088, 165, 1},
+    {9089, 166, 1},
+    {9090, 167, 1},
+    {9091, 168, 1},
+    {9092, 169, 1},
+    {9093, 170, 1},
+    {9094, 171, 1},
+    {9095, 172, 1},
+    {9096, 173, 1},
+    {9097, 174, 1},
+    {9098, 175, 1},
+    {9099, 176, 1},
+    {9100, 177, 1},
+    {9102, 178, 1},
+    {9103, 179, 1},
+    {9104, 180, 1},
+    {9105, 181, 1},
+    {9106, 182, 1},
+    {9107, 183, 1},
+    {9108, 184, 1},
+    {9109, 185, 1},
+    {9110, 186, 1},
+    {9111, 187, 1},
+    {9112, 188, 1},
+    {9113, 189, 1},
+    {9114, 190, 1},
+    {9115, 191, 1},
+    {9116, 192, 1},
+    {9117, 193, 1},
+    {9118, 194, 1},
+    {9119, 195, 1},
+    {9120, 196, 1},
+    {9121, 197, 1},
+    {9122, 198, 1},
+    {9123, 199, 1},
+    {9124, 200, 1},
+    {9125, 201, 1},
+    {9126, 202, 1},
+    {9127, 203, 1},
+    {9128, 204, 1},
+    {9129, 205, 1},
+    {9130, 206, 1},
+    {9131, 207, 1},
+    {9132, 208, 1},
+    {9133, 209, 1},
+    {9134, 210, 1},
+    {9135, 211, 1},
+    {9136, 212, 1},
+    {9137, 213, 1},
+    {9138, 214, 1},
+    {9139, 215, 1},
+    {9140, 216, 1},
+    {9141, 217, 1},
+    {9142, 218, 1},
+    {9143, 219, 1},
+    {9144, 220, 1},
+    {9145, 221, 1},
+    {9146, 222, 1},
+    {9147, 223, 1},
+    {9148, 224, 1},
+    {9149, 225, 1},
+    {9150, 226, 1},
+    {9151, 227, 1},
+    {9152, 228, 1},
+    {9153, 229, 1},
+    {9154, 230, 1},
+    {9155, 231, 1},
+    {9157, 232, 1},
+    {9158, 233, 1},
+    {9159, 234, 1},
+    {9160, 235, 1},
+    {9161, 236, 1},
+    {9162, 237, 1},
+    {9163, 238, 1},
+    {9164, 239, 1},
+    {9165, 240, 1},
+    {9166, 241, 1},
+    {9167, 242, 1},
+    {9168, 243, 1},
+    {9169, 244, 1},
+    {9170, 245, 1},
+    {9171, 246, 1},
+    {9172, 247, 1},
+    {9173, 248, 1},
+    {9174, 249, 1},
+    {9175, 250, 1},
+    {9176, 251, 1},
+    {9177, 252, 1},
+    {9178, 253, 1},
+    {9179, 254, 1},
+    {9180, 255, 1},
+    {9181, 256, 1},
+    {9182, 257, 1},
+    {9183, 258, 1},
+    {9184, 259, 1},
+    {9185, 260, 1},
+    {9186, 261, 1},
+    {9187, 262, 1},
+    {9188, 263, 1},
+    {9189, 264, 1},
+    {9190, 265, 1},
+    {9191, 266, 1},
+    {9192, 267, 1},
+    {9193, 268, 1},
+    {9194, 269, 1},
+    {9195, 270, 1},
+    {9196, 271, 2},
+    {9197, 273, 2},
+    {9198, 275, 2},
+    {9199, 277, 2},
+    {9200, 279, 2},
+    {9201, 281, 2},
+    {9202, 283, 2},
+    {9203, 285, 2},
+    {9204, 287, 6},
+    {9205, 293, 2},
+    {9206, 295, 1},
+    {9207, 296, 2},
+    {9208, 298, 3},
+    {9209, 301, 3},
+    {9210, 304, 2},
+    {9211, 306, 2},
+    {9212, 308, 2},
+    {9213, 310, 2},
+    {9214, 312, 2},
+    {9215, 314, 2},
+    {9216, 316, 1},
+    {9217, 317, 1},
+    {9218, 318, 1},
+    {9219, 319, 2},
+    {9220, 321, 1},
+    {9221, 322, 1},
+    {9222, 323, 1},
+    {9223, 324, 1},
+    {9224, 325, 2},
+    {9225, 327, 3},
+    {9226, 330, 3},
+    {9227, 333, 3},
+    {9228, 336, 2},
+    {9229, 338, 4},
+    {9230, 342, 3},
+    {9231, 345, 2},
+    {9232, 347, 2},
+    {9233, 349, 3},
+    {9234, 352, 3},
+    {9235, 355, 1},
+    {9236, 356, 1},
+    {9237, 357, 1},
+    {9238, 358, 1},
+    {9239, 359, 1},
+    {9240, 360, 1},
+    {9241, 361, 1},
+    {9242, 362, 1},
+    {9243, 363, 1},
+    {9244, 364, 1},
+    {9245, 365, 1},
+    {9246, 366, 1},
+    {9247, 367, 1},
+    {9248, 368, 1},
+    {9249, 369, 1},
+    {9250, 370, 1},
+    {9251, 371, 1},
+    {9252, 372, 1},
+    {9253, 373, 1},
+    {9254, 374, 1},
+    {9255, 375, 1},
+    {9256, 376, 1},
+    {9257, 377, 1},
+    {9258, 378, 1},
+    {9259, 379, 1},
+    {9260, 380, 1},
+    {9261, 381, 1},
+    {9262, 382, 1},
+    {9263, 383, 1},
+    {9264, 384, 1},
+    {9265, 385, 1},
+    {9266, 386, 1},
+    {9267, 387, 1},
+    {9268, 388, 1},
+    {9269, 389, 1},
+    {9270, 390, 1},
+    {9271, 391, 1},
+    {9272, 392, 1},
+    {9273, 393, 1},
+    {9274, 394, 1},
+    {9275, 395, 1},
+    {9276, 396, 1},
+    {9277, 397, 1},
+    {9278, 398, 1},
+    {9279, 399, 1},
+    {9280, 400, 1},
+    {9281, 401, 1},
+    {9282, 402, 1},
+    {9283, 403, 1},
+    {9284, 404, 1},
+    {9285, 405, 1},
+    {9286, 406, 1},
+    {9287, 407, 1},
+    {9288, 408, 1},
+    {9289, 409, 1},
+    {9290, 410, 3},
+    {9292, 413, 1},
+    {9293, 414, 1},
+    {9294, 415, 1},
+    {9295, 416, 1},
+    {9296, 417, 1},
+    {9297, 418, 1},
+    {9298, 419, 1},
+    {9317, 420, 1},
+    {9318, 421, 1},
+    {9319, 422, 3},
+    {9322, 425, 1},
+    {9323, 426, 1},
+    {9324, 427, 1},
+    {9325, 428, 1},
+    {9326, 429, 1},
+    {9327, 430, 1},
+    {9346, 431, 1},
+    {9347, 432, 1},
+    {9348, 433, 3},
+    {9351, 436, 1},
+    {9352, 437, 1},
+    {9353, 438, 1},
+    {9354, 439, 1},
+    {9355, 440, 1},
+    {9356, 441, 1},
+    {9375, 442, 1},
+    {9376, 443, 1},
+    {9379, 444, 1},
+    {9380, 445, 1},
+    {9381, 446, 1},
+    {9382, 447, 3},
+    {9383, 450, 5},
+    {9385, 455, 1},
+    {9387, 456, 1},
+    {9388, 457, 1},
+    {9391, 458, 1},
+    {9392, 459, 1},
+    {9394, 460, 1},
+    {9395, 461, 1},
+    {9398, 462, 1},
+    {9399, 463, 1},
+    {9400, 464, 2},
+    {9403, 466, 1},
+    {9405, 467, 1},
+    {9409, 468, 1},
+    {9410, 469, 1},
+    {9411, 470, 1},
+    {9413, 471, 1},
+    {9414, 472, 1},
+    {9416, 473, 1},
+    {9417, 474, 1},
+    {9418, 475, 1},
+    {9419, 476, 1},
+    {9420, 477, 3},
+    {9423, 480, 1},
+    {9424, 481, 1},
+    {9425, 482, 1},
+    {9426, 483, 4},
+    {9427, 487, 5},
+    {9428, 492, 4},
+    {9429, 496, 4},
+    {9434, 500, 1},
+    {9435, 501, 1},
+    {9436, 502, 1},
+    {9437, 503, 1},
+    {9438, 504, 1},
+    {9439, 505, 3},
+    {9441, 508, 1},
+    {9442, 509, 1},
+    {9444, 510, 1},
+    {9445, 511, 1},
+    {9446, 512, 1},
+    {9447, 513, 1},
+    {9449, 514, 1},
+    {9450, 515, 1},
+    {9451, 516, 1},
+    {9452, 517, 1},
+    {9455, 518, 1},
+    {9458, 519, 1},
+    {9463, 520, 1},
+    {9464, 521, 1},
+    {9465, 522, 1},
+    {9466, 523, 1},
+    {9467, 524, 1},
+    {9468, 525, 1},
+    {9469, 526, 1},
+    {9470, 527, 1},
+    {9471, 528, 1},
+    {9472, 529, 1},
+    {9473, 530, 1},
+    {9474, 531, 1},
+    {9475, 532, 1},
+    {9477, 533, 1},
+    {9478, 534, 1},
+    {9479, 535, 1},
+    {9480, 536, 1},
+    {9481, 537, 1},
+    {9482, 538, 1},
+    {9483, 539, 1},
+    {9484, 540, 1},
+    {9485, 541, 1},
+    {9486, 542, 1},
+    {9487, 543, 1},
+    {9488, 544, 1},
+    {9489, 545, 1},
+    {9490, 546, 1},
+    {9491, 547, 1},
+    {9492, 548, 1},
+    {9493, 549, 1},
+    {9494, 550, 1},
+    {9495, 551, 1},
+    {9496, 552, 1},
+    {9497, 553, 1},
+    {9498, 554, 1},
+    {9499, 555, 1},
+    {9500, 556, 1},
+    {9501, 557, 1},
+    {9502, 558, 1},
+    {9503, 559, 1},
+    {9504, 560, 1},
+    {9505, 561, 1},
+    {9506, 562, 1},
+    {9508, 563, 1},
+    {9511, 564, 1},
+    {9512, 565, 1},
+    {9513, 566, 1},
+    {9514, 567, 1},
+    {9515, 568, 1},
+    {9516, 569, 1},
+    {9517, 570, 1},
+    {9518, 571, 1},
+    {9519, 572, 1},
+    {9520, 573, 1},
+    {9521, 574, 1},
+    {9522, 575, 1},
+    {9523, 576, 1},
+    {9524, 577, 4},
+    {9527, 581, 1},
+    {9528, 582, 1},
+    {9529, 583, 1},
+    {9530, 584, 1},
+    {9531, 585, 1},
+    {9537, 586, 1},
+    {9538, 587, 1},
+    {9539, 588, 1},
+    {9540, 589, 1},
+    {9541, 590, 1},
+    {9542, 591, 1},
+    {9543, 592, 1},
+    {9544, 593, 1},
+    {9545, 594, 1},
+    {9546, 595, 1},
+    {9547, 596, 1},
+    {9548, 597, 1},
+    {9549, 598, 1},
+    {9550, 599, 3},
+    {9551, 602, 1},
+    {9552, 603, 1},
+    {9553, 604, 1},
+    {9554, 605, 1},
+    {9555, 606, 1},
+    {9556, 607, 1},
+    {9557, 608, 1},
+    {9561, 609, 1},
+    {9565, 610, 1},
+    {9566, 611, 1},
+    {9567, 612, 1},
+    {9568, 613, 1},
+    {9571, 614, 1},
+    {9572, 615, 1},
+    {9573, 616, 1},
+    {9574, 617, 1},
+    {9576, 618, 1},
+    {9578, 619, 1},
+    {9580, 620, 5},
+    {9584, 625, 4},
+    {9585, 629, 3},
+    {9586, 632, 2},
+    {9587, 634, 2},
+    {9588, 636, 3},
+    {9589, 639, 1},
+    {9594, 640, 1},
+    {9595, 641, 1},
+    {9596, 642, 1},
+    {9597, 643, 1},
+    {9598, 644, 5},
+    {9599, 649, 1},
+    {9601, 650, 1},
+    {9602, 651, 1},
+    {9603, 652, 1},
+    {9604, 653, 1},
+    {9605, 654, 1},
+    {9606, 655, 1},
+    {9607, 656, 1},
+    {9608, 657, 2},
+    {9609, 659, 2},
+    {9610, 661, 1},
+    {9611, 662, 1},
+    {9612, 663, 1},
+    {9613, 664, 1},
+    {9614, 665, 1},
+    {9615, 666, 1},
+    {9616, 667, 1},
+    {9617, 668, 1},
+    {9618, 669, 1},
+    {9619, 670, 1},
+    {9620, 671, 1},
+    {9621, 672, 1},
+    {9622, 673, 1},
+    {9623, 674, 1},
+    {9624, 675, 1},
+    {9625, 676, 1},
+    {9626, 677, 1},
+    {9627, 678, 1},
+    {9628, 679, 1},
+    {9629, 680, 1},
+    {9630, 681, 1},
+    {9631, 682, 1},
+    {9632, 683, 1},
+    {9633, 684, 1},
+    {9634, 685, 1},
+    {9635, 686, 1},
+    {9636, 687, 1},
+    {9637, 688, 1},
+    {9638, 689, 1},
+    {9642, 690, 1},
+    {9643, 691, 1},
+    {9644, 692, 1},
+    {9645, 693, 1},
+    {9646, 694, 1},
+    {9647, 695, 1},
+    {9648, 696, 1},
+    {9649, 697, 1},
+    {9650, 698, 1},
+    {9651, 699, 1},
+    {9652, 700, 1},
+    {9653, 701, 1},
+    {9654, 702, 1},
+    {9655, 703, 1},
+    {9656, 704, 1},
+    {9657, 705, 1},
+    {9658, 706, 1},
+    {9659, 707, 1},
+    {9660, 708, 1},
+    {9661, 709, 1},
+    {9662, 710, 1},
+    {9663, 711, 1},
+    {9664, 712, 1},
+    {9665, 713, 1},
+    {9666, 714, 1},
+    {9667, 715, 1},
+    {9668, 716, 1},
+    {9669, 717, 1},
+    {9670, 718, 1},
+    {9671, 719, 1},
+    {9672, 720, 1},
+    {9673, 721, 1},
+    {9674, 722, 1},
+    {9675, 723, 1},
+    {9676, 724, 1},
+    {9677, 725, 1},
+    {9678, 726, 1},
+    {9679, 727, 1},
+    {9680, 728, 1},
+    {9681, 729, 1},
+    {9682, 730, 1},
+    {9683, 731, 1},
+    {9684, 732, 1},
+    {9685, 733, 1},
+    {9686, 734, 1},
+    {9687, 735, 1},
+    {9688, 736, 1},
+    {9689, 737, 1},
+    {9690, 738, 1},
+    {9691, 739, 1},
+    {9692, 740, 1},
+    {9693, 741, 1},
+    {9694, 742, 1},
+    {9695, 743, 1},
+    {9696, 744, 1},
+    {9697, 745, 1},
+    {9698, 746, 1},
+    {9699, 747, 1},
+    {9700, 748, 1},
+    {9701, 749, 1},
+    {9702, 750, 1},
+    {9703, 751, 1},
+    {9704, 752, 1},
+    {9705, 753, 1},
+    {9706, 754, 1},
+    {9707, 755, 1},
+    {9708, 756, 1},
+    {9709, 757, 1},
+    {9710, 758, 1},
+    {9711, 759, 1},
+    {9712, 760, 1},
+    {9713, 761, 1},
+    {9714, 762, 1},
+    {9715, 763, 1},
+    {9716, 764, 1},
+    {9717, 765, 1},
+    {9718, 766, 1},
+    {9719, 767, 1},
+    {9720, 768, 1},
+    {9721, 769, 1},
+    {9722, 770, 1},
+    {9723, 771, 1},
+    {9724, 772, 1},
+    {9725, 773, 1},
+    {9726, 774, 1},
+    {9727, 775, 1},
+    {9728, 776, 1},
+    {9729, 777, 1},
+    {9730, 778, 1},
+    {9731, 779, 1},
+    {9732, 780, 1},
+    {9733, 781, 1},
+    {9734, 782, 1},
+    {9735, 783, 1},
+    {9736, 784, 1},
+    {9737, 785, 1},
+    {9738, 786, 1},
+    {9739, 787, 1},
+    {9740, 788, 1},
+    {9741, 789, 1},
+    {9742, 790, 1},
+    {9743, 791, 1},
+    {9744, 792, 1},
+    {9745, 793, 1},
+    {9746, 794, 1},
+    {9747, 795, 1},
+    {9748, 796, 1},
+    {9749, 797, 1},
+    {9750, 798, 1},
+    {9751, 799, 1},
+    {9752, 800, 1},
+    {9753, 801, 1},
+    {9754, 802, 1},
+    {9755, 803, 1},
+    {9756, 804, 1},
+    {9757, 805, 1},
+    {9758, 806, 1},
+    {9759, 807, 1},
+    {9760, 808, 1},
+    {9761, 809, 1},
+    {9762, 810, 1},
+    {9763, 811, 1},
+    {9764, 812, 1},
+    {9765, 813, 1},
+    {9766, 814, 1},
+    {9767, 815, 1},
+    {9768, 816, 1},
+    {9769, 817, 1},
+    {9770, 818, 1},
+    {9771, 819, 1},
+    {9772, 820, 1},
+    {9773, 821, 1},
+    {9774, 822, 1},
+    {9775, 823, 1},
+    {9776, 824, 1},
+    {9777, 825, 1},
+    {9778, 826, 1},
+    {9779, 827, 1},
+    {9780, 828, 1},
+    {9781, 829, 1},
+    {9782, 830, 1},
+    {9783, 831, 1},
+    {9784, 832, 1},
+    {9785, 833, 1},
+    {9786, 834, 1},
+    {9787, 835, 1},
+    {9790, 836, 1},
+    {9791, 837, 3},
+    {9793, 840, 1},
+    {9794, 841, 1},
+    {9795, 842, 1},
+    {9796, 843, 1},
+    {9797, 844, 1},
+    {9798, 845, 1},
+    {9799, 846, 1},
+    {9800, 847, 1},
+    {9801, 848, 1},
+    {9802, 849, 1},
+    {9803, 850, 1},
+    {9804, 851, 1},
+    {9805, 852, 1},
+    {9806, 853, 2},
+    {9807, 855, 1},
+    {9808, 856, 1},
+    {9809, 857, 1},
+    {9810, 858, 1},
+    {9811, 859, 1},
+    {9812, 860, 1},
+    {9813, 861, 1},
+    {9814, 862, 1},
+    {9815, 863, 2},
+    {9816, 865, 1},
+    {9817, 866, 3},
+    {9818, 869, 1},
+    {9819, 870, 1},
+    {9820, 871, 1},
+    {9821, 872, 2},
+    {9822, 874, 1},
+    {9823, 875, 1},
+    {9824, 876, 2},
+    {9825, 878, 1},
+    {9826, 879, 1},
+    {9827, 880, 2},
+    {9828, 882, 1},
+    {9829, 883, 1},
+    {9830, 884, 2},
+    {9831, 886, 1},
+    {9832, 887, 1},
+    {9833, 888, 2},
+    {9834, 890, 1},
+    {9835, 891, 1},
+    {9836, 892, 2},
+    {9837, 894, 1},
+    {9838, 895, 1},
+    {9839, 896, 2},
+    {9840, 898, 1},
+    {9841, 899, 1},
+    {9842, 900, 1},
+    {9843, 901, 1},
+    {9844, 902, 1},
+    {9845, 903, 1},
+    {9846, 904, 2},
+    {9847, 906, 1},
+    {9848, 907, 1},
+    {9849, 908, 2},
+    {9850, 910, 1},
+    {9851, 911, 1},
+    {9852, 912, 1},
+    {9853, 913, 1},
+    {9854, 914, 1},
+    {9855, 915, 1},
+    {9856, 916, 2},
+    {9857, 918, 1},
+    {9858, 919, 2},
+    {9859, 921, 1},
+    {9860, 922, 2},
+    {9861, 924, 1},
+    {9862, 925, 2},
+    {9863, 927, 1},
+    {9864, 928, 2},
+    {9865, 930, 1},
+    {9866, 931, 2},
+    {9867, 933, 1},
+    {9868, 934, 2},
+    {9869, 936, 1},
+    {9870, 937, 2},
+    {9871, 939, 1},
+    {9872, 940, 2},
+    {9873, 942, 1},
+    {9874, 943, 1},
+    {9878, 944, 3},
+    {9884, 947, 1},
+    {9885, 948, 1},
+    {9886, 949, 1},
+    {9887, 950, 1},
+    {9888, 951, 1},
+    {9889, 952, 1},
+    {9890, 953, 3},
+    {9891, 956, 1},
+    {9892, 957, 3},
+    {9893, 960, 1},
+    {9894, 961, 3},
+    {9895, 964, 1},
+    {9896, 965, 3},
+    {9897, 968, 1},
+    {9898, 969, 3},
+    {9899, 972, 1},
+    {9900, 973, 1},
+    {9901, 974, 1},
+    {9902, 975, 6},
+    {9903, 981, 3},
+    {9904, 984, 6},
+    {9905, 990, 3},
+    {9906, 993, 6},
+    {9907, 999, 3},
+    {9908, 1002, 4},
+    {9909, 1006, 4},
+    {9910, 1010, 4},
+    {9911, 1014, 5},
+    {9912, 1019, 5},
+    {9913, 1024, 5},
+    {9914, 1029, 3},
+    {9915, 1032, 1},
+    {9916, 1033, 2},
+    {9917, 1035, 2},
+    {9918, 1037, 2},
+    {9919, 1039, 3},
+    {9920, 1042, 3},
+    {9921, 1045, 2},
+    {9922, 1047, 2},
+    {9923, 1049, 2},
+    {9924, 1051, 3},
+    {9925, 1054, 3},
+    {9926, 1057, 4},
+    {9927, 1061, 1},
+    {9928, 1062, 2},
+    {9929, 1064, 2},
+    {9930, 1066, 2},
+    {9931, 1068, 2},
+    {9932, 1070, 4},
+    {9933, 1074, 5},
+    {9934, 1079, 5},
+    {9935, 1084, 5},
+    {9936, 1089, 4},
+    {9937, 1093, 4},
+    {9938, 1097, 6},
+    {9939, 1103, 4},
+    {9940, 1107, 4},
+    {9941, 1111, 4},
+    {9942, 1115, 4},
+    {9943, 1119, 4},
+    {9947, 1123, 1},
+    {9948, 1124, 1},
+    {9949, 1125, 3},
+    {9950, 1128, 1},
+    {9951, 1129, 1},
+    {9952, 1130, 1},
+    {9953, 1131, 1},
+    {9954, 1132, 1},
+    {9955, 1133, 1},
+    {9956, 1134, 1},
+    {9957, 1135, 1},
+    {9958, 1136, 1},
+    {9959, 1137, 1},
+    {9960, 1138, 1},
+    {9961, 1139, 1},
+    {9962, 1140, 1},
+    {9963, 1141, 1},
+    {9964, 1142, 1},
+    {9965, 1143, 1},
+    {9966, 1144, 1},
+    {9967, 1145, 1},
+    {9968, 1146, 1},
+    {9969, 1147, 1},
+    {9970, 1148, 1},
+    {9971, 1149, 1},
+    {9972, 1150, 1},
+    {9973, 1151, 1},
+    {9974, 1152, 1},
+    {9975, 1153, 1},
+    {9976, 1154, 1},
+    {9977, 1155, 1},
+    {9978, 1156, 1},
+    {9979, 1157, 1},
+    {9980, 1158, 1},
+    {9981, 1159, 1},
+    {9982, 1160, 1},
+    {9983, 1161, 1},
+    {9984, 1162, 1},
+    {9985, 1163, 1},
+    {9986, 1164, 1},
+    {9987, 1165, 1},
+    {9988, 1166, 1},
+    {9989, 1167, 1},
+    {9990, 1168, 1},
+    {9991, 1169, 1},
+    {9992, 1170, 1},
+    {9993, 1171, 1},
+    {9994, 1172, 1},
+    {9995, 1173, 1},
+    {9996, 1174, 1},
+    {9997, 1175, 1},
+    {9998, 1176, 1},
+    {9999, 1177, 1},
+    {10000, 1178, 1},
+    {10001, 1179, 1},
+    {10002, 1180, 1},
+    {10003, 1181, 1},
+    {10004, 1182, 1},
+    {10005, 1183, 1},
+    {10006, 1184, 1},
+    {10007, 1185, 1},
+    {10008, 1186, 1},
+    {10009, 1187, 1},
+    {10010, 1188, 1},
+    {10011, 1189, 1},
+    {10012, 1190, 1},
+    {10013, 1191, 1},
+    {10014, 1192, 1},
+    {10015, 1193, 1},
+    {10016, 1194, 1},
+    {10017, 1195, 1},
+    {10018, 1196, 1},
+    {10019, 1197, 1},
+    {10020, 1198, 1},
+    {10021, 1199, 1},
+    {10022, 1200, 1},
+    {10023, 1201, 1},
+    {10024, 1202, 1},
+    {10025, 1203, 1},
+    {10026, 1204, 1},
+    {10027, 1205, 1},
+    {10028, 1206, 1},
+    {10029, 1207, 1},
+    {10030, 1208, 1},
+    {10031, 1209, 1},
+    {10032, 1210, 1},
+    {10033, 1211, 1},
+    {10034, 1212, 1},
+    {10035, 1213, 1},
+    {10036, 1214, 1},
+    {10037, 1215, 1},
+    {10038, 1216, 1},
+    {10039, 1217, 1},
+    {10040, 1218, 1},
+    {10041, 1219, 1},
+    {10042, 1220, 1},
+    {10043, 1221, 1},
+    {10044, 1222, 1},
+    {10045, 1223, 1},
+    {10046, 1224, 1},
+    {10047, 1225, 1},
+    {10048, 1226, 1},
+    {10049, 1227, 1},
+    {10050, 1228, 1},
+    {10051, 1229, 1},
+    {10052, 1230, 1},
+    {10053, 1231, 1},
+    {10054, 1232, 1},
+    {10055, 1233, 1},
+    {10056, 1234, 1},
+    {10057, 1235, 1},
+    {10058, 1236, 1},
+    {10059, 1237, 1},
+    {10060, 1238, 1},
+    {10061, 1239, 1},
+    {10062, 1240, 1},
+    {10063, 1241, 1},
+    {10064, 1242, 1},
+    {10065, 1243, 1},
+    {10066, 1244, 1},
+    {10067, 1245, 1},
+    {10068, 1246, 1},
+    {10069, 1247, 1},
+    {10070, 1248, 1},
+    {10071, 1249, 1},
+    {10072, 1250, 1},
+    {10073, 1251, 1},
+    {10074, 1252, 1},
+    {10075, 1253, 1},
+    {10076, 1254, 1},
+    {10077, 1255, 1},
+    {10078, 1256, 1},
+    {10079, 1257, 1},
+    {10080, 1258, 1},
+    {10081, 1259, 1},
+    {10082, 1260, 1},
+    {10083, 1261, 1},
+    {10084, 1262, 1},
+    {10085, 1263, 1},
+    {10086, 1264, 1},
+    {10087, 1265, 1},
+    {10088, 1266, 1},
+    {10089, 1267, 1},
+    {10090, 1268, 1},
+    {10091, 1269, 1},
+    {10092, 1270, 1},
+    {10093, 1271, 1},
+    {10094, 1272, 1},
+    {10095, 1273, 1},
+    {10096, 1274, 1},
+    {10097, 1275, 1},
+    {10098, 1276, 1},
+    {10099, 1277, 1},
+    {10100, 1278, 1},
+    {10101, 1279, 1},
+    {10102, 1280, 1},
+    {10103, 1281, 1},
+    {10104, 1282, 1},
+    {10105, 1283, 1},
+    {10106, 1284, 1},
+    {10107, 1285, 1},
+    {10108, 1286, 1},
+    {10109, 1287, 1},
+    {10110, 1288, 1},
+    {10111, 1289, 1},
+    {10112, 1290, 1},
+    {10113, 1291, 1},
+    {10114, 1292, 1},
+    {10115, 1293, 1},
+    {10116, 1294, 1},
+    {10117, 1295, 1},
+    {10118, 1296, 1},
+    {10119, 1297, 1},
+    {10120, 1298, 1},
+    {10121, 1299, 1},
+    {10122, 1300, 1},
+    {10123, 1301, 1},
+    {10124, 1302, 1},
+    {10125, 1303, 1},
+    {10126, 1304, 1},
+    {10127, 1305, 1},
+    {10128, 1306, 1},
+    {10129, 1307, 1},
+    {10130, 1308, 1},
+    {10131, 1309, 1},
+    {10132, 1310, 1},
+    {10133, 1311, 1},
+    {10134, 1312, 1},
+    {10135, 1313, 1},
+    {10136, 1314, 1},
+    {10137, 1315, 1},
+    {10138, 1316, 1},
+    {10139, 1317, 1},
+    {10140, 1318, 1},
+    {10141, 1319, 1},
+    {10142, 1320, 1},
+    {10143, 1321, 1},
+    {10144, 1322, 1},
+    {10145, 1323, 1},
+    {10146, 1324, 1},
+    {10147, 1325, 1},
+    {10148, 1326, 1},
+    {10149, 1327, 1},
+    {10150, 1328, 1},
+    {10151, 1329, 1},
+    {10152, 1330, 1},
+    {10153, 1331, 1},
+    {10154, 1332, 1},
+    {10155, 1333, 1},
+    {10156, 1334, 1},
+    {10157, 1335, 1},
+    {10158, 1336, 1},
+    {10159, 1337, 1},
+    {10160, 1338, 1},
+    {10161, 1339, 1},
+    {10162, 1340, 6},
+    {10163, 1346, 1},
+    {10164, 1347, 1},
+    {10165, 1348, 1},
+    {10166, 1349, 1},
+    {10167, 1350, 1},
+    {10168, 1351, 1},
+    {10169, 1352, 6},
+    {10170, 1358, 6},
+    {10171, 1364, 6},
+    {10172, 1370, 6},
+    {10173, 1376, 1},
+    {10174, 1377, 1},
+    {10175, 1378, 1},
+    {10176, 1379, 1},
+    {10177, 1380, 1},
+    {10178, 1381, 1},
+    {10179, 1382, 1},
+    {10180, 1383, 1},
+    {10181, 1384, 1},
+    {10182, 1385, 1},
+    {10183, 1386, 1},
+    {10184, 1387, 3},
+    {10185, 1390, 1},
+    {10186, 1391, 1},
+    {10187, 1392, 1},
+    {10188, 1393, 1},
+    {10194, 1394, 1},
+    {10195, 1395, 1},
+    {10196, 1396, 1},
+    {10197, 1397, 4},
+    {10198, 1401, 4},
+    {10199, 1405, 3},
+    {10200, 1408, 3},
+    {10201, 1411, 3},
+    {10202, 1414, 1},
+    {10203, 1415, 1},
+    {10204, 1416, 1},
+    {10205, 1417, 1},
+    {10206, 1418, 1},
+    {10207, 1419, 1},
+    {10208, 1420, 1},
+    {10209, 1421, 1},
+    {10210, 1422, 1},
+    {10211, 1423, 1},
+    {10212, 1424, 1},
+    {10213, 1425, 1},
+    {10214, 1426, 1},
+    {10215, 1427, 1},
+    {10216, 1428, 1},
+    {10217, 1429, 1},
+    {10218, 1430, 1},
+    {10219, 1431, 1},
+    {10220, 1432, 1},
+    {10221, 1433, 1},
+    {10222, 1434, 1},
+    {10223, 1435, 1},
+    {10224, 1436, 1},
+    {10225, 1437, 1},
+    {10226, 1438, 1},
+    {10227, 1439, 1},
+    {10228, 1440, 1},
+    {10229, 1441, 1},
+    {10230, 1442, 1},
+    {10231, 1443, 1},
+    {10232, 1444, 1},
+    {10233, 1445, 1},
+    {10234, 1446, 1},
+    {10235, 1447, 1},
+    {10236, 1448, 1},
+    {10237, 1449, 1},
+    {10238, 1450, 1},
+    {10239, 1451, 1},
+    {10240, 1452, 1},
+    {10241, 1453, 1},
+    {10242, 1454, 1},
+    {10243, 1455, 1},
+    {10244, 1456, 1},
+    {10245, 1457, 1},
+    {10246, 1458, 1},
+    {10247, 1459, 1},
+    {10248, 1460, 1},
+    {10249, 1461, 1},
+    {10250, 1462, 1},
+    {10251, 1463, 1},
+    {10252, 1464, 1},
+    {10253, 1465, 1},
+    {10254, 1466, 1},
+    {10255, 1467, 1},
+    {10256, 1468, 1},
+    {10257, 1469, 1},
+    {10258, 1470, 1},
+    {10259, 1471, 1},
+    {10260, 1472, 1},
+    {10261, 1473, 1},
+    {10262, 1474, 1},
+    {10263, 1475, 1},
+    {10264, 1476, 1},
+    {10265, 1477, 1},
+    {10266, 1478, 1},
+    {10267, 1479, 1},
+    {10268, 1480, 1},
+    {10269, 1481, 1},
+    {10270, 1482, 1},
+    {10271, 1483, 1},
+    {10272, 1484, 1},
+    {10273, 1485, 1},
+    {10279, 1486, 1},
+    {10294, 1487, 1},
+    {10295, 1488, 1},
+    {10296, 1489, 1},
+    {10297, 1490, 1},
+    {10298, 1491, 1},
+    {10299, 1492, 1},
+    {10300, 1493, 1},
+    {10301, 1494, 1},
+    {10302, 1495, 1},
+    {10303, 1496, 1},
+    {10304, 1497, 1},
+    {10305, 1498, 1},
+    {10306, 1499, 1},
+    {10307, 1500, 1},
+    {10308, 1501, 1},
+    {10309, 1502, 1},
+    {10310, 1503, 1},
+    {10311, 1504, 1},
+    {10312, 1505, 1},
+    {10313, 1506, 1},
+    {10314, 1507, 1},
+    {10315, 1508, 1},
+    {10316, 1509, 3},
+    {10317, 1512, 3},
+    {10318, 1515, 3},
+    {10319, 1518, 3},
+    {10320, 1521, 3},
+    {10321, 1524, 1},
+    {10322, 1525, 1},
+    {10323, 1526, 1},
+    {10324, 1527, 1},
+    {10325, 1528, 1},
+    {10326, 1529, 1},
+    {10327, 1530, 1},
+    {10328, 1531, 1},
+    {10329, 1532, 1},
+    {10330, 1533, 1},
+    {10331, 1534, 1},
+    {10332, 1535, 1},
+    {10333, 1536, 1},
+    {10334, 1537, 1},
+    {10335, 1538, 1},
+    {10336, 1539, 1},
+    {10337, 1540, 1},
+    {10338, 1541, 1},
+    {10339, 1542, 1},
+    {10340, 1543, 1},
+    {10341, 1544, 1},
+    {10342, 1545, 1},
+    {10343, 1546, 1},
+    {10344, 1547, 1},
+    {10345, 1548, 1},
+    {10346, 1549, 1},
+    {10347, 1550, 1},
+    {10348, 1551, 1},
+    {10349, 1552, 1},
+    {10350, 1553, 1},
+    {10351, 1554, 1},
+    {10352, 1555, 1},
+    {10353, 1556, 1},
+    {10354, 1557, 1},
+    {10355, 1558, 1},
+    {10356, 1559, 1},
+    {10357, 1560, 1},
+    {10358, 1561, 1},
+    {10359, 1562, 1},
+    {10360, 1563, 1},
+    {10361, 1564, 1},
+    {10362, 1565, 1},
+    {10363, 1566, 1},
+    {10364, 1567, 1},
+    {10365, 1568, 1},
+    {10366, 1569, 1},
+    {10367, 1570, 1},
+    {10368, 1571, 1},
+    {10369, 1572, 2},
+    {10370, 1574, 1},
+    {10371, 1575, 1},
+    {10372, 1576, 1},
+    {10373, 1577, 1},
+    {10374, 1578, 1},
+    {10375, 1579, 2},
+    {10376, 1581, 1},
+    {10377, 1582, 1},
+    {10378, 1583, 1},
+    {10379, 1584, 1},
+    {10380, 1585, 1},
+    {10381, 1586, 2},
+    {10382, 1588, 2},
+    {10383, 1590, 3},
+    {10384, 1593, 4},
+    {10385, 1597, 4},
+    {10386, 1601, 4},
+    {10387, 1605, 1},
+    {10388, 1606, 3},
+    {10389, 1609, 1},
+    {10390, 1610, 2},
+    {10391, 1612, 1},
+    {10392, 1613, 1},
+    {10393, 1614, 1},
+    {10394, 1615, 1},
+    {10395, 1616, 1},
+    {10396, 1617, 5},
+    {10397, 1622, 1},
+    {10398, 1623, 1},
+    {10399, 1624, 1},
+    {10400, 1625, 1},
+    {10401, 1626, 1},
+    {10402, 1627, 1},
+    {10403, 1628, 1},
+    {10404, 1629, 1},
+    {10405, 1630, 1},
+    {10406, 1631, 1},
+    {10407, 1632, 1},
+    {10408, 1633, 1},
+    {10409, 1634, 1},
+    {10410, 1635, 1},
+    {10411, 1636, 1},
+    {10412, 1637, 1},
+    {10413, 1638, 1},
+    {10414, 1639, 1},
+    {10415, 1640, 1},
+    {10416, 1641, 1},
+    {10417, 1642, 1},
+    {10418, 1643, 1},
+    {10419, 1644, 1},
+    {10420, 1645, 1},
+    {10424, 1646, 3},
+    {10425, 1649, 1},
+    {10426, 1650, 1},
+    {10427, 1651, 1},
+    {10428, 1652, 1},
+    {10429, 1653, 1},
+    {10430, 1654, 5},
+    {10431, 1659, 5},
+    {10432, 1664, 5},
+    {10433, 1669, 1},
+    {10434, 1670, 1},
+    {10440, 1671, 6},
+    {10441, 1677, 5},
+    {10442, 1682, 5},
+    {10443, 1687, 5},
+    {10444, 1692, 1},
+    {10445, 1693, 1},
+    {10446, 1694, 1},
+    {10447, 1695, 1},
+    {10448, 1696, 1},
+    {10449, 1697, 1},
+    {10450, 1698, 1},
+    {10451, 1699, 1},
+    {10452, 1700, 1},
+    {10453, 1701, 1},
+    {10454, 1702, 1},
+    {10455, 1703, 1},
+    {10456, 1704, 1},
+    {10457, 1705, 1},
+    {10458, 1706, 1},
+    {10459, 1707, 1},
+    {10460, 1708, 1},
+    {10461, 1709, 1},
+    {10462, 1710, 1},
+    {10463, 1711, 1},
+    {10464, 1712, 1},
+    {10465, 1713, 1},
+    {10466, 1714, 1},
+    {10467, 1715, 1},
+    {10468, 1716, 1},
+    {10469, 1717, 2},
+    {10470, 1719, 3},
+    {10471, 1722, 1},
+    {10472, 1723, 1},
+    {10473, 1724, 1},
+    {10474, 1725, 2},
+    {10475, 1727, 1},
+    {10476, 1728, 1},
+    {10477, 1729, 1},
+    {10478, 1730, 1},
+    {10479, 1731, 1},
+    {10480, 1732, 1},
+    {10481, 1733, 1},
+    {10482, 1734, 1},
+    {10483, 1735, 1},
+    {10484, 1736, 1},
+    {10485, 1737, 1},
+    {10486, 1738, 1},
+    {10487, 1739, 1},
+    {10488, 1740, 1},
+    {10489, 1741, 1},
+    {10490, 1742, 1},
+    {10491, 1743, 1},
+    {10492, 1744, 1},
+    {10493, 1745, 1},
+    {10494, 1746, 1},
+    {10495, 1747, 1},
+    {10496, 1748, 1},
+    {10497, 1749, 1},
+    {10498, 1750, 1},
+    {10499, 1751, 1},
+    {10500, 1752, 1},
+    {10501, 1753, 1},
+    {10502, 1754, 1},
+    {10503, 1755, 1},
+    {10504, 1756, 1},
+    {10505, 1757, 1},
+    {10506, 1758, 1},
+    {10507, 1759, 1},
+    {10508, 1760, 1},
+    {10509, 1761, 1},
+    {10510, 1762, 1},
+    {10511, 1763, 1},
+    {10512, 1764, 1},
+    {10513, 1765, 1},
+    {10514, 1766, 1},
+    {10515, 1767, 1},
+    {10516, 1768, 1},
+    {10517, 1769, 1},
+    {10518, 1770, 1},
+    {10519, 1771, 1},
+    {10520, 1772, 1},
+    {10521, 1773, 1},
+    {10522, 1774, 1},
+    {10523, 1775, 1},
+    {10524, 1776, 1},
+    {10525, 1777, 1},
+    {10526, 1778, 1},
+    {10527, 1779, 1},
+    {10528, 1780, 1},
+    {10529, 1781, 1},
+    {10530, 1782, 1},
+    {10531, 1783, 1},
+    {10532, 1784, 1},
+    {10533, 1785, 1},
+    {10534, 1786, 1},
+    {10535, 1787, 1},
+    {10536, 1788, 1},
+    {10537, 1789, 1},
+    {10538, 1790, 1},
+    {10539, 1791, 1},
+    {10540, 1792, 1},
+    {10541, 1793, 1},
+    {10542, 1794, 1},
+    {10543, 1795, 1},
+    {10544, 1796, 1},
+    {10545, 1797, 1},
+    {10546, 1798, 1},
+    {10547, 1799, 1},
+    {10548, 1800, 1},
+    {10549, 1801, 1},
+    {10550, 1802, 1},
+    {10551, 1803, 1},
+    {10552, 1804, 1},
+    {10553, 1805, 1},
+    {10554, 1806, 1},
+    {10555, 1807, 1},
+    {10556, 1808, 1},
+    {10557, 1809, 1},
+    {10558, 1810, 1},
+    {10559, 1811, 1},
+    {10560, 1812, 1},
+    {10561, 1813, 1},
+    {10562, 1814, 1},
+    {10563, 1815, 1},
+    {10564, 1816, 1},
+    {10565, 1817, 1},
+    {10566, 1818, 1},
+    {10567, 1819, 1},
+    {10568, 1820, 1},
+    {10569, 1821, 1},
+    {10570, 1822, 1},
+    {10571, 1823, 1},
+    {10572, 1824, 1},
+    {10573, 1825, 1},
+    {10574, 1826, 1},
+    {10575, 1827, 1},
+    {10576, 1828, 1},
+    {10577, 1829, 3},
+    {10578, 1832, 1},
+    {10579, 1833, 1},
+    {10580, 1834, 1},
+    {10581, 1835, 1},
+    {10582, 1836, 1},
+    {10583, 1837, 1},
+    {10584, 1838, 1},
+    {10585, 1839, 1},
+    {10586, 1840, 1},
+    {10587, 1841, 1},
+    {10588, 1842, 1},
+    {10589, 1843, 1},
+    {10590, 1844, 1},
+    {10591, 1845, 1},
+    {10592, 1846, 1},
+    {10593, 1847, 1},
+    {10594, 1848, 1},
+    {10595, 1849, 1},
+    {10596, 1850, 1},
+    {10597, 1851, 1},
+    {10598, 1852, 1},
+    {10599, 1853, 1},
+    {10600, 1854, 1},
+    {10601, 1855, 1},
+    {10602, 1856, 1},
+    {10603, 1857, 1},
+    {10604, 1858, 1},
+    {10605, 1859, 1},
+    {10606, 1860, 1},
+    {10607, 1861, 1},
+    {10608, 1862, 1},
+    {10609, 1863, 1},
+    {10610, 1864, 1},
+    {10611, 1865, 1},
+    {10612, 1866, 1},
+    {10613, 1867, 1},
+    {10614, 1868, 1},
+    {10615, 1869, 1},
+    {10616, 1870, 1},
+    {10617, 1871, 1},
+    {10618, 1872, 1},
+    {10619, 1873, 1},
+    {10620, 1874, 1},
+    {10621, 1875, 1},
+    {10622, 1876, 1},
+    {10623, 1877, 1},
+    {10624, 1878, 1},
+    {10625, 1879, 1},
+    {10626, 1880, 1},
+    {10627, 1881, 1},
+    {10628, 1882, 1},
+    {10629, 1883, 1},
+    {10630, 1884, 1},
+    {10631, 1885, 1},
+    {10632, 1886, 1},
+    {10633, 1887, 1},
+    {10634, 1888, 1},
+    {10635, 1889, 1},
+    {10636, 1890, 1},
+    {10637, 1891, 1},
+    {10638, 1892, 1},
+    {10639, 1893, 1},
+    {10640, 1894, 1},
+    {10641, 1895, 1},
+    {10642, 1896, 1},
+    {10643, 1897, 1},
+    {10644, 1898, 1},
+    {10645, 1899, 1},
+    {10646, 1900, 1},
+    {10647, 1901, 1},
+    {10648, 1902, 1},
+    {10649, 1903, 1},
+    {10650, 1904, 1},
+    {10651, 1905, 1},
+    {10652, 1906, 1},
+    {10653, 1907, 1},
+    {10654, 1908, 1},
+    {10655, 1909, 1},
+    {10656, 1910, 1},
+    {10657, 1911, 1},
+    {10658, 1912, 1},
+    {10659, 1913, 1},
+    {10660, 1914, 1},
+    {10661, 1915, 1},
+    {10662, 1916, 3},
+    {10663, 1919, 1},
+    {10664, 1920, 1},
+    {10665, 1921, 1},
+    {10666, 1922, 1},
+    {10667, 1923, 1},
+    {10668, 1924, 1},
+    {10669, 1925, 1},
+    {10670, 1926, 1},
+    {10671, 1927, 1},
+    {10672, 1928, 1},
+    {10673, 1929, 1},
+    {10674, 1930, 4},
+    {10675, 1934, 1},
+    {10676, 1935, 4},
+    {10677, 1939, 4},
+    {10678, 1943, 4},
+    {10679, 1947, 4},
+    {10680, 1951, 1},
+    {10681, 1952, 4},
+    {10682, 1956, 1},
+    {10683, 1957, 1},
+    {10684, 1958, 1},
+    {10685, 1959, 1},
+    {10686, 1960, 1},
+    {10687, 1961, 2},
+    {10688, 1963, 2},
+    {10689, 1965, 1},
+    {10690, 1966, 1},
+    {10691, 1967, 4},
+    {10692, 1971, 1},
+    {10693, 1972, 1},
+    {10694, 1973, 1},
+    {10695, 1974, 1},
+    {10696, 1975, 1},
+    {10697, 1976, 4},
+    {10698, 1980, 1},
+    {10699, 1981, 1},
+    {10700, 1982, 1},
+    {10701, 1983, 1},
+    {10702, 1984, 1},
+    {10703, 1985, 1},
+    {10704, 1986, 1},
+    {10705, 1987, 1},
+    {10706, 1988, 1},
+    {10707, 1989, 1},
+    {10708, 1990, 1},
+    {10709, 1991, 1},
+    {10710, 1992, 1},
+    {10711, 1993, 1},
+    {10712, 1994, 1},
+    {10713, 1995, 1},
+    {10714, 1996, 5},
+    {10715, 2001, 2},
+    {10716, 2003, 1},
+    {10717, 2004, 1},
+    {10718, 2005, 1},
+    {10719, 2006, 1},
+    {10720, 2007, 1},
+    {10721, 2008, 1},
+    {10722, 2009, 1},
+    {10723, 2010, 1},
+    {10724, 2011, 1},
+    {10725, 2012, 1},
+    {10726, 2013, 1},
+    {10727, 2014, 1},
+    {10728, 2015, 1},
+    {10729, 2016, 1},
+    {10730, 2017, 1},
+    {10731, 2018, 1},
+    {10732, 2019, 1},
+    {10733, 2020, 1},
+    {10734, 2021, 1},
+    {10735, 2022, 1},
+    {10736, 2023, 1},
+    {10737, 2024, 1},
+    {10738, 2025, 1},
+    {10739, 2026, 1},
+    {10740, 2027, 1},
+    {10741, 2028, 1},
+    {10742, 2029, 1},
+    {10743, 2030, 1},
+    {10744, 2031, 1},
+    {10745, 2032, 1},
+    {10746, 2033, 1},
+    {10747, 2034, 1},
+    {10748, 2035, 1},
+    {10749, 2036, 1},
+    {10750, 2037, 1},
+    {10751, 2038, 1},
+    {10752, 2039, 1},
+    {10753, 2040, 1},
+    {10754, 2041, 1},
+    {10755, 2042, 1},
+    {10756, 2043, 1},
+    {10757, 2044, 1},
+    {10758, 2045, 1},
+    {10759, 2046, 1},
+    {10760, 2047, 1},
+    {10761, 2048, 1},
+    {10762, 2049, 1},
+    {10763, 2050, 1},
+    {10764, 2051, 1},
+    {10765, 2052, 1},
+    {10766, 2053, 1},
+    {10767, 2054, 1},
+    {10768, 2055, 1},
+    {10769, 2056, 1},
+    {10770, 2057, 1},
+    {10771, 2058, 1},
+    {10772, 2059, 1},
+    {10773, 2060, 1},
+    {10774, 2061, 1},
+    {10775, 2062, 1},
+    {10776, 2063, 1},
+    {10777, 2064, 1},
+    {10778, 2065, 1},
+    {10779, 2066, 1},
+    {10780, 2067, 1},
+    {10781, 2068, 1},
+    {10782, 2069, 1},
+    {10783, 2070, 1},
+    {10784, 2071, 1},
+    {10785, 2072, 1},
+    {10786, 2073, 1},
+    {10787, 2074, 1},
+    {10788, 2075, 1},
+    {10789, 2076, 1},
+    {10790, 2077, 1},
+    {10791, 2078, 1},
+    {10792, 2079, 1},
+    {10793, 2080, 1},
+    {10794, 2081, 1},
+    {10795, 2082, 1},
+    {10796, 2083, 1},
+    {10797, 2084, 1},
+    {10798, 2085, 1},
+    {10799, 2086, 1},
+    {10800, 2087, 1},
+    {10801, 2088, 1},
+    {10802, 2089, 1},
+    {10803, 2090, 1},
+    {10804, 2091, 1},
+    {10805, 2092, 1},
+    {10806, 2093, 1},
+    {10807, 2094, 1},
+    {10808, 2095, 1},
+    {10809, 2096, 1},
+    {10810, 2097, 1},
+    {10811, 2098, 1},
+    {10812, 2099, 1},
+    {10813, 2100, 1},
+    {10814, 2101, 1},
+    {10815, 2102, 1},
+    {10816, 2103, 1},
+    {10817, 2104, 1},
+    {10818, 2105, 1},
+    {10819, 2106, 1},
+    {10820, 2107, 1},
+    {10821, 2108, 1},
+    {10822, 2109, 1},
+    {10823, 2110, 2},
+    {10824, 2112, 1},
+    {10825, 2113, 1},
+    {10826, 2114, 1},
+    {10827, 2115, 1},
+    {10828, 2116, 1},
+    {10829, 2117, 4},
+    {10830, 2121, 4},
+    {10831, 2125, 4},
+    {10832, 2129, 4},
+    {10833, 2133, 4},
+    {10834, 2137, 1},
+    {10835, 2138, 1},
+    {10836, 2139, 1},
+    {10837, 2140, 1},
+    {10838, 2141, 1},
+    {10839, 2142, 1},
+    {10840, 2143, 1},
+    {10841, 2144, 1},
+    {10842, 2145, 1},
+    {10843, 2146, 1},
+    {10844, 2147, 1},
+    {10845, 2148, 1},
+    {10846, 2149, 1},
+    {10847, 2150, 1},
+    {10848, 2151, 1},
+    {10849, 2152, 1},
+    {10850, 2153, 2},
+    {10851, 2155, 2},
+    {10852, 2157, 2},
+    {10853, 2159, 1},
+    {10854, 2160, 1},
+    {10855, 2161, 1},
+    {10856, 2162, 1},
+    {10857, 2163, 1},
+    {10858, 2164, 5},
+    {10859, 2169, 3},
+    {10860, 2172, 2},
+    {10861, 2174, 1},
+    {10862, 2175, 1},
+    {10863, 2176, 1},
+    {10864, 2177, 1},
+    {10865, 2178, 3},
+    {10866, 2181, 3},
+    {10867, 2184, 3},
+    {10868, 2187, 2},
+    {10869, 2189, 2},
+    {10870, 2191, 1},
+    {10871, 2192, 1},
+    {10872, 2193, 1},
+    {10873, 2194, 1},
+    {10874, 2195, 1},
+    {10875, 2196, 1},
+    {10876, 2197, 1},
+    {10877, 2198, 1},
+    {10878, 2199, 1},
+    {10879, 2200, 1},
+    {10880, 2201, 3},
+    {10881, 2204, 1},
+    {10882, 2205, 1},
+    {10883, 2206, 1},
+    {10884, 2207, 1},
+    {10885, 2208, 1},
+    {10886, 2209, 1},
+    {10887, 2210, 1},
+    {10888, 2211, 1},
+    {10889, 2212, 1},
+    {10890, 2213, 1},
+    {10891, 2214, 1},
+    {10892, 2215, 1},
+    {10893, 2216, 1},
+    {10894, 2217, 1},
+    {10895, 2218, 1},
+    {10896, 2219, 1},
+    {10897, 2220, 1},
+    {10898, 2221, 1},
+    {10899, 2222, 1},
+    {10900, 2223, 1},
+    {10901, 2224, 1},
+    {10902, 2225, 1},
+    {10903, 2226, 1},
+    {10904, 2227, 1},
+    {10905, 2228, 1},
+    {10906, 2229, 1},
+    {10907, 2230, 1},
+    {10908, 2231, 1},
+    {10909, 2232, 1},
+    {10910, 2233, 1},
+    {10911, 2234, 1},
+    {10912, 2235, 1},
+    {10913, 2236, 1},
+    {10914, 2237, 1},
+    {10915, 2238, 1},
+    {10916, 2239, 1},
+    {10917, 2240, 1},
+    {10918, 2241, 1},
+    {10919, 2242, 1},
+    {10920, 2243, 1},
+    {10921, 2244, 2},
+    {10922, 2246, 2},
+    {10923, 2248, 1},
+    {10924, 2249, 1},
+    {10925, 2250, 1},
+    {10926, 2251, 1},
+    {10927, 2252, 1},
+    {10928, 2253, 4},
+    {10929, 2257, 1},
+    {10930, 2258, 1},
+    {10931, 2259, 1},
+    {10932, 2260, 1},
+    {10933, 2261, 5},
+    {10934, 2266, 1},
+    {10935, 2267, 1},
+    {10936, 2268, 1},
+    {10937, 2269, 1},
+    {10938, 2270, 1},
+    {10939, 2271, 1},
+    {10940, 2272, 1},
+    {10941, 2273, 4},
+    {10942, 2277, 1},
+    {10943, 2278, 1},
+    {10944, 2279, 1},
+    {10945, 2280, 1},
+    {10946, 2281, 3},
+    {10947, 2284, 3},
+    {10948, 2287, 3},
+    {10949, 2290, 1},
+    {10950, 2291, 1},
+    {10951, 2292, 1},
+    {10952, 2293, 1},
+    {10953, 2294, 1},
+    {10954, 2295, 1},
+    {10955, 2296, 1},
+    {10956, 2297, 1},
+    {10957, 2298, 1},
+    {10958, 2299, 1},
+    {10959, 2300, 1},
+    {10960, 2301, 1},
+    {10961, 2302, 1},
+    {10962, 2303, 1},
+    {10963, 2304, 1},
+    {10964, 2305, 1},
+    {10965, 2306, 1},
+    {10966, 2307, 1},
+    {10967, 2308, 1},
+    {10968, 2309, 1},
+    {10969, 2310, 1},
+    {10970, 2311, 1},
+    {10971, 2312, 1},
+    {10972, 2313, 1},
+    {10973, 2314, 1},
+    {10974, 2315, 1},
+    {10975, 2316, 1},
+    {10976, 2317, 1},
+    {10977, 2318, 1},
+    {10978, 2319, 1},
+    {10979, 2320, 1},
+    {10980, 2321, 1},
+    {10981, 2322, 1},
+    {10982, 2323, 1},
+    {10983, 2324, 1},
+    {10984, 2325, 1},
+    {10985, 2326, 1},
+    {10986, 2327, 1},
+    {10987, 2328, 1},
+    {10988, 2329, 1},
+    {10989, 2330, 1},
+    {10990, 2331, 1},
+    {10991, 2332, 1},
+    {10992, 2333, 1},
+    {10993, 2334, 1},
+    {10994, 2335, 1},
+    {10995, 2336, 1},
+    {10996, 2337, 1},
+    {10997, 2338, 1},
+    {10998, 2339, 1},
+    {10999, 2340, 1},
+    {11000, 2341, 1},
+    {11001, 2342, 1},
+    {11002, 2343, 1},
+    {11003, 2344, 1},
+    {11004, 2345, 1},
+    {11005, 2346, 2},
+    {11006, 2348, 2},
+    {11007, 2350, 1},
+    {11008, 2351, 1},
+    {11009, 2352, 1},
+    {11010, 2353, 1},
+    {11011, 2354, 1},
+    {11012, 2355, 1},
+    {11013, 2356, 1},
+    {11014, 2357, 1},
+    {11015, 2358, 1},
+    {11016, 2359, 3},
+    {11017, 2362, 2},
+    {11018, 2364, 3},
+    {11019, 2367, 1},
+    {11020, 2368, 1},
+    {11021, 2369, 1},
+    {11022, 2370, 1},
+    {11023, 2371, 1},
+    {11024, 2372, 4},
+    {11025, 2376, 3},
+    {11026, 2379, 2},
+    {11027, 2381, 2},
+    {11028, 2383, 6},
+    {11029, 2389, 1},
+    {11030, 2390, 1},
+    {11031, 2391, 1},
+    {11032, 2392, 3},
+    {11033, 2395, 1},
+    {11034, 2396, 1},
+    {11035, 2397, 1},
+    {11036, 2398, 1},
+    {11037, 2399, 1},
+    {11038, 2400, 1},
+    {11039, 2401, 1},
+    {11040, 2402, 1},
+    {11041, 2403, 1},
+    {11042, 2404, 1},
+    {11043, 2405, 1},
+    {11044, 2406, 1},
+    {11045, 2407, 1},
+    {11046, 2408, 1},
+    {11047, 2409, 1},
+    {11048, 2410, 1},
+    {11049, 2411, 1},
+    {11050, 2412, 1},
+    {11051, 2413, 1},
+    {11052, 2414, 1},
+    {11053, 2415, 1},
+    {11054, 2416, 1},
+    {11055, 2417, 1},
+    {11056, 2418, 1},
+    {11057, 2419, 1},
+    {11058, 2420, 1},
+    {11059, 2421, 1},
+    {11060, 2422, 1},
+    {11061, 2423, 1},
+    {11062, 2424, 1},
+    {11063, 2425, 1},
+    {11064, 2426, 1},
+    {11065, 2427, 1},
+    {11066, 2428, 1},
+    {11067, 2429, 5},
+    {11068, 2434, 1},
+    {11069, 2435, 1},
+    {11070, 2436, 1},
+    {11071, 2437, 1},
+    {11072, 2438, 1},
+    {11073, 2439, 1},
+    {11074, 2440, 1},
+    {11075, 2441, 1},
+    {11076, 2442, 1},
+    {11077, 2443, 1},
+    {11078, 2444, 1},
+    {11079, 2445, 1},
+    {11080, 2446, 1},
+    {11081, 2447, 1},
+    {11082, 2448, 3},
+    {11083, 2451, 1},
+    {11084, 2452, 1},
+    {11085, 2453, 1},
+    {11086, 2454, 1},
+    {11087, 2455, 1},
+    {11088, 2456, 1},
+    {11089, 2457, 1},
+    {11090, 2458, 1},
+    {11091, 2459, 1},
+    {11092, 2460, 1},
+    {11093, 2461, 1},
+    {11094, 2462, 1},
+    {11095, 2463, 1},
+    {11096, 2464, 1},
+    {11097, 2465, 1},
+    {11098, 2466, 1},
+    {11099, 2467, 1},
+    {11100, 2468, 1},
+    {11101, 2469, 1},
+    {11102, 2470, 1},
+    {11103, 2471, 1},
+    {11104, 2472, 1},
+    {11105, 2473, 1},
+    {11106, 2474, 1},
+    {11107, 2475, 1},
+    {11108, 2476, 1},
+    {11109, 2477, 1},
+    {11110, 2478, 1},
+    {11111, 2479, 1},
+    {11112, 2480, 1},
+    {11113, 2481, 1},
+    {11114, 2482, 1},
+    {11115, 2483, 1},
+    {11116, 2484, 1},
+    {11117, 2485, 1},
+    {11118, 2486, 1},
+    {11119, 2487, 1},
+    {11120, 2488, 1},
+    {11121, 2489, 1},
+    {11122, 2490, 1},
+    {11123, 2491, 1},
+    {11124, 2492, 1},
+    {11125, 2493, 3},
+    {11126, 2496, 3},
+    {11127, 2499, 3},
+    {11128, 2502, 3},
+    {11129, 2505, 3},
+    {11130, 2508, 3},
+    {11131, 2511, 3},
+    {11132, 2514, 3},
+    {11133, 2517, 3},
+    {11134, 2520, 3},
+    {11135, 2523, 3},
+    {11136, 2526, 3},
+    {11137, 2529, 3},
+    {11138, 2532, 3},
+    {11139, 2535, 3},
+    {11140, 2538, 3},
+    {11141, 2541, 3},
+    {11142, 2544, 3},
+    {11143, 2547, 3},
+    {11144, 2550, 3},
+}};
+
+} // namespace sunrise::state::record_claims::objective_slot_table

+ 44 - 0
Sunrise/src/state/record_claims/parent_bar_table.h

@@ -0,0 +1,44 @@
+#pragma once
+
+#include <array>
+#include <cstdint>
+
+namespace sunrise::state::record_claims::parent_bar_table {
+
+/**
+ * The value bank index a lore book's parent-triumph bar reads.
+ *
+ * Taken from the record row's own expression at field 136, read out of the shipped tables and
+ * logged from the live build. Eighteen books name a value there; the other twelve name a flag
+ * instead and are absent from this table -- their bars are driven by something else entirely.
+ *
+ * This replaces writing to the node's valueIndex, which resolves for only some books and left
+ * Ecdysis, Trials and Tribulations and The Singular Exegete static despite correct counts.
+ */
+struct Bar {
+    std::uint16_t recordRow;
+    std::uint16_t valueIndex;
+};
+
+inline constexpr std::array<Bar, 18> kBars{{
+    {1588U, 2344U},  // node 823 — Stolen Intelligence
+    {1608U, 2346U},  // node 824 — The Warlock Aunor
+    {1840U, 2520U},  // node 825 — Luna's Lost
+    {1851U, 2521U},  // node 826 — Letters from Eris
+    {2003U, 2574U},  // node 828 — Constellations
+    {2209U, 2664U},  // node 829 — Duress and Egress
+    {1412U, 4619U},  // node 835 — The Book of Unmaking
+    {1578U, 2343U},  // node 836 — For Every Rose, a Thorn
+    {1797U, 2514U},  // node 839 — Unveiling
+    {1809U, 2517U},  // node 840 — Last Days on Kraken Mare
+    {1819U, 2519U},  // node 841 — Inquisition of the Damned
+    {2075U, 2586U},  // node 842 — Trials and Tribulations
+    {2194U, 2662U},  // node 843 — The Singular Exegete
+    {1598U, 2345U},  // node 849 — Ecdysis
+    {1558U, 2341U},  // node 850 — A Man with No Name
+    {1866U, 2516U},  // node 852 — Aspect
+    {1876U, 2518U},  // node 853 — Revelation
+    {2085U, 2584U},  // node 854 — The Liar
+}};
+
+} // namespace sunrise::state::record_claims::parent_bar_table

+ 309 - 41
Sunrise/src/state/record_claims/record_claims.cpp

@@ -2,6 +2,7 @@
 
 #include "record_claims.h"
 
+#include <algorithm>
 #include <array>
 #include <bit>
 #include <cstdio>
@@ -17,6 +18,8 @@
 #include "../build_data/nodes/node_catalog.h"
 #include "../build_data/runtime.h"
 #include "../unlocks/definition.h"
+#include "objective_slot_table.h"
+#include "parent_bar_table.h"
 
 namespace sunrise::state::record_claims {
 namespace {
@@ -35,6 +38,18 @@ constexpr std::size_t kEntrySize = 2 * sizeof(std::uint16_t);
 /** Far above the 2242 records the build ships, and small enough to read in one go. */
 constexpr std::uint32_t kMaximumEntries = 8192;
 
+/**
+ * Completions that have not been claimed live in their own file, beside the claim file.
+ *
+ * Finding lore completes a record without claiming it, and that completion has to outlive the
+ * process the same way a claim does -- otherwise every relaunch forgets what the player collected
+ * and hands the same chapter out again. It is a separate file rather than a column added to the
+ * claim file so the claim format keeps loading unchanged, including files written before this.
+ */
+constexpr std::wstring_view kClaimableFileSuffix = L"\\cache\\record_claimable.bin";
+/** Distinct from kMagic so neither file can ever be read as the other. */
+constexpr std::array<char, 8> kClaimableMagic{'S', 'N', 'R', 'S', 'C', 'M', 'P', '1'};
+
 std::mutex g_lock;
 std::array<std::uint64_t, kWordCount> g_claimed{};
 /** Records complete but not yet claimed. A claim supersedes this, never the other way round. */
@@ -44,6 +59,8 @@ std::size_t g_count{};
 std::uint32_t g_score{};
 core::path::Buffer g_path{};
 bool g_pathReady{};
+core::path::Buffer g_claimablePath{};
+bool g_claimablePathReady{};
 
 void report(const char* stage, const char* result, std::size_t detail) noexcept {
     std::array<char, 128> line{};
@@ -171,6 +188,112 @@ void load_locked() noexcept {
     report("load", "ok", restored);
 }
 
+/** Writes every completion that has not been claimed. The caller holds the lock. */
+void store_claimable_locked() noexcept {
+    if (!g_claimablePathReady) {
+        return;
+    }
+    std::vector<char> document{};
+    document.insert(document.end(), kClaimableMagic.begin(), kClaimableMagic.end());
+    std::size_t held = 0;
+    std::vector<char> entries{};
+    for (std::size_t word = 0; word < g_claimable.size(); ++word) {
+        // A claim is the later state, so a record that reached it needs no claimable row: the claim
+        // file already carries it and load_locked restores it first.
+        std::uint64_t bits = g_claimable[word] & ~g_claimed[word];
+        while (bits != 0) {
+            const auto offset = static_cast<std::size_t>(std::countr_zero(bits));
+            bits &= bits - 1;
+            const auto packedIndex = static_cast<std::uint16_t>(word * kWordBits + offset);
+            constexpr std::uint16_t kUnscored = 0;
+            const auto* indexBytes = reinterpret_cast<const char*>(&packedIndex);
+            const auto* scoreBytes = reinterpret_cast<const char*>(&kUnscored);
+            entries.insert(entries.end(), indexBytes, indexBytes + sizeof packedIndex);
+            entries.insert(entries.end(), scoreBytes, scoreBytes + sizeof kUnscored);
+            ++held;
+        }
+    }
+    const auto count = static_cast<std::uint32_t>(held);
+    const auto* countBytes = reinterpret_cast<const char*>(&count);
+    document.insert(document.end(), countBytes, countBytes + sizeof count);
+    document.insert(document.end(), entries.begin(), entries.end());
+
+    const HANDLE file = CreateFileW(g_claimablePath.chars.data(),
+                                    GENERIC_WRITE,
+                                    0,
+                                    nullptr,
+                                    CREATE_ALWAYS,
+                                    FILE_ATTRIBUTE_NORMAL,
+                                    nullptr);
+    if (file == INVALID_HANDLE_VALUE) {
+        report("store_claimable", "open_fail", held);
+        return;
+    }
+    DWORD written = 0;
+    const auto size = static_cast<DWORD>(document.size());
+    bool complete =
+        WriteFile(file, document.data(), size, &written, nullptr) != FALSE && written == size;
+    complete = CloseHandle(file) != FALSE && complete;
+    report("store_claimable", complete ? "ok" : "write_fail", held);
+}
+
+/** Reads every unclaimed completion the file holds. The caller holds the lock. */
+void load_claimable_locked() noexcept {
+    const HANDLE file = CreateFileW(g_claimablePath.chars.data(),
+                                    GENERIC_READ,
+                                    FILE_SHARE_READ,
+                                    nullptr,
+                                    OPEN_EXISTING,
+                                    FILE_ATTRIBUTE_NORMAL,
+                                    nullptr);
+    if (file == INVALID_HANDLE_VALUE) {
+        report("load_claimable", "absent", 0);
+        return;
+    }
+    std::array<char, sizeof(kClaimableMagic) + sizeof(std::uint32_t)> header{};
+    DWORD read = 0;
+    if (ReadFile(file, header.data(), static_cast<DWORD>(header.size()), &read, nullptr) == FALSE
+        || read != header.size()
+        || std::memcmp(header.data(), kClaimableMagic.data(), kClaimableMagic.size()) != 0) {
+        (void)CloseHandle(file);
+        report("load_claimable", "header_fail", 0);
+        return;
+    }
+    std::uint32_t entries = 0;
+    std::memcpy(&entries, header.data() + kClaimableMagic.size(), sizeof entries);
+    if (entries > kMaximumEntries) {
+        (void)CloseHandle(file);
+        report("load_claimable", "count_fail", entries);
+        return;
+    }
+    std::vector<char> payload(static_cast<std::size_t>(entries) * kEntrySize);
+    read = 0;
+    const bool readOk =
+        payload.empty()
+        || (ReadFile(file, payload.data(), static_cast<DWORD>(payload.size()), &read, nullptr)
+                != FALSE
+            && read == payload.size());
+    (void)CloseHandle(file);
+    if (!readOk) {
+        report("load_claimable", "read_fail", entries);
+        return;
+    }
+
+    std::size_t restored = 0;
+    for (std::uint32_t entry = 0; entry < entries; ++entry) {
+        std::uint16_t index = 0;
+        std::memcpy(&index, payload.data() + static_cast<std::size_t>(entry) * kEntrySize,
+                    sizeof index);
+        if (static_cast<std::size_t>(index) >= kIndexCapacity) {
+            continue;
+        }
+        g_claimable[static_cast<std::size_t>(index) / kWordBits] |=
+            std::uint64_t{1} << (static_cast<std::size_t>(index) % kWordBits);
+        ++restored;
+    }
+    report("load_claimable", "ok", restored);
+}
+
 } // namespace
 
 /** Derives the claim file path and loads any claims already held. */
@@ -183,7 +306,18 @@ bool initialize(void* module) noexcept {
         return false;
     }
     g_pathReady = true;
+    // Claims first: load_claimable_locked restores completions that were never claimed, and a
+    // record that has since been claimed must already be known so it is not double counted.
     load_locked();
+
+    g_claimablePathReady = false;
+    if (core::path::artifact_directory(module, g_claimablePath)
+        && core::path::append(g_claimablePath, kClaimableFileSuffix)) {
+        g_claimablePathReady = true;
+        load_claimable_locked();
+    } else {
+        report("initialize", "claimable_path_fail", 0);
+    }
     return true;
 }
 
@@ -214,6 +348,9 @@ bool claim(std::uint16_t flagIndex, std::uint16_t scoreValue) noexcept {
         // Written per claim rather than at shutdown: a crash must not lose what the client is
         // already showing as Acquired.
         store_locked();
+        // The claimable file lists completions still awaiting a claim, so this index has to leave
+        // it now that the claim supersedes it -- otherwise it is carried in both files forever.
+        store_claimable_locked();
     }
     return true;
 }
@@ -239,23 +376,10 @@ std::size_t apply(std::span<std::uint8_t> accountFlags) noexcept {
         }
     }
 
-    // Claimable records fill in behind the claims: a record that is both stays claimed, since a
-    // claim is the later state and overwriting it would undo what the player did.
-    for (std::size_t word = 0; word < g_claimable.size(); ++word) {
-        std::uint64_t bits = g_claimable[word] & ~g_claimed[word];
-        while (bits != 0) {
-            const auto offset = static_cast<std::size_t>(std::countr_zero(bits));
-            bits &= bits - 1;
-            const std::size_t index = word * kWordBits + offset;
-            if (index >= accountFlags.size()) {
-                continue;
-            }
-            if (accountFlags[index] != unlocks::kFlagAvailable) {
-                accountFlags[index] = unlocks::kFlagAvailable;
-                ++changed;
-            }
-        }
-    }
+    // Claimable records are deliberately left alone here: the completion flag is a 2-bit
+    // redeemed-only field with no value that means claimable (all four were measured), so a
+    // claimable record's flag has to stay clear. What makes it read claimable is
+    // apply_claimable_objectives writing its objective value(s) instead -- see that function.
     return changed;
 }
 
@@ -269,6 +393,28 @@ struct NodeProgress {
     std::span<const char> categories;
 };
 
+/**
+ * The objective slot run one record owns, or an empty span when the table does not name it.
+ *
+ * The slot space was derived and verified in game against thirteen measured points; see
+ * objective_slot_table.h. A record's objectives occupy consecutive slots, so the run is found once
+ * rather than a lookup per slot.
+ */
+[[nodiscard]] std::span<const objective_slot_table::ObjectiveSlot> objective_slots_for(
+    std::uint16_t flagIndex) noexcept {
+    const std::span<const objective_slot_table::RecordEntry> table{objective_slot_table::kRecords};
+    const auto found = std::lower_bound(
+        table.begin(), table.end(), flagIndex,
+        [](const objective_slot_table::RecordEntry& entry, std::uint16_t key) {
+            return entry.flagIndex < key;
+        });
+    if (found == table.end() || found->flagIndex != flagIndex) {
+        return {};
+    }
+    return std::span<const objective_slot_table::ObjectiveSlot>{objective_slot_table::kObjectives}
+        .subspan(found->firstObjective, found->objectiveCount);
+}
+
 /** True when this account flag bank row is held. The caller owns the claim lock. */
 [[nodiscard]] bool claimed_locked(std::uint16_t flagIndex) noexcept {
     if (static_cast<std::size_t>(flagIndex) >= kIndexCapacity) {
@@ -279,6 +425,16 @@ struct NodeProgress {
     return (g_claimed[word] & bit) != 0;
 }
 
+/** True when this account flag bank row is complete but unclaimed. The caller owns the claim lock. */
+[[nodiscard]] bool claimable_locked(std::uint16_t flagIndex) noexcept {
+    if (static_cast<std::size_t>(flagIndex) >= kIndexCapacity) {
+        return false;
+    }
+    const std::size_t word = static_cast<std::size_t>(flagIndex) / kWordBits;
+    const std::uint64_t bit = std::uint64_t{1} << (static_cast<std::size_t>(flagIndex) % kWordBits);
+    return (g_claimable[word] & bit) != 0;
+}
+
 } // namespace
 
 /**
@@ -293,7 +449,11 @@ struct NodeProgress {
         build_data::records::Definition record{};
         if (build_data::find_record_definition(node.children[child], record)
             && record.completionFlagIndex != build_data::records::kUnavailableFlagIndex
-            && claimed_locked(record.completionFlagIndex)) {
+            && (claimed_locked(record.completionFlagIndex)
+                || claimable_locked(record.completionFlagIndex))) {
+            // A book's bar counts chapters FOUND, not chapters redeemed. Finding lore leaves a
+            // record complete and unclaimed, so counting claims alone left every bar reading zero
+            // however many chapters the player had collected.
             ++claimed;
         }
     }
@@ -339,7 +499,7 @@ std::size_t apply_node_progress(std::span<std::int32_t> objectiveValues) noexcep
     // The claim lock is taken first and the node lock inside the walk. Nothing takes them the other
     // way round, so the order cannot close a cycle.
     const std::lock_guard<std::mutex> guard(g_lock);
-    build_data::nodes::for_each_driving(
+    build_data::nodes::for_each(
         &progress, [](void* context, const build_data::nodes::Definition& node) noexcept {
             auto* state = static_cast<NodeProgress*>(context);
             // Only the lore books are counted. Every other category that drives a bar keeps what
@@ -348,30 +508,124 @@ std::size_t apply_node_progress(std::span<std::int32_t> objectiveValues) noexcep
             if (!build_data::nodes::lore_category(node.definitionIndex)) {
                 return;
             }
-            // Two counts, because a category and its parent record keep separate bars. The category
-            // counts every child it owns, the parent among them; the parent's own bar counts only
-            // the chapters, which is why its denominator is one lower. The parent is the child
-            // naming the category's own value slot.
-            const std::int32_t claimed = claimed_children(node);
-            const std::int32_t claimedChapters = claimed - claimed_parent(node);
-            // The parent's bar sits one slot above its category on the books where that slot is
-            // free, confirmed by marker on two of them. Categories are allocated contiguously
-            // though, so for many books the slot above is the next book's category, and writing it
-            // drove the wrong book's bar. Those are skipped: a bar left at zero is wrong, a bar
-            // carrying another book's count is worse. Where those parents read is not yet known.
-            if (static_cast<std::size_t>(node.parentValueIndex) < state->values.size()
-                && state->categories[node.parentValueIndex] == 0) {
-                state->values[node.parentValueIndex] = claimedChapters;
-                ++state->written;
+            // Counted from the records themselves, not from the node's own value slot: a book's
+            // chapters are the children naming a lore row, and its parent triumph is the child
+            // naming none. Both come from record rows, so neither needs the node's slot to have
+            // resolved -- several books ship with no resolvable slot at all and were skipped
+            // entirely while the count keyed on it.
+            std::int32_t chapters = 0;
+            build_data::records::Definition parent{};
+            bool haveParent = false;
+            for (std::size_t child = 0; child < node.childCount; ++child) {
+                build_data::records::Definition record{};
+                if (!build_data::find_record_definition(node.children[child], record)
+                    || record.completionFlagIndex
+                           == build_data::records::kUnavailableFlagIndex) {
+                    continue;
+                }
+                if (record.loreRow == build_data::records::kUnavailableLoreRow) {
+                    parent = record;
+                    haveParent = true;
+                    continue;
+                }
+                // Claimed only: measured in game, a book's bar moves when a chapter's triumph is
+                // claimed, not when finding the lore makes it claimable.
+                if (claimed_locked(record.completionFlagIndex)) {
+                    ++chapters;
+                }
             }
-            if (static_cast<std::size_t>(node.valueIndex) < state->values.size()) {
-                state->values[node.valueIndex] = claimed;
+
+            // The bar reads the value index the parent record's own expression names, read out of
+            // the shipped tables rather than derived: writing the parent record's objective slot
+            // was tried and moved nothing, and the node's valueIndex resolves for only some books.
+            std::int32_t parentSlot = -1;
+            if (haveParent) {
+                for (const auto& bar : parent_bar_table::kBars) {
+                    if (bar.recordRow != parent.definitionIndex) {
+                        continue;
+                    }
+                    if (static_cast<std::size_t>(bar.valueIndex) < state->values.size()) {
+                        state->values[bar.valueIndex] = chapters;
+                        parentSlot = static_cast<std::int32_t>(bar.valueIndex);
+                        ++state->written;
+                    }
+                    break;
+                }
+            }
+            // The category's own bar, where the node resolved a slot for it.
+            if (node.valueIndex != build_data::nodes::kUnavailableValueIndex
+                && static_cast<std::size_t>(node.valueIndex) < state->values.size()) {
+                state->values[node.valueIndex] = chapters + (haveParent ? 1 : 0);
                 ++state->written;
             }
+            std::array<char, 160> line{};
+            const int told = std::snprintf(line.data(), line.size(),
+                                           "ev=claims stage=node_bar node=%u children=%u "
+                                           "chapters=%d parent_slot=%d value_slot=%u",
+                                           static_cast<unsigned>(node.definitionIndex),
+                                           static_cast<unsigned>(node.childCount), chapters,
+                                           parentSlot, static_cast<unsigned>(node.valueIndex));
+            if (told > 0) {
+                core::log::write(core::log::Channel::state, core::log::Level::info,
+                                 {line.data(), static_cast<std::size_t>(told)});
+            }
         });
     return progress.written;
 }
 
+/**
+ * Writes each claimable-and-unclaimed record's authored objective value(s) into the objective
+ * value bank.
+ *
+ * A triumph is claimable when its objective value equals its completionValue and its completion
+ * flag is clear -- the flag has no value that means claimable, only claimed or nothing, so this is
+ * the only bank that can carry the state. objective_slot_table maps a record's completion-flag
+ * index to the slot(s) its objective(s) occupy; a multi-objective record's slots are consecutive,
+ * found here as a run rather than one lookup per slot.
+ */
+std::size_t apply_claimable_objectives(std::span<std::int32_t> objectiveValues) noexcept {
+    std::size_t written = 0;
+    const std::span<const objective_slot_table::RecordEntry> table{objective_slot_table::kRecords};
+    const std::lock_guard<std::mutex> guard(g_lock);
+    for (std::size_t word = 0; word < g_claimable.size(); ++word) {
+        // Only claimable-and-unclaimed records. Writing claimed ones too was tried and redacted
+        // nearly every lore book: record objective slots share the 2746-5686 range with the value
+        // indices some book gates read (4619, 4719, 4991 among them), so writing a value per claim
+        // trampled those gates. A claim already shows through its completion flag.
+        std::uint64_t bits = g_claimable[word] & ~g_claimed[word];
+        while (bits != 0) {
+            const auto offset = static_cast<std::size_t>(std::countr_zero(bits));
+            bits &= bits - 1;
+            const auto flagIndex = static_cast<std::uint16_t>(word * kWordBits + offset);
+            const auto found = std::lower_bound(
+                table.begin(), table.end(), flagIndex,
+                [](const objective_slot_table::RecordEntry& entry, std::uint16_t key) {
+                    return entry.flagIndex < key;
+                });
+            if (found == table.end() || found->flagIndex != flagIndex) {
+                // No objective slot for this record -- nothing this pass can write for it.
+                continue;
+            }
+            for (std::uint8_t slot = 0; slot < found->objectiveCount; ++slot) {
+                const std::size_t objectiveIndex =
+                    static_cast<std::size_t>(found->firstObjective) + slot;
+                if (objectiveIndex >= objective_slot_table::kObjectives.size()) {
+                    break;
+                }
+                const objective_slot_table::ObjectiveSlot& objective =
+                    objective_slot_table::kObjectives[objectiveIndex];
+                // A bank shorter than the slot space is not an error: the tail simply is not sent.
+                if (static_cast<std::size_t>(objective.slot) >= objectiveValues.size()) {
+                    continue;
+                }
+                objectiveValues[objective.slot] = objective.completionValue;
+                ++written;
+            }
+        }
+    }
+    return written;
+}
+
 /** Writes each category's claimed-child count into the character value slot its bar reads. */
 std::size_t apply_character_node_progress(std::span<std::int32_t> characterValues) noexcept {
     NodeProgress progress{characterValues, 0, {}};
@@ -380,13 +634,24 @@ std::size_t apply_character_node_progress(std::span<std::int32_t> characterValue
     build_data::nodes::for_each(
         &progress, [](void* context, const build_data::nodes::Definition& node) noexcept {
             auto* state = static_cast<NodeProgress*>(context);
-            if (!build_data::nodes::lore_category(node.definitionIndex)
-                || node.characterValueIndex == build_data::nodes::kUnavailableValueIndex
-                || static_cast<std::size_t>(node.characterValueIndex) >= state->values.size()) {
+            if (!build_data::nodes::lore_category(node.definitionIndex)) {
                 return;
             }
-            state->values[node.characterValueIndex] = claimed_children(node);
-            ++state->written;
+            if (node.characterValueIndex != build_data::nodes::kUnavailableValueIndex
+                && static_cast<std::size_t>(node.characterValueIndex) < state->values.size()) {
+                state->values[node.characterValueIndex] = claimed_children(node);
+                ++state->written;
+            }
+            // The character-scoped books need their parent bar fed here too: their category
+            // counts from this bank, and their parent's bar is character-scoped as well. The
+            // parent index was resolved through the character value map at extraction.
+            if (node.parentCharacterValueIndex != build_data::nodes::kUnavailableValueIndex
+                && static_cast<std::size_t>(node.parentCharacterValueIndex)
+                       < state->values.size()) {
+                state->values[node.parentCharacterValueIndex] =
+                    claimed_children(node) - claimed_parent(node);
+                ++state->written;
+            }
         });
     return progress.written;
 }
@@ -405,6 +670,9 @@ bool mark_claimable(std::uint16_t flagIndex) noexcept {
     const std::lock_guard<std::mutex> guard(g_lock);
     g_claimable[static_cast<std::size_t>(flagIndex) / kWordBits] |=
         1ULL << (static_cast<std::size_t>(flagIndex) % kWordBits);
+    // Written through immediately, as mark_claimed does: a pickup is the player's progress and has
+    // to survive the process, not just the session that recorded it.
+    store_claimable_locked();
     return true;
 }
 

+ 11 - 0
Sunrise/src/state/record_claims/record_claims.h

@@ -67,6 +67,17 @@ std::size_t apply(std::span<std::uint8_t> accountFlags) noexcept;
  */
 std::size_t apply_node_progress(std::span<std::int32_t> objectiveValues) noexcept;
 
+/**
+ * Writes each claimable-and-unclaimed record's authored objective value(s) into the objective
+ * value bank, so its triumph reads at completionValue while its completion flag stays clear --
+ * the two conditions the client requires before it will offer a claim. The flag itself is never
+ * touched here: writing it can only mean claimed or nothing, never claimable, so claimable is
+ * carried by the objective bank alone.
+ * @param objectiveValues Account value bank, already filled from the authored policy.
+ * @return Number of values this wrote.
+ */
+std::size_t apply_claimable_objectives(std::span<std::int32_t> objectiveValues) noexcept;
+
 /** @return True when this index is already held. */
 [[nodiscard]] bool claimed(std::uint16_t flagIndex) noexcept;
 

+ 6 - 8
Sunrise/src/state/unlocks/definition.h

@@ -34,16 +34,14 @@ using ProgressionBank = std::array<ProgressionLanes, build_data::progressions::k
 inline constexpr std::uint8_t kFlagSet = 2;
 
 /**
- * A record that is complete but not yet claimed, which the client offers as claimable.
+ * A clear acquired flag is stored as zero.
  *
- * The flag reads as a small enum rather than a boolean: zero and two are the values the authored
- * policy uses, and one is unused by it. Finding lore should leave a triumph waiting to be claimed
- * rather than claiming it, which is what this value is for. That reading is inferred from the gap
- * in the enum, not measured, so a record that stays invisible when set to it is the sign it is
- * wrong.
+ * The flag is a 2-bit field and encodes redeemed state only: all four values were measured (0 and
+ * 1 show nothing, 2 -- kFlagSet -- shows claimed, 3 shows nothing) and none of them means
+ * claimable. Claimable is carried by the objective bank instead: a record reads claimable when its
+ * objective value equals its completionValue while this flag stays clear. See
+ * record_claims::apply_claimable_objectives.
  */
-inline constexpr std::uint8_t kFlagAvailable = 1;
-/** A clear acquired flag is stored as zero. */
 inline constexpr std::uint8_t kFlagClear = 0;
 
 /**