/* Copyright (C) 2004 Michael Liebscher 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 2 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * wolf_gfx.c: Decode Wolfenstein3-D GFX data. * * Author: Michael Liebscher * Date: 2004 * * Acknowledgement: * This code was derived from Wolfenstein3-D, and was originally * written by Id Software, Inc. * */ #include #include #include #include "wolf_def.h" #include "../loaders/tga.h" #include "../hq2x.h" #include "../string/com_string.h" #include "../filesys/file.h" #include "../../../common/common_utils.h" #include "../../../common/arch.h" #include "../memory/memory.h" extern char gamepal[]; #define FILEPOSSIZE 3 #define GFXHEADFNAME "VGAHEAD" #define GFXFNAME "VGAGRAPH" #define GFXDICTFNAME "VGADICT" #define FONTWIDTH 256 #define FONTHEIGHT 128 typedef struct { short height; short location[ 256 ]; char width[ 256 ]; } fontstruct; typedef struct { // 0-255 is a character, > is a pointer to a node W16 bit0, bit1; } huffnode; typedef struct { W16 width, height; } pictabletype; pictabletype *pictable; huffnode grhuffman[ 255 ]; FILE *grhandle; SW32 *grstarts; // array of offsets in vgagraph, -1 for sparse void *grsegs[ NUMCHUNKS ]; /* ----------------------------------------------------------------------------- Function: GRFILEPOS() -Calculate graphic file position. Parameters: chunk -[in] Chunk number to calculate file offset. Returns: File offset value or -1 for sparse tile. Notes: See 'wl6_name.h' for valid chunk numbers for SOD and WL6. grstarts must be allocated and initialized before call. Uses global variable grstarts. ----------------------------------------------------------------------------- */ PRIVATE SW32 GRFILEPOS( W32 c ) { SW32 value; W32 offset; offset = c * 3; value = *(PW32)( ( (PW8)grstarts ) + offset ); value &= 0x00ffffffl; if( value == 0xffffffl ) { value = -1; } return value; }; /* ----------------------------------------------------------------------------- Function: CAL_GetGrChunkLength() -Calculate length of compressed graphic chunk. Parameters: chunk -[in] Chunk number to calculate file offset. Returns: The length of the compressed graphic chunk. Notes: Gets the length of an explicit length chunk (not tiles). The file pointer is positioned so the compressed data can be read in next. ----------------------------------------------------------------------------- */ PRIVATE SW32 CAL_GetGrChunkLength( W32 chunk ) { fseek( grhandle, GRFILEPOS( chunk ) + sizeof( W32 ), SEEK_SET ); return ( GRFILEPOS( chunk + 1 ) - GRFILEPOS( chunk ) - 4 ); } /* ----------------------------------------------------------------------------- Function: CAL_HuffExpand() -Expand compressed data. Parameters: source -[in] Pointer to compressed data. destination -[out] Pointer to hold decompressed data. length -[in] Length of expanded data. hufftable -[in] Huffman dictionary data. Returns: Nothing. Notes: Uses classic Huffman node tree (not optimized). ----------------------------------------------------------------------------- */ PRIVATE void CAL_HuffExpand( const W8 *source, W8 *destination, W32 length, huffnode *hufftable ) { W32 bx; // node pointer W32 dx; W32 ch; // high order of CX W32 cl; // low order of CX const W8 *si; // Source Index W8 *di; // Destination Index bx = 254; // head node is always node 254 si = source; di = destination; ch = *si; // load first byte si++; cl = 1; do { if( (ch & cl) & 0xff ) // bit set? dx = hufftable[ bx ].bit1; // take bit1 path from node else dx = hufftable[ bx ].bit0; // take bit0 path from node cl <<= 1; // advance to next bit position if( cl & 0x100 ) { ch = *si; // load next byte si++; cl = 1; // back to first bit } if( (cl & 0x100) == 0 ) { if( dx < 256 ) { *di = (W8)(dx & 0xff); // write a decompressed byte out di++; bx = 254; // back to the head node for next bit length--; } else { bx = dx - 256; // next node = (huffnode *)code } } } while( length ); } /* ----------------------------------------------------------------------------- Function: CAL_SetupGrFile() -Initialize graphic files and arrays. Parameters: extension -[in] Pointer to a null-terminated string that specifies the file extension. (must be in '.XXX' format). Returns: 1 on success, 0 otherwise. Notes: Uses global variables grhandle and pictable. 1. Open vgadict.XXX, read huffman dictionary data. 2. Open vgahead.XXX, read data offsets. 3. Open vgagraph.XXX, read pic and sprite header, expand data. ----------------------------------------------------------------------------- */ PRIVATE W8 CAL_SetupGrFile( const char *extension ) { void *compseg; FILE *handle; char filename[ 16 ]; SW32 chunkcomplen; // chunk compressed length // // load vgadict.ext (huffman dictionary for graphics files) // cs_strlcpy( filename, GFXDICTFNAME, sizeof( filename ) ); cs_strlcat( filename, extension, sizeof( filename ) ); if( ( handle = fopen( cs_strupr( filename ), "rb" ) ) == NULL ) { if( ( handle = fopen( cs_strlwr( filename ), "rb" ) ) == NULL ) { printf( "Could not open file (%s) for read!\n", filename ); return 0; } } fread( grhuffman, sizeof( grhuffman ), 1, handle ); fclose( handle ); // // Open then load the data offsets from vgahead.ext // cs_strlcpy( filename, GFXHEADFNAME, sizeof( filename ) ); cs_strlcat( filename, extension, sizeof( filename ) ); if( (handle = fopen( cs_strupr( filename ), "rb" )) == NULL ) { if( (handle = fopen( cs_strlwr( filename ), "rb" )) == NULL ) { printf( "Could not open file (%s) for read!\n", filename ); return 0; } } grstarts = MM_MALLOC( (NUMCHUNKS+1) * FILEPOSSIZE ); if( grstarts == NULL ) { return 0; } fread( grstarts, sizeof( long ), (NUMCHUNKS+1) * FILEPOSSIZE, handle ); fclose( handle ); // // Open the graphics file 'vgagraph.XXX'. // cs_strlcpy( filename, GFXFNAME, sizeof( filename ) ); cs_strlcat( filename, extension, sizeof( filename ) ); if( ( grhandle = fopen( cs_strupr( filename ), "rb" ) ) == NULL ) { if( ( grhandle = fopen( cs_strlwr( filename ), "rb" ) ) == NULL ) { printf( "Could not open file (%s) for read!\n", filename ); return 0; } } // // load the pic and sprite headers into the arrays. // pictable = MM_MALLOC( NUMPICS * sizeof( pictabletype ) ); if( pictable == NULL ) { return 0; } chunkcomplen = CAL_GetGrChunkLength( STRUCTPIC ); // position file pointer compseg = MM_MALLOC( chunkcomplen ); if( compseg == NULL ) { return 0; } fread( compseg, chunkcomplen, 1, grhandle ); CAL_HuffExpand( compseg, (PW8)pictable, NUMPICS * sizeof( pictabletype ), grhuffman ); MM_FREE( compseg ); return 1; } /* ----------------------------------------------------------------------------- Function: CAL_Shutdown() -Shutdown module. Parameters: Nothing. Returns: Nothing. Notes: Frees grstarts, pictable and grsegs data. Closes grhandle file handle. ----------------------------------------------------------------------------- */ PUBLIC void CAL_Shutdown( void ) { W32 i; if( grstarts ) { MM_FREE( grstarts ); } if( pictable ) { MM_FREE( pictable ); } if( grhandle ) { fclose( grhandle ); } for( i = 0; i < NUMCHUNKS; ++i ) { if( grsegs[ i ] ) { MM_FREE( grsegs[ i ] ); } } } /* ----------------------------------------------------------------------------- Function: CAL_ExpandGrChunk() -Expand compressed graphic chunk. Parameters: chunk -[in] Chunk number to expand. source -[in] Pointer to compressed data. version -[in] extension version. 1 -WL6 2 -SOD Returns: Nothing. Notes: ----------------------------------------------------------------------------- */ PRIVATE void CAL_ExpandGrChunk( W32 chunk, const W8 *source, W16 version ) { W32 expanded; W16 offset = 0; if( version & SOD_PAK ) offset = SOD_STARTDIFF; if( chunk >= ( STARTTILE8 + offset ) && chunk < ( STARTEXTERNS + offset ) ) { // // expanded sizes of tile8/16/32 are implicit // #define BLOCK 64 #define MASKBLOCK 128 if( chunk < (STARTTILE8M + offset) ) // tile 8s are all in one chunk! expanded = BLOCK * NUMTILE8; else if( chunk < (STARTTILE16 + offset) ) expanded = MASKBLOCK * NUMTILE8M; else if( chunk < (STARTTILE16M + offset) ) // all other tiles are one/chunk expanded = BLOCK << 2; else if( chunk < (STARTTILE32 + offset) ) expanded = MASKBLOCK << 2; else if( chunk < (STARTTILE32M + offset) ) expanded = BLOCK << 4; else expanded = MASKBLOCK << 4; } else { // // everything else has an explicit size longword // expanded = *(PW32)source; source += 4; // skip over length } // // allocate final space and decompress it. // Sprites need to have shifts made and various other junk. // grsegs[ chunk ] = MM_MALLOC( expanded ); if( grsegs[ chunk ] == NULL ) { return; } CAL_HuffExpand( source, grsegs[ chunk ], expanded, grhuffman ); } /* ----------------------------------------------------------------------------- Function: CA_CacheGrChunk() -Cache graphic chunk. Parameters: chunk -[in] Chunk number to cache. version -[in] extension version. 1 -WL6 2 -SOD Returns: Nothing. Notes: Makes sure a given chunk is in memory, loading it if needed. ----------------------------------------------------------------------------- */ PRIVATE void CA_CacheGrChunk( W32 chunk, W16 version ) { SW32 pos; W32 compressed; void *buffer; W32 next; if( grsegs[ chunk ] ) { return; // already in memory } // // load the chunk into a buffer // pos = GRFILEPOS( chunk ); if( pos < 0 ) // $FFFFFFFF start is a sparse tile { return; } next = chunk + 1; while( GRFILEPOS( next ) == -1 ) // skip past any sparse tiles { next++; } compressed = GRFILEPOS( next ) - pos; fseek( grhandle, pos, SEEK_SET ); buffer = MM_MALLOC( compressed ); if( buffer == NULL ) { return; } fread( buffer, 1, compressed, grhandle ); CAL_ExpandGrChunk( chunk, buffer, version ); MM_FREE( buffer ); } /* ----------------------------------------------------------------------------- Function: RGB32toRGB24() -Convert image from 32bits to 24bits. Parameters: src -[in] Buffer to convert from. dest -[in/out] Buffer to convert to. size -[in] Size of src buffer. Returns: Nothing. Notes: src and dest can point to the same memory block. ----------------------------------------------------------------------------- */ PUBLIC void RGB32toRGB24( const W8 *src, W8 *dest, size_t size ) { size_t i; size_t npixels = size >> 2; for( i = 0 ; i < npixels ; ++i ) { dest[ i * 3 + 0 ] = src[ i * 4 + 0 ]; dest[ i * 3 + 1 ] = src[ i * 4 + 1 ]; dest[ i * 3 + 2 ] = src[ i * 4 + 2 ]; } } /* ----------------------------------------------------------------------------- Function: MergePics() -Merge image inside another image. Parameters: src -[in] Source image to copy from. dest -[in] Destination image to copy to. width -[in] Width of source image. height -[in] Height of source image. bpp -[in] Bytes per pixel of images. totalwidth -[in] -Total width (in pixels) of destination image. x_offset -[in] X offset (in pixels) in destination image. y_offset -[in] Y offset (in pixels) in destination image. Returns: Nothing. Notes: ----------------------------------------------------------------------------- */ PRIVATE void MergePics( const W8 *src, W8 *dest, W16 width, W16 height, W16 bpp, W16 totalwidth, W32 x_offset, W32 y_offset ) { W16 x, y; W8 *dptr; dptr = dest + (y_offset * totalwidth * bpp) + (x_offset * bpp); for( y = 0 ; y < height ; ++y ) { for( x = 0 ; x < width ; ++x ) { dptr[ 0 ] = src[ 0 ]; dptr[ 1 ] = src[ 1 ]; src += bpp; dptr += bpp; } dptr += (totalwidth * bpp) - width * bpp; } } PRIVATE void MergeImages( W8 *src, W16 src_bpp, W16 src_totalwidth, W16 src_region_width, W16 src_region_height, W32 src_x_offset, W32 src_y_offset, W8 *dest, W16 dest_bpp, W16 dest_totalwidth, W16 dest_region_width, W16 dest_region_height, W32 dest_x_offset, W32 dest_y_offset ) { W16 x, y; W8 *dptr; W8 *sptr; dptr = dest + (dest_y_offset * dest_totalwidth * dest_bpp) + (dest_x_offset * dest_bpp); sptr = src + (src_y_offset * src_totalwidth * src_bpp) + (src_x_offset * src_bpp); for( y = 0 ; y < src_region_height ; ++y ) { for( x = 0 ; x < src_region_width ; ++x ) { dptr[ 0 ] = sptr[ 0 ]; dptr[ 1 ] = sptr[ 1 ]; sptr += src_bpp; dptr += dest_bpp; } dptr += (dest_totalwidth * dest_bpp) - dest_region_width * dest_bpp; sptr += (src_totalwidth * src_bpp) - src_region_width * src_bpp; } } /* ----------------------------------------------------------------------------- Function: SavePic() -Save graphic lump in targa image format. Parameters: chunk -[in] Chunk number to save. version -[in] extension version. 1 -WL6 2 -SOD Returns: Nothing. Notes: ----------------------------------------------------------------------------- */ PRIVATE void SavePic( W32 chunknum, W16 version, W8 *buffer, W8 *buffer2 ) { W16 i; W16 temp; char filename[32]; char *fname; W8 *ptr; W8 *pic; W32 picnum; W16 width, height; W16 linewidth, plane, sx, sy; W8 r,g,b; W16 rgb; W8 *tpalette = gamepal; static W16 offset = 0; if( ( (chunknum == WL1_N_BLANKPIC || chunknum == WL1_NOKEYPIC ) && (version & WL1_PAK) ) ) { return; } if( ( (chunknum == N_BLANKPIC || chunknum == NOKEYPIC ) && (version & WL6_PAK) ) ) { return; } if( ( (chunknum == SDM_N_BLANKPIC || chunknum == SDM_NOKEYPIC ) && (version & SDM_PAK) ) ) { return; } if( ( (chunknum == SOD_N_BLANKPIC || chunknum == SOD_NOKEYPIC ) && (version & SOD_PAK) ) ) { return; } // Spear used multiple palettes, so // pull out the one we need. if( version & SDM_PAK ) { switch( chunknum ) { case SDM_TITLE1PIC: case SDM_TITLE2PIC: CA_CacheGrChunk( SDM_TITLEPALETTE, version ); tpalette = grsegs[ SDM_TITLEPALETTE ]; break; default: tpalette = gamepal; break; } } if( version & SOD_PAK ) { switch( chunknum ) { case SOD_IDGUYS1PIC: case SOD_IDGUYS2PIC: CA_CacheGrChunk( SOD_IDGUYSPALETTE, version ); tpalette = grsegs[ SOD_IDGUYSPALETTE ]; break; case SOD_TITLE1PIC: case SOD_TITLE2PIC: CA_CacheGrChunk( SOD_TITLEPALETTE, version ); tpalette = grsegs[ SOD_TITLEPALETTE ]; break; case SOD_ENDSCREEN11PIC: CA_CacheGrChunk( SOD_END1PALETTE, version ); tpalette = grsegs[ SOD_END1PALETTE ]; break; case SOD_ENDSCREEN12PIC: CA_CacheGrChunk( SOD_END2PALETTE, version ); tpalette = grsegs[ SOD_END2PALETTE ]; break; case SOD_ENDSCREEN3PIC: CA_CacheGrChunk( SOD_END3PALETTE, version ); tpalette = grsegs[ SOD_END3PALETTE ]; break; case SOD_ENDSCREEN4PIC: CA_CacheGrChunk( SOD_END4PALETTE, version ); tpalette = grsegs[ SOD_END4PALETTE ]; break; case SOD_ENDSCREEN5PIC: CA_CacheGrChunk( SOD_END5PALETTE, version ); tpalette = grsegs[ SOD_END5PALETTE ]; break; case SOD_ENDSCREEN6PIC: CA_CacheGrChunk( SOD_END6PALETTE, version ); tpalette = grsegs[ SOD_END6PALETTE ]; break; case SOD_ENDSCREEN7PIC: CA_CacheGrChunk( SOD_END7PALETTE, version ); tpalette = grsegs[ SOD_END7PALETTE ]; break; case SOD_ENDSCREEN8PIC: CA_CacheGrChunk( SOD_END8PALETTE, version ); tpalette = grsegs[ SOD_END8PALETTE ]; break; case SOD_ENDSCREEN9PIC: CA_CacheGrChunk( SOD_END9PALETTE, version ); tpalette = grsegs[ SOD_END9PALETTE ]; break; default: tpalette = gamepal; break; } // End switch chunknum } // End if version & SOD_PAK STATUSBARHACK: picnum = chunknum - STARTPICS; pic = grsegs[ chunknum ]; width = pictable[ picnum ].width; height= pictable[ picnum ].height; linewidth = width / 4; for( i = 0; i < ( width * height ); ++i, pic++ ) { plane = i / ( (width * height) / 4 ); sx = ( ( i % ( linewidth ) ) * 4 ) + plane; sy = ( ( i / linewidth ) % height ); ptr = buffer + ( (sx*2) + (sy * width)*2); temp = (*pic) * 3; r = tpalette[ temp ] >> 1; g = tpalette[ temp+1 ]; b = tpalette[ temp+2 ] >> 1; rgb = (b << 11) | (g << 5) | r; ptr[ 0 ] = rgb & 0xff; ptr[ 1 ] = rgb >> 8; } // // Hacks to reassemble images // if( version & WL1_PAK ) { if( chunknum == WL1_STATUSBARPIC ) { memcpy( buffer2, buffer, width * height * 2 ); // Save Status bar pic CA_CacheGrChunk( WL1_NOKEYPIC, version ); // cache NOKEYPIC chunknum = WL1_NOKEYPIC; goto STATUSBARHACK; } else if( chunknum == WL1_H_BOTTOMINFOPIC ) { MergeImages( buffer, 2, 304, 91, 16, 24, 4, buffer2, 2, 91, 91, 16, 0, 0 ); MergeImages( buffer, 2, 304, 91, 16, 192, 4, buffer2, 2, 91, 91, 16, 0, 16 ); hq2x_32( buffer2, buffer, 91, 16, (91*2)*4 ); RGB32toRGB24( buffer, buffer, 182*32*4 ); cs_snprintf( filename, sizeof( filename ), "%s/%s.tga", LGFXDIR, "PLAQUE_PAGE" ); WriteTGA( filename, 24, 182, 32, buffer, 0, 0 ); hq2x_32( buffer2 + (16 * 91 * 2), buffer, 91, 16, (91*2)*4 ); RGB32toRGB24( buffer, buffer, 182*32*4 ); cs_snprintf( filename, sizeof( filename ), "%s/%s.tga", LGFXDIR, "PLAQUE_BLANK" ); WriteTGA( filename, 24, 182, 32, buffer, 0, 1 ); return; } else if( chunknum == WL1_NOKEYPIC ) { chunknum = WL1_STATUSBARPIC; MergePics( buffer, buffer2, width, height, 2, 320, 240, 4 ); MergePics( buffer, buffer2, width, height, 2, 320, 240, 4+height ); memcpy( buffer, buffer2, 320 * 40 * 2 ); width = 320; height = 40; } else if( chunknum == WL1_L_COLONPIC ) { memset( buffer2, 0, 256*64*2 ); MergePics( buffer, buffer2, width, height, 2, 256, 160, 16 ); return; } else if( chunknum == WL1_L_EXPOINTPIC ) { MergePics( buffer, buffer2, width, height, 2, 256, 16, 0 ); return; } else if( chunknum == WL1_L_APOSTROPHEPIC ) { W16 i; MergePics( buffer, buffer2, width, height, 2, 256, 112, 0 ); memcpy( buffer, buffer2, 256 * 64 * 2 ); for( i = 0 ; i < 256 * 64 * 2 ; i += 2 ) { if( buffer[ i ] == 0 && buffer[ i + 1 ] == 0 ) { buffer[ i + 1 ] = 66; } } offset = 0; width = 256; height = 64; } else if( chunknum == WL1_L_PERCENTPIC ) { offset = 16; // this is for L_APIC... MergePics( buffer, buffer2, width, height, 2, 256, 80, 0 ); return; } else if( chunknum >= WL1_L_NUM0PIC && chunknum <= WL1_L_NUM9PIC ) { MergePics( buffer, buffer2, width, height, 2, 256, offset, 16 ); offset += width; return; } else if( chunknum >= WL1_N_0PIC && chunknum < WL1_N_9PIC ) { MergePics( buffer, buffer2, width, height, 2, 90, offset, 0 ); offset += width + 1; return; } else if( chunknum == WL1_N_9PIC ) { W32 i; MergePics( buffer, buffer2, width, height, 2, 90, offset, 0 ); memcpy( buffer, buffer2, 90 * height * 2 ); for( i = 0 ; i < 90 * 16 * 2 ; i += 2 ) { if( ! (i % 9) && i != 0 ) { buffer[ i - 2 ] = 0; buffer[ i - 1 ] = 160; } } width = 90; offset = 0; } else if( chunknum >= WL1_L_APIC && chunknum <= WL1_L_ZPIC ) { static W32 yoffset = 32; MergePics( buffer, buffer2, width, height, 2, 256, offset, yoffset ); offset += width; if( offset >= 256 ) { offset = 0; yoffset += 16; } return; } else if( chunknum == WL1_FACE5CPIC ) { // hmmm... Why is this one messed up? MergeImages( buffer, 2, 24, 18, height-2, 8, 2, buffer2, 2, 24, 18, height-2, 0, 0 ); MergeImages( buffer, 2, 24, 8, height-3, 0, 3, buffer2, 2, 24, 8, height-2, 16, 0 ); MergeImages( buffer, 2, 24, 18, 2, 9, 0, buffer2, 2, 24, 18, 2, 0, height-2 ); MergeImages( buffer, 2, 24, 7, 3, 1, 0, buffer2, 2, 24, 7, 3, 16, height-3 ); memcpy( buffer, buffer2, 24 * 32 * 2 ); buffer[ (30 * 24 * 2) + (3 * 2) ] = 73; buffer[ (30 * 24 * 2) + (3 * 2) + 1 ] = 74; buffer[ (31 * 24 * 2) + (3 * 2) ] = 73; buffer[ (31 * 24 * 2) + (3 * 2) + 1 ] = 74; buffer[ (29 * 24 * 2) + (23 * 2) ] = 73; buffer[ (29 * 24 * 2) + (23 * 2) + 1 ] = 74; buffer[ (30 * 24 * 2) + (23 * 2) ] = 73; buffer[ (30 * 24 * 2) + (23 * 2) + 1 ] = 74; buffer[ (31 * 24 * 2) + (23 * 2) ] = 73; buffer[ (31 * 24 * 2) + (23 * 2) + 1 ] = 74; buffer[ (29 * 24 * 2) + (19 * 2) ] = 255; buffer[ (29 * 24 * 2) + (19 * 2) + 1 ] = 100; buffer[ (30 * 24 * 2) + (19 * 2) ] = 63; buffer[ (30 * 24 * 2) + (19 * 2) + 1 ] = 117; buffer[ (31 * 24 * 2) + (19 * 2) ] = 52; buffer[ (31 * 24 * 2) + (19 * 2) + 1 ] = 59; buffer[ (30 * 24 * 2) + (7 * 2) ] = 19; buffer[ (30 * 24 * 2) + (7 * 2) + 1 ] = 59; buffer[ (31 * 24 * 2) + (7 * 2) ] = 19; buffer[ (31 * 24 * 2) + (7 * 2) + 1 ] = 59; buffer[ (30 * 24 * 2) + (11 * 2) ] = 91; buffer[ (30 * 24 * 2) + (11 * 2) + 1 ] = 84; buffer[ (31 * 24 * 2) + (11 * 2) ] = 190; buffer[ (31 * 24 * 2) + (11 * 2) + 1 ] = 92; buffer[ (30 * 24 * 2) + (15 * 2) ] = 249; buffer[ (30 * 24 * 2) + (15 * 2) + 1 ] = 75; buffer[ (31 * 24 * 2) + (15 * 2) ] = 190; buffer[ (31 * 24 * 2) + (15 * 2) + 1 ] = 92; } } else if( version & WL6_PAK ) { if( chunknum == STATUSBARPIC ) { memcpy( buffer2, buffer, width * height * 2 ); // Save Status bar pic CA_CacheGrChunk( NOKEYPIC, version ); // cache NOKEYPIC chunknum = NOKEYPIC; goto STATUSBARHACK; } else if( chunknum == H_BOTTOMINFOPIC ) { MergeImages( buffer, 2, 304, 91, 16, 24, 4, buffer2, 2, 91, 91, 16, 0, 0 ); MergeImages( buffer, 2, 304, 91, 16, 192, 4, buffer2, 2, 91, 91, 16, 0, 16 ); hq2x_32( buffer2, buffer, 91, 16, (91*2)*4 ); RGB32toRGB24( buffer, buffer, 182*32*4 ); cs_snprintf( filename, sizeof( filename ), "%s/%s.tga", LGFXDIR, "PLAQUE_PAGE" ); WriteTGA( filename, 24, 182, 32, buffer, 0, 0 ); hq2x_32( buffer2 + (16 * 91 * 2), buffer, 91, 16, (91*2)*4 ); RGB32toRGB24( buffer, buffer, 182*32*4 ); cs_snprintf( filename, sizeof( filename ), "%s/%s.tga", LGFXDIR, "PLAQUE_BLANK" ); WriteTGA( filename, 24, 182, 32, buffer, 0, 1 ); return; } else if( chunknum == NOKEYPIC ) { chunknum = STATUSBARPIC; MergePics( buffer, buffer2, width, height, 2, 320, 240, 4 ); MergePics( buffer, buffer2, width, height, 2, 320, 240, 4+height ); memcpy( buffer, buffer2, 320 * 40 * 2 ); width = 320; height = 40; } else if( chunknum == L_COLONPIC ) { memset( buffer2, 0, 256*64*2 ); MergePics( buffer, buffer2, width, height, 2, 256, 160, 16 ); return; } else if( chunknum == L_EXPOINTPIC ) { MergePics( buffer, buffer2, width, height, 2, 256, 16, 0 ); return; } else if( chunknum == L_APOSTROPHEPIC ) { W16 i; MergePics( buffer, buffer2, width, height, 2, 256, 112, 0 ); memcpy( buffer, buffer2, 256 * 64 * 2 ); for( i = 0 ; i < 256 * 64 * 2 ; i += 2 ) { if( buffer[ i ] == 0 && buffer[ i + 1 ] == 0 ) { buffer[ i + 1 ] = 66; } } offset = 0; width = 256; height = 64; } else if( chunknum == L_PERCENTPIC ) { offset = 16; // this is for L_APIC... MergePics( buffer, buffer2, width, height, 2, 256, 80, 0 ); return; } else if( chunknum >= L_NUM0PIC && chunknum <= L_NUM9PIC ) { MergePics( buffer, buffer2, width, height, 2, 256, offset, 16 ); offset += width; return; } else if( chunknum >= N_0PIC && chunknum < N_9PIC ) { MergePics( buffer, buffer2, width, height, 2, 90, offset, 0 ); offset += width + 1; return; } else if( chunknum == N_9PIC ) { W32 i; MergePics( buffer, buffer2, width, height, 2, 90, offset, 0 ); memcpy( buffer, buffer2, 90 * height * 2 ); for( i = 0 ; i < 90 * 16 * 2 ; i += 2 ) { if( ! (i % 9) && i != 0 ) { buffer[ i - 2 ] = 0; buffer[ i - 1 ] = 160; } } width = 90; offset = 0; } else if( chunknum >= L_APIC && chunknum <= L_ZPIC ) { static W32 yoffset = 32; MergePics( buffer, buffer2, width, height, 2, 256, offset, yoffset ); offset += width; if( offset >= 256 ) { offset = 0; yoffset += 16; } return; } } else if( version & SDM_PAK ) { if( chunknum == SDM_STATUSBARPIC ) { memcpy( buffer2, buffer, width * height * 2 ); // Save Status bar pic CA_CacheGrChunk( SDM_NOKEYPIC, version ); // cache SOD_NOKEYPIC chunknum = SDM_NOKEYPIC; goto STATUSBARHACK; } else if( chunknum == SDM_NOKEYPIC ) { chunknum = SDM_STATUSBARPIC; MergePics( buffer, buffer2, width, height, 2, 320, 240, 4 ); MergePics( buffer, buffer2, width, height, 2, 320, 240, 4+height ); memcpy( buffer, buffer2, 320 * 40 * 2 ); width = 320; height = 40; } else if( chunknum == SDM_L_COLONPIC ) { memset( buffer2, 0, 256*64*2 ); MergePics( buffer, buffer2, width, height, 2, 256, 160, 16 ); return; } else if( chunknum == SDM_L_EXPOINTPIC ) { MergePics( buffer, buffer2, width, height, 2, 256, 16, 0 ); return; } else if( chunknum == SDM_L_APOSTROPHEPIC ) { W16 i; MergePics( buffer, buffer2, width, height, 2, 256, 112, 0 ); memcpy( buffer, buffer2, 256 * 64 * 2 ); for( i = 0 ; i < 256 * 64 * 2 ; i += 2 ) { if( buffer[ i ] == 0 && buffer[ i + 1 ] == 0 ) { buffer[ i + 1 ] = 66; } } offset = 0; width = 256; height = 64; } else if( chunknum == SDM_L_PERCENTPIC ) { offset = 16; // this is for L_APIC... MergePics( buffer, buffer2, width, height, 2, 256, 80, 0 ); return; } else if( chunknum >= SDM_L_NUM0PIC && chunknum <= SDM_L_NUM9PIC ) { MergePics( buffer, buffer2, width, height, 2, 256, offset, 16 ); offset += width; return; } else if( chunknum >= SDM_N_0PIC && chunknum < SDM_N_9PIC ) { MergePics( buffer, buffer2, width, height, 2, 90, offset, 0 ); offset += width + 1; return; } else if( chunknum == SDM_N_9PIC ) { W32 i; MergePics( buffer, buffer2, width, height, 2, 90, offset, 0 ); memcpy( buffer, buffer2, 90 * height * 2 ); for( i = 0 ; i < 90 * 16 * 2 ; i += 2 ) { if( ! (i % 9) && i != 0 ) { buffer[ i - 2 ] = 0; buffer[ i - 1 ] = 160; } } width = 90; offset = 0; } else if( chunknum >= SDM_L_APIC && chunknum <= SDM_L_ZPIC ) { static W32 yoffset = 32; MergePics( buffer, buffer2, width, height, 2, 256, offset, yoffset ); offset += width; if( offset >= 256 ) { offset = 0; yoffset += 16; } return; } else if( chunknum == SDM_TITLE1PIC ) { memcpy( buffer2+offset, buffer, (width*height*2) ); offset += width*height*2; return; } else if( chunknum == SDM_TITLE2PIC ) { memcpy( buffer2+offset, buffer, (width*height*2) ); memcpy( buffer, buffer2, 320*200*2 ); height = 200; offset = 0; } } else if( version & SOD_PAK ) { if( chunknum == SOD_STATUSBARPIC ) { memcpy( buffer2, buffer, width * height * 2 ); // Save Status bar pic CA_CacheGrChunk( SOD_NOKEYPIC, version ); // cache SOD_NOKEYPIC chunknum = SOD_NOKEYPIC; goto STATUSBARHACK; } else if( chunknum == SOD_NOKEYPIC ) { chunknum = SOD_STATUSBARPIC; MergePics( buffer, buffer2, width, height, 2, 320, 240, 4 ); MergePics( buffer, buffer2, width, height, 2, 320, 240, 4+height ); memcpy( buffer, buffer2, 320 * 40 * 2 ); width = 320; height = 40; } else if( chunknum == SOD_L_COLONPIC ) { memset( buffer2, 0, 256*64*2 ); MergePics( buffer, buffer2, width, height, 2, 256, 160, 16 ); return; } else if( chunknum == SOD_L_EXPOINTPIC ) { MergePics( buffer, buffer2, width, height, 2, 256, 16, 0 ); return; } else if( chunknum == SOD_L_APOSTROPHEPIC ) { W16 i; MergePics( buffer, buffer2, width, height, 2, 256, 112, 0 ); memcpy( buffer, buffer2, 256 * 64 * 2 ); for( i = 0 ; i < 256 * 64 * 2 ; i += 2 ) { if( buffer[ i ] == 0 && buffer[ i + 1 ] == 0 ) { buffer[ i + 1 ] = 66; } } offset = 0; width = 256; height = 64; } else if( chunknum == SOD_L_PERCENTPIC ) { offset = 16; // this is for L_APIC... MergePics( buffer, buffer2, width, height, 2, 256, 80, 0 ); return; } else if( chunknum >= SOD_L_NUM0PIC && chunknum <= SOD_L_NUM9PIC ) { MergePics( buffer, buffer2, width, height, 2, 256, offset, 16 ); offset += width; return; } else if( chunknum >= SOD_N_0PIC && chunknum < SOD_N_9PIC ) { MergePics( buffer, buffer2, width, height, 2, 90, offset, 0 ); offset += width + 1; return; } else if( chunknum == SOD_N_9PIC ) { W32 i; MergePics( buffer, buffer2, width, height, 2, 90, offset, 0 ); memcpy( buffer, buffer2, 90 * height * 2 ); for( i = 0 ; i < 90 * 16 * 2 ; i += 2 ) { if( ! (i % 9) && i != 0 ) { buffer[ i - 2 ] = 0; buffer[ i - 1 ] = 160; } } width = 90; offset = 0; } else if( chunknum >= SOD_L_APIC && chunknum <= SOD_L_ZPIC ) { static W32 yoffset = 32; MergePics( buffer, buffer2, width, height, 2, 256, offset, yoffset ); offset += width; if( offset >= 256 ) { offset = 0; yoffset += 16; } return; } else if( chunknum == SOD_IDGUYS1PIC ) { memcpy( buffer2+offset, buffer, (width*height*2) ); offset += width*height*2; return; } else if( chunknum == SOD_IDGUYS2PIC ) { memcpy( buffer2+offset, buffer, (width*height*2) ); memcpy( buffer, buffer2, 320*200*2 ); height = 200; offset = 0; } else if( chunknum == SOD_TITLE1PIC ) { memcpy( buffer2+offset, buffer, (width*height*2) ); offset += width*height*2; return; } else if( chunknum == SOD_TITLE2PIC ) { memcpy( buffer2+offset, buffer, (width*height*2) ); memcpy( buffer, buffer2, 320*200*2 ); height = 200; offset = 0; } } // // End of images hacks // if( version & WL1_PAK ) { fname = GetLumpFileName_WL1( chunknum ); } else if( version & WL6_PAK ) { fname = GetLumpFileName_WL6( chunknum ); } else if( version & SDM_PAK ) { fname = GetLumpFileName_SDM( chunknum ); } else if( version & SOD_PAK ) { fname = GetLumpFileName_SOD( chunknum ); } else { printf( "Unknown file extension!\n" ); return; } if( fname == NULL ) { printf( "File name not found for item: (%d)\n", chunknum ); return; } cs_snprintf( filename, sizeof( filename ), "%s/%s.tga", LGFXDIR, fname ); hq2x_32( buffer, buffer2, width, height, (width*2)*4 ); // Get rid of alpha channel RGB32toRGB24( buffer2, buffer2, (width*2)*(height*2)*4 ); WriteTGA( filename, 24, (width*2), (height*2), buffer2, 0, 1 ); return; } /* ----------------------------------------------------------------------------- Function: Fontline() -Extract and save font. Parameters: fontnumber -[in] font to save. version -[in] extension version. 1 -WL6 2 -SOD Returns: Nothing. Notes: Font must be cached in grsegs[] before calling. ----------------------------------------------------------------------------- */ PRIVATE void Fontline( W32 fontnumber, W16 version ) { fontstruct *font; W16 i; W16 x, y; W16 px, py; W8 *buffer; W8 *source; W8 *ptr; char filename[ 256 ]; font = (fontstruct *)grsegs[ fontnumber ]; buffer = MM_MALLOC( FONTWIDTH * FONTHEIGHT * 4 ); if( buffer == NULL ) return; ptr = buffer; for( x = 0; x < FONTWIDTH; ++x ) { for( y = 0; y < FONTHEIGHT; ++y, ptr += 4 ) { ptr[ 0 ] = ptr[ 1 ] = ptr[ 2 ] = 0xFF; ptr[ 3 ] = 0x00; } } px = py = 0; for( i = 0; i < 256; ++i ) { if( ! font->width[ i ] ) continue; if( px + font->width[ i ] > FONTWIDTH-1 ) { py += font->height; px = 0; } source = ((PW8) font) + font->location[ i ]; ptr = buffer + (py * FONTWIDTH + px) * 4; for( y = 0; y < font->height; ++y, ptr += FONTWIDTH * 4 ) { for( x = 0; x < font->width[ i ]; ++x ) { if( *source++ ) { ptr[ x * 4 + 3 ] = 0xFF; } } } px += 16; } // end for i = 0; i < 256; ++i cs_snprintf( filename, sizeof( filename ), "%s/font%d.tga", LGFXDIR, fontnumber ); WriteTGA( filename, 32, FONTWIDTH, FONTHEIGHT, buffer, 0, 1 ); MM_FREE( buffer ); } PRIVATE void DecodeText( W16 version ) { char *text; int artnum; int endextern; int i; int length; FILE *fhandle; int limit; limit = 6; if( version & SOD_PAK ) { endextern = 168; fhandle = fopen( "sod.txt", "wb" ); if( ! fhandle ) { return; } limit = 1; } else if( version & WL6_PAK ) { endextern = 143; fhandle = fopen( "wl6.txt", "wb" ); if( ! fhandle ) { return; } } else { return; } for( i = 0 ; i < limit ; ++i ) { artnum = endextern + i; CA_CacheGrChunk( artnum, version ); text = (char *)grsegs[ artnum ]; length = strlen( text ); fwrite( text, sizeof( W8 ), length, fhandle ); fprintf( fhandle, "\n\n" ); MM_FREE( grsegs[ artnum ] ); } fclose( fhandle ); } /* ----------------------------------------------------------------------------- Function: LumpExtractor() -Extract Lump gfx from Wolf3D and SOD data files. Parameters: fextension -[in] String holding file extension (must be in '.XXX' format). limit -[in] max version -[in] extension version. 1 -WL6 2 -SOD Returns: Nothing. Notes: ----------------------------------------------------------------------------- */ PUBLIC _boolean LumpExtractor( const char *fextension, W32 limit, W16 version ) { W32 i; W8 *buffer, *buffer2; if( ! fextension || ! *fextension ) { printf( "Invalid file extension passed into LumpExtractor!\n" ); return false; } // // Setup // if( 0 == FS_Mkdir( LGFXDIR ) ) { printf( "[%s] Could not create directory (%s)!\n", "wolf_gfx.c", LGFXDIR ); return false; } if( ! CAL_SetupGrFile( fextension ) ) { CAL_Shutdown(); return false; } // // Allocate buffers // buffer = MM_MALLOC( 320 * 416 * 2 ); if( buffer == NULL ) { CAL_Shutdown(); return false; } buffer2 = MM_MALLOC( 640 * 400 * 4 ); if( buffer2 == NULL ) { MM_FREE( buffer ); CAL_Shutdown(); return false; } // // Decode GFX data // printf( "Decoding GFX Data...\n" ); // (void)DecodeText( version ); for( i = STARTFONT; i < STARTPICS; ++i ) { CA_CacheGrChunk( i, version ); Fontline( i, version ); } for( i = STARTPICS; i < limit+1; ++i ) { CA_CacheGrChunk( i, version ); SavePic( i, version, buffer, buffer2 ); } // // Shutdown // MM_FREE( buffer2 ); MM_FREE( buffer ); CAL_Shutdown(); return true; }