mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2024-11-15 01:01:33 +00:00
Merge branch 'translations' into 'next'
ZDoom translations See merge request STJr/SRB2!2190
This commit is contained in:
commit
a3b79056e9
31 changed files with 1595 additions and 261 deletions
|
@ -35,6 +35,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
|
||||||
m_misc.c
|
m_misc.c
|
||||||
m_perfstats.c
|
m_perfstats.c
|
||||||
m_random.c
|
m_random.c
|
||||||
|
m_tokenizer.c
|
||||||
m_queue.c
|
m_queue.c
|
||||||
info.c
|
info.c
|
||||||
p_ceilng.c
|
p_ceilng.c
|
||||||
|
@ -68,6 +69,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
|
||||||
r_things.c
|
r_things.c
|
||||||
r_bbox.c
|
r_bbox.c
|
||||||
r_textures.c
|
r_textures.c
|
||||||
|
r_translation.c
|
||||||
r_patch.c
|
r_patch.c
|
||||||
r_patchrotation.c
|
r_patchrotation.c
|
||||||
r_picformats.c
|
r_picformats.c
|
||||||
|
|
|
@ -29,6 +29,7 @@ m_menu.c
|
||||||
m_misc.c
|
m_misc.c
|
||||||
m_perfstats.c
|
m_perfstats.c
|
||||||
m_random.c
|
m_random.c
|
||||||
|
m_tokenizer.c
|
||||||
m_queue.c
|
m_queue.c
|
||||||
info.c
|
info.c
|
||||||
p_ceilng.c
|
p_ceilng.c
|
||||||
|
@ -62,6 +63,7 @@ r_splats.c
|
||||||
r_things.c
|
r_things.c
|
||||||
r_bbox.c
|
r_bbox.c
|
||||||
r_textures.c
|
r_textures.c
|
||||||
|
r_translation.c
|
||||||
r_patch.c
|
r_patch.c
|
||||||
r_patchrotation.c
|
r_patchrotation.c
|
||||||
r_picformats.c
|
r_picformats.c
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
#include "p_saveg.h"
|
#include "p_saveg.h"
|
||||||
#include "r_main.h"
|
#include "r_main.h"
|
||||||
#include "r_local.h"
|
#include "r_local.h"
|
||||||
|
#include "r_translation.h"
|
||||||
#include "s_sound.h"
|
#include "s_sound.h"
|
||||||
#include "st_stuff.h"
|
#include "st_stuff.h"
|
||||||
#include "v_video.h"
|
#include "v_video.h"
|
||||||
|
@ -1471,6 +1472,8 @@ void D_SRB2Main(void)
|
||||||
// setup loading screen
|
// setup loading screen
|
||||||
SCR_Startup();
|
SCR_Startup();
|
||||||
|
|
||||||
|
PaletteRemap_Init();
|
||||||
|
|
||||||
HU_Init();
|
HU_Init();
|
||||||
|
|
||||||
CON_Init();
|
CON_Init();
|
||||||
|
|
|
@ -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);
|
if (mathlib) return luaL_error(L, "NiGHTS grade '%s' could not be found.\n", word);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else if (fastncmp("MN_",word,3)) {
|
else if (fastncmp("MN_",word,3))
|
||||||
|
{
|
||||||
p = word+3;
|
p = word+3;
|
||||||
for (i = 0; i < NUMMENUTYPES; i++)
|
for (i = 0; i < NUMMENUTYPES; i++)
|
||||||
if (fastcmp(p, MENUTYPES_LIST[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);
|
if (mathlib) return luaL_error(L, "menutype '%s' could not be found.\n", word);
|
||||||
return 0;
|
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
|
// TODO: 2.3: Delete this alias
|
||||||
if (fastcmp(word, "BT_USE"))
|
if (fastcmp(word, "BT_USE"))
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "m_misc.h"
|
#include "m_misc.h"
|
||||||
#include "p_local.h"
|
#include "p_local.h"
|
||||||
#include "st_stuff.h"
|
#include "st_stuff.h"
|
||||||
|
#include "r_translation.h"
|
||||||
#include "fastcmp.h"
|
#include "fastcmp.h"
|
||||||
#include "lua_script.h"
|
#include "lua_script.h"
|
||||||
#include "lua_libs.h"
|
#include "lua_libs.h"
|
||||||
|
|
|
@ -219,6 +219,7 @@ actionpointer_t actionpointers[] =
|
||||||
{{A_ChangeColorRelative}, "A_CHANGECOLORRELATIVE"},
|
{{A_ChangeColorRelative}, "A_CHANGECOLORRELATIVE"},
|
||||||
{{A_ChangeColorAbsolute}, "A_CHANGECOLORABSOLUTE"},
|
{{A_ChangeColorAbsolute}, "A_CHANGECOLORABSOLUTE"},
|
||||||
{{A_Dye}, "A_DYE"},
|
{{A_Dye}, "A_DYE"},
|
||||||
|
{{A_SetTranslation}, "A_SETTRANSLATION"},
|
||||||
{{A_MoveRelative}, "A_MOVERELATIVE"},
|
{{A_MoveRelative}, "A_MOVERELATIVE"},
|
||||||
{{A_MoveAbsolute}, "A_MOVEABSOLUTE"},
|
{{A_MoveAbsolute}, "A_MOVEABSOLUTE"},
|
||||||
{{A_Thrust}, "A_THRUST"},
|
{{A_Thrust}, "A_THRUST"},
|
||||||
|
|
|
@ -555,6 +555,7 @@ void M_UnGetToken(void);
|
||||||
void M_TokenizerOpen(const char *inputString);
|
void M_TokenizerOpen(const char *inputString);
|
||||||
void M_TokenizerClose(void);
|
void M_TokenizerClose(void);
|
||||||
const char *M_TokenizerRead(UINT32 i);
|
const char *M_TokenizerRead(UINT32 i);
|
||||||
|
const char *M_TokenizerReadZDoom(UINT32 i);
|
||||||
UINT32 M_TokenizerGetEndPos(void);
|
UINT32 M_TokenizerGetEndPos(void);
|
||||||
void M_TokenizerSetEndPos(UINT32 newPos);
|
void M_TokenizerSetEndPos(UINT32 newPos);
|
||||||
char *sizeu1(size_t num);
|
char *sizeu1(size_t num);
|
||||||
|
|
|
@ -74,8 +74,6 @@ typedef struct gl_vissprite_s
|
||||||
float spritexscale, spriteyscale;
|
float spritexscale, spriteyscale;
|
||||||
float spritexoffset, spriteyoffset;
|
float spritexoffset, spriteyoffset;
|
||||||
|
|
||||||
skincolornum_t color;
|
|
||||||
|
|
||||||
UINT32 renderflags;
|
UINT32 renderflags;
|
||||||
UINT8 rotateflags;
|
UINT8 rotateflags;
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "../m_cheat.h"
|
#include "../m_cheat.h"
|
||||||
#include "../f_finale.h"
|
#include "../f_finale.h"
|
||||||
#include "../r_things.h" // R_GetShadowZ
|
#include "../r_things.h" // R_GetShadowZ
|
||||||
|
#include "../r_translation.h"
|
||||||
#include "../d_main.h"
|
#include "../d_main.h"
|
||||||
#include "../p_slopes.h"
|
#include "../p_slopes.h"
|
||||||
#include "hw_md2.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 vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !R_ThingVerticallyFlipped(thing));
|
||||||
boolean mirrored = thing->mirrored;
|
boolean mirrored = thing->mirrored;
|
||||||
boolean hflip = (!R_ThingHorizontallyFlipped(thing) != !mirrored);
|
boolean hflip = (!R_ThingHorizontallyFlipped(thing) != !mirrored);
|
||||||
|
skincolornum_t color;
|
||||||
|
UINT16 translation;
|
||||||
INT32 dispoffset;
|
INT32 dispoffset;
|
||||||
|
|
||||||
angle_t ang;
|
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->gpatch = (patch_t *)W_CachePatchNum(sprframe->lumppat[rot], PU_SPRITE);
|
||||||
|
|
||||||
vis->mobj = thing;
|
vis->mobj = thing;
|
||||||
|
|
||||||
if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer && thing->color == SKINCOLOR_NONE)
|
if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer && thing->color == SKINCOLOR_NONE)
|
||||||
vis->color = thing->tracer->color;
|
color = thing->tracer->color;
|
||||||
else
|
else
|
||||||
vis->color = thing->color;
|
color = thing->color;
|
||||||
|
|
||||||
|
if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer && thing->translation == 0)
|
||||||
|
translation = thing->tracer->translation;
|
||||||
|
else
|
||||||
|
translation = thing->translation;
|
||||||
|
|
||||||
//Hurdler: 25/04/2000: now support colormap in hardware mode
|
//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"
|
vis->colormap = R_GetTranslationForThing(vis->mobj, color, translation);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
vis->colormap = NULL;
|
|
||||||
|
|
||||||
// set top/bottom coords
|
// set top/bottom coords
|
||||||
vis->gzt = gzt;
|
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->gpatch = (patch_t *)W_CachePatchNum(sprframe->lumppat[rot], PU_SPRITE);
|
||||||
vis->flip = flip;
|
vis->flip = flip;
|
||||||
vis->mobj = (mobj_t *)thing;
|
vis->mobj = (mobj_t *)thing;
|
||||||
vis->color = SKINCOLOR_NONE;
|
|
||||||
|
|
||||||
vis->colormap = NULL;
|
vis->colormap = NULL;
|
||||||
|
|
||||||
|
|
|
@ -173,6 +173,7 @@ enum actionnum
|
||||||
A_CHANGECOLORRELATIVE,
|
A_CHANGECOLORRELATIVE,
|
||||||
A_CHANGECOLORABSOLUTE,
|
A_CHANGECOLORABSOLUTE,
|
||||||
A_DYE,
|
A_DYE,
|
||||||
|
A_SETTRANSLATION,
|
||||||
A_MOVERELATIVE,
|
A_MOVERELATIVE,
|
||||||
A_MOVEABSOLUTE,
|
A_MOVEABSOLUTE,
|
||||||
A_THRUST,
|
A_THRUST,
|
||||||
|
@ -445,6 +446,7 @@ void A_SetRandomTics();
|
||||||
void A_ChangeColorRelative();
|
void A_ChangeColorRelative();
|
||||||
void A_ChangeColorAbsolute();
|
void A_ChangeColorAbsolute();
|
||||||
void A_Dye();
|
void A_Dye();
|
||||||
|
void A_SetTranslation();
|
||||||
void A_MoveRelative();
|
void A_MoveRelative();
|
||||||
void A_MoveAbsolute();
|
void A_MoveAbsolute();
|
||||||
void A_Thrust();
|
void A_Thrust();
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "r_patch.h"
|
#include "r_patch.h"
|
||||||
#include "r_picformats.h"
|
#include "r_picformats.h"
|
||||||
#include "r_things.h"
|
#include "r_things.h"
|
||||||
|
#include "r_translation.h"
|
||||||
#include "r_draw.h" // R_GetColorByName
|
#include "r_draw.h" // R_GetColorByName
|
||||||
#include "doomstat.h" // luabanks[]
|
#include "doomstat.h" // luabanks[]
|
||||||
|
|
||||||
|
@ -1899,6 +1900,32 @@ static int colorramp_len(lua_State *L)
|
||||||
return 1;
|
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!
|
// 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, "spr2defaults", lib_getSpr2default, lib_setSpr2default, lib_spr2namelen);
|
||||||
LUA_RegisterGlobalUserdata(L, "states", lib_getState, lib_setState, lib_statelen);
|
LUA_RegisterGlobalUserdata(L, "states", lib_getState, lib_setState, lib_statelen);
|
||||||
LUA_RegisterGlobalUserdata(L, "mobjinfo", lib_getMobjInfo, lib_setMobjInfo, lib_mobjinfolen);
|
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, "skincolors", lib_getSkinColor, lib_setSkinColor, lib_skincolorslen);
|
||||||
LUA_RegisterGlobalUserdata(L, "spriteinfo", lib_getSpriteInfo, lib_setSpriteInfo, lib_spriteinfolen);
|
LUA_RegisterGlobalUserdata(L, "spriteinfo", lib_getSpriteInfo, lib_setSpriteInfo, lib_spriteinfolen);
|
||||||
LUA_RegisterGlobalUserdata(L, "sfxinfo", lib_getSfxInfo, lib_setSfxInfo, lib_sfxlen);
|
LUA_RegisterGlobalUserdata(L, "sfxinfo", lib_getSfxInfo, lib_setSfxInfo, lib_sfxlen);
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "fastcmp.h"
|
#include "fastcmp.h"
|
||||||
#include "r_data.h"
|
#include "r_data.h"
|
||||||
#include "r_skins.h"
|
#include "r_skins.h"
|
||||||
|
#include "r_translation.h"
|
||||||
#include "p_local.h"
|
#include "p_local.h"
|
||||||
#include "g_game.h"
|
#include "g_game.h"
|
||||||
#include "p_setup.h"
|
#include "p_setup.h"
|
||||||
|
@ -66,6 +67,7 @@ enum mobj_e {
|
||||||
mobj_renderflags,
|
mobj_renderflags,
|
||||||
mobj_skin,
|
mobj_skin,
|
||||||
mobj_color,
|
mobj_color,
|
||||||
|
mobj_translation,
|
||||||
mobj_blendmode,
|
mobj_blendmode,
|
||||||
mobj_bnext,
|
mobj_bnext,
|
||||||
mobj_bprev,
|
mobj_bprev,
|
||||||
|
@ -146,6 +148,7 @@ static const char *const mobj_opt[] = {
|
||||||
"renderflags",
|
"renderflags",
|
||||||
"skin",
|
"skin",
|
||||||
"color",
|
"color",
|
||||||
|
"translation",
|
||||||
"blendmode",
|
"blendmode",
|
||||||
"bnext",
|
"bnext",
|
||||||
"bprev",
|
"bprev",
|
||||||
|
@ -338,6 +341,16 @@ static int mobj_get(lua_State *L)
|
||||||
case mobj_color:
|
case mobj_color:
|
||||||
lua_pushinteger(L, mo->color);
|
lua_pushinteger(L, mo->color);
|
||||||
break;
|
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:
|
case mobj_blendmode:
|
||||||
lua_pushinteger(L, mo->blendmode);
|
lua_pushinteger(L, mo->blendmode);
|
||||||
break;
|
break;
|
||||||
|
@ -689,12 +702,26 @@ static int mobj_set(lua_State *L)
|
||||||
}
|
}
|
||||||
case mobj_color:
|
case mobj_color:
|
||||||
{
|
{
|
||||||
UINT16 newcolor = (UINT16)luaL_checkinteger(L,3);
|
UINT16 newcolor = (UINT16)luaL_checkinteger(L, 3);
|
||||||
if (newcolor >= numskincolors)
|
if (newcolor >= numskincolors)
|
||||||
return luaL_error(L, "mobj.color %d out of range (0 - %d).", newcolor, numskincolors-1);
|
return luaL_error(L, "mobj.color %d out of range (0 - %d).", newcolor, numskincolors-1);
|
||||||
mo->color = newcolor;
|
mo->color = newcolor;
|
||||||
break;
|
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:
|
case mobj_blendmode:
|
||||||
{
|
{
|
||||||
INT32 blendmode = (INT32)luaL_checkinteger(L, 3);
|
INT32 blendmode = (INT32)luaL_checkinteger(L, 3);
|
||||||
|
|
154
src/m_misc.c
154
src/m_misc.c
|
@ -31,6 +31,7 @@
|
||||||
#include "doomdef.h"
|
#include "doomdef.h"
|
||||||
#include "g_game.h"
|
#include "g_game.h"
|
||||||
#include "m_misc.h"
|
#include "m_misc.h"
|
||||||
|
#include "m_tokenizer.h"
|
||||||
#include "hu_stuff.h"
|
#include "hu_stuff.h"
|
||||||
#include "st_stuff.h"
|
#include "st_stuff.h"
|
||||||
#include "v_video.h"
|
#include "v_video.h"
|
||||||
|
@ -1975,168 +1976,39 @@ void M_UnGetToken(void)
|
||||||
endPos = oldendPos;
|
endPos = oldendPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define NUMTOKENS 2
|
static tokenizer_t *globalTokenizer = NULL;
|
||||||
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 */
|
|
||||||
|
|
||||||
void M_TokenizerOpen(const char *inputString)
|
void M_TokenizerOpen(const char *inputString)
|
||||||
{
|
{
|
||||||
size_t i;
|
globalTokenizer = Tokenizer_Open(inputString, 2);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void M_TokenizerClose(void)
|
void M_TokenizerClose(void)
|
||||||
{
|
{
|
||||||
size_t i;
|
Tokenizer_Close(globalTokenizer);
|
||||||
|
globalTokenizer = NULL;
|
||||||
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';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *M_TokenizerRead(UINT32 i)
|
const char *M_TokenizerRead(UINT32 i)
|
||||||
{
|
{
|
||||||
if (!tokenizerInput)
|
if (!globalTokenizer)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
tokenizerStartPos = tokenizerEndPos;
|
return Tokenizer_SRB2Read(globalTokenizer, i);
|
||||||
|
|
||||||
// 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;
|
|
||||||
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];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UINT32 M_TokenizerGetEndPos(void)
|
UINT32 M_TokenizerGetEndPos(void)
|
||||||
{
|
{
|
||||||
return tokenizerEndPos;
|
if (!globalTokenizer)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return Tokenizer_GetEndPos(globalTokenizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void M_TokenizerSetEndPos(UINT32 newPos)
|
void M_TokenizerSetEndPos(UINT32 newPos)
|
||||||
{
|
{
|
||||||
tokenizerEndPos = newPos;
|
if (globalTokenizer)
|
||||||
|
Tokenizer_SetEndPos(globalTokenizer, newPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Count bits in a number.
|
/** Count bits in a number.
|
||||||
|
|
278
src/m_tokenizer.c
Normal file
278
src/m_tokenizer.c
Normal file
|
@ -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;
|
||||||
|
}
|
38
src/m_tokenizer.h
Normal file
38
src/m_tokenizer.h
Normal file
|
@ -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
|
|
@ -23,6 +23,7 @@
|
||||||
#include "m_random.h"
|
#include "m_random.h"
|
||||||
#include "m_misc.h"
|
#include "m_misc.h"
|
||||||
#include "r_skins.h"
|
#include "r_skins.h"
|
||||||
|
#include "r_translation.h"
|
||||||
#include "i_video.h"
|
#include "i_video.h"
|
||||||
#include "z_zone.h"
|
#include "z_zone.h"
|
||||||
#include "lua_hook.h"
|
#include "lua_hook.h"
|
||||||
|
@ -196,6 +197,7 @@ void A_SetRandomTics(mobj_t *actor);
|
||||||
void A_ChangeColorRelative(mobj_t *actor);
|
void A_ChangeColorRelative(mobj_t *actor);
|
||||||
void A_ChangeColorAbsolute(mobj_t *actor);
|
void A_ChangeColorAbsolute(mobj_t *actor);
|
||||||
void A_Dye(mobj_t *actor);
|
void A_Dye(mobj_t *actor);
|
||||||
|
void A_SetTranslation(mobj_t *actor);
|
||||||
void A_MoveRelative(mobj_t *actor);
|
void A_MoveRelative(mobj_t *actor);
|
||||||
void A_MoveAbsolute(mobj_t *actor);
|
void A_MoveAbsolute(mobj_t *actor);
|
||||||
void A_Thrust(mobj_t *actor);
|
void A_Thrust(mobj_t *actor);
|
||||||
|
@ -9215,6 +9217,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
|
// Function: A_MoveRelative
|
||||||
//
|
//
|
||||||
// Description: Moves an object (wrapper for P_Thrust)
|
// Description: Moves an object (wrapper for P_Thrust)
|
||||||
|
|
|
@ -331,9 +331,14 @@ typedef struct mobj_s
|
||||||
UINT16 eflags; // extra flags
|
UINT16 eflags; // extra flags
|
||||||
|
|
||||||
void *skin; // overrides 'sprite' when non-NULL (for player bodies to 'remember' the skin)
|
void *skin; // overrides 'sprite' when non-NULL (for player bodies to 'remember' the skin)
|
||||||
|
|
||||||
// Player and mobj sprites in multiplayer modes are modified
|
// Player and mobj sprites in multiplayer modes are modified
|
||||||
// using an internal color lookup table for re-indexing.
|
// 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 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)
|
struct mobj_s *dontdrawforviewmobj; // If set, hides the mobj if dontdrawforviewmobj is the current camera (first-person player or awayviewmobj)
|
||||||
|
|
||||||
|
|
|
@ -1741,7 +1741,8 @@ typedef enum
|
||||||
MD2_FLOORSPRITESLOPE = 1<<22,
|
MD2_FLOORSPRITESLOPE = 1<<22,
|
||||||
MD2_DISPOFFSET = 1<<23,
|
MD2_DISPOFFSET = 1<<23,
|
||||||
MD2_DRAWONLYFORPLAYER = 1<<24,
|
MD2_DRAWONLYFORPLAYER = 1<<24,
|
||||||
MD2_DONTDRAWFORVIEWMOBJ = 1<<25
|
MD2_DONTDRAWFORVIEWMOBJ = 1<<25,
|
||||||
|
MD2_TRANSLATION = 1<<26
|
||||||
} mobj_diff2_t;
|
} mobj_diff2_t;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
|
@ -1930,6 +1931,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
|
||||||
diff2 |= MD2_CVMEM;
|
diff2 |= MD2_CVMEM;
|
||||||
if (mobj->color)
|
if (mobj->color)
|
||||||
diff2 |= MD2_COLOR;
|
diff2 |= MD2_COLOR;
|
||||||
|
if (mobj->translation)
|
||||||
|
diff2 |= MD2_TRANSLATION;
|
||||||
if (mobj->skin)
|
if (mobj->skin)
|
||||||
diff2 |= MD2_SKIN;
|
diff2 |= MD2_SKIN;
|
||||||
if (mobj->extravalue1)
|
if (mobj->extravalue1)
|
||||||
|
@ -2163,6 +2166,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
|
||||||
WRITEUINT32(save_p, mobj->dontdrawforviewmobj->mobjnum);
|
WRITEUINT32(save_p, mobj->dontdrawforviewmobj->mobjnum);
|
||||||
if (diff2 & MD2_DISPOFFSET)
|
if (diff2 & MD2_DISPOFFSET)
|
||||||
WRITEINT32(save_p, mobj->dispoffset);
|
WRITEINT32(save_p, mobj->dispoffset);
|
||||||
|
if (diff2 & MD2_TRANSLATION)
|
||||||
|
WRITEUINT16(save_p, mobj->translation);
|
||||||
|
|
||||||
WRITEUINT32(save_p, mobj->mobjnum);
|
WRITEUINT32(save_p, mobj->mobjnum);
|
||||||
}
|
}
|
||||||
|
@ -3225,6 +3230,8 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
|
||||||
mobj->dispoffset = READINT32(save_p);
|
mobj->dispoffset = READINT32(save_p);
|
||||||
else
|
else
|
||||||
mobj->dispoffset = mobj->info->dispoffset;
|
mobj->dispoffset = mobj->info->dispoffset;
|
||||||
|
if (diff2 & MD2_TRANSLATION)
|
||||||
|
mobj->translation = READUINT16(save_p);
|
||||||
|
|
||||||
if (diff & MD_REDFLAG)
|
if (diff & MD_REDFLAG)
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "r_data.h"
|
#include "r_data.h"
|
||||||
#include "r_things.h" // for R_AddSpriteDefs
|
#include "r_things.h" // for R_AddSpriteDefs
|
||||||
#include "r_textures.h"
|
#include "r_textures.h"
|
||||||
|
#include "r_translation.h"
|
||||||
#include "r_patch.h"
|
#include "r_patch.h"
|
||||||
#include "r_picformats.h"
|
#include "r_picformats.h"
|
||||||
#include "r_sky.h"
|
#include "r_sky.h"
|
||||||
|
@ -8245,6 +8246,8 @@ static boolean P_LoadAddon(UINT16 numlumps)
|
||||||
HWR_ClearAllTextures();
|
HWR_ClearAllTextures();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
R_LoadParsedTranslations();
|
||||||
|
|
||||||
//
|
//
|
||||||
// search for sprite replacements
|
// search for sprite replacements
|
||||||
//
|
//
|
||||||
|
|
|
@ -2047,6 +2047,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
|
||||||
}
|
}
|
||||||
|
|
||||||
ghost->color = mobj->color;
|
ghost->color = mobj->color;
|
||||||
|
ghost->translation = mobj->translation;
|
||||||
ghost->colorized = mobj->colorized; // alternatively, "true" for sonic advance style colourisation
|
ghost->colorized = mobj->colorized; // alternatively, "true" for sonic advance style colourisation
|
||||||
|
|
||||||
ghost->angle = (mobj->player ? mobj->player->drawangle : mobj->angle);
|
ghost->angle = (mobj->player ? mobj->player->drawangle : mobj->angle);
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "m_misc.h"
|
#include "m_misc.h"
|
||||||
#include "r_data.h"
|
#include "r_data.h"
|
||||||
#include "r_textures.h"
|
#include "r_textures.h"
|
||||||
|
#include "r_translation.h"
|
||||||
#include "r_patch.h"
|
#include "r_patch.h"
|
||||||
#include "r_picformats.h"
|
#include "r_picformats.h"
|
||||||
#include "w_wad.h"
|
#include "w_wad.h"
|
||||||
|
@ -1217,6 +1218,9 @@ void R_InitData(void)
|
||||||
R_Init8to16();
|
R_Init8to16();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CONS_Printf("R_LoadParsedTranslations()...\n");
|
||||||
|
R_LoadParsedTranslations();
|
||||||
|
|
||||||
CONS_Printf("R_LoadTextures()...\n");
|
CONS_Printf("R_LoadTextures()...\n");
|
||||||
R_LoadTextures();
|
R_LoadTextures();
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,11 @@
|
||||||
|
|
||||||
#include "screen.h" // MAXVIDWIDTH, MAXVIDHEIGHT
|
#include "screen.h" // MAXVIDWIDTH, MAXVIDHEIGHT
|
||||||
|
|
||||||
#ifdef HWRENDER
|
|
||||||
#include "m_aatree.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "taglist.h"
|
#include "taglist.h"
|
||||||
|
|
||||||
|
// Amount of colors in the palette
|
||||||
|
#define NUM_PALETTE_ENTRIES 256
|
||||||
|
|
||||||
//
|
//
|
||||||
// ClipWallSegment
|
// ClipWallSegment
|
||||||
// Clips the given range of columns
|
// Clips the given range of columns
|
||||||
|
|
43
src/r_draw.c
43
src/r_draw.c
|
@ -18,6 +18,7 @@
|
||||||
#include "doomdef.h"
|
#include "doomdef.h"
|
||||||
#include "doomstat.h"
|
#include "doomstat.h"
|
||||||
#include "r_local.h"
|
#include "r_local.h"
|
||||||
|
#include "r_translation.h"
|
||||||
#include "st_stuff.h" // need ST_HEIGHT
|
#include "st_stuff.h" // need ST_HEIGHT
|
||||||
#include "i_video.h"
|
#include "i_video.h"
|
||||||
#include "v_video.h"
|
#include "v_video.h"
|
||||||
|
@ -452,8 +453,14 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 translatio
|
||||||
switch (translation)
|
switch (translation)
|
||||||
{
|
{
|
||||||
case TC_ALLWHITE:
|
case TC_ALLWHITE:
|
||||||
memset(dest_colormap, 0, NUM_PALETTE_ENTRIES * sizeof(UINT8));
|
case TC_DASHMODE:
|
||||||
|
remaptable_t *tr = R_GetBuiltInTranslation((SINT8)translation);
|
||||||
|
if (tr)
|
||||||
|
{
|
||||||
|
memcpy(dest_colormap, tr->remap, NUM_PALETTE_ENTRIES);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case TC_RAINBOW:
|
case TC_RAINBOW:
|
||||||
if (color >= numskincolors)
|
if (color >= numskincolors)
|
||||||
I_Error("Invalid skin color #%hu", (UINT16)color);
|
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++)
|
for (i = 0; i < COLORRAMPSIZE; i++)
|
||||||
dest_colormap[96+i] = dest_colormap[skincolors[SKINCOLOR_COBALT].ramp[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;
|
return;
|
||||||
}
|
}
|
||||||
else if (color == SKINCOLOR_NONE)
|
else if (color == SKINCOLOR_NONE)
|
||||||
|
|
|
@ -404,7 +404,7 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr
|
||||||
}
|
}
|
||||||
|
|
||||||
ds_colormap = vis->colormap;
|
ds_colormap = vis->colormap;
|
||||||
ds_translation = R_GetSpriteTranslation(vis);
|
ds_translation = R_GetTranslationForThing(vis->mobj, vis->color, vis->translation);
|
||||||
if (ds_translation == NULL)
|
if (ds_translation == NULL)
|
||||||
ds_translation = colormaps;
|
ds_translation = colormaps;
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "i_system.h"
|
#include "i_system.h"
|
||||||
#include "r_fps.h"
|
#include "r_fps.h"
|
||||||
#include "r_things.h"
|
#include "r_things.h"
|
||||||
|
#include "r_translation.h"
|
||||||
#include "r_patch.h"
|
#include "r_patch.h"
|
||||||
#include "r_patchrotation.h"
|
#include "r_patchrotation.h"
|
||||||
#include "r_picformats.h"
|
#include "r_picformats.h"
|
||||||
|
@ -761,50 +762,46 @@ void R_DrawFlippedMaskedColumn(column_t *column)
|
||||||
dc_texturemid = basetexturemid;
|
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)
|
if (R_ThingIsFlashing(mobj)) // Bosses "flash"
|
||||||
&& (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 (vis->mobj->type == MT_CYBRAKDEMON || vis->mobj->colorized)
|
if (mobj->type == MT_CYBRAKDEMON || mobj->colorized)
|
||||||
return R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE);
|
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);
|
return R_GetTranslationColormap(TC_METALSONIC, 0, GTC_CACHE);
|
||||||
else
|
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
|
// New colormap stuff for skins Tails 06-07-2002
|
||||||
if (!(vis->cut & SC_PRECIP) && vis->mobj->colorized)
|
if (mobj->colorized)
|
||||||
return R_GetTranslationColormap(TC_RAINBOW, vis->color, GTC_CACHE);
|
return R_GetTranslationColormap(TC_RAINBOW, color, GTC_CACHE);
|
||||||
else if (!(vis->cut & SC_PRECIP)
|
else if (mobj->player && mobj->player->dashmode >= DASHMODE_THRESHOLD
|
||||||
&& vis->mobj->player && vis->mobj->player->dashmode >= DASHMODE_THRESHOLD
|
&& (mobj->player->charflags & SF_DASHMODE)
|
||||||
&& (vis->mobj->player->charflags & SF_DASHMODE)
|
|
||||||
&& ((leveltime/2) & 1))
|
&& ((leveltime/2) & 1))
|
||||||
{
|
{
|
||||||
if (vis->mobj->player->charflags & SF_MACHINE)
|
if (mobj->player->charflags & SF_MACHINE)
|
||||||
return R_GetTranslationColormap(TC_DASHMODE, 0, GTC_CACHE);
|
return R_GetTranslationColormap(TC_DASHMODE, 0, GTC_CACHE);
|
||||||
else
|
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;
|
UINT8 skinnum = ((skin_t*)mobj->skin)->skinnum;
|
||||||
return R_GetTranslationColormap(skinnum, vis->color, GTC_CACHE);
|
return R_GetTranslationColormap(skinnum, color, GTC_CACHE);
|
||||||
}
|
}
|
||||||
else // Use the defaults
|
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 R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_BLUE, GTC_CACHE);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -852,11 +849,11 @@ static void R_DrawVisSprite(vissprite_t *vis)
|
||||||
|
|
||||||
colfunc = colfuncs[BASEDRAWFUNC]; // hack: this isn't resetting properly somewhere.
|
colfunc = colfuncs[BASEDRAWFUNC]; // hack: this isn't resetting properly somewhere.
|
||||||
dc_colormap = vis->colormap;
|
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
|
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];
|
colfunc = colfuncs[COLDRAWFUNC_TRANSTRANS];
|
||||||
dc_transmap = vis->transmap;
|
dc_transmap = vis->transmap;
|
||||||
|
@ -866,9 +863,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
|
||||||
colfunc = colfuncs[COLDRAWFUNC_FUZZY];
|
colfunc = colfuncs[COLDRAWFUNC_FUZZY];
|
||||||
dc_transmap = vis->transmap; //Fab : 29-04-98: translucency table
|
dc_transmap = vis->transmap; //Fab : 29-04-98: translucency table
|
||||||
}
|
}
|
||||||
else if (vis->color) // translate green skin to another color
|
else if (dc_translation) // 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.
|
|
||||||
colfunc = colfuncs[COLDRAWFUNC_TRANS];
|
colfunc = colfuncs[COLDRAWFUNC_TRANS];
|
||||||
|
|
||||||
// Hack: Use a special column function for drop shadows that bypasses
|
// 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->mobj = thing; // Easy access! Tails 06-07-2002
|
||||||
shadow->color = thing->color;
|
shadow->color = thing->color;
|
||||||
|
shadow->translation = 0;
|
||||||
|
|
||||||
shadow->x1 = x1 < portalclipstart ? portalclipstart : x1;
|
shadow->x1 = x1 < portalclipstart ? portalclipstart : x1;
|
||||||
shadow->x2 = x2 >= portalclipend ? portalclipend-1 : x2;
|
shadow->x2 = x2 >= portalclipend ? portalclipend-1 : x2;
|
||||||
|
@ -2200,6 +2196,11 @@ static void R_ProjectSprite(mobj_t *thing)
|
||||||
else
|
else
|
||||||
vis->color = oldthing->color;
|
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->x1 = x1 < portalclipstart ? portalclipstart : x1;
|
||||||
vis->x2 = x2 >= portalclipend ? portalclipend-1 : x2;
|
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->extra_colormap = thing->subsector->sector->extra_colormap;
|
||||||
vis->heightsec = thing->subsector->sector->heightsec;
|
vis->heightsec = thing->subsector->sector->heightsec;
|
||||||
vis->color = SKINCOLOR_NONE;
|
vis->color = SKINCOLOR_NONE;
|
||||||
|
vis->translation = 0;
|
||||||
|
|
||||||
// Fullbright
|
// Fullbright
|
||||||
vis->colormap = colormaps;
|
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);
|
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!
|
// 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
|
// 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)
|
void R_ThingOffsetOverlay(mobj_t *thing, fixed_t *x, fixed_t *y)
|
||||||
|
|
|
@ -88,6 +88,10 @@ boolean R_ThingIsFullBright (mobj_t *thing);
|
||||||
boolean R_ThingIsSemiBright (mobj_t *thing);
|
boolean R_ThingIsSemiBright (mobj_t *thing);
|
||||||
boolean R_ThingIsFullDark (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);
|
void R_ThingOffsetOverlay (mobj_t *thing, fixed_t *outx, fixed_t *outy);
|
||||||
|
|
||||||
// --------------
|
// --------------
|
||||||
|
@ -216,6 +220,7 @@ typedef struct vissprite_s
|
||||||
fixed_t shadowscale;
|
fixed_t shadowscale;
|
||||||
|
|
||||||
skincolornum_t color;
|
skincolornum_t color;
|
||||||
|
UINT16 translation;
|
||||||
|
|
||||||
INT16 clipbot[MAXVIDWIDTH], cliptop[MAXVIDWIDTH];
|
INT16 clipbot[MAXVIDWIDTH], cliptop[MAXVIDWIDTH];
|
||||||
|
|
||||||
|
@ -226,12 +231,8 @@ extern UINT32 visspritecount, numvisiblesprites;
|
||||||
|
|
||||||
void R_ClipSprites(drawseg_t* dsstart, portal_t* portal);
|
void R_ClipSprites(drawseg_t* dsstart, portal_t* portal);
|
||||||
|
|
||||||
boolean R_SpriteIsFlashing(vissprite_t *vis);
|
|
||||||
|
|
||||||
void R_DrawThingBoundingBox(vissprite_t *spr);
|
void R_DrawThingBoundingBox(vissprite_t *spr);
|
||||||
|
|
||||||
UINT8 *R_GetSpriteTranslation(vissprite_t *vis);
|
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
// DRAW NODES
|
// DRAW NODES
|
||||||
// ----------
|
// ----------
|
||||||
|
|
993
src/r_translation.c
Normal file
993
src/r_translation.c
Normal file
|
@ -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;
|
||||||
|
}
|
45
src/r_translation.h
Normal file
45
src/r_translation.h
Normal file
|
@ -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
|
|
@ -292,6 +292,7 @@
|
||||||
<ClInclude Include="..\m_fixed.h" />
|
<ClInclude Include="..\m_fixed.h" />
|
||||||
<ClInclude Include="..\m_menu.h" />
|
<ClInclude Include="..\m_menu.h" />
|
||||||
<ClInclude Include="..\m_misc.h" />
|
<ClInclude Include="..\m_misc.h" />
|
||||||
|
<ClInclude Include="..\m_tokenizer.h" />
|
||||||
<ClInclude Include="..\m_perfstats.h" />
|
<ClInclude Include="..\m_perfstats.h" />
|
||||||
<ClInclude Include="..\m_queue.h" />
|
<ClInclude Include="..\m_queue.h" />
|
||||||
<ClInclude Include="..\m_random.h" />
|
<ClInclude Include="..\m_random.h" />
|
||||||
|
@ -342,6 +343,7 @@
|
||||||
<ClInclude Include="..\r_state.h" />
|
<ClInclude Include="..\r_state.h" />
|
||||||
<ClInclude Include="..\r_textures.h" />
|
<ClInclude Include="..\r_textures.h" />
|
||||||
<ClInclude Include="..\r_things.h" />
|
<ClInclude Include="..\r_things.h" />
|
||||||
|
<ClInclude Include="..\r_translation.h" />
|
||||||
<ClInclude Include="..\screen.h" />
|
<ClInclude Include="..\screen.h" />
|
||||||
<ClInclude Include="..\snake.h" />
|
<ClInclude Include="..\snake.h" />
|
||||||
<ClInclude Include="..\sounds.h" />
|
<ClInclude Include="..\sounds.h" />
|
||||||
|
@ -468,6 +470,7 @@
|
||||||
<ClCompile Include="..\m_fixed.c" />
|
<ClCompile Include="..\m_fixed.c" />
|
||||||
<ClCompile Include="..\m_menu.c" />
|
<ClCompile Include="..\m_menu.c" />
|
||||||
<ClCompile Include="..\m_misc.c" />
|
<ClCompile Include="..\m_misc.c" />
|
||||||
|
<ClCompile Include="..\m_tokenizer.c" />
|
||||||
<ClCompile Include="..\m_perfstats.c" />
|
<ClCompile Include="..\m_perfstats.c" />
|
||||||
<ClCompile Include="..\m_queue.c" />
|
<ClCompile Include="..\m_queue.c" />
|
||||||
<ClCompile Include="..\m_random.c" />
|
<ClCompile Include="..\m_random.c" />
|
||||||
|
@ -530,6 +533,7 @@
|
||||||
<ClCompile Include="..\r_splats.c" />
|
<ClCompile Include="..\r_splats.c" />
|
||||||
<ClCompile Include="..\r_textures.c" />
|
<ClCompile Include="..\r_textures.c" />
|
||||||
<ClCompile Include="..\r_things.c" />
|
<ClCompile Include="..\r_things.c" />
|
||||||
|
<ClCompile Include="..\r_translation.c" />
|
||||||
<ClCompile Include="..\screen.c" />
|
<ClCompile Include="..\screen.c" />
|
||||||
<ClCompile Include="..\snake.c" />
|
<ClCompile Include="..\snake.c" />
|
||||||
<ClCompile Include="..\sounds.c" />
|
<ClCompile Include="..\sounds.c" />
|
||||||
|
|
|
@ -339,6 +339,9 @@
|
||||||
<ClInclude Include="..\m_misc.h">
|
<ClInclude Include="..\m_misc.h">
|
||||||
<Filter>M_Misc</Filter>
|
<Filter>M_Misc</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\m_tokenizer.h">
|
||||||
|
<Filter>M_Misc</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\m_perfstats.h">
|
<ClInclude Include="..\m_perfstats.h">
|
||||||
<Filter>M_Misc</Filter>
|
<Filter>M_Misc</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -537,6 +540,9 @@
|
||||||
<ClInclude Include="..\r_textures.h">
|
<ClInclude Include="..\r_textures.h">
|
||||||
<Filter>R_Rend</Filter>
|
<Filter>R_Rend</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\r_translation.h">
|
||||||
|
<Filter>R_Rend</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\r_portal.h">
|
<ClInclude Include="..\r_portal.h">
|
||||||
<Filter>R_Rend</Filter>
|
<Filter>R_Rend</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -831,6 +837,9 @@
|
||||||
<ClCompile Include="..\m_misc.c">
|
<ClCompile Include="..\m_misc.c">
|
||||||
<Filter>M_Misc</Filter>
|
<Filter>M_Misc</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\m_tokenizer.c">
|
||||||
|
<Filter>M_Misc</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\m_perfstats.c">
|
<ClCompile Include="..\m_perfstats.c">
|
||||||
<Filter>M_Misc</Filter>
|
<Filter>M_Misc</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -1081,6 +1090,9 @@
|
||||||
<ClCompile Include="..\r_textures.c">
|
<ClCompile Include="..\r_textures.c">
|
||||||
<Filter>R_Rend</Filter>
|
<Filter>R_Rend</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\r_translation.c">
|
||||||
|
<Filter>R_Rend</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\r_portal.c">
|
<ClCompile Include="..\r_portal.c">
|
||||||
<Filter>R_Rend</Filter>
|
<Filter>R_Rend</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|
14
src/w_wad.c
14
src/w_wad.c
|
@ -59,6 +59,7 @@
|
||||||
#include "r_textures.h"
|
#include "r_textures.h"
|
||||||
#include "r_patch.h"
|
#include "r_patch.h"
|
||||||
#include "r_picformats.h"
|
#include "r_picformats.h"
|
||||||
|
#include "r_translation.h"
|
||||||
#include "i_time.h"
|
#include "i_time.h"
|
||||||
#include "i_system.h"
|
#include "i_system.h"
|
||||||
#include "i_video.h" // rendermode
|
#include "i_video.h" // rendermode
|
||||||
|
@ -829,6 +830,16 @@ static void W_ReadFileShaders(wadfile_t *wadfile)
|
||||||
#endif
|
#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
|
// Allocate a wadfile, setup the lumpinfo (directory) and
|
||||||
// lumpcache, add the wadfile to the current active wadfiles
|
// lumpcache, add the wadfile to the current active wadfiles
|
||||||
//
|
//
|
||||||
|
@ -979,6 +990,9 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup)
|
||||||
// Read shaders from file
|
// Read shaders from file
|
||||||
W_ReadFileShaders(wadfile);
|
W_ReadFileShaders(wadfile);
|
||||||
|
|
||||||
|
// 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.
|
// 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)
|
switch (wadfile->type)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue