Repository navigation
Releases: VTSTech/D2LoD-libslang-vk
Release list
D2LoD Vanilla+0.0.7-R98
Release notes — R88 → R98
R88 — Vanilla+ 0.0.7-R88 — 2026-11-02 — tag R88 @ 9526633c (merge 1d401785)
The second upstream sync of the 0.0.7 line — and the SUPERSESSION LEDGER OPENS.
Upstream c8445e73..bc47c6bc (9 commits) merged with the Vanilla+ surface intact:
Ghidra DB 1839 → 1851, byte match 6,232 → 6,242 of 14,642 functions (19.50% →
19.54% of bytes), 22 conflicts / 27 hunks resolved. Where upstream recovered the
retail form of a fork fix, upstream wins and the fork keeps the root-cause note:
- fix 16 (Defect 13: SFX silent, music OK) superseded by upstream's
D2FloatIntUnion— same root cause, retail-faithful form. - The R54/R85 quest-gossip hand-tables (#13,
#22) superseded by
upstream's verbatim retail tables — all 39aNpcGossipDataexterns now sized
retail-exact (28 were unsized); the track-J singular-table watch resolves. - Sound constants take upstream's exact decoded decimals (the
-2000.0centibel
diagnosis preserved). - Checked and NOT superseded: the issue #11
[glide fix 11]hook (the wrapper ships from theGlide3xVKbranch, which
upstream does not carry).
Gates 326/326 (10 stale expectations re-pinned to the superseding state).
Field-run green 2026-11-03 riding the R91 pass.
R89 — Vanilla+ 0.0.7-R89 — 2026-11-02 — tag R89 @ 4014ef65
The monster-components data-model fix (issue #8 hardening).
Raised skeleton/monster component bytes were overlaying pUnitDataPlayer->szCharName[4..];
they now write the monster unit-data union (pUnitDataMonster->Components[0x10],
->pAiGeneral) with null guards (SKILLNEC_SetSkeletonComponentsByMasteryLvl,
D2Game/UNIT/Monster.cpp). Skeleton classes 0x16b/0x16c get their palshift.dat
ownership polarity forced from the live hostility check regardless of byUtrans
(D2Client/UNIT/CUnit.cpp). Field-verified 2026-11-03 riding the R90b run —
"Skeletons are finally white again" (issue #8).
R90 / R90b — Vanilla+ 0.0.7-R90(b) — 2026-11-03 — tags R90 @ fff470d7 + R90b @ 3726f3ce (merge 77e87e02)
The third upstream sync + two Windows-build blockers fixed.
Upstream bc47c6bc..57380e21 (2 commits) merged: Ghidra DB 1851 → 1855, byte match
6,273 of 14,642 functions (19.51% of bytes), 3 conflicts / 9 hunks. The ledger grows:
upstream rebuilt the D3D ground-tile index buffers in retail form (stack
aStripIndices[60] prefill → properly-extent gpD3DGroundTileVertexBuffer; fan
gdwRendererMode = {0,5,0x23,0x1e} byte-match verified), superseding fork fixes 24
(fullscreen black ground) and 25 (perspective floor diagonal bars) — DrawGroundTile
and InitGroundTileCache are now byte-identical to upstream. D2UnitFindDataStrc.h
deleted upstream (structs relocated to D2Common/Unit.h).
R90b fixes the owner's first MSYS2 build of R90: a stale D2UnitFindDataStrc.h
include in VanillaPlusCore.cpp (kitchen-sink block missed by the merge) and a
RENDERER_D3D_DrawGlyph splice referencing float globals the upstream regen deleted.
Cross-verified in the sandbox with llvm-mingw (whole-tree include audit: 14,915
quoted includes → 0 unresolvable; d2.exe links pei-i386). Field-run green
2026-11-03 (see R91).
R91 — Vanilla+ 0.0.7-R91 — 2026-11-03 — commit fd46e40a (tag pending)
The field-verify round — one report, two closures.
The owner's MSYS2 MINGW32 build of R90b went green and the game runs:
"Skeletons are finally white again" — issue #8
RESOLVED IN THE FIELD (the R50 roster-recolor polarity + R51 spawn-polarity write +
R89 component hardening all confirmed on one build; GitHub close pending the owner).
R90/R90b field verification closed with it. Docs-only otherwise; regular rounds
return to the overlay-zip workflow (git bundles reserved for upstream-merge rounds).
Gates 326/326.
R92 — Vanilla+ 0.0.7-R92 — 2026-11-03 — commit fc84bd67 (tag pending)
Issue #26 fix round 1 — the 720p panel blackness root-caught.
"[720p] With only Left or Right UI Panel open, Black triangles on that side of
screen" — root cause: every panel-open adjustment in the world pipeline is a
fraction of the window width, tuned for 640x480/800x600 where the open 800-class
panel reaches the screen edge. The Vanilla+ wide modes center that rigid 800 px UI
(240 px margins at 1280x720), so the culls still assumed edge-hugging geometry and
the strip beyond the open panel was culled to the black backbuffer. Fixed for wide
layouts only (retail byte-identical): the tile walk keeps the full panned span
(Path.cpp), the perspective cull rects open the margin side (D3D + Glide), unit
clicks reject only the real panel rectangle (CUnit.cpp — margin units clickable
again), town-portal fade and weather clamps span the full width. Gates 332/332.
Field result: PARTIAL — left side fixed; right side and both-panels still black.
R93 — Vanilla+ 0.0.7-R93 — 2026-11-03 — commit ab34c7ea (tag pending)
Issue #26 fix round 2 — the three remaining family members.
The border-3 ViewDraw2 world-draw skip (Game.cpp): with both panels open the
whole world pass never ran — every renderer, both perspective modes. The
bottom-band vertical cuts: non-perspective builders anchor-cut at h-0xf,
rendering the perspective-OFF bottom strip black. Plus the case-2 pan-anchor flip.
4 files; gates +6. Field result: PARTIAL — non-perspective single panels
verified; both panels still black; the perspective right margin survived.
R94 — Vanilla+ 0.0.7-R94 — 2026-11-03 — commit a6ef88b9 (tag pending)
Issue #26 fix round 3 — both panels closed.
Fixed the both-panels artifacts (the border-3 world-draw delivery + the D3D/Glide
bottom-band anchors): "both panels open, no more blackness" — green in BOTH
perspective modes and BOTH renderers. Field result: MOSTLY GREEN — the
perspective-ON right-panel margin and the world-slide question moved to R95.
R95 — Vanilla+ 0.0.7-R95 — 2026-11-03 — commit d1e7e350 (tag pending)
Issue #26 round 4 + the no_panel_shift feature — "Can we open the panel without
moving anything?"
Answer: the quarter-screen world slide with one panel open is VANILLA
(TypeOfUiOverlayOverWorld pans the world by ∓ScreenSizeX/4 to re-center the
action in the uncovered strip; exact only when the open panel reaches the screen
edge — 640x480/800x600). At 720p the floating 800-class UI makes the slide drag the
world out from under the visible margins. R95 adds the opt-in no_panel_shift
d2vp.ini feature: while 1, a single open panel takes the border-0 world pipeline
verbatim (Path.cpp camera gate + a neutralizer at the 11 world-draw origins) — the
renderers receive 0 and take their own full-screen no-pan path; zero renderer edits;
retail dims byte-identical; 9 files; gates +10. Field-verified in Glide: "no
shifting is observed when opening a panel ... All blackness appears fixed in Glide
as well" — issue #26 field-green in Glide.
R96 — Vanilla+ 0.0.7-R96 — 2026-11-03 — commit ce77c056 (tag pending)
Issue #26 round 5 — the D3D perspective-toggle triage (diagnostics only, zero
behavior change).
Owner report: "toggling perspective mode in D3D crashes the game ... Glide can
toggle." R96 ships five one-shot d2boot.log probes bracketing the toggle (videoopt
load/apply, world frame enter/drawn, first D3D perspective DrawGroundTile) plus a
read-out matrix (SMOKE §L item 46). The first field pass named the death: D3D
800x600, ~6.5 s of perspective-ON play, then videoopt: perspective apply ... active=1 -> 0 and halt report: line=200 in the same 15 ms tick — Redivivus "Fog's
unrecoverable-error halt", no access violation.
R97 — Vanilla+ 0.0.7-R97 — 2026-11-03 — commit b4211fbe (tag pending)
Issue #26 round 6 — the promote guards: degrade instead of dying.
line=200 exists at exactly two sites: the retail nIndex > nMaxNodes range halt
inside D3DFLOOR_PromoteNodeToMRU / D3DSUBTILE_PromoteNodeToMRU (the D3D
floor/subtile texture caches). Every static link audited retail-clean, so R97 turns
the death into a logged, self-healing degradation (dead cache → skip the draw;
out-of-range slot word → bootlog mark + heal to the alloc path) and re-arms the
probes as per-flip transition marks (world: perspective now %d, view frame [R97],
d3d: perspective now %d, floor tile pass ... [R97]). Gates 356 → 362. The field
pass returned outcome (b) exactly as designed: the guard fired —
subtile promote out-of-range idx=4097 ... healed to uncached [R97] — the game
continued one step, then AV READ at FF600060. The mark's numbers were the
root-cause data.
R98 — Vanilla+ 0.0.7-R98 — 2026-11-03 — commit 1be20222 (tag pending)
Issue #26 round 7 / #27
— the D3D perspective-toggle death ROOT-CAUSED AND ROOT-FIXED.
Both field numbers decode to one defect: `RENDERER_D3D...
D2LoD Vanilla+0.0.7-R91
Closes #8
D2LoD Vanilla+0.0.7-R101
Release Notes — Vanilla+ working rounds R98 → R101
Four rounds, one storyline: issue #26 (no_panel_shift) goes from
"perspective toggle dies in D3D" to "fixed, field-proven, and toggleable
in-game." All four rounds carry the 0.0.7 build line — no new ini keys
anywhere, existing d2vp.ini files stay valid as-is.
| Round | Self-ID | Date | One-liner |
|---|---|---|---|
| R98 | Vanilla+0.0.7-R98 |
2026-11-03 | D3D perspective-toggle death: root cause fixed |
| R99 | Vanilla+0.0.7-R99 |
2026-11-03 | Upstream sync (Ghidra 1864–1877 library rounds) |
| R100 | Vanilla+0.0.7-R100 |
2026-11-04 | Unit-sprite pan regression from the R99 merge — fixed |
| R101 | Vanilla+0.0.7-R101 |
2026-11-04 | no_panel_shift toggle added to the Vanilla+ Options panel |
Skip straight to R101 if you are upgrading from anything older — it
contains everything above.
Vanilla+0.0.7-R98 — D3D perspective-toggle death: root cause fixed
Issue #26 round 7. The R97 instrumentation guard fired on its first field
pass, and the log it produced was the smoking gun the probe rows were built
to catch:
d3d: subtile promote out-of-range idx=4097 max=1536 ... healed to uncached [R97]
VEH: fatal exception 0xC0000005 / AV READ at FF600060
What was actually wrong
The D3D flat-wall builder (RENDERER_D3D_BuildWallTileVerticesNoPerspective)
walks its subtile array by byte offset, and the decompiler had typed that
iterator uint8_t — every sibling iterator in the tree is a full-width
int. Accumulating 0x14 per record, it wraps at 256: from the 13th subtile
on, the loop reads the real array shifted +4 bytes. The fake records
explain both artifacts precisely — idx=4097 = 0x1001 is a DT1 wFormat
word mistaken for a cache index (the same word that halted line=200 in R96),
and FF600060 is a wall subtile's {nOffsetY=-160, nOffsetX=+96} pair
mistaken for a pixel pointer. Latent since the original decompilation; it
only ever fired on the first flat-wall frame after a perspective 1 → 0 flip,
which is why D3D sessions that never toggled lived.
Fixed
- The byte-offset iterator is now
int— real records at any subtile count.
This also kills the silent wrong-record class the wrap could produce
(wrong-textured quads that never crashed). - The R97 promote guards stay verbatim as defense in depth.
- New confirmation probe: tiles with > 12 subtiles (the exact wrap
population) logd3d: flat wall subtiles=<n> [R98], first 8 per session.
Verification
SMOKE §L item 48: in D3D, toggle Perspective both directions — no death, no
healed to uncached marks; a few flat wall subtiles= marks are the
expected confirmation data. Then finish the #26 D3D matrix (items 41–45).
Vanilla+0.0.7-R99 — upstream sync (Ghidra 1864–1877 library rounds)
The periodic upstream alignment, done as one merge round: upstream
088c4ee3..94ec239d (one-TU globals made static, 48 functions re-filed into
their source files, msvc8 evidence at 6,478 functions / 20.66% of bytes).
209 files changed, +12,860 / −13,350 — net 490 lines out of the tree.
Highlights relevant to Vanilla+:
- Retail bodies landed for the unit-sprite draw path (
DRAW_WORLD_Unit_1,
GFX_DrawUnitSelectionShape, …) among the Ghidra 1864–1877 library rounds.
⚠️ Known follow-up: these retail bodies re-armed the vanilla ±quarter
sprite pan in the perspective branch, which the R95no_panel_shift
neutralizer didn't cover — fixed in R100 (see below). R99 builds
should not be used withno_panel_shift=1. - Sound-table gates re-pinned to upstream 1870 spellings; the sound-init hwnd
fix superseded by upstream's 1870BOOLparameter; texture-cache init
branches realigned verbatim. - The merge was redone cleanly after a first pass broke the
ui.cppbuild;
the shipped merge (18838a8b) compiles and all gates are green.
Vanilla+0.0.7-R100 — unit-sprite pan regression fixed (issue #26 round 8)
Field report: "clean build successful. no_panel_shift is still missing. with
it set to 0 shifts normally, when set to 1, player, npc, campfire get pushed
into wrong locations."
Decode
Exactly the three unit classes — players, monsters, objects — riding the
path the R99 merge re-armed. The R95 feature covered every terrain pass
(floor/wall/roof/shadow, weather, automap) with the
PANELSHIFT_BorderForWorld neutralizer, but the pre-1864 unit copies had no
border handling at all (they rode the view rect, already fixed). The new
retail bodies compute sprite screen-x the vanilla way in the perspective
branch: AdjustPerspectivePosition + a hand-applied ±quarter pan keyed on
the real window border. So with no_panel_shift=1 the world stayed at the
border-0 camera while units — and everything anchored to them — panned one
quarter sideways. no_panel_shift=0 was never affected (there the sprite pan
matches the world pan).
Fixed
All 14 unit-anchored world-render consumers now read the border through
PANELSHIFT_BorderForWorld:
Gfx.cpp×7 — unit sprites, state overlays, shadows, colored/selection
shapes,DRAW_WORLD_Unit_1GfxUtil.cpp×1 — unit COF directionText.cpp×2 — monster/object hover textShowItems.cpp×1 — ground-item label anchor (label culling keeps the
real border, per charter)ui.cpp×3 — mercenary HP bar, player name plates, unit/warp tooltip anchor
UI/interaction consumers (panel click rects, hit tests, mouse warps) keep the
real border by charter; no_panel_shift=0 behavior is byte-identical to
before. 5 new gates pin every call-site count — suite 377/377.
Verification
SMOKE items 41a/41b. Owner-confirmed on the field build:
"no_panel_shift is working as intended now."
Vanilla+0.0.7-R101 — the Options-panel toggle (issue #26 round 9)
Field ask: "Great job, no_panel_shift is working as intended now. Let's add
the toggle to the Vanilla+ Options panel."
What's new
Esc → Options → Vanilla+ Options grows a seventh row — No Panel World
Shift — wired exactly like the existing rows: one entry in the row table
riding the same ini read/write bridge. Nothing else needed touching: the
write bridge already flushes the PANELSHIFT gate cache, so the flip applies
the same frame — the world stays put the moment you flip On, and slides
again the moment you flip Off. No ini edit needed, ever; your existing
[features] no_panel_shift key is read and shown as-is.
Layout
Seven rows overflowed the R67 page geometry (row 7 would have overlapped
Previous Menu by 6 px), so the Previous Menu row moved down one pitch
(0x124 → 0x144): same spacing, still inside the panel box, and
hit-test/hover/draw all move in step via the shared row-geometry helper.
As documented on the ini template since the feature shipped: the feature is
wide-layout only (1280x720 / 1344x700). At 640x480 / 800x600 the row
flips the key but the vanilla slide is kept by design. no_panel_shift=0
behavior unchanged everywhere. Gates 380/380 (3 new).
Verification
SMOKE item 2 now includes the panel round trip: toggle On → open the
character screen at 1280x720 → world stays put (units included); toggle Off →
world slides again.
Full Changelog: R98...R101
D2LoD Vanilla+0.0.7-R88
R84 — Vanilla+ 0.0.7-R84 — 2026-11-02 — tag R84 @ 63e020a3
The item-tooltip zero fix (#16).
Items could drop with a mod line reading "0 poison damage over 0 seconds", and
'of Frost' gear could show "+0 Cold Damage": the item-stat tooltip formatter's
single-value elemental/poison branches called the wide-string formatter without the
numeric varargs (a latent retail-1.14d bug the fork's build layout makes visible), so
any roll whose range collapsed to min==max printed stack residue instead of the value
('of Frost' weapons roll cold-max 1..2 with cold-min fixed at 1 — every drop that
rolled a 1 collapsed; poison collapses through the length rounding). Display-side only —
item stats and generation were never wrong.
- Field-verified by the owner the same day (SMOKE §J items 37–38 proven at tag).
- Gates 308 → 311; self-ID
Vanilla+0.0.7-R84(d2boot.log line 1 pins the exe).
R85 — Vanilla+ 0.0.7-R85 — 2026-11-02 — tag R85 @ 71f4772f
The A1Q2 "Sisters' Burial Grounds" turn-in fix (#22).
Blood Raven killed, but Kashya's reward (the rogue mercenary) could not be claimed:
the A1Q2 gossip table was emitted singular (accept line only) while the handler reads
slot 3 whenever RewardAwaits is set — the same truncated-gossip-table regen defect as
#13. The turn-in line never
existed, so the reward gate was never asked. Table widened to 5 slots (slot 3 = turn-in
line 0x5c, pinned by the quest's own gate; flavor slots left empty rather than
invented) + 7 Kashya chain marks for one-log diagnosis.
- Field-proven — the owner closed the issue 2026-11-02, the same day.
- Gates 311 → 320; self-ID
Vanilla+0.0.7-R85. - R88 note: upstream later recovered the retail-exact 6-slot table (with the flavor
lines the fork had left empty); the hand-build was retired at R88, notes kept.
R86 — Vanilla+ 0.0.7-R86 — 2026-11-02 — tag R86 @ 3ca2da40
The -nointro boot switch.
d2vp.exe -nointro skips the boot intro videos — the Blizzard / Blizzard North logo
.biks that play on every launch plus the auto-played title/first-run cinematics — by
never entering cutscene mode: both boot/title entries take the retail begin-failed path
straight to the next screen. Writes a [vp] cmdline -nointro mark to d2boot.log.
In-game quest cutscenes are unaffected; boot without the switch is byte-for-byte
vanilla.
- Field-verified 2026-11-02 — "R86 verified, -nointro works" (SMOKE §K items 39–40).
- Gates 320 → 326; self-ID
Vanilla+0.0.7-R86.
R87 — Vanilla+ 0.0.7-R87 — 2026-11-02 — tag R87 @ 486b936c
The first upstream sync of the 0.0.7 line — merge b2937091: upstream 18 commits,
Ghidra DB 1806 → 1839, byte match 6,029 → 6,232 of 14,642 functions (18.40% → 19.50% of
bytes). Vanilla+ intact on top.
What came in:
-
Ghidra 1807–1813 regen rounds (packet/roster/monster/subtile parameters,
SkillDesc/OOG types, zero-init/signed compares) and generator rounds (loop gate +
signed division, folded-store arm order, range tests aslo <= x && x <= hi). -
The VC8 harness + CI deps (upstream PR jaenster#32); dtchitt issue jaenster#37–jaenster#76 names; names for
3,500 variables/fields + Storm PowerPC names + the new D2Hell file. -
Real behavior fixes: roster party ids
uint16_tthroughout (joining a game no longer
hangs in the party-grouping loop); three value-bug fixes (wide char literal, byte-mask
select, Win32 calls shadowed by thunks) with VC8 near-miss type fixes. -
Merge-side latent fix:
DRAW_ShouldDrawEdgeshad returned 0 unconditionally since the
0.0.6 merge port — wide modes never drew level edges; caught and restored during
resolution. -
Field-run green — "Finally, builds, runs, no regressions." Gates 326/326.
R88 — Vanilla+ 0.0.7-R88 — merge 1d401785; tag after field verify
The second upstream sync — upstream 9 commits (c8445e73..bc47c6bc), Ghidra DB
1839 → 1851, byte match 6,232 → 6,242 of 14,642 functions (19.50% → 19.54% of bytes);
22 conflicts / 27 hunks resolved, Vanilla+ intact on top.
Synced:
- Types/prototypes from Commit-Will's, Niehztog's and Nugwa628's proposals; the third
naming round with Mac cross-check types (CLIENTMODE_WaitForQServerConnection,
MAINMENU_OnPressSkipEmailAddress,Objects_InitFnNN_<role>,StaticInit_gpsz*). QUESTLOG_HitTestCloseButtonreturns bool; affix rollers takebSpawnableOnly, not
bIsSuffix;fnGetDatabaseFileTimeis__fastcall(FILETIME *);
D2Sha1ContextStrcis 0x60 bytes.
The supersession ledger opens — upstream recovered the retail forms of two fork
fixes; the fork takes them ("upstream wins") and keeps the root-cause notes in code:
- The R54/R85 gossip hand-tables (#13
/ #22): upstream's
bc47c6bcrestored the retail tables verbatim — A1Q1 8 / A1Q2 6 / A2Q1 9 slots —
plus the flavor string ids the fork had left empty rather than invent. All 39
aNpcGossipDataexterns are now sized retail-exact (28 were unsized pre-merge): the
~17-quest singular-table watch (track J) resolves wholesale. - vanilla-plus fix 16 (Defect 13: SFX silent, music OK): upstream's
D2FloatIntUnionis the same root cause in the retail-faithful form (.fValue
store, integer.nValueread-back). - Sound constants: upstream's exact decoded decimals
(9.999999747378752e-05= the float320.0001promotion) replace the fork's
spellings; the-2000.0centibel diagnosis stays (upstream ships the same scale).
Checked and not superseded: the issue #11 [glide fix 11] hook — still fork-side
(re-planted at the R87 merge); the glide3x.dll wrapper ships from the Glide3xVK
branch, which upstream does not carry.
- Gates: 10 stale expectations updated to the superseding state with R87-merge notes
(epsilon ×4, the-2000.0cast, gossip sizes ×6, Bloodraven0x5c×2 — retail
carries bMenu 0 and bMenu 2 variants); self-ID row pinnedVanilla+0.0.7-R88.
Suite 326/326.
Full Changelog: R87...R88
D2LoD Vanilla+0.0.7-R87
Merge upstream Ghidra Recovery 1807-1839
Full Changelog: R86...R87
D2LoD Vanilla+0.0.7-R86
Added -nointro command line parameter
D2LoD Vanilla+0.0.7-R85
Closes #22
D2LoD Vanilla+0.0.7-R84
Closes #16
D2LoD Vanilla+0.0.7-R83
Vanilla+ 0.0.7 — Release Notes: R77 → R83
Seven rounds, 2026-11-01 → 2026-11-02. One stash-tab polish round (R77), three upstream Ghidra-recovery syncs with zero feature changes (R78–R80, upstream 1792 → 1806), one capacity round (R82, shared stash 2 → 10 pages), and two 720p UI fixes filed and confirmed by the owner (R81 = issue #21, R83 = issue #23). The Vanilla+ gate suite grew 297 → 308 and is green at the R83 tip; every round was field-run on the Win11 build. No new d2vp.ini keys in the whole span — all six toggles unchanged, and a missing/garbage ini is still retail byte-for-byte.
Full changelog: R77...R83
| Round | Date | Commit | Type | Headline |
|---|---|---|---|---|
| R77 | 2026-11-01 | 866883e |
field polish | Stash tab lifted 45 px into the panel's top-right corner |
| R78 | 2026-11-02 | ea0b84d + e6a31f7 |
upstream sync | Ghidra 1794 absorbed — zero feature changes |
| R79 | 2026-11-02 | f518fda + bd7a610 |
upstream sync | Ghidra 1801 absorbed — zero feature changes |
| R80 | 2026-11-02 | f427f96 + 9c8d430 |
upstream sync | Ghidra 1806 absorbed — zero feature changes |
| R81 | 2026-11-02 | 3f73e56 |
fix (#21) | Waypoint row icons fixed at 720p — "waygateicons are back" |
| R82 | 2026-11-02 | 988abd9 |
feature | Shared stash grows to ten pages |
| R83 | 2026-11-02 | d970fb6 |
fix (#23) | Video-options 1280x720 label right-aligned |
R77 — stash tab lifts into the panel's top-right corner
The shared-stash tab was approved horizontally at R76 but the < pager arrow's halo still kissed the gold amount text's last glyph — owner: "Looks good horizontally, but still drawing on top of the last gold character a bit with the arrow. can we move the stash label up, closer to the top right corner ?"
- The tab row moved up 45 px (
VPSHAREDUI_TabBaselineY−0x19f/−0xdb → −0x1cc/−0x108, expansion/classic) into the empty band between the panel's top edge and the retail gold display row. The R76 right anchor (+0x13c, inside the art-seam rule) is untouched. - The band's right side is verified-empty retail space: the coin rect and the gold-area button rect live at X
+0x49..+0x96, far left of the tab; the halo top stays ≥ 7 px inside the panel edge and the click band never probes the playfield (the R69 down-walk class cannot re-open). - Gates 297 → 299. This is the round the
R77GitHub release shipped (tagR77@866883e, carrying the whole R67→R77 shared-stash refresh).
R78 — upstream sync: Ghidra 1794 (zero feature changes)
Upstream 14f0227a absorbed (merge ea0b84d): the Ghidra v1793/v1794 regen waves + two generator rounds — split-bounds getters and per-arm calls. Upstream byte-match now 5,988 of 14,642 functions (40.9%), code 471,419 of 2,602,915 bytes (18.11%).
- 188 upstream files changed, 10 textual conflicts (6 EOL-only; Direct3D hunks onto the
gadwBitMasksregen shape; Inv wide-mode page key, QuestLog's 8 wide-offset terms and Panel's crop-draw calls re-planted; Panel was a convergence — upstream shipped the same calls the fork already carried). - Zero compile-fix rounds — the full battery green first try: gates 299/299, merge-site sweep 32 TUs / 0 unique errors, placement OK=46 MOVED=141 NEW=193, VP-marker census zero losses, fork TUs 4/4.
- Upstream added its MIT
LICENSEthis round. Docs synced (e6a31f7, Rev 101). - Field verdict: "Compiles cleanly. Boots first try. No obvious regressions."
R79 — upstream sync: Ghidra 1801 (zero feature changes)
Upstream 95de360a absorbed (merge f518fda): the 1795–1801 retyping waves — widths/signedness/BOOL fields, D2ObjectInitFnArg, pointer/return types across skills, inventory, Storm and the UI, unit/game/inventory pointers where ints were declared, parameter names checked against live packet captures, and the Storm file layout. Byte-match 5,990 of 14,642 (40.9%), code 471,891 bytes (18.13%).
- 446 upstream files, 70 unmerged paths — 62 zero-fork-delta regen TUs taken upstream-verbatim, 2 Storm-layout renames accepted, 6 manual hunks with the Vanilla+ anchors re-planted. Host shim gained the
_RTL_CRITICAL_SECTIONstruct tag (host syntax-check only; real builds get the SDK form). - Zero compile-fix rounds — battery green first try: gates 299/299, sweep 46 TUs / 0 errors, fork TUs 4/4, d2boot ALL VISIBLE. Docs synced (
bd7a610, Rev 102). - Field verdict: "Builds and Runs without error again, no regressions."
R80 — upstream sync: Ghidra 1806 (zero feature changes)
Upstream 55016505 absorbed (merge f427f96): the 1802–1806 waves — MONSTERAI flag output as a 4-byte BOOL, level/room/unit pointers in the DRLG path and grid helpers, DT1/DC6/path types + flag-word returns + character buffers, parameter names for monster spawn/skills/item stats/UI, then the generator round. Byte-match 6,029 of 14,642 (41.2%), code 479,019 bytes (18.40%), instructions 1,745,463 (67.1%).
- 266 upstream files, 8 unmerged paths — 5 zero-delta regen TUs upstream-taken, 3 manual hunks (QuestLog site-5 a zero-edit replant — the fork line already spelled upstream's new shape; Waypoint's wide-Y term re-planted on the ternary reshape; Direct3D's
UploadTextureToCacheonto the typedDC6Block*with the fix-22 halt-dump and fix-27 LUT stamp re-planted). Host shim PE structs renamed to_SHIMaliases. - Zero compile-fix rounds — battery green first try: gates 299/299, sweep 39 TUs / 0 errors, continuity census zero losses. Docs synced (
9c8d430, Rev 103). - Field verdict: "Great job, another clean merge. builds, runs. no regressions."
R81 — waypoint row icons fixed at 720p (issue #21, closed)
Owner filed "[720p] Art not showing on Waypoints Panel" (narrowed to the per-waypoint button icons). Root cause, code-pinned: in DRAW_UI_Waypoint every top-anchored Y carried the site-6 wide-Y term from the 720p retrofit — except the two waygateicons row draws, which kept the vanilla decompile shape byte-identical since the initial recovery. At 720p the panel art, texts and hit zones ride +120 px while the icons stayed at retail Y: all nine row icons rendered ~120 px above their buttons (rows 0–5 fully above the panel), while clicks still landed — reading as "icons not showing".
- Fix:
+ VPRES_GetWideUiOffsetY()+ the// VP: [resolution site-6]marker on both draws. At 800×600 the offset is 0 — retail stays byte-for-byte; only the widescreen modes move. - Gates:
site-6: waypoint Y shifts10 → 12 (relabeled "(R81)"); 299/299. - Field verdict: "Great job waygateicons are back" — issue #21 closed.
R82 — shared stash: ten pages
Owner: "Let's increase the shared stash pages to 10 for R82" — the widening the .sss v2 format reserved room for back at R75 (the original roadmap said "If that works we'll do 10 pages. Then 100.").
- 2 → 10 shared pages. Same
VPS2magic + version — onlypageCountand the per-page size/checksum pair table grow: header 28 → 92 bytes (12 + 8/page). The two fixed page ingests became one per-page loop (page-1's verdict still drives the.bakchase; later pages degrade to empty). - Read-back chain: v1 files (one page) → page 1; pre-R82 v2 files (two pages) → pages 3–10 start empty (logged:
v2 file has %d pages - pages %d..%d start empty (pre-R82 file)); the next save rewrites all ten. A file carrying more pages than the build holds is rejected whole, untouched, with a mark — it needs a newer binary. - Pager labels come from a bounds-guarded literal table (
Shared…Shared 10); the arrows still step one page, the word still toggles personal↔shared-1, and there is one shared gold pool across all ten. Character.d2ssaves stay retail-pure (the I1 invariant). - Gates 299 → 306 (7 new R82 rows + the ingest-helper count re-pin + self-id bump); fork TUs 4/4 host-compiled.
- Field: the ten-page build boots and runs (the R83 fix was field-run on it); a dedicated ten-page pager pass is on the SMOKE list (§H items 33–34).
R83 — video-options "1280x720" label right-aligned (issue #23, closed)
Owner: "the literal resolution text '1280x720' in the video options is not aligned with the video options menu … everything else is correct at 720p — except that text." Screenshot-measured: the label ran a full label width (~100 px) right of the value column and floated ~4 px above the neighboring images' ink line.
- Root cause (two engine conventions):
DRAW_EscMenudraws the row's value images with alignment 2 — cell RIGHT edge on the anchor X — while the label text left-anchored at that same X; andDRAW_text's y anchors the glyph cell bottom (digit ink ~6 px above it) while the retail value DC6s bottom-anchor at the row bottom with ~4 px of baked padding under their ink. - Fix: right-align via the rendered advance width (
DRAW_text0, the same metric the renderer advances by) and draw at row bottom + 2 (RESLABEL_DESCENDER_COMP), putting the ink at row bottom − 4 — exactly on theHIGH/ONbaseline. The row-band math was already exact; the misanchor was purely in the text call, so the fix is resolution-independent — 800×600 picks up the same corrected placement. - Gates 306 → 308 (the two #23 anchor pins + self-id bump); fork TUs 4/4; the edited body additionally host-compile-checked via a stub harness.
- Field verdict: "Great job. Text is aligned now." — issue #23 closed.
Upg...
D2LoD Vanilla+0.0.7-R82
Shared Stash now has 10 pages
Full Changelog: R80...R82