perf(content): add typed prepared asset source

Introduce explicit loaded, missing, and corrupt outcomes over typed pak keys, validate package/DAT identity at open, add zero-I/O TOC probes, preserve cancellation, and route the package path through RuntimeOptions. Production injection follows in the next checkpoint.
This commit is contained in:
Erik 2026-07-24 14:52:12 +02:00
parent 7fb7189de8
commit c42f93b323
7 changed files with 750 additions and 8 deletions

View file

@ -0,0 +1,24 @@
namespace AcDream.Content.Pak;
/// <summary>
/// TOC-only state for one exact typed pak key. <see cref="Available"/> means
/// the immutable TOC row exists and has a structurally valid file range; CRC
/// and payload structure are deliberately not faulted in by a probe.
/// </summary>
public enum PakEntryState
{
Missing,
Available,
Corrupt,
}
/// <summary>
/// Result of reading and deserializing one pak payload. This preserves the
/// distinction between an absent key and a present-but-damaged external file.
/// </summary>
public enum PakObjectReadStatus
{
Missing,
Loaded,
Corrupt,
}

View file

@ -112,21 +112,45 @@ public sealed class PakReader : IDisposable {
return VerdictFor(index) == 1;
}
/// <summary>
/// Probes the immutable TOC only. No payload bytes are copied, CRC-checked,
/// or deserialized. A structurally invalid entry is reported as corrupt;
/// a CRC or payload-structure fault becomes visible after the first read.
/// </summary>
public PakEntryState ProbeEntry(ulong key) {
int index = BinarySearch(key);
if (index < 0)
return PakEntryState.Missing;
return _entryVerdictByTocIndex.TryGetValue(index, out int verdict)
&& verdict == 0
? PakEntryState.Corrupt
: PakEntryState.Available;
}
/// <summary>
/// Reads and deserializes the <see cref="ObjectMeshData"/> stored under
/// <paramref name="key"/>. Returns false (data null) if the key is absent
/// OR the blob fails any tripwire (bounds, CRC, structural deserialization
/// failure) — logged once per entry, per the corrupt-=-missing contract.
/// </summary>
public bool TryReadObjectMeshData(ulong key, out ObjectMeshData? data) {
public bool TryReadObjectMeshData(ulong key, out ObjectMeshData? data) =>
ReadObjectMeshData(key, out data) == PakObjectReadStatus.Loaded;
/// <summary>
/// Reads one payload while preserving absent-versus-corrupt state for the
/// production prepared-asset boundary.
/// </summary>
public PakObjectReadStatus ReadObjectMeshData(
ulong key,
out ObjectMeshData? data) {
data = null;
int index = BinarySearch(key);
if (index < 0) return false;
if (index < 0) return PakObjectReadStatus.Missing;
// Previously judged bad (bounds at open, or an earlier CRC/structure
// failure): missing, no re-read, no re-log.
bool judged = _entryVerdictByTocIndex.TryGetValue(index, out var verdict);
if (judged && verdict == 0) return false;
if (judged && verdict == 0) return PakObjectReadStatus.Corrupt;
// Single pass (review finding 4): ONE copy out of the map; CRC and
// deserialization both run over this same buffer.
@ -139,14 +163,14 @@ public sealed class PakReader : IDisposable {
if (actualCrc != entry.Crc32) {
_entryVerdictByTocIndex[index] = 0;
LogCorruptionOnce(index, $"crc mismatch (expected 0x{entry.Crc32:X8}, got 0x{actualCrc:X8})");
return false;
return PakObjectReadStatus.Corrupt;
}
_entryVerdictByTocIndex[index] = 1;
}
try {
data = ObjectMeshDataSerializer.Read(bytes);
return true;
return PakObjectReadStatus.Loaded;
}
catch (Exception ex) {
// Structurally malformed blob behind a valid CRC (bake-side bug or
@ -155,7 +179,7 @@ public sealed class PakReader : IDisposable {
data = null;
_entryVerdictByTocIndex[index] = 0;
LogCorruptionOnce(index, $"deserialization failed despite matching CRC: {ex.GetType().Name}: {ex.Message}");
return false;
return PakObjectReadStatus.Corrupt;
}
}