1043 lines
25 KiB
C
1043 lines
25 KiB
C
// 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_patch.c
|
|
/// \brief Patch generation.
|
|
|
|
#include "byteptr.h"
|
|
#include "dehacked.h"
|
|
#include "i_video.h"
|
|
#include "r_data.h"
|
|
#include "r_draw.h"
|
|
#include "r_patch.h"
|
|
#include "r_things.h"
|
|
#include "z_zone.h"
|
|
#include "w_wad.h"
|
|
|
|
#ifdef HWRENDER
|
|
#include "hardware/hw_glob.h"
|
|
#endif
|
|
|
|
static unsigned char imgbuf[1<<26];
|
|
|
|
//
|
|
// R_CheckIfPatch
|
|
//
|
|
// Returns true if the lump is a valid patch.
|
|
//
|
|
boolean R_CheckIfPatch(lumpnum_t lump)
|
|
{
|
|
size_t size;
|
|
INT16 width, height;
|
|
patch_t *patch;
|
|
boolean result;
|
|
|
|
size = W_LumpLength(lump);
|
|
|
|
// minimum length of a valid Doom patch
|
|
if (size < 13)
|
|
return false;
|
|
|
|
patch = (patch_t *)W_CacheLumpNum(lump, PU_STATIC);
|
|
|
|
width = patch->width;
|
|
height = patch->height;
|
|
|
|
result = (height > 0 && height <= 16384 && width > 0 && width <= 16384 && width < (INT16)(size / 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)size)
|
|
{
|
|
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 + LONG(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 + LONG(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.
|
|
//
|
|
patch_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 (patch_t *)img;
|
|
}
|
|
|
|
//
|
|
// R_MaskedFlatToPatch
|
|
//
|
|
// Convert a masked flat to a patch.
|
|
// Explanation of "masked" flats in R_PatchToMaskedFlat.
|
|
//
|
|
patch_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 (patch_t *)img;
|
|
}
|
|
|
|
//
|
|
// 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]; // >variable 'skinnumbers' set but not used >compile error when commented out
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
// allocate a spriteinfo
|
|
info = Z_Calloc(sizeof(spriteinfo_t), PU_STATIC, NULL);
|
|
info->available = true;
|
|
|
|
// 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_AnySkinAvailable(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;
|
|
if (allskins[skinnum].localskin)
|
|
skin = &localskins[allskins[skinnum].localnum];
|
|
else
|
|
skin = &skins[allskins[skinnum].localnum];
|
|
M_Memcpy(&skin->sprinfo, 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)
|
|
{
|
|
CONS_Printf("Attempting to parse le SPRTINFO lump for ya..\n");
|
|
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 < SHORT(patch->width))
|
|
{
|
|
INT32 topdelta, prevdelta = -1;
|
|
column = (column_t *)((UINT8 *)patch + LONG(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;
|
|
}
|
|
|
|
patch_t *Patch_GetRotatedSprite(
|
|
spriteframe_t *sprite,
|
|
size_t frame, size_t spriteangle,
|
|
boolean flip, boolean adjustfeet,
|
|
void *info, INT32 rotationangle)
|
|
{
|
|
rotsprite_t *rotsprite;
|
|
spriteinfo_t *sprinfo = (spriteinfo_t *)info;
|
|
INT32 idx = rotationangle;
|
|
UINT8 type = (adjustfeet ? 1 : 0);
|
|
|
|
if (rotationangle < 1 || rotationangle >= ROTANGLES)
|
|
return NULL;
|
|
|
|
rotsprite = sprite->rotated[type][spriteangle];
|
|
|
|
if (rotsprite == NULL)
|
|
{
|
|
rotsprite = RotatedPatch_Create(ROTANGLES);
|
|
sprite->rotated[type][spriteangle] = rotsprite;
|
|
}
|
|
|
|
if (flip)
|
|
idx += rotsprite->angles;
|
|
|
|
if (rotsprite->patches[idx] == NULL)
|
|
{
|
|
patch_t *patch;
|
|
INT32 xpivot = 0, ypivot = 0;
|
|
lumpnum_t lump = sprite->lumppat[spriteangle];
|
|
|
|
if (lump == LUMPERROR)
|
|
return NULL;
|
|
|
|
patch = (patch_t *)W_CacheLumpNum(lump, PU_STATIC);
|
|
|
|
if (sprinfo->available)
|
|
{
|
|
xpivot = sprinfo->pivot[frame].x;
|
|
ypivot = sprinfo->pivot[frame].y;
|
|
}
|
|
else
|
|
{
|
|
xpivot = patch->leftoffset;
|
|
ypivot = patch->height / 2;
|
|
}
|
|
|
|
RotatedPatch_DoRotation(rotsprite, patch, rotationangle, xpivot, ypivot, flip);
|
|
|
|
//BP: we cannot use special tric in hardware mode because feet in ground caused by z-buffer
|
|
if (adjustfeet)
|
|
((patch_t *)rotsprite->patches[idx])->topoffset += FEETADJUST>>FRACBITS;
|
|
|
|
// free image data
|
|
Z_Free(patch);
|
|
}
|
|
|
|
return rotsprite->patches[idx];
|
|
}
|
|
|
|
rotsprite_t *RotatedPatch_Create(INT32 numangles)
|
|
{
|
|
rotsprite_t *rotsprite = Z_Calloc(sizeof(rotsprite_t), PU_STATIC, NULL);
|
|
rotsprite->angles = numangles;
|
|
rotsprite->patches = Z_Calloc(rotsprite->angles * 2 * sizeof(void *), PU_STATIC, NULL);
|
|
return rotsprite;
|
|
}
|
|
|
|
static void RotatedPatch_CalculateDimensions(
|
|
INT32 width, INT32 height,
|
|
fixed_t ca, fixed_t sa,
|
|
INT32 *newwidth, INT32 *newheight)
|
|
{
|
|
fixed_t fixedwidth = (width * FRACUNIT);
|
|
fixed_t fixedheight = (height * FRACUNIT);
|
|
|
|
fixed_t w1 = abs(FixedMul(fixedwidth, ca) - FixedMul(fixedheight, sa));
|
|
fixed_t w2 = abs(FixedMul(-fixedwidth, ca) - FixedMul(fixedheight, sa));
|
|
fixed_t h1 = abs(FixedMul(fixedwidth, sa) + FixedMul(fixedheight, ca));
|
|
fixed_t h2 = abs(FixedMul(-fixedwidth, sa) + FixedMul(fixedheight, 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));
|
|
}
|
|
|
|
#ifdef HWRENDER
|
|
static patch_t *R_CreateHardwarePatch(patch_t *patch)
|
|
{
|
|
GLPatch_t *grPatch = Z_Calloc(sizeof(GLPatch_t), PU_HWRPATCHINFO, NULL);
|
|
grPatch->mipmap = Z_Calloc(sizeof(GLMipmap_t), PU_HWRPATCHINFO, NULL);
|
|
grPatch->rawpatch = patch;
|
|
HWR_MakePatch(patch, grPatch, grPatch->mipmap, false);
|
|
return (patch_t *)grPatch;
|
|
}
|
|
#endif
|
|
|
|
void RotatedPatch_DoRotation(rotsprite_t *rotsprite, patch_t *patch, INT32 angle, INT32 xpivot, INT32 ypivot, boolean flip)
|
|
{
|
|
UINT32 i;
|
|
patch_t *rotated;
|
|
|
|
#ifdef HWRENDER
|
|
patch_t *hwpatch;
|
|
#endif
|
|
|
|
UINT16 *rawdst, *rawconv;
|
|
size_t size;
|
|
INT32 bflip = (flip != 0x00);
|
|
|
|
INT32 width = patch->width;
|
|
INT32 height = patch->height;
|
|
INT32 leftoffset = patch->leftoffset;
|
|
INT32 newwidth, newheight;
|
|
|
|
fixed_t ca = rollcosang[angle];
|
|
fixed_t sa = rollsinang[angle];
|
|
fixed_t xcenter, ycenter;
|
|
INT32 idx = angle;
|
|
INT32 x, y;
|
|
INT32 sx, sy;
|
|
INT32 dx, dy;
|
|
INT32 ox, oy;
|
|
INT32 minx, miny, maxx, maxy;
|
|
|
|
// Don't cache angle = 0
|
|
if (angle < 1 || angle >= ROTANGLES)
|
|
return;
|
|
|
|
if (flip)
|
|
{
|
|
idx += rotsprite->angles;
|
|
xpivot = width - xpivot;
|
|
leftoffset = width - leftoffset;
|
|
}
|
|
|
|
if (rotsprite->patches[idx])
|
|
return;
|
|
|
|
// Find the dimensions of the rotated patch.
|
|
RotatedPatch_CalculateDimensions(width, height, ca, sa, &newwidth, &newheight);
|
|
|
|
xcenter = (xpivot * FRACUNIT);
|
|
ycenter = (ypivot * FRACUNIT);
|
|
|
|
if (xpivot != width / 2 || ypivot != height / 2)
|
|
{
|
|
newwidth *= 2;
|
|
newheight *= 2;
|
|
}
|
|
|
|
minx = newwidth;
|
|
miny = newheight;
|
|
maxx = 0;
|
|
maxy = 0;
|
|
|
|
// 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 (i = 0; i < size; i++)
|
|
rawdst[i] = 0xFF00;
|
|
|
|
for (dy = 0; dy < newheight; dy++)
|
|
{
|
|
for (dx = 0; dx < newwidth; dx++)
|
|
{
|
|
x = (dx - (newwidth / 2)) * FRACUNIT;
|
|
y = (dy - (newheight / 2)) * FRACUNIT;
|
|
sx = FixedMul(x, ca) + FixedMul(y, sa) + xcenter;
|
|
sy = -FixedMul(x, sa) + FixedMul(y, ca) + ycenter;
|
|
|
|
sx >>= FRACBITS;
|
|
sy >>= FRACBITS;
|
|
|
|
if (sx >= 0 && sy >= 0 && sx < width && sy < height)
|
|
rawdst[(dy*newwidth)+dx] = GetPatchPixel(patch, sx, sy, bflip);
|
|
}
|
|
}
|
|
|
|
ox = (newwidth / 2) + (leftoffset - xpivot);
|
|
oy = (newheight / 2) + (patch->topoffset - ypivot);
|
|
width = (maxx - minx);
|
|
height = (maxy - miny);
|
|
|
|
if ((unsigned)(width * height) > size)
|
|
{
|
|
UINT16 *src, *dest;
|
|
|
|
size = (width * height);
|
|
rawconv = Z_Calloc(size * sizeof(UINT16), PU_STATIC, NULL);
|
|
|
|
src = &rawdst[(miny * newwidth) + minx];
|
|
dest = rawconv;
|
|
dy = height;
|
|
|
|
while (dy--)
|
|
{
|
|
M_Memcpy(dest, src, width * sizeof(UINT16));
|
|
dest += width;
|
|
src += newwidth;
|
|
}
|
|
|
|
ox -= minx;
|
|
oy -= miny;
|
|
|
|
Z_Free(rawdst);
|
|
}
|
|
else
|
|
{
|
|
rawconv = rawdst;
|
|
width = newwidth;
|
|
height = newheight;
|
|
}
|
|
|
|
// make patch
|
|
rotated = R_MaskedFlatToPatch(rawconv, width, height, 0, 0, &size);
|
|
{
|
|
rotated->leftoffset = ox;
|
|
rotated->topoffset = oy;
|
|
}
|
|
#ifdef HWRENDER
|
|
if (rendermode == render_opengl)
|
|
{
|
|
hwpatch = R_CreateHardwarePatch(rotated);
|
|
}
|
|
#endif // HWRENDER
|
|
|
|
//Z_ChangeTag(rotated, PU_PATCH_ROTATED); // important for memory clearing
|
|
#ifdef HWRENDER
|
|
if (rendermode == render_opengl)
|
|
Z_SetUser(hwpatch, (void **)(&rotsprite->patches[idx]));
|
|
else
|
|
#endif
|
|
Z_SetUser(rotated, (void **)(&rotsprite->patches[idx]));
|
|
|
|
Z_Free(rawconv);
|
|
|
|
rotated->leftoffset = ox;
|
|
rotated->topoffset = oy;
|
|
}
|
|
#endif
|