Paranoia2/utils/bsp31migrate/bspfile.cpp
2020-08-31 19:50:41 +03:00

1212 lines
29 KiB
C++

/*
bspfile.cpp - load, convert & write bsp
Copyright (C) 2016 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "bsp31migrate.h"
#include "filesystem.h"
#define VALVE_FORMAT 220
map_type g_maptype = MAP_UNKNOWN;
sub_type g_subtype = MAP_NORMAL;
//=============================================================================
bool g_found_extradata = false;
int g_nummodels;
dmodel_t g_dmodels[MAX_MAP_MODELS];
int g_visdatasize;
byte g_dvisdata[MAX_MAP_VISIBILITY];
int g_lightdatasize;
byte *g_dlightdata;
int g_deluxdatasize;
byte *g_ddeluxdata;
int g_shadowdatasize;
byte *g_dshadowdata;
int g_texdatasize;
byte* g_dtexdata; // (dmiptexlump_t)
int g_entdatasize;
char g_dentdata[MAX_MAP_ENTSTRING];
int g_numleafs;
dleaf_t g_dleafs[MAX_MAP_LEAFS];
int g_numplanes;
dplane_t g_dplanes[MAX_INTERNAL_MAP_PLANES];
int g_numvertexes;
dvertex_t g_dvertexes[MAX_MAP_VERTS];
int g_numnodes;
dnode_t g_dnodes[MAX_MAP_NODES];
int g_numtexinfo;
dtexinfo_t g_texinfo[MAX_INTERNAL_MAP_TEXINFO];
int g_numfaces;
dface_t g_dfaces[MAX_MAP_FACES];
int g_numclipnodes;
dclipnode_t g_dclipnodes[MAX_MAP_CLIPNODES];
dclipnode32_t g_dclipnodes32[MAX_MAP_CLIPNODES32];
int g_numedges;
dedge_t g_dedges[MAX_MAP_EDGES];
int g_nummarksurfaces;
dmarkface_t g_dmarksurfaces[MAX_MAP_MARKSURFACES];
int g_numsurfedges;
int g_dsurfedges[MAX_MAP_SURFEDGES];
int g_numfaceinfo;
dfaceinfo_t g_dfaceinfo[MAX_MAP_FACEINFO];
int g_numnormals;
dnormal_t g_dnormals[MAX_MAP_NORMS];
int g_numcubemaps;
dcubemap_t g_dcubemaps[MAX_MAP_CUBEMAPS];
int g_numleaflights;
dleafsample_t g_dleaflights[MAX_MAP_LEAFLIGHTS];
int g_numworldlights;
dworldlight_t g_dworldlights[MAX_MAP_WORLDLIGHTS];
int g_vlightdatasize;
byte *g_dvlightdata; // (dvlightlump_t)
int g_numfacedata;
dfacedata_t g_dfacedata[MAX_MAP_FACES];
int g_numTNbasis;
dTNbasis_t g_dTNbasis[MAX_MAP_TNBASIS];
int g_bumpdatasize;
byte *g_dbumpdata;
int g_numentities;
entity_t g_entities[MAX_MAP_ENTITIES];
char g_mapinfo[8192];
char g_mapname[1024];
int g_mapversion;
byte *mod_base;
void PrintMapInfo( void )
{
size_t infolen = Q_strlen( g_mapinfo );
char *ptr = &g_mapinfo[infolen-2];
if( *ptr == ',' ) *ptr = '.';
Msg( "Map name: %s", g_mapname );
Msg( "\nMap type: " );
switch( g_maptype )
{
case MAP_XASH31:
Msg( "^2XashXT BSP31^7" );
break;
default:
COM_FatalError( "%s unknown map format\n", g_mapname );
break;
}
if( g_subtype != MAP_NORMAL )
Msg( "\nSub type: " );
else Msg( "\n" );
switch( g_subtype )
{
case MAP_HLFX06:
Msg( "^4HLFX 0.6^7\n" );
break;
case MAP_XASHXT_OLD:
Msg( "^4XashXT 0.5^7\n" );
break;
case MAP_P2SAVIOR:
Msg( "^4Paranoia2: Savior^7\n" );
break;
case MAP_DEPRECATED:
Msg( "^1intermediate deprecated version^7\n" );
break;
case MAP_XASH3D_EXT:
Msg( "^4Xash3D extended^7\n" );
break;
}
if( g_mapinfo[0] ) Msg( "Map info: %s", g_mapinfo );
Msg( "\n\n" );
}
//=============================================================================
int CopyLump( int lump, void *dest, int size, dheader31_t *header )
{
int length = header->lumps[lump].filelen;
int ofs = header->lumps[lump].fileofs;
if( length % size )
COM_FatalError( "LoadBSPFile: odd lump size\n" );
// alloc matched size
if( lump == LUMP_TEXTURES )
dest = g_dtexdata = (byte *)Mem_Alloc( length );
if( lump == LUMP_LIGHTING )
dest = g_dlightdata = (byte *)Mem_Alloc( length );
memcpy( dest, (byte *)header + ofs, length );
return length / size;
}
int CopyLump( int lump, void *dest, int size, dheader31_hlfx_t *header )
{
int length = header->lumps[lump].filelen;
int ofs = header->lumps[lump].fileofs;
if( length % size )
COM_FatalError( "LoadBSPFile: odd lump size\n" );
// alloc matched size
if( lump == LUMP_TEXTURES )
dest = g_dtexdata = (byte *)Mem_Alloc( length );
if( lump == LUMP_LIGHTING )
dest = g_dlightdata = (byte *)Mem_Alloc( length );
if( lump == HLFX31_LUMP_BUMP )
dest = g_dbumpdata = (byte *)Mem_Alloc( length );
memcpy( dest, (byte *)header + ofs, length );
return length / size;
}
static int CopyExtraLump( int lump, void *dest, int size, const dheader31_t *header )
{
dextrahdr_t *extrahdr = (dextrahdr_t *)((byte *)header + sizeof( dheader31_t ));
int length = extrahdr->lumps[lump].filelen;
int ofs = extrahdr->lumps[lump].fileofs;
if( length % size )
COM_FatalError( "LoadBSPFile: odd lump size\n" );
if( lump == LUMP_LIGHTVECS )
dest = g_ddeluxdata = (byte *)Mem_Alloc( length );
if( lump == LUMP_SHADOWMAP )
dest = g_dshadowdata = (byte *)Mem_Alloc( length );
if( lump == LUMP_VERTEX_LIGHT )
dest = g_dvlightdata = (byte *)Mem_Alloc( length );
memcpy( dest, (byte *)header + ofs, length );
return length / size;
}
//=============================================================================
static void AddLump( int lumpnum, void *data, size_t len, dheader30_hlfx_t *header, long handle )
{
dlump_t *lump = &header->lumps[lumpnum];
lump->fileofs = tell( handle );
lump->filelen = len;
SafeWrite( handle, data, (len + 3) & ~3 );
}
static void AddLump( int lumpnum, void *data, size_t len, dheader_t *header, long handle )
{
dlump_t *lump = &header->lumps[lumpnum];
lump->fileofs = tell( handle );
lump->filelen = len;
SafeWrite( handle, data, (len + 3) & ~3 );
}
static void AddExtraLump( int lumpnum, void *data, int len, dextrahdr_t *header, long handle )
{
dlump_t* lump = &header->lumps[lumpnum];
lump->fileofs = tell( handle );
lump->filelen = len;
SafeWrite( handle, data, (len + 3) & ~3 );
}
void AddLumpClipnodes( int lumpnum, dheader_t *header, long handle )
{
dlump_t *lump = &header->lumps[lumpnum];
lump->fileofs = tell( handle );
if( g_numclipnodes < MAX_MAP_CLIPNODES )
{
// copy clipnodes into 16-bit array
for( int i = 0; i < g_numclipnodes; i++ )
{
g_dclipnodes[i].children[0] = (short)g_dclipnodes32[i].children[0];
g_dclipnodes[i].children[1] = (short)g_dclipnodes32[i].children[1];
g_dclipnodes[i].planenum = g_dclipnodes32[i].planenum;
}
lump->filelen = g_numclipnodes * sizeof( dclipnode_t );
SafeWrite( handle, g_dclipnodes, (lump->filelen + 3) & ~3 );
}
else
{
// copy clipnodes into 32-bit array
lump->filelen = g_numclipnodes * sizeof( dclipnode32_t );
SafeWrite( handle, g_dclipnodes32, (lump->filelen + 3) & ~3 );
}
}
//============================================
void StripTrailing( char *e )
{
char *s;
s = e + Q_strlen( e ) - 1;
while( s >= e && *s <= 32 )
{
*s = 0;
s--;
}
}
/*
================
InsertLinkBefore
================
*/
void InsertLinkBefore( epair_t *e, entity_t *mapent )
{
e->next = NULL;
if( mapent->epairs != NULL )
{
e->prev = mapent->tail;
mapent->tail->next = e;
mapent->tail = e;
}
else
{
mapent->epairs = mapent->tail = e;
e->prev = NULL;
}
}
/*
================
UnlinkEpair
================
*/
void UnlinkEpair( epair_t *e, entity_t *mapent )
{
if( e->prev ) e->prev->next = e->next;
else mapent->epairs = e->next;
if( e->next ) e->next->prev = e->prev;
else mapent->tail = e->prev;
e->prev = e->next = e;
}
/*
=================
ParseEpair
=================
*/
epair_t *ParseEpair( void )
{
const char *ext;
epair_t *e;
e = (epair_t *)Mem_Alloc( sizeof( epair_t ), C_EPAIR );
if( Q_strlen( token ) >= ( MAX_KEY - 1 ))
COM_FatalError( "ParseEpair: 'key' token too long\n" );
e->key = copystring( token );
GetToken( false );
if( Q_strlen( token ) >= ( MAX_VALUE - 1 ))
COM_FatalError( "ParseEpair: 'value' token too long\n" );
ext = COM_FileExtension( token );
if( !Q_stricmp( ext, "wav" ) || !Q_stricmp( ext, "mdl" ) || !Q_stricmp( ext, "bsp" ))
COM_FixSlashes( token );
e->value = copystring( token );
// strip trailing spaces
StripTrailing( e->key );
StripTrailing( e->value );
return e;
}
/*
================
ParseEntity
================
*/
bool ParseEntity( void )
{
entity_t *mapent;
epair_t *e;
if( !GetToken( true ))
return false;
if( Q_strcmp( token, "{" ))
{
if( g_numentities == 0 )
COM_FatalError( "ParseEntity: '{' not found\n" );
else return false; // probably entity string is broken at the end
}
if( g_numentities == MAX_MAP_ENTITIES )
COM_FatalError( "MAX_MAP_ENTITIES limit exceeded\n" );
mapent = &g_entities[g_numentities];
g_numentities++;
do
{
if( !GetToken( true ))
COM_FatalError( "ParseEntity: EOF without closing brace\n" );
if( !Q_strcmp( token, "}" ))
break;
e = ParseEpair();
InsertLinkBefore( e, mapent );
} while( 1 );
return true;
}
/*
================
ParseEntities
Parses the dentdata string into entities
================
*/
void ParseEntities( void )
{
ParseFromMemory( g_dentdata, g_entdatasize );
memset( &g_entities, 0, sizeof( g_entities ));
g_numentities = 0;
while( ParseEntity( ));
}
/*
================
FreeEntities
release all the entity data
================
*/
void FreeEntity( entity_t *mapent )
{
epair_t *ep, *next;
for( ep = mapent->epairs; ep != NULL; ep = next )
{
next = ep->next;
freestring( ep->key );
freestring( ep->value );
Mem_Free( ep, C_EPAIR );
}
if( mapent->cache ) Mem_Free( mapent->cache );
mapent->epairs = mapent->tail = NULL;
mapent->cache = NULL;
}
/*
================
FreeEntities
release all the dynamically allocated data
================
*/
void FreeEntities( void )
{
for( int i = 0; i < g_numentities; i++ )
{
FreeEntity( &g_entities[i] );
}
}
/*
================
UnparseEntities
Generates the dentdata string from all the entities
================
*/
void UnparseEntities( void )
{
char *buf, *end;
char line[2048];
char key[1024], value[1024];
epair_t *ep;
buf = g_dentdata;
end = buf;
*end = 0;
for( int i = 0; i < g_numentities; i++ )
{
entity_t *ent = &g_entities[i];
if( !ent->epairs ) continue; // ent got removed
Q_strcat( end, "{\n" );
end += 2;
for( ep = ent->epairs; ep; ep = ep->next )
{
Q_strncpy( key, ep->key, sizeof( key ));
StripTrailing( key );
Q_strncpy( value, ep->value, sizeof( value ));
StripTrailing( value );
Q_snprintf( line, sizeof( line ), "\"%s\" \"%s\"\n", key, value );
Q_strcat( end, line );
end += Q_strlen( line );
}
Q_strcat( end, "}\n" );
end += 2;
if( end > ( buf + MAX_MAP_ENTSTRING ))
COM_FatalError( "Entity text too long\n" );
}
g_entdatasize = end - buf + 1; // write term
}
void RemoveKey( entity_t *ent, const char *key )
{
epair_t *ep;
for( ep = ent->epairs; ep; ep = ep->next )
{
if( !Q_strcmp( ep->key, key ))
{
freestring( ep->key );
freestring( ep->value );
UnlinkEpair( ep, ent );
Mem_Free( ep, C_EPAIR );
return;
}
}
}
void SetKeyValue( entity_t *ent, const char *key, const char *value )
{
epair_t *ep;
for( ep = ent->epairs; ep; ep = ep->next )
{
if( !Q_strcmp( ep->key, key ))
{
freestring( ep->value );
ep->value = copystring( value );
return;
}
}
ep = (epair_t *)Mem_Alloc( sizeof( epair_t ), C_EPAIR );
ep->key = copystring( key );
ep->value = copystring( value );
InsertLinkBefore( ep, ent );
}
char *ValueForKey( entity_t *ent, const char *key, bool check )
{
epair_t *ep;
for( ep = ent->epairs; ep; ep = ep->next )
{
if( !Q_strcmp( ep->key, key ))
return ep->value;
}
if( check )
return NULL;
return "";
}
vec_t FloatForKey( entity_t *ent, const char *key )
{
return atof( ValueForKey( ent, key ));
}
int IntForKey( entity_t *ent, const char *key )
{
return atoi( ValueForKey( ent, key ));
}
bool BoolForKey( entity_t *ent, const char *key )
{
if( atoi( ValueForKey( ent, key )))
return true;
return false;
}
int GetVectorForKey( entity_t *ent, const char *key, vec3_t vec )
{
double v1, v2, v3;
int count;
char *k;
k = ValueForKey( ent, key );
// scanf into doubles, then assign, so it is vec_t size independent
v1 = v2 = v3 = 0;
count = sscanf( k, "%lf %lf %lf", &v1, &v2, &v3 );
vec[0] = v1;
vec[1] = v2;
vec[2] = v3;
return count;
}
void ReleaseBSPFile( void )
{
FreeEntities();
g_maptype = MAP_UNKNOWN;
g_subtype = MAP_NORMAL;
g_nummodels = 0;
memset( g_dmodels, 0, sizeof( g_dmodels ));
g_visdatasize = 0;
memset( g_dvisdata, 0, sizeof( g_dvisdata ));
g_lightdatasize = 0;
Mem_Free( g_dlightdata );
g_dlightdata = NULL;
g_deluxdatasize = 0;
Mem_Free( g_ddeluxdata );
g_ddeluxdata = NULL;
g_texdatasize = 0;
Mem_Free( g_dtexdata );
g_dtexdata = NULL;
g_shadowdatasize = 0;
Mem_Free( g_dshadowdata );
g_dshadowdata = NULL;
g_vlightdatasize = 0;
Mem_Free( g_dvlightdata );
g_dvlightdata = NULL;
g_bumpdatasize = 0;
Mem_Free( g_dbumpdata );
g_dbumpdata = NULL;
g_entdatasize = 0;
memset( g_dentdata, 0, sizeof( g_dentdata ));
g_numleafs = 0;
memset( g_dleafs, 0, sizeof( g_dleafs ));
g_numplanes = 0;
memset( g_dplanes, 0, sizeof( g_dplanes ));
g_numvertexes = 0;
memset( g_dvertexes, 0, sizeof( g_dvertexes ));
g_numnormals = 0;
memset( g_dnormals, 0, sizeof( g_dnormals ));
g_numnodes = 0;
memset( g_dnodes, 0, sizeof( g_dnodes ));
g_numtexinfo = 0;
memset( g_texinfo, 0, sizeof( g_texinfo ));
g_numfaces = 0;
memset( g_dfaces, 0, sizeof( g_dfaces ));
g_numclipnodes = 0;
memset( g_dclipnodes, 0, sizeof( g_dclipnodes ));
memset( g_dclipnodes32, 0, sizeof( g_dclipnodes32 ));
g_numedges = 0;
memset( g_dedges, 0, sizeof( g_dedges ));
g_nummarksurfaces = 0;
memset( g_dmarksurfaces, 0, sizeof( g_dmarksurfaces ));
g_numsurfedges = 0;
memset( g_dsurfedges, 0, sizeof( g_dsurfedges ));
g_numentities = 0;
memset( g_entities, 0, sizeof( g_entities ));
g_numfaceinfo = 0;
memset( g_dfaceinfo, 0, sizeof( g_dfaceinfo ));
g_numcubemaps = 0;
memset( g_dcubemaps, 0, sizeof( g_dcubemaps ));
g_numleaflights = 0;
memset( g_dleaflights, 0, sizeof( g_dleaflights ));
g_numworldlights = 0;
memset( g_dworldlights, 0, sizeof( g_dworldlights ));
g_mapinfo[0] = '\0';
g_mapname[0] = '\0';
g_mapversion = 0;
Mem_Free( mod_base );
mod_base = NULL;
}
// =====================================================================================
// WriteBSPFile
// Swaps the bsp file in place, so it should not be referenced again
// =====================================================================================
void WriteBSPFile( const char *filename )
{
dheader30_hlfx_t outheader; // long header from HLFX
dheader_t *header;
dheader30_hlfx_t *hlfxhdr;
dextrahdr_t outextrahdr;
dextrahdr_t *extrahdr;
long bspfile;
header = (dheader_t *)&outheader;
hlfxhdr = &outheader;
memset( &outheader, 0, sizeof( dheader30_hlfx_t ));
extrahdr = &outextrahdr;
memset( extrahdr, 0, sizeof( dextrahdr_t ));
header->version = BSPVERSION;
extrahdr->id = IDEXTRAHEADER;
extrahdr->version = EXTRA_VERSION;
hlfxhdr->magicID = HLFX_BSP_MAGIC_ID;
COM_CreatePath( (char *)filename );
bspfile = SafeOpenWrite( filename );
if( g_subtype == MAP_HLFX06 )
{
SafeWrite( bspfile, hlfxhdr, sizeof( dheader30_hlfx_t )); // overwritten later
}
else
{
SafeWrite( bspfile, header, sizeof( dheader_t )); // overwritten later
SafeWrite( bspfile, extrahdr, sizeof( dextrahdr_t )); // overwritten later
}
UnparseEntities();
// LUMP TYPE DATA LENGTH HEADER BSPFILE
AddLump( LUMP_PLANES, g_dplanes, g_numplanes * sizeof( dplane_t ), header, bspfile );
AddLump( LUMP_LEAFS, g_dleafs, g_numleafs * sizeof( dleaf_t ), header, bspfile );
AddLump( LUMP_VERTEXES, g_dvertexes, g_numvertexes * sizeof( dvertex_t ), header, bspfile );
AddLump( LUMP_NODES, g_dnodes, g_numnodes * sizeof( dnode_t ), header, bspfile );
AddLump( LUMP_TEXINFO, g_texinfo, g_numtexinfo * sizeof( dtexinfo_t ), header, bspfile );
AddLump( LUMP_FACES, g_dfaces, g_numfaces * sizeof( dface_t ), header, bspfile );
AddLump( LUMP_MARKSURFACES, g_dmarksurfaces, g_nummarksurfaces * sizeof( dmarkface_t ), header, bspfile );
AddLump( LUMP_SURFEDGES, g_dsurfedges, g_numsurfedges * sizeof( dsurfedge_t ), header, bspfile );
AddLump( LUMP_EDGES, g_dedges, g_numedges * sizeof( dedge_t ), header, bspfile );
AddLump( LUMP_MODELS, g_dmodels, g_nummodels * sizeof( dmodel_t ), header, bspfile );
AddLumpClipnodes( LUMP_CLIPNODES, header, bspfile ); // clipnodes can using 16-bit or 32-bit indexes
AddLump( LUMP_LIGHTING, g_dlightdata, g_lightdatasize, header, bspfile );
AddLump( LUMP_VISIBILITY, g_dvisdata, g_visdatasize, header, bspfile );
AddLump( LUMP_ENTITIES, g_dentdata, g_entdatasize, header, bspfile );
AddLump( LUMP_TEXTURES, g_dtexdata, g_texdatasize, header, bspfile );
if( g_subtype == MAP_HLFX06 )
{
AddLump( HLFX30_LUMP_FACEINFO, g_dfacedata, g_numfacedata * sizeof( dfacedata_t ), hlfxhdr, bspfile );
AddLump( HLFX30_LUMP_BUMP, g_dbumpdata, g_bumpdatasize, hlfxhdr, bspfile );
AddLump( HLFX30_LUMP_TNBASIS, g_dTNbasis, g_numTNbasis * sizeof( dTNbasis_t ), hlfxhdr, bspfile );
}
else
{
AddExtraLump( LUMP_VERTNORMALS, g_dnormals, g_numnormals * sizeof( dnormal_t ), extrahdr, bspfile );
AddExtraLump( LUMP_LIGHTVECS, g_ddeluxdata, g_deluxdatasize, extrahdr, bspfile );
AddExtraLump( LUMP_CUBEMAPS, g_dcubemaps, g_numcubemaps * sizeof( dcubemap_t ), extrahdr, bspfile );
AddExtraLump( LUMP_FACEINFO, g_dfaceinfo, g_numfaceinfo * sizeof( dfaceinfo_t ), extrahdr, bspfile );
AddExtraLump( LUMP_WORLDLIGHTS, g_dworldlights, g_numworldlights * sizeof( dworldlight_t ), extrahdr, bspfile );
AddExtraLump( LUMP_SHADOWMAP, g_dshadowdata, g_shadowdatasize, extrahdr, bspfile );
AddExtraLump( LUMP_LEAF_LIGHTING,g_dleaflights, g_numleaflights * sizeof( dleafsample_t ), extrahdr, bspfile );
AddExtraLump( LUMP_VERTEX_LIGHT, g_dvlightdata, g_vlightdatasize, extrahdr, bspfile );
}
lseek( bspfile, 0, SEEK_SET );
if( g_subtype == MAP_HLFX06 )
{
SafeWrite( bspfile, hlfxhdr, sizeof( dheader30_hlfx_t ));
}
else
{
SafeWrite( bspfile, header, sizeof( dheader_t ));
SafeWrite( bspfile, extrahdr, sizeof( dextrahdr_t ));
}
close( bspfile );
ReleaseBSPFile();
}
/*
=================
Mod_FindModelOrigin
routine to detect bmodels with origin-brush
=================
*/
static void Mod_FindModelOrigin( const char *modelname, vec3_t origin )
{
VectorClear( origin );
if( !g_numentities ) return;
// skip the world
for( int i = 1; i < g_numentities; i++ )
{
entity_t *mapent = &g_entities[i];
if( !Q_stricmp( modelname, ValueForKey( mapent, "model" )))
{
GetVectorForKey( mapent, "origin", origin );
return;
}
}
}
/*
===============================================================================
MAP LOADING
===============================================================================
*/
/*
=================
Mod_LoadSubmodels
=================
*/
static void Mod_LoadSubmodels( const dlump_t *l )
{
dmodel_t *out = g_dmodels;
dmodel_t *in;
int i, j;
in = (dmodel_t *)(mod_base + l->fileofs);
if( l->filelen % sizeof( *in ))
COM_FatalError( "Mod_LoadSubmodels: funny lump size\n" );
g_nummodels = l->filelen / sizeof( *in );
if( g_nummodels < 1 ) COM_FatalError( "Map %s without models\n", g_mapname );
if( g_nummodels > MAX_MAP_MODELS ) COM_FatalError( "Map %s has too many models\n", g_mapname );
for( i = 0; i < g_nummodels; i++, in++, out++ )
{
for( j = 0; j < 3; j++ )
{
// spread the mins / maxs by a pixel
out->mins[j] = in->mins[j];
out->maxs[j] = in->maxs[j];
out->origin[j] = in->origin[j];
}
if( i != 0 && VectorCompare( vec3_origin, out->origin ))
Mod_FindModelOrigin( va( "*%i", i ), out->origin );
for( j = 0; j < MAX_MAP_HULLS; j++ )
out->headnode[j] = in->headnode[j];
if( in->visleafs >= 65535 )
COM_FatalError( "Mod_LoadSubmodels: visleafs %i exceeds an unsigned short limit\n", in->visleafs );
out->visleafs = in->visleafs;
out->firstface = in->firstface;
out->numfaces = in->numfaces;
}
}
/*
=================
Mod_LoadEntities
=================
*/
static void Mod_LoadEntities( const dlump_t *l )
{
if( l->filelen > MAX_MAP_ENTSTRING )
COM_FatalError( "Map %s exceeds MAX_MAP_ENTSTRING\n", g_mapname );
memcpy( g_dentdata, mod_base + l->fileofs, l->filelen );
g_entdatasize = l->filelen;
ParseEntities();
// grab mapversion
g_mapversion = IntForKey( &g_entities[0], "mapversion" );
}
/*
=================
Mod_LoadTexInfo
=================
*/
static void Mod_LoadTexInfo( const dlump_t *l )
{
dtexinfo_t *out = g_texinfo;
dtexinfo_t *in;
int i, j;
in = (dtexinfo_t *)(mod_base + l->fileofs);
if( l->filelen % sizeof( *in ))
COM_FatalError( "Mod_LoadTexInfo: funny lump size in %s\n", g_mapname );
g_numtexinfo = l->filelen / sizeof( *in );
for( i = 0; i < g_numtexinfo; i++, in++, out++ )
{
for( j = 0; j < 8; j++ )
out->vecs[0][j] = in->vecs[0][j];
out->miptex = in->miptex;
out->flags = in->flags;
// tell the engine about hi-res lightmaps
SetBits( out->flags, TEX_EXTRA_LIGHTMAP );
if( g_subtype == MAP_XASH3D_EXT )
out->faceinfo = in->faceinfo;
else out->faceinfo = -1;
}
}
/*
=================
Mod_LoadDeluxemap
=================
*/
static void Mod_LoadDeluxemap( void )
{
char path[64];
size_t filesize;
byte *in;
if( g_subtype == MAP_XASH3D_EXT || g_subtype == MAP_HLFX06 )
return; // Xash3D ext format have built-in deluxemap
Q_strncpy( path, g_mapname, sizeof( path ));
COM_StripExtension( path );
COM_DefaultExtension( path, ".dlit" );
if( !COM_FileExists( path )) return; // missed
in = (byte *)COM_LoadFile( path, &filesize );
if( *(uint *)in != IDDELUXEMAPHEADER || *((uint *)in + 1) != DELUXEMAP_VERSION )
{
MsgDev( D_ERROR, "Mod_LoadDeluxemap: %s is not a deluxemap file\n", path );
g_ddeluxdata = NULL;
g_deluxdatasize = 0;
Mem_Free( in );
return;
}
g_deluxdatasize = filesize;
// skip header bytes
g_deluxdatasize -= 8;
if( g_deluxdatasize != g_lightdatasize )
{
MsgDev( D_ERROR, "Mod_LoadDeluxemap: %s has mismatched size (%i should be %i)\n", path, g_deluxdatasize, g_lightdatasize );
g_ddeluxdata = NULL;
g_deluxdatasize = 0;
Mem_Free( in );
return;
}
Q_strncat( g_mapinfo, "deluxemap included, ", sizeof( g_mapinfo ));
MsgDev( D_REPORT, "Mod_LoadDeluxemap: %s loaded\n", path );
g_ddeluxdata = (byte *)Mem_Alloc( g_deluxdatasize );
memcpy( g_ddeluxdata, in + 8, g_deluxdatasize );
Mem_Free( in );
}
/*
=================
Mod_LoadLightVecs
=================
*/
static void Mod_LoadLightVecs( const dlump_t *l )
{
byte *in;
in = (byte *)(mod_base + l->fileofs);
g_deluxdatasize = l->filelen;
if( !l->filelen ) return;
if( g_deluxdatasize != g_lightdatasize )
{
MsgDev( D_ERROR, "Mod_LoadLightVecs: has mismatched size (%i should be %i)\n", g_deluxdatasize, g_lightdatasize );
g_ddeluxdata = NULL;
g_deluxdatasize = 0;
return;
}
Q_strncat( g_mapinfo, "deluxemap included, ", sizeof( g_mapinfo ));
g_ddeluxdata = (byte *)Mem_Alloc( g_deluxdatasize );
memcpy( g_ddeluxdata, in, g_deluxdatasize );
}
/*
=================
Mod_LoadClipnodes
=================
*/
static void Mod_LoadClipnodes( const dlump_t *l, const dlump_t *l2, const dlump_t *l3 )
{
dclipnode_t *in, *in2, *in3;
dclipnode32_t *out = g_dclipnodes32;
int i, count, count2, count3;
in = (dclipnode_t *)(mod_base + l->fileofs);
if( l->filelen % sizeof( *in )) COM_FatalError( "Mod_LoadClipnodes1: funny lump size\n" );
count = l->filelen / sizeof( *in );
in2 = (dclipnode_t *)(mod_base + l2->fileofs);
if( l2->filelen % sizeof( *in2 )) COM_FatalError( "Mod_LoadClipnodes2: funny lump size\n" );
count2 = l2->filelen / sizeof( *in2 );
in3 = (dclipnode_t *)(mod_base + l3->fileofs);
if( l3->filelen % sizeof( *in3 )) COM_FatalError( "Mod_LoadClipnodes3: funny lump size\n" );
count3 = l3->filelen / sizeof( *in3 );
g_numclipnodes = 0;
for( i = 0; i < count; i++, out++, in++ )
{
out->planenum = in->planenum;
out->children[0] = in->children[0];
out->children[1] = in->children[1];
g_numclipnodes++;
}
// merge offsets so we have shared array of clipnodes again
for( i = 0; i < count2; i++, out++, in2++ )
{
out->planenum = in2->planenum;
out->children[0] = in2->children[0];
out->children[1] = in2->children[1];
if( out->children[0] >= 0 )
out->children[0] += count;
if( out->children[1] >= 0 )
out->children[1] += count;
g_numclipnodes++;
}
// merge offsets so we have shared array of clipnodes again
for( i = 0; i < count3; i++, out++, in3++ )
{
out->planenum = in3->planenum;
out->children[0] = in3->children[0];
out->children[1] = in3->children[1];
if( out->children[0] >= 0 )
out->children[0] += (count + count2);
if( out->children[1] >= 0 )
out->children[1] += (count + count2);
g_numclipnodes++;
}
// update headnode offsets
for( i = 0; i < g_nummodels; i++ )
{
g_dmodels[i].headnode[2] += count;
g_dmodels[i].headnode[3] += (count + count2);
}
if( g_numclipnodes != ( count + count2 + count3 ))
COM_FatalError( "Mod_LoadClipnodes: mismatch node count (%i should be %i)\n", g_numclipnodes, ( count + count2 + count3 ));
}
/*
=============
LoadBsp31
load XashXT level file format
=============
*/
void LoadBsp31( void )
{
dheader31_t *header = (dheader31_t *)mod_base;
dextrahdr_t *extrahdr = (dextrahdr_t *)((byte *)mod_base + sizeof( dheader31_t ));
dheader31_hlfx_t *hlfxhdr = (dheader31_hlfx_t *)mod_base;
g_maptype = MAP_XASH31;
if( extrahdr->id == IDEXTRAHEADER )
{
switch( extrahdr->version )
{
case 1:
g_subtype = MAP_XASHXT_OLD;
break;
case EXTRA_VERSION_OLD:
g_subtype = MAP_P2SAVIOR;
break;
case 3:
g_subtype = MAP_DEPRECATED;
break;
case EXTRA_VERSION:
g_subtype = MAP_XASH3D_EXT;
break;
}
}
else if( hlfxhdr->magicID == HLFX_BSP_MAGIC_ID )
{
g_subtype = MAP_HLFX06;
}
// load into heap
Mod_LoadEntities( &header->lumps[LUMP_ENTITIES] );
if( g_mapversion != VALVE_FORMAT )
MsgDev( D_WARN, "%s not a Valve 220 format\n", g_mapname );
Mod_LoadSubmodels( &header->lumps[LUMP_MODELS] );
g_numplanes = CopyLump( LUMP_PLANES, g_dplanes, sizeof( dplane_t ), header );
g_numvertexes = CopyLump( LUMP_VERTEXES, g_dvertexes, sizeof( dvertex_t ), header );
g_numedges = CopyLump( LUMP_EDGES, g_dedges, sizeof( dedge_t ), header );
g_numsurfedges = CopyLump( LUMP_SURFEDGES, g_dsurfedges, sizeof( dsurfedge_t ), header );
g_visdatasize = CopyLump( LUMP_VISIBILITY, g_dvisdata, 1, header );
Mod_LoadTexInfo( &header->lumps[LUMP_TEXINFO] );
g_texdatasize = CopyLump( LUMP_TEXTURES, g_dtexdata, 1, header );
g_lightdatasize = CopyLump( LUMP_LIGHTING, g_dlightdata, 1, header );
g_numfaces = CopyLump( LUMP_FACES, g_dfaces, sizeof( dface_t ), header );
g_nummarksurfaces = CopyLump( LUMP_MARKSURFACES, g_dmarksurfaces, sizeof( dmarkface_t ), header );
g_numleafs = CopyLump( LUMP_LEAFS, g_dleafs, sizeof( dleaf_t ), header );
g_numnodes = CopyLump( LUMP_NODES, g_dnodes, sizeof( dnode_t ), header );
Mod_LoadClipnodes( &header->lumps[LUMP_CLIPNODES], &header->lumps[LUMP_CLIPNODES2], &header->lumps[LUMP_CLIPNODES3] );
Mod_LoadDeluxemap ();
if( g_subtype == MAP_XASH3D_EXT )
{
g_found_extradata = true;
// g-cont. copy the extra lumps
g_numnormals = CopyExtraLump( LUMP_VERTNORMALS, g_dnormals, sizeof( dnormal_t ), header );
g_deluxdatasize = CopyExtraLump( LUMP_LIGHTVECS, g_ddeluxdata, 1, header );
g_numcubemaps = CopyExtraLump( LUMP_CUBEMAPS, g_dcubemaps, sizeof( dcubemap_t ), header );
g_numfaceinfo = CopyExtraLump( LUMP_FACEINFO, g_dfaceinfo, sizeof( dfaceinfo_t ), header );
g_numworldlights = CopyExtraLump( LUMP_WORLDLIGHTS, g_dworldlights, sizeof( dworldlight_t ), header );
g_numleaflights = CopyExtraLump( LUMP_LEAF_LIGHTING, g_dleaflights, sizeof( dleafsample_t ), header );
g_shadowdatasize = CopyExtraLump( LUMP_SHADOWMAP, g_dshadowdata, 1, header );
g_vlightdatasize = CopyExtraLump( LUMP_VERTEX_LIGHT, g_dvlightdata, 1, header );
}
else if( g_subtype == MAP_P2SAVIOR )
{
// P2: Savior regular format
g_numcubemaps = CopyExtraLump( LUMP_CUBEMAPS, g_dcubemaps, sizeof( dcubemap_t ), header );
g_numworldlights = CopyExtraLump( LUMP_WORLDLIGHTS, g_dworldlights, sizeof( dworldlight_t ), header );
}
else if( g_subtype == MAP_HLFX06 )
{
g_numfacedata = CopyLump( HLFX31_LUMP_FACEINFO, g_dfacedata, sizeof( dfacedata_t ), hlfxhdr );
g_bumpdatasize = CopyLump( HLFX31_LUMP_BUMP, g_dbumpdata, 1, hlfxhdr );
g_numTNbasis = CopyLump( HLFX31_LUMP_TNBASIS, g_dTNbasis, sizeof( dTNbasis_t ), hlfxhdr );
}
// preform some operations here...
}
/*
=============
LoadBSPFile
=============
*/
void LoadBSPFile( const char *infilename, const char *outfilename )
{
MsgDev( D_REPORT, "Loading: %s\n", infilename );
Q_strncpy( g_mapname, infilename, sizeof( g_mapname ));
mod_base = (byte *)COM_LoadFile( g_mapname, NULL );
if( *(uint *)mod_base == XT_BSPVERSION )
{
LoadBsp31();
PrintMapInfo();
WriteBSPFile( outfilename );
}
else
{
// not an BSP31
Mem_Free( mod_base );
}
}
int BspConvert( int argc, char **argv )
{
char source[1024], name[1024];
char output[1024];
if( !COM_GetParmExt( "-file", source, sizeof( source )))
Q_strncpy( source, "*.bsp", sizeof( source ));
search_t *search = COM_Search( source, true );
if( !search ) return 0;
for( int i = 0; i < search->numfilenames; i++ )
{
COM_FileBase( search->filenames[i], name );
Q_snprintf( output, sizeof( output ), "%s.bsp", name );
#if 0
if( COM_FileExists( output ))
continue; // map already converted
#endif
LoadBSPFile( search->filenames[i], output );
}
Mem_Free( search );
Mem_Check();
Msg( "press any key to exit\n" );
system( "pause>nul" );
return 0;
}