.streamdb File Extension
.streamdb stands for stream database. The ".tga"streamdb files usedcontain the majority of game data in Doom Eternal are not targa images. They are actually "BIM" (or .bimage) files.
These files are stored in two parts. The header is embedded in Doom Eternal's .resources files. The body of the file is embedded in Doom Eternal's .streamdb files.Eternal.
File Signature
Doom Eternal ".tga"streamdb" files can be identified by the first 38 bytes of the file header, which is alwaysalways: 0x42 0x49 0x4D0x50A5C2292EF3C761. or "BIM", short for "bimage" or "binary image".
HeaderBasic Structure
The .tgastreamdb headerfile consists of 3 parts. An INDEX section, which is extracteda fromlist Doomof Eternal'sall the files contained within, followed (optionally) by a PREFETCH section, and finally the DATA section, which contains data referenced by the index.
The INDEX contains a list of hashed IDs rather than plaintext names. All files contained within the .streamdb are stored in a headerless format, and are usually compressed via Oodle Kraken or Oodle Leviathan compression technology. This means the files are impossible to identify by looking at the .streamdb alone. (They can, however, be identified by using the .resources files. files as a starting point).
struct STREAM_DB_FILE
{
INDEX index;
PREFETCH prefetch;
DATA data;
};
Index Structure
The .streamdb INDEX structure is of varying length. It consists of of a 63-32-byteHEADERstruct,header, followed by a variable number of 36-16-byte entries. MIPMAPstructs. EverythingThe overall structure is little-endian.
Theas technical details of .resources file extraction are beyond the scope of this article. You can extract the .tga headers yourself using the EternalResourceExtractor tool. follows:
struct ETERNAL_TGA_HEADERINDEX
{
HEADERstreamDatabaseHeader_t header; MIPMAP// mipmap[File signature + metadata
streamDatabaseEntry2_t streamdbEntries[]; // FlexibleOne array16-byte member,entry lengthfor varies witheach header.MipCount;numEntries
};
The struct is a HEADERstreamDatabaseHeader_t63-32-byte sequence:
struct HEADERstreamDatabaseHeader_t
{
charuint64 Signature[3];magic; // "BIM"50 byteA5 Version;C2 29 2E F3 C7 61
uint32 headerLength;
uint32 pad0; // 0x15null intpadding
TextureType;uint32 pad1; // enumnull textureType_tpadding
intuint32 TextureMaterialKind;pad2; // enumnull textureMaterialKind_tpadding
intuint32 PixelWidth;numEntries; // imageDoes widthnot ininclude pixelsprefetch intentries
PixelHeight;uint32 flags; // imageAlways height in pixels
int Depth;
int MipCount; // determines # of MIPMAP structs to follow
int64_t MipLevel;
float unkFloat1; // usually 1.0, purpose unknown
byte boolIsEnvironmentMap; // 1 if image is an environment map
int TextureFormat; // enum textureFormat_t
int Always7; // literally always 7, purpose unknown
int nullPadding;
short AtlasPadding;
byte boolIsStreamed; // 1 if file is located in streamDB
byte unkBool;
byte boolNoMips; // 1 if image has no mips
byte boolFFTBloom; // 1 if using FFT Bloom
int StreamDBMipCount; // usually same number of mips stored in streamdb3
};
TextureTypeis a member of the enumtextureType_t- it marks the image as 2-dimensional, 3-dimensional, or cubic.TextureMaterialKindis a member of the enumtextureMaterialKind_t- it tells the engine what type of material this texture is (albedo, normal, specular, etc).TextureFormatis a member of the enumtextureFormat_t- it tells the engine how the image is encoded (image format, block compression type, etc).
The
Mipmap Struct
The struct is a MIPMAPstreamDatabaseEntry2_t36-16-byte sequencesequence. that comes immediately after the HEADER. This structIt will be repeated repeated n times, where n = streamDatabaseHeader_t.numEntries is. equal to HEADER.MipCount above.
struct MIPMAP
{
int64_t MipLevel; // Starts at 0, increment by 1 each time it repeats
int MipPixelWidth; // Original PixelWidth reduced by 50% for each MipLevel
int MipPixelHeight; // Original PixelHeight reduced by 50% for each MipLevel
int UnknownFlagA;
int DecompressedSize; // Decompressed size in bytes
int FlagIsCompressed; // 1 ifTherefore, the texture is compressed
int CompressedSize; // Compressed size in bytes
int CumulativeSizeStreamDB;
};CumulativeSizeStreamDBis always zero for the first mipmap. For additional mipmaps, it is the sum of previous mipmaps compressed sizes.
The total size (in bytes)length of the section can be calculated TGA_FILE_HEADERINDEXas:as length 63= 32 + (3616 * HEADER.MipCount)streamDatabaseHeader_t.numEntries).
Non-Streamingstruct Images
streamDatabaseEntry2_t
struct ImagesIn{
mostuint64 cases,identity; // Shuffled version of .resources ID
uint32 offset16; // Multiply by 16 for data offset within .streamdb
uint32 length; // Size of the full-size versions of these ".tga" images are storedfile in .streamdb files,(usually wherecompressed)
they};
are
After accessedthe last entry, the INDEX section ends and the PREFETCH section begins.
Prefetch Structure
The .streamdb PREFETCH structure is of varying length. It consists of a 16-byte header, followed (optionally) by theeither gameone engineor two 16-byte prefetchBlocks, and a number of 8-byte prefetchIDs.
The overall PREFETCH structure is described as needed.follows:
struct somePREFETCH
images{
arestreamDatabasePrefetchHeader_t prefetchHeader; // Prefetch section header
streamDatabasePrefetchBlock_t prefetchBlock[]; // (Optional) Between 0-2 prefetch "non-streaming,"blocks"
whichuint64 meansprefetchEntries[]; they// aren't(Optional) 0 or more prefetch file IDs.
};
The streamDatabasePrefetchHeader_t struct is a 16-byte sequence. It is always present in the .streamdb filesfile, ateven all.if Non-streamingthis images.streamdb willdoes alwaysnot havecontain aany prefetch entries.
struct streamDatabasePrefetchHeader_t { uint32 numPrefetchBlocks; uint32 totalLength; // Total length ofHEADER.boolIsStreamed0prefetch header, blocks, entries };(false).
InTHIS thatSECTION case,INCOMPLETE, the body of the image is stored in the .resources file, and begins immediately after the TGA_FILE_HEADER structure described above. The offset (in bytes) at which the TGA_FILE_HEADER ends and the non-streamed image begins can be calculated as 63 + (36 * HEADER.MipCount).
Often, there will be several images packed together here (starting with the largest "mip"). The size of each of each image (in bytes) is given by MIPMAP.DecompressedSize. WIP
010 Editor Template
A 010 Editor template for use with Doom Eternal's TGA.streamdb file headersfiles can be found here: https://github.com/brongo/eternal-010-templates/blob/main/templates/DoomEternalTGA.DoomEternalStreamDB.bt