feat: basic .text scan cache

This currently saves cached jsons in the working directory, we might wanna change that
This commit is contained in:
goaaats 2022-06-19 20:40:15 +02:00
parent f473c0e6d6
commit eca2b7f5ee
No known key found for this signature in database
GPG key ID: 49E2AA8C6A76498B
2 changed files with 57 additions and 7 deletions

View file

@ -106,7 +106,12 @@ namespace Dalamud
Service<ServiceContainer>.Set(); Service<ServiceContainer>.Set();
// Initialize the process information. // Initialize the process information.
Service<SigScanner>.Set(new SigScanner(true)); var info = Service<DalamudStartInfo>.Get();
var cacheDir = new DirectoryInfo(Path.Combine(info.WorkingDirectory!, "cachedSigs"));
if (!cacheDir.Exists)
cacheDir.Create();
Service<SigScanner>.Set(new SigScanner(true, new FileInfo(Path.Combine(cacheDir.FullName, $"{info.GameVersion}.json"))));
Service<HookManager>.Set(); Service<HookManager>.Set();
// Signal the main game thread to continue // Signal the main game thread to continue
@ -117,7 +122,7 @@ namespace Dalamud
// Initialize FFXIVClientStructs function resolver // Initialize FFXIVClientStructs function resolver
using (Timings.Start("CS Resolver Init")) using (Timings.Start("CS Resolver Init"))
{ {
FFXIVClientStructs.Resolver.Initialize(); FFXIVClientStructs.Resolver.InitializeParallel();
Log.Information("[T1] FFXIVClientStructs initialized!"); Log.Information("[T1] FFXIVClientStructs initialized!");
} }
@ -390,7 +395,10 @@ namespace Dalamud
Service<AntiDebug>.GetNullable()?.Dispose(); Service<AntiDebug>.GetNullable()?.Dispose();
Service<DalamudAtkTweaks>.GetNullable()?.Dispose(); Service<DalamudAtkTweaks>.GetNullable()?.Dispose();
Service<HookManager>.GetNullable()?.Dispose(); Service<HookManager>.GetNullable()?.Dispose();
Service<SigScanner>.GetNullable()?.Dispose();
var sigScanner = Service<SigScanner>.Get();
sigScanner.Save();
sigScanner.Dispose();
SerilogEventSink.Instance.LogLine -= SerilogOnLogLine; SerilogEventSink.Instance.LogLine -= SerilogOnLogLine;

View file

