NoSoliciting/NoSoliciting/Filter.cs
2020-08-25 08:00:43 -04:00

140 lines
5.1 KiB
C#

using Dalamud.Game.Chat;
using Dalamud.Game.Chat.SeStringHandling;
using Dalamud.Hooking;
using Dalamud.Plugin;
using System;
using System.Runtime.InteropServices;
namespace NoSoliciting {
public partial class Filter : IDisposable {
private readonly Plugin plugin;
private delegate void HandlePFPacketDelegate(IntPtr param_1, IntPtr param_2);
private readonly Hook<HandlePFPacketDelegate> handlePacketHook;
private bool disposedValue;
public Filter(Plugin plugin) {
this.plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null");
IntPtr delegatePtr = this.plugin.Interface.TargetModuleScanner.ScanText("40 53 41 57 48 83 EC 28 48 8B D9");
if (delegatePtr == IntPtr.Zero) {
PluginLog.Log("Party Finder filtering disabled because hook could not be created.");
return;
}
this.handlePacketHook = new Hook<HandlePFPacketDelegate>(delegatePtr, new HandlePFPacketDelegate(this.HandlePFPacket));
this.handlePacketHook.Enable();
}
private void HandlePFPacket(IntPtr param_1, IntPtr param_2) {
if (this.plugin.Definitions == null) {
this.handlePacketHook.Original(param_1, param_2);
return;
}
IntPtr dataPtr = param_2 + 0x10;
// parse the packet into a struct
PFPacket packet = Marshal.PtrToStructure<PFPacket>(dataPtr);
for (int i = 0; i < packet.listings.Length; i++) {
PFListing listing = packet.listings[i];
// only look at listings that aren't null
if (listing.IsNull()) {
continue;
}
string desc = listing.Description();
bool filter = false;
filter = filter || (this.plugin.Config.FilterHugeItemLevelPFs && listing.minimumItemLevel > FilterUtil.MaxItemLevelAttainable(this.plugin.Interface.Data));
foreach (Definition def in this.plugin.Definitions.PartyFinder.Values) {
filter = filter || (this.plugin.Config.FilterStatus.TryGetValue(def.Id, out bool enabled)
&& enabled
&& def.Matches(XivChatType.None, desc));
}
// check for custom filters if enabled
filter = filter || (this.plugin.Config.CustomPFFilter && PartyFinder.MatchesCustomFilters(desc, this.plugin.Config));
if (!filter) {
continue;
}
// replace the listing with an empty one
packet.listings[i] = new PFListing();
PluginLog.Log($"Filtered PF listing from {listing.Name()}: {listing.Description()}");
}
// get some memory for writing to
byte[] newPacket = new byte[PacketInfo.packetSize];
GCHandle pinnedArray = GCHandle.Alloc(newPacket, GCHandleType.Pinned);
IntPtr pointer = pinnedArray.AddrOfPinnedObject();
// write our struct into the memory (doing this directly crashes the game)
Marshal.StructureToPtr(packet, pointer, false);
// copy our new memory over the game's
Marshal.Copy(newPacket, 0, dataPtr, PacketInfo.packetSize);
// free memory
pinnedArray.Free();
// call original function
this.handlePacketHook.Original(param_1, param_2);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "fulfilling a delegate")]
public void OnChat(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) {
if (message == null) {
throw new ArgumentNullException(nameof(message), "SeString cannot be null");
}
if (this.plugin.Definitions == null) {
return;
}
string text = message.TextValue;
bool filter = false;
foreach (Definition def in this.plugin.Definitions.Chat.Values) {
filter = filter || (this.plugin.Config.FilterStatus.TryGetValue(def.Id, out bool enabled)
&& enabled
&& def.Matches(type, text));
}
// check for custom filters if enabled
filter = filter || (this.plugin.Config.CustomChatFilter && Chat.MatchesCustomFilters(text, this.plugin.Config));
if (!filter) {
return;
}
PluginLog.Log($"Filtered chat message: {text}");
isHandled = true;
}
protected virtual void Dispose(bool disposing) {
if (!disposedValue) {
if (disposing) {
this.handlePacketHook?.Dispose();
}
disposedValue = true;
}
}
public void Dispose() {
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}