-
-
Notifications
You must be signed in to change notification settings - Fork 27
Spearmint BSP Format
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 identifying the data in BSP files and allow optional new data to be added.
I realized I'm reinventing the Doom WAD format. WADs have list of lumps with offsets and size at end of file. Seems like it would be easier to write. The other difference between SBSP and WAD formats is lump filename length (SBSP, variable; WAD, fixed length of 8 characters).
BSP files are specialized WAD files for fast loading (no string reading to find lumps, no number of lumps in header because it's not needed), but I want to generalize them like WADs.
This leaves me thinking about just replacing the BSP/WAD format with a (zipped) directory containing files. (A separate zip, not zip in zip.) BSP/WAD is just a directory really. BSP has the benefit of reading into memory all at once, but my generalized way would not (don't know if all lumps would be used, so it would be a waste of memory).
Doom 3 uses multiple files instead of a BSP file (source). I think I read that Rage and Doom 3 BFG Edition have each level in it's own archive with all of it's textures, models, and whatnot.
This leads to the idea of requiring each map to be a separate zip file (with a special filename "map-*.pk3" and possibly different extension for legacy map compatibility). Allow textures, models, shaders, and sounds to be added; but only accessible when the map is loaded. We need to be able to get levelshot and some info (name, gametypes supported, etc) even when if the map isn't loaded.
The reason for only one map per-zip is it makes it easier to decide if "map zip" has current map (look at filename), which will be used to decide what parts of the zip will be accessible. Plus might be better for joining servers (only download current map). I'm pretty sure one map per-zip is already fairly common for games.
To reduce duplicate textures, shaders, etc; add support for content packages, zip files that only loaded when current map references them. They will only be able to add new content, not replace things, so can use multiple content packages without conflict. The map compiler could create the list of required content packages automatically.
(Pk3 dependencies has been brought up by Unvanquished developers to be used for map dependencies.)
Map zip structure
map/MAPNAME/*
Content pack zip structure
res/PACKNAME/shaders/*.shader
res/PACKNAME/* (textures, sounds, models, and whatever else)
May want to actually virtually mount the zips at "map/MAPNAME/" or "res/PACKNAME/" to be sure files are located there. So that "shader/.shader" in zip would be "res/PACKNAME/shaders/.shader" in virtual file system.
*.pk3dir
directories could be used for development.
It will have a different ident and version than existing games. And include number of lumps!
int ident;
int version;
int numLumps;
// followed by lumps
Lump definitions will be a flexible size. sizeof (int) * 2 + strlen(lump->name) + 1
Lumps will be identified by name rather than number.
int filelen;
int fileofs;
char *name;
Support loading .sbsp files that contain it's own lump list (that is labeled) and support referencing data for lumps internally or from external files (read: legacy BSP files). That way you could have "SBSP" patch data for a game, without having to modifying all of the BSPs and redistributing them (which is probably copyright infringement and a waste of bandwidth/space).
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".
(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.
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? Might be nice to have a single file containing legacy_bitflags for each game?
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.
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, should be possible.
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.)
##Load concept
#define LUMP_BLAH 0
#define LUMP_RBSP_BLAH 1
struct {
string name;
pointer lump;
int file; // external bsp file index
} lumpinfolist = {
{ "q3_blah", NULL, -1 },
{ "rbsp_blah", NULL, -1 },
};
open file
check ident and version
for lumpinfo in lumpinfolist
lumpinfo->lump = NULL;
lumpinfo->file = -1;
file = -1;
for lump in lumplist
{
if lump->name == "bsp_external"
file++;
externalBsp[file].name = readString
externalBsp[file].length = readInt
externalBsp[file].checksum = readInt
continue;
for lumpinfo in lumpinfolist
if lumpinfo->name == lump->name
lumpinfo->lump = lump;
lumpinfo->file = file;
break;
if not found lumpinfo for lump and cvar for showing unknown lumps is true
Com_Printf("Unknown lump '%s' in '%s'\n", lump->name, bspname);
}
// Now we know which lumps are referenced and what files they're in.
// So check if have enough ones to load a level.
// Convert lumps into the format we want (change contents and surface bit flags, etc).