openDecal/Managed/Decal.Core/DecalCoreImpl.cs
erik d1442e3747 Initial commit: Complete open-source Decal rebuild
All 5 phases of the open-source Decal rebuild:

Phase 1: 14 decompiled .NET projects (Interop.*, Adapter, FileService, DecalUtil)
Phase 2: 10 native DLLs rewritten as C# COM servers with matching GUIDs
  - DecalDat, DHS, SpellFilter, DecalInput, DecalNet, DecalFilters
  - Decal.Core, DecalControls, DecalRender, D3DService
Phase 3: C++ shims for Inject.DLL (D3D9 hooking) and LauncherHook.DLL
Phase 4: DenAgent WinForms tray application
Phase 5: WiX installer and build script

25 C# projects building with 0 errors.
Native C++ projects require VS 2022 + Windows SDK (x86).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 18:27:56 +01:00

346 lines
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Decal.Interop.Core;
using Microsoft.Win32;
namespace Decal.Core
{
[ComVisible(true)]
[Guid("4557D5A1-00DB-48F6-ACB3-4FEF30E2F358")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces("Decal.Interop.Core.IDecalEvents\0\0")]
[ProgId("Decal.DecalCore")]
public class DecalCoreImpl : IDecalCore
{
// COM events
public event IDecalEvents_InitializeCompleteEventHandler InitializeComplete;
public event IDecalEvents_TerminateCompleteEventHandler TerminateComplete;
// Services and plugins
private struct ServiceEntry
{
public Guid Clsid;
public IDecalService Service;
public IDecalRender Renderer;
}
private readonly List<ServiceEntry> _services = new List<ServiceEntry>();
private readonly List<PluginSite2Impl> _plugins = new List<PluginSite2Impl>();
private readonly ACHooksImpl _hooks = new ACHooksImpl();
private bool _servicesRunning;
private bool _pluginsRunning;
// Graphics
private object _d3dDevice;
private int _hwnd;
private int _screenWidth = 800;
private int _screenHeight = 600;
internal ACHooksImpl ACHooksInstance => _hooks;
// IDecalCore implementation
public void InitGraphics(object pD3DDevice)
{
_d3dDevice = pD3DDevice;
// Notify render services
foreach (var svc in _services)
{
svc.Renderer?.ChangeDirectX();
}
}
public object GetD3DDevice(ref Guid riid) => _d3dDevice;
public int HWND
{
get => _hwnd;
set
{
_hwnd = value;
foreach (var svc in _services)
svc.Renderer?.ChangeHWND();
}
}
public bool Focus => true;
public void SendWM(int HWND, short uMsg, int wParam, int lParam, ref bool pbEat)
{
pbEat = false;
}
public void GetScreenSize(out int pWidth, out int pHeight)
{
pWidth = _screenWidth;
pHeight = _screenHeight;
}
public string MapPath(string pPath)
{
if (string.IsNullOrEmpty(pPath)) return pPath;
string result = pPath;
int pos = 0;
while (pos < result.Length)
{
int start = result.IndexOf('%', pos);
if (start < 0) break;
int end = result.IndexOf('%', start + 1);
if (end < 0) break;
string token = result.Substring(start + 1, end - start - 1);
string resolved = ResolveToken(token);
result = result.Substring(0, start) + resolved + result.Substring(end + 1);
pos = start + resolved.Length;
}
return result;
}
private string ResolveToken(string token)
{
string lower = token.ToLowerInvariant();
if (lower == "ac" || lower.StartsWith("ac:"))
{
try
{
using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Turbine\Asheron's Call"))
{
return key?.GetValue("Path") as string ?? "";
}
}
catch { return ""; }
}
if (lower == "decal" || lower.StartsWith("decal:"))
{
try
{
using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Decal\Agent"))
{
return key?.GetValue("AgentPath") as string ?? "";
}
}
catch { return ""; }
}
return "%" + token + "%";
}
public void StartPlugins()
{
if (_pluginsRunning) return;
if (!_servicesRunning) StartServices();
// Notify services: before plugins
foreach (var svc in _services)
svc.Service.BeforePlugins();
// Enumerate and load plugins from registry
var pluginEnum = new DecalEnumImpl();
pluginEnum.Initialize("Plugins");
try
{
pluginEnum.Next(); // Move to first
while (true)
{
if (pluginEnum.Enabled)
{
try
{
var riid = typeof(IPlugin2).GUID;
var pluginObj = pluginEnum.CreateInstance(ref riid);
var plugin = pluginObj as IPlugin2;
if (plugin != null)
{
var site = new PluginSite2Impl();
site.Setup(this, plugin, pluginEnum.ComClass);
_plugins.Add(site);
plugin.Initialize((PluginSite2)(object)site);
}
}
catch { } // Bad plugins are skipped
}
pluginEnum.Next();
}
}
catch { } // Next() throws when past end
_pluginsRunning = true;
// Notify services: after plugins
foreach (var svc in _services)
svc.Service.AfterPlugins();
InitializeComplete?.Invoke(eDecalComponentType.ePlugin);
}
public void StopPlugins()
{
if (!_pluginsRunning) return;
// Unload all plugins (copy list since Unload modifies it)
var sites = new List<PluginSite2Impl>(_plugins);
foreach (var site in sites)
{
try { site.Unload(); } catch { }
}
_plugins.Clear();
_pluginsRunning = false;
// Notify services: after plugins stopped
foreach (var svc in _services)
{
try { svc.Service.AfterPlugins(); } catch { }
}
TerminateComplete?.Invoke(eDecalComponentType.ePlugin);
}
public object Object => null;
public void StartServices()
{
if (_servicesRunning) return;
// Initialize ACHooks first
_hooks.SetDecal(this);
// Enumerate and load services from registry
var serviceEnum = new DecalEnumImpl();
serviceEnum.Initialize("Services");
try
{
serviceEnum.Next();
while (true)
{
if (serviceEnum.Enabled)
{
try
{
var riid = typeof(IDecalService).GUID;
var svcObj = serviceEnum.CreateInstance(ref riid);
var service = svcObj as IDecalService;
if (service != null)
{
service.Initialize((DecalCore)(object)this);
// Check for render capability
IDecalRender renderer = svcObj as IDecalRender;
_services.Add(new ServiceEntry
{
Clsid = serviceEnum.ComClass,
Service = service,
Renderer = renderer
});
}
}
catch { } // Service init failure - skip
}
serviceEnum.Next();
}
}
catch { } // Next() throws when past end
_servicesRunning = true;
InitializeComplete?.Invoke(eDecalComponentType.eService);
}
public DecalEnum Configuration
{
get
{
var e = new DecalEnumImpl();
e.Initialize("Plugins");
return (DecalEnum)(object)e;
}
}
public void StopServices()
{
if (!_servicesRunning) return;
if (_pluginsRunning) StopPlugins();
// Stop services in reverse order (LIFO)
for (int i = _services.Count - 1; i >= 0; i--)
{
try { _services[i].Service.Terminate(); } catch { }
}
_services.Clear();
_servicesRunning = false;
TerminateComplete?.Invoke(eDecalComponentType.eService);
}
public object Plugin => null;
public object Service => null;
public void Render2D()
{
foreach (var svc in _services)
svc.Renderer?.Render2D();
}
public void Render3D()
{
_hooks.FireRenderPreUI();
foreach (var svc in _services)
svc.Renderer?.Render3D();
}
public void PreReset()
{
foreach (var svc in _services)
svc.Renderer?.PreReset();
}
public void PostReset()
{
foreach (var svc in _services)
svc.Renderer?.PostReset();
}
public ACHooks Hooks => (ACHooks)(object)_hooks;
public void StartFilters()
{
// Filters are started as part of services
}
public void StopFilters()
{
// Filters are stopped as part of services
}
public bool KillBitCheckByGUID(string bstrGuid, string bstrVersion) => false;
public bool KillBitCheckByProgID(string bstProgID, string bstrVersion) => false;
public bool PluginsRunning => _pluginsRunning;
// Internal methods for PluginSite2
internal void RemovePlugin(PluginSite2Impl site) => _plugins.Remove(site);
internal object GetService(Guid clsid)
{
foreach (var svc in _services)
if (svc.Clsid == clsid) return svc.Service;
return null;
}
internal object GetPlugin(Guid clsid)
{
foreach (var site in _plugins)
if (site.PluginClsid == clsid) return site.Plugin;
return null;
}
}
}