Skip to content

Commit

Permalink
chore!: cleanup namespaces and adjustments to layout
Browse files Browse the repository at this point in the history
  • Loading branch information
xCykrix committed Jul 18, 2024
1 parent d2dcbd1 commit cc058fc
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 119 deletions.
3 changes: 2 additions & 1 deletion ICoreManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
using Dalamud.IoC;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using DalamudSystem.Manager;
using DalamudSystem.Managers;
using DalamudSystem.Managers.Base;
using DalamudSystem.Module;

namespace DalamudSystem;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

namespace DalamudSystem.Manager;
namespace DalamudSystem.Managers.Base;

public static class IManagerController
{
Expand Down
2 changes: 1 addition & 1 deletion Module/Manager.cs → Managers/Base/Manager.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

namespace DalamudSystem.Manager;
namespace DalamudSystem.Managers.Base;

public abstract class IManager(string? ManagerName) : IDisposable
{
Expand Down
3 changes: 2 additions & 1 deletion Managers/IActionManager.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

using DalamudSystem.Managers.Base;
using FFXIVClientStructs.FFXIV.Client.Game;

namespace DalamudSystem.Manager;
namespace DalamudSystem.Managers;

public unsafe class IActionManager : IManager, IDisposable
{
Expand Down
218 changes: 108 additions & 110 deletions Managers/IChatMessageManager.cs
Original file line number Diff line number Diff line change
@@ -1,144 +1,142 @@
using DalamudSystem.Manager;
using DalamudSystem.Managers.Base;
using FFXIVClientStructs.FFXIV.Client.System.Memory;
using FFXIVClientStructs.FFXIV.Client.System.String;
using System.Runtime.InteropServices;
using System.Text;
using Framework = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework;

namespace DalamudSystem.Managers
{
namespace DalamudSystem.Managers;

/// <summary>
/// Class used to send chat messages to the client.
///
/// This class is UNSTABLE and DANGEROUS to use. Please use with extreme caution.
/// </summary>
public unsafe class IChatMessageManager : IManager, IDisposable
/// <summary>
/// Class used to send chat messages to the client.
///
/// This class is UNSTABLE and DANGEROUS to use. Please use with extreme caution.
/// </summary>
public unsafe class IChatMessageManager : IManager, IDisposable
{
#region Memory Scanner Magic - Finds and Exposes Additional APIs that Dalamud does not provide.
private static class SignatureTable
{
#region Memory Scanner Magic - Finds and Exposes Additional APIs that Dalamud does not provide.
private static class SignatureTable
{
internal const string SendChat = "48 89 5C 24 ?? 57 48 83 EC 20 48 8B FA 48 8B D9 45 84 C9";
internal const string SanitizeString = "E8 ?? ?? ?? ?? 48 8D 4C 24 ?? 0F B6 F0 E8 ?? ?? ?? ?? 48 8D 4D C0";
}
internal const string SendChat = "48 89 5C 24 ?? 57 48 83 EC 20 48 8B FA 48 8B D9 45 84 C9";
internal const string SanitizeString = "E8 ?? ?? ?? ?? 48 8D 4C 24 ?? 0F B6 F0 E8 ?? ?? ?? ?? 48 8D 4D C0";
}

private delegate void ProcessChatBoxDelegate(IntPtr ui, IntPtr message, IntPtr unused, byte a4);
private ProcessChatBoxDelegate? ProcessChatBox { get; }
private delegate void ProcessChatBoxDelegate(IntPtr ui, IntPtr message, IntPtr unused, byte a4);
private ProcessChatBoxDelegate? ProcessChatBox { get; }

private readonly unsafe delegate* unmanaged<Utf8String*, int, IntPtr, void> _sanitize = null!;
private readonly unsafe delegate* unmanaged<Utf8String*, int, IntPtr, void> _sanitize = null!;

internal IChatMessageManager() : base(nameof(IChatMessageManager))
{
if (ICoreManager.SigScanner.TryScanText(SignatureTable.SendChat, out var processChatBoxPtr))
{
this.ProcessChatBox = Marshal.GetDelegateForFunctionPointer<ProcessChatBoxDelegate>(processChatBoxPtr);
}

if (ICoreManager.SigScanner.TryScanText(SignatureTable.SanitizeString, out var SanitizePtr))
{
this._sanitize = (delegate* unmanaged<Utf8String*, int, IntPtr, void>)SanitizePtr;
}
}
#endregion

/// <summary>
/// This method is more inherintly safe but is limited on validation that can be provided. Please use InvokeChatCommand when possible to better protect the players.
/// </summary>
/// <param name="ChatMessage">String to send to current chat.</param>
/// <exception cref="InvalidOperationException">If memory address lookup failed.</exception>
/// <exception cref="ArgumentException">If the message is empty, exceeds 500 bytes, or does not match sanitization.</exception>
public void InvokeChatMessage(string ChatMessage)
internal IChatMessageManager() : base(nameof(IChatMessageManager))
{
if (ICoreManager.SigScanner.TryScanText(SignatureTable.SendChat, out var processChatBoxPtr))
{
if (ProcessChatBox == null) throw new InvalidOperationException("Could not find signature for chat process box. Please report this to DalamudSystem repo.");

ChatMessage = ChatMessage.Trim();
var bytes = Encoding.UTF8.GetBytes(ChatMessage);
if (bytes.Length == 0 || bytes.Length > 500) throw new ArgumentException($"Attempted to invoke a 'Text Message' but the 'ChatMessage' was invalid: {ChatMessage}");
if (ChatMessage.Length != SanitizeText(ChatMessage).Length) throw new ArgumentException($"Attempted to invoke a 'Text Command' but the 'ChatMessage' contained illegal characters: Provided = '{ChatMessage}' | Parsed = '{SanitizeText(ChatMessage)}'");

var ui = (IntPtr)Framework.Instance()->GetUIModule();
using var payload = new ChatPayload(bytes);
var memstore = Marshal.AllocHGlobal(400);
Marshal.StructureToPtr(payload, memstore, false);
this.ProcessChatBox(ui, memstore, IntPtr.Zero, 0);
Marshal.FreeHGlobal(memstore);
this.ProcessChatBox = Marshal.GetDelegateForFunctionPointer<ProcessChatBoxDelegate>(processChatBoxPtr);
}

/// <summary>
/// This method is considered better to use over InvokeChatMessage. Sending to chat is preferred to use client commands within validation bounds.
/// </summary>
/// <param name="ChatMessage"></param>
/// <exception cref="InvalidOperationException">If memory address lookup failed or no prefix is provided to the ChatMessage.</exception>
/// <exception cref="ArgumentException">If the message is empty, exceeds 500 bytes, or does not match sanitization.</exception>
public void InvokeChatCommand(string ChatMessage)
if (ICoreManager.SigScanner.TryScanText(SignatureTable.SanitizeString, out var SanitizePtr))
{
ChatMessage = ChatMessage.Trim();
if (!ChatMessage.StartsWith("/")) throw new InvalidOperationException($"Attempted to invoke a 'Slash Command' but was improperly prefixed with '/': {ChatMessage}");
InvokeChatMessage(ChatMessage);
this._sanitize = (delegate* unmanaged<Utf8String*, int, IntPtr, void>)SanitizePtr;
}
}
#endregion

/// <summary>
/// Internal method to call SQEX built-in sanitization functions.
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
private unsafe string SanitizeText(string text)
{
if (_sanitize == null) throw new InvalidOperationException("Could not find signature for chat sanitization. Please report this to DalamudSystem repo.");
/// <summary>
/// This method is more inherintly safe but is limited on validation that can be provided. Please use InvokeChatCommand when possible to better protect the players.
/// </summary>
/// <param name="ChatMessage">String to send to current chat.</param>
/// <exception cref="InvalidOperationException">If memory address lookup failed.</exception>
/// <exception cref="ArgumentException">If the message is empty, exceeds 500 bytes, or does not match sanitization.</exception>
public void InvokeChatMessage(string ChatMessage)
{
if (ProcessChatBox == null) throw new InvalidOperationException("Could not find signature for chat process box. Please report this to DalamudSystem repo.");

ChatMessage = ChatMessage.Trim();
var bytes = Encoding.UTF8.GetBytes(ChatMessage);
if (bytes.Length == 0 || bytes.Length > 500) throw new ArgumentException($"Attempted to invoke a 'Text Message' but the 'ChatMessage' was invalid: {ChatMessage}");
if (ChatMessage.Length != SanitizeText(ChatMessage).Length) throw new ArgumentException($"Attempted to invoke a 'Text Command' but the 'ChatMessage' contained illegal characters: Provided = '{ChatMessage}' | Parsed = '{SanitizeText(ChatMessage)}'");

var ui = (IntPtr)Framework.Instance()->GetUIModule();
using var payload = new ChatPayload(bytes);
var memstore = Marshal.AllocHGlobal(400);
Marshal.StructureToPtr(payload, memstore, false);
this.ProcessChatBox(ui, memstore, IntPtr.Zero, 0);
Marshal.FreeHGlobal(memstore);
}

// Convert to UTF8
var utf8Text = Utf8String.FromString(text);
_sanitize(utf8Text, 0x27F, IntPtr.Zero);
/// <summary>
/// This method is considered better to use over InvokeChatMessage. Sending to chat is preferred to use client commands within validation bounds.
/// </summary>
/// <param name="ChatMessage"></param>
/// <exception cref="InvalidOperationException">If memory address lookup failed or no prefix is provided to the ChatMessage.</exception>
/// <exception cref="ArgumentException">If the message is empty, exceeds 500 bytes, or does not match sanitization.</exception>
public void InvokeChatCommand(string ChatMessage)
{
ChatMessage = ChatMessage.Trim();
if (!ChatMessage.StartsWith("/")) throw new InvalidOperationException($"Attempted to invoke a 'Slash Command' but was improperly prefixed with '/': {ChatMessage}");
InvokeChatMessage(ChatMessage);
}

// PConvert utf8Text to String and Dtor(?)
var sanitized = utf8Text->ToString();
utf8Text->Dtor();

// Clean Memory
IMemorySpace.Free(utf8Text);
/// <summary>
/// Internal method to call SQEX built-in sanitization functions.
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
private unsafe string SanitizeText(string text)
{
if (_sanitize == null) throw new InvalidOperationException("Could not find signature for chat sanitization. Please report this to DalamudSystem repo.");

// Return Result
return sanitized;
}
// Convert to UTF8
var utf8Text = Utf8String.FromString(text);
_sanitize(utf8Text, 0x27F, IntPtr.Zero);

// ChatPayload Struct
[StructLayout(LayoutKind.Explicit)]
private readonly struct ChatPayload : IDisposable
{
[FieldOffset(0)]
private readonly IntPtr textPtr;
// PConvert utf8Text to String and Dtor(?)
var sanitized = utf8Text->ToString();
utf8Text->Dtor();

[FieldOffset(16)]
private readonly ulong textLen;
// Clean Memory
IMemorySpace.Free(utf8Text);

[FieldOffset(8)]
private readonly ulong unk1;
// Return Result
return sanitized;
}

// ChatPayload Struct
[StructLayout(LayoutKind.Explicit)]
private readonly struct ChatPayload : IDisposable
{
[FieldOffset(0)]
private readonly IntPtr textPtr;

[FieldOffset(16)]
private readonly ulong textLen;

[FieldOffset(24)]
private readonly ulong unk2;
[FieldOffset(8)]
private readonly ulong unk1;

internal ChatPayload(byte[] stringBytes)
{
this.textPtr = Marshal.AllocHGlobal(stringBytes.Length + 30);
Marshal.Copy(stringBytes, 0, this.textPtr, stringBytes.Length);
Marshal.WriteByte(this.textPtr + stringBytes.Length, 0);
[FieldOffset(24)]
private readonly ulong unk2;

this.textLen = (ulong)(stringBytes.Length + 1);
internal ChatPayload(byte[] stringBytes)
{
this.textPtr = Marshal.AllocHGlobal(stringBytes.Length + 30);
Marshal.Copy(stringBytes, 0, this.textPtr, stringBytes.Length);
Marshal.WriteByte(this.textPtr + stringBytes.Length, 0);

this.unk1 = 64;
this.unk2 = 0;
}
this.textLen = (ulong)(stringBytes.Length + 1);

public void Dispose()
{
Marshal.FreeHGlobal(this.textPtr);
}
this.unk1 = 64;
this.unk2 = 0;
}

public override void Dispose()
public void Dispose()
{
GC.SuppressFinalize(this);
Marshal.FreeHGlobal(this.textPtr);
}
}

public override void Dispose()
{
GC.SuppressFinalize(this);
}
}
3 changes: 2 additions & 1 deletion Managers/IMovementManager.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@


using DalamudSystem.Managers.Base;
using System.Numerics;

namespace DalamudSystem.Manager;
namespace DalamudSystem.Managers;

public class IMovementManager : IManager, IDisposable
{
Expand Down
3 changes: 2 additions & 1 deletion Managers/ITerritoryManager.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@

using Dalamud.Plugin.Services;
using DalamudSystem.Managers.Base;
using Lumina.Excel.GeneratedSheets;

namespace DalamudSystem.Manager;
namespace DalamudSystem.Managers;

public class ITerritoryManager : IManager, IDisposable
{
Expand Down
4 changes: 1 addition & 3 deletions Module/IModuleController.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@

using DalamudSystem.Module;

namespace DalamudSystem.Manager;
namespace DalamudSystem.Module;

public static class IModuleController
{
Expand Down

0 comments on commit cc058fc

Please sign in to comment.