Binary Wire Format v1
Aegis Save uses a custom, bit-exact binary wire format designed for durability, forward/backward compatibility, and zero-copy bundle reuse.
This guide details the physical byte layout of .aegis save files and explains how the format guarantees perpetual compatibility across engine upgrades.
1. Physical Container Layout
An .aegis save container is organized into four distinct sections:
┌─────────────────────────────────────────────────────────────┐
│ 1. Container Header (Fixed 64-byte uncompressed header) │
│ - Magic Identifier ("AEGIS\0\0\0") │
│ - Format Version (Frozen at 1) │
│ - Engine Version & Custom Version GUID Table │
├─────────────────────────────────────────────────────────────┤
│ 2. Metadata Section (Fast Slot Browser Index) │
│ - Slot Display Name, UTC Timestamp, Playtime Seconds │
│ - Game Schema Version & Record Count │
├─────────────────────────────────────────────────────────────┤
│ 3. Bundle Index & Hash Table │
│ - Table of Bundle Offsets, Sizes, & CRC32 Checksums │
│ - Spatial Morton Z-Order Locality Tags │
├─────────────────────────────────────────────────────────────┤
│ 4. Payload Data Bundles (Oodle Kraken Compressed) │
│ - Bundle 0: [Records 0..N] │
│ - Bundle 1: [Records N+1..M] │
│ - ... │
├─────────────────────────────────────────────────────────────┤
│ 5. Container Trailer │
│ - Overall Payload CRC32 Checksum │
└─────────────────────────────────────────────────────────────┘Key Architectural Strengths
- Fast Metadata Reads: Reading slot info (
Get Slot Info/Get All Slot Info) reads only the first few hundred bytes of Header and Metadata. It never decompresses the record bundles, enabling ultra-fast save browser menus even with hundreds of slots. - Verbatim Bundle Reuse (
F-3): Bundles that contain no modified records are copied directly byte-for-byte from the old file to the new file, skipping CPU-intensive re-compression. - Atomic Integrity Validation: Every bundle and the container trailer carry CRC32 checksums. If a power cut or write interruption occurs, corruption is caught immediately.
2. Tagged Property Encoding (G-3)
Unlike brittle raw memory dumps, Aegis Save uses Schema-Tagged Property Serialization:
- Each property written to the bundle carries its name, property type tag, and byte size.
- If you add a new variable to your character Blueprint in a patch, existing player saves load seamlessly (the new variable adopts its default value).
- If you remove a variable, Aegis cleanly skips the tagged property bytes without corrupting adjacent variables.
- If you reorder variables, tagged lookup matches fields by name rather than binary byte offset.
3. Perpetual Compatibility & Golden Save Corpus (G-1, E-1)
Aegis Save 1.0 establishes a perpetual backwards compatibility guarantee (G-1):
Every save file written by Aegis 1.0 on any supported engine version will load bit-exact in every future version of Aegis Save.
The Golden Save Corpus
To mathematically prove this guarantee rather than merely claim it, the repository contains a committed Golden Save Corpus (Plugins/AegisSave/Corpus/):
v1_5.5.aegis(Captured on UE 5.5.4)v1_5.6.aegis(Captured on UE 5.6.1)v1_5.7.aegis(Captured on UE 5.7.4)v1_5.8.aegis(Captured on UE 5.8.1)Manifest.json(Bit-exact property expectations)
The automated test Aegis.Corpus.LoadsAndVerifiesAllEntries executes across all 16 cross-engine permutations ($4 \times 4$) on every engine release gate.
4. Custom Binary Serializers (G-14, FO-20)
If your C++ actors implement custom binary serialization via Serialize(FArchive& Ar) instead of standard reflected UPROPERTYs, follow this mandatory rule:
Branch on Meaning, Never on Width (FO-20)
When implementing custom serialization, always branch on engine custom versions or semantic format flags. Never assume fixed byte widths or pack raw C++ structs directly without version tags!
// Correct: Version-branched custom serialization
void AMyComplexActor::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
Ar.UsingCustomVersion(FMyGameCustomVersion::GUID);
const int32 CustomVer = Ar.CustomVer(FMyGameCustomVersion::GUID);
if (Ar.IsSaving())
{
Ar << MyCoreValue;
Ar << MyNewPatchValue; // Added in Version 2
}
else if (Ar.IsLoading())
{
Ar << MyCoreValue;
if (CustomVer >= FMyGameCustomVersion::AddedNewPatchValue)
{
Ar << MyNewPatchValue;
}
else
{
MyNewPatchValue = DefaultValue; // Safe fallback for older saves
}
}
}Next Steps
- Jump into practical examples in the Cookbook & Recipes.
- Learn how to evolve schemas safely in Schema Evolution & Migrations.