Skip to content

Releases: VTSTech/D2VP-QoL

D2VP Vanilla+QoL-0.0.7-R105

Choose a tag to compare

@VTSTech VTSTech released this 04 Nov 04:48

Upstream merge Ghidra Recovery 1892 -> 1894

Full Changelog: R104...R105

D2VP Vanilla+QoL-0.0.7-R104

Choose a tag to compare

@VTSTech VTSTech released this 04 Nov 00:09
2f4485f
image image image

Full Changelog: R101...R104

D2LoD Vanilla+0.0.7-R98

Pre-release

Choose a tag to compare

@VTSTech VTSTech released this 03 Nov 10:13
image image image

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 39 aNpcGossipData externs now sized
    retail-exact (28 were unsized); the track-J singular-table watch resolves.
  • Sound constants take upstream's exact decoded decimals (the -2000.0 centibel
    diagnosis preserved).
  • Checked and NOT superseded: the issue #11
    [glide fix 11] hook (the wrapper ships from the Glide3xVK branch, 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...

Read more

D2LoD Vanilla+0.0.7-R91

Pre-release

Choose a tag to compare

@VTSTech VTSTech released this 03 Nov 02:59

Closes #8

D2LoD Vanilla+0.0.7-R101

Pre-release

Choose a tag to compare

@VTSTech VTSTech released this 03 Nov 21:25
769bcac
image image image

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) log d3d: 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 R95 no_panel_shift
    neutralizer didn't cover — fixed in R100 (see below). R99 builds
    should not be used with no_panel_shift=1.
  • Sound-table gates re-pinned to upstream 1870 spellings; the sound-init hwnd
    fix superseded by upstream's 1870 BOOL parameter; texture-cache init
    branches realigned verbatim.
  • The merge was redone cleanly after a first pass broke the ui.cpp build;
    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_1
  • GfxUtil.cpp ×1 — unit COF direction
  • Text.cpp ×2 — monster/object hover text
  • ShowItems.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

Pre-release

Choose a tag to compare

@VTSTech VTSTech released this 02 Nov 23:39
image image image

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 as lo <= 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_t throughout (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_ShouldDrawEdges had 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_HitTestCloseButton returns bool; affix rollers take bSpawnableOnly, not
    bIsSuffix; fnGetDatabaseFileTime is __fastcall(FILETIME *);
    D2Sha1ContextStrc is 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
    bc47c6bc restored 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
    aNpcGossipData externs 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
    D2FloatIntUnion is the same root cause in the retail-faithful form (.fValue
    store, integer .nValue read-back).
  • Sound constants: upstream's exact decoded decimals
    (9.999999747378752e-05 = the float32 0.0001 promotion) replace the fork's
    spellings; the -2000.0 centibel 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.0 cast, gossip sizes ×6, Bloodraven 0x5c ×2 — retail
    carries bMenu 0 and bMenu 2 variants); self-ID row pinned Vanilla+0.0.7-R88.
    Suite 326/326.

Full Changelog: R87...R88

D2LoD Vanilla+0.0.7-R87

Pre-release

Choose a tag to compare

@VTSTech VTSTech released this 02 Nov 22:48

Merge upstream Ghidra Recovery 1807-1839

Full Changelog: R86...R87

D2LoD Vanilla+0.0.7-R86

Pre-release

Choose a tag to compare

@VTSTech VTSTech released this 02 Nov 09:16

Added -nointro command line parameter

D2LoD Vanilla+0.0.7-R85

Pre-release

Choose a tag to compare

@VTSTech VTSTech released this 02 Nov 08:37

Closes #22

D2LoD Vanilla+0.0.7-R84

Pre-release

Choose a tag to compare

@VTSTech VTSTech released this 02 Nov 07:36

Closes #16