diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 4b3c9c014ef6aa4e65853119961276c77e0c304d..52a875e50c0c550f2c10dbb1acd56647955baf01 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -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; } + 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; } diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 3facec82b82da21c92d5041aba81fb1f9f543deb..b2730c5931f0c00db2eca2848827526661631084 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -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); diff --git a/src/m_misc.c b/src/m_misc.c index 5815d17c54619f46afe6fc29c1277a3621ecc77b..1b6a90c50acd6230cb9d8c56f99df7178926c777 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -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) { diff --git a/src/m_misc.h b/src/m_misc.h index 753991e70465c075fa874ce7662d6aa6603e7b6a..04ac66ca65e1ff92d44172b12e1186cdaa04b648 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -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); diff --git a/src/m_tokenizer.c b/src/m_tokenizer.c index 26275881d3f81fe8db6c22d2e35226839c75af56..f36f7f6f323133c51c4975992beea9919c27e4bd 100644 --- a/src/m_tokenizer.c +++ b/src/m_tokenizer.c @@ -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); diff --git a/src/m_tokenizer.h b/src/m_tokenizer.h index 88cb2a566907d27b0d70508fb20c9ab1dff84fbb..f5111730194915c69fea455a53ba1da3cf66068b 100644 --- a/src/m_tokenizer.h +++ b/src/m_tokenizer.h @@ -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; diff --git a/src/r_defs.h b/src/r_defs.h index 39e6765cd7af35d6478e690de8c48045d063b505..16c660b01938ed86669550f709f7b65336cfaebc 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -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 diff --git a/src/r_draw.c b/src/r_draw.c index b87a8404ebc3ea5895425dd81b1c78dc6040fdfa..ff2e43df31d9291ed6ae07facf2da0c1beaaa4e1 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -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) diff --git a/src/r_draw.h b/src/r_draw.h index 9cde3cf54f4421233ba517996ceee173067a42fe..29370015a1c46cb6405bf5b698d6c23425ee79e2 100644 --- a/src/r_draw.h +++ b/src/r_draw.h @@ -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); diff --git a/src/r_things.c b/src/r_things.c index 628d8f490c59f2e24d21c620ca3631657300de98..7291594eb92901246bd547442d3b8d963cc9acf8 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -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); diff --git a/src/r_translation.c b/src/r_translation.c index a4df3cde0c9bdc04702f963e62b3703ca6f42c58..7e1e30d0cdec7afd2d810f6b8d1a6b26da0c42bb 100644 --- a/src/r_translation.c +++ b/src/r_translation.c @@ -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') - return false; - - if (errno == ERANGE) + const char *tkn = sc->get(sc, 0); + if (!tkn) 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') - return false; - - if (errno == ERANGE) + if (!tkn) 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(); + + if (strcmp(tkn, "\"") != 0) + { + PrintError(name, "Expected '\"', got '%s'", tkn); goto fail; } tkn = sc->get(sc, 0); + CHECK_EOF(); - struct PaletteRemapParseResult *result = NULL; + 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 (!tkn || strcmp(tkn, "\"") != 0) + { + if (tkn) + PrintError(name, "Expected '\"', got '%s'", tkn); + else + PrintError(name, "Expected '\"', got EOF"); + goto fail; + } - if (strcmp(tkn, ",") != 0) + // 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) diff --git a/src/r_translation.h b/src/r_translation.h index 70bc2fd27e4e248301029c93de562c24dab467ac..1eb40233d12070708d9bda26bfa8b53792235352 100644 --- a/src/r_translation.h +++ b/src/r_translation.h @@ -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 +{ + 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[256]; + 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);