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:
erik 2026-02-08 18:27:56 +01:00
commit d1442e3747
1382 changed files with 170725 additions and 0 deletions

View file

@ -0,0 +1,56 @@
using System;
namespace Decal.Adapter.NetParser;
internal class MemberParser : MarshalByRefObject
{
public MemberParser Next;
public MemberParser Child;
public MemberParserType MemberType;
public string MemberName;
public MemberParserCondition Condition;
public string ConditionField;
public long ConditionXor;
public long ConditionAnd;
public long ConditionResult;
public string LengthField;
public long LengthMask;
public int LengthDelta;
public int PreAlignment;
public int PostAlignment;
public MemberParser()
{
}
public MemberParser(MemberParser Source)
{
Next = Source.Next;
Child = Source.Child;
MemberType = Source.MemberType;
MemberName = Source.MemberName;
Condition = Source.Condition;
ConditionField = Source.ConditionField;
ConditionXor = Source.ConditionXor;
ConditionAnd = Source.ConditionAnd;
ConditionResult = Source.ConditionResult;
LengthField = Source.LengthField;
LengthMask = Source.LengthMask;
LengthDelta = Source.LengthDelta;
PreAlignment = Source.PreAlignment;
PostAlignment = Source.PostAlignment;
}
}

View file

@ -0,0 +1,12 @@
namespace Decal.Adapter.NetParser;
internal enum MemberParserCondition
{
None,
EQ,
NE,
GE,
GT,
LE,
LT
}

View file

@ -0,0 +1,18 @@
namespace Decal.Adapter.NetParser;
internal enum MemberParserType
{
BYTE,
WORD,
PackedWORD,
DWORD,
PackedDWORD,
QWORD,
@float,
@double,
String,
WString,
Struct,
Vector,
Case
}

View file

@ -0,0 +1,24 @@
using System;
using System.Runtime.InteropServices;
using Decal.Interop.Net;
namespace Decal.Adapter.NetParser;
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IMessageFactory))]
[ProgId("DecalAdapter.MessageFactory")]
[Guid("59D7815E-6716-4bd7-B264-69B6A9382701")]
public class MessageFactory : IMessageFactory
{
IMessage2 IMessageFactory.CreateMessage(int pData, int size, bool outgoing)
{
if (pData != 0 && size != 0)
{
byte[] array = (byte[])Array.CreateInstance(typeof(byte), size);
Marshal.Copy(new IntPtr(pData), array, 0, size);
return new MessageWrapper(new Message(array, Message.GetParser(BitConverter.ToInt32(array, 0), outgoing ? MessageDirection.Outbound : MessageDirection.Inbound)));
}
return null;
}
}

View file

@ -0,0 +1,62 @@
using System.Runtime.InteropServices;
using Decal.Interop.Net;
namespace Decal.Adapter.NetParser;
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IMessageMember))]
[ProgId("DecalAdapter.MessageMemberWrapper")]
[Guid("BD22CFC5-0950-4ab3-9752-5053EE934BE6")]
public class MessageMemberWrapper : MessageRootImpl, IMessageMember
{
public int BeginOffset => Data.mOffset;
public int Count => Data.Count;
public int EndOffset => Data.mOffset + Data.mLength;
// IMessageMember properties - parameterless accessors for the COM interface
object IMessageMember.Value => get_Value(0);
byte[] IMessageMember.RawValue => get_RawValue(0);
IMessageMember IMessageMember.Struct => get_Struct(0);
public MessageMemberWrapper()
{
}
internal MessageMemberWrapper(MessageStruct msg, MessageWrapper wrapper)
: base(msg, wrapper)
{
}
public string get_FieldName(int Index)
{
return Data.Name(Index);
}
public byte[] get_RawValue(object vIndex)
{
return Data.RawValue(Data.ObjectToIndex(vIndex));
}
public IMessageMember get_Struct(object vIndex)
{
int num = Data.ObjectToIndex(vIndex);
if (num < 0)
{
throw new COMHResultException((HResults)(-2147352571));
}
return new MessageMemberWrapper(Data.Struct(num), Wrapped);
}
public object get_Value(object vIndex)
{
int num = Data.ObjectToIndex(vIndex);
if (num >= 0)
{
return Data.Value<object>(num);
}
throw new COMHResultException((HResults)(-2147352571));
}
}

View file

