# Plan: Open-Source Decal Rebuild ## Context Decal is a plugin framework for Asheron's Call. The game's official servers shut down, but the community runs it via ACEmulator. The modern Decal installer (v2.9.8.3) is closed-source. We have old C++ source code (partial match) and have decompiled all .NET assemblies. Goal: produce a fully open-source Decal that works with the original AC client, buildable from a single Visual Studio 2022 solution. ## Architecture Decision - **Rewrite native C++ DLLs as C# COM servers** — same GUIDs, same interfaces, different implementation language - **Keep only 2 components in C++:** `Inject.DLL` (D3D hooking, ~500-800 lines) and `LauncherHook.DLL` (~200 lines) - **Use Vortice.DirectX** for D3D9 rendering in C# (DecalRender, D3DService) - **Preserve plugin compatibility** by matching all COM CLSIDs/IIDs exactly ## Project Structure ``` reconstructed/ ├── Managed/ # 25 C# projects │ ├── Decal.sln # Solution file (25 projects, 0 errors) │ ├── Directory.Build.props # Shared: net472, x86, LangVersion 12 │ ├── Decal.Interop.*/ # 11 COM interface assemblies (decompiled) │ ├── Decal.Adapter/ # Plugin adapter (decompiled) │ ├── Decal.FileService/ # File service (decompiled) │ ├── DecalUtil/ # Utility exe (decompiled) │ ├── Decal.DecalDat/ # DAT file I/O (COM server) │ ├── Decal.DHS/ # Hotkey system (COM server) │ ├── Decal.SpellFilter/ # Spell data (COM server) │ ├── Decal.DecalInput/ # Input handling (COM server) │ ├── Decal.DecalNet/ # Network packets (COM server) │ ├── Decal.DecalFilters/ # Game data/world objects (COM server) │ ├── Decal.Core/ # Plugin manager/lifecycle (COM server) │ ├── Decal.DecalControls/ # UI widgets (COM server) │ ├── Decal.DecalRender/ # HUD rendering (COM server) │ ├── Decal.D3DService/ # 3D markers (COM server) │ └── Decal.DenAgent/ # System tray app (WinForms) ├── Native/ │ ├── InjectModern/ # Inject.DLL - D3D9 vtable hooking (C++) │ ├── LauncherHookModern/ # LauncherHook.DLL - process injection (C++) │ ├── CMakeLists.txt # Top-level CMake for native builds │ ├── Inject/ # Old C++ source (reference only) │ ├── LobbyHook/ # Old C++ source (reference only) │ ├── Decal/ # Old C++ source (reference only) │ └── ... # Other old native source (reference) ├── Installer/ │ ├── Package.wxs # WiX v5 MSI package definition │ └── Decal.Installer.wixproj # WiX project file ├── build.cmd # Top-level build script └── docs/ └── PLAN.md # This file ``` --- ## Status: Phases 1-5 Complete (Skeleton) All 5 phases are structurally complete. The solution compiles with 0 errors. Most COM server methods are **stubs** — they implement the correct interfaces and preserve GUIDs, but many methods are no-ops that need real logic. --- ## Phase 1: Build Existing .NET Layer ✅ COMPLETE - 14 decompiled .NET projects building from `Decal.sln` - `Directory.Build.props` centralizes build settings - All Interop projects use ProjectReference ## Phase 2: Rewrite Native DLLs as C# COM Servers ✅ COMPLETE (stubs) | # | Component | CLSID | Status | |---|-----------|-------|--------| | 1 | DecalDat | IDatService, IDatReader | Stub | | 2 | DHS | IHotkeySystem, IHotkey | Stub | | 3 | SpellFilter | ISpells, ISpell | Stub | | 4 | DecalInput | IInputService, IKeyboard, IMouse | Stub | | 5 | DecalNet | INetService, IMessage2, INetworkFilter2 | Stub | | 6 | DecalFilters | IWorld, IWorldObject, ICharacterStats | Stub | | 7 | Decal.Core | IDecalCore, IPluginSite2, IACHooks, IDecalEnum | Stub | | 8 | DecalControls | 16 control types | Stub | | 9 | DecalRender | IRenderService, IHUDView, IHUDBackground | Stub | | 10 | D3DService | ID3DService, ID3DObj | Stub | All preserve original CLSIDs/GUIDs for plugin compatibility. ## Phase 3: C++ Shims ✅ COMPLETE - `Native/InjectModern/` — Inject.DLL (~530 lines) — D3D9 vtable hooking, lifecycle - `Native/LauncherHookModern/` — LauncherHook.DLL (~330 lines) — CreateProcess IAT hook - CMake build: `cmake -G "Visual Studio 17 2022" -A Win32` ## Phase 4: DenAgent ✅ COMPLETE - `Managed/Decal.DenAgent/` — System tray, plugin management, options dialog ## Phase 5: Build System & Installer ✅ COMPLETE - `Installer/Package.wxs` — WiX v5 MSI - `build.cmd` — Top-level build script --- ## Phase 6: Verification & Implementation (IN PROGRESS) Goal: **Pixel-perfect drop-in replacement** for the closed-source Decal v2.9.8.3. Strategy: **Bottom-up, one DLL at a time.** Replace one original DLL with our C# version while keeping all other original DLLs in place. Test. Move to the next. ### Step 1: Build on Windows ✅ COMPLETE ``` dotnet build Managed\Decal.sln -c Release # 25 projects, 0 errors ``` ### Step 2: COM Registration Test ✅ COMPLETE Automated script: `tools/Test-ComRegistration.ps1 -Build` - 10/10 DLLs register successfully with regasm /codebase - 50/50 CLSIDs verified in registry (WOW6432Node for 32-bit on 64-bit Windows) - 0 GUID mismatches vs original Decal .rgs files - 35 CLSIDs match original .rgs files exactly - 15 additional CLSIDs from decompiled interops (no .rgs source available) - 23 original CLSIDs not in scope (Inject module C++, internal action classes) ### Step 2b: DecalDat Functional Test ✅ COMPLETE `Managed/Decal.DecalDat.Tests/` — 13/13 tests pass against real AC DAT files: - Opens portal.dat (884 MB, sector size 1024) and cell.dat (332 MB, sector size 256) - Reads 6 known file IDs from portal.dat (SpellTable, ComponentTable, etc.) - DatStream: Read, Restart, ReadBinary with Tell tracking ### Step 3: Smoke Test — DenAgent Run `Decal.DenAgent.exe` and verify: - Tray icon appears - Configure dialog opens - Plugin list populates from registry ### Step 4: Integration Test (one DLL at a time) Install original Decal v2.9.8.3, then swap one DLL at a time: 1. Replace `DecalDat.DLL` → test DAT file reading 2. Replace `DHS.dll` → test hotkeys 3. Replace `SpellFilter.dll` → test spell data 4. Continue through all 10... Each swap that fails tells us exactly which stubs need real implementations. ### Step 5: Plugin Compatibility Test Load community Decal plugins and verify they work: - VTank - Mag-Tools - Other popular plugins --- ## Known Pitfalls - `record` types need IsExternalInit, unavailable in net472 → use class instead - `CollectionsMarshal.AsSpan` unavailable in net472 - LongValueKey enum: correct names are `keyStackCount`, `keyContainer` - tagPOINT defined in Interop.Inject, needed by Interop.Render → add ProjectReference - ICommandEvents delegates ambiguous between Interop.Controls and Interop.Inject → use `using` aliases ## Build Requirements - .NET SDK 8.0+ (targets net472 via NuGet reference assemblies) - Visual Studio 2022 with C++ Desktop workload (for native DLLs) - Windows SDK (for d3d9.h, Win32 APIs) - WiX Toolset v5 (optional, for MSI installer)