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:
parent
7fb7189de8
commit
c42f93b323
7 changed files with 750 additions and 8 deletions
24
src/AcDream.Content/Pak/PakReadStatus.cs
Normal file
24
src/AcDream.Content/Pak/PakReadStatus.cs
Normal 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,
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue