using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Threading; using Decal.Adapter.IDQueue; using Decal.Adapter.Messages; using Decal.Adapter.Support; using Decal.Adapter.Wrappers; using Decal.Interop.Core; using Decal.Interop.D3DService; using Decal.Interop.DHS; using Decal.Interop.Filters; using Decal.Interop.Inject; using Decal.Interop.Render; using Microsoft.Win32; namespace Decal.Adapter; /// /// Lifetime Service required for the .NET Surrogate to function. /// [CLSCompliant(true)] public sealed class CoreManager : MarshalByRefObject { private struct sAssemblyPreloadData { public string AssemblyName; public string Path; } private static bool myServiceRunning; private static CoreManager myService; private DecalCoreClass myDecal; private ACHooksClass myHooks; private PluginSite mySitev1; private DecalWrapper myWrappedDecal; private HooksWrapper myWrappedHooks; private global::Decal.Adapter.Wrappers.HotkeySystem myDHS; private CharacterFilter myCharacterFilter; private WorldFilter myWorldFilter; private global::Decal.Adapter.Wrappers.EchoFilter2 myEchoFilter; private D3DService myD3DService; private RenderServiceWrapper myRenderService; private FairIDQueue myIDQueue; private Collection myPaths = new Collection(); private Dictionary> myAssemblies = new Dictionary>(); private int myAssemblyPreloadCounter = 1; private bool myAssemblyPreloadStarted; private Dictionary myFilters = new Dictionary(); private Dictionary myNamedFilters = new Dictionary(); private Dictionary myServices = new Dictionary(); private Dictionary myNamedServices = new Dictionary(); private Dictionary myPlugins = new Dictionary(); private EventHandler myItemSelected; private EventHandler myItemDestroyed; private EventHandler myChatParserIntercept; private EventHandler myRegionChange3D; private EventHandler myChatTextIntercept; private EventHandler myStatusTextIntercept; private EventHandler myMessageProcessed; private EventHandler myContainerOpened; private EventHandler myChatClickIntercept; private EventHandler myWindowMessage; private EventHandler myFilterInitComplete; private EventHandler myServiceInitComplete; private EventHandler myPluginInitComplete; private EventHandler myFilterTermComplete; private EventHandler myServiceTermComplete; private EventHandler myPluginTermComplete; private EventHandler myRender_PreUI; private EventHandler myExtensionMessage; private AppDomain myPluginDomain; private AppDomain myDomain; /// /// Returns if the service was initialized by Decal. /// public static bool ServiceRunning => myServiceRunning; /// /// Returns the Singleton instance of the Service /// (Initializes if necessary) /// public static CoreManager Current { get { if (myService == null) { myService = new CoreManager(); } return myService; } } /// /// Tracing level for Decal.Adapter, and all loaded plugins. /// public static int TracingLevel => Util.TraceLevel; /// /// FileService (Decal.FileService) /// public FilterBase FileService => Filter("Decal.FileService"); /// /// Decal Hotkey System /// public global::Decal.Adapter.Wrappers.HotkeySystem HotkeySystem => myDHS; /// /// Character Filter /// [CLSCompliant(false)] public CharacterFilter CharacterFilter => myCharacterFilter; [CLSCompliant(false)] public WorldFilter WorldFilter => myWorldFilter; /// /// Direct3D Service /// [CLSCompliant(false)] public global::Decal.Adapter.Wrappers.EchoFilter2 EchoFilter => myEchoFilter; [CLSCompliant(false)] public D3DService D3DService => myD3DService; [CLSCompliant(false)] public HooksWrapper Actions => myWrappedHooks; [CLSCompliant(false)] public DecalWrapper Decal => myWrappedDecal; public RenderServiceWrapper RenderService => myRenderService; public FairIDQueue IDQueue => myIDQueue; internal AppDomain PluginDomain => myDomain; [CLSCompliant(false)] public event EventHandler ItemSelected { add { myItemSelected = (EventHandler)Delegate.Combine(myItemSelected, value); } remove { myItemSelected = (EventHandler)Delegate.Remove(myItemSelected, value); } } [CLSCompliant(false)] public event EventHandler ItemDestroyed { add { myItemDestroyed = (EventHandler)Delegate.Combine(myItemDestroyed, value); } remove { myItemDestroyed = (EventHandler)Delegate.Remove(myItemDestroyed, value); } } [CLSCompliant(false)] public event EventHandler CommandLineText { add { myChatParserIntercept = (EventHandler)Delegate.Combine(myChatParserIntercept, value); } remove { myChatParserIntercept = (EventHandler)Delegate.Remove(myChatParserIntercept, value); } } [CLSCompliant(false)] public event EventHandler StatusBoxMessage { add { myStatusTextIntercept = (EventHandler)Delegate.Combine(myStatusTextIntercept, value); } remove { myStatusTextIntercept = (EventHandler)Delegate.Remove(myStatusTextIntercept, value); } } [CLSCompliant(false)] public event EventHandler RegionChange3D { add { myRegionChange3D = (EventHandler)Delegate.Combine(myRegionChange3D, value); } remove { myRegionChange3D = (EventHandler)Delegate.Remove(myRegionChange3D, value); } } [CLSCompliant(false)] public event EventHandler ChatBoxMessage { add { myChatTextIntercept = (EventHandler)Delegate.Combine(myChatTextIntercept, value); } remove { myChatTextIntercept = (EventHandler)Delegate.Remove(myChatTextIntercept, value); } } [CLSCompliant(false)] public event EventHandler MessageProcessed { add { myMessageProcessed = (EventHandler)Delegate.Combine(myMessageProcessed, value); } remove { myMessageProcessed = (EventHandler)Delegate.Remove(myMessageProcessed, value); } } [CLSCompliant(false)] public event EventHandler ContainerOpened { add { myContainerOpened = (EventHandler)Delegate.Combine(myContainerOpened, value); } remove { myContainerOpened = (EventHandler)Delegate.Remove(myContainerOpened, value); } } [CLSCompliant(false)] public event EventHandler ChatNameClicked { add { myChatClickIntercept = (EventHandler)Delegate.Combine(myChatClickIntercept, value); } remove { myChatClickIntercept = (EventHandler)Delegate.Remove(myChatClickIntercept, value); } } public event EventHandler FilterInitComplete { add { myFilterInitComplete = (EventHandler)Delegate.Combine(myFilterInitComplete, value); } remove { myFilterInitComplete = (EventHandler)Delegate.Remove(myFilterInitComplete, value); } } public event EventHandler ServiceInitComplete { add { myServiceInitComplete = (EventHandler)Delegate.Combine(myServiceInitComplete, value); } remove { myServiceInitComplete = (EventHandler)Delegate.Remove(myServiceInitComplete, value); } } public event EventHandler PluginInitComplete { add { myPluginInitComplete = (EventHandler)Delegate.Combine(myPluginInitComplete, value); } remove { myPluginInitComplete = (EventHandler)Delegate.Remove(myPluginInitComplete, value); } } public event EventHandler FilterTermComplete { add { myFilterTermComplete = (EventHandler)Delegate.Combine(myFilterTermComplete, value); } remove { myFilterTermComplete = (EventHandler)Delegate.Remove(myFilterTermComplete, value); } } public event EventHandler ServiceTermComplete { add { myServiceTermComplete = (EventHandler)Delegate.Combine(myServiceTermComplete, value); } remove { myServiceTermComplete = (EventHandler)Delegate.Remove(myServiceTermComplete, value); } } public event EventHandler PluginTermComplete { add { myPluginTermComplete = (EventHandler)Delegate.Combine(myPluginTermComplete, value); } remove { myPluginTermComplete = (EventHandler)Delegate.Remove(myPluginTermComplete, value); } } public event EventHandler RenderFrame { add { myRender_PreUI = (EventHandler)Delegate.Combine(myRender_PreUI, value); } remove { myRender_PreUI = (EventHandler)Delegate.Remove(myRender_PreUI, value); } } [CLSCompliant(false)] public event EventHandler WindowMessage { add { myWindowMessage = (EventHandler)Delegate.Combine(myWindowMessage, value); } remove { myWindowMessage = (EventHandler)Delegate.Remove(myWindowMessage, value); } } internal event EventHandler ExtensionMessage { add { myExtensionMessage = (EventHandler)Delegate.Combine(myExtensionMessage, value); } remove { myExtensionMessage = (EventHandler)Delegate.Remove(myExtensionMessage, value); } } private CoreManager() { AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; string location = System.Reflection.Assembly.GetExecutingAssembly().Location; AddAssemblyPath(Path.GetDirectoryName(location)); myDomain = AppDomain.CurrentDomain; Util.InitializeTracing("HKEY_LOCAL_MACHINE\\Software\\Decal\\Decal.Adapter", "Tracing", 0); } private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { AppDomain appDomain = (AppDomain)sender; Assembly[] assemblies = appDomain.GetAssemblies(); string text = appDomain.ApplyPolicy(args.Name); if (string.IsNullOrEmpty(text)) { text = args.Name; } Assembly[] array = assemblies; foreach (Assembly assembly in array) { if (assembly.FullName == text) { return assembly; } } if (!appDomain.IsDefaultAppDomain()) { assemblies = myDomain.GetAssemblies(); array = assemblies; foreach (Assembly assembly2 in array) { if (assembly2.FullName == text) { AssemblyName name = assembly2.GetName(copiedName: false); return appDomain.Load(name); } } } string text2 = ((text.IndexOf(",") <= 0) ? text : text.Substring(0, text.IndexOf(","))); if (!string.IsNullOrEmpty(text2)) { text2 = text2.ToLowerInvariant(); if (string.IsNullOrEmpty(Path.GetExtension(text2))) { text2 += ".dll"; } foreach (string myPath in myPaths) { if (File.Exists(Path.Combine(myPath, text2))) { AssemblyName assemblyName = new AssemblyName(); assemblyName.CodeBase = Path.Combine(myPath, text2); return appDomain.Load(assemblyName); } } } Util.WriteLine("Assembly resolve failed for assembly:" + args.Name); return null; } private void AddAssemblyPath(string path) { string item = path.ToLowerInvariant(); if (!myPaths.Contains(item)) { myPaths.Add(item); } } private void myDecal_InitializeComplete(eDecalComponentType myComponent) { Util.WriteLine("InitializeComplete for: " + myComponent); switch (myComponent) { case eDecalComponentType.eService: Message.Initialize(Path.Combine((string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Decal\\Agent", "AgentPath", ""), "messages.xml")); if (myWrappedDecal != null) { try { if (myWrappedDecal.GetObject("services\\D3DService.Service") is CService obj) { myD3DService = new D3DService(obj); } if (myWrappedDecal.GetObject("services\\DecalRender.RenderService") is RenderService render) { myRenderService = new RenderServiceWrapper(render); } object obj2 = myWrappedDecal.GetObject("services\\DecalPlugins.InjectService\\site", DecalWrapper.IID_IUNKNOWN); mySitev1 = obj2 as PluginSite; } catch (Exception ex) { Util.WriteLine("Unable to get service reference: " + ex.Message); } } else { Util.WriteLine("Not getting services, we don't have a Decal object to call GetObject on."); } try { myIDQueue = new FairIDQueue(); } catch (Exception ex2) { Util.WriteLine("Service initialization exception: " + ex2.Message); } Util.SafeFireEvent(this, myServiceInitComplete, new EventArgs()); break; case eDecalComponentType.eNetworkFilter: if (myWrappedDecal != null) { try { if (myWrappedDecal.GetObject("services\\DecalNet.NetService\\DecalFilters.World") is World obj3) { myWorldFilter = new WorldFilter(obj3); } if (myWrappedDecal.GetObject("services\\DecalNet.NetService\\DecalFilters.CharacterStats") is CharacterStats obj4) { myCharacterFilter = new CharacterFilter(obj4); } if (myWrappedDecal.GetObject("services\\DecalNet.NetService\\DecalFilters.EchoFilter2") is global::Decal.Interop.Filters.EchoFilter2 obj5) { myEchoFilter = new global::Decal.Adapter.Wrappers.EchoFilter2(obj5); } } catch (Exception ex3) { Util.WriteLine("Unable to get filter reference: " + ex3.Message); } } else { Util.WriteLine("Not getting filters, we don't have a Decal object to call GetObject on."); } Util.SafeFireEvent(this, myFilterInitComplete, new EventArgs()); DecrementAssemblyPreloadLockCounter(); break; case eDecalComponentType.ePlugin: if (myWrappedDecal.GetObject("plugins\\DHS.HotkeySystem") is global::Decal.Interop.DHS.HotkeySystem hks) { myDHS = new global::Decal.Adapter.Wrappers.HotkeySystem(hks); } myIDQueue.Start(); Util.SafeFireEvent(this, myPluginInitComplete, new EventArgs()); break; } } private void myDecal_TerminateComplete(eDecalComponentType myComponent) { Util.WriteLine("TerminateComplete for: " + myComponent); switch (myComponent) { case eDecalComponentType.eService: if (myD3DService != null) { myD3DService.Dispose(); myD3DService = null; } if (myRenderService != null) { myRenderService.Dispose(); myRenderService = null; } if (mySitev1 != null) { Marshal.ReleaseComObject(mySitev1); } myServices.Clear(); myNamedServices.Clear(); Util.SafeFireEvent(this, myServiceTermComplete, new EventArgs()); break; case eDecalComponentType.eNetworkFilter: if (myWorldFilter != null) { myWorldFilter.Dispose(); myWorldFilter = null; } if (myCharacterFilter != null) { myCharacterFilter.Dispose(); myCharacterFilter = null; } if (myEchoFilter != null) { myEchoFilter.Dispose(); myEchoFilter = null; } myFilters.Clear(); myNamedFilters.Clear(); Util.SafeFireEvent(this, myFilterTermComplete, new EventArgs()); break; case eDecalComponentType.ePlugin: myIDQueue.Stop(); if (myDHS != null) { myDHS.Dispose(); myDHS = null; } myPlugins.Clear(); if (myPluginDomain != null) { lock (myAssemblies) { if (myAssemblies.ContainsKey(myPluginDomain)) { myAssemblies[myPluginDomain].Clear(); myAssemblies.Remove(myPluginDomain); } AppDomain.Unload(myPluginDomain); myPluginDomain = null; } } Util.SafeFireEvent(this, myPluginTermComplete, new EventArgs()); break; } } private void myHooks_ObjectSelected(int lGUID) { Util.SafeFireEvent(this, myItemSelected, new ItemSelectedEventArgs(lGUID)); } private void myHooks_ObjectDestroyed(int lGUID) { Util.SafeFireEvent(this, myItemDestroyed, new ItemDestroyedEventArgs(lGUID)); } private void myHooks_AC3DRegionChanged(int left, int top, int right, int bottom) { Util.SafeFireEvent(this, myRegionChange3D, new RegionChange3DEventArgs(left, top, right, bottom)); } private void myHooks_ChatParserIntercept(string bstrText, ref bool bEat) { Util.SafeFireEvent(this, myChatParserIntercept, new ChatParserInterceptEventArgs(bstrText, bEat), ref bEat); } private void myHooks_StatusTextIntercept(string bstrText, ref bool bEat) { Util.SafeFireEvent(this, myStatusTextIntercept, new StatusTextInterceptEventArgs(bstrText, bEat), ref bEat); } private void myHooks_ChatTextIntercept(string bstrText, int lColor, int target, ref bool bEat) { Util.SafeFireEvent(this, myChatTextIntercept, new ChatTextInterceptEventArgs(bstrText, lColor, target, bEat), ref bEat); } private void myHooks_MessageProcessed(int pbData, int dwSize) { if (myMessageProcessed != null) { MessageProcessedEventArgs args = new MessageProcessedEventArgs(pbData, dwSize); Util.SafeFireEvent(this, myMessageProcessed, args); } } private void myHooks_ContainerOpened(int lGUID) { Util.SafeFireEvent(this, myContainerOpened, new ContainerOpenedEventArgs(lGUID)); } private void myHooks_ChatClickIntercept(string bstrText, int id, ref bool bEat) { Util.SafeFireEvent(this, myChatClickIntercept, new ChatClickInterceptEventArgs(bstrText, id, bEat), ref bEat); } private void myHooks_RenderPreUI() { Util.SafeFireEvent(this, myRender_PreUI, new EventArgs()); } internal bool FireWindowMessage(int HWND, short uMsg, int wParam, int lParam) { bool eaten = false; WindowMessageEventArgs args = new WindowMessageEventArgs(HWND, uMsg, wParam, lParam); Util.SafeFireEvent(this, myWindowMessage, args, ref eaten); return eaten; } internal void FireExtensionMessage(object sender, AdapterMessageEventArgs args) { args.CanAddHandlers = true; Util.SafeFireEvent(this, myExtensionMessage, args); args.CanAddHandlers = false; } public FilterBase Filter(string filterName) { try { if (myNamedFilters.ContainsKey(filterName)) { return myNamedFilters[filterName]; } Type type = Type.GetType(filterName); if (null != type && myFilters.ContainsKey(type)) { return myFilters[type]; } } catch (Exception) { } return null; } public T Filter() where T : FilterBase { try { if (myFilters.ContainsKey(typeof(T))) { return (T)myFilters[typeof(T)]; } } catch (Exception) { } return null; } public ServiceBase Service(string serviceName) { try { if (myNamedServices.ContainsKey(serviceName)) { return myNamedServices[serviceName]; } Type type = Type.GetType(serviceName); if (null != type && myServices.ContainsKey(type)) { return myServices[type]; } } catch (Exception) { } return null; } public T Service() where T : ServiceBase { try { if (myServices.ContainsKey(typeof(T))) { return (T)myServices[typeof(T)]; } } catch (Exception) { } return null; } /// /// Get Mapped Keyboard Key /// /// Name to retrive mapping for. /// Mapped Key public int QueryKeyBoardMap(string name) { int result = 0; if (mySitev1 != null) { result = mySitev1.QueryKeyboardMap(name); } return result; } internal void AddFilter(FilterBase newFilter) { AddFilter(newFilter.ReferenceName, newFilter); } internal void AddFilter(string filterName, FilterBase newFilter) { myFilters.Add(newFilter.GetType(), newFilter); myNamedFilters.Add(filterName, newFilter); } internal void AddService(ServiceBase newService) { AddService(newService.ReferenceName, newService); } internal void AddService(string serviceName, ServiceBase newService) { myServices.Add(newService.GetType(), newService); myNamedServices.Add(serviceName, newService); } internal void AddPlugin(PluginBase newPlugin) { AddPlugin(newPlugin.ReferenceName, newPlugin); } internal void AddPlugin(string pluginName, PluginBase newPlugin) { myPlugins.Add(pluginName, newPlugin); } /// /// Load the Assembly requested, first checking our internal cache /// /// Assembly name to load /// Path to the assembly /// Loaded Assembly, from cache if already loaded internal Assembly Assembly(string name, string path) { return Assembly(name, path, AppDomain.CurrentDomain); } internal Assembly Assembly(string name, string path, AppDomain useDomain) { lock (myAssemblies) { string text = name.ToLowerInvariant(); useDomain = myDomain; Dictionary dictionary; if (myAssemblies.ContainsKey(useDomain)) { dictionary = myAssemblies[useDomain]; } else { myAssemblies.Add(useDomain, new Dictionary()); dictionary = myAssemblies[useDomain]; } if (dictionary.ContainsKey(text)) { return dictionary[text]; } AddAssemblyPath(path); Assembly assembly = System.Reflection.Assembly.LoadFrom(Path.Combine(path, text)); dictionary.Add(text, assembly); return assembly; } } internal void Initialize(DecalCore pDecal) { Util.WriteLine("Decal.Adapter Lifetime-service Initialize Begun"); myServiceRunning = true; Util.Write("Get Decal.. "); myDecal = (DecalCoreClass)pDecal; myWrappedDecal = new DecalWrapper(myDecal); Util.WriteLine("Sink Decal Events.. "); myDecal.InitializeComplete += myDecal_InitializeComplete; myDecal.TerminateComplete += myDecal_TerminateComplete; Util.Write("Get Hooks.. "); myHooks = (ACHooksClass)myDecal.Hooks; myWrappedHooks = new HooksWrapper(myHooks); Util.WriteLine("Sink Hooks Events.. "); myHooks.ChatTextIntercept += myHooks_ChatTextIntercept; myHooks.StatusTextIntercept += myHooks_StatusTextIntercept; myHooks.ChatParserIntercept += myHooks_ChatParserIntercept; myHooks.AC3DRegionChanged += myHooks_AC3DRegionChanged; myHooks.ObjectSelected += myHooks_ObjectSelected; myHooks.ObjectDestroyed += myHooks_ObjectDestroyed; myHooks.MessageProcessed += myHooks_MessageProcessed; myHooks.ContainerOpened += myHooks_ContainerOpened; myHooks.ChatClickIntercept += myHooks_ChatClickIntercept; myHooks.RenderPreUI += myHooks_RenderPreUI; Util.WriteLine("Decal.Adapter Lifetime-service Initiliazed"); } internal void Terminate() { myDecal.InitializeComplete -= myDecal_InitializeComplete; myDecal.TerminateComplete -= myDecal_TerminateComplete; myHooks.ChatTextIntercept -= myHooks_ChatTextIntercept; myHooks.StatusTextIntercept -= myHooks_StatusTextIntercept; myHooks.ChatParserIntercept -= myHooks_ChatParserIntercept; myHooks.AC3DRegionChanged -= myHooks_AC3DRegionChanged; myHooks.ObjectSelected -= myHooks_ObjectSelected; myHooks.ObjectDestroyed -= myHooks_ObjectDestroyed; myHooks.MessageProcessed -= myHooks_MessageProcessed; myHooks.ContainerOpened -= myHooks_ContainerOpened; myHooks.ChatClickIntercept -= myHooks_ChatClickIntercept; myHooks.RenderPreUI -= myHooks_RenderPreUI; myWrappedDecal.Dispose(); myWrappedDecal = null; Marshal.ReleaseComObject(myDecal); myDecal = null; myHooks = null; myWrappedHooks.Dispose(); myWrappedHooks = null; myService = null; myServiceRunning = false; } public void IncrementAssemblyPreloadLockCounter() { myAssemblyPreloadCounter++; } public void DecrementAssemblyPreloadLockCounter() { myAssemblyPreloadCounter--; if (myAssemblyPreloadCounter < 0) { myAssemblyPreloadCounter = 0; } if (myAssemblyPreloadCounter != 0 || myAssemblyPreloadStarted) { return; } myAssemblyPreloadStarted = true; List list = new List(); Guid clsidAdvance = Guid.Empty; Guid guid = new Guid("{71A69713-6593-47EC-0002-0000000DECA1}"); DecalEnum decalEnum = ((IDecalCore)Decal.Underlying).get_Configuration("Plugins", ref clsidAdvance); try { while (true) { decalEnum.Next(); if (!decalEnum.Enabled || decalEnum.SurrogateClass != guid) { continue; } try { if (decalEnum.Restricted) { continue; } } catch { continue; } list.Add(new sAssemblyPreloadData { AssemblyName = (string)((IDecalEnum)decalEnum).get_Property("Assembly"), Path = (string)((IDecalEnum)decalEnum).get_Property("Path") }); } } catch { } finally { Marshal.ReleaseComObject(decalEnum); } ThreadPool.QueueUserWorkItem(AssemblyPreloadThreadProc, list); } internal void AssemblyPreloadThreadProc(object data) { if (!(data is List list)) { return; } foreach (sAssemblyPreloadData item in list) { try { Util.WriteLine("Preloading plugin assembly {0}, {1}", item.AssemblyName, item.Path); Assembly(item.AssemblyName, item.Path, AppDomain.CurrentDomain); } catch { } } } }