Understanding EBX, RES, chunks, bundles, and how Frosty manages game assets
Frostbite games organize content into three primary asset types: EBX (entity data), RES (resources), and Chunks (binary data). These assets are packaged into bundles and managed by the AssetManager.
EBX files use a binary format with reflection metadata:
Header
Type Descriptors
Fields
Instances
internal enum EbxVersion{ Version2 = 0x0FB2D1CE, // Older games Version4 = 0x0FB4D1CE // Newer games (2017+)}// EBX header contains:// - Magic number (version)// - String offset table// - Type descriptor array// - Instance array// - Pointer fixup data
From EbxReader.cs:48.
public struct EbxClass{ public string Name; // Type name public uint NameHash; // FNV1 hash public int FieldIndex; // First field index public byte FieldCount; // Number of fields public byte Alignment; // Memory alignment public ushort Type; // Type flags public ushort Size; // Instance size public string Namespace; // Type namespace}
From EbxReader.cs:66.
public struct EbxField{ public string Name; // Field name public uint NameHash; // FNV1 hash public ushort Type; // Field type flags public ushort ClassRef; // Reference to type public uint DataOffset; // Offset in instance public uint SecondOffset; // Secondary offset (arrays)}
From EbxReader.cs:54.
public struct EbxInstance{ public ushort ClassRef; // Index into class array public ushort Count; // Number of instances public bool IsExported; // Can be referenced externally}
Instances contain the actual object data laid out per type descriptors.
RES assets are typed binary resources like textures, meshes, and audio.ResAssetEntry Structure:
public class ResAssetEntry : AssetEntry{ public override string Type => ((ResourceType)ResType).ToString(); public override string AssetType => "res"; public ulong ResRid; // Resource ID (unique) public uint ResType; // Resource type hash public byte[] ResMeta; // Resource metadata}
Chunks store large binary blobs referenced by resources.ChunkAssetEntry Structure:
public class ChunkAssetEntry : AssetEntry{ public override string Name => Id.ToString(); public override string Type => "Chunk"; public override string AssetType => "chunk"; public Guid Id; // Chunk identifier public uint BundledSize; // Compressed size in bundle public uint LogicalOffset; // Offset for streaming public uint LogicalSize; // Logical size public uint RangeStart; // Range start (textures) public uint RangeEnd; // Range end (textures) public int H32; // FNV1 hash of parent public int FirstMip; // First mipmap level public bool IsTocChunk; // Is in TOC chunk list}
Bundles group related assets together.BundleEntry Structure:
public class BundleEntry{ public string Name; // Bundle name public int SuperBundleId; // Parent superbundle public EbxAssetEntry Blueprint; // Blueprint asset (if any) public BundleType Type; // Bundle type public bool Added; // User-added bundle}public enum BundleType{ None = -1, SubLevel, // Contains level data BlueprintBundle, // Contains blueprint definitions SharedBundle // Shared resources}
public class AssetEntry{ public List<int> Bundles = new List<int>(); // Original bundles public List<int> AddedBundles = new List<int>(); // User-added public List<int> RemBundles = new List<int>(); // Removed bundles public bool IsInBundle(int bid) => Bundles.Contains(bid) || AddedBundles.Contains(bid); public IEnumerable<int> EnumerateBundles(bool addedOnly = false) { if (!addedOnly) { for (int i = 0; i < Bundles.Count; i++) { if (!RemBundles.Contains(Bundles[i])) yield return Bundles[i]; } } for (int i = 0; i < AddedBundles.Count; i++) yield return AddedBundles[i]; }}
public void LinkAsset(AssetEntry assetToLink){ if (!LinkedAssets.Contains(assetToLink)) LinkedAssets.Add(assetToLink); // Store parent reference in chunk if (assetToLink is ChunkAssetEntry entry) { if (entry.HasModifiedData) { entry.ModifiedEntry.H32 = Fnv1.HashString(Name.ToLower()); } else { entry.H32 = Fnv1.HashString(Name.ToLower()); } }}
From AssetManager.cs:244.Common Linking Patterns:
1
Texture → Chunk
TextureAsset (RES) links to texture data chunk
2
Mesh → Multiple Chunks
MeshSet (RES) links to vertex buffer, index buffer, and optional LOD chunks
public enum AssetDataLocation{ Cas, // In catalog CAS archive SuperBundle, // In binary superbundle Cache, // In Frosty cache (patched data) CasNonIndexed // In CAS but via manifest}public class AssetExtraData{ public Sha1 BaseSha1; // Base asset hash public Sha1 DeltaSha1; // Delta hash (patches) public long DataOffset; // Offset in file public int SuperBundleId; // Superbundle reference public bool IsPatch; // Is patched data public string CasPath; // Path to CAS file}
// Get all texturesforeach (ResAssetEntry entry in App.AssetManager.EnumerateRes()){ if (entry.ResType == (uint)ResourceType.Texture) { // Process texture... }}// Get modified EBX of specific typeforeach (EbxAssetEntry entry in App.AssetManager.EnumerateEbx("MeshAsset", modifiedOnly: true)){ // Process modified meshes...}// Get assets in specific bundleBundleEntry bundle = App.AssetManager.GetBundle(bundleId);foreach (EbxAssetEntry entry in App.AssetManager.EnumerateEbx(bundle)){ // Process bundle assets...}