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>
380 lines
9.1 KiB
C#
380 lines
9.1 KiB
C#
using System;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using Decal.Adapter.Support;
|
|
using Decal.Interop.Core;
|
|
using Decal.Interop.Render;
|
|
|
|
namespace Decal.Adapter.Wrappers;
|
|
|
|
[CLSCompliant(true)]
|
|
public class HudRenderTarget : MarshalByRefObject, IDisposable
|
|
{
|
|
private IRenderTarget myTarget;
|
|
|
|
private bool isDisposed;
|
|
|
|
private Rectangle areaRect;
|
|
|
|
private bool inRender;
|
|
|
|
private bool inText;
|
|
|
|
public bool Lost => myTarget.Lost;
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public Rectangle Constraint
|
|
{
|
|
get
|
|
{
|
|
Rectangle region = Region;
|
|
return new Rectangle(0, 0, region.Width, region.Height);
|
|
}
|
|
}
|
|
|
|
public Rectangle Region
|
|
{
|
|
get
|
|
{
|
|
return Util.toRectangle(myTarget.Region);
|
|
}
|
|
set
|
|
{
|
|
tagRECT pVal = Util.toTagRECT(value);
|
|
myTarget.Region = pVal;
|
|
areaRect.Width = value.Width;
|
|
areaRect.Height = value.Height;
|
|
}
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public object UnsafeSurface => myTarget.GetSurface();
|
|
|
|
[CLSCompliant(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public tagRECT UnsafeRegion
|
|
{
|
|
get
|
|
{
|
|
return myTarget.Region;
|
|
}
|
|
set
|
|
{
|
|
myTarget.Region = value;
|
|
}
|
|
}
|
|
|
|
protected bool IsDisposed => isDisposed;
|
|
|
|
[CLSCompliant(false)]
|
|
protected HudRenderTarget(IRenderTarget newTarget)
|
|
{
|
|
if (newTarget == null)
|
|
{
|
|
throw new ArgumentNullException("newTarget");
|
|
}
|
|
myTarget = newTarget;
|
|
tagRECT region = myTarget.Region;
|
|
areaRect = new Rectangle(0, 0, region.right - region.left, region.bottom - region.top);
|
|
}
|
|
|
|
~HudRenderTarget()
|
|
{
|
|
Dispose(disposing: false);
|
|
}
|
|
|
|
public void BeginRender()
|
|
{
|
|
BeginRender(enableTextureFilter: false);
|
|
}
|
|
|
|
public void BeginRender(bool enableTextureFilter)
|
|
{
|
|
if (!inRender)
|
|
{
|
|
myTarget.BeginRender(enableTextureFilter);
|
|
inRender = true;
|
|
return;
|
|
}
|
|
throw new RenderViolationException("Cannot call 'BeginRender' when already rendering");
|
|
}
|
|
|
|
public void EndRender()
|
|
{
|
|
if (inRender)
|
|
{
|
|
myTarget.EndRender();
|
|
inRender = false;
|
|
return;
|
|
}
|
|
throw new RenderViolationException("Cannot call 'EndRender' without a previous call to 'BeginRender'");
|
|
}
|
|
|
|
public void BeginText(string fontName, int pixelHeight)
|
|
{
|
|
BeginText(fontName, pixelHeight, FontWeight.Normal, italic: false);
|
|
}
|
|
|
|
public void BeginText(string fontName, int pixelHeight, FontWeight weight, bool italic)
|
|
{
|
|
UnsafeBeginText(fontName, pixelHeight, (int)weight, italic);
|
|
}
|
|
|
|
public void WriteText(string text)
|
|
{
|
|
WriteText(text, Color.Black, WriteTextFormats.None, areaRect);
|
|
}
|
|
|
|
public void WriteText(string text, Color color)
|
|
{
|
|
WriteText(text, color, WriteTextFormats.None, areaRect);
|
|
}
|
|
|
|
public void WriteText(string text, Color color, WriteTextFormats format, Rectangle region)
|
|
{
|
|
UnsafeWriteText(Util.toTagRECT(region), color.ToArgb(), (int)format, text);
|
|
}
|
|
|
|
public void WriteText(string text, int color, WriteTextFormats format, Rectangle region)
|
|
{
|
|
UnsafeWriteText(Util.toTagRECT(region), color, (int)format, text);
|
|
}
|
|
|
|
public void EndText()
|
|
{
|
|
if (inRender && inText)
|
|
{
|
|
myTarget.EndText();
|
|
inText = false;
|
|
return;
|
|
}
|
|
throw new RenderViolationException("Cannot call 'EndText' without a previous call to 'BeginText'");
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
Clear(areaRect);
|
|
}
|
|
|
|
public void Clear(Rectangle clearArea)
|
|
{
|
|
UnsafeClear(Util.toTagRECT(clearArea));
|
|
}
|
|
|
|
public void DrawPortalImage(int portalFile, Rectangle destinationArea)
|
|
{
|
|
UnsafeDrawPortalImage(portalFile, Util.toTagRECT(destinationArea));
|
|
}
|
|
|
|
public void DrawPortalImage(int portalFile, int alpha, Rectangle srcArea, Rectangle destinationArea)
|
|
{
|
|
UnsafeDrawPortalImage(portalFile, alpha, Util.toTagRECT(srcArea), Util.toTagRECT(destinationArea));
|
|
}
|
|
|
|
public void DrawPortalImage(int portalFile, Rectangle srcArea, Rectangle destinationArea)
|
|
{
|
|
UnsafeDrawPortalImage(portalFile, 255, Util.toTagRECT(srcArea), Util.toTagRECT(destinationArea));
|
|
}
|
|
|
|
public void TilePortalImage(int portalFile, Rectangle destinationArea)
|
|
{
|
|
UnsafeTilePortalImage(portalFile, Util.toTagRECT(destinationArea));
|
|
}
|
|
|
|
public void TilePortalImage(int portalFile, Rectangle srcArea, Rectangle destinationArea)
|
|
{
|
|
UnsafeTilePortalImage(portalFile, Util.toTagRECT(srcArea), Util.toTagRECT(destinationArea));
|
|
}
|
|
|
|
public void DrawImage(Image image, Rectangle destinationRegion)
|
|
{
|
|
DrawImage(image, destinationRegion, Color.Cyan.ToArgb());
|
|
}
|
|
|
|
public void DrawImage(Image image, Rectangle destinationRegion, Color colorKey)
|
|
{
|
|
DrawImage(image, destinationRegion, colorKey.ToArgb());
|
|
}
|
|
|
|
public void DrawImage(Image image, Rectangle destinationRegion, int colorKey)
|
|
{
|
|
if (image == null)
|
|
{
|
|
throw new ArgumentNullException("image");
|
|
}
|
|
MemoryStream memoryStream = new MemoryStream();
|
|
image.Save((Stream)memoryStream, ImageFormat.Bmp);
|
|
byte[] buffer = memoryStream.GetBuffer();
|
|
IntPtr zero = IntPtr.Zero;
|
|
int num = buffer.Length;
|
|
zero = Marshal.AllocCoTaskMem(num);
|
|
try
|
|
{
|
|
Marshal.Copy(buffer, 0, zero, num);
|
|
if (zero != IntPtr.Zero)
|
|
{
|
|
UnsafeDrawImage(zero, num, Util.toTagRECT(destinationRegion), colorKey);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Marshal.FreeCoTaskMem(zero);
|
|
memoryStream.Dispose();
|
|
}
|
|
}
|
|
|
|
public void Fill(Color color)
|
|
{
|
|
Fill(areaRect, color);
|
|
}
|
|
|
|
public void Fill(Rectangle fillArea, Color color)
|
|
{
|
|
Fill(fillArea, color.ToArgb());
|
|
}
|
|
|
|
public void Fill(Rectangle fillArea, int color)
|
|
{
|
|
UnsafeFill(Util.toTagRECT(fillArea), color);
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public void UnsafeBeginText(string fontName, int pixelHeight, int weight, bool italic)
|
|
{
|
|
if (inRender && !inText)
|
|
{
|
|
myTarget.BeginText(fontName, pixelHeight, weight, italic);
|
|
inText = true;
|
|
return;
|
|
}
|
|
if (inRender && inText)
|
|
{
|
|
throw new RenderViolationException("Cannot call 'BeginText' when already rendering text");
|
|
}
|
|
throw new RenderViolationException("Cannot call 'BeginText' outside of a BeginRender/EndRender block");
|
|
}
|
|
|
|
[CLSCompliant(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public void UnsafeWriteText(tagRECT region, int color, int format, string text)
|
|
{
|
|
if (inRender && inText)
|
|
{
|
|
myTarget.WriteText(ref region, color, format, text);
|
|
return;
|
|
}
|
|
throw new RenderViolationException("Cannot call 'WriteText' outside of a BeginText/EndText block");
|
|
}
|
|
|
|
[CLSCompliant(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public void UnsafeClear(tagRECT clearArea)
|
|
{
|
|
if (!inRender)
|
|
{
|
|
myTarget.Clear(ref clearArea);
|
|
return;
|
|
}
|
|
throw new RenderViolationException("MUST call 'Clear' outside of a BeginRender/EndRender block");
|
|
}
|
|
|
|
[CLSCompliant(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public void UnsafeDrawPortalImage(int portalFile, tagRECT destinationArea)
|
|
{
|
|
if (inRender)
|
|
{
|
|
myTarget.DrawPortalImage(portalFile, ref destinationArea);
|
|
return;
|
|
}
|
|
throw new RenderViolationException("Cannot call 'DrawPortalImage' outside of a BeginRender/EndRender block");
|
|
}
|
|
|
|
[CLSCompliant(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public void UnsafeDrawPortalImage(int portalFile, int alpha, tagRECT srcArea, tagRECT destinationArea)
|
|
{
|
|
if (inRender)
|
|
{
|
|
myTarget.DrawPortalImageEx(portalFile, alpha, ref srcArea, ref destinationArea);
|
|
return;
|
|
}
|
|
throw new RenderViolationException("Cannot call 'DrawPortalImageEx' outside of a BeginRender/EndRender block");
|
|
}
|
|
|
|
[CLSCompliant(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public void UnsafeTilePortalImage(int portalFile, tagRECT destinationArea)
|
|
{
|
|
if (inRender)
|
|
{
|
|
myTarget.TilePortalImage(portalFile, ref destinationArea);
|
|
return;
|
|
}
|
|
throw new RenderViolationException("Cannot call 'TilePortalImage' outside of a BeginRender/EndRender block");
|
|
}
|
|
|
|
[CLSCompliant(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public void UnsafeTilePortalImage(int portalFile, tagRECT srcArea, tagRECT destinationArea)
|
|
{
|
|
if (inRender)
|
|
{
|
|
myTarget.TilePortalImageEx(portalFile, ref srcArea, ref destinationArea);
|
|
return;
|
|
}
|
|
throw new RenderViolationException("Cannot call 'TilePortalImageEx' outside of a BeginRender/EndRender block");
|
|
}
|
|
|
|
[CLSCompliant(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public void UnsafeDrawImage(IntPtr buffer, int size, tagRECT destinationRegion, int colorKey)
|
|
{
|
|
if (inRender)
|
|
{
|
|
myTarget.DrawImage((int)buffer, size, ref destinationRegion, colorKey);
|
|
return;
|
|
}
|
|
throw new RenderViolationException("Cannot call 'DrawImage' outside of a BeginRender/EndRender block");
|
|
}
|
|
|
|
[CLSCompliant(false)]
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public void UnsafeFill(tagRECT fillArea, int color)
|
|
{
|
|
if (!inRender)
|
|
{
|
|
myTarget.Fill(ref fillArea, color);
|
|
return;
|
|
}
|
|
throw new RenderViolationException("MUST call 'Fill' outside of a BeginRender/EndRender block");
|
|
}
|
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public void UnsafeSetSurface(object pSurface)
|
|
{
|
|
myTarget.SetSurface(pSurface);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(disposing: true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (!isDisposed)
|
|
{
|
|
}
|
|
if (myTarget != null)
|
|
{
|
|
Marshal.ReleaseComObject(myTarget);
|
|
}
|
|
myTarget = null;
|
|
isDisposed = true;
|
|
}
|
|
}
|