diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 437dbe4ba..5ec8d1534 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -126,7 +126,9 @@ set(SRB2_CORE_RENDER_SOURCES r_sky.c r_splats.c r_things.c + r_textures.c r_patch.c + r_picformats.c r_portal.c r_bsp.h @@ -142,7 +144,9 @@ set(SRB2_CORE_RENDER_SOURCES r_splats.h r_state.h r_things.h + r_textures.h r_patch.h + r_picformats.h r_portal.h ) diff --git a/src/Makefile b/src/Makefile index 9ea1ea239..e69bccd26 100644 --- a/src/Makefile +++ b/src/Makefile @@ -469,7 +469,9 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/r_sky.o \ $(OBJDIR)/r_splats.o \ $(OBJDIR)/r_things.o \ + $(OBJDIR)/r_textures.o \ $(OBJDIR)/r_patch.o \ + $(OBJDIR)/r_picformats.o \ $(OBJDIR)/r_portal.o \ $(OBJDIR)/screen.o \ $(OBJDIR)/v_video.o \ diff --git a/src/dehacked.c b/src/dehacked.c index c801dcde9..613c683f3 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -29,8 +29,10 @@ #include "p_local.h" // for var1 and var2, and some constants #include "p_setup.h" #include "r_data.h" +#include "r_textures.h" #include "r_draw.h" #include "r_patch.h" +#include "r_picformats.h" #include "r_things.h" // R_Char2Frame #include "r_sky.h" #include "fastcmp.h" diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 901c0184a..138301d01 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -20,11 +20,13 @@ #include "../doomstat.h" //gamemode #include "../i_video.h" //rendermode #include "../r_data.h" +#include "../r_textures.h" #include "../w_wad.h" #include "../z_zone.h" #include "../v_video.h" #include "../r_draw.h" #include "../r_patch.h" +#include "../r_picformats.h" #include "../p_setup.h" INT32 patchformat = GL_TEXFMT_AP_88; // use alpha for holes @@ -99,6 +101,10 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm count--; texel = source[yfrac>>FRACBITS]; + alpha = 0xFF; + // Make pixel transparent if chroma keyed + if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)) + alpha = 0x00; //Hurdler: 25/04/2000: now support colormap in hardware mode if (mipmap->colormap) @@ -211,17 +217,15 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block, count--; texel = source[yfrac>>FRACBITS]; + alpha = 0xFF; + // Make pixel transparent if chroma keyed + if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)) + alpha = 0x00; //Hurdler: 25/04/2000: now support colormap in hardware mode if (mipmap->colormap) texel = mipmap->colormap[texel]; - // If the mipmap is chromakeyed, check if the texel's color - // is equivalent to the chroma key's color index. - alpha = 0xff; - if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)) - alpha = 0x00; - // hope compiler will get this switch out of the loops (dreams...) // gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?) // Alam: SRB2 uses Mingw, HUGS @@ -508,13 +512,17 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *grtex) realpatch = (softwarepatch_t *)pdata; #ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) - realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength, NULL); + if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) + { + // Dummy variables. + INT32 pngwidth, pngheight; + realpatch = (softwarepatch_t *)Picture_PNGConvert(pdata, PICFMT_PATCH, &pngwidth, &pngheight, NULL, NULL, lumplength, NULL, 0); + } else #endif #ifdef WALLFLATS if (texture->type == TEXTURETYPE_FLAT) - realpatch = R_FlatToPatch(pdata, texture->width, texture->height, 0, 0, NULL, false); + realpatch = (softwarepatch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_PATCH, 0, NULL, texture->width, texture->height, 0, 0, 0); else #endif { @@ -834,6 +842,8 @@ static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum) static void HWR_CacheTextureAsFlat(GLMipmap_t *grMipmap, INT32 texturenum) { UINT8 *flat; + UINT8 *converted; + size_t size; // setup the texture info grMipmap->format = GL_TEXFMT_P_8; @@ -841,11 +851,12 @@ static void HWR_CacheTextureAsFlat(GLMipmap_t *grMipmap, INT32 texturenum) grMipmap->width = (UINT16)textures[texturenum]->width; grMipmap->height = (UINT16)textures[texturenum]->height; + size = (grMipmap->width * grMipmap->height); - flat = Z_Malloc(grMipmap->width * grMipmap->height, PU_HWRCACHE, &grMipmap->data); - memset(flat, TRANSPARENTPIXEL, grMipmap->width * grMipmap->height); - - R_TextureToFlat(texturenum, flat); + flat = Z_Malloc(size, PU_HWRCACHE, &grMipmap->data); + converted = (UINT8 *)Picture_TextureToFlat(texturenum); + M_Memcpy(flat, converted, size); + Z_Free(converted); } // Download a Doom 'flat' to the hardware cache and make it ready for use @@ -885,7 +896,7 @@ void HWR_GetLevelFlat(levelflat_t *levelflat) INT32 texturenum = levelflat->u.texture.num; #ifdef PARANOIA if ((unsigned)texturenum >= gl_numtextures) - I_Error("HWR_GetLevelFlat: texturenum >= numtextures\n"); + I_Error("HWR_GetLevelFlat: texturenum >= numtextures"); #endif // Who knows? @@ -907,6 +918,53 @@ void HWR_GetLevelFlat(levelflat_t *levelflat) // The system-memory data can be purged now. Z_ChangeTag(grtex->mipmap.data, PU_HWRCACHE_UNLOCKED); } + else if (levelflat->type == LEVELFLAT_PATCH) + { + patch_t *patch = W_CachePatchNum(levelflat->u.flat.lumpnum, PU_CACHE); + levelflat->width = (UINT16)(patch->width); + levelflat->height = (UINT16)(patch->height); + HWR_GetPatch(patch); + } +#ifndef NO_PNG_LUMPS + else if (levelflat->type == LEVELFLAT_PNG) + { + INT32 pngwidth, pngheight; + GLMipmap_t *mipmap = levelflat->mipmap; + UINT8 *flat; + size_t size; + + // Cache the picture. + if (!levelflat->picture) + { + levelflat->picture = Picture_PNGConvert(W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE), PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); + levelflat->width = (UINT16)pngwidth; + levelflat->height = (UINT16)pngheight; + } + + // Make the mipmap. + if (mipmap == NULL) + { + mipmap = Z_Calloc(sizeof(GLMipmap_t), PU_LEVEL, NULL); + mipmap->format = GL_TEXFMT_P_8; + mipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; + levelflat->mipmap = mipmap; + } + + if (!mipmap->data && !mipmap->downloaded) + { + mipmap->width = levelflat->width; + mipmap->height = levelflat->height; + size = (mipmap->width * mipmap->height); + flat = Z_Malloc(size, PU_LEVEL, &mipmap->data); + if (levelflat->picture == NULL) + I_Error("HWR_GetLevelFlat: levelflat->picture == NULL"); + M_Memcpy(flat, levelflat->picture, size); + } + + // Tell the hardware driver to bind the current texture to the flat's mipmap + HWD.pfnSetTexture(mipmap); + } +#endif else // set no texture HWR_SetCurrentTexture(NULL); } diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index e9d52c791..486b03f33 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -26,6 +26,7 @@ #include "../p_setup.h" #include "../r_local.h" #include "../r_patch.h" +#include "../r_picformats.h" #include "../r_bsp.h" #include "../d_clisrv.h" #include "../w_wad.h" @@ -358,7 +359,6 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool float fflatwidth = 64.0f, fflatheight = 64.0f; INT32 flatflag = 63; boolean texflat = false; - size_t len; float scrollx = 0.0f, scrolly = 0.0f; angle_t angle = 0; FSurfaceInfo Surf; @@ -413,16 +413,9 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool // set texture for polygon if (levelflat != NULL) { - if (levelflat->type == LEVELFLAT_TEXTURE) + if (levelflat->type == LEVELFLAT_FLAT) { - fflatwidth = textures[levelflat->u.texture.num]->width; - fflatheight = textures[levelflat->u.texture.num]->height; - texflat = true; - } - else if (levelflat->type == LEVELFLAT_FLAT) - { - len = W_LumpLength(levelflat->u.flat.lumpnum); - + size_t len = W_LumpLength(levelflat->u.flat.lumpnum); switch (len) { case 4194304: // 2048x2048 lump @@ -447,9 +440,22 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool fflatwidth = fflatheight = 64.0f; break; } - flatflag = ((INT32)fflatwidth)-1; } + else + { + if (levelflat->type == LEVELFLAT_TEXTURE) + { + fflatwidth = textures[levelflat->u.texture.num]->width; + fflatheight = textures[levelflat->u.texture.num]->height; + } + else if (levelflat->type == LEVELFLAT_PATCH || levelflat->type == LEVELFLAT_PNG) + { + fflatwidth = levelflat->width; + fflatheight = levelflat->height; + } + texflat = true; + } } else // set no texture HWR_SetCurrentTexture(NULL); @@ -2659,7 +2665,6 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, float fflatwidth = 64.0f, fflatheight = 64.0f; INT32 flatflag = 63; boolean texflat = false; - size_t len; float scrollx = 0.0f, scrolly = 0.0f; angle_t angle = 0; FSurfaceInfo Surf; @@ -2693,16 +2698,9 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, // set texture for polygon if (levelflat != NULL) { - if (levelflat->type == LEVELFLAT_TEXTURE) + if (levelflat->type == LEVELFLAT_FLAT) { - fflatwidth = textures[levelflat->u.texture.num]->width; - fflatheight = textures[levelflat->u.texture.num]->height; - texflat = true; - } - else if (levelflat->type == LEVELFLAT_FLAT) - { - len = W_LumpLength(levelflat->u.flat.lumpnum); - + size_t len = W_LumpLength(levelflat->u.flat.lumpnum); switch (len) { case 4194304: // 2048x2048 lump @@ -2727,9 +2725,22 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, fflatwidth = fflatheight = 64.0f; break; } - flatflag = ((INT32)fflatwidth)-1; } + else + { + if (levelflat->type == LEVELFLAT_TEXTURE) + { + fflatwidth = textures[levelflat->u.texture.num]->width; + fflatheight = textures[levelflat->u.texture.num]->height; + } + else if (levelflat->type == LEVELFLAT_PATCH || levelflat->type == LEVELFLAT_PNG) + { + fflatwidth = levelflat->width; + fflatheight = levelflat->height; + } + texflat = true; + } } else // set no texture HWR_SetCurrentTexture(NULL); diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 712983cac..13b5f1cb5 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -18,6 +18,7 @@ #include "p_local.h" #include "z_zone.h" #include "r_patch.h" +#include "r_picformats.h" #include "r_things.h" #include "r_draw.h" // R_GetColorByName #include "doomstat.h" // luabanks[] diff --git a/src/p_maputl.c b/src/p_maputl.c index c6e064d18..90718a41c 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -18,6 +18,7 @@ #include "p_local.h" #include "r_main.h" #include "r_data.h" +#include "r_textures.h" #include "p_maputl.h" #include "p_polyobj.h" #include "p_slopes.h" diff --git a/src/p_saveg.c b/src/p_saveg.c index 276b3eb05..4f6f31803 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -22,6 +22,8 @@ #include "p_setup.h" #include "p_saveg.h" #include "r_data.h" +#include "r_textures.h" +#include "r_things.h" #include "r_skins.h" #include "r_state.h" #include "w_wad.h" diff --git a/src/p_setup.c b/src/p_setup.c index 8d7904bb0..5e0b0304b 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -28,7 +28,9 @@ #include "r_data.h" #include "r_things.h" // for R_AddSpriteDefs +#include "r_textures.h" #include "r_patch.h" +#include "r_picformats.h" #include "r_sky.h" #include "r_draw.h" @@ -547,6 +549,8 @@ Ploadflat (levelflat_t *levelflat, const char *flatname, boolean resize) lumpnum_t flatnum; int texturenum; + patch_t *flatpatch; + size_t lumplength; size_t i; @@ -603,7 +607,9 @@ texturefound: { flatfound: /* This could be a flat, patch, or PNG. */ - if (Patch_CheckIfDoom((patch_t *)W_CacheLumpNum(flatnum, PU_STATIC), W_LumpLength(flatnum))) + flatpatch = W_CacheLumpNum(flatnum, PU_CACHE); + lumplength = W_LumpLength(flatnum); + if (Picture_CheckIfPatch(flatpatch, lumplength)) levelflat->type = LEVELFLAT_PATCH; else { @@ -613,12 +619,14 @@ flatfound: FIXME: Put this elsewhere. */ W_ReadLumpHeader(flatnum, buffer, 8, 0); - if (R_IsLumpPNG(buffer, W_LumpLength(flatnum))) + if (Picture_IsLumpPNG(buffer, lumplength)) levelflat->type = LEVELFLAT_PNG; else #endif/*NO_PNG_LUMPS*/ levelflat->type = LEVELFLAT_FLAT;/* phew */ } + if (flatpatch) + Z_Free(flatpatch); levelflat->u.flat. lumpnum = flatnum; levelflat->u.flat.baselumpnum = LUMPERROR; diff --git a/src/p_setup.h b/src/p_setup.h index 497870628..a02c24d98 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -37,9 +37,7 @@ enum LEVELFLAT_NONE,/* HOM time my friend */ LEVELFLAT_FLAT, LEVELFLAT_PATCH, -#ifndef NO_PNG_LUMPS LEVELFLAT_PNG, -#endif LEVELFLAT_TEXTURE, }; @@ -72,15 +70,17 @@ typedef struct u; UINT16 width, height; - fixed_t topoffset, leftoffset; // for flat animation INT32 animseq; // start pos. in the anim sequence INT32 numpics; INT32 speed; - // for patchflats - UINT8 *flatpatch; + // for textures + UINT8 *picture; +#ifdef HWRENDER + void *mipmap; +#endif } levelflat_t; extern size_t numlevelflats; diff --git a/src/p_spec.c b/src/p_spec.c index f30e25f7f..b045ad010 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -20,6 +20,7 @@ #include "p_local.h" #include "p_setup.h" // levelflats for flat animation #include "r_data.h" +#include "r_textures.h" #include "m_random.h" #include "p_mobj.h" #include "i_system.h" diff --git a/src/r_data.c b/src/r_data.c index dc2c41150..0e0e613f4 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -19,7 +19,9 @@ #include "p_local.h" #include "m_misc.h" #include "r_data.h" +#include "r_textures.h" #include "r_patch.h" +#include "r_picformats.h" #include "w_wad.h" #include "z_zone.h" #include "p_setup.h" // levelflats @@ -32,64 +34,6 @@ #include // alloca(sizeof) #endif -#ifdef HWRENDER -#include "hardware/hw_main.h" // HWR_LoadMapTextures -#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 -#if 0 -#define AVOID_ERRNO -#else -#include -#endif - -// -// 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 @@ -100,20 +44,6 @@ typedef struct size_t numspritelumps, max_spritelumps; -// textures -INT32 numtextures = 0; // total number of textures found, -// size of following tables - -texture_t **textures = NULL; -textureflat_t *texflats = NULL; -static UINT32 **texturecolumnofs; // column offset lookup table for each texture -static UINT8 **texturecache; // graphics data for each generated full-size texture - -INT32 *texturewidth; -fixed_t *textureheight; // needed for texture pegging - -INT32 *texturetranslation; - // needed for pre rendering sprcache_t *spritecachedinfo; @@ -127,106 +57,6 @@ size_t flatmemory, spritememory, texturememory; 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, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) -{ - INT32 count, position; - UINT8 *source; - INT32 topdelta, prevdelta = -1; - INT32 originy = originPatch->originy; - - (void)patchheight; // This parameter is unused - - 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; - source -= position; // start further down the column - 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_DrawFlippedColumnInCache -// Similar to R_DrawColumnInCache; it draws the column inverted, however. -// -static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) -{ - INT32 count, position; - UINT8 *source, *dest; - INT32 topdelta, prevdelta = -1; - INT32 originy = originPatch->originy; - - while (patch->topdelta != 0xff) - { - topdelta = patch->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - topdelta = patchheight-patch->length-topdelta; - source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1) - count = patch->length; - position = originy + topdelta; - - if (position < 0) - { - count += position; - source += position; // start further UP the column - position = 0; - } - - if (position + count > cacheheight) - count = cacheheight - position; - - dest = cache + position; - if (count > 0) - { - for (; dest < cache + position + count; --source) - *dest++ = *source; - } - - patch = (column_t *)((UINT8 *)patch + patch->length + 4); - } -} - // Blends two pixels together, using the equation // that matches the specified alpha style. UINT32 ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha) @@ -376,1183 +206,6 @@ UINT8 ASTBlendPaletteIndexes(UINT8 background, UINT8 foreground, int style, UINT return background; } -// -// R_DrawBlendColumnInCache -// Draws a translucent column into the cache, applying a half-cooked equation to get a proper translucency value (Needs code in R_GenerateTexture()). -// -static inline void R_DrawBlendColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) -{ - INT32 count, position; - UINT8 *source, *dest; - INT32 topdelta, prevdelta = -1; - INT32 originy = originPatch->originy; - - (void)patchheight; // This parameter is unused - - 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; - source -= position; // start further down the column - position = 0; - } - - if (position + count > cacheheight) - count = cacheheight - position; - - dest = cache + position; - if (count > 0) - { - for (; dest < cache + position + count; source++, dest++) - if (*source != 0xFF) - *dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha); - } - - patch = (column_t *)((UINT8 *)patch + patch->length + 4); - } -} - -// -// R_DrawBlendFlippedColumnInCache -// Similar to the one above except that the column is inverted. -// -static inline void R_DrawBlendFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) -{ - INT32 count, position; - UINT8 *source, *dest; - INT32 topdelta, prevdelta = -1; - INT32 originy = originPatch->originy; - - while (patch->topdelta != 0xff) - { - topdelta = patch->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - topdelta = patchheight-patch->length-topdelta; - source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1) - count = patch->length; - position = originy + topdelta; - - if (position < 0) - { - count += position; - source += position; // start further UP the column - position = 0; - } - - if (position + count > cacheheight) - count = cacheheight - position; - - dest = cache + position; - if (count > 0) - { - for (; dest < cache + position + count; --source, dest++) - if (*source != 0xFF) - *dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha); - } - - 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 -// been simplified for the sake of highcolor (lol), dynamic ligthing, & speed. -// -// 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; - softwarepatch_t *realpatch; - UINT8 *pdata; - int x, x1, x2, i, width, height; - size_t blocksize; - column_t *patchcol; - UINT8 *colofs; - - UINT16 wadnum; - lumpnum_t lumpnum; - size_t lumplength; - - 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; - - wadnum = patch->wad; - lumpnum = patch->lump; - lumplength = W_LumpLengthPwad(wadnum, lumpnum); - pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); - realpatch = (softwarepatch_t *)pdata; - -#ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) - goto multipatch; -#endif -#ifdef WALLFLATS - if (texture->type == TEXTURETYPE_FLAT) - goto multipatch; -#endif - - // Check the patch for holes. - if (texture->width > SHORT(realpatch->width) || texture->height > SHORT(realpatch->height)) - holey = true; - colofs = (UINT8 *)realpatch->columnofs; - for (x = 0; x < texture->width && !holey; x++) - { - column_t *col = (column_t *)((UINT8 *)realpatch + LONG(*(UINT32 *)&colofs[x<<2])); - 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; - texture->flip = patch->flip; - blocksize = lumplength; - 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 = (block + 8); - texturecolumnofs[texnum] = (UINT32 *)colofs; - blocktex = block; - if (patch->flip & 1) // flip the patch horizontally - { - UINT8 *realcolofs = (UINT8 *)realpatch->columnofs; - for (x = 0; x < texture->width; x++) - *(UINT32 *)&colofs[x<<2] = realcolofs[( texture->width-1-x )<<2]; // swap with the offset of the other side of the texture - } - // we can't as easily flip the patch vertically sadly though, - // we have wait until the texture itself is drawn to do that - for (x = 0; x < texture->width; x++) - *(UINT32 *)&colofs[x<<2] = LONG(LONG(*(UINT32 *)&colofs[x<<2]) + 3); - goto done; - } - - // Otherwise, do multipatch format. - } - - // multi-patch textures (or 'composite') - multipatch: - texture->holes = false; - texture->flip = 0; - blocksize = (texture->width * 4) + (texture->width * texture->height); - texturememory += blocksize; - block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]); - - memset(block, TRANSPARENTPIXEL, blocksize+1); // Transparency hack - - // columns lookup table - colofs = block; - texturecolumnofs[texnum] = (UINT32 *)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++) - { - boolean dealloc = true; - static void (*ColumnDrawerPointer)(column_t *, UINT8 *, texpatch_t *, INT32, INT32); // Column drawing function pointer. - if (patch->style != AST_COPY) - ColumnDrawerPointer = (patch->flip & 2) ? R_DrawBlendFlippedColumnInCache : R_DrawBlendColumnInCache; - else - ColumnDrawerPointer = (patch->flip & 2) ? R_DrawFlippedColumnInCache : R_DrawColumnInCache; - - wadnum = patch->wad; - lumpnum = patch->lump; - pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); - lumplength = W_LumpLengthPwad(wadnum, lumpnum); - realpatch = (softwarepatch_t *)pdata; - dealloc = true; - -#ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)realpatch, lumplength)) - realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength, NULL); - else -#endif -#ifdef WALLFLATS - if (texture->type == TEXTURETYPE_FLAT) - realpatch = R_FlatToPatch(pdata, texture->width, texture->height, 0, 0, NULL, false); - else -#endif - { - (void)lumplength; - dealloc = false; - } - - x1 = patch->originx; - width = SHORT(realpatch->width); - height = SHORT(realpatch->height); - x2 = x1 + width; - - if (x1 > texture->width || x2 < 0) - continue; // patch not located within texture's x bounds, ignore - - if (patch->originy > texture->height || (patch->originy + height) < 0) - continue; // patch not located within texture's y bounds, ignore - - // patch is actually inside the texture! - // now check if texture is partly off-screen and adjust accordingly - - // left edge - if (x1 < 0) - x = 0; - else - x = x1; - - // right edge - if (x2 > texture->width) - x2 = texture->width; - - for (; x < x2; x++) - { - if (patch->flip & 1) - patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[(x1+width-1)-x])); - else - patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[x-x1])); - - // generate column ofset lookup - *(UINT32 *)&colofs[x<<2] = LONG((x * texture->height) + (texture->width*4)); - ColumnDrawerPointer(patchcol, block + LONG(*(UINT32 *)&colofs[x<<2]), patch, texture->height, height); - } - - if (dealloc) - Z_Free(realpatch); - } - -done: - // Now that the texture has been built in column cache, it is purgable from zone memory. - Z_ChangeTag(block, PU_CACHE); - return blocktex; -} - -// -// 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]; -} - -// -// 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); -} - -// -// R_GetColumn -// -UINT8 *R_GetColumn(fixed_t tex, INT32 col) -{ - UINT8 *data; - INT32 width = texturewidth[tex]; - - if (width & (width - 1)) - col = (UINT32)col % width; - else - col &= (width - 1); - - data = texturecache[tex]; - 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" -int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum); -void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *index); - -#ifdef WALLFLATS -static INT32 -Rloadflats (INT32 i, INT32 w) -{ - UINT16 j; - UINT16 texstart, texend; - texture_t *texture; - texpatch_t *patch; - - // Yes - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); - } - else - { - texstart = W_CheckNumForMarkerStartPwad("F_START", (UINT16)w, 0); - texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); - } - - if (!( texstart == INT16_MAX || texend == INT16_MAX )) - { - // Work through each lump between the markers in the WAD. - for (j = 0; j < (texend - texstart); j++) - { - UINT8 *flatlump; - UINT16 wadnum = (UINT16)w; - lumpnum_t lumpnum = texstart + j; - size_t lumplength; - size_t flatsize = 0; - - if (wadfiles[w]->type == RET_PK3) - { - if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder - continue; // If it is then SKIP IT - } - - flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); - lumplength = W_LumpLengthPwad(wadnum, lumpnum); - - switch (lumplength) - { - case 4194304: // 2048x2048 lump - flatsize = 2048; - break; - case 1048576: // 1024x1024 lump - flatsize = 1024; - break; - case 262144:// 512x512 lump - flatsize = 512; - break; - case 65536: // 256x256 lump - flatsize = 256; - break; - case 16384: // 128x128 lump - flatsize = 128; - break; - case 1024: // 32x32 lump - flatsize = 32; - break; - default: // 64x64 lump - flatsize = 64; - break; - } - - //CONS_Printf("\n\"%s\" is a flat, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),flatsize,flatsize); - texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); - - // Set texture properties. - M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); - -#ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)flatlump, lumplength)) - { - INT16 width, height; - R_PNGDimensions((UINT8 *)flatlump, &width, &height, lumplength); - texture->width = width; - texture->height = height; - } - else -#endif - texture->width = texture->height = flatsize; - - texture->type = TEXTURETYPE_FLAT; - texture->patchcount = 1; - texture->holes = false; - texture->flip = 0; - - // Allocate information for the texture's patches. - patch = &texture->patches[0]; - - patch->originx = patch->originy = 0; - patch->wad = (UINT16)w; - patch->lump = texstart + j; - patch->flip = 0; - - Z_Unlock(flatlump); - - texturewidth[i] = texture->width; - textureheight[i] = texture->height << FRACBITS; - i++; - } - } - - return i; -} -#endif/*WALLFLATS*/ - -#define TX_START "TX_START" -#define TX_END "TX_END" - -static INT32 -Rloadtextures (INT32 i, INT32 w) -{ - UINT16 j; - UINT16 texstart, texend, texturesLumpPos; - texture_t *texture; - patch_t *patchlump; - texpatch_t *patch; - - // Get the lump numbers for the markers in the WAD, if they exist. - 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_CheckNumForMarkerStartPwad(TX_START, (UINT16)w, 0); - texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); - if (texturesLumpPos != INT16_MAX) - R_ParseTEXTURESLump(w, texturesLumpPos, &i); - } - - if (!( texstart == INT16_MAX || texend == INT16_MAX )) - { - // Work through each lump between the markers in the WAD. - for (j = 0; j < (texend - texstart); j++) - { - UINT16 wadnum = (UINT16)w; - lumpnum_t lumpnum = texstart + j; -#ifndef NO_PNG_LUMPS - size_t lumplength; -#endif - - if (wadfiles[w]->type == RET_PK3) - { - if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder - continue; // If it is then SKIP IT - } - - patchlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); -#ifndef NO_PNG_LUMPS - lumplength = W_LumpLengthPwad(wadnum, lumpnum); -#endif - - //CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height); - texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); - - // Set texture properties. - M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); - -#ifndef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)patchlump, lumplength)) - { - INT16 width, height; - R_PNGDimensions((UINT8 *)patchlump, &width, &height, lumplength); - texture->width = width; - texture->height = height; - } - else -#endif - { - texture->width = SHORT(patchlump->width); - texture->height = SHORT(patchlump->height); - } - - texture->type = TEXTURETYPE_SINGLEPATCH; - texture->patchcount = 1; - texture->holes = false; - texture->flip = 0; - - // Allocate information for the texture's patches. - patch = &texture->patches[0]; - - patch->originx = patch->originy = 0; - patch->wad = (UINT16)w; - patch->lump = texstart + j; - patch->flip = 0; - - Z_Unlock(patchlump); - - texturewidth[i] = texture->width; - textureheight[i] = texture->height << FRACBITS; - i++; - } - } - - return i; -} - -// -// R_LoadTextures -// Initializes the texture list with the textures from the world map. -// -void R_LoadTextures(void) -{ - INT32 i, w; - UINT16 j; - UINT16 texstart, texend, texturesLumpPos; - - // 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); - Z_Free(texflats); - } - - // 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++) - { -#ifdef WALLFLATS - // Count flats - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); - } - else - { - texstart = W_CheckNumForMarkerStartPwad("F_START", (UINT16)w, 0); - texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); - } - - if (!( texstart == INT16_MAX || texend == INT16_MAX )) - { - // PK3s have subfolders, so we can't just make a simple sum - if (wadfiles[w]->type == RET_PK3) - { - for (j = texstart; j < texend; j++) - { - if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it - numtextures++; - } - } - else // Add all the textures between F_START and F_END - { - numtextures += (UINT32)(texend - texstart); - } - } -#endif/*WALLFLATS*/ - - // Count the textures from TEXTURES lumps - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); - while (texturesLumpPos != INT16_MAX) - { - numtextures += R_CountTexturesInTEXTURESLump((UINT16)w, (UINT16)texturesLumpPos); - texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); - } - - // Count single-patch textures - if (wadfiles[w]->type == RET_PK3) - { - texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); - texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); - } - else - { - texstart = W_CheckNumForMarkerStartPwad(TX_START, (UINT16)w, 0); - texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); - } - - if (texstart == INT16_MAX || texend == INT16_MAX) - continue; - - // PK3s have subfolders, so we can't just make a simple sum - if (wadfiles[w]->type == RET_PK3) - { - for (j = texstart; j < texend; j++) - { - if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it - numtextures++; - } - } - else // Add all the textures between TX_START and TX_END - { - numtextures += (UINT32)(texend - texstart); - } - } - - // If no textures found by this point, bomb out - if (!numtextures) - 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); - texflats = Z_Calloc((numtextures * sizeof(*texflats)), PU_STATIC, NULL); - - // Allocate texture column offset table. - texturecolumnofs = (void *)((UINT8 *)textures + (numtextures * sizeof(void *))); - // Allocate texture referencing cache. - texturecache = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 2)); - // Allocate texture width table. - texturewidth = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 3)); - // Allocate texture height table. - 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++) - { -#ifdef WALLFLATS - i = Rloadflats(i, w); -#endif - i = Rloadtextures(i, w); - } - -#ifdef HWRENDER - if (rendermode == render_opengl) - HWR_LoadMapTextures(numtextures); -#endif -} - -static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch) -{ - char *texturesToken; - size_t texturesTokenLength; - char *endPos; - char *patchName = NULL; - INT16 patchXPos; - INT16 patchYPos; - UINT8 flip = 0; - UINT8 alpha = 255; - enum patchalphastyle style = AST_COPY; - 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); - - // Patch parameters block (OPTIONAL) - // added by Monster Iestyn (22/10/16) - - // Left Curly Brace - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - ; // move on and ignore, R_ParseTextures will deal with this - else - { - if (strcmp(texturesToken,"{")==0) - { - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters should be",patchName); - } - while (strcmp(texturesToken,"}")!=0) - { - if (stricmp(texturesToken, "ALPHA")==0) - { - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - alpha = 255*strtof(texturesToken, NULL); - } - else if (stricmp(texturesToken, "STYLE")==0) - { - Z_Free(texturesToken); - texturesToken = M_GetToken(NULL); - if (stricmp(texturesToken, "TRANSLUCENT")==0) - style = AST_TRANSLUCENT; - else if (stricmp(texturesToken, "ADD")==0) - style = AST_ADD; - else if (stricmp(texturesToken, "SUBTRACT")==0) - style = AST_SUBTRACT; - else if (stricmp(texturesToken, "REVERSESUBTRACT")==0) - style = AST_REVERSESUBTRACT; - else if (stricmp(texturesToken, "MODULATE")==0) - style = AST_MODULATE; - } - else if (stricmp(texturesToken, "FLIPX")==0) - flip |= 1; - else if (stricmp(texturesToken, "FLIPY")==0) - flip |= 2; - Z_Free(texturesToken); - - texturesToken = M_GetToken(NULL); - if (texturesToken == NULL) - { - I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters or right curly brace should be",patchName); - } - } - } - else - { - // this is not what we wanted... - // undo last read so R_ParseTextures can re-get the token for its own purposes - M_UnGetToken(); - } - 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; - resultPatch->flip = flip; - resultPatch->alpha = alpha; - resultPatch->style = style; - // 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; - size_t texturesTokenLength; - char *endPos; - INT32 newTextureWidth; - INT32 newTextureHeight; - texture_t *resultTexture = NULL; - texpatch_t *newPatch; - char newTextureName[9]; // no longer dynamically allocated - - // 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 - { - 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 - } - 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; - resultTexture->type = TEXTURETYPE_COMPOSITE; - } - 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. -int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum) -{ - 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. - texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); - // 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. - texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum); - 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) - { - if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) - { - numTexturesInLump++; - Z_Free(texturesToken); - R_ParseTexture(false); - } - else - { - I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); - } - texturesToken = M_GetToken(NULL); - } - Z_Free(texturesToken); - Z_Free((void *)texturesText); - - return numTexturesInLump; -} - -// Parses the TEXTURES lump... for real, this time. -void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex) -{ - 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. - texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); - // 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. - texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum); - 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) - { - if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) - { - Z_Free(texturesToken); - // Get the new texture - newTexture = R_ParseTexture(true); - // Store the new texture - textures[*texindex] = newTexture; - texturewidth[*texindex] = newTexture->width; - textureheight[*texindex] = newTexture->height << FRACBITS; - // Increment i back in R_LoadTextures() - (*texindex)++; - } - else - { - I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); - } - texturesToken = M_GetToken(NULL); - } - Z_Free(texturesToken); - Z_Free((void *)texturesText); -} - #ifdef EXTRACOLORMAPLUMPS static lumplist_t *colormaplumps = NULL; ///\todo free leak static size_t numcolormaplumps = 0; @@ -1607,54 +260,6 @@ static void R_InitExtraColormaps(void) } #endif -// Search for flat name. -lumpnum_t R_GetFlatNumForName(const char *name) -{ - 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_CheckNumForMarkerStartPwad("F_START", (UINT16)i, 0)) == INT16_MAX) - { - if ((start = W_CheckNumForMarkerStartPwad("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; - } - - return lump; -} - // // R_InitSpriteLumps // Finds the width and hoffset of all sprites in the wad, so the sprite does not need to be @@ -2619,74 +1224,6 @@ void R_InitData(void) 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 // diff --git a/src/r_data.h b/src/r_data.h index fda342083..aec52b54b 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -22,6 +22,14 @@ #pragma interface #endif +// Store lists of lumps for F_START/F_END etc. +typedef struct +{ + UINT16 wadfile; + UINT16 firstlump; + size_t numlumps; +} lumplist_t; + // Possible alpha types for a patch. enum patchalphastyle {AST_COPY, AST_TRANSLUCENT, AST_ADD, AST_SUBTRACT, AST_REVERSESUBTRACT, AST_MODULATE, AST_OVERLAY}; @@ -31,97 +39,17 @@ UINT8 ASTBlendPaletteIndexes(UINT8 background, UINT8 foreground, int style, UINT extern INT32 ASTTextureBlendingThreshold[2]; -UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b); - -// moved here for r_sky.c (texpatch_t is used) - -// A single patch from a texture definition, -// basically a rectangular area within -// the texture rectangle. -typedef struct -{ - // Block origin (always UL), which has already accounted for the internal origin of the patch. - INT16 originx, originy; - UINT16 wad, lump; - UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both - UINT8 alpha; // Translucency value - enum patchalphastyle style; -} texpatch_t; - -// texture type -enum -{ - TEXTURETYPE_UNKNOWN, - TEXTURETYPE_SINGLEPATCH, - TEXTURETYPE_COMPOSITE, -#ifdef WALLFLATS - TEXTURETYPE_FLAT, -#endif -}; - -// A maptexturedef_t describes a rectangular texture, -// which is composed of one or more mappatch_t structures -// that arrange graphic patches. -typedef struct -{ - // Keep name for switch changing, etc. - char name[8]; - UINT8 type; // TEXTURETYPE_ - INT16 width, height; - boolean holes; - UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both - - // All the patches[patchcount] are drawn back to front into the cached texture. - INT16 patchcount; - texpatch_t patches[0]; -} texture_t; - -typedef struct -{ - UINT8 *flat; - INT16 width, height; -} textureflat_t; - -// all loaded and prepared textures from the start of the game -extern texture_t **textures; -extern textureflat_t *texflats; - -extern INT32 *texturewidth; -extern fixed_t *textureheight; // needed for texture pegging - extern INT16 color8to16[256]; // remap color index to highcolor extern INT16 *hicolormaps; // remap high colors to high colors.. extern CV_PossibleValue_t Color_cons_t[]; -// Load TEXTURES definitions, create lookup tables -void R_LoadTextures(void); -void R_FlushTextureCache(void); - -INT32 R_GetTextureNum(INT32 texnum); -void R_CheckTextureCache(INT32 tex); - -// Retrieve column data for span blitting. -UINT8 *R_GetColumn(fixed_t tex, INT32 col); -UINT8 *R_GetFlat(lumpnum_t flatnum); - // I/O, setting up the stuff. void R_InitData(void); void R_PrecacheLevel(void); extern size_t flatmemory, spritememory, texturememory; -// Retrieval. -// Floor/ceiling opaque texture tiles, -// lookup by name. For animation? -lumpnum_t R_GetFlatNumForName(const char *name); - -// Called by P_Ticker for switches and animations, -// returns the texture number for the texture name. -void R_ClearTextureNumCache(boolean btell); -INT32 R_TextureNumForName(const char *name); -INT32 R_CheckTextureNumForName(const char *name); - // Extra Colormap lumps (C_START/C_END) are not used anywhere // Uncomment to enable //#define EXTRACOLORMAPLUMPS @@ -195,6 +123,4 @@ const char *R_NameForColormap(extracolormap_t *extra_colormap); UINT8 NearestPaletteColor(UINT8 r, UINT8 g, UINT8 b, RGBA_t *palette); #define NearestColor(r, g, b) NearestPaletteColor(r, g, b, NULL) -extern INT32 numtextures; - #endif diff --git a/src/r_local.h b/src/r_local.h index 48044118d..4ccb766cf 100644 --- a/src/r_local.h +++ b/src/r_local.h @@ -31,6 +31,7 @@ #include "r_plane.h" #include "r_sky.h" #include "r_data.h" +#include "r_textures.h" #include "r_things.h" #include "r_draw.h" diff --git a/src/r_main.h b/src/r_main.h index 6c0aec3f5..20d9524c8 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -16,6 +16,7 @@ #include "d_player.h" #include "r_data.h" +#include "r_textures.h" // // POV related. diff --git a/src/r_patch.c b/src/r_patch.c index 9a5364f9a..e70d0cb7c 100644 --- a/src/r_patch.c +++ b/src/r_patch.c @@ -1,9 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 1993-1996 by id Software, Inc. -// Copyright (C) 2005-2009 by Andrey "entryway" Budko. -// Copyright (C) 2018-2020 by Jaime "Lactozilla" Passos. -// Copyright (C) 2019-2020 by Sonic Team Junior. +// Copyright (C) 2020 by Jaime "Lactozilla" Passos. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -12,44 +9,15 @@ /// \file r_patch.c /// \brief Patch generation. -#include "byteptr.h" -#include "dehacked.h" -#include "i_video.h" -#include "r_data.h" -#include "r_draw.h" +#include "doomdef.h" #include "r_patch.h" -#include "r_things.h" +#include "r_defs.h" #include "z_zone.h" -#include "w_wad.h" #ifdef HWRENDER #include "hardware/hw_glob.h" #endif -#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 - -static unsigned char imgbuf[1<<26]; - // // Creates a patch. // Assumes a PU_PATCH zone memory tag and no user, but can always be set later @@ -141,1372 +109,3 @@ void *Patch_CreateGL(patch_t *patch) return grPatch; } #endif // HWRENDER - -// -// Patch_CheckIfDoom -// -// Returns true if the lump is a valid Doom patch. -// -boolean Patch_CheckIfDoom(softwarepatch_t *patch, size_t length) -{ - INT16 width, height; - boolean result; - - // minimum length of a valid Doom patch - if (length < 13) - return false; - - width = SHORT(patch->width); - height = SHORT(patch->height); - - result = (height > 0 && height <= 16384 && width > 0 && width <= 16384 && width < (INT16)(length / 4)); - - 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)length) - { - result = false; - break; - } - } - } - - return result; -} - -// -// R_TextureToFlat -// -// Convert a texture to a flat. -// -void R_TextureToFlat(size_t tex, UINT8 *flat) -{ - texture_t *texture = textures[tex]; - - fixed_t col, ofs; - column_t *column; - UINT8 *desttop, *dest, *deststop; - UINT8 *source; - - // yea - R_CheckTextureCache(tex); - - desttop = flat; - deststop = desttop + (texture->width * texture->height); - - for (col = 0; col < texture->width; col++, desttop++) - { - // no post_t info - if (!texture->holes) - { - column = (column_t *)(R_GetColumn(tex, col)); - source = (UINT8 *)(column); - dest = desttop; - for (ofs = 0; dest < deststop && ofs < texture->height; ofs++) - { - if (source[ofs] != TRANSPARENTPIXEL) - *dest = source[ofs]; - dest += texture->width; - } - } - else - { - INT32 topdelta, prevdelta = -1; - column = (column_t *)((UINT8 *)R_GetColumn(tex, col) - 3); - 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); - } - } - } -} - -// -// R_PatchToFlat -// -// Convert a patch to a flat. -// -void R_PatchToFlat(patch_t *patch, UINT8 *flat) -{ - fixed_t col, ofs; - column_t *column; - UINT8 *desttop, *dest, *deststop; - UINT8 *source; - - desttop = flat; - deststop = desttop + (SHORT(patch->width) * SHORT(patch->height)); - - for (col = 0; col < SHORT(patch->width); col++, desttop++) - { - INT32 topdelta, prevdelta = -1; - column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[col])); - - while (column->topdelta != 0xff) - { - topdelta = column->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - - dest = desttop + (topdelta * SHORT(patch->width)); - source = (UINT8 *)(column) + 3; - for (ofs = 0; dest < deststop && ofs < column->length; ofs++) - { - *dest = source[ofs]; - dest += SHORT(patch->width); - } - column = (column_t *)((UINT8 *)column + column->length + 4); - } - } -} - -// -// R_PatchToMaskedFlat -// -// Convert a patch to a masked flat. -// Now, what is a "masked" flat anyway? -// It means the flat uses two bytes to store image data. -// The upper byte is used to store the transparent pixel, -// and the lower byte stores a palette index. -// -void R_PatchToMaskedFlat(patch_t *patch, UINT16 *raw, boolean flip) -{ - fixed_t col, ofs; - column_t *column; - UINT16 *desttop, *dest, *deststop; - UINT8 *source; - - desttop = raw; - deststop = desttop + (SHORT(patch->width) * SHORT(patch->height)); - - for (col = 0; col < SHORT(patch->width); col++, desttop++) - { - INT32 topdelta, prevdelta = -1; - column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[flip ? (patch->width-1-col) : col])); - while (column->topdelta != 0xff) - { - topdelta = column->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - dest = desttop + (topdelta * SHORT(patch->width)); - source = (UINT8 *)(column) + 3; - for (ofs = 0; dest < deststop && ofs < column->length; ofs++) - { - *dest = source[ofs]; - dest += SHORT(patch->width); - } - column = (column_t *)((UINT8 *)column + column->length + 4); - } - } -} - -// -// R_FlatToPatch -// -// Convert a flat to a patch. -// -softwarepatch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize, boolean transparency) -{ - UINT32 x, y; - UINT8 *img; - UINT8 *imgptr = imgbuf; - UINT8 *colpointers, *startofspan; - size_t size = 0; - - if (!raw) - return NULL; - - // Write image size and offset - WRITEINT16(imgptr, width); - WRITEINT16(imgptr, height); - WRITEINT16(imgptr, leftoffset); - WRITEINT16(imgptr, topoffset); - - // 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; - - // Write column pointer - WRITEINT32(colpointers, imgptr - imgbuf); - - // Write pixels - for (y = 0; y < height; y++) - { - UINT8 paletteIndex = raw[((y * width) + x)]; - boolean opaque = transparency ? (paletteIndex != TRANSPARENTPIXEL) : true; - - // End span if we have a transparent pixel - if (!opaque) - { - if (startofspan) - WRITEUINT8(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) - WRITEUINT8(imgptr, 0); - - if (y > 254) - { - // Make sure we're aligned to 254 - if (lastStartY < 254) - { - WRITEUINT8(imgptr, 254); - WRITEUINT8(imgptr, 0); - imgptr += 2; - lastStartY = 254; - } - - // Write stopgap empty spans if needed - writeY = y - lastStartY; - - while (writeY > 254) - { - WRITEUINT8(imgptr, 254); - WRITEUINT8(imgptr, 0); - imgptr += 2; - writeY -= 254; - } - } - - startofspan = imgptr; - WRITEUINT8(imgptr, writeY); - imgptr += 2; - spanSize = 0; - - lastStartY = y; - } - - // Write the pixel - WRITEUINT8(imgptr, paletteIndex); - spanSize++; - startofspan[1] = spanSize; - } - - if (startofspan) - WRITEUINT8(imgptr, 0); - - WRITEUINT8(imgptr, 0xFF); - } - - size = imgptr-imgbuf; - img = Z_Malloc(size, PU_STATIC, NULL); - memcpy(img, imgbuf, size); - - Z_Free(raw); - - if (destsize != NULL) - *destsize = size; - return (softwarepatch_t *)img; -} - -// -// R_MaskedFlatToPatch -// -// Convert a masked flat to a patch. -// Explanation of "masked" flats in R_PatchToMaskedFlat. -// -softwarepatch_t *R_MaskedFlatToPatch(UINT16 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize) -{ - UINT32 x, y; - UINT8 *img; - UINT8 *imgptr = imgbuf; - UINT8 *colpointers, *startofspan; - size_t size = 0; - - if (!raw) - return NULL; - - // Write image size and offset - WRITEINT16(imgptr, width); - WRITEINT16(imgptr, height); - WRITEINT16(imgptr, leftoffset); - WRITEINT16(imgptr, topoffset); - - // 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; - - // Write column pointer - WRITEINT32(colpointers, imgptr - imgbuf); - - // Write pixels - for (y = 0; y < height; y++) - { - UINT16 pixel = raw[((y * width) + x)]; - UINT8 paletteIndex = (pixel & 0xFF); - UINT8 opaque = (pixel != 0xFF00); // If 1, we have a pixel - - // End span if we have a transparent pixel - if (!opaque) - { - if (startofspan) - WRITEUINT8(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) - WRITEUINT8(imgptr, 0); - - if (y > 254) - { - // Make sure we're aligned to 254 - if (lastStartY < 254) - { - WRITEUINT8(imgptr, 254); - WRITEUINT8(imgptr, 0); - imgptr += 2; - lastStartY = 254; - } - - // Write stopgap empty spans if needed - writeY = y - lastStartY; - - while (writeY > 254) - { - WRITEUINT8(imgptr, 254); - WRITEUINT8(imgptr, 0); - imgptr += 2; - writeY -= 254; - } - } - - startofspan = imgptr; - WRITEUINT8(imgptr, writeY); - imgptr += 2; - spanSize = 0; - - lastStartY = y; - } - - // Write the pixel - WRITEUINT8(imgptr, paletteIndex); - spanSize++; - startofspan[1] = spanSize; - } - - if (startofspan) - WRITEUINT8(imgptr, 0); - - WRITEUINT8(imgptr, 0xFF); - } - - size = imgptr-imgbuf; - img = Z_Malloc(size, PU_STATIC, NULL); - memcpy(img, imgbuf, size); - - if (destsize != NULL) - *destsize = size; - return (softwarepatch_t *)img; -} - -// -// R_IsLumpPNG -// -// Returns true if the lump is a valid PNG. -// -boolean R_IsLumpPNG(const 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); -} - -#ifndef NO_PNG_LUMPS -#ifdef HAVE_PNG - -/*#if PNG_LIBPNG_VER_DLLNUM < 14 -typedef PNG_CONST png_byte *png_const_bytep; -#endif*/ -typedef struct -{ - const UINT8 *buffer; - UINT32 size; - UINT32 position; -} png_io_t; - -static void PNG_IOReader(png_structp png_ptr, png_bytep data, png_size_t length) -{ - png_io_t *f = png_get_io_ptr(png_ptr); - if (length > (f->size - f->position)) - png_error(png_ptr, "PNG_IOReader: buffer overrun"); - memcpy(data, f->buffer + f->position, length); - f->position += length; -} - -typedef struct -{ - char name[4]; - void *data; - size_t size; -} png_chunk_t; - -static png_byte *chunkname = NULL; -static png_chunk_t chunk; - -static int PNG_ChunkReader(png_structp png_ptr, png_unknown_chunkp chonk) -{ - (void)png_ptr; - if (!memcmp(chonk->name, chunkname, 4)) - { - memcpy(chunk.name, chonk->name, 4); - chunk.size = chonk->size; - chunk.data = Z_Malloc(chunk.size, PU_STATIC, NULL); - memcpy(chunk.data, chonk->data, chunk.size); - return 1; - } - return 0; -} - -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(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, 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_io_t png_io; - png_bytep *row_pointers; - - png_byte grAb_chunk[5] = {'g', 'r', 'A', 'b', (png_byte)'\0'}; - png_voidp *user_chunk_ptr; - - 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 - - // set our own read function - png_io.buffer = png; - png_io.size = size; - png_io.position = 0; - png_set_read_fn(png_ptr, &png_io, PNG_IOReader); - - memset(&chunk, 0x00, sizeof(png_chunk_t)); - chunkname = grAb_chunk; // I want to read a grAb chunk - - user_chunk_ptr = png_get_user_chunk_ptr(png_ptr); - png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, PNG_ChunkReader); - png_set_keep_unknown_chunks(png_ptr, 2, chunkname, 1); - -#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); - - // Read grAB chunk - if ((topoffset || leftoffset) && (chunk.data != NULL)) - { - INT32 *offsets = (INT32 *)chunk.data; - // read left offset - if (leftoffset != NULL) - *leftoffset = (INT16)BIGENDIAN_LONG(*offsets); - offsets++; - // read top offset - if (topoffset != NULL) - *topoffset = (INT16)BIGENDIAN_LONG(*offsets); - } - - // bye - png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); - if (chunk.data) - Z_Free(chunk.data); - - *w = (INT32)width; - *h = (INT32)height; - return row_pointers; -} - -// Convert a PNG to a raw image. -static UINT8 *PNG_RawConvert(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) -{ - UINT8 *flat; - png_uint_32 x, y; - png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, size); - png_uint_32 width = *w, height = *h; - - if (!row_pointers) - I_Error("PNG_RawConvert: conversion failed"); - - // Convert the image to 8bpp - flat = Z_Malloc(width * height, PU_LEVEL, 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]); - if ((UINT8)px[3]) - flat[((y * width) + x)] = NearestColor((UINT8)px[0], (UINT8)px[1], (UINT8)px[2]); - } - } - free(row_pointers); - - return flat; -} - -// Convert a PNG with transparency to a raw image. -static UINT16 *PNG_MaskedRawConvert(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) -{ - UINT16 *flat; - png_uint_32 x, y; - png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, size); - png_uint_32 width = *w, height = *h; - size_t flatsize, i; - - if (!row_pointers) - I_Error("PNG_MaskedRawConvert: conversion failed"); - - // Convert the image to 16bpp - flatsize = (width * height); - flat = Z_Malloc(flatsize * sizeof(UINT16), PU_LEVEL, NULL); - - // can't memset here - for (i = 0; i < flatsize; i++) - flat[i] = 0xFF00; - - for (y = 0; y < height; y++) - { - png_bytep row = row_pointers[y]; - for (x = 0; x < width; x++) - { - png_bytep px = &(row[x * 4]); - if ((UINT8)px[3]) - flat[((y * width) + x)] = NearestColor((UINT8)px[0], (UINT8)px[1], (UINT8)px[2]); - } - } - free(row_pointers); - - return flat; -} - -// -// R_PNGToFlat -// -// Convert a PNG to a flat. -// -UINT8 *R_PNGToFlat(UINT16 *width, UINT16 *height, UINT8 *png, size_t size) -{ - return PNG_RawConvert(png, width, height, NULL, NULL, size); -} - -// -// R_PNGToPatch -// -// Convert a PNG to a patch. -// -softwarepatch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize) -{ - UINT16 width, height; - INT16 topoffset = 0, leftoffset = 0; - UINT16 *raw = PNG_MaskedRawConvert(png, &width, &height, &topoffset, &leftoffset, size); - - if (!raw) - I_Error("R_PNGToPatch: conversion failed"); - - return R_MaskedFlatToPatch(raw, width, height, leftoffset, topoffset, destsize); -} - -// -// R_PNGDimensions -// -// Get the dimensions of a PNG file. -// -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_io_t 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 - - // set our own read function - png_io.buffer = png; - png_io.size = size; - png_io.position = 0; - 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 - -// -// R_ParseSpriteInfoFrame -// -// Parse a SPRTINFO frame. -// -static void R_ParseSpriteInfoFrame(spriteinfo_t *info) -{ - char *sprinfoToken; - size_t sprinfoTokenLength; - char *frameChar = NULL; - UINT8 frameFrame = 0xFF; - INT16 frameXPivot = 0; - INT16 frameYPivot = 0; - rotaxis_t frameRotAxis = 0; - - // Sprite identifier - sprinfoToken = M_GetToken(NULL); - if (sprinfoToken == NULL) - { - I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite frame should be"); - } - sprinfoTokenLength = strlen(sprinfoToken); - if (sprinfoTokenLength != 1) - { - I_Error("Error parsing SPRTINFO lump: Invalid frame \"%s\"",sprinfoToken); - } - else - frameChar = sprinfoToken; - - frameFrame = R_Char2Frame(frameChar[0]); - Z_Free(sprinfoToken); - - // Left Curly Brace - sprinfoToken = M_GetToken(NULL); - if (sprinfoToken == NULL) - I_Error("Error parsing SPRTINFO lump: Missing sprite info"); - else - { - if (strcmp(sprinfoToken,"{")==0) - { - Z_Free(sprinfoToken); - sprinfoToken = M_GetToken(NULL); - if (sprinfoToken == NULL) - { - I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite info should be"); - } - while (strcmp(sprinfoToken,"}")!=0) - { - if (stricmp(sprinfoToken, "XPIVOT")==0) - { - Z_Free(sprinfoToken); - sprinfoToken = M_GetToken(NULL); - frameXPivot = atoi(sprinfoToken); - } - else if (stricmp(sprinfoToken, "YPIVOT")==0) - { - Z_Free(sprinfoToken); - sprinfoToken = M_GetToken(NULL); - frameYPivot = atoi(sprinfoToken); - } - else if (stricmp(sprinfoToken, "ROTAXIS")==0) - { - Z_Free(sprinfoToken); - sprinfoToken = M_GetToken(NULL); - if ((stricmp(sprinfoToken, "X")==0) || (stricmp(sprinfoToken, "XAXIS")==0) || (stricmp(sprinfoToken, "ROLL")==0)) - frameRotAxis = ROTAXIS_X; - else if ((stricmp(sprinfoToken, "Y")==0) || (stricmp(sprinfoToken, "YAXIS")==0) || (stricmp(sprinfoToken, "PITCH")==0)) - frameRotAxis = ROTAXIS_Y; - else if ((stricmp(sprinfoToken, "Z")==0) || (stricmp(sprinfoToken, "ZAXIS")==0) || (stricmp(sprinfoToken, "YAW")==0)) - frameRotAxis = ROTAXIS_Z; - } - Z_Free(sprinfoToken); - - sprinfoToken = M_GetToken(NULL); - if (sprinfoToken == NULL) - { - I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite info or right curly brace should be"); - } - } - } - Z_Free(sprinfoToken); - } - - // set fields - info->pivot[frameFrame].x = frameXPivot; - info->pivot[frameFrame].y = frameYPivot; - info->pivot[frameFrame].rotaxis = frameRotAxis; -} - -// -// R_ParseSpriteInfo -// -// Parse a SPRTINFO lump. -// -static void R_ParseSpriteInfo(boolean spr2) -{ - spriteinfo_t *info; - char *sprinfoToken; - size_t sprinfoTokenLength; - char newSpriteName[5]; // no longer dynamically allocated - spritenum_t sprnum = NUMSPRITES; - playersprite_t spr2num = NUMPLAYERSPRITES; - INT32 i; - INT32 skinnumbers[MAXSKINS]; - INT32 foundskins = 0; - - // Sprite name - sprinfoToken = M_GetToken(NULL); - if (sprinfoToken == NULL) - { - I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite name should be"); - } - sprinfoTokenLength = strlen(sprinfoToken); - if (sprinfoTokenLength != 4) - { - I_Error("Error parsing SPRTINFO lump: Sprite name \"%s\" isn't 4 characters long",sprinfoToken); - } - else - { - memset(&newSpriteName, 0, 5); - M_Memcpy(newSpriteName, sprinfoToken, sprinfoTokenLength); - // ^^ we've confirmed that the token is == 4 characters so it will never overflow a 5 byte char buffer - strupr(newSpriteName); // Just do this now so we don't have to worry about it - } - Z_Free(sprinfoToken); - - if (!spr2) - { - for (i = 0; i <= NUMSPRITES; i++) - { - if (i == NUMSPRITES) - I_Error("Error parsing SPRTINFO lump: Unknown sprite name \"%s\"", newSpriteName); - if (!memcmp(newSpriteName,sprnames[i],4)) - { - sprnum = i; - break; - } - } - } - else - { - for (i = 0; i <= NUMPLAYERSPRITES; i++) - { - if (i == NUMPLAYERSPRITES) - I_Error("Error parsing SPRTINFO lump: Unknown sprite2 name \"%s\"", newSpriteName); - if (!memcmp(newSpriteName,spr2names[i],4)) - { - spr2num = i; - break; - } - } - } - - // allocate a spriteinfo - info = Z_Calloc(sizeof(spriteinfo_t), PU_STATIC, NULL); - info->available = true; - -#ifdef ROTSPRITE - if ((sprites != NULL) && (!spr2)) - R_FreeRotSprite(&sprites[sprnum]); -#endif - - // Left Curly Brace - sprinfoToken = M_GetToken(NULL); - if (sprinfoToken == NULL) - { - I_Error("Error parsing SPRTINFO lump: Unexpected end of file where open curly brace for sprite \"%s\" should be",newSpriteName); - } - if (strcmp(sprinfoToken,"{")==0) - { - Z_Free(sprinfoToken); - sprinfoToken = M_GetToken(NULL); - if (sprinfoToken == NULL) - { - I_Error("Error parsing SPRTINFO lump: Unexpected end of file where definition for sprite \"%s\" should be",newSpriteName); - } - while (strcmp(sprinfoToken,"}")!=0) - { - if (stricmp(sprinfoToken, "SKIN")==0) - { - INT32 skinnum; - char *skinName = NULL; - if (!spr2) - I_Error("Error parsing SPRTINFO lump: \"SKIN\" token found outside of a sprite2 definition"); - - Z_Free(sprinfoToken); - - // Skin name - sprinfoToken = M_GetToken(NULL); - if (sprinfoToken == NULL) - { - I_Error("Error parsing SPRTINFO lump: Unexpected end of file where skin frame should be"); - } - - // copy skin name yada yada - sprinfoTokenLength = strlen(sprinfoToken); - skinName = (char *)Z_Malloc((sprinfoTokenLength+1)*sizeof(char),PU_STATIC,NULL); - M_Memcpy(skinName,sprinfoToken,sprinfoTokenLength*sizeof(char)); - skinName[sprinfoTokenLength] = '\0'; - strlwr(skinName); - Z_Free(sprinfoToken); - - skinnum = R_SkinAvailable(skinName); - if (skinnum == -1) - I_Error("Error parsing SPRTINFO lump: Unknown skin \"%s\"", skinName); - - skinnumbers[foundskins] = skinnum; - foundskins++; - } - else if (stricmp(sprinfoToken, "FRAME")==0) - { - R_ParseSpriteInfoFrame(info); - Z_Free(sprinfoToken); - if (spr2) - { - if (!foundskins) - I_Error("Error parsing SPRTINFO lump: No skins specified in this sprite2 definition"); - for (i = 0; i < foundskins; i++) - { - size_t skinnum = skinnumbers[i]; - skin_t *skin = &skins[skinnum]; - spriteinfo_t *sprinfo = skin->sprinfo; -#ifdef ROTSPRITE - R_FreeSkinRotSprite(skinnum); -#endif - M_Memcpy(&sprinfo[spr2num], info, sizeof(spriteinfo_t)); - } - } - else - M_Memcpy(&spriteinfo[sprnum], info, sizeof(spriteinfo_t)); - } - else - { - I_Error("Error parsing SPRTINFO lump: Unknown keyword \"%s\" in sprite %s",sprinfoToken,newSpriteName); - } - - sprinfoToken = M_GetToken(NULL); - if (sprinfoToken == NULL) - { - I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite info or right curly brace for sprite \"%s\" should be",newSpriteName); - } - } - } - else - { - I_Error("Error parsing SPRTINFO lump: Expected \"{\" for sprite \"%s\", got \"%s\"",newSpriteName,sprinfoToken); - } - Z_Free(sprinfoToken); - Z_Free(info); -} - -// -// R_ParseSPRTINFOLump -// -// Read a SPRTINFO lump. -// -void R_ParseSPRTINFOLump(UINT16 wadNum, UINT16 lumpNum) -{ - char *sprinfoLump; - size_t sprinfoLumpLength; - char *sprinfoText; - char *sprinfoToken; - - // 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. - sprinfoLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); - // If that didn't exist, we have nothing to do here. - if (sprinfoLump == NULL) return; - // If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly. - sprinfoLumpLength = W_LumpLengthPwad(wadNum, lumpNum); - sprinfoText = (char *)Z_Malloc((sprinfoLumpLength+1)*sizeof(char),PU_STATIC,NULL); - // Now move the contents of the lump into this new location. - memmove(sprinfoText,sprinfoLump,sprinfoLumpLength); - // Make damn well sure the last character in our new memory location is \0. - sprinfoText[sprinfoLumpLength] = '\0'; - // Finally, free up the memory from the first data load, because we really - // don't need it. - Z_Free(sprinfoLump); - - sprinfoToken = M_GetToken(sprinfoText); - while (sprinfoToken != NULL) - { - if (!stricmp(sprinfoToken, "SPRITE")) - R_ParseSpriteInfo(false); - else if (!stricmp(sprinfoToken, "SPRITE2")) - R_ParseSpriteInfo(true); - else - I_Error("Error parsing SPRTINFO lump: Unknown keyword \"%s\"", sprinfoToken); - Z_Free(sprinfoToken); - sprinfoToken = M_GetToken(NULL); - } - Z_Free((void *)sprinfoText); -} - -// -// R_LoadSpriteInfoLumps -// -// Load and read every SPRTINFO lump from the specified file. -// -void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps) -{ - lumpinfo_t *lumpinfo = wadfiles[wadnum]->lumpinfo; - UINT16 i; - char *name; - - for (i = 0; i < numlumps; i++, lumpinfo++) - { - name = lumpinfo->name; - // Load SPRTINFO and SPR_ lumps as SpriteInfo - if (!memcmp(name, "SPRTINFO", 8) || !memcmp(name, "SPR_", 4)) - R_ParseSPRTINFOLump(wadnum, i); - } -} - -static UINT16 GetPatchPixel(patch_t *patch, INT32 x, INT32 y, boolean flip) -{ - fixed_t ofs; - column_t *column; - UINT8 *source; - - if (x >= 0 && x < patch->width) - { - INT32 topdelta, prevdelta = -1; - column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[flip ? (patch->width-1-x) : x])); - while (column->topdelta != 0xff) - { - topdelta = column->topdelta; - if (topdelta <= prevdelta) - topdelta += prevdelta; - prevdelta = topdelta; - source = (UINT8 *)(column) + 3; - for (ofs = 0; ofs < column->length; ofs++) - { - if ((topdelta + ofs) == y) - return source[ofs]; - } - column = (column_t *)((UINT8 *)column + column->length + 4); - } - } - - return 0xFF00; -} - -#ifdef ROTSPRITE -// -// R_GetRollAngle -// -// Angles precalculated in R_InitSprites. -// -fixed_t rollcosang[ROTANGLES]; -fixed_t rollsinang[ROTANGLES]; -INT32 R_GetRollAngle(angle_t rollangle) -{ - INT32 ra = AngleFixed(rollangle)>>FRACBITS; -#if (ROTANGDIFF > 1) - ra += (ROTANGDIFF/2); -#endif - ra /= ROTANGDIFF; - ra %= ROTANGLES; - return ra; -} - -// -// R_CacheRotSprite -// -// Create a rotated sprite. -// -void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, spriteframe_t *sprframe, INT32 rot, UINT8 flip) -{ - UINT32 i; - INT32 angle; - patch_t *patch, *newpatch; - softwarepatch_t *swpatch; - UINT16 *rawdst; - size_t size; - INT32 bflip = (flip != 0x00); - -#define SPRITE_XCENTER (leftoffset) -#define SPRITE_YCENTER (height / 2) -#define ROTSPRITE_XCENTER (newwidth / 2) -#define ROTSPRITE_YCENTER (newheight / 2) - - if (!(sprframe->rotsprite.cached & (1<lumppat[rot]; - - if (lump == LUMPERROR) - return; - - patch = (patch_t *)W_CachePatchNum(lump, PU_STATIC); - width = patch->width; - height = patch->height; - leftoffset = patch->leftoffset; - - // rotation pivot - px = SPRITE_XCENTER; - py = SPRITE_YCENTER; - - // get correct sprite info for sprite - if (sprinfo == NULL) - sprinfo = &spriteinfo[sprnum]; - if (sprinfo->available) - { - px = sprinfo->pivot[frame].x; - py = sprinfo->pivot[frame].y; - } - if (bflip) - { - px = width - px; - leftoffset = width - leftoffset; - } - - // Don't cache angle = 0 - for (angle = 1; angle < ROTANGLES; angle++) - { - INT32 newwidth, newheight; - - ca = rollcosang[angle]; - sa = rollsinang[angle]; - - // Find the dimensions of the rotated patch. - { - INT32 w1 = abs(FixedMul(width << FRACBITS, ca) - FixedMul(height << FRACBITS, sa)); - INT32 w2 = abs(FixedMul(-(width << FRACBITS), ca) - FixedMul(height << FRACBITS, sa)); - INT32 h1 = abs(FixedMul(width << FRACBITS, sa) + FixedMul(height << FRACBITS, ca)); - INT32 h2 = abs(FixedMul(-(width << FRACBITS), sa) + FixedMul(height << FRACBITS, ca)); - w1 = FixedInt(FixedCeil(w1 + (FRACUNIT/2))); - w2 = FixedInt(FixedCeil(w2 + (FRACUNIT/2))); - h1 = FixedInt(FixedCeil(h1 + (FRACUNIT/2))); - h2 = FixedInt(FixedCeil(h2 + (FRACUNIT/2))); - newwidth = max(width, max(w1, w2)); - newheight = max(height, max(h1, h2)); - } - - // check boundaries - { - fixed_t top[2][2]; - fixed_t bottom[2][2]; - - top[0][0] = FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, sa) + (px << FRACBITS); - top[0][1] = FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, ca) + (py << FRACBITS); - top[1][0] = FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, sa) + (px << FRACBITS); - top[1][1] = FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, ca) + (py << FRACBITS); - - bottom[0][0] = FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, sa) + (px << FRACBITS); - bottom[0][1] = -FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, ca) + (py << FRACBITS); - bottom[1][0] = FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, sa) + (px << FRACBITS); - bottom[1][1] = -FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, ca) + (py << FRACBITS); - - top[0][0] >>= FRACBITS; - top[0][1] >>= FRACBITS; - top[1][0] >>= FRACBITS; - top[1][1] >>= FRACBITS; - - bottom[0][0] >>= FRACBITS; - bottom[0][1] >>= FRACBITS; - bottom[1][0] >>= FRACBITS; - bottom[1][1] >>= FRACBITS; - -#define BOUNDARYWCHECK(b) (b[0] < 0 || b[0] >= width) -#define BOUNDARYHCHECK(b) (b[1] < 0 || b[1] >= height) -#define BOUNDARYADJUST(x) x *= 2 - // top left/right - if (BOUNDARYWCHECK(top[0]) || BOUNDARYWCHECK(top[1])) - BOUNDARYADJUST(newwidth); - // bottom left/right - else if (BOUNDARYWCHECK(bottom[0]) || BOUNDARYWCHECK(bottom[1])) - BOUNDARYADJUST(newwidth); - // top left/right - if (BOUNDARYHCHECK(top[0]) || BOUNDARYHCHECK(top[1])) - BOUNDARYADJUST(newheight); - // bottom left/right - else if (BOUNDARYHCHECK(bottom[0]) || BOUNDARYHCHECK(bottom[1])) - BOUNDARYADJUST(newheight); -#undef BOUNDARYWCHECK -#undef BOUNDARYHCHECK -#undef BOUNDARYADJUST - } - - // Draw the rotated sprite to a temporary buffer. - size = (newwidth * newheight); - if (!size) - size = (width * height); - - rawdst = Z_Malloc(size * sizeof(UINT16), PU_STATIC, NULL); - for (i = 0; i < size; i++) - rawdst[i] = 0xFF00; - - for (dy = 0; dy < newheight; dy++) - { - for (dx = 0; dx < newwidth; dx++) - { - INT32 x = (dx-ROTSPRITE_XCENTER) << FRACBITS; - INT32 y = (dy-ROTSPRITE_YCENTER) << FRACBITS; - INT32 sx = FixedMul(x, ca) + FixedMul(y, sa) + (px << FRACBITS); - INT32 sy = -FixedMul(x, sa) + FixedMul(y, ca) + (py << FRACBITS); - sx >>= FRACBITS; - sy >>= FRACBITS; - if (sx >= 0 && sy >= 0 && sx < width && sy < height) - rawdst[(dy*newwidth)+dx] = GetPatchPixel(patch, sx, sy, bflip); - } - } - - // make patch - swpatch = R_MaskedFlatToPatch(rawdst, newwidth, newheight, 0, 0, &size); - { - swpatch->leftoffset = (swpatch->width / 2) + (leftoffset - px); - swpatch->topoffset = (swpatch->height / 2) + (patch->topoffset - py); - } - - //BP: we cannot use special tric in hardware mode because feet in ground caused by z-buffer - if (rendermode != render_none) // not for psprite - swpatch->topoffset += FEETADJUST>>FRACBITS; - - // P_PrecacheLevel - if (devparm) spritememory += size; - - // convert everything to little-endian, for big-endian support - swpatch->width = SHORT(swpatch->width); - swpatch->height = SHORT(swpatch->height); - swpatch->leftoffset = SHORT(swpatch->leftoffset); - swpatch->topoffset = SHORT(swpatch->topoffset); - - newpatch = Patch_Create(swpatch, size, NULL); - -#ifdef HWRENDER - if (rendermode == render_opengl) - Patch_CreateGL(newpatch); -#endif - - sprframe->rotsprite.patch[rot][angle] = newpatch; - - // free rotated image data - Z_Free(rawdst); - } - - // This rotation is cached now - sprframe->rotsprite.cached |= (1<numframes; frame++) - { - spriteframe_t *sprframe = &spritedef->spriteframes[frame]; - for (rot = 0; rot < 16; rot++) - { - if (sprframe->rotsprite.cached & (1<rotsprite.patch[rot][ang]; - if (rotsprite) - Patch_Free(rotsprite); - rotsprite = NULL; - } - sprframe->rotsprite.cached &= ~(1<sprites; - for (i = 0; i < NUMPLAYERSPRITES*2; i++) - { - R_FreeRotSprite(skinsprites); - skinsprites++; - } -} -#endif diff --git a/src/r_patch.h b/src/r_patch.h index 9fe5fde72..2caa8d0e4 100644 --- a/src/r_patch.h +++ b/src/r_patch.h @@ -1,8 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 1993-1996 by id Software, Inc. -// Copyright (C) 2018-2020 by Jaime "Lactozilla" Passos. -// Copyright (C) 2019-2020 by Sonic Team Junior. +// Copyright (C) 2020 by Jaime "Lactozilla" Passos. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -17,26 +15,6 @@ #include "r_defs.h" #include "doomdef.h" -// Structs -typedef enum -{ - ROTAXIS_X, // Roll (the default) - ROTAXIS_Y, // Pitch - ROTAXIS_Z // Yaw -} rotaxis_t; - -typedef struct -{ - INT32 x, y; - rotaxis_t rotaxis; -} spriteframepivot_t; - -typedef struct -{ - spriteframepivot_t pivot[64]; - boolean available; -} spriteinfo_t; - // Patch functions patch_t *Patch_Create(softwarepatch_t *source, size_t srcsize, void *dest); void Patch_Free(patch_t *patch); @@ -46,38 +24,4 @@ void *Patch_AllocateHardwarePatch(patch_t *patch); void *Patch_CreateGL(patch_t *patch); #endif -boolean Patch_CheckIfDoom(softwarepatch_t *patch, size_t length); - -// Conversions between patches / flats / textures... -void R_TextureToFlat(size_t tex, UINT8 *flat); -void R_PatchToFlat(patch_t *patch, UINT8 *flat); -void R_PatchToMaskedFlat(patch_t *patch, UINT16 *raw, boolean flip); -softwarepatch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize, boolean transparency); -softwarepatch_t *R_MaskedFlatToPatch(UINT16 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize); - -// PNGs -boolean R_IsLumpPNG(const UINT8 *d, size_t s); -#define W_ThrowPNGError(lumpname, wadfilename) I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .png - please convert to either Doom or Flat (raw) image format.", lumpname, wadfilename); // Fears Of LJ Sonic - -#ifndef NO_PNG_LUMPS -UINT8 *R_PNGToFlat(UINT16 *width, UINT16 *height, UINT8 *png, size_t size); -softwarepatch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize); -boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size); -#endif - -// SpriteInfo -extern spriteinfo_t spriteinfo[NUMSPRITES]; -void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps); -void R_ParseSPRTINFOLump(UINT16 wadNum, UINT16 lumpNum); - -// Sprite rotation -#ifdef ROTSPRITE -INT32 R_GetRollAngle(angle_t rollangle); -void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, spriteframe_t *sprframe, INT32 rot, UINT8 flip); -void R_FreeRotSprite(spritedef_t *spritedef); -void R_FreeSkinRotSprite(size_t skinnum); -extern fixed_t rollcosang[ROTANGLES]; -extern fixed_t rollsinang[ROTANGLES]; -#endif - #endif // __R_PATCH__ diff --git a/src/r_picformats.c b/src/r_picformats.c new file mode 100644 index 000000000..e351466c9 --- /dev/null +++ b/src/r_picformats.c @@ -0,0 +1,1693 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 2005-2009 by Andrey "entryway" Budko. +// Copyright (C) 2018-2020 by Jaime "Lactozilla" Passos. +// Copyright (C) 2019-2020 by Sonic Team Junior. +// +// 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_picformats.c +/// \brief Picture generation. + +#include "byteptr.h" +#include "dehacked.h" +#include "i_video.h" +#include "r_data.h" +#include "r_patch.h" +#include "r_textures.h" +#include "r_draw.h" +#include "r_patch.h" +#include "r_picformats.h" +#include "r_things.h" +#include "v_video.h" +#include "z_zone.h" +#include "w_wad.h" + +#ifdef HWRENDER +#include "hardware/hw_glob.h" +#endif + +#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 + +static unsigned char imgbuf[1<<26]; + +/** Converts a picture between two formats. + * + * \param informat Input picture format. + * \param picture Input picture data. + * \param outformat Output picture format. + * \param insize Input picture size. + * \param outsize Output picture size, as a pointer. + * \param inwidth Input picture width. + * \param inheight Input picture height. + * \param inleftoffset Input picture left offset, for patches. + * \param intopoffset Input picture top offset, for patches. + * \param flags Input picture flags. + * \return A pointer to the converted picture. + * \sa Picture_PatchConvert + * \sa Picture_FlatConvert + */ +void *Picture_Convert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT32 inwidth, INT32 inheight, INT32 inleftoffset, INT32 intopoffset, + pictureflags_t flags) +{ + if (informat == PICFMT_NONE) + I_Error("Picture_Convert: input format was PICFMT_NONE!"); + else if (outformat == PICFMT_NONE) + I_Error("Picture_Convert: output format was PICFMT_NONE!"); + else if (informat == outformat) + I_Error("Picture_Convert: input and output formats were the same!"); + + if (Picture_IsPatchFormat(outformat)) + return Picture_PatchConvert(informat, picture, outformat, insize, outsize, inwidth, inheight, inleftoffset, intopoffset, flags); + else if (Picture_IsFlatFormat(outformat)) + return Picture_FlatConvert(informat, picture, outformat, insize, outsize, inwidth, inheight, inleftoffset, intopoffset, flags); + else + I_Error("Picture_Convert: unsupported input format!"); + + return NULL; +} + +/** Converts a picture to a patch. + * + * \param informat Input picture format. + * \param picture Input picture data. + * \param outformat Output picture format. + * \param insize Input picture size. + * \param outsize Output picture size, as a pointer. + * \param inwidth Input picture width. + * \param inheight Input picture height. + * \param inleftoffset Input picture left offset, for patches. + * \param intopoffset Input picture top offset, for patches. + * \param flags Input picture flags. + * \return A pointer to the converted picture. + */ +void *Picture_PatchConvert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset, + pictureflags_t flags) +{ + INT16 x, y; + UINT8 *img; + UINT8 *imgptr = imgbuf; + UINT8 *colpointers, *startofspan; + size_t size = 0; + softwarepatch_t *inpatch = NULL; + INT32 inbpp = Picture_FormatBPP(informat); + + (void)insize; // ignore + + if (informat == PICFMT_NONE) + I_Error("Picture_PatchConvert: input format was PICFMT_NONE!"); + else if (outformat == PICFMT_NONE) + I_Error("Picture_PatchConvert: output format was PICFMT_NONE!"); + else if (informat == outformat) + I_Error("Picture_PatchConvert: input and output formats were the same!"); + + if (inbpp == PICDEPTH_NONE) + I_Error("Picture_PatchConvert: unknown input bits per pixel?!"); + if (Picture_FormatBPP(outformat) == PICDEPTH_NONE) + I_Error("Picture_PatchConvert: unknown output bits per pixel?!"); + + // If it's a patch, you can just figure out + // the dimensions from the header. + if (Picture_IsPatchFormat(informat)) + { + inpatch = (patch_t *)picture; + inwidth = SHORT(inpatch->width); + inheight = SHORT(inpatch->height); + inleftoffset = SHORT(inpatch->leftoffset); + intopoffset = SHORT(inpatch->topoffset); + } + + // Write image size and offset + WRITEINT16(imgptr, inwidth); + WRITEINT16(imgptr, inheight); + WRITEINT16(imgptr, inleftoffset); + WRITEINT16(imgptr, intopoffset); + + // Leave placeholder to column pointers + colpointers = imgptr; + imgptr += inwidth*4; + + // Write columns + for (x = 0; x < inwidth; x++) + { + int lastStartY = 0; + int spanSize = 0; + startofspan = NULL; + + // Write column pointer + WRITEINT32(colpointers, imgptr - imgbuf); + + // Write pixels + for (y = 0; y < inheight; y++) + { + void *input = NULL; + boolean opaque = false; + + // Read pixel + if (Picture_IsPatchFormat(informat)) + input = Picture_GetPatchPixel(inpatch, informat, x, y, flags); + else if (Picture_IsFlatFormat(informat)) + { + size_t offs = ((y * inwidth) + x); + switch (informat) + { + case PICFMT_FLAT32: + input = (UINT32 *)picture + offs; + break; + case PICFMT_FLAT16: + input = (UINT16 *)picture + offs; + break; + case PICFMT_FLAT: + input = (UINT8 *)picture + offs; + break; + default: + I_Error("Picture_PatchConvert: unsupported flat input format!"); + break; + } + } + else + I_Error("Picture_PatchConvert: unsupported input format!"); + + // Determine opacity + if (input != NULL) + { + UINT8 alpha = 0xFF; + if (inbpp == PICDEPTH_32BPP) + { + RGBA_t px = *(RGBA_t *)input; + alpha = px.s.alpha; + } + else if (inbpp == PICDEPTH_16BPP) + { + UINT16 px = *(UINT16 *)input; + alpha = (px & 0xFF00) >> 8; + } + else if (inbpp == PICDEPTH_8BPP) + { + UINT8 px = *(UINT8 *)input; + if (px == TRANSPARENTPIXEL) + alpha = 0; + } + opaque = (alpha > 1); + } + + // End span if we have a transparent pixel + if (!opaque) + { + if (startofspan) + WRITEUINT8(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) + WRITEUINT8(imgptr, 0); + + if (y > 254) + { + // Make sure we're aligned to 254 + if (lastStartY < 254) + { + WRITEUINT8(imgptr, 254); + WRITEUINT8(imgptr, 0); + imgptr += 2; + lastStartY = 254; + } + + // Write stopgap empty spans if needed + writeY = y - lastStartY; + + while (writeY > 254) + { + WRITEUINT8(imgptr, 254); + WRITEUINT8(imgptr, 0); + imgptr += 2; + writeY -= 254; + } + } + + startofspan = imgptr; + WRITEUINT8(imgptr, writeY); + imgptr += 2; + spanSize = 0; + + lastStartY = y; + } + + // Write the pixel + switch (outformat) + { + case PICFMT_PATCH32: + { + if (inbpp == PICDEPTH_32BPP) + { + RGBA_t out = *(RGBA_t *)input; + WRITEUINT32(imgptr, out.rgba); + } + else if (inbpp == PICDEPTH_16BPP) + { + RGBA_t out = pMasterPalette[*((UINT16 *)input) & 0xFF]; + WRITEUINT32(imgptr, out.rgba); + } + else // PICFMT_PATCH + { + RGBA_t out = pMasterPalette[*((UINT8 *)input) & 0xFF]; + WRITEUINT32(imgptr, out.rgba); + } + break; + } + case PICFMT_PATCH16: + if (inbpp == PICDEPTH_32BPP) + { + RGBA_t in = *(RGBA_t *)input; + UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); + WRITEUINT16(imgptr, (0xFF00 | out)); + } + else if (inbpp == PICDEPTH_16BPP) + WRITEUINT16(imgptr, *(UINT16 *)input); + else // PICFMT_PATCH + WRITEUINT16(imgptr, (0xFF00 | (*(UINT8 *)input))); + break; + default: // PICFMT_PATCH + { + if (inbpp == PICDEPTH_32BPP) + { + RGBA_t in = *(RGBA_t *)input; + UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); + WRITEUINT8(imgptr, out); + } + else if (inbpp == PICDEPTH_16BPP) + { + UINT16 out = *(UINT16 *)input; + WRITEUINT8(imgptr, (out & 0xFF)); + } + else // PICFMT_PATCH + WRITEUINT8(imgptr, *(UINT8 *)input); + break; + } + } + + spanSize++; + startofspan[1] = spanSize; + } + + if (startofspan) + WRITEUINT8(imgptr, 0); + + WRITEUINT8(imgptr, 0xFF); + } + + size = imgptr-imgbuf; + img = Z_Malloc(size, PU_STATIC, NULL); + memcpy(img, imgbuf, size); + + if (outsize != NULL) + *outsize = size; + return img; +} + +/** Converts a picture to a flat. + * + * \param informat Input picture format. + * \param picture Input picture data. + * \param outformat Output picture format. + * \param insize Input picture size. + * \param outsize Output picture size, as a pointer. + * \param inwidth Input picture width. + * \param inheight Input picture height. + * \param inleftoffset Input picture left offset, for patches. + * \param intopoffset Input picture top offset, for patches. + * \param flags Input picture flags. + * \return A pointer to the converted picture. + */ +void *Picture_FlatConvert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset, + pictureflags_t flags) +{ + void *outflat; + patch_t *inpatch = NULL; + INT32 inbpp = Picture_FormatBPP(informat); + INT32 outbpp = Picture_FormatBPP(outformat); + INT32 x, y; + size_t size; + + (void)insize; // ignore + (void)inleftoffset; // ignore + (void)intopoffset; // ignore + + if (informat == PICFMT_NONE) + I_Error("Picture_FlatConvert: input format was PICFMT_NONE!"); + else if (outformat == PICFMT_NONE) + I_Error("Picture_FlatConvert: output format was PICFMT_NONE!"); + else if (informat == outformat) + I_Error("Picture_FlatConvert: input and output formats were the same!"); + + if (inbpp == PICDEPTH_NONE) + I_Error("Picture_FlatConvert: unknown input bits per pixel?!"); + if (outbpp == PICDEPTH_NONE) + I_Error("Picture_FlatConvert: unknown output bits per pixel?!"); + + // If it's a patch, you can just figure out + // the dimensions from the header. + if (Picture_IsPatchFormat(informat)) + { + inpatch = (patch_t *)picture; + inwidth = SHORT(inpatch->width); + inheight = SHORT(inpatch->height); + } + + size = (inwidth * inheight) * (outbpp / 8); + outflat = Z_Calloc(size, PU_STATIC, NULL); + if (outsize) + *outsize = size; + + // Set transparency + if (outbpp == PICDEPTH_8BPP) + memset(outflat, TRANSPARENTPIXEL, size); + + for (y = 0; y < inheight; y++) + for (x = 0; x < inwidth; x++) + { + void *input; + size_t offs = ((y * inwidth) + x); + + // Read pixel + if (Picture_IsPatchFormat(informat)) + input = Picture_GetPatchPixel(inpatch, informat, x, y, flags); + else if (Picture_IsFlatFormat(informat)) + input = (UINT8 *)picture + (offs * (inbpp / 8)); + else + I_Error("Picture_FlatConvert: unsupported input format!"); + + if (!input) + continue; + + switch (outformat) + { + case PICFMT_FLAT32: + { + UINT32 *f32 = (UINT32 *)outflat; + if (inbpp == PICDEPTH_32BPP) + { + RGBA_t out = *(RGBA_t *)input; + f32[offs] = out.rgba; + } + else if (inbpp == PICDEPTH_16BPP) + { + RGBA_t out = pMasterPalette[*((UINT16 *)input) & 0xFF]; + f32[offs] = out.rgba; + } + else // PICFMT_PATCH + { + RGBA_t out = pMasterPalette[*((UINT8 *)input) & 0xFF]; + f32[offs] = out.rgba; + } + break; + } + case PICFMT_FLAT16: + { + UINT16 *f16 = (UINT16 *)outflat; + if (inbpp == PICDEPTH_32BPP) + { + RGBA_t in = *(RGBA_t *)input; + UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); + f16[offs] = (0xFF00 | out); + } + else if (inbpp == PICDEPTH_16BPP) + f16[offs] = *(UINT16 *)input; + else // PICFMT_PATCH + f16[offs] = (0xFF00 | *((UINT8 *)input)); + break; + } + case PICFMT_FLAT: + { + UINT8 *f8 = (UINT8 *)outflat; + if (inbpp == PICDEPTH_32BPP) + { + RGBA_t in = *(RGBA_t *)input; + UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue); + f8[offs] = out; + } + else if (inbpp == PICDEPTH_16BPP) + { + UINT16 out = *(UINT16 *)input; + f8[offs] = (out & 0xFF); + } + else // PICFMT_PATCH + f8[offs] = *(UINT8 *)input; + break; + } + default: + I_Error("Picture_FlatConvert: unsupported output format!"); + } + } + + return outflat; +} + +/** Returns a pixel from a patch. + * + * \param patch Input patch. + * \param informat Input picture format. + * \param x Pixel X position. + * \param y Pixel Y position. + * \param flags Input picture flags. + * \return A pointer to a pixel in the patch. Returns NULL if not opaque. + */ +void *Picture_GetPatchPixel( + patch_t *patch, pictureformat_t informat, + INT32 x, INT32 y, + pictureflags_t flags) +{ + fixed_t ofs; + column_t *column; + UINT8 *s8 = NULL; + UINT16 *s16 = NULL; + UINT32 *s32 = NULL; + + if (patch == NULL) + I_Error("Picture_GetPatchPixel: patch == NULL"); + + if (x >= 0 && x < SHORT(patch->width)) + { + INT32 topdelta, prevdelta = -1; + INT32 colofs = 0; + + if (flags & PICFLAGS_XFLIP) + colofs = LONG(patch->columnofs[(SHORT(patch->width)-1)-x]); + else + colofs = LONG(patch->columnofs[x]); + + // Column offsets are pointers so no casting required + column = (column_t *)((UINT8 *)patch + colofs); + + while (column->topdelta != 0xff) + { + topdelta = column->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + s8 = (UINT8 *)(column) + 3; + if (informat == PICFMT_PATCH32) + s32 = (UINT32 *)s8; + else if (informat == PICFMT_PATCH16) + s16 = (UINT16 *)s8; + for (ofs = 0; ofs < column->length; ofs++) + { + if ((topdelta + ofs) == y) + { + if (informat == PICFMT_PATCH32) + return &s32[ofs]; + else if (informat == PICFMT_PATCH16) + return &s16[ofs]; + else // PICFMT_PATCH + return &s8[ofs]; + } + } + if (informat == PICFMT_PATCH32) + column = (column_t *)((UINT32 *)column + column->length); + else if (informat == PICFMT_PATCH16) + column = (column_t *)((UINT16 *)column + column->length); + else + column = (column_t *)((UINT8 *)column + column->length); + column = (column_t *)((UINT8 *)column + 4); + } + } + + return NULL; +} + +/** Returns the amount of bits per pixel in the specified picture format. + * + * \param format Input picture format. + * \return The bits per pixel amount of the picture format. + */ +INT32 Picture_FormatBPP(pictureformat_t format) +{ + INT32 bpp = PICDEPTH_NONE; + switch (format) + { + case PICFMT_PATCH32: + case PICFMT_FLAT32: + case PICFMT_PNG: + bpp = PICDEPTH_32BPP; + break; + case PICFMT_PATCH16: + case PICFMT_FLAT16: + bpp = PICDEPTH_16BPP; + break; + case PICFMT_PATCH: + case PICFMT_FLAT: + bpp = PICDEPTH_8BPP; + break; + default: + break; + } + return bpp; +} + +/** Checks if the specified picture format is a patch. + * + * \param format Input picture format. + * \return True if the picture format is a patch, false if not. + */ +boolean Picture_IsPatchFormat(pictureformat_t format) +{ + return (format == PICFMT_PATCH || format == PICFMT_PATCH16 || format == PICFMT_PATCH32); +} + +/** Checks if the specified picture format is a flat. + * + * \param format Input picture format. + * \return True if the picture format is a flat, false if not. + */ +boolean Picture_IsFlatFormat(pictureformat_t format) +{ + return (format == PICFMT_FLAT || format == PICFMT_FLAT16 || format == PICFMT_FLAT32); +} + +/** Returns true if the lump is a valid Doom patch. + * PICFMT_PATCH only, I think?? + * + * \param patch Input patch. + * \param picture Input patch size. + * \return True if the input patch is valid. + */ +boolean Picture_CheckIfPatch(softwarepatch_t *patch, size_t size) +{ + INT16 width, height; + boolean result; + + // minimum length of a valid Doom patch + if (size < 13) + return false; + + width = SHORT(patch->width); + height = SHORT(patch->height); + result = (height > 0 && height <= 16384 && width > 0 && width <= 16384); + + 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; +} + +/** Converts a texture to a flat. + * + * \param trickytex The texture number. + * \return The converted flat. + */ +void *Picture_TextureToFlat(size_t trickytex) +{ + texture_t *texture; + size_t tex; + + UINT8 *converted; + size_t flatsize; + fixed_t col, ofs; + column_t *column; + UINT8 *desttop, *dest, *deststop; + UINT8 *source; + + if (trickytex >= (unsigned)numtextures) + I_Error("Picture_TextureToFlat: invalid texture number!"); + + // Check the texture cache + // If the texture's not there, it'll be generated right now + tex = trickytex; + texture = textures[tex]; + R_CheckTextureCache(tex); + + // Allocate the flat + flatsize = (texture->width * texture->height); + converted = Z_Malloc(flatsize, PU_STATIC, NULL); + memset(converted, TRANSPARENTPIXEL, flatsize); + + // Now we're gonna write to it + desttop = converted; + deststop = desttop + flatsize; + for (col = 0; col < texture->width; col++, desttop++) + { + // no post_t info + if (!texture->holes) + { + column = (column_t *)(R_GetColumn(tex, col)); + source = (UINT8 *)(column); + dest = desttop; + for (ofs = 0; dest < deststop && ofs < texture->height; ofs++) + { + if (source[ofs] != TRANSPARENTPIXEL) + *dest = source[ofs]; + dest += texture->width; + } + } + else + { + INT32 topdelta, prevdelta = -1; + column = (column_t *)((UINT8 *)R_GetColumn(tex, col) - 3); + 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); + } + } + } + + return converted; +} + +/** Returns true if the lump is a valid PNG. + * + * \param d The lump to be checked. + * \param s The lump size. + * \return True if the lump is a PNG image. + */ +boolean Picture_IsLumpPNG(const 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); +} + +#ifndef NO_PNG_LUMPS +#ifdef HAVE_PNG + +/*#if PNG_LIBPNG_VER_DLLNUM < 14 +typedef PNG_CONST png_byte *png_const_bytep; +#endif*/ +typedef struct +{ + const UINT8 *buffer; + UINT32 size; + UINT32 position; +} png_io_t; + +static void PNG_IOReader(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_io_t *f = png_get_io_ptr(png_ptr); + if (length > (f->size - f->position)) + png_error(png_ptr, "PNG_IOReader: buffer overrun"); + memcpy(data, f->buffer + f->position, length); + f->position += length; +} + +typedef struct +{ + char name[4]; + void *data; + size_t size; +} png_chunk_t; + +static png_byte *chunkname = NULL; +static png_chunk_t chunk; + +static int PNG_ChunkReader(png_structp png_ptr, png_unknown_chunkp chonk) +{ + (void)png_ptr; + if (!memcmp(chonk->name, chunkname, 4)) + { + memcpy(chunk.name, chonk->name, 4); + chunk.size = chonk->size; + chunk.data = Z_Malloc(chunk.size, PU_STATIC, NULL); + memcpy(chunk.data, chonk->data, chunk.size); + return 1; + } + return 0; +} + +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(const UINT8 *png, INT32 *w, INT32 *h, INT16 *topoffset, INT16 *leftoffset, 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_io_t png_io; + png_bytep *row_pointers; + + png_byte grAb_chunk[5] = {'g', 'r', 'A', 'b', (png_byte)'\0'}; + png_voidp *user_chunk_ptr; + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, PNG_error, PNG_warn); + if (!png_ptr) + I_Error("PNG_Read: Couldn't initialize libpng!"); + + png_info_ptr = png_create_info_struct(png_ptr); + if (!png_info_ptr) + { + png_destroy_read_struct(&png_ptr, NULL, NULL); + I_Error("PNG_Read: libpng couldn't allocate memory!"); + } + +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_jmpbuf(png_ptr))) +#endif + { + png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); + I_Error("PNG_Read: libpng load error!"); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); +#endif + + // set our own read function + png_io.buffer = png; + png_io.size = size; + png_io.position = 0; + png_set_read_fn(png_ptr, &png_io, PNG_IOReader); + + memset(&chunk, 0x00, sizeof(png_chunk_t)); + chunkname = grAb_chunk; // I want to read a grAb chunk + + user_chunk_ptr = png_get_user_chunk_ptr(png_ptr); + png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, PNG_ChunkReader); + png_set_keep_unknown_chunks(png_ptr, 2, chunkname, 1); + +#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); + + // Read grAB chunk + if ((topoffset || leftoffset) && (chunk.data != NULL)) + { + INT32 *offsets = (INT32 *)chunk.data; + // read left offset + if (leftoffset != NULL) + *leftoffset = (INT16)BIGENDIAN_LONG(*offsets); + offsets++; + // read top offset + if (topoffset != NULL) + *topoffset = (INT16)BIGENDIAN_LONG(*offsets); + } + + // bye + png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); + if (chunk.data) + Z_Free(chunk.data); + + *w = (INT32)width; + *h = (INT32)height; + return row_pointers; +} + +/** Converts a PNG to a picture. + * + * \param png The PNG image. + * \param outformat The output picture's format. + * \param w The output picture's width, as a pointer. + * \param h The output picture's height, as a pointer. + * \param topoffset The output picture's top offset, for sprites, as a pointer. + * \param leftoffset The output picture's left offset, for sprites, as a pointer. + * \param insize The input picture's size. + * \param outsize A pointer to the output picture's size. + * \param flags Input picture flags. + * \return A pointer to the converted picture. + */ +void *Picture_PNGConvert( + const UINT8 *png, pictureformat_t outformat, + INT32 *w, INT32 *h, + INT16 *topoffset, INT16 *leftoffset, + size_t insize, size_t *outsize, + pictureflags_t flags) +{ + void *flat; + INT32 outbpp; + size_t flatsize; + png_uint_32 x, y; + png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, insize); + png_uint_32 width = *w, height = *h; + + if (png == NULL) + I_Error("Picture_PNGConvert: picture was NULL!"); + + // Find the output format's bits per pixel amount + outbpp = Picture_FormatBPP(outformat); + + // Hack for patches because you'll want to preserve transparency. + if (Picture_IsPatchFormat(outformat)) + { + // Force a higher bit depth + if (outbpp == PICDEPTH_8BPP) + outbpp = PICDEPTH_16BPP; + } + + // Shouldn't happen. + if (outbpp == PICDEPTH_NONE) + I_Error("Picture_PNGConvert: unknown output bits per pixel?!"); + + // Figure out the size + flatsize = (width * height) * (outbpp / 8); + if (outsize) + *outsize = flatsize; + + // Convert the image + flat = Z_Calloc(flatsize, PU_STATIC, NULL); + + // Set transparency + if (outbpp == PICDEPTH_8BPP) + 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]); + if ((UINT8)px[3]) + { + UINT8 red = (UINT8)px[0]; + UINT8 green = (UINT8)px[1]; + UINT8 blue = (UINT8)px[2]; + UINT8 alpha = (UINT8)px[3]; + if (outbpp == PICDEPTH_32BPP) + { + UINT32 *outflat = (UINT32 *)flat; + RGBA_t out; + out.s.red = red; + out.s.green = green; + out.s.blue = blue; + out.s.alpha = alpha; + outflat[((y * width) + x)] = out.rgba; + } + else + { + UINT8 palidx = NearestColor(red, green, blue); + if (outbpp == PICDEPTH_16BPP) + { + UINT16 *outflat = (UINT16 *)flat; + outflat[((y * width) + x)] = (alpha << 8) | palidx; + } + else // 8bpp + { + UINT8 *outflat = (UINT8 *)flat; + outflat[((y * width) + x)] = palidx; + } + } + } + } + } + + // Free the row pointers that we allocated for libpng. + free(row_pointers); + + // But wait, there's more! + if (Picture_IsPatchFormat(outformat)) + { + void *converted; + pictureformat_t informat = PICFMT_NONE; + INT16 patleftoffset = 0, pattopoffset = 0; + + // Figure out the format of the flat, from the bit depth of the output format + switch (outbpp) + { + case 32: + informat = PICFMT_FLAT32; + break; + case 16: + informat = PICFMT_FLAT16; + break; + default: + informat = PICFMT_FLAT; + break; + } + + // Also find out if leftoffset and topoffset aren't pointing to NULL. + if (leftoffset) + patleftoffset = *leftoffset; + if (topoffset) + pattopoffset = *topoffset; + + // Now, convert it! + converted = Picture_PatchConvert(informat, flat, outformat, insize, outsize, (INT16)width, (INT16)height, patleftoffset, pattopoffset, flags); + Z_Free(flat); + return converted; + } + + // Return the converted flat! + return flat; +} + +/** Returns the dimensions of a PNG image, but doesn't perform any conversions. + * + * \param png The PNG image. + * \param width A pointer to the input picture's width. + * \param height A pointer to the input picture's height. + * \param size The input picture's size. + * \return True if reading the file succeeded, false if it failed. + */ +boolean Picture_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_io_t png_io; + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, + PNG_error, PNG_warn); + if (!png_ptr) + I_Error("Picture_PNGDimensions: Couldn't initialize libpng!"); + + png_info_ptr = png_create_info_struct(png_ptr); + if (!png_info_ptr) + { + png_destroy_read_struct(&png_ptr, NULL, NULL); + I_Error("Picture_PNGDimensions: libpng couldn't allocate memory!"); + } + +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_jmpbuf(png_ptr))) +#endif + { + png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL); + I_Error("Picture_PNGDimensions: libpng load error!"); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf); +#endif + + // set our own read function + png_io.buffer = png; + png_io.size = size; + png_io.position = 0; + 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 + +// +// R_ParseSpriteInfoFrame +// +// Parse a SPRTINFO frame. +// +static void R_ParseSpriteInfoFrame(spriteinfo_t *info) +{ + char *sprinfoToken; + size_t sprinfoTokenLength; + char *frameChar = NULL; + UINT8 frameFrame = 0xFF; + INT16 frameXPivot = 0; + INT16 frameYPivot = 0; + rotaxis_t frameRotAxis = 0; + + // Sprite identifier + sprinfoToken = M_GetToken(NULL); + if (sprinfoToken == NULL) + { + I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite frame should be"); + } + sprinfoTokenLength = strlen(sprinfoToken); + if (sprinfoTokenLength != 1) + { + I_Error("Error parsing SPRTINFO lump: Invalid frame \"%s\"",sprinfoToken); + } + else + frameChar = sprinfoToken; + + frameFrame = R_Char2Frame(frameChar[0]); + Z_Free(sprinfoToken); + + // Left Curly Brace + sprinfoToken = M_GetToken(NULL); + if (sprinfoToken == NULL) + I_Error("Error parsing SPRTINFO lump: Missing sprite info"); + else + { + if (strcmp(sprinfoToken,"{")==0) + { + Z_Free(sprinfoToken); + sprinfoToken = M_GetToken(NULL); + if (sprinfoToken == NULL) + { + I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite info should be"); + } + while (strcmp(sprinfoToken,"}")!=0) + { + if (stricmp(sprinfoToken, "XPIVOT")==0) + { + Z_Free(sprinfoToken); + sprinfoToken = M_GetToken(NULL); + frameXPivot = atoi(sprinfoToken); + } + else if (stricmp(sprinfoToken, "YPIVOT")==0) + { + Z_Free(sprinfoToken); + sprinfoToken = M_GetToken(NULL); + frameYPivot = atoi(sprinfoToken); + } + else if (stricmp(sprinfoToken, "ROTAXIS")==0) + { + Z_Free(sprinfoToken); + sprinfoToken = M_GetToken(NULL); + if ((stricmp(sprinfoToken, "X")==0) || (stricmp(sprinfoToken, "XAXIS")==0) || (stricmp(sprinfoToken, "ROLL")==0)) + frameRotAxis = ROTAXIS_X; + else if ((stricmp(sprinfoToken, "Y")==0) || (stricmp(sprinfoToken, "YAXIS")==0) || (stricmp(sprinfoToken, "PITCH")==0)) + frameRotAxis = ROTAXIS_Y; + else if ((stricmp(sprinfoToken, "Z")==0) || (stricmp(sprinfoToken, "ZAXIS")==0) || (stricmp(sprinfoToken, "YAW")==0)) + frameRotAxis = ROTAXIS_Z; + } + Z_Free(sprinfoToken); + + sprinfoToken = M_GetToken(NULL); + if (sprinfoToken == NULL) + { + I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite info or right curly brace should be"); + } + } + } + Z_Free(sprinfoToken); + } + + // set fields + info->pivot[frameFrame].x = frameXPivot; + info->pivot[frameFrame].y = frameYPivot; + info->pivot[frameFrame].rotaxis = frameRotAxis; +} + +// +// R_ParseSpriteInfo +// +// Parse a SPRTINFO lump. +// +static void R_ParseSpriteInfo(boolean spr2) +{ + spriteinfo_t *info; + char *sprinfoToken; + size_t sprinfoTokenLength; + char newSpriteName[5]; // no longer dynamically allocated + spritenum_t sprnum = NUMSPRITES; + playersprite_t spr2num = NUMPLAYERSPRITES; + INT32 i; + INT32 skinnumbers[MAXSKINS]; + INT32 foundskins = 0; + + // Sprite name + sprinfoToken = M_GetToken(NULL); + if (sprinfoToken == NULL) + { + I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite name should be"); + } + sprinfoTokenLength = strlen(sprinfoToken); + if (sprinfoTokenLength != 4) + { + I_Error("Error parsing SPRTINFO lump: Sprite name \"%s\" isn't 4 characters long",sprinfoToken); + } + else + { + memset(&newSpriteName, 0, 5); + M_Memcpy(newSpriteName, sprinfoToken, sprinfoTokenLength); + // ^^ we've confirmed that the token is == 4 characters so it will never overflow a 5 byte char buffer + strupr(newSpriteName); // Just do this now so we don't have to worry about it + } + Z_Free(sprinfoToken); + + if (!spr2) + { + for (i = 0; i <= NUMSPRITES; i++) + { + if (i == NUMSPRITES) + I_Error("Error parsing SPRTINFO lump: Unknown sprite name \"%s\"", newSpriteName); + if (!memcmp(newSpriteName,sprnames[i],4)) + { + sprnum = i; + break; + } + } + } + else + { + for (i = 0; i <= NUMPLAYERSPRITES; i++) + { + if (i == NUMPLAYERSPRITES) + I_Error("Error parsing SPRTINFO lump: Unknown sprite2 name \"%s\"", newSpriteName); + if (!memcmp(newSpriteName,spr2names[i],4)) + { + spr2num = i; + break; + } + } + } + + // allocate a spriteinfo + info = Z_Calloc(sizeof(spriteinfo_t), PU_STATIC, NULL); + info->available = true; + +#ifdef ROTSPRITE + if ((sprites != NULL) && (!spr2)) + R_FreeRotSprite(&sprites[sprnum]); +#endif + + // Left Curly Brace + sprinfoToken = M_GetToken(NULL); + if (sprinfoToken == NULL) + { + I_Error("Error parsing SPRTINFO lump: Unexpected end of file where open curly brace for sprite \"%s\" should be",newSpriteName); + } + if (strcmp(sprinfoToken,"{")==0) + { + Z_Free(sprinfoToken); + sprinfoToken = M_GetToken(NULL); + if (sprinfoToken == NULL) + { + I_Error("Error parsing SPRTINFO lump: Unexpected end of file where definition for sprite \"%s\" should be",newSpriteName); + } + while (strcmp(sprinfoToken,"}")!=0) + { + if (stricmp(sprinfoToken, "SKIN")==0) + { + INT32 skinnum; + char *skinName = NULL; + if (!spr2) + I_Error("Error parsing SPRTINFO lump: \"SKIN\" token found outside of a sprite2 definition"); + + Z_Free(sprinfoToken); + + // Skin name + sprinfoToken = M_GetToken(NULL); + if (sprinfoToken == NULL) + { + I_Error("Error parsing SPRTINFO lump: Unexpected end of file where skin frame should be"); + } + + // copy skin name yada yada + sprinfoTokenLength = strlen(sprinfoToken); + skinName = (char *)Z_Malloc((sprinfoTokenLength+1)*sizeof(char),PU_STATIC,NULL); + M_Memcpy(skinName,sprinfoToken,sprinfoTokenLength*sizeof(char)); + skinName[sprinfoTokenLength] = '\0'; + strlwr(skinName); + Z_Free(sprinfoToken); + + skinnum = R_SkinAvailable(skinName); + if (skinnum == -1) + I_Error("Error parsing SPRTINFO lump: Unknown skin \"%s\"", skinName); + + skinnumbers[foundskins] = skinnum; + foundskins++; + } + else if (stricmp(sprinfoToken, "FRAME")==0) + { + R_ParseSpriteInfoFrame(info); + Z_Free(sprinfoToken); + if (spr2) + { + if (!foundskins) + I_Error("Error parsing SPRTINFO lump: No skins specified in this sprite2 definition"); + for (i = 0; i < foundskins; i++) + { + size_t skinnum = skinnumbers[i]; + skin_t *skin = &skins[skinnum]; + spriteinfo_t *sprinfo = skin->sprinfo; +#ifdef ROTSPRITE + R_FreeSkinRotSprite(skinnum); +#endif + M_Memcpy(&sprinfo[spr2num], info, sizeof(spriteinfo_t)); + } + } + else + M_Memcpy(&spriteinfo[sprnum], info, sizeof(spriteinfo_t)); + } + else + { + I_Error("Error parsing SPRTINFO lump: Unknown keyword \"%s\" in sprite %s",sprinfoToken,newSpriteName); + } + + sprinfoToken = M_GetToken(NULL); + if (sprinfoToken == NULL) + { + I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite info or right curly brace for sprite \"%s\" should be",newSpriteName); + } + } + } + else + { + I_Error("Error parsing SPRTINFO lump: Expected \"{\" for sprite \"%s\", got \"%s\"",newSpriteName,sprinfoToken); + } + Z_Free(sprinfoToken); + Z_Free(info); +} + +// +// R_ParseSPRTINFOLump +// +// Read a SPRTINFO lump. +// +void R_ParseSPRTINFOLump(UINT16 wadNum, UINT16 lumpNum) +{ + char *sprinfoLump; + size_t sprinfoLumpLength; + char *sprinfoText; + char *sprinfoToken; + + // 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. + sprinfoLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); + // If that didn't exist, we have nothing to do here. + if (sprinfoLump == NULL) return; + // If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly. + sprinfoLumpLength = W_LumpLengthPwad(wadNum, lumpNum); + sprinfoText = (char *)Z_Malloc((sprinfoLumpLength+1)*sizeof(char),PU_STATIC,NULL); + // Now move the contents of the lump into this new location. + memmove(sprinfoText,sprinfoLump,sprinfoLumpLength); + // Make damn well sure the last character in our new memory location is \0. + sprinfoText[sprinfoLumpLength] = '\0'; + // Finally, free up the memory from the first data load, because we really + // don't need it. + Z_Free(sprinfoLump); + + sprinfoToken = M_GetToken(sprinfoText); + while (sprinfoToken != NULL) + { + if (!stricmp(sprinfoToken, "SPRITE")) + R_ParseSpriteInfo(false); + else if (!stricmp(sprinfoToken, "SPRITE2")) + R_ParseSpriteInfo(true); + else + I_Error("Error parsing SPRTINFO lump: Unknown keyword \"%s\"", sprinfoToken); + Z_Free(sprinfoToken); + sprinfoToken = M_GetToken(NULL); + } + Z_Free((void *)sprinfoText); +} + +// +// R_LoadSpriteInfoLumps +// +// Load and read every SPRTINFO lump from the specified file. +// +void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps) +{ + lumpinfo_t *lumpinfo = wadfiles[wadnum]->lumpinfo; + UINT16 i; + char *name; + + for (i = 0; i < numlumps; i++, lumpinfo++) + { + name = lumpinfo->name; + // Load SPRTINFO and SPR_ lumps as SpriteInfo + if (!memcmp(name, "SPRTINFO", 8) || !memcmp(name, "SPR_", 4)) + R_ParseSPRTINFOLump(wadnum, i); + } +} + +#ifdef ROTSPRITE +// +// R_GetRollAngle +// +// Angles precalculated in R_InitSprites. +// +fixed_t rollcosang[ROTANGLES]; +fixed_t rollsinang[ROTANGLES]; +INT32 R_GetRollAngle(angle_t rollangle) +{ + INT32 ra = AngleFixed(rollangle)>>FRACBITS; +#if (ROTANGDIFF > 1) + ra += (ROTANGDIFF/2); +#endif + ra /= ROTANGDIFF; + ra %= ROTANGLES; + return ra; +} + +// +// R_CacheRotSprite +// +// Create a rotated sprite. +// +void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, spriteframe_t *sprframe, INT32 rot, UINT8 flip) +{ + INT32 angle; + patch_t *patch, *newpatch; + softwarepatch_t *swpatch; + UINT16 *rawdst; + size_t size; + pictureflags_t bflip = (flip) ? PICFLAGS_XFLIP : 0; + +#define SPRITE_XCENTER (leftoffset) +#define SPRITE_YCENTER (height / 2) +#define ROTSPRITE_XCENTER (newwidth / 2) +#define ROTSPRITE_YCENTER (newheight / 2) + + if (!(sprframe->rotsprite.cached & (1<lumppat[rot]; + + if (lump == LUMPERROR) + return; + + patch = (patch_t *)W_CachePatchNum(lump, PU_STATIC); + width = patch->width; + height = patch->height; + leftoffset = patch->leftoffset; + + // rotation pivot + px = SPRITE_XCENTER; + py = SPRITE_YCENTER; + + // get correct sprite info for sprite + if (sprinfo == NULL) + sprinfo = &spriteinfo[sprnum]; + if (sprinfo->available) + { + px = sprinfo->pivot[frame].x; + py = sprinfo->pivot[frame].y; + } + if (bflip) + { + px = width - px; + leftoffset = width - leftoffset; + } + + // Don't cache angle = 0 + for (angle = 1; angle < ROTANGLES; angle++) + { + INT32 newwidth, newheight; + + ca = rollcosang[angle]; + sa = rollsinang[angle]; + + // Find the dimensions of the rotated patch. + { + INT32 w1 = abs(FixedMul(width << FRACBITS, ca) - FixedMul(height << FRACBITS, sa)); + INT32 w2 = abs(FixedMul(-(width << FRACBITS), ca) - FixedMul(height << FRACBITS, sa)); + INT32 h1 = abs(FixedMul(width << FRACBITS, sa) + FixedMul(height << FRACBITS, ca)); + INT32 h2 = abs(FixedMul(-(width << FRACBITS), sa) + FixedMul(height << FRACBITS, ca)); + w1 = FixedInt(FixedCeil(w1 + (FRACUNIT/2))); + w2 = FixedInt(FixedCeil(w2 + (FRACUNIT/2))); + h1 = FixedInt(FixedCeil(h1 + (FRACUNIT/2))); + h2 = FixedInt(FixedCeil(h2 + (FRACUNIT/2))); + newwidth = max(width, max(w1, w2)); + newheight = max(height, max(h1, h2)); + } + + // check boundaries + { + fixed_t top[2][2]; + fixed_t bottom[2][2]; + + top[0][0] = FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, sa) + (px << FRACBITS); + top[0][1] = FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, ca) + (py << FRACBITS); + top[1][0] = FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, sa) + (px << FRACBITS); + top[1][1] = FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((-ROTSPRITE_YCENTER) << FRACBITS, ca) + (py << FRACBITS); + + bottom[0][0] = FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, sa) + (px << FRACBITS); + bottom[0][1] = -FixedMul((-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, ca) + (py << FRACBITS); + bottom[1][0] = FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, ca) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, sa) + (px << FRACBITS); + bottom[1][1] = -FixedMul((newwidth-ROTSPRITE_XCENTER) << FRACBITS, sa) + FixedMul((newheight-ROTSPRITE_YCENTER) << FRACBITS, ca) + (py << FRACBITS); + + top[0][0] >>= FRACBITS; + top[0][1] >>= FRACBITS; + top[1][0] >>= FRACBITS; + top[1][1] >>= FRACBITS; + + bottom[0][0] >>= FRACBITS; + bottom[0][1] >>= FRACBITS; + bottom[1][0] >>= FRACBITS; + bottom[1][1] >>= FRACBITS; + +#define BOUNDARYWCHECK(b) (b[0] < 0 || b[0] >= width) +#define BOUNDARYHCHECK(b) (b[1] < 0 || b[1] >= height) +#define BOUNDARYADJUST(x) x *= 2 + // top left/right + if (BOUNDARYWCHECK(top[0]) || BOUNDARYWCHECK(top[1])) + BOUNDARYADJUST(newwidth); + // bottom left/right + else if (BOUNDARYWCHECK(bottom[0]) || BOUNDARYWCHECK(bottom[1])) + BOUNDARYADJUST(newwidth); + // top left/right + if (BOUNDARYHCHECK(top[0]) || BOUNDARYHCHECK(top[1])) + BOUNDARYADJUST(newheight); + // bottom left/right + else if (BOUNDARYHCHECK(bottom[0]) || BOUNDARYHCHECK(bottom[1])) + BOUNDARYADJUST(newheight); +#undef BOUNDARYWCHECK +#undef BOUNDARYHCHECK +#undef BOUNDARYADJUST + } + + // Draw the rotated sprite to a temporary buffer. + size = (newwidth * newheight); + if (!size) + size = (width * height); + rawdst = Z_Calloc(size * sizeof(UINT16), PU_STATIC, NULL); + + for (dy = 0; dy < newheight; dy++) + { + for (dx = 0; dx < newwidth; dx++) + { + INT32 x = (dx-ROTSPRITE_XCENTER) << FRACBITS; + INT32 y = (dy-ROTSPRITE_YCENTER) << FRACBITS; + INT32 sx = FixedMul(x, ca) + FixedMul(y, sa) + (px << FRACBITS); + INT32 sy = -FixedMul(x, sa) + FixedMul(y, ca) + (py << FRACBITS); + sx >>= FRACBITS; + sy >>= FRACBITS; + if (sx >= 0 && sy >= 0 && sx < width && sy < height) + { + void *input = Picture_GetPatchPixel(patch, PICFMT_PATCH, sx, sy, bflip); + if (input != NULL) + rawdst[(dy*newwidth)+dx] = (0xFF00 | (*(UINT8 *)input)); + } + } + } + + // make patch + swpatch = (softwarepatch_t *)Picture_Convert(PICFMT_FLAT16, rawdst, PICFMT_PATCH, 0, &size, newwidth, newheight, 0, 0, 0); + { + swpatch->leftoffset = (swpatch->width / 2) + (leftoffset - px); + swpatch->topoffset = (swpatch->height / 2) + (patch->topoffset - py); + } + + //BP: we cannot use special tric in hardware mode because feet in ground caused by z-buffer + if (rendermode != render_none) // not for psprite + swpatch->topoffset += FEETADJUST>>FRACBITS; + + // P_PrecacheLevel + if (devparm) spritememory += size; + + // convert everything to little-endian, for big-endian support + swpatch->width = SHORT(swpatch->width); + swpatch->height = SHORT(swpatch->height); + swpatch->leftoffset = SHORT(swpatch->leftoffset); + swpatch->topoffset = SHORT(swpatch->topoffset); + + newpatch = Patch_Create(swpatch, size, NULL); + +#ifdef HWRENDER + if (rendermode == render_opengl) + Patch_CreateGL(newpatch); +#endif + + sprframe->rotsprite.patch[rot][angle] = newpatch; + + // free rotated image data + Z_Free(rawdst); + } + + // This rotation is cached now + sprframe->rotsprite.cached |= (1<numframes; frame++) + { + spriteframe_t *sprframe = &spritedef->spriteframes[frame]; + for (rot = 0; rot < 16; rot++) + { + if (sprframe->rotsprite.cached & (1<rotsprite.patch[rot][ang]; + if (rotsprite) + Patch_Free(rotsprite); + rotsprite = NULL; + } + sprframe->rotsprite.cached &= ~(1<sprites; + for (i = 0; i < NUMPLAYERSPRITES*2; i++) + { + R_FreeRotSprite(skinsprites); + skinsprites++; + } +} +#endif diff --git a/src/r_picformats.h b/src/r_picformats.h new file mode 100644 index 000000000..d7f862450 --- /dev/null +++ b/src/r_picformats.h @@ -0,0 +1,132 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 2018-2020 by Jaime "Lactozilla" Passos. +// Copyright (C) 2019-2020 by Sonic Team Junior. +// +// 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_picformats.h +/// \brief Patch generation. + +#ifndef __R_PICFORMATS__ +#define __R_PICFORMATS__ + +#include "r_defs.h" +#include "doomdef.h" + +typedef enum +{ + PICFMT_NONE = 0, + + // Doom formats + PICFMT_PATCH, + PICFMT_FLAT, + + // PNG + PICFMT_PNG, + + // 16bpp + PICFMT_PATCH16, + PICFMT_FLAT16, + + // 32bpp + PICFMT_PATCH32, + PICFMT_FLAT32 +} pictureformat_t; + +typedef enum +{ + PICFLAGS_XFLIP = 1, + PICFLAGS_YFLIP = 1<<1 +} pictureflags_t; + +enum +{ + PICDEPTH_NONE = 0, + PICDEPTH_8BPP = 8, + PICDEPTH_16BPP = 16, + PICDEPTH_32BPP = 32 +}; + +void *Picture_Convert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT32 inwidth, INT32 inheight, INT32 inleftoffset, INT32 intopoffset, + pictureflags_t flags); + +void *Picture_PatchConvert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset, + pictureflags_t flags); +void *Picture_FlatConvert( + pictureformat_t informat, void *picture, pictureformat_t outformat, + size_t insize, size_t *outsize, + INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset, + pictureflags_t flags); +void *Picture_GetPatchPixel( + patch_t *patch, pictureformat_t informat, + INT32 x, INT32 y, + pictureflags_t flags); + +void *Picture_TextureToFlat(size_t trickytex); + +INT32 Picture_FormatBPP(pictureformat_t format); +boolean Picture_IsPatchFormat(pictureformat_t format); +boolean Picture_IsFlatFormat(pictureformat_t format); +boolean Picture_CheckIfPatch(softwarepatch_t *patch, size_t size); + +// Structs +typedef enum +{ + ROTAXIS_X, // Roll (the default) + ROTAXIS_Y, // Pitch + ROTAXIS_Z // Yaw +} rotaxis_t; + +typedef struct +{ + INT32 x, y; + rotaxis_t rotaxis; +} spriteframepivot_t; + +typedef struct +{ + spriteframepivot_t pivot[64]; + boolean available; +} spriteinfo_t; + +// Portable Network Graphics +boolean Picture_IsLumpPNG(const UINT8 *d, size_t s); +#define Picture_ThrowPNGError(lumpname, wadfilename) I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .png - please convert to either Doom or Flat (raw) image format.", lumpname, wadfilename); // Fears Of LJ Sonic + +#ifndef NO_PNG_LUMPS +void *Picture_PNGConvert( + const UINT8 *png, pictureformat_t outformat, + INT32 *w, INT32 *h, + INT16 *topoffset, INT16 *leftoffset, + size_t insize, size_t *outsize, + pictureflags_t flags); +boolean Picture_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size); +#endif + +// SpriteInfo +extern spriteinfo_t spriteinfo[NUMSPRITES]; +void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps); +void R_ParseSPRTINFOLump(UINT16 wadNum, UINT16 lumpNum); + +// Sprite rotation +#ifdef ROTSPRITE +INT32 R_GetRollAngle(angle_t rollangle); +void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, spriteframe_t *sprframe, INT32 rot, UINT8 flip); +void R_FreeRotSprite(spritedef_t *spritedef); +void R_FreeSkinRotSprite(size_t skinnum); +extern fixed_t rollcosang[ROTANGLES]; +extern fixed_t rollsinang[ROTANGLES]; +void R_FreeAllRotSprite(void); +#endif + +#endif // __R_PICFORMATS__ diff --git a/src/r_plane.c b/src/r_plane.c index 92795d0fb..6c238896c 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -19,6 +19,7 @@ #include "p_setup.h" // levelflats #include "p_slopes.h" #include "r_data.h" +#include "r_textures.h" #include "r_local.h" #include "r_state.h" #include "r_splats.h" // faB(21jan):testing @@ -644,188 +645,6 @@ static void R_DrawSkyPlane(visplane_t *pl) } } -// -// R_CheckPowersOfTwo -// -// Self-explanatory? -// -boolean R_CheckPowersOfTwo(void) -{ - boolean wpow2 = (!(ds_flatwidth & (ds_flatwidth - 1))); - boolean hpow2 = (!(ds_flatheight & (ds_flatheight - 1))); - - // Initially, the flat isn't powers-of-two-sized. - ds_powersoftwo = false; - - // But if the width and height are powers of two, - // and are EQUAL, then it's okay :] - if ((ds_flatwidth == ds_flatheight) && (wpow2 && hpow2)) - ds_powersoftwo = true; - - // Just return ds_powersoftwo. - return ds_powersoftwo; -} - -// -// R_CheckFlatLength -// -// Determine the flat's dimensions from the lump length. -// -void R_CheckFlatLength(size_t size) -{ - switch (size) - { - case 4194304: // 2048x2048 lump - nflatmask = 0x3FF800; - nflatxshift = 21; - nflatyshift = 10; - nflatshiftup = 5; - ds_flatwidth = ds_flatheight = 2048; - break; - case 1048576: // 1024x1024 lump - nflatmask = 0xFFC00; - nflatxshift = 22; - nflatyshift = 12; - nflatshiftup = 6; - ds_flatwidth = ds_flatheight = 1024; - break; - case 262144:// 512x512 lump - nflatmask = 0x3FE00; - nflatxshift = 23; - nflatyshift = 14; - nflatshiftup = 7; - ds_flatwidth = ds_flatheight = 512; - break; - case 65536: // 256x256 lump - nflatmask = 0xFF00; - nflatxshift = 24; - nflatyshift = 16; - nflatshiftup = 8; - ds_flatwidth = ds_flatheight = 256; - break; - case 16384: // 128x128 lump - nflatmask = 0x3F80; - nflatxshift = 25; - nflatyshift = 18; - nflatshiftup = 9; - ds_flatwidth = ds_flatheight = 128; - break; - case 1024: // 32x32 lump - nflatmask = 0x3E0; - nflatxshift = 27; - nflatyshift = 22; - nflatshiftup = 11; - ds_flatwidth = ds_flatheight = 32; - break; - default: // 64x64 lump - nflatmask = 0xFC0; - nflatxshift = 26; - nflatyshift = 20; - nflatshiftup = 10; - ds_flatwidth = ds_flatheight = 64; - break; - } -} - -// -// R_GenerateFlat -// -// Generate a flat from specified width and height. -// -static UINT8 *R_GenerateFlat(UINT16 width, UINT16 height) -{ - UINT8 *flat = Z_Malloc(width * height, PU_LEVEL, NULL); - memset(flat, TRANSPARENTPIXEL, width * height); - return flat; -} - -// -// R_GetTextureFlat -// -// Convert a texture or patch to a flat. -// -static UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boolean ispng) -{ - UINT8 *flat; - textureflat_t *texflat = &texflats[levelflat->u.texture.num]; - patch_t *patch = NULL; - boolean texturechanged = (leveltexture ? (levelflat->u.texture.num != levelflat->u.texture.lastnum) : false); - - (void)ispng; - - // Check if the texture changed. - if (leveltexture && (!texturechanged)) - { - if (texflat != NULL && texflat->flat) - { - flat = texflat->flat; - ds_flatwidth = texflat->width; - ds_flatheight = texflat->height; - texturechanged = false; - } - else - texturechanged = true; - } - - // If the texture changed, or the patch doesn't exist, convert either of them to a flat. - if (levelflat->flatpatch == NULL || texturechanged) - { - // Level texture - if (leveltexture) - { - texture_t *texture = textures[levelflat->u.texture.num]; - texflat->width = ds_flatwidth = texture->width; - texflat->height = ds_flatheight = texture->height; - - texflat->flat = R_GenerateFlat(ds_flatwidth, ds_flatheight); - R_TextureToFlat(levelflat->u.texture.num, texflat->flat); - flat = texflat->flat; - - levelflat->flatpatch = flat; - levelflat->width = ds_flatwidth; - levelflat->height = ds_flatheight; - } - // Patch (never happens yet) - else - { - patch = (patch_t *)ds_source; -#ifndef NO_PNG_LUMPS - if (ispng) - { - levelflat->flatpatch = R_PNGToFlat(&levelflat->width, &levelflat->height, ds_source, W_LumpLength(levelflat->u.flat.lumpnum)); - levelflat->topoffset = levelflat->leftoffset = 0; - ds_flatwidth = levelflat->width; - ds_flatheight = levelflat->height; - } - else -#endif - { - levelflat->width = ds_flatwidth = SHORT(patch->width); - levelflat->height = ds_flatheight = SHORT(patch->height); - - levelflat->topoffset = patch->topoffset * FRACUNIT; - levelflat->leftoffset = patch->leftoffset * FRACUNIT; - - levelflat->flatpatch = R_GenerateFlat(ds_flatwidth, ds_flatheight); - R_PatchToFlat(patch, levelflat->flatpatch); - } - flat = levelflat->flatpatch; - } - } - else - { - flat = levelflat->flatpatch; - ds_flatwidth = levelflat->width; - ds_flatheight = levelflat->height; - } - - xoffs += levelflat->leftoffset; - yoffs += levelflat->topoffset; - - levelflat->u.texture.lastnum = levelflat->u.texture.num; - return flat; -} - static void R_SlopeVectors(visplane_t *pl, INT32 i, float fudge) { // Potentially override other stuff for now cus we're mean. :< But draw a slope plane! @@ -919,12 +738,11 @@ d.z = (v1.x * v2.y) - (v1.y * v2.x) void R_DrawSinglePlane(visplane_t *pl) { - UINT8 *flat; + levelflat_t *levelflat; INT32 light = 0; INT32 x; INT32 stop, angle; ffloor_t *rover; - levelflat_t *levelflat; int type; int spanfunctype = BASEDRAWFUNC; @@ -1077,30 +895,15 @@ void R_DrawSinglePlane(visplane_t *pl) case LEVELFLAT_NONE: return; case LEVELFLAT_FLAT: - ds_source = W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE); + ds_source = (UINT8 *)R_GetFlat(levelflat->u.flat.lumpnum); R_CheckFlatLength(W_LumpLength(levelflat->u.flat.lumpnum)); // Raw flats always have dimensions that are powers-of-two numbers. ds_powersoftwo = true; break; default: - switch (type) - { - case LEVELFLAT_TEXTURE: - /* Textures get cached differently and don't need ds_source */ - ds_source = R_GetTextureFlat(levelflat, true, false); - break; - default: - ds_source = W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_STATIC); - flat = R_GetTextureFlat(levelflat, false, -#ifndef NO_PNG_LUMPS - ( type == LEVELFLAT_PNG ) -#else - false -#endif - ); - Z_ChangeTag(ds_source, PU_CACHE); - ds_source = flat; - } + ds_source = (UINT8 *)R_GetLevelFlat(levelflat); + if (!ds_source) + return; // Check if this texture or patch has power-of-two dimensions. if (R_CheckPowersOfTwo()) R_CheckFlatLength(ds_flatwidth * ds_flatheight); diff --git a/src/r_plane.h b/src/r_plane.h index 67fa19f38..15f7f07f3 100644 --- a/src/r_plane.h +++ b/src/r_plane.h @@ -16,6 +16,7 @@ #include "screen.h" // needs MAXVIDWIDTH/MAXVIDHEIGHT #include "r_data.h" +#include "r_textures.h" #include "p_polyobj.h" #define MAXVISPLANES 512 diff --git a/src/r_portal.h b/src/r_portal.h index 406b98d10..e665a26e6 100644 --- a/src/r_portal.h +++ b/src/r_portal.h @@ -15,6 +15,7 @@ #define __R_PORTAL__ #include "r_data.h" +#include "r_textures.h" #include "r_plane.h" // visplanes /** Portal structure for the software renderer. diff --git a/src/r_skins.h b/src/r_skins.h index 45c90bdb4..fbbb38743 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -17,7 +17,8 @@ #include "info.h" #include "sounds.h" #include "d_player.h" // skinflags -#include "r_patch.h" // spriteinfo_t +#include "r_patch.h" +#include "r_picformats.h" // spriteinfo_t #include "r_defs.h" // spritedef_t /// Defaults diff --git a/src/r_textures.c b/src/r_textures.c new file mode 100644 index 000000000..f0d3021f0 --- /dev/null +++ b/src/r_textures.c @@ -0,0 +1,1653 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 by Sonic Team Junior. +// +// 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_textures.c +/// \brief Texture generation. + +#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 "r_textures.h" +#include "r_patch.h" +#include "r_picformats.h" +#include "w_wad.h" +#include "z_zone.h" +#include "p_setup.h" // levelflats +#include "byteptr.h" +#include "dehacked.h" + +// I don't know what this is even for, but r_data.c had it. +#ifdef _WIN32 +#include // alloca(sizeof) +#endif + +#ifdef HWRENDER +#include "hardware/hw_glob.h" // HWR_LoadMapTextures +#endif + +#include + +// +// TEXTURE_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. +// + +INT32 numtextures = 0; // total number of textures found, +// size of following tables + +texture_t **textures = NULL; +UINT32 **texturecolumnofs; // column offset lookup table for each texture +UINT8 **texturecache; // graphics data for each generated full-size texture + +INT32 *texturewidth; +fixed_t *textureheight; // needed for texture pegging + +INT32 *texturetranslation; + +// 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, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) +{ + INT32 count, position; + UINT8 *source; + INT32 topdelta, prevdelta = -1; + INT32 originy = originPatch->originy; + + (void)patchheight; // This parameter is unused + + 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; + source -= position; // start further down the column + 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_DrawFlippedColumnInCache +// Similar to R_DrawColumnInCache; it draws the column inverted, however. +// +static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) +{ + INT32 count, position; + UINT8 *source, *dest; + INT32 topdelta, prevdelta = -1; + INT32 originy = originPatch->originy; + + while (patch->topdelta != 0xff) + { + topdelta = patch->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + topdelta = patchheight-patch->length-topdelta; + source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1) + count = patch->length; + position = originy + topdelta; + + if (position < 0) + { + count += position; + source += position; // start further UP the column + position = 0; + } + + if (position + count > cacheheight) + count = cacheheight - position; + + dest = cache + position; + if (count > 0) + { + for (; dest < cache + position + count; --source) + *dest++ = *source; + } + + patch = (column_t *)((UINT8 *)patch + patch->length + 4); + } +} + +// +// R_DrawBlendColumnInCache +// Draws a translucent column into the cache, applying a half-cooked equation to get a proper translucency value (Needs code in R_GenerateTexture()). +// +static inline void R_DrawBlendColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) +{ + INT32 count, position; + UINT8 *source, *dest; + INT32 topdelta, prevdelta = -1; + INT32 originy = originPatch->originy; + + (void)patchheight; // This parameter is unused + + 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; + source -= position; // start further down the column + position = 0; + } + + if (position + count > cacheheight) + count = cacheheight - position; + + dest = cache + position; + if (count > 0) + { + for (; dest < cache + position + count; source++, dest++) + if (*source != 0xFF) + *dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha); + } + + patch = (column_t *)((UINT8 *)patch + patch->length + 4); + } +} + +// +// R_DrawBlendFlippedColumnInCache +// Similar to the one above except that the column is inverted. +// +static inline void R_DrawBlendFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight) +{ + INT32 count, position; + UINT8 *source, *dest; + INT32 topdelta, prevdelta = -1; + INT32 originy = originPatch->originy; + + while (patch->topdelta != 0xff) + { + topdelta = patch->topdelta; + if (topdelta <= prevdelta) + topdelta += prevdelta; + prevdelta = topdelta; + topdelta = patchheight-patch->length-topdelta; + source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1) + count = patch->length; + position = originy + topdelta; + + if (position < 0) + { + count += position; + source += position; // start further UP the column + position = 0; + } + + if (position + count > cacheheight) + count = cacheheight - position; + + dest = cache + position; + if (count > 0) + { + for (; dest < cache + position + count; --source, dest++) + if (*source != 0xFF) + *dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha); + } + + 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 +// been simplified for the sake of highcolor (lol), dynamic ligthing, & speed. +// +// This is not optimised, but it's supposed to be executed only once +// per level, when enough memory is available. +// +UINT8 *R_GenerateTexture(size_t texnum) +{ + UINT8 *block; + UINT8 *blocktex; + texture_t *texture; + texpatch_t *patch; + softwarepatch_t *realpatch; + UINT8 *pdata; + int x, x1, x2, i, width, height; + size_t blocksize; + column_t *patchcol; + UINT8 *colofs; + + UINT16 wadnum; + lumpnum_t lumpnum; + size_t lumplength; + + 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; + + wadnum = patch->wad; + lumpnum = patch->lump; + lumplength = W_LumpLengthPwad(wadnum, lumpnum); + pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + realpatch = (softwarepatch_t *)pdata; + +#ifndef NO_PNG_LUMPS + if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) + goto multipatch; +#endif +#ifdef WALLFLATS + if (texture->type == TEXTURETYPE_FLAT) + goto multipatch; +#endif + + // Check the patch for holes. + if (texture->width > SHORT(realpatch->width) || texture->height > SHORT(realpatch->height)) + holey = true; + colofs = (UINT8 *)realpatch->columnofs; + for (x = 0; x < texture->width && !holey; x++) + { + column_t *col = (column_t *)((UINT8 *)realpatch + LONG(*(UINT32 *)&colofs[x<<2])); + 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; + texture->flip = patch->flip; + blocksize = lumplength; + 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 = (block + 8); + texturecolumnofs[texnum] = (UINT32 *)colofs; + blocktex = block; + if (patch->flip & 1) // flip the patch horizontally + { + UINT8 *realcolofs = (UINT8 *)realpatch->columnofs; + for (x = 0; x < texture->width; x++) + *(UINT32 *)&colofs[x<<2] = realcolofs[( texture->width-1-x )<<2]; // swap with the offset of the other side of the texture + } + // we can't as easily flip the patch vertically sadly though, + // we have wait until the texture itself is drawn to do that + for (x = 0; x < texture->width; x++) + *(UINT32 *)&colofs[x<<2] = LONG(LONG(*(UINT32 *)&colofs[x<<2]) + 3); + goto done; + } + + // Otherwise, do multipatch format. + } + + // multi-patch textures (or 'composite') + multipatch: + texture->holes = false; + texture->flip = 0; + blocksize = (texture->width * 4) + (texture->width * texture->height); + texturememory += blocksize; + block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]); + + memset(block, TRANSPARENTPIXEL, blocksize+1); // Transparency hack + + // columns lookup table + colofs = block; + texturecolumnofs[texnum] = (UINT32 *)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++) + { + boolean dealloc = true; + static void (*ColumnDrawerPointer)(column_t *, UINT8 *, texpatch_t *, INT32, INT32); // Column drawing function pointer. + if (patch->style != AST_COPY) + ColumnDrawerPointer = (patch->flip & 2) ? R_DrawBlendFlippedColumnInCache : R_DrawBlendColumnInCache; + else + ColumnDrawerPointer = (patch->flip & 2) ? R_DrawFlippedColumnInCache : R_DrawColumnInCache; + + wadnum = patch->wad; + lumpnum = patch->lump; + pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + lumplength = W_LumpLengthPwad(wadnum, lumpnum); + realpatch = (softwarepatch_t *)pdata; + dealloc = true; + +#ifndef NO_PNG_LUMPS + if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength)) + { + // Dummy variables. + INT32 pngwidth, pngheight; + realpatch = (softwarepatch_t *)Picture_PNGConvert((UINT8 *)realpatch, PICFMT_PATCH, &pngwidth, &pngheight, NULL, NULL, lumplength, NULL, 0); + } + else +#endif +#ifdef WALLFLATS + if (texture->type == TEXTURETYPE_FLAT) + realpatch = (softwarepatch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_PATCH, 0, NULL, texture->width, texture->height, 0, 0, 0); + else +#endif + { + (void)lumplength; + dealloc = false; + } + + x1 = patch->originx; + width = SHORT(realpatch->width); + height = SHORT(realpatch->height); + x2 = x1 + width; + + if (x1 > texture->width || x2 < 0) + { + if (dealloc) + Z_Free(realpatch); + continue; // patch not located within texture's x bounds, ignore + } + + if (patch->originy > texture->height || (patch->originy + height) < 0) + { + if (dealloc) + Z_Free(realpatch); + continue; // patch not located within texture's y bounds, ignore + } + + // patch is actually inside the texture! + // now check if texture is partly off-screen and adjust accordingly + + // left edge + if (x1 < 0) + x = 0; + else + x = x1; + + // right edge + if (x2 > texture->width) + x2 = texture->width; + + for (; x < x2; x++) + { + if (patch->flip & 1) + patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[(x1+width-1)-x])); + else + patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[x-x1])); + + // generate column ofset lookup + *(UINT32 *)&colofs[x<<2] = LONG((x * texture->height) + (texture->width*4)); + ColumnDrawerPointer(patchcol, block + LONG(*(UINT32 *)&colofs[x<<2]), patch, texture->height, height); + } + + if (dealloc) + Z_Free(realpatch); + } + +done: + // Now that the texture has been built in column cache, it is purgable from zone memory. + Z_ChangeTag(block, PU_CACHE); + return blocktex; +} + +// +// R_GenerateTextureAsFlat +// +// Generates a flat picture for a texture. +// +UINT8 *R_GenerateTextureAsFlat(size_t texnum) +{ + texture_t *texture = textures[texnum]; + UINT8 *converted = NULL; + size_t size = (texture->width * texture->height); + + // The flat picture for this texture was not generated yet. + if (!texture->flat) + { + // Well, let's do it now, then. + texture->flat = Z_Malloc(size, PU_STATIC, NULL); + + // Picture_TextureToFlat handles everything for us. + converted = (UINT8 *)Picture_TextureToFlat(texnum); + M_Memcpy(texture->flat, converted, size); + Z_Free(converted); + } + + return texture->flat; +} + +// +// 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]; +} + +// +// 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); +} + +// +// R_GetColumn +// +UINT8 *R_GetColumn(fixed_t tex, INT32 col) +{ + UINT8 *data; + INT32 width = texturewidth[tex]; + + if (width & (width - 1)) + col = (UINT32)col % width; + else + col &= (width - 1); + + data = texturecache[tex]; + if (!data) + data = R_GenerateTexture(tex); + + return data + LONG(texturecolumnofs[tex][col]); +} + +void *R_GetFlat(lumpnum_t flatlumpnum) +{ + return W_CacheLumpNum(flatlumpnum, PU_CACHE); +} + +// +// R_GetLevelFlat +// +// If needed, convert a texture or patch to a flat. +// +void *R_GetLevelFlat(levelflat_t *levelflat) +{ + boolean isleveltexture = (levelflat->type == LEVELFLAT_TEXTURE); + texture_t *texture = (isleveltexture ? textures[levelflat->u.texture.num] : NULL); + boolean texturechanged = (isleveltexture ? (levelflat->u.texture.num != levelflat->u.texture.lastnum) : false); + UINT8 *flatdata = NULL; + + // Check if the texture changed. + if (isleveltexture && (!texturechanged)) + { + if (texture->flat) + { + flatdata = texture->flat; + ds_flatwidth = texture->width; + ds_flatheight = texture->height; + texturechanged = false; + } + else + texturechanged = true; + } + + // If the texture changed, or the flat wasn't generated, convert. + if (levelflat->picture == NULL || texturechanged) + { + // Level texture + if (isleveltexture) + { + levelflat->picture = R_GenerateTextureAsFlat(levelflat->u.texture.num); + ds_flatwidth = levelflat->width = texture->width; + ds_flatheight = levelflat->height = texture->height; + } + else + { +#ifndef NO_PNG_LUMPS + if (levelflat->type == LEVELFLAT_PNG) + { + INT32 pngwidth, pngheight; + + levelflat->picture = Picture_PNGConvert(W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE), PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); + levelflat->width = (UINT16)pngwidth; + levelflat->height = (UINT16)pngheight; + + ds_flatwidth = levelflat->width; + ds_flatheight = levelflat->height; + } + else +#endif + if (levelflat->type == LEVELFLAT_PATCH) + { + UINT8 *converted; + size_t size; + softwarepatch_t *patch = W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE); + + levelflat->width = ds_flatwidth = SHORT(patch->width); + levelflat->height = ds_flatheight = SHORT(patch->height); + + levelflat->picture = Z_Malloc(levelflat->width * levelflat->height, PU_LEVEL, NULL); + converted = Picture_FlatConvert(PICFMT_PATCH, patch, PICFMT_FLAT, 0, &size, levelflat->width, levelflat->height, patch->topoffset, patch->leftoffset, 0); + M_Memcpy(levelflat->picture, converted, size); + Z_Free(converted); + } + } + } + else + { + ds_flatwidth = levelflat->width; + ds_flatheight = levelflat->height; + } + + levelflat->u.texture.lastnum = levelflat->u.texture.num; + + if (flatdata == NULL) + flatdata = levelflat->picture; + return flatdata; +} + +// +// R_CheckPowersOfTwo +// +// Self-explanatory? +// +boolean R_CheckPowersOfTwo(void) +{ + boolean wpow2 = (!(ds_flatwidth & (ds_flatwidth - 1))); + boolean hpow2 = (!(ds_flatheight & (ds_flatheight - 1))); + + // Initially, the flat isn't powers-of-two-sized. + ds_powersoftwo = false; + + // But if the width and height are powers of two, + // and are EQUAL, then it's okay :] + if ((ds_flatwidth == ds_flatheight) && (wpow2 && hpow2)) + ds_powersoftwo = true; + + // Just return ds_powersoftwo. + return ds_powersoftwo; +} + +// +// R_CheckFlatLength +// +// Determine the flat's dimensions from its lump length. +// +void R_CheckFlatLength(size_t size) +{ + switch (size) + { + case 4194304: // 2048x2048 lump + nflatmask = 0x3FF800; + nflatxshift = 21; + nflatyshift = 10; + nflatshiftup = 5; + ds_flatwidth = ds_flatheight = 2048; + break; + case 1048576: // 1024x1024 lump + nflatmask = 0xFFC00; + nflatxshift = 22; + nflatyshift = 12; + nflatshiftup = 6; + ds_flatwidth = ds_flatheight = 1024; + break; + case 262144:// 512x512 lump + nflatmask = 0x3FE00; + nflatxshift = 23; + nflatyshift = 14; + nflatshiftup = 7; + ds_flatwidth = ds_flatheight = 512; + break; + case 65536: // 256x256 lump + nflatmask = 0xFF00; + nflatxshift = 24; + nflatyshift = 16; + nflatshiftup = 8; + ds_flatwidth = ds_flatheight = 256; + break; + case 16384: // 128x128 lump + nflatmask = 0x3F80; + nflatxshift = 25; + nflatyshift = 18; + nflatshiftup = 9; + ds_flatwidth = ds_flatheight = 128; + break; + case 1024: // 32x32 lump + nflatmask = 0x3E0; + nflatxshift = 27; + nflatyshift = 22; + nflatshiftup = 11; + ds_flatwidth = ds_flatheight = 32; + break; + default: // 64x64 lump + nflatmask = 0xFC0; + nflatxshift = 26; + nflatyshift = 20; + nflatshiftup = 10; + ds_flatwidth = ds_flatheight = 64; + break; + } +} + +// +// 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_textures.h so they're "private" +int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum); +void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *index); + +#ifdef WALLFLATS +static INT32 +Rloadflats (INT32 i, INT32 w) +{ + UINT16 j; + UINT16 texstart, texend; + texture_t *texture; + texpatch_t *patch; + + // Yes + if (wadfiles[w]->type == RET_PK3) + { + texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); + } + else + { + texstart = W_CheckNumForMarkerStartPwad("F_START", (UINT16)w, 0); + texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); + } + + if (!( texstart == INT16_MAX || texend == INT16_MAX )) + { + // Work through each lump between the markers in the WAD. + for (j = 0; j < (texend - texstart); j++) + { + UINT8 *flatlump; + UINT16 wadnum = (UINT16)w; + lumpnum_t lumpnum = texstart + j; + size_t lumplength; + size_t flatsize = 0; + + if (wadfiles[w]->type == RET_PK3) + { + if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder + continue; // If it is then SKIP IT + } + + flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + lumplength = W_LumpLengthPwad(wadnum, lumpnum); + + switch (lumplength) + { + case 4194304: // 2048x2048 lump + flatsize = 2048; + break; + case 1048576: // 1024x1024 lump + flatsize = 1024; + break; + case 262144:// 512x512 lump + flatsize = 512; + break; + case 65536: // 256x256 lump + flatsize = 256; + break; + case 16384: // 128x128 lump + flatsize = 128; + break; + case 1024: // 32x32 lump + flatsize = 32; + break; + default: // 64x64 lump + flatsize = 64; + break; + } + + //CONS_Printf("\n\"%s\" is a flat, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),flatsize,flatsize); + texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); + + // Set texture properties. + M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); + +#ifndef NO_PNG_LUMPS + if (Picture_IsLumpPNG((UINT8 *)flatlump, lumplength)) + { + INT16 width, height; + Picture_PNGDimensions((UINT8 *)flatlump, &width, &height, lumplength); + texture->width = width; + texture->height = height; + } + else +#endif + texture->width = texture->height = flatsize; + + texture->type = TEXTURETYPE_FLAT; + texture->patchcount = 1; + texture->holes = false; + texture->flip = 0; + + // Allocate information for the texture's patches. + patch = &texture->patches[0]; + + patch->originx = patch->originy = 0; + patch->wad = (UINT16)w; + patch->lump = texstart + j; + patch->flip = 0; + + Z_Unlock(flatlump); + + texturewidth[i] = texture->width; + textureheight[i] = texture->height << FRACBITS; + i++; + } + } + + return i; +} +#endif/*WALLFLATS*/ + +#define TX_START "TX_START" +#define TX_END "TX_END" + +static INT32 +Rloadtextures (INT32 i, INT32 w) +{ + UINT16 j; + UINT16 texstart, texend, texturesLumpPos; + texture_t *texture; + softwarepatch_t *patchlump; + texpatch_t *patch; + + // Get the lump numbers for the markers in the WAD, if they exist. + 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_CheckNumForMarkerStartPwad(TX_START, (UINT16)w, 0); + texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); + if (texturesLumpPos != INT16_MAX) + R_ParseTEXTURESLump(w, texturesLumpPos, &i); + } + + if (!( texstart == INT16_MAX || texend == INT16_MAX )) + { + // Work through each lump between the markers in the WAD. + for (j = 0; j < (texend - texstart); j++) + { + UINT16 wadnum = (UINT16)w; + lumpnum_t lumpnum = texstart + j; +#ifndef NO_PNG_LUMPS + size_t lumplength; +#endif + + if (wadfiles[w]->type == RET_PK3) + { + if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder + continue; // If it is then SKIP IT + } + + patchlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); +#ifndef NO_PNG_LUMPS + lumplength = W_LumpLengthPwad(wadnum, lumpnum); +#endif + + //CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height); + texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL); + + // Set texture properties. + M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); + +#ifndef NO_PNG_LUMPS + if (Picture_IsLumpPNG((UINT8 *)patchlump, lumplength)) + { + INT16 width, height; + Picture_PNGDimensions((UINT8 *)patchlump, &width, &height, lumplength); + texture->width = width; + texture->height = height; + } + else +#endif + { + texture->width = SHORT(patchlump->width); + texture->height = SHORT(patchlump->height); + } + + texture->type = TEXTURETYPE_SINGLEPATCH; + texture->patchcount = 1; + texture->holes = false; + texture->flip = 0; + + // Allocate information for the texture's patches. + patch = &texture->patches[0]; + + patch->originx = patch->originy = 0; + patch->wad = (UINT16)w; + patch->lump = texstart + j; + patch->flip = 0; + + Z_Unlock(patchlump); + + texturewidth[i] = texture->width; + textureheight[i] = texture->height << FRACBITS; + i++; + } + } + + return i; +} + +// +// R_LoadTextures +// Initializes the texture list with the textures from the world map. +// +void R_LoadTextures(void) +{ + INT32 i, w; + UINT16 j; + UINT16 texstart, texend, texturesLumpPos; + + // 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); + } + + // 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++) + { +#ifdef WALLFLATS + // Count flats + if (wadfiles[w]->type == RET_PK3) + { + texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart); + } + else + { + texstart = W_CheckNumForMarkerStartPwad("F_START", (UINT16)w, 0); + texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart); + } + + if (!( texstart == INT16_MAX || texend == INT16_MAX )) + { + // PK3s have subfolders, so we can't just make a simple sum + if (wadfiles[w]->type == RET_PK3) + { + for (j = texstart; j < texend; j++) + { + if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it + numtextures++; + } + } + else // Add all the textures between F_START and F_END + { + numtextures += (UINT32)(texend - texstart); + } + } +#endif/*WALLFLATS*/ + + // Count the textures from TEXTURES lumps + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0); + while (texturesLumpPos != INT16_MAX) + { + numtextures += R_CountTexturesInTEXTURESLump((UINT16)w, (UINT16)texturesLumpPos); + texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1); + } + + // Count single-patch textures + if (wadfiles[w]->type == RET_PK3) + { + texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0); + texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart); + } + else + { + texstart = W_CheckNumForMarkerStartPwad(TX_START, (UINT16)w, 0); + texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0); + } + + if (texstart == INT16_MAX || texend == INT16_MAX) + continue; + + // PK3s have subfolders, so we can't just make a simple sum + if (wadfiles[w]->type == RET_PK3) + { + for (j = texstart; j < texend; j++) + { + if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it + numtextures++; + } + } + else // Add all the textures between TX_START and TX_END + { + numtextures += (UINT32)(texend - texstart); + } + } + + // If no textures found by this point, bomb out + if (!numtextures) + 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); + + // Allocate texture column offset table. + texturecolumnofs = (void *)((UINT8 *)textures + (numtextures * sizeof(void *))); + // Allocate texture referencing cache. + texturecache = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 2)); + // Allocate texture width table. + texturewidth = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 3)); + // Allocate texture height table. + 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++) + { +#ifdef WALLFLATS + i = Rloadflats(i, w); +#endif + i = Rloadtextures(i, w); + } + +#ifdef HWRENDER + if (rendermode == render_opengl) + HWR_LoadMapTextures(numtextures); +#endif +} + +static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch) +{ + char *texturesToken; + size_t texturesTokenLength; + char *endPos; + char *patchName = NULL; + INT16 patchXPos; + INT16 patchYPos; + UINT8 flip = 0; + UINT8 alpha = 255; + enum patchalphastyle style = AST_COPY; + 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); + + // Patch parameters block (OPTIONAL) + // added by Monster Iestyn (22/10/16) + + // Left Curly Brace + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + ; // move on and ignore, R_ParseTextures will deal with this + else + { + if (strcmp(texturesToken,"{")==0) + { + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters should be",patchName); + } + while (strcmp(texturesToken,"}")!=0) + { + if (stricmp(texturesToken, "ALPHA")==0) + { + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + alpha = 255*strtof(texturesToken, NULL); + } + else if (stricmp(texturesToken, "STYLE")==0) + { + Z_Free(texturesToken); + texturesToken = M_GetToken(NULL); + if (stricmp(texturesToken, "TRANSLUCENT")==0) + style = AST_TRANSLUCENT; + else if (stricmp(texturesToken, "ADD")==0) + style = AST_ADD; + else if (stricmp(texturesToken, "SUBTRACT")==0) + style = AST_SUBTRACT; + else if (stricmp(texturesToken, "REVERSESUBTRACT")==0) + style = AST_REVERSESUBTRACT; + else if (stricmp(texturesToken, "MODULATE")==0) + style = AST_MODULATE; + } + else if (stricmp(texturesToken, "FLIPX")==0) + flip |= 1; + else if (stricmp(texturesToken, "FLIPY")==0) + flip |= 2; + Z_Free(texturesToken); + + texturesToken = M_GetToken(NULL); + if (texturesToken == NULL) + { + I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters or right curly brace should be",patchName); + } + } + } + else + { + // this is not what we wanted... + // undo last read so R_ParseTextures can re-get the token for its own purposes + M_UnGetToken(); + } + 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; + resultPatch->flip = flip; + resultPatch->alpha = alpha; + resultPatch->style = style; + // 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; + size_t texturesTokenLength; + char *endPos; + INT32 newTextureWidth; + INT32 newTextureHeight; + texture_t *resultTexture = NULL; + texpatch_t *newPatch; + char newTextureName[9]; // no longer dynamically allocated + + // 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 + { + 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 + } + 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; + resultTexture->type = TEXTURETYPE_COMPOSITE; + } + 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. +int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum) +{ + 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. + texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); + // 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. + texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum); + 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) + { + if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) + { + numTexturesInLump++; + Z_Free(texturesToken); + R_ParseTexture(false); + } + else + { + I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); + } + texturesToken = M_GetToken(NULL); + } + Z_Free(texturesToken); + Z_Free((void *)texturesText); + + return numTexturesInLump; +} + +// Parses the TEXTURES lump... for real, this time. +void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex) +{ + 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. + texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); + // 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. + texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum); + 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) + { + if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0) + { + Z_Free(texturesToken); + // Get the new texture + newTexture = R_ParseTexture(true); + // Store the new texture + textures[*texindex] = newTexture; + texturewidth[*texindex] = newTexture->width; + textureheight[*texindex] = newTexture->height << FRACBITS; + // Increment i back in R_LoadTextures() + (*texindex)++; + } + else + { + I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken); + } + texturesToken = M_GetToken(NULL); + } + Z_Free(texturesToken); + Z_Free((void *)texturesText); +} + +// Search for flat name. +lumpnum_t R_GetFlatNumForName(const char *name) +{ + 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_CheckNumForMarkerStartPwad("F_START", (UINT16)i, 0)) == INT16_MAX) + { + if ((start = W_CheckNumForMarkerStartPwad("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; + } + + return lump; +} + +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; +} diff --git a/src/r_textures.h b/src/r_textures.h new file mode 100644 index 000000000..74a94a9ed --- /dev/null +++ b/src/r_textures.h @@ -0,0 +1,103 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1993-1996 by id Software, Inc. +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2020 by Sonic Team Junior. +// +// 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_textures.h +/// \brief Texture generation. + +#ifndef __R_TEXTURES__ +#define __R_TEXTURES__ + +#include "r_defs.h" +#include "r_state.h" +#include "p_setup.h" // levelflats +#include "r_data.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +// A single patch from a texture definition, +// basically a rectangular area within +// the texture rectangle. +typedef struct +{ + // Block origin (always UL), which has already accounted for the internal origin of the patch. + INT16 originx, originy; + UINT16 wad, lump; + UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both + UINT8 alpha; // Translucency value + enum patchalphastyle style; +} texpatch_t; + +// texture type +enum +{ + TEXTURETYPE_UNKNOWN, + TEXTURETYPE_SINGLEPATCH, + TEXTURETYPE_COMPOSITE, +#ifdef WALLFLATS + TEXTURETYPE_FLAT, +#endif +}; + +// A texture_t describes a rectangular texture, +// which is composed of one or more texpatch_t structures +// that arrange graphic patches. +typedef struct +{ + // Keep name for switch changing, etc. + char name[8]; + UINT8 type; // TEXTURETYPE_ + INT16 width, height; + boolean holes; + UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both + void *flat; // The texture, as a flat. + + // All the patches[patchcount] are drawn back to front into the cached texture. + INT16 patchcount; + texpatch_t patches[0]; +} texture_t; + +// all loaded and prepared textures from the start of the game +extern texture_t **textures; + +extern INT32 *texturewidth; +extern fixed_t *textureheight; // needed for texture pegging + +extern UINT32 **texturecolumnofs; // column offset lookup table for each texture +extern UINT8 **texturecache; // graphics data for each generated full-size texture + +// Load TEXTURES definitions, create lookup tables +void R_LoadTextures(void); +void R_FlushTextureCache(void); + +// Texture generation +UINT8 *R_GenerateTexture(size_t texnum); +UINT8 *R_GenerateTextureAsFlat(size_t texnum); +INT32 R_GetTextureNum(INT32 texnum); +void R_CheckTextureCache(INT32 tex); +void R_ClearTextureNumCache(boolean btell); + +// Retrieve texture data. +void *R_GetLevelFlat(levelflat_t *levelflat); +UINT8 *R_GetColumn(fixed_t tex, INT32 col); +void *R_GetFlat(lumpnum_t flatnum); + +boolean R_CheckPowersOfTwo(void); +void R_CheckFlatLength(size_t size); + +// Returns the texture number for the texture name. +INT32 R_TextureNumForName(const char *name); +INT32 R_CheckTextureNumForName(const char *name); +lumpnum_t R_GetFlatNumForName(const char *name); + +extern INT32 numtextures; + +#endif diff --git a/src/r_things.c b/src/r_things.c index 0636a880d..9b32a6d5f 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -25,6 +25,7 @@ #include "i_system.h" #include "r_things.h" #include "r_patch.h" +#include "r_picformats.h" #include "r_plane.h" #include "r_portal.h" #include "p_tick.h" @@ -280,10 +281,14 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 softwarepatch_t *png = W_CacheLumpNumPwad(wadnum, l, PU_STATIC); size_t len = W_LumpLengthPwad(wadnum, l); // lump is a png so convert it - if (R_IsLumpPNG((UINT8 *)png, len)) + if (Picture_IsLumpPNG((UINT8 *)png, len)) { - png = R_PNGToPatch((UINT8 *)png, len, NULL); - M_Memcpy(&patch, png, sizeof(INT16)*4); + // Dummy variables. + INT32 pngwidth, pngheight; + INT16 topoffset, leftoffset; + patch_t *converted = (patch_t *)Picture_PNGConvert((UINT8 *)png, PICFMT_PATCH, &pngwidth, &pngheight, &topoffset, &leftoffset, len, NULL, 0); + M_Memcpy(&patch, converted, sizeof(INT16)*4); // only copy the header because that's all we need + Z_Free(converted); } Z_Free(png); } diff --git a/src/r_things.h b/src/r_things.h index 7a0fe3a60..f241a78ba 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -16,6 +16,7 @@ #include "r_plane.h" #include "r_patch.h" +#include "r_picformats.h" #include "r_portal.h" #include "r_defs.h" #include "r_skins.h" diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index 5592de86b..ce359f739 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -281,14 +281,15 @@ + - + @@ -446,13 +447,14 @@ true + - + diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters index db1aa123f..b37f2c695 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj.filters +++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters @@ -471,7 +471,10 @@ Hw_Hardware - + + R_Rend + + R_Rend @@ -943,7 +946,10 @@ Hw_Hardware - + + R_Rend + + R_Rend diff --git a/src/w_wad.c b/src/w_wad.c index 0d329302a..21004e6e8 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -56,6 +56,9 @@ #include "d_clisrv.h" #include "r_defs.h" #include "r_data.h" +#include "r_textures.h" +#include "r_patch.h" +#include "r_picformats.h" #include "i_system.h" #include "md5.h" #include "lua_script.h" @@ -65,7 +68,6 @@ #include "m_misc.h" // M_MapNumber #ifdef HWRENDER -#include "r_data.h" #include "hardware/hw_main.h" #include "hardware/hw_glob.h" #endif @@ -1384,8 +1386,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si #ifdef NO_PNG_LUMPS { size_t bytesread = fread(dest, 1, size, handle); - if (R_IsLumpPNG((UINT8 *)dest, bytesread)) - W_ThrowPNGError(l->fullname, wadfiles[wad]->filename); + if (Picture_IsLumpPNG((UINT8 *)dest, bytesread)) + Picture_ThrowPNGError(l->fullname, wadfiles[wad]->filename); return bytesread; } #else @@ -1426,8 +1428,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si Z_Free(rawData); Z_Free(decData); #ifdef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)dest, size)) - W_ThrowPNGError(l->fullname, wadfiles[wad]->filename); + if (Picture_IsLumpPNG((UINT8 *)dest, size)) + Picture_ThrowPNGError(l->fullname, wadfiles[wad]->filename); #endif return size; #else @@ -1489,8 +1491,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si Z_Free(decData); #ifdef NO_PNG_LUMPS - if (R_IsLumpPNG((UINT8 *)dest, size)) - W_ThrowPNGError(l->fullname, wadfiles[wad]->filename); + if (Picture_IsLumpPNG((UINT8 *)dest, size)) + Picture_ThrowPNGError(l->fullname, wadfiles[wad]->filename); #endif return size; } @@ -1678,10 +1680,11 @@ void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) #ifndef NO_PNG_LUMPS // lump is a png so convert it - if (R_IsLumpPNG((UINT8 *)lumpdata, len)) + if (Picture_IsLumpPNG((UINT8 *)lumpdata, len)) { size_t newlen; - void *converted = R_PNGToPatch((UINT8 *)lumpdata, len, &newlen); + INT32 pngwidth, pngheight; // Dummy variables. + void *converted = Picture_PNGConvert((UINT8 *)lumpdata, PICFMT_PATCH, &pngwidth, &pngheight, NULL, NULL, len, &newlen, 0); ptr = Z_Malloc(newlen, PU_STATIC, NULL); M_Memcpy(ptr, converted, newlen); Z_Free(converted); diff --git a/src/win32/Srb2win-vc10.vcxproj b/src/win32/Srb2win-vc10.vcxproj index 6855a4135..52617037b 100644 --- a/src/win32/Srb2win-vc10.vcxproj +++ b/src/win32/Srb2win-vc10.vcxproj @@ -298,12 +298,13 @@ true + - + @@ -453,13 +454,14 @@ + - + diff --git a/src/win32/Srb2win-vc10.vcxproj.filters b/src/win32/Srb2win-vc10.vcxproj.filters index 4a980c6bd..0689a4ac0 100644 --- a/src/win32/Srb2win-vc10.vcxproj.filters +++ b/src/win32/Srb2win-vc10.vcxproj.filters @@ -469,7 +469,7 @@ Hw_Hardware - + R_Rend @@ -886,7 +886,10 @@ Hw_Hardware - + + R_Rend + + R_Rend diff --git a/src/z_zone.c b/src/z_zone.c index 7a6dfe040..02f9a9920 100644 --- a/src/z_zone.c +++ b/src/z_zone.c @@ -28,6 +28,7 @@ #include "doomdef.h" #include "doomstat.h" #include "r_patch.h" +#include "r_picformats.h" #include "i_system.h" // I_GetFreeMem #include "i_video.h" // rendermode #include "z_zone.h"