diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 49f783722ab429986fb45b317a2d00c36a4b5f94..21f7c6c45fd2a26aff061aba3df305a845adcca9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,6 +35,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 m_misc.c m_perfstats.c m_random.c + m_tokenizer.c m_queue.c info.c p_ceilng.c @@ -68,6 +69,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 r_things.c r_bbox.c r_textures.c + r_translation.c r_patch.c r_patchrotation.c r_picformats.c diff --git a/src/Sourcefile b/src/Sourcefile index 7beb98c9e313286506c55d359bb19b38b013bfb0..ad5eacfeb9e96f75316ce0d4c66ae8d1e31b4fe1 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -29,6 +29,7 @@ m_menu.c m_misc.c m_perfstats.c m_random.c +m_tokenizer.c m_queue.c info.c p_ceilng.c @@ -62,6 +63,7 @@ r_splats.c r_things.c r_bbox.c r_textures.c +r_translation.c r_patch.c r_patchrotation.c r_picformats.c diff --git a/src/command.c b/src/command.c index fdded341327e688607dc4d2c1aa0a59b495126a1..a065f764cc5d7d7990826ce3e8174e42b7f02563 100644 --- a/src/command.c +++ b/src/command.c @@ -1429,8 +1429,8 @@ void CV_RegisterVar(consvar_t *variable) #ifdef PARANOIA if ((variable->flags & CV_NOINIT) && !(variable->flags & CV_CALL)) I_Error("variable %s has CV_NOINIT without CV_CALL\n", variable->name); - if ((variable->flags & CV_CALL) && !variable->func) - I_Error("variable %s has CV_CALL without a function\n", variable->name); + if ((variable->flags & CV_CALL) && !(variable->func || variable->can_change)) + I_Error("variable %s has CV_CALL without any callbacks\n", variable->name); #endif if (variable->flags & CV_NOINIT) @@ -1496,12 +1496,35 @@ static void Setvalue(consvar_t *var, const char *valstr, boolean stealth) boolean override = false; INT32 overrideval = 0; - // If we want messages informing us if cheats have been enabled or disabled, - // we need to rework the consvars a little bit. This call crashes the game - // on load because not all variables will be registered at that time. -/* boolean prevcheats = false; - if (var->flags & CV_CHEAT) - prevcheats = CV_CheatsEnabled(); */ + // raise 'can change' code + LUA_CVarChanged(var); // let consolelib know what cvar this is. + if (var->flags & CV_CALL && var->can_change && !stealth) + { + if (!var->can_change(valstr)) + { + // The callback refused the default value on register. How naughty... + // So we just use some fallback value. + if (var->string == NULL) + { + if (var->PossibleValue) + { + // Use PossibleValue + valstr = var->PossibleValue[0].strvalue; + } + else + { + // Else, use an empty string + valstr = ""; + } + } + else + { + // Callback returned false, and the game is not registering this variable, + // so we can return safely. + return; + } + } + } if (var->PossibleValue) { @@ -1663,16 +1686,6 @@ found: } finish: - // See the note above. -/* if (var->flags & CV_CHEAT) - { - boolean newcheats = CV_CheatsEnabled(); - - if (!prevcheats && newcheats) - CONS_Printf(M_GetText("Cheats have been enabled.\n")); - else if (prevcheats && !newcheats) - CONS_Printf(M_GetText("Cheats have been disabled.\n")); - } */ if (var->flags & CV_SHOWMODIFONETIME || var->flags & CV_SHOWMODIF) { @@ -1685,8 +1698,7 @@ finish: } var->flags |= CV_MODIFIED; // raise 'on change' code - LUA_CVarChanged(var); // let consolelib know what cvar this is. - if (var->flags & CV_CALL && !stealth) + if (var->flags & CV_CALL && var->func && !stealth) var->func(); return; diff --git a/src/command.h b/src/command.h index 619d8c1dcab07a7ab5dfbe20e98cae409c77fb9d..f0dc62418a8815abfa9e7c9b84d9357aac406b73 100644 --- a/src/command.h +++ b/src/command.h @@ -136,6 +136,7 @@ typedef struct consvar_s //NULL, NULL, 0, NULL, NULL |, 0, NULL, NULL, 0, 0, NUL INT32 flags; // flags see cvflags_t above CV_PossibleValue_t *PossibleValue; // table of possible values void (*func)(void); // called on change, if CV_CALL set + boolean (*can_change)(const char*); // called before change, if CV_CALL set INT32 value; // for INT32 and fixed_t const char *string; // value in string char *zstring; // Either NULL or same as string. @@ -158,6 +159,9 @@ typedef struct consvar_s //NULL, NULL, 0, NULL, NULL |, 0, NULL, NULL, 0, 0, NUL /* name, defaultvalue, flags, PossibleValue, func */ #define CVAR_INIT( ... ) \ +{ __VA_ARGS__, NULL, 0, NULL, NULL, {0, {NULL}}, 0U, (char)0, NULL } + +#define CVAR_INIT_WITH_CALLBACKS( ... ) \ { __VA_ARGS__, 0, NULL, NULL, {0, {NULL}}, 0U, (char)0, NULL } #ifdef OLD22DEMOCOMPAT diff --git a/src/d_main.c b/src/d_main.c index c9419e93027432a2e7df6599a36aaac35bccfda7..d7ee81689ca40ed789316d6a2b6a687d6122eaba 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -50,6 +50,7 @@ #include "p_saveg.h" #include "r_main.h" #include "r_local.h" +#include "r_translation.h" #include "s_sound.h" #include "st_stuff.h" #include "v_video.h" @@ -1467,6 +1468,8 @@ void D_SRB2Main(void) // setup loading screen SCR_Startup(); + PaletteRemap_Init(); + HU_Init(); CON_Init(); diff --git a/src/deh_lua.c b/src/deh_lua.c index 8552360e0c3fb60514ac3ab5f2b932b3ba75508e..3600c3554da059091e513c559f7a9e80cff72de2 100644 --- a/src/deh_lua.c +++ b/src/deh_lua.c @@ -573,7 +573,8 @@ static int ScanConstants(lua_State *L, boolean mathlib, const char *word) if (mathlib) return luaL_error(L, "NiGHTS grade '%s' could not be found.\n", word); return 0; } - else if (fastncmp("MN_",word,3)) { + else if (fastncmp("MN_",word,3)) + { p = word+3; for (i = 0; i < NUMMENUTYPES; i++) if (fastcmp(p, MENUTYPES_LIST[i])) { @@ -583,6 +584,17 @@ static int ScanConstants(lua_State *L, boolean mathlib, const char *word) if (mathlib) return luaL_error(L, "menutype '%s' could not be found.\n", word); return 0; } + else if (mathlib && fastncmp("TRANSLATION_",word,12)) + { + p = word+12; + int id = R_FindCustomTranslation_CaseInsensitive(p); + if (id != -1) + { + lua_pushinteger(L, id); + return 1; + } + return luaL_error(L, "translation '%s' could not be found.\n", word); + } // TODO: 2.3: Delete this alias if (fastcmp(word, "BT_USE")) diff --git a/src/deh_lua.h b/src/deh_lua.h index 1bec371ccb42d5b76549103d9068c5e471825c98..9c6fb6257413aefa47f87766a72d7ca0402c8264 100644 --- a/src/deh_lua.h +++ b/src/deh_lua.h @@ -20,6 +20,7 @@ #include "m_misc.h" #include "p_local.h" #include "st_stuff.h" +#include "r_translation.h" #include "fastcmp.h" #include "lua_script.h" #include "lua_libs.h" diff --git a/src/deh_tables.c b/src/deh_tables.c index 6d29fa2c77e51b27649cc253c11c1905fe992382..fa86518e2e0eaf51555d6ee334a97cad22b3afa3 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -219,6 +219,7 @@ actionpointer_t actionpointers[] = {{A_ChangeColorRelative}, "A_CHANGECOLORRELATIVE"}, {{A_ChangeColorAbsolute}, "A_CHANGECOLORABSOLUTE"}, {{A_Dye}, "A_DYE"}, + {{A_SetTranslation}, "A_SETTRANSLATION"}, {{A_MoveRelative}, "A_MOVERELATIVE"}, {{A_MoveAbsolute}, "A_MOVEABSOLUTE"}, {{A_Thrust}, "A_THRUST"}, diff --git a/src/doomdef.h b/src/doomdef.h index f73c242915c0986f3050cb3bf5ad4c1c5dfde28e..c85abe2d68f6cb87610c3f25eb0857da72312036 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -570,6 +570,7 @@ void M_UnGetToken(void); void M_TokenizerOpen(const char *inputString); void M_TokenizerClose(void); const char *M_TokenizerRead(UINT32 i); +const char *M_TokenizerReadZDoom(UINT32 i); UINT32 M_TokenizerGetEndPos(void); void M_TokenizerSetEndPos(UINT32 newPos); char *sizeu1(size_t num); diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h index d391c415670846fad27d4a22e1e33f46ccb367b3..fbb02f46322c614107fc5523884e7081064c5c4a 100644 --- a/src/hardware/hw_glob.h +++ b/src/hardware/hw_glob.h @@ -74,8 +74,6 @@ typedef struct gl_vissprite_s float spritexscale, spriteyscale; float spritexoffset, spriteyoffset; - skincolornum_t color; - UINT32 renderflags; UINT8 rotateflags; diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index e0548e50d420dbf59457c89c75a8cd31a233a841..36d206843151b202d485f269660ca4094b1d741a 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -39,6 +39,7 @@ #include "../m_cheat.h" #include "../f_finale.h" #include "../r_things.h" // R_GetShadowZ +#include "../r_translation.h" #include "../d_main.h" #include "../p_slopes.h" #include "hw_md2.h" @@ -5064,6 +5065,8 @@ static void HWR_ProjectSprite(mobj_t *thing) boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !R_ThingVerticallyFlipped(thing)); boolean mirrored = thing->mirrored; boolean hflip = (!R_ThingHorizontallyFlipped(thing) != !mirrored); + skincolornum_t color; + UINT16 translation; INT32 dispoffset; angle_t ang; @@ -5495,45 +5498,19 @@ static void HWR_ProjectSprite(mobj_t *thing) vis->gpatch = (patch_t *)W_CachePatchNum(sprframe->lumppat[rot], PU_SPRITE); vis->mobj = thing; + if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer && thing->color == SKINCOLOR_NONE) - vis->color = thing->tracer->color; + color = thing->tracer->color; else - vis->color = thing->color; + color = thing->color; - //Hurdler: 25/04/2000: now support colormap in hardware mode - if ((vis->mobj->flags & (MF_ENEMY|MF_BOSS)) && (vis->mobj->flags2 & MF2_FRET) && !(vis->mobj->flags & MF_GRENADEBOUNCE) && (leveltime & 1)) // Bosses "flash" - { - if (vis->mobj->type == MT_CYBRAKDEMON || vis->mobj->colorized) - vis->colormap = R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE); - else if (vis->mobj->type == MT_METALSONIC_BATTLE) - vis->colormap = R_GetTranslationColormap(TC_METALSONIC, 0, GTC_CACHE); - else - vis->colormap = R_GetTranslationColormap(TC_BOSS, vis->color, GTC_CACHE); - } - else if (vis->color) - { - // New colormap stuff for skins Tails 06-07-2002 - if (thing->colorized) - vis->colormap = R_GetTranslationColormap(TC_RAINBOW, vis->color, GTC_CACHE); - else if (thing->player && thing->player->dashmode >= DASHMODE_THRESHOLD - && (thing->player->charflags & SF_DASHMODE) - && ((leveltime/2) & 1)) - { - if (thing->player->charflags & SF_MACHINE) - vis->colormap = R_GetTranslationColormap(TC_DASHMODE, 0, GTC_CACHE); - else - vis->colormap = R_GetTranslationColormap(TC_RAINBOW, vis->color, GTC_CACHE); - } - else if (thing->skin && thing->sprite == SPR_PLAY) // This thing is a player! - { - UINT8 skinnum = ((skin_t*)thing->skin)->skinnum; - vis->colormap = R_GetTranslationColormap(skinnum, vis->color, GTC_CACHE); - } - else - vis->colormap = R_GetTranslationColormap(TC_DEFAULT, vis->color ? vis->color : SKINCOLOR_CYAN, GTC_CACHE); - } + if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer && thing->translation == 0) + translation = thing->tracer->translation; else - vis->colormap = NULL; + translation = thing->translation; + + //Hurdler: 25/04/2000: now support colormap in hardware mode + vis->colormap = R_GetTranslationForThing(vis->mobj, color, translation); // set top/bottom coords vis->gzt = gzt; @@ -5659,7 +5636,6 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) vis->gpatch = (patch_t *)W_CachePatchNum(sprframe->lumppat[rot], PU_SPRITE); vis->flip = flip; vis->mobj = (mobj_t *)thing; - vis->color = SKINCOLOR_NONE; vis->colormap = NULL; diff --git a/src/info.h b/src/info.h index 20a0a4b8a5823114ad99265d72876fdf5e606c1d..2362935f0629cf7304df3ea5a9f673f2aa3b3183 100644 --- a/src/info.h +++ b/src/info.h @@ -173,6 +173,7 @@ enum actionnum A_CHANGECOLORRELATIVE, A_CHANGECOLORABSOLUTE, A_DYE, + A_SETTRANSLATION, A_MOVERELATIVE, A_MOVEABSOLUTE, A_THRUST, @@ -445,6 +446,7 @@ void A_SetRandomTics(); void A_ChangeColorRelative(); void A_ChangeColorAbsolute(); void A_Dye(); +void A_SetTranslation(); void A_MoveRelative(); void A_MoveAbsolute(); void A_Thrust(); diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c index 1fabe2c00ae17bbd505b576635d7ef7c47a7fd94..aaa676526d1eac6adbad4ac5d8ba34e31388d2cd 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -300,6 +300,30 @@ static void Lua_OnChange(void) lua_remove(gL, 1); // remove LUA_GetErrorMessage } +static boolean Lua_CanChange(const char *valstr) +{ + lua_pushcfunction(gL, LUA_GetErrorMessage); + lua_insert(gL, 1); // Because LUA_Call wants it at index 1. + + // From CV_CanChange registry field, get the function for this cvar by name. + lua_getfield(gL, LUA_REGISTRYINDEX, "CV_CanChange"); + I_Assert(lua_istable(gL, -1)); + lua_pushlightuserdata(gL, this_cvar); + lua_rawget(gL, -2); // get function + + LUA_RawPushUserdata(gL, this_cvar); + lua_pushstring(gL, valstr); + + boolean result; + + LUA_Call(gL, 2, 1, 1); // call function(cvar, valstr) + result = lua_toboolean(gL, -1); + lua_pop(gL, 1); // pop CV_CanChange table + lua_remove(gL, 1); // remove LUA_GetErrorMessage + + return result; +} + static int lib_cvRegisterVar(lua_State *L) { const char *k; @@ -458,6 +482,20 @@ static int lib_cvRegisterVar(lua_State *L) lua_pop(L, 1); cvar->func = Lua_OnChange; } + else if (cvar->flags & CV_CALL && (k && fasticmp(k, "can_change"))) + { + if (!lua_isfunction(L, 4)) + { + TYPEERROR("func", LUA_TFUNCTION) + } + lua_getfield(L, LUA_REGISTRYINDEX, "CV_CanChange"); + I_Assert(lua_istable(L, 5)); + lua_pushlightuserdata(L, cvar); + lua_pushvalue(L, 4); + lua_rawset(L, 5); + lua_pop(L, 1); + cvar->can_change = Lua_CanChange; + } lua_pop(L, 1); } @@ -479,9 +517,9 @@ static int lib_cvRegisterVar(lua_State *L) return luaL_error(L, M_GetText("Variable %s has CV_NOINIT without CV_CALL"), cvar->name); } - if ((cvar->flags & CV_CALL) && !cvar->func) + if ((cvar->flags & CV_CALL) && !(cvar->func || cvar->can_change)) { - return luaL_error(L, M_GetText("Variable %s has CV_CALL without a function"), cvar->name); + return luaL_error(L, M_GetText("Variable %s has CV_CALL without any callbacks"), cvar->name); } cvar->flags |= CV_ALLOWLUA; @@ -672,6 +710,8 @@ int LUA_ConsoleLib(lua_State *L) lua_setfield(L, LUA_REGISTRYINDEX, "CV_PossibleValue"); lua_newtable(L); lua_setfield(L, LUA_REGISTRYINDEX, "CV_OnChange"); + lua_newtable(L); + lua_setfield(L, LUA_REGISTRYINDEX, "CV_CanChange"); // Push opaque CV_PossibleValue pointers // Because I don't care enough to bother. diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 03888f32e0d8244428c6035fc59d1724affcaa1e..3facec82b82da21c92d5041aba81fb1f9f543deb 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -22,6 +22,7 @@ #include "r_patch.h" #include "r_picformats.h" #include "r_things.h" +#include "r_translation.h" #include "r_draw.h" // R_GetColorByName #include "doomstat.h" // luabanks[] @@ -1899,6 +1900,32 @@ 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! @@ -1931,6 +1958,7 @@ 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/lua_mobjlib.c b/src/lua_mobjlib.c index b5c6c0329556e3463dc95e1dc3ce6f8b16cd7f50..85e4590c571e23cfa701f6f9fb15b5948350e1f0 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -14,6 +14,7 @@ #include "fastcmp.h" #include "r_data.h" #include "r_skins.h" +#include "r_translation.h" #include "p_local.h" #include "g_game.h" #include "p_setup.h" @@ -66,6 +67,7 @@ enum mobj_e { mobj_renderflags, mobj_skin, mobj_color, + mobj_translation, mobj_blendmode, mobj_bnext, mobj_bprev, @@ -146,6 +148,7 @@ static const char *const mobj_opt[] = { "renderflags", "skin", "color", + "translation", "blendmode", "bnext", "bprev", @@ -338,6 +341,16 @@ static int mobj_get(lua_State *L) case mobj_color: lua_pushinteger(L, mo->color); break; + case mobj_translation: + if (mo->translation) + { + const char *name = R_GetCustomTranslationName(mo->translation); + if (name) + lua_pushstring(L, name); + break; + } + lua_pushnil(L); + break; case mobj_blendmode: lua_pushinteger(L, mo->blendmode); break; @@ -689,12 +702,26 @@ static int mobj_set(lua_State *L) } case mobj_color: { - UINT16 newcolor = (UINT16)luaL_checkinteger(L,3); + UINT16 newcolor = (UINT16)luaL_checkinteger(L, 3); if (newcolor >= numskincolors) return luaL_error(L, "mobj.color %d out of range (0 - %d).", newcolor, numskincolors-1); mo->color = newcolor; break; } + case mobj_translation: + { + if (!lua_isnil(L, 3)) { + const char *tr = luaL_checkstring(L, 3); + int id = R_FindCustomTranslation(tr); + if (id != -1) + mo->translation = id; + else + return luaL_error(L, "invalid translation '%s'.", tr); + } + else + mo->translation = 0; + break; + } case mobj_blendmode: { INT32 blendmode = (INT32)luaL_checkinteger(L, 3); diff --git a/src/m_misc.c b/src/m_misc.c index 084dc2896eb19b2d23eae9d5ea1f3ad2839cba33..2cf260e949ec63c967d3b4982365085d96d97a13 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -31,6 +31,7 @@ #include "doomdef.h" #include "g_game.h" #include "m_misc.h" +#include "m_tokenizer.h" #include "hu_stuff.h" #include "st_stuff.h" #include "v_video.h" @@ -1975,168 +1976,39 @@ void M_UnGetToken(void) endPos = oldendPos; } -#define NUMTOKENS 2 -static const char *tokenizerInput = NULL; -static UINT32 tokenCapacity[NUMTOKENS] = {0}; -static char *tokenizerToken[NUMTOKENS] = {NULL}; -static UINT32 tokenizerStartPos = 0; -static UINT32 tokenizerEndPos = 0; -static UINT32 tokenizerInputLength = 0; -static UINT8 tokenizerInComment = 0; // 0 = not in comment, 1 = // Single-line, 2 = /* Multi-line */ +static tokenizer_t *globalTokenizer = NULL; void M_TokenizerOpen(const char *inputString) { - size_t i; - - tokenizerInput = inputString; - for (i = 0; i < NUMTOKENS; i++) - { - tokenCapacity[i] = 1024; - tokenizerToken[i] = (char*)Z_Malloc(tokenCapacity[i] * sizeof(char), PU_STATIC, NULL); - } - tokenizerInputLength = strlen(tokenizerInput); + globalTokenizer = Tokenizer_Open(inputString, 2); } void M_TokenizerClose(void) { - size_t i; - - tokenizerInput = NULL; - for (i = 0; i < NUMTOKENS; i++) - Z_Free(tokenizerToken[i]); - tokenizerStartPos = 0; - tokenizerEndPos = 0; - tokenizerInComment = 0; -} - -static void M_DetectComment(UINT32 *pos) -{ - if (tokenizerInComment) - return; - - if (*pos >= tokenizerInputLength - 1) - return; - - if (tokenizerInput[*pos] != '/') - return; - - //Single-line comment start - if (tokenizerInput[*pos + 1] == '/') - tokenizerInComment = 1; - //Multi-line comment start - else if (tokenizerInput[*pos + 1] == '*') - tokenizerInComment = 2; -} - -static void M_ReadTokenString(UINT32 i) -{ - UINT32 tokenLength = tokenizerEndPos - tokenizerStartPos; - if (tokenLength + 1 > tokenCapacity[i]) - { - tokenCapacity[i] = tokenLength + 1; - // Assign the memory. Don't forget an extra byte for the end of the string! - tokenizerToken[i] = (char *)Z_Malloc(tokenCapacity[i] * sizeof(char), PU_STATIC, NULL); - } - // Copy the string. - M_Memcpy(tokenizerToken[i], tokenizerInput + tokenizerStartPos, (size_t)tokenLength); - // Make the final character NUL. - tokenizerToken[i][tokenLength] = '\0'; + Tokenizer_Close(globalTokenizer); + globalTokenizer = NULL; } const char *M_TokenizerRead(UINT32 i) { - if (!tokenizerInput) - return NULL; - - tokenizerStartPos = tokenizerEndPos; - - // Try to detect comments now, in case we're pointing right at one - M_DetectComment(&tokenizerStartPos); - - // Find the first non-whitespace char, or else the end of the string trying - while ((tokenizerInput[tokenizerStartPos] == ' ' - || tokenizerInput[tokenizerStartPos] == '\t' - || tokenizerInput[tokenizerStartPos] == '\r' - || tokenizerInput[tokenizerStartPos] == '\n' - || tokenizerInput[tokenizerStartPos] == '\0' - || tokenizerInput[tokenizerStartPos] == '=' || tokenizerInput[tokenizerStartPos] == ';' // UDMF TEXTMAP. - || tokenizerInComment != 0) - && tokenizerStartPos < tokenizerInputLength) - { - // Try to detect comment endings now - if (tokenizerInComment == 1 && tokenizerInput[tokenizerStartPos] == '\n') - tokenizerInComment = 0; // End of line for a single-line comment - else if (tokenizerInComment == 2 - && tokenizerStartPos < tokenizerInputLength - 1 - && tokenizerInput[tokenizerStartPos] == '*' - && tokenizerInput[tokenizerStartPos+1] == '/') - { - // End of multi-line comment - tokenizerInComment = 0; - tokenizerStartPos++; // Make damn well sure we're out of the comment ending at the end of it all - } - - tokenizerStartPos++; - M_DetectComment(&tokenizerStartPos); - } - - // If the end of the string is reached, no token is to be read - if (tokenizerStartPos == tokenizerInputLength) { - tokenizerEndPos = tokenizerInputLength; + if (!globalTokenizer) return NULL; - } - // Else, if it's one of these three symbols, capture only this one character - else if (tokenizerInput[tokenizerStartPos] == ',' - || tokenizerInput[tokenizerStartPos] == '{' - || tokenizerInput[tokenizerStartPos] == '}') - { - tokenizerEndPos = tokenizerStartPos + 1; - tokenizerToken[i][0] = tokenizerInput[tokenizerStartPos]; - tokenizerToken[i][1] = '\0'; - return tokenizerToken[i]; - } - // Return entire string within quotes, except without the quotes. - else if (tokenizerInput[tokenizerStartPos] == '"') - { - tokenizerEndPos = ++tokenizerStartPos; - while (tokenizerInput[tokenizerEndPos] != '"' && tokenizerEndPos < tokenizerInputLength) - tokenizerEndPos++; - - M_ReadTokenString(i); - tokenizerEndPos++; - return tokenizerToken[i]; - } - // Now find the end of the token. This includes several additional characters that are okay to capture as one character, but not trailing at the end of another token. - tokenizerEndPos = tokenizerStartPos + 1; - while ((tokenizerInput[tokenizerEndPos] != ' ' - && tokenizerInput[tokenizerEndPos] != '\t' - && tokenizerInput[tokenizerEndPos] != '\r' - && tokenizerInput[tokenizerEndPos] != '\n' - && tokenizerInput[tokenizerEndPos] != ',' - && tokenizerInput[tokenizerEndPos] != '{' - && tokenizerInput[tokenizerEndPos] != '}' - && tokenizerInput[tokenizerEndPos] != '=' && tokenizerInput[tokenizerEndPos] != ';' // UDMF TEXTMAP. - && tokenizerInComment == 0) - && tokenizerEndPos < tokenizerInputLength) - { - tokenizerEndPos++; - // Try to detect comment starts now; if it's in a comment, we don't want it in this token - M_DetectComment(&tokenizerEndPos); - } - - M_ReadTokenString(i); - return tokenizerToken[i]; + return Tokenizer_SRB2Read(globalTokenizer, i); } UINT32 M_TokenizerGetEndPos(void) { - return tokenizerEndPos; + if (!globalTokenizer) + return 0; + + return Tokenizer_GetEndPos(globalTokenizer); } void M_TokenizerSetEndPos(UINT32 newPos) { - tokenizerEndPos = newPos; + if (globalTokenizer) + Tokenizer_SetEndPos(globalTokenizer, newPos); } /** Count bits in a number. diff --git a/src/m_tokenizer.c b/src/m_tokenizer.c new file mode 100644 index 0000000000000000000000000000000000000000..26275881d3f81fe8db6c22d2e35226839c75af56 --- /dev/null +++ b/src/m_tokenizer.c @@ -0,0 +1,278 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2013-2023 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file m_tokenizer.c +/// \brief Tokenizer + +#include "m_tokenizer.h" +#include "z_zone.h" + +tokenizer_t *Tokenizer_Open(const char *inputString, unsigned numTokens) +{ + tokenizer_t *tokenizer = Z_Malloc(sizeof(tokenizer_t), PU_STATIC, NULL); + + tokenizer->input = inputString; + tokenizer->startPos = 0; + tokenizer->endPos = 0; + tokenizer->inputLength = 0; + tokenizer->inComment = 0; + tokenizer->get = Tokenizer_Read; + + if (numTokens < 1) + numTokens = 1; + + tokenizer->numTokens = numTokens; + tokenizer->capacity = Z_Malloc(sizeof(UINT32) * numTokens, PU_STATIC, NULL); + tokenizer->token = Z_Malloc(sizeof(char*) * numTokens, PU_STATIC, NULL); + + for (size_t i = 0; i < numTokens; i++) + { + tokenizer->capacity[i] = 1024; + tokenizer->token[i] = (char*)Z_Malloc(tokenizer->capacity[i] * sizeof(char), PU_STATIC, NULL); + } + + tokenizer->inputLength = strlen(tokenizer->input); + + return tokenizer; +} + +void Tokenizer_Close(tokenizer_t *tokenizer) +{ + if (!tokenizer) + return; + + for (size_t i = 0; i < tokenizer->numTokens; i++) + Z_Free(tokenizer->token[i]); + Z_Free(tokenizer->capacity); + Z_Free(tokenizer->token); + Z_Free(tokenizer); +} + +static void Tokenizer_DetectComment(tokenizer_t *tokenizer, UINT32 *pos) +{ + if (tokenizer->inComment) + return; + + if (*pos >= tokenizer->inputLength - 1) + return; + + if (tokenizer->input[*pos] != '/') + return; + + // Single-line comment start + if (tokenizer->input[*pos + 1] == '/') + tokenizer->inComment = 1; + // Multi-line comment start + else if (tokenizer->input[*pos + 1] == '*') + tokenizer->inComment = 2; +} + +static void Tokenizer_ReadTokenString(tokenizer_t *tokenizer, UINT32 i) +{ + UINT32 tokenLength = tokenizer->endPos - tokenizer->startPos; + if (tokenLength + 1 > tokenizer->capacity[i]) + { + tokenizer->capacity[i] = tokenLength + 1; + // Assign the memory. Don't forget an extra byte for the end of the string! + tokenizer->token[i] = (char *)Z_Malloc(tokenizer->capacity[i] * sizeof(char), PU_STATIC, NULL); + } + // Copy the string. + M_Memcpy(tokenizer->token[i], tokenizer->input + tokenizer->startPos, (size_t)tokenLength); + // Make the final character NUL. + tokenizer->token[i][tokenLength] = '\0'; +} + +const char *Tokenizer_Read(tokenizer_t *tokenizer, UINT32 i) +{ + if (!tokenizer->input) + return NULL; + + tokenizer->startPos = tokenizer->endPos; + + // Try to detect comments now, in case we're pointing right at one + Tokenizer_DetectComment(tokenizer, &tokenizer->startPos); + + // Find the first non-whitespace char, or else the end of the string trying + while ((tokenizer->input[tokenizer->startPos] == ' ' + || tokenizer->input[tokenizer->startPos] == '\t' + || tokenizer->input[tokenizer->startPos] == '\r' + || tokenizer->input[tokenizer->startPos] == '\n' + || tokenizer->input[tokenizer->startPos] == '\0' + || tokenizer->inComment != 0) + && tokenizer->startPos < tokenizer->inputLength) + { + // Try to detect comment endings now + if (tokenizer->inComment == 1 && tokenizer->input[tokenizer->startPos] == '\n') + tokenizer->inComment = 0; // End of line for a single-line comment + else if (tokenizer->inComment == 2 + && tokenizer->startPos < tokenizer->inputLength - 1 + && tokenizer->input[tokenizer->startPos] == '*' + && tokenizer->input[tokenizer->startPos+1] == '/') + { + // End of multi-line comment + tokenizer->inComment = 0; + tokenizer->startPos++; // Make damn well sure we're out of the comment ending at the end of it all + } + + tokenizer->startPos++; + Tokenizer_DetectComment(tokenizer, &tokenizer->startPos); + } + + // If the end of the string is reached, no token is to be read + if (tokenizer->startPos == tokenizer->inputLength) { + tokenizer->endPos = tokenizer->inputLength; + return NULL; + } + // Else, if it's one of these three symbols, capture only this one character + else if (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->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++; + + Tokenizer_ReadTokenString(tokenizer, i); + tokenizer->endPos++; + return tokenizer->token[i]; + } + + // Now find the end of the token. This includes several additional characters that are okay to capture as one character, but not trailing at the end of another token. + tokenizer->endPos = tokenizer->startPos + 1; + while ((tokenizer->input[tokenizer->endPos] != ' ' + && tokenizer->input[tokenizer->endPos] != '\t' + && tokenizer->input[tokenizer->endPos] != '\r' + && tokenizer->input[tokenizer->endPos] != '\n' + && tokenizer->input[tokenizer->endPos] != ',' + && tokenizer->input[tokenizer->endPos] != '{' + && tokenizer->input[tokenizer->endPos] != '}' + && 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); + } + + Tokenizer_ReadTokenString(tokenizer, i); + return tokenizer->token[i]; +} + +const char *Tokenizer_SRB2Read(tokenizer_t *tokenizer, UINT32 i) +{ + if (!tokenizer->input) + return NULL; + + tokenizer->startPos = tokenizer->endPos; + + // Try to detect comments now, in case we're pointing right at one + Tokenizer_DetectComment(tokenizer, &tokenizer->startPos); + + // Find the first non-whitespace char, or else the end of the string trying + while ((tokenizer->input[tokenizer->startPos] == ' ' + || tokenizer->input[tokenizer->startPos] == '\t' + || tokenizer->input[tokenizer->startPos] == '\r' + || tokenizer->input[tokenizer->startPos] == '\n' + || tokenizer->input[tokenizer->startPos] == '\0' + || tokenizer->input[tokenizer->startPos] == '=' || tokenizer->input[tokenizer->startPos] == ';' // UDMF TEXTMAP. + || tokenizer->inComment != 0) + && tokenizer->startPos < tokenizer->inputLength) + { + // Try to detect comment endings now + if (tokenizer->inComment == 1 && tokenizer->input[tokenizer->startPos] == '\n') + tokenizer->inComment = 0; // End of line for a single-line comment + else if (tokenizer->inComment == 2 + && tokenizer->startPos < tokenizer->inputLength - 1 + && tokenizer->input[tokenizer->startPos] == '*' + && tokenizer->input[tokenizer->startPos+1] == '/') + { + // End of multi-line comment + tokenizer->inComment = 0; + tokenizer->startPos++; // Make damn well sure we're out of the comment ending at the end of it all + } + + tokenizer->startPos++; + Tokenizer_DetectComment(tokenizer, &tokenizer->startPos); + } + + // If the end of the string is reached, no token is to be read + if (tokenizer->startPos == tokenizer->inputLength) { + tokenizer->endPos = tokenizer->inputLength; + return NULL; + } + // Else, if it's one of these three symbols, capture only this one character + else if (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++; + + Tokenizer_ReadTokenString(tokenizer, i); + tokenizer->endPos++; + return tokenizer->token[i]; + } + + // Now find the end of the token. This includes several additional characters that are okay to capture as one character, but not trailing at the end of another token. + tokenizer->endPos = tokenizer->startPos + 1; + while ((tokenizer->input[tokenizer->endPos] != ' ' + && tokenizer->input[tokenizer->endPos] != '\t' + && tokenizer->input[tokenizer->endPos] != '\r' + && tokenizer->input[tokenizer->endPos] != '\n' + && tokenizer->input[tokenizer->endPos] != ',' + && tokenizer->input[tokenizer->endPos] != '{' + && tokenizer->input[tokenizer->endPos] != '}' + && tokenizer->input[tokenizer->endPos] != '=' && tokenizer->input[tokenizer->endPos] != ';' // UDMF TEXTMAP. + && 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); + } + + Tokenizer_ReadTokenString(tokenizer, i); + return tokenizer->token[i]; +} + +UINT32 Tokenizer_GetEndPos(tokenizer_t *tokenizer) +{ + return tokenizer->endPos; +} + +void Tokenizer_SetEndPos(tokenizer_t *tokenizer, UINT32 newPos) +{ + tokenizer->endPos = newPos; +} diff --git a/src/m_tokenizer.h b/src/m_tokenizer.h new file mode 100644 index 0000000000000000000000000000000000000000..88cb2a566907d27b0d70508fb20c9ab1dff84fbb --- /dev/null +++ b/src/m_tokenizer.h @@ -0,0 +1,38 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2013-2023 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file m_tokenizer.h +/// \brief Tokenizer + +#ifndef __M_TOKENIZER__ +#define __M_TOKENIZER__ + +#include "doomdef.h" + +typedef struct Tokenizer +{ + const char *input; + unsigned numTokens; + UINT32 *capacity; + char **token; + UINT32 startPos; + UINT32 endPos; + UINT32 inputLength; + UINT8 inComment; // 0 = not in comment, 1 = // Single-line, 2 = /* Multi-line */ + const char *(*get)(struct Tokenizer*, UINT32); +} tokenizer_t; + +tokenizer_t *Tokenizer_Open(const char *inputString, unsigned numTokens); +void Tokenizer_Close(tokenizer_t *tokenizer); + +const char *Tokenizer_Read(tokenizer_t *tokenizer, UINT32 i); +const char *Tokenizer_SRB2Read(tokenizer_t *tokenizer, UINT32 i); +UINT32 Tokenizer_GetEndPos(tokenizer_t *tokenizer); +void Tokenizer_SetEndPos(tokenizer_t *tokenizer, UINT32 newPos); + +#endif diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c index 0c3d519be11c93c12894f52a02d3c3276a38ef20..938a91a0de0c098b036e9980541d13c9b72cbb9b 100644 --- a/src/netcode/d_netcmd.c +++ b/src/netcode/d_netcmd.c @@ -109,6 +109,9 @@ static void Color2_OnChange(void); static void DummyConsvar_OnChange(void); static void SoundTest_OnChange(void); +static boolean Skin_CanChange(const char *valstr); +static boolean Skin2_CanChange(const char *valstr); + #ifdef NETGAME_DEVMODE static void Fishcake_OnChange(void); #endif @@ -228,7 +231,6 @@ consvar_t cv_seenames = CVAR_INIT ("seenames", "Ally/Foe", CV_SAVE|CV_ALLOWLUA, consvar_t cv_allowseenames = CVAR_INIT ("allowseenames", "Yes", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_YesNo, NULL); // names -static char *lastskinnames[2]; consvar_t cv_playername = CVAR_INIT ("name", "Sonic", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name_OnChange); consvar_t cv_playername2 = CVAR_INIT ("name2", "Tails", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name2_OnChange); // player colors @@ -236,8 +238,8 @@ UINT16 lastgoodcolor = SKINCOLOR_BLUE, lastgoodcolor2 = SKINCOLOR_BLUE; consvar_t cv_playercolor = CVAR_INIT ("color", "Blue", CV_CALL|CV_NOINIT|CV_ALLOWLUA, Color_cons_t, Color_OnChange); consvar_t cv_playercolor2 = CVAR_INIT ("color2", "Orange", CV_CALL|CV_NOINIT|CV_ALLOWLUA, Color_cons_t, Color2_OnChange); // player's skin, saved for commodity, when using a favorite skins wad.. -consvar_t cv_skin = CVAR_INIT ("skin", DEFAULTSKIN, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin_OnChange); -consvar_t cv_skin2 = CVAR_INIT ("skin2", DEFAULTSKIN2, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin2_OnChange); +consvar_t cv_skin = CVAR_INIT_WITH_CALLBACKS ("skin", DEFAULTSKIN, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin_OnChange, Skin_CanChange); +consvar_t cv_skin2 = CVAR_INIT_WITH_CALLBACKS ("skin2", DEFAULTSKIN2, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin2_OnChange, Skin2_CanChange); // saved versions of the above six consvar_t cv_defaultplayercolor = CVAR_INIT ("defaultcolor", "Blue", CV_SAVE, Color_cons_t, NULL); @@ -4790,6 +4792,41 @@ static void Name2_OnChange(void) SendNameAndColor2(); } +static boolean Skin_CanChange(const char *valstr) +{ + (void)valstr; + + if (!Playing()) + return true; // do whatever you want + + if (!(multiplayer || netgame)) // In single player. + return true; + + if (CanChangeSkin(consoleplayer) && !P_PlayerMoving(consoleplayer)) + return true; + else + { + CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); + return false; + } +} + +static boolean Skin2_CanChange(const char *valstr) +{ + (void)valstr; + + if (!Playing() || !splitscreen) + return true; // do whatever you want + + if (CanChangeSkin(secondarydisplayplayer) && !P_PlayerMoving(secondarydisplayplayer)) + return true; + else + { + CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); + return false; + } +} + /** Sends a skin change for the console player, unless that player is moving. * \sa cv_skin, Skin2_OnChange, Color_OnChange * \author Graue <graue@oceanbase.org> @@ -4797,10 +4834,7 @@ static void Name2_OnChange(void) static void Skin_OnChange(void) { if (!Playing()) - return; // do whatever you want - - if (lastskinnames[0] == NULL) - lastskinnames[0] = Z_StrDup(cv_skin.string); + return; if (!(multiplayer || netgame)) // In single player. { @@ -4816,17 +4850,7 @@ static void Skin_OnChange(void) return; } - if (CanChangeSkin(consoleplayer) && !P_PlayerMoving(consoleplayer)) - { - SendNameAndColor(); - Z_Free(lastskinnames[0]); - lastskinnames[0] = Z_StrDup(cv_skin.string); - } - else - { - CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); - CV_StealthSet(&cv_skin, lastskinnames[0]); - } + SendNameAndColor(); } /** Sends a skin change for the secondary splitscreen player, unless that @@ -4837,22 +4861,9 @@ static void Skin_OnChange(void) static void Skin2_OnChange(void) { if (!Playing() || !splitscreen) - return; // do whatever you want - - if (lastskinnames[1] == NULL) - lastskinnames[1] = Z_StrDup(cv_skin2.string); + return; - if (CanChangeSkin(secondarydisplayplayer) && !P_PlayerMoving(secondarydisplayplayer)) - { - SendNameAndColor2(); - Z_Free(lastskinnames[1]); - lastskinnames[1] = Z_StrDup(cv_skin.string); - } - else - { - CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); - CV_StealthSet(&cv_skin2, lastskinnames[1]); - } + SendNameAndColor2(); } /** Sends a color change for the console player, unless that player is moving. diff --git a/src/p_enemy.c b/src/p_enemy.c index 23d855a637b3182c908c58336c710e2bb8cc049b..dd259f410bbe9c825e179c47e2e607fe39a554a1 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -23,6 +23,7 @@ #include "m_random.h" #include "m_misc.h" #include "r_skins.h" +#include "r_translation.h" #include "i_video.h" #include "z_zone.h" #include "lua_hook.h" @@ -196,6 +197,7 @@ void A_SetRandomTics(mobj_t *actor); void A_ChangeColorRelative(mobj_t *actor); void A_ChangeColorAbsolute(mobj_t *actor); void A_Dye(mobj_t *actor); +void A_SetTranslation(mobj_t *actor); void A_MoveRelative(mobj_t *actor); void A_MoveAbsolute(mobj_t *actor); void A_Thrust(mobj_t *actor); @@ -9214,6 +9216,26 @@ void A_Dye(mobj_t *actor) } } +// Function: A_SetTranslation +// +// Description: Changes the translation of an actor. +// +// var1 = translation ID +// var2 = unused +// +void A_SetTranslation(mobj_t *actor) +{ + INT32 locvar1 = var1; + + if (LUA_CallAction(A_SETTRANSLATION, actor)) + return; + + if (R_TranslationIsValid(locvar1)) + actor->translation = (UINT32)locvar1; + else + actor->translation = 0; +} + // Function: A_MoveRelative // // Description: Moves an object (wrapper for P_Thrust) diff --git a/src/p_mobj.h b/src/p_mobj.h index a980691beb8b296b4fd16415141e4f9e5f484af8..f2e4cbf3d63ff1675825078c1e6fc165aa515af8 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -331,9 +331,14 @@ typedef struct mobj_s UINT16 eflags; // extra flags void *skin; // overrides 'sprite' when non-NULL (for player bodies to 'remember' the skin) + // Player and mobj sprites in multiplayer modes are modified // using an internal color lookup table for re-indexing. - UINT16 color; // This replaces MF_TRANSLATION. Use 0 for default (no translation). + UINT16 color; + + // This replaces MF_TRANSLATION. Use 0 for default (no translation). + UINT16 translation; + struct player_s *drawonlyforplayer; // If set, hides the mobj for everyone except this player and their spectators struct mobj_s *dontdrawforviewmobj; // If set, hides the mobj if dontdrawforviewmobj is the current camera (first-person player or awayviewmobj) diff --git a/src/p_saveg.c b/src/p_saveg.c index 6440ab0e36bf0cf953ce5411146d6a5ead455e99..2c7b9e238fd62a1081a56e3c9894eacf4555cdaf 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -1741,7 +1741,8 @@ typedef enum MD2_FLOORSPRITESLOPE = 1<<22, MD2_DISPOFFSET = 1<<23, MD2_DRAWONLYFORPLAYER = 1<<24, - MD2_DONTDRAWFORVIEWMOBJ = 1<<25 + MD2_DONTDRAWFORVIEWMOBJ = 1<<25, + MD2_TRANSLATION = 1<<26 } mobj_diff2_t; typedef enum @@ -1930,6 +1931,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) diff2 |= MD2_CVMEM; if (mobj->color) diff2 |= MD2_COLOR; + if (mobj->translation) + diff2 |= MD2_TRANSLATION; if (mobj->skin) diff2 |= MD2_SKIN; if (mobj->extravalue1) @@ -2163,6 +2166,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) WRITEUINT32(save_p, mobj->dontdrawforviewmobj->mobjnum); if (diff2 & MD2_DISPOFFSET) WRITEINT32(save_p, mobj->dispoffset); + if (diff2 & MD2_TRANSLATION) + WRITEUINT16(save_p, mobj->translation); WRITEUINT32(save_p, mobj->mobjnum); } @@ -3225,6 +3230,8 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker) mobj->dispoffset = READINT32(save_p); else mobj->dispoffset = mobj->info->dispoffset; + if (diff2 & MD2_TRANSLATION) + mobj->translation = READUINT16(save_p); if (diff & MD_REDFLAG) { diff --git a/src/p_setup.c b/src/p_setup.c index f3b4b6b8f0c7717fd71f58ffee3aaf3cbc16f323..ced9c3b92a4790e7c1a9a5170063df9c65b57608 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -30,6 +30,7 @@ #include "r_data.h" #include "r_things.h" // for R_AddSpriteDefs #include "r_textures.h" +#include "r_translation.h" #include "r_patch.h" #include "r_picformats.h" #include "r_sky.h" @@ -8319,6 +8320,8 @@ static boolean P_LoadAddon(UINT16 numlumps) HWR_ClearAllTextures(); #endif + R_LoadParsedTranslations(); + // // search for sprite replacements // diff --git a/src/p_user.c b/src/p_user.c index 35f2e1ebe857b7f8a79e4ebf360553391af83ef0..fae18af67ffd64cee4e043ca07c5a72711d1b31f 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2042,6 +2042,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) } ghost->color = mobj->color; + ghost->translation = mobj->translation; ghost->colorized = mobj->colorized; // alternatively, "true" for sonic advance style colourisation ghost->angle = (mobj->player ? mobj->player->drawangle : mobj->angle); diff --git a/src/r_data.c b/src/r_data.c index e2b74da40712014986b3adc495b77a3ce5c9a221..0a13d27dbaf55f9e9092adeb0de4635523c96606 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -20,6 +20,7 @@ #include "m_misc.h" #include "r_data.h" #include "r_textures.h" +#include "r_translation.h" #include "r_patch.h" #include "r_picformats.h" #include "w_wad.h" @@ -1217,6 +1218,9 @@ void R_InitData(void) R_Init8to16(); } + CONS_Printf("R_LoadParsedTranslations()...\n"); + R_LoadParsedTranslations(); + CONS_Printf("R_LoadTextures()...\n"); R_LoadTextures(); diff --git a/src/r_defs.h b/src/r_defs.h index 2931eb1c8afca3c2b70bcb58c880c86003e8a65e..39e6765cd7af35d6478e690de8c48045d063b505 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -24,12 +24,11 @@ #include "screen.h" // MAXVIDWIDTH, MAXVIDHEIGHT -#ifdef HWRENDER -#include "m_aatree.h" -#endif - #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 6e7efd004e656121e895ae4169cc5842b8b01932..b87a8404ebc3ea5895425dd81b1c78dc6040fdfa 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -18,6 +18,7 @@ #include "doomdef.h" #include "doomstat.h" #include "r_local.h" +#include "r_translation.h" #include "st_stuff.h" // need ST_HEIGHT #include "i_video.h" #include "v_video.h" @@ -452,8 +453,14 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 translatio switch (translation) { case TC_ALLWHITE: - memset(dest_colormap, 0, NUM_PALETTE_ENTRIES * sizeof(UINT8)); - return; + case TC_DASHMODE: + remaptable_t *tr = R_GetBuiltInTranslation((SINT8)translation); + if (tr) + { + memcpy(dest_colormap, tr->remap, NUM_PALETTE_ENTRIES); + return; + } + break; case TC_RAINBOW: if (color >= numskincolors) I_Error("Invalid skin color #%hu", (UINT16)color); @@ -501,40 +508,6 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 translatio for (i = 0; i < COLORRAMPSIZE; i++) dest_colormap[96+i] = dest_colormap[skincolors[SKINCOLOR_COBALT].ramp[i]]; } - else if (translation == TC_DASHMODE) // This is a long one, because MotorRoach basically hand-picked the indices - { - // greens -> ketchups - dest_colormap[96] = dest_colormap[97] = 48; - dest_colormap[98] = 49; - dest_colormap[99] = 51; - dest_colormap[100] = 52; - dest_colormap[101] = dest_colormap[102] = 54; - dest_colormap[103] = 34; - dest_colormap[104] = 37; - dest_colormap[105] = 39; - dest_colormap[106] = 41; - for (i = 0; i < 5; i++) - dest_colormap[107 + i] = 43 + i; - - // reds -> steel blues - dest_colormap[32] = 146; - dest_colormap[33] = 147; - dest_colormap[34] = dest_colormap[35] = 170; - dest_colormap[36] = 171; - dest_colormap[37] = dest_colormap[38] = 172; - dest_colormap[39] = dest_colormap[40] = dest_colormap[41] = 173; - dest_colormap[42] = dest_colormap[43] = dest_colormap[44] = 174; - dest_colormap[45] = dest_colormap[46] = dest_colormap[47] = 175; - dest_colormap[71] = 139; - - // steel blues -> oranges - dest_colormap[170] = 52; - dest_colormap[171] = 54; - dest_colormap[172] = 56; - dest_colormap[173] = 42; - dest_colormap[174] = 45; - dest_colormap[175] = 47; - } return; } else if (color == SKINCOLOR_NONE) diff --git a/src/r_splats.c b/src/r_splats.c index e9665e84a35a4089e72cfa40120157cf418cf9e9..027ccd720aed2902b7c096040548b95e3d3b369d 100644 --- a/src/r_splats.c +++ b/src/r_splats.c @@ -404,7 +404,7 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr } ds_colormap = vis->colormap; - ds_translation = R_GetSpriteTranslation(vis); + ds_translation = R_GetTranslationForThing(vis->mobj, vis->color, vis->translation); if (ds_translation == NULL) ds_translation = colormaps; diff --git a/src/r_things.c b/src/r_things.c index d2163d14a09c7d8e899a435465229a57015dbadd..9da647501e711c4e4e3943d379249763d5441712 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -25,6 +25,7 @@ #include "i_system.h" #include "r_fps.h" #include "r_things.h" +#include "r_translation.h" #include "r_patch.h" #include "r_patchrotation.h" #include "r_picformats.h" @@ -761,50 +762,46 @@ void R_DrawFlippedMaskedColumn(column_t *column) dc_texturemid = basetexturemid; } -boolean R_SpriteIsFlashing(vissprite_t *vis) +UINT8 *R_GetTranslationForThing(mobj_t *mobj, skincolornum_t color, UINT16 translation) { - return (!(vis->cut & SC_PRECIP) - && (vis->mobj->flags & (MF_ENEMY|MF_BOSS)) - && (vis->mobj->flags2 & MF2_FRET) - && !(vis->mobj->flags & MF_GRENADEBOUNCE) - && (leveltime & 1)); -} - -UINT8 *R_GetSpriteTranslation(vissprite_t *vis) -{ - if (R_SpriteIsFlashing(vis)) // Bosses "flash" + if (R_ThingIsFlashing(mobj)) // Bosses "flash" { - if (vis->mobj->type == MT_CYBRAKDEMON || vis->mobj->colorized) + if (mobj->type == MT_CYBRAKDEMON || mobj->colorized) return R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE); - else if (vis->mobj->type == MT_METALSONIC_BATTLE) + else if (mobj->type == MT_METALSONIC_BATTLE) return R_GetTranslationColormap(TC_METALSONIC, 0, GTC_CACHE); else - return R_GetTranslationColormap(TC_BOSS, vis->color, GTC_CACHE); + return R_GetTranslationColormap(TC_BOSS, color, GTC_CACHE); } - else if (vis->color) + else if (translation != 0) + { + remaptable_t *tr = R_GetTranslationByID(translation); + if (tr != NULL) + return tr->remap; + } + else if (color != SKINCOLOR_NONE) { // New colormap stuff for skins Tails 06-07-2002 - if (!(vis->cut & SC_PRECIP) && vis->mobj->colorized) - return R_GetTranslationColormap(TC_RAINBOW, vis->color, GTC_CACHE); - else if (!(vis->cut & SC_PRECIP) - && vis->mobj->player && vis->mobj->player->dashmode >= DASHMODE_THRESHOLD - && (vis->mobj->player->charflags & SF_DASHMODE) + if (mobj->colorized) + return R_GetTranslationColormap(TC_RAINBOW, color, GTC_CACHE); + else if (mobj->player && mobj->player->dashmode >= DASHMODE_THRESHOLD + && (mobj->player->charflags & SF_DASHMODE) && ((leveltime/2) & 1)) { - if (vis->mobj->player->charflags & SF_MACHINE) + if (mobj->player->charflags & SF_MACHINE) return R_GetTranslationColormap(TC_DASHMODE, 0, GTC_CACHE); else - return R_GetTranslationColormap(TC_RAINBOW, vis->color, GTC_CACHE); + return R_GetTranslationColormap(TC_RAINBOW, color, GTC_CACHE); } - else if (!(vis->cut & SC_PRECIP) && vis->mobj->skin && vis->mobj->sprite == SPR_PLAY) // This thing is a player! + else if (mobj->skin && mobj->sprite == SPR_PLAY) // This thing is a player! { - UINT8 skinnum = ((skin_t*)vis->mobj->skin)->skinnum; - return R_GetTranslationColormap(skinnum, vis->color, GTC_CACHE); + UINT8 skinnum = ((skin_t*)mobj->skin)->skinnum; + return R_GetTranslationColormap(skinnum, color, GTC_CACHE); } else // Use the defaults - return R_GetTranslationColormap(TC_DEFAULT, vis->color, GTC_CACHE); + return R_GetTranslationColormap(TC_DEFAULT, color, GTC_CACHE); } - else if (vis->mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome. + 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); return NULL; @@ -852,11 +849,11 @@ static void R_DrawVisSprite(vissprite_t *vis) colfunc = colfuncs[BASEDRAWFUNC]; // hack: this isn't resetting properly somewhere. dc_colormap = vis->colormap; - dc_translation = R_GetSpriteTranslation(vis); + dc_translation = R_GetTranslationForThing(vis->mobj, vis->color, vis->translation); - if (R_SpriteIsFlashing(vis)) // Bosses "flash" + if (R_ThingIsFlashing(vis->mobj)) // Bosses "flash" colfunc = colfuncs[COLDRAWFUNC_TRANS]; // translate certain pixels to white - else if (vis->color && vis->transmap) // Color mapping + else if (dc_translation && vis->transmap) // Color mapping { colfunc = colfuncs[COLDRAWFUNC_TRANSTRANS]; dc_transmap = vis->transmap; @@ -866,9 +863,7 @@ static void R_DrawVisSprite(vissprite_t *vis) colfunc = colfuncs[COLDRAWFUNC_FUZZY]; dc_transmap = vis->transmap; //Fab : 29-04-98: translucency table } - else if (vis->color) // translate green skin to another color - colfunc = colfuncs[COLDRAWFUNC_TRANS]; - else if (vis->mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome. + else if (dc_translation) // translate green skin to another color colfunc = colfuncs[COLDRAWFUNC_TRANS]; // Hack: Use a special column function for drop shadows that bypasses @@ -1403,6 +1398,7 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, shadow->mobj = thing; // Easy access! Tails 06-07-2002 shadow->color = thing->color; + shadow->translation = 0; shadow->x1 = x1 < portalclipstart ? portalclipstart : x1; shadow->x2 = x2 >= portalclipend ? portalclipend-1 : x2; @@ -2200,6 +2196,11 @@ static void R_ProjectSprite(mobj_t *thing) else vis->color = oldthing->color; + if ((oldthing->flags2 & MF2_LINKDRAW) && oldthing->tracer && oldthing->translation == 0) + vis->translation = oldthing->tracer->translation; + else + vis->translation = oldthing->translation; + vis->x1 = x1 < portalclipstart ? portalclipstart : x1; vis->x2 = x2 >= portalclipend ? portalclipend-1 : x2; @@ -2470,6 +2471,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) vis->extra_colormap = thing->subsector->sector->extra_colormap; vis->heightsec = thing->subsector->sector->heightsec; vis->color = SKINCOLOR_NONE; + vis->translation = 0; // Fullbright vis->colormap = colormaps; @@ -3570,6 +3572,14 @@ boolean R_ThingIsFullDark(mobj_t *thing) return ((thing->frame & FF_BRIGHTMASK) == FF_FULLDARK || (thing->renderflags & RF_BRIGHTMASK) == RF_FULLDARK); } +boolean R_ThingIsFlashing(mobj_t *thing) +{ + if (thing == NULL) + return false; + + return (thing->flags & (MF_ENEMY|MF_BOSS)) && (thing->flags2 & MF2_FRET) && !(thing->flags & MF_GRENADEBOUNCE) && (leveltime & 1); +} + // Offsets MT_OVERLAY towards the camera at render-time - Works in splitscreen! // The &x and &y arguments should be pre-interpolated, and will be modified void R_ThingOffsetOverlay(mobj_t *thing, fixed_t *x, fixed_t *y) diff --git a/src/r_things.h b/src/r_things.h index ed2156baab4b15784360c0664352a1b8b2461d19..bd0a350093453a7798e3d254c6ddb7b3c16cce35 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -88,6 +88,10 @@ boolean R_ThingIsFullBright (mobj_t *thing); boolean R_ThingIsSemiBright (mobj_t *thing); boolean R_ThingIsFullDark (mobj_t *thing); +boolean R_ThingIsFlashing (mobj_t *thing); + +UINT8 *R_GetTranslationForThing(mobj_t *mobj, skincolornum_t color, UINT16 translation); + void R_ThingOffsetOverlay (mobj_t *thing, fixed_t *outx, fixed_t *outy); // -------------- @@ -216,6 +220,7 @@ typedef struct vissprite_s fixed_t shadowscale; skincolornum_t color; + UINT16 translation; INT16 clipbot[MAXVIDWIDTH], cliptop[MAXVIDWIDTH]; @@ -226,12 +231,8 @@ extern UINT32 visspritecount, numvisiblesprites; void R_ClipSprites(drawseg_t* dsstart, portal_t* portal); -boolean R_SpriteIsFlashing(vissprite_t *vis); - void R_DrawThingBoundingBox(vissprite_t *spr); -UINT8 *R_GetSpriteTranslation(vissprite_t *vis); - // ---------- // DRAW NODES // ---------- diff --git a/src/r_translation.c b/src/r_translation.c new file mode 100644 index 0000000000000000000000000000000000000000..a4df3cde0c9bdc04702f963e62b3703ca6f42c58 --- /dev/null +++ b/src/r_translation.c @@ -0,0 +1,993 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2006 by Randy Heit. +// Copyright (C) 2023 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_translation.c +/// \brief Translation table handling + +#include "r_translation.h" +#include "r_data.h" +#include "r_draw.h" +#include "v_video.h" // pMasterPalette +#include "z_zone.h" +#include "w_wad.h" +#include "m_tokenizer.h" + +#include <errno.h> + +static remaptable_t **paletteremaps = NULL; +static unsigned numpaletteremaps = 0; + +static int allWhiteRemap = 0; +static int dashModeRemap = 0; + +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); + +enum PaletteRemapType +{ + REMAP_ADD_INDEXRANGE, + REMAP_ADD_COLORRANGE, + REMAP_ADD_COLOURISATION, + REMAP_ADD_DESATURATION, + REMAP_ADD_TINT +}; + +struct PaletteRemapParseResult +{ + 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; + }; + + boolean has_error; + char error[4096]; +}; + +void PaletteRemap_Init(void) +{ + // First translation must be the identity one. + remaptable_t *base = PaletteRemap_New(); + PaletteRemap_SetIdentity(base); + 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)); + + // All white (TC_ALLWHITE) + remaptable_t *allWhite = PaletteRemap_New(); + memset(allWhite->remap, 0, NUM_PALETTE_ENTRIES * sizeof(UINT8)); + allWhiteRemap = PaletteRemap_Add(allWhite); + R_AddCustomTranslation("AllWhite", allWhiteRemap); + + // All black + remaptable_t *allBlack = PaletteRemap_New(); + memset(allBlack->remap, 31, NUM_PALETTE_ENTRIES * sizeof(UINT8)); + 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)); + + // Dash mode (TC_DASHMODE) + MakeDashModeRemap(); +} + +remaptable_t *PaletteRemap_New(void) +{ + remaptable_t *tr = Z_Calloc(sizeof(remaptable_t), PU_STATIC, NULL); + tr->num_entries = NUM_PALETTE_ENTRIES; + return tr; +} + +remaptable_t *PaletteRemap_Copy(remaptable_t *tr) +{ + remaptable_t *copy = Z_Malloc(sizeof(remaptable_t), PU_STATIC, NULL); + memcpy(copy, tr, sizeof(remaptable_t)); + return copy; +} + +boolean PaletteRemap_Equal(remaptable_t *a, remaptable_t *b) +{ + if (a->num_entries != b->num_entries) + return false; + + return memcmp(a->remap, b->remap, a->num_entries) == 0; +} + +void PaletteRemap_SetIdentity(remaptable_t *tr) +{ + for (unsigned i = 0; i < tr->num_entries; i++) + { + tr->remap[i] = i; + } +} + +boolean PaletteRemap_IsIdentity(remaptable_t *tr) +{ + for (unsigned i = 0; i < NUM_PALETTE_ENTRIES; i++) + { + if (tr->remap[i] != i) + return false; + } + + return true; +} + +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; + + return numpaletteremaps - 1; +} + +// This is a long one, because MotorRoach basically hand-picked the indices +static void MakeDashModeRemap(void) +{ + remaptable_t *dashmode = PaletteRemap_New(); + + PaletteRemap_SetIdentity(dashmode); + + UINT8 *dest_colormap = dashmode->remap; + + // greens -> ketchups + dest_colormap[96] = dest_colormap[97] = 48; + dest_colormap[98] = 49; + dest_colormap[99] = 51; + dest_colormap[100] = 52; + dest_colormap[101] = dest_colormap[102] = 54; + dest_colormap[103] = 34; + dest_colormap[104] = 37; + dest_colormap[105] = 39; + dest_colormap[106] = 41; + for (unsigned i = 0; i < 5; i++) + dest_colormap[107 + i] = 43 + i; + + // reds -> steel blues + dest_colormap[32] = 146; + dest_colormap[33] = 147; + dest_colormap[34] = dest_colormap[35] = 170; + dest_colormap[36] = 171; + dest_colormap[37] = dest_colormap[38] = 172; + dest_colormap[39] = dest_colormap[40] = dest_colormap[41] = 173; + dest_colormap[42] = dest_colormap[43] = dest_colormap[44] = 174; + dest_colormap[45] = dest_colormap[46] = dest_colormap[47] = 175; + dest_colormap[71] = 139; + + // steel blues -> oranges + dest_colormap[170] = 52; + dest_colormap[171] = 54; + dest_colormap[172] = 56; + dest_colormap[173] = 42; + dest_colormap[174] = 45; + dest_colormap[175] = 47; + + dashModeRemap = PaletteRemap_Add(dashmode); + + R_AddCustomTranslation("DashMode", dashModeRemap); +} + +static boolean PalIndexOutOfRange(int color) +{ + return color < 0 || color > 255; +} + +static boolean IndicesOutOfRange(int start, int end) +{ + return PalIndexOutOfRange(start) || PalIndexOutOfRange(end); +} + +static boolean IndicesOutOfRange2(int start1, int end1, int start2, int end2) +{ + return IndicesOutOfRange(start1, end1) || IndicesOutOfRange(start2, end2); +} + +#define SWAP(a, b, t) { \ + t swap = a; \ + a = b; \ + b = swap; \ +} + +static boolean PaletteRemap_AddIndexRange(remaptable_t *tr, int start, int end, int pal1, int pal2) +{ + if (IndicesOutOfRange2(start, end, pal1, pal2)) + return false; + + if (start > end) + { + SWAP(start, end, int); + SWAP(pal1, pal2, int); + } + else if (start == end) + { + tr->remap[start] = pal1; + return true; + } + + double palcol = pal1; + double palstep = (pal2 - palcol) / (end - start); + + for (int i = start; i <= end; palcol += palstep, ++i) + { + double idx = round(palcol); + tr->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) +{ + if (IndicesOutOfRange(start, end)) + return false; + + double r1 = r1i; + double g1 = g1i; + double b1 = b1i; + double r2 = r2i; + double g2 = g2i; + double b2 = b2i; + double r, g, b; + double rs, gs, bs; + + if (start > end) + { + SWAP(start, end, int); + + r = r2; + g = g2; + b = b2; + rs = r1 - r2; + gs = g1 - g2; + bs = b1 - b2; + } + else + { + r = r1; + g = g1; + b = b1; + rs = r2 - r1; + gs = g2 - g1; + bs = b2 - b1; + } + + if (start == end) + { + tr->remap[start] = NearestColor(r, g, b); + } + else + { + rs /= (end - start); + gs /= (end - start); + bs /= (end - start); + + for (int i = start; i <= end; ++i) + { + tr->remap[i] = NearestColor(r, g, b); + r += rs; + g += gs; + b += bs; + } + } + + return true; +} + +#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) +{ + if (IndicesOutOfRange(start, end)) + return false; + + r1 = CLAMP(r1, 0.0, 2.0); + g1 = CLAMP(g1, 0.0, 2.0); + b1 = CLAMP(b1, 0.0, 2.0); + r2 = CLAMP(r2, 0.0, 2.0); + g2 = CLAMP(g2, 0.0, 2.0); + b2 = CLAMP(b2, 0.0, 2.0); + + if (start > end) + { + SWAP(start, end, int); + SWAP(r1, r2, double); + SWAP(g1, g2, double); + SWAP(b1, b2, double); + } + + r2 -= r1; + g2 -= g1; + b2 -= b1; + r1 *= 255; + g1 *= 255; + b1 *= 255; + + 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; + + tr->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))) + ); + } + + return true; +} + +#undef CLAMP + +#undef SWAP + +static boolean PaletteRemap_AddColourisation(remaptable_t *tr, 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 grey = (br * 0.299 + bg * 0.587 + bb * 0.114) / 255.0f; + if (grey > 1.0) + grey = 1.0; + + br = r * grey; + bg = g * grey; + bb = b * grey; + + tr->remap[i] = NearestColor( + (int)br, + (int)bg, + (int)bb + ); + } + + return true; +} + +static boolean PaletteRemap_AddTint(remaptable_t *tr, 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 a = amount * 0.01f; + float ia = 1.0f - a; + + br = br * ia + r * a; + bg = bg * ia + g * a; + bb = bb * ia + b * a; + + tr->remap[i] = NearestColor( + (int)br, + (int)bg, + (int)bb + ); + } + + return true; +} + +static boolean PaletteRemap_AddInvert(remaptable_t *tr, 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] + ); + } + + return true; +} + +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) +{ + 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]; + + if (parsedTranslationListHead == NULL) + parsedTranslationListHead = parsedTranslationListTail = node; + else + { + parsedTranslationListTail->next = node; + parsedTranslationListTail = node; + } +} + +static void PaletteRemap_ApplyResult(remaptable_t *tr, struct PaletteRemapParseResult *data) +{ + int start = data->start; + int end = data->end; + + switch (data->type) + { + case REMAP_ADD_INDEXRANGE: + PaletteRemap_AddIndexRange(tr, start, end, data->indexRange.pal1, data->indexRange.pal2); + break; + case REMAP_ADD_COLORRANGE: + PaletteRemap_AddColorRange(tr, 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, + data->colourisation.r, data->colourisation.g, data->colourisation.b); + break; + case REMAP_ADD_DESATURATION: + PaletteRemap_AddDesaturation(tr, 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); + break; + } +} + +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); + + Z_Free(result); + Z_Free(node); + + node = next; + } + + parsedTranslationListHead = parsedTranslationListTail = NULL; +} + +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) + return false; + + *out = result; + + return true; +} + +static boolean ParseNumber(tokenizer_t *sc, int *out) +{ + return StringToNumber(sc->get(sc, 0), 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) + return false; + + *out = result; + + return true; +} + +static struct PaletteRemapParseResult *ThrowError(const char *format, ...) +{ + struct PaletteRemapParseResult *err = Z_Calloc(sizeof(struct PaletteRemapParseResult), PU_STATIC, NULL); + + va_list argptr; + va_start(argptr, format); + vsnprintf(err->error, sizeof err->error, format, argptr); + va_end(argptr); + + err->has_error = true; + + return err; +} + +static struct PaletteRemapParseResult *MakeResult(enum PaletteRemapType 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; + return tr; +} + +static struct PaletteRemapParseResult *PaletteRemap_ParseString(tokenizer_t *sc) +{ + int start, end; + + if (!ParseNumber(sc, &start)) + return ThrowError("expected a number for start range"); + if (!ExpectToken(sc, ":")) + return ThrowError("expected ':'"); + if (!ParseNumber(sc, &end)) + return ThrowError("expected a number for end range"); + + if (start < 0 || start > 255 || end < 0 || end > 255) + return ThrowError("palette indices out of range"); + + if (!ExpectToken(sc, "=")) + return ThrowError("expected '='"); + + const char *tkn = sc->get(sc, 0); + if (strcmp(tkn, "[") == 0) + { + // translation using RGB values + int r1, g1, b1; + int r2, g2, b2; + + // start + if (!ParseNumber(sc, &r1)) + return ThrowError("expected a number for starting red"); + if (!ExpectToken(sc, ",")) + return ThrowError("expected ','"); + + if (!ParseNumber(sc, &g1)) + return ThrowError("expected a number for starting green"); + if (!ExpectToken(sc, ",")) + return ThrowError("expected ','"); + + if (!ParseNumber(sc, &b1)) + return ThrowError("expected a number for starting blue"); + if (!ExpectToken(sc, ",")) + return ThrowError("expected ','"); + + if (!ExpectToken(sc, "]")) + return ThrowError("expected ']'"); + if (!ExpectToken(sc, ":")) + return ThrowError("expected ':'"); + if (!ExpectToken(sc, "[")) + return ThrowError("expected '["); + + // end + if (!ParseNumber(sc, &r2)) + return ThrowError("expected a number for ending red"); + if (!ExpectToken(sc, ",")) + return ThrowError("expected ','"); + + if (!ParseNumber(sc, &g2)) + return ThrowError("expected a number for ending green"); + if (!ExpectToken(sc, ",")) + return ThrowError("expected ','"); + + if (!ParseNumber(sc, &b2)) + return ThrowError("expected a number for ending blue"); + if (!ExpectToken(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; + return tr; + } + else if (strcmp(tkn, "%") == 0) + { + // translation using RGB values (desaturation) + double r1, g1, b1; + double r2, g2, b2; + + if (!ExpectToken(sc, "[")) + return ThrowError("expected '["); + + // start + if (!ParseDecimal(sc, &r1)) + return ThrowError("expected a number for starting red"); + if (!ExpectToken(sc, ",")) + return ThrowError("expected ','"); + + if (!ParseDecimal(sc, &g1)) + return ThrowError("expected a number for starting green"); + if (!ExpectToken(sc, ",")) + return ThrowError("expected ','"); + + if (!ParseDecimal(sc, &b1)) + return ThrowError("expected a number for starting blue"); + if (!ExpectToken(sc, "]")) + return ThrowError("expected ']'"); + + if (!ExpectToken(sc, ":")) + return ThrowError("expected ':'"); + if (!ExpectToken(sc, "[")) + return ThrowError("expected '["); + + // end + if (!ParseDecimal(sc, &r2)) + return ThrowError("expected a number for ending red"); + if (!ExpectToken(sc, ",")) + return ThrowError("expected ','"); + + if (!ParseDecimal(sc, &g2)) + return ThrowError("expected a number for ending green"); + if (!ExpectToken(sc, ",")) + return ThrowError("expected ','"); + + if (!ParseDecimal(sc, &b2)) + return ThrowError("expected a number for ending blue"); + if (!ExpectToken(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; + return tr; + } + else if (strcmp(tkn, "#") == 0) + { + // Colourise translation + int r, g, b; + + if (!ExpectToken(sc, "[")) + return ThrowError("expected '["); + if (!ParseNumber(sc, &r)) + return ThrowError("expected a number for red"); + if (!ExpectToken(sc, ",")) + return ThrowError("expected ','"); + if (!ParseNumber(sc, &g)) + return ThrowError("expected a number for green"); + if (!ExpectToken(sc, ",")) + return ThrowError("expected ','"); + if (!ParseNumber(sc, &b)) + return ThrowError("expected a number for blue"); + if (!ExpectToken(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; + return tr; + } + else if (strcmp(tkn, "@") == 0) + { + // 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 (!ParseNumber(sc, &r)) + return ThrowError("expected a number for red"); + if (!ExpectToken(sc, ",")) + return ThrowError("expected ','"); + if (!ParseNumber(sc, &g)) + return ThrowError("expected a number for green"); + if (!ExpectToken(sc, ",")) + return ThrowError("expected ','"); + if (!ParseNumber(sc, &b)) + return ThrowError("expected a number for blue"); + if (!ExpectToken(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; + return tr; + } + else + { + int pal1, pal2; + + if (!StringToNumber(tkn, &pal1)) + return ThrowError("expected a number for starting index"); + if (!ExpectToken(sc, ":")) + return ThrowError("expected ':'"); + if (!ParseNumber(sc, &pal2)) + 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; + return tr; + } + + return NULL; +} + +static struct PaletteRemapParseResult *PaletteRemap_ParseTranslation(const char *translation) +{ + tokenizer_t *sc = Tokenizer_Open(translation, 1); + struct PaletteRemapParseResult *result = PaletteRemap_ParseString(sc); + Tokenizer_Close(sc); + return result; +} + +void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum) +{ + 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); + memmove(text, lumpData, lumpLength); + text[lumpLength] = '\0'; + Z_Free(lumpData); + + tokenizer_t *sc = Tokenizer_Open(text, 1); + const char *tkn = sc->get(sc, 0); + while (tkn != NULL) + { + int base_translation = -1; + + char *name = Z_StrDup(tkn); + + tkn = sc->get(sc, 0); + if (strcmp(tkn, ":") == 0) + { + tkn = sc->get(sc, 0); + + 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; + } + + tkn = sc->get(sc, 0); + } + + 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); + + struct PaletteRemapParseResult *result = NULL; + do { + result = PaletteRemap_ParseTranslation(tkn); + if (result->has_error) + { + CONS_Alert(CONS_ERROR, "Error parsing translation '%s': %s\n", name, result->error); + Z_Free(result); + goto fail; + } + + tkn = sc->get(sc, 0); + if (!tkn) + break; + + if (strcmp(tkn, ",") != 0) + break; + + tkn = sc->get(sc, 0); + } 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: + Tokenizer_Close(sc); + Z_Free(text); +} + +typedef struct CustomTranslation +{ + char *name; + unsigned id; + UINT32 hash; +} customtranslation_t; + +static customtranslation_t *customtranslations = NULL; +static unsigned numcustomtranslations = 0; + +int R_FindCustomTranslation(const char *name) +{ + UINT32 hash = quickncasehash(name, strlen(name)); + + for (unsigned i = 0; i < numcustomtranslations; i++) + { + if (hash == customtranslations[i].hash && strcmp(name, customtranslations[i].name) == 0) + return (int)customtranslations[i].id; + } + + return -1; +} + +// This is needed for SOC (which is case insensitive) +int R_FindCustomTranslation_CaseInsensitive(const char *name) +{ + for (unsigned i = 0; i < numcustomtranslations; i++) + { + if (stricmp(name, customtranslations[i].name) == 0) + return (int)customtranslations[i].id; + } + + return -1; +} + +void R_AddCustomTranslation(const char *name, int trnum) +{ + customtranslation_t *tr = NULL; + UINT32 hash = quickncasehash(name, strlen(name)); + + for (unsigned i = 0; i < numcustomtranslations; i++) + { + customtranslation_t *lookup = &customtranslations[i]; + if (hash == lookup->hash && strcmp(name, lookup->name) == 0) + { + tr = lookup; + break; + } + } + + if (tr == NULL) + { + numcustomtranslations++; + customtranslations = Z_Realloc(customtranslations, sizeof(customtranslation_t) * numcustomtranslations, PU_STATIC, NULL); + tr = &customtranslations[numcustomtranslations - 1]; + } + + tr->id = trnum; + tr->name = Z_StrDup(name); + tr->hash = quickncasehash(name, strlen(name)); +} + +const char *R_GetCustomTranslationName(unsigned id) +{ + for (unsigned i = 0; i < numcustomtranslations; i++) + { + if (id == customtranslations[i].id) + return customtranslations[i].name; + } + + return NULL; +} + +unsigned R_NumCustomTranslations(void) +{ + return numcustomtranslations; +} + +remaptable_t *R_GetTranslationByID(int id) +{ + if (!R_TranslationIsValid(id)) + return NULL; + + return paletteremaps[id]; +} + +boolean R_TranslationIsValid(int id) +{ + if (id < 0 || id >= (signed)numpaletteremaps) + return false; + + return true; +} + +remaptable_t *R_GetBuiltInTranslation(SINT8 tc) +{ + switch (tc) + { + case TC_ALLWHITE: + return R_GetTranslationByID(allWhiteRemap); + case TC_DASHMODE: + return R_GetTranslationByID(dashModeRemap); + } + return NULL; +} diff --git a/src/r_translation.h b/src/r_translation.h new file mode 100644 index 0000000000000000000000000000000000000000..70bc2fd27e4e248301029c93de562c24dab467ac --- /dev/null +++ b/src/r_translation.h @@ -0,0 +1,45 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2006 by Randy Heit. +// Copyright (C) 2023 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file r_translation.h +/// \brief Translation table handling + +#ifndef __R_TRANSLATION__ +#define __R_TRANSLATION__ + +#include "doomdef.h" + +typedef struct +{ + UINT8 remap[256]; + unsigned num_entries; +} remaptable_t; + +void PaletteRemap_Init(void); +remaptable_t *PaletteRemap_New(void); +remaptable_t *PaletteRemap_Copy(remaptable_t *tr); +boolean PaletteRemap_Equal(remaptable_t *a, remaptable_t *b); +void PaletteRemap_SetIdentity(remaptable_t *tr); +boolean PaletteRemap_IsIdentity(remaptable_t *tr); +unsigned PaletteRemap_Add(remaptable_t *tr); + +int R_FindCustomTranslation(const char *name); +int R_FindCustomTranslation_CaseInsensitive(const char *name); +void R_AddCustomTranslation(const char *name, int trnum); +const char *R_GetCustomTranslationName(unsigned id); +unsigned R_NumCustomTranslations(void); +remaptable_t *R_GetTranslationByID(int id); +boolean R_TranslationIsValid(int id); + +void R_ParseTrnslate(INT32 wadNum, UINT16 lumpnum); +void R_LoadParsedTranslations(void); + +remaptable_t *R_GetBuiltInTranslation(SINT8 tc); + +#endif diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index 9b51cfb8094a1d6897e0685e9531ccd21df0fbdb..7142cb64cb571e5b1c84489b1b38c9c48bdbf561 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -292,6 +292,7 @@ <ClInclude Include="..\m_fixed.h" /> <ClInclude Include="..\m_menu.h" /> <ClInclude Include="..\m_misc.h" /> + <ClInclude Include="..\m_tokenizer.h" /> <ClInclude Include="..\m_perfstats.h" /> <ClInclude Include="..\m_queue.h" /> <ClInclude Include="..\m_random.h" /> @@ -342,6 +343,7 @@ <ClInclude Include="..\r_state.h" /> <ClInclude Include="..\r_textures.h" /> <ClInclude Include="..\r_things.h" /> + <ClInclude Include="..\r_translation.h" /> <ClInclude Include="..\screen.h" /> <ClInclude Include="..\snake.h" /> <ClInclude Include="..\sounds.h" /> @@ -468,6 +470,7 @@ <ClCompile Include="..\m_fixed.c" /> <ClCompile Include="..\m_menu.c" /> <ClCompile Include="..\m_misc.c" /> + <ClCompile Include="..\m_tokenizer.c" /> <ClCompile Include="..\m_perfstats.c" /> <ClCompile Include="..\m_queue.c" /> <ClCompile Include="..\m_random.c" /> @@ -530,6 +533,7 @@ <ClCompile Include="..\r_splats.c" /> <ClCompile Include="..\r_textures.c" /> <ClCompile Include="..\r_things.c" /> + <ClCompile Include="..\r_translation.c" /> <ClCompile Include="..\screen.c" /> <ClCompile Include="..\snake.c" /> <ClCompile Include="..\sounds.c" /> diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters index 96501b2160e587937f5513fa864f3a2f93a6d6ac..44c353ae29899fd561065a64b50d517cff34505b 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj.filters +++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters @@ -339,6 +339,9 @@ <ClInclude Include="..\m_misc.h"> <Filter>M_Misc</Filter> </ClInclude> + <ClInclude Include="..\m_tokenizer.h"> + <Filter>M_Misc</Filter> + </ClInclude> <ClInclude Include="..\m_perfstats.h"> <Filter>M_Misc</Filter> </ClInclude> @@ -537,6 +540,9 @@ <ClInclude Include="..\r_textures.h"> <Filter>R_Rend</Filter> </ClInclude> + <ClInclude Include="..\r_translation.h"> + <Filter>R_Rend</Filter> + </ClInclude> <ClInclude Include="..\r_portal.h"> <Filter>R_Rend</Filter> </ClInclude> @@ -831,6 +837,9 @@ <ClCompile Include="..\m_misc.c"> <Filter>M_Misc</Filter> </ClCompile> + <ClCompile Include="..\m_tokenizer.c"> + <Filter>M_Misc</Filter> + </ClCompile> <ClCompile Include="..\m_perfstats.c"> <Filter>M_Misc</Filter> </ClCompile> @@ -1081,6 +1090,9 @@ <ClCompile Include="..\r_textures.c"> <Filter>R_Rend</Filter> </ClCompile> + <ClCompile Include="..\r_translation.c"> + <Filter>R_Rend</Filter> + </ClCompile> <ClCompile Include="..\r_portal.c"> <Filter>R_Rend</Filter> </ClCompile> diff --git a/src/w_wad.c b/src/w_wad.c index 10359de22826096ef2cbb5700564436d9fa79a22..06025b6b1ee4547b6cdaccf6b5788dd6453089c4 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -59,6 +59,7 @@ #include "r_textures.h" #include "r_patch.h" #include "r_picformats.h" +#include "r_translation.h" #include "i_time.h" #include "i_system.h" #include "i_video.h" // rendermode @@ -810,6 +811,16 @@ static void W_ReadFileShaders(wadfile_t *wadfile) #endif } +static void W_LoadTrnslateLumps(UINT16 w) +{ + UINT16 lump = W_CheckNumForNamePwad("TRNSLATE", w, 0); + while (lump != INT16_MAX) + { + R_ParseTrnslate(w, lump); + lump = W_CheckNumForNamePwad("TRNSLATE", (UINT16)w, lump + 1); + } +} + // Allocate a wadfile, setup the lumpinfo (directory) and // lumpcache, add the wadfile to the current active wadfiles // @@ -963,6 +974,9 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) // Load maps from file P_LoadMapsFromFile(numwadfiles - 1, !startup); + // The below hack makes me load this here. + W_LoadTrnslateLumps(numwadfiles - 1); + // TODO: HACK ALERT - Load Lua & SOC stuff right here. I feel like this should be out of this place, but... Let's stick with this for now. switch (wadfile->type) {