wolf3d-ios/wolf3d/wolfextractor/wolf/wolf_pm.c

944 lines
20 KiB
C
Raw Normal View History

/*
Copyright (C) 2004-2005 Michael Liebscher <johnnycanuck@users.sourceforge.net>
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_pm.c: Decode Wolfenstein3-D Page data.
*
* Author: Michael Liebscher <johnnycanuck@users.sourceforge.net>
*
* Acknowledgement:
* This code was derived from Wolfenstein 3-D, and was originally
* written by Id Software, Inc.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "wolf_def.h"
#include "../../../common/arch.h"
#include "../../../common/common_utils.h"
#include "../loaders/tga.h"
#include "../hq2x.h"
#include "../loaders/wav.h"
#include "../filesys/file.h"
#include "../string/com_string.h"
#include "../memory/memory.h"
extern char gamepal[];
#define PAGEFNAME "VSWAP"
#define SAMPLERATE 7000 // In Hz
typedef struct
{
W32 offset; // Offset of chunk into file
W16 length; // Length of the chunk
} PageList_t;
typedef struct
{
W16 leftpix, rightpix;
W16 dataofs[ 64 ];
// table data after dataofs[ rightpix - leftpix + 1 ]
} t_compshape;
PRIVATE PageList_t *PMPages;
PRIVATE FILE *PageFile = NULL;
PRIVATE W16 PMNumBlocks;
PRIVATE W16 PMSpriteStart, PMSoundStart;
/*
-----------------------------------------------------------------------------
Function: CAL_GetGrChunkLength() -Opens the page file and sets up
the page info.
Parameters: extension -[in] Pointer to a null-terminated string that
specifies the file extension.
(must be in '.XXX' format)
Returns: 1 on success, otherwise 0.
Notes:
-----------------------------------------------------------------------------
*/
PRIVATE W8 PML_OpenPageFile( const char *extension )
{
W32 i;
W32 size;
void *buf;
char filename[ 16 ];
W32 *offsetptr;
W16 *lengthptr;
PageList_t *page;
if( ! extension || ! *extension )
{
printf( "Invalid file extension passed to PML_OpenPageFile()\n" );
return 0;
}
cs_strlcpy( filename, PAGEFNAME, sizeof( filename ) );
cs_strlcat( filename, extension, sizeof( filename ) );
PageFile = fopen( cs_strupr( filename ), "rb" );
if( PageFile == NULL )
{
PageFile = fopen( cs_strlwr( filename ), "rb" );
if( PageFile == NULL )
{
printf( "Could not open file (%s) for read!\n", filename );
return 0;
}
}
// Read in header variables
fread( &PMNumBlocks, sizeof( PMNumBlocks ), 1, PageFile );
fread( &PMSpriteStart, sizeof( PMSpriteStart ), 1, PageFile );
fread( &PMSoundStart, sizeof( PMSoundStart ), 1, PageFile );
// Allocate and clear the page list
PMPages = (PageList_t *) MM_MALLOC( sizeof( PageList_t ) * PMNumBlocks );
if( PMPages == NULL )
{
return 0;
}
memset( PMPages, 0, sizeof( PageList_t ) * PMNumBlocks );
// Read in the chunk offsets
size = sizeof( W32 ) * PMNumBlocks;
buf = MM_MALLOC( size );
if( buf == NULL )
{
return 0;
}
if( fread( buf, 1, size, PageFile ) == 0 )
{
printf( "PML_OpenPageFile: Length read failed\n" );
}
offsetptr = (PW32) buf;
for( i = 0, page = PMPages; i < PMNumBlocks; i++, page++ )
{
page->offset = *offsetptr++;
}
MM_FREE( buf );
// Read in the chunk lengths
size = sizeof( W16 ) * PMNumBlocks;
buf = MM_MALLOC( size );
if( buf == NULL )
{
return 0;
}
if( fread( buf, 1, size, PageFile ) == 0 )
{
printf( "PML_OpenPageFile: Length read failed\n" );
}
lengthptr = (PW16)buf;
for( i = 0, page = PMPages; i < PMNumBlocks; ++i, page++ )
{
page->length = *lengthptr++;
}
MM_FREE( buf );
return 1;
}
/*
-----------------------------------------------------------------------------
Function: PML_ReadFromFile() -Reads in data from Page file.
Parameters: buf -[out] Storage location for data.
offset -[in] Number of bytes from beginning of file.
length -[in] Maximum number of items to be read.
Returns: Nothing.
Notes:
-----------------------------------------------------------------------------
*/
PRIVATE void PML_ReadFromFile( W8 *buf, SW32 offset, W16 length )
{
if( ! buf )
{
printf( "[PML_ReadFromFile]: NULL pointer\n" );
return;
}
if( ! offset )
{
printf( "[PML_ReadFromFile]: Zero offset\n" );
return;
}
if( fseek( PageFile, offset, SEEK_SET ) )
{
printf( "[PML_ReadFromFile]: Seek failed\n" );
return;
}
if( ! fread( buf, 1, length, PageFile ) )
{
printf( "[PML_ReadFromFile]: Read failed\n" );
return;
}
}
/*
-----------------------------------------------------------------------------
Function: PML_LoadPage() -Reads in data from Page file.
Parameters: buf -[out] Storage location for data.
offset -[in] Number of bytes from beginning of file.
clength -[in] Maximum number of items to be read.
Returns: Nothing.
Notes:
-----------------------------------------------------------------------------
*/
PRIVATE void *PML_LoadPage( W32 pagenum, W16 *clength )
{
W8 *addr;
PageList_t *page;
page = &PMPages[ pagenum ];
if( page->length == 0 )
{
return NULL;
}
*clength = page->length;
addr = MM_MALLOC( page->length );
if( addr == NULL )
{
return NULL;
}
PML_ReadFromFile( addr, page->offset, page->length );
return addr;
}
void OutlineImage( W8 *rgba, int width, int height ) {
W8 *data_p;
W8 *copy_p;
W8 *copy = (W8 *)_alloca( width * height * 4 );
int x, y;
memcpy( copy, rgba, width * height * 4 );
data_p = rgba;
copy_p = copy;
for ( y = 0 ; y < height ; y++ ) {
for ( x = 0 ; x < width ; x++, data_p+=4, copy_p+=4 ) {
if ( data_p[3] != 0 ) {
continue;
}
if ( x < width-1 && copy_p[7] != 0 ) {
*(int *)data_p = ((int *)copy_p)[1];
} else if ( x > 0 && copy_p[-1] != 0 ) {
*(int *)data_p = ((int *)copy_p)[-1];
} else if ( y < height-1 && copy_p[width*4+3] != 0 ) {
*(int *)data_p = ((int *)copy_p)[width];
} else if ( y > 0 && copy_p[-width*4+3] != 0 ) {
*(int *)data_p = ((int *)copy_p)[-width];
}
data_p[3] = 1;
}
}
}
typedef struct {
int hasAlpha;
int srcWidth;
int srcHeight;
int uploadWidth;
int uploadHeight;
// track the outlines of up to two boxes of non-transparent pixels.
// The reason for two boxes is that the common lights have something
// at the top and something at the bottom, with nothing inbetween.
// These are inclusive bounds of the rows / columns with non-0 alpha
int numBounds;
int bounds[2][2][2];
} picHeader_t;
int RowClear( W8 *rgba, int w, int h, int y ) {
int x;
for ( x = 0 ; x < w ; x++ ) {
if ( rgba[(y*w+x)*4+3] != 0 ) {
return 0;
}
}
return 1;
}
void Write5551( const char *tgaName, int w, int h, W8 *rgba, int hasAlpha ) {
unsigned short *s = _alloca( w*h*4 );
FILE *f;
picHeader_t *header = (picHeader_t *)s;
unsigned short *s_p;
int i;
int shorts;
W8 *tempMip;
W8 *rgba_p;
int b, x, y, c;
char newName[1024];
char *ext;
memset( header, 0, sizeof( *header ) );
header->hasAlpha = hasAlpha;
header->srcWidth = w;
header->srcHeight = h;
header->uploadWidth = w;
header->uploadHeight = h;
if ( !hasAlpha ) {
// convert from 24 bit to 32 bit
W8 *newPic = _alloca( w * h * 4 );
for ( i = 0 ; i < w*h ; i++ ) {
newPic[i*4+0] = rgba[i*3+0];
newPic[i*4+1] = rgba[i*3+1];
newPic[i*4+2] = rgba[i*3+2];
newPic[i*4+3] = 255;
}
rgba = newPic;
}
// find the bounding boxes for more efficient drawing
header->numBounds = 1;
for ( y = 0 ; y < h ; y++ ) {
if ( !RowClear( rgba, w, h, y ) ) {
// this row is needed
header->bounds[0][0][1] = y;
break;
}
}
for ( y = h-1 ; y >= 0 ; y-- ) {
if ( !RowClear( rgba, w, h, y ) ) {
// this row is needed
header->bounds[0][1][1] = y;
break;
}
}
// if the middle row is clear, make two boxes
// We could make a better test, but this catches the ones we care about...
if ( header->bounds[0][0][1] < h/2 && header->bounds[0][1][1] > h / 2 && RowClear( rgba, w, h, h/2 ) ) {
header->numBounds = 2;
header->bounds[1][1][1] = header->bounds[0][1][1];
for ( y = h/2-1 ; y >= 0 ; y-- ) {
if ( !RowClear( rgba, w, h, y ) ) {
header->bounds[0][1][1] = y;
break;
}
}
for ( y = h/2+1 ; y < h ; y++ ) {
if ( !RowClear( rgba, w, h, y ) ) {
header->bounds[1][0][1] = y;
break;
}
}
}
for ( b = 0 ; b < header->numBounds ; b++ ) {
for ( x = 0 ; x < w ; x++ ) {
for ( y = header->bounds[b][0][1] ; y <= header->bounds[b][1][1] ; y++ ) {
if ( rgba[(y*w+x)*4+3] != 0 ) {
// this column is needed
header->bounds[b][0][0] = x;
break;
}
}
if ( y <= header->bounds[b][1][1] ) {
break;
}
}
for ( x = w-1 ; x >= 0 ; x-- ) {
for ( y = header->bounds[b][0][1] ; y <= header->bounds[b][1][1] ; y++ ) {
if ( rgba[(y*w+x)*4+3] != 0 ) {
// this column is needed
header->bounds[b][1][0] = x;
break;
}
}
if ( y <= header->bounds[b][1][1] ) {
break;
}
}
}
s_p = (unsigned short *)(header+1);
while ( 1 ) {
rgba_p = rgba;
// convert to 5551
for ( i = 0 ; i < w*h ; i++, rgba_p+=4 ) {
int r = rgba_p[0];
int g = rgba_p[1];
int b = rgba_p[2];
int a = rgba_p[3];
*s_p++ = ((r>>3)<<11) | ((g>>3)<<6) | ((b>>3)<<1) | (a>>7);
}
if ( w == 1 && h == 1 ) {
break;
}
// mip map
w >>= 1;
if ( w == 0 ) {
w = 1;
}
h >>= 1;
if ( h == 0 ) {
h = 1;
}
tempMip = _alloca( w * h * 4 );
for ( y = 0 ; y < h ; y++ ) {
for ( x = 0 ; x < w ; x++ ) {
for ( c = 0 ; c < 4 ; c++ ) {
tempMip[(y*w+x)*4+c] = (
rgba[((y*2+0)*w*2+(x*2+0))*4+c] +
rgba[((y*2+0)*w*2+(x*2+1))*4+c] +
rgba[((y*2+1)*w*2+(x*2+0))*4+c] +
rgba[((y*2+1)*w*2+(x*2+1))*4+c] ) >> 2;
}
}
}
rgba = tempMip;
}
shorts = s_p - s;
// byte swap
#if 0
for ( i = 0 ; i < shorts ; i++ ) {
int temp = ((W8 *)s)[i*2+0];
((W8 *)s)[i*2+0] = ((W8 *)s)[i*2+1];
((W8 *)s)[i*2+1] = temp;
}
#endif
// write
strcpy( newName, tgaName );
ext = strstr( newName, ".tga" );
strcpy( ext, ".5551" );
f = fopen( newName, "wb" );
if( f == NULL ) {
printf( "Could not open file (%s) for write!\n", newName );
return;
}
fwrite( s, 2, shorts, f );
fclose( f );
}
#include <assert.h>
void UpdateSingleSprite( const char *srcTGA, const char *destTGA ) {
W8 *rgba;
int width, height;
int i;
// HACK HACK HACK just to convert the single health pack sprite without the
// trademarked "red cross"
LoadTGA( srcTGA, &rgba, &width, &height );
assert( rgba );
assert( width == 64 && height == 64 );
// JDC: outline the image to avoid dark halos with filtering
for ( i = 0 ; i < 8 ; i++ ) {
OutlineImage( rgba, 64, 64 );
}
for ( i = 0 ; i < 64*64 ; i++ ) {
if ( rgba[i*4+3] == 1 ) {
rgba[i*4+3] = 0;
}
}
WriteTGA( destTGA, 32, 64, 64, rgba, 0, 1 );
Write5551( destTGA, 64, 64, rgba, 1 );
}
/*
-----------------------------------------------------------------------------
Function: PML_LoadPage() -Save image data from page file.
Parameters: nPage -[in] Page number to save.
filename -[in] Pointer to string with filename.
buffer -[in] Allocated memory buffer to hold image data.
buffer2 -[in] Allocated memory buffer to hold hq2x data.
Returns: Non-zero on success, otherwise zero.
Notes:
-----------------------------------------------------------------------------
*/
PRIVATE W8 PML_SaveGFXPage( W32 nPage, const char *filename,
W8 *buffer, W8 *buffer2, _boolean iswall, W32 GunFlash )
{
W16 x, y;
W8 *data;
W8 *ptr;
W16 temp;
W8 r,g,b;
W16 clength; // Chunk length
data = (PW8)PML_LoadPage( nPage, &clength );
if( data == NULL )
{
return 0;
}
if( nPage < PMSpriteStart )
{
// simple 64x64 image
W8 *rgb = (W8 *)_alloca( 64*64*3 );
for( x = 0; x < 64; ++x )
{
for( y = 0; y < 64; ++y )
{
temp = ( data[ (x<<6)+y ] ) * 3;
// gamepal is in 6 bit color
r = gamepal[ temp ];
g = gamepal[ temp+1 ];
b = gamepal[ temp+2 ];
ptr = rgb + ( ( (y << 6) + x ) * 3 );
ptr[ 0 ] = ( r << 2 ) | ( r >> 4 );
ptr[ 1 ] = ( g << 2 ) | ( g >> 4 );
ptr[ 2 ] = ( b << 2 ) | ( b >> 4 );
}
}
WriteTGA( filename, 24, 64, 64, rgb, 0, 1 );
Write5551( filename, 64, 64, rgb, 0 );
}
else if( nPage >= PMSpriteStart && nPage < PMSoundStart )
{
W16 *cmdptr;
short *linecmds;
t_compshape *shape;
int x, y;
W8 *rgba = (W8 *)_alloca( 64*64*4 );
int i;
// all transparent at the beginning
memset( rgba, 0, 64*64*4 );
// draw the spans into the buffer
shape = (t_compshape *)data;
cmdptr = shape->dataofs;
for( x = shape->leftpix; x <= shape->rightpix; ++x )
{
linecmds = (short *)( data + *cmdptr++ );
for( ; *linecmds; linecmds += 3 )
{
i = linecmds[ 2 ] / 2 + linecmds[ 1 ];
for( y = linecmds[ 2 ] / 2; y < linecmds[ 0 ] / 2; ++y, ++i )
{
temp = ( data[ i ] ) * 3;
r = gamepal[ temp ];
g = gamepal[ temp+1 ];
b = gamepal[ temp+2 ];
ptr = rgba + ( (y * 64 + x) * 4 );
ptr[ 0 ] = ( r << 2 ) | ( r >> 4 );
ptr[ 1 ] = ( g << 2 ) | ( g >> 4 );
ptr[ 2 ] = ( b << 2 ) | ( b >> 4 );
ptr[ 3 ] = 255;
}
}
}
// JDC: outline the image to avoid dark halos with filtering
for ( i = 0 ; i < 8 ; i++ ) {
OutlineImage( rgba, 64, 64 );
}
for ( i = 0 ; i < 64*64 ; i++ ) {
if ( rgba[i*4+3] == 1 ) {
rgba[i*4+3] = 0;
}
}
WriteTGA( filename, 32, 64, 64, rgba, 0, 1 );
Write5551( filename, 64, 64, rgba, 1 );
}
else
{
MM_FREE( data );
printf( "Out of bounds page number passed into PML_SavePage()!\n" );
return 0;
}
MM_FREE( data );
return 1;
}
/*
-----------------------------------------------------------------------------
Function: PML_SaveSoundPage() -Save sound data from Page file.
Parameters: nPage -[in] Page number to save.
filename -[in] Pointer to string with filename.
buffer -[in] Allocated memory buffer to hold sound data.
size -[in] Size of allocated memory buffer.
Returns: Non-zero on success, otherwise zero.
Notes:
-----------------------------------------------------------------------------
*/
PRIVATE W8 PML_SaveSoundPage( W32 nPage, char *filename,
W8 *buffer,
W32 size )
{
static W16 totallength = 0;
W8 *data;
W16 clength; // Chunk length
if( nPage < PMSoundStart || nPage > PMNumBlocks )
{
printf( "Out of bounds page number passed into PML_SaveSound()!\n" );
return 1;
}
data = (PW8) PML_LoadPage( nPage, &clength );
if( data == NULL )
{
return 1;
}
if( totallength > size )
{
printf( "[wolf_pmc] Buffer not large enough!\n" );
return 2;
}
memcpy( buffer + totallength, data, clength );
totallength += clength;
if( clength < 4096 )
{
write_wav( filename, buffer, totallength, 1, SAMPLERATE, 1 );
totallength = 0;
}
MM_FREE( data );
return 0;
}
/*
-----------------------------------------------------------------------------
Function: PML_Shutdown() -Shutdown page cache.
Parameters: Nothing.
Returns: Nothing.
Notes:
-----------------------------------------------------------------------------
*/
PRIVATE void PML_Shutdown()
{
if( PageFile )
{
fclose( PageFile );
PageFile = NULL;
}
if( PMPages )
{
MM_FREE( PMPages );
}
}
/*
-----------------------------------------------------------------------------
Function: PExtractor() -Interface to page manager.
Parameters: extension -[in] Ponter to string with file extenion of data
files.
Returns: Nothing.
Notes:
-----------------------------------------------------------------------------
*/
PUBLIC _boolean PExtractor( const char *extension, W16 version )
{
W32 i, j;
char filename[ 256 ];
W8 *buffer, *buffer2;
W32 Flash;
//
// Setup
//
if( 0 == FS_Mkdir( GFXWALLDIR ) )
{
printf( "[%s] Could not create directory (%s)!\n", "PExtractor", GFXWALLDIR );
return false;
}
if( version == SOD_PAK || version == SDM_PAK )
{
if( 0 == FS_Mkdir( SODGFXSPRITEDIR ) )
{
printf( "[%s] Could not create directory (%s)!\n", "PExtractor", GFXSPRITEDIR );
return false;
}
if( 0 == FS_Mkdir( SODSFXDIR ) )
{
printf( "[%s] Could not create directory (%s)!\n", "PExtractor", SODSFXDIR );
return false;
}
}
else
{
if( 0 == FS_Mkdir( GFXSPRITEDIR ) )
{
printf( "[%s] Could not create directory (%s)!\n", "PExtractor", GFXSPRITEDIR );
return false;
}
if( 0 == FS_Mkdir( SFXDIR ) )
{
printf( "[%s] Could not create directory (%s)!\n", "PExtractor", SFXDIR );
return false;
}
}
if( ! PML_OpenPageFile( extension ) )
{
PML_Shutdown();
return false;
}
//
// Allocate buffers
//
buffer = MM_MALLOC( 64 * 64 * 2 );
if( buffer == NULL )
{
PML_Shutdown();
return false;
}
buffer2 = MM_MALLOC( 128 * 128 * 4 );
if( buffer2 == NULL )
{
MM_FREE( buffer );
PML_Shutdown();
return false;
}
//
// Decode Page data
//
printf( "Decoding Page Data...\n" );
for( i = 0, j = 0; i < PMSpriteStart; ++i, ++j )
{
// Hacks
if( version == WL6_PAK || version == WL1_PAK )
{
if( 98 == j )
{
j = 126;
}
}
cs_snprintf( filename, sizeof( filename ), "%s/%.3d.tga", GFXWALLDIR, j );
PML_SaveGFXPage( i, filename, buffer, buffer2, 1, 0 );
}
for( i = PMSpriteStart, j = 0; i < PMSoundStart; ++i, ++j )
{
// Hacks
if( version == WL1_PAK )
{
if( j == 50 )
{
j = 54;
}
if( j == 191 )
{
j = 300;
i += 109;
}
if( j == 311 )
{
j = 431;
i += 101;
}
if( j == 439 )
{
j = 514;
}
}
if( version == WL6_PAK )
{
if( j == 50 )
{
j = 54;
}
if( j == 389 )
{
j = 408;
}
if( j == 439 )
{
j = 514;
}
}
if( version == SDM_PAK )
{
if( j == 401 )
{
j = 514;
}
}
if( version == SOD_PAK )
{
if( j == 292 )
{
j = 374;
}
if( j == 408 )
{
j = 439;
}
}
if( version == SOD_PAK || version == SDM_PAK )
{
cs_snprintf( filename, sizeof( filename ), "%s/%.3d.tga", SODGFXSPRITEDIR, j );
}
else
{
cs_snprintf( filename, sizeof( filename ), "%s/%.3d.tga", GFXSPRITEDIR, j );
}
if( j == 531 ||
j == 532 ||
j == 526 ||
j == 521 )
{
Flash = j;
}
else
{
Flash = 0;
}
PML_SaveGFXPage( i, filename, buffer, buffer2, 0, Flash );
}
for( i = PMSoundStart, j = 0; i < PMNumBlocks-1; ++i, ++j )
{
if( version == SOD_PAK || version == SDM_PAK )
{
cs_snprintf( filename, sizeof( filename ), "%s/%.3d.wav", SODSFXDIR, j );
}
else
{
cs_snprintf( filename, sizeof( filename ), "%s/%.3d.wav", SFXDIR, j );
}
PML_SaveSoundPage( i, filename, buffer2, 128 * 128 * 4 );
}
//
// Shutdown
//
MM_FREE( buffer );
MM_FREE( buffer2 );
PML_Shutdown();
return true;
}