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>
This commit is contained in:
commit
d1442e3747
1382 changed files with 170725 additions and 0 deletions
346
Managed/Decal.Core/DecalCoreImpl.cs
Normal file
346
Managed/Decal.Core/DecalCoreImpl.cs
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue