Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • STJr/SRB2
  • Sryder/SRB2
  • wolfy852/SRB2
  • Alpha2244/SRB2
  • Inuyasha/SRB2
  • yoshibot/SRB2
  • TehRealSalt/SRB2
  • PrisimaTF/SRB2
  • Hatninja/SRB2
  • SteelT/SRB2
  • james/SRB2
  • ShaderWraith/SRB2
  • SinnamonLat/SRB2
  • mazmazz_/SRB2
  • filpAM/SRB2
  • chaoloveicemdboy/SRB2
  • Whooa21/SRB2
  • Machturne/SRB2
  • Golden/SRB2
  • Tatsuru/SRB2
  • Snu/SRB2
  • Zwip-Zwap_Zapony/SRB2
  • fickleheart/SRB2
  • alphaRexJames/SRB2
  • JJK/SRB2
  • diskpoppy/SRB2
  • Hannu_Hanhi/SRB2
  • ZipperQR/SRB2
  • kays/SRB2
  • spherallic/SRB2
  • Zippy_Zolton/SRB2
  • namiishere/SRB2
  • Ors/SRB2
  • SMS_Alfredo/SRB2
  • sonic_edge/SRB2
  • lavla/SRB2
  • ashi/SRB2
  • X.organic/SRB2
  • Fafabis/SRB2
  • Meziu/SRB2
  • v-rob/SRB2
  • tertu/SRB2
  • bitten2up/SRB2
  • flarn2006/SRB2
  • Krabs/SRB2
  • clairebun/SRB2
  • Lactozilla/SRB2
  • thehackstack/SRB2
  • Spice/SRB2
  • win8linux/SRB2
  • JohnFrostFox/SRB2
  • talktoneon726/SRB2
  • Wane/SRB2
  • Lamibe/SRB2
  • spectrumuk2/srb-2
  • nerdyminer18/srb-2
  • 256nil/SRB2
  • ARJr/SRB2
  • Alam/SRB2
  • Zenya/srb-2-marathon-demos
  • Acelite/srb-2-archivedmodifications
  • MIDIMan/SRB2
  • Lach/SRB2
  • Frostiikin/bounce-tweaks
  • Jaden/SRB2
  • Tyron/SRB2
  • Astronight/SRB2
  • Mari0shi06/SRB2
  • aiire/SRB2
  • Galactice/SRB2
  • srb2-ports/srb2-dreamcast
  • sdasdas/SRB2
  • chreas/srb-2-vr
  • StarManiaKG/the-story-of-sinically-rocketing-and-botching-the-2nd
  • LoganAir/SRB2
  • NepDisk/srb-2
  • alufolie91/SRB2
  • Felicia.iso/SRB2
  • twi/SRB2
  • BarrelsOFun/SRB2
  • Speed2411/SRB2
  • Leather_Realms/SRB2
  • Ayemar/SRB2
  • Acelite/SRB2
  • VladDoc/SRB2
  • kaldrum/model-features
  • strawberryfox417/SRB2
  • Lugent/SRB2
  • Rem/SRB2
  • Refrag/SRB2
  • Henry_3230/srb-3230
  • TehPuertoRicanSpartan2/tprs-srb2
  • Leminn/srb-2-marathon-stuff
  • chromaticpipe2/SRB2
  • MiguelGustavo15/SRB2
  • Maru/srb-2-tests
  • SilicDev/SRB2
  • UnmatchedBracket/SRB2
  • HybridDog/SRB2
  • xordspar0/SRB2
  • jsjhbewfhh/SRB2
  • Fancy2209/SRB2
  • Lorsoen/SRB2
  • shindoukin/SRB2
  • GamerOfDays/SRB2
  • Craftyawesome/SRB2
  • tenshi-tensai-tennoji/SRB2
  • Scarfdudebalder/SRB2
  • luigi-budd/srb-2-fix-interplag-lockon
  • mskluesner/SRB2
  • johnpetersa19/SRB2
  • Pheazant/SRB2
  • chromaticpipe2/srb2classic
  • romoney5/SRB2
  • PAS/SRB2Classic
  • BlueStaggo/SRB2
  • Jisk/srb-2-beef-jerky
117 results
Select Git revision
Show changes
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -1042,6 +1042,9 @@ static void ST_drawInput(void) ...@@ -1042,6 +1042,9 @@ static void ST_drawInput(void)
INT32 x = hudinfo[HUD_INPUT].x, y = hudinfo[HUD_INPUT].y; INT32 x = hudinfo[HUD_INPUT].x, y = hudinfo[HUD_INPUT].y;
if (hu_showscores)
return;
if (stplyr->powers[pw_carry] == CR_NIGHTSMODE) if (stplyr->powers[pw_carry] == CR_NIGHTSMODE)
y += 8; y += 8;
else if (modeattacking || !LUA_HudEnabled(hud_lives)) else if (modeattacking || !LUA_HudEnabled(hud_lives))
...@@ -1173,10 +1176,8 @@ static void ST_drawInput(void) ...@@ -1173,10 +1176,8 @@ static void ST_drawInput(void)
V_DrawFill(x+16+(xoffs), y+(yoffs)-offs, 10, 10, col);\ V_DrawFill(x+16+(xoffs), y+(yoffs)-offs, 10, 10, col);\
V_DrawCharacter(x+16+1+(xoffs), y+1+(yoffs)-offs, hudinfo[HUD_INPUT].f|symb, false) V_DrawCharacter(x+16+1+(xoffs), y+1+(yoffs)-offs, hudinfo[HUD_INPUT].f|symb, false)
drawbutt( 4,-3, BT_JUMP, 'J' ); drawbutt( 4,-3, BT_JUMP, 'J');
drawbutt(15,-3, BT_SPIN, 'S' ); drawbutt(15,-3, BT_SPIN, 'S');
drawbutt(26,-3, BT_SHIELD, '\0'); // Instead of a wide 'J' or 'S', we'll draw a thin "SH" for Shield
V_DrawThinString(x+16+26, y+2+(-3)-offs, hudinfo[HUD_LIVES].f, "SH");
V_DrawFill(x+16+4, y+8, 21, 10, hudinfo[HUD_INPUT].f|20); // sundial backing V_DrawFill(x+16+4, y+8, 21, 10, hudinfo[HUD_INPUT].f|20); // sundial backing
if (stplyr->mo) if (stplyr->mo)
...@@ -1255,6 +1256,8 @@ static void ST_drawInput(void) ...@@ -1255,6 +1256,8 @@ static void ST_drawInput(void)
V_DrawThinString(x, y, hudinfo[HUD_INPUT].f|((leveltime & 4) ? V_YELLOWMAP : V_REDMAP), "BAD DEMO!!"); V_DrawThinString(x, y, hudinfo[HUD_INPUT].f|((leveltime & 4) ? V_YELLOWMAP : V_REDMAP), "BAD DEMO!!");
} }
static boolean lt_active = false;
static patch_t *lt_patches[3]; static patch_t *lt_patches[3];
static INT32 lt_scroll = 0; static INT32 lt_scroll = 0;
static INT32 lt_mom = 0; static INT32 lt_mom = 0;
...@@ -1269,19 +1272,19 @@ tic_t lt_exitticker = 0, lt_endtime = 0; ...@@ -1269,19 +1272,19 @@ tic_t lt_exitticker = 0, lt_endtime = 0;
// //
static void ST_cacheLevelTitle(void) static void ST_cacheLevelTitle(void)
{ {
#define SETPATCH(default, warning, custom, idx) \ #define SETPATCH(def, warning, custom, idx) \
{ \ { \
lumpnum_t patlumpnum = LUMPERROR; \ lumpnum_t patlumpnum = LUMPERROR; \
if (mapheaderinfo[gamemap-1]->custom[0] != '\0') \ if (mapheaderinfo[gamemap-1]->custom[0] != '\0') \
{ \ { \
patlumpnum = W_CheckNumForName(mapheaderinfo[gamemap-1]->custom); \ patlumpnum = W_CheckNumForPatchName(mapheaderinfo[gamemap-1]->custom); \
if (patlumpnum != LUMPERROR) \ if (patlumpnum != LUMPERROR) \
lt_patches[idx] = (patch_t *)W_CachePatchNum(patlumpnum, PU_HUDGFX); \ lt_patches[idx] = (patch_t *)W_CachePatchNum(patlumpnum, PU_HUDGFX); \
} \ } \
if (patlumpnum == LUMPERROR) \ if (patlumpnum == LUMPERROR) \
{ \ { \
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_WARNINGTITLE)) \ if (!(mapheaderinfo[gamemap-1]->levelflags & LF_WARNINGTITLE)) \
lt_patches[idx] = (patch_t *)W_CachePatchName(default, PU_HUDGFX); \ lt_patches[idx] = (patch_t *)W_CachePatchName(def, PU_HUDGFX); \
else \ else \
lt_patches[idx] = (patch_t *)W_CachePatchName(warning, PU_HUDGFX); \ lt_patches[idx] = (patch_t *)W_CachePatchName(warning, PU_HUDGFX); \
} \ } \
...@@ -1303,6 +1306,7 @@ void ST_startTitleCard(void) ...@@ -1303,6 +1306,7 @@ void ST_startTitleCard(void)
ST_cacheLevelTitle(); ST_cacheLevelTitle();
// initialize HUD variables // initialize HUD variables
lt_active = true;
lt_ticker = lt_exitticker = lt_lasttic = 0; lt_ticker = lt_exitticker = lt_lasttic = 0;
lt_endtime = 2*TICRATE + (10*NEWTICRATERATIO); lt_endtime = 2*TICRATE + (10*NEWTICRATERATIO);
lt_scroll = BASEVIDWIDTH * FRACUNIT; lt_scroll = BASEVIDWIDTH * FRACUNIT;
...@@ -1311,21 +1315,11 @@ void ST_startTitleCard(void) ...@@ -1311,21 +1315,11 @@ void ST_startTitleCard(void)
} }
// //
// What happens before drawing the title card. // Stops the title card.
// Which is just setting the HUD translucency.
// //
void ST_preDrawTitleCard(void) void ST_stopTitleCard(void)
{ {
if (!G_IsTitleCardAvailable()) lt_active = false;
return;
if (lt_ticker >= (lt_endtime + TICRATE))
return;
if (!lt_exitticker)
st_translucency = 0;
else
st_translucency = max(0, min((INT32)lt_exitticker-4, cv_translucenthud.value));
} }
// //
...@@ -1334,47 +1328,43 @@ void ST_preDrawTitleCard(void) ...@@ -1334,47 +1328,43 @@ void ST_preDrawTitleCard(void)
// //
void ST_runTitleCard(void) void ST_runTitleCard(void)
{ {
boolean run = !(paused || P_AutoPause()); if (!lt_active || ((paused || P_AutoPause()) && lt_ticker >= PRELEVELTIME))
if (!G_IsTitleCardAvailable())
return;
if (lt_ticker >= (lt_endtime + TICRATE))
return; return;
if (run || (lt_ticker < PRELEVELTIME)) if (!lt_exitticker)
{ {
// tick if (abs(lt_scroll) > FRACUNIT)
lt_ticker++; lt_scroll -= (lt_scroll>>2);
if (lt_ticker >= lt_endtime) else
lt_exitticker++; lt_scroll = 0;
// scroll to screen (level title) if (abs(lt_zigzag) > FRACUNIT)
if (!lt_exitticker) lt_zigzag -= (lt_zigzag>>2);
{
if (abs(lt_scroll) > FRACUNIT)
lt_scroll -= (lt_scroll>>2);
else
lt_scroll = 0;
}
// scroll away from screen (level title)
else else
lt_zigzag = 0;
}
else
{
lt_mom -= FRACUNIT*6;
if (lt_scroll > -BASEVIDWIDTH * FRACUNIT * 2)
{ {
lt_mom -= FRACUNIT*6;
lt_scroll += lt_mom; lt_scroll += lt_mom;
} }
// scroll to screen (zigzag) if (lt_zigzag > -(lt_patches[1]->width)*FRACUNIT)
if (!lt_exitticker)
{ {
if (abs(lt_zigzag) > FRACUNIT)
lt_zigzag -= (lt_zigzag>>2);
else
lt_zigzag = 0;
}
// scroll away from screen (zigzag)
else
lt_zigzag += lt_mom; lt_zigzag += lt_mom;
}
}
lt_ticker++;
lt_exitticker = max((signed)lt_ticker - (signed)lt_endtime, 0);
if (lt_ticker >= (lt_endtime + TICRATE))
{
lt_active = false;
return;
} }
} }
...@@ -1401,9 +1391,6 @@ void ST_drawTitleCard(void) ...@@ -1401,9 +1391,6 @@ void ST_drawTitleCard(void)
colormap = R_GetTranslationColormap(TC_DEFAULT, colornum, GTC_CACHE); colormap = R_GetTranslationColormap(TC_DEFAULT, colornum, GTC_CACHE);
if (!G_IsTitleCardAvailable())
return;
if (!LUA_HudEnabled(hud_stagetitle)) if (!LUA_HudEnabled(hud_stagetitle))
goto luahook; goto luahook;
...@@ -1484,13 +1471,14 @@ void ST_preLevelTitleCardDrawer(void) ...@@ -1484,13 +1471,14 @@ void ST_preLevelTitleCardDrawer(void)
// //
void ST_drawWipeTitleCard(void) void ST_drawWipeTitleCard(void)
{ {
if (!lt_active)
return;
stplyr = &players[consoleplayer]; stplyr = &players[consoleplayer];
ST_preDrawTitleCard();
ST_drawTitleCard(); ST_drawTitleCard();
if (splitscreen) if (splitscreen)
{ {
stplyr = &players[secondarydisplayplayer]; stplyr = &players[secondarydisplayplayer];
ST_preDrawTitleCard();
ST_drawTitleCard(); ST_drawTitleCard();
} }
} }
...@@ -2078,24 +2066,25 @@ static void ST_drawNiGHTSHUD(void) ...@@ -2078,24 +2066,25 @@ static void ST_drawNiGHTSHUD(void)
if (!stplyr->exiting && !oldspecialstage && LUA_HudEnabled(hud_nightsscore)) if (!stplyr->exiting && !oldspecialstage && LUA_HudEnabled(hud_nightsscore))
ST_DrawNightsOverlayNum(304<<FRACBITS, 14<<FRACBITS, FRACUNIT, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTORIGHT, stplyr->marescore, nightsnum, SKINCOLOR_AZURE); ST_DrawNightsOverlayNum(304<<FRACBITS, 14<<FRACBITS, FRACUNIT, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTORIGHT, stplyr->marescore, nightsnum, SKINCOLOR_AZURE);
// TODO give this its own section for Lua // TODO: give this its own section for Lua
// TODO: on multi-mare maps, show time & grade for each completed mare
if (!stplyr->exiting && LUA_HudEnabled(hud_nightsscore)) if (!stplyr->exiting && LUA_HudEnabled(hud_nightsscore))
{ {
if (modeattacking == ATTACKING_NIGHTS) if (modeattacking == ATTACKING_NIGHTS)
{ {
INT32 maretime = max(stplyr->realtime - stplyr->marebegunat, 0); INT32 maretime = max(stplyr->realtime - stplyr->marebegunat, 0);
#define VFLAGS V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_PERPLAYER|V_HUDTRANS #define VFLAGS V_SNAPTOTOP|V_SNAPTORIGHT|V_PERPLAYER|V_HUDTRANS
V_DrawScaledPatch(BASEVIDWIDTH-22, BASEVIDHEIGHT-20, VFLAGS, W_CachePatchName("NGRTIMER", PU_HUDGFX)); V_DrawScaledPatch(BASEVIDWIDTH-16, 40, VFLAGS, W_CachePatchName("NGRTIMER", PU_HUDGFX));
V_DrawPaddedTallNum(BASEVIDWIDTH-22, BASEVIDHEIGHT-20, VFLAGS, G_TicsToCentiseconds(maretime), 2); V_DrawPaddedTallNum(BASEVIDWIDTH-16, 40, VFLAGS, G_TicsToCentiseconds(maretime), 2);
V_DrawScaledPatch(BASEVIDWIDTH-46, BASEVIDHEIGHT-20, VFLAGS, sboperiod); V_DrawScaledPatch(BASEVIDWIDTH-40, 40, VFLAGS, sboperiod);
if (maretime < 60*TICRATE) if (maretime < 60*TICRATE)
V_DrawTallNum(BASEVIDWIDTH-46, BASEVIDHEIGHT-20, VFLAGS, G_TicsToSeconds(maretime)); V_DrawTallNum(BASEVIDWIDTH-40, 40, VFLAGS, G_TicsToSeconds(maretime));
else else
{ {
V_DrawPaddedTallNum(BASEVIDWIDTH-46, BASEVIDHEIGHT-20, VFLAGS, G_TicsToSeconds(maretime), 2); V_DrawPaddedTallNum(BASEVIDWIDTH-40, 40, VFLAGS, G_TicsToSeconds(maretime), 2);
V_DrawScaledPatch(BASEVIDWIDTH-70, BASEVIDHEIGHT-20, VFLAGS, sbocolon); V_DrawScaledPatch(BASEVIDWIDTH-64, 40, VFLAGS, sbocolon);
V_DrawTallNum(BASEVIDWIDTH-70, BASEVIDHEIGHT-20, VFLAGS, G_TicsToMinutes(maretime, true)); V_DrawTallNum(BASEVIDWIDTH-64, 40, VFLAGS, G_TicsToMinutes(maretime, true));
} }
#undef VFLAGS #undef VFLAGS
} }
...@@ -2665,7 +2654,7 @@ static boolean ST_doItemFinderIconsAndSound(void) ...@@ -2665,7 +2654,7 @@ static boolean ST_doItemFinderIconsAndSound(void)
// Scan thinkers to find emblem mobj with these ids // Scan thinkers to find emblem mobj with these ids
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -2701,24 +2690,14 @@ static boolean ST_doItemFinderIconsAndSound(void) ...@@ -2701,24 +2690,14 @@ static boolean ST_doItemFinderIconsAndSound(void)
return true; return true;
} }
static boolean drawstagetitle = false;
// //
// Draw the status bar overlay, customisable: the user chooses which // Draw the status bar overlay, customisable: the user chooses which
// kind of information to overlay // kind of information to overlay
// //
static void ST_overlayDrawer(void) static void ST_overlayDrawer(void)
{ {
// Decide whether to draw the stage title or not
boolean stagetitle = false;
// Check for a valid level title
// If the HUD is enabled
// And, if Lua is running, if the HUD library has the stage title enabled
if (G_IsTitleCardAvailable() && *mapheaderinfo[gamemap-1]->lvlttl != '\0' && !(hu_showscores && (netgame || multiplayer)))
{
stagetitle = true;
ST_preDrawTitleCard();
}
// hu_showscores = auto hide score/time/rings when tab rankings are shown // hu_showscores = auto hide score/time/rings when tab rankings are shown
if (!(hu_showscores && (netgame || multiplayer))) if (!(hu_showscores && (netgame || multiplayer)))
{ {
...@@ -2841,7 +2820,7 @@ static void ST_overlayDrawer(void) ...@@ -2841,7 +2820,7 @@ static void ST_overlayDrawer(void)
} }
else if (cv_powerupdisplay.value == 2 && LUA_HudEnabled(hud_powerups)) else if (cv_powerupdisplay.value == 2 && LUA_HudEnabled(hud_powerups))
ST_drawPowerupHUD(); // same as it ever was... ST_drawPowerupHUD(); // same as it ever was...
} }
else if (!(netgame || multiplayer) && cv_powerupdisplay.value == 2 && LUA_HudEnabled(hud_powerups)) else if (!(netgame || multiplayer) && cv_powerupdisplay.value == 2 && LUA_HudEnabled(hud_powerups))
ST_drawPowerupHUD(); // same as it ever was... ST_drawPowerupHUD(); // same as it ever was...
...@@ -2858,7 +2837,7 @@ static void ST_overlayDrawer(void) ...@@ -2858,7 +2837,7 @@ static void ST_overlayDrawer(void)
} }
// draw level title Tails // draw level title Tails
if (stagetitle && (!WipeInAction) && (!WipeStageTitle)) if (drawstagetitle && !WipeInAction && !WipeStageTitle)
ST_drawTitleCard(); ST_drawTitleCard();
if (!hu_showscores && (netgame || multiplayer) && LUA_HudEnabled(hud_textspectator)) if (!hu_showscores && (netgame || multiplayer) && LUA_HudEnabled(hud_textspectator))
...@@ -2929,7 +2908,22 @@ void ST_Drawer(void) ...@@ -2929,7 +2908,22 @@ void ST_Drawer(void)
} }
} }
st_translucency = cv_translucenthud.value; // Decide whether to draw the stage title or not
if (lt_active)
{
drawstagetitle = !(hu_showscores && (netgame || multiplayer));
if (!lt_exitticker)
st_translucency = 0;
else
st_translucency = max(0, min((INT32)lt_exitticker-4, cv_translucenthud.value));
}
else
{
st_translucency = cv_translucenthud.value;
drawstagetitle = false;
}
if (st_overlay) if (st_overlay)
{ {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -49,9 +49,9 @@ void ST_doPaletteStuff(void); ...@@ -49,9 +49,9 @@ void ST_doPaletteStuff(void);
// title card // title card
void ST_startTitleCard(void); void ST_startTitleCard(void);
void ST_stopTitleCard(void);
void ST_runTitleCard(void); void ST_runTitleCard(void);
void ST_drawTitleCard(void); void ST_drawTitleCard(void);
void ST_preDrawTitleCard(void);
void ST_preLevelTitleCardDrawer(void); void ST_preLevelTitleCardDrawer(void);
void ST_drawWipeTitleCard(void); void ST_drawWipeTitleCard(void);
......
// SONIC ROBO BLAST 2 // SONIC ROBO BLAST 2
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 2006 by Graue. // Copyright (C) 2006 by Graue.
// Copyright (C) 2006-2023 by Sonic Team Junior. // Copyright (C) 2006-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -83,7 +83,7 @@ char *xstrtok(char *line, const char *delims) ...@@ -83,7 +83,7 @@ char *xstrtok(char *line, const char *delims)
return NULL; return NULL;
p = saveline; // save start of this token p = saveline; // save start of this token
saveline += strcspn(saveline, delims); // get the number of non-delims characters, go past delimiter saveline += strcspn(saveline, delims); // get the number of non-delims characters, go past delimiter
if(*saveline != '\0') // trash the delim if necessary if(*saveline != '\0') // trash the delim if necessary
...@@ -91,4 +91,3 @@ char *xstrtok(char *line, const char *delims) ...@@ -91,4 +91,3 @@ char *xstrtok(char *line, const char *delims)
return p; return p;
} }
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// Copyright (C) 2009 by Stephen McGranahan. // Copyright (C) 2009 by Stephen McGranahan.
// //
// This program is free software distributed under the // This program is free software distributed under the
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
......
/*
Copyright (c) 2003-2022, Troy D. Hanson https://troydhanson.github.io/uthash/
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef UTHASH_H
#define UTHASH_H
#define UTHASH_VERSION 2.3.0
#include <string.h> /* memcmp, memset, strlen */
#include <stddef.h> /* ptrdiff_t */
#include <stdlib.h> /* exit */
#if defined(HASH_DEFINE_OWN_STDINT) && HASH_DEFINE_OWN_STDINT
/* This codepath is provided for backward compatibility, but I plan to remove it. */
#warning "HASH_DEFINE_OWN_STDINT is deprecated; please use HASH_NO_STDINT instead"
typedef unsigned int uint32_t;
typedef unsigned char uint8_t;
#elif defined(HASH_NO_STDINT) && HASH_NO_STDINT
#else
#include <stdint.h> /* uint8_t, uint32_t */
#endif
/* These macros use decltype or the earlier __typeof GNU extension.
As decltype is only available in newer compilers (VS2010 or gcc 4.3+
when compiling c++ source) this code uses whatever method is needed
or, for VS2008 where neither is available, uses casting workarounds. */
#if !defined(DECLTYPE) && !defined(NO_DECLTYPE)
#if defined(_MSC_VER) /* MS compiler */
#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
#define DECLTYPE(x) (decltype(x))
#else /* VS2008 or older (or VS2010 in C mode) */
#define NO_DECLTYPE
#endif
#elif defined(__MCST__) /* Elbrus C Compiler */
#define DECLTYPE(x) (__typeof(x))
#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__)
#define NO_DECLTYPE
#else /* GNU, Sun and other compilers */
#define DECLTYPE(x) (__typeof(x))
#endif
#endif
#ifdef NO_DECLTYPE
#define DECLTYPE(x)
#define DECLTYPE_ASSIGN(dst,src) \
do { \
char **_da_dst = (char**)(&(dst)); \
*_da_dst = (char*)(src); \
} while (0)
#else
#define DECLTYPE_ASSIGN(dst,src) \
do { \
(dst) = DECLTYPE(dst)(src); \
} while (0)
#endif
#ifndef uthash_malloc
#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
#endif
#ifndef uthash_free
#define uthash_free(ptr,sz) free(ptr) /* free fcn */
#endif
#ifndef uthash_bzero
#define uthash_bzero(a,n) memset(a,'\0',n)
#endif
#ifndef uthash_strlen
#define uthash_strlen(s) strlen(s)
#endif
#ifndef HASH_FUNCTION
#define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv)
#endif
#ifndef HASH_KEYCMP
#define HASH_KEYCMP(a,b,n) memcmp(a,b,n)
#endif
#ifndef uthash_noexpand_fyi
#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
#endif
#ifndef uthash_expand_fyi
#define uthash_expand_fyi(tbl) /* can be defined to log expands */
#endif
#ifndef HASH_NONFATAL_OOM
#define HASH_NONFATAL_OOM 0
#endif
#if HASH_NONFATAL_OOM
/* malloc failures can be recovered from */
#ifndef uthash_nonfatal_oom
#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */
#endif
#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0)
#define IF_HASH_NONFATAL_OOM(x) x
#else
/* malloc failures result in lost memory, hash tables are unusable */
#ifndef uthash_fatal
#define uthash_fatal(msg) exit(-1) /* fatal OOM error */
#endif
#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory")
#define IF_HASH_NONFATAL_OOM(x)
#endif
/* initial number of buckets */
#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */
#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */
#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */
/* calculate the element whose hash handle address is hhp */
#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
/* calculate the hash handle from element address elp */
#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho)))
#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \
do { \
struct UT_hash_handle *_hd_hh_item = (itemptrhh); \
unsigned _hd_bkt; \
HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
(head)->hh.tbl->buckets[_hd_bkt].count++; \
_hd_hh_item->hh_next = NULL; \
_hd_hh_item->hh_prev = NULL; \
} while (0)
#define HASH_VALUE(keyptr,keylen,hashv) \
do { \
HASH_FUNCTION(keyptr, keylen, hashv); \
} while (0)
#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \
do { \
(out) = NULL; \
if (head) { \
unsigned _hf_bkt; \
HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \
if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \
HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \
} \
} \
} while (0)
#define HASH_FIND(hh,head,keyptr,keylen,out) \
do { \
(out) = NULL; \
if (head) { \
unsigned _hf_hashv; \
HASH_VALUE(keyptr, keylen, _hf_hashv); \
HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \
} \
} while (0)
#ifdef HASH_BLOOM
#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM)
#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL)
#define HASH_BLOOM_MAKE(tbl,oomed) \
do { \
(tbl)->bloom_nbits = HASH_BLOOM; \
(tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
if (!(tbl)->bloom_bv) { \
HASH_RECORD_OOM(oomed); \
} else { \
uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
(tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
} \
} while (0)
#define HASH_BLOOM_FREE(tbl) \
do { \
uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
} while (0)
#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U)))
#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U)))
#define HASH_BLOOM_ADD(tbl,hashv) \
HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))
#define HASH_BLOOM_TEST(tbl,hashv) \
HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))
#else
#define HASH_BLOOM_MAKE(tbl,oomed)
#define HASH_BLOOM_FREE(tbl)
#define HASH_BLOOM_ADD(tbl,hashv)
#define HASH_BLOOM_TEST(tbl,hashv) (1)
#define HASH_BLOOM_BYTELEN 0U
#endif
#define HASH_MAKE_TABLE(hh,head,oomed) \
do { \
(head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \
if (!(head)->hh.tbl) { \
HASH_RECORD_OOM(oomed); \
} else { \
uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \
(head)->hh.tbl->tail = &((head)->hh); \
(head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
(head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
(head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
(head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \
(head)->hh.tbl->signature = HASH_SIGNATURE; \
if (!(head)->hh.tbl->buckets) { \
HASH_RECORD_OOM(oomed); \
uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
} else { \
uthash_bzero((head)->hh.tbl->buckets, \
HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \
HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \
IF_HASH_NONFATAL_OOM( \
if (oomed) { \
uthash_free((head)->hh.tbl->buckets, \
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
} \
) \
} \
} \
} while (0)
#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \
do { \
(replaced) = NULL; \
HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
if (replaced) { \
HASH_DELETE(hh, head, replaced); \
} \
HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \
} while (0)
#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \
do { \
(replaced) = NULL; \
HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
if (replaced) { \
HASH_DELETE(hh, head, replaced); \
} \
HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \
} while (0)
#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \
do { \
unsigned _hr_hashv; \
HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \
HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \
} while (0)
#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \
do { \
unsigned _hr_hashv; \
HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \
HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \
} while (0)
#define HASH_APPEND_LIST(hh, head, add) \
do { \
(add)->hh.next = NULL; \
(add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
(head)->hh.tbl->tail->next = (add); \
(head)->hh.tbl->tail = &((add)->hh); \
} while (0)
#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \
do { \
do { \
if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \
break; \
} \
} while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \
} while (0)
#ifdef NO_DECLTYPE
#undef HASH_AKBI_INNER_LOOP
#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \
do { \
char *_hs_saved_head = (char*)(head); \
do { \
DECLTYPE_ASSIGN(head, _hs_iter); \
if (cmpfcn(head, add) > 0) { \
DECLTYPE_ASSIGN(head, _hs_saved_head); \
break; \
} \
DECLTYPE_ASSIGN(head, _hs_saved_head); \
} while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \
} while (0)
#endif
#if HASH_NONFATAL_OOM
#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \
do { \
if (!(oomed)) { \
unsigned _ha_bkt; \
(head)->hh.tbl->num_items++; \
HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \
HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \
if (oomed) { \
HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \
HASH_DELETE_HH(hh, head, &(add)->hh); \
(add)->hh.tbl = NULL; \
uthash_nonfatal_oom(add); \
} else { \
HASH_BLOOM_ADD((head)->hh.tbl, hashval); \
HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \
} \
} else { \
(add)->hh.tbl = NULL; \
uthash_nonfatal_oom(add); \
} \
} while (0)
#else
#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \
do { \
unsigned _ha_bkt; \
(head)->hh.tbl->num_items++; \
HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \
HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \
HASH_BLOOM_ADD((head)->hh.tbl, hashval); \
HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \
} while (0)
#endif
#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \
do { \
IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \
(add)->hh.hashv = (hashval); \
(add)->hh.key = (char*) (keyptr); \
(add)->hh.keylen = (unsigned) (keylen_in); \
if (!(head)) { \
(add)->hh.next = NULL; \
(add)->hh.prev = NULL; \
HASH_MAKE_TABLE(hh, add, _ha_oomed); \
IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \
(head) = (add); \
IF_HASH_NONFATAL_OOM( } ) \
} else { \
void *_hs_iter = (head); \
(add)->hh.tbl = (head)->hh.tbl; \
HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \
if (_hs_iter) { \
(add)->hh.next = _hs_iter; \
if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \
HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \
} else { \
(head) = (add); \
} \
HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \
} else { \
HASH_APPEND_LIST(hh, head, add); \
} \
} \
HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \
HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \
} while (0)
#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \
do { \
unsigned _hs_hashv; \
HASH_VALUE(keyptr, keylen_in, _hs_hashv); \
HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \
} while (0)
#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \
HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn)
#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \
HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn)
#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \
do { \
IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \
(add)->hh.hashv = (hashval); \
(add)->hh.key = (const void*) (keyptr); \
(add)->hh.keylen = (unsigned) (keylen_in); \
if (!(head)) { \
(add)->hh.next = NULL; \
(add)->hh.prev = NULL; \
HASH_MAKE_TABLE(hh, add, _ha_oomed); \
IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \
(head) = (add); \
IF_HASH_NONFATAL_OOM( } ) \
} else { \
(add)->hh.tbl = (head)->hh.tbl; \
HASH_APPEND_LIST(hh, head, add); \
} \
HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \
HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \
} while (0)
#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
do { \
unsigned _ha_hashv; \
HASH_VALUE(keyptr, keylen_in, _ha_hashv); \
HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \
} while (0)
#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \
HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add)
#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add)
#define HASH_TO_BKT(hashv,num_bkts,bkt) \
do { \
bkt = ((hashv) & ((num_bkts) - 1U)); \
} while (0)
/* delete "delptr" from the hash table.
* "the usual" patch-up process for the app-order doubly-linked-list.
* The use of _hd_hh_del below deserves special explanation.
* These used to be expressed using (delptr) but that led to a bug
* if someone used the same symbol for the head and deletee, like
* HASH_DELETE(hh,users,users);
* We want that to work, but by changing the head (users) below
* we were forfeiting our ability to further refer to the deletee (users)
* in the patch-up process. Solution: use scratch space to
* copy the deletee pointer, then the latter references are via that
* scratch pointer rather than through the repointed (users) symbol.
*/
#define HASH_DELETE(hh,head,delptr) \
HASH_DELETE_HH(hh, head, &(delptr)->hh)
#define HASH_DELETE_HH(hh,head,delptrhh) \
do { \
const struct UT_hash_handle *_hd_hh_del = (delptrhh); \
if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \
HASH_BLOOM_FREE((head)->hh.tbl); \
uthash_free((head)->hh.tbl->buckets, \
(head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
(head) = NULL; \
} else { \
unsigned _hd_bkt; \
if (_hd_hh_del == (head)->hh.tbl->tail) { \
(head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \
} \
if (_hd_hh_del->prev != NULL) { \
HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \
} else { \
DECLTYPE_ASSIGN(head, _hd_hh_del->next); \
} \
if (_hd_hh_del->next != NULL) { \
HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \
} \
HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
(head)->hh.tbl->num_items--; \
} \
HASH_FSCK(hh, head, "HASH_DELETE_HH"); \
} while (0)
/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
#define HASH_FIND_STR(head,findstr,out) \
do { \
unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \
HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \
} while (0)
#define HASH_ADD_STR(head,strfield,add) \
do { \
unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \
HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \
} while (0)
#define HASH_REPLACE_STR(head,strfield,add,replaced) \
do { \
unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \
HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \
} while (0)
#define HASH_FIND_INT(head,findint,out) \
HASH_FIND(hh,head,findint,sizeof(int),out)
#define HASH_ADD_INT(head,intfield,add) \
HASH_ADD(hh,head,intfield,sizeof(int),add)
#define HASH_REPLACE_INT(head,intfield,add,replaced) \
HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
#define HASH_FIND_PTR(head,findptr,out) \
HASH_FIND(hh,head,findptr,sizeof(void *),out)
#define HASH_ADD_PTR(head,ptrfield,add) \
HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \
HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
#define HASH_DEL(head,delptr) \
HASH_DELETE(hh,head,delptr)
/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
* This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
*/
#ifdef HASH_DEBUG
#include <stdio.h> /* fprintf, stderr */
#define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0)
#define HASH_FSCK(hh,head,where) \
do { \
struct UT_hash_handle *_thh; \
if (head) { \
unsigned _bkt_i; \
unsigned _count = 0; \
char *_prev; \
for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \
unsigned _bkt_count = 0; \
_thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
_prev = NULL; \
while (_thh) { \
if (_prev != (char*)(_thh->hh_prev)) { \
HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \
(where), (void*)_thh->hh_prev, (void*)_prev); \
} \
_bkt_count++; \
_prev = (char*)(_thh); \
_thh = _thh->hh_next; \
} \
_count += _bkt_count; \
if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \
(where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
} \
} \
if (_count != (head)->hh.tbl->num_items) { \
HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \
(where), (head)->hh.tbl->num_items, _count); \
} \
_count = 0; \
_prev = NULL; \
_thh = &(head)->hh; \
while (_thh) { \
_count++; \
if (_prev != (char*)_thh->prev) { \
HASH_OOPS("%s: invalid prev %p, actual %p\n", \
(where), (void*)_thh->prev, (void*)_prev); \
} \
_prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
_thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \
} \
if (_count != (head)->hh.tbl->num_items) { \
HASH_OOPS("%s: invalid app item count %u, actual %u\n", \
(where), (head)->hh.tbl->num_items, _count); \
} \
} \
} while (0)
#else
#define HASH_FSCK(hh,head,where)
#endif
/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
* the descriptor to which this macro is defined for tuning the hash function.
* The app can #include <unistd.h> to get the prototype for write(2). */
#ifdef HASH_EMIT_KEYS
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
do { \
unsigned _klen = fieldlen; \
write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \
} while (0)
#else
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
#endif
/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */
#define HASH_BER(key,keylen,hashv) \
do { \
unsigned _hb_keylen = (unsigned)keylen; \
const unsigned char *_hb_key = (const unsigned char*)(key); \
(hashv) = 0; \
while (_hb_keylen-- != 0U) { \
(hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \
} \
} while (0)
/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
* http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
* (archive link: https://archive.is/Ivcan )
*/
#define HASH_SAX(key,keylen,hashv) \
do { \
unsigned _sx_i; \
const unsigned char *_hs_key = (const unsigned char*)(key); \
hashv = 0; \
for (_sx_i=0; _sx_i < keylen; _sx_i++) { \
hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
} \
} while (0)
/* FNV-1a variation */
#define HASH_FNV(key,keylen,hashv) \
do { \
unsigned _fn_i; \
const unsigned char *_hf_key = (const unsigned char*)(key); \
(hashv) = 2166136261U; \
for (_fn_i=0; _fn_i < keylen; _fn_i++) { \
hashv = hashv ^ _hf_key[_fn_i]; \
hashv = hashv * 16777619U; \
} \
} while (0)
#define HASH_OAT(key,keylen,hashv) \
do { \
unsigned _ho_i; \
const unsigned char *_ho_key=(const unsigned char*)(key); \
hashv = 0; \
for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
hashv += _ho_key[_ho_i]; \
hashv += (hashv << 10); \
hashv ^= (hashv >> 6); \
} \
hashv += (hashv << 3); \
hashv ^= (hashv >> 11); \
hashv += (hashv << 15); \
} while (0)
#define HASH_JEN_MIX(a,b,c) \
do { \
a -= b; a -= c; a ^= ( c >> 13 ); \
b -= c; b -= a; b ^= ( a << 8 ); \
c -= a; c -= b; c ^= ( b >> 13 ); \
a -= b; a -= c; a ^= ( c >> 12 ); \
b -= c; b -= a; b ^= ( a << 16 ); \
c -= a; c -= b; c ^= ( b >> 5 ); \
a -= b; a -= c; a ^= ( c >> 3 ); \
b -= c; b -= a; b ^= ( a << 10 ); \
c -= a; c -= b; c ^= ( b >> 15 ); \
} while (0)
#define HASH_JEN(key,keylen,hashv) \
do { \
unsigned _hj_i,_hj_j,_hj_k; \
unsigned const char *_hj_key=(unsigned const char*)(key); \
hashv = 0xfeedbeefu; \
_hj_i = _hj_j = 0x9e3779b9u; \
_hj_k = (unsigned)(keylen); \
while (_hj_k >= 12U) { \
_hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
+ ( (unsigned)_hj_key[2] << 16 ) \
+ ( (unsigned)_hj_key[3] << 24 ) ); \
_hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
+ ( (unsigned)_hj_key[6] << 16 ) \
+ ( (unsigned)_hj_key[7] << 24 ) ); \
hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
+ ( (unsigned)_hj_key[10] << 16 ) \
+ ( (unsigned)_hj_key[11] << 24 ) ); \
\
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
\
_hj_key += 12; \
_hj_k -= 12U; \
} \
hashv += (unsigned)(keylen); \
switch ( _hj_k ) { \
case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \
case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \
case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \
case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \
case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \
case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \
case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \
case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \
case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \
case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \
case 1: _hj_i += _hj_key[0]; /* FALLTHROUGH */ \
default: ; \
} \
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
} while (0)
/* The Paul Hsieh hash function */
#undef get16bits
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
#define get16bits(d) (*((const uint16_t *) (d)))
#endif
#if !defined (get16bits)
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
+(uint32_t)(((const uint8_t *)(d))[0]) )
#endif
#define HASH_SFH(key,keylen,hashv) \
do { \
unsigned const char *_sfh_key=(unsigned const char*)(key); \
uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \
\
unsigned _sfh_rem = _sfh_len & 3U; \
_sfh_len >>= 2; \
hashv = 0xcafebabeu; \
\
/* Main loop */ \
for (;_sfh_len > 0U; _sfh_len--) { \
hashv += get16bits (_sfh_key); \
_sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \
hashv = (hashv << 16) ^ _sfh_tmp; \
_sfh_key += 2U*sizeof (uint16_t); \
hashv += hashv >> 11; \
} \
\
/* Handle end cases */ \
switch (_sfh_rem) { \
case 3: hashv += get16bits (_sfh_key); \
hashv ^= hashv << 16; \
hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \
hashv += hashv >> 11; \
break; \
case 2: hashv += get16bits (_sfh_key); \
hashv ^= hashv << 11; \
hashv += hashv >> 17; \
break; \
case 1: hashv += *_sfh_key; \
hashv ^= hashv << 10; \
hashv += hashv >> 1; \
break; \
default: ; \
} \
\
/* Force "avalanching" of final 127 bits */ \
hashv ^= hashv << 3; \
hashv += hashv >> 5; \
hashv ^= hashv << 4; \
hashv += hashv >> 17; \
hashv ^= hashv << 25; \
hashv += hashv >> 6; \
} while (0)
/* iterate over items in a known bucket to find desired item */
#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \
do { \
if ((head).hh_head != NULL) { \
DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \
} else { \
(out) = NULL; \
} \
while ((out) != NULL) { \
if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \
if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \
break; \
} \
} \
if ((out)->hh.hh_next != NULL) { \
DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \
} else { \
(out) = NULL; \
} \
} \
} while (0)
/* add an item to a bucket */
#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \
do { \
UT_hash_bucket *_ha_head = &(head); \
_ha_head->count++; \
(addhh)->hh_next = _ha_head->hh_head; \
(addhh)->hh_prev = NULL; \
if (_ha_head->hh_head != NULL) { \
_ha_head->hh_head->hh_prev = (addhh); \
} \
_ha_head->hh_head = (addhh); \
if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \
&& !(addhh)->tbl->noexpand) { \
HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \
IF_HASH_NONFATAL_OOM( \
if (oomed) { \
HASH_DEL_IN_BKT(head,addhh); \
} \
) \
} \
} while (0)
/* remove an item from a given bucket */
#define HASH_DEL_IN_BKT(head,delhh) \
do { \
UT_hash_bucket *_hd_head = &(head); \
_hd_head->count--; \
if (_hd_head->hh_head == (delhh)) { \
_hd_head->hh_head = (delhh)->hh_next; \
} \
if ((delhh)->hh_prev) { \
(delhh)->hh_prev->hh_next = (delhh)->hh_next; \
} \
if ((delhh)->hh_next) { \
(delhh)->hh_next->hh_prev = (delhh)->hh_prev; \
} \
} while (0)
/* Bucket expansion has the effect of doubling the number of buckets
* and redistributing the items into the new buckets. Ideally the
* items will distribute more or less evenly into the new buckets
* (the extent to which this is true is a measure of the quality of
* the hash function as it applies to the key domain).
*
* With the items distributed into more buckets, the chain length
* (item count) in each bucket is reduced. Thus by expanding buckets
* the hash keeps a bound on the chain length. This bounded chain
* length is the essence of how a hash provides constant time lookup.
*
* The calculation of tbl->ideal_chain_maxlen below deserves some
* explanation. First, keep in mind that we're calculating the ideal
* maximum chain length based on the *new* (doubled) bucket count.
* In fractions this is just n/b (n=number of items,b=new num buckets).
* Since the ideal chain length is an integer, we want to calculate
* ceil(n/b). We don't depend on floating point arithmetic in this
* hash, so to calculate ceil(n/b) with integers we could write
*
* ceil(n/b) = (n/b) + ((n%b)?1:0)
*
* and in fact a previous version of this hash did just that.
* But now we have improved things a bit by recognizing that b is
* always a power of two. We keep its base 2 log handy (call it lb),
* so now we can write this with a bit shift and logical AND:
*
* ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
*
*/
#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \
do { \
unsigned _he_bkt; \
unsigned _he_bkt_i; \
struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
_he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \
if (!_he_new_buckets) { \
HASH_RECORD_OOM(oomed); \
} else { \
uthash_bzero(_he_new_buckets, \
sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \
(tbl)->ideal_chain_maxlen = \
((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \
((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \
(tbl)->nonideal_items = 0; \
for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \
_he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \
while (_he_thh != NULL) { \
_he_hh_nxt = _he_thh->hh_next; \
HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \
_he_newbkt = &(_he_new_buckets[_he_bkt]); \
if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \
(tbl)->nonideal_items++; \
if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \
_he_newbkt->expand_mult++; \
} \
} \
_he_thh->hh_prev = NULL; \
_he_thh->hh_next = _he_newbkt->hh_head; \
if (_he_newbkt->hh_head != NULL) { \
_he_newbkt->hh_head->hh_prev = _he_thh; \
} \
_he_newbkt->hh_head = _he_thh; \
_he_thh = _he_hh_nxt; \
} \
} \
uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \
(tbl)->num_buckets *= 2U; \
(tbl)->log2_num_buckets++; \
(tbl)->buckets = _he_new_buckets; \
(tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \
((tbl)->ineff_expands+1U) : 0U; \
if ((tbl)->ineff_expands > 1U) { \
(tbl)->noexpand = 1; \
uthash_noexpand_fyi(tbl); \
} \
uthash_expand_fyi(tbl); \
} \
} while (0)
/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
/* Note that HASH_SORT assumes the hash handle name to be hh.
* HASH_SRT was added to allow the hash handle name to be passed in. */
#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
#define HASH_SRT(hh,head,cmpfcn) \
do { \
unsigned _hs_i; \
unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
if (head != NULL) { \
_hs_insize = 1; \
_hs_looping = 1; \
_hs_list = &((head)->hh); \
while (_hs_looping != 0U) { \
_hs_p = _hs_list; \
_hs_list = NULL; \
_hs_tail = NULL; \
_hs_nmerges = 0; \
while (_hs_p != NULL) { \
_hs_nmerges++; \
_hs_q = _hs_p; \
_hs_psize = 0; \
for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \
_hs_psize++; \
_hs_q = ((_hs_q->next != NULL) ? \
HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \
if (_hs_q == NULL) { \
break; \
} \
} \
_hs_qsize = _hs_insize; \
while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \
if (_hs_psize == 0U) { \
_hs_e = _hs_q; \
_hs_q = ((_hs_q->next != NULL) ? \
HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \
_hs_qsize--; \
} else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \
_hs_e = _hs_p; \
if (_hs_p != NULL) { \
_hs_p = ((_hs_p->next != NULL) ? \
HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \
} \
_hs_psize--; \
} else if ((cmpfcn( \
DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \
DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \
)) <= 0) { \
_hs_e = _hs_p; \
if (_hs_p != NULL) { \
_hs_p = ((_hs_p->next != NULL) ? \
HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \
} \
_hs_psize--; \
} else { \
_hs_e = _hs_q; \
_hs_q = ((_hs_q->next != NULL) ? \
HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \
_hs_qsize--; \
} \
if ( _hs_tail != NULL ) { \
_hs_tail->next = ((_hs_e != NULL) ? \
ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \
} else { \
_hs_list = _hs_e; \
} \
if (_hs_e != NULL) { \
_hs_e->prev = ((_hs_tail != NULL) ? \
ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \
} \
_hs_tail = _hs_e; \
} \
_hs_p = _hs_q; \
} \
if (_hs_tail != NULL) { \
_hs_tail->next = NULL; \
} \
if (_hs_nmerges <= 1U) { \
_hs_looping = 0; \
(head)->hh.tbl->tail = _hs_tail; \
DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \
} \
_hs_insize *= 2U; \
} \
HASH_FSCK(hh, head, "HASH_SRT"); \
} \
} while (0)
/* This function selects items from one hash into another hash.
* The end result is that the selected items have dual presence
* in both hashes. There is no copy of the items made; rather
* they are added into the new hash through a secondary hash
* hash handle that must be present in the structure. */
#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
do { \
unsigned _src_bkt, _dst_bkt; \
void *_last_elt = NULL, *_elt; \
UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
if ((src) != NULL) { \
for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
_src_hh != NULL; \
_src_hh = _src_hh->hh_next) { \
_elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
if (cond(_elt)) { \
IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \
_dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho); \
_dst_hh->key = _src_hh->key; \
_dst_hh->keylen = _src_hh->keylen; \
_dst_hh->hashv = _src_hh->hashv; \
_dst_hh->prev = _last_elt; \
_dst_hh->next = NULL; \
if (_last_elt_hh != NULL) { \
_last_elt_hh->next = _elt; \
} \
if ((dst) == NULL) { \
DECLTYPE_ASSIGN(dst, _elt); \
HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \
IF_HASH_NONFATAL_OOM( \
if (_hs_oomed) { \
uthash_nonfatal_oom(_elt); \
(dst) = NULL; \
continue; \
} \
) \
} else { \
_dst_hh->tbl = (dst)->hh_dst.tbl; \
} \
HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \
(dst)->hh_dst.tbl->num_items++; \
IF_HASH_NONFATAL_OOM( \
if (_hs_oomed) { \
HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \
HASH_DELETE_HH(hh_dst, dst, _dst_hh); \
_dst_hh->tbl = NULL; \
uthash_nonfatal_oom(_elt); \
continue; \
} \
) \
HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \
_last_elt = _elt; \
_last_elt_hh = _dst_hh; \
} \
} \
} \
} \
HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \
} while (0)
#define HASH_CLEAR(hh,head) \
do { \
if ((head) != NULL) { \
HASH_BLOOM_FREE((head)->hh.tbl); \
uthash_free((head)->hh.tbl->buckets, \
(head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \
uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
(head) = NULL; \
} \
} while (0)
#define HASH_OVERHEAD(hh,head) \
(((head) != NULL) ? ( \
(size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \
((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \
sizeof(UT_hash_table) + \
(HASH_BLOOM_BYTELEN))) : 0U)
#ifdef NO_DECLTYPE
#define HASH_ITER(hh,head,el,tmp) \
for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \
(el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL)))
#else
#define HASH_ITER(hh,head,el,tmp) \
for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \
(el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL)))
#endif
/* obtain a count of items in the hash */
#define HASH_COUNT(head) HASH_CNT(hh,head)
#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U)
typedef struct UT_hash_bucket {
struct UT_hash_handle *hh_head;
unsigned count;
/* expand_mult is normally set to 0. In this situation, the max chain length
* threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
* the bucket's chain exceeds this length, bucket expansion is triggered).
* However, setting expand_mult to a non-zero value delays bucket expansion
* (that would be triggered by additions to this particular bucket)
* until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
* (The multiplier is simply expand_mult+1). The whole idea of this
* multiplier is to reduce bucket expansions, since they are expensive, in
* situations where we know that a particular bucket tends to be overused.
* It is better to let its chain length grow to a longer yet-still-bounded
* value, than to do an O(n) bucket expansion too often.
*/
unsigned expand_mult;
} UT_hash_bucket;
/* random signature used only to find hash tables in external analysis */
#define HASH_SIGNATURE 0xa0111fe1u
#define HASH_BLOOM_SIGNATURE 0xb12220f2u
typedef struct UT_hash_table {
UT_hash_bucket *buckets;
unsigned num_buckets, log2_num_buckets;
unsigned num_items;
struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
/* in an ideal situation (all buckets used equally), no bucket would have
* more than ceil(#items/#buckets) items. that's the ideal chain length. */
unsigned ideal_chain_maxlen;
/* nonideal_items is the number of items in the hash whose chain position
* exceeds the ideal chain maxlen. these items pay the penalty for an uneven
* hash distribution; reaching them in a chain traversal takes >ideal steps */
unsigned nonideal_items;
/* ineffective expands occur when a bucket doubling was performed, but
* afterward, more than half the items in the hash had nonideal chain
* positions. If this happens on two consecutive expansions we inhibit any
* further expansion, as it's not helping; this happens when the hash
* function isn't a good fit for the key domain. When expansion is inhibited
* the hash will still work, albeit no longer in constant time. */
unsigned ineff_expands, noexpand;
uint32_t signature; /* used only to find hash tables in external analysis */
#ifdef HASH_BLOOM
uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
uint8_t *bloom_bv;
uint8_t bloom_nbits;
#endif
} UT_hash_table;
typedef struct UT_hash_handle {
struct UT_hash_table *tbl;
void *prev; /* prev element in app order */
void *next; /* next element in app order */
struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
struct UT_hash_handle *hh_next; /* next hh in bucket order */
const void *key; /* ptr to enclosing struct's key */
unsigned keylen; /* enclosing struct's key len */
unsigned hashv; /* result of hash-fcn(key) */
} UT_hash_handle;
#endif /* UTHASH_H */
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -1137,7 +1137,7 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) ...@@ -1137,7 +1137,7 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
} }
#endif #endif
if (splitscreen && (c & V_PERPLAYER)) if (splitscreen && (c & V_PERPLAYER))
{ {
...@@ -1953,7 +1953,7 @@ char *V_FontWordWrap(INT32 x, INT32 w, INT32 option, fixed_t scale, const char * ...@@ -1953,7 +1953,7 @@ char *V_FontWordWrap(INT32 x, INT32 w, INT32 option, fixed_t scale, const char *
INT32 spacewidth = font.spacewidth, charwidth = 0; INT32 spacewidth = font.spacewidth, charwidth = 0;
slen = strlen(string); slen = strlen(string);
if (w == 0) if (w == 0)
w = BASEVIDWIDTH; w = BASEVIDWIDTH;
w -= x; w -= x;
...@@ -2131,7 +2131,7 @@ void V_DrawAlignedFontStringAtFixed(fixed_t x, fixed_t y, INT32 option, fixed_t ...@@ -2131,7 +2131,7 @@ void V_DrawAlignedFontStringAtFixed(fixed_t x, fixed_t y, INT32 option, fixed_t
lx = x - (V_FontStringWidth(line, option, font)*pscale); lx = x - (V_FontStringWidth(line, option, font)*pscale);
break; break;
} }
V_DrawFontStringAtFixed(lx, ly, option, pscale, vscale, line, font); V_DrawFontStringAtFixed(lx, ly, option, pscale, vscale, line, font);
ly += FixedMul(((option & V_RETURN8) ? 8 : font.linespacing)<<FRACBITS, vscale); ly += FixedMul(((option & V_RETURN8) ? 8 : font.linespacing)<<FRACBITS, vscale);
...@@ -2422,7 +2422,7 @@ INT32 V_FontStringWidth(const char *string, INT32 option, fontdef_t font) ...@@ -2422,7 +2422,7 @@ INT32 V_FontStringWidth(const char *string, INT32 option, fontdef_t font)
if (wline < w) wline = w; if (wline < w) wline = w;
w = 0; w = 0;
continue; continue;
} }
if (string[i] & 0x80) if (string[i] & 0x80)
continue; continue;
...@@ -2455,7 +2455,7 @@ INT32 V_FontStringHeight(const char *string, INT32 option, fontdef_t font) ...@@ -2455,7 +2455,7 @@ INT32 V_FontStringHeight(const char *string, INT32 option, fontdef_t font)
{ {
result += (option & V_RETURN8) ? 8 : font.linespacing; result += (option & V_RETURN8) ? 8 : font.linespacing;
h = 0; h = 0;
} }
continue; continue;
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
......
#define SRB2VERSION "2.2.14"/* this must be the first line, for cmake !! */ #define SRB2VERSION "2.2.16"/* this must be the first line, for cmake !! */
// The Modification ID; must be obtained from a Master Server Admin ( https://mb.srb2.org/members/?key=ms_admin ). // The Modification ID; must be obtained from a Master Server Admin ( https://mb.srb2.org/members/?key=ms_admin ).
// DO NOT try to set this otherwise, or your modification will be unplayable through the Master Server. // DO NOT try to set this otherwise, or your modification will be unplayable through the Master Server.
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
// it's only for detection of the version the player is using so the MS can alert them of an update. // it's only for detection of the version the player is using so the MS can alert them of an update.
// Only set it higher, not lower, obviously. // Only set it higher, not lower, obviously.
// Note that we use this to help keep internal testing in check; this is why v2.2.0 is not version "1". // Note that we use this to help keep internal testing in check; this is why v2.2.0 is not version "1".
#define MODVERSION 55 #define MODVERSION 57
// Define this as a prerelease version suffix (pre#, RC#) // Define this as a prerelease version suffix (pre#, RC#)
#define BETAVERSION "nightly" #define BETAVERSION "nightly"
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
#include "i_video.h" // rendermode #include "i_video.h" // rendermode
#include "md5.h" #include "md5.h"
#include "lua_script.h" #include "lua_script.h"
#include "lua_hook.h"
#ifdef SCANTHINGS #ifdef SCANTHINGS
#include "p_setup.h" // P_ScanThings #include "p_setup.h" // P_ScanThings
#endif #endif
...@@ -98,6 +99,7 @@ typedef struct lumpnum_cache_s ...@@ -98,6 +99,7 @@ typedef struct lumpnum_cache_s
{ {
char lumpname[32]; char lumpname[32];
lumpnum_t lumpnum; lumpnum_t lumpnum;
UINT32 hash;
} lumpnum_cache_t; } lumpnum_cache_t;
static lumpnum_cache_t lumpnumcache[LUMPNUMCACHESIZE]; static lumpnum_cache_t lumpnumcache[LUMPNUMCACHESIZE];
...@@ -306,12 +308,10 @@ static void W_LoadDehackedLumps(UINT16 wadnum, boolean mainfile) ...@@ -306,12 +308,10 @@ static void W_LoadDehackedLumps(UINT16 wadnum, boolean mainfile)
* \param resblock resulting MD5 checksum * \param resblock resulting MD5 checksum
* \return 0 if MD5 checksum was made, and is at resblock, 1 if error was found * \return 0 if MD5 checksum was made, and is at resblock, 1 if error was found
*/ */
#ifndef NOMD5
static INT32 W_MakeFileMD5(const char *filename, void *resblock) static INT32 W_MakeFileMD5(const char *filename, void *resblock)
{ {
#ifdef NOMD5
(void)filename;
memset(resblock, 0x00, 16);
#else
FILE *fhandle; FILE *fhandle;
if ((fhandle = fopen(filename, "rb")) != NULL) if ((fhandle = fopen(filename, "rb")) != NULL)
...@@ -328,9 +328,9 @@ static INT32 W_MakeFileMD5(const char *filename, void *resblock) ...@@ -328,9 +328,9 @@ static INT32 W_MakeFileMD5(const char *filename, void *resblock)
fclose(fhandle); fclose(fhandle);
return 0; return 0;
} }
#endif
return 1; return 1;
} }
#endif
// Invalidates the cache of lump numbers. Call this whenever a wad is added. // Invalidates the cache of lump numbers. Call this whenever a wad is added.
static void W_InvalidateLumpnumCache(void) static void W_InvalidateLumpnumCache(void)
...@@ -1010,6 +1010,10 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) ...@@ -1010,6 +1010,10 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup)
break; break;
} }
lua_lumploading++;
LUA_HookVoid(HOOK(AddonLoaded));
lua_lumploading--;
W_InvalidateLumpnumCache(); W_InvalidateLumpnumCache();
return wadfile->numlumps; return wadfile->numlumps;
} }
...@@ -1170,6 +1174,11 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) ...@@ -1170,6 +1174,11 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup)
W_ReadFileShaders(wadfile); W_ReadFileShaders(wadfile);
W_LoadTrnslateLumps(numwadfiles - 1); W_LoadTrnslateLumps(numwadfiles - 1);
W_LoadDehackedLumpsPK3(numwadfiles - 1, mainfile); W_LoadDehackedLumpsPK3(numwadfiles - 1, mainfile);
lua_lumploading++;
LUA_HookVoid(HOOK(AddonLoaded));
lua_lumploading--;
W_InvalidateLumpnumCache(); W_InvalidateLumpnumCache();
return wadfile->numlumps; return wadfile->numlumps;
...@@ -1328,10 +1337,10 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum ...@@ -1328,10 +1337,10 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum
/* SLADE is special and puts a single directory entry. Skip that. */ /* SLADE is special and puts a single directory entry. Skip that. */
if (strlen(lump_p->fullname) == name_length) if (strlen(lump_p->fullname) == name_length)
i++; i++;
break; return i;
} }
} }
return i; return INT16_MAX;
} }
// In a PK3 type of resource file, it looks for the next lumpinfo entry that doesn't share the specified pathfile. // In a PK3 type of resource file, it looks for the next lumpinfo entry that doesn't share the specified pathfile.
...@@ -1475,6 +1484,63 @@ UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump) ...@@ -1475,6 +1484,63 @@ UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump)
return INT16_MAX; return INT16_MAX;
} }
static lumpnum_t CheckLumpInCache(const char *name, boolean longname)
{
if (longname)
{
UINT32 hash = quickncasehash(name, 32);
// Loop backwards so that we check most recent entries first
for (INT32 i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--)
{
if (lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].hash == hash
&& stricmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name) == 0)
{
lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1);
return lumpnumcache[lumpnumcacheindex].lumpnum;
}
}
}
else
{
UINT32 hash = quickncasehash(name, 8);
// Loop backwards so that we check most recent entries first
for (INT32 i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--)
{
if (lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].hash == hash
&& lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname[8] == '\0'
&& strnicmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name, 8) == 0)
{
lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1);
return lumpnumcache[lumpnumcacheindex].lumpnum;
}
}
}
return LUMPERROR;
}
static void AddLumpToCache(lumpnum_t lumpnum, const char *name, boolean longname)
{
if (longname && strlen(name) >= 32)
return;
lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1);
memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', 32);
if (longname)
{
strlcpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 32);
lumpnumcache[lumpnumcacheindex].hash = quickncasehash(name, 32);
}
else
{
strncpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 8);
lumpnumcache[lumpnumcacheindex].hash = quickncasehash(name, 8);
}
lumpnumcache[lumpnumcacheindex].lumpnum = lumpnum;
}
// //
// W_CheckNumForName // W_CheckNumForName
// Returns LUMPERROR if name not found. // Returns LUMPERROR if name not found.
...@@ -1487,17 +1553,10 @@ lumpnum_t W_CheckNumForName(const char *name) ...@@ -1487,17 +1553,10 @@ lumpnum_t W_CheckNumForName(const char *name)
if (!*name) // some doofus gave us an empty string? if (!*name) // some doofus gave us an empty string?
return LUMPERROR; return LUMPERROR;
// Check the lumpnumcache first. Loop backwards so that we check // Check the lumpnumcache first.
// most recent entries first lumpnum_t cachenum = CheckLumpInCache(name, false);
for (i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--) if (cachenum != LUMPERROR)
{ return cachenum;
if (!lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname[8]
&& strncmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name, 8) == 0)
{
lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1);
return lumpnumcache[lumpnumcacheindex].lumpnum;
}
}
// scan wad files backwards so patch lump files take precedence // scan wad files backwards so patch lump files take precedence
for (i = numwadfiles - 1; i >= 0; i--) for (i = numwadfiles - 1; i >= 0; i--)
...@@ -1511,12 +1570,11 @@ lumpnum_t W_CheckNumForName(const char *name) ...@@ -1511,12 +1570,11 @@ lumpnum_t W_CheckNumForName(const char *name)
else else
{ {
// Update the cache. // Update the cache.
lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1); lumpnum_t lumpnum = (i << 16) + check;
memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', 32);
strncpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 8); AddLumpToCache(lumpnum, name, false);
lumpnumcache[lumpnumcacheindex].lumpnum = (i<<16)+check;
return lumpnumcache[lumpnumcacheindex].lumpnum; return lumpnum;
} }
} }
...@@ -1534,16 +1592,10 @@ lumpnum_t W_CheckNumForLongName(const char *name) ...@@ -1534,16 +1592,10 @@ lumpnum_t W_CheckNumForLongName(const char *name)
if (!*name) // some doofus gave us an empty string? if (!*name) // some doofus gave us an empty string?
return LUMPERROR; return LUMPERROR;
// Check the lumpnumcache first. Loop backwards so that we check // Check the lumpnumcache first.
// most recent entries first lumpnum_t cachenum = CheckLumpInCache(name, true);
for (i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--) if (cachenum != LUMPERROR)
{ return cachenum;
if (strcmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name) == 0)
{
lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1);
return lumpnumcache[lumpnumcacheindex].lumpnum;
}
}
// scan wad files backwards so patch lump files take precedence // scan wad files backwards so patch lump files take precedence
for (i = numwadfiles - 1; i >= 0; i--) for (i = numwadfiles - 1; i >= 0; i--)
...@@ -1556,16 +1608,12 @@ lumpnum_t W_CheckNumForLongName(const char *name) ...@@ -1556,16 +1608,12 @@ lumpnum_t W_CheckNumForLongName(const char *name)
if (check == INT16_MAX) return LUMPERROR; if (check == INT16_MAX) return LUMPERROR;
else else
{ {
if (strlen(name) < 32) // Update the cache.
{ lumpnum_t lumpnum = (i << 16) + check;
// Update the cache.
lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1); AddLumpToCache(lumpnum, name, true);
memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', 32);
strlcpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 32);
lumpnumcache[lumpnumcacheindex].lumpnum = (i << 16) + check;
}
return (i << 16) + check; return lumpnum;
} }
} }
...@@ -1647,6 +1695,166 @@ lumpnum_t W_GetNumForLongName(const char *name) ...@@ -1647,6 +1695,166 @@ lumpnum_t W_GetNumForLongName(const char *name)
return i; return i;
} }
//
// Same as W_CheckNumForNamePwad, but handles namespaces.
//
static UINT16 W_CheckNumForPatchNamePwad(const char *name, UINT16 wad, boolean longname)
{
UINT16 i, start = INT16_MAX, end = INT16_MAX;
static char uname[8 + 1] = { 0 };
UINT32 hash = 0;
lumpinfo_t *lump_p;
if (!TestValidLump(wad,0))
return INT16_MAX;
if (!longname)
{
strlcpy(uname, name, sizeof uname);
strupr(uname);
hash = quickncasehash(uname, 8);
}
// SRB2 doesn't have a specific namespace for graphics, which means someone can do weird things
// like placing graphics inside a namespace it doesn't make sense for them to be in, like Sounds/ or SOC/
// So for now, this checks for lumps OUTSIDE of the flats namespace.
// When this situation changes, change the loops below to check for lumps INSIDE the namespaces to look in.
// TODO: cache namespace lump IDs
if (W_FileHasFolders(wadfiles[wad]))
{
start = W_CheckNumForFolderStartPK3("Flats/", wad, 0);
end = W_CheckNumForFolderEndPK3("Flats/", wad, start);
// if the start and end is the same, the folder is empty
if (end <= start)
{
start = INT16_MAX;
end = INT16_MAX;
}
}
else
{
start = W_CheckNumForMarkerStartPwad("F_START", wad, 0);
end = W_CheckNumForNamePwad("F_END", wad, start);
if (end != INT16_MAX)
end++;
}
lump_p = wadfiles[wad]->lumpinfo;
if (start == INT16_MAX)
start = wadfiles[wad]->numlumps;
for (i = 0; i < start; i++, lump_p++)
{
if ((!longname && lump_p->hash == hash && !strncmp(lump_p->name, uname, sizeof(uname) - 1))
|| (longname && stricmp(lump_p->longname, name) == 0))
return i;
}
if (end != INT16_MAX && start < end)
{
lump_p = wadfiles[wad]->lumpinfo + end;
for (i = end; i < wadfiles[wad]->numlumps; i++, lump_p++)
{
if ((!longname && lump_p->hash == hash && !strncmp(lump_p->name, uname, sizeof(uname) - 1))
|| (longname && stricmp(lump_p->longname, name) == 0))
return i;
}
}
// not found.
return INT16_MAX;
}
//
// W_CheckNumForPatchNameInternal
// Gets a lump number out of a patch name. Returns LUMPERROR if name not found.
//
static lumpnum_t W_CheckNumForPatchNameInternal(const char *name, boolean longname)
{
INT32 i;
lumpnum_t check = INT16_MAX;
if (!*name) // some doofus gave us an empty string?
return LUMPERROR;
// Check the lumpnumcache first.
lumpnum_t cachenum = CheckLumpInCache(name, longname);
if (cachenum != LUMPERROR)
return cachenum;
// scan wad files backwards so patch lump files take precedence
for (i = numwadfiles - 1; i >= 0; i--)
{
check = W_CheckNumForPatchNamePwad(name,(UINT16)i,longname);
if (check != INT16_MAX)
break; //found it
}
if (check == INT16_MAX) return LUMPERROR;
else
{
// Update the cache.
lumpnum_t lumpnum = (i << 16) + check;
AddLumpToCache(lumpnum, name, longname);
return lumpnum;
}
}
//
// W_CheckNumForPatchName
// Wrapper for W_CheckNumForPatchNameInternal(name, false). Returns LUMPERROR if name not found.
//
lumpnum_t W_CheckNumForPatchName(const char *name)
{
return W_CheckNumForPatchNameInternal(name, false);
}
//
// Like W_CheckNumForPatchName, but can find entries with long names.
// Wrapper for W_CheckNumForPatchNameInternal(name, true). Returns LUMPERROR if name not found.
//
lumpnum_t W_CheckNumForLongPatchName(const char *name)
{
return W_CheckNumForPatchNameInternal(name, true);
}
//
// W_GetNumForPatchName
//
// Calls W_CheckNumForPatchName, but bombs out if not found.
//
lumpnum_t W_GetNumForPatchName(const char *name)
{
lumpnum_t i;
i = W_CheckNumForPatchName(name);
if (i == LUMPERROR)
I_Error("W_CheckNumForPatchName: %s not found!\n", name);
return i;
}
//
// Like W_GetNumForPatchName, but can find entries with long names
//
lumpnum_t W_GetNumForLongPatchName(const char *name)
{
lumpnum_t i;
i = W_CheckNumForLongPatchName(name);
if (i == LUMPERROR)
I_Error("W_GetNumForLongPatchName: %s not found!\n", name);
return i;
}
// //
// W_CheckNumForNameInBlock // W_CheckNumForNameInBlock
// Checks only in blocks from blockstart lump to blockend lump // Checks only in blocks from blockstart lump to blockend lump
...@@ -2291,10 +2499,10 @@ void *W_CachePatchName(const char *name, INT32 tag) ...@@ -2291,10 +2499,10 @@ void *W_CachePatchName(const char *name, INT32 tag)
{ {
lumpnum_t num; lumpnum_t num;
num = W_CheckNumForName(name); num = W_CheckNumForPatchName(name);
if (num == LUMPERROR) if (num == LUMPERROR)
return W_CachePatchNum(W_GetNumForName("MISSING"), tag); return W_CachePatchNum(W_GetNumForPatchName("MISSING"), tag);
return W_CachePatchNum(num, tag); return W_CachePatchNum(num, tag);
} }
...@@ -2302,10 +2510,10 @@ void *W_CachePatchLongName(const char *name, INT32 tag) ...@@ -2302,10 +2510,10 @@ void *W_CachePatchLongName(const char *name, INT32 tag)
{ {
lumpnum_t num; lumpnum_t num;
num = W_CheckNumForLongName(name); num = W_CheckNumForLongPatchName(name);
if (num == LUMPERROR) if (num == LUMPERROR)
return W_CachePatchNum(W_GetNumForLongName("MISSING"), tag); return W_CachePatchNum(W_GetNumForLongPatchName("MISSING"), tag);
return W_CachePatchNum(num, tag); return W_CachePatchNum(num, tag);
} }
#ifndef NOMD5 #ifndef NOMD5
...@@ -2450,7 +2658,8 @@ static lumpchecklist_t folderblacklist[] = ...@@ -2450,7 +2658,8 @@ static lumpchecklist_t folderblacklist[] =
{ {
{"Lua/", 4}, {"Lua/", 4},
{"SOC/", 4}, {"SOC/", 4},
{"Sprites/", 8}, {"Sprites/", 8},
{"LongSprites/", 12},
{"Textures/", 9}, {"Textures/", 9},
{"Patches/", 8}, {"Patches/", 8},
{"Flats/", 6}, {"Flats/", 6},
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -193,6 +193,12 @@ lumpnum_t W_CheckNumForName(const char *name); ...@@ -193,6 +193,12 @@ lumpnum_t W_CheckNumForName(const char *name);
lumpnum_t W_CheckNumForLongName(const char *name); lumpnum_t W_CheckNumForLongName(const char *name);
lumpnum_t W_GetNumForName(const char *name); // like W_CheckNumForName but I_Error on LUMPERROR lumpnum_t W_GetNumForName(const char *name); // like W_CheckNumForName but I_Error on LUMPERROR
lumpnum_t W_GetNumForLongName(const char *name); lumpnum_t W_GetNumForLongName(const char *name);
lumpnum_t W_CheckNumForPatchName(const char *name);
lumpnum_t W_CheckNumForLongPatchName(const char *name);
lumpnum_t W_GetNumForPatchName(const char *name); // like W_CheckNumForPatchName but I_Error on LUMPERROR
lumpnum_t W_GetNumForLongPatchName(const char *name);
lumpnum_t W_CheckNumForNameInBlock(const char *name, const char *blockstart, const char *blockend); lumpnum_t W_CheckNumForNameInBlock(const char *name, const char *blockstart, const char *blockend);
UINT8 W_LumpExists(const char *name); // Lua uses this. UINT8 W_LumpExists(const char *name); // Lua uses this.
......
...@@ -77,8 +77,8 @@ END ...@@ -77,8 +77,8 @@ END
#include "../doomdef.h" // Needed for version string #include "../doomdef.h" // Needed for version string
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 2,2,14,0 FILEVERSION 2,2,16,0
PRODUCTVERSION 2,2,14,0 PRODUCTVERSION 2,2,16,0
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
...@@ -98,7 +98,7 @@ BEGIN ...@@ -98,7 +98,7 @@ BEGIN
VALUE "FileDescription", "Sonic Robo Blast 2\0" VALUE "FileDescription", "Sonic Robo Blast 2\0"
VALUE "FileVersion", VERSIONSTRING_RC VALUE "FileVersion", VERSIONSTRING_RC
VALUE "InternalName", "srb2\0" VALUE "InternalName", "srb2\0"
VALUE "LegalCopyright", "Copyright 1998-2023 by Sonic Team Junior\0" VALUE "LegalCopyright", "Copyright 1998-2025 by Sonic Team Junior\0"
VALUE "LegalTrademarks", "Sonic the Hedgehog and related characters are trademarks of Sega.\0" VALUE "LegalTrademarks", "Sonic the Hedgehog and related characters are trademarks of Sega.\0"
VALUE "OriginalFilename", "srb2win.exe\0" VALUE "OriginalFilename", "srb2win.exe\0"
VALUE "PrivateBuild", "\0" VALUE "PrivateBuild", "\0"
......
// SONIC ROBO BLAST 2 // SONIC ROBO BLAST 2
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 2004-2023 by Sonic Team Junior. // Copyright (C) 2004-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -109,6 +109,7 @@ typedef union ...@@ -109,6 +109,7 @@ typedef union
UINT16 *color[MAXPLAYERS]; // Winner's color # UINT16 *color[MAXPLAYERS]; // Winner's color #
boolean spectator[MAXPLAYERS]; // Spectator list boolean spectator[MAXPLAYERS]; // Spectator list
UINT8 *character[MAXPLAYERS]; // Winner's character # UINT8 *character[MAXPLAYERS]; // Winner's character #
INT32 ctfteam[MAXPLAYERS]; // Winner's ctfteam #
INT32 num[MAXPLAYERS]; // Winner's player # INT32 num[MAXPLAYERS]; // Winner's player #
char *name[MAXPLAYERS]; // Winner's name char *name[MAXPLAYERS]; // Winner's name
patch_t *result; // RESULT patch_t *result; // RESULT
...@@ -579,9 +580,9 @@ void Y_IntermissionDrawer(void) ...@@ -579,9 +580,9 @@ void Y_IntermissionDrawer(void)
{ {
if (LUA_HudEnabled(hud_intermissiontitletext)) if (LUA_HudEnabled(hud_intermissiontitletext))
{ {
const char *ringtext = "\x82" "get 50 rings, then"; const char *ringtext = "\x82" "50 rings, no shield";
const char *tut1text = "\x82" "press " "\x80" "shield"; const char *tut1text = "\x82" "press " "\x80" "spin";
const char *tut2text = "\x82" "to transform"; const char *tut2text = "\x82" "mid-" "\x80" "jump";
ttheight = 8; ttheight = 8;
V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1); V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1);
ttheight += V_LevelNameHeight(data.spec.passed3) + 2; ttheight += V_LevelNameHeight(data.spec.passed3) + 2;
...@@ -849,7 +850,7 @@ void Y_IntermissionDrawer(void) ...@@ -849,7 +850,7 @@ void Y_IntermissionDrawer(void)
{ {
UINT8 *colormap = R_GetTranslationColormap(*data.match.character[i], *data.match.color[i], GTC_CACHE); UINT8 *colormap = R_GetTranslationColormap(*data.match.character[i], *data.match.color[i], GTC_CACHE);
if (*data.match.color[i] == SKINCOLOR_RED) //red if (data.match.ctfteam[i] == 1) //red
{ {
if (redplayers++ > 9) if (redplayers++ > 9)
continue; continue;
...@@ -857,7 +858,7 @@ void Y_IntermissionDrawer(void) ...@@ -857,7 +858,7 @@ void Y_IntermissionDrawer(void)
y = (redplayers * 16) + 32; y = (redplayers * 16) + 32;
V_DrawCenteredString(x+6, y, 0, va("%d", redplayers)); V_DrawCenteredString(x+6, y, 0, va("%d", redplayers));
} }
else if (*data.match.color[i] == SKINCOLOR_BLUE) //blue else if (data.match.ctfteam[i] == 2) //blue
{ {
if (blueplayers++ > 9) if (blueplayers++ > 9)
continue; continue;
...@@ -1129,7 +1130,7 @@ void Y_Ticker(void) ...@@ -1129,7 +1130,7 @@ void Y_Ticker(void)
else if (mapheaderinfo[gamemap-1]->musintername[0] && S_MusicExists(mapheaderinfo[gamemap-1]->musintername, !midi_disabled, !digital_disabled)) else if (mapheaderinfo[gamemap-1]->musintername[0] && S_MusicExists(mapheaderinfo[gamemap-1]->musintername, !midi_disabled, !digital_disabled))
S_ChangeMusicInternal(mapheaderinfo[gamemap-1]->musintername, false); // don't loop it S_ChangeMusicInternal(mapheaderinfo[gamemap-1]->musintername, false); // don't loop it
else else
S_ChangeMusicInternal("_clear", false); // don't loop it S_ChangeMusicInternal(stagefailed ? "CHFAIL" : "CHPASS", false); // don't loop it
tallydonetic = -1; tallydonetic = -1;
} }
...@@ -1622,6 +1623,7 @@ static void Y_CalculateMatchWinners(void) ...@@ -1622,6 +1623,7 @@ static void Y_CalculateMatchWinners(void)
boolean completed[MAXPLAYERS]; boolean completed[MAXPLAYERS];
// Initialize variables // Initialize variables
memset(data.match.ctfteam, 0, sizeof (data.match.ctfteam));
memset(data.match.scores, 0, sizeof (data.match.scores)); memset(data.match.scores, 0, sizeof (data.match.scores));
memset(data.match.color, 0, sizeof (data.match.color)); memset(data.match.color, 0, sizeof (data.match.color));
memset(data.match.character, 0, sizeof (data.match.character)); memset(data.match.character, 0, sizeof (data.match.character));
...@@ -1642,8 +1644,15 @@ static void Y_CalculateMatchWinners(void) ...@@ -1642,8 +1644,15 @@ static void Y_CalculateMatchWinners(void)
if (players[i].score >= data.match.scores[data.match.numplayers] && completed[i] == false) if (players[i].score >= data.match.scores[data.match.numplayers] && completed[i] == false)
{ {
data.match.ctfteam[data.match.numplayers] = players[i].ctfteam;
data.match.scores[data.match.numplayers] = players[i].score; data.match.scores[data.match.numplayers] = players[i].score;
data.match.color[data.match.numplayers] = &players[i].skincolor; data.match.color[data.match.numplayers] = &players[i].skincolor;
if (data.match.ctfteam[data.match.numplayers] == 1) // red team
data.match.color[data.match.numplayers] = &skincolor_redteam;
if (data.match.ctfteam[data.match.numplayers] == 2) // blue team
data.match.color[data.match.numplayers] = &skincolor_blueteam;
data.match.character[data.match.numplayers] = &players[i].skin; data.match.character[data.match.numplayers] = &players[i].skin;
data.match.name[data.match.numplayers] = player_names[i]; data.match.name[data.match.numplayers] = player_names[i];
data.match.spectator[data.match.numplayers] = players[i].spectator; data.match.spectator[data.match.numplayers] = players[i].spectator;
...@@ -2065,7 +2074,7 @@ static void Y_AwardCoopBonuses(void) ...@@ -2065,7 +2074,7 @@ static void Y_AwardCoopBonuses(void)
(bonuses_list[bonusnum][j])(&players[i], &localbonuses[j]); (bonuses_list[bonusnum][j])(&players[i], &localbonuses[j]);
else else
Y_SetNullBonus(&players[i], &localbonuses[j]); Y_SetNullBonus(&players[i], &localbonuses[j]);
players[i].score += localbonuses[j].points; players[i].score += localbonuses[j].points;
if (players[i].score > MAXSCORE) if (players[i].score > MAXSCORE)
players[i].score = MAXSCORE; players[i].score = MAXSCORE;
......
...@@ -671,6 +671,7 @@ static void Command_Memfree_f(void) ...@@ -671,6 +671,7 @@ static void Command_Memfree_f(void)
CONS_Printf(M_GetText("Cached textures : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRCACHE)>>10)); CONS_Printf(M_GetText("Cached textures : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRCACHE)>>10));
CONS_Printf(M_GetText("Texture colormaps : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPATCHCOLMIPMAP)>>10)); CONS_Printf(M_GetText("Texture colormaps : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPATCHCOLMIPMAP)>>10));
CONS_Printf(M_GetText("Model textures : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRMODELTEXTURE)>>10)); CONS_Printf(M_GetText("Model textures : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRMODELTEXTURE)>>10));
CONS_Printf(M_GetText("Light table textures : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRLIGHTTABLEDATA)>>10));
CONS_Printf(M_GetText("Plane polygons : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPLANE)>>10)); CONS_Printf(M_GetText("Plane polygons : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPLANE)>>10));
CONS_Printf(M_GetText("All GPU textures : %7d KB\n"), HWR_GetTextureUsed()>>10); CONS_Printf(M_GetText("All GPU textures : %7d KB\n"), HWR_GetTextureUsed()>>10);
} }
......
...@@ -55,6 +55,7 @@ enum ...@@ -55,6 +55,7 @@ enum
PU_HWRPATCHINFO = 21, // Hardware GLPatch_t struct for OpenGL texture cache PU_HWRPATCHINFO = 21, // Hardware GLPatch_t struct for OpenGL texture cache
PU_HWRPATCHCOLMIPMAP = 22, // Hardware GLMipmap_t struct colormap variation of patch PU_HWRPATCHCOLMIPMAP = 22, // Hardware GLMipmap_t struct colormap variation of patch
PU_HWRMODELTEXTURE = 23, // Hardware model texture PU_HWRMODELTEXTURE = 23, // Hardware model texture
PU_HWRLIGHTTABLEDATA = 24, // Hardware light table data
PU_HWRCACHE = 48, // static until unlocked PU_HWRCACHE = 48, // static until unlocked
PU_CACHE = 49, // static until unlocked PU_CACHE = 49, // static until unlocked
......
...@@ -55,4 +55,3 @@ else() ...@@ -55,4 +55,3 @@ else()
endif() endif()
FetchContent_MakeAvailable(curl) FetchContent_MakeAvailable(curl)
...@@ -36,4 +36,3 @@ FetchContent_Declare( ...@@ -36,4 +36,3 @@ FetchContent_Declare(
) )
FetchContent_MakeAvailable(SDL2_mixer_ext) FetchContent_MakeAvailable(SDL2_mixer_ext)
...@@ -33,4 +33,3 @@ FetchContent_MakeAvailable(ZLIB) ...@@ -33,4 +33,3 @@ FetchContent_MakeAvailable(ZLIB)
add_library(ZLIB::ZLIB ALIAS zlibstatic) add_library(ZLIB::ZLIB ALIAS zlibstatic)
set(ZLIB_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/zlib" "${zlib_BINARY_DIR}" CACHE PATH "" FORCE) set(ZLIB_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/zlib" "${zlib_BINARY_DIR}" CACHE PATH "" FORCE)
...@@ -2,84 +2,67 @@ ...@@ -2,84 +2,67 @@
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
"name": "srb2", "name": "srb2",
"version": "1.0.0", "version": "1.0.0",
"builtin-baseline": "c823fd3e57035b10d970a96da2796a2db55e5df5", "builtin-baseline": "d5ec528843d29e3a52d745a64b469f810b2cedbf",
"dependencies": [ "dependencies": [
"curl",
{ {
"name": "libgme", "name": "curl",
"platform": "!(windows & mingw) & !native" "platform": "!wasm32"
}, },
"libgme",
{ {
"name": "libopenmpt", "name":"libopenmpt",
"platform": "!(windows & mingw)" "platform": "!wasm32"
}, },
"libpng", "libpng",
"miniupnpc", "miniupnpc",
"sdl2",
{ {
"name": "sdl2-mixer-ext", "name": "sdl2",
"default-features": false,
"features": [ "features": [
{ {
"name": "cmd", "name": "wayland",
"platform": "linux" "platform": "linux"
}, },
{ {
"name": "libflac", "name": "x11",
"platform": "!(windows & mingw & !static)" "platform": "!windows"
}, }
{ ],
"name": "libgme", "platform": "!wasm32",
"platform": "!(windows & mingw) & !native" "version>=": "2.30.6#2"
}, },
{ {
"name": "libmodplug", "name": "sdl2-mixer-ext",
"platform": "!(windows & mingw)" "features": [
},
{
"name": "libopnmidi",
"platform": "!(windows & mingw)"
},
{
"name": "libvorbis",
"platform": "!(windows & mingw & !static)"
},
{
"name": "libxmp",
"platform": "!(windows & mingw)"
},
{ {
"name": "mpg123", "name":"fluidsynth",
"platform": "!(windows & mingw)" "platform": "!static"
}, },
{ {
"name": "nativemidi", "name":"libflac",
"platform": "!(windows & mingw)" "platform": "!wasm32"
}, },
"libgme",
"libmodplug",
{ {
"name": "opusfile", "name":"libvorbis",
"platform": "!(windows & mingw)" "platform": "!wasm32"
}, },
"libxmp",
{ {
"name": "pxtone", "name":"mpg123",
"platform": "!(windows & mingw)" "platform": "!wasm32"
}, },
{ {
"name": "timidity", "name":"opusfile",
"platform": "!(windows & mingw)" "platform": "!wasm32"
}, },
{ "pxtone",
"name": "wavpack", "timidity"
"platform": "!(windows & mingw)" ],
} "platform": "!wasm32",
] "version>=": "2.6.0#0"
}, },
"zlib" "zlib"
],
"overrides": [
{
"name": "sdl2",
"version": "2.28.5",
"port-version": 1
}
] ]
} }