Merge branch 'fixes-for-2190' into 'next'

Fixes for !2190

See merge request STJr/SRB2!2273
This commit is contained in:
Lactozilla 2024-01-21 23:54:22 +00:00
commit 0739c310b3
12 changed files with 631 additions and 308 deletions

View file

@ -14,6 +14,7 @@
#include "fastcmp.h"
#include "r_defs.h"
#include "r_local.h"
#include "r_translation.h"
#include "st_stuff.h" // hudinfo[]
#include "g_game.h"
#include "i_video.h" // rendermode
@ -1125,7 +1126,10 @@ static int libd_getColormap(lua_State *L)
INT32 skinnum = TC_DEFAULT;
skincolornum_t color = luaL_optinteger(L, 2, 0);
UINT8* colormap = NULL;
int translation_id = -1;
HUDONLY
if (lua_isnoneornil(L, 1))
; // defaults to TC_DEFAULT
else if (lua_type(L, 1) == LUA_TNUMBER) // skin number
@ -1144,9 +1148,21 @@ static int libd_getColormap(lua_State *L)
skinnum = i;
}
// all was successful above, now we generate the colormap at last!
if (!lua_isnoneornil(L, 3))
{
const char *translation_name = luaL_checkstring(L, 3);
translation_id = R_FindCustomTranslation(translation_name);
if (translation_id == -1)
return luaL_error(L, "invalid translation '%s'.", translation_name);
}
// all was successful above, now we generate the colormap at last!
if (translation_id != -1)
colormap = R_GetTranslationRemap(translation_id, color, skinnum);
if (colormap == NULL)
colormap = R_GetTranslationColormap(skinnum, color, GTC_CACHE);
colormap = R_GetTranslationColormap(skinnum, color, GTC_CACHE);
LUA_PushUserdata(L, colormap, META_COLORMAP); // push as META_COLORMAP userdata, specifically for patches to use!
return 1;
}

View file

@ -1900,32 +1900,6 @@ static int colorramp_len(lua_State *L)
return 1;
}
//////////////////////
// TRANSLATION INFO //
//////////////////////
// Arbitrary translations[] table index -> colormap_t *
static int lib_getTranslation(lua_State *L)
{
lua_remove(L, 1);
const char *name = luaL_checkstring(L, 1);
remaptable_t *tr = R_GetTranslationByID(R_FindCustomTranslation(name));
if (tr)
LUA_PushUserdata(L, &tr->remap, META_COLORMAP);
else
lua_pushnil(L);
return 1;
}
// #translations -> R_NumCustomTranslations()
static int lib_translationslen(lua_State *L)
{
lua_pushinteger(L, R_NumCustomTranslations());
return 1;
}
//////////////////////////////
//
// Now push all these functions into the Lua state!
@ -1958,7 +1932,6 @@ int LUA_InfoLib(lua_State *L)
LUA_RegisterGlobalUserdata(L, "spr2defaults", lib_getSpr2default, lib_setSpr2default, lib_spr2namelen);
LUA_RegisterGlobalUserdata(L, "states", lib_getState, lib_setState, lib_statelen);
LUA_RegisterGlobalUserdata(L, "mobjinfo", lib_getMobjInfo, lib_setMobjInfo, lib_mobjinfolen);
LUA_RegisterGlobalUserdata(L, "translations", lib_getTranslation, NULL, lib_translationslen);
LUA_RegisterGlobalUserdata(L, "skincolors", lib_getSkinColor, lib_setSkinColor, lib_skincolorslen);
LUA_RegisterGlobalUserdata(L, "spriteinfo", lib_getSpriteInfo, lib_setSpriteInfo, lib_spriteinfolen);
LUA_RegisterGlobalUserdata(L, "sfxinfo", lib_getSfxInfo, lib_setSfxInfo, lib_sfxlen);

View file

@ -2259,6 +2259,44 @@ boolean M_IsStringEmpty(const char *s)
return true;
}
// Converts a string containing a whole number into an int. Returns false if the conversion failed.
boolean M_StringToNumber(const char *input, int *out)
{
char *end_position = NULL;
errno = 0;
int result = strtol(input, &end_position, 10);
if (end_position == input || *end_position != '\0')
return false;
if (errno == ERANGE)
return false;
*out = result;
return true;
}
// Converts a string containing a number into a double. Returns false if the conversion failed.
boolean M_StringToDecimal(const char *input, double *out)
{
char *end_position = NULL;
errno = 0;
double result = strtod(input, &end_position);
if (end_position == input || *end_position != '\0')
return false;
if (errno == ERANGE)
return false;
*out = result;
return true;
}
// Rounds off floating numbers and checks for 0 - 255 bounds
int M_RoundUp(double number)
{

View file

@ -109,6 +109,12 @@ const char * M_Ftrim (double);
// Returns true if the string is empty.
boolean M_IsStringEmpty(const char *s);
// Converts a string containing a whole number into an int. Returns false if the conversion failed.
boolean M_StringToNumber(const char *input, int *out);
// Converts a string containing a number into a double. Returns false if the conversion failed.
boolean M_StringToDecimal(const char *input, double *out);
// counting bits, for weapon ammo code, usually
FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size);

View file

