Whose work this is
principal author, and an honest division of labor
- Much of the reverse-engineering and all of the mod itself is mine. The UI was reversed and re-authored by hand, in x86 assembly — no source, working from the disassembly of a closed binary and a lot of community knowledge.1 The tangible result: 835 changed bytes across 86 hand-authored assembly patches in
D2Client.dll, plus aD2Commoncodecave stub and aD2Gamepatch — many built on the community's reversing work, many my own, all hand-placed in machine code. - The recent chapter (2026) is a collaboration with Claude — Anthropic's AI, which has genuinely impressed me. I started working with Claude to consolidate that hand-built body of work — turning dozens of permanent, scattered on-disk patches into one auditable, version-guarded runtime DLL — and to document and assist with more ambitious development ahead.
What it is
- The whole game, reworked. Beyond the engine-level assembly, the mod reshapes nearly every system of the base game — interface, all seven character classes, items, enemies, and economy. That's the soft-code half: over 2,100 rows of game data edited across 35 tables, measured by diffing against a clean 1.13c baseline — all kept in a hand-written changelog.
- The mod is the product of reverse-engineering the game's UI and rendering by hand: 86 assembly patches in
D2Client.dll, aD2Commoncodecave stub, and aD2Gamepatch — each one a change found in the disassembly and written in machine code, directly against the shipped binary. - The recent consolidation, esoteric.dll, lifts that hand-built work into one runtime layer: ship stock Blizzard DLLs, re-apply every modification on load, and keep it toggleable, version-guarded, and documented — so the RE is preserved in one auditable place instead of baked irreversibly into the binaries.
- It injects in-game through an existing load hook (
PlugY DllToLoad2), applied after the resolution layer initialises.
The map pipeline
a second body of work — authoring 36 custom levels at scale
Alongside the binary work, the mod ships 36 hand-built custom maps across all five acts — new levels in their own tile namespace, not reskins of existing ones. Building that many by hand only stayed tractable because it was turned into a repeatable pipeline:
- A written SOP. Every new level is a checklist across the five game files that must agree —
Levels.txt,LvlPrest.txt, an entry-file definition, the string table, and the.ds1map itself — with the exact fields to set: preset generation type, the Vis/Warp entry-exit wiring, waypoint and act-sequence slots. - One-click tooling per step. A dedicated shortcut folder opens each level tool on demand — the map editor, the tile browser, and the
Levels/LvlPrest/LvlWarptables — so authoring a level is navigation, not archaeology. (It's part of a broader set of ~50 launchers that front every data table in the mod.) - A troubleshooting ledger. The recurring failure modes are written down — entry-exits on unwalkable tiles, warps too close to topography, maze-generation bounds, worldspace size limits — so each new map avoids the last one's mistakes.
Same instinct as the reverse-engineering: turn a hard, error-prone task into a documented, repeatable process.
.ds1 source (floor / wall / object layers), no in-game screenshots:The art pipeline
entirely custom — recoloring the game's own sprites, frame by frame
The new look isn't a shader pass over the old art — it's the actual character sprites, rebuilt. Because Diablo II composites characters in layers, changing one attribute touches an enormous number of frames:
- Sorceress hair → black: 59,040 individual frames. The hair layer alone carries a sprite for every helmet class × weapon class × animation × direction × frame — and every one had to change for the colour to stay consistent as she moves and equips.
- A full werebear reskin. The Druid's shapeshift form recoloured across every animation mode, original and recolour kept side by side.
The game stores characters as compiled DCC sprites, so the workflow was: decompile each to its thousands of raw frames (pixel offsets preserved so they re-align exactly), recolour them with a custom Adobe batch script I built, then recompile. Sixty thousand frames by hand isn't feasible — the batch tooling is what made it possible, and the result blends seamlessly with the untouched art.
The feat, milestone by milestone
- Reading a binary with no map. The engine ships as opaque 32-bit machine code — no source, no docs. The work drives it to the level of individual instructions: reading disassembly, finding the exact bytes that govern layout and rendering, and rewriting them.
- A compiler out of nothing. The machine had no C/C++ toolchain (an empty Visual Studio shell, no
cl.exe, no Windows SDK) and a no-elevation constraint. Solution: portable Zig (zig cc), targetx86-windows-gnu— a single unzipped download that emits a loadable 32-bit Windows DLL with no VS and no SDK. - Version-guarded patching. Every write verifies the site holds the known stock bytes first, or refuses and logs. No silent corruption. The same invariant is checked at three gates: RE-time, build-time, and load-time.
- The render pipeline, mapped by hand, address by address:
D2Clientcel thunks →D2gfxdispatch (0x6FA8BB50) → the D2Glide driver → its live drawer (0x6F856840) →grDrawVertexArrayContiguous→ D2DX (Glide→Direct3D 11), established to run in series, not as alternatives. - The panel-shift mechanism, mapped: layout mode (0x6FBCC414), pixel offset (0x6FBCC418 = −320), display width (0x6F865A68) — all located and explained.
The discipline
the actual differentiator on a black-box target
- Ledger before attempt. Every hypothesis is written down with its falsifier before the code is touched.
- A static trace is only a hypothesis until an in-game probe proves itself. A sanity band must read nonzero or the result is thrown out.
- Decoys are first-class records — refuted addresses are kept, so no future pass re-walks a dead lead.
- Consensus ≠ correct. Even a multi-model panel and an adversarially-verified multi-agent trace were wrong, and only an empirical
total = 0probe read caught it.
Consulting a panel of experts — as an experiment, not an oracle
At one knowledge boundary — which function clips the ground tiles at the panel edge, and where — the fastest static traces had already been wrong twice. So the question went to a purpose-built multi-model consultation harness: one structured RE prompt, relayed to four frontier models in parallel, one JSON payload per wave each way, iterated over three waves.
The discipline is the point. No model's claim was taken on authority; every specific was checked against the binary, and the whole consultation was logged — who claimed what, in which wave, and whether it survived verification.
- Consensus ≠ correct. All four models backed a "renderer scissor" for two waves. Reading the actual renderer source refuted it — the relevant clip call is a stub (
assert(false)) and the scissor is full-width. - Feeding verified refutations back — not more opinions — is what advanced it.
- The win was falsifiable and sourced: one model pinned the exact layer (the D2Glide driver's tile-draw), the global (
DisplayWidth @ D2Glide+0x15A68), the function shape (clipRight = DisplayWidth − DisplayWidth/4), and the upstream patch site — with public-source citations, all verified in the binary. Its one miss: it gave the offset as a literal 0x140 when it's actuallyDispW/4— which is exactly why you verify even the expert that's right. - And even that verified answer was later superseded: in-game probing showed the specific clamp functions the panel named are dead code for the live floor path, relocating the true cause downstream. A binary-verified expert conclusion still had to answer to the running program.
The failure ledger
the honest record — what was tried, and why it failed
960 → 1280 logged) and the region stayed black.Every one is a version number burned. Laid side by side, they stop looking like separate bugs and start looking like one wrong mental model of the engine — which is exactly what the ledger is for.
Status
kept honest
Seven years of hand assembly RE; the mod built and shipped. Toolchain, framework scaffold, and full modification inventory verified.
Render dispatch chain and the panel-shift globals located and documented, address by address.
Walls and units correctly un-shift; the tile layer's shift traced into the downstream D3D11 layer. Not yet shipped.
What it demonstrates
- Reverse-engineering a closed-source Win32 binary at the assembly level — no source — returned to on and off across a seven-year span.
- Runtime code injection, codecave and detour patching, and safe in-memory instrumentation of a live process.
- Building a reproducible cross-compilation toolchain under real constraints (no admin, no SDK).
- A falsifiable, probe-driven investigation method that treats static analysis as a hypothesis and the running program as ground truth.
- Orchestrating a multi-model expert panel with tooling built for it — and the judgment to treat its consensus as a hypothesis to falsify, not an answer to accept.
- Process and tooling design: reducing a hard, multi-file authoring task to a documented SOP with one-click tooling — the pipeline that produced 36 custom levels across all five acts.
- Building a custom Adobe batch pipeline to recolour tens of thousands of compiled sprite frames — a full character hair recolour (59,040 frames) and a werebear reskin — that blend seamlessly with the original art.