LiveSplit.TZA/LiveSplit.TZA/GameMemory.cs

114 lines
4.8 KiB
C#
Executable File

using System.Diagnostics;
using System.Runtime.InteropServices;
using LiveSplit.ComponentUtil;
using LiveSplit.TZA.Splits;
namespace LiveSplit.TZA;
public class GameMemory : MemoryWatcherList {
private Process Game { get; }
internal MemoryWatcher<uint> IsLoaded { get; }
internal MemoryWatcher<ulong> ConfigMenu { get; }
internal MemoryWatcher<uint> Stage { get; }
internal MemoryWatcher<ushort> Location { get; }
internal MemoryWatcher<uint> BossHp { get; }
internal MemoryWatcher<UndyingInfo> UndyingInfo { get; }
internal StringWatcher Cutscene { get; }
internal Dictionary<Character, MemoryWatcher<byte>> Levels { get; } = new();
public GameMemory(Process game) {
this.Game = game;
var scanner = new SignatureScanner(game, game.MainModuleWow64Safe().BaseAddress, game.MainModuleWow64Safe().ModuleMemorySize);
var configPtrIns = scanner.Scan(new SigScanTarget(3, "48 89 1D ?? ?? ?? ?? 89 BB ?? ?? ?? ?? 66 89 BB"));
this.ConfigMenu = new MemoryWatcher<ulong>(new DeepPointer(configPtrIns + 4 + game.ReadValue<int>(configPtrIns))) {
FailAction = MemoryWatcher.ReadFailAction.SetZeroOrNull,
};
var dataPtrIns = scanner.Scan(new SigScanTarget(3, "48 8D 0D ?? ?? ?? ?? 41 B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 33 C9 E8"));
if (!new DeepPointer(dataPtrIns + 4 + game.ReadValue<int>(dataPtrIns)).DerefOffsets(this.Game, out var dataPtr)) {
dataPtr = IntPtr.Zero;
}
this.Stage = new MemoryWatcher<uint>(dataPtr + 0x200) {
FailAction = MemoryWatcher.ReadFailAction.SetZeroOrNull,
};
this.Location = new MemoryWatcher<ushort>(dataPtr - 0x90) {
FailAction = MemoryWatcher.ReadFailAction.SetZeroOrNull,
};
var bossHpIns = scanner.Scan(new SigScanTarget(3, "48 8D 1D ?? ?? ?? ?? 8B F5 48 89 29 8B FD 4C 8B"));
if (!new DeepPointer(bossHpIns + 4 + game.ReadValue<int>(bossHpIns)).DerefOffsets(this.Game, out var bossHpPtr)) {
bossHpPtr = IntPtr.Zero;
}
this.BossHp = new MemoryWatcher<uint>(bossHpPtr - 4) {
FailAction = MemoryWatcher.ReadFailAction.SetZeroOrNull,
};
var partyIns = scanner.Scan(new SigScanTarget(15, "F2 0F 10 05 ?? ?? ?? ?? F2 0F 11 41 ?? 8B 05 ?? ?? ?? ?? 89 41 28 C3"));
if (!new DeepPointer(partyIns + 4 + game.ReadValue<int>(partyIns)).DerefOffsets(this.Game, out var partyPtr)) {
partyPtr = IntPtr.Zero;
}
partyPtr += 4 + 8;
foreach (var character in (Character[]) Enum.GetValues(typeof(Character))) {
this.Levels[character] = new MemoryWatcher<byte>(partyPtr + 0x1C8 * (int) character + 0x1C2) {
FailAction = MemoryWatcher.ReadFailAction.SetZeroOrNull,
};
}
var cutsceneNameIns = scanner.Scan(new SigScanTarget(3, "48 8B 05 ?? ?? ?? ?? 48 3B 05 ?? ?? ?? ?? 74 11 C6 00 00 48 8B 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 63 0D"));
if (!new DeepPointer(cutsceneNameIns + 4 + game.ReadValue<int>(cutsceneNameIns)).DerefOffsets(this.Game, out var cutsceneNamePtr)) {
cutsceneNamePtr = IntPtr.Zero;
}
this.Cutscene = new StringWatcher(new DeepPointer(cutsceneNamePtr, 0), ReadStringType.UTF8, 32) {
FailAction = MemoryWatcher.ReadFailAction.SetZeroOrNull,
};
var isLoadedIns = scanner.Scan(new SigScanTarget(3, "4C 8D 3D ?? ?? ?? ?? 48 89 0D ?? ?? ?? ?? 48 85 C0 74 31 8B 48 0C E8"));
if (!new DeepPointer(isLoadedIns + 4 + game.ReadValue<int>(isLoadedIns)).DerefOffsets(this.Game, out var isLoadedPtr)) {
isLoadedPtr = IntPtr.Zero;
}
this.IsLoaded = new MemoryWatcher<uint>(isLoadedPtr) {
FailAction = MemoryWatcher.ReadFailAction.SetZeroOrNull,
};
var undyingInfoIns = scanner.Scan(new SigScanTarget(17, "33 C9 B8 ?? ?? ?? ?? 66 89 05 ?? ?? ?? ?? 48 89 0D ?? ?? ?? ?? 89 0D ?? ?? ?? ?? 66 89 0D"));
this.UndyingInfo = new MemoryWatcher<UndyingInfo>(new DeepPointer(undyingInfoIns + 4 + game.ReadValue<int>(undyingInfoIns), 0)) {
FailAction = MemoryWatcher.ReadFailAction.SetZeroOrNull,
};
}
public bool Update() {
if (this.Game.HasExited) {
return false;
}
this.IsLoaded.Update(this.Game);
this.ConfigMenu.Update(this.Game);
this.Stage.Update(this.Game);
this.Location.Update(this.Game);
this.BossHp.Update(this.Game);
this.UndyingInfo.Update(this.Game);
foreach (var level in this.Levels.Values) {
level.Update(this.Game);
}
this.Cutscene.Update(this.Game);
return true;
}
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
internal struct UndyingInfo {
[FieldOffset(0x48)]
internal readonly uint Hp;
}