From 8b554d4de4c66e91a6df91efb716d6dedad493bd Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 10 Aug 2026 12:32:09 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20CH6a+CH6b=20review=20REJECT=20findings?= =?UTF-8?q?=20=E2=80=94=20invisible=20borders,=20wrong=20window-id=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 --- .../2026-08-10-ch6ab-review-findings.md | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 docs/research/2026-08-10-ch6ab-review-findings.md diff --git a/docs/research/2026-08-10-ch6ab-review-findings.md b/docs/research/2026-08-10-ch6ab-review-findings.md new file mode 100644 index 00000000..e2f9a655 --- /dev/null +++ b/docs/research/2026-08-10-ch6ab-review-findings.md @@ -0,0 +1,129 @@ +# CH6a+CH6b review findings — `1fd51543` + `22020ef2` (REJECT; rework list) + +Dual-lens Opus review, 2026-08-10. The retail research held up under +independent re-derivation (Type-2/Type-9 correction, filter masks, Alt+1..4, +pure-mirror indicators all CONFIRMED); the rework list below is what did not. + +## BLOCKER 1 — CH6a: chat window loses 7 of its 8 authored border pieces + +`UiResizeGrip : UiElement` (`UiResizeGrip.cs:33`) inherits the no-op +`UiElement.OnDraw` and is constructed WITHOUT the `ElementInfo`/`resolve` +pair (`DatWidgetFactory.cs:270-278`) — the seven Type-9 grips render +NOTHING. Pre-CH6a, Type 9 fell through to `UiDatElement` (whose class doc +names resize grips explicitly). Same commit also hides the eight `_Locked` +media twins (`ChatWindowController.cs:239-245`) and switches to +`Chrome = RetailWindowChrome.Imported` (`RetailWindowFrame.Mount:133-142` +makes outerFrame = content, no wrapper chrome). Fixture proof: all 8 border +elements carry real art (0x06006129/2A/2B/2C/2D family); the window root has +NO background image. Net: only the 400×5 top strip (Type 2 → UiDatElement) +draws — no left/right/bottom edges, no corners. + +**Fix:** `UiResizeGrip` derives from `UiDatElement` (or carries info+resolve +and draws its active state's media), keeping `ClickThrough = false`; +`BuildResizeGrip(info)` → `BuildResizeGrip(info, resolve)`. Conformance +assertion: each of the seven grips resolves a non-zero sprite. + +## SHOULD-FIX 2 — CH6b: wrong window-id model asserted as decomp fact + +`ChatWindowState.cs:31-42` claims `UpdateFromPlayerModule @0x004F3920` +early-returns for the MAIN window and overloads id 0 as both "main" and +"broadcast". Decomp refutes both: `PostInit @0x004F3DD0` switches on +`m_eWindowID - 1` (case 0→0xFBFFFFFF, 1→0x101C, 2→0x40C00, 3→0x80000, +4→0x78000000, 7→0xFBFFFFFF); `gmChatOptionsUI::InitOptions @0x0049FC60` +builds five per-window filter blocks with SetUserData window ids **8** +(main, default 0xFBFFFFFF @0x0049FDC9), then **2/3/4/5** (@0x0049FF4E, +:FF71, :FF93, @0x004A0000 default 0x78000000). Retail: main window is +`m_eWindowID == 8`; floaties are 2–5; `m_eWindowID == 0` means UNAUTHORED +(ctor default — what the UpdateFromPlayerModule guard is for); wire +`arg5 == 0` is a pure broadcast sentinel, never a window id. + +Consequences: the MASKS are correct once re-indexed (confirmed); the main +window's filter IS user-settable in retail (first block on the options +page, with a dedicated high-dword Society child @0x0049FEFB — so `0x20` +Society is opt-in on main; acdream's `accept: null` main view over-displays +it today); `SetFilter(0,…)` hard no-op (`:119`) blocks the future options +UI; the 0-overloading makes AP-180's `m_idCurrentCommandSource` routing +inexpressible. `ShouldDisplay_MainWindow_ShowsEveryBroadcastRegardlessOfSeededFilter` +(`:137-146`) pins the wrong behavior including 0x1A displaying in main. + +**Fix:** separate the broadcast sentinel from the main-window id — either +carry retail's real ids (main 8, floaties 2–5) in `ChatWindowState`, or +keep 0–4 internally plus an explicit `BroadcastTargetWindow` constant +distinct from every id; drop the SetFilter main no-op; give the main window +a real accept predicate (default 0xFBFFFFFF, i.e. everything except 0x1A +and with the high dword zeroed so Society is opt-in) in +`ChatWindowController.GetTranscriptLines`; correct the class doc; re-point +the test. + +## SHOULD-FIX 3 — CH6b: indicator buttons self-toggle on click + +`UiButton.cs:438-439`: `_pressed && _pointerOver && Enabled && +ToggleBehavior → _selected = !_selected`; the fixture shows +0x10000522/0x10000523 carry DAT property `0x0B = true`, so a click flips +Highlight↔Normal art with no visibility change — the mirror lies until the +next real toggle. Retail: clicking does nothing (0x10000522–0x10000525 +appear ONLY in `RecvNotice_SetPanelVisibility @0x004CCD80`). The existing +test asserts `OnClick == null` (wrong level) on a synthetic fixture without +0x0B. **Fix:** a `SuppressSelfToggle` opt-out checked at `UiButton.cs:438`, +set in `ChatWindowController.Bind` with the @0x004CDA80 citation; test +presses/releases an indicator built WITH 0x0B=true, state unchanged. + +## SHOULD-FIX 4 — CH6b: 0x2100005B never dumped (research doc's explicit instruction) + +No `chat_floaty_2100005b.json` fixture; `FloatingChatWindowControllerTests` +is fully synthetic with Type-3 stand-ins. Three untested production +assumptions: `FloatingChatWindowController.cs:122` (`FindElement(InputId) +as UiField` — if 0x10000016 isn't Type-12 with Editable 0x16, all four +windows mount non-interactive), `:199` (`TitleBarId is UiText` — if it +imports as UiDatElement the "Chat N" title never renders), `:211` +(`CloseButtonId is UiButton` — no test at all). The dump's `widget_kind` +is media-presence-driven and cannot settle element types. **Fix:** add +`0x2100005B` to `RetailLayoutFixtureGenerator.Layouts`, commit the fixture, +conformance tests pinning the resolved widget types of 0x10000016, +0x10000019, 0x100004D9, 0x1000052A, and the border/corner element types +(also determines whether floaties resize from real grips). + +## SHOULD-FIX 5 — CH6b: missing register row for the shared-transcript model + +Retail: per-ChatInterface `m_chatLog`, truncated at 10,000 +(`RecvNotice_DisplayFinalStringInfo @0x004F4711` → TruncateChatLog); a +closed window keeps accumulating (`gmFloatyMainChatUI::SetVisible +@0x004CE9B0` never unregisters the handler). acdream: one 500-entry +ChatLog with a 200-line display tail each window filters — accumulate- +while-closed and per-window scroll fall out correctly, but the EFFECTIVE +per-window scrollback depth differs (a fellowship-only window retains up to +10,000 fellowship lines in retail; here only those inside the last 200 +shared lines). **Fix:** file the register row (AP series) describing the +depth divergence. + +## NITs + +N1 — filter persistence saves only from `/saveautoui` (`SaveLayout()`), +while geometry/visibility auto-save; harmless today, note for CH6e. +N2 — `ChatWindows.ResetToDefaults()` called only in Dispose; not resetting +across reconnect is likely correct but undocumented — add the one-line +comment (deliberate, user preference survives reconnect). +N3 — research doc §1.3 still says UNVERIFIED for the modifier decode +(CH6b resolved it: MetaKeys 1=Shift, 2=Ctrl, 3=Alt→mask 0x4, 4=Win — also +fix the §1.3 "0x00000002 = shift" mislabel). +N4 — §1.4's "the ONLY function that branches on idMessage == 1" superlative +is false (`gmFloatyChatUI::ListenToElementMessage @0x004CE330` does too); +the substantive claim stands; fix the wording. +N5 — `ChatTranscriptRenderer.BuildLines` calls back into +`ChatWindowController.WrapText`; move WrapText into the renderer to close +the circular dependency. + +## CONFIRMED-OK (do not churn) + +CH6a layout geometry vs the independent 2026-06-25 dump; the Type-2/Type-9 +correction; `DecodeBorderLocation` vs `StartMouseResizing @0x0046B7E0`; +deleted compensations have no survivors and each removal is DAT-justified; +cursor ids byte-exact; AP-185 accurate; the UiRoot grip/dragbar priority +change is retail-correct. CH6b masks (via the options page, independent of +PostInit); Alt+1..4 proven; pure-mirror indicators (the finding, not the +implementation); closed-window accumulation reproduced; SpewBox 0x1A +exclusion survives everywhere; ChatTranscriptRenderer behavior-identical +for main; layering clean (Core presentation-free, single canonical +instance, zero env reads, zero GameWindow changes); input pipeline +conventions followed; visibility chokepoint genuine; AP-187/AP-188/#369 +accurate.