@ -0,0 +1,130 @@
using System.Diagnostics;
using Decal.Interop.Net;
namespace Decal.Adapter.NetParser;
public class MessageRootImpl : MessageRoot, IMessageIterator
{
protected MessageWrapper Wrapped;
protected MessageStruct Data;
protected int FieldIndex;
protected const int Error = -1;
public object Current => Data.Value<object>(FieldIndex);
public string FieldName => Data.Name(FieldIndex);
public int Index => FieldIndex;
public IMessage Message => Wrapped;
public MessageRoot NextObjectIndex
{
[DebuggerNonUserCode]
get
{
if (FieldIndex < Data.mCount)
{
MessageMemberWrapper result = new MessageMemberWrapper(Data.Struct(FieldIndex), Wrapped);
FieldIndex++;
return result;
}
throw new COMHResultException(HResults.E_FAIL);
}
}
// IMessageIterator properties - parameterless accessors for the COM interface.
// The parameterized get_Next/get_NextString etc. below are the COM parameterized property accessors.
object IMessageIterator.Next => throw new COMHResultException(HResults.E_FAIL);
string IMessageIterator.NextString => throw new COMHResultException(HResults.E_FAIL);
int IMessageIterator.NextInt => throw new COMHResultException(HResults.E_FAIL);
double IMessageIterator.NextFloat => throw new COMHResultException(HResults.E_FAIL);
MessageRoot IMessageIterator.NextObject => throw new COMHResultException(HResults.E_FAIL);
ulong IMessageIterator.NextQWord => throw new COMHResultException(HResults.E_FAIL);
protected MessageRootImpl()
{
}
protected MessageRootImpl(MessageStruct data, MessageWrapper wrapper)
: this()
{
Wrapped = wrapper;
Data = data;
if (!Data.mParsed)
{
Data.Parse();
}
}
public void Reset()
{
FieldIndex = 0;
}
[DebuggerNonUserCode]
private T GetNext<T>(string name)
{
FieldIndex = Data.IndexFromName(name);
if (FieldIndex != -1)
{
return Data.Value<T>(FieldIndex);
}
throw new COMHResultException(HResults.E_FAIL);
}
[DebuggerNonUserCode]
private T GetNext<T>(int index)
{
if (index >= 0 && index < Data.mCount)
{
FieldIndex = index;
return Data.Value<T>(FieldIndex);
}
throw new COMHResultException(HResults.E_FAIL);
}
[DebuggerNonUserCode]
public object get_Next(object vIndex)
{
return GetNext<object>(Data.ObjectToIndex(vIndex));
}
[DebuggerNonUserCode]
public double get_NextFloat(string Name)
{
return GetNext<double>(Name);
}
[DebuggerNonUserCode]
public int get_NextInt(string Name)
{
return GetNext<int>(Name);
}
[DebuggerNonUserCode]
public MessageRoot get_NextObject(string Name)
{
FieldIndex = Data.IndexFromName(Name);
if (FieldIndex != -1)
{
return new MessageMemberWrapper(Data.Struct(FieldIndex), Wrapped);
}
throw new COMHResultException(HResults.E_FAIL);
}
[DebuggerNonUserCode]
public ulong get_NextQWord(string Name)
{
return GetNext<ulong>(Name);
}
[DebuggerNonUserCode]
public string get_NextString(string Name)
{
return GetNext<string>(Name);
}
}

View file

@ -0,0 +1,21 @@
using System.Runtime.InteropServices;
using Decal.Interop.Net;
namespace Decal.Adapter.NetParser;
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(MessageRoot))]
[ProgId("DecalAdapter.MessageRootWrapper")]
[Guid("E33FBF17-B6C5-471e-9ED6-A394DEDEBFE5")]
public class MessageRootWrapper : MessageRootImpl
{
public MessageRootWrapper()
{
}
internal MessageRootWrapper(MessageWrapper msg)
: base(msg.Wrapped.mStruct, msg)
{
}
}

View file

@ -0,0 +1,77 @@
using System.Runtime.InteropServices;
using Decal.Interop.Net;
namespace Decal.Adapter.NetParser;
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IMessage2))]
[ProgId("DecalAdapter.MessageWrapper")]
[Guid("76142307-98E3-494d-8320-3C801010221D")]
public class MessageWrapper : IMessage2, IMessage
{
private Message msg;
internal Message Wrapped => msg;
public MessageRoot Begin => new MessageRootWrapper(this);
public int Count => msg.Count;
public byte[] RawData => msg.RawData;
public int Type => msg.Type;
// IMessage/IMessage2 properties - parameterless accessors for the COM interface
object IMessage.Value => get_Value(0);
IMessageMember IMessage.Struct => get_Struct(0);
byte[] IMessage.RawValue => get_RawValue(0);
string IMessage.FieldName => get_FieldName(0);
object IMessage2.Value => get_Value(0);
IMessageMember IMessage2.Struct => get_Struct(0);
byte[] IMessage2.RawValue => get_RawValue(0);
string IMessage2.FieldName => get_FieldName(0);
public MessageWrapper()
{
}
internal MessageWrapper(Message msg)
{
this.msg = msg;
if (!this.msg.mStruct.mParsed)
{
this.msg.mStruct.Parse();
}
}
public string get_FieldName(int Index)
{
return msg.Name(Index);
}
public byte[] get_RawValue(object vElement)
{
return msg.RawValue(msg.mStruct.ObjectToIndex(vElement));
}
public IMessageMember get_Struct(object vElement)
{
int num = msg.mStruct.ObjectToIndex(vElement);
if (num < 0)
{
throw new COMHResultException((HResults)1);
}
return new MessageMemberWrapper(msg.Struct(num), this);
}
public object get_Value(object vElement)
{
int num = msg.mStruct.ObjectToIndex(vElement);
if (num >= 0)
{
return msg.Value<object>(num);
}
return null;
}
}