EPB Empyrion Blueprint - ApanLoon/EmpyrionStuff GitHub Wiki

The Empyrion Blueprint format is used to save blueprints of models in the game.

The files of this format comprise two parts, one for quick and immediate access and one zipped part that defies detais suck as blocks, colours, textures and signal logic.

The structure is somewhat close to this:

EPB
{
    UInt32       identifier; // = 0x78945245
    UInt32       version;
    UInt8        bluePrintType; //0 = Voxel, 2 = Base, 4 = SmallVessel, 8 = CapitalVessel, 16 = HoverVessel
    UInt32       width;
    UInt32       height;
    UInt32       depth;
    UInt16       unknown01;

    UInt16       nMetaTags;
    EPBMetaTag[] metaTags;

    UInt16       unknown02;

    UInt32       lightCount;
    UInt32       doorCount;
    UInt32       deviceCount;
    UInt32       unknownCount02;
    UInt32       solidsCount;
    #if version >= 13
    UInt32       unknownCount03;
    #endif
    #if version >= 18
    UInt32       triangleCount;
    #endif

    UInt16                     nBlockCounts;
    EPBBlockCount[nBlockCount] blockCounts;

    #if version > 8
    UInt8        deviceGroupVersion;
    UInt16       nDeviceGroups;
    EPBDeviceGroup[nDeviceGroups] deviceGroups;
    #endif

    #if version >= 23
    UInt32 dataLength;
    UInt16 dataFlags; //Zipped = 0x0100
    UInt8[dataLength]   data; // If dataFlags.Zipped is set, the data is zipped
    #else
    UInt8[rest]   zippedData; // PKZipped block, In versions < 22, replace the two zero bytes before 0x03, 0x04, ... with 'PK' (0x50, 0x4b) before unzipping
    #endif

    #if version >= 23
    UInt32 matricesLength; // Number of bytes for data matrices following the next unknown.
    UInt16 matrixUnknown01;
    EPBTypedMatrix[] matrices;
    #endif

    #if buildVersion < 2197
    UInt8[rest] unknown; // Probably erroneous stuff caused by a bug. Some data is repeated from the data block above.
    #endif
}
EPBString
{
    UInt8[] len; // As long as top bit is set, interpret the next byte as part of the length indicator - shift and add.
    UInt8[] utf8Characters; // len number of bytes worth of UTF8 character data
}
EPBBlockCount
{
    #if version < 13
    UInt32 blockType;
    #else
    UInt16 blockType;
    #endif
    UInt32 count;     // Total number of blocks of this type in the blueprint
}
EPBDeviceGroup
{
    EPBString name;
    #if deviceGroupVersion >= 5
    UInt8     deviceGroupUnknown03;
    #endif

    UInt8     deviceGroupUnknown01;

    #if deviceGroupVersion >= 4
    UInt8     shortcut; // Index of the custom switch in the main control panel or 0xff for "none"
    #endif

    UInt16    nDevices;
    EPBDeviceGroupEntry[nDevices] entries;
}
EPBDeviceGroupEntry
{
    #if version >= 13
    EPBBlockPos pos;
    #else
    UInt32 x;
    UInt32 y;
    UInt32 z;
    #endif

    EPBString name;
}
EPBBlockPos
{
    UInt32 u1     : 4, // uuuu---- -------- -------- --------
           x      : 8, // ----xxxx xxxx---- -------- --------
           y      : 8, // -------- ----yyyy yyyy---- --------
           u2     : 4, // -------- -------- ----uuuu --------
           z      : 8; // -------- -------- -------- zzzzzzzz
}
EPBTypedMatrix
{
    UInt16 id; // Identifier of the matrix. Seen values: 0=Filler, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21
    EPBMatrix<bool> matrix; // So far I have only seen matrices without additional data here
}

To see the details of the model, the zippedData block must be unzipped. In version < 22, the first two zero bytes must be replaced with 'PK' (0x50, 0x4b) before running the PK unzip algorithm. The resulting structure is close to something like this:

EPBBlocks
{
    #if version <= 4
    EPBlock[depth, height, width] blocks;
    #else
    EPBMatrix<EPBBlock> blocks;
    #endif

    #if version > 10
    EPBMatrix<EPBDamageState> damageStateMatrix;
    #endif

    UInt8 nUnknown02; // If 0, replace with width * height * depth
    UInt8[nUnknown02] unknown02;

    #if version >= 8
    EPBMatrix<EPBColour> colours;
    #elseif version > 4
    EPBRawMatrix<UInt8> colours; //TODO: Check the format of this
    #endif

    #if version >= 8
    EPBMatrix<EPBTexture> textures;
    #elseif version > 4
    EPBRawMatrix<UInt8> textures; //TODO: Check the format of this
    #endif

    #if version >= 20
    EPBMatrix<EPBTextureFlip> textureFlips;
    #endif

    #if version >= 8
    EPBMatrix<EPBSymbol> symbols;
    #endif

    #if version >= 8
    EPBMatrix<EPBSymbolRotation> symbolRotations;
    #endif

    #if version > 10
    UInt16 nBlockTags;
    EPBBlockPosTag[nBlockTags] blockTags; // Specifies additional information per block using tags.
    #endif

    #if version > 10
    UInt16 nLockCodes;
    EPBLockCode[nLockCodes] lockCodes;
    #endif

    #if version > 13
    UInt16 nSignalSources;
    EPBSignalSource[nSignalSources] signalSources;
    #endif

    #if version > 13
    UInt16 nSignalTargetMappings;
    EPBSignalTargetMapping[nSignalTargets] signalTargetMappings;
    #endif

    #if version > 16
    UInt16 nSignalOperators;
    EPBSignalOperator[nSignalOperators] signalOperators;
    #endif

    #if version > 14
    UInt16 nCustomNames; // Number of names assigned to the "Custom" switches in the main control panel
    EPBString[nCustomNames] customNames;
    #endif

    #if version > 18
    UInt16 nUnknown08;
    EPBUnknown08[nUnknown08] unknown08;
    #endif

    #if version > 20
    UInt8 nCustomPalettes;
    EPBPalette[nCustomPalettes] customPalettes;
    #endif
}

