Skip to content

Spearmint BSP Format

Zack Middleton edited this page Nov 16, 2013 · 13 revisions

This is a work-in-progress specification/idea, no software actually supports this format. Format name subject to change, like everything else.

Many of id Tech 3 based games use similar but incompatible BSP map formats. The goal of Spearmint BSP format is to allow abstracting the differences in BSP formats and allow optional new data to be added.

Compatibility (internal)

BSP files have a 4 byte identifier and 4 byte version number. Unfortunately some games (such as ET and QuakeLive have the same ident and version with incompatible BSP formats; QL has advertisements lump plus incompatible contents/surface flags?). There may be other cases that even more incompatible.

We can add additional lumps and then when loading look for new Spearmint BSP info lump; allowing any BSP to support it's original game and the Spearmint BSP format. This also allows loading any BSP that has the SBSP info lump, even if we don't know what the original game is.

Compatibility (external)

Hmm, it's probably good to support SBSP extension in a separate file. That way you could have "SBSP" patch data for a game, without say modifying all of the BSPs and redistributing them (which is probably copyright infringement and a waste of bandwidth/space).

Actually it's debatable whether internal SBSP extension is worth having at all.

Idea: Support loading SBSP from .sbsp extension. Add support for a lump to specific checksum, length and filename of legacy BSP file. If file doesn't exist or checksums/length mismatch, fallback to loading .bsp extension.

TODO: We'll need a way to know which lumps are in the legacy file and which are in the .sbsp file. I don't think there is a reason to support having lumps in more than two BSPs, so a single bit flag for each lump would work.

NOTE: I only had this idea after writing most of the article, so it doesn't reflect this idea yet.

Non-compat

For regular (non-backward compatible) BSPs, can use the same header style as id Tech BSPs, with the first lump being Spearmint BSP info lump. This will allow loading all BSPs that contain "Spearmint BSP" the same way, looking through all lumps until find SBSP info lump.

For sanity, it will have a different ident and version than existing games.

typedef struct {
int fileofs;
int filelen;
} lump_t;

typedef struct {
int ident;
int version;

lump_t lumps[unknownNumberOfLump];
} header_t;

EDIT: Per the above external compat support, the file extension could be .sbsp or .bsp.

Locate Spearmint BSP info lump (for internal compat)

BSPs do not include the number of lumps in the file. We could use ident and version to guess how many there are, which may be incorrect (ET vs QL; plus recent GtkRadiant puts extra QL lump in ET BSPs?), or we could check if all possible lumps are the Spearmint BSP info lump.

For known formats (such as loading Q3 IBSP in a Q3 based engine) we could start as end of lumps, to save some time.

Example:

int bspMinHeaderSize = sizeof ( int ) * 4; // ident, version, lump file fileofs and filelen
int SBSP_INFO_MIN_SIZE = sizeof ( int ) * 5 + 2; // must be five ints plus a null terminated string
int filelen; // set to length of BSP file

// Assume no BSP has more than 72 lumps.. we could go forever
// but I'm not sure how long it would take to find invalid lump.
// Needs testing. Reason for 72 is Value BSPs have 64 lumps.
for (i = 0; lump = lumps; i < 72; i++, lump++)
{
if ( lump->fileofs < bspMinHeaderSize || lump->filelen < 0 || lump->fileofs+lump->filelen > filelen )
break; // this is either a broken lump or we read past number of real lumps.

// TODO: pick max size.
if ( lump->filelen < SBSP_INFO_MIN_SIZE || lump->filelen > SBSP_INFO_MAX_SIZE )
continue;

// TODO: might want to check more than 8 bytes to avoid false positives?
// I wouldn't mind having a 12 character string or something,
// so could check which BSPs support Spearmint BSP format using grep...
// (This probably isn't valid C code. I haven't tried to compile it.)
if ((int)*(byte*)(header+lump->fileofs) != SBSP_IDENT)
continue;
if ((int)*(byte*)(header+lump->fileofs+sizeof(int)) != SBSP_VERSION)
continue;

// found SBSP info lump
break;
}

Issue: Call of Duty BSPs switch the lump fileofs and filelen order in the BSP. So probably need to detect BSPs with switched lumps by ident/version.

EDIT: External SBSP wouldn't read legacy BSP's header, so wouldn't have to worry about CoD BSP lump offset or len order.

Spearmint BSP info lump

The info lump acts as an identifier for existing lumps in backward compatible BSPs as well as identifying new lumps.

Header

int ident;
int version;

int numLumps;
// followed by lumps

Lumps

Lump definitions will be a flexible size. sizeof (int) * 2 + strlen(lump->filename) + 1

Lumps will be identified by filename rather than number.

Lump fileofs will be based on the overall file, allowing referencing legacy lumps in backward compatible BSPs.

int filelen;
int fileofs;
char *filename;

Lumps

The SBSP format itself can have lumps with any name that are any format. It's up to the loader to know what the names mean and what format the data is. For this reason lumps should not have generic names such as "brushsides" or "hdrlight".

Reserved lump names

(q3map2 has support for writing all of these.)

  • q3_* will be the Quake 3 / Team Arena / RTCW / ET / Elite Force format lumps.
  • rbsp_* will be used the two lumps that Raven changed from q3 format in SoF2/JK2/JA (brush sides and draw surfaces)
    • Could include both q3_* and rbsp_* brush sides and draw surfaces in the same BSP (incase loader only supports/wants one or the other).
    • Might want to create new lumps that contain only the new stuff added to rbsp? Doesn't help compat with Raven engines, it would save space in q3-compatible BSPs though. I want all maps to have brushside draw surface indexes (for cool point as surface get shader name {Spearmint's cg_drawShaderInfo}, if nothing else).
  • ql_ads will be the QuakeLive advertisements lump format.

New lumps for cross-game compatibility

bsp_external (name subject to change)

Set external bsp to load data from. All lumps after bsp_external are assumed to be in the external bsp.

TODO: Should there be support for multiple bsp_external lumps?

This could be used for referencing data in a legacy BSP or another SBSP. The header doesn't need to be read from the referenced BSP. The main SBSP file has the lump info.

legacy_bitflags (name subject to change)

Define strings for surface flags and content flags, allowing engine to identify and convert bit flags to it's own internal bits. Might get a little complicated supporting SOF2/JK2/JA material bits.

The BSP itself is likely dependent on external textures and/or game entities. It would be useful for the Spearmint Engine as it attempts to load BSPs/gamelogic for multiple games.

Not sure you could make a BSP that was fully useable in both Q3 and RTCW-MP, due to different items and weapons names etc. Could add "notRtcw" key to rtcw-mp (so vanilla rtcw-mp would just spam about some missing entity classes. (Q3 and Team Arena already have notq3 and notta entity keys.)

Clone this wiki locally