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();
// 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();
// Signal the main game thread to continue
@ -117,7 +122,7 @@ namespace Dalamud
// Initialize FFXIVClientStructs function resolver
using (Timings.Start("CS Resolver Init"))
{
FFXIVClientStructs.Resolver.Initialize();
FFXIVClientStructs.Resolver.InitializeParallel();
Log.Information("[T1] FFXIVClientStructs initialized!");
}
@ -390,7 +395,10 @@ namespace Dalamud
Service<AntiDebug>.GetNullable()?.Dispose();
Service<DalamudAtkTweaks>.GetNullable()?.Dispose();
Service<HookManager>.GetNullable()?.Dispose();
Service<SigScanner>.GetNullable()?.Dispose();
var sigScanner = Service<SigScanner>.Get();
sigScanner.Save();
sigScanner.Dispose();
SerilogEventSink.Instance.LogLine -= SerilogOnLogLine;

View file

@ -2,11 +2,13 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Newtonsoft.Json;
using Serilog;
namespace Dalamud.Game
@ -16,17 +18,22 @@ namespace Dalamud.Game
/// </summary>
[PluginInterface]
[InterfaceVersion("1.0")]
public sealed class SigScanner : IDisposable
public class SigScanner : IDisposable
{
private readonly FileInfo? cacheFile;
private IntPtr moduleCopyPtr;
private long moduleCopyOffset;
private Dictionary<string, IntPtr>? textCache;
/// <summary>
/// Initializes a new instance of the <see cref="SigScanner"/> class using the main module of the current process.
/// </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>
public SigScanner(bool doCopy = false)
: this(Process.GetCurrentProcess().MainModule!, doCopy)
/// <param name="cacheFile">File used to cached signatures.</param>
public SigScanner(bool doCopy = false, FileInfo? cacheFile = null)
: this(Process.GetCurrentProcess().MainModule!, doCopy, cacheFile)
{
}
@ -35,8 +42,10 @@ namespace Dalamud.Game
/// </summary>
/// <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>
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.Is32BitProcess = !Environment.Is64BitProcess;
this.IsCopy = doCopy;
@ -49,6 +58,9 @@ namespace Dalamud.Game
Log.Verbose($"Module base: 0x{this.TextSectionBase.ToInt64():X}");
Log.Verbose($"Module size: 0x{this.TextSectionSize:X}");
if (cacheFile != null)
this.Load();
}
/// <summary>
@ -294,6 +306,12 @@ namespace Dalamud.Game
/// <returns>The real offset of the found signature.</returns>
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 scanRet = Scan(mBase, this.TextSectionSize, signature);
@ -306,6 +324,8 @@ namespace Dalamud.Game
if (insnByte == 0xE8 || insnByte == 0xE9)
return ReadJmpCallSig(scanRet);
this.textCache?.Add(signature, scanRet);
return scanRet;
}
@ -337,6 +357,17 @@ namespace Dalamud.Game
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>
/// Helper for ScanText to get the correct address for IDA sigs that mark the first JMP or CALL location.
/// </summary>
@ -479,5 +510,16 @@ namespace Dalamud.Game
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>();
}
}
}