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 _services = new List(); private readonly List _plugins = new List(); 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(_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; } } }