Repository navigation
Releases: defessler/D4Scanner
Release list
v0.105.0
Capture & updater audit — nine fixes, all adversarially code-reviewed. This release sweeps a previously-unaudited surface: the TTS-shim installer, the OCR / screen-capture path, Maxroll import, and the auto-updater.
Most user-visible
- Stale capture DLL is now detected. The shim version check now matches the order Diablo IV actually loads the DLL, so a stale copy left in your game folder is correctly flagged for upgrade instead of being masked by a newer copy elsewhere.
- Installing capture no longer corrupts your PATH. The PATH-fallback install used to flatten
%USERPROFILE%-style entries to fixed paths (and downgrade the variable's expandable type); it now edits the registry directly and preserves them. - Safer auto-update. A truncated or empty download is no longer staged as a valid update (which could leave the app unable to relaunch after "updating"); it is rejected and retried.
- Maxroll import no longer fails with a file error when you import during the app's first-run game-data download.
Smaller capture fixes
- A real item icon no longer gets stuck as a silhouette for the session after a transient game-data read race.
- A held item tooltip is re-read after a momentary OCR hiccup instead of being skipped until it changes on screen.
- Closed a couple of native-bitmap / JSON memory leaks on error paths.
No behavior change for normal play — this is a reliability/robustness release. Build is warning-free; all 1029 Core tests pass.
v0.104.0
Full Changelog: v0.103.0...v0.104.0
v0.103.0
v0.103.0 — Hover card anchoring, actually fixed (and verified)
v0.101/v0.102 shipped this unverified because a transparent popup can't be captured by the headless render harness. This release adds a numeric placement probe (env-gated, no live effect), which surfaced the real bugs — now fixed and verified across window sizes (1400/1000/760px): the hovered icon is never covered, the card is always on-screen, and it sits right beside the icon.
- Moving to a new icon now re-anchors the card. The main bug: when the card was already open, switching to a different icon left it parked at the old icon — assigning a new target to an already-open WPF popup doesn't re-run its placement. The card now repositions to whichever icon you move to.
- The card always fits the side it lands on. Its width is now budgeted to the side it actually opens on (right of the icon by default; flips left, or drops below, at a window edge), so a flipped card can't overflow.
- Subpixel edge fix so a card whose width fills to the exact window edge isn't bumped off its anchor.
Net behavior: hover an icon → the compare card opens beside it with that icon fully visible; move off → it dismisses; move to another icon → it follows. At the 760px minimum, edge-column cards flip to the other side or above, still keeping the icon visible and on-screen.
Notes
- Build warning-free; 1025 Core tests green; static paper-doll render byte-identical (the live change is only the popup placement). A reusable hover-placement probe (
D4_RENDER_HOVER=1) is now part of the render-test seam.
v0.102.0
v0.102.0 — Hover card now actually hugs the icon you're hovering
A correction to v0.101.0. That release tried to open the card on the hovered icon's column side but still kept it beside the whole paper-doll — and since the doll is left-aligned in the window, there's no room to its left, so armor hovers just flipped back to the right exactly as before. Net effect: no visible change.
This release fixes it properly: the compare card now anchors directly beside the specific icon you're hovering.
- Opens right next to the hovered icon (to its right; flips to the left, or drops below/above, when it would run off a window edge). The icon you're hovering stays fully visible — the card may overlap neighboring icons or panels, which is the normal, expected behavior for a transient hover card.
- Stays fully on-screen at any window size and slot position (width budgeted to the space beside the icon).
- Open delay (no flicker on a quick mouse sweep), instant switch between icons, fade-in, and Esc to close — all retained from v0.101.0.
Dismiss-on-leave and switch-on-new-icon are unchanged.
Notes
- Build warning-free; 1025 Core tests green; static paper-doll render byte-identical (the change is purely the hover popup's placement).
- The optional connector "beak" is still a fast-follow — now that the card hugs the icon, it'll be straightforward to add with a quick visual check.
v0.101.0
v0.101.0 — Hover compare card now anchors to the icon you're hovering
The floating compare card now connects to the specific gear icon under your cursor, instead of always parking on the right side of the paper doll.
- Anchored to the hovered icon's side. Hover an armor slot (left column) and the card opens to the left, beside that icon; hover a ring/amulet/weapon (right column) and it opens to the right. The card still sits beside the doll — it never covers any icon, always stays fully on-screen, and its width is budgeted to fit (falling back to whichever side has room on a narrow window).
- No more flashing on a quick mouse pass. A short (~90 ms) open delay means sweeping the cursor across the tight icon grid no longer flickers a card per slot — but when a card is already open, moving to another icon switches instantly (no delay).
- Gentle fade-in when the card appears.
- Esc closes the card (after any real modal).
Dismiss-on-leave and switch-on-new-icon (the existing deferred-close + both-targets hit-test) are unchanged — they were already solid.
Notes
- Build warning-free; the static paper-doll render is byte-identical (the change is purely hover behavior).
- A small connector "beak" pointing from the card at the icon was scoped but deferred: its vertical alignment needs one round of live visual confirmation (the hover popup can't be exercised by the headless render harness), so it's a fast follow rather than something shipped unverified.
v0.100.0
v0.100.0 — Dead-code removal (App layer)
A conservative cleanup of the WPF App layer (the one surface without headless unit tests). Every candidate was independently reference-checked across all .cs and .xaml (event handlers, x:Name controls, reflection, and the headless-render seam), with the compiler as the final guarantee that nothing live was touched. No behavior change — verified by a warning-free build, the full 1014-assertion Core suite, and render smokes (the default render is byte-identical).
Under the hood
Removed ~90 lines of genuinely unreachable code:
- A whole vestigial detail-view subtree from an older rendering approach —
SkillsView,ParagonView, and their exclusive helpersIconTile(ReqItem),Chip(ReqItem), andMonogram(skills/paragon are now rendered inline elsewhere; the detail panel routes them toGroupRows). SocketProgressRow— an orphaned predecessor of the liveSocketSlotRow.IconLegend— a badge-legend builder never added to any panel.- An unused theme brush (
GoldHi) and a vestigial P/Invoke flag constant (LOAD_LIBRARY_AS_DATAFILE).
Found by a multi-agent scan with adversarial reference-verification; the one duplication candidate it surfaced (the three modal close-button blocks) was correctly rejected as carrying load-bearing comments and too risky to extract in untested UI.
v0.99.0
v0.99.0 — Core simplification (behavior-preserving)
A quality pass across the Core engine (six file groups reviewed; every candidate adversarially verified for exact semantic equivalence). The Core is already clean — only two genuine simplifications cleared the bar, both verified by the full 1014-assertion test suite with no behavior change:
Under the hood
GearParser— folded the whole-word, case-insensitive\b…\bmatch idiom (duplicated five times across item-type / rarity / Ancestral / charm-designation detection) into a single namedWordInhelper.DiffEngine.EvalSlot— removed a redundant second pass over a slot's affixes: the "umbrella-covered" set is now tracked inline during the main matching loop instead of re-deriving it with a secondMatchSlotwalk (this runs once per gear slot on every render).
No functional changes; build is warning-free.
v0.98.0
v0.98.0 — Code hygiene (behavior-preserving)
A small cleanup pass over the code added across this run's log-management, replay, and updater work. No behavior changes — verified by the full Core test suite (1014 assertions) and a warning-free build.
Under the hood
- De-duplicated the "self-heal" stale-equipped-copy eviction shared by the two one-shot replay parsers (
BuildFromLinesand the newReplayCharacters) into a single helper, so the subtle fingerprint-match logic lives in one place. - Removed a dead branch in the per-character replay grouping (the class is already fixed by the group key, so the in-loop class upgrade could never fire).
- Simplified profile restoration to fill identity through one code path instead of a redundant initializer + guard.
v0.97.0
v0.97.0 — Auto-updater always installs the version it advertises
A discovery audit of the previously-unswept capture path and external-input helpers (Maxroll import, the updater, icon resolution, OCR/screen capture) came back clean except for one real updater bug — fixed here.
- The "Update ready" prompt now always installs the exact version it names. If you let a staged update sit without restarting and a newer release shipped before you did, the app would advertise the newest version ("v0.X ready") but actually install the older staged one — and skip downloading the new version on that check. It now downloads and applies whichever version it shows you, and always picks the newest staged build when more than one is present.
Under the hood
- +4 Core test assertions (1010 → 1014) for the staged-version selection (newest-wins, order-independent, ignores stale/non-asset files). Build is warning-free.
- The audit's other candidates were correctly dismissed after adversarial verification: a download-integrity gap unreachable behind GitHub's CDN framing; a WGC frame-leak that can't fire in this configuration (verified against 789 real capture records — all use the PrintWindow path); and the TTS-wins-over-OCR slot merge, which is intentional, documented, and tested behavior.
v0.96.0
v0.96.0 — Clearing the live-gear cache now rebuilds EVERY character (rotated-archive replay)
The deferred data-loss fix from the v0.95.0 log-management audit, shipped with dedicated test coverage.
- "Clear live gear cache" now restores all of your characters, not just the active one. When your TTS log had been rotated into the
logs\archive folder (which happens automatically once it grows past the size cap), clearing the live-gear cache only rebuilt the character you were currently playing. Every other character — whose recent sessions lived in the archives — silently stayed empty until you logged into them again, even though the app promised to "rebuild by replaying the whole TTS log." Now each archived character's worn loadout is reconstructed into its own profile during the replay.
The reconstruction attributes each session's equipped gear to the character that was actually playing it, and keys profiles by name and class — so a Rogue and a Barbarian who happen to share a name (e.g. both "Heoki") are never merged, and a character played across several sessions keeps its newest gear per slot.
Under the hood
- New Core
LogWatcher.ReplayCharactersrebuilds per-character loadouts from archived logs — fully headless-testable. The oldPrefeedpath (which accumulated archive gear but never surfaced it, so the rebuild was dropped) is removed. - +6 Core test assertions (1004 → 1010), including the critical case that two same-named, different-class characters reconstruct as separate profiles with no cross-contamination, and that cross-session merges keep the newest gear per slot. Build is warning-free.
- The restore runs before the live tail begins, so the currently-active character's newest session still merges cleanly on top.