Closes the mechanism lens's REOPEN (one MUST-FIX) and both lenses' small
residuals on the OP2 rework (b236a442); the blast lens re-review was
CLOSED outright. Fable-direct per the two-failure escalation rule.
- AP-195 filed: UIOption_CheckboxBitfield64 ports HALF of Refresh
@0x004859C0 — the ANY-set checkbox predicate is exact, but the ALL-set
LED media swap (P0x10000082=0x06004D17 / P0x10000083=0x06004D19) and
the ListBox self-sizing tail (ResizeTo/CalculatePaperSize — the block
IS a UIElement_ListBox in retail) are unported, and the block's row
stacking is a second divergent implementation beside UiTemplateListBox.
All due at OP5 before the Chat tab's connected gate; the IsSet doc
comment now names both halves instead of quoting only the ported one.
- Row tooltips: UiButton gains settable TooltipText surfaced through the
shared GetTooltipText hover pipeline (UiCatalogSlot's pattern);
UiCheckboxBitfield64.AddChild applies the row tooltip retail stamps in
CreateChildren @0x00485DF0, and documents that the 0x10000084 row-index
attribute stamp is deliberately replaced by the typed mask closure.
- AD-73 addendum: UiTemplateListBox.ConsumesDatChildren=true is inert
only while no authored Type-5 element carries children — that premise
is now conformance-PINNED across all 32 fixtures (a future DAT
regeneration surfacing an authored child fails the build instead of
silently dropping it).
- Plan doc: OP2's contract names UiTabPanel.cs (retail UIElement_Panel),
not the fictional-class-named UiTabControl.cs; ledger records OP1 and
OP2 both CLOSED.
Full Release suite: 12,871 passed / 4 skipped / 0 failed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
24 KiB
Campaign OP slice OP2 rework — MECHANISM-lens FOCUSED RE-REVIEW
Date: 2026-08-11
Rework under review: b236a442 — "fix(ui): OP2 rework — dormant UiDatElement
subclasses, fixed Panel/CheckboxBitfield64 mechanism"
Round-1 findings being closed: docs/research/2026-08-11-op2-review-mechanism.md
(verdict REJECT on df9c7a35)
Mode: READ-ONLY. No build, no test run, no launch. Every claim is a quote from
committed source, a decode of a committed fixture, or an extract from
docs/research/named-retail/acclient_2013_pseudo_c.txt.
VERDICT: REOPEN — one MUST-FIX, narrowly scoped
All four behavioural MUST-FIXes and all five SHOULD-FIXes from round 1 are genuinely closed, with regression tests that would actually catch a relapse. This is not a redo — it is a one-item fix round.
The one thing that fails is the tail of round-1 MUST-FIX 6, and it is the same
failure mode that caused the round-1 REJECT, on the same element: the commit
asserts a completeness claim about 0x10000520's authored data and about retail's
Refresh that the data and the decomp both refute.
UiCheckboxBitfield64.cs:194-201's doc comment quotesUIOption_CheckboxBitfield64::Refresh @0x004859C0as if the ANY-bit predicate were the whole function. It is not.Refreshcomputes both an all-set and an any-set result and uses them for two different things (§2.2 below). The all-set half drives a per-row LED media swap between the element's two authored RenderSurface DIDs —P0x10000082 = 0x06004D17andP0x10000083 = 0x06004D19, both still present in the regenerated fixture, both still unread anywhere insrc/AcDream.App(verified by grep: zero hits outside one prose mention).- That unported half has no register row. Round-1 MUST-FIX 6 asked for exactly
this ("Also missing rows for … the dropped
0x64/0x10000082/0x10000083authored data").0x64is now consumed; the two DIDs are not, and the register gained only AD-73 (tab dormancy) while TS-72 was retired.
Fix = one AP row naming the unported Refresh media branch + the unported
ResizeTo(GetWidth(), CalculatePaperSize(0,-1)) tail, and a corrected doc comment
that stops presenting a half-quoted Refresh as complete. The port itself belongs to
OP5 with the consumer; nothing is user-reachable today.
1. Finding-by-finding closure check
1.1 UiTabPanel — CLOSED (all five sub-checks)
| Round-1 item | Rework | Evidence |
|---|---|---|
MUST-FIX 5 — cite UIElement_Panel, not the fictional UIElement_TabControl |
✔ | src/AcDream.App/UI/UiTabPanel.cs:8-21 names UIElement_Panel::Create @0x0046C6B7, SetupTabPageHash @0x0046C2E0, Update @0x0046BD00, OpenTab @0x0046BE20, InqTabFromPage @0x0046BEB0, ListenToElementMessage @0x0046BF90, and explicitly records the naming correction. DatWidgetFactory.cs:110-119 carries the same correction at the factory arm. Class renamed UiTabControl → UiTabPanel; the old file is deleted. |
| Switching semantics preserved | ✔ | UiTabPanel.cs:184-199 is byte-equivalent to the round-1 reading of UIElement_Panel::Update @0x0046BD00: exactly one page Visible, every tab button driven Open/Closed via RetailTabBinding.SetOpen → RetailUiStateIds.Open/Closed = retail 0xC/0xB. Nothing in this commit touched RetailTabBinding.cs or RetailUiStateIds. |
| SHOULD-FIX 8 — recursive descendant resolution | ✔ | FindDescendant (UiTabPanel.cs:208-219) is a depth-first descendant walk used for both the button (:149) and the page (:150, :191, :194), matching retail's GetChildRecursive at 0x0046C4EE / 0x0046BD00. FindDirectChild is gone. |
SHOULD-FIX 9 — no invented _tabs[0] fallback |
✔ | UiTabPanel.cs:172-173 — if (defaultEntry is { } def) SwitchTo(def.PageElementId); with no ??=. Independently re-verified against the decomp this round: SetupTabPageHash zeroes m_OpenTabToken/m_OpenPageToken at 0x0046C312/0x0046C318, seeds ebp_1 = 0 at 0x0046C31E, only writes ebp_1/var_3c inside the 0x32-true branch at 0x0046C4DF/0x0046C4E3, and tail-calls Update(this, var_3c, ebp_1) at 0x0046C5EB. With nothing authoring 0x32, that is Update(0,0) against zeroed tokens, and Update's guard arg2 != m_OpenPageToken && arg3 != m_OpenTabToken is false → no switch. Confirmed. |
| SHOULD-FIX 7 — reader skips malformed entries | ✔ | ElementReader.cs:526 — if (buttonId == 0u || pageId == 0u) continue; with the retail filter cited inline at :520-525 (InqProperty(0x30) @0x0046C3E5, InqProperty(0x31) @0x0046C3FC). I re-read both sites: a missing 0x30 jumps label_46c510 and a missing 0x31 jumps the same, both landing on label_46c594 (next entry) without touching either hash. Match. |
Round-1 SHOULD-FIX 3's "a silently-empty table and a wholly-unresolvable table are
indistinguishable" is also addressed: UnresolvedEntries (:118) plus a [D.2b]
diagnostic line (:154-157).
Two retail behaviours in SetupTabPageHash remain unported and are now newly
visible because the rework reads that function properly — both inert on today's data,
both NOTE (§3).
1.2 UiCheckboxBitfield64 — CLOSED on all three named sub-checks
(a) Rows built from the authored template via AddItemFromTemplateList(0). ✔
UiCheckboxBitfield64.AddChild (:140-180) resolves Templates[0] through
TemplateResolver, finds 0x10000219 by recursive descendant search
(FindCheckboxRecursive, :182-192), and stamps label/checked/click onto that
widget. The synthesized ElementInfo{Type=1} and the invented RowHeight = 14f are
deleted; BuildCheckboxBitfield64 is deleted from the factory and the arm is now
0x10000044u => new UiCheckboxBitfield64(info.TemplateList)
(DatWidgetFactory.cs:162). Row geometry now comes from row.Height
(:173-176) — the authored 272×20.
This matches retail CreateChildren @0x00485DF0 as I re-read it this round:
AddItemFromTemplateList(&this->vtable, 0, nullptr) per row → GetChildRecursive(row, 0x10000219) → DynamicCast(1) → SetAttribute_Int(cb, 0x10000084, i) →
SetStringInfo → SetTooltip.
Fixture-verified independently (I decoded options_2100002B.json myself, not the
commit message): 0x10000520 is T=0x10000044, 272×100, zero children, empty
StateMedia, and authors exactly
TemplateList = [{0x2100002B, 0x10000521}]; 0x10000521 is T=3, 272×20, one child
0x10000219. The conformance test pins that exact pair
(OptionsPanelLayoutConformanceTests …Bitfield64Template_BuildsAsEmptyUiCheckboxBitfield64).
(b) IsSet is retail's ANY-bit predicate. ✔ :200-201 —
(CurrentLow & lowMask) != 0 || (CurrentHigh & highMask) != 0. Matches
Refresh @0x00485A11: ebx = ((eax_2 | ecx_2) != 0) ? 0 : 1 →
SetAttribute_Bool(cb, 0xE, ebx == 0).
(c) A multi-bit test that actually discriminates. ✔
UiCheckboxBitfield64_MultiBitMask_IsSet_UsesAnyBitNotAllBits seeds current
0x00000040 against Combat's real two-bit mask 0x00600040 and asserts
row.Selected == true. Under the old all-bits predicate IsSet returns false and
Assert.True(row!.Selected) fails — the test genuinely discriminates, unlike the
single-bit Error case it replaced (which is retained and now also pins that the row
resolves to TemplateCheckboxElementId). It further pins that clicking clears
both mask bits, i.e. the AND-NOT half.
ToggleRow (:209-224) is unchanged and remains correct: turnOn = !IsSet(...)
derives from the bitfield, not from Selected, so a ToggleBehavior self-toggle on
the resolved UiButton cannot double-flip it.
1.3 TS-72 retirement — PARTIALLY CLOSED (the MUST-FIX)
The retirement itself is legitimate. TS-72's stated unknown was the click-toggle bit
math; that is now decomp-confirmed
(ListenToElementMessage @0x00485AE0 → BitUtils::SetBitsOnOrOff @0x004857D0:
arg3 != 0 → OR-in both halves, else AND-NOT both halves). The retirement note at
docs/architecture/retail-divergence-register.md:350 states plainly that the toggle
half was always right and the checked-state half was wrong and is now fixed. Accurate,
and written per register convention (struck row retained with its original text; the
section header's active count moved 41 → 40).
What was swept: the note ends "nothing about its own click/checked mechanism
remains genuinely unverified". Verified is not the same as ported. Reading Refresh
in full (§2.2) shows a decomp-confirmed, unported presentation half of the checked
mechanism, on the same element whose "authors nothing" claim caused round 1's REJECT,
with the two authored DIDs it consumes still sitting unread in the fixture. That gap
has no row anywhere in the register.
AD-73 is correct as far as it goes but is scoped to UiTabPanel only (§1.4).
1.4 The dormancy shape — CLOSED, with one undocumented shape change
Overrides cannot leak pre-activation. I enumerated every member both subclasses add or override:
UiTabPanel |
UiTemplateListBox |
|
|---|---|---|
| base | UiDatElement (:79) |
UiDatElement (:81) |
OnDraw |
not overridden → UiDatElement's authored-media draw |
not overridden |
ClickThrough |
base ctor sets true (UiDatElement.cs:138) |
same |
IUiDatStateful |
inherited unchanged | inherited unchanged |
ConsumesDatChildren |
not overridden → false, children recurse |
overridden true (:145) ← the one change |
AddChild |
not overridden | not overridden |
| import-time hook | OnChildrenAttached is an empty body (:126-128) |
none |
| side-effecting members | ActivateTabBehavior() / SwitchTo() — explicit calls only |
AddItemFromTemplateList(); Scroll getter materialises the viewport (:107) |
UiDatElement itself changed only sealed → public class (:43) and _info →
protected readonly ElementInfo Info (:57) plus mechanical renames. No behavioural
edit in the base. ✔
The one shape change: UiTemplateListBox.ConsumesDatChildren => true is not
identical to the pre-OP2 fallback, which recursed into dat children. I verified the
class doc's justifying claim myself by walking every committed fixture: every
Type-5 element in the corpus has zero authored children —
character 0x1000023D / 0x10000532, effects_positive/effects_negative 0x10000123, examine 0x10000149 / 0x10000335 / 0x1000032D,
mini_game 0x10000174, spellbook 0x10000464, and the three Options ListBoxes
0x100001FA / 0x10000200 / 0x1000050D. So the change is inert today and the claim
holds. It is a latent divergence (a future Type-5 that authors children loses them
silently), and it is not mentioned in AD-73 or anywhere in the register — NOTE, §3.
Tab-table entry counts survived the new 0x30/0x31 skip — character 3, combat
8, spellbook 2, vendor 3, options 4, i.e. the same counts round 1 recorded. The filter
is inert on real data, exactly as retail's is.
AD-73 states the adaptation truthfully. Register :108. I checked each claim:
- "Retail … unconditionally activate the authored default page for ANY instance that
carries a tab table" — correct, and I confirmed the trigger this round:
SetupTabPageHashhas exactly one caller,UIElement_Panel::OnSetAttribute @0x0046C6DC, fired when property0x2Eis applied. Per-instance, unconditional. - Oracle citations
SetupTabPageHash @0x0046C2E0,Update @0x0046BD00,OpenTab @0x0046BE20— all three verified present at those addresses inacclient_2013_pseudo_c.txt. - The four named hosts and their controller ownership match the fixture decode
(character
0x10000227tab=3, spellbook0x100002A8tab=2, vendor0x100000B8tab=3, combat0x100000A2tab=8, plus the tab-table-less vendor backdrop0x1000008D). - The Risk column names the real failure mode (a forgotten
ActivateTabBehavior()leaves every page slot visible at once, silently). Accurate.
AD-73 covers only UiTabPanel. UiTemplateListBox's dormancy (lazy viewport +
ConsumesDatChildren) rides on the same adaptation and is unregistered — NOTE, §3.
1.5 Reader-level tests — CLOSED
tests/AcDream.App.Tests/UI/Layout/ElementReaderTests.cs:354-481 adds five tests that
build a synthetic ElementInfo, stuff a raw UiPropertyValue into
States[DirectStateId].Properties.Values[0x2E/0x64/0x72], and call
ElementReader.ApplyCanonicalLegacyProjection(info) directly.
Would they catch what the fixture tests miss? Yes, decisively. The fixture tests
deserialize ElementInfo.TabTable/TemplateList/ScrollbarElementId as already-
projected JSON fields; LayoutImporter.Build never re-runs the projection at load, so
deleting the body of ReadTabTable would not fail one fixture test. These five drive
the reader functions themselves. Coverage:
ReadTabTable_DecodesButtonPageDefaultInAuthoredOrder— struct-member ids, authored order,0x32present/absent.ReadTabTable_SkipsEntriesMissingButtonOrPage— the new retail filter, with a well-formed entry after two malformed ones, so an early-breakregression fails too.ReadTemplateList_DecodesLayoutDidAndElementIdInAuthoredOrder—0x63/0x62pair, order.…ScrollbarLinkage_DecodesEnumProperty/…AbsentPropertyStaysZero— the0x72value and its absent case.
Gap (NOTE, not a miss of the round-1 finding): none of the five exercises
ReadStructMemberId's Enum/DataId/Integer kind coercion — every test uses
Enum. A regression narrowing that coercion would still slip through.
The blast-radius pins (OP2ReworkBlastRadiusConformanceTests.cs) are the other half
of the answer: six built-widget tests across the five pre-existing Type-8 elements and
one representative Type-5, asserting BehaviorActive == false,
ActivePageElementId == 0, ClickThrough == true, non-zero ActiveMedia() on the
vendor backdrop, 16 children still present and all Visible on combat, and
Empty(Children) + ContentHeight == 0 on the effects list. Those are exactly the
properties the dormancy model rests on.
2. New mechanism infidelity introduced or newly exposed by the rework
2.1 MUST-FIX — Refresh's all-set branch is unported, its two authored DIDs are unread, and neither the doc comment nor the register says so
UIOption_CheckboxBitfield64::Refresh @0x004859C0, in full, per row:
lowHit = m_current.low & mask.low; // eax_2 @0x004859F9
highHit = m_current.high & mask.high; // ecx_2 @0x004859FB
allSet = (lowHit == mask.low && highHit == mask.high); // var_d_1 @0x004859FF..0x00485A0A
ebx = ((lowHit | highHit) != 0) ? 0 : 1; // anySet @0x00485A11
row = UIElement_ListBox::GetItem(this, i); // @0x00485A21
cb = UIElement::GetChild(row, 0x10000219); // @0x00485A35
UIElement::SetAttribute_Bool(cb, 0xE, ebx == 0); // checked <=> ANY set ← ported ✔
if (ebx == 0) { // @0x00485A51 — only when ANY set
led = UIElement::GetFirstChildElement(cb); // @0x00485A5A (0x10000328)
if (led) {
if (allSet && GetAttribute_DataID(this, 0x10000082, &v)) // @0x00485A69/0x00485A7C
UIElement::SetMediaImageForState(led, v, 1, 6); // @0x00485AA7
else if (GetAttribute_DataID(this, 0x10000083, &v)) // @0x00485A9A
UIElement::SetMediaImageForState(led, v, 1, 6);
}
}
Retail therefore renders three row states, not two: unchecked, checked-partial
(0x10000083), checked-complete (0x10000082). Eight of the thirteen Chat filter
masks are multi-bit (Gameplay 0x83912021, Combat 0x00600040, Magic 0x00020080,
AreaSpeech 0x00001004, Tells 0x00000018, Allegiance 0x00040C00, …), so the
partial state is reachable in ordinary play, not a corner case.
Evidence of the gap:
src/AcDream.App/UI/UiCheckboxBitfield64.cs:194-201quotesRefreshaschecked = (lowHit | highHit) != 0and stops there — theallSetcomputation and the entire media branch are absent from both the code and the citation.RefreshRowVisuals(:226-230) writes onlyrow.Toggle.Selected.grep -i '0x10000082\|0x10000083\|10000328\|0x10000084' src/AcDream.App/**/*.csreturns exactly one hit, a prose mention atUiCheckboxBitfield64.cs:28. The two DIDs are read by no code path;ElementReaderdoes not project them ontoElementInfoeither.- They are unambiguously authored: decoded from
options_2100002B.json, element0x10000520DirectState carriesP0x10000082 = DataID(0x06004D17)andP0x10000083 = DataID(0x06004D19)alongsideP0x64. docs/architecture/retail-divergence-register.mdgained AD-73 only. No AP/TS row names this.
Adjacent and in the same fix: retail's CreateChildren tail is
ResizeTo(GetWidth(), CalculatePaperSize(0, -1)) at 0x00485E87/0x00485E97 — the
block sizes itself to its rows because it is a non-scrolling UIElement_ListBox
nested inside the Chat page's scroller. UiCheckboxBitfield64 tracks _contentHeight
(:53, :174-175) but never applies it to Height, so the block stays at its
authored 272×100 while thirteen 20 px rows need 260 px. Also unported, also unrowed.
Required: one AP row covering both (unported Refresh media branch + unported
ResizeTo/CalculatePaperSize), naming 0x10000082/0x10000083/0x10000328 and
pointing at OP5 as the consumer gate; and a corrected IsSet doc comment that quotes
Refresh completely instead of half. The port itself can land at OP5 with the
consumer — nothing user-reachable observes it today.
2.2 SHOULD-FIX — AddChild drops retail's tooltip and per-row index stamp
CreateChildren @0x00485E50/@0x00485E65 calls SetStringInfo(cb, siLabel) and
SetTooltip(cb, siTooltip); @0x00485E3E stamps SetAttribute_Int(cb, 0x10000084, i). acdream sets checkbox.Label (:169) and stores tooltip in the Row record
(:178) but never applies it; there is no settable tooltip on UiElement (only a
virtual GetTooltipText() returning null, UiElement.cs:454), so this needs a
one-line override or a settable property. The 0x10000084 index stamp is correctly
replaced by a mask closure (:171) — equivalent and better typed; call that out in
the doc rather than leaving it silently absent.
2.3 SHOULD-FIX — the ListBox mechanism is now implemented twice, divergently
UIOption_CheckboxBitfield64 is a UIElement_ListBox in retail (it calls
AddItemFromTemplateList/GetItem/CalculatePaperSize/ListenToElementMessage on
itself; PostInit tail-calls UIElement_Scrollable::PostInit). The rework's class
doc argues against reusing UiTemplateListBox because "retail's block does not itself
scroll" — an assertion with no citation, and one the CalculatePaperSize/ResizeTo
tail partly contradicts (the block does do ListBox self-sizing). The result is two
independent row-stacking implementations that already differ: UiTemplateListBox
stacks into a lazily-created UiScrollablePanel
(UiTemplateListBox.cs:172-187), UiCheckboxBitfield64 stacks directly onto itself
(:173-176). Not a defect today; it is the seam where §2.1's ResizeTo gap and any
future ListBox fix will diverge. Either reuse the class or record why in the register
alongside §2.1's row.
3. NOTES
UiTemplateListBox.ConsumesDatChildren => true(:145) is the one place the dormant subclass is not byte-identical to theUiDatElementfallback. Inert today (all ten pre-existing Type-5 elements have zero authored children — verified across every committed fixture), unregistered, and silent if a future Type-5 authors children. One sentence in AD-73 or its own row.UiTemplateListBox.Scroll(:107) is a side-effecting getter — reading it materialises and attaches the viewport, breaking dormancy without any explicit activation call.ContentHeight(:111) andLineHeight(:116-124) are both null-safe;Scrollis the odd one out. Nothing reads it today.- AD-73 scopes dormancy to
UiTabPanelonly.UiTemplateListBox's lazy viewport is the same adaptation against retail's always-scrollable ListBox and rides uncovered. SetupTabPageHash's early return is unported. At0x0046C4EE/0x0046C4F5retail doesGetChildRecursive(this, tabId)and, on a miss, returns outright — abandoning the remaining entries and never callingUpdate.UiTabPanellogs the miss, keeps iterating, and still performs the default switch (:149-173). Inert on today's data (every options entry resolves) and arguably the better diagnostic, but it is a divergence the round-1 SHOULD-FIX 3 response introduced.tab != pageandMasterPropertyId == 0x2Ffilters still unimplemented (0x0046C3A6,0x0046C46B). Retail adds the hash entries either way and only gates the default-flag read andSetMouseVisible(1)ontab != page. Harmless today. Carried from round-1 NOTE 12.SetMouseVisible(1)per tab button (0x0046C4FB) andBroadcastElementMessage(this, 0x2C, …)after eachUpdateremain unported. OP3 needs the second for research §3.6's apply/revert semantics. Carried from round-1 NOTE 14.0x71(horizontal scrollbar) still unread;ScrollbarElementIdis specifically the vertical link (UIElement_Scrollable::OnSetAttribute @0x00474630) and is still not named as such. Carried from round-1 NOTE 13.SaveCurrentValue/RestoreSavedValue/Changed(@0x004856A0–@0x00485700) are unported onUiCheckboxBitfield64— the Apply/Cancel triad OP5 needs. Not introduced here;SetDefaultValue/RestoreDefaultValueshipped in OP2 as-is.SpriteResolveandLabelFontare now dead onUiCheckboxBitfield64— the factory no longer setsSpriteResolve(the row subtree carries its own resolver) andLabelFont's own doc says it is unused (:80-82). Harmless; delete at OP5.- The campaign plan is stale.
docs/plans/2026-08-10-options-panel-campaign.md:181still says "Createsrc/AcDream.App/UI/UiTabControl.cs" — the exact fictional-class name this round corrected — and still cites "lane A §5" for the switching contract when the tab table is §1.3 and its host §1.4 (round-1 NOTE 18, unaddressed). OP3/OP8 will read that line. - Reader tests use only
Enum-kind struct members, soReadStructMemberId'sEnum/DataId/Integercoercion tolerance is unpinned. IUiChildrenAttachedListenernow has exactly one implementer with an empty body (UiTabPanel.cs:126-128). Still the right hook for OP3's activation plumbing; just no longer load-bearing in this commit.- Research §1.5's "
0x10000521… UNKNOWN consumer" / §10.5 U10 are now closed by the fixture +CreateChildren, and the research doc still says UNKNOWN. Carried from round-1 NOTE 17.
4. What I re-derived independently this round
Everything below was read from primary source, not taken from the commit message:
| Claim | Source |
|---|---|
SetupTabPageHash has exactly one caller, OnSetAttribute on property 0x2E |
acclient_2013_pseudo_c.txt 0x0046C6C0–0x0046C6DC (grep: 2 hits total for the symbol) |
Zeroed tokens + Update(0,0) ⇒ no default switch |
0x0046C312, 0x0046C318, 0x0046C31E, 0x0046C4DF, 0x0046C5EB; Update guard 0x0046BD00 |
Missing 0x30/0x31 skips the entry |
0x0046C3E5, 0x0046C3FC → label_46c510 → label_46c594 |
Refresh computes all-set AND any-set, and uses all-set for the LED media swap |
0x004859F9–0x00485AA7 (full function read) |
SetBitsOnOrOff = OR-on / AND-NOT-off |
0x004857D0–0x0048580B |
CreateChildren = per-row AddItemFromTemplateList(0) + GetChildRecursive(0x10000219) + index/label/tooltip + ResizeTo(CalculatePaperSize) |
0x00485DF0–0x00485E97 |
0x10000520 authors P0x64 = [{0x2100002B, 0x10000521}], P0x10000082 = 0x06004D17, P0x10000083 = 0x06004D19, zero children, empty StateMedia |
decode of committed options_2100002B.json |
0x10000521 is T=3, 272×20, one child 0x10000219 |
same |
| Every Type-5 element in every committed fixture has zero authored children | walk of all 32 committed fixtures |
| Tab-table entry counts unchanged post-regeneration (character 3 / combat 8 / spellbook 2 / vendor 3 / options 4) | same walk |
Vendor 0x1000008D is T=8 with no tab table (the backdrop) |
same walk |
Fixture decodes used a throwaway reader in the scratchpad. Nothing in the repository was modified except this document.