EPBRawMatrix<T>
{
    UInt32                        matrixSize; // width * height * depth
    UInt8[matrixSize * sizeof(T)] matrix;
}

EPBMatrix<T>
{
    UInt32            matrixSize; // width * height * depth / 8
    UInt8[matrixSize] matrix; //One bit per possible position in the voxel space, if true the data for this position follows
    T[x] data; // x = the total number of bits set to 1 in the matrix
}
EPBMatrix<EPBBlock>
{
    UInt32            matrixSize;
    UInt8[matrixSize] matrix;
    EPBBlock[x]       blocks;
}
EPBMatrix<bool>
{
    UInt32            matrixSize;
    UInt8[matrixSize] matrix;
}
EPBBlock
{
    UInt32 data; // Bit mask: xxvvvvvu uuuuuuuu rrrrrbbb bbbbbbbb x and u = unknown, v = variant, r = rotation code, b = block type (This is not entirely correct)
}
EPBMatrix<EPBDamageState>
{
    UInt32            matrixSize;
    UInt8[matrixSize] matrix;
    EPBDamageState[x] damageState;
}
EPBDamageState
{
    UInt16 damage; // Number of hit points removed from the "fully healed state" of the corresponding block type
}
EPBMatrix<EPBColour>
{
    UInt32            matrixSize;
    UInt8[matrixSize] matrix;
    EPBColour[x]      colours;
}
EPBColour
{
    UInt32 data; // Five bit colour indices for six sides, padded with zero
}
EPBMatrix<EPBTexture>
{
    UInt32            matrixSize;
    UInt8[matrixSize] matrix;
    EPBTexture[x]     textures;
}
EPBTexture
{
    UInt64 data; // Six bit texture indices for six sides, padded with zero
}
EPBMatrix<EPBTextureFlip>
{
    UInt32            matrixSize;
    UInt8[matrixSize] matrix;
    EPBTextureFlip[x] textureFlips;
}
EPBTextureFlip
{
    UInt8 data; // One bit for six sides, padded with zero. If 1, the texture is flipped.
}
EPBMatrix<EPBSymbol>
{
    UInt32            matrixSize;
    UInt8[matrixSize] matrix;
    EPBSymbol[x]      symbols;
}
EPBSymbol
{
    UInt32 data; // Five bit symbol indices for six sides and a two bit symbol page index
}
EPBMatrix<EPBSymbolRotation>
{
    UInt32               matrixSize;
    UInt8[matrixSize]    matrix;
    EPBSymbolRotation[x] symbolRotations;
}
EPBSymbolRotation
{
    #if version >= 20
    UInt32 data; // Two bit symbol rotation codes for six sides, padded with zero
    #elseif version >= 8
    UInt32 data; // Five bit symbol rotation codes for six sides, padded with zero
    #endif
}
EPBBlockPosTag
{
    EPBBlockPos        pos;
    UInt8              unknown06;
    UInt16             nTags;
    EPBBlockTag[nTags] tags;
}
EPBLockCode
{
    EPBBlockPos        pos;
    UInt16             code;
}
EPBSignalSource
{
    #if version > 14
    UInt8              signalSourceUnknown01;
    UInt16             nTags;
    EPBBlockTag[nTags] tags;
    #elseif version > 13
    EPBBlockPos pos;
    EPBString   name;
    #endif
}
EPBSignalTargetMapping
{
    EPBString                 signal;
    UInt16                    nTargets;
    EPBSignalTarget[nTargets] targets;
}
EPBSignalTarget
{
    UInt8              targetUnknown01;
    UInt16             nTags;
    EPBBlockTag[nTags] tags;
}
EPBSignalOperator
{
    UInt8              signalOperatorUnknown01;
    UInt16             nTags;
    EPBBlockTag[nTags] tags;
}
EPBUnknown08
{
    UInt8[8] unknown;
}
EPBPalette
{
    UInt32 nColours;
    EPBColourRGB[nColours] colours;
}
EPBColourRGB
{
    UInt8 red;
    UInt8 green;
    UInt8 blue;
}
⚠️ **GitHub.com Fallback** ⚠️