This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/launch/tools/conv_bsp.c

396 lines
10 KiB
C

//=======================================================================
// Copyright XashXT Group 2007 ©
// conv_bsp.c - convert bsp lumps
//=======================================================================
#include "launch.h"
#include "ripper.h"
#include "wadfile.h"
#define IDBSPMODHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'I') // little-endian "IBSP" q2 bsp's
#define VDBSPMODHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'V') // little-endian "VBSP" hl2 bsp's
#define IDIWADHEADER (('D'<<24)+('A'<<16)+('W'<<8)+'I') // little-endian "IWAD" doom1 game wad
#define IDPWADHEADER (('D'<<24)+('A'<<16)+('W'<<8)+'P') // little-endian "PWAD" doom1 game wad
#define IDWAD2HEADER (('2'<<24)+('D'<<16)+('A'<<8)+'W') // little-endian "WAD2" quake1 gfx.wad
#define IDWAD3HEADER (('3'<<24)+('D'<<16)+('A'<<8)+'W') // little-endian "WAD3" half-life wads
typedef struct
{
int ident; // should be IWAD, WAD2 or WAD3
int numlumps; // num files
int infotableofs;
} dwadheader_t;
typedef struct
{
int fileofs;
int filelen;
} dlump_t;
typedef struct
{
int version;
dlump_t lumps[15];
} dbspheader_t;
typedef struct
{
int ident;
int version;
} gbsphdr_t;
typedef struct
{
int nummiptex;
int dataofs[4]; // [nummiptex]
} dmiptexlump_t;
typedef struct
{
char name[64];
} dmiptexname_t;
typedef struct
{
const char *texname;
const char *detail;
const char material;
int lMin;
int lMax;
} dmaterial_t;
byte* bsp_base;
qboolean bsp_halflife = false;
dmiptexname_t *mipnames = NULL;
int mip_count = 0;
file_t *detail_txt;
static const dmaterial_t detail_table[] =
{
{ "crt", "dt_conc", 'C', 0, 0 }, // concrete
{ "rock", "dt_rock", 'C', 0, 0 },
{ "conc", "dt_conc", 'C', 0, 0 },
{ "wall", "dt_brick", 'C', 0, 0 },
{ "crete", "dt_conc", 'C', 0, 0 },
{ "generic", "dt_brick", 'C', 0, 0 },
{ "metal", "dt_metal%i", 'M', 1, 2 }, // metal
{ "mtl", "dt_metal%i", 'M', 1, 2 },
{ "pipe", "dt_metal%i", 'M', 1, 2 },
{ "elev", "dt_metal%i", 'M', 1, 2 },
{ "sign", "dt_metal%i", 'M', 1, 2 },
{ "barrel", "dt_metal%i", 'M', 1, 2 },
{ "bath", "dt_ssteel1", 'M', 1, 2 },
{ "refbridge", "dt_metal%i", 'M', 1, 2 },
{ "panel", "dt_ssteel1", 'M', 0, 0 },
{ "brass", "dt_ssteel1", 'M', 0, 0 },
{ "car", "dt_metal%i", 'M', 1, 2 },
{ "circuit", "dt_metal%i", 'M', 1, 2 },
{ "steel", "dt_ssteel1", 'M', 0, 0 },
{ "dirt", "dt_ground%i", 'D', 1, 5 }, // dirt
{ "drt", "dt_ground%i", 'D', 1, 5 },
{ "out", "dt_ground%i", 'D', 1, 5 },
{ "grass", "dt_grass1", 'D', 0, 0 },
{ "mud", "dt_carpet1", 'D', 0, 0 }, // FIXME
{ "vent", "dt_ssteel1", 'V', 1, 4 }, // vent
{ "duct", "dt_ssteel1", 'V', 1, 4 },
{ "tile", "dt_smooth%i", 'T', 1, 2 },
{ "labflr", "dt_smooth%i", 'T', 1, 2 },
{ "bath", "dt_smooth%i", 'T', 1, 2 },
{ "grate", "dt_stone%i", 'G', 1, 4 }, // vent
{ "stone", "dt_stone%i", 'G', 1, 4 },
{ "grt", "dt_stone%i", 'G', 1, 4 },
{ "wood", "dt_wood%i", 'W', 1, 3 },
{ "wd", "dt_wood%i", 'W', 1, 3 },
{ "table", "dt_wood%i", 'W', 1, 3 },
{ "board", "dt_wood%i", 'W', 1, 3 },
{ "chair", "dt_wood%i", 'W', 1, 3 },
{ "brd", "dt_wood%i", 'W', 1, 3 },
{ "carp", "dt_carpet1", 'W', 1, 3 },
{ "book", "dt_wood%i", 'W', 1, 3 },
{ "box", "dt_wood%i", 'W', 1, 3 },
{ "cab", "dt_wood%i", 'W', 1, 3 },
{ "couch", "dt_wood%i", 'W', 1, 3 },
{ "crate", "dt_wood%i", 'W', 1, 3 },
{ "poster", "dt_plaster%i", 'W', 1, 2 },
{ "sheet", "dt_plaster%i", 'W', 1, 2 },
{ "stucco", "dt_plaster%i", 'W', 1, 2 },
{ "comp", "dt_smooth1", 'P', 0, 0 },
{ "cmp", "dt_smooth1", 'P', 0, 0 },
{ "elec", "dt_smooth1", 'P', 0, 0 },
{ "vend", "dt_smooth1", 'P', 0, 0 },
{ "monitor", "dt_smooth1", 'P', 0, 0 },
{ "phone", "dt_smooth1", 'P', 0, 0 },
{ "glass", "dt_ssteel1", 'Y', 0, 0 },
{ "window", "dt_ssteel1", 'Y', 0, 0 },
{ "flesh", "dt_rough1", 'F', 0, 0 },
{ "meat", "dt_rough1", 'F', 0, 0 },
{ "fls", "dt_rough1", 'F', 0, 0 },
{ "ground", "dt_ground%i", 'D', 1, 5 },
{ "gnd", "dt_ground%i", 'D', 1, 5 },
{ "snow", "dt_snow%i", 'O', 1, 2 }, // snow
{ NULL, NULL, 0, 0, 0 }
};
qboolean MipExist( const char *name )
{
int i;
if( !name ) return false;
for( i = 0; i < mip_count; i++ )
{
if( !com.stricmp( name, mipnames[i].name ))
return true;
}
return FS_FileExists( name, false );
}
static const char *DetailTextureForName( const char *name )
{
const dmaterial_t *table;
if( !name || !*name ) return NULL;
if( !com.strnicmp( name, "sky", 3 ))
return NULL; // never details for sky
// never apply details for liquids
if( !com.strnicmp( name + 1, "!lava", 5 ))
return NULL;
if( !com.strnicmp( name + 1, "!slime", 6 ))
return NULL;
if( !com.strnicmp( name, "!cur_90", 7 ))
return NULL;
if( !com.strnicmp( name, "!cur_0", 6 ))
return NULL;
if( !com.strnicmp( name, "!cur_270", 8 ))
return NULL;
if( !com.strnicmp( name, "!cur_180", 8 ))
return NULL;
if( !com.strnicmp( name, "!cur_up", 7 ))
return NULL;
if( !com.strnicmp( name, "!cur_dwn", 8 ))
return NULL;
if( name[0] == '!' )
return NULL;
// never apply details to the special textures
if( !com.strnicmp( name, "origin", 6 ))
return NULL;
if( !com.strnicmp( name, "clip", 4 ))
return NULL;
if( !com.strnicmp( name, "hint", 4 ))
return NULL;
if( !com.strnicmp( name, "skip", 4 ))
return NULL;
if( !com.strnicmp( name, "translucent", 11 ))
return NULL;
if( !com.strnicmp( name, "3dsky", 5 ))
return NULL;
if( name[0] == '@' )
return NULL;
// last check ...
if( !com.strnicmp( name, "null", 4 ))
return NULL;
for( table = detail_table; table && table->texname; table++ )
{
if( com.stristr( name, table->texname ))
{
if(( table->lMin + table->lMax ) > 0 )
return va( table->detail, Com_RandomLong( table->lMin, table->lMax ));
return table->detail;
}
}
return "dt_smooth1"; // default
}
void Conv_BspTextures( const char *name, dlump_t *l, const char *ext )
{
dmiptexlump_t *m;
string genericname;
const char *det_name;
mip_t *mip;
int i, k;
int *dofs, size;
byte *buffer;
if( !l->filelen ) return; // no textures stored
FS_FileBase( name, genericname );
m = (dmiptexlump_t *)(bsp_base + l->fileofs);
dofs = m->dataofs;
detail_txt = FS_Open( va( "%s/%s_detail.txt", gs_gamedir, name ), "wb", false );
mipnames = Mem_Realloc( Sys.basepool, mipnames, m->nummiptex * sizeof( dmiptexname_t ));
mip_count = 0;
// first pass: store all names into linear array
for( i = 0; i < m->nummiptex; i++ )
{
if( dofs[i] == -1 ) continue;
// needs to simulate directly loading
mip = (mip_t *)((byte *)m + dofs[i]);
// build detailtexture string
det_name = DetailTextureForName( mip->name ); // lookup both registers
com.strnlwr( mip->name, mip->name, sizeof( mip->name )); // name
// detailtexture detected
if( det_name ) FS_Printf( detail_txt, "%s detail/%s 10.0 10.0\n", mip->name, det_name );
if( !mip->offsets[0] ) continue; // not in bsp, skipped
// check for '*' symbol issues
k = com.strlen( com.strrchr( mip->name, '*' ));
if( k ) mip->name[com.strlen(mip->name)-k] = '!'; // quake1 issues
// some Q1 mods contains blank names (e.g. "after the fall")
if( !com.strlen( mip->name )) com.snprintf( mip->name, 16, "%s_%d", genericname, i );
com.snprintf( mipnames[mip_count].name, 64, "%s/%s.mip", genericname, mip->name ); // build name
mip_count++;
}
// second pass: convert lumps
for( i = 0; i < m->nummiptex; i++ )
{
if( dofs[i] == -1 ) continue;
// needs to simulate direct loading
mip = (mip_t *)((byte *)m + dofs[i]);
if( !mip->offsets[0] ) continue; // not in bsp
buffer = ((byte *)m + dofs[i]); // buffer
size = (int)sizeof(mip_t) + (((mip->width * mip->height) * 85)>>6);
if( bsp_halflife ) size += sizeof(short) + 768; // palette
if( !FS_FileExists( va( "%s/%s/%s.%s", gs_gamedir, name, mip->name, ext ), false ))
ConvMIP( va("%s/%s", name, mip->name ), buffer, size, ext ); // convert it
}
mip_count = 0; // freed and invaliadte
FS_Close( detail_txt );
}
/*
============
ConvBSP
============
*/
qboolean ConvBSP( const char *name, byte *buffer, size_t filesize, const char *ext )
{
dbspheader_t *header = (dbspheader_t *)buffer;
switch( header->version )
{
case 28:
case 29:
bsp_halflife = false;
break;
case 30:
bsp_halflife = true;
break;
default:
return false; // another bsp version
}
bsp_base = (byte*)buffer;
Conv_BspTextures( name, &header->lumps[2], ext ); // LUMP_TEXTURES
return true;
}
qboolean Conv_CheckMap( const char *mapname )
{
file_t *f = FS_Open( mapname, "rb", false );
gbsphdr_t hdr; // generic header
if( !f ) return false;
if( FS_Read( f, &hdr, sizeof(gbsphdr_t)) != sizeof( gbsphdr_t ))
{
FS_Close( f ); // very strange file with size smaller than 8 bytes and ext .bsp
return false;
}
// detect game type
switch( hdr.ident )
{
case 28: // quake 1 beta
case 29: // quake 1 release
game_family = GAME_QUAKE1;
FS_Close( f );
return true;
case 30:
game_family = GAME_HALFLIFE;
FS_Close( f );
return true;
case IDBSPMODHEADER: // continue checking
if( hdr.version == 38 )
{
game_family = GAME_QUAKE2;
FS_Close( f );
return true;
}
break;
case VDBSPMODHEADER: // continue checking
switch( hdr.version )
{
case 18:
game_family = GAME_HALFLIFE2_BETA;
FS_Close( f );
return true;
case 19:
case 20:
game_family = GAME_HALFLIFE2;
FS_Close( f );
return true;
}
}
game_family = GAME_GENERIC;
FS_Close( f );
return false;
}
qboolean Conv_CheckWad( const char *wadname )
{
file_t *f = FS_Open( wadname, "rb", false );
dwadheader_t hdr; // generic header
if( !f ) return false;
if(FS_Read( f, &hdr, sizeof( dwadheader_t )) != sizeof( dwadheader_t ))
{
FS_Close( f ); // very strange file with size smaller than 12 bytes and ext .wad
return false;
}
// detect game type
switch( hdr.ident )
{
case IDIWADHEADER:
case IDPWADHEADER:
game_family = GAME_DOOM1;
FS_Close( f );
break;
case IDWAD2HEADER:
game_family = GAME_QUAKE1;
FS_Close( f );
break;
case IDWAD3HEADER:
game_family = GAME_HALFLIFE;
FS_Close( f );
break;
default:
game_family = GAME_GENERIC;
break;
}
// and check wadnames in case
if(!com.stricmp( wadname, "tnt.wad" ) && game_family == GAME_DOOM1 )
{
Msg( "Wow! Doom2 na TNT!\n" );
}
return (game_family != GAME_GENERIC);
}