diff options
Diffstat (limited to 'utils/xwad')
| -rw-r--r-- | utils/xwad/goldsrc_bspfile.h | 310 | ||||
| -rw-r--r-- | utils/xwad/goldsrc_standin.cpp | 322 | ||||
| -rw-r--r-- | utils/xwad/goldsrc_standin.h | 42 | ||||
| -rw-r--r-- | utils/xwad/lbmlib.cpp | 741 | ||||
| -rw-r--r-- | utils/xwad/lbmlib.h | 55 | ||||
| -rw-r--r-- | utils/xwad/wadlib.cpp | 336 | ||||
| -rw-r--r-- | utils/xwad/wadlib.h | 61 | ||||
| -rw-r--r-- | utils/xwad/xwad.cpp | 1182 | ||||
| -rw-r--r-- | utils/xwad/xwad.vcproj | 185 |
9 files changed, 3234 insertions, 0 deletions
diff --git a/utils/xwad/goldsrc_bspfile.h b/utils/xwad/goldsrc_bspfile.h new file mode 100644 index 0000000..2ae1812 --- /dev/null +++ b/utils/xwad/goldsrc_bspfile.h @@ -0,0 +1,310 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef BSPFILE_H +#define BSPFILE_H + +// upper design bounds + +#define MAX_MAP_HULLS 4 + +#define MAX_MAP_MODELS 400 +#define MAX_MAP_BRUSHES 4096 +#define MAX_MAP_ENTITIES 1024 +#define MAX_MAP_ENTSTRING (128*1024) + +#define MAX_MAP_PLANES 32767 +#define MAX_MAP_NODES 32767 // because negative shorts are contents +#define MAX_MAP_CLIPNODES 32767 // +#define MAX_MAP_LEAFS 8192 +#define MAX_MAP_VERTS 65535 +#define MAX_MAP_FACES 65535 +#define MAX_MAP_MARKSURFACES 65535 +#define MAX_MAP_TEXINFO 8192 +#define MAX_MAP_EDGES 256000 +#define MAX_MAP_SURFEDGES 512000 +#define MAX_MAP_TEXTURES 512 +#define MAX_MAP_MIPTEX 0x400000 +#define MAX_MAP_LIGHTING 0x100000 +#define MAX_MAP_VISIBILITY 0x100000 + +#define MAX_MAP_PORTALS 65536 + +// key / value pair sizes + +#define MAX_KEY 32 +#define MAX_VALUE 1024 + +//============================================================================= + + +#define BSPVERSION 30 +#define TOOLVERSION 2 + +typedef struct +{ + int fileofs, filelen; +} lump_t; + +#define LUMP_ENTITIES 0 +#define LUMP_PLANES 1 +#define LUMP_TEXTURES 2 +#define LUMP_VERTEXES 3 +#define LUMP_VISIBILITY 4 +#define LUMP_NODES 5 +#define LUMP_TEXINFO 6 +#define LUMP_FACES 7 +#define LUMP_LIGHTING 8 +#define LUMP_CLIPNODES 9 +#define LUMP_LEAFS 10 +#define LUMP_MARKSURFACES 11 +#define LUMP_EDGES 12 +#define LUMP_SURFEDGES 13 +#define LUMP_MODELS 14 + +#define HEADER_LUMPS 15 + +typedef struct +{ + float mins[3], maxs[3]; + float origin[3]; + int headnode[MAX_MAP_HULLS]; + int visleafs; // not including the solid leaf 0 + int firstface, numfaces; +} dmodel_t; + +typedef struct +{ + int version; + lump_t lumps[HEADER_LUMPS]; +} dheader_t; + +typedef struct +{ + int nummiptex; + int dataofs[4]; // [nummiptex] +} dmiptexlump_t; + +#define MIPLEVELS 4 +typedef struct miptex_s +{ + char name[16]; + unsigned width, height; + unsigned offsets[MIPLEVELS]; // four mip maps stored +} miptex_t; + + +typedef struct +{ + float point[3]; +} dvertex_t; + + +// 0-2 are axial planes +#define PLANE_X 0 +#define PLANE_Y 1 +#define PLANE_Z 2 + +// 3-5 are non-axial planes snapped to the nearest +#define PLANE_ANYX 3 +#define PLANE_ANYY 4 +#define PLANE_ANYZ 5 + +typedef struct +{ + float normal[3]; + float dist; + int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate +} dplane_t; + + +/*** -1..-6 now in const.h ***/ +#define CONTENTS_ORIGIN -7 // removed at csg time +#define CONTENTS_CLIP -8 // changed to contents_solid +#define CONTENTS_CURRENT_0 -9 +#define CONTENTS_CURRENT_90 -10 +#define CONTENTS_CURRENT_180 -11 +#define CONTENTS_CURRENT_270 -12 +#define CONTENTS_CURRENT_UP -13 +#define CONTENTS_CURRENT_DOWN -14 + +#define CONTENTS_TRANSLUCENT -15 + + +// !!! if this is changed, it must be changed in asm_i386.h too !!! +typedef struct +{ + int planenum; + short children[2]; // negative numbers are -(leafs+1), not nodes + short mins[3]; // for sphere culling + short maxs[3]; + unsigned short firstface; + unsigned short numfaces; // counting both sides +} dnode_t; + +typedef struct +{ + int planenum; + short children[2]; // negative numbers are contents +} dclipnode_t; + + +typedef struct texinfo_s +{ + float vecs[2][4]; // [s/t][xyz offset] + int miptex; + int flags; +} texinfo_t; +#define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision + +// note that edge 0 is never used, because negative edge nums are used for +// counterclockwise use of the edge in a face +typedef struct +{ + unsigned short v[2]; // vertex numbers +} dedge_t; + +#define MAXLIGHTMAPS 4 +typedef struct +{ + short planenum; + short side; + + int firstedge; // we must support > 64k edges + short numedges; + short texinfo; + +// lighting info + byte styles[MAXLIGHTMAPS]; + int lightofs; // start of [numstyles*surfsize] samples +} dface_t; + + + +#define AMBIENT_WATER 0 +#define AMBIENT_SKY 1 +#define AMBIENT_SLIME 2 +#define AMBIENT_LAVA 3 + +#define NUM_AMBIENTS 4 // automatic ambient sounds + +// leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas +// all other leafs need visibility info +typedef struct +{ + int contents; + int visofs; // -1 = no visibility info + + short mins[3]; // for frustum culling + short maxs[3]; + + unsigned short firstmarksurface; + unsigned short nummarksurfaces; + + byte ambient_level[NUM_AMBIENTS]; +} dleaf_t; + + +//============================================================================ + +#ifndef QUAKE_GAME + +#define ANGLE_UP -1 +#define ANGLE_DOWN -2 + + +// the utilities get to be lazy and just use large static arrays + +extern int nummodels; +extern dmodel_t dmodels[MAX_MAP_MODELS]; + +extern int visdatasize; +extern byte dvisdata[MAX_MAP_VISIBILITY]; + +extern int lightdatasize; +extern byte dlightdata[MAX_MAP_LIGHTING]; + +extern int texdatasize; +extern byte dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t) + +extern int entdatasize; +extern char dentdata[MAX_MAP_ENTSTRING]; + +extern int numleafs; +extern dleaf_t dleafs[MAX_MAP_LEAFS]; + +extern int numplanes; +extern dplane_t dplanes[MAX_MAP_PLANES]; + +extern int numvertexes; +extern dvertex_t dvertexes[MAX_MAP_VERTS]; + +extern int numnodes; +extern dnode_t dnodes[MAX_MAP_NODES]; + +extern int numtexinfo; +extern texinfo_t texinfo[MAX_MAP_TEXINFO]; + +extern int numfaces; +extern dface_t dfaces[MAX_MAP_FACES]; + +extern int numclipnodes; +extern dclipnode_t dclipnodes[MAX_MAP_CLIPNODES]; + +extern int numedges; +extern dedge_t dedges[MAX_MAP_EDGES]; + +extern int nummarksurfaces; +extern unsigned short dmarksurfaces[MAX_MAP_MARKSURFACES]; + +extern int numsurfedges; +extern int dsurfedges[MAX_MAP_SURFEDGES]; + + +void DecompressVis (byte *in, byte *decompressed); +int CompressVis (byte *vis, byte *dest); + +void LoadBSPFile (char *filename); +void WriteBSPFile (char *filename); +void PrintBSPFileSizes (void); + +//=============== + + +typedef struct epair_s +{ + struct epair_s *next; + char *key; + char *value; +} epair_t; + +typedef struct +{ + vec3_t origin; + int firstbrush; + int numbrushes; + epair_t *epairs; +} entity_t; + +extern int num_entities; +extern entity_t entities[MAX_MAP_ENTITIES]; + +void ParseEntities (void); +void UnparseEntities (void); + +void SetKeyValue (entity_t *ent, char *key, char *value); +char *ValueForKey (entity_t *ent, char *key); +// will return "" if not present + +vec_t FloatForKey (entity_t *ent, char *key); +void GetVectorForKey (entity_t *ent, char *key, vec3_t vec); + +epair_t *ParseEpair (void); + +#endif + +#endif diff --git a/utils/xwad/goldsrc_standin.cpp b/utils/xwad/goldsrc_standin.cpp new file mode 100644 index 0000000..8ae8df0 --- /dev/null +++ b/utils/xwad/goldsrc_standin.cpp @@ -0,0 +1,322 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include <windows.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include "goldsrc_standin.h" + + +extern int errno; + + +static unsigned short g_InitialColor = 0xFFFF; +static unsigned short g_LastColor = 0xFFFF; +static unsigned short g_BadColor = 0xFFFF; +static WORD g_BackgroundFlags = 0xFFFF; +static bool g_bGotInitialColors = false; + +static void GetInitialColors( ) +{ + if ( g_bGotInitialColors ) + return; + + g_bGotInitialColors = true; + + // Get the old background attributes. + CONSOLE_SCREEN_BUFFER_INFO oldInfo; + GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &oldInfo ); + g_InitialColor = g_LastColor = oldInfo.wAttributes & (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY); + g_BackgroundFlags = oldInfo.wAttributes & (BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_INTENSITY); + + g_BadColor = 0; + if (g_BackgroundFlags & BACKGROUND_RED) + g_BadColor |= FOREGROUND_RED; + if (g_BackgroundFlags & BACKGROUND_GREEN) + g_BadColor |= FOREGROUND_GREEN; + if (g_BackgroundFlags & BACKGROUND_BLUE) + g_BadColor |= FOREGROUND_BLUE; + if (g_BackgroundFlags & BACKGROUND_INTENSITY) + g_BadColor |= FOREGROUND_INTENSITY; +} + +static WORD SetConsoleTextColor( int red, int green, int blue, int intensity ) +{ + GetInitialColors(); + + WORD ret = g_LastColor; + + g_LastColor = 0; + if( red ) g_LastColor |= FOREGROUND_RED; + if( green ) g_LastColor |= FOREGROUND_GREEN; + if( blue ) g_LastColor |= FOREGROUND_BLUE; + if( intensity ) g_LastColor |= FOREGROUND_INTENSITY; + + // Just use the initial color if there's a match... + if (g_LastColor == g_BadColor) + g_LastColor = g_InitialColor; + + SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), g_LastColor | g_BackgroundFlags ); + return ret; +} + + +static void RestoreConsoleTextColor( WORD color ) +{ + SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), color | g_BackgroundFlags ); + g_LastColor = color; +} + + +void Msg( const char *pMsg, ... ) +{ + va_list marker; + va_start( marker, pMsg ); + vprintf( pMsg, marker ); + va_end( marker ); +} + + +void Warning( const char *pMsg, ... ) +{ + WORD old = SetConsoleTextColor( 1, 1, 0, 1 ); + + va_list marker; + va_start( marker, pMsg ); + vprintf( pMsg, marker ); + va_end( marker ); + + RestoreConsoleTextColor( old ); +} + + +void Error (const char *error, ...) +{ + WORD old = SetConsoleTextColor( 1, 0, 0, 1 ); + + va_list argptr; + + printf ("\n************ ERROR ************\n"); + + va_start (argptr,error); + vprintf (error,argptr); + va_end (argptr); + printf ("\n"); + + extern void PrintExitStuff(); + PrintExitStuff(); + + RestoreConsoleTextColor( old ); + + TerminateProcess( GetCurrentProcess(), 100 ); +} + + +/* +================ +filelength +================ +*/ +int filelength (FILE *f) +{ + int pos; + int end; + + pos = ftell (f); + fseek (f, 0, SEEK_END); + end = ftell (f); + fseek (f, pos, SEEK_SET); + + return end; +} + + +FILE *SafeOpenWrite (char *filename) +{ + FILE *f; + + f = fopen(filename, "wb"); + + if (!f) + Error ("Error opening %s: %s",filename,strerror(errno)); + + return f; +} + +FILE *SafeOpenRead (char *filename) +{ + FILE *f; + + f = fopen(filename, "rb"); + + if (!f) + Error ("Error opening %s: %s",filename,strerror(errno)); + + return f; +} + + +void SafeRead (FILE *f, void *buffer, int count) +{ + if ( fread (buffer, 1, count, f) != (size_t)count) + Error ("File read failure"); +} + + +void SafeWrite (FILE *f, void *buffer, int count) +{ + if (fwrite (buffer, 1, count, f) != (size_t)count) + Error ("File read failure"); +} + + + +/* +============== +LoadFile +============== +*/ +int LoadFile (char *filename, void **bufferptr) +{ + FILE *f; + int length; + void *buffer; + + f = SafeOpenRead (filename); + length = filelength (f); + buffer = malloc (length+1); + ((char *)buffer)[length] = 0; + SafeRead (f, buffer, length); + fclose (f); + + *bufferptr = buffer; + return length; +} + + +void SaveFile (char *filename, void *buffer, int count) +{ + FILE *f; + + f = SafeOpenWrite (filename); + SafeWrite (f, buffer, count); + fclose (f); +} + + +#ifdef __BIG_ENDIAN__ + +short LittleShort (short l) +{ + byte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short BigShort (short l) +{ + return l; +} + + +int LittleLong (int l) +{ + byte b1,b2,b3,b4; + + b1 = l&255; + b2 = (l>>8)&255; + b3 = (l>>16)&255; + b4 = (l>>24)&255; + + return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +} + +int BigLong (int l) +{ + return l; +} + + +float LittleFloat (float l) +{ + union {byte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float BigFloat (float l) +{ + return l; +} + + +#else + + +short BigShort (short l) +{ + byte b1,b2; + + b1 = l&255; + b2 = (l>>8)&255; + + return (b1<<8) + b2; +} + +short LittleShort (short l) +{ + return l; +} + + +int BigLong (int l) +{ + byte b1,b2,b3,b4; + + b1 = l&255; + b2 = (l>>8)&255; + b3 = (l>>16)&255; + b4 = (l>>24)&255; + + return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4; +} + +int LittleLong (int l) +{ + return l; +} + +float BigFloat (float l) +{ + union {byte b[4]; float f;} in, out; + + in.f = l; + out.b[0] = in.b[3]; + out.b[1] = in.b[2]; + out.b[2] = in.b[1]; + out.b[3] = in.b[0]; + + return out.f; +} + +float LittleFloat (float l) +{ + return l; +} + + +#endif diff --git a/utils/xwad/goldsrc_standin.h b/utils/xwad/goldsrc_standin.h new file mode 100644 index 0000000..2acb7be --- /dev/null +++ b/utils/xwad/goldsrc_standin.h @@ -0,0 +1,42 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: This file provides some of the goldsrc functionality for xwad. +// +//=============================================================================// + +#ifndef GOLDSRC_STANDIN_H +#define GOLDSRC_STANDIN_H +#ifdef _WIN32 +#pragma once +#endif + + +typedef float vec_t; +typedef float vec3_t[3]; + +typedef unsigned char byte; +typedef int qboolean; + + +void Msg( PRINTF_FORMAT_STRING const char *pMsg, ... ); +void Warning( PRINTF_FORMAT_STRING const char *pMsg, ... ); +void Error( PRINTF_FORMAT_STRING const char *pMsg, ... ); + +int LoadFile (char *filename, void **bufferptr); +void SaveFile (char *filename, void *buffer, int count); + +short BigShort (short l); +short LittleShort (short l); +int BigLong (int l); +int LittleLong (int l); +float BigFloat (float l); +float LittleFloat (float l); + + +FILE *SafeOpenWrite (char *filename); +FILE *SafeOpenRead (char *filename); +void SafeRead (FILE *f, void *buffer, int count); +void SafeWrite (FILE *f, void *buffer, int count); + + +#endif // GOLDSRC_STANDIN_H diff --git a/utils/xwad/lbmlib.cpp b/utils/xwad/lbmlib.cpp new file mode 100644 index 0000000..c3dfad8 --- /dev/null +++ b/utils/xwad/lbmlib.cpp @@ -0,0 +1,741 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +// lbmlib.c + +#include <WINDOWS.H> +#include <STDIO.H> +#include "lbmlib.h" +#include "goldsrc_standin.h" + + + +/* +============================================================================ + + LBM STUFF + +============================================================================ +*/ + + +#define FORMID ('F'+('O'<<8)+((int)'R'<<16)+((int)'M'<<24)) +#define ILBMID ('I'+('L'<<8)+((int)'B'<<16)+((int)'M'<<24)) +#define PBMID ('P'+('B'<<8)+((int)'M'<<16)+((int)' '<<24)) +#define BMHDID ('B'+('M'<<8)+((int)'H'<<16)+((int)'D'<<24)) +#define BODYID ('B'+('O'<<8)+((int)'D'<<16)+((int)'Y'<<24)) +#define CMAPID ('C'+('M'<<8)+((int)'A'<<16)+((int)'P'<<24)) + + +bmhd_t bmhd; + +int Align (int l) +{ + if (l&1) + return l+1; + return l; +} + + + +/* +================ += += LBMRLEdecompress += += Source must be evenly aligned! += +================ +*/ + +byte *LBMRLEDecompress (byte *source,byte *unpacked, int bpwidth) +{ + int count; + byte b,rept; + + count = 0; + + do + { + rept = *source++; + + if (rept > 0x80) + { + rept = (rept^0xff)+2; + b = *source++; + memset(unpacked,b,rept); + unpacked += rept; + } + else if (rept < 0x80) + { + rept++; + memcpy(unpacked,source,rept); + unpacked += rept; + source += rept; + } + else + rept = 0; // rept of 0x80 is NOP + + count += rept; + + } while (count<bpwidth); + + if (count>bpwidth) + Error ("Decompression exceeded width!\n"); + + + return source; +} + + +#define BPLANESIZE 128 +byte bitplanes[9][BPLANESIZE]; // max size 1024 by 9 bit planes + + +/* +================= += += MungeBitPlanes8 += += This destroys the bit plane data! += +================= +*/ + +void MungeBitPlanes8 (int width, byte *dest) +{ + *dest=width; // shut up the compiler warning + Error ("MungeBitPlanes8 not rewritten!"); +#if 0 +asm les di,[dest] +asm mov si,-1 +asm mov cx,[width] +mungebyte: +asm inc si +asm mov dx,8 +mungebit: +asm shl [BYTE PTR bitplanes + BPLANESIZE*7 +si],1 +asm rcl al,1 +asm shl [BYTE PTR bitplanes + BPLANESIZE*6 +si],1 +asm rcl al,1 +asm shl [BYTE PTR bitplanes + BPLANESIZE*5 +si],1 +asm rcl al,1 +asm shl [BYTE PTR bitplanes + BPLANESIZE*4 +si],1 +asm rcl al,1 +asm shl [BYTE PTR bitplanes + BPLANESIZE*3 +si],1 +asm rcl al,1 +asm shl [BYTE PTR bitplanes + BPLANESIZE*2 +si],1 +asm rcl al,1 +asm shl [BYTE PTR bitplanes + BPLANESIZE*1 +si],1 +asm rcl al,1 +asm shl [BYTE PTR bitplanes + BPLANESIZE*0 +si],1 +asm rcl al,1 +asm stosb +asm dec cx +asm jz done +asm dec dx +asm jnz mungebit +asm jmp mungebyte + +done: +#endif +} + + +void MungeBitPlanes4 (int width, byte *dest) +{ + *dest=width; // shut up the compiler warning + Error ("MungeBitPlanes4 not rewritten!"); +#if 0 + +asm les di,[dest] +asm mov si,-1 +asm mov cx,[width] +mungebyte: +asm inc si +asm mov dx,8 +mungebit: +asm xor al,al +asm shl [BYTE PTR bitplanes + BPLANESIZE*3 +si],1 +asm rcl al,1 +asm shl [BYTE PTR bitplanes + BPLANESIZE*2 +si],1 +asm rcl al,1 +asm shl [BYTE PTR bitplanes + BPLANESIZE*1 +si],1 +asm rcl al,1 +asm shl [BYTE PTR bitplanes + BPLANESIZE*0 +si],1 +asm rcl al,1 +asm stosb +asm dec cx +asm jz done +asm dec dx +asm jnz mungebit +asm jmp mungebyte + +done: +#endif +} + + +void MungeBitPlanes2 (int width, byte *dest) +{ + *dest=width; // shut up the compiler warning + Error ("MungeBitPlanes2 not rewritten!"); +#if 0 +asm les di,[dest] +asm mov si,-1 +asm mov cx,[width] +mungebyte: +asm inc si +asm mov dx,8 +mungebit: +asm xor al,al +asm shl [BYTE PTR bitplanes + BPLANESIZE*1 +si],1 +asm rcl al,1 +asm shl [BYTE PTR bitplanes + BPLANESIZE*0 +si],1 +asm rcl al,1 +asm stosb +asm dec cx +asm jz done +asm dec dx +asm jnz mungebit +asm jmp mungebyte + +done: +#endif +} + + +void MungeBitPlanes1 (int width, byte *dest) +{ + *dest=width; // shut up the compiler warning + Error ("MungeBitPlanes1 not rewritten!"); +#if 0 +asm les di,[dest] +asm mov si,-1 +asm mov cx,[width] +mungebyte: +asm inc si +asm mov dx,8 +mungebit: +asm xor al,al +asm shl [BYTE PTR bitplanes + BPLANESIZE*0 +si],1 +asm rcl al,1 +asm stosb +asm dec cx +asm jz done +asm dec dx +asm jnz mungebit +asm jmp mungebyte + +done: +#endif +} + +int LoadBMP (const char* szFile, BYTE** ppbBits, BYTE** ppbPalette) +{ + int i, rc = 0; + FILE *pfile = NULL; + BITMAPFILEHEADER bmfh; + BITMAPINFOHEADER bmih; + RGBQUAD rgrgbPalette[256]; + ULONG cbBmpBits; + BYTE* pbBmpBits; + byte *pb, *pbPal = NULL; + ULONG cbPalBytes; + ULONG biTrueWidth; + + // Bogus parameter check + if (!(ppbPalette != NULL && ppbBits != NULL)) + { fprintf(stderr, "invalid BMP file\n"); rc = -1000; goto GetOut; } + + // File exists? + if ((pfile = fopen(szFile, "rb")) == NULL) + { fprintf(stderr, "unable to open BMP file\n"); rc = -1; goto GetOut; } + + // Read file header + if (fread(&bmfh, sizeof bmfh, 1/*count*/, pfile) != 1) + { rc = -2; goto GetOut; } + + // Bogus file header check + if (!(bmfh.bfReserved1 == 0 && bmfh.bfReserved2 == 0)) + { rc = -2000; goto GetOut; } + + // Read info header + if (fread(&bmih, sizeof bmih, 1/*count*/, pfile) != 1) + { rc = -3; goto GetOut; } + + // Bogus info header check + if (!(bmih.biSize == sizeof bmih && bmih.biPlanes == 1)) + { fprintf(stderr, "invalid BMP file header\n"); rc = -3000; goto GetOut; } + + // Bogus bit depth? Only 8-bit supported. + if (bmih.biBitCount != 8) + { fprintf(stderr, "BMP file not 8 bit\n"); rc = -4; goto GetOut; } + + // Bogus compression? Only non-compressed supported. + if (bmih.biCompression != BI_RGB) + { fprintf(stderr, "invalid BMP compression type\n"); rc = -5; goto GetOut; } + + // Figure out how many entires are actually in the table + if (bmih.biClrUsed == 0) + { + bmih.biClrUsed = 256; + cbPalBytes = (1 << bmih.biBitCount) * sizeof( RGBQUAD ); + } + else + { + cbPalBytes = bmih.biClrUsed * sizeof( RGBQUAD ); + } + + // Read palette (bmih.biClrUsed entries) + if (fread(rgrgbPalette, cbPalBytes, 1/*count*/, pfile) != 1) + { rc = -6; goto GetOut; } + + // convert to a packed 768 byte palette + pbPal = (unsigned char*)malloc(768); + if (pbPal == NULL) + { rc = -7; goto GetOut; } + + pb = pbPal; + + // Copy over used entries + for (i = 0; i < (int)bmih.biClrUsed; i++) + { + *pb++ = rgrgbPalette[i].rgbRed; + *pb++ = rgrgbPalette[i].rgbGreen; + *pb++ = rgrgbPalette[i].rgbBlue; + } + + // Fill in unused entires will 0,0,0 + for (i = bmih.biClrUsed; i < 256; i++) + { + *pb++ = 0; + *pb++ = 0; + *pb++ = 0; + } + + // Read bitmap bits (remainder of file) + cbBmpBits = bmfh.bfSize - ftell(pfile); + pb = (unsigned char*)malloc(cbBmpBits); + if (fread(pb, cbBmpBits, 1/*count*/, pfile) != 1) + { rc = -7; goto GetOut; } + + pbBmpBits = (unsigned char*)malloc(cbBmpBits); + + // data is actually stored with the width being rounded up to a multiple of 4 + biTrueWidth = (bmih.biWidth + 3) & ~3; + + // reverse the order of the data. + pb += (bmih.biHeight - 1) * biTrueWidth; + for(i = 0; i < bmih.biHeight; i++) + { + memmove(&pbBmpBits[biTrueWidth * i], pb, biTrueWidth); + pb -= biTrueWidth; + } + + pb += biTrueWidth; + free(pb); + + bmhd.w = (WORD)bmih.biWidth; + bmhd.h = (WORD)bmih.biHeight; + // Set output parameters + *ppbPalette = pbPal; + *ppbBits = pbBmpBits; + +GetOut: + if (pfile) + fclose(pfile); + + return rc; +} + + +int WriteBMPfile (char *szFile, byte *pbBits, int width, int height, byte *pbPalette) +{ + int i, rc = 0; + FILE *pfile = NULL; + BITMAPFILEHEADER bmfh; + BITMAPINFOHEADER bmih; + RGBQUAD rgrgbPalette[256]; + ULONG cbBmpBits; + BYTE* pbBmpBits; + byte *pb, *pbPal = NULL; + ULONG cbPalBytes; + ULONG biTrueWidth; + + // Bogus parameter check + if (!(pbPalette != NULL && pbBits != NULL)) + { rc = -1000; goto GetOut; } + + // File exists? + if ((pfile = fopen(szFile, "wb")) == NULL) + { rc = -1; goto GetOut; } + + biTrueWidth = ((width + 3) & ~3); + cbBmpBits = biTrueWidth * height; + cbPalBytes = 256 * sizeof( RGBQUAD ); + + // Bogus file header check + bmfh.bfType = MAKEWORD( 'B', 'M' ); + bmfh.bfSize = sizeof bmfh + sizeof bmih + cbBmpBits + cbPalBytes; + bmfh.bfReserved1 = 0; + bmfh.bfReserved2 = 0; + bmfh.bfOffBits = sizeof bmfh + sizeof bmih + cbPalBytes; + + // Write file header + if (fwrite(&bmfh, sizeof bmfh, 1/*count*/, pfile) != 1) + { rc = -2; goto GetOut; } + + // Size of structure + bmih.biSize = sizeof bmih; + // Width + bmih.biWidth = biTrueWidth; + // Height + bmih.biHeight = height; + // Only 1 plane + bmih.biPlanes = 1; + // Only 8-bit supported. + bmih.biBitCount = 8; + // Only non-compressed supported. + bmih.biCompression = BI_RGB; + bmih.biSizeImage = 0; + + // huh? + bmih.biXPelsPerMeter = 0; + bmih.biYPelsPerMeter = 0; + + // Always full palette + bmih.biClrUsed = 256; + bmih.biClrImportant = 0; + + // Write info header + if (fwrite(&bmih, sizeof bmih, 1/*count*/, pfile) != 1) + { rc = -3; goto GetOut; } + + + // convert to expanded palette + pb = pbPalette; + + // Copy over used entries + for (i = 0; i < (int)bmih.biClrUsed; i++) + { + rgrgbPalette[i].rgbRed = *pb++; + rgrgbPalette[i].rgbGreen = *pb++; + rgrgbPalette[i].rgbBlue = *pb++; + rgrgbPalette[i].rgbReserved = 0; + } + + // Write palette (bmih.biClrUsed entries) + cbPalBytes = bmih.biClrUsed * sizeof( RGBQUAD ); + if (fwrite(rgrgbPalette, cbPalBytes, 1/*count*/, pfile) != 1) + { rc = -6; goto GetOut; } + + + pbBmpBits = (unsigned char*)malloc(cbBmpBits); + + pb = pbBits; + // reverse the order of the data. + pb += (height - 1) * width; + for(i = 0; i < bmih.biHeight; i++) + { + memmove(&pbBmpBits[biTrueWidth * i], pb, width); + pb -= width; + } + + // Write bitmap bits (remainder of file) + if (fwrite(pbBmpBits, cbBmpBits, 1/*count*/, pfile) != 1) + { rc = -7; goto GetOut; } + + free(pbBmpBits); + +GetOut: + if (pfile) + fclose(pfile); + + return rc; +} + +/* +================= += += LoadLBM += +================= +*/ + +void LoadLBM (char *filename, byte **picture, byte **palette) +{ + byte *LBMbuffer, *picbuffer, *cmapbuffer; + int y,p,planes; + byte *LBM_P, *LBMEND_P; + byte *pic_p; + byte *body_p; + unsigned rowsize; + + int formtype,formlength; + int chunktype,chunklength; + void (*mungecall) (int, byte *); + +// qiet compiler warnings + picbuffer = NULL; + cmapbuffer = NULL; + mungecall = NULL; + +// +// load the LBM +// + LoadFile (filename, (void **)&LBMbuffer); + +// +// parse the LBM header +// + LBM_P = LBMbuffer; + if ( *(int *)LBMbuffer != LittleLong(FORMID) ) + Error ("No FORM ID at start of file!\n"); + + LBM_P += 4; + formlength = BigLong( *(int *)LBM_P ); + LBM_P += 4; + LBMEND_P = LBM_P + Align(formlength); + + formtype = LittleLong(*(int *)LBM_P); + + if (formtype != ILBMID && formtype != PBMID) + Error ("Unrecognized form type: %c%c%c%c\n", formtype&0xff + ,(formtype>>8)&0xff,(formtype>>16)&0xff,(formtype>>24)&0xff); + + LBM_P += 4; + +// +// parse chunks +// + + while (LBM_P < LBMEND_P) + { + chunktype = LBM_P[0] + (LBM_P[1]<<8) + (LBM_P[2]<<16) + (LBM_P[3]<<24); + LBM_P += 4; + chunklength = LBM_P[3] + (LBM_P[2]<<8) + (LBM_P[1]<<16) + (LBM_P[0]<<24); + LBM_P += 4; + + switch ( chunktype ) + { + case BMHDID: + memcpy (&bmhd,LBM_P,sizeof(bmhd)); + bmhd.w = BigShort(bmhd.w); + bmhd.h = BigShort(bmhd.h); + bmhd.x = BigShort(bmhd.x); + bmhd.y = BigShort(bmhd.y); + bmhd.pageWidth = BigShort(bmhd.pageWidth); + bmhd.pageHeight = BigShort(bmhd.pageHeight); + break; + + case CMAPID: + cmapbuffer = (unsigned char*)malloc (768); + memset (cmapbuffer, 0, 768); + memcpy (cmapbuffer, LBM_P, chunklength); + break; + + case BODYID: + body_p = LBM_P; + + pic_p = picbuffer = (unsigned char*)malloc (bmhd.w*bmhd.h); + if (formtype == PBMID) + { + // + // unpack PBM + // + for (y=0 ; y<bmhd.h ; y++, pic_p += bmhd.w) + { + if (bmhd.compression == cm_rle1) + body_p = LBMRLEDecompress ((byte *)body_p + , pic_p , bmhd.w); + else if (bmhd.compression == cm_none) + { + memcpy (pic_p,body_p,bmhd.w); + body_p += Align(bmhd.w); + } + } + + } + else + { + // + // unpack ILBM + // + planes = bmhd.nPlanes; + if (bmhd.masking == ms_mask) + planes++; + rowsize = (bmhd.w+15)/16 * 2; + switch (bmhd.nPlanes) + { + case 1: + mungecall = MungeBitPlanes1; + break; + case 2: + mungecall = MungeBitPlanes2; + break; + case 4: + mungecall = MungeBitPlanes4; + break; + case 8: + mungecall = MungeBitPlanes8; + break; + default: + Error ("Can't munge %i bit planes!\n",bmhd.nPlanes); + } + + for (y=0 ; y<bmhd.h ; y++, pic_p += bmhd.w) + { + for (p=0 ; p<planes ; p++) + if (bmhd.compression == cm_rle1) + body_p = LBMRLEDecompress ((byte *)body_p + , bitplanes[p] , rowsize); + else if (bmhd.compression == cm_none) + { + memcpy (bitplanes[p],body_p,rowsize); + body_p += rowsize; + } + + mungecall (bmhd.w , pic_p); + } + } + break; + } + + LBM_P += Align(chunklength); + } + + free (LBMbuffer); + + *picture = picbuffer; + *palette = cmapbuffer; +} + + +/* +============================================================================ + + WRITE LBM + +============================================================================ +*/ + +/* +============== += += WriteLBMfile += +============== +*/ + +void WriteLBMfile (char *filename, byte *data, int width, int height, byte *palette) +{ + byte *lbm, *lbmptr; + int *formlength, *bmhdlength, *cmaplength, *bodylength; + int length; + bmhd_t basebmhd; + + lbm = lbmptr = (unsigned char*)malloc (width*height+1000); + +// +// start FORM +// + *lbmptr++ = 'F'; + *lbmptr++ = 'O'; + *lbmptr++ = 'R'; + *lbmptr++ = 'M'; + + formlength = (int*)lbmptr; + lbmptr+=4; // leave space for length + + *lbmptr++ = 'P'; + *lbmptr++ = 'B'; + *lbmptr++ = 'M'; + *lbmptr++ = ' '; + +// +// write BMHD +// + *lbmptr++ = 'B'; + *lbmptr++ = 'M'; + *lbmptr++ = 'H'; + *lbmptr++ = 'D'; + + bmhdlength = (int *)lbmptr; + lbmptr+=4; // leave space for length + + memset (&basebmhd,0,sizeof(basebmhd)); + basebmhd.w = BigShort((short)width); + basebmhd.h = BigShort((short)height); + basebmhd.nPlanes = (BYTE)BigShort(8); + basebmhd.xAspect = (BYTE)BigShort(5); + basebmhd.yAspect = (BYTE)BigShort(6); + basebmhd.pageWidth = BigShort((short)width); + basebmhd.pageHeight = BigShort((short)height); + + memcpy (lbmptr,&basebmhd,sizeof(basebmhd)); + lbmptr += sizeof(basebmhd); + + length = lbmptr-(byte *)bmhdlength-4; + *bmhdlength = BigLong(length); + if (length&1) + *lbmptr++ = 0; // pad chunk to even offset + +// +// write CMAP +// + *lbmptr++ = 'C'; + *lbmptr++ = 'M'; + *lbmptr++ = 'A'; + *lbmptr++ = 'P'; + + cmaplength = (int *)lbmptr; + lbmptr+=4; // leave space for length + + memcpy (lbmptr,palette,768); + lbmptr += 768; + + length = lbmptr-(byte *)cmaplength-4; + *cmaplength = BigLong(length); + if (length&1) + *lbmptr++ = 0; // pad chunk to even offset + +// +// write BODY +// + *lbmptr++ = 'B'; + *lbmptr++ = 'O'; + *lbmptr++ = 'D'; + *lbmptr++ = 'Y'; + + bodylength = (int *)lbmptr; + lbmptr+=4; // leave space for length + + memcpy (lbmptr,data,width*height); + lbmptr += width*height; + + length = lbmptr-(byte *)bodylength-4; + *bodylength = BigLong(length); + if (length&1) + *lbmptr++ = 0; // pad chunk to even offset + +// +// done +// + length = lbmptr-(byte *)formlength-4; + *formlength = BigLong(length); + if (length&1) + *lbmptr++ = 0; // pad chunk to even offset + +// +// write output file +// + SaveFile (filename, lbm, lbmptr-lbm); + free (lbm); +} + diff --git a/utils/xwad/lbmlib.h b/utils/xwad/lbmlib.h new file mode 100644 index 0000000..23ac560 --- /dev/null +++ b/utils/xwad/lbmlib.h @@ -0,0 +1,55 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +// lbmlib.h + +typedef unsigned char UBYTE; + +#ifndef _WINDOWS_ +typedef short WORD; +#endif + +typedef unsigned short UWORD; +typedef long LONG; + +typedef enum +{ + ms_none, + ms_mask, + ms_transcolor, + ms_lasso +} mask_t; + +typedef enum +{ + cm_none, + cm_rle1 +} compress_t; + +typedef struct +{ + UWORD w,h; + WORD x,y; + UBYTE nPlanes; + UBYTE masking; + UBYTE compression; + UBYTE pad1; + UWORD transparentColor; + UBYTE xAspect,yAspect; + WORD pageWidth,pageHeight; +} bmhd_t; + +extern bmhd_t bmhd; // will be in native byte order + + +void LoadLBM (char *filename, byte **picture, byte **palette); +int LoadBMP (const char* szFile, byte** ppbBits, byte** ppbPalette); +void WriteLBMfile (char *filename, byte *data, int width, int height + , byte *palette); +int WriteBMPfile (char *szFile, byte *pbBits, int width, int height, byte *pbPalette); + diff --git a/utils/xwad/wadlib.cpp b/utils/xwad/wadlib.cpp new file mode 100644 index 0000000..c7d4750 --- /dev/null +++ b/utils/xwad/wadlib.cpp @@ -0,0 +1,336 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +// wad2lib.c + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +//#include <sys/file.h> +#include <stdarg.h> + +#ifdef NeXT +#include <libc.h> +#endif +#include "goldsrc_standin.h" +#include "wadlib.h" + +/* +============================================================================ + + WAD READING + +============================================================================ +*/ + + +lumpinfo_t *lumpinfo; // location of each lump on disk +int numlumps; + +wadinfo_t header; +FILE *wadhandle; + + +/* +==================== +W_OpenWad +==================== +*/ +void W_OpenWad (const char *filename) +{ + lumpinfo_t *lump_p; + int i; + int length; + +// +// open the file and add to directory +// + wadhandle = SafeOpenRead ((char*)filename); + SafeRead (wadhandle, &header, sizeof(header)); + + if (strncmp(header.identification,"WAD2",4) && + strncmp(header.identification, "WAD3", 4)) + Error ("Wad file %s doesn't have WAD2/WAD3 id\n",filename); + + header.numlumps = LittleLong(header.numlumps); + header.infotableofs = LittleLong(header.infotableofs); + + numlumps = header.numlumps; + + length = numlumps*sizeof(lumpinfo_t); + lumpinfo = (lumpinfo_t*)malloc (length); + lump_p = lumpinfo; + + fseek (wadhandle, header.infotableofs, SEEK_SET); + SafeRead (wadhandle, lumpinfo, length); + +// +// Fill in lumpinfo +// + + for (i=0 ; i<numlumps ; i++,lump_p++) + { + lump_p->filepos = LittleLong(lump_p->filepos); + lump_p->size = LittleLong(lump_p->size); + } +} + + +void CleanupName (char *in, char *out) +{ + int i; + + for (i=0 ; i<sizeof( ((lumpinfo_t *)0)->name ) ; i++ ) + { + if (!in[i]) + break; + + out[i] = toupper(in[i]); + } + + for ( ; i<sizeof( ((lumpinfo_t *)0)->name ); i++ ) + out[i] = 0; +} + + +/* +==================== +W_CheckNumForName + +Returns -1 if name not found +==================== +*/ +int W_CheckNumForName (char *name) +{ + char cleanname[16]; + int v1,v2, v3, v4; + int i; + lumpinfo_t *lump_p; + + CleanupName (name, cleanname); + +// make the name into four integers for easy compares + + v1 = *(int *)cleanname; + v2 = *(int *)&cleanname[4]; + v3 = *(int *)&cleanname[8]; + v4 = *(int *)&cleanname[12]; + +// find it + + lump_p = lumpinfo; + for (i=0 ; i<numlumps ; i++, lump_p++) + { + if ( *(int *)lump_p->name == v1 + && *(int *)&lump_p->name[4] == v2 + && *(int *)&lump_p->name[8] == v3 + && *(int *)&lump_p->name[12] == v4) + return i; + } + + return -1; +} + + +/* +==================== +W_GetNumForName + +Calls W_CheckNumForName, but bombs out if not found +==================== +*/ +int W_GetNumForName (char *name) +{ + int i; + + i = W_CheckNumForName (name); + if (i != -1) + return i; + + Error ("W_GetNumForName: %s not found!",name); + return -1; +} + + +/* +==================== +W_LumpLength + +Returns the buffer size needed to load the given lump +==================== +*/ +int W_LumpLength (int lump) +{ + if (lump >= numlumps) + Error ("W_LumpLength: %i >= numlumps",lump); + return lumpinfo[lump].size; +} + + +/* +==================== +W_ReadLumpNum + +Loads the lump into the given buffer, which must be >= W_LumpLength() +==================== +*/ +void W_ReadLumpNum (int lump, void *dest) +{ + lumpinfo_t *l; + + if (lump >= numlumps) + Error ("W_ReadLump: %i >= numlumps",lump); + l = lumpinfo+lump; + + fseek (wadhandle, l->filepos, SEEK_SET); + SafeRead (wadhandle, dest, l->size); +} + + + +/* +==================== +W_LoadLumpNum +==================== +*/ +void *W_LoadLumpNum (int lump) +{ + void *buf; + + if ((unsigned)lump >= (unsigned)numlumps) + Error ("W_CacheLumpNum: %i >= numlumps",lump); + + buf = malloc (W_LumpLength (lump)); + W_ReadLumpNum (lump, buf); + + return buf; +} + + +/* +==================== +W_LoadLumpName +==================== +*/ +void *W_LoadLumpName (char *name) +{ + return W_LoadLumpNum (W_GetNumForName(name)); +} + + +/* +=============================================================================== + + WAD CREATION + +=============================================================================== +*/ + +FILE *outwad; + +lumpinfo_t outinfo[4096]; +int outlumps; + +short (*wadshort) (short l); +int (*wadlong) (int l); + +/* +=============== +NewWad +=============== +*/ + +void NewWad (char *pathname, qboolean bigendien) +{ + outwad = SafeOpenWrite (pathname); + fseek (outwad, sizeof(wadinfo_t), SEEK_SET); + memset (outinfo, 0, sizeof(outinfo)); + + if (bigendien) + { + wadshort = BigShort; + wadlong = BigLong; + } + else + { + wadshort = LittleShort; + wadlong = LittleLong; + } + + outlumps = 0; +} + + +/* +=============== +AddLump +=============== +*/ + +void AddLump (char *name, void *buffer, int length, int type, int compress) +{ + lumpinfo_t *info; + int ofs; + + info = &outinfo[outlumps]; + outlumps++; + + memset (info,0,sizeof(info)); + + strcpy (info->name, name); + strupr (info->name); + + ofs = ftell(outwad); + info->filepos = wadlong(ofs); + info->size = info->disksize = wadlong(length); + info->type = type; + info->compression = compress; + +// FIXME: do compression + + SafeWrite (outwad, buffer, length); +} + + +/* +=============== +WriteWad +=============== +*/ + +void WriteWad (int wad3) +{ + wadinfo_t header; + int ofs; + +// write the lumpingo + ofs = ftell(outwad); + + SafeWrite (outwad, outinfo, outlumps*sizeof(lumpinfo_t) ); + +// write the header + +// a program will be able to tell the ednieness of a wad by the id + header.identification[0] = 'W'; + header.identification[1] = 'A'; + header.identification[2] = 'D'; + header.identification[3] = wad3 ? '3' : '2'; + + header.numlumps = wadlong(outlumps); + header.infotableofs = wadlong(ofs); + + fseek (outwad, 0, SEEK_SET); + SafeWrite (outwad, &header, sizeof(header)); + fclose (outwad); +} + + diff --git a/utils/xwad/wadlib.h b/utils/xwad/wadlib.h new file mode 100644 index 0000000..e675fa3 --- /dev/null +++ b/utils/xwad/wadlib.h @@ -0,0 +1,61 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +// wadlib.h + +// +// wad reading +// + +#define CMP_NONE 0 +#define CMP_LZSS 1 + +#define TYP_NONE 0 +#define TYP_LABEL 1 +#define TYP_LUMPY 64 // 64 + grab command number + +typedef struct +{ + char identification[4]; // should be WAD2 or 2DAW + int numlumps; + int infotableofs; +} wadinfo_t; + + +typedef struct +{ + int filepos; + int disksize; + int size; // uncompressed + char type; + char compression; + char pad1, pad2; + char name[16]; // must be null terminated +} lumpinfo_t; + +extern lumpinfo_t *lumpinfo; // location of each lump on disk +extern int numlumps; +extern wadinfo_t header; + +void W_OpenWad (const char *filename); +int W_CheckNumForName (char *name); +int W_GetNumForName (char *name); +int W_LumpLength (int lump); +void W_ReadLumpNum (int lump, void *dest); +void *W_LoadLumpNum (int lump); +void *W_LoadLumpName (char *name); + +void CleanupName (char *in, char *out); + +// +// wad creation +// +void NewWad (char *pathname, qboolean bigendien); +void AddLump (char *name, void *buffer, int length, int type, int compress); +void WriteWad (int wad3); + diff --git a/utils/xwad/xwad.cpp b/utils/xwad/xwad.cpp new file mode 100644 index 0000000..dce4b7f --- /dev/null +++ b/utils/xwad/xwad.cpp @@ -0,0 +1,1182 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include <windows.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <conio.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <io.h> +#include <direct.h> +#include <stdarg.h> + +#include "goldsrc_standin.h" + +#include "wadlib.h" +#include "goldsrc_bspfile.h" + + +extern FILE *wadhandle; + + +#pragma pack( 1 ) + struct TGAHeader_t + { + unsigned char id_length; + unsigned char colormap_type; + unsigned char image_type; + unsigned short colormap_index; + unsigned short colormap_length; + unsigned char colormap_size; + unsigned short x_origin; + unsigned short y_origin; + unsigned short width; + unsigned short height; + unsigned char pixel_size; + unsigned char attributes; + }; +#pragma pack() + + +typedef enum {ST_SYNC=0, ST_RAND } synctype_t; +typedef enum { SPR_SINGLE=0, SPR_GROUP } spriteframetype_t; + +typedef struct { + int ident; + int version; + int type; + int texFormat; + float boundingradius; + int width; + int height; + int numframes; + float beamlength; + synctype_t synctype; +} dsprite_t; + +typedef struct { + int origin[2]; + int width; + int height; +} dspriteframe_t; + + +class RGBAColor +{ +public: + unsigned char r,g,b,a; +}; + + +const char *g_pDefaultShader = "LightmappedGeneric"; +const char *g_pShader = g_pDefaultShader; +bool g_bBMPAllowTranslucent = false; +bool g_bDecal = false; +bool g_bQuiet = false; + +#define MAX_VMT_PARAMS 16 + +struct VTexVMTParam_t +{ + const char *m_szParam; + const char *m_szValue; +}; + +static VTexVMTParam_t g_VMTParams[MAX_VMT_PARAMS]; + +static int g_NumVMTParams = 0; + +void PrintExitStuff() +{ + if ( !g_bQuiet ) + { + printf( "Press a key to quit.\n" ); + getch(); + } +} + + +RGBAColor* ConvertToRGBUpsideDown( byte *pBits, int width, int height, byte *pPalette, bool *bTranslucent ) +{ + RGBAColor *pRet = new RGBAColor[width*height]; + + byte newPalette[256][3]; + for ( int i=0; i < 256; i++ ) + { + newPalette[i][0] = pPalette[i*3+2]; + newPalette[i][1] = pPalette[i*3+1]; + newPalette[i][2] = pPalette[i*3+0]; + } + + // Write the lines upside-down. + for ( int y=0; y < height; y++ ) + { + byte *pLine = &pBits[(height-y-1)*width]; + for ( int x=0; x < width; x++ ) + { + pRet[y*width+x].r = newPalette[pLine[x]][0]; + pRet[y*width+x].g = newPalette[pLine[x]][1]; + pRet[y*width+x].b = newPalette[pLine[x]][2]; + if ( pLine[x] == 255 ) + { + *bTranslucent = true; + pRet[y*width+x].a = 0; + } + else + { + pRet[y*width+x].a = 255; + } + } + } + + return pRet; +} + + +void FloodSolidPixels( RGBAColor *pTexels, int width, int height ) +{ + byte *pAlphaMap = new byte[width*height]; + byte *pNewAlphaMap = new byte[width*height]; + + for ( int y=0; y < height; y++ ) + for ( int x=0; x < width; x++ ) + pAlphaMap[y*width+x] = pTexels[y*width+x].a; + + bool bHappy = false; + while ( !bHappy ) + { + bHappy = true; + + memcpy( pNewAlphaMap, pAlphaMap, width * height ); + + for ( int y=0; y < height; y++ ) + { + RGBAColor *pLine = &pTexels[y*width]; + + for ( int x=0; x < width; x++ ) + { + if ( pAlphaMap[y*width+x] == 0 ) + { + int nNeighbors = 0; + int neighborTotal[3] = {0,0,0}; + + // Blend all the neighboring solid pixels. + for ( int offsetX=-1; offsetX <= 1; offsetX++ ) + { + for ( int offsetY=-1; offsetY <= 1; offsetY++ ) + { + int testX = x + offsetX; + int testY = y + offsetY; + if ( testX >= 0 && testY >= 0 && testX < width && testY < height ) + { + if ( pAlphaMap[testY*width+testX] ) + { + RGBAColor *pNeighbor = &pTexels[testY*width+testX]; + ++nNeighbors; + neighborTotal[0] += pNeighbor->r; + neighborTotal[1] += pNeighbor->g; + neighborTotal[2] += pNeighbor->b; + } + } + } + } + + if ( nNeighbors ) + { + pNewAlphaMap[y*width+x] = 255; + bHappy = false; + pLine[x].r = (byte)( neighborTotal[0] / nNeighbors ); + pLine[x].g = (byte)( neighborTotal[1] / nNeighbors ); + pLine[x].b = (byte)( neighborTotal[2] / nNeighbors ); + } + } + } + } + + memcpy( pAlphaMap, pNewAlphaMap, width * height ); + } + + delete [] pAlphaMap; + delete [] pNewAlphaMap; +} + + +RGBAColor* ResampleImage( RGBAColor *pRGB, int width, int height, int newWidth, int newHeight ) +{ + RGBAColor *pResampled = new RGBAColor[newWidth * newHeight]; + for ( int y=0; y < newHeight; y++ ) + { + float yPercent = (float)y / (newHeight - 1); + float flSrcY = yPercent * (height - 1.00001f); + int iSrcY = (int)flSrcY; + float flYFrac = flSrcY - iSrcY; + + for ( int x=0; x < newWidth; x++ ) + { + float xPercent = (float)x / (newWidth - 1); + float flSrcX = xPercent * (width - 1.00001f); + int iSrcX = (int)flSrcX; + float flXFrac = flSrcX - iSrcX; + + byte *pSrc0 = ((byte*)&pRGB[iSrcY*width+iSrcX]); + byte *pSrc1 = ((byte*)&pRGB[iSrcY*width+iSrcX+1]); + byte *pSrc2 = ((byte*)&pRGB[(iSrcY+1)*width+iSrcX]); + byte *pSrc3 = ((byte*)&pRGB[(iSrcY+1)*width+iSrcX+1]); + byte *pDest = (byte*)&pResampled[y*newWidth+x]; + + // Now blend the nearest 4 source pixels. + for ( int i=0; i < 4; i++ ) + { + //pDest[i] = pSrc0[i]; + float topColor = ( pSrc0[i]*(1-flXFrac) + pSrc1[i]*flXFrac ); + float bottomColor = ( pSrc2[i]*(1-flXFrac) + pSrc3[i]*flXFrac ); + pDest[i] = (byte)( topColor*(1-flYFrac) + bottomColor*flYFrac ); + } + } + } + return pResampled; +} + + +bool WriteTGAFile( + const char *pFilename, + bool bAllowTranslucent, + byte *pBits, + int width, + int height, + byte *pPalette, + bool bPowerOf2, + bool *bTranslucent, + bool *bResized + ) +{ + *bResized = *bTranslucent = false; + + RGBAColor *pRGB = ConvertToRGBUpsideDown( pBits, width, height, pPalette, bTranslucent ); + + // Unless the filename starts with '{', we don't allow translucency. + if ( !bAllowTranslucent ) + *bTranslucent = false; + + // Flood the solid texel colors into the transparent texels. + // Since we turn on point sampling for these textures, this only matters if we're resizing the texture. + if ( *bTranslucent ) + { + FloodSolidPixels( pRGB, width, height ); + } + + if ( bPowerOf2 ) + { + // Is it not a power of 2? + if ( (width & (width - 1)) || (height & (height - 1)) ) + { + // Ok, resize it to the next highest power of 2. + int newWidth = width; + while ( (newWidth & (newWidth - 1)) ) + ++newWidth; + + int newHeight = height; + while ( (newHeight & (newHeight - 1)) ) + ++newHeight; + + printf( "\t(%dx%d) -> (%dx%d)", width, height, newWidth, newHeight ); + + RGBAColor *pResampled = ResampleImage( pRGB, width, height, newWidth, newHeight ); + delete [] pRGB; + pRGB = pResampled; + + width = newWidth; + height = newHeight; + + *bResized = true; + } + } + + // Write it.. + TGAHeader_t hdr; + memset( &hdr, 0, sizeof( hdr ) ); + + hdr.width = width; + hdr.height = height; + hdr.colormap_type = 0; // no, no colormap please + hdr.image_type = 2; // uncompressed, true-color + hdr.pixel_size = 32; // 32 bits per pixel + + FILE *fp = fopen( pFilename, "wb" ); + if ( !fp ) + return false; + + SafeWrite( fp, &hdr, sizeof( hdr ) ); + SafeWrite( fp, pRGB, sizeof( RGBAColor ) * width * height ); + fclose( fp ); + + delete [] pRGB; + return true; +} + + +int PrintUsage( const char *pExtra, ... ) +{ + va_list marker; + va_start( marker, pExtra ); + vprintf( pExtra, marker ); + va_end( marker ); + + printf( + "%s \n" + "\t[-AutoDir]\n" + "\t\tAutomatically detects -basedir and -wadfile or -bmpfile based\n" + "\t\ton the last parameter (it must be a WAD file or a BMP file.\n" + "\t[-decal]\n" + "\t\tCreates VMTs for decals and creates VMTs for model decals.\n" + "\t[-onlytex <tex name>]\n" + "\t[-shader <shader name>]\n" + "\t\tSpecify the shader to use in the VMT file (default\n" + "\t\tis LightmappedGeneric.\n" + "\t[-vtex]\n" + "\t\tIf -vtex is specified, then it calls vtex on each newly-created\n" + "\t\t.TGA file.\n" + "\t[-vmtparam <ParamName> <ParamValue>]\n" + "\t\tif -vtex was specified, passes the parameters to that process.\n" + "\t\tUsed to add parameters to the generated .vmt file\n" + "\t-BaseDir <basedir>\n" + "\t-game <basedir>\n" + "\t\tSpecifies where the root mod directory is.\n" + "\t-WadFile <wildcard>\n" + "\t\t-wadfile will make (power-of-2) TGAs, VTFs, and VMTs for each\n" + "\t\ttexture in the WAD. It will also place them under a directory\n" + "\t\twith the name of the WAD. In addition, it will create\n" + "\t\t.resizeinfo files in the materials directory if it has to\n" + "\t\tresize the texture. Then Hammer's File->WAD To Material\n" + "\t\tcommand will use them to rescale texture coordinates.\n" + "\t-bmpfile <wildcard>\n" + "\t\t-bmpfile acts like -WadFile but for BMP files, and it'll place\n" + "\t\tthem in the root materials directory.\n" + "\t-sprfile <wildcard>\n" + "\t\tActs like -bmpfile, but ports a sprite.\n" + "\t-Transparent (BMP files only)\n" + "\t\tIf this is set, then it will treat palette index 255 as a\n" + "\t\ttransparent pixel." + "\t-SubDir <subdirectory>\n" + "\t\t-SubDir tells it what directory under materials to place the\n" + "\t\tfinal art. If using a WAD file, then it will automatically\n" + "\t\tuse the WAD filename if no -SubDir is specified.\n" + "\t-Quiet\n" + "\t\tDon't print out anything or wait for a keypress on exit.\n" + "\n" + , __argv[0] ); + printf( "ex: %s -vtex -BaseDir c:\\hl2\\dod -WadFile c:\\hl1\\dod\\*.wad\n", __argv[0] ); + printf( "ex: %s -vtex -BaseDir c:\\hl2\\dod -bmpfile test.bmp -SubDir models\\props\n", __argv[0] ); + printf( "ex: %s -vtex -vmtparam $ignorez 1 -BaseDir c:\\hl2\\dod -sprfile test.spr -SubDir sprites\\props\n", __argv[0] ); + + PrintExitStuff(); + return 1; +} + + +// Take something like "c:/a/b/c/filename.ext" and return "filename". +void GetBaseFilename( const char *pWadFilename, char wadBaseName[512] ) +{ + const char *pBase = strrchr( pWadFilename, '\\' ); + if ( strrchr( pWadFilename, '/' ) > pBase ) + pBase = strrchr( pWadFilename, '/' ); + + if ( pBase ) + strcpy( wadBaseName, pBase+1 ); + else + strcpy( wadBaseName, pWadFilename ); + + char *pDot = strchr( wadBaseName, '.' ); + if ( pDot ) + *pDot = 0; +} + + +const char *LastSlash( const char *pSrc ) +{ + const char *pRet = strrchr( pSrc, '/' ); + const char *pRet2 = strrchr( pSrc, '\\' ); + return (pRet > pRet2) ? pRet : pRet2; +} + + +void EnsureDirExists( const char *pDir ) +{ + if ( _access( pDir, 0 ) != 0 ) + { + // We use the shell's "md" command here instead of the _mkdir() function because + // md will create all the subdirectories leading up to the bottom one and _mkdir() won't. + char cmd[1024]; + _snprintf( cmd, sizeof( cmd ), "md \"%s\"", pDir ); + system( cmd ); + + if ( _access( pDir, 0 ) != 0 ) + Error( "\tCan't create directory: %s.\n", pDir ); + } +} + + +void WriteVMTFile( const char *pBaseDir, const char *pSubDir, const char *pName, bool bTranslucent ) +{ + char vmtFilename[512]; + sprintf( vmtFilename, "%s\\materials\\%s\\%s.vmt", pBaseDir, pSubDir, pName ); + + FILE *fp = fopen( vmtFilename, "wt" ); + if ( !fp ) + { + Error( "\tWriteVMTFile failed to open %s for writing.\n", vmtFilename ); + return; + } + + fprintf( fp, "\"%s\"\n{\n", g_pShader ); + fprintf( fp, "\t\"$basetexture\"\t\"%s\\%s\"\n", pSubDir, pName ); + + if ( bTranslucent || g_bDecal ) + { + fprintf( fp, "\t\"$alphatest\"\t\"1\"\n" ); + fprintf( fp, "\t\"$ALPHATESTREFERENCE\"\t\"0.5\"\n" ); + } + + if ( g_bDecal ) + { + fprintf( fp, "\t\"$decal\"\t\t\"1\"\n" ); + + } + + int i; + for( i=0;i<g_NumVMTParams;i++ ) + { + fprintf( fp, "\t\"%s\" \"%s\"\n", g_VMTParams[i].m_szParam, g_VMTParams[i].m_szValue ); + } + + fprintf( fp, "}" ); + + fclose( fp ); +} + + +void WriteTXTFile( const char *pBaseDir, const char *pSubDir, const char *pName ) +{ + char filename[512]; + sprintf( filename, "%s\\materialsrc\\%s\\%s.txt", pBaseDir, pSubDir, pName ); + + FILE *fp = fopen( filename, "wt" ); + if ( !fp ) + Error( "\tWriteTXTFile: can't open %s for writing.\n", filename ); + + fprintf( fp, "\"pointsample\" \"1\"\n" ); + fclose( fp ); +} + + +void WriteResizeInfoFile( const char *pBaseDir, const char *pSubDir, const char *pName, int width, int height ) +{ + char filename[512]; + sprintf( filename, "%s\\materials\\%s\\%s.resizeinfo", pBaseDir, pSubDir, pName ); + + FILE *fp = fopen( filename, "wt" ); + if ( !fp ) + { + Error( "\tWriteResizeInfoFile failed to open %s for writing.\n", filename ); + return; + } + + fprintf( fp, "%d %d", width, height ); + fclose( fp ); +} + + +void RunVTexOnFile( const char *pBaseDir, const char *pFilename ) +{ + char executableDir[MAX_PATH]; + GetModuleFileName( NULL, executableDir, sizeof( executableDir ) ); + + char *pLastSlash = max( strrchr( executableDir, '/' ), strrchr( executableDir, '\\' ) ); + if ( !pLastSlash ) + Error( "Can't find filename in '%s'.\n", executableDir ); + + *pLastSlash = 0; + + // Set the vproject environment variable (vtex doesn't allow game yet). + char envStr[MAX_PATH]; + _snprintf( envStr, sizeof( envStr ), "vproject=%s", pBaseDir ); + putenv( envStr ); + + // Call vtex on this texture now. + char vtexCommand[1024]; + sprintf( vtexCommand, "%s\\vtex.exe -quiet -nopause \"%s\"", executableDir, pFilename ); + if ( system( vtexCommand ) != 0 ) + { + Error( "\tCommand '%s' failed!\n", vtexCommand ); + } +} + + +void WriteOutputFiles( + const char *pBaseDir, + const char *pSubDir, + const char *pName, + bool bAllowTranslucent, + byte *buffer, + int width, + int height, + byte *pPalette, + bool bVTex +) +{ + bool bTranslucent, bResized; + bool bPowerOf2 = true; + + char tgaFilename[1024]; + sprintf( tgaFilename, "%s\\materialsrc\\%s\\%s.tga", pBaseDir, pSubDir, pName ); + if ( !WriteTGAFile( + tgaFilename, + bAllowTranslucent, + buffer, + width, + height, + pPalette, + bPowerOf2, + &bTranslucent, + &bResized ) ) + { + Error( "\tError writing %s.\n", tgaFilename ); + } + + // Write its .VMT file. + WriteVMTFile( pBaseDir, pSubDir, pName, bTranslucent ); + + // Write a text file for it if it's translucent so we can enable pointsample for vtex. +// if ( bTranslucent ) +// WriteTXTFile( pBaseDir, pSubDir, pName ); + + // Write its .resizeinfo file. + if ( bResized ) + { + WriteResizeInfoFile( pBaseDir, pSubDir, pName, width, height ); + } + + if ( bVTex ) + { + RunVTexOnFile( pBaseDir, tgaFilename ); + } +} + + +void EnsureDirectoriesExist( const char *pBaseDir, const char *pSubDir ) +{ + char materialsrcDir[512], materialsDir[512]; + sprintf( materialsrcDir, "%s\\materialsrc\\%s", pBaseDir, pSubDir ); + sprintf( materialsDir, "%s\\materials\\%s", pBaseDir, pSubDir ); + EnsureDirExists( materialsrcDir ); + EnsureDirExists( materialsDir ); +} + + +void ProcessWadFile( const char *pWadFilename, const char *pBaseDir, const char *pSubDir, const char *pOnlyTex, bool bVTex ) +{ + if ( !g_bQuiet ) + printf( "\n\n[WADFILE %s]\n\n", pWadFilename ); + + // If no -subdir was specified, then figure it out from the wad filename. + char wadBaseName[512]; + if ( !pSubDir ) + { + // Get the base wad filename. + GetBaseFilename( pWadFilename, wadBaseName ); + pSubDir = wadBaseName; + } + + EnsureDirectoriesExist( pBaseDir, pSubDir ); + + + // Now process all the images in the wad. + W_OpenWad( pWadFilename ); + + #define MAXLUMP (640*480*85/64) + byte inbuffer[MAXLUMP]; + + for (int i = 0; i < numlumps; i++) + { + if ( pOnlyTex && stricmp( pOnlyTex, lumpinfo[i].name ) != 0 ) + continue; + + fseek( wadhandle, lumpinfo[i].filepos, SEEK_SET ); + SafeRead ( wadhandle, inbuffer, lumpinfo[i].size ); + + miptex_t *qtex = (miptex_t *)inbuffer; + int width = LittleLong(qtex->width); + int height = LittleLong(qtex->height); + + if ( width <= 0 || height <= 0 || width > 5000 || height > 5000 ) + { + if ( !g_bQuiet ) + printf("\tskipping %s @ %d size %d (not an image?)\n", lumpinfo[i].name, lumpinfo[i].filepos, lumpinfo[i].size ); + continue; + } + else + { + if ( !g_bQuiet ) + printf( "\t%s", lumpinfo[i].name ); + } + + byte *pPalette = inbuffer + LittleLong( qtex->offsets[3] ) + width * height / 64 + 2; + byte *psrc, *pdest; + + byte outbuffer[(640+320)*480]; + + // The old xwad put the mipmaps in there too, but we don't want that now (usually). + // copy in 0 image + psrc = inbuffer + LittleLong( qtex->offsets[0] ); + pdest = outbuffer; + for (int t = 0; t < height; t++) + { + memcpy( pdest + t * width, psrc + t * width, width ); + } + + + WriteOutputFiles( + pBaseDir, // base directory + pSubDir, // subdir under materials + qtex->name, // filename (w/o extension) + qtex->name[0] == '{', // allow transparency? + outbuffer, + width, + height, + pPalette, + bVTex + ); + + if ( !g_bQuiet ) + printf( "\n" ); + } +} + + +void ProcessBMPFile( const char *pBaseDir, const char *pSubDir, const char *pFilename, bool bVTex ) +{ + if ( !g_bQuiet ) + printf( "[%s]\n", pFilename ); + + if ( !pSubDir ) + pSubDir = "."; + + // First make directories under materialsrc and materials if they don't exist. + EnsureDirectoriesExist( pBaseDir, pSubDir ); + + // Read in the 8-bit BMP file. + FILE *fp = fopen( pFilename, "rb" ); + if ( !fp ) + Error( "ProcessBMPFile( %s ) can't open the file for reading.\n", pFilename ); + + BITMAPFILEHEADER bfh; + BITMAPINFOHEADER bih; + unsigned char pixelData[512*512]; + + SafeRead( fp, &bfh, sizeof( bfh ) ); + SafeRead( fp, &bih, sizeof( bih ) ); + + // Make sure it's an 8-bit one like we want. + if ( bih.biSize != sizeof( bih ) || + bih.biPlanes != 1 || + bih.biBitCount != 8 || + bih.biCompression != BI_RGB || + bih.biHeight < 0 || + bih.biWidth * bih.biHeight > sizeof( pixelData ) ) + { + Error( "ProcessBMPFile( %s ) - invalid format.\n", pFilename ); + } + + int nColorsUsed = 256; + if ( bih.biClrUsed != 0 ) + { + nColorsUsed = bih.biClrUsed; + if ( nColorsUsed > 256 ) + Error( "ProcessBMPFile( %s ) - bih.biClrUsed = %d.\n", pFilename, bih.biClrUsed ); + } + + RGBQUAD quadPalette[256]; + SafeRead( fp, quadPalette, sizeof( quadPalette[0] ) * nColorsUsed ); + + // Usually, bfOffBits is the same place we are at now, but sometimes it's a little different. + fseek( fp, bfh.bfOffBits, SEEK_SET ); + + // Now read the bitmap data. + SafeRead( fp, pixelData, bih.biWidth * bih.biHeight ); + + fclose( fp ); + + + // Convert the palette. + byte palette[256][3]; + for ( int i=0; i < 256; i++ ) + { + palette[i][0] = quadPalette[i].rgbRed; + palette[i][1] = quadPalette[i].rgbGreen; + palette[i][2] = quadPalette[i].rgbBlue; + } + + // Unflip the pixel data. + for ( int y=0; y < bih.biHeight / 2; y++ ) + { + byte tempLine[1024]; + memcpy( tempLine, &pixelData[y*bih.biWidth], bih.biWidth ); + memcpy( &pixelData[y*bih.biWidth], &pixelData[(bih.biHeight-y-1)*bih.biWidth], bih.biWidth ); + memcpy( &pixelData[(bih.biHeight-y-1)*bih.biWidth], tempLine, bih.biWidth ); + } + + + char baseFilename[512]; + GetBaseFilename( pFilename, baseFilename ); + + // Save it out. + WriteOutputFiles( + pBaseDir, // base directory + pSubDir, // subdir under materials + baseFilename, // filename (w/o extension) + g_bBMPAllowTranslucent, // allow transparency + pixelData, + bih.biWidth, + bih.biHeight, + (byte*)palette, + bVTex + ); +} + + +void ProcessSPRFile( const char *pBaseDir, const char *pSubDir, const char *pFilename, bool bVTex ) +{ + if ( !g_bQuiet ) + printf( "[%s]\n", pFilename ); + + if ( !pSubDir ) + pSubDir = "."; + + char baseFilename[512]; + GetBaseFilename( pFilename, baseFilename ); + + // First make directories under materialsrc and materials if they don't exist. + EnsureDirectoriesExist( pBaseDir, pSubDir ); + + // Read in the SPR file. + FILE *fp = fopen( pFilename, "rb" ); + if ( !fp ) + Error( "ProcessSPRFile( %s ) can't open the file for reading.\n", pFilename ); + + dsprite_t header; + SafeRead( fp, &header, sizeof( header ) ); + + // Make sure it's a sprite file. + if ( ((header.ident>>0) & 0xFF) != 'I' || + ((header.ident>>8) & 0xFF) != 'D' || + ((header.ident>>16) & 0xFF) != 'S' || + ((header.ident>>24) & 0xFF) != 'P' ) + { + Warning( "WARNING: sprite %s is not a sprite file. Skipping.\n", pFilename ); + fclose( fp ); + return; + } + + if ( header.version != 2 ) + { + Warning( "WARNING: sprite %s is not a version 2 sprite file. Skipping.\n", pFilename ); + fclose( fp ); + return; + } + + // Read the palette. + short cnt; + byte palette[768]; + SafeRead( fp, &cnt, sizeof( cnt ) ); + SafeRead( fp, palette, sizeof( palette ) ); + + // Read the frames. + int i; + for ( i=0; i < header.numframes; i++ ) + { + spriteframetype_t type; + SafeRead( fp, &type, sizeof( type ) ); + if ( type == SPR_SINGLE ) + { + dspriteframe_t frame; + SafeRead( fp, &frame, sizeof( frame ) ); + if ( frame.width > 5000 || frame.height > 5000 || frame.width < 1 || frame.height < 1 ) + { + Warning( "WARNING: sprite %s has an invalid frame size (%d x %d) for frame %d.\n", pFilename, frame.width, frame.height, i ); + fclose( fp ); + return; + } + + byte *frameData = new byte[frame.width * frame.height]; + SafeRead( fp, frameData, frame.width * frame.height ); + + Msg( "\tFrame %d ", i ); + + // Write the TGA file for this frame. + bool bTranslucent, bResized; + char frameFilename[512]; + _snprintf( frameFilename, sizeof( frameFilename ), "%s\\materialsrc\\%s\\%s%03d.tga", pBaseDir, pSubDir, baseFilename, i ); + if ( !WriteTGAFile( + frameFilename, + g_bBMPAllowTranslucent, + frameData, + frame.width, + frame.height, + palette, + true, // allow power-of-2 + &bTranslucent, + &bResized ) ) + { + Error( "\tError writing %s.\n", frameFilename ); + } + + if ( !g_bQuiet ) + printf( "\n" ); + + delete [] frameData; + } + else if ( type == SPR_GROUP ) + { + Error( "Sprite %s uses type SPR_GROUP. Get a programmer to add support for it.\n", pFilename ); + } + else + { + Warning( "WARNING: sprite %s has an invalid frame type (%d) for frame %d.\n", pFilename, type, i ); + fclose( fp ); + return; + } + } + + fclose( fp ); + + // + // Generate a .txt file for the sprite. + // + char txtFilename[512]; + sprintf( txtFilename, "%s\\materialsrc\\%s\\%s.txt", pBaseDir, pSubDir, baseFilename ); + + fp = fopen( txtFilename, "wt" ); + if ( !fp ) + Error( "\tProcessSPRFile: can't open %s for writing.\n", txtFilename ); + + fprintf( fp, "\"startframe\" \"0\"\n" ); + fprintf( fp, "\"endframe\" \"%d\"\n", header.numframes-1 ); + fprintf( fp, "\"nomip\" \"1\"\n" ); + fprintf( fp, "\"nolod\" \"1\"\n" ); + fclose( fp ); + + // + // Run VTEX on the .txt file? + // + if ( bVTex ) + { + RunVTexOnFile( pBaseDir, txtFilename ); + } + + // + // Generate a .vmt file. + // + char vmtFilename[512]; + _snprintf( vmtFilename, sizeof( vmtFilename ), "%s\\materials\\%s\\%s.vmt", pBaseDir, pSubDir, baseFilename ); + fp = fopen( vmtFilename, "wt" ); + if ( !fp ) + Error( "\tProcessSPRFile: can't open %s for writing.\n", vmtFilename ); + + if ( g_pShader == g_pDefaultShader ) + fprintf( fp, "\"UnlitGeneric\"\n" ); + else + fprintf( fp, "\"%s\"\n", g_pShader ); + + fprintf( fp, "{\n" ); + fprintf( fp, "\t\"$spriteorientation\" \"vp_parallel\"\n" ); + fprintf( fp, "\t\"$spriteorigin\" \"[ 0.50 0.50 ]\"\n" ); + fprintf( fp, "\t\"$basetexture\" \"%s/%s\"\n", pSubDir, baseFilename ); + + for( i=0;i<g_NumVMTParams;i++ ) + { + fprintf( fp, "\t\"%s\" \"%s\"\n", g_VMTParams[i].m_szParam, g_VMTParams[i].m_szValue ); + } + + fprintf( fp, "}" ); + + fclose( fp ); +} + + +void ExtractDirectory( const char *pFilename, char *prefix ) +{ + const char *pSlash = strrchr( pFilename, '/' ); + if ( strrchr( pFilename, '\\' ) > pSlash ) + pSlash = strrchr( pFilename, '\\' ); + + if ( pSlash ) + { + memcpy( prefix, pFilename, pSlash - pFilename ); + prefix[ pSlash - pFilename ] = 0; + } + else + { + strcpy( prefix, ".\\" ); + } +} + + +// This allows them to have a WAD or BMP under their materialsrc directory and it'll try to figure out +// all the other parameters for them. +bool DragAndDropCheck( + const char **pBaseDir, + const char **pSubDir, + const char **pWadFilenames, + const char **pBMPFilenames, + const char **pSPRFilenames, + bool *bVTex ) +{ + const char *pLastParam = __argv[__argc-1]; + + // Get the first argument in upper case. + char arg1[512]; + strncpy( arg1, pLastParam, sizeof( arg1 ) ); + strupr( arg1 ); + + // Only handle it if there's a full path (with a colon). + if ( !strchr( arg1, ':' ) ) + return false; + + if ( strstr( arg1, ".WAD" ) ) + *pWadFilenames = pLastParam; + else if ( strstr( arg1, ".BMP" ) ) + *pBMPFilenames = pLastParam; + else if ( strstr( arg1, ".SPR" ) ) + *pSPRFilenames = pLastParam; + else + return false; + + // Ok, we know that argv[1] has a valid filename. Is it under materialsrc? + char *pMatSrc = strstr( arg1, "MATERIALSRC" ); + if ( !pMatSrc || pMatSrc == arg1 ) + return false; + + // The base directory is everything before materialsrc. + static char baseDir[512]; + *pBaseDir = baseDir; + memcpy( baseDir, arg1, pMatSrc-arg1 ); + baseDir[pMatSrc-arg1-1] = 0; + + // The subdirectory is everything after materialsrc, minus the filename. + char *pSubDirSrc = pMatSrc + strlen( "MATERIALSRC" ) + 1; + char *pEnd = strrchr( pSubDirSrc, '\\' ); + if ( strrchr( pSubDirSrc, '/' ) > pEnd ) + pEnd = strrchr( pSubDirSrc, '/' ); + + if ( !pEnd ) + return false; + + static char subDir[512]; + *pSubDir = subDir; + memcpy( subDir, pSubDirSrc, pEnd - pSubDirSrc ); + subDir[pEnd-pSubDirSrc] = 0; + + // Always use vtex in drag-and-drop mode. + *bVTex = true; + return true; +} + + +int main (int argc, char **argv) +{ + if (argc < 2) + { + return PrintUsage( "" ); + } + + bool bWriteTGA = true; + bool bWriteBMP = false; + bool bPowerOf2 = true; + bool bAutoDir = false; + + bool bVTex = false; + const char *pBaseDir = NULL; + const char *pWadFilenames = NULL; + const char *pBMPFilenames = NULL; + const char *pSPRFilenames = NULL; + const char *pSubDir = NULL; + const char *pOnlyTex = NULL; + + // Scan for options. + for ( int i=1; i < argc; i++ ) + { + if ( (i+2) < argc ) + { + if ( stricmp( argv[i], "-vmtparam" ) == 0 && g_NumVMTParams < MAX_VMT_PARAMS ) + { + g_VMTParams[g_NumVMTParams].m_szParam = argv[i+1]; + g_VMTParams[g_NumVMTParams].m_szValue = argv[i+2]; + + if( !g_bQuiet ) + { + fprintf( stderr, "Adding .vmt parameter: \"%s\"\t\"%s\"\n", + g_VMTParams[g_NumVMTParams].m_szParam, + g_VMTParams[g_NumVMTParams].m_szValue ); + } + + g_NumVMTParams++; + + i += 2; + } + } + + if ( (i+1) < argc ) + { + if ( stricmp( argv[i], "-basedir" ) == 0 ) + { + pBaseDir = argv[i+1]; + ++i; + } + else if ( stricmp( argv[i], "-wadfile" ) == 0 ) + { + pWadFilenames = argv[i+1]; + ++i; + } + else if ( stricmp( argv[i], "-bmpfile" ) == 0 ) + { + pBMPFilenames = argv[i+1]; + ++i; + } + else if ( stricmp( argv[i], "-sprfile" ) == 0 ) + { + pSPRFilenames = argv[i+1]; + ++i; + } + else if ( stricmp( argv[i], "-OnlyTex" ) == 0 ) + { + pOnlyTex = argv[i+1]; + ++i; + } + else if ( stricmp( argv[i], "-SubDir" ) == 0 ) + { + pSubDir = argv[i+1]; + ++i; + } + else if ( stricmp( argv[i], "-shader" ) == 0 ) + { + g_pShader = argv[i+1]; + ++i; + } + } + + if ( stricmp( argv[i], "-AutoDir" ) == 0 ) + { + bAutoDir = true; + } + else if ( stricmp( argv[i], "-Transparent" ) == 0 ) + { + g_bBMPAllowTranslucent = true; + } + else if ( stricmp( argv[i], "-Decal" ) == 0 ) + { + g_bDecal = true; + if ( g_pShader == g_pDefaultShader ) + g_pShader = "DecalModulate"; + } + else if ( stricmp( argv[i], "-quiet" ) == 0 ) + { + g_bQuiet = true; + } + else if ( stricmp( argv[i], "-vtex" ) == 0 ) + { + bVTex = true; + } + } + + if ( bAutoDir ) + { + if ( !DragAndDropCheck( &pBaseDir, &pSubDir, &pWadFilenames, &pBMPFilenames, &pSPRFilenames, &bVTex ) ) + return PrintUsage( "-AutoDir failed to setup directories." ); + } + + if ( !pBaseDir || (!pWadFilenames && !pBMPFilenames && !pSPRFilenames) ) + return PrintUsage( "Missing a parameter.\n" ); + + char prefix[512]; + + // Scan through each wadfile. + if ( pWadFilenames ) + { + ExtractDirectory( pWadFilenames, prefix ); + + _finddata_t findData; + long handle = _findfirst( pWadFilenames, &findData ); + if ( handle != -1 ) + { + do + { + if ( !(findData.attrib & _A_SUBDIR) ) + { + char fullFilename[512]; + sprintf( fullFilename, "%s\\%s", prefix, findData.name ); + ProcessWadFile( fullFilename, pBaseDir, pSubDir, pOnlyTex, bVTex ); + } + } while( _findnext( handle, &findData ) == 0 ); + + _findclose( handle ); + } + } + + // Process BMP files. + if ( pBMPFilenames ) + { + ExtractDirectory( pBMPFilenames, prefix ); + + _finddata_t findData; + long handle = _findfirst( pBMPFilenames, &findData ); + if ( handle != -1 ) + { + do + { + if ( !(findData.attrib & _A_SUBDIR) ) + { + char fullFilename[512]; + sprintf( fullFilename, "%s\\%s", prefix, findData.name ); + ProcessBMPFile( pBaseDir, pSubDir, fullFilename, bVTex ); + } + } while( _findnext( handle, &findData ) == 0 ); + + _findclose( handle ); + } + } + + if ( pSPRFilenames ) + { + ExtractDirectory( pSPRFilenames, prefix ); + + _finddata_t findData; + long handle = _findfirst( pSPRFilenames, &findData ); + if ( handle != -1 ) + { + do + { + if ( !(findData.attrib & _A_SUBDIR) ) + { + char fullFilename[512]; + sprintf( fullFilename, "%s\\%s", prefix, findData.name ); + ProcessSPRFile( pBaseDir, pSubDir, fullFilename, bVTex ); + } + } while( _findnext( handle, &findData ) == 0 ); + + _findclose( handle ); + } + } + + PrintExitStuff(); + return 0; +} + diff --git a/utils/xwad/xwad.vcproj b/utils/xwad/xwad.vcproj new file mode 100644 index 0000000..76bc9d8 --- /dev/null +++ b/utils/xwad/xwad.vcproj @@ -0,0 +1,185 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="7.10" + Name="xwad" + ProjectGUID="{B850012C-98A2-42F7-B023-9F65C448D938}" + SccProjectName="" + SccAuxPath="" + SccLocalPath="" + SccProvider=""> + <Platforms> + <Platform + Name="Win32"/> + </Platforms> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory=".\Debug" + IntermediateDirectory=".\Debug" + ConfigurationType="1" + UseOfMFC="0" + ATLMinimizesCRunTimeLibraryUsage="FALSE" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + BasicRuntimeChecks="3" + RuntimeLibrary="5" + UsePrecompiledHeader="2" + PrecompiledHeaderFile=".\Debug/xwad.pch" + AssemblerListingLocation=".\Debug/" + ObjectFile=".\Debug/" + ProgramDataBaseFileName=".\Debug/" + WarningLevel="3" + SuppressStartupBanner="TRUE" + DebugInformationFormat="4"/> + <Tool + Name="VCCustomBuildTool" + CommandLine="if exist ..\..\..\game\bin\"$(TargetName)".exe attrib -r ..\..\..\game\bin\"$(TargetName)".exe +copy "$(TargetPath)" ..\..\..\game\bin\"$(TargetName)".exe +if exist ..\..\..\game\bin\"$(TargetName)".pdb attrib -r ..\..\..\game\bin\"$(TargetName)".pdb +copy "$(TargetPath)" ..\..\..\game\bin\"$(TargetName)".pdb" + Outputs="..\..\..\game\bin\xwad.exe;..\..\..\game\bin\xwad.pdb"/> + <Tool + Name="VCLinkerTool" + OutputFile=".\Debug/xwad.exe" + LinkIncremental="2" + SuppressStartupBanner="TRUE" + GenerateDebugInformation="TRUE" + ProgramDatabaseFile=".\Debug/xwad.pdb" + SubSystem="1" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool" + TypeLibraryName=".\Debug/xwad.tlb" + HeaderFileName=""/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool" + PreprocessorDefinitions="_DEBUG" + Culture="1033"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory=".\Release" + IntermediateDirectory=".\Release" + ConfigurationType="1" + UseOfMFC="0" + ATLMinimizesCRunTimeLibraryUsage="FALSE" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + InlineFunctionExpansion="1" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + StringPooling="TRUE" + RuntimeLibrary="4" + EnableFunctionLevelLinking="TRUE" + UsePrecompiledHeader="2" + PrecompiledHeaderFile=".\Release/xwad.pch" + AssemblerListingLocation=".\Release/" + ObjectFile=".\Release/" + ProgramDataBaseFileName=".\Release/" + WarningLevel="3" + SuppressStartupBanner="TRUE"/> + <Tool + Name="VCCustomBuildTool" + CommandLine="if exist ..\..\..\game\bin\"$(TargetName)".exe attrib -r ..\..\..\game\bin\"$(TargetName)".exe +copy "$(TargetPath)" ..\..\..\game\bin\"$(TargetName)".exe +if exist ..\..\..\game\bin\"$(TargetName)".pdb attrib -r ..\..\..\game\bin\"$(TargetName)".pdb +copy "$(TargetPath)" ..\..\..\game\bin\"$(TargetName)".pdb" + Outputs="..\..\..\game\bin\xwad.exe;..\..\..\game\bin\xwad.pdb"/> + <Tool + Name="VCLinkerTool" + OutputFile=".\Release/xwad.exe" + LinkIncremental="1" + SuppressStartupBanner="TRUE" + ProgramDatabaseFile=".\Release/xwad.pdb" + SubSystem="1" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool" + TypeLibraryName=".\Release/xwad.tlb" + HeaderFileName=""/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool" + PreprocessorDefinitions="NDEBUG" + Culture="1033"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"> + <File + RelativePath="goldsrc_bspfile.h"> + </File> + <File + RelativePath="goldsrc_standin.cpp"> + </File> + <File + RelativePath="goldsrc_standin.h"> + </File> + <File + RelativePath="lbmlib.cpp"> + </File> + <File + RelativePath="lbmlib.h"> + </File> + <File + RelativePath="wadlib.cpp"> + </File> + <File + RelativePath="wadlib.h"> + </File> + <File + RelativePath="xwad.cpp"> + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl"> + </Filter> + <Filter + Name="Resource Files" + Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"> + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> |