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
10
Managed/Decal.DecalInput/Decal.DecalInput.csproj
Normal file
10
Managed/Decal.DecalInput/Decal.DecalInput.csproj
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<AssemblyName>Decal.DecalInput</AssemblyName>
|
||||
<RootNamespace>Decal.DecalInput</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Decal.Interop.Core\Decal.Interop.Core.csproj" />
|
||||
<ProjectReference Include="..\Decal.Interop.Input\Decal.Interop.Input.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
120
Managed/Decal.DecalInput/HotkeyImpl.cs
Normal file
120
Managed/Decal.DecalInput/HotkeyImpl.cs
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Decal.Interop.Input;
|
||||
|
||||
namespace Decal.DecalInput
|
||||
{
|
||||
[ComVisible(true)]
|
||||
[Guid("F183506A-3664-49D6-8CA4-CFD295F2811D")]
|
||||
[ClassInterface(ClassInterfaceType.None)]
|
||||
[ComSourceInterfaces(typeof(IHotkeyEvents))]
|
||||
[ProgId("DecalInput.Hotkey")]
|
||||
public class HotkeyImpl : IHotkey
|
||||
{
|
||||
private object _tag;
|
||||
private int _vk = -1;
|
||||
private bool _enabled;
|
||||
|
||||
public event IHotkeyEvents_HotkeyEventHandler Hotkey;
|
||||
|
||||
public object Tag
|
||||
{
|
||||
get => _tag;
|
||||
set => _tag = value;
|
||||
}
|
||||
|
||||
public string Key
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_vk == -1) return string.Empty;
|
||||
string name = KeyNames.NameFromVK(_vk);
|
||||
return name != null ? "{" + name + "}" : ((char)_vk).ToString();
|
||||
}
|
||||
set
|
||||
{
|
||||
string s = value?.ToUpperInvariant();
|
||||
if (string.IsNullOrEmpty(s)) return;
|
||||
|
||||
if (s[0] == '{')
|
||||
{
|
||||
int end = s.IndexOf('}');
|
||||
if (end > 1)
|
||||
{
|
||||
string keyName = s.Substring(1, end - 1);
|
||||
int vk = KeyNames.VKFromName(keyName);
|
||||
if (vk >= 0) _vk = vk;
|
||||
}
|
||||
}
|
||||
else if (s.Length == 1)
|
||||
{
|
||||
_vk = s[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set
|
||||
{
|
||||
if (value == _enabled) return;
|
||||
_enabled = value;
|
||||
|
||||
if (InputServiceImpl.Instance != null)
|
||||
{
|
||||
if (_enabled)
|
||||
InputServiceImpl.Instance.RegisterHotkey(this);
|
||||
else
|
||||
InputServiceImpl.Instance.UnregisterHotkey(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void FireHotkey()
|
||||
{
|
||||
Hotkey?.Invoke((Hotkey)(object)this);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class KeyNames
|
||||
{
|
||||
private static readonly Dictionary<string, int> _nameToVK = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{"BACKSPACE", 0x08}, {"BS", 0x08}, {"BKSP", 0x08},
|
||||
{"CAPSLOCK", 0x14}, {"DELETE", 0x2E}, {"DEL", 0x2E},
|
||||
{"DOWN", 0x28}, {"END", 0x23}, {"ENTER", 0x0D},
|
||||
{"ESC", 0x1B}, {"HELP", 0x2F}, {"HOME", 0x24},
|
||||
{"INS", 0x2D}, {"INSERT", 0x2D}, {"LEFT", 0x25},
|
||||
{"NUMLOCK", 0x90}, {"PGDN", 0x22}, {"PGUP", 0x21},
|
||||
{"PRTSC", 0x2C}, {"RIGHT", 0x27}, {"SCROLLLOCK", 0x91},
|
||||
{"TAB", 0x09}, {"UP", 0x26},
|
||||
{"F1", 0x70}, {"F2", 0x71}, {"F3", 0x72}, {"F4", 0x73},
|
||||
{"F5", 0x74}, {"F6", 0x75}, {"F7", 0x76}, {"F8", 0x77},
|
||||
{"F9", 0x78}, {"F10", 0x79}, {"F11", 0x7A}, {"F12", 0x7B},
|
||||
{"F13", 0x7C}, {"F14", 0x7D}, {"F15", 0x7E}, {"F16", 0x7F},
|
||||
{"+", 0x6B}, {"SHIFT", 0x10}, {"CTRL", 0x11}, {"ALT", 0x12},
|
||||
{"LBUTTON", 0x01}, {"RBUTTON", 0x02}
|
||||
};
|
||||
|
||||
private static readonly Dictionary<int, string> _vkToName;
|
||||
|
||||
static KeyNames()
|
||||
{
|
||||
_vkToName = new Dictionary<int, string>();
|
||||
// Build reverse mapping, preferring longer names
|
||||
foreach (var kvp in _nameToVK)
|
||||
{
|
||||
if (!_vkToName.ContainsKey(kvp.Value) || kvp.Key.Length > _vkToName[kvp.Value].Length)
|
||||
_vkToName[kvp.Value] = kvp.Key;
|
||||
}
|
||||
}
|
||||
|
||||
public static int VKFromName(string name) =>
|
||||
_nameToVK.TryGetValue(name, out int vk) ? vk : -1;
|
||||
|
||||
public static string NameFromVK(int vk) =>
|
||||
_vkToName.TryGetValue(vk, out string name) ? name : null;
|
||||
}
|
||||
}
|
||||
74
Managed/Decal.DecalInput/InputBufferImpl.cs
Normal file
74
Managed/Decal.DecalInput/InputBufferImpl.cs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Decal.Interop.Input;
|
||||
|
||||
namespace Decal.DecalInput
|
||||
{
|
||||
[ComVisible(true)]
|
||||
[Guid("F0A17A04-7F8F-4A17-A41D-8C297A1E929B")]
|
||||
[ClassInterface(ClassInterfaceType.None)]
|
||||
[ComSourceInterfaces(typeof(IInputBufferEvents))]
|
||||
[ProgId("DecalInput.InputBuffer")]
|
||||
public class InputBufferImpl : IInputBuffer
|
||||
{
|
||||
private object _tag;
|
||||
private readonly List<string> _commands = new List<string>();
|
||||
private bool _running;
|
||||
|
||||
public event IInputBufferEvents_BeginEventHandler Begin;
|
||||
public event IInputBufferEvents_EndEventHandler End;
|
||||
public event IInputBufferEvents_EventEventHandler Event;
|
||||
|
||||
public object Tag
|
||||
{
|
||||
get => _tag;
|
||||
set => _tag = value;
|
||||
}
|
||||
|
||||
public void Add(string Command)
|
||||
{
|
||||
_commands.Add(Command);
|
||||
}
|
||||
|
||||
public void Push(string Command)
|
||||
{
|
||||
_commands.Insert(0, Command);
|
||||
}
|
||||
|
||||
public void Pop()
|
||||
{
|
||||
if (_commands.Count > 0)
|
||||
_commands.RemoveAt(_commands.Count - 1);
|
||||
}
|
||||
|
||||
public bool CanRun => !_running && InputServiceImpl.Instance?.ActiveBuffer == null;
|
||||
|
||||
public void Run()
|
||||
{
|
||||
if (!CanRun) return;
|
||||
_running = true;
|
||||
|
||||
if (InputServiceImpl.Instance != null)
|
||||
InputServiceImpl.Instance.ActiveBuffer = this;
|
||||
|
||||
Begin?.Invoke((InputBuffer)(object)this);
|
||||
// Actions would be processed in Render2D tick
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_running = false;
|
||||
|
||||
if (InputServiceImpl.Instance?.ActiveBuffer == this)
|
||||
InputServiceImpl.Instance.ActiveBuffer = null;
|
||||
|
||||
End?.Invoke((InputBuffer)(object)this);
|
||||
_commands.Clear();
|
||||
}
|
||||
|
||||
internal void FireEvent(int eventId, object param)
|
||||
{
|
||||
Event?.Invoke((InputBuffer)(object)this, eventId, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
170
Managed/Decal.DecalInput/InputServiceImpl.cs
Normal file
170
Managed/Decal.DecalInput/InputServiceImpl.cs
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Decal.Interop.Core;
|
||||
using Decal.Interop.Input;
|
||||
|
||||
namespace Decal.DecalInput
|
||||
{
|
||||
[ComVisible(true)]
|
||||
[Guid("B33307BA-706D-474A-80B9-70BB8D13EF3E")]
|
||||
[ClassInterface(ClassInterfaceType.None)]
|
||||
[ProgId("DecalInput.InputService")]
|
||||
public class InputServiceImpl : IInputService, IDecalService
|
||||
{
|
||||
internal static InputServiceImpl Instance { get; private set; }
|
||||
|
||||
private IDecalCore _decalCore;
|
||||
private readonly List<HotkeyImpl> _hotkeys = new List<HotkeyImpl>();
|
||||
private readonly List<TimerImpl> _timers = new List<TimerImpl>();
|
||||
private readonly List<WinMsgHookImpl> _msgHooks = new List<WinMsgHookImpl>();
|
||||
private readonly Dictionary<string, int> _commands = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
private WndMsgImpl _message;
|
||||
|
||||
internal InputBufferImpl ActiveBuffer { get; set; }
|
||||
|
||||
public DecalCore Decal => (DecalCore)_decalCore;
|
||||
|
||||
// Parameterized property - KeyByName takes a string parameter via IDispatch
|
||||
public int KeyByName => 0;
|
||||
|
||||
internal int GetKeyByName(string name)
|
||||
{
|
||||
return KeyNames.VKFromName(name);
|
||||
}
|
||||
|
||||
// IDecalService
|
||||
public void Initialize(DecalCore pDecal)
|
||||
{
|
||||
_decalCore = (IDecalCore)pDecal;
|
||||
Instance = this;
|
||||
_message = new WndMsgImpl();
|
||||
}
|
||||
|
||||
public void BeforePlugins()
|
||||
{
|
||||
// In native code: subclasses game window and loads keymap
|
||||
// Window hooking requires being in-process, handled by Inject.DLL
|
||||
}
|
||||
|
||||
public void AfterPlugins()
|
||||
{
|
||||
_commands.Clear();
|
||||
// Native code restores original window procedure here
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
_hotkeys.Clear();
|
||||
_timers.Clear();
|
||||
_msgHooks.Clear();
|
||||
_commands.Clear();
|
||||
_message = null;
|
||||
ActiveBuffer = null;
|
||||
|
||||
if (Instance == this)
|
||||
Instance = null;
|
||||
}
|
||||
|
||||
// Registration methods for child objects
|
||||
internal void RegisterHotkey(HotkeyImpl hotkey)
|
||||
{
|
||||
if (!_hotkeys.Contains(hotkey))
|
||||
_hotkeys.Add(hotkey);
|
||||
}
|
||||
|
||||
internal void UnregisterHotkey(HotkeyImpl hotkey)
|
||||
{
|
||||
_hotkeys.Remove(hotkey);
|
||||
}
|
||||
|
||||
internal void RegisterTimer(TimerImpl timer)
|
||||
{
|
||||
if (!_timers.Contains(timer))
|
||||
_timers.Add(timer);
|
||||
}
|
||||
|
||||
internal void UnregisterTimer(TimerImpl timer)
|
||||
{
|
||||
_timers.Remove(timer);
|
||||
}
|
||||
|
||||
internal void RegisterMsgHook(WinMsgHookImpl hook)
|
||||
{
|
||||
if (!_msgHooks.Contains(hook))
|
||||
_msgHooks.Add(hook);
|
||||
}
|
||||
|
||||
internal void UnregisterMsgHook(WinMsgHookImpl hook)
|
||||
{
|
||||
_msgHooks.Remove(hook);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from the window hook procedure for each window message.
|
||||
/// Returns true if the message should be eaten (suppressed).
|
||||
/// </summary>
|
||||
internal bool ProcessWindowMessage(int hwnd, int msg, int wParam, int lParam)
|
||||
{
|
||||
_message.Set(hwnd, msg, wParam, lParam);
|
||||
|
||||
// Process message hooks
|
||||
foreach (var hook in _msgHooks)
|
||||
{
|
||||
hook.FireMessage(hook, _message);
|
||||
if (_message.Eat)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Process hotkeys on WM_KEYUP (0x0101), only if no active buffer
|
||||
if (msg == 0x0101 && ActiveBuffer == null)
|
||||
{
|
||||
foreach (var hotkey in _hotkeys)
|
||||
{
|
||||
if (hotkey.Enabled && hotkey.Key.Length > 0)
|
||||
{
|
||||
// Match virtual key
|
||||
int vk = wParam;
|
||||
string keyName = hotkey.Key;
|
||||
int hotkeyVk;
|
||||
|
||||
if (keyName.StartsWith("{") && keyName.EndsWith("}"))
|
||||
{
|
||||
string name = keyName.Substring(1, keyName.Length - 2);
|
||||
hotkeyVk = KeyNames.VKFromName(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
hotkeyVk = char.ToUpper(keyName[0]);
|
||||
}
|
||||
|
||||
if (vk == hotkeyVk)
|
||||
{
|
||||
hotkey.FireHotkey();
|
||||
break; // Only one hotkey per key press
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from the render loop to process timers and input buffer delays.
|
||||
/// </summary>
|
||||
internal void ProcessTimers()
|
||||
{
|
||||
uint now = GetTickCount();
|
||||
// Copy list to avoid modification during iteration
|
||||
var timersCopy = new List<TimerImpl>(_timers);
|
||||
foreach (var timer in timersCopy)
|
||||
{
|
||||
timer.CheckTimeout(now);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern uint GetTickCount();
|
||||
}
|
||||
}
|
||||
59
Managed/Decal.DecalInput/TimerImpl.cs
Normal file
59
Managed/Decal.DecalInput/TimerImpl.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Decal.Interop.Input;
|
||||
|
||||
namespace Decal.DecalInput
|
||||
{
|
||||
[ComVisible(true)]
|
||||
[Guid("79497C87-92E1-416B-AE5C-9D6C4C59133C")]
|
||||
[ClassInterface(ClassInterfaceType.None)]
|
||||
[ComSourceInterfaces(typeof(ITimerEvents))]
|
||||
[ProgId("DecalInput.Timer")]
|
||||
public class TimerImpl : IDecalTimer
|
||||
{
|
||||
private object _tag;
|
||||
private bool _running;
|
||||
private int _interval;
|
||||
private uint _startTime;
|
||||
|
||||
public event ITimerEvents_TimeoutEventHandler Timeout;
|
||||
|
||||
public void Start(int Interval)
|
||||
{
|
||||
_interval = Interval;
|
||||
_startTime = GetTickCount();
|
||||
_running = true;
|
||||
|
||||
InputServiceImpl.Instance?.RegisterTimer(this);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_running = false;
|
||||
InputServiceImpl.Instance?.UnregisterTimer(this);
|
||||
}
|
||||
|
||||
public object Tag
|
||||
{
|
||||
get => _tag;
|
||||
set => _tag = value;
|
||||
}
|
||||
|
||||
public bool Running => _running;
|
||||
|
||||
internal bool CheckTimeout(uint currentTime)
|
||||
{
|
||||
if (!_running) return false;
|
||||
if (currentTime - _startTime >= (uint)_interval)
|
||||
{
|
||||
_startTime = currentTime;
|
||||
Timeout?.Invoke((Timer)(object)this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern uint GetTickCount();
|
||||
}
|
||||
}
|
||||
47
Managed/Decal.DecalInput/WinMsgHookImpl.cs
Normal file
47
Managed/Decal.DecalInput/WinMsgHookImpl.cs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using Decal.Interop.Input;
|
||||
|
||||
namespace Decal.DecalInput
|
||||
{
|
||||
[ComVisible(true)]
|
||||
[Guid("F3170E85-517E-43A4-B7B4-6F006A7B1B85")]
|
||||
[ClassInterface(ClassInterfaceType.None)]
|
||||
[ComSourceInterfaces(typeof(IWinMsgHookEvents))]
|
||||
[ProgId("DecalInput.WinMsgHook")]
|
||||
public class WinMsgHookImpl : IWinMsgHook
|
||||
{
|
||||
private object _tag;
|
||||
private bool _enabled;
|
||||
|
||||
public event IWinMsgHookEvents_MessageEventHandler Message;
|
||||
|
||||
public object Tag
|
||||
{
|
||||
get => _tag;
|
||||
set => _tag = value;
|
||||
}
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set
|
||||
{
|
||||
if (value == _enabled) return;
|
||||
_enabled = value;
|
||||
|
||||
if (InputServiceImpl.Instance != null)
|
||||
{
|
||||
if (_enabled)
|
||||
InputServiceImpl.Instance.RegisterMsgHook(this);
|
||||
else
|
||||
InputServiceImpl.Instance.UnregisterMsgHook(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void FireMessage(WinMsgHookImpl hook, WndMsgImpl msg)
|
||||
{
|
||||
Message?.Invoke((WinMsgHook)(object)hook, (WndMsg)(object)msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
Managed/Decal.DecalInput/WndMsgImpl.cs
Normal file
27
Managed/Decal.DecalInput/WndMsgImpl.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using Decal.Interop.Input;
|
||||
|
||||
namespace Decal.DecalInput
|
||||
{
|
||||
[ComVisible(true)]
|
||||
[Guid("85AB0296-124E-4E68-A6A8-FCF5721AC09B")]
|
||||
[ClassInterface(ClassInterfaceType.None)]
|
||||
[ProgId("DecalInput.WndMsg")]
|
||||
public class WndMsgImpl : IWndMsg
|
||||
{
|
||||
public int HWND { get; internal set; }
|
||||
public int Message { get; internal set; }
|
||||
public int WParam { get; internal set; }
|
||||
public int LParam { get; internal set; }
|
||||
public bool Eat { get; set; }
|
||||
|
||||
internal void Set(int hwnd, int msg, int wParam, int lParam)
|
||||
{
|
||||
HWND = hwnd;
|
||||
Message = msg;
|
||||
WParam = wParam;
|
||||
LParam = lParam;
|
||||
Eat = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue