From 8bbc512b14b3a02af1ad42556763bcf5e9e6aece Mon Sep 17 00:00:00 2001 From: Lactozilla Date: Fri, 19 Jan 2024 16:40:32 -0300 Subject: [PATCH 1/7] Tokenizer changes: Capture '"' characters Capture ';' tokens Implement tracking of the current line --- src/m_tokenizer.c | 79 ++++++++++++++++++++++++++++++++++------------- src/m_tokenizer.h | 2 ++ 2 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/m_tokenizer.c b/src/m_tokenizer.c index 26275881d..876dc6b20 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,15 @@ 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->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 +200,13 @@ 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->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 +221,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 +233,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 +249,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 +272,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 +297,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 88cb2a566..f51117301 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; From 6d1908c93ee2f7b687a9c0c0cc2313e1db3a676d Mon Sep 17 00:00:00 2001 From: Lactozilla Date: Fri, 19 Jan 2024 17:01:27 -0300 Subject: [PATCH 2/7] Changes: Moved string conversion helpers into m_misc.c Adapted parser to check for '"' Check if tokenizer returned NULL --- src/m_misc.c | 38 ++++++++++++++++++++++ src/m_misc.h | 6 ++++ src/r_translation.c | 77 ++++++++++++++++++++++++--------------------- 3 files changed, 85 insertions(+), 36 deletions(-) diff --git a/src/m_misc.c b/src/m_misc.c index 5815d17c5..1b6a90c50 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 753991e70..04ac66ca6 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/r_translation.c b/src/r_translation.c index a4df3cde0..d86f3ad95 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 @@ -528,50 +529,26 @@ 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, ...) @@ -615,6 +592,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 @@ -785,7 +765,7 @@ static struct PaletteRemapParseResult *PaletteRemap_ParseString(tokenizer_t *sc) { 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 ':'"); @@ -809,6 +789,13 @@ static struct PaletteRemapParseResult *PaletteRemap_ParseTranslation(const char return result; } +#define CHECK_EOF() \ + if (!tkn) \ + { \ + CONS_Alert(CONS_ERROR, "Error parsing translation '%s': Unexpected EOF\n", name); \ + goto fail; \ + } + void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum) { char *lumpData = (char *)W_CacheLumpNumPwad(wadNum, lumpnum, PU_STATIC); @@ -827,9 +814,11 @@ void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum) char *name = Z_StrDup(tkn); 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) @@ -847,6 +836,15 @@ void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum) goto fail; } 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); + goto fail; + } + tkn = sc->get(sc, 0); + CHECK_EOF(); struct PaletteRemapParseResult *result = NULL; do { @@ -862,10 +860,15 @@ void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum) if (!tkn) break; - if (strcmp(tkn, ",") != 0) - break; + if (strcmp(tkn, "\"") != 0) + { + CONS_Alert(CONS_ERROR, "Error parsing translation '%s': Expected '=', got '%s'\n", name, tkn); + goto fail; + } tkn = sc->get(sc, 0); + if (!tkn || strcmp(tkn, ",") != 0) + break; } while (true); // Allocate it and register it @@ -886,6 +889,8 @@ fail: Z_Free(text); } +#undef CHECK_EOF + typedef struct CustomTranslation { char *name; From 4b057f84ecc8dba7a11ac48a87cf041a135bad76 Mon Sep 17 00:00:00 2001 From: Lactozilla Date: Fri, 19 Jan 2024 19:35:29 -0300 Subject: [PATCH 3/7] Support compound translations --- src/r_translation.c | 230 +++++++++++++++++++++++++++++++++----------- 1 file changed, 173 insertions(+), 57 deletions(-) diff --git a/src/r_translation.c b/src/r_translation.c index d86f3ad95..d732f6b08 100644 --- a/src/r_translation.c +++ b/src/r_translation.c @@ -75,8 +75,7 @@ struct PaletteRemapParseResult } tint; }; - boolean has_error; - char error[4096]; + char *error; }; void PaletteRemap_Init(void) @@ -156,14 +155,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; @@ -453,15 +444,13 @@ struct ParsedTranslation 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 PaletteRemapParseResult *data) { struct ParsedTranslation *node = Z_Calloc(sizeof(struct ParsedTranslation), PU_STATIC, NULL); - node->remap = paletteremaps[id]; + node->remap = remap; node->data = data; - - if (base_translation != -1) - node->baseTranslation = paletteremaps[base_translation]; + node->baseTranslation = base_translation; if (parsedTranslationListHead == NULL) parsedTranslationListHead = parsedTranslationListTail = node; @@ -511,12 +500,20 @@ void R_LoadParsedTranslations(void) struct ParsedTranslation *next = node->next; remaptable_t *tr = node->remap; - PaletteRemap_SetIdentity(tr); + if (tr) + { + if (tr->num_entries == 0) + { + tr->num_entries = NUM_PALETTE_ENTRIES; - if (node->baseTranslation) - memcpy(tr, node->baseTranslation, sizeof(remaptable_t)); + PaletteRemap_SetIdentity(tr); - PaletteRemap_ApplyResult(tr, result); + if (node->baseTranslation) + memcpy(tr, node->baseTranslation, sizeof(remaptable_t)); + } + + PaletteRemap_ApplyResult(tr, result); + } Z_Free(result); Z_Free(node); @@ -553,15 +550,17 @@ static boolean ParseDecimal(tokenizer_t *sc, double *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; } @@ -789,15 +788,119 @@ 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) \ { \ - CONS_Alert(CONS_ERROR, "Error parsing translation '%s': Unexpected EOF\n", name); \ + 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. + for (size_t j = 0; j < entry->num_results; j++) + AddParsedTranslation(tr, base_translation, entry->results[j]); + + tr->num_entries = 0; + + 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); @@ -805,14 +908,18 @@ 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) @@ -820,19 +927,15 @@ void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum) 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); @@ -840,52 +943,65 @@ void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum) 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; + 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) + if (!tkn || strcmp(tkn, "\"") != 0) { - CONS_Alert(CONS_ERROR, "Error parsing translation '%s': Expected '=', got '%s'\n", name, tkn); + 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); } From 4e818b87fd8a599e67f1476b20a1949d5825284e Mon Sep 17 00:00:00 2001 From: Lactozilla Date: Fri, 19 Jan 2024 20:39:28 -0300 Subject: [PATCH 4/7] Support combinations of skincolors and translations --- src/m_tokenizer.c | 2 + src/r_things.c | 17 +-- src/r_translation.c | 298 +++++++++++++++++++++++++------------------- src/r_translation.h | 50 ++++++++ 4 files changed, 230 insertions(+), 137 deletions(-) diff --git a/src/m_tokenizer.c b/src/m_tokenizer.c index 876dc6b20..f36f7f6f3 100644 --- a/src/m_tokenizer.c +++ b/src/m_tokenizer.c @@ -175,6 +175,7 @@ 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->endPos = tokenizer->startPos + 1; @@ -200,6 +201,7 @@ 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) diff --git a/src/r_things.c b/src/r_things.c index 084871700..d135386a0 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 d732f6b08..3799c87cd 100644 --- a/src/r_translation.c +++ b/src/r_translation.c @@ -27,56 +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; - char *error; -}; + 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(); @@ -162,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) { @@ -229,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; @@ -241,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; } @@ -251,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; @@ -294,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 { @@ -304,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; @@ -316,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; @@ -345,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))) @@ -361,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; @@ -379,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 @@ -389,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; @@ -406,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 @@ -416,40 +425,44 @@ 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(remaptable_t *remap, remaptable_t *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 = remap; - node->data = data; node->baseTranslation = base_translation; if (parsedTranslationListHead == NULL) @@ -461,7 +474,7 @@ static void AddParsedTranslation(remaptable_t *remap, remaptable_t *base_transla } } -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; @@ -469,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; } } @@ -496,26 +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; - if (tr) - { - if (tr->num_entries == 0) - { - tr->num_entries = NUM_PALETTE_ENTRIES; - PaletteRemap_SetIdentity(tr); + PaletteRemap_SetIdentity(tr); - if (node->baseTranslation) - memcpy(tr, node->baseTranslation, sizeof(remaptable_t)); - } + 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; @@ -564,12 +572,10 @@ static struct PaletteRemapParseResult *ThrowError(const char *format, ...) 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; } @@ -640,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) @@ -695,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) @@ -724,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) @@ -734,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, ",")) @@ -754,10 +758,10 @@ 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 @@ -772,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; } @@ -884,10 +888,16 @@ static void PrepareNewTranslations(struct NewTranslation *list, size_t count) // The translation is not generated until later, because the palette may not have been loaded. // We store the result for when it's needed. - for (size_t j = 0; j < entry->num_results; j++) - AddParsedTranslation(tr, base_translation, entry->results[j]); + tr->sources = Z_Malloc(entry->num_results * sizeof(paletteremap_t), PU_STATIC, NULL); + tr->num_sources = entry->num_results; - tr->num_entries = 0; + 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); @@ -1093,6 +1103,36 @@ remaptable_t *R_GetTranslationByID(int id) return paletteremaps[id]; } +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_remap) + tr->skincolor_remap = Z_Calloc(NUM_PALETTE_ENTRIES * MAXSKINCOLORS, PU_LEVEL, &tr->skincolor_remap); + + if (!tr->skincolor_remap[skincolor]) + { + UINT8 *remap = Z_Calloc(NUM_PALETTE_ENTRIES, PU_LEVEL, NULL); + + 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]); + + tr->skincolor_remap[skincolor] = remap; + } + + return tr->skincolor_remap[skincolor]; +} + boolean R_TranslationIsValid(int id) { if (id < 0 || id >= (signed)numpaletteremaps) diff --git a/src/r_translation.h b/src/r_translation.h index 70bc2fd27..6219278f6 100644 --- a/src/r_translation.h +++ b/src/r_translation.h @@ -15,10 +15,59 @@ #include "doomdef.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]; unsigned num_entries; + + paletteremap_t *sources; + unsigned num_sources; + + // A remap is 256 bytes long, and there is a max of 1182. + // This means allocating (1182 * 256) bytes, which equals 302592, or ~302kb of memory for every translation. + // So we allocate a list instead. + UINT8 **skincolor_remap; } remaptable_t; void PaletteRemap_Init(void); @@ -35,6 +84,7 @@ 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); boolean R_TranslationIsValid(int id); void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum); From 712c274988d08e9c63d897e1bc43695de2e137fe Mon Sep 17 00:00:00 2001 From: Lactozilla Date: Fri, 19 Jan 2024 20:50:36 -0300 Subject: [PATCH 5/7] Add third argument to getColormap that accepts a translation name Delete 'translations' Lua global --- src/lua_hudlib.c | 20 ++++++++++++++++++-- src/lua_infolib.c | 27 --------------------------- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 19f8b74c7..c56eaf6c5 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; } - // 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; } diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 3facec82b..b2730c593 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); From 9851ec56cfc2c9b06ef5f3003d200ee59f002af1 Mon Sep 17 00:00:00 2001 From: Lactozilla Date: Fri, 19 Jan 2024 21:16:20 -0300 Subject: [PATCH 6/7] Correct comment --- src/r_translation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_translation.h b/src/r_translation.h index 6219278f6..794eb063c 100644 --- a/src/r_translation.h +++ b/src/r_translation.h @@ -64,7 +64,7 @@ typedef struct paletteremap_t *sources; unsigned num_sources; - // A remap is 256 bytes long, and there is a max of 1182. + // A typical remap is 256 bytes long, and there is currently a maximum of 1182 skincolors. // This means allocating (1182 * 256) bytes, which equals 302592, or ~302kb of memory for every translation. // So we allocate a list instead. UINT8 **skincolor_remap; From 8cff33e681deb963b8a48ef939be1eefebba76d9 Mon Sep 17 00:00:00 2001 From: Lactozilla Date: Sat, 20 Jan 2024 01:47:03 -0300 Subject: [PATCH 7/7] Changes: Ported skincolor cache management from the secondcolor branch Adjusted R_GetTranslationRemap to use a similar caching scheme Made sure that if a skincolor was updated, then related translations would be rebuilt --- src/r_defs.h | 3 -- src/r_draw.c | 85 ++++++++++++++++++++------------------------- src/r_draw.h | 21 +++++++++++ src/r_translation.c | 51 ++++++++++++++++++++------- src/r_translation.h | 13 ++++--- 5 files changed, 105 insertions(+), 68 deletions(-) diff --git a/src/r_defs.h b/src/r_defs.h index 39e6765cd..16c660b01 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 b87a8404e..ff2e43df3 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 9cde3cf54..29370015a 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_translation.c b/src/r_translation.c index 3799c87cd..7e1e30d0c 100644 --- a/src/r_translation.c +++ b/src/r_translation.c @@ -1103,6 +1103,17 @@ 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); @@ -1112,25 +1123,39 @@ UINT8 *R_GetTranslationRemap(int id, skincolornum_t skincolor, INT32 skinnum) if (!tr->num_sources || skincolor == SKINCOLOR_NONE) return tr->remap; - if (!tr->skincolor_remap) - tr->skincolor_remap = Z_Calloc(NUM_PALETTE_ENTRIES * MAXSKINCOLORS, PU_LEVEL, &tr->skincolor_remap); + if (!tr->skincolor_remaps) + Z_Calloc(sizeof(*tr->skincolor_remaps) * TT_CACHE_SIZE, PU_LEVEL, &tr->skincolor_remaps); - if (!tr->skincolor_remap[skincolor]) + 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) { - UINT8 *remap = Z_Calloc(NUM_PALETTE_ENTRIES, PU_LEVEL, NULL); + cache = Z_Calloc(sizeof(colorcache_t), PU_LEVEL, NULL); - UINT8 *base_skincolor = R_GetTranslationColormap(skinnum, skincolor, GTC_CACHE); + R_ApplyTranslationRemap(tr, cache->colors, skincolor, skinnum); - 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]); - - tr->skincolor_remap[skincolor] = remap; + tr->skincolor_remaps[skinnum][skincolor] = cache; } - return tr->skincolor_remap[skincolor]; + 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) diff --git a/src/r_translation.h b/src/r_translation.h index 794eb063c..1eb40233d 100644 --- a/src/r_translation.h +++ b/src/r_translation.h @@ -15,6 +15,8 @@ #include "doomdef.h" +#include "r_draw.h" + typedef enum { REMAP_ADD_INDEXRANGE, @@ -58,16 +60,16 @@ typedef struct 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. - // This means allocating (1182 * 256) bytes, which equals 302592, or ~302kb of memory for every translation. - // So we allocate a list instead. - UINT8 **skincolor_remap; + // 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); @@ -85,6 +87,7 @@ 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);