@ -21,6 +21,7 @@ tokenizer_t *Tokenizer_Open(const char *inputString, unsigned numTokens)
tokenizer->endPos = 0;
tokenizer->inputLength = 0;
tokenizer->inComment = 0;
tokenizer->inString = 0;
tokenizer->get = Tokenizer_Read;
if (numTokens < 1)
@ -53,7 +54,18 @@ void Tokenizer_Close(tokenizer_t *tokenizer)
Z_Free(tokenizer);
}
static void Tokenizer_DetectComment(tokenizer_t *tokenizer, UINT32 *pos)
static boolean DetectLineBreak(tokenizer_t *tokenizer, size_t pos)
{
if (tokenizer->input[pos] == '\n')
{
tokenizer->line++;
return true;
}
return false;
}
static void DetectComment(tokenizer_t *tokenizer, UINT32 *pos)
{
if (tokenizer->inComment)
return;
@ -94,8 +106,31 @@ const char *Tokenizer_Read(tokenizer_t *tokenizer, UINT32 i)
tokenizer->startPos = tokenizer->endPos;
// If in a string, return the entire string within quotes, except without the quotes.
if (tokenizer->inString == 1)
{
while (tokenizer->input[tokenizer->endPos] != '"' && tokenizer->endPos < tokenizer->inputLength)
{
DetectLineBreak(tokenizer, tokenizer->endPos);
tokenizer->endPos++;
}
Tokenizer_ReadTokenString(tokenizer, i);
tokenizer->inString = 2;
return tokenizer->token[i];
}
// If just ended a string, return only a quotation mark.
else if (tokenizer->inString == 2)
{
tokenizer->endPos = tokenizer->startPos + 1;
tokenizer->token[i][0] = tokenizer->input[tokenizer->startPos];
tokenizer->token[i][1] = '\0';
tokenizer->inString = 0;
return tokenizer->token[i];
}
// Try to detect comments now, in case we're pointing right at one
Tokenizer_DetectComment(tokenizer, &tokenizer->startPos);
DetectComment(tokenizer, &tokenizer->startPos);
// Find the first non-whitespace char, or else the end of the string trying
while ((tokenizer->input[tokenizer->startPos] == ' '
@ -106,8 +141,10 @@ const char *Tokenizer_Read(tokenizer_t *tokenizer, UINT32 i)
|| tokenizer->inComment != 0)
&& tokenizer->startPos < tokenizer->inputLength)
{
boolean inLineBreak = DetectLineBreak(tokenizer, tokenizer->startPos);
// Try to detect comment endings now
if (tokenizer->inComment == 1 && tokenizer->input[tokenizer->startPos] == '\n')
if (tokenizer->inComment == 1 && inLineBreak)
tokenizer->inComment = 0; // End of line for a single-line comment
else if (tokenizer->inComment == 2
&& tokenizer->startPos < tokenizer->inputLength - 1
@ -120,11 +157,12 @@ const char *Tokenizer_Read(tokenizer_t *tokenizer, UINT32 i)
}
tokenizer->startPos++;
Tokenizer_DetectComment(tokenizer, &tokenizer->startPos);
DetectComment(tokenizer, &tokenizer->startPos);
}
// If the end of the string is reached, no token is to be read
if (tokenizer->startPos == tokenizer->inputLength) {
if (tokenizer->startPos == tokenizer->inputLength)
{
tokenizer->endPos = tokenizer->inputLength;
return NULL;
}
@ -136,22 +174,16 @@ const char *Tokenizer_Read(tokenizer_t *tokenizer, UINT32 i)
|| tokenizer->input[tokenizer->startPos] == ']'
|| tokenizer->input[tokenizer->startPos] == '='
|| tokenizer->input[tokenizer->startPos] == ':'
|| tokenizer->input[tokenizer->startPos] == '%')
|| tokenizer->input[tokenizer->startPos] == '%'
|| tokenizer->input[tokenizer->startPos] == '@'
|| tokenizer->input[tokenizer->startPos] == '"')
{
tokenizer->endPos = tokenizer->startPos + 1;
tokenizer->token[i][0] = tokenizer->input[tokenizer->startPos];
tokenizer->token[i][1] = '\0';
return tokenizer->token[i];
}
// Return entire string within quotes, except without the quotes.
else if (tokenizer->input[tokenizer->startPos] == '"')
{
tokenizer->endPos = ++tokenizer->startPos;
while (tokenizer->input[tokenizer->endPos] != '"' && tokenizer->endPos < tokenizer->inputLength)
tokenizer->endPos++;
if (tokenizer->input[tokenizer->startPos] == '"')
tokenizer->inString = 1;
Tokenizer_ReadTokenString(tokenizer, i);
tokenizer->endPos++;
return tokenizer->token[i];
}
@ -169,12 +201,14 @@ const char *Tokenizer_Read(tokenizer_t *tokenizer, UINT32 i)
&& tokenizer->input[tokenizer->endPos] != '='
&& tokenizer->input[tokenizer->endPos] != ':'
&& tokenizer->input[tokenizer->endPos] != '%'
&& tokenizer->input[tokenizer->endPos] != '@'
&& tokenizer->input[tokenizer->endPos] != ';'
&& tokenizer->inComment == 0)
&& tokenizer->endPos < tokenizer->inputLength)
{
tokenizer->endPos++;
// Try to detect comment starts now; if it's in a comment, we don't want it in this token
Tokenizer_DetectComment(tokenizer, &tokenizer->endPos);
DetectComment(tokenizer, &tokenizer->endPos);
}
Tokenizer_ReadTokenString(tokenizer, i);
@ -189,7 +223,7 @@ const char *Tokenizer_SRB2Read(tokenizer_t *tokenizer, UINT32 i)
tokenizer->startPos = tokenizer->endPos;
// Try to detect comments now, in case we're pointing right at one
Tokenizer_DetectComment(tokenizer, &tokenizer->startPos);
DetectComment(tokenizer, &tokenizer->startPos);
// Find the first non-whitespace char, or else the end of the string trying
while ((tokenizer->input[tokenizer->startPos] == ' '
@ -201,8 +235,10 @@ const char *Tokenizer_SRB2Read(tokenizer_t *tokenizer, UINT32 i)
|| tokenizer->inComment != 0)
&& tokenizer->startPos < tokenizer->inputLength)
{
boolean inLineBreak = DetectLineBreak(tokenizer, tokenizer->startPos);
// Try to detect comment endings now
if (tokenizer->inComment == 1 && tokenizer->input[tokenizer->startPos] == '\n')
if (tokenizer->inComment == 1 && inLineBreak)
tokenizer->inComment = 0; // End of line for a single-line comment
else if (tokenizer->inComment == 2
&& tokenizer->startPos < tokenizer->inputLength - 1
@ -215,7 +251,7 @@ const char *Tokenizer_SRB2Read(tokenizer_t *tokenizer, UINT32 i)
}
tokenizer->startPos++;
Tokenizer_DetectComment(tokenizer, &tokenizer->startPos);
DetectComment(tokenizer, &tokenizer->startPos);
}
// If the end of the string is reached, no token is to be read
@ -238,7 +274,10 @@ const char *Tokenizer_SRB2Read(tokenizer_t *tokenizer, UINT32 i)
{
tokenizer->endPos = ++tokenizer->startPos;
while (tokenizer->input[tokenizer->endPos] != '"' && tokenizer->endPos < tokenizer->inputLength)
{
DetectLineBreak(tokenizer, tokenizer->endPos);
tokenizer->endPos++;
}
Tokenizer_ReadTokenString(tokenizer, i);
tokenizer->endPos++;
@ -260,7 +299,7 @@ const char *Tokenizer_SRB2Read(tokenizer_t *tokenizer, UINT32 i)
{
tokenizer->endPos++;
// Try to detect comment starts now; if it's in a comment, we don't want it in this token
Tokenizer_DetectComment(tokenizer, &tokenizer->endPos);
DetectComment(tokenizer, &tokenizer->endPos);
}
Tokenizer_ReadTokenString(tokenizer, i);

View file

@ -24,6 +24,8 @@ typedef struct Tokenizer
UINT32 endPos;
UINT32 inputLength;
UINT8 inComment; // 0 = not in comment, 1 = // Single-line, 2 = /* Multi-line */
UINT8 inString; // 0 = not in string, 1 = in string, 2 = just left string
int line;
const char *(*get)(struct Tokenizer*, UINT32);
} tokenizer_t;

View file

@ -26,9 +26,6 @@
#include "taglist.h"
// Amount of colors in the palette
#define NUM_PALETTE_ENTRIES 256
//
// ClipWallSegment
// Clips the given range of columns

View file

