933 lines
23 KiB
C++
933 lines
23 KiB
C++
|
/*
|
||
|
wadfile.cpp - reading & writing wad file lumps
|
||
|
Copyright (C) 2015 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 "conprint.h"
|
||
|
#include <windows.h>
|
||
|
#include <direct.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <stdio.h>
|
||
|
#include <io.h>
|
||
|
#include "cmdlib.h"
|
||
|
#include "stringlib.h"
|
||
|
#include "filesystem.h"
|
||
|
#include "wfile.h"
|
||
|
|
||
|
#ifdef ALLOW_WADS_IN_PACKS
|
||
|
long FS_Tell( file_t *file );
|
||
|
int FS_Seek( file_t *file, long offset, int whence );
|
||
|
file_t *FS_Open( const char *filepath, const char *mode, bool gamedironly );
|
||
|
long FS_Read( file_t *file, void *buffer, size_t buffersize );
|
||
|
long FS_Write( file_t *file, const void *data, size_t datasize );
|
||
|
int FS_Close( file_t *file );
|
||
|
#else
|
||
|
#define FS_Tell( x ) tell( x )
|
||
|
#define FS_Seek( x, y, z ) lseek( x, y, z )
|
||
|
#define FS_Read( x, y, z ) read( x, y, z )
|
||
|
#define FS_Write( x, y, z ) write( x, y, z )
|
||
|
#define FS_Close( x ) close( x )
|
||
|
#endif
|
||
|
|
||
|
static int replace_level = REP_IGNORE;
|
||
|
|
||
|
/*
|
||
|
=============================================================================
|
||
|
|
||
|
WADSYSTEM PRIVATE COMMON FUNCTIONS
|
||
|
|
||
|
=============================================================================
|
||
|
*/
|
||
|
// associate extension with wad type
|
||
|
static const wadtype_t wad_types[] =
|
||
|
{
|
||
|
{ "pal", TYP_PALETTE}, // palette
|
||
|
{ "dds", TYP_DDSTEX }, // DDS image
|
||
|
{ "lmp", TYP_GFXPIC }, // quake1, hl pic
|
||
|
{ "fnt", TYP_QFONT }, // hl qfonts
|
||
|
{ "mip", TYP_MIPTEX }, // hl/q1 texture
|
||
|
{ NULL, TYP_NONE } // terminator
|
||
|
};
|
||
|
|
||
|
// suffix converts to img_type and back
|
||
|
const wadtype_t wad_hints[] =
|
||
|
{
|
||
|
{ "", IMG_DIFFUSE }, // no suffix
|
||
|
{ "_mask", IMG_ALPHAMASK }, // alpha-channel stored to another lump
|
||
|
{ "_norm", IMG_NORMALMAP }, // indexed normalmap
|
||
|
{ "_spec", IMG_GLOSSMAP }, // grayscale\color specular
|
||
|
{ "_gpow", IMG_GLOSSPOWER }, // grayscale gloss power
|
||
|
{ "_hmap", IMG_HEIGHTMAP }, // heightmap (can be converted to normalmap)
|
||
|
{ "_luma", IMG_LUMA }, // self-illuminate parts on the diffuse
|
||
|
{ "_adec", IMG_DECAL_ALPHA }, // classic HL-decal (with alpha-channel)
|
||
|
{ "_cdec", IMG_DECAL_COLOR }, // paranoia decal (base 127 127 127)
|
||
|
{ NULL, 0 } // terminator
|
||
|
};
|
||
|
|
||
|
void SetReplaceLevel( int level )
|
||
|
{
|
||
|
if( level < REP_IGNORE ) level = REP_IGNORE;
|
||
|
if( level > REP_FORCE ) level = REP_FORCE;
|
||
|
|
||
|
replace_level = level;
|
||
|
}
|
||
|
|
||
|
int GetReplaceLevel( void )
|
||
|
{
|
||
|
return replace_level;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
W_CheckHandle
|
||
|
|
||
|
return filehandle
|
||
|
===========
|
||
|
*/
|
||
|
static bool W_CheckHandle( wfile_t *wad )
|
||
|
{
|
||
|
if( !wad ) return false;
|
||
|
#ifdef ALLOW_WADS_IN_PACKS
|
||
|
return (wad->handle != NULL) ? true : false;
|
||
|
#else
|
||
|
return (wad->handle >= 0) ? true : false;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
W_GetHandle
|
||
|
|
||
|
return filehandle
|
||
|
===========
|
||
|
*/
|
||
|
int W_GetHandle( wfile_t *wad )
|
||
|
{
|
||
|
if( !wad ) return -1;
|
||
|
#ifndef ALLOW_WADS_IN_PACKS
|
||
|
return wad->handle;
|
||
|
#endif
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
W_TypeFromExt
|
||
|
|
||
|
Extracts file type from extension
|
||
|
===========
|
||
|
*/
|
||
|
char W_TypeFromExt( const char *lumpname )
|
||
|
{
|
||
|
const char *ext = COM_FileExtension( lumpname );
|
||
|
const wadtype_t *type;
|
||
|
|
||
|
// we not known about filetype, so match only by filename
|
||
|
if( !Q_strcmp( ext, "*" ) || !Q_strcmp( ext, "" ))
|
||
|
return TYP_ANY;
|
||
|
|
||
|
for( type = wad_types; type->ext; type++ )
|
||
|
{
|
||
|
if( !Q_stricmp( ext, type->ext ))
|
||
|
return type->type;
|
||
|
}
|
||
|
|
||
|
return TYP_NONE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
W_ExtFromType
|
||
|
|
||
|
Convert type to extension
|
||
|
===========
|
||
|
*/
|
||
|
const char *W_ExtFromType( char lumptype )
|
||
|
{
|
||
|
const wadtype_t *type;
|
||
|
|
||
|
// we not known about filetype, so match only by filename
|
||
|
if( lumptype == TYP_NONE || lumptype == TYP_ANY )
|
||
|
return "";
|
||
|
|
||
|
for( type = wad_types; type->ext; type++ )
|
||
|
{
|
||
|
if( lumptype == type->type )
|
||
|
return type->ext;
|
||
|
}
|
||
|
|
||
|
return "";
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
W_HintFromSuf
|
||
|
|
||
|
Convert name suffix into image type
|
||
|
===========
|
||
|
*/
|
||
|
char W_HintFromSuf( const char *lumpname )
|
||
|
{
|
||
|
char barename[64];
|
||
|
char suffix[8];
|
||
|
size_t namelen;
|
||
|
const wadtype_t *hint;
|
||
|
|
||
|
// trying to extract hint from the name
|
||
|
Q_strncpy( barename, lumpname, sizeof( barename ));
|
||
|
namelen = Q_strlen( barename );
|
||
|
|
||
|
if( namelen <= HINT_NAMELEN )
|
||
|
return IMG_DIFFUSE;
|
||
|
|
||
|
Q_strncpy( suffix, barename + namelen - HINT_NAMELEN, sizeof( suffix ));
|
||
|
|
||
|
// we not known about filetype, so match only by filename
|
||
|
for( hint = wad_hints; hint->ext; hint++ )
|
||
|
{
|
||
|
if( !Q_stricmp( suffix, hint->ext ))
|
||
|
return hint->type;
|
||
|
}
|
||
|
|
||
|
// no any special type was found
|
||
|
return IMG_DIFFUSE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
W_FindLump
|
||
|
|
||
|
Serach for already existed lump
|
||
|
===========
|
||
|
*/
|
||
|
dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const char matchtype )
|
||
|
{
|
||
|
char img_type = IMG_DIFFUSE;
|
||
|
char barename[64], suffix[8];
|
||
|
int left, right;
|
||
|
size_t namelen;
|
||
|
const wadtype_t *hint;
|
||
|
|
||
|
if( !wad || !wad->lumps || matchtype == TYP_NONE )
|
||
|
return NULL;
|
||
|
|
||
|
// trying to extract hint from the name
|
||
|
Q_strncpy( barename, name, sizeof( barename ));
|
||
|
namelen = Q_strlen( barename );
|
||
|
|
||
|
if( namelen > HINT_NAMELEN )
|
||
|
{
|
||
|
Q_strncpy( suffix, barename + namelen - HINT_NAMELEN, sizeof( suffix ));
|
||
|
|
||
|
// we not known about filetype, so match only by filename
|
||
|
for( hint = wad_hints; hint->ext; hint++ )
|
||
|
{
|
||
|
if( !Q_stricmp( suffix, hint->ext ))
|
||
|
{
|
||
|
img_type = hint->type;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( img_type != IMG_DIFFUSE )
|
||
|
barename[namelen - HINT_NAMELEN] = '\0'; // kill the suffix
|
||
|
}
|
||
|
|
||
|
// look for the file (binary search)
|
||
|
left = 0;
|
||
|
right = wad->numlumps - 1;
|
||
|
|
||
|
while( left <= right )
|
||
|
{
|
||
|
int middle = (left + right) / 2;
|
||
|
int diff = Q_stricmp( wad->lumps[middle].name, barename );
|
||
|
|
||
|
if( !diff )
|
||
|
{
|
||
|
if( wad->lumps[middle].img_type > img_type )
|
||
|
diff = 1;
|
||
|
else if( wad->lumps[middle].img_type < img_type )
|
||
|
diff = -1;
|
||
|
else if(( matchtype == TYP_ANY ) || ( matchtype == wad->lumps[middle].type ))
|
||
|
return &wad->lumps[middle]; // found
|
||
|
else if( wad->lumps[middle].type < matchtype )
|
||
|
diff = 1;
|
||
|
else if( wad->lumps[middle].type > matchtype )
|
||
|
diff = -1;
|
||
|
else break; // not found
|
||
|
}
|
||
|
|
||
|
// if we're too far in the list
|
||
|
if( diff > 0 ) right = middle - 1;
|
||
|
else left = middle + 1;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
====================
|
||
|
W_AddFileToWad
|
||
|
|
||
|
Add a file to the list of files contained into a package
|
||
|
and sort LAT in alpha-bethical order
|
||
|
====================
|
||
|
*/
|
||
|
static dlumpinfo_t *W_AddFileToWad( const char *name, wfile_t *wad, dlumpinfo_t *newlump )
|
||
|
{
|
||
|
int left, right;
|
||
|
dlumpinfo_t *plump;
|
||
|
|
||
|
// look for the slot we should put that file into (binary search)
|
||
|
left = 0;
|
||
|
right = wad->numlumps - 1;
|
||
|
|
||
|
while( left <= right )
|
||
|
{
|
||
|
int middle = ( left + right ) / 2;
|
||
|
int diff = Q_stricmp( wad->lumps[middle].name, name );
|
||
|
|
||
|
if( !diff )
|
||
|
{
|
||
|
if( wad->lumps[middle].img_type > newlump->img_type )
|
||
|
diff = 1;
|
||
|
else if( wad->lumps[middle].img_type < newlump->img_type )
|
||
|
diff = -1;
|
||
|
else if( wad->lumps[middle].type < newlump->type )
|
||
|
diff = 1;
|
||
|
else if( wad->lumps[middle].type > newlump->type )
|
||
|
diff = -1;
|
||
|
else MsgDev( D_WARN, "Wad %s contains the file %s several times\n", wad->filename, name );
|
||
|
}
|
||
|
|
||
|
// if we're too far in the list
|
||
|
if( diff > 0 ) right = middle - 1;
|
||
|
else left = middle + 1;
|
||
|
}
|
||
|
|
||
|
// we have to move the right of the list by one slot to free the one we need
|
||
|
plump = &wad->lumps[left];
|
||
|
memmove( plump + 1, plump, ( wad->numlumps - left ) * sizeof( *plump ));
|
||
|
wad->numlumps++;
|
||
|
|
||
|
*plump = *newlump;
|
||
|
memcpy( plump->name, name, sizeof( plump->name ));
|
||
|
|
||
|
return plump;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
W_ReadLump
|
||
|
|
||
|
reading lump into temp buffer
|
||
|
===========
|
||
|
*/
|
||
|
byte *W_ReadLump( wfile_t *wad, dlumpinfo_t *lump, size_t *lumpsizeptr )
|
||
|
{
|
||
|
size_t oldpos, size = 0;
|
||
|
byte *buf;
|
||
|
|
||
|
// assume error
|
||
|
if( lumpsizeptr ) *lumpsizeptr = 0;
|
||
|
|
||
|
// no wads loaded
|
||
|
if( !wad || !lump ) return NULL;
|
||
|
|
||
|
oldpos = FS_Tell( wad->handle ); // don't forget restore original position
|
||
|
|
||
|
if( FS_Seek( wad->handle, lump->filepos, SEEK_SET ) == -1 )
|
||
|
{
|
||
|
MsgDev( D_ERROR, "W_ReadLump: %s is corrupted\n", lump->name );
|
||
|
FS_Seek( wad->handle, oldpos, SEEK_SET );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
buf = (byte *)Mem_Alloc( lump->disksize, C_FILESYSTEM );
|
||
|
size = FS_Read( wad->handle, buf, lump->disksize );
|
||
|
|
||
|
if( size < lump->disksize )
|
||
|
{
|
||
|
MsgDev( D_WARN, "W_ReadLump: %s is probably corrupted\n", lump->name );
|
||
|
FS_Seek( wad->handle, oldpos, SEEK_SET );
|
||
|
Mem_Free( buf, C_FILESYSTEM );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if( lumpsizeptr ) *lumpsizeptr = lump->disksize;
|
||
|
FS_Seek( wad->handle, oldpos, SEEK_SET );
|
||
|
|
||
|
return buf;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
W_WriteLump
|
||
|
|
||
|
compress and write lump
|
||
|
===========
|
||
|
*/
|
||
|
bool W_WriteLump( wfile_t *wad, dlumpinfo_t *lump, const void *data, size_t datasize )
|
||
|
{
|
||
|
if( !wad || !lump ) return false;
|
||
|
|
||
|
if( !data || !datasize )
|
||
|
{
|
||
|
MsgDev( D_WARN, "W_WriteLump: ignore blank lump %s - nothing to save\n", lump->name );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if( wad->mode == O_RDONLY )
|
||
|
{
|
||
|
MsgDev( D_ERROR, "W_WriteLump: %s opened in readonly mode\n", wad->filename );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
lump->size = lump->disksize = datasize;
|
||
|
|
||
|
if( FS_Write( wad->handle, data, datasize ) == datasize )
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static bool W_SysOpen( wfile_t *wad, const char *filename, const char *mode, bool ext_path )
|
||
|
{
|
||
|
#ifndef ALLOW_WADS_IN_PACKS
|
||
|
int mod, opt;
|
||
|
|
||
|
// parse the mode string
|
||
|
switch( mode[0] )
|
||
|
{
|
||
|
case 'r':
|
||
|
mod = O_RDONLY;
|
||
|
opt = 0;
|
||
|
break;
|
||
|
case 'w':
|
||
|
mod = O_WRONLY;
|
||
|
opt = O_CREAT|O_TRUNC;
|
||
|
break;
|
||
|
case 'a':
|
||
|
mod = O_WRONLY;
|
||
|
opt = O_CREAT;
|
||
|
break;
|
||
|
default:
|
||
|
MsgDev( D_ERROR, "W_Open(%s, %s): invalid mode\n", filename, mode );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for( int ind = 1; mode[ind] != '\0'; ind++ )
|
||
|
{
|
||
|
switch( mode[ind] )
|
||
|
{
|
||
|
case '+':
|
||
|
mod = O_RDWR;
|
||
|
break;
|
||
|
case 'b':
|
||
|
opt |= O_BINARY;
|
||
|
break;
|
||
|
default:
|
||
|
MsgDev( D_ERROR, "W_Open: %s: unknown char in mode (%c)\n", filename, mode, mode[ind] );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
wad->handle = open( filename, mod|opt, 0666 );
|
||
|
return true;
|
||
|
#else
|
||
|
// NOTE: FS_Open is load wad file from the first pak in the list (while ext_path is false)
|
||
|
if( ext_path ) wad->handle = FS_Open( filename, "rb", false );
|
||
|
else wad->handle = FS_Open( COM_FileWithoutPath( filename ), "rb", false );
|
||
|
return true;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=============================================================================
|
||
|
|
||
|
WADSYSTEM PUBLIC BASE FUNCTIONS
|
||
|
|
||
|
=============================================================================
|
||
|
*/
|
||
|
/*
|
||
|
===========
|
||
|
W_Open
|
||
|
|
||
|
open the wad for reading & writing
|
||
|
===========
|
||
|
*/
|
||
|
wfile_t *W_Open( const char *filename, const char *mode, int *error, bool ext_path )
|
||
|
{
|
||
|
dwadinfo_t header;
|
||
|
wfile_t *wad = (wfile_t *)Mem_Alloc( sizeof( wfile_t ), C_FILESYSTEM );
|
||
|
const char *comment = "Generated by Xash3D MakeWad tool.\0";
|
||
|
int ind, mod, opt, lumpcount;
|
||
|
|
||
|
// parse the mode string
|
||
|
switch( mode[0] )
|
||
|
{
|
||
|
case 'r':
|
||
|
mod = O_RDONLY;
|
||
|
opt = 0;
|
||
|
break;
|
||
|
case 'w':
|
||
|
mod = O_WRONLY;
|
||
|
opt = O_CREAT|O_TRUNC;
|
||
|
break;
|
||
|
case 'a':
|
||
|
mod = O_WRONLY;
|
||
|
opt = O_CREAT;
|
||
|
break;
|
||
|
default:
|
||
|
MsgDev( D_ERROR, "W_Open(%s, %s): invalid mode\n", filename, mode );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
for( ind = 1; mode[ind] != '\0'; ind++ )
|
||
|
{
|
||
|
switch( mode[ind] )
|
||
|
{
|
||
|
case '+':
|
||
|
mod = O_RDWR;
|
||
|
break;
|
||
|
case 'b':
|
||
|
opt |= O_BINARY;
|
||
|
break;
|
||
|
default:
|
||
|
MsgDev( D_ERROR, "W_Open: %s: unknown char in mode (%c)\n", filename, mode, mode[ind] );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef ALLOW_WADS_IN_PACKS
|
||
|
// NOTE: FS_Open is load wad file from the first pak in the list (while ext_path is false)
|
||
|
if( ext_path ) wad->handle = FS_Open( filename, "rb", false );
|
||
|
else wad->handle = FS_Open( COM_FileWithoutPath( filename ), "rb", false );
|
||
|
#else
|
||
|
wad->handle = open( filename, mod|opt, 0666 );
|
||
|
#endif
|
||
|
if( !W_CheckHandle( wad ))
|
||
|
{
|
||
|
MsgDev( D_ERROR, "W_Open: couldn't open %s\n", filename );
|
||
|
if( error ) *error = WAD_LOAD_COULDNT_OPEN;
|
||
|
W_Close( wad );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// copy wad name
|
||
|
Q_strncpy( wad->filename, filename, sizeof( wad->filename ));
|
||
|
wad->filetime = COM_FileTime( filename );
|
||
|
FS_Seek( wad->handle, 0, SEEK_END );
|
||
|
size_t wadsize = FS_Tell( wad->handle );
|
||
|
FS_Seek( wad->handle, 0, SEEK_SET );
|
||
|
|
||
|
// if the file is opened in "write", "append", or "read/write" mode
|
||
|
if( mod == O_WRONLY || !wadsize )
|
||
|
{
|
||
|
dwadinfo_t hdr;
|
||
|
|
||
|
wad->numlumps = 0; // blank wad
|
||
|
wad->lumps = NULL; //
|
||
|
wad->mode = O_WRONLY;
|
||
|
|
||
|
// save space for header
|
||
|
hdr.ident = IDWAD3HEADER;
|
||
|
hdr.numlumps = wad->numlumps;
|
||
|
hdr.infotableofs = sizeof( dwadinfo_t );
|
||
|
FS_Write( wad->handle, &hdr, sizeof( hdr ));
|
||
|
FS_Write( wad->handle, comment, Q_strlen( comment ) + 1 );
|
||
|
wad->infotableofs = FS_Tell( wad->handle );
|
||
|
}
|
||
|
else if( mod == O_RDWR || mod == O_RDONLY )
|
||
|
{
|
||
|
if( mod == O_RDWR )
|
||
|
wad->mode = O_APPEND;
|
||
|
else wad->mode = O_RDONLY;
|
||
|
|
||
|
if( FS_Read( wad->handle, &header, sizeof( dwadinfo_t )) != sizeof( dwadinfo_t ))
|
||
|
{
|
||
|
MsgDev( D_ERROR, "W_Open: %s can't read header\n", filename );
|
||
|
if( error ) *error = WAD_LOAD_BAD_HEADER;
|
||
|
W_Close( wad );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if( header.ident != IDWAD3HEADER )
|
||
|
{
|
||
|
if( header.ident != IDWAD2HEADER )
|
||
|
{
|
||
|
MsgDev( D_ERROR, "W_Open: %s is not a WAD3 file\n", filename );
|
||
|
if( error ) *error = WAD_LOAD_BAD_HEADER;
|
||
|
}
|
||
|
else if( error )
|
||
|
*error = WAD_LOAD_NO_FILES;// shut up the warnings
|
||
|
|
||
|
W_Close( wad );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
lumpcount = header.numlumps;
|
||
|
|
||
|
if( lumpcount >= MAX_FILES_IN_WAD && wad->mode == O_APPEND )
|
||
|
{
|
||
|
MsgDev( D_WARN, "W_Open: %s is full (%i lumps)\n", filename, lumpcount );
|
||
|
if( error ) *error = WAD_LOAD_TOO_MANY_FILES;
|
||
|
wad->mode = O_RDONLY; // set read-only mode
|
||
|
}
|
||
|
else if( lumpcount <= 0 && wad->mode == O_RDONLY )
|
||
|
{
|
||
|
MsgDev( D_ERROR, "W_Open: %s has no lumps\n", filename );
|
||
|
if( error ) *error = WAD_LOAD_NO_FILES;
|
||
|
W_Close( wad );
|
||
|
return NULL;
|
||
|
}
|
||
|
else if( error ) *error = WAD_LOAD_OK;
|
||
|
|
||
|
wad->infotableofs = header.infotableofs; // save infotableofs position
|
||
|
|
||
|
if( FS_Seek( wad->handle, wad->infotableofs, SEEK_SET ) == -1 )
|
||
|
{
|
||
|
MsgDev( D_ERROR, "W_Open: %s can't find lump allocation table\n", filename );
|
||
|
if( error ) *error = WAD_LOAD_BAD_FOLDERS;
|
||
|
W_Close( wad );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
size_t lat_size = lumpcount * sizeof( dlumpinfo_t );
|
||
|
|
||
|
// NOTE: lumps table can be reallocated for O_APPEND mode
|
||
|
dlumpinfo_t *srclumps = (dlumpinfo_t *)Mem_Alloc( lat_size, C_FILESYSTEM );
|
||
|
|
||
|
if( FS_Read( wad->handle, srclumps, lat_size ) != lat_size )
|
||
|
{
|
||
|
MsgDev( D_ERROR, "W_ReadLumpTable: %s has corrupted lump allocation table\n", wad->filename );
|
||
|
if( error ) *error = WAD_LOAD_CORRUPTED;
|
||
|
Mem_Free( srclumps, C_FILESYSTEM );
|
||
|
W_Close( wad );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// starting to add lumps
|
||
|
wad->lumps = (dlumpinfo_t *)Mem_Alloc( lat_size, C_FILESYSTEM );
|
||
|
wad->numlumps = 0;
|
||
|
|
||
|
// sort lumps for binary search
|
||
|
for( int i = 0; i < lumpcount; i++ )
|
||
|
{
|
||
|
char name[16];
|
||
|
int k;
|
||
|
|
||
|
// cleanup lumpname
|
||
|
Q_strncpy( name, srclumps[i].name, sizeof( name ));
|
||
|
|
||
|
// check for '*' symbol issues (quake1)
|
||
|
k = Q_strlen( Q_strrchr( name, '*' ));
|
||
|
if( k ) name[Q_strlen( name ) - k] = '!'; // quake1 issues (can't save images that contain '*' symbol)
|
||
|
|
||
|
// fixups bad image types (some quake wads)
|
||
|
if( srclumps[i].img_type < 0 || srclumps[i].img_type > IMG_DECAL_COLOR )
|
||
|
srclumps[i].img_type = IMG_DIFFUSE;
|
||
|
|
||
|
W_AddFileToWad( name, wad, &srclumps[i] );
|
||
|
}
|
||
|
|
||
|
// release source lumps
|
||
|
Mem_Free( srclumps, C_FILESYSTEM );
|
||
|
|
||
|
// if we are in append mode - we need started from infotableofs poisition
|
||
|
// overwrite lumptable as well, we have her copy in wad->lumps
|
||
|
if( wad->mode == O_APPEND )
|
||
|
FS_Seek( wad->handle, wad->infotableofs, SEEK_SET );
|
||
|
}
|
||
|
|
||
|
// and leave the file open
|
||
|
return wad;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
W_Close
|
||
|
|
||
|
finalize wad or just close
|
||
|
===========
|
||
|
*/
|
||
|
void W_Close( wfile_t *wad )
|
||
|
{
|
||
|
if( !wad ) return;
|
||
|
|
||
|
if( W_CheckHandle( wad ) && ( wad->mode == O_APPEND || wad->mode == O_WRONLY ))
|
||
|
{
|
||
|
dwadinfo_t hdr;
|
||
|
|
||
|
// write the lumpinfo
|
||
|
size_t ofs = FS_Tell( wad->handle );
|
||
|
FS_Write( wad->handle, wad->lumps, wad->numlumps * sizeof( dlumpinfo_t ));
|
||
|
|
||
|
// write the header
|
||
|
hdr.ident = IDWAD3HEADER;
|
||
|
hdr.numlumps = wad->numlumps;
|
||
|
hdr.infotableofs = ofs;
|
||
|
|
||
|
FS_Seek( wad->handle, 0, SEEK_SET );
|
||
|
FS_Write( wad->handle, &hdr, sizeof( hdr ));
|
||
|
}
|
||
|
|
||
|
if( wad->lumps )
|
||
|
Mem_Free( wad->lumps, C_FILESYSTEM );
|
||
|
|
||
|
if( W_CheckHandle( wad ))
|
||
|
FS_Close( wad->handle );
|
||
|
Mem_Free( wad, C_FILESYSTEM ); // free himself
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
W_SaveLump
|
||
|
|
||
|
write new or replace existed lump
|
||
|
===========
|
||
|
*/
|
||
|
int W_SaveLump( wfile_t *wad, const char *lump, const void *data, size_t datasize, char type, char attribs )
|
||
|
{
|
||
|
dlumpinfo_t *find, newlump;
|
||
|
int lat_size, oldpos;
|
||
|
char lumpname[64];
|
||
|
|
||
|
if( !wad || !lump ) return -1;
|
||
|
|
||
|
if( !data || !datasize )
|
||
|
{
|
||
|
MsgDev( D_WARN, "W_SaveLump: ignore blank lump %s - nothing to save\n", lump );
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if( wad->mode == O_RDONLY )
|
||
|
{
|
||
|
MsgDev( D_ERROR, "W_SaveLump: %s opened in readonly mode\n", wad->filename );
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if( wad->numlumps >= MAX_FILES_IN_WAD )
|
||
|
{
|
||
|
MsgDev( D_ERROR, "W_SaveLump: %s is full\n", wad->filename );
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
find = W_FindLump( wad, lump, type );
|
||
|
|
||
|
if( find != NULL )
|
||
|
{
|
||
|
switch( replace_level )
|
||
|
{
|
||
|
case REP_IGNORE:
|
||
|
MsgDev( D_ERROR, "W_SaveLump: %s already exist\n", lump );
|
||
|
return -1;
|
||
|
case REP_NORMAL:
|
||
|
case REP_FORCE:
|
||
|
if( FBitSet( find->attribs, ATTR_READONLY ))
|
||
|
{
|
||
|
// g-cont. i left this limitation as a protect of the replacement of compressed lumps
|
||
|
MsgDev( D_ERROR, "W_ReplaceLump: %s is read-only\n", find->name );
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if( datasize != find->size )
|
||
|
{
|
||
|
MsgDev( D_ERROR, "W_ReplaceLump: %s [%s] should be [%s]\n",
|
||
|
lumpname, Q_memprint( datasize ), Q_memprint( find->size ));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
oldpos = FS_Tell( wad->handle ); // don't forget restore original position
|
||
|
|
||
|
if( FS_Seek( wad->handle, find->filepos, SEEK_SET ) == -1 )
|
||
|
{
|
||
|
MsgDev( D_ERROR, "W_ReplaceLump: %s is corrupted\n", find->name );
|
||
|
FS_Seek( wad->handle, oldpos, SEEK_SET );
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if( FS_Write( wad->handle, data, datasize ) != find->disksize )
|
||
|
MsgDev( D_WARN, "W_ReplaceLump: %s probably replaced with errors\n", find->name );
|
||
|
|
||
|
// restore old position
|
||
|
FS_Seek( wad->handle, oldpos, SEEK_SET );
|
||
|
|
||
|
return wad->numlumps;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// prepare lump name
|
||
|
Q_strncpy( lumpname, lump, sizeof( lumpname ));
|
||
|
|
||
|
// extract image hint
|
||
|
char hint = W_HintFromSuf( lumpname );
|
||
|
if( hint != IMG_DIFFUSE ) lumpname[Q_strlen( lumpname ) - HINT_NAMELEN] = '\0'; // kill the suffix
|
||
|
|
||
|
if( Q_strlen( lumpname ) >= WAD3_NAMELEN )
|
||
|
{
|
||
|
// name is too long
|
||
|
MsgDev( D_ERROR, "W_SaveLump: %s more than %i symbols\n", lumpname, WAD3_NAMELEN );
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
lat_size = sizeof( dlumpinfo_t ) * (wad->numlumps + 1);
|
||
|
|
||
|
// reallocate lumptable
|
||
|
wad->lumps = (dlumpinfo_t *)Mem_Realloc( wad->lumps, lat_size, C_FILESYSTEM );
|
||
|
|
||
|
memset( &newlump, 0, sizeof( newlump ));
|
||
|
|
||
|
// write header
|
||
|
Q_strncpy( newlump.name, lumpname, WAD3_NAMELEN );
|
||
|
newlump.filepos = FS_Tell( wad->handle );
|
||
|
newlump.attribs = attribs;
|
||
|
newlump.img_type = hint;
|
||
|
newlump.type = type;
|
||
|
|
||
|
if( !W_WriteLump( wad, &newlump, data, datasize ))
|
||
|
return -1;
|
||
|
|
||
|
// record entry and re-sort table
|
||
|
W_AddFileToWad( lumpname, wad, &newlump );
|
||
|
|
||
|
MsgDev( D_REPORT, "W_SaveLump: %s, size %s\n", newlump.name, Q_memprint( newlump.disksize ));
|
||
|
|
||
|
return wad->numlumps;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
W_LoadLump
|
||
|
|
||
|
loading lump into the tmp buffer
|
||
|
===========
|
||
|
*/
|
||
|
byte *W_LoadLump( wfile_t *wad, const char *lumpname, size_t *lumpsizeptr, const char type )
|
||
|
{
|
||
|
// assume error
|
||
|
if( lumpsizeptr ) *lumpsizeptr = 0;
|
||
|
|
||
|
if( !wad ) return NULL;
|
||
|
dlumpinfo_t *lump = W_FindLump( wad, lumpname, type );
|
||
|
return W_ReadLump( wad, lump, lumpsizeptr );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
W_FindMiptex
|
||
|
|
||
|
find specified lump for .mip
|
||
|
===========
|
||
|
*/
|
||
|
dlumpinfo_t *W_FindMiptex( wfile_t *wad, const char *name )
|
||
|
{
|
||
|
return W_FindLump( wad, name, TYP_MIPTEX );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
W_FindLmptex
|
||
|
|
||
|
find specified lump for .lmp
|
||
|
===========
|
||
|
*/
|
||
|
dlumpinfo_t *W_FindLmptex( wfile_t *wad, const char *name )
|
||
|
{
|
||
|
return W_FindLump( wad, name, TYP_GFXPIC );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
W_SearchForFile
|
||
|
|
||
|
search in wad for filename
|
||
|
===========
|
||
|
*/
|
||
|
void W_SearchForFile( wfile_t *wad, const char *pattern, stringlist_t *resultlist )
|
||
|
{
|
||
|
char wadpattern[256], wadname[256], temp2[256];
|
||
|
const char *slash, *backslash, *colon, *separator;
|
||
|
char type = W_TypeFromExt( pattern );
|
||
|
char wadfolder[256], temp[1024];
|
||
|
bool anywadname = true;
|
||
|
int resultlistindex;
|
||
|
|
||
|
if( !wad ) return;
|
||
|
|
||
|
// quick reject by filetype
|
||
|
if( type == TYP_NONE ) return;
|
||
|
COM_ExtractFilePath( pattern, wadname );
|
||
|
COM_FileBase( pattern, wadpattern );
|
||
|
wadfolder[0] = '\0';
|
||
|
|
||
|
if( Q_strlen( wadname ))
|
||
|
{
|
||
|
COM_FileBase( wadname, wadname );
|
||
|
Q_strncpy( wadfolder, wadname, sizeof( wadfolder ));
|
||
|
COM_DefaultExtension( wadname, ".wad" );
|
||
|
anywadname = false;
|
||
|
}
|
||
|
|
||
|
// make wadname from wad fullpath
|
||
|
COM_FileBase( wad->filename, temp2 );
|
||
|
COM_DefaultExtension( temp2, ".wad" );
|
||
|
|
||
|
// quick reject by wadname
|
||
|
if( !anywadname && Q_stricmp( wadname, temp2 ))
|
||
|
return;
|
||
|
|
||
|
// look through all the wad file elements
|
||
|
for( int i = 0; i < wad->numlumps; i++ )
|
||
|
{
|
||
|
// if type not matching, we already have no chance ...
|
||
|
if( type != TYP_ANY && wad->lumps[i].type != type )
|
||
|
continue;
|
||
|
|
||
|
// build the lumpname with image suffix (if present)
|
||
|
Q_snprintf( temp, sizeof( temp ), "%s%s", wad->lumps[i].name, wad_hints[wad->lumps[i].img_type].ext );
|
||
|
|
||
|
while( temp[0] )
|
||
|
{
|
||
|
if( matchpattern( temp, wadpattern, true ))
|
||
|
{
|
||
|
for( resultlistindex = 0; resultlistindex < resultlist->numstrings; resultlistindex++ )
|
||
|
{
|
||
|
if( !Q_strcmp( resultlist->strings[resultlistindex], temp ))
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if( resultlistindex == resultlist->numstrings )
|
||
|
{
|
||
|
// build path: wadname/lumpname.ext
|
||
|
Q_snprintf( temp2, sizeof( temp2 ), "%s/%s.%s", wadfolder, temp,
|
||
|
W_ExtFromType( wad->lumps[i].type ));
|
||
|
stringlistappend( resultlist, temp2 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// strip off one path element at a time until empty
|
||
|
// this way directories are added to the listing if they match the pattern
|
||
|
slash = Q_strrchr( temp, '/' );
|
||
|
backslash = Q_strrchr( temp, '\\' );
|
||
|
colon = Q_strrchr( temp, ':' );
|
||
|
separator = temp;
|
||
|
if( separator < slash )
|
||
|
separator = slash;
|
||
|
if( separator < backslash )
|
||
|
separator = backslash;
|
||
|
if( separator < colon )
|
||
|
separator = colon;
|
||
|
*((char *)separator) = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|