# RE-05: D2 Reverse-Engineering Reference (living knowledge base) Everything of lasting value discovered this project, whether or not immediately used. Sources: my static RE of the live 1.13c binary, the Phrozen Keep Discord scrape (`research/kb/discord/`, 52k msgs 2017-2026), and forum threads (`research/kb/forum/`). **All 1.13c addresses are absolute VAs at imagebase 0x6FAB0000** (RVA = VA − 0x6FAB0000) unless noted. --- ## 1. Versions, imagebases, the compatibility boundary Per-version `D2Client.dll` in `Diablo II - Resources\VersionChanger\LoD\`: | Version | imagebase | build ts | note | |---|---|---|---| | 1.07, 1.08 | 0x6FAD0000 | 3b00/3b2e | | | 1.09, 1.09b, 1.09d | 0x6FAA0000 | 3b7c/3c07 | | | 1.10, 1.10 Beta1/2 | 0x6FAA0000 | 3f7c/3f04/3f24 | Necrolis' KB offsets are THIS base | | 1.11, 1.11b | 0x6FAB0000 | 42e6/4302 | **major LoD rewrite** | | 1.12a | 0x6FAB0000 | 483c | | | **1.13c** (ours) | **0x6FAB0000** | 4b95ca3e | live game = C:\Diablo II | | 1.13d | 0x6FAB0000 | 4e9de60a | | **Key fact:** the 1.10→1.11 rewrite changed both code layout AND D2Common export ordinals. Byte-signature translation (`tools/version_xlate.py`) works WITHIN a family (1.11–1.13d, or 1.07–1.10) but NOT across the 1.10/1.11 line. 1.13c does not import D2Common #10030 or #10049. ## 2. D2Client 1.13c addresses — VERIFIED by our RE **Screen / view globals** - `0x6FB8BC48` screen width (1280) · `0x6FB8BC4C` height (720) · `0x6FB8BC50` width copy - `0x6FB8BC54` **coord/cursor center-X** (640 no-panel → 960 panel) · `0x6FB8BC58` center-Y (360) - `0x6FBC9960` **scroll/screen origin-X** · `0x6FBC995C` origin-Y. `ViewportX`/`MouseOffsetX` (1.13d 0x106844) = getter `0x6FAEF6C0` returns `[0x6FBC9960] − offX(0x6FBCC418)`; `0x6FAEF6E0` returns raw `[0x6FBC9960]`. Verified via `version_xlate 1.13d 0x6FB0BC20 1.13c`. Coordinate/mouse layer uses offX; terrain's `nViewXOffset` is a SEPARATE value (why offX-hold fixed units, not terrain). - `0x6FBA9E14` play-area width · `0x6FBA9E18` play-area height−0x28 (set by FOV fn; constant) - `0x6FBA9E20/2C/30` view/play-area struct fields (read by panel-layout setter) **Panel state** - `0x6FBAAD80` **UI panel-open array** (dword per panel): inv `+4` (0x6FBAAD84), char/stats `+8` (0x6FBAAD88), skill `+0x10` (0x6FBAAD90), waypoint `+0x50` (0x6FBAADD0), etc. - `0x6FBCC088` derived "inventory|skill open" · `0x6FBCC08C` derived "ANY panel open" - `0x6FBCC414` **panel layout mode** (0=none,1/2/3=which side) · `0x6FBCC418` **offX shift** (0, ±width/4=±320) **Key functions** - `0x6FAC0DF0` FOV / perspective-dims setup (writes the screen globals; stock, unmodified) - `0x6FABA282` resolution-mode mapper (`cmp eax,0x500`; only literal 1280 in the client) - `0x6FAD8050` UI panel mouse hit-test (reads dims, sets hover flags 0x6FBCC4xx) - `0x6FB00020` HUD center draw (uses width/2) - `0x6FACE700` **panel-layout setter** — writes mode+offX from panel state (calls 0x6FABD0B4, 0x6FAECC60, 0x6FACD8F0, 0x6FB6E2E0). NOT the per-frame view offset. - **Terrain/floor render region `0x6FB29xxx–0x6FB2Axxx`** — tile draw loops; `0x6FB29D30` = tile draw-ORDER insertion sort. Found via Lectem's `(tileFlags>>14)&7` idiom = `shr reg,0xE; and reg,7`. **The 15 mode-based ±width/4 shift transforms** (`mov reg,[0x6FBCC414]` → `xor reg,reg` neutralizes): RVAs (eax): 0x590DE, 0x60275, 0x6A958, 0x6AB96, 0x6AF4D, 0x6B76C, 0x6BCA5, 0x6C51F, 0x6CEB3, 0x6E6AF, 0xC0DB0, 0xC145B; (ecx/edx): 0x84C92, 0x84E10, 0x84E95. ## 3. Structs & enums (from KB — mostly given in 1.10 terms; layouts are stable across versions) ```c // Necrolis — 1.10 @ D2Client.0x6FB9A990, sizeof 0x44. Per-frame floor render params. struct D2FloorTileParamsStrc { BOOL bLowQuality;+00 BOOL bPerspective;+04 BOOL bUpdateRainEffects;+08 BOOL bUpdateLevelEffects;+0C int nRainFactor;+10 DWORD dwCurrentClientFrame;+14 DWORD dwLevelEffectsNextFrame;+18 D2SeedStrc* pClientPlayerSeed;+1C int nScreenPanels;+20 D2ViewStrc* pView;+24 BOOL bDrawEdgeTiles;+28 int nXMin;+2C int nXMax;+30 int nYMin;+34 int nYMax;+38 // tile cull bounds int nViewXOffset;+3C int nViewYOffset;+40 }; // THE view shift // Necrolis 2024 — room cull args struct D2RoomCullArgsStrc { D2ViewStrc* pView;+00 RECT pCullingRect;+04 DRLGRoom* pClientRoom;+14 BOOL bTilesOnly;+18 }; // Tile flags (Necrolis): TILEFLAG_HIDDEN 0x8, FADED 0x400, DRAW_GROUND 0x4000 (layers1-4); // draw-order mask = 0x7<<14 (extract via (flags>>14)&7) // UI panel enum: UI_MINIPANEL 0x15, UI_PARTYSCREEN 0x16, UI_MPTRADE 0x17, UI_MSGLOG 0x18 ... // Play-area rect globals (Conqueror 1.13c decomp, addresses unknown): D2CLIENT_gbVarPositionXmin/Xmax/Ymin/Ymax // -- the "is (x,y) in the world" bounds; the render culls to them; narrow on panel-open. // dzik: D2RendererFunctionsStrc (fpAllocCoreStrc/fpRelease/...) -- debug out of D2GFX+D2GDI. ``` ## 4. Named offsets from KB forum t=69575 ("[1.13d] function sigs & offsets") — **1.13d RVAs** (1.13d & 1.13c share base 0x6FAB0000 but code moved; use `version_xlate.py 1.13d 1.13c`.) - D2CLIENT: GetAutomapSize 0x6FDD0, NewAutomapCell 0x703C0, AddAutomapCell 0x71EA0, RevealAutomapRoom 0x73160, InitAutomapLayer_I 0x733D0, GetMouseXOffset 0x5BC20, GetMouseYOffset 0x5BC30, DrawRectFrame 0x17D10, ClearScreen 0x7AB80 - VARs: ScreenSizeX 0xF7034, ScreenSizeY 0xF7038, MouseOffsetX/Y 0x106844/40, MouseX 0x11C950, MouseY 0x11C94C, AutomapOn 0x11C8B8, AutomapMode 0xF34F8, Offset(POINT) 0x11CF5C, ViewportX/Y 0x106844/40, **ScreenCovered 0x1E8F9** ("which screens are opened") - D2COMMON: GetLayer 0x30B00, AbsScreenToMap 0x35810, MapToAbsScreen 0x35AA0 - D2GFX: DrawLine 0x81A0, DrawRectangle 0x8210, GetScreenSize 0xA940, DrawAutomapCell 0x7C80 (RECT* cliprect) - D2WIN: DrawSprites 0xEAA0, DrawText 0x13B30 ## 5. Necrolis' 1.10 render internals (base 0x6FAA0000; translate to 1.13c is cross-boundary → RE directly) - `MULTIRES_DrawGroundTiles` 0x6FAA2E60 & 0x6FAA3330 · `DrawRoofTiles` 0x6FAA3B90 · `DrawUpperWallTiles` 0x6FAA63E0 · `DrawLowerWallTiles` 0x6FAA6670 · per-tile `Draw_dFloor` 0x6FAA2E70 - Floor tiles from `D2Common #10030 GetFloorTilesFromRoom`; cull rect filled via `D2Common #10049 DUNGEON_CallRoomCallback`. **Culling done in worldspace**: diff unit subtile pos vs world-view subtile pos, cull vs the view rect, then isometric-project to screen. - Inventory/stash/cube/trade panel draw = 1.10 `D2Client.0x3EAC0` (Mnw1995). - Hover-text positioning `D2Win.6F8AA6E0 FONTS_DrawHoverText` (Silvermane): `nXpos=max(0,nXpos); if(nXpos>nGameWidth-nWidth) nXpos=nGameWidth-nWidth-5; else if(nXpos<5) nXpos=5;` ## 6. Doug's existing mod surface (this game) — see RE-00..02 88 D2Client hand patches (a hand-built 1280×720 res engine via codecave @ `0x6FB7DDFB` that spills into D2Common slack @ `0x6FDD87D3`) + 1 D2Common stub + 1 D2Game 2-byte patch @ `0x6FCCF4C7` (D2Game relocates to ~0x02Cxxxxx at runtime). D2gfx/glide3x edits belong to SGD2FreeRes/D2DX. ## 7. Tooling (all in `tools/`) - `d2re.py` — PE parse / diff / capstone disasm / dword-xref helpers. - `dc6.py` — DC6 read + make-transparent (transparent frame = height×0x80 EOL bytes). - `version_xlate.py` — cross-version code-offset translator (byte-sig; within-family only). - `crawl_forum.py` — resumable d2mods.info forum crawler → `research/kb/forum/`. - `discord-kb/` — Chrome extension scraping a channel → JSON (text+code+attachment img URLs; Stop&Save). - `kb_search.py` — search all `research/kb/**/*.json` (AND per message/post). ## 7b. AUTHORITATIVE NAMES (SGD2FreeRes source + mir-diablo-ii-tools/Diablo-II-Address-Table 1.13C.txt) Cross-checked our RE against two open-source RE projects that reimplement the exact code paths. **Every one of our reverse-engineered globals is confirmed and now named** (all D2Client 1.13c): | Our VA | RVA | Authoritative name | Role | |---|---|---|---| | 0x6FBCC414 | 0x11C414 | **ScreenOpenMode** | which side panel(s) open → drives L/R view shift | | 0x6FBCC418 | 0x11C418 | **GeneralPlayAreaCameraShiftX** | camera X-shift when a panel opens (our "offX") | | 0x6FBCB9A0 | 0x11B9A0 | **ScreenShiftX** (+4 = ScreenShiftY 0x11B9A4) | resolution-centering world origin; SGD2FreeRes writes it = (DispW/2−320, −(DispH/2−240)) | | 0x6FB8BC48 | 0xDBC48 | **GeneralDisplayWidth** (+4 = GeneralDisplayHeight 0xDBC4C) | play-area dims | | 0x6FB8BC54/58 | — | (our center-X/Y; ScreenShift-derived) | | | 0x6FBAADA8 | 0xFADA8 | IsAutomapOpen | (0xFADA4 IsGameMenuOpen) | - **Screen-shift setter** (writes ScreenShiftX/Y) = D2Client RVA **0xC39F6 → 0x6FB739F6** (SGD2FreeRes hooks here; found via write-BP on ScreenShiftX). Formula recentres world by half the (res − 640×480) delta. - **Tile-culling-bound setter** = RVA **0x8A4D0 → 0x6FB3A4D0** (SGD2FreeRes hooks; found via the "Perspective" string's first callee). Operates on `CullingSpec {u32 flags; RECT draw_window+4; RECT tile_culling+0x14}`. **Verified in our binary:** it fills tile_culling(+0x14) = (−0x50,−0x50, DispW+0x50, DispH−0x2f) = FULL-SCREEN regardless of panel; sets flags|=1; draw_window(+4) from caller args; perspective branch expands the rect. So the cull box is already full-res → the leftover terrain problem was the SHIFT, not culling. - Renderer clip: floor tiles drawn via `D2Gfx D2GFX_FloorTileDraw(...,nScreenPanels,...)` (d2moo, ord passes ScreenOpenMode through to the video-DLL which does the actual panel clip). D2Client only computes screen-X. - d2tweaks getters: get_view_offset_x/y = base+0x15890/0x158A0. maphack `Offset`(POINT) @ 0x6FBCC1F8 is the world-view ORIGIN (written ONCE @0x6FB10D3E/4C from scrollOrigin 0x6FBC9960/995C ÷ Divisor 0x6FBA16B0 and play-area dims 0x6FBA9E14/18 — NOT from offX/mode); used by coord helpers 0x6FB0F8xx/102xx/10Bxx/11Axx. ## 7c. THE TERRAIN VIEW-SETUP DISPATCHER — floor shift root cause (SOLVED 2026-07-05, corrected) Function `0x6FB29140 .. ~0x6FB29A80` is the per-frame **master world-view configurator**. After handling panel open/close transitions (0x29159..0x29194: calls 0x6FAECCC0/CC60, 0x6FB6E2E0, 0x6FB27DA0), at **0x6FB29199** it tests EVERY panel-open flag (0x6FBAAD88 char/stats, 0x6FBAADD0 waypoint, 0x6FBAADBC/C0, 0x6FBAAE10/14, 0x6FBAADD8; skill panel 0x6FBAAD90 pre-loaded into esi) and dispatches to a case. Each case: `mov [offX],; mov [ScreenOpenMode],; call 0x6FABD1B0; build play-area rect from ecx + [0x6FBA9E14](playAreaW)/[0x6FBA9E18](playAreaH); call 0x6FB3A4D0 (cull-bound setter); jmp draw tail`. The floor tiles project through the **cull/draw rect** this builds (the shifted play-area extent), **NOT** through the offX global. **KEY LESSON — two failed hypotheses before this:** - ✗ v0.6 "hold offX=0 via thread": fixed UNITS (they read offX directly) but not terrain. - ✗ v0.7 "NOP the 3 nonzero offX stores (0x291ED/293D5/29A27, all `A3 18 C4 BC 6F`)": **no effect on terrain** — proved the floor does NOT read offX; it reads the cull/draw rect. (Even the "no-panel" branch 0x6FB293BF computes offX=−DispW/4 and a shifted rect; only sub-branch **0x6FB29412** is truly zero.) - ✓ v0.8 FIX: the all-panels-closed sub-branch **0x6FB29412** sets offX=0 / ScreenOpenMode=0 / cull rect from FULL playAreaW/H (no shift). Overwrite the flag-test head at **0x6FB29199** (`cmp [0x6FBAAD88],ebx` = 39 1D 88 AD BA 6F) with unconditional **`jmp 0x6FB29412`** = `E9 74 02 00 00 90` (rel32 0x274). Terrain is then ALWAYS configured full-screen/centered/unshifted regardless of panels; transition calls preserved; mode=0 written consistently at the game's own setup point (no thread race → avoids the v0.5 black-diamond). feature_panelworld.c layer (C), toggle `[Features] PanelWorldTerrain=1`. If confirmed, layers (A)+(B) may be redundant (offX/mode now written 0 here each frame) — candidate simplification after in-game verification. **v0.8 IN-GAME RESULT (2026-07-05):** SHIFT FIXED ✓ — world now renders unshifted at correct position, char at panel edge (no more push). BUT terrain is still CLIPPED to the non-panel half: region behind/around the panel is black AND non-clickable ("can't click in the black") → the PLAY-AREA RECT is narrowed on panel-open. Branch 0x29412 draws terrain into draw_window=(0,0,playAreaW,playAreaH) [cull-bound setter maps caller ecx=playAreaW, eax=playAreaH → SetRect(&dw,0,0,playAreaW,playAreaH)]. No-panel gameplay uses this same branch at full width, so **playAreaW/H (view struct @0x6FBA9E14/18, RVA 0xF9E14/18) is being shrunk to ~640 when a panel opens** — written via a struct-pointer alias (0 absolute writes; can't static-xref). Same rect governs mouse hit-test → the non-clickable black. Panel-layout setter 0x6FACE700 reconfigures view w/ DispW/2 (sar,1) splits + writes offX/mode + calls cull, but the playAreaW narrowing point isn't visible statically. NEXT: diag.c [key] readout extended to dump DispW/H, playAreaW/H, view20-30, mode/offX, ScreenShiftX/Y, Offset — capture panel closed vs open to pin which field halves, then force it full. (esoteric.dll built+deployed w/ readout 2026-07-05 08:38.) **DIAG CAPTURE RESULT (2026-07-05) — decisive:** (a) v0.8's dispatch patch had NEVER APPLIED — RVA BUG: I wrote `cb+0x29199` but the real RVA of VA 0x6FB29199 is **0x79199** (0x6FB29199−0x6FAB0000=0x79199; I'd subtracted 0x6FB00000 not the true imagebase 0x6FAB0000 → off by 0x50000). The version guard REFUSED (logged `VERIFY FAIL @6FAD9199`). **The v0.7 3-NOP terrain patch (0x291ED/293D5/29A27 → real 0x791ED/793D5/79A27) was ALSO off by 0x50000 and silently refused — so "offX-NOP had no effect" was a MISDIAGNOSIS; the patch simply never ran.** LESSON: RVA = VA − 0x6FAB0000 (NOT VA & 0xFFFFF, NOT −0x6FB00000). (b) `[key]` diff no-panel vs inventory-open: EVERYTHING identical (playAreaW/H=1280/680 both, offX=0 both, ScreenShiftX=320, Offset=0, view20-30 unchanged) EXCEPT **ScreenOpenMode 0→1**. So the terrain half-clip is driven PURELY by mode; baseline gameplay (mode=0) already renders full terrain → forcing mode=0 on panel-open = full terrain. playAreaW is NOT narrowed (earlier hypothesis wrong, now disproven by data). v0.8.1 FIX: dispatch jump at the CORRECT RVA 0x79199 → 0x6FB29412 (writes mode=0/offX=0/full rect). Built+deployed 2026-07-05 09:18 as "0.8.1-terrain-modefix". NOTE: 3 ecx/edx mode-xforms (0x84C92/E10/E95) still VERIFY FAIL (expected bytes mismatch, separate pre-existing issue — units work without them; revisit if needed). **v0.8.1 IN-GAME (2026-07-05): patch APPLIED cleanly (log: OK @6FB29199, branch 1/1) but SAME visual (terrain still half/black).** So forcing mode=0 *inside the per-frame dispatcher 0x6FB29140* does NOT fix terrain → either (a) mode is RE-WRITTEN to 1 after it by the panel-layout setter 0x6FACE700 (`mov [mode],1` @0x6FACE8F9, runs on panel toggle), or (b) the terrain clip is NOT driven by the mode global (it's a renderer scissor set from panel state independently). DECISIVE EXPERIMENT v0.8.2: thread now force-holds ScreenOpenMode=0 every 1ms (toggle `[Features] PanelWorldHoldMode=1`), overriding ALL writers. Outcome tells us: terrain fills → mode IS the driver (multi-written; refine to avoid v0.5 black-diamond); terrain still half → mode is a red herring, pivot to the D2Gfx clip/scissor path (find D2Client's SetClipRect-equiv call gated by panel state). Deployed 09:27. (Recall v0.5 mode=0-hold caused black-diamond culling artifacts — if they reappear that CONFIRMS mode drives terrain culling, and the clean fix becomes: force mode=0 only where terrain reads it for POSITION, not where it reads it for CULL granularity.) **v0.8.2 IN-GAME (2026-07-05): BREAKTHROUGH — mode-hold (PanelWorldHoldMode=1) makes the WORLD RENDER FULL- SCREEN behind/around the panel** (floor+ground full, no black dead-zone, mouseover zones correct, no black- diamond). So ScreenOpenMode IS the terrain driver, written in multiple places; the 1ms thread override wins for floor. RESIDUAL: **walls still shift** (Doug) + mouse cursor shifts on panel CLOSE (minor). Cause: the view- setup fn 0x6FB29140 has TWO structurally-identical dispatches — floor pass head @0x6FB29199, **wall/2nd pass head @0x6FB299B1** (same `cmp [0x6FBAAD88],ebx` chain → jne 0x6FB29A17 shifted / fall-through 0x6FB299E9 full). The 2nd dispatch writes mode=2/offX=DispW/4 immediately before the WALL draw → wins the race vs the thread. v0.9 FIX: force BOTH dispatch heads to their no-shift branch — floor 0x79199→jmp 0x6FB29412 (E9 74 02 00 00 90), walls **0x799B1→jmp 0x6FB299E9 (E9 33 00 00 00 90)**. Both expected `39 1D 88 AD BA 6F`. RVA check: 0x6FB299B1− 0x6FAB0000=0x799B1. Built+deployed 09:46 as "0.9-floor+walls". mode-hold thread kept (belt+suspenders). Remaining-after: mouse-shift-on-close (transient; likely the panel CLOSE transition briefly sets offX before thread catches — investigate if it bothers; Doug deprioritized). **v0.9 IN-GAME: world renders FULL behind ALL panels ✓ (mode-hold did it) but walls STILL shift.** Shift-delta diag (hunt |Δ|~320/640) proved the wall shift is NOT a persistent global (0 candidates w/ 1 panel; the 320/640 hits @0x6FBBB2xx/0x6FBCBDxx are UI PANEL layout coords, read in 0x6FB3C-4C UI code — red herrings). So wall shift = mode/offX read TRANSIENTLY (register) in a per-tile pass, race-lost by the 1ms thread. mode has ~72 writes, offX ~150, across ~20 per-layer view-setups — can't whack-a-mole. KEY STRUCTURE: the dispatch head `cmp [0x6FBAAD88],ebx` = **39 1D 88 AD BA 6F** occurs at **10 sites** (one per view-setup): 0x3B6CB, 0x3C3D1, 0x5099C, **0x79199, 0x7936B, 0x799B1** (all 3 in the TERRAIN fn 0x6FB29xxx), 0x86B60, 0x86E4B, 0x9C6DB, 0xC3B40. Terrain has THREE passes (floor/wall/roof); v0.8-0.9 patched only 2 (0x79199→jmp0x29412, 0x799B1→jmp0x299E9). **v1.0 adds the 3rd: 0x7936B→jmp 0x6FB29412 (E9 A2 00 00 00 90, rel 0xA2)** — its chain shifts to 0x29443 when a panel's open; it's a SEPARATE entry (not reached by the 0x79199 jump). Deployed 10:07. If walls now stable, the world-behind-panels feature is complete. The other 7 heads (0x3B/3C/50/86/9C/C3 regions) are automap/minimap/ overlay view-setups — force them too only if those elements also shift. **v1.0: all 3 terrain-fn dispatches forced but walls STILL shift** → the wall shift is a SEPARATE view-setup. Found it: **0x6FB2F0C3** in the tile region — a DIFFERENT dispatch (tests inventory 0x6FBAAD84 + skill 0x6FBAAD90, not the 7-flag chain) that bakes offX=±DispW/4 into its OWN cull rect (the shift is pre-computed into the per-pass RECT at setup, NOT a thread race — which is why holding offX=0 can't undo it). Forced -> no-shift branch 0x6FB2F110: patch 0x7F0C3 (39 2D 84 AD BA 6F) -> jmp 0x6FB2F110 (E9 48 00 00 00 90). This is the KEY structural lesson: the panel view-shift is implemented as MANY independent per-pass setups, each writing offX/mode then baking it into a per-pass cull RECT via 0x6FB3A4D0; the fix is to force each to its no-shift branch (holding the globals can't help since the rect is already baked). Deployed 14:03 as v1.0 (4 passes). If walls now stable, likely done; if a few structures still slide, sweep the remaining setups the same way (search the `mov [offX],eax; sar eax,2` + panel-flag-cmp idiom). ## 7d. THE CANONICAL PANEL-WORLD SOLUTION (KB breakthrough 2026-07-05) — see RE-06 Searching the newly-scraped forum KB (`research/kb/forum/highvalue/`, 522 posts) surfaced the REAL approach, which reframes the whole feature: - **SwineFlu (t=56507):** "Floor tiles are handled SEPARATELY from wall/roof tiles and units — hence two distinct functions instead of one." → This is WHY un-shifting fixed the floor but not the walls: they are DIFFERENT draw functions. Forcing the floor view-setup only touched the floor pass. - The proper fix (Sluggy's multires, reverse-engineered by **Necrolis, t=59615**, full code in RE-06): when a panel covers part of the screen, the client CULLS the rooms behind it (thinks off-screen). The solution ITERATES nearby rooms not already drawn, and re-runs the additional drawing — with floor vs wall/roof handled by their two separate functions (called recursively per room). - **misiek1294 (2017):** hit our exact symptoms with lolet/Sluggy multires — "screenshift +30x", "when 2 panels open free space holds clicks", "if(UiCover==BOTH) don't draw". `UiCover` = our ScreenOpenMode. - Structs (Sluggy, ~1.09 base 0x10000000): `D2Rect{nWidth,nWidthEx,nHeight,nHeightEx}`, `D2View{dwPad,nWidth,nWidthEx,nHeight,nHeightEx}`, `D2Bounds{dwPad,nBounds,nPad[3],D2Rect pRects[]}`. - **NEXT for walls:** stop treating it as "un-shift"; instead find the 1.13c WALL/ROOF draw function (separate from floor) and either (a) apply the same no-shift view-setup to ITS pass, or (b) implement the additional-room drawing per RE-06. RE-06 has Necrolis's reversed reference to port from. **v1.0 IMPLEMENTATION (2026-07-05): the SwineFlu insight (floor vs wall/roof = separate fns) let me find it.** The tile region 0x6FB29000-0x6FB30000 has exactly SIX panel dispatch heads = TWO functions × THREE passes each: - FLOOR fn 0x6FB29xxx: 3 dispatches (7-flag chain, `cmp [0x6FBAAD88],ebx`): 0x79199→jmp0x29412, 0x7936B→jmp 0x29412, 0x799B1→jmp0x299E9. (all force the all-panels-closed / no-shift branch) - WALL/ROOF fn 0x6FB2Fxxx: 3 dispatches (inv+skill check `cmp [0x6FBAAD84],reg`): 0x7F0C3(ebp)→jmp0x2F110, **0x7F3A9(ebx)→jmp0x2F3F9, 0x7F648(ebx)→jmp0x2F698** (the last two were the residual wall shift). All 6 now forced to their no-shift branch (offX=0/mode=0/full cull rect). Head sigs: floor=39 1D 88 AD BA 6F, wall p1=39 2D 84 AD BA 6F, wall p2/p3=39 1D 84 AD BA 6F. Deployed 14:40 "1.0-wallroof-6pass". If walls now sit still, the world-behind-panels feature is COMPLETE (mode-hold thread now likely redundant — candidate cleanup). ## 7e. THE TERRAIN SHIFT = nViewXOffset (KB gave exact 1.13c struct, 2026-07-05) Doug confirmed (screenshots) the terrain SHIFTS (whole tile scene slides on panel-open) — NOT missing. And the mode-hold band-aid causes the black-diamond culling artifacts (mode drives culling; the 1ms thread race → inconsistent mode → dropped tiles). Mining the KB (Necrolis, 2018-12-24) gave the EXACT 1.13c struct — this is the clean single lever: ```c struct D2FloorTileParamsStrc // sizeof 0x44 - D2Client.0x6FB9A990 (1.13c, base 0x6FAB0000, RVA 0xEA990) { BOOL bLowQuality;+00 bPerspective;+04 bUpdateRain;+08 bUpdateLevel;+0C int nRainFactor;+10 DWORD dwCurrentClientFrame;+14 dwLevelEffectsNextFrame;+18 D2SeedStrc* pClientPlayerSeed;+1C int nScreenPanels;+20 D2ViewStrc* pView;+24 BOOL bDrawEdgeTiles;+28 int nXMin;+2C nXMax;+30 nYMin;+34 nYMax;+38 // tile cull bounds int nViewXOffset;+3C int nViewYOffset;+40 }; // THE view shift, applied by every tile-draw fn ``` Separate draw fns (Necrolis, 1.10 base — confirms SwineFlu "floor≠wall/roof"): `MULTIRES_DrawGroundTiles` 0x6FAA2E60/0x6FAA3330, `DrawRoofTiles` 0x6FAA3B90, `DrawUpperWallTiles` 0x6FAA63E0, `DrawLowerWallTiles` 0x6FAA6670 — ALL take `pView`, ALL project through nViewXOffset. So nViewXOffset (**0x6FB9A9CC**, RVA 0xEA9CC) is the SINGLE terrain-shift value for floor+wall+roof. **v1.1: hold nViewXOffset/nViewYOffset=0 (toggle PanelWorldHoldViewOffset), disable mode-hold (PanelWorldHoldMode=0 → kills diamonds).** Struct accessed via pointer (0 absolute xrefs) so the fill fn wasn't static-findable; the hold tests the hypothesis + diag now dumps TILEPARAMS. If terrain stops shifting & diamonds gone → confirmed; then find the fill writer (via the pointer chain / the DrawGame caller) to patch it race-free. Other KB gold: Lectem's 1.13c perspective code (GetPlayerUnit 0x6FB283D0, D2GFX #10064 SetPerspectiveScale, dwords 0x6FBAF8C4/C8, 0x6FBAC20C); Nefarius/ssjkakaroto 1.13d→1.13c FUNCPTR list (research/kb/forum/highvalue/t69575). ## 7f. FREEZE-MODE test (2026-07-05) — race-free mode=0, the decisive shift/diamond experiment Confirmed dead ends: nViewXOffset @0x6FB9A990 (Necrolis addr) reads all-zeros in our build (incl nXMin/Max) → struct not live there (wrong 1.13c revision or stack-built). mode-hold thread = needed for world-fill (mode drives cull width) but its 1ms race → black diamonds + residual shift. So v1.2 tests the hypothesis that BOTH diamonds & shift are the race: NOP all **70** writes to ScreenOpenMode 0x6FBCC414 (feature layer D, `mov [mode],reg` 6-byte, mode-addr = last 4 bytes verified per-site) → mode permanently 0, race-free. Toggle `PanelWorldFreezeMode=1`. OUTCOMES: (a) world full + NO shift + NO diamonds → race was the whole problem, WIN (then strip the now-redundant thread/dispatch/xform patches); (b) shift and/or diamonds persist → shift is NOT mode-driven → pivot to porting the D2MultiRes redraw (DrawGame hook + iterate culled rooms + separate floor/ wall/roof passes; refs: RE-06, Necrolis draw fns 0x6FAA2E60/3B90/63E0/6670, D2Common room fns from t=69575: GetRoomFromUnit #10331, AddRoomData, AbsScreenToMap/MapToAbsScreen). Deployed 17:16 "1.2-freezemode". ## 8. Open leads (not yet pinned — for future work) - ~~**1.13c `nViewXOffset`** (terrain view shift)~~ **SOLVED §7c** — it's `GeneralPlayAreaCameraShiftX` (offX 0x6FBCC418) re-written = ±DispW/4 by the terrain view-setup fn @0x6FB291DD; fixed by NOP-ing its 3 nonzero stores (feature_panelworld.c layer C). Awaiting in-game confirmation. - ~~**1.13c `gbVarPositionXmin/Xmax/Ymin/Ymax`** (play-area cull rect)~~ **SOLVED §7b** — it's the `CullingSpec` filled by the tile-culling-bound setter @0x6FB3A4D0; tile_culling window is already full-res (DispW/DispH) in our binary, so culling was never the blocker — the shift was. - ~~D2Common ordinal = GetFloorTilesFromRoom / CallRoomCallback~~ **RESOLVED**: ordinals are STABLE 1.10→1.13x (only addresses move). `DUNGEON_GetFloorTilesFromRoom`=**D2Common @10030**, `DUNGEON_CallRoomCallback`=**@10049**, `ClientTileDrawPositionToGameCoords`(≈AbsScreenToMap)=**@10114**, `GameToClientTileDrawPositionCoords`(≈MapToAbsScreen)=**@10115**. D2Gfx tile draw: `FloorTileDraw`=**@10079** (nScreenPanels = arg 8/9), `TileDrawLit`=**@10080** (last arg), `TileDrawTrans`=**@10081** (arg5/6, before nAlpha), `ShadowTileDraw`=**@10082** (last arg). Full table: `research/kb/reference/1.13c-address-tables.md`. nScreenPanels here == ScreenOpenMode/ScreenCovered — the "open panels" value that drives the renderer's behind-panel tile clip. THIS is the fallback lever if layer-(3) terrain clips to half after the offX fix. - `ScreenCovered` 1.13c address — still unpublished anywhere; only recoverable by RE (relocate 1.13d 0x1E8F9's referencing fn, translate to 1.13c). NOT blocking — ScreenOpenMode 0x6FBCC414 is the lever we use.