@ -125,49 +125,37 @@ UINT32 nflatxshift, nflatyshift, nflatshiftup, nflatmask;
// TRANSLATION COLORMAP CODE
// =========================================================================
enum
{
DEFAULT_TT_CACHE_INDEX,
BOSS_TT_CACHE_INDEX,
METALSONIC_TT_CACHE_INDEX,
ALLWHITE_TT_CACHE_INDEX,
RAINBOW_TT_CACHE_INDEX,
BLINK_TT_CACHE_INDEX,
DASHMODE_TT_CACHE_INDEX,
TT_CACHE_SIZE
};
static UINT8 **translationtablecache[TT_CACHE_SIZE] = {NULL};
static UINT8 **skintranslationcache[NUM_PALETTE_ENTRIES] = {NULL};
static colorcache_t **translationtablecache[TT_CACHE_SIZE] = {NULL};
boolean skincolor_modified[MAXSKINCOLORS];
static INT32 TranslationToCacheIndex(INT32 translation)
static INT32 SkinToCacheIndex(INT32 translation)
{
switch (translation)
{
case TC_DEFAULT: return DEFAULT_TT_CACHE_INDEX;
case TC_BOSS: return BOSS_TT_CACHE_INDEX;
case TC_METALSONIC: return METALSONIC_TT_CACHE_INDEX;
case TC_ALLWHITE: return ALLWHITE_TT_CACHE_INDEX;
case TC_RAINBOW: return RAINBOW_TT_CACHE_INDEX;
case TC_BLINK: return BLINK_TT_CACHE_INDEX;
case TC_DASHMODE: return DASHMODE_TT_CACHE_INDEX;
default: return DEFAULT_TT_CACHE_INDEX;
default: return translation;
}
}
static INT32 CacheIndexToTranslation(INT32 index)
static INT32 CacheIndexToSkin(INT32 index)
{
switch (index)
{
case DEFAULT_TT_CACHE_INDEX: return TC_DEFAULT;
case BOSS_TT_CACHE_INDEX: return TC_BOSS;
case METALSONIC_TT_CACHE_INDEX: return TC_METALSONIC;
case ALLWHITE_TT_CACHE_INDEX: return TC_ALLWHITE;
case RAINBOW_TT_CACHE_INDEX: return TC_RAINBOW;
case BLINK_TT_CACHE_INDEX: return TC_BLINK;
case DASHMODE_TT_CACHE_INDEX: return TC_DASHMODE;
default: return TC_DEFAULT;
default: return index;
}
}
@ -553,23 +541,22 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 translatio
*/
UINT8* R_GetTranslationColormap(INT32 skinnum, skincolornum_t color, UINT8 flags)
{
UINT8 ***cache = NULL;
INT32 index, starttranscolor;
UINT8 *ret;
colorcache_t *ret;
INT32 index = 0;
INT32 starttranscolor = DEFAULT_STARTTRANSCOLOR;
// Adjust if we want the default colormap
if (skinnum >= numskins)
I_Error("Invalid skin number %d", skinnum);
else if (skinnum >= 0)
{
cache = skintranslationcache;
starttranscolor = index = skins[skinnum]->starttranscolor;
index = skins[skinnum]->skinnum;
starttranscolor = skins[skinnum]->starttranscolor;
}
else if (skinnum <= TC_DEFAULT)
{
cache = translationtablecache;
starttranscolor = DEFAULT_STARTTRANSCOLOR;
index = TranslationToCacheIndex(skinnum);
// Do default translation
index = SkinToCacheIndex(skinnum);
}
else
I_Error("Invalid translation %d", skinnum);
@ -577,41 +564,48 @@ UINT8* R_GetTranslationColormap(INT32 skinnum, skincolornum_t color, UINT8 flags
if (flags & GTC_CACHE)
{
// Allocate table for skin if necessary
if (!cache[index])
cache[index] = Z_Calloc(MAXSKINCOLORS * sizeof(UINT8**), PU_STATIC, NULL);
if (!translationtablecache[index])
translationtablecache[index] = Z_Calloc(MAXSKINCOLORS * sizeof(colorcache_t**), PU_STATIC, NULL);
// Get colormap
ret = cache[index][color];
ret = translationtablecache[index][color];
// Rebuild the cache if necessary
if (skincolor_modified[color])
{
INT32 i;
for (i = 0; i < TT_CACHE_SIZE; i++)
if (translationtablecache[i] && translationtablecache[i][color])
R_GenerateTranslationColormap(translationtablecache[i][color], CacheIndexToTranslation(i), color, starttranscolor);
for (i = 0; i < NUM_PALETTE_ENTRIES; i++)
if (skintranslationcache[i] && skintranslationcache[i][color])
R_GenerateTranslationColormap(skintranslationcache[i][color], 0, color, i);
// Moved up here so that R_UpdateTranslationRemaps doesn't cause a stack overflow,
// since in this situation, it will call R_GetTranslationColormap
skincolor_modified[color] = false;
for (unsigned i = 0; i < TT_CACHE_SIZE; i++)
{
if (translationtablecache[i])
{
colorcache_t *cache = translationtablecache[i][color];
if (cache)
{
R_GenerateTranslationColormap(cache->colors, CacheIndexToSkin(i), color, starttranscolor);
R_UpdateTranslationRemaps(color, i);
}
}
}
}
}
else ret = NULL;
else
ret = NULL;
// Generate the colormap if necessary
if (!ret)
{
ret = Z_MallocAlign(NUM_PALETTE_ENTRIES, (flags & GTC_CACHE) ? PU_LEVEL : PU_STATIC, NULL, 8);
R_GenerateTranslationColormap(ret, skinnum, color, starttranscolor);
ret = Z_Malloc(sizeof(colorcache_t), (flags & GTC_CACHE) ? PU_LEVEL : PU_STATIC, NULL);
R_GenerateTranslationColormap(ret->colors, skinnum, color, starttranscolor);
// Cache the colormap if desired
if (flags & GTC_CACHE)
cache[index][color] = ret;
translationtablecache[index][color] = ret;
}
return ret;
return ret->colors;
}
/** \brief Flushes cache of translation colormaps.
@ -629,9 +623,6 @@ void R_FlushTranslationColormapCache(void)
for (i = 0; i < TT_CACHE_SIZE; i++)
if (translationtablecache[i])
memset(translationtablecache[i], 0, MAXSKINCOLORS * sizeof(UINT8**));
for (i = 0; i < NUM_PALETTE_ENTRIES; i++)
if (skintranslationcache[i])
memset(skintranslationcache[i], 0, MAXSKINCOLORS * sizeof(UINT8**));
}
UINT16 R_GetColorByName(const char *name)

View file

@ -117,6 +117,27 @@ enum
TC_DEFAULT
};
// Amount of colors in the palette
#define NUM_PALETTE_ENTRIES 256
typedef struct colorcache_s
{
UINT8 colors[NUM_PALETTE_ENTRIES];
} colorcache_t;
enum
{
DEFAULT_TT_CACHE_INDEX = MAXSKINS,
BOSS_TT_CACHE_INDEX,
METALSONIC_TT_CACHE_INDEX,
ALLWHITE_TT_CACHE_INDEX,
RAINBOW_TT_CACHE_INDEX,
BLINK_TT_CACHE_INDEX,
DASHMODE_TT_CACHE_INDEX,
TT_CACHE_SIZE
};
// Custom player skin translation
// Initialize color translation tables, for player rendering etc.
UINT8* R_GetTranslationColormap(INT32 skinnum, skincolornum_t color, UINT8 flags);

View file

@ -764,6 +764,12 @@ void R_DrawFlippedMaskedColumn(column_t *column)
UINT8 *R_GetTranslationForThing(mobj_t *mobj, skincolornum_t color, UINT16 translation)
{
INT32 skinnum = TC_DEFAULT;
boolean is_player = mobj->skin && mobj->sprite == SPR_PLAY;
if (is_player) // This thing is a player!
skinnum = ((skin_t*)mobj->skin)->skinnum;
if (R_ThingIsFlashing(mobj)) // Bosses "flash"
{
if (mobj->type == MT_CYBRAKDEMON || mobj->colorized)
@ -775,9 +781,9 @@ UINT8 *R_GetTranslationForThing(mobj_t *mobj, skincolornum_t color, UINT16 trans
}
else if (translation != 0)
{
remaptable_t *tr = R_GetTranslationByID(translation);
UINT8 *tr = R_GetTranslationRemap(translation, color, skinnum);
if (tr != NULL)
return tr->remap;
return tr;
}
else if (color != SKINCOLOR_NONE)
{
@ -793,13 +799,8 @@ UINT8 *R_GetTranslationForThing(mobj_t *mobj, skincolornum_t color, UINT16 trans
else
return R_GetTranslationColormap(TC_RAINBOW, color, GTC_CACHE);
}
else if (mobj->skin && mobj->sprite == SPR_PLAY) // This thing is a player!
{
UINT8 skinnum = ((skin_t*)mobj->skin)->skinnum;
else
return R_GetTranslationColormap(skinnum, color, GTC_CACHE);
}
else // Use the defaults
return R_GetTranslationColormap(TC_DEFAULT, color, GTC_CACHE);
}
else if (mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome.
return R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_BLUE, GTC_CACHE);

View file

@ -17,6 +17,7 @@
#include "z_zone.h"
#include "w_wad.h"
#include "m_tokenizer.h"
#include "m_misc.h"
#include <errno.h>
@ -26,57 +27,39 @@ static unsigned numpaletteremaps = 0;
static int allWhiteRemap = 0;
static int dashModeRemap = 0;
static void MakeGrayscaleRemap(void);
static void MakeInvertRemap(void);
static void MakeDashModeRemap(void);
static boolean PaletteRemap_AddIndexRange(remaptable_t *tr, int start, int end, int pal1, int pal2);
static boolean PaletteRemap_AddColorRange(remaptable_t *tr, int start, int end, int r1i, int g1i, int b1i, int r2i, int g2i, int b2i);
static boolean PaletteRemap_AddDesaturation(remaptable_t *tr, int start, int end, double r1, double g1, double b1, double r2, double g2, double b2);
static boolean PaletteRemap_AddColourisation(remaptable_t *tr, int start, int end, int r, int g, int b);
static boolean PaletteRemap_AddTint(remaptable_t *tr, int start, int end, int r, int g, int b, int amount);
static boolean PaletteRemap_AddInvert(remaptable_t *tr, int start, int end);
static boolean PaletteRemap_DoIndexRange(UINT8 *remap, int start, int end, int pal1, int pal2);
static boolean PaletteRemap_DoColorRange(UINT8 *remap, int start, int end, int r1i, int g1i, int b1i, int r2i, int g2i, int b2i);
static boolean PaletteRemap_DoDesaturation(UINT8 *remap, int start, int end, double r1, double g1, double b1, double r2, double g2, double b2);
static boolean PaletteRemap_DoColourisation(UINT8 *remap, int start, int end, int r, int g, int b);
static boolean PaletteRemap_DoTint(UINT8 *remap, int start, int end, int r, int g, int b, int amount);
static boolean PaletteRemap_DoInvert(UINT8 *remap, int start, int end);
enum PaletteRemapType
static void PaletteRemap_Apply(UINT8 *remap, paletteremap_t *data);
static void InitSource(paletteremap_t *source, paletteremaptype_t type, int start, int end)
{
REMAP_ADD_INDEXRANGE,
REMAP_ADD_COLORRANGE,
REMAP_ADD_COLOURISATION,
REMAP_ADD_DESATURATION,
REMAP_ADD_TINT
};
source->type = type;
source->start = start;
source->end = end;
}
struct PaletteRemapParseResult
static paletteremap_t *AddSource(remaptable_t *tr, paletteremaptype_t type, int start, int end)
{
int start, end;
enum PaletteRemapType type;
union
{
struct
{
int pal1, pal2;
} indexRange;
struct
{
int r1, g1, b1;
int r2, g2, b2;
} colorRange;
struct
{
double r1, g1, b1;
double r2, g2, b2;
} desaturation;
struct
{
int r, g, b;
} colourisation;
struct
{
int r, g, b, amount;
} tint;
};
paletteremap_t *remap = NULL;
boolean has_error;
char error[4096];
};
tr->num_sources++;
tr->sources = Z_Realloc(tr->sources, tr->num_sources * sizeof(paletteremap_t), PU_STATIC, NULL);
remap = &tr->sources[tr->num_sources - 1];
InitSource(remap, type, start, end);
return remap;
}
void PaletteRemap_Init(void)
{
@ -86,10 +69,7 @@ void PaletteRemap_Init(void)
PaletteRemap_Add(base);
// Grayscale translation
remaptable_t *grayscale = PaletteRemap_New();
PaletteRemap_SetIdentity(grayscale);
PaletteRemap_AddDesaturation(grayscale, 0, 255, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
R_AddCustomTranslation("Grayscale", PaletteRemap_Add(grayscale));
MakeGrayscaleRemap();
// All white (TC_ALLWHITE)
remaptable_t *allWhite = PaletteRemap_New();
@ -103,10 +83,7 @@ void PaletteRemap_Init(void)
R_AddCustomTranslation("AllBlack", PaletteRemap_Add(allBlack));
// Invert
remaptable_t *invertRemap = PaletteRemap_New();
PaletteRemap_SetIdentity(invertRemap);
PaletteRemap_AddInvert(invertRemap, 0, 255);
R_AddCustomTranslation("Invert", PaletteRemap_Add(invertRemap));
MakeInvertRemap();
// Dash mode (TC_DASHMODE)
MakeDashModeRemap();
@ -155,14 +132,6 @@ boolean PaletteRemap_IsIdentity(remaptable_t *tr)
unsigned PaletteRemap_Add(remaptable_t *tr)
{
#if 0
for (unsigned i = 0; i < numpaletteremaps; i++)
{
if (PaletteRemap_Equal(tr, paletteremaps[i]))
return i;
}
#endif
numpaletteremaps++;
paletteremaps = Z_Realloc(paletteremaps, sizeof(remaptable_t *) * numpaletteremaps, PU_STATIC, NULL);
paletteremaps[numpaletteremaps - 1] = tr;
@ -170,6 +139,36 @@ unsigned PaletteRemap_Add(remaptable_t *tr)
return numpaletteremaps - 1;
}
static void MakeGrayscaleRemap(void)
{
remaptable_t *grayscale = PaletteRemap_New();
paletteremap_t *source = AddSource(grayscale, REMAP_ADD_DESATURATION, 0, 255);
source->desaturation.r1 = 0.0;
source->desaturation.g1 = 0.0;
source->desaturation.b1 = 0.0;
source->desaturation.r2 = 1.0;
source->desaturation.g2 = 1.0;
source->desaturation.b2 = 1.0;
PaletteRemap_SetIdentity(grayscale);
PaletteRemap_Apply(grayscale->remap, source);
R_AddCustomTranslation("Grayscale", PaletteRemap_Add(grayscale));
}
static void MakeInvertRemap(void)
{
remaptable_t *invertRemap = PaletteRemap_New();
paletteremap_t *source = AddSource(invertRemap, REMAP_ADD_INVERT, 0, 255);
PaletteRemap_SetIdentity(invertRemap);
PaletteRemap_Apply(invertRemap->remap, source);
R_AddCustomTranslation("Invert", PaletteRemap_Add(invertRemap));
}
// This is a long one, because MotorRoach basically hand-picked the indices
static void MakeDashModeRemap(void)
{
@ -237,7 +236,7 @@ static boolean IndicesOutOfRange2(int start1, int end1, int start2, int end2)
b = swap; \
}
static boolean PaletteRemap_AddIndexRange(remaptable_t *tr, int start, int end, int pal1, int pal2)
static boolean PaletteRemap_DoIndexRange(UINT8 *remap, int start, int end, int pal1, int pal2)
{
if (IndicesOutOfRange2(start, end, pal1, pal2))
return false;
@ -249,7 +248,7 @@ static boolean PaletteRemap_AddIndexRange(remaptable_t *tr, int start, int end,
}
else if (start == end)
{
tr->remap[start] = pal1;
remap[start] = pal1;
return true;
}
@ -259,13 +258,13 @@ static boolean PaletteRemap_AddIndexRange(remaptable_t *tr, int start, int end,
for (int i = start; i <= end; palcol += palstep, ++i)
{
double idx = round(palcol);
tr->remap[i] = (int)idx;
remap[i] = (int)idx;
}
return true;
}
static boolean PaletteRemap_AddColorRange(remaptable_t *tr, int start, int end, int r1i, int g1i, int b1i, int r2i, int g2i, int b2i)
static boolean PaletteRemap_DoColorRange(UINT8 *remap, int start, int end, int r1i, int g1i, int b1i, int r2i, int g2i, int b2i)
{
if (IndicesOutOfRange(start, end))
return false;
@ -302,7 +301,7 @@ static boolean PaletteRemap_AddColorRange(remaptable_t *tr, int start, int end,
if (start == end)
{
tr->remap[start] = NearestColor(r, g, b);
remap[start] = NearestColor(r, g, b);
}
else
{
@ -312,7 +311,7 @@ static boolean PaletteRemap_AddColorRange(remaptable_t *tr, int start, int end,
for (int i = start; i <= end; ++i)
{
tr->remap[i] = NearestColor(r, g, b);
remap[i] = NearestColor(r, g, b);
r += rs;
g += gs;
b += bs;
@ -324,7 +323,7 @@ static boolean PaletteRemap_AddColorRange(remaptable_t *tr, int start, int end,
#define CLAMP(val, minval, maxval) max(min(val, maxval), minval)
static boolean PaletteRemap_AddDesaturation(remaptable_t *tr, int start, int end, double r1, double g1, double b1, double r2, double g2, double b2)
static boolean PaletteRemap_DoDesaturation(UINT8 *remap, int start, int end, double r1, double g1, double b1, double r2, double g2, double b2)
{
if (IndicesOutOfRange(start, end))
return false;
@ -353,9 +352,11 @@ static boolean PaletteRemap_AddDesaturation(remaptable_t *tr, int start, int end
for (int c = start; c <= end; c++)
{
double intensity = (pMasterPalette[c].s.red * 77 + pMasterPalette[c].s.green * 143 + pMasterPalette[c].s.blue * 37) / 255.0;
double intensity = (pMasterPalette[remap[c]].s.red * 77
+ pMasterPalette[remap[c]].s.green * 143
+ pMasterPalette[remap[c]].s.blue * 37) / 255.0;
tr->remap[c] = NearestColor(
remap[c] = NearestColor(
min(255, max(0, (int)(r1 + intensity*r2))),
min(255, max(0, (int)(g1 + intensity*g2))),
min(255, max(0, (int)(b1 + intensity*b2)))
@ -369,16 +370,16 @@ static boolean PaletteRemap_AddDesaturation(remaptable_t *tr, int start, int end
#undef SWAP
static boolean PaletteRemap_AddColourisation(remaptable_t *tr, int start, int end, int r, int g, int b)
static boolean PaletteRemap_DoColourisation(UINT8 *remap, int start, int end, int r, int g, int b)
{
if (IndicesOutOfRange(start, end))
return false;
for (int i = start; i < end; ++i)
{
double br = pMasterPalette[i].s.red;
double bg = pMasterPalette[i].s.green;
double bb = pMasterPalette[i].s.blue;
double br = pMasterPalette[remap[i]].s.red;
double bg = pMasterPalette[remap[i]].s.green;
double bb = pMasterPalette[remap[i]].s.blue;
double grey = (br * 0.299 + bg * 0.587 + bb * 0.114) / 255.0f;
if (grey > 1.0)
grey = 1.0;
@ -387,7 +388,7 @@ static boolean PaletteRemap_AddColourisation(remaptable_t *tr, int start, int en
bg = g * grey;
bb = b * grey;
tr->remap[i] = NearestColor(
remap[i] = NearestColor(
(int)br,
(int)bg,
(int)bb
@ -397,16 +398,16 @@ static boolean PaletteRemap_AddColourisation(remaptable_t *tr, int start, int en
return true;
}
static boolean PaletteRemap_AddTint(remaptable_t *tr, int start, int end, int r, int g, int b, int amount)
static boolean PaletteRemap_DoTint(UINT8 *remap, int start, int end, int r, int g, int b, int amount)
{
if (IndicesOutOfRange(start, end))
return false;
for (int i = start; i < end; ++i)
{
float br = pMasterPalette[i].s.red;
float bg = pMasterPalette[i].s.green;
float bb = pMasterPalette[i].s.blue;
float br = pMasterPalette[remap[i]].s.red;
float bg = pMasterPalette[remap[i]].s.green;
float bb = pMasterPalette[remap[i]].s.blue;
float a = amount * 0.01f;
float ia = 1.0f - a;
@ -414,7 +415,7 @@ static boolean PaletteRemap_AddTint(remaptable_t *tr, int start, int end, int r,
bg = bg * ia + g * a;
bb = bb * ia + b * a;
tr->remap[i] = NearestColor(
remap[i] = NearestColor(
(int)br,
(int)bg,
(int)bb
@ -424,43 +425,45 @@ static boolean PaletteRemap_AddTint(remaptable_t *tr, int start, int end, int r,
return true;
}
static boolean PaletteRemap_AddInvert(remaptable_t *tr, int start, int end)
static boolean PaletteRemap_DoInvert(UINT8 *remap, int start, int end)
{
if (IndicesOutOfRange(start, end))
return false;
for (int i = start; i < end; ++i)
{
tr->remap[i] = NearestColor(
255 - tr->remap[pMasterPalette[i].s.red],
255 - tr->remap[pMasterPalette[i].s.green],
255 - tr->remap[pMasterPalette[i].s.blue]
remap[i] = NearestColor(
255 - pMasterPalette[remap[i]].s.red,
255 - pMasterPalette[remap[i]].s.green,
255 - pMasterPalette[remap[i]].s.blue
);
}
return true;
}
struct PaletteRemapParseResult
{
paletteremap_t remap;
char *error;
};
struct ParsedTranslation
{
struct ParsedTranslation *next;
remaptable_t *remap;
remaptable_t *baseTranslation;
struct PaletteRemapParseResult *data;
};
static struct ParsedTranslation *parsedTranslationListHead = NULL;
static struct ParsedTranslation *parsedTranslationListTail = NULL;
static void AddParsedTranslation(unsigned id, int base_translation, struct PaletteRemapParseResult *data)
static void AddParsedTranslation(remaptable_t *remap, remaptable_t *base_translation)
{
struct ParsedTranslation *node = Z_Calloc(sizeof(struct ParsedTranslation), PU_STATIC, NULL);
node->remap = paletteremaps[id];
node->data = data;
if (base_translation != -1)
node->baseTranslation = paletteremaps[base_translation];
node->remap = remap;
node->baseTranslation = base_translation;
if (parsedTranslationListHead == NULL)
parsedTranslationListHead = parsedTranslationListTail = node;
@ -471,7 +474,7 @@ static void AddParsedTranslation(unsigned id, int base_translation, struct Palet
}
}
static void PaletteRemap_ApplyResult(remaptable_t *tr, struct PaletteRemapParseResult *data)
static void PaletteRemap_Apply(UINT8 *remap, paletteremap_t *data)
{
int start = data->start;
int end = data->end;
@ -479,24 +482,27 @@ static void PaletteRemap_ApplyResult(remaptable_t *tr, struct PaletteRemapParseR
switch (data->type)
{
case REMAP_ADD_INDEXRANGE:
PaletteRemap_AddIndexRange(tr, start, end, data->indexRange.pal1, data->indexRange.pal2);
PaletteRemap_DoIndexRange(remap, start, end, data->indexRange.pal1, data->indexRange.pal2);
break;
case REMAP_ADD_COLORRANGE:
PaletteRemap_AddColorRange(tr, start, end,
PaletteRemap_DoColorRange(remap, start, end,
data->colorRange.r1, data->colorRange.g1, data->colorRange.b1,
data->colorRange.r2, data->colorRange.g2, data->colorRange.b2);
break;
case REMAP_ADD_COLOURISATION:
PaletteRemap_AddColourisation(tr, start, end,
PaletteRemap_DoColourisation(remap, start, end,
data->colourisation.r, data->colourisation.g, data->colourisation.b);
break;
case REMAP_ADD_DESATURATION:
PaletteRemap_AddDesaturation(tr, start, end,
PaletteRemap_DoDesaturation(remap, start, end,
data->desaturation.r1, data->desaturation.g1, data->desaturation.b1,
data->desaturation.r2, data->desaturation.g2, data->desaturation.b2);
break;
case REMAP_ADD_TINT:
PaletteRemap_AddTint(tr, start, end, data->tint.r, data->tint.g, data->tint.b, data->tint.amount);
PaletteRemap_DoTint(remap, start, end, data->tint.r, data->tint.g, data->tint.b, data->tint.amount);
break;
case REMAP_ADD_INVERT:
PaletteRemap_DoInvert(remap, start, end);
break;
}
}
@ -506,18 +512,18 @@ void R_LoadParsedTranslations(void)
struct ParsedTranslation *node = parsedTranslationListHead;
while (node)
{
struct PaletteRemapParseResult *result = node->data;
struct ParsedTranslation *next = node->next;
remaptable_t *tr = node->remap;
PaletteRemap_SetIdentity(tr);
if (node->baseTranslation)
memcpy(tr, node->baseTranslation, sizeof(remaptable_t));
PaletteRemap_ApplyResult(tr, result);
for (unsigned i = 0; i < tr->num_sources; i++)
PaletteRemap_Apply(tr->remap, &tr->sources[i]);
Z_Free(result);
Z_Free(node);
node = next;
@ -528,72 +534,48 @@ void R_LoadParsedTranslations(void)
static boolean ExpectToken(tokenizer_t *sc, const char *expect)
{
return strcmp(sc->get(sc, 0), expect) == 0;
}
static boolean StringToNumber(const char *tkn, int *out)
{
char *endPos = NULL;
errno = 0;
int result = strtol(tkn, &endPos, 10);
if (endPos == tkn || *endPos != '\0')
const char *tkn = sc->get(sc, 0);
if (!tkn)
return false;
if (errno == ERANGE)
return false;
*out = result;
return true;
return strcmp(tkn, expect) == 0;
}
static boolean ParseNumber(tokenizer_t *sc, int *out)
{
return StringToNumber(sc->get(sc, 0), out);
const char *tkn = sc->get(sc, 0);
if (!tkn)
return false;
return M_StringToNumber(tkn, out);
}
static boolean ParseDecimal(tokenizer_t *sc, double *out)
{
const char *tkn = sc->get(sc, 0);
char *endPos = NULL;
errno = 0;
double result = strtod(tkn, &endPos);
if (endPos == tkn || *endPos != '\0')
if (!tkn)
return false;
if (errno == ERANGE)
return false;
*out = result;
return true;
return M_StringToDecimal(tkn, out);
}
static struct PaletteRemapParseResult *ThrowError(const char *format, ...)
{
const size_t err_size = 512 * sizeof(char);
struct PaletteRemapParseResult *err = Z_Calloc(sizeof(struct PaletteRemapParseResult), PU_STATIC, NULL);
err->error = Z_Calloc(err_size, PU_STATIC, NULL);
va_list argptr;
va_start(argptr, format);
vsnprintf(err->error, sizeof err->error, format, argptr);
vsnprintf(err->error, err_size, format, argptr);
va_end(argptr);
err->has_error = true;
return err;
}
static struct PaletteRemapParseResult *MakeResult(enum PaletteRemapType type, int start, int end)
static struct PaletteRemapParseResult *MakeResult(paletteremaptype_t type, int start, int end)
{
struct PaletteRemapParseResult *tr = Z_Calloc(sizeof(struct PaletteRemapParseResult), PU_STATIC, NULL);
tr->type = type;
tr->start = start;
tr->end = end;
InitSource(&tr->remap, type, start, end);
return tr;
}
@ -615,6 +597,9 @@ static struct PaletteRemapParseResult *PaletteRemap_ParseString(tokenizer_t *sc)
return ThrowError("expected '='");
const char *tkn = sc->get(sc, 0);
if (tkn == NULL)
return ThrowError("unexpected EOF");
if (strcmp(tkn, "[") == 0)
{
// translation using RGB values
@ -661,12 +646,12 @@ static struct PaletteRemapParseResult *PaletteRemap_ParseString(tokenizer_t *sc)
return ThrowError("expected ']'");
struct PaletteRemapParseResult *tr = MakeResult(REMAP_ADD_COLORRANGE, start, end);
tr->colorRange.r1 = r1;
tr->colorRange.g1 = g1;
tr->colorRange.b1 = b1;
tr->colorRange.r2 = r2;
tr->colorRange.g2 = g2;
tr->colorRange.b2 = b2;
tr->remap.colorRange.r1 = r1;
tr->remap.colorRange.g1 = g1;
tr->remap.colorRange.b1 = b1;
tr->remap.colorRange.r2 = r2;
tr->remap.colorRange.g2 = g2;
tr->remap.colorRange.b2 = b2;
return tr;
}
else if (strcmp(tkn, "%") == 0)
@ -716,12 +701,12 @@ static struct PaletteRemapParseResult *PaletteRemap_ParseString(tokenizer_t *sc)
return ThrowError("expected ']'");
struct PaletteRemapParseResult *tr = MakeResult(REMAP_ADD_DESATURATION, start, end);
tr->desaturation.r1 = r1;
tr->desaturation.g1 = g1;
tr->desaturation.b1 = b1;
tr->desaturation.r2 = r2;
tr->desaturation.g2 = g2;
tr->desaturation.b2 = b2;
tr->remap.desaturation.r1 = r1;
tr->remap.desaturation.g1 = g1;
tr->remap.desaturation.b1 = b1;
tr->remap.desaturation.r2 = r2;
tr->remap.desaturation.g2 = g2;
tr->remap.desaturation.b2 = b2;
return tr;
}
else if (strcmp(tkn, "#") == 0)
@ -745,9 +730,9 @@ static struct PaletteRemapParseResult *PaletteRemap_ParseString(tokenizer_t *sc)
return ThrowError("expected ']'");
struct PaletteRemapParseResult *tr = MakeResult(REMAP_ADD_COLOURISATION, start, end);
tr->colourisation.r = r;
tr->colourisation.g = g;
tr->colourisation.b = b;
tr->remap.colourisation.r = r;
tr->remap.colourisation.g = g;
tr->remap.colourisation.b = b;
return tr;
}
else if (strcmp(tkn, "@") == 0)
@ -755,12 +740,10 @@ static struct PaletteRemapParseResult *PaletteRemap_ParseString(tokenizer_t *sc)
// Tint translation
int a, r, g, b;
if (!ExpectToken(sc, "["))
return ThrowError("expected '[");
if (!ParseNumber(sc, &a))
return ThrowError("expected a number for amount");
if (!ExpectToken(sc, ","))
return ThrowError("expected ','");
if (!ExpectToken(sc, "["))
return ThrowError("expected '[");
if (!ParseNumber(sc, &r))
return ThrowError("expected a number for red");
if (!ExpectToken(sc, ","))
@ -775,17 +758,17 @@ static struct PaletteRemapParseResult *PaletteRemap_ParseString(tokenizer_t *sc)
return ThrowError("expected ']'");
struct PaletteRemapParseResult *tr = MakeResult(REMAP_ADD_TINT, start, end);
tr->tint.r = r;
tr->tint.g = g;
tr->tint.b = b;
tr->tint.amount = a;
tr->remap.tint.r = r;
tr->remap.tint.g = g;
tr->remap.tint.b = b;
tr->remap.tint.amount = a;
return tr;
}
else
{
int pal1, pal2;
if (!StringToNumber(tkn, &pal1))
if (!M_StringToNumber(tkn, &pal1))
return ThrowError("expected a number for starting index");
if (!ExpectToken(sc, ":"))
return ThrowError("expected ':'");
@ -793,8 +776,8 @@ static struct PaletteRemapParseResult *PaletteRemap_ParseString(tokenizer_t *sc)
return ThrowError("expected a number for ending index");
struct PaletteRemapParseResult *tr = MakeResult(REMAP_ADD_INDEXRANGE, start, end);
tr->indexRange.pal1 = pal1;
tr->indexRange.pal2 = pal2;
tr->remap.indexRange.pal1 = pal1;
tr->remap.indexRange.pal2 = pal2;
return tr;
}
@ -809,8 +792,125 @@ static struct PaletteRemapParseResult *PaletteRemap_ParseTranslation(const char
return result;
}
static void PrintError(const char *name, const char *format, ...)
{
char error[256];
va_list argptr;
va_start(argptr, format);
vsnprintf(error, sizeof error, format, argptr);
va_end(argptr);
CONS_Alert(CONS_ERROR, "Error parsing translation '%s': %s\n", name, error);
}
#define CHECK_EOF() \
if (!tkn) \
{ \
PrintError(name, "Unexpected EOF"); \
goto fail; \
}
struct NewTranslation
{
int id;
char *name;
char *base_translation_name;
struct PaletteRemapParseResult **results;
size_t num_results;
};
static void AddNewTranslation(struct NewTranslation **list_p, size_t *num, char *name, int id, char *base_translation_name, struct PaletteRemapParseResult *parse_result)
{
struct NewTranslation *list = *list_p;
size_t count = *num;
for (size_t i = 0; i < count; i++)
{
struct NewTranslation *entry = &list[i];
if (entry->id == id && strcmp(entry->name, name) == 0)
{
if (entry->base_translation_name && base_translation_name
&& strcmp(entry->base_translation_name, base_translation_name) != 0)
continue;
entry->num_results++;
entry->results = Z_Realloc(entry->results,
entry->num_results * sizeof(struct PaletteRemapParseResult **), PU_STATIC, NULL);
entry->results[entry->num_results - 1] = parse_result;
return;
}
}
size_t i = count;
count++;
list = Z_Realloc(list, count * sizeof(struct NewTranslation), PU_STATIC, NULL);
struct NewTranslation *entry = &list[i];
entry->name = name;
entry->id = id;
entry->base_translation_name = base_translation_name;
entry->num_results = 1;
entry->results = Z_Realloc(entry->results, 1 * sizeof(struct PaletteRemapParseResult **), PU_STATIC, NULL);
entry->results[0] = parse_result;
*list_p = list;
*num = count;
}
static void PrepareNewTranslations(struct NewTranslation *list, size_t count)
{
if (!list)
return;
for (size_t i = 0; i < count; i++)
{
struct NewTranslation *entry = &list[i];
remaptable_t *tr = R_GetTranslationByID(entry->id);
if (tr == NULL)
{
tr = PaletteRemap_New();
R_AddCustomTranslation(entry->name, PaletteRemap_Add(tr));
}
remaptable_t *base_translation = NULL;
char *base_translation_name = entry->base_translation_name;
if (base_translation_name)
{
int base_translation_id = R_FindCustomTranslation(base_translation_name);
if (base_translation_id == -1)
PrintError(entry->name, "No translation named '%s'", base_translation_name);
else
base_translation = R_GetTranslationByID(base_translation_id);
}
// The translation is not generated until later, because the palette may not have been loaded.
// We store the result for when it's needed.
tr->sources = Z_Malloc(entry->num_results * sizeof(paletteremap_t), PU_STATIC, NULL);
tr->num_sources = entry->num_results;
for (size_t j = 0; j < entry->num_results; j++)
{
memcpy(&tr->sources[j], &entry->results[j]->remap, sizeof(paletteremap_t));
Z_Free(entry->results[j]);
}
AddParsedTranslation(tr, base_translation);
Z_Free(base_translation_name);
Z_Free(entry->results);
Z_Free(entry->name);
}
Z_Free(list);
}
void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum)
{
tokenizer_t *sc = NULL;
const char *tkn = NULL;
char *lumpData = (char *)W_CacheLumpNumPwad(wadNum, lumpnum, PU_STATIC);
size_t lumpLength = W_LumpLengthPwad(wadNum, lumpnum);
char *text = (char *)Z_Malloc((lumpLength + 1), PU_STATIC, NULL);
@ -818,74 +918,105 @@ void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum)
text[lumpLength] = '\0';
Z_Free(lumpData);
tokenizer_t *sc = Tokenizer_Open(text, 1);
const char *tkn = sc->get(sc, 0);
sc = Tokenizer_Open(text, 1);
tkn = sc->get(sc, 0);
struct NewTranslation *list = NULL;
size_t list_count = 0;
while (tkn != NULL)
{
int base_translation = -1;
char *name = Z_StrDup(tkn);
char *base_translation_name = NULL;
tkn = sc->get(sc, 0);
CHECK_EOF();
if (strcmp(tkn, ":") == 0)
{
tkn = sc->get(sc, 0);
CHECK_EOF();
base_translation = R_FindCustomTranslation(tkn);
if (base_translation == -1)
{
CONS_Alert(CONS_ERROR, "Error parsing translation '%s': No translation named '%s'\n", name, tkn);
goto fail;
}
base_translation_name = Z_StrDup(tkn);
tkn = sc->get(sc, 0);
CHECK_EOF();
}
if (strcmp(tkn, "=") != 0)
{
CONS_Alert(CONS_ERROR, "Error parsing translation '%s': Expected '=', got '%s'\n", name, tkn);
PrintError(name, "Expected '=', got '%s'", tkn);
goto fail;
}
tkn = sc->get(sc, 0);
CHECK_EOF();
struct PaletteRemapParseResult *result = NULL;
if (strcmp(tkn, "\"") != 0)
{
PrintError(name, "Expected '\"', got '%s'", tkn);
goto fail;
}
tkn = sc->get(sc, 0);
CHECK_EOF();
int existing_id = R_FindCustomTranslation(name);
// Parse all of the translations
do {
result = PaletteRemap_ParseTranslation(tkn);
if (result->has_error)
struct PaletteRemapParseResult *parse_result = PaletteRemap_ParseTranslation(tkn);
if (parse_result->error)
{
CONS_Alert(CONS_ERROR, "Error parsing translation '%s': %s\n", name, result->error);
Z_Free(result);
PrintError(name, "%s", parse_result->error);
Z_Free(parse_result->error);
goto fail;
}
else
{
AddNewTranslation(&list, &list_count, name, existing_id, base_translation_name, parse_result);
}
tkn = sc->get(sc, 0);
if (!tkn)
break;
if (strcmp(tkn, ",") != 0)
break;
if (!tkn || strcmp(tkn, "\"") != 0)
{
if (tkn)
PrintError(name, "Expected '\"', got '%s'", tkn);
else
PrintError(name, "Expected '\"', got EOF");
goto fail;
}
// Get ',' or parse the next line
tkn = sc->get(sc, 0);
if (!tkn || strcmp(tkn, ",") != 0)
break;
// Get '"'
tkn = sc->get(sc, 0);
if (!tkn || strcmp(tkn, "\"") != 0)
{
if (!tkn)
PrintError(name, "Expected '\"', got EOF");
else
PrintError(name, "Expected '\"', got '%s'", tkn);
goto fail;
}
tkn = sc->get(sc, 0);
CHECK_EOF();
} while (true);
// Allocate it and register it
remaptable_t *tr = PaletteRemap_New();
unsigned id = PaletteRemap_Add(tr);
R_AddCustomTranslation(name, id);
// Free this, since it's no longer needed
Z_Free(name);
// The translation is not generated until later, because the palette may not have been loaded.
// We store the result for when it's needed.
AddParsedTranslation(id, base_translation, result);
}
fail:
// Now add all of the new translations
if (list)
PrepareNewTranslations(list, list_count);
Tokenizer_Close(sc);
Z_Free(text);
}
#undef CHECK_EOF
typedef struct CustomTranslation
{
char *name;
@ -972,6 +1103,61 @@ remaptable_t *R_GetTranslationByID(int id)
return paletteremaps[id];
}
static void R_ApplyTranslationRemap(remaptable_t *tr, UINT8 *remap, skincolornum_t skincolor, INT32 skinnum)
{
UINT8 *base_skincolor = R_GetTranslationColormap(skinnum, skincolor, GTC_CACHE);
for (unsigned i = 0; i < NUM_PALETTE_ENTRIES; i++)
remap[i] = base_skincolor[i];
for (unsigned i = 0; i < tr->num_sources; i++)
PaletteRemap_Apply(remap, &tr->sources[i]);
}
UINT8 *R_GetTranslationRemap(int id, skincolornum_t skincolor, INT32 skinnum)
{
remaptable_t *tr = R_GetTranslationByID(id);
if (!tr)
return NULL;
if (!tr->num_sources || skincolor == SKINCOLOR_NONE)
return tr->remap;
if (!tr->skincolor_remaps)
Z_Calloc(sizeof(*tr->skincolor_remaps) * TT_CACHE_SIZE, PU_LEVEL, &tr->skincolor_remaps);
if (!tr->skincolor_remaps[skinnum])
tr->skincolor_remaps[skinnum] = Z_Calloc(NUM_PALETTE_ENTRIES * MAXSKINCOLORS, PU_LEVEL, NULL);
colorcache_t *cache = tr->skincolor_remaps[skinnum][skincolor];
if (!cache)
{
cache = Z_Calloc(sizeof(colorcache_t), PU_LEVEL, NULL);
R_ApplyTranslationRemap(tr, cache->colors, skincolor, skinnum);
tr->skincolor_remaps[skinnum][skincolor] = cache;
}
return cache->colors;
}
static void R_UpdateTranslation(remaptable_t *tr, skincolornum_t skincolor, INT32 skinnum)
{
if (!tr->num_sources || !tr->skincolor_remaps || !tr->skincolor_remaps[skinnum])
return;
colorcache_t *cache = tr->skincolor_remaps[skinnum][skincolor];
if (cache)
R_ApplyTranslationRemap(tr, cache->colors, skincolor, skinnum);
}
void R_UpdateTranslationRemaps(skincolornum_t skincolor, INT32 skinnum)
{
for (unsigned i = 0; i < numpaletteremaps; i++)
R_UpdateTranslation(paletteremaps[i], skincolor, skinnum);
}
boolean R_TranslationIsValid(int id)
{
if (id < 0 || id >= (signed)numpaletteremaps)

View file

@ -15,10 +15,61 @@
#include "doomdef.h"
#include "r_draw.h"
typedef enum
{
REMAP_ADD_INDEXRANGE,
REMAP_ADD_COLORRANGE,
REMAP_ADD_COLOURISATION,
REMAP_ADD_DESATURATION,
REMAP_ADD_TINT,
REMAP_ADD_INVERT
} paletteremaptype_t;
typedef struct
{
UINT8 remap[256];
int start, end;
paletteremaptype_t type;
union
{
struct
{
int pal1, pal2;
} indexRange;
struct
{
int r1, g1, b1;
int r2, g2, b2;
} colorRange;
struct
{
double r1, g1, b1;
double r2, g2, b2;
} desaturation;
struct
{
int r, g, b;
} colourisation;
struct
{
int r, g, b, amount;
} tint;
};
} paletteremap_t;
typedef struct
{
UINT8 remap[NUM_PALETTE_ENTRIES];
unsigned num_entries;
paletteremap_t *sources;
unsigned num_sources;
// A typical remap is 256 bytes long, and there is currently a maximum of 1182 skincolors, and 263 possible color cache entries.
// This would mean allocating (1182 * 256 * 263) bytes, which equals 79581696 bytes, or ~79mb of memory for every remap.
// So instead a few lists are allocated.
colorcache_t ***skincolor_remaps;
} remaptable_t;
void PaletteRemap_Init(void);
@ -35,6 +86,8 @@ void R_AddCustomTranslation(const char *name, int trnum);
const char *R_GetCustomTranslationName(unsigned id);
unsigned R_NumCustomTranslations(void);
remaptable_t *R_GetTranslationByID(int id);
UINT8 *R_GetTranslationRemap(int id, skincolornum_t skincolor, INT32 skinnum);
void R_UpdateTranslationRemaps(skincolornum_t skincolor, INT32 skinnum);
boolean R_TranslationIsValid(int id);
void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum);