Q2BSP + QBSP (#313)

* Add QBSP - the BSP2-esque variant to Q2BSP - to bspinfo, and all of its accompanying structures.

* pass around ident, since Q2 needs it - admittedly it's a bit ugly, but it works for now.
conversion for QBSP

* Fix light

* _qbsp_ -> _qbism_

* Introduced bspversion_t, a struct that holds pertinent information about different BSP versions and also acts as a tagged pointer type for direct comparisons. This makes a lot of code paths simpler. I'm not entirely set on the wordings or usages yet, and maybe we can stuff boolean flags inside of them for different behaviors (for instance Q2, QBism and HL would have the "colored lightmap" boolean set to true, which replaces the check-for-all-three in the lightmapper)
Swapped arguments to ConvertBSPFormat to have the conversion target last instead of first
Finished rename of qbsp -> qbism
Tested:
 - bspinfo on various BSPs I had laying around (Q1, Q2, Qbism)
 - bsputil converting between Q2 and Qbism, and that they loaded in engine/roundtripped properly
Not tested:
 - vis/rad on anything major (I still can't run rad due to embree being weird)
 - bsputil conversion of Q1-esque maps
This commit is contained in:
Jonathan 2021-08-23 11:56:33 -04:00 committed by GitHub
parent ff7f8a1542
commit eaa86c71c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1388 additions and 714 deletions

View File

@ -541,7 +541,7 @@ main(int argc, char **argv)
LoadBSPFile(source, &bspdata);
ConvertBSPFormat(GENERIC_BSP, &bspdata);
ConvertBSPFormat(&bspdata, &bspver_generic);
for (i = 0; i < argc - 1; i++) {
if (!strcmp(argv[i], "--compare")) {
@ -556,7 +556,7 @@ main(int argc, char **argv)
strcpy(refbspname, argv[i]);
DefaultExtension(refbspname, ".bsp");
LoadBSPFile(refbspname, &refbspdata);
ConvertBSPFormat(GENERIC_BSP, &refbspdata);
ConvertBSPFormat(&refbspdata, &bspver_generic);
printf("comparing reference bsp %s with test bsp %s\n", refbspname, source);
@ -570,20 +570,20 @@ main(int argc, char **argv)
Error("--convert requires an argument");
}
int fmt;
if (!strcmp(argv[i], "bsp29")) {
fmt = BSPVERSION;
} else if (!strcmp(argv[i], "bsp2")) {
fmt = BSP2VERSION;
} else if (!strcmp(argv[i], "bsp2rmq")) {
fmt = BSP2RMQVERSION;
} else if (!strcmp(argv[i], "q2bsp")) {
fmt = Q2_BSPVERSION;
} else {
const bspversion_t *fmt = nullptr;
for (const bspversion_t *bspver : bspversions) {
if (!strcmp(argv[i], bspver->short_name)) {
fmt = bspver;
break;
}
}
if (!fmt) {
Error("Unsupported format %s", argv[i]);
}
ConvertBSPFormat(fmt, &bspdata);
ConvertBSPFormat(&bspdata, fmt);
StripExtension(source);
strcat(source, "-");
@ -671,7 +671,7 @@ main(int argc, char **argv)
bsp2_dface_t* face = BSP_GetFace(bsp, fnum);
face->texinfo = texinfonum;
ConvertBSPFormat(bspdata.loadversion, &bspdata);
ConvertBSPFormat(&bspdata, bspdata.loadversion);
// Overwrite source bsp!
WriteBSPFile(source, &bspdata);

File diff suppressed because it is too large Load Diff

View File

@ -188,7 +188,7 @@ bool Face_IsLightmapped(const mbsp_t *bsp, const bsp2_dface_t *face)
if (texinfo == nullptr)
return false;
if (bsp->loadversion == Q2_BSPVERSION) {
if (bsp->loadversion == &bspver_q2 || bsp->loadversion == &bspver_qbism) {
if (texinfo->flags & (Q2_SURF_WARP|Q2_SURF_SKY|Q2_SURF_NODRAW)) { //mxd. +Q2_SURF_NODRAW
return false;
}
@ -223,7 +223,7 @@ TextureName_Contents(const char *texname)
bool //mxd
Contents_IsTranslucent(const mbsp_t *bsp, const int contents)
{
if (bsp->loadversion == Q2_BSPVERSION)
if (bsp->loadversion == &bspver_q2 || bsp->loadversion == &bspver_qbism)
return (contents & Q2_SURF_TRANSLUCENT) && ((contents & Q2_SURF_TRANSLUCENT) != Q2_SURF_TRANSLUCENT); // Don't count KMQ2 fence flags combo as translucent
else
return contents == CONTENTS_WATER || contents == CONTENTS_LAVA || contents == CONTENTS_SLIME;
@ -238,7 +238,7 @@ Face_IsTranslucent(const mbsp_t *bsp, const bsp2_dface_t *face)
int //mxd. Returns CONTENTS_ value for Q1, Q2_SURF_ bitflags for Q2...
Face_Contents(const mbsp_t *bsp, const bsp2_dface_t *face)
{
if (bsp->loadversion == Q2_BSPVERSION) {
if (bsp->loadversion == &bspver_q2 || bsp->loadversion == &bspver_qbism) {
const gtexinfo_t *info = Face_Texinfo(bsp, face);
return info->flags;
} else {
@ -282,7 +282,12 @@ static bool Light_PointInSolid_r(const mbsp_t *bsp, const int nodenum, const vec
if (nodenum < 0) {
const mleaf_t *leaf = BSP_GetLeafFromNodeNum(bsp, nodenum);
return (bsp->loadversion == Q2_BSPVERSION ? leaf->contents & Q2_CONTENTS_SOLID : (leaf->contents == CONTENTS_SOLID || leaf->contents == CONTENTS_SKY)); //mxd
//mxd
if (bsp->loadversion == &bspver_q2 || bsp->loadversion == &bspver_qbism) {
return leaf->contents & Q2_CONTENTS_SOLID;
}
return (leaf->contents == CONTENTS_SOLID || leaf->contents == CONTENTS_SKY);
}
const bsp2_dnode_t *node = &bsp->dnodes[nodenum];

View File

@ -52,17 +52,41 @@
#define MAX_ENT_KEY 32
#define MAX_ENT_VALUE 1024
struct bspversion_t
{
/* identifier value, the first int32_t in the header */
int32_t ident;
/* version value, if supported; use NO_VERSION if a version is not required */
int32_t version;
/* short name used for command line args, etc */
const char *short_name;
/* full display name for printing */
const char *name;
};
#define NO_VERSION -1
#define BSPVERSION 29
#define BSP2RMQVERSION (('B' << 24) | ('S' << 16) | ('P' << 8) | '2')
#define BSP2VERSION ('B' | ('S' << 8) | ('P' << 16) | ('2' << 24))
#define BSPHLVERSION 30 //24bit lighting, and private palettes in the textures lump.
#define Q2_BSPIDENT (('P'<<24)+('S'<<16)+('B'<<8)+'I')
#define Q2_BSPVERSION 38
#define Q2_QBISMIDENT (('P'<<24)+('S'<<16)+('B'<<8)+'Q')
/* Not an actual file format, but the mbsp_t struct */
/* TODO: Should probably separate the type tag for bspdata_t from the file
version numbers */
#define GENERIC_BSP 99
extern const bspversion_t bspver_generic, bspver_q1, bspver_h2, bspver_bsp2, bspver_bsp2rmq, bspver_hl, bspver_q2, bspver_qbism;
/* table of supported versions */
constexpr const bspversion_t *const bspversions[] = {
&bspver_generic,
&bspver_q1,
&bspver_h2,
&bspver_bsp2,
&bspver_bsp2rmq,
&bspver_hl,
&bspver_q2,
&bspver_qbism
};
typedef struct {
int32_t fileofs;
@ -290,6 +314,8 @@ struct q2_dnode_t {
uint16_t numfaces; // counting both sides
};
using q2_dnode_qbism_t = bsp2_dnode_t;
/*
* Note that children are interpreted as unsigned values now, so that we can
* handle > 32k clipnodes. Values > 0xFFF0 can be assumed to be CONTENTS
@ -387,6 +413,8 @@ typedef struct {
uint32_t v[2]; /* vertex numbers */
} bsp2_dedge_t;
using q2_dedge_qbism_t = bsp2_dedge_t;
#define MAXLIGHTMAPS 4
typedef struct {
int16_t planenum;
@ -424,6 +452,18 @@ typedef struct {
int32_t lightofs; // start of [numstyles*surfsize] samples
} q2_dface_t;
typedef struct {
uint32_t planenum; // NOTE: only difference from bsp2_dface_t
int32_t side;
int32_t firstedge; // we must support > 64k edges
int32_t numedges;
int32_t texinfo;
// lighting info
uint8_t styles[MAXLIGHTMAPS];
int32_t lightofs; // start of [numstyles*surfsize] samples
} q2_dface_qbism_t;
/* Ambient Sounds */
#define AMBIENT_WATER 0
#define AMBIENT_SKY 1
@ -481,6 +521,22 @@ typedef struct {
uint16_t numleafbrushes;
} q2_dleaf_t;
typedef struct {
int32_t contents; // OR of all brushes (not needed?)
int32_t cluster;
int32_t area;
float mins[3]; // for frustum culling
float maxs[3];
uint32_t firstleafface;
uint32_t numleaffaces;
uint32_t firstleafbrush;
uint32_t numleafbrushes;
} q2_dleaf_qbism_t;
typedef struct {
// bsp2_dleaf_t
int32_t contents;
@ -492,10 +548,10 @@ typedef struct {
uint8_t ambient_level[NUM_AMBIENTS];
// q2 extras
int16_t cluster;
int16_t area;
uint16_t firstleafbrush;
uint16_t numleafbrushes;
int32_t cluster;
int32_t area;
uint32_t firstleafbrush;
uint32_t numleafbrushes;
} mleaf_t;
typedef struct {
@ -503,6 +559,11 @@ typedef struct {
int16_t texinfo;
} dbrushside_t;
typedef struct {
uint32_t planenum; // facing out of the leaf
int32_t texinfo;
} q2_dbrushside_qbism_t;
typedef struct {
int32_t firstside;
int32_t numsides;
@ -742,8 +803,66 @@ typedef struct {
uint8_t dpop[256];
} q2bsp_t;
typedef struct {
int nummodels;
q2_dmodel_t *dmodels;
int visdatasize;
dvis_t *dvis;
int lightdatasize;
uint8_t *dlightdata;
int entdatasize;
char *dentdata;
int numleafs;
q2_dleaf_qbism_t *dleafs;
int numplanes;
dplane_t *dplanes;
int numvertexes;
dvertex_t *dvertexes;
int numnodes;
q2_dnode_qbism_t *dnodes;
int numtexinfo;
q2_texinfo_t *texinfo;
int numfaces;
q2_dface_qbism_t *dfaces;
int numedges;
q2_dedge_qbism_t *dedges;
int numleaffaces;
uint32_t *dleaffaces;
int numleafbrushes;
uint32_t *dleafbrushes;
int numsurfedges;
int32_t *dsurfedges;
int numareas;
darea_t *dareas;
int numareaportals;
dareaportal_t *dareaportals;
int numbrushes;
dbrush_t *dbrushes;
int numbrushsides;
q2_dbrushside_qbism_t *dbrushsides;
uint8_t dpop[256];
} q2bsp_qbism_t;
struct mbsp_t {
int32_t loadversion;
const bspversion_t *loadversion;
int nummodels;
dmodelh2_t *dmodels;
@ -792,7 +911,7 @@ struct mbsp_t {
uint32_t *dleaffaces;
int numleafbrushes;
uint16_t *dleafbrushes;
uint32_t *dleafbrushes;
int numsurfedges;
int32_t *dsurfedges;
@ -807,7 +926,7 @@ struct mbsp_t {
dbrush_t *dbrushes;
int numbrushsides;
dbrushside_t *dbrushsides;
q2_dbrushside_qbism_t *dbrushsides;
uint8_t dpop[256];
}; // "generic" bsp - superset of all other supported types
@ -824,8 +943,7 @@ typedef struct {
} q2_dheader_t;
typedef struct {
int32_t loadversion;
int32_t version;
const bspversion_t *version, *loadversion;
int hullcount;
struct {
@ -834,6 +952,7 @@ typedef struct {
bsp2_t bsp2;
q2bsp_t q2bsp;
mbsp_t mbsp;
q2bsp_qbism_t q2bsp_qbism;
} data;
bspxentry_t *bspxentries;
@ -842,7 +961,7 @@ typedef struct {
void LoadBSPFile(char *filename, bspdata_t *bspdata); //returns the filename as contained inside a bsp
void WriteBSPFile(const char *filename, bspdata_t *bspdata);
void PrintBSPFileSizes(const bspdata_t *bspdata);
void ConvertBSPFormat(int32_t version, bspdata_t *bspdata);
void ConvertBSPFormat(bspdata_t *bspdata, const bspversion_t *to_version);
void BSPX_AddLump(bspdata_t *bspdata, const char *xname, const void *xdata, size_t xsize);
const void *BSPX_GetLump(bspdata_t *bspdata, const char *xname, size_t *xsize);

View File

@ -1579,7 +1579,7 @@ static void MakeSurfaceLights(const mbsp_t *bsp)
for (int i = 0; i < bsp->numleafs; i++) {
const mleaf_t *leaf = bsp->dleafs + i;
const qboolean underwater = (bsp->loadversion == Q2_BSPVERSION ? leaf->contents & Q2_CONTENTS_LIQUID : leaf->contents != CONTENTS_EMPTY); //mxd
const qboolean underwater = ((bsp->loadversion == &bspver_q2 || bsp->loadversion == &bspver_qbism) ? leaf->contents & Q2_CONTENTS_LIQUID : leaf->contents != CONTENTS_EMPTY); //mxd
for (int k = 0; k < leaf->nummarksurfaces; k++) {
const int facenum = bsp->dleaffaces[leaf->firstmarksurface + k];

View File

@ -44,7 +44,7 @@ void // WHO TOUCHED MY PALET?
LoadPalette(bspdata_t *bspdata)
{
// Load Quake 2 palette
if (bspdata->loadversion == Q2_BSPVERSION) {
if (bspdata->loadversion == &bspver_q2 || bspdata->loadversion == &bspver_qbism) {
uint8_t *palette;
char path[1024];
char colormap[] = "pics/colormap.pcx";
@ -66,7 +66,7 @@ LoadPalette(bspdata_t *bspdata)
for (int i = 0; i < 768; i++)
thepalette[i] = palette[i];
} else if (bspdata->hullcount == MAX_MAP_HULLS_H2) { // Gross hacks
} else if (bspdata->loadversion == &bspver_h2) {
// Copy Hexen 2 palette
for (int i = 0; i < 768; i++)
thepalette[i] = hexen2palette[i];
@ -721,7 +721,7 @@ void // Expects correct palette and game/mod paths to be set
LoadOrConvertTextures(mbsp_t *bsp)
{
// Load or convert textures...
if (bsp->loadversion == Q2_BSPVERSION)
if (bsp->loadversion == &bspver_q2 || bsp->loadversion == &bspver_qbism)
LoadTextures(bsp);
else if (bsp->texdatasize > 0)
ConvertTextures(bsp);

View File

@ -439,7 +439,7 @@ LightWorld(bspdata_t *bspdata, qboolean forcedscale)
CalcualateVertexNormals(bsp);
const qboolean bouncerequired = cfg_static.bounce.boolValue() && (debugmode == debugmode_none || debugmode == debugmode_bounce || debugmode == debugmode_bouncelights); //mxd
const qboolean isQuake2map = (bsp->loadversion == Q2_BSPVERSION); //mxd
const qboolean isQuake2map = (bsp->loadversion == &bspver_q2 || bsp->loadversion == &bspver_qbism); //mxd
if (bouncerequired || isQuake2map) {
MakeTextureColors(bsp);
@ -470,7 +470,7 @@ LightWorld(bspdata_t *bspdata, qboolean forcedscale)
// Transfer greyscale lightmap (or color lightmap for Q2/HL) to the bsp and update lightdatasize
if (!litonly) {
free(bsp->dlightdata);
if (bsp->loadversion == Q2_BSPVERSION || bsp->loadversion == BSPHLVERSION) {
if (isQuake2map || bsp->loadversion == &bspver_hl) {
bsp->lightdatasize = lit_file_p;
bsp->dlightdata = (uint8_t *)malloc(bsp->lightdatasize);
memcpy(bsp->dlightdata, lit_filebase, bsp->lightdatasize);
@ -545,9 +545,9 @@ LoadExtendedTexinfoFlags(const char *sourcefilename, const mbsp_t *bsp)
static const char* //mxd
GetBaseDirName(bspdata_t *bspdata)
{
if (bspdata->loadversion == Q2_BSPVERSION)
if (bspdata->loadversion == &bspver_q2 || bspdata->loadversion == &bspver_qbism)
return "BASEQ2";
if (bspdata->hullcount == MAX_MAP_HULLS_H2)
if (bspdata->loadversion == &bspver_h2)
return "DATA1";
return "ID1";
}
@ -951,7 +951,7 @@ light_main(int argc, const char **argv)
{
bspdata_t bspdata;
mbsp_t *const bsp = &bspdata.data.mbsp;
int32_t loadversion;
const bspversion_t *loadversion;
int i;
double start;
double end;
@ -1199,10 +1199,10 @@ light_main(int argc, const char **argv)
LoadBSPFile(source, &bspdata);
loadversion = bspdata.version;
ConvertBSPFormat(GENERIC_BSP, &bspdata);
ConvertBSPFormat(&bspdata, &bspver_generic);
//mxd. Use 1.0 rangescale as a default to better match with qrad3/arghrad
if (loadversion == Q2_BSPVERSION && !cfg.rangescale.isChanged())
if ((loadversion == &bspver_q2 || loadversion == &bspver_qbism) && !cfg.rangescale.isChanged())
{
const auto rs = new lockable_vec_t(cfg.rangescale.primaryName(), 1.0f, 0.0f, 100.0f);
cfg.rangescale = *rs; // Gross hacks to avoid displaying this in OptionsSummary...
@ -1242,7 +1242,7 @@ light_main(int argc, const char **argv)
if (!onlyents)
{
if (loadversion != Q2_BSPVERSION && bsp->loadversion != BSPHLVERSION) //mxd. No lit for Quake 2
if (loadversion != &bspver_q2 && loadversion != &bspver_qbism && bsp->loadversion != &bspver_hl) //mxd. No lit for Quake 2
CheckLitNeeded(cfg);
SetupDirt(cfg);
@ -1281,7 +1281,7 @@ light_main(int argc, const char **argv)
WriteEntitiesToString(cfg, bsp);
/* Convert data format back if necessary */
ConvertBSPFormat(loadversion, &bspdata);
ConvertBSPFormat(&bspdata, loadversion);
if (!litonly) {
WriteBSPFile(source, &bspdata);

View File

@ -1034,7 +1034,7 @@ GetLightContrib(const globalconfig_t &cfg, const light_t *entity, const vec3_t s
if (dist < 0.1) {
// Catch 0 distance between sample point and light (produces infinite brightness / nan's) and causes
// problems later
dist = 0.1;
dist = 0.1f;
VectorSet(surfpointToLightDir_out, 0, 0, 1);
}
const float add = GetLightValueWithAngle(cfg, entity, surfnorm, surfpointToLightDir_out, dist, twosided);
@ -3081,7 +3081,7 @@ WriteLightmaps(const mbsp_t *bsp, bsp2_dface_t *face, facesup_t *facesup, const
continue;
// skip lightmaps where all samples have brightness below 1
if (bsp->loadversion != Q2_BSPVERSION) { // HACK: don't do this on Q2. seems if all styles are 0xff, the face is drawn fullbright instead of black (Q1)
if (bsp->loadversion != &bspver_q2 && bsp->loadversion != &bspver_qbism) { // HACK: don't do this on Q2. seems if all styles are 0xff, the face is drawn fullbright instead of black (Q1)
const float maxb = Lightmap_MaxBrightness(&lightmap, lightsurf);
if (maxb < 1)
continue;
@ -3146,7 +3146,7 @@ WriteLightmaps(const mbsp_t *bsp, bsp2_dface_t *face, facesup_t *facesup, const
// q2 support
int lightofs;
if (bsp->loadversion == Q2_BSPVERSION || bsp->loadversion == BSPHLVERSION) {
if (bsp->loadversion == &bspver_q2 || bsp->loadversion == &bspver_qbism || bsp->loadversion == &bspver_hl) {
lightofs = lit - lit_filebase;
} else {
lightofs = out - filebase;

View File

@ -716,7 +716,7 @@ TraceFaces (traceinfo_t *ti, int node, const vec3_t start, const vec3_t end)
// only solid and sky faces stop the trace.
bool issolid, issky; //mxd
if(bsp_static->loadversion == Q2_BSPVERSION) {
if(bsp_static->loadversion == &bspver_q2 || bsp_static->loadversion == &bspver_qbism) {
issolid = !(fi->content & Q2_SURF_TRANSLUCENT);
issky = (fi->content & Q2_SURF_SKY);
} else {

View File

@ -354,7 +354,7 @@ Embree_FilterFuncN(const struct RTCFilterFunctionNArguments* args)
//mxd
bool isFence, isGlass;
if(bsp_static->loadversion == Q2_BSPVERSION) {
if(bsp_static->loadversion == &bspver_q2 || bsp_static->loadversion == &bspver_qbism) {
const int contents = Face_Contents(bsp_static, face);
isFence = ((contents & Q2_SURF_TRANSLUCENT) == Q2_SURF_TRANSLUCENT); // KMQuake 2-specific. Use texture alpha chanel when both flags are set.
isGlass = !isFence && (contents & Q2_SURF_TRANSLUCENT);
@ -559,7 +559,7 @@ MakeFaces_r(const mbsp_t *bsp, const int nodenum, std::vector<plane_t> *planes,
const int leafnum = -nodenum - 1;
const mleaf_t *leaf = &bsp->dleafs[leafnum];
if (bsp->loadversion == Q2_BSPVERSION ? leaf->contents & Q2_CONTENTS_SOLID : leaf->contents == CONTENTS_SOLID) {
if ((bsp->loadversion == &bspver_q2 || bsp->loadversion == &bspver_qbism) ? leaf->contents & Q2_CONTENTS_SOLID : leaf->contents == CONTENTS_SOLID) {
std::vector<winding_t *> leaf_windings = Leaf_MakeFaces(bsp, leaf, *planes);
for (winding_t *w : leaf_windings) {
result->push_back(w);
@ -631,15 +631,16 @@ Embree_TraceInit(const mbsp_t *bsp)
const int contents = Face_Contents(bsp, face); //mxd
const gtexinfo_t *texinfo = Face_Texinfo(bsp, face);
const bool is_q2 = bsp->loadversion == &bspver_q2 || bsp->loadversion == &bspver_qbism;
//mxd. Skip NODRAW faces, but not SKY ones (Q2's sky01.wal has both flags set)
if(bsp->loadversion == Q2_BSPVERSION && (contents & Q2_SURF_NODRAW) && !(contents & Q2_SURF_SKY))
if(is_q2 && (contents & Q2_SURF_NODRAW) && !(contents & Q2_SURF_SKY))
continue;
// handle glass / water
const float alpha = Face_Alpha(model, face);
if (alpha < 1.0f
|| (bsp->loadversion == Q2_BSPVERSION && (contents & Q2_SURF_TRANSLUCENT))) { //mxd. Both fence and transparent textures are done using SURF_TRANS flags in Q2
|| (is_q2 && (contents & Q2_SURF_TRANSLUCENT))) { //mxd. Both fence and transparent textures are done using SURF_TRANS flags in Q2
filterfaces.push_back(face);
continue;
}
@ -652,7 +653,7 @@ Embree_TraceInit(const mbsp_t *bsp)
}
// handle sky
if (bsp->loadversion == Q2_BSPVERSION) {
if (is_q2) {
// Q2: arghrad compat: sky faces only emit sunlight if:
// sky flag set, light flag set, value nonzero
if ((contents & Q2_SURF_SKY) != 0

View File

@ -1234,7 +1234,7 @@ main(int argc, char **argv)
{
bspdata_t bspdata;
mbsp_t *const bsp = &bspdata.data.mbsp;
int32_t loadversion;
const bspversion_t *loadversion;
int i;
init_log("vis.log");
@ -1309,7 +1309,7 @@ main(int argc, char **argv)
LoadBSPFile(sourcefile, &bspdata);
loadversion = bspdata.version;
ConvertBSPFormat(GENERIC_BSP, &bspdata);
ConvertBSPFormat(&bspdata, &bspver_generic);
strcpy(portalfile, argv[i]);
StripExtension(portalfile);
@ -1341,7 +1341,7 @@ main(int argc, char **argv)
CalcAmbientSounds(bsp);
/* Convert data format back if necessary */
ConvertBSPFormat(loadversion, &bspdata);
ConvertBSPFormat(&bspdata, loadversion);
WriteBSPFile(sourcefile, &bspdata);