2014-03-15 09:59:03 -07:00
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
2018-11-25 04:35:38 -08:00
// Copyright (C) 1999-2018 by Sonic Team Junior.
2014-03-15 09:59:03 -07:00
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file r_data.c
/// \brief Preparation of data for rendering,generation of lookups, caching, retrieval by name
# include "doomdef.h"
# include "g_game.h"
# include "i_video.h"
# include "r_local.h"
# include "r_sky.h"
# include "p_local.h"
# include "m_misc.h"
# include "r_data.h"
# include "w_wad.h"
# include "z_zone.h"
# include "p_setup.h" // levelflats
# include "v_video.h" // pLocalPalette
# include "dehacked.h"
# if defined (_WIN32) || defined (_WIN32_WCE)
# include <malloc.h> // alloca(sizeof)
# endif
# if defined(_MSC_VER)
# pragma pack(1)
# endif
// Not sure if this is necessary, but it was in w_wad.c, so I'm putting it here too -Shadow Hog
# ifdef _WIN32_WCE
# define AVOID_ERRNO
# else
# include <errno.h>
# endif
2019-05-26 19:37:23 -07:00
# ifdef HAVE_PNG
# ifndef _MSC_VER
# ifndef _LARGEFILE64_SOURCE
# define _LARGEFILE64_SOURCE
# endif
# endif
# ifndef _LFS64_LARGEFILE
# define _LFS64_LARGEFILE
# endif
# ifndef _FILE_OFFSET_BITS
# define _FILE_OFFSET_BITS 0
# endif
# include "png.h"
# ifndef PNG_READ_SUPPORTED
# undef HAVE_PNG
# endif
# endif
2014-03-15 09:59:03 -07:00
//
// Texture definition.
// Each texture is composed of one or more patches,
// with patches being lumps stored in the WAD.
// The lumps are referenced by number, and patched
// into the rectangular texture space using origin
// and possibly other attributes.
//
typedef struct
{
INT16 originx , originy ;
INT16 patch , stepdir , colormap ;
} ATTRPACK mappatch_t ;
//
// Texture definition.
// An SRB2 wall texture is a list of patches
// which are to be combined in a predefined order.
//
typedef struct
{
char name [ 8 ] ;
INT32 masked ;
INT16 width ;
INT16 height ;
INT32 columndirectory ; // FIXTHIS: OBSOLETE
INT16 patchcount ;
mappatch_t patches [ 1 ] ;
} ATTRPACK maptexture_t ;
# if defined(_MSC_VER)
# pragma pack()
# endif
// Store lists of lumps for F_START/F_END etc.
typedef struct
{
UINT16 wadfile ;
UINT16 firstlump ;
size_t numlumps ;
} lumplist_t ;
//
// Graphics.
// SRB2 graphics for walls and sprites
// is stored in vertical runs of opaque pixels (posts).
// A column is composed of zero or more posts,
// a patch or sprite is composed of zero or more columns.
//
size_t numspritelumps , max_spritelumps ;
// textures
INT32 numtextures = 0 ; // total number of textures found,
// size of following tables
texture_t * * textures = NULL ;
2019-05-21 11:24:26 -07:00
textureflat_t * texflats = NULL ;
2014-03-15 09:59:03 -07:00
static UINT32 * * texturecolumnofs ; // column offset lookup table for each texture
static UINT8 * * texturecache ; // graphics data for each generated full-size texture
2019-05-20 20:28:52 -07:00
INT32 * texturewidth ;
2014-03-15 09:59:03 -07:00
fixed_t * textureheight ; // needed for texture pegging
INT32 * texturetranslation ;
// needed for pre rendering
sprcache_t * spritecachedinfo ;
lighttable_t * colormaps ;
// for debugging/info purposes
static size_t flatmemory , spritememory , texturememory ;
// highcolor stuff
INT16 color8to16 [ 256 ] ; // remap color index to highcolor rgb value
INT16 * hicolormaps ; // test a 32k colormap remaps high -> high
// Painfully simple texture id cacheing to make maps load faster. :3
static struct {
char name [ 9 ] ;
INT32 id ;
} * tidcache = NULL ;
static INT32 tidcachelen = 0 ;
//
// MAPTEXTURE_T CACHING
// When a texture is first needed, it counts the number of composite columns
// required in the texture and allocates space for a column directory and
// any new columns.
// The directory will simply point inside other patches if there is only one
// patch in a given column, but any columns with multiple patches will have
// new column_ts generated.
//
//
// R_DrawColumnInCache
// Clip and draw a column from a patch into a cached post.
//
static inline void R_DrawColumnInCache ( column_t * patch , UINT8 * cache , INT32 originy , INT32 cacheheight )
{
INT32 count , position ;
UINT8 * source ;
INT32 topdelta , prevdelta = - 1 ;
while ( patch - > topdelta ! = 0xff )
{
topdelta = patch - > topdelta ;
if ( topdelta < = prevdelta )
topdelta + = prevdelta ;
prevdelta = topdelta ;
source = ( UINT8 * ) patch + 3 ;
count = patch - > length ;
position = originy + topdelta ;
if ( position < 0 )
{
count + = position ;
position = 0 ;
}
if ( position + count > cacheheight )
count = cacheheight - position ;
if ( count > 0 )
M_Memcpy ( cache + position , source , count ) ;
patch = ( column_t * ) ( ( UINT8 * ) patch + patch - > length + 4 ) ;
}
}
//
// R_GenerateTexture
//
// Allocate space for full size texture, either single patch or 'composite'
// Build the full textures from patches.
// The texture caching system is a little more hungry of memory, but has
2019-05-26 19:37:23 -07:00
// been simplified for the sake of highcolor (lol), dynamic ligthing, & speed.
2014-03-15 09:59:03 -07:00
//
// This is not optimised, but it's supposed to be executed only once
// per level, when enough memory is available.
//
static UINT8 * R_GenerateTexture ( size_t texnum )
{
UINT8 * block ;
UINT8 * blocktex ;
texture_t * texture ;
texpatch_t * patch ;
patch_t * realpatch ;
int x , x1 , x2 , i ;
size_t blocksize ;
column_t * patchcol ;
UINT32 * colofs ;
2019-05-26 19:37:23 -07:00
UINT16 wadnum ;
lumpnum_t lumpnum ;
size_t lumplength ;
2014-03-15 09:59:03 -07:00
I_Assert ( texnum < = ( size_t ) numtextures ) ;
texture = textures [ texnum ] ;
I_Assert ( texture ! = NULL ) ;
// allocate texture column offset lookup
// single-patch textures can have holes in them and may be used on
// 2sided lines so they need to be kept in 'packed' format
// BUT this is wrong for skies and walls with over 255 pixels,
// so check if there's holes and if not strip the posts.
if ( texture - > patchcount = = 1 )
{
boolean holey = false ;
patch = texture - > patches ;
2019-05-26 19:37:23 -07:00
wadnum = patch - > wad ;
lumpnum = patch - > lump ;
lumplength = W_LumpLengthPwad ( wadnum , lumpnum ) ;
realpatch = W_CacheLumpNumPwad ( wadnum , lumpnum , PU_CACHE ) ;
if ( R_IsLumpPNG ( ( UINT8 * ) realpatch , lumplength ) )
realpatch = R_PNGToPatch ( ( UINT8 * ) realpatch , lumplength ) ;
2014-03-15 09:59:03 -07:00
// Check the patch for holes.
if ( texture - > width > SHORT ( realpatch - > width ) | | texture - > height > SHORT ( realpatch - > height ) )
holey = true ;
colofs = ( UINT32 * ) realpatch - > columnofs ;
for ( x = 0 ; x < texture - > width & & ! holey ; x + + )
{
column_t * col = ( column_t * ) ( ( UINT8 * ) realpatch + LONG ( colofs [ x ] ) ) ;
INT32 topdelta , prevdelta = - 1 , y = 0 ;
while ( col - > topdelta ! = 0xff )
{
topdelta = col - > topdelta ;
if ( topdelta < = prevdelta )
topdelta + = prevdelta ;
prevdelta = topdelta ;
if ( topdelta > y )
break ;
y = topdelta + col - > length + 1 ;
col = ( column_t * ) ( ( UINT8 * ) col + col - > length + 4 ) ;
}
if ( y < texture - > height )
holey = true ; // this texture is HOLEy! D:
}
// If the patch uses transparency, we have to save it this way.
if ( holey )
{
texture - > holes = true ;
2019-05-26 19:37:23 -07:00
blocksize = lumplength ;
2014-03-15 09:59:03 -07:00
block = Z_Calloc ( blocksize , PU_STATIC , // will change tag at end of this function
& texturecache [ texnum ] ) ;
M_Memcpy ( block , realpatch , blocksize ) ;
texturememory + = blocksize ;
// use the patch's column lookup
colofs = ( UINT32 * ) ( void * ) ( block + 8 ) ;
texturecolumnofs [ texnum ] = colofs ;
blocktex = block ;
for ( x = 0 ; x < texture - > width ; x + + )
colofs [ x ] = LONG ( LONG ( colofs [ x ] ) + 3 ) ;
goto done ;
}
// Otherwise, do multipatch format.
}
// multi-patch textures (or 'composite')
texture - > holes = false ;
blocksize = ( texture - > width * 4 ) + ( texture - > width * texture - > height ) ;
texturememory + = blocksize ;
block = Z_Malloc ( blocksize + 1 , PU_STATIC , & texturecache [ texnum ] ) ;
memset ( block , 0xF7 , blocksize + 1 ) ; // Transparency hack
// columns lookup table
colofs = ( UINT32 * ) ( void * ) block ;
texturecolumnofs [ texnum ] = colofs ;
// texture data after the lookup table
blocktex = block + ( texture - > width * 4 ) ;
// Composite the columns together.
for ( i = 0 , patch = texture - > patches ; i < texture - > patchcount ; i + + , patch + + )
{
2019-05-26 19:37:23 -07:00
wadnum = patch - > wad ;
lumpnum = patch - > lump ;
lumplength = W_LumpLengthPwad ( wadnum , lumpnum ) ;
realpatch = W_CacheLumpNumPwad ( wadnum , lumpnum , PU_CACHE ) ;
if ( R_IsLumpPNG ( ( UINT8 * ) realpatch , lumplength ) )
realpatch = R_PNGToPatch ( ( UINT8 * ) realpatch , lumplength ) ;
2014-03-15 09:59:03 -07:00
x1 = patch - > originx ;
x2 = x1 + SHORT ( realpatch - > width ) ;
if ( x1 < 0 )
x = 0 ;
else
x = x1 ;
if ( x2 > texture - > width )
x2 = texture - > width ;
for ( ; x < x2 ; x + + )
{
patchcol = ( column_t * ) ( ( UINT8 * ) realpatch + LONG ( realpatch - > columnofs [ x - x1 ] ) ) ;
// generate column ofset lookup
colofs [ x ] = LONG ( ( x * texture - > height ) + ( texture - > width * 4 ) ) ;
R_DrawColumnInCache ( patchcol , block + LONG ( colofs [ x ] ) , patch - > originy , texture - > height ) ;
}
}
done :
// Now that the texture has been built in column cache, it is purgable from zone memory.
Z_ChangeTag ( block , PU_CACHE ) ;
return blocktex ;
}
2016-12-16 13:38:53 -08:00
//
// R_GetTextureNum
//
// Returns the actual texture id that we should use.
// This can either be texnum, the current frame for texnum's anim (if animated),
// or 0 if not valid.
//
INT32 R_GetTextureNum ( INT32 texnum )
{
if ( texnum < 0 | | texnum > = numtextures )
return 0 ;
return texturetranslation [ texnum ] ;
}
2016-12-17 11:59:54 -08:00
//
// R_CheckTextureCache
//
// Use this if you need to make sure the texture is cached before R_GetColumn calls
// e.g.: midtextures and FOF walls
//
void R_CheckTextureCache ( INT32 tex )
{
if ( ! texturecache [ tex ] )
R_GenerateTexture ( tex ) ;
}
2014-03-15 09:59:03 -07:00
//
// R_GetColumn
//
UINT8 * R_GetColumn ( fixed_t tex , INT32 col )
{
UINT8 * data ;
2019-05-20 20:28:52 -07:00
INT32 width = texturewidth [ tex ] ;
2014-03-15 09:59:03 -07:00
2019-05-20 20:28:52 -07:00
if ( width & ( width - 1 ) )
col = ( UINT32 ) col % width ;
else
col & = ( width - 1 ) ;
2014-03-15 09:59:03 -07:00
2019-05-20 20:28:52 -07:00
data = texturecache [ tex ] ;
2014-03-15 09:59:03 -07:00
if ( ! data )
data = R_GenerateTexture ( tex ) ;
return data + LONG ( texturecolumnofs [ tex ] [ col ] ) ;
}
// convert flats to hicolor as they are requested
//
UINT8 * R_GetFlat ( lumpnum_t flatlumpnum )
{
return W_CacheLumpNum ( flatlumpnum , PU_CACHE ) ;
}
//
// Empty the texture cache (used for load wad at runtime)
//
void R_FlushTextureCache ( void )
{
INT32 i ;
if ( numtextures )
for ( i = 0 ; i < numtextures ; i + + )
Z_Free ( texturecache [ i ] ) ;
}
// Need these prototypes for later; defining them here instead of r_data.h so they're "private"
2018-11-23 07:58:16 -08:00
int R_CountTexturesInTEXTURESLump ( UINT16 wadNum , UINT16 lumpNum ) ;
void R_ParseTEXTURESLump ( UINT16 wadNum , UINT16 lumpNum , INT32 * index ) ;
2014-03-15 09:59:03 -07:00
//
// R_LoadTextures
// Initializes the texture list with the textures from the world map.
//
# define TX_START "TX_START"
# define TX_END "TX_END"
void R_LoadTextures ( void )
{
2019-05-20 20:28:52 -07:00
INT32 i , w ;
2014-03-15 09:59:03 -07:00
UINT16 j ;
UINT16 texstart , texend , texturesLumpPos ;
patch_t * patchlump ;
texpatch_t * patch ;
texture_t * texture ;
// Free previous memory before numtextures change.
if ( numtextures )
{
for ( i = 0 ; i < numtextures ; i + + )
{
Z_Free ( textures [ i ] ) ;
Z_Free ( texturecache [ i ] ) ;
}
Z_Free ( texturetranslation ) ;
Z_Free ( textures ) ;
2019-05-21 11:24:26 -07:00
Z_Free ( texflats ) ;
2014-03-15 09:59:03 -07:00
}
// Load patches and textures.
// Get the number of textures to check.
// NOTE: Make SURE the system does not process
// the markers.
// This system will allocate memory for all duplicate/patched textures even if it never uses them,
// but the alternative is to spend a ton of time checking and re-checking all previous entries just to skip any potentially patched textures.
for ( w = 0 , numtextures = 0 ; w < numwadfiles ; w + + )
{
2018-11-23 07:58:16 -08:00
if ( wadfiles [ w ] - > type = = RET_PK3 )
{
texstart = W_CheckNumForFolderStartPK3 ( " textures/ " , ( UINT16 ) w , 0 ) ;
texend = W_CheckNumForFolderEndPK3 ( " textures/ " , ( UINT16 ) w , texstart ) ;
}
else
{
texstart = W_CheckNumForNamePwad ( TX_START , ( UINT16 ) w , 0 ) + 1 ;
texend = W_CheckNumForNamePwad ( TX_END , ( UINT16 ) w , 0 ) ;
}
2014-03-15 09:59:03 -07:00
2018-11-23 07:58:16 -08:00
texturesLumpPos = W_CheckNumForNamePwad ( " TEXTURES " , ( UINT16 ) w , 0 ) ;
while ( texturesLumpPos ! = INT16_MAX )
2014-03-15 09:59:03 -07:00
{
2018-11-23 07:58:16 -08:00
numtextures + = R_CountTexturesInTEXTURESLump ( ( UINT16 ) w , ( UINT16 ) texturesLumpPos ) ;
texturesLumpPos = W_CheckNumForNamePwad ( " TEXTURES " , ( UINT16 ) w , texturesLumpPos + 1 ) ;
2014-03-15 09:59:03 -07:00
}
// Add all the textures between TX_START and TX_END
if ( texstart ! = INT16_MAX & & texend ! = INT16_MAX )
{
numtextures + = ( UINT32 ) ( texend - texstart ) ;
}
// If no textures found by this point, bomb out
if ( ! numtextures & & w = = ( numwadfiles - 1 ) )
{
I_Error ( " No textures detected in any WADs! \n " ) ;
}
}
// Allocate memory and initialize to 0 for all the textures we are initialising.
// There are actually 5 buffers allocated in one for convenience.
textures = Z_Calloc ( ( numtextures * sizeof ( void * ) ) * 5 , PU_STATIC , NULL ) ;
2019-05-21 11:24:26 -07:00
texflats = Z_Calloc ( ( numtextures * sizeof ( * texflats ) ) , PU_STATIC , NULL ) ;
2014-03-15 09:59:03 -07:00
// Allocate texture column offset table.
texturecolumnofs = ( void * ) ( ( UINT8 * ) textures + ( numtextures * sizeof ( void * ) ) ) ;
// Allocate texture referencing cache.
texturecache = ( void * ) ( ( UINT8 * ) textures + ( ( numtextures * sizeof ( void * ) ) * 2 ) ) ;
2019-05-20 20:28:52 -07:00
// Allocate texture width table.
texturewidth = ( void * ) ( ( UINT8 * ) textures + ( ( numtextures * sizeof ( void * ) ) * 3 ) ) ;
// Allocate texture height table.
2014-03-15 09:59:03 -07:00
textureheight = ( void * ) ( ( UINT8 * ) textures + ( ( numtextures * sizeof ( void * ) ) * 4 ) ) ;
// Create translation table for global animation.
texturetranslation = Z_Malloc ( ( numtextures + 1 ) * sizeof ( * texturetranslation ) , PU_STATIC , NULL ) ;
for ( i = 0 ; i < numtextures ; i + + )
texturetranslation [ i ] = i ;
for ( i = 0 , w = 0 ; w < numwadfiles ; w + + )
{
// Get the lump numbers for the markers in the WAD, if they exist.
2018-11-23 07:58:16 -08:00
if ( wadfiles [ w ] - > type = = RET_PK3 )
{
texstart = W_CheckNumForFolderStartPK3 ( " textures/ " , ( UINT16 ) w , 0 ) ;
texend = W_CheckNumForFolderEndPK3 ( " textures/ " , ( UINT16 ) w , texstart ) ;
texturesLumpPos = W_CheckNumForNamePwad ( " TEXTURES " , ( UINT16 ) w , 0 ) ;
while ( texturesLumpPos ! = INT16_MAX )
{
R_ParseTEXTURESLump ( w , texturesLumpPos , & i ) ;
texturesLumpPos = W_CheckNumForNamePwad ( " TEXTURES " , ( UINT16 ) w , texturesLumpPos + 1 ) ;
}
}
else
{
texstart = W_CheckNumForNamePwad ( TX_START , ( UINT16 ) w , 0 ) + 1 ;
texend = W_CheckNumForNamePwad ( TX_END , ( UINT16 ) w , 0 ) ;
texturesLumpPos = W_CheckNumForNamePwad ( " TEXTURES " , ( UINT16 ) w , 0 ) ;
if ( texturesLumpPos ! = INT16_MAX )
R_ParseTEXTURESLump ( w , texturesLumpPos , & i ) ;
}
2014-03-15 09:59:03 -07:00
if ( texstart = = INT16_MAX | | texend = = INT16_MAX )
continue ;
// Work through each lump between the markers in the WAD.
for ( j = 0 ; j < ( texend - texstart ) ; i + + , j + + )
{
2019-05-26 19:37:23 -07:00
UINT16 wadnum = ( UINT16 ) w ;
lumpnum_t lumpnum = texstart + j ;
size_t lumplength = W_LumpLengthPwad ( wadnum , lumpnum ) ;
patchlump = W_CacheLumpNumPwad ( wadnum , lumpnum , PU_CACHE ) ;
2014-03-15 09:59:03 -07:00
// Then, check the lump directly to see if it's a texture SOC,
// and if it is, load it using dehacked instead.
if ( strstr ( ( const char * ) patchlump , " TEXTURE " ) )
{
CONS_Alert ( CONS_WARNING , " %s is a Texture SOC. \n " , W_CheckNameForNumPwad ( ( UINT16 ) w , texstart + j ) ) ;
Z_Unlock ( patchlump ) ;
DEH_LoadDehackedLumpPwad ( ( UINT16 ) w , texstart + j ) ;
}
else
{
//CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height);
2017-04-19 12:00:19 -07:00
texture = textures [ i ] = Z_Calloc ( sizeof ( texture_t ) + sizeof ( texpatch_t ) , PU_STATIC , NULL ) ;
2014-03-15 09:59:03 -07:00
// Set texture properties.
2019-05-26 19:37:23 -07:00
M_Memcpy ( texture - > name , W_CheckNameForNumPwad ( wadnum , lumpnum ) , sizeof ( texture - > name ) ) ;
if ( R_IsLumpPNG ( ( UINT8 * ) patchlump , lumplength ) )
{
INT16 width , height ;
R_PNGDimensions ( ( UINT8 * ) patchlump , & width , & height , lumplength ) ;
texture - > width = width ;
texture - > height = height ;
}
else
{
texture - > width = SHORT ( patchlump - > width ) ;
texture - > height = SHORT ( patchlump - > height ) ;
}
2017-04-19 12:00:19 -07:00
texture - > patchcount = 1 ;
2014-03-15 09:59:03 -07:00
texture - > holes = false ;
// Allocate information for the texture's patches.
2017-04-19 12:00:19 -07:00
patch = & texture - > patches [ 0 ] ;
patch - > originx = patch - > originy = 0 ;
patch - > wad = ( UINT16 ) w ;
patch - > lump = texstart + j ;
2014-03-15 09:59:03 -07:00
Z_Unlock ( patchlump ) ;
2019-05-20 20:28:52 -07:00
texturewidth [ i ] = texture - > width ;
2014-03-15 09:59:03 -07:00
textureheight [ i ] = texture - > height < < FRACBITS ;
}
}
}
}
static texpatch_t * R_ParsePatch ( boolean actuallyLoadPatch )
{
char * texturesToken ;
2016-05-21 21:44:12 -07:00
size_t texturesTokenLength ;
2014-03-15 09:59:03 -07:00
char * endPos ;
char * patchName = NULL ;
INT16 patchXPos ;
INT16 patchYPos ;
texpatch_t * resultPatch = NULL ;
lumpnum_t patchLumpNum ;
// Patch identifier
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where patch name should be " ) ;
}
texturesTokenLength = strlen ( texturesToken ) ;
if ( texturesTokenLength > 8 )
{
I_Error ( " Error parsing TEXTURES lump: Patch name \" %s \" exceeds 8 characters " , texturesToken ) ;
}
else
{
if ( patchName ! = NULL )
{
Z_Free ( patchName ) ;
}
patchName = ( char * ) Z_Malloc ( ( texturesTokenLength + 1 ) * sizeof ( char ) , PU_STATIC , NULL ) ;
M_Memcpy ( patchName , texturesToken , texturesTokenLength * sizeof ( char ) ) ;
patchName [ texturesTokenLength ] = ' \0 ' ;
}
// Comma 1
Z_Free ( texturesToken ) ;
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where comma after \" %s \" 's patch name should be " , patchName ) ;
}
if ( strcmp ( texturesToken , " , " ) ! = 0 )
{
I_Error ( " Error parsing TEXTURES lump: Expected \" , \" after %s's patch name, got \" %s \" " , patchName , texturesToken ) ;
}
// XPos
Z_Free ( texturesToken ) ;
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where patch \" %s \" 's x coordinate should be " , patchName ) ;
}
endPos = NULL ;
# ifndef AVOID_ERRNO
errno = 0 ;
# endif
patchXPos = strtol ( texturesToken , & endPos , 10 ) ;
( void ) patchXPos ; //unused for now
if ( endPos = = texturesToken // Empty string
| | * endPos ! = ' \0 ' // Not end of string
# ifndef AVOID_ERRNO
| | errno = = ERANGE // Number out-of-range
# endif
)
{
I_Error ( " Error parsing TEXTURES lump: Expected an integer for patch \" %s \" 's x coordinate, got \" %s \" " , patchName , texturesToken ) ;
}
// Comma 2
Z_Free ( texturesToken ) ;
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where comma after patch \" %s \" 's x coordinate should be " , patchName ) ;
}
if ( strcmp ( texturesToken , " , " ) ! = 0 )
{
I_Error ( " Error parsing TEXTURES lump: Expected \" , \" after patch \" %s \" 's x coordinate, got \" %s \" " , patchName , texturesToken ) ;
}
// YPos
Z_Free ( texturesToken ) ;
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where patch \" %s \" 's y coordinate should be " , patchName ) ;
}
endPos = NULL ;
# ifndef AVOID_ERRNO
errno = 0 ;
# endif
patchYPos = strtol ( texturesToken , & endPos , 10 ) ;
( void ) patchYPos ; //unused for now
if ( endPos = = texturesToken // Empty string
| | * endPos ! = ' \0 ' // Not end of string
# ifndef AVOID_ERRNO
| | errno = = ERANGE // Number out-of-range
# endif
)
{
I_Error ( " Error parsing TEXTURES lump: Expected an integer for patch \" %s \" 's y coordinate, got \" %s \" " , patchName , texturesToken ) ;
}
Z_Free ( texturesToken ) ;
if ( actuallyLoadPatch = = true )
{
// Check lump exists
patchLumpNum = W_GetNumForName ( patchName ) ;
// If so, allocate memory for texpatch_t and fill 'er up
resultPatch = ( texpatch_t * ) Z_Malloc ( sizeof ( texpatch_t ) , PU_STATIC , NULL ) ;
resultPatch - > originx = patchXPos ;
resultPatch - > originy = patchYPos ;
resultPatch - > lump = patchLumpNum & 65535 ;
resultPatch - > wad = patchLumpNum > > 16 ;
// Clean up a little after ourselves
Z_Free ( patchName ) ;
// Then return it
return resultPatch ;
}
else
{
Z_Free ( patchName ) ;
return NULL ;
}
}
static texture_t * R_ParseTexture ( boolean actuallyLoadTexture )
{
char * texturesToken ;
2016-05-21 21:44:12 -07:00
size_t texturesTokenLength ;
2014-03-15 09:59:03 -07:00
char * endPos ;
INT32 newTextureWidth ;
INT32 newTextureHeight ;
texture_t * resultTexture = NULL ;
texpatch_t * newPatch ;
2016-05-08 20:34:43 -07:00
char newTextureName [ 9 ] ; // no longer dynamically allocated
2014-03-15 09:59:03 -07:00
// Texture name
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where texture name should be " ) ;
}
texturesTokenLength = strlen ( texturesToken ) ;
if ( texturesTokenLength > 8 )
{
I_Error ( " Error parsing TEXTURES lump: Texture name \" %s \" exceeds 8 characters " , texturesToken ) ;
}
else
{
2016-05-08 20:34:43 -07:00
memset ( & newTextureName , 0 , 9 ) ;
M_Memcpy ( newTextureName , texturesToken , texturesTokenLength ) ;
// ^^ we've confirmed that the token is <= 8 characters so it will never overflow a 9 byte char buffer
strupr ( newTextureName ) ; // Just do this now so we don't have to worry about it
2014-03-15 09:59:03 -07:00
}
Z_Free ( texturesToken ) ;
// Comma 1
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where comma after texture \" %s \" 's name should be " , newTextureName ) ;
}
else if ( strcmp ( texturesToken , " , " ) ! = 0 )
{
I_Error ( " Error parsing TEXTURES lump: Expected \" , \" after texture \" %s \" 's name, got \" %s \" " , newTextureName , texturesToken ) ;
}
Z_Free ( texturesToken ) ;
// Width
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where texture \" %s \" 's width should be " , newTextureName ) ;
}
endPos = NULL ;
# ifndef AVOID_ERRNO
errno = 0 ;
# endif
newTextureWidth = strtol ( texturesToken , & endPos , 10 ) ;
if ( endPos = = texturesToken // Empty string
| | * endPos ! = ' \0 ' // Not end of string
# ifndef AVOID_ERRNO
| | errno = = ERANGE // Number out-of-range
# endif
| | newTextureWidth < 0 ) // Number is not positive
{
I_Error ( " Error parsing TEXTURES lump: Expected a positive integer for texture \" %s \" 's width, got \" %s \" " , newTextureName , texturesToken ) ;
}
Z_Free ( texturesToken ) ;
// Comma 2
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where comma after texture \" %s \" 's width should be " , newTextureName ) ;
}
if ( strcmp ( texturesToken , " , " ) ! = 0 )
{
I_Error ( " Error parsing TEXTURES lump: Expected \" , \" after texture \" %s \" 's width, got \" %s \" " , newTextureName , texturesToken ) ;
}
Z_Free ( texturesToken ) ;
// Height
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where texture \" %s \" 's height should be " , newTextureName ) ;
}
endPos = NULL ;
# ifndef AVOID_ERRNO
errno = 0 ;
# endif
newTextureHeight = strtol ( texturesToken , & endPos , 10 ) ;
if ( endPos = = texturesToken // Empty string
| | * endPos ! = ' \0 ' // Not end of string
# ifndef AVOID_ERRNO
| | errno = = ERANGE // Number out-of-range
# endif
| | newTextureHeight < 0 ) // Number is not positive
{
I_Error ( " Error parsing TEXTURES lump: Expected a positive integer for texture \" %s \" 's height, got \" %s \" " , newTextureName , texturesToken ) ;
}
Z_Free ( texturesToken ) ;
// Left Curly Brace
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where open curly brace for texture \" %s \" should be " , newTextureName ) ;
}
if ( strcmp ( texturesToken , " { " ) = = 0 )
{
if ( actuallyLoadTexture )
{
// Allocate memory for a zero-patch texture. Obviously, we'll be adding patches momentarily.
resultTexture = ( texture_t * ) Z_Calloc ( sizeof ( texture_t ) , PU_STATIC , NULL ) ;
M_Memcpy ( resultTexture - > name , newTextureName , 8 ) ;
resultTexture - > width = newTextureWidth ;
resultTexture - > height = newTextureHeight ;
}
Z_Free ( texturesToken ) ;
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where patch definition for texture \" %s \" should be " , newTextureName ) ;
}
while ( strcmp ( texturesToken , " } " ) ! = 0 )
{
if ( stricmp ( texturesToken , " PATCH " ) = = 0 )
{
Z_Free ( texturesToken ) ;
if ( resultTexture )
{
// Get that new patch
newPatch = R_ParsePatch ( true ) ;
// Make room for the new patch
resultTexture = Z_Realloc ( resultTexture , sizeof ( texture_t ) + ( resultTexture - > patchcount + 1 ) * sizeof ( texpatch_t ) , PU_STATIC , NULL ) ;
// Populate the uninitialized values in the new patch entry of our array
M_Memcpy ( & resultTexture - > patches [ resultTexture - > patchcount ] , newPatch , sizeof ( texpatch_t ) ) ;
// Account for the new number of patches in the texture
resultTexture - > patchcount + + ;
// Then free up the memory assigned to R_ParsePatch, as it's unneeded now
Z_Free ( newPatch ) ;
}
else
{
R_ParsePatch ( false ) ;
}
}
else
{
I_Error ( " Error parsing TEXTURES lump: Expected \" PATCH \" in texture \" %s \" , got \" %s \" " , newTextureName , texturesToken ) ;
}
texturesToken = M_GetToken ( NULL ) ;
if ( texturesToken = = NULL )
{
I_Error ( " Error parsing TEXTURES lump: Unexpected end of file where patch declaration or right curly brace for texture \" %s \" should be " , newTextureName ) ;
}
}
if ( resultTexture & & resultTexture - > patchcount = = 0 )
{
I_Error ( " Error parsing TEXTURES lump: Texture \" %s \" must have at least one patch " , newTextureName ) ;
}
}
else
{
I_Error ( " Error parsing TEXTURES lump: Expected \" { \" for texture \" %s \" , got \" %s \" " , newTextureName , texturesToken ) ;
}
Z_Free ( texturesToken ) ;
if ( actuallyLoadTexture ) return resultTexture ;
else return NULL ;
}
// Parses the TEXTURES lump... but just to count the number of textures.
2018-11-23 07:58:16 -08:00
int R_CountTexturesInTEXTURESLump ( UINT16 wadNum , UINT16 lumpNum )
2014-03-15 09:59:03 -07:00
{
char * texturesLump ;
size_t texturesLumpLength ;
char * texturesText ;
UINT32 numTexturesInLump = 0 ;
char * texturesToken ;
// Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll
// need to make a space of memory where I can ensure that it will terminate
// correctly. Start by loading the relevant data from the WAD.
2018-11-23 07:58:16 -08:00
texturesLump = ( char * ) W_CacheLumpNumPwad ( wadNum , lumpNum , PU_STATIC ) ;
2014-03-15 09:59:03 -07:00
// If that didn't exist, we have nothing to do here.
if ( texturesLump = = NULL ) return 0 ;
// If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly.
2018-11-23 07:58:16 -08:00
texturesLumpLength = W_LumpLengthPwad ( wadNum , lumpNum ) ;
2014-03-15 09:59:03 -07:00
texturesText = ( char * ) Z_Malloc ( ( texturesLumpLength + 1 ) * sizeof ( char ) , PU_STATIC , NULL ) ;
// Now move the contents of the lump into this new location.
memmove ( texturesText , texturesLump , texturesLumpLength ) ;
// Make damn well sure the last character in our new memory location is \0.
texturesText [ texturesLumpLength ] = ' \0 ' ;
// Finally, free up the memory from the first data load, because we really
// don't need it.
Z_Free ( texturesLump ) ;
texturesToken = M_GetToken ( texturesText ) ;
while ( texturesToken ! = NULL )
{
2019-05-26 03:02:43 -07:00
if ( stricmp ( texturesToken , " WALLTEXTURE " ) = = 0 | | stricmp ( texturesToken , " TEXTURE " ) = = 0 )
2014-03-15 09:59:03 -07:00
{
numTexturesInLump + + ;
Z_Free ( texturesToken ) ;
R_ParseTexture ( false ) ;
}
else
{
2019-05-26 03:02:43 -07:00
I_Error ( " Error parsing TEXTURES lump: Expected \" WALLTEXTURE \" or \" TEXTURE \" , got \" %s \" " , texturesToken ) ;
2014-03-15 09:59:03 -07:00
}
texturesToken = M_GetToken ( NULL ) ;
}
Z_Free ( texturesToken ) ;
Z_Free ( ( void * ) texturesText ) ;
return numTexturesInLump ;
}
// Parses the TEXTURES lump... for real, this time.
2018-11-23 07:58:16 -08:00
void R_ParseTEXTURESLump ( UINT16 wadNum , UINT16 lumpNum , INT32 * texindex )
2014-03-15 09:59:03 -07:00
{
char * texturesLump ;
size_t texturesLumpLength ;
char * texturesText ;
char * texturesToken ;
texture_t * newTexture ;
I_Assert ( texindex ! = NULL ) ;
// Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll
// need to make a space of memory where I can ensure that it will terminate
// correctly. Start by loading the relevant data from the WAD.
2018-11-23 07:58:16 -08:00
texturesLump = ( char * ) W_CacheLumpNumPwad ( wadNum , lumpNum , PU_STATIC ) ;
2014-03-15 09:59:03 -07:00
// If that didn't exist, we have nothing to do here.
if ( texturesLump = = NULL ) return ;
// If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly.
2018-11-23 07:58:16 -08:00
texturesLumpLength = W_LumpLengthPwad ( wadNum , lumpNum ) ;
2014-03-15 09:59:03 -07:00
texturesText = ( char * ) Z_Malloc ( ( texturesLumpLength + 1 ) * sizeof ( char ) , PU_STATIC , NULL ) ;
// Now move the contents of the lump into this new location.
memmove ( texturesText , texturesLump , texturesLumpLength ) ;
// Make damn well sure the last character in our new memory location is \0.
texturesText [ texturesLumpLength ] = ' \0 ' ;
// Finally, free up the memory from the first data load, because we really
// don't need it.
Z_Free ( texturesLump ) ;
texturesToken = M_GetToken ( texturesText ) ;
while ( texturesToken ! = NULL )
{
2019-05-26 03:02:43 -07:00
if ( stricmp ( texturesToken , " WALLTEXTURE " ) = = 0 | | stricmp ( texturesToken , " TEXTURE " ) = = 0 )
2014-03-15 09:59:03 -07:00
{
Z_Free ( texturesToken ) ;
// Get the new texture
newTexture = R_ParseTexture ( true ) ;
// Store the new texture
textures [ * texindex ] = newTexture ;
2019-05-20 20:28:52 -07:00
texturewidth [ * texindex ] = newTexture - > width ;
2014-03-15 09:59:03 -07:00
textureheight [ * texindex ] = newTexture - > height < < FRACBITS ;
// Increment i back in R_LoadTextures()
( * texindex ) + + ;
}
else
{
2019-05-26 03:02:43 -07:00
I_Error ( " Error parsing TEXTURES lump: Expected \" WALLTEXTURE \" or \" TEXTURE \" , got \" %s \" " , texturesToken ) ;
2014-03-15 09:59:03 -07:00
}
texturesToken = M_GetToken ( NULL ) ;
}
Z_Free ( texturesToken ) ;
Z_Free ( ( void * ) texturesText ) ;
}
static inline lumpnum_t R_CheckNumForNameList ( const char * name , lumplist_t * list , size_t listsize )
{
size_t i ;
UINT16 lump ;
for ( i = listsize - 1 ; i < INT16_MAX ; i - - )
{
lump = W_CheckNumForNamePwad ( name , list [ i ] . wadfile , list [ i ] . firstlump ) ;
if ( lump = = INT16_MAX | | lump > ( list [ i ] . firstlump + list [ i ] . numlumps ) )
continue ;
else
return ( list [ i ] . wadfile < < 16 ) + lump ;
}
return LUMPERROR ;
}
static lumplist_t * colormaplumps = NULL ; ///\todo free leak
static size_t numcolormaplumps = 0 ;
static void R_InitExtraColormaps ( void )
{
lumpnum_t startnum , endnum ;
UINT16 cfile , clump ;
static size_t maxcolormaplumps = 16 ;
for ( cfile = clump = 0 ; cfile < numwadfiles ; cfile + + , clump = 0 )
{
startnum = W_CheckNumForNamePwad ( " C_START " , cfile , clump ) ;
2017-05-15 06:29:31 -07:00
if ( startnum = = INT16_MAX )
2014-03-15 09:59:03 -07:00
continue ;
endnum = W_CheckNumForNamePwad ( " C_END " , cfile , clump ) ;
2017-05-15 06:29:31 -07:00
if ( endnum = = INT16_MAX )
2014-03-15 09:59:03 -07:00
I_Error ( " R_InitExtraColormaps: C_START without C_END \n " ) ;
2017-05-15 06:38:55 -07:00
// This shouldn't be possible when you use the Pwad function, silly
//if (WADFILENUM(startnum) != WADFILENUM(endnum))
//I_Error("R_InitExtraColormaps: C_START and C_END in different wad files!\n");
2014-03-15 09:59:03 -07:00
if ( numcolormaplumps > = maxcolormaplumps )
maxcolormaplumps * = 2 ;
colormaplumps = Z_Realloc ( colormaplumps ,
sizeof ( * colormaplumps ) * maxcolormaplumps , PU_STATIC , NULL ) ;
2017-05-15 06:38:55 -07:00
colormaplumps [ numcolormaplumps ] . wadfile = cfile ;
colormaplumps [ numcolormaplumps ] . firstlump = startnum + 1 ;
2014-03-15 09:59:03 -07:00
colormaplumps [ numcolormaplumps ] . numlumps = endnum - ( startnum + 1 ) ;
numcolormaplumps + + ;
}
CONS_Printf ( M_GetText ( " Number of Extra Colormaps: %s \n " ) , sizeu1 ( numcolormaplumps ) ) ;
}
2018-11-23 07:58:16 -08:00
// Search for flat name.
2014-03-15 09:59:03 -07:00
lumpnum_t R_GetFlatNumForName ( const char * name )
{
2018-11-23 07:58:16 -08:00
INT32 i ;
lumpnum_t lump ;
lumpnum_t start ;
lumpnum_t end ;
// Scan wad files backwards so patched flats take preference.
for ( i = numwadfiles - 1 ; i > = 0 ; i - - )
{
switch ( wadfiles [ i ] - > type )
{
case RET_WAD :
if ( ( start = W_CheckNumForNamePwad ( " F_START " , ( UINT16 ) i , 0 ) ) = = INT16_MAX )
{
if ( ( start = W_CheckNumForNamePwad ( " FF_START " , ( UINT16 ) i , 0 ) ) = = INT16_MAX )
continue ;
else if ( ( end = W_CheckNumForNamePwad ( " FF_END " , ( UINT16 ) i , start ) ) = = INT16_MAX )
continue ;
}
else
if ( ( end = W_CheckNumForNamePwad ( " F_END " , ( UINT16 ) i , start ) ) = = INT16_MAX )
continue ;
break ;
case RET_PK3 :
if ( ( start = W_CheckNumForFolderStartPK3 ( " Flats/ " , i , 0 ) ) = = INT16_MAX )
continue ;
if ( ( end = W_CheckNumForFolderEndPK3 ( " Flats/ " , i , start ) ) = = INT16_MAX )
continue ;
break ;
default :
continue ;
}
// Now find lump with specified name in that range.
lump = W_CheckNumForNamePwad ( name , ( UINT16 ) i , start ) ;
if ( lump < end )
{
lump + = ( i < < 16 ) ; // found it, in our constraints
break ;
}
lump = LUMPERROR ;
}
2019-05-21 11:24:26 -07:00
// Detect textures
2019-05-20 20:28:52 -07:00
if ( lump = = LUMPERROR )
{
2019-05-21 11:24:26 -07:00
// Scan wad files backwards so patched textures take preference.
2019-05-20 20:28:52 -07:00
for ( i = numwadfiles - 1 ; i > = 0 ; i - - )
{
switch ( wadfiles [ i ] - > type )
{
case RET_WAD :
if ( ( start = W_CheckNumForNamePwad ( " TX_START " , ( UINT16 ) i , 0 ) ) = = INT16_MAX )
continue ;
if ( ( end = W_CheckNumForNamePwad ( " TX_END " , ( UINT16 ) i , start ) ) = = INT16_MAX )
continue ;
break ;
case RET_PK3 :
if ( ( start = W_CheckNumForFolderStartPK3 ( " Textures/ " , i , 0 ) ) = = INT16_MAX )
continue ;
if ( ( end = W_CheckNumForFolderEndPK3 ( " Textures/ " , i , start ) ) = = INT16_MAX )
continue ;
break ;
default :
continue ;
}
// Now find lump with specified name in that range.
lump = W_CheckNumForNamePwad ( name , ( UINT16 ) i , start ) ;
if ( lump < end )
{
lump + = ( i < < 16 ) ; // found it, in our constraints
break ;
}
lump = LUMPERROR ;
}
}
2014-03-15 09:59:03 -07:00
if ( lump = = LUMPERROR )
{
if ( strcmp ( name , SKYFLATNAME ) )
CONS_Debug ( DBG_SETUP , " R_GetFlatNumForName: Could not find flat %.8s \n " , name ) ;
lump = W_CheckNumForName ( " REDFLR " ) ;
}
return lump ;
}
//
// R_InitSpriteLumps
// Finds the width and hoffset of all sprites in the wad, so the sprite does not need to be
// cached completely, just for having the header info ready during rendering.
//
//
// allocate sprite lookup tables
//
static void R_InitSpriteLumps ( void )
{
numspritelumps = 0 ;
max_spritelumps = 8192 ;
Z_Malloc ( max_spritelumps * sizeof ( * spritecachedinfo ) , PU_STATIC , & spritecachedinfo ) ;
}
//
// R_InitColormaps
//
static void R_InitColormaps ( void )
{
lumpnum_t lump ;
// Load in the light tables
lump = W_GetNumForName ( " COLORMAP " ) ;
colormaps = Z_MallocAlign ( W_LumpLength ( lump ) , PU_STATIC , NULL , 8 ) ;
W_ReadLump ( lump , colormaps ) ;
// Init Boom colormaps.
R_ClearColormaps ( ) ;
R_InitExtraColormaps ( ) ;
}
void R_ReInitColormaps ( UINT16 num )
{
char colormap [ 9 ] = " COLORMAP " ;
lumpnum_t lump ;
2014-08-03 20:49:33 -07:00
if ( num > 0 & & num < = 10000 )
2014-03-15 09:59:03 -07:00
snprintf ( colormap , 8 , " CLM%04u " , num - 1 ) ;
// Load in the light tables, now 64k aligned for smokie...
lump = W_GetNumForName ( colormap ) ;
if ( lump = = LUMPERROR )
lump = W_GetNumForName ( " COLORMAP " ) ;
W_ReadLump ( lump , colormaps ) ;
// Init Boom colormaps.
R_ClearColormaps ( ) ;
}
static lumpnum_t foundcolormaps [ MAXCOLORMAPS ] ;
//
// R_ClearColormaps
//
// Clears out extra colormaps between levels.
//
void R_ClearColormaps ( void )
{
size_t i ;
num_extra_colormaps = 0 ;
for ( i = 0 ; i < MAXCOLORMAPS ; i + + )
foundcolormaps [ i ] = LUMPERROR ;
memset ( extra_colormaps , 0 , sizeof ( extra_colormaps ) ) ;
}
INT32 R_ColormapNumForName ( char * name )
{
lumpnum_t lump , i ;
if ( num_extra_colormaps = = MAXCOLORMAPS )
I_Error ( " R_ColormapNumForName: Too many colormaps! the limit is %d \n " , MAXCOLORMAPS ) ;
lump = R_CheckNumForNameList ( name , colormaplumps , numcolormaplumps ) ;
if ( lump = = LUMPERROR )
I_Error ( " R_ColormapNumForName: Cannot find colormap lump %.8s \n " , name ) ;
for ( i = 0 ; i < num_extra_colormaps ; i + + )
if ( lump = = foundcolormaps [ i ] )
return i ;
foundcolormaps [ num_extra_colormaps ] = lump ;
// aligned on 8 bit for asm code
extra_colormaps [ num_extra_colormaps ] . colormap = Z_MallocAlign ( W_LumpLength ( lump ) , PU_LEVEL , NULL , 16 ) ;
W_ReadLump ( lump , extra_colormaps [ num_extra_colormaps ] . colormap ) ;
// We set all params of the colormap to normal because there
// is no real way to tell how GL should handle a colormap lump anyway..
extra_colormaps [ num_extra_colormaps ] . maskcolor = 0xffff ;
extra_colormaps [ num_extra_colormaps ] . fadecolor = 0x0 ;
extra_colormaps [ num_extra_colormaps ] . maskamt = 0x0 ;
extra_colormaps [ num_extra_colormaps ] . fadestart = 0 ;
2018-08-25 08:46:45 -07:00
extra_colormaps [ num_extra_colormaps ] . fadeend = 31 ;
2014-03-15 09:59:03 -07:00
extra_colormaps [ num_extra_colormaps ] . fog = 0 ;
num_extra_colormaps + + ;
return ( INT32 ) num_extra_colormaps - 1 ;
}
//
// R_CreateColormap
//
// This is a more GL friendly way of doing colormaps: Specify colormap
// data in a special linedef's texture areas and use that to generate
// custom colormaps at runtime. NOTE: For GL mode, we only need to color
// data and not the colormap data.
//
static double deltas [ 256 ] [ 3 ] , map [ 256 ] [ 3 ] ;
static int RoundUp ( double number ) ;
INT32 R_CreateColormap ( char * p1 , char * p2 , char * p3 )
{
double cmaskr , cmaskg , cmaskb , cdestr , cdestg , cdestb ;
2018-09-09 14:48:09 -07:00
double maskamt = 0 , othermask = 0 ;
2014-03-15 09:59:03 -07:00
int mask , fog = 0 ;
size_t mapnum = num_extra_colormaps ;
size_t i ;
UINT32 cr , cg , cb , maskcolor , fadecolor ;
2018-08-25 08:46:45 -07:00
UINT32 fadestart = 0 , fadeend = 31 , fadedist = 31 ;
2014-03-15 09:59:03 -07:00
# define HEX2INT(x) (UINT32)(x >= '0' && x <= '9' ? x - '0' : x >= 'a' && x <= 'f' ? x - 'a' + 10 : x >= 'A' && x <= 'F' ? x - 'A' + 10 : 0)
if ( p1 [ 0 ] = = ' # ' )
{
cr = ( ( HEX2INT ( p1 [ 1 ] ) * 16 ) + HEX2INT ( p1 [ 2 ] ) ) ;
cmaskr = cr ;
cg = ( ( HEX2INT ( p1 [ 3 ] ) * 16 ) + HEX2INT ( p1 [ 4 ] ) ) ;
cmaskg = cg ;
cb = ( ( HEX2INT ( p1 [ 5 ] ) * 16 ) + HEX2INT ( p1 [ 6 ] ) ) ;
cmaskb = cb ;
// Create a rough approximation of the color (a 16 bit color)
maskcolor = ( ( cb ) > > 3 ) + ( ( ( cg ) > > 2 ) < < 5 ) + ( ( ( cr ) > > 3 ) < < 11 ) ;
if ( p1 [ 7 ] > = ' a ' & & p1 [ 7 ] < = ' z ' )
mask = ( p1 [ 7 ] - ' a ' ) ;
else if ( p1 [ 7 ] > = ' A ' & & p1 [ 7 ] < = ' Z ' )
mask = ( p1 [ 7 ] - ' A ' ) ;
else
mask = 24 ;
maskamt = ( double ) ( mask / 24.0 l ) ;
othermask = 1 - maskamt ;
maskamt / = 0xff ;
cmaskr * = maskamt ;
cmaskg * = maskamt ;
cmaskb * = maskamt ;
}
else
{
cmaskr = cmaskg = cmaskb = 0xff ;
maskamt = 0 ;
maskcolor = ( ( 0xff ) > > 3 ) + ( ( ( 0xff ) > > 2 ) < < 5 ) + ( ( ( 0xff ) > > 3 ) < < 11 ) ;
}
# define NUMFROMCHAR(c) (c >= '0' && c <= '9' ? c - '0' : 0)
if ( p2 [ 0 ] = = ' # ' )
{
// Get parameters like fadestart, fadeend, and the fogflag
fadestart = NUMFROMCHAR ( p2 [ 3 ] ) + ( NUMFROMCHAR ( p2 [ 2 ] ) * 10 ) ;
fadeend = NUMFROMCHAR ( p2 [ 5 ] ) + ( NUMFROMCHAR ( p2 [ 4 ] ) * 10 ) ;
2018-08-25 08:46:45 -07:00
if ( fadestart > 30 )
2014-03-15 09:59:03 -07:00
fadestart = 0 ;
2018-08-25 08:46:45 -07:00
if ( fadeend > 31 | | fadeend < 1 )
fadeend = 31 ;
2014-03-15 09:59:03 -07:00
fadedist = fadeend - fadestart ;
2018-08-25 08:46:45 -07:00
fog = NUMFROMCHAR ( p2 [ 1 ] ) ;
2014-03-15 09:59:03 -07:00
}
2018-09-09 14:48:09 -07:00
# undef NUMFROMCHAR
2014-03-15 09:59:03 -07:00
if ( p3 [ 0 ] = = ' # ' )
{
cdestr = cr = ( ( HEX2INT ( p3 [ 1 ] ) * 16 ) + HEX2INT ( p3 [ 2 ] ) ) ;
cdestg = cg = ( ( HEX2INT ( p3 [ 3 ] ) * 16 ) + HEX2INT ( p3 [ 4 ] ) ) ;
cdestb = cb = ( ( HEX2INT ( p3 [ 5 ] ) * 16 ) + HEX2INT ( p3 [ 6 ] ) ) ;
fadecolor = ( ( ( cb ) > > 3 ) + ( ( ( cg ) > > 2 ) < < 5 ) + ( ( ( cr ) > > 3 ) < < 11 ) ) ;
}
else
cdestr = cdestg = cdestb = fadecolor = 0 ;
# undef HEX2INT
for ( i = 0 ; i < num_extra_colormaps ; i + + )
{
if ( foundcolormaps [ i ] ! = LUMPERROR )
continue ;
if ( maskcolor = = extra_colormaps [ i ] . maskcolor
& & fadecolor = = extra_colormaps [ i ] . fadecolor
2018-12-24 17:22:10 -08:00
& & fabs ( maskamt - extra_colormaps [ i ] . maskamt ) < DBL_EPSILON
2014-03-15 09:59:03 -07:00
& & fadestart = = extra_colormaps [ i ] . fadestart
& & fadeend = = extra_colormaps [ i ] . fadeend
& & fog = = extra_colormaps [ i ] . fog )
{
return ( INT32 ) i ;
}
}
if ( num_extra_colormaps = = MAXCOLORMAPS )
I_Error ( " R_CreateColormap: Too many colormaps! the limit is %d \n " , MAXCOLORMAPS ) ;
num_extra_colormaps + + ;
2018-09-09 14:48:09 -07:00
foundcolormaps [ mapnum ] = LUMPERROR ;
// aligned on 8 bit for asm code
extra_colormaps [ mapnum ] . colormap = NULL ;
extra_colormaps [ mapnum ] . maskcolor = ( UINT16 ) maskcolor ;
extra_colormaps [ mapnum ] . fadecolor = ( UINT16 ) fadecolor ;
extra_colormaps [ mapnum ] . maskamt = maskamt ;
extra_colormaps [ mapnum ] . fadestart = ( UINT16 ) fadestart ;
extra_colormaps [ mapnum ] . fadeend = ( UINT16 ) fadeend ;
extra_colormaps [ mapnum ] . fog = fog ;
// This code creates the colormap array used by software renderer
2014-03-15 09:59:03 -07:00
if ( rendermode = = render_soft )
{
2018-09-09 14:48:09 -07:00
double r , g , b , cbrightness ;
int p ;
char * colormap_p ;
// Initialise the map and delta arrays
// map[i] stores an RGB color (as double) for index i,
// which is then converted to SRB2's palette later
// deltas[i] stores a corresponding fade delta between the RGB color and the final fade color;
// map[i]'s values are decremented by after each use
2014-03-15 09:59:03 -07:00
for ( i = 0 ; i < 256 ; i + + )
{
r = pLocalPalette [ i ] . s . red ;
g = pLocalPalette [ i ] . s . green ;
b = pLocalPalette [ i ] . s . blue ;
cbrightness = sqrt ( ( r * r ) + ( g * g ) + ( b * b ) ) ;
map [ i ] [ 0 ] = ( cbrightness * cmaskr ) + ( r * othermask ) ;
if ( map [ i ] [ 0 ] > 255.0 l )
map [ i ] [ 0 ] = 255.0 l ;
deltas [ i ] [ 0 ] = ( map [ i ] [ 0 ] - cdestr ) / ( double ) fadedist ;
map [ i ] [ 1 ] = ( cbrightness * cmaskg ) + ( g * othermask ) ;
if ( map [ i ] [ 1 ] > 255.0 l )
map [ i ] [ 1 ] = 255.0 l ;
deltas [ i ] [ 1 ] = ( map [ i ] [ 1 ] - cdestg ) / ( double ) fadedist ;
map [ i ] [ 2 ] = ( cbrightness * cmaskb ) + ( b * othermask ) ;
if ( map [ i ] [ 2 ] > 255.0 l )
map [ i ] [ 2 ] = 255.0 l ;
deltas [ i ] [ 2 ] = ( map [ i ] [ 2 ] - cdestb ) / ( double ) fadedist ;
}
2018-09-09 14:48:09 -07:00
// Now allocate memory for the actual colormap array itself!
colormap_p = Z_MallocAlign ( ( 256 * 34 ) + 10 , PU_LEVEL , NULL , 8 ) ;
extra_colormaps [ mapnum ] . colormap = ( UINT8 * ) colormap_p ;
2014-03-15 09:59:03 -07:00
2018-09-09 14:48:09 -07:00
// Calculate the palette index for each palette index, for each light level
// (as well as the two unused colormap lines we inherited from Doom)
for ( p = 0 ; p < 34 ; p + + )
{
for ( i = 0 ; i < 256 ; i + + )
{
* colormap_p = NearestColor ( ( UINT8 ) RoundUp ( map [ i ] [ 0 ] ) ,
( UINT8 ) RoundUp ( map [ i ] [ 1 ] ) ,
( UINT8 ) RoundUp ( map [ i ] [ 2 ] ) ) ;
colormap_p + + ;
if ( ( UINT32 ) p < fadestart )
continue ;
# define ABS2(x) ((x) < 0 ? -(x) : (x))
if ( ABS2 ( map [ i ] [ 0 ] - cdestr ) > ABS2 ( deltas [ i ] [ 0 ] ) )
map [ i ] [ 0 ] - = deltas [ i ] [ 0 ] ;
else
map [ i ] [ 0 ] = cdestr ;
if ( ABS2 ( map [ i ] [ 1 ] - cdestg ) > ABS2 ( deltas [ i ] [ 1 ] ) )
map [ i ] [ 1 ] - = deltas [ i ] [ 1 ] ;
else
map [ i ] [ 1 ] = cdestg ;
if ( ABS2 ( map [ i ] [ 2 ] - cdestb ) > ABS2 ( deltas [ i ] [ 1 ] ) )
map [ i ] [ 2 ] - = deltas [ i ] [ 2 ] ;
else
map [ i ] [ 2 ] = cdestb ;
# undef ABS2
}
}
}
2014-03-15 09:59:03 -07:00
return ( INT32 ) mapnum ;
}
// Thanks to quake2 source!
// utils3/qdata/images.c
2019-05-26 19:37:23 -07:00
UINT8 NearestColor ( UINT8 r , UINT8 g , UINT8 b )
2014-03-15 09:59:03 -07:00
{
int dr , dg , db ;
int distortion , bestdistortion = 256 * 256 * 4 , bestcolor = 0 , i ;
for ( i = 0 ; i < 256 ; i + + )
{
dr = r - pLocalPalette [ i ] . s . red ;
dg = g - pLocalPalette [ i ] . s . green ;
db = b - pLocalPalette [ i ] . s . blue ;
distortion = dr * dr + dg * dg + db * db ;
if ( distortion < bestdistortion )
{
if ( ! distortion )
return ( UINT8 ) i ;
bestdistortion = distortion ;
bestcolor = i ;
}
}
return ( UINT8 ) bestcolor ;
}
// Rounds off floating numbers and checks for 0 - 255 bounds
static int RoundUp ( double number )
{
if ( number > 255.0 l )
return 255 ;
if ( number < 0.0 l )
return 0 ;
if ( ( int ) number < = ( int ) ( number - 0.5f ) )
return ( int ) number + 1 ;
return ( int ) number ;
}
const char * R_ColormapNameForNum ( INT32 num )
{
if ( num = = - 1 )
return " NONE " ;
if ( num < 0 | | num > MAXCOLORMAPS )
I_Error ( " R_ColormapNameForNum: num %d is invalid! \n " , num ) ;
if ( foundcolormaps [ num ] = = LUMPERROR )
return " INLEVEL " ;
return W_CheckNameForNum ( foundcolormaps [ num ] ) ;
}
//
// build a table for quick conversion from 8bpp to 15bpp
//
//
// added "static inline" keywords, linking with the debug version
// of allegro, it have a makecol15 function of it's own, now
// with "static inline" keywords,it sloves this problem ;)
//
FUNCMATH static inline int makecol15 ( int r , int g , int b )
{
return ( ( ( r > > 3 ) < < 10 ) | ( ( g > > 3 ) < < 5 ) | ( ( b > > 3 ) ) ) ;
}
static void R_Init8to16 ( void )
{
UINT8 * palette ;
int i ;
palette = W_CacheLumpName ( " PLAYPAL " , PU_CACHE ) ;
for ( i = 0 ; i < 256 ; i + + )
{
// PLAYPAL uses 8 bit values
color8to16 [ i ] = ( INT16 ) makecol15 ( palette [ 0 ] , palette [ 1 ] , palette [ 2 ] ) ;
palette + = 3 ;
}
// test a big colormap
hicolormaps = Z_Malloc ( 16384 * sizeof ( * hicolormaps ) , PU_STATIC , NULL ) ;
for ( i = 0 ; i < 16384 ; i + + )
hicolormaps [ i ] = ( INT16 ) ( i < < 1 ) ;
}
//
// R_InitData
//
// Locates all the lumps that will be used by all views
// Must be called after W_Init.
//
void R_InitData ( void )
{
if ( highcolor )
{
CONS_Printf ( " InitHighColor... \n " ) ;
R_Init8to16 ( ) ;
}
CONS_Printf ( " R_LoadTextures()... \n " ) ;
R_LoadTextures ( ) ;
2016-10-19 00:22:11 -07:00
CONS_Printf ( " P_InitPicAnims()... \n " ) ;
P_InitPicAnims ( ) ;
2014-03-15 09:59:03 -07:00
CONS_Printf ( " R_InitSprites()... \n " ) ;
R_InitSpriteLumps ( ) ;
R_InitSprites ( ) ;
CONS_Printf ( " R_InitColormaps()... \n " ) ;
R_InitColormaps ( ) ;
}
void R_ClearTextureNumCache ( boolean btell )
{
if ( tidcache )
Z_Free ( tidcache ) ;
tidcache = NULL ;
if ( btell )
CONS_Debug ( DBG_SETUP , " Fun Fact: There are %d textures used in this map. \n " , tidcachelen ) ;
tidcachelen = 0 ;
}
//
// R_CheckTextureNumForName
//
// Check whether texture is available. Filter out NoTexture indicator.
//
INT32 R_CheckTextureNumForName ( const char * name )
{
INT32 i ;
// "NoTexture" marker.
if ( name [ 0 ] = = ' - ' )
return 0 ;
for ( i = 0 ; i < tidcachelen ; i + + )
if ( ! strncasecmp ( tidcache [ i ] . name , name , 8 ) )
return tidcache [ i ] . id ;
// Need to parse the list backwards, so textures loaded more recently are used in lieu of ones loaded earlier
//for (i = 0; i < numtextures; i++) <- old
for ( i = ( numtextures - 1 ) ; i > = 0 ; i - - ) // <- new
if ( ! strncasecmp ( textures [ i ] - > name , name , 8 ) )
{
tidcachelen + + ;
Z_Realloc ( tidcache , tidcachelen * sizeof ( * tidcache ) , PU_STATIC , & tidcache ) ;
strncpy ( tidcache [ tidcachelen - 1 ] . name , name , 8 ) ;
tidcache [ tidcachelen - 1 ] . name [ 8 ] = ' \0 ' ;
# ifndef ZDEBUG
CONS_Debug ( DBG_SETUP , " texture #%s: %s \n " , sizeu1 ( tidcachelen ) , tidcache [ tidcachelen - 1 ] . name ) ;
# endif
tidcache [ tidcachelen - 1 ] . id = i ;
return i ;
}
return - 1 ;
}
//
// R_TextureNumForName
//
// Calls R_CheckTextureNumForName, aborts with error message.
//
INT32 R_TextureNumForName ( const char * name )
{
const INT32 i = R_CheckTextureNumForName ( name ) ;
if ( i = = - 1 )
{
static INT32 redwall = - 2 ;
CONS_Debug ( DBG_SETUP , " WARNING: R_TextureNumForName: %.8s not found \n " , name ) ;
if ( redwall = = - 2 )
redwall = R_CheckTextureNumForName ( " REDWALL " ) ;
if ( redwall ! = - 1 )
return redwall ;
return 1 ;
}
return i ;
}
//
// R_PrecacheLevel
//
// Preloads all relevant graphics for the level.
//
void R_PrecacheLevel ( void )
{
char * texturepresent , * spritepresent ;
size_t i , j , k ;
lumpnum_t lump ;
thinker_t * th ;
spriteframe_t * sf ;
if ( demoplayback )
return ;
// do not flush the memory, Z_Malloc twice with same user will cause error in Z_CheckHeap()
if ( rendermode ! = render_soft )
return ;
// Precache flats.
flatmemory = P_PrecacheLevelFlats ( ) ;
//
// Precache textures.
//
// no need to precache all software textures in 3D mode
// (note they are still used with the reference software view)
2016-05-10 12:19:42 -07:00
texturepresent = calloc ( numtextures , sizeof ( * texturepresent ) ) ;
2014-03-15 09:59:03 -07:00
if ( texturepresent = = NULL ) I_Error ( " %s: Out of memory looking up textures " , " R_PrecacheLevel " ) ;
for ( j = 0 ; j < numsides ; j + + )
{
// huh, a potential bug here????
2016-05-10 12:19:42 -07:00
if ( sides [ j ] . toptexture > = 0 & & sides [ j ] . toptexture < numtextures )
2014-03-15 09:59:03 -07:00
texturepresent [ sides [ j ] . toptexture ] = 1 ;
2016-05-10 12:19:42 -07:00
if ( sides [ j ] . midtexture > = 0 & & sides [ j ] . midtexture < numtextures )
2014-03-15 09:59:03 -07:00
texturepresent [ sides [ j ] . midtexture ] = 1 ;
2016-05-10 12:19:42 -07:00
if ( sides [ j ] . bottomtexture > = 0 & & sides [ j ] . bottomtexture < numtextures )
2014-03-15 09:59:03 -07:00
texturepresent [ sides [ j ] . bottomtexture ] = 1 ;
}
// Sky texture is always present.
// Note that F_SKY1 is the name used to indicate a sky floor/ceiling as a flat,
// while the sky texture is stored like a wall texture, with a skynum dependent name.
texturepresent [ skytexture ] = 1 ;
texturememory = 0 ;
for ( j = 0 ; j < ( unsigned ) numtextures ; j + + )
{
if ( ! texturepresent [ j ] )
continue ;
if ( ! texturecache [ j ] )
R_GenerateTexture ( j ) ;
// pre-caching individual patches that compose textures became obsolete,
// since we cache entire composite textures
}
free ( texturepresent ) ;
//
// Precache sprites.
//
spritepresent = calloc ( numsprites , sizeof ( * spritepresent ) ) ;
if ( spritepresent = = NULL ) I_Error ( " %s: Out of memory looking up sprites " , " R_PrecacheLevel " ) ;
for ( th = thinkercap . next ; th ! = & thinkercap ; th = th - > next )
if ( th - > function . acp1 = = ( actionf_p1 ) P_MobjThinker )
spritepresent [ ( ( mobj_t * ) th ) - > sprite ] = 1 ;
spritememory = 0 ;
for ( i = 0 ; i < numsprites ; i + + )
{
if ( ! spritepresent [ i ] )
continue ;
for ( j = 0 ; j < sprites [ i ] . numframes ; j + + )
{
sf = & sprites [ i ] . spriteframes [ j ] ;
for ( k = 0 ; k < 8 ; k + + )
{
// see R_InitSprites for more about lumppat,lumpid
lump = sf - > lumppat [ k ] ;
if ( devparm )
spritememory + = W_LumpLength ( lump ) ;
W_CachePatchNum ( lump , PU_CACHE ) ;
}
}
}
free ( spritepresent ) ;
// FIXME: this is no longer correct with OpenGL render mode
CONS_Debug ( DBG_SETUP , " Precache level done: \n "
" flatmemory: %s k \n "
" texturememory: %s k \n "
" spritememory: %s k \n " , sizeu1 ( flatmemory > > 10 ) , sizeu2 ( texturememory > > 10 ) , sizeu3 ( spritememory > > 10 ) ) ;
}
2019-05-20 20:28:52 -07:00
// https://github.com/coelckers/prboom-plus/blob/master/prboom2/src/r_patch.c#L350
boolean R_CheckIfPatch ( lumpnum_t lump )
{
size_t size ;
INT16 width , height ;
patch_t * patch ;
boolean result ;
size = W_LumpLength ( lump ) ;
// minimum length of a valid Doom patch
if ( size < 13 )
return false ;
patch = ( patch_t * ) W_CacheLumpNum ( lump , PU_STATIC ) ;
width = SHORT ( patch - > width ) ;
height = SHORT ( patch - > height ) ;
2019-05-21 07:03:53 -07:00
result = ( height > 0 & & height < = 16384 & & width > 0 & & width < = 16384 & & width < ( INT16 ) ( size / 4 ) ) ;
2019-05-20 20:28:52 -07:00
if ( result )
{
// The dimensions seem like they might be valid for a patch, so
// check the column directory for extra security. All columns
// must begin after the column directory, and none of them must
// point past the end of the patch.
INT16 x ;
for ( x = 0 ; x < width ; x + + )
{
UINT32 ofs = LONG ( patch - > columnofs [ x ] ) ;
// Need one byte for an empty column (but there's patches that don't know that!)
if ( ofs < ( UINT32 ) width * 4 + 8 | | ofs > = ( UINT32 ) size )
{
result = false ;
break ;
}
}
}
return result ;
}
2019-05-26 19:37:23 -07:00
void R_PatchToFlat ( patch_t * patch , UINT8 * flat )
2019-05-20 20:28:52 -07:00
{
fixed_t col , ofs ;
column_t * column ;
UINT8 * desttop , * dest , * deststop ;
UINT8 * source ;
desttop = flat ;
2019-05-26 12:22:33 -07:00
deststop = desttop + ( SHORT ( patch - > width ) * SHORT ( patch - > height ) ) ;
2019-05-20 20:28:52 -07:00
for ( col = 0 ; col < SHORT ( patch - > width ) ; col + + , desttop + + )
{
INT32 topdelta , prevdelta = - 1 ;
column = ( column_t * ) ( ( UINT8 * ) patch + LONG ( patch - > columnofs [ col ] ) ) ;
while ( column - > topdelta ! = 0xff )
{
topdelta = column - > topdelta ;
if ( topdelta < = prevdelta )
topdelta + = prevdelta ;
prevdelta = topdelta ;
2019-05-26 12:22:33 -07:00
dest = desttop + ( topdelta * SHORT ( patch - > width ) ) ;
2019-05-20 20:28:52 -07:00
source = ( UINT8 * ) ( column ) + 3 ;
for ( ofs = 0 ; dest < deststop & & ofs < column - > length ; ofs + + )
{
if ( source [ ofs ] ! = TRANSPARENTPIXEL )
* dest = source [ ofs ] ;
2019-05-26 12:22:33 -07:00
dest + = SHORT ( patch - > width ) ;
2019-05-20 20:28:52 -07:00
}
column = ( column_t * ) ( ( UINT8 * ) column + column - > length + 4 ) ;
}
}
}
2019-05-26 19:37:23 -07:00
# ifndef NO_PNG_LUMPS
boolean R_IsLumpPNG ( UINT8 * d , size_t s )
{
if ( s < 67 ) // http://garethrees.org/2007/11/14/pngcrush/
return false ;
// Check for PNG file signature using memcmp
// As it may be faster on CPUs with slow unaligned memory access
// Ref: http://www.libpng.org/pub/png/spec/1.2/PNG-Rationale.html#R.PNG-file-signature
return ( memcmp ( & d [ 0 ] , " \x89 \x50 \x4e \x47 \x0d \x0a \x1a \x0a " , 8 ) = = 0 ) ;
}
# ifdef HAVE_PNG
typedef struct {
png_bytep buffer ;
png_uint_32 bufsize ;
png_uint_32 current_pos ;
} png_ioread ;
static void PNG_IOReader ( png_structp png_ptr , png_bytep data , png_size_t length )
{
png_ioread * f = png_get_io_ptr ( png_ptr ) ;
if ( length > ( f - > bufsize - f - > current_pos ) )
png_error ( png_ptr , " read error in read_data_memory (loadpng) " ) ;
memcpy ( data , f - > buffer + f - > current_pos , length ) ;
f - > current_pos + = length ;
}
static void PNG_error ( png_structp PNG , png_const_charp pngtext )
{
CONS_Debug ( DBG_RENDER , " libpng error at %p: %s " , PNG , pngtext ) ;
//I_Error("libpng error at %p: %s", PNG, pngtext);
}
static void PNG_warn ( png_structp PNG , png_const_charp pngtext )
{
CONS_Debug ( DBG_RENDER , " libpng warning at %p: %s " , PNG , pngtext ) ;
}
static png_bytep * PNG_Read ( UINT8 * png , UINT16 * w , UINT16 * h , size_t size )
{
png_structp png_ptr ;
png_infop png_info_ptr ;
png_uint_32 width , height ;
int bit_depth , color_type ;
png_uint_32 y ;
# ifdef PNG_SETJMP_SUPPORTED
# ifdef USE_FAR_KEYWORD
jmp_buf jmpbuf ;
# endif
# endif
png_ioread png_io ;
png_bytep * row_pointers ;
png_ptr = png_create_read_struct ( PNG_LIBPNG_VER_STRING , NULL ,
PNG_error , PNG_warn ) ;
if ( ! png_ptr )
{
CONS_Debug ( DBG_RENDER , " PNG_Load: Error on initialize libpng \n " ) ;
return NULL ;
}
png_info_ptr = png_create_info_struct ( png_ptr ) ;
if ( ! png_info_ptr )
{
CONS_Debug ( DBG_RENDER , " PNG_Load: Error on allocate for libpng \n " ) ;
png_destroy_read_struct ( & png_ptr , NULL , NULL ) ;
return NULL ;
}
# ifdef USE_FAR_KEYWORD
if ( setjmp ( jmpbuf ) )
# else
if ( setjmp ( png_jmpbuf ( png_ptr ) ) )
# endif
{
//CONS_Debug(DBG_RENDER, "libpng load error on %s\n", filename);
png_destroy_read_struct ( & png_ptr , & png_info_ptr , NULL ) ;
return NULL ;
}
# ifdef USE_FAR_KEYWORD
png_memcpy ( png_jmpbuf ( png_ptr ) , jmpbuf , sizeof jmp_buf ) ;
# endif
// png_source is array which have png data
png_io . buffer = ( png_bytep ) png ;
png_io . bufsize = size ;
png_io . current_pos = 0 ;
// set our own read_function
png_set_read_fn ( png_ptr , & png_io , PNG_IOReader ) ;
# ifdef PNG_SET_USER_LIMITS_SUPPORTED
png_set_user_limits ( png_ptr , 2048 , 2048 ) ;
# endif
png_read_info ( png_ptr , png_info_ptr ) ;
png_get_IHDR ( png_ptr , png_info_ptr , & width , & height , & bit_depth , & color_type ,
NULL , NULL , NULL ) ;
if ( bit_depth = = 16 )
png_set_strip_16 ( png_ptr ) ;
if ( color_type = = PNG_COLOR_TYPE_GRAY | | color_type = = PNG_COLOR_TYPE_GRAY_ALPHA )
png_set_gray_to_rgb ( png_ptr ) ;
else if ( color_type = = PNG_COLOR_TYPE_PALETTE )
png_set_palette_to_rgb ( png_ptr ) ;
if ( png_get_valid ( png_ptr , png_info_ptr , PNG_INFO_tRNS ) )
png_set_tRNS_to_alpha ( png_ptr ) ;
else if ( color_type ! = PNG_COLOR_TYPE_RGB_ALPHA & & color_type ! = PNG_COLOR_TYPE_GRAY_ALPHA )
{
# if PNG_LIBPNG_VER < 10207
png_set_filler ( png_ptr , 0xFF , PNG_FILLER_AFTER ) ;
# else
png_set_add_alpha ( png_ptr , 0xFF , PNG_FILLER_AFTER ) ;
# endif
}
png_read_update_info ( png_ptr , png_info_ptr ) ;
// Read the image
row_pointers = ( png_bytep * ) malloc ( sizeof ( png_bytep ) * height ) ;
for ( y = 0 ; y < height ; y + + )
row_pointers [ y ] = ( png_byte * ) malloc ( png_get_rowbytes ( png_ptr , png_info_ptr ) ) ;
png_read_image ( png_ptr , row_pointers ) ;
png_destroy_read_struct ( & png_ptr , & png_info_ptr , NULL ) ;
* w = ( INT32 ) width ;
* h = ( INT32 ) height ;
return row_pointers ;
}
// Convert a PNG to a raw image.
static UINT8 * PNG_RawConvert ( UINT8 * png , UINT16 * w , UINT16 * h , size_t size )
{
UINT8 * flat ;
png_uint_32 x , y ;
png_bytep * row_pointers = PNG_Read ( png , w , h , size ) ;
png_uint_32 width = * w , height = * h ;
if ( ! row_pointers )
return NULL ;
// Convert the image to 8bpp
flat = Z_Malloc ( width * height , PU_STATIC , NULL ) ;
memset ( flat , TRANSPARENTPIXEL , width * height ) ;
for ( y = 0 ; y < height ; y + + )
{
png_bytep row = row_pointers [ y ] ;
for ( x = 0 ; x < width ; x + + )
{
png_bytep px = & ( row [ x * 4 ] ) ;
flat [ ( ( y * width ) + x ) ] = NearestColor ( ( UINT8 ) px [ 0 ] , ( UINT8 ) px [ 1 ] , ( UINT8 ) px [ 2 ] ) ;
}
}
free ( row_pointers ) ;
return flat ;
}
// Get the alpha mask of the image.
static UINT8 * PNG_GetAlphaMask ( UINT8 * png , size_t size )
{
UINT8 * mask ;
png_uint_32 x , y ;
UINT16 width , height ;
png_bytep * row_pointers = PNG_Read ( png , & width , & height , size ) ;
if ( ! row_pointers )
return NULL ;
// Convert the image to 8bpp
mask = Z_Malloc ( width * height , PU_STATIC , NULL ) ;
memset ( mask , 0 , width * height ) ;
for ( y = 0 ; y < height ; y + + )
{
png_bytep row = row_pointers [ y ] ;
for ( x = 0 ; x < width ; x + + )
{
png_bytep px = & ( row [ x * 4 ] ) ;
mask [ ( ( y * width ) + x ) ] = ( UINT8 ) px [ 3 ] ;
}
}
free ( row_pointers ) ;
return mask ;
}
// Convert a PNG to a flat.
UINT8 * R_PNGToFlat ( levelflat_t * levelflat , UINT8 * png , size_t size )
{
return PNG_RawConvert ( png , & levelflat - > width , & levelflat - > height , size ) ;
}
// Convert a PNG to a patch.
// This is adapted from the "kartmaker" utility
static unsigned char imgbuf [ 1 < < 26 ] ;
patch_t * R_PNGToPatch ( UINT8 * png , size_t size )
{
UINT16 width , height ;
UINT8 * raw = PNG_RawConvert ( png , & width , & height , size ) ;
UINT8 * alphamask = PNG_GetAlphaMask ( png , size ) ;
UINT32 x , y ;
UINT8 * img ;
UINT8 * imgptr = imgbuf ;
UINT8 * colpointers , * startofspan ;
# define WRITE8(buf, a) ({*buf = (a); buf++;})
# define WRITE16(buf, a) ({*buf = (a)&255; buf++; *buf = (a)>>8; buf++;})
# define WRITE32(buf, a) ({WRITE16(buf, (a)&65535); WRITE16(buf, (a)>>16);})
if ( ! raw )
return NULL ;
// Write image size and offset
WRITE16 ( imgptr , width ) ;
WRITE16 ( imgptr , height ) ;
// no offsets
WRITE16 ( imgptr , 0 ) ;
WRITE16 ( imgptr , 0 ) ;
// Leave placeholder to column pointers
colpointers = imgptr ;
imgptr + = width * 4 ;
// Write columns
for ( x = 0 ; x < width ; x + + )
{
int lastStartY = 0 ;
int spanSize = 0 ;
startofspan = NULL ;
//printf("%d ", x);
// Write column pointer (@TODO may be wrong)
WRITE32 ( colpointers , imgptr - imgbuf ) ;
// Write pixels
for ( y = 0 ; y < height ; y + + )
{
UINT8 paletteIndex = raw [ ( ( y * width ) + x ) ] ;
UINT8 opaque = alphamask [ ( ( y * width ) + x ) ] ; // If 1, we have a pixel
// End span if we have a transparent pixel
if ( ! opaque )
{
if ( startofspan )
WRITE8 ( imgptr , 0 ) ;
startofspan = NULL ;
continue ;
}
// Start new column if we need to
if ( ! startofspan | | spanSize = = 255 )
{
int writeY = y ;
// If we reached the span size limit, finish the previous span
if ( startofspan )
WRITE8 ( imgptr , 0 ) ;
if ( y > 254 )
{
// Make sure we're aligned to 254
if ( lastStartY < 254 )
{
WRITE8 ( imgptr , 254 ) ;
WRITE8 ( imgptr , 0 ) ;
imgptr + = 2 ;
lastStartY = 254 ;
}
// Write stopgap empty spans if needed
writeY = y - lastStartY ;
while ( writeY > 254 )
{
WRITE8 ( imgptr , 254 ) ;
WRITE8 ( imgptr , 0 ) ;
imgptr + = 2 ;
writeY - = 254 ;
}
}
startofspan = imgptr ;
WRITE8 ( imgptr , writeY ) ; ///@TODO calculate starting y pos
imgptr + = 2 ;
spanSize = 0 ;
lastStartY = y ;
}
// Write the pixel
WRITE8 ( imgptr , paletteIndex ) ;
spanSize + + ;
startofspan [ 1 ] = spanSize ;
}
if ( startofspan )
WRITE8 ( imgptr , 0 ) ;
WRITE8 ( imgptr , 0xFF ) ;
}
# undef WRITE8
# undef WRITE16
# undef WRITE32
size = imgptr - imgbuf ;
img = malloc ( size ) ;
memcpy ( img , imgbuf , size ) ;
return ( patch_t * ) img ;
}
boolean R_PNGDimensions ( UINT8 * png , INT16 * width , INT16 * height , size_t size )
{
png_structp png_ptr ;
png_infop png_info_ptr ;
png_uint_32 w , h ;
int bit_depth , color_type ;
# ifdef PNG_SETJMP_SUPPORTED
# ifdef USE_FAR_KEYWORD
jmp_buf jmpbuf ;
# endif
# endif
png_ioread png_io ;
png_ptr = png_create_read_struct ( PNG_LIBPNG_VER_STRING , NULL ,
PNG_error , PNG_warn ) ;
if ( ! png_ptr )
{
CONS_Debug ( DBG_RENDER , " PNG_Load: Error on initialize libpng \n " ) ;
return false ;
}
png_info_ptr = png_create_info_struct ( png_ptr ) ;
if ( ! png_info_ptr )
{
CONS_Debug ( DBG_RENDER , " PNG_Load: Error on allocate for libpng \n " ) ;
png_destroy_read_struct ( & png_ptr , NULL , NULL ) ;
return false ;
}
# ifdef USE_FAR_KEYWORD
if ( setjmp ( jmpbuf ) )
# else
if ( setjmp ( png_jmpbuf ( png_ptr ) ) )
# endif
{
//CONS_Debug(DBG_RENDER, "libpng load error on %s\n", filename);
png_destroy_read_struct ( & png_ptr , & png_info_ptr , NULL ) ;
return false ;
}
# ifdef USE_FAR_KEYWORD
png_memcpy ( png_jmpbuf ( png_ptr ) , jmpbuf , sizeof jmp_buf ) ;
# endif
// png_source is array which have png data
png_io . buffer = ( png_bytep ) png ;
png_io . bufsize = size ;
png_io . current_pos = 0 ;
// set our own read_function
png_set_read_fn ( png_ptr , & png_io , PNG_IOReader ) ;
# ifdef PNG_SET_USER_LIMITS_SUPPORTED
png_set_user_limits ( png_ptr , 2048 , 2048 ) ;
# endif
png_read_info ( png_ptr , png_info_ptr ) ;
png_get_IHDR ( png_ptr , png_info_ptr , & w , & h , & bit_depth , & color_type ,
NULL , NULL , NULL ) ;
// okay done. stop.
png_destroy_read_struct ( & png_ptr , & png_info_ptr , NULL ) ;
* width = ( INT32 ) w ;
* height = ( INT32 ) h ;
return true ;
}
# endif
# endif
void R_TextureToFlat ( size_t tex , UINT8 * flat )
2019-05-20 20:28:52 -07:00
{
texture_t * texture = textures [ tex ] ;
fixed_t col , ofs ;
column_t * column ;
UINT8 * desttop , * dest , * deststop ;
UINT8 * source ;
desttop = flat ;
deststop = desttop + ( texture - > width * texture - > height ) ;
2019-05-26 12:22:33 -07:00
for ( col = 0 ; col < texture - > width ; col + + , desttop + + )
2019-05-20 20:28:52 -07:00
{
column = ( column_t * ) R_GetColumn ( tex , col ) ;
if ( ! texture - > holes )
{
dest = desttop ;
source = ( UINT8 * ) ( column ) ;
for ( ofs = 0 ; dest < deststop & & ofs < texture - > height ; ofs + + )
{
if ( source [ ofs ] ! = TRANSPARENTPIXEL )
* dest = source [ ofs ] ;
dest + = texture - > width ;
}
}
else
{
2019-05-26 12:22:33 -07:00
INT32 topdelta , prevdelta = - 1 ;
2019-05-20 20:28:52 -07:00
while ( column - > topdelta ! = 0xff )
{
topdelta = column - > topdelta ;
if ( topdelta < = prevdelta )
topdelta + = prevdelta ;
prevdelta = topdelta ;
dest = desttop + ( topdelta * texture - > width ) ;
source = ( UINT8 * ) ( column ) + 3 ;
for ( ofs = 0 ; dest < deststop & & ofs < column - > length ; ofs + + )
{
if ( source [ ofs ] ! = TRANSPARENTPIXEL )
* dest = source [ ofs ] ;
dest + = texture - > width ;
}
column = ( column_t * ) ( ( UINT8 * ) column + column - > length + 4 ) ;
}
}
}
}