@ -2,11 +2,13 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Dalamud.IoC; using Dalamud.IoC;
using Dalamud.IoC.Internal; using Dalamud.IoC.Internal;
using Newtonsoft.Json;
using Serilog; using Serilog;
namespace Dalamud.Game namespace Dalamud.Game
@ -16,17 +18,22 @@ namespace Dalamud.Game
/// </summary> /// </summary>
[PluginInterface] [PluginInterface]
[InterfaceVersion("1.0")] [InterfaceVersion("1.0")]
public sealed class SigScanner : IDisposable public class SigScanner : IDisposable
{ {
private readonly FileInfo? cacheFile;
private IntPtr moduleCopyPtr; private IntPtr moduleCopyPtr;
private long moduleCopyOffset; private long moduleCopyOffset;
private Dictionary<string, IntPtr>? textCache;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SigScanner"/> class using the main module of the current process. /// Initializes a new instance of the <see cref="SigScanner"/> class using the main module of the current process.
/// </summary> /// </summary>
/// <param name="doCopy">Whether or not to copy the module upon initialization for search operations to use, as to not get disturbed by possible hooks.</param> /// <param name="doCopy">Whether or not to copy the module upon initialization for search operations to use, as to not get disturbed by possible hooks.</param>
public SigScanner(bool doCopy = false) /// <param name="cacheFile">File used to cached signatures.</param>
: this(Process.GetCurrentProcess().MainModule!, doCopy) public SigScanner(bool doCopy = false, FileInfo? cacheFile = null)
: this(Process.GetCurrentProcess().MainModule!, doCopy, cacheFile)
{ {
} }
@ -35,8 +42,10 @@ namespace Dalamud.Game
/// </summary> /// </summary>
/// <param name="module">The ProcessModule to be used for scanning.</param> /// <param name="module">The ProcessModule to be used for scanning.</param>
/// <param name="doCopy">Whether or not to copy the module upon initialization for search operations to use, as to not get disturbed by possible hooks.</param> /// <param name="doCopy">Whether or not to copy the module upon initialization for search operations to use, as to not get disturbed by possible hooks.</param>
public SigScanner(ProcessModule module, bool doCopy = false) /// <param name="cacheFile">File used to cached signatures.</param>
public SigScanner(ProcessModule module, bool doCopy = false, FileInfo? cacheFile = null)
{ {
this.cacheFile = cacheFile;
this.Module = module; this.Module = module;
this.Is32BitProcess = !Environment.Is64BitProcess; this.Is32BitProcess = !Environment.Is64BitProcess;
this.IsCopy = doCopy; this.IsCopy = doCopy;
@ -49,6 +58,9 @@ namespace Dalamud.Game
Log.Verbose($"Module base: 0x{this.TextSectionBase.ToInt64():X}"); Log.Verbose($"Module base: 0x{this.TextSectionBase.ToInt64():X}");
Log.Verbose($"Module size: 0x{this.TextSectionSize:X}"); Log.Verbose($"Module size: 0x{this.TextSectionSize:X}");
if (cacheFile != null)
this.Load();
} }
/// <summary> /// <summary>
@ -294,6 +306,12 @@ namespace Dalamud.Game
/// <returns>The real offset of the found signature.</returns> /// <returns>The real offset of the found signature.</returns>
public IntPtr ScanText(string signature) public IntPtr ScanText(string signature)
{ {
if (this.textCache != null && this.textCache.TryGetValue(signature, out var address))
{
Log.Information("Found signature {Signature} in cache: {Address}", signature, address);
return address;
}
var mBase = this.IsCopy ? this.moduleCopyPtr : this.TextSectionBase; var mBase = this.IsCopy ? this.moduleCopyPtr : this.TextSectionBase;
var scanRet = Scan(mBase, this.TextSectionSize, signature); var scanRet = Scan(mBase, this.TextSectionSize, signature);
@ -306,6 +324,8 @@ namespace Dalamud.Game
if (insnByte == 0xE8 || insnByte == 0xE9) if (insnByte == 0xE8 || insnByte == 0xE9)
return ReadJmpCallSig(scanRet); return ReadJmpCallSig(scanRet);
this.textCache?.Add(signature, scanRet);
return scanRet; return scanRet;
} }
@ -337,6 +357,17 @@ namespace Dalamud.Game
Marshal.FreeHGlobal(this.moduleCopyPtr); Marshal.FreeHGlobal(this.moduleCopyPtr);
} }
/// <summary>
/// Save the current state of the cache.
/// </summary>
internal void Save()
{
if (this.cacheFile == null)
return;
File.WriteAllText(this.cacheFile.FullName, JsonConvert.SerializeObject(this.textCache));
}
/// <summary> /// <summary>
/// Helper for ScanText to get the correct address for IDA sigs that mark the first JMP or CALL location. /// Helper for ScanText to get the correct address for IDA sigs that mark the first JMP or CALL location.
/// </summary> /// </summary>
@ -479,5 +510,16 @@ namespace Dalamud.Game
this.moduleCopyOffset = this.moduleCopyPtr.ToInt64() - this.Module.BaseAddress.ToInt64(); this.moduleCopyOffset = this.moduleCopyPtr.ToInt64() - this.Module.BaseAddress.ToInt64();
} }
private void Load()
{
if (this.cacheFile is not { Exists: true })
{
this.textCache = new();
return;
}
this.textCache = JsonConvert.DeserializeObject<Dictionary<string, IntPtr>>(File.ReadAllText(this.cacheFile.FullName)) ?? new Dictionary<string, IntPtr>();
}
} }
} }