107 lines
3.3 KiB
C#
107 lines
3.3 KiB
C#
using Dalamud.Hooking;
|
|
using Dalamud.Plugin.Services;
|
|
using Dalamud.Utility.Signatures;
|
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
|
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
|
using MareSynchronos.MareConfiguration;
|
|
using Microsoft.Extensions.Logging;
|
|
using Serilog;
|
|
using Serilog.Core;
|
|
using System.Buffers;
|
|
|
|
namespace MareSynchronos.Interop;
|
|
|
|
public unsafe sealed class ColorTableHook : IDisposable
|
|
{
|
|
// Based on https://github.com/Exter-N/MemShield/blob/main/MemShield/Plugin.cs
|
|
|
|
private readonly ILogger<ColorTableHook> _logger;
|
|
private const int ColorTableSize = 2048;
|
|
private const int ColorDyeTableSize = 128;
|
|
private readonly MareConfigService _configService;
|
|
|
|
#region signatures
|
|
#pragma warning disable CS0649
|
|
[Signature("E8 ?? ?? ?? ?? 49 89 04 3E",
|
|
DetourName = nameof(PrepareColorTableDetour))]
|
|
private Hook<MaterialResourceHandle.Delegates.PrepareColorTable> _prepareColorTable = null!;
|
|
#pragma warning restore CS0649
|
|
#endregion
|
|
|
|
public ColorTableHook(ILogger<ColorTableHook> logger, IGameInteropProvider gameInteropProvider, MareConfigService configService)
|
|
{
|
|
_logger = logger;
|
|
_configService = configService;
|
|
|
|
logger.LogInformation("Initializing ColorTableHook");
|
|
gameInteropProvider.InitializeFromAttributes(this);
|
|
|
|
if (_configService.Current.AttemptColorTableProtection)
|
|
{
|
|
_prepareColorTable.Enable();
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (_configService.Current.AttemptColorTableProtection)
|
|
{
|
|
_prepareColorTable.Disable();
|
|
}
|
|
_prepareColorTable.Dispose();
|
|
}
|
|
|
|
public void EnableHooks()
|
|
{
|
|
_prepareColorTable.Enable();
|
|
}
|
|
|
|
public void DisableHooks()
|
|
{
|
|
_prepareColorTable.Disable();
|
|
}
|
|
|
|
private static int GetDataSetExpectedSize(uint dataFlags)
|
|
=> (dataFlags & 16) != 0
|
|
? ColorTableSize + ((dataFlags & 8) != 0 ? ColorDyeTableSize : 0)
|
|
: 0;
|
|
|
|
private Texture* PrepareColorTableDetour(MaterialResourceHandle* @this, byte stain0Id, byte stain1Id)
|
|
{
|
|
if(!_configService.Current.AttemptColorTableProtection)
|
|
{
|
|
return _prepareColorTable.Original(@this, stain0Id, stain1Id);
|
|
}
|
|
|
|
var expectedSize = GetDataSetExpectedSize(@this->DataFlags);
|
|
if (@this->DataSetSize >= expectedSize)
|
|
return _prepareColorTable.Original(@this, stain0Id, stain1Id);
|
|
var originalDataSetPtr = @this->DataSet;
|
|
var originalDataSet = new ReadOnlySpan<byte>(originalDataSetPtr, @this->DataSetSize);
|
|
var pool = ArrayPool<byte>.Shared;
|
|
var buffer = pool.Rent(expectedSize);
|
|
try
|
|
{
|
|
var bufferSpan = buffer.AsSpan(..expectedSize);
|
|
originalDataSet.CopyTo(bufferSpan);
|
|
bufferSpan[@this->DataSetSize..].Clear();
|
|
fixed (byte* bufferPtr = buffer)
|
|
{
|
|
@this->DataSet = bufferPtr;
|
|
try
|
|
{
|
|
return _prepareColorTable.Original(@this, stain0Id, stain1Id);
|
|
}
|
|
finally
|
|
{
|
|
@this->DataSet = originalDataSetPtr;
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
pool.Return(buffer);
|
|
}
|
|
}
|
|
}
|