lots done
This commit is contained in:
parent
1ba7c39c30
commit
fbd0ba445a
59 changed files with 6074 additions and 3598 deletions
203
src/Automata.Memory/GameStateReader.cs
Normal file
203
src/Automata.Memory/GameStateReader.cs
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
using Serilog;
|
||||
|
||||
namespace Automata.Memory;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves GameState → Controller → InGameState, reads state slots, loading/escape state.
|
||||
/// </summary>
|
||||
public sealed class GameStateReader
|
||||
{
|
||||
private readonly MemoryContext _ctx;
|
||||
|
||||
public GameStateReader(MemoryContext ctx)
|
||||
{
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves InGameState pointer from the GameState controller.
|
||||
/// </summary>
|
||||
public nint ResolveInGameState(GameStateSnapshot snap)
|
||||
{
|
||||
var mem = _ctx.Memory;
|
||||
var offsets = _ctx.Offsets;
|
||||
|
||||
var controller = mem.ReadPointer(_ctx.GameStateBase);
|
||||
if (controller == 0) return 0;
|
||||
snap.ControllerPtr = controller;
|
||||
|
||||
// Direct offset mode: read InGameState straight from controller
|
||||
if (offsets.InGameStateDirectOffset > 0)
|
||||
{
|
||||
var igs = mem.ReadPointer(controller + offsets.InGameStateDirectOffset);
|
||||
if (igs != 0)
|
||||
{
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
var slotOffset = offsets.StatesBeginOffset + i * offsets.StateStride + offsets.StatePointerOffset;
|
||||
var ptr = mem.ReadPointer(controller + slotOffset);
|
||||
if (ptr == 0) break;
|
||||
snap.StatesCount++;
|
||||
}
|
||||
return igs;
|
||||
}
|
||||
}
|
||||
|
||||
if (offsets.StatesInline)
|
||||
{
|
||||
var inlineOffset = offsets.StatesBeginOffset
|
||||
+ offsets.InGameStateIndex * offsets.StateStride
|
||||
+ offsets.StatePointerOffset;
|
||||
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
var slotOffset = offsets.StatesBeginOffset + i * offsets.StateStride + offsets.StatePointerOffset;
|
||||
var ptr = mem.ReadPointer(controller + slotOffset);
|
||||
if (ptr == 0) break;
|
||||
snap.StatesCount++;
|
||||
}
|
||||
|
||||
return mem.ReadPointer(controller + inlineOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
var statesBegin = mem.ReadPointer(controller + offsets.StatesBeginOffset);
|
||||
if (statesBegin == 0) return 0;
|
||||
|
||||
var statesEnd = mem.ReadPointer(controller + offsets.StatesBeginOffset + 8);
|
||||
if (statesEnd > statesBegin && statesEnd - statesBegin < 0x1000 && offsets.StateStride > 0)
|
||||
{
|
||||
snap.StatesCount = (int)((statesEnd - statesBegin) / offsets.StateStride);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
if (mem.ReadPointer(statesBegin + i * offsets.StateStride + offsets.StatePointerOffset) == 0) break;
|
||||
snap.StatesCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (offsets.InGameStateIndex < 0 || offsets.InGameStateIndex >= snap.StatesCount)
|
||||
return 0;
|
||||
|
||||
return mem.ReadPointer(statesBegin + offsets.InGameStateIndex * offsets.StateStride + offsets.StatePointerOffset);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all state slot pointers and active states vector from the controller.
|
||||
/// </summary>
|
||||
public void ReadStateSlots(GameStateSnapshot snap)
|
||||
{
|
||||
var controller = snap.ControllerPtr;
|
||||
if (controller == 0) return;
|
||||
|
||||
var mem = _ctx.Memory;
|
||||
var offsets = _ctx.Offsets;
|
||||
|
||||
var count = offsets.StateCount;
|
||||
var slots = new nint[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var slotOffset = offsets.StatesBeginOffset + i * offsets.StateStride + offsets.StatePointerOffset;
|
||||
slots[i] = mem.ReadPointer(controller + slotOffset);
|
||||
}
|
||||
snap.StateSlots = slots;
|
||||
|
||||
var values = new int[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if (slots[i] != 0)
|
||||
values[i] = mem.Read<int>(slots[i] + 0x08);
|
||||
}
|
||||
snap.StateSlotValues = values;
|
||||
|
||||
// Read active states vector
|
||||
if (offsets.ActiveStatesOffset > 0)
|
||||
{
|
||||
var beginPtr = mem.ReadPointer(controller + offsets.ActiveStatesOffset);
|
||||
var endPtr = mem.ReadPointer(controller + offsets.ActiveStatesOffset + 16);
|
||||
snap.ActiveStatesBegin = beginPtr;
|
||||
snap.ActiveStatesEnd = endPtr;
|
||||
|
||||
if (beginPtr != 0 && endPtr > beginPtr)
|
||||
{
|
||||
var size = (int)(endPtr - beginPtr);
|
||||
if (size is > 0 and < 0x1000)
|
||||
{
|
||||
var data = mem.ReadBytes(beginPtr, size);
|
||||
if (data is not null)
|
||||
{
|
||||
var rawList = new List<nint>();
|
||||
for (var i = 0; i + 8 <= data.Length; i += offsets.StateStride)
|
||||
{
|
||||
var ptr = (nint)BitConverter.ToInt64(data, i);
|
||||
rawList.Add(ptr);
|
||||
if (ptr != 0)
|
||||
snap.ActiveStates.Add(ptr);
|
||||
}
|
||||
snap.ActiveStatesRaw = rawList.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read all non-null pointer-like qwords from controller (outside state array)
|
||||
var stateArrayStart = offsets.StatesBeginOffset;
|
||||
var stateArrayEnd = stateArrayStart + count * offsets.StateStride;
|
||||
var watches = new List<(int, nint)>();
|
||||
|
||||
var ctrlData = mem.ReadBytes(controller, 0x350);
|
||||
if (ctrlData is not null)
|
||||
{
|
||||
for (var offset = 0; offset + 8 <= ctrlData.Length; offset += 8)
|
||||
{
|
||||
if (offset >= stateArrayStart && offset < stateArrayEnd) continue;
|
||||
var value = (nint)BitConverter.ToInt64(ctrlData, offset);
|
||||
if (value == 0) continue;
|
||||
var high = (ulong)value >> 32;
|
||||
if (high > 0 && high < 0x7FFF && (value & 0x3) == 0)
|
||||
watches.Add((offset, value));
|
||||
}
|
||||
}
|
||||
snap.WatchOffsets = watches.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects loading by comparing the active state pointer to InGameStatePtr.
|
||||
/// </summary>
|
||||
public void ReadIsLoading(GameStateSnapshot snap)
|
||||
{
|
||||
var controller = snap.ControllerPtr;
|
||||
if (controller == 0 || _ctx.Offsets.IsLoadingOffset <= 0)
|
||||
return;
|
||||
|
||||
var value = _ctx.Memory.ReadPointer(controller + _ctx.Offsets.IsLoadingOffset);
|
||||
|
||||
if (value == snap.InGameStatePtr && snap.InGameStatePtr != 0)
|
||||
snap.IsLoading = false;
|
||||
else if (value == 0)
|
||||
snap.IsLoading = false;
|
||||
else
|
||||
snap.IsLoading = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads escape menu state from active states vector or InGameState flag.
|
||||
/// </summary>
|
||||
public void ReadEscapeState(GameStateSnapshot snap)
|
||||
{
|
||||
if (snap.ActiveStates.Count > 0 && snap.StateSlots.Length > 3 && snap.StateSlots[3] != 0)
|
||||
{
|
||||
snap.IsEscapeOpen = snap.ActiveStates.Contains(snap.StateSlots[3]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (snap.InGameStatePtr == 0 || _ctx.Offsets.EscapeStateOffset <= 0)
|
||||
return;
|
||||
|
||||
var value = _ctx.Memory.Read<int>(snap.InGameStatePtr + _ctx.Offsets.EscapeStateOffset);
|
||||
snap.IsEscapeOpen = value != 0;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue