Source BSP File Format/Game-Specific

From Valve Developer Community
Jump to: navigation, search

This page documents some game-specific differences in the BSP file format, compared to the format used for the traditional engine branches.

Alien Swarm / Portal 2

Both games use a slightly different dbrushside_t structure:

struct dbrushside_t
	unsigned short		planenum;	// facing out of the leaf
	short			texinfo;
	short			dispinfo;	// displacement info (BSPVERSION 7)
	byte			bevel;		// is the side a bevel plane? (BSPVERSION 7)
	byte			thin;		// is a thin side?

Dark Messiah of Might and Magic

Dark Messiah maps have a strange version number, it is possibly split into two shorts:

struct dheader_dm_t
	int	ident;                // BSP file identifier
	short	version;              // BSP file version (20)
	short	unknown;              // always 4?
	lump_t	lumps[HEADER_LUMPS];  // lump directory array
	int	mapRevision;          // the map's revision (iteration, version) number

The game lump header has an extra unknown integer:

struct dgamelumpheader_t
	int		lumpCount;	// number of game lumps
	int		unknown;
	dgamelump_t	gamelump[lumpCount];

The dgamelump_t structure also adds an integer value for unknown purposes.

struct dgamelump_dm_t
	int		id;		// gamelump ID
	unsigned short	flags;		// flags
	unsigned short	version;	// gamelump version
	int		fileofs;	// offset to this gamelump
	int		filelen;	// length
	int		unknown;

Static props use a modified StaticPropLumpV6_t structure, which contains additional 72 bytes for unknown purposes at the end:

struct StaticPropLumpV6_dm_t
	Vector			m_Origin;
	QAngle			m_Angles;
	unsigned short		m_PropType;
	unsigned short		m_FirstLeaf;
	unsigned short		m_LeafCount;
	unsigned char		m_Solid;
	unsigned char		m_Flags;
	int			m_Skin;
	float			m_FadeMinDist;
	float			m_FadeMaxDist;
	Vector			m_LightingOrigin;
	float			m_flForcedFadeScale;
	unsigned short		m_nMinDXLevel;
	unsigned short		m_nMaxDXLevel;
	byte			m_unknown[72];

Furthermore, the DirectX levels seem to have different ranges.

Models using dmodel_t have one additional integer field between origin and headnode:

struct dmodel_dm_t
	Vector	mins, maxs;		// bounding box
	Vector	origin;			// for sounds or lights
	int	unknown;		// always 0?
	int	headnode;		// index into node array
	int	firstface, numfaces;	// index into face array

Texinfos using texinfo_t have additional 24 bytes between lightmapVecs and flags:

struct texinfo_dm_t
	float	textureVecs[2][4];	// [s/t][xyz offset]
	float	lightmapVecs[2][4];	// [s/t][xyz offset] - length is in units of texels/area
	byte	unknown[24];
	int	flags;			// miptex flags	overrides
	int	texdata;		// Pointer to texture name, size, etc.

Left 4 Dead 2 / Contagion

The fields in lump_t have a different order:

struct lump_t
	int	version;	// lump format version
	int	fileofs;	// offset into file (bytes)
	int	filelen;	// length of lump(bytes)
	char	fourCC[4];	// lump ident code

The Ship / Bloody Good Time

Static props use a modified StaticPropLumpV5_t structure, which contains null-padded target name strings.

struct StaticPropLumpV5_ship_t
	Vector		m_Origin;
	QAngle		m_Angles;
	unsigned short	m_PropType;
	unsigned short	m_FirstLeaf;
	unsigned short	m_LeafCount;
	unsigned char	m_Solid;
	unsigned char	m_Flags;
	int		m_Skin;
	float		m_FadeMinDist;
	float		m_FadeMaxDist;
	Vector		m_LightingOrigin;
	float		m_flForcedFadeScale;
	char		m_TargetName[128];

Vampire The Masquerade - Bloodlines

Version 17 BSP files contain a substantially modified dface_t structure. The known elements are:

// MAXLIGHTMAPS changed from 4 to 8 
// 4 lightstyle for day + 4 for night = 8 ( nightime lightmapping system )

struct dface_bsp17_t
	colorRGBExp32	m_AvgLightColor[MAXLIGHTMAPS]; // For computing lighting information
	unsigned short	planenum;
	byte		side;	// faces opposite to the node's plane direction
	byte		onNode; // 1 of on node, 0 if in leaf
	int		firstedge;		// we must support > 64k edges
	short		numedges;	
	short		texinfo;
	short		dispinfo;
	short		surfaceFogVolumeID;
	byte		styles[MAXLIGHTMAPS];	// lighting info
	byte		day[MAXLIGHTMAPS];		// Nightime lightmapping system
	byte		night[MAXLIGHTMAPS];		// Nightime lightmapping system
	int		lightofs;		// start of [numstyles*surfsize] samples
	float		area;
	int		m_LightmapTextureMinsInLuxels[2];
	int		m_LightmapTextureSizeInLuxels[2];
	int		origFace;    // reference the original face this face was derived from
	unsigned int	smoothingGroups;

The extra data seems to be related to lighting of the face, and makes the length of the structure 104 bytes per face. Both the face lump and the original face lump in version 17 files use this structure.


Vindictus generally uses a lot more integers in place of shorts for its map data structures.

The game uses a different dgamelump_t format:

struct dgamelump_t
	int		id;		// gamelump ID
	int		flags;		// flags
	int		version;	// gamelump version
	int		fileofs;	// offset to this gamelump
	int		filelen;	// length

Nodes and Leaves are also different, mainly changing to use ints instead of shorts:

struct dnode_t
	int		planenum;	// index into plane array
	int		children[2];	// negative numbers are -(leafs + 1), not nodes
	int		mins[3];	// for frustum culling
	int		maxs[3];
	int		firstface;	// index into face array
	int		numfaces;	// counting both sides
	int		unknown;	// seems to always be 0

The Leaves structure, among other things, does away with the area field, and flags has its own integer field.

struct dleaf_t
	int			contents;		// OR of all brushes (not needed?)
	int			cluster;		// cluster this leaf is in
	int			flags;			// flags
	int			mins[3];		// for frustum culling
	int			maxs[3];
	unsigned int		firstleafface;		// index into leaffaces
	unsigned int		numleaffaces;
	unsigned int		firstleafbrush;		// index into leafbrushes
	unsigned int		numleafbrushes;
	int			leafWaterDataID;	// -1 for not in water

Faces use one of two different structures, 72 bytes or 76 bytes, depending on the lump version in the file header. Once again, the biggest difference is changing from shorts to ints:

struct dface_t
	unsigned int	planenum;		// the plane number
	byte		side;			// faces opposite to the node's plane direction
	byte		onNode;			// 1 of on node, 0 if in leaf
	short		unknown0;		// always 0?
	int		firstedge;		// index into surfedges
	int		numedges;		// number of surfedges
	int		texinfo;		// texture info
	int		dispinfo;		// displacement info
	int		surfaceFogVolumeID;	// ?
	// Comment this unknown for v1
	int		unknown1;		// v2 Faces only. Always negative?
	byte		styles[4];		// switchable lighting info
	int		lightofs;		// offset into lightmap lump
	float		area;			// face area in units^2
	int		LightmapTextureMinsInLuxels[2];	// texture lighting info
	int		LightmapTextureSizeInLuxels[2];	// texture lighting info
	unsigned int	origFace;		// original face this was split from
	unsigned int	numPrims;		// primitives
	unsigned int	firstPrimID;
	unsigned int	smoothingGroups;	// lightmap smoothing group

Brush sides have an identical format, except using ints instead of shorts:

struct dbrushside_t
	unsigned int	planenum;
	int		texinfo;
	int		dispinfo;
	int		bevel;

Edges are also identical except for using ints:

struct dedge_t
	unsigned int	v[2];

