using System.Globalization;
using System.Text.RegularExpressions;
namespace AcDream.App.Tests.Rendering.Walk;
///
/// S3 chunk 1 (§11.2 B2): small, read-only parsers for the OH capture
/// family's two companion logs — the PARTS log
/// (tools/walk-oracle/oh/oh-capture-parts.cdb.template: PD/DM) and
/// the ALPHA-DEPTH log
/// (tools/walk-oracle/oh/oh-capture-alpha-depth.cdb.template: AM/FL/
/// PM/PC), both dumped from the SAME docs/research/2026-09-01-overhaul/
/// oh-capture/ directory as the walk log
/// already parses. These are records only — no validator, no canonical
/// JSONL, no new tool ([[feedback-evidence-infrastructure-sink]]) — a later
/// chunk/S4 names the first real consumer and pins specific values then.
///
public static class WalkOraclePartsTrace
{
/// Parses a PARTS log (<pose>.parts.log). Same
/// F/P framing and truncated-last-frame drop rule as
/// — the harness detaches at the
/// frame marker, so the final "F n" never records its own PD/DM lines.
public static IReadOnlyList Parse(IEnumerable lines)
{
var frames = new List();
List? partDraws = null;
List? meshDraws = null;
int currentNumber = 0;
foreach (string line in lines)
{
Match frameMatch = FramePattern.Match(line);
if (frameMatch.Success)
{
if (partDraws is not null)
{
frames.Add(new WalkOraclePartsFrame(currentNumber, partDraws, meshDraws!));
}
currentNumber = int.Parse(
frameMatch.Groups[1].Value, CultureInfo.InvariantCulture);
partDraws = new List();
meshDraws = new List();
continue;
}
if (partDraws is null)
continue;
Match pd = PartDrawPattern.Match(line);
if (pd.Success)
{
partDraws.Add(new WalkOraclePartDraw(
ParseHex(pd.Groups[1].Value),
ParseHex(pd.Groups[2].Value),
pd.Groups[3].Value != "0",
ParseHex(pd.Groups[4].Value)));
continue;
}
Match dm = MeshDrawPattern.Match(line);
if (dm.Success)
{
meshDraws!.Add(new WalkOracleMeshDraw(
ParseHex(dm.Groups[1].Value),
ParseHex(dm.Groups[2].Value),
dm.Groups[3].Value != "0",
int.Parse(dm.Groups[4].Value, CultureInfo.InvariantCulture),
ParseHex(dm.Groups[5].Value)));
continue;
}
// Anything else (the "P …" pose line included — parts fixtures
// carry no pose consumer today) is ignored, matching
// WalkOracleTrace's own "cdb chrome" tolerance.
}
return frames;
}
public static IReadOnlyList Load(string root, string fixtureName)
=> Parse(File.ReadLines(FixturePath(root, fixtureName, ".parts.log")));
private static string FixturePath(string root, string fixtureName, string suffix)
{
string repoRoot = WalkOracleTraceRepoRoot.Find();
return Path.Combine(
repoRoot, Path.Combine(root.Split('/')), fixtureName + suffix);
}
private static uint ParseHex(string hex)
=> uint.Parse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
private static readonly Regex FramePattern = new(@"^F (\d+)\s*$", RegexOptions.Compiled);
// PD gfx= did= force=<0|1> cell=
private static readonly Regex PartDrawPattern = new(
@"^PD gfx=([0-9a-f]{8}) did=([0-9a-f]{8}) force=(\d) cell=([0-9a-f]{8})\s*$",
RegexOptions.Compiled);
// DM gfx= did= force=<0|1> bound=<0|1|2> cell=
private static readonly Regex MeshDrawPattern = new(
@"^DM gfx=([0-9a-f]{8}) did=([0-9a-f]{8}) force=(\d) bound=(\d) cell=([0-9a-f]{8})\s*$",
RegexOptions.Compiled);
}
/// One CPhysicsPart::Draw @0x0050D7A0 entry (PD line).
public sealed record WalkOraclePartDraw(uint Gfx, uint DataId, bool Force, uint Cell);
/// One RenderDeviceD3D::DrawMeshInternal @0x0059F360 entry
/// (DM line). is retail's BoundingType (0=OUTSIDE,
/// 1=PARTIALLY_INSIDE, 2=ENTIRELY_INSIDE).
public sealed record WalkOracleMeshDraw(uint Gfx, uint DataId, bool Force, int Bound, uint Cell);
public sealed record WalkOraclePartsFrame(
int Number,
IReadOnlyList PartDraws,
IReadOnlyList MeshDraws);
///
/// S3 chunk 1 (§11.2 B2): parses an ALPHA-DEPTH log
/// (<pose>.alphadepth.log). Same F/P framing and
/// truncated-last-frame drop rule as .
///
public static class WalkOracleAlphaDepthTrace
{
public static IReadOnlyList Parse(IEnumerable lines)
{
var frames = new List();
List? meshAdds = null;
List? flushes = null;
List? portalPolyDraws = null;
List? drawCellsSamples = null;
int currentNumber = 0;
foreach (string line in lines)
{
Match frameMatch = FramePattern.Match(line);
if (frameMatch.Success)
{
if (meshAdds is not null)
{
frames.Add(new WalkOracleAlphaDepthFrame(
currentNumber, meshAdds, flushes!, portalPolyDraws!, drawCellsSamples!));
}
currentNumber = int.Parse(
frameMatch.Groups[1].Value, CultureInfo.InvariantCulture);
meshAdds = new List();
flushes = new List();
portalPolyDraws = new List();
drawCellsSamples = new List();
continue;
}
if (meshAdds is null)
continue;
Match am = MeshAddPattern.Match(line);
if (am.Success)
{
meshAdds.Add(new WalkOracleAlphaMeshAdd(
ParseHex(am.Groups[1].Value),
int.Parse(am.Groups[2].Value, CultureInfo.InvariantCulture),
ParseHex(am.Groups[3].Value),
am.Groups[4].Value != "0",
am.Groups[5].Value != "0",
int.Parse(am.Groups[6].Value, CultureInfo.InvariantCulture)));
continue;
}
Match fl = FlushPattern.Match(line);
if (fl.Success)
{
flushes!.Add(new WalkOracleAlphaFlush(
ParseHex(fl.Groups[1].Value), ParseHex(fl.Groups[2].Value)));
continue;
}
Match pm = PortalPolyPattern.Match(line);
if (pm.Success)
{
portalPolyDraws!.Add(new WalkOraclePortalPolyDraw(
ParseHex(pm.Groups[1].Value),
int.Parse(pm.Groups[2].Value, CultureInfo.InvariantCulture),
int.Parse(pm.Groups[3].Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture)));
continue;
}
Match pc = DrawCellsSamplePattern.Match(line);
if (pc.Success)
{
drawCellsSamples!.Add(new WalkOracleDrawCellsSample(
int.Parse(pc.Groups[1].Value, CultureInfo.InvariantCulture),
int.Parse(pc.Groups[2].Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture),
pc.Groups[3].Value != "0"));
continue;
}
}
return frames;
}
public static IReadOnlyList Load(string root, string fixtureName)
=> Parse(File.ReadLines(Path.Combine(
WalkOracleTraceRepoRoot.Find(),
Path.Combine(root.Split('/')),
fixtureName + ".alphadepth.log")));
private static uint ParseHex(string hex)
=> uint.Parse(hex, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
private static readonly Regex FramePattern = new(@"^F (\d+)\s*$", RegexOptions.Compiled);
// AM mesh= surf= csurf= new=<0|1> clip=<0|1> listSel=
private static readonly Regex MeshAddPattern = new(
@"^AM mesh=([0-9a-f]{8}) surf=(\d+) csurf=([0-9a-f]{8}) new=(\d) clip=(\d) listSel=(\d+)\s*$",
RegexOptions.Compiled);
// FL thresh= ret=
private static readonly Regex FlushPattern = new(
@"^FL thresh=([0-9a-f]{8}) ret=([0-9a-f]{8})\s*$", RegexOptions.Compiled);
// PM poly= mode= counterBefore=
private static readonly Regex PortalPolyPattern = new(
@"^PM poly=([0-9a-f]{8}) mode=(\d) counterBefore=([0-9a-f]{4})\s*$",
RegexOptions.Compiled);
// PC ov= counter= fc=<0|1>
private static readonly Regex DrawCellsSamplePattern = new(
@"^PC ov=(\d+) counter=([0-9a-f]{4}) fc=(\d)\s*$", RegexOptions.Compiled);
}
/// One D3DPolyRender::AddMeshToAlphaList @0x0059C230 entry
/// (AM line). 0 selects the ALPHA list,
/// nonzero the CLIP list.
public sealed record WalkOracleAlphaMeshAdd(
uint Mesh, int Surface, uint ClipSurface, bool New, bool Clip, int ListSelector);
/// One D3DPolyRender::FlushAlphaList @0x0059D2E0 entry (FL
/// line). is the raw IEEE-754 bits of the
/// threshold float argument.
public sealed record WalkOracleAlphaFlush(uint ThresholdBits, uint ReturnAddress);
/// One D3DPolyRender::DrawPortalPolyInternal @0x0059BC90
/// entry (PM line). 0 = true-depth/exit-seal,
/// nonzero = far-Z/building punch.
public sealed record WalkOraclePortalPolyDraw(uint Poly, int Mode, int CounterBefore);
/// One PView::DrawCells @0x005A4840 entry, sampled for the
/// persistent portalsDrawnCount depth-lifecycle state machine (PC
/// line) — same breakpoint address as the walk log's DC line, but this is a
/// SEPARATE capture that does not also record the cell roster.
public sealed record WalkOracleDrawCellsSample(int OutsideViewCount, int Counter, bool ForceClear);
public sealed record WalkOracleAlphaDepthFrame(
int Number,
IReadOnlyList MeshAdds,
IReadOnlyList Flushes,
IReadOnlyList PortalPolyDraws,
IReadOnlyList DrawCellsSamples);
/// Shared repo-root finder — the SAME walk-up-to-AcDream.slnx
/// logic already has privately; factored out
/// so the parts/alpha-depth parsers don't duplicate it a second and third
/// time.
internal static class WalkOracleTraceRepoRoot
{
internal static string Find()
{
DirectoryInfo? dir = new(AppContext.BaseDirectory);
while (dir is not null)
{
if (File.Exists(Path.Combine(dir.FullName, "AcDream.slnx")))
return dir.FullName;
dir = dir.Parent;
}
throw new InvalidOperationException(
"AcDream.slnx not found above the test base directory; walk-oracle fixtures unavailable.");
}
}