added items

This commit is contained in:
Boki 2026-02-28 15:13:31 -05:00
parent c3de5fdb63
commit 5f90bc137b
158 changed files with 2316 additions and 512 deletions

View file

@ -0,0 +1,141 @@
using Serilog;
namespace Automata.Memory;
public sealed class WalkabilityGrid
{
public int Width { get; }
public int Height { get; }
public byte[] Data { get; }
public WalkabilityGrid(int width, int height, byte[] data)
{
Width = width;
Height = height;
Data = data;
}
public bool IsWalkable(int x, int y)
{
if (x < 0 || x >= Width || y < 0 || y >= Height)
return false;
return Data[y * Width + x] == 0;
}
}
public sealed class TerrainReader : IDisposable
{
private readonly TerrainOffsets _offsets;
private ProcessMemory? _memory;
private PatternScanner? _scanner;
private nint _gameStateBase;
private bool _disposed;
public bool IsReady => _gameStateBase != 0;
public TerrainReader(TerrainOffsets offsets)
{
_offsets = offsets;
}
public bool Initialize()
{
_memory?.Dispose();
_memory = ProcessMemory.Attach(_offsets.ProcessName);
if (_memory is null)
return false;
if (string.IsNullOrWhiteSpace(_offsets.GameStatePattern))
{
Log.Warning("GameStatePattern is empty — offsets not yet configured for POE2");
return false;
}
_scanner = new PatternScanner(_memory);
_gameStateBase = _scanner.FindPatternRip(_offsets.GameStatePattern);
if (_gameStateBase == 0)
{
Log.Error("Failed to resolve GameState base pointer");
return false;
}
Log.Information("GameState base: 0x{Address:X}", _gameStateBase);
return true;
}
public WalkabilityGrid? ReadTerrain()
{
if (_memory is null || _gameStateBase == 0)
return null;
// Follow pointer chain: GameState → InGameState → IngameData → TerrainData
var terrainBase = _memory.FollowChain(_gameStateBase, [
_offsets.InGameStateOffset,
_offsets.IngameDataOffset,
_offsets.TerrainDataOffset
]);
if (terrainBase == 0)
{
Log.Debug("Terrain pointer chain returned null");
return null;
}
var numCols = _memory.Read<int>(terrainBase + _offsets.NumColsOffset);
var numRows = _memory.Read<int>(terrainBase + _offsets.NumRowsOffset);
var bytesPerRow = _memory.Read<int>(terrainBase + _offsets.BytesPerRowOffset);
if (numCols <= 0 || numRows <= 0 || bytesPerRow <= 0)
{
Log.Warning("Invalid terrain dimensions: {Cols}x{Rows}, bytesPerRow={Bpr}", numCols, numRows, bytesPerRow);
return null;
}
var gridWidth = numCols * _offsets.SubTilesPerCell;
var gridHeight = numRows * _offsets.SubTilesPerCell;
// Read melee layer pointer
var layerPtr = _memory.ReadPointer(terrainBase + _offsets.LayerMeleeOffset);
if (layerPtr == 0)
{
Log.Warning("Melee layer pointer is null");
return null;
}
// Read raw terrain data
var rawSize = bytesPerRow * gridHeight;
var rawData = _memory.ReadBytes(layerPtr, rawSize);
if (rawData is null)
{
Log.Warning("Failed to read terrain data ({Size} bytes)", rawSize);
return null;
}
// Unpack 4-bit nibbles: each byte → 2 cells (low nibble = even col, high nibble = odd col)
var data = new byte[gridWidth * gridHeight];
for (var row = 0; row < gridHeight; row++)
{
var rowStart = row * bytesPerRow;
for (var col = 0; col < gridWidth; col++)
{
var byteIndex = rowStart + col / 2;
if (byteIndex >= rawData.Length) break;
data[row * gridWidth + col] = (col % 2 == 0)
? (byte)(rawData[byteIndex] & 0x0F)
: (byte)((rawData[byteIndex] >> 4) & 0x0F);
}
}
Log.Information("Terrain read: {Width}x{Height} ({Cols}x{Rows} cells)", gridWidth, gridHeight, numCols, numRows);
return new WalkabilityGrid(gridWidth, gridHeight, data);
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
_memory?.Dispose();
}
}