With 232 bytes, the displacement info struct of Vindictus is notably bigger than the conventional 176 byte struct in other Source engine games. This is mostly because of CDispNeighbor and CDispCornerNeighbors using ints instead of shorts:

struct ddispinfo_t
	Vector			startPosition;		// start position used for orientation
	int			DispVertStart;		// Index into LUMP_DISP_VERTS.
	int			DispTriStart;		// Index into LUMP_DISP_TRIS.
	int			power;			// power - indicates size of surface (2^power	1)
	float			smoothingAngle;		// lighting smoothing angle
	int			unknown0;
	int			contents;		// surface contents
	unsigned int		MapFace;		// Which map face this displacement comes from.
	int			LightmapAlphaStart;	// Index into ddisplightmapalpha.
	int			LightmapSamplePositionStart;	// Index into LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS.
	CDispNeighbor		EdgeNeighbors[4];	// Indexed by NEIGHBOREDGE_ defines.
	CDispCornerNeighbors	CornerNeighbors[4];	// Indexed by CORNER_ defines.
	unsigned int		AllowedVerts[10];	// active verticies

Vindictus' Static Props v5 is identical to vanilla Source engine. However, Vindictus maps may instead use a v6 Static Props lump that is different from the normal v6 Props. The StaticPropLump_t struct remains the same (like v5 Static Props) but there is an additional struct inserted between the StaticPropLeafLump_t array and the StaticPropLump_t array, perhaps providing additional prop scaling information. As with the other arrays, this struct begins with an int declaring how many elements there are. There seems to always be the same amount of this struct as there are StaticPropLump_t.

struct StaticPropScales_t
	int	staticProp;	// Index into the StaticPropLump_t array
	Vector	scale;		// Speculative, is this really scaling?

The overlay structure also follows the change from short to int:

struct doverlay_t
	int		Id;
	int		TexInfo;
	unsigned int	FaceCountAndRenderOrder;
	float		U[2];
	float		V[2];
	Vector		UVPoints[4];
	Vector		Origin;
	Vector		BasisNormal;

So does the areaportal structure:

struct dareaportal_t
	unsigned int	portalKey;		// binds the area portal to a func_areaportal entity with the same portalnumber key
	unsigned int	otherarea;		// The area this portal looks into.
	unsigned int	firstClipPortalVert;	// Portal geometry.
	unsigned int	clipPortalVerts;
	int		planenum;

The LeafFace and LeafBrush lumps also use unsigned ints rather than unsigned shorts.


Titanfall uses a heavily modified Source engine, which also alters the file format for the maps.

The header uses rBSP (Respawn BSP) instead of VBSP as identifier and HEADER_LUMPS was risen from 64 to 128, doubling the maximum amount of lumps. As of the closed beta, the maps use 29 as BSP version.

struct dheader_t
	int	ident;                // BSP file identifier
	int	version;              // BSP file version
	int	mapRevision;          // the map's revision (iteration, version) number
	int     unknown;              // always 127?
	lump_t	lumps[HEADER_LUMPS];  // lump directory array

Titanfall maps also make extensive use of the lump file system. Unlike to other Source games, Titanfall lump files are headerless and use their lump ID instead of a continuous number in the file name, which is formatted as following: <BSP file name>.<ID>.bsp_lump.

Note that the lump ID is a four digit hex string with leading zeros. A lump file with the name mp_angel_city.bsp.0044.bsp_lump therefore contains the data for lump 0x44 (68 decimal) for the map mp_angel_city.bsp. These lump files probably also override the lumps defined inside the BSP file like in other Source games.

The entities are stored in different files: inside the BSP file, in the entity lump file and in five .ent files. The entity files start with the header ENTITIES01 and may otherwise be empty.

The lumps in Titanfall maps have changed drastically. Most lumps mandatory for other Source games are empty and unused, such as LUMP_NODES, LUMP_FACES, LUMP_EDGES, LUMP_BRUSHES and LUMP_BRUSHSIDES, so the geometry is now most likely stored in the new lumps above ID 64.