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
  • 21-installer-nodd
  • 2210-pre1
  • 2210-pre2
  • 2210-rc1
  • 2210-rc2
  • 2210-rc3
  • 2211-pre1
  • 2211-pre2
  • 2211-rc1
  • 2212-pre1
  • 2212-pre2
  • 2212-pre3
  • 2212-rc1
  • 2213
  • 2214-pre1
  • 2214-pre2
  • 2214-pre3
  • 2214-pre4
  • 2_2_12
  • 64-gl-log
  • COM_ImmedExecute-lua
  • DJGPP
  • accel-momentum
  • acs
  • action-args
  • alpha-fixes
  • any-resolution
  • appveyor
  • blend-locking
  • blentran
  • blua-unary-not-fix
  • boost-tickrate
  • bustablesoundz
  • cleanup-opengl
  • cleanupmusic
  • clipmidtex
  • cmake-valgrind
  • crawlacommander-sprites
  • custom-map-names
  • custom-teams
  • cutscene-cleanup
  • dd-music-bypass
  • dd-music-fix
  • delfile2
  • deprecate-lua-dedicated-server
  • dpl-2
  • dropshadows-spawning
  • dynabsp
  • emblem-drawing
  • exchndl-xp-fix
  • extra-textures
  • few-kart-lua-changes
  • ffloorclip
  • fix-167
  • fix-cvar-conflicts
  • fix-opengl-parameter-crash
  • fix-opengl-shear-roll
  • flipfuncpointers
  • fof-lightlist-fixes
  • font-FUCK
  • frictionrefactor
  • fuck-macros-1
  • gamepad-luakeydown
  • gamepad-morefixes
  • gamepad_experiments
  • gametype-refactor
  • gametype-refactor-1
  • gametype-refactor-player-spawns
  • ghost-networking
  • gif-splitting
  • grr-lj
  • hitboxviewer
  • hwr-texture-cache-refactor
  • hwrender2
  • improve-439
  • increase-maxconditionsets
  • increase-packet-tics
  • input-display
  • input-display-translucency
  • io
  • joystick-juggling-maz
  • just-in-case
  • keycodes-only
  • ksf-wadfiles
  • ld413-mp-fix
  • levelstruct
  • libpng-version-support
  • linedef-actions
  • lj-test
  • lol-states
  • loopedsounds
  • lower-unpegged-fix
  • lua-change-gametype
  • lua-command-netids
  • lua-gfx-2
  • lua-gfx-sprites
  • lua-local
  • makefile-auto-mingw-gcc
  • makefile-tinkering
  • map-components-signedness-fixes
  • SRB2_release_2.1
  • SRB2_release_2.1.1
  • SRB2_release_2.1.10
  • SRB2_release_2.1.11
  • SRB2_release_2.1.12
  • SRB2_release_2.1.14
  • SRB2_release_2.1.15
  • SRB2_release_2.1.16
  • SRB2_release_2.1.16a
  • SRB2_release_2.1.17
  • SRB2_release_2.1.18
  • SRB2_release_2.1.19
  • SRB2_release_2.1.2
  • SRB2_release_2.1.20
  • SRB2_release_2.1.21
  • SRB2_release_2.1.22
  • SRB2_release_2.1.23
  • SRB2_release_2.1.24
  • SRB2_release_2.1.25
  • SRB2_release_2.1.3
  • SRB2_release_2.1.4
  • SRB2_release_2.1.5
  • SRB2_release_2.1.6
  • SRB2_release_2.1.7
  • SRB2_release_2.1.8
  • SRB2_release_2.1.9
  • SRB2_release_2.2.0
  • SRB2_release_2.2.1
  • SRB2_release_2.2.10
  • SRB2_release_2.2.11
  • SRB2_release_2.2.12
  • SRB2_release_2.2.13
  • SRB2_release_2.2.15
  • SRB2_release_2.2.2
  • SRB2_release_2.2.3
  • SRB2_release_2.2.4
  • SRB2_release_2.2.5
  • SRB2_release_2.2.6
  • SRB2_release_2.2.7
  • SRB2_release_2.2.8
  • SRB2_release_2.2.9
  • td-release-v1.0.0
142 results

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
  • voltybystorm/SRB2
118 results
Select Git revision
  • 21-installer-nodd
  • 2210-pre1
  • 2210-pre2
  • 2210-rc1
  • 2210-rc2
  • 2210-rc3
  • 2211-pre1
  • 2211-pre2
  • 2211-rc1
  • 2212-pre1
  • 2212-pre2
  • 2212-pre3
  • 2212-rc1
  • 2213
  • 2214-pre1
  • 2214-pre2
  • 2214-pre3
  • 2_2_12
  • 64-gl-log
  • COM_ImmedExecute-lua
  • DJGPP
  • SRB_release_french_2.2.13
  • accel-momentum
  • acs
  • action-args
  • alpha-fixes
  • any-resolution
  • appveyor
  • better-player-states
  • blend-locking
  • blentran
  • blua-unary-not-fix
  • boost-tickrate
  • bustablesoundz
  • cleanup-opengl
  • cleanupmusic
  • clipmidtex
  • cmake-valgrind
  • crawlacommander-sprites
  • custom-map-names
  • custom-teams
  • cutscene-cleanup
  • dd-music-bypass
  • dd-music-fix
  • delfile2
  • deprecate-lua-dedicated-server
  • dpl-2
  • dropshadows-spawning
  • dynabsp
  • emblem-drawing
  • exchndl-xp-fix
  • extra-textures
  • few-kart-lua-changes
  • ffloorclip
  • fix-167
  • fix-cvar-conflicts
  • fix-opengl-parameter-crash
  • fix-opengl-shear-roll
  • flipfuncpointers
  • fof-lightlist-fixes
  • font-FUCK
  • frictionrefactor
  • fuck-macros-1
  • gamepad-luakeydown
  • gamepad-morefixes
  • gamepad_experiments
  • gametype-refactor
  • gametype-refactor-1
  • gametype-refactor-player-spawns
  • ghost-networking
  • gif-splitting
  • gitlab-ci
  • grr-lj
  • hitboxviewer
  • hwr-texture-cache-refactor
  • hwrender2
  • improve-439
  • increase-maxconditionsets
  • increase-packet-tics
  • input-display
  • input-display-translucency
  • io
  • joystick-juggling-maz
  • just-in-case
  • keycodes-only
  • ksf-wadfiles
  • ld413-mp-fix
  • levelstruct
  • libpng-version-support
  • linedef-actions
  • lj-test
  • lol-states
  • loopedsounds
  • lower-unpegged-fix
  • lua-change-gametype
  • lua-command-netids
  • lua-debug-library
  • lua-gfx-2
  • lua-gfx-sprites
  • lua-local
  • SRB2_release_2.1
  • SRB2_release_2.1.1
  • SRB2_release_2.1.10
  • SRB2_release_2.1.11
  • SRB2_release_2.1.12
  • SRB2_release_2.1.14
  • SRB2_release_2.1.15
  • SRB2_release_2.1.16
  • SRB2_release_2.1.16a
  • SRB2_release_2.1.17
  • SRB2_release_2.1.18
  • SRB2_release_2.1.19
  • SRB2_release_2.1.2
  • SRB2_release_2.1.20
  • SRB2_release_2.1.21
  • SRB2_release_2.1.22
  • SRB2_release_2.1.23
  • SRB2_release_2.1.24
  • SRB2_release_2.1.25
  • SRB2_release_2.1.3
  • SRB2_release_2.1.4
  • SRB2_release_2.1.5
  • SRB2_release_2.1.6
  • SRB2_release_2.1.7
  • SRB2_release_2.1.8
  • SRB2_release_2.1.9
  • SRB2_release_2.2.0
  • SRB2_release_2.2.1
  • SRB2_release_2.2.10
  • SRB2_release_2.2.11
  • SRB2_release_2.2.12
  • SRB2_release_2.2.13
  • SRB2_release_2.2.2
  • SRB2_release_2.2.3
  • SRB2_release_2.2.4
  • SRB2_release_2.2.5
  • SRB2_release_2.2.6
  • SRB2_release_2.2.7
  • SRB2_release_2.2.8
  • SRB2_release_2.2.9
  • td-release-v1.0.0
141 results
Show changes
...@@ -165,7 +165,7 @@ ...@@ -165,7 +165,7 @@
<MultiProcessorCompilation>true</MultiProcessorCompilation> <MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild> <MinimalRebuild>false</MinimalRebuild>
<LanguageStandard>stdcpp17</LanguageStandard> <LanguageStandard>stdcpp17</LanguageStandard>
<PreprocessorDefinitions>HAVE_CURL;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>HAVE_CURL;HAVE_THREADS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\libs\curl\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>..\..\libs\curl\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
<CustomBuild> <CustomBuild>
...@@ -447,6 +447,7 @@ ...@@ -447,6 +447,7 @@
<ClCompile Include="..\blua\lauxlib.c" /> <ClCompile Include="..\blua\lauxlib.c" />
<ClCompile Include="..\blua\lbaselib.c" /> <ClCompile Include="..\blua\lbaselib.c" />
<ClCompile Include="..\blua\lcode.c" /> <ClCompile Include="..\blua\lcode.c" />
<ClCompile Include="..\blua\ldblib.c" />
<ClCompile Include="..\blua\ldebug.c" /> <ClCompile Include="..\blua\ldebug.c" />
<ClCompile Include="..\blua\ldo.c" /> <ClCompile Include="..\blua\ldo.c" />
<ClCompile Include="..\blua\ldump.c" /> <ClCompile Include="..\blua\ldump.c" />
...@@ -512,6 +513,7 @@ ...@@ -512,6 +513,7 @@
<ClCompile Include="..\lua_hudlib_drawlist.c" /> <ClCompile Include="..\lua_hudlib_drawlist.c" />
<ClCompile Include="..\lua_infolib.c" /> <ClCompile Include="..\lua_infolib.c" />
<ClCompile Include="..\lua_inputlib.c" /> <ClCompile Include="..\lua_inputlib.c" />
<ClCompile Include="..\lua_interceptlib.c" />
<ClCompile Include="..\lua_maplib.c" /> <ClCompile Include="..\lua_maplib.c" />
<ClCompile Include="..\lua_mathlib.c" /> <ClCompile Include="..\lua_mathlib.c" />
<ClCompile Include="..\lua_mobjlib.c" /> <ClCompile Include="..\lua_mobjlib.c" />
...@@ -628,6 +630,7 @@ ...@@ -628,6 +630,7 @@
<ClCompile Include="i_main.c" /> <ClCompile Include="i_main.c" />
<ClCompile Include="i_net.c" /> <ClCompile Include="i_net.c" />
<ClCompile Include="i_system.c" /> <ClCompile Include="i_system.c" />
<ClCompile Include="i_threads.c" />
<ClCompile Include="i_ttf.c" /> <ClCompile Include="i_ttf.c" />
<ClCompile Include="i_video.c" /> <ClCompile Include="i_video.c" />
<ClCompile Include="mixer_sound.c" /> <ClCompile Include="mixer_sound.c" />
......
...@@ -604,6 +604,9 @@ ...@@ -604,6 +604,9 @@
<ClCompile Include="..\blua\ldebug.c"> <ClCompile Include="..\blua\ldebug.c">
<Filter>BLUA</Filter> <Filter>BLUA</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\blua\ldblib.c">
<Filter>BLUA</Filter>
</ClCompile>
<ClCompile Include="..\blua\ldo.c"> <ClCompile Include="..\blua\ldo.c">
<Filter>BLUA</Filter> <Filter>BLUA</Filter>
</ClCompile> </ClCompile>
...@@ -754,9 +757,6 @@ ...@@ -754,9 +757,6 @@
<ClCompile Include="..\hardware\hw_shaders.c"> <ClCompile Include="..\hardware\hw_shaders.c">
<Filter>Hw_Hardware</Filter> <Filter>Hw_Hardware</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\hardware\u_list.c">
<Filter>Hw_Hardware</Filter>
</ClCompile>
<ClCompile Include="..\filesrch.c"> <ClCompile Include="..\filesrch.c">
<Filter>I_Interface</Filter> <Filter>I_Interface</Filter>
</ClCompile> </ClCompile>
...@@ -780,6 +780,9 @@ ...@@ -780,6 +780,9 @@
</ClCompile> </ClCompile>
<ClCompile Include="..\lua_inputlib.c"> <ClCompile Include="..\lua_inputlib.c">
<Filter>LUA</Filter> <Filter>LUA</Filter>
</ClCompile>
<ClCompile Include="..\lua_interceptlib.c">
<Filter>LUA</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\lua_maplib.c"> <ClCompile Include="..\lua_maplib.c">
<Filter>LUA</Filter> <Filter>LUA</Filter>
...@@ -1119,6 +1122,9 @@ ...@@ -1119,6 +1122,9 @@
<ClCompile Include="..\r_bbox.c"> <ClCompile Include="..\r_bbox.c">
<Filter>R_Rend</Filter> <Filter>R_Rend</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="i_threads.c">
<Filter>SDLApp</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Image Include="Srb2SDL.ico"> <Image Include="Srb2SDL.ico">
......
...@@ -112,6 +112,7 @@ void *hwSym(const char *funcName,void *handle) ...@@ -112,6 +112,7 @@ void *hwSym(const char *funcName,void *handle)
GETFUNC(SetPaletteLookup); GETFUNC(SetPaletteLookup);
GETFUNC(CreateLightTable); GETFUNC(CreateLightTable);
GETFUNC(UpdateLightTable);
GETFUNC(ClearLightTables); GETFUNC(ClearLightTables);
GETFUNC(SetScreenPalette); GETFUNC(SetScreenPalette);
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
// //
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Portions Copyright (C) 1998-2000 by DooM Legacy Team. // Portions Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 2014-2023 by Sonic Team Junior. // Copyright (C) 2014-2025 by Sonic Team Junior.
// //
// This program is free software; you can redistribute it and/or // This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License // modify it under the terms of the GNU General Public License
...@@ -78,10 +78,11 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T); ...@@ -78,10 +78,11 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
#include "SDL_cpuinfo.h" #include "SDL_cpuinfo.h"
#define HAVE_SDLCPUINFO #define HAVE_SDLCPUINFO
#if defined (__unix__) || defined(__APPLE__) || (defined (UNIXCOMMON) && !defined (__HAIKU__)) #if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
#if defined (__linux__) #if defined (__linux__) || defined (__HAIKU__)
#include <sys/vfs.h> #include <sys/statvfs.h>
#else #else
#include <sys/statvfs.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/mount.h> #include <sys/mount.h>
/*For meminfo*/ /*For meminfo*/
...@@ -94,7 +95,7 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T); ...@@ -94,7 +95,7 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
#endif #endif
#endif #endif
#if defined (__linux__) || (defined (UNIXCOMMON) && !defined (__HAIKU__)) #if defined (__linux__) || defined (UNIXCOMMON)
#ifndef NOTERMIOS #ifndef NOTERMIOS
#include <termios.h> #include <termios.h>
#include <sys/ioctl.h> // ioctl #include <sys/ioctl.h> // ioctl
...@@ -109,8 +110,10 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T); ...@@ -109,8 +110,10 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
#if defined (__unix__) || (defined (UNIXCOMMON) && !defined (__APPLE__)) #if defined (__unix__) || (defined (UNIXCOMMON) && !defined (__APPLE__))
#include <errno.h> #include <errno.h>
#include <sys/wait.h> #include <sys/wait.h>
#ifndef __HAIKU__ // haiku's crash dialog is just objectively better
#define NEWSIGNALHANDLER #define NEWSIGNALHANDLER
#endif #endif
#endif
#ifndef NOMUMBLE #ifndef NOMUMBLE
#ifdef __linux__ // need -lrt #ifdef __linux__ // need -lrt
...@@ -271,75 +274,86 @@ SDL_bool framebuffer = SDL_FALSE; ...@@ -271,75 +274,86 @@ SDL_bool framebuffer = SDL_FALSE;
UINT8 keyboard_started = false; UINT8 keyboard_started = false;
#ifdef UNIXBACKTRACE #ifdef UNIXBACKTRACE
#define STDERR_WRITE(string) if (fd != -1) I_OutputMsg("%s", string)
#define CRASHLOG_WRITE(string) if (fd != -1) junk = write(fd, string, strlen(string)) static void bt_write_file(int fd, const char *string) {
#define CRASHLOG_STDERR_WRITE(string) \ ssize_t written = 0;
if (fd != -1)\ ssize_t sourcelen = strlen(string);
junk = write(fd, string, strlen(string));\
I_OutputMsg("%s", string) while (fd != -1 && (written != -1 && errno != EINTR) && written < sourcelen)
written = write(fd, string, sourcelen);
}
static void bt_write_stderr(const char *string) {
bt_write_file(STDERR_FILENO, string);
}
static void bt_write_all(int fd, const char *string) {
bt_write_file(fd, string);
bt_write_file(STDERR_FILENO, string);
}
static void write_backtrace(INT32 signal) static void write_backtrace(INT32 signal)
{ {
int fd = -1; int fd = -1;
#ifndef NOEXECINFO
size_t size;
#endif
time_t rawtime; time_t rawtime;
struct tm timeinfo; struct tm timeinfo;
ssize_t junk;
enum { BT_SIZE = 1024, STR_SIZE = 32 }; enum { BT_SIZE = 1024, STR_SIZE = 32 };
#ifndef NOEXECINFO #ifndef NOEXECINFO
void *array[BT_SIZE]; void *funcptrs[BT_SIZE];
size_t bt_size;
#endif #endif
char timestr[STR_SIZE]; char timestr[STR_SIZE];
const char *error = "An error occurred within SRB2! Send this stack trace to someone who can help!\n"; const char *filename = va("%s" PATHSEP "%s", srb2home, "crash-log.txt");
const char *error2 = "(Or find crash-log.txt in your SRB2 directory.)\n"; // Shown only to stderr.
fd = open(va("%s" PATHSEP "%s", srb2home, "crash-log.txt"), O_CREAT|O_APPEND|O_RDWR, S_IRUSR|S_IWUSR); fd = open(filename, O_CREAT|O_APPEND|O_RDWR, S_IRUSR|S_IWUSR);
if (fd == -1) if (fd == -1) // File handle error
I_OutputMsg("\nWARNING: Couldn't open crash log for writing! Make sure your permissions are correct. Please save the below report!\n"); bt_write_stderr("\nWARNING: Couldn't open crash log for writing! Make sure your permissions are correct. Please save the below report!\n");
// Get the current time as a string. // Get the current time as a string.
time(&rawtime); time(&rawtime);
localtime_r(&rawtime, &timeinfo); localtime_r(&rawtime, &timeinfo);
strftime(timestr, STR_SIZE, "%a, %d %b %Y %T %z", &timeinfo); strftime(timestr, STR_SIZE, "%a, %d %b %Y %T %z", &timeinfo);
CRASHLOG_WRITE("------------------------\n"); // Nice looking seperator bt_write_file(fd, "------------------------\n"); // Nice looking seperator
bt_write_all(fd, "\n"); // Newline to look nice for both outputs.
bt_write_all(fd, "An error occurred within SRB2! Send this stack trace to someone who can help!\n");
CRASHLOG_STDERR_WRITE("\n"); // Newline to look nice for both outputs. if (fd != -1) // If the crash log exists,
CRASHLOG_STDERR_WRITE(error); // "Oops, SRB2 crashed" message bt_write_stderr("(Or find crash-log.txt in your SRB2 directory.)\n"); // tell the user where the crash log is.
STDERR_WRITE(error2); // Tell the user where the crash log is.
// Tell the log when we crashed. // Tell the log when we crashed.
CRASHLOG_WRITE("Time of crash: "); bt_write_file(fd, "Time of crash: ");
CRASHLOG_WRITE(timestr); bt_write_file(fd, timestr);
CRASHLOG_WRITE("\n"); bt_write_file(fd, "\n");
// Give the crash log the cause and a nice 'Backtrace:' thing // Give the crash log the cause and a nice 'Backtrace:' thing
// The signal is given to the user when the parent process sees we crashed. // The signal is given to the user when the parent process sees we crashed.
CRASHLOG_WRITE("Cause: "); bt_write_file(fd, "Cause: ");
CRASHLOG_WRITE(strsignal(signal)); bt_write_file(fd, strsignal(signal));
CRASHLOG_WRITE("\n"); // Newline for the signal name bt_write_file(fd, "\n"); // Newline for the signal name
#ifndef NOEXECINFO #ifdef NOEXECINFO
CRASHLOG_STDERR_WRITE("\nBacktrace:\n"); bt_write_all(fd, "\nNo Backtrace support\n");
#else
bt_write_all(fd, "\nBacktrace:\n");
// Flood the output and log with the backtrace // Flood the output and log with the backtrace
size = backtrace(array, BT_SIZE); bt_size = backtrace(funcptrs, BT_SIZE);
backtrace_symbols_fd(array, size, fd); backtrace_symbols_fd(funcptrs, bt_size, fd);
backtrace_symbols_fd(array, size, STDERR_FILENO); backtrace_symbols_fd(funcptrs, bt_size, STDERR_FILENO);
#endif #endif
CRASHLOG_WRITE("\n"); // Write another newline to the log so it looks nice :) bt_write_file(fd, "\n"); // Write another newline to the log so it looks nice :)
(void)junk; if (fd != -1) {
fsync(fd); // reaaaaally make sure we got that data written.
close(fd); close(fd);
} }
#undef STDERR_WRITE }
#undef CRASHLOG_WRITE
#undef CRASHLOG_STDERR_WRITE
#endif // UNIXBACKTRACE #endif // UNIXBACKTRACE
static void I_ReportSignal(int num, int coredumped) static void I_ReportSignal(int num, int coredumped)
...@@ -466,10 +480,9 @@ typedef struct ...@@ -466,10 +480,9 @@ typedef struct
feild_t tty_con; feild_t tty_con;
// when printing general stuff to stdout stderr (Sys_Printf) // lock to prevent clearing partial lines, since not everything
// we need to disable the tty console stuff // printed ends on a newline.
// this increments so we can recursively disable static boolean ttycon_ateol = true;
static INT32 ttycon_hide = 0;
// some key codes that the terminal may be using // some key codes that the terminal may be using
// TTimo NOTE: I'm not sure how relevant this is // TTimo NOTE: I'm not sure how relevant this is
static INT32 tty_erase; static INT32 tty_erase;
...@@ -497,63 +510,31 @@ static inline void tty_FlushIn(void) ...@@ -497,63 +510,31 @@ static inline void tty_FlushIn(void)
// TTimo NOTE: it seems on some terminals just sending '\b' is not enough // TTimo NOTE: it seems on some terminals just sending '\b' is not enough
// so for now, in any case we send "\b \b" .. yeah well .. // so for now, in any case we send "\b \b" .. yeah well ..
// (there may be a way to find out if '\b' alone would work though) // (there may be a way to find out if '\b' alone would work though)
// Hanicef NOTE: using \b this way is unreliable because of terminal state,
// it's better to use \r to reset the cursor to the beginning of the
// line and clear from there.
static void tty_Back(void) static void tty_Back(void)
{ {
char key; write(STDOUT_FILENO, "\r", 1);
ssize_t d;
key = '\b';
d = write(STDOUT_FILENO, &key, 1);
key = ' ';
d = write(STDOUT_FILENO, &key, 1);
key = '\b';
d = write(STDOUT_FILENO, &key, 1);
(void)d;
}
static void tty_Clear(void)
{
size_t i;
if (tty_con.cursor>0) if (tty_con.cursor>0)
{ {
for (i=0; i<tty_con.cursor; i++) write(STDOUT_FILENO, tty_con.buffer, tty_con.cursor);
{
tty_Back();
}
} }
write(STDOUT_FILENO, " \b", 2);
}
// clear the display of the line currently edited
// bring cursor back to beginning of line
static inline void tty_Hide(void)
{
//I_Assert(consolevent);
if (ttycon_hide)
{
ttycon_hide++;
return;
}
tty_Clear();
ttycon_hide++;
} }
// show the current line static void tty_Clear(void)
// FIXME TTimo need to position the cursor if needed??
static inline void tty_Show(void)
{ {
size_t i; size_t i;
ssize_t d; write(STDOUT_FILENO, "\r", 1);
//I_Assert(consolevent); if (tty_con.cursor>0)
I_Assert(ttycon_hide>0);
ttycon_hide--;
if (ttycon_hide == 0 && tty_con.cursor)
{ {
for (i=0; i<tty_con.cursor; i++) for (i=0; i<tty_con.cursor; i++)
{ {
d = write(STDOUT_FILENO, tty_con.buffer+i, 1); write(STDOUT_FILENO, " ", 1);
} }
write(STDOUT_FILENO, "\r", 1);
} }
(void)d;
} }
// never exit without calling this, or your terminal will be left in a pretty bad state // never exit without calling this, or your terminal will be left in a pretty bad state
...@@ -661,6 +642,11 @@ void I_GetConsoleEvents(void) ...@@ -661,6 +642,11 @@ void I_GetConsoleEvents(void)
tty_con.cursor = 0; tty_con.cursor = 0;
ev.key = KEY_ENTER; ev.key = KEY_ENTER;
} }
else if (key == 0x4) // ^D, aka EOF
{
// shut down, most unix programs behave this way
I_Quit();
}
else continue; else continue;
} }
else if (tty_con.cursor < sizeof (tty_con.buffer)) else if (tty_con.cursor < sizeof (tty_con.buffer))
...@@ -868,9 +854,16 @@ static void I_RegisterChildSignals(void) ...@@ -868,9 +854,16 @@ static void I_RegisterChildSignals(void)
void I_OutputMsg(const char *fmt, ...) void I_OutputMsg(const char *fmt, ...)
{ {
size_t len; size_t len;
char txt[8192]; char *txt;
va_list argptr; va_list argptr;
va_start(argptr,fmt);
len = vsnprintf(NULL, 0, fmt, argptr);
va_end(argptr);
if (len == 0)
return;
txt = malloc(len+1);
va_start(argptr,fmt); va_start(argptr,fmt);
vsprintf(txt, fmt, argptr); vsprintf(txt, fmt, argptr);
va_end(argptr); va_end(argptr);
...@@ -904,7 +897,10 @@ void I_OutputMsg(const char *fmt, ...) ...@@ -904,7 +897,10 @@ void I_OutputMsg(const char *fmt, ...)
DWORD bytesWritten; DWORD bytesWritten;
if (co == INVALID_HANDLE_VALUE) if (co == INVALID_HANDLE_VALUE)
{
free(txt);
return; return;
}
if (GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &bytesWritten)) if (GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &bytesWritten))
{ {
...@@ -920,11 +916,16 @@ void I_OutputMsg(const char *fmt, ...) ...@@ -920,11 +916,16 @@ void I_OutputMsg(const char *fmt, ...)
if (oldLength > 0) if (oldLength > 0)
{ {
LPVOID blank = malloc(oldLength); LPVOID blank = malloc(oldLength);
if (!blank) return; if (!blank)
{
free(txt);
return;
}
memset(blank, ' ', oldLength); // Blank out. memset(blank, ' ', oldLength); // Blank out.
oldLines = malloc(oldLength*sizeof(TCHAR)); oldLines = malloc(oldLength*sizeof(TCHAR));
if (!oldLines) if (!oldLines)
{ {
free(txt);
free(blank); free(blank);
return; return;
} }
...@@ -959,18 +960,20 @@ void I_OutputMsg(const char *fmt, ...) ...@@ -959,18 +960,20 @@ void I_OutputMsg(const char *fmt, ...)
} }
#else #else
#ifdef HAVE_TERMIOS #ifdef HAVE_TERMIOS
if (consolevent) if (consolevent && ttycon_ateol)
{ {
tty_Hide(); tty_Clear();
ttycon_ateol = false;
} }
#endif #endif
if (!framebuffer) if (!framebuffer)
fprintf(stderr, "%s", txt); fprintf(stderr, "%s", txt);
#ifdef HAVE_TERMIOS #ifdef HAVE_TERMIOS
if (consolevent) if (consolevent && txt[len-1] == '\n')
{ {
tty_Show(); write(STDOUT_FILENO, tty_con.buffer, tty_con.cursor);
ttycon_ateol = true;
} }
#endif #endif
...@@ -979,6 +982,7 @@ void I_OutputMsg(const char *fmt, ...) ...@@ -979,6 +982,7 @@ void I_OutputMsg(const char *fmt, ...)
fflush(stderr); fflush(stderr);
#endif #endif
free(txt);
} }
// //
...@@ -2296,8 +2300,13 @@ void I_Sleep(UINT32 ms) ...@@ -2296,8 +2300,13 @@ void I_Sleep(UINT32 ms)
void I_SleepDuration(precise_t duration) void I_SleepDuration(precise_t duration)
{ {
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__linux__) || defined(__FreeBSD__) || defined(__HAIKU__)
UINT64 precision = I_GetPrecisePrecision(); UINT64 precision = I_GetPrecisePrecision();
precise_t dest = I_GetPreciseTime() + duration;
precise_t slack = (precision / 5000); // 0.2 ms slack
if (duration > slack)
{
duration -= slack;
struct timespec ts = { struct timespec ts = {
.tv_sec = duration / precision, .tv_sec = duration / precision,
.tv_nsec = duration * 1000000000 / precision % 1000000000, .tv_nsec = duration * 1000000000 / precision % 1000000000,
...@@ -2305,6 +2314,10 @@ void I_SleepDuration(precise_t duration) ...@@ -2305,6 +2314,10 @@ void I_SleepDuration(precise_t duration)
int status; int status;
do status = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &ts); do status = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &ts);
while (status == EINTR); while (status == EINTR);
}
// busy-wait the rest
while (((INT64)dest - (INT64)I_GetPreciseTime()) > 0);
#elif defined (MIN_SLEEP_DURATION_MS) #elif defined (MIN_SLEEP_DURATION_MS)
UINT64 precision = I_GetPrecisePrecision(); UINT64 precision = I_GetPrecisePrecision();
INT32 sleepvalue = cv_sleep.value; INT32 sleepvalue = cv_sleep.value;
...@@ -2743,18 +2756,13 @@ void I_ShutdownSystem(void) ...@@ -2743,18 +2756,13 @@ void I_ShutdownSystem(void)
void I_GetDiskFreeSpace(INT64 *freespace) void I_GetDiskFreeSpace(INT64 *freespace)
{ {
#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) #if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
#if defined (SOLARIS) || defined (__HAIKU__) struct statvfs stfs;
*freespace = INT32_MAX; if (statvfs(srb2home, &stfs) == -1)
return;
#else // Both Linux and BSD have this, apparently.
struct statfs stfs;
if (statfs(srb2home, &stfs) == -1)
{ {
*freespace = INT32_MAX; *freespace = INT32_MAX;
return; return;
} }
*freespace = stfs.f_bavail * stfs.f_bsize; *freespace = stfs.f_bavail * stfs.f_bsize;
#endif
#elif defined (_WIN32) #elif defined (_WIN32)
static p_GetDiskFreeSpaceExA pfnGetDiskFreeSpaceEx = NULL; static p_GetDiskFreeSpaceExA pfnGetDiskFreeSpaceEx = NULL;
static boolean testwin95 = false; static boolean testwin95 = false;
...@@ -2976,7 +2984,7 @@ static void pathonly(char *s) ...@@ -2976,7 +2984,7 @@ static void pathonly(char *s)
*/ */
static const char *searchWad(const char *searchDir) static const char *searchWad(const char *searchDir)
{ {
static char tempsw[256] = ""; static char tempsw[MAX_WADPATH] = "";
filestatus_t fstemp; filestatus_t fstemp;
strcpy(tempsw, WADKEYWORD1); strcpy(tempsw, WADKEYWORD1);
...@@ -2992,8 +3000,8 @@ static const char *searchWad(const char *searchDir) ...@@ -2992,8 +3000,8 @@ static const char *searchWad(const char *searchDir)
#define CHECKWADPATH(ret) \ #define CHECKWADPATH(ret) \
do { \ do { \
I_OutputMsg(",%s", returnWadPath); \ I_OutputMsg(",%s", ret); \
if (isWadPathOk(returnWadPath)) \ if (isWadPathOk(ret)) \
return ret; \ return ret; \
} while (0) } while (0)
...@@ -3022,7 +3030,9 @@ static const char *locateWad(void) ...@@ -3022,7 +3030,9 @@ static const char *locateWad(void)
#ifndef NOCWD #ifndef NOCWD
// examine current dir // examine current dir
strcpy(returnWadPath, "."); strcpy(returnWadPath, ".");
CHECKWADPATH(NULL); I_OutputMsg(",%s", returnWadPath);
if (isWadPathOk(returnWadPath))
return NULL;
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
...@@ -3039,9 +3049,16 @@ static const char *locateWad(void) ...@@ -3039,9 +3049,16 @@ static const char *locateWad(void)
#ifndef NOHOME #ifndef NOHOME
// find in $HOME // find in $HOME
I_OutputMsg(",HOME"); I_OutputMsg(",HOME/" DEFAULTDIR);
if ((envstr = I_GetEnv("HOME")) != NULL) if ((envstr = I_GetEnv("HOME")) != NULL)
SEARCHWAD(envstr); {
char *tmp = malloc(strlen(envstr) + 1 + sizeof(DEFAULTDIR));
strcpy(tmp, envstr);
strcat(tmp, "/");
strcat(tmp, DEFAULTDIR);
CHECKWADPATH(tmp);
free(tmp);
}
#endif #endif
// search paths // search paths
...@@ -3067,8 +3084,10 @@ const char *I_LocateWad(void) ...@@ -3067,8 +3084,10 @@ const char *I_LocateWad(void)
{ {
// change to the directory where we found srb2.pk3 // change to the directory where we found srb2.pk3
#if defined (_WIN32) #if defined (_WIN32)
waddir = _fullpath(NULL, waddir, MAX_PATH);
SetCurrentDirectoryA(waddir); SetCurrentDirectoryA(waddir);
#else #else
waddir = realpath(waddir, NULL);
if (chdir(waddir) == -1) if (chdir(waddir) == -1)
I_OutputMsg("Couldn't change working directory\n"); I_OutputMsg("Couldn't change working directory\n");
#endif #endif
...@@ -3275,4 +3294,18 @@ const char *I_GetSysName(void) ...@@ -3275,4 +3294,18 @@ const char *I_GetSysName(void)
return SDL_GetPlatform(); return SDL_GetPlatform();
} }
void I_SetTextInputMode(boolean active)
{
if (active)
SDL_StartTextInput();
else
SDL_StopTextInput();
}
boolean I_GetTextInputMode(void)
{
return SDL_IsTextInputActive();
}
#endif #endif
...@@ -87,7 +87,7 @@ ...@@ -87,7 +87,7 @@
#endif #endif
// maximum number of windowed modes (see windowedModes[][]) // maximum number of windowed modes (see windowedModes[][])
#define MAXWINMODES (18) #define MAXWINMODES (21)
/** \brief /** \brief
*/ */
...@@ -157,7 +157,9 @@ static INT32 windowedModes[MAXWINMODES][2] = ...@@ -157,7 +157,9 @@ static INT32 windowedModes[MAXWINMODES][2] =
{1920,1080}, // 1.66 {1920,1080}, // 1.66
{1680,1050}, // 1.60,5.25 {1680,1050}, // 1.60,5.25
{1600,1200}, // 1.33 {1600,1200}, // 1.33
{1600,1000}, // 1.60,5.00
{1600, 900}, // 1.66 {1600, 900}, // 1.66
{1536, 864}, // 1.66,4.80
{1366, 768}, // 1.66 {1366, 768}, // 1.66
{1440, 900}, // 1.60,4.50 {1440, 900}, // 1.60,4.50
{1280,1024}, // 1.33? {1280,1024}, // 1.33?
...@@ -166,6 +168,7 @@ static INT32 windowedModes[MAXWINMODES][2] = ...@@ -166,6 +168,7 @@ static INT32 windowedModes[MAXWINMODES][2] =
{1280, 720}, // 1.66 {1280, 720}, // 1.66
{1152, 864}, // 1.33,3.60 {1152, 864}, // 1.33,3.60
{1024, 768}, // 1.33,3.20 {1024, 768}, // 1.33,3.20
{ 960, 600}, // 1.60,3.00
{ 800, 600}, // 1.33,2.50 { 800, 600}, // 1.33,2.50
{ 640, 480}, // 1.33,2.00 { 640, 480}, // 1.33,2.00
{ 640, 400}, // 1.60,2.00 { 640, 400}, // 1.60,2.00
...@@ -382,6 +385,8 @@ static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code) ...@@ -382,6 +385,8 @@ static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code)
static boolean ShouldIgnoreMouse(void) static boolean ShouldIgnoreMouse(void)
{ {
if (cv_alwaysgrabmouse.value)
return false;
if (menuactive) if (menuactive)
return !M_MouseNeeded(); return !M_MouseNeeded();
if (paused || con_destlines || chat_on) if (paused || con_destlines || chat_on)
...@@ -1704,7 +1709,7 @@ static void Impl_VideoSetupBuffer(void) ...@@ -1704,7 +1709,7 @@ static void Impl_VideoSetupBuffer(void)
vid.direct = NULL; vid.direct = NULL;
if (vid.buffer) if (vid.buffer)
free(vid.buffer); free(vid.buffer);
vid.buffer = calloc(vid.rowbytes*vid.height, NUMSCREENS); vid.buffer = calloc(NUMSCREENS, vid.rowbytes*vid.height);
if (!vid.buffer) if (!vid.buffer)
{ {
I_Error("%s", M_GetText("Not enough memory for video buffer\n")); I_Error("%s", M_GetText("Not enough memory for video buffer\n"));
...@@ -1852,6 +1857,9 @@ void I_StartupGraphics(void) ...@@ -1852,6 +1857,9 @@ void I_StartupGraphics(void)
if (mousegrabok && !disable_mouse) if (mousegrabok && !disable_mouse)
SDLdoGrabMouse(); SDLdoGrabMouse();
// disable text input right off the bat, since we don't need it at the start.
I_SetTextInputMode(textinputmodeenabledbylua);
graphics_started = true; graphics_started = true;
} }
...@@ -1899,6 +1907,7 @@ void VID_StartupOpenGL(void) ...@@ -1899,6 +1907,7 @@ void VID_StartupOpenGL(void)
HWD.pfnSetPaletteLookup = hwSym("SetPaletteLookup",NULL); HWD.pfnSetPaletteLookup = hwSym("SetPaletteLookup",NULL);
HWD.pfnCreateLightTable = hwSym("CreateLightTable",NULL); HWD.pfnCreateLightTable = hwSym("CreateLightTable",NULL);
HWD.pfnUpdateLightTable = hwSym("UpdateLightTable",NULL);
HWD.pfnClearLightTables = hwSym("ClearLightTables",NULL); HWD.pfnClearLightTables = hwSym("ClearLightTables",NULL);
HWD.pfnSetScreenPalette = hwSym("SetScreenPalette",NULL); HWD.pfnSetScreenPalette = hwSym("SetScreenPalette",NULL);
...@@ -1943,8 +1952,6 @@ void I_ShutdownGraphics(void) ...@@ -1943,8 +1952,6 @@ void I_ShutdownGraphics(void)
I_OutputMsg("shut down\n"); I_OutputMsg("shut down\n");
#ifdef HWRENDER #ifdef HWRENDER
if (GLUhandle)
hwClose(GLUhandle);
if (sdlglcontext) if (sdlglcontext)
{ {
SDL_GL_DeleteContext(sdlglcontext); SDL_GL_DeleteContext(sdlglcontext);
......
...@@ -135,7 +135,7 @@ static void Midiplayer_Onchange(void) ...@@ -135,7 +135,7 @@ static void Midiplayer_Onchange(void)
if (Mix_GetMidiPlayer() != cv_midiplayer.value) if (Mix_GetMidiPlayer() != cv_midiplayer.value)
{ {
if (Mix_SetMidiPlayer(cv_midiplayer.value)) // <> 0 means error if (Mix_SetMidiPlayer(cv_midiplayer.value)) // <> 0 means error
CONS_Alert(CONS_ERROR, "Midi player error: %s", Mix_GetError()); CONS_Alert(CONS_ERROR, "Midi player error: %s\n", Mix_GetError());
else else
restart = true; restart = true;
} }
...@@ -143,7 +143,7 @@ static void Midiplayer_Onchange(void) ...@@ -143,7 +143,7 @@ static void Midiplayer_Onchange(void)
if (!Mix_GetSoundFonts() || stricmp(Mix_GetSoundFonts(), cv_midisoundfontpath.string)) if (!Mix_GetSoundFonts() || stricmp(Mix_GetSoundFonts(), cv_midisoundfontpath.string))
{ {
if (!Mix_SetSoundFonts(cv_midisoundfontpath.string)) // == 0 means error if (!Mix_SetSoundFonts(cv_midisoundfontpath.string)) // == 0 means error
CONS_Alert(CONS_ERROR, "Sound font error: %s", Mix_GetError()); CONS_Alert(CONS_ERROR, "Sound font error: %s\n", Mix_GetError());
else else
restart = true; restart = true;
} }
...@@ -190,7 +190,7 @@ static void MidiSoundfontPath_Onchange(void) ...@@ -190,7 +190,7 @@ static void MidiSoundfontPath_Onchange(void)
if (proceed) if (proceed)
{ {
if (!Mix_SetSoundFonts(cv_midisoundfontpath.string)) if (!Mix_SetSoundFonts(cv_midisoundfontpath.string))
CONS_Alert(CONS_ERROR, "Sound font error: %s", Mix_GetError()); CONS_Alert(CONS_ERROR, "Sound font error: %s\n", Mix_GetError());
else else
S_StartEx(true); S_StartEx(true);
} }
...@@ -199,7 +199,18 @@ static void MidiSoundfontPath_Onchange(void) ...@@ -199,7 +199,18 @@ static void MidiSoundfontPath_Onchange(void)
// make sure that s_sound.c does not already verify these // make sure that s_sound.c does not already verify these
// which happens when: defined(HAVE_MIXERX) && !defined(HAVE_MIXER) // which happens when: defined(HAVE_MIXERX) && !defined(HAVE_MIXER)
static CV_PossibleValue_t midiplayer_cons_t[] = {{MIDI_OPNMIDI, "OPNMIDI"}, {MIDI_Fluidsynth, "Fluidsynth"}, {MIDI_Timidity, "Timidity"}, {MIDI_Native, "Native"}, {0, NULL}}; static CV_PossibleValue_t midiplayer_cons_t[] = {
{MIDI_ADLMIDI, "ADLMIDI"},
{MIDI_OPNMIDI, "OPNMIDI"},
{MIDI_Timidity, "Timidity"},
{MIDI_Fluidsynth, "Fluidsynth"},
#if SDL_MIXER_VERSION_ATLEAST(2,6,0)
{MIDI_EDMIDI, "EDMIDI"},
#endif
{MIDI_Native, "Native"},
{MIDI_ANY, "Any"},
{0, NULL}
};
consvar_t cv_midiplayer = CVAR_INIT ("midiplayer", "OPNMIDI" /*MIDI_OPNMIDI*/, CV_CALL|CV_NOINIT|CV_SAVE, midiplayer_cons_t, Midiplayer_Onchange); consvar_t cv_midiplayer = CVAR_INIT ("midiplayer", "OPNMIDI" /*MIDI_OPNMIDI*/, CV_CALL|CV_NOINIT|CV_SAVE, midiplayer_cons_t, Midiplayer_Onchange);
consvar_t cv_midisoundfontpath = CVAR_INIT ("midisoundfont", "sf2/8bitsf.SF2", CV_CALL|CV_NOINIT|CV_SAVE, NULL, MidiSoundfontPath_Onchange); consvar_t cv_midisoundfontpath = CVAR_INIT ("midisoundfont", "sf2/8bitsf.SF2", CV_CALL|CV_NOINIT|CV_SAVE, NULL, MidiSoundfontPath_Onchange);
consvar_t cv_miditimiditypath = CVAR_INIT ("midisoundbank", "./timidity", CV_SAVE, NULL, NULL); consvar_t cv_miditimiditypath = CVAR_INIT ("midisoundbank", "./timidity", CV_SAVE, NULL, NULL);
......
...@@ -70,18 +70,10 @@ PFNglGetString pglGetString; ...@@ -70,18 +70,10 @@ PFNglGetString pglGetString;
/** \brief SDL video display surface /** \brief SDL video display surface
*/ */
INT32 oglflags = 0; INT32 oglflags = 0;
void *GLUhandle = NULL;
SDL_GLContext sdlglcontext = 0; SDL_GLContext sdlglcontext = 0;
void *GetGLFunc(const char *proc) void *GetGLFunc(const char *proc)
{ {
if (strncmp(proc, "glu", 3) == 0)
{
if (GLUhandle)
return hwSym(proc, GLUhandle);
else
return NULL;
}
return SDL_GL_GetProcAddress(proc); return SDL_GL_GetProcAddress(proc);
} }
...@@ -89,7 +81,6 @@ boolean LoadGL(void) ...@@ -89,7 +81,6 @@ boolean LoadGL(void)
{ {
#ifndef STATIC_OPENGL #ifndef STATIC_OPENGL
const char *OGLLibname = NULL; const char *OGLLibname = NULL;
const char *GLULibname = NULL;
if (M_CheckParm("-OGLlib") && M_IsNextParm()) if (M_CheckParm("-OGLlib") && M_IsNextParm())
OGLLibname = M_GetNextParm(); OGLLibname = M_GetNextParm();
...@@ -102,43 +93,6 @@ boolean LoadGL(void) ...@@ -102,43 +93,6 @@ boolean LoadGL(void)
CONS_Printf("If you know what is the OpenGL library's name, use -OGLlib\n"); CONS_Printf("If you know what is the OpenGL library's name, use -OGLlib\n");
return 0; return 0;
} }
#if 0
GLULibname = "/proc/self/exe";
#elif defined (_WIN32)
GLULibname = "GLU32.DLL";
#elif defined (__MACH__)
GLULibname = "/System/Library/Frameworks/OpenGL.framework/Libraries/libGLU.dylib";
#elif defined (macintos)
GLULibname = "OpenGLLibrary";
#elif defined (__unix__)
GLULibname = "libGLU.so.1";
#elif defined (__HAIKU__)
GLULibname = "libGLU.so";
#else
GLULibname = NULL;
#endif
if (M_CheckParm("-GLUlib") && M_IsNextParm())
GLULibname = M_GetNextParm();
if (GLULibname)
{
GLUhandle = hwOpen(GLULibname);
if (GLUhandle)
return SetupGLfunc();
else
{
CONS_Alert(CONS_ERROR, "Could not load GLU Library: %s\n", GLULibname);
if (!M_CheckParm ("-GLUlib"))
CONS_Alert(CONS_ERROR, "If you know what is the GLU library's name, use -GLUlib\n");
}
}
else
{
CONS_Alert(CONS_ERROR, "Could not load GLU Library\n");
CONS_Alert(CONS_ERROR, "If you know what is the GLU library's name, use -GLUlib\n");
}
#endif #endif
return SetupGLfunc(); return SetupGLfunc();
} }
...@@ -155,6 +109,7 @@ boolean OglSdlSurface(INT32 w, INT32 h) ...@@ -155,6 +109,7 @@ boolean OglSdlSurface(INT32 w, INT32 h)
{ {
INT32 cbpp = cv_scr_depth.value < 16 ? 16 : cv_scr_depth.value; INT32 cbpp = cv_scr_depth.value < 16 ? 16 : cv_scr_depth.value;
static boolean first_init = false; static boolean first_init = false;
static int majorGL = 0, minorGL = 0;
oglflags = 0; oglflags = 0;
...@@ -189,6 +144,12 @@ boolean OglSdlSurface(INT32 w, INT32 h) ...@@ -189,6 +144,12 @@ boolean OglSdlSurface(INT32 w, INT32 h)
else else
maximumAnisotropy = 1; maximumAnisotropy = 1;
if (sscanf((const char*)gl_version, "%d.%d", &majorGL, &minorGL)
&& (!(majorGL == 1 && minorGL <= 3)))
supportMipMap = true;
else
supportMipMap = false;
SetupGLFunc4(); SetupGLFunc4();
glanisotropicmode_cons_t[1].value = maximumAnisotropy; glanisotropicmode_cons_t[1].value = maximumAnisotropy;
......
...@@ -19,8 +19,6 @@ ...@@ -19,8 +19,6 @@
#include "../v_video.h" #include "../v_video.h"
extern void *GLUhandle;
boolean OglSdlSurface(INT32 w, INT32 h); boolean OglSdlSurface(INT32 w, INT32 h);
void OglSdlFinishUpdate(boolean vidwait); void OglSdlFinishUpdate(boolean vidwait);
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2024 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 simplehash.h
/// \brief Macros for handling basic hashmap types
#ifndef __SIMPLEHASH__
#define __SIMPLEHASH__
#include "uthash.h"
typedef struct
{
INT32 k;
INT32 v;
UT_hash_handle hh;
} hashentry_int32_int32_t;
// hashmap<type>[key] = value;
#define SIMPLEHASH_REPLACE_INT(hashmap, type, key, value) \
{ \
type *entry = malloc(sizeof(type)); \
if (!entry) \
I_Error("%s:%d: Out of memory\n", __func__, __LINE__); \
entry->k = (key); \
entry->v = (value); \
\
type *oldentry; \
HASH_REPLACE_INT((hashmap), k, entry, oldentry); \
if (oldentry) \
free(oldentry); \
}
#define SIMPLEHASH_CLEAR(hashmap, type) \
{ \
type *entry, *tmpentry; \
HASH_ITER(hh, (hashmap), entry, tmpentry) \
{ \
HASH_DEL((hashmap), entry); \
free(entry); \
} \
}
// value = hashmap<type>[key] or fallback;
#define SIMPLEHASH_FIND_INT(hashmap, type, key, fallback, value) \
{ \
int tmpkey = (key); \
type *entry; \
HASH_FIND_INT((hashmap), &tmpkey, entry); \
(value) = entry ? entry->v : (int)(fallback); \
}
#endif //__SIMPLEHASH__
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
#define SPEED 5 #define SPEED 5
#define NUM_BLOCKS_X 20 #define NUM_BLOCKS_X 20
#define NUM_BLOCKS_Y 10 #define NUM_BLOCKS_Y 8
#define BLOCK_SIZE 12 #define BLOCK_SIZE 12
#define BORDER_SIZE 12 #define BORDER_SIZE 12
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#define LEFT_X ((BASEVIDWIDTH - MAP_WIDTH) / 2 - BORDER_SIZE) #define LEFT_X ((BASEVIDWIDTH - MAP_WIDTH) / 2 - BORDER_SIZE)
#define RIGHT_X (LEFT_X + MAP_WIDTH + BORDER_SIZE * 2 - 1) #define RIGHT_X (LEFT_X + MAP_WIDTH + BORDER_SIZE * 2 - 1)
#define BOTTOM_Y (BASEVIDHEIGHT - 48) #define BOTTOM_Y (BASEVIDHEIGHT - 76)
#define TOP_Y (BOTTOM_Y - MAP_HEIGHT - BORDER_SIZE * 2 + 1) #define TOP_Y (BOTTOM_Y - MAP_HEIGHT - BORDER_SIZE * 2 + 1)
enum bonustype_s { enum bonustype_s {
...@@ -313,7 +313,7 @@ void Snake_Update(void *opaque) ...@@ -313,7 +313,7 @@ void Snake_Update(void *opaque)
{ {
snake->snakebonus = BONUS_NONE; snake->snakebonus = BONUS_NONE;
snake->snakelength = i; snake->snakelength = i;
S_StartSound(NULL, sfx_adderr); S_StartSoundFromEverywhere(sfx_adderr);
} }
else else
snake->gameover = true; snake->gameover = true;
...@@ -321,7 +321,7 @@ void Snake_Update(void *opaque) ...@@ -321,7 +321,7 @@ void Snake_Update(void *opaque)
if (snake->gameover) if (snake->gameover)
{ {
S_StartSound(NULL, sfx_lose); S_StartSoundFromEverywhere(sfx_lose);
return; return;
} }
...@@ -351,7 +351,7 @@ void Snake_Update(void *opaque) ...@@ -351,7 +351,7 @@ void Snake_Update(void *opaque)
FindFreeSlot(snake, &snake->bonusx, &snake->bonusy, x, y); FindFreeSlot(snake, &snake->bonusx, &snake->bonusy, x, y);
} }
S_StartSound(NULL, sfx_s3k6b); S_StartSoundFromEverywhere(sfx_s3k6b);
} }
if (snake->snakelength > 1 && snake->snakedir[0]) if (snake->snakelength > 1 && snake->snakedir[0])
...@@ -395,7 +395,7 @@ void Snake_Update(void *opaque) ...@@ -395,7 +395,7 @@ void Snake_Update(void *opaque)
// Check collision with bonus // Check collision with bonus
if (snake->bonustype != BONUS_NONE && x == snake->bonusx && y == snake->bonusy) if (snake->bonustype != BONUS_NONE && x == snake->bonusx && y == snake->bonusy)
{ {
S_StartSound(NULL, sfx_ncchip); S_StartSoundFromEverywhere(sfx_ncchip);
switch (snake->bonustype) switch (snake->bonustype)
{ {
...@@ -419,7 +419,7 @@ void Snake_Update(void *opaque) ...@@ -419,7 +419,7 @@ void Snake_Update(void *opaque)
snake->snakedir[i] = snake->snakedir[0]; snake->snakedir[i] = snake->snakedir[0];
} }
S_StartSound(NULL, sfx_bkpoof); S_StartSoundFromEverywhere(sfx_bkpoof);
break; break;
case BONUS_SCISSORS: case BONUS_SCISSORS:
snake->snakebonus = BONUS_SCISSORS; snake->snakebonus = BONUS_SCISSORS;
...@@ -444,13 +444,13 @@ void Snake_Update(void *opaque) ...@@ -444,13 +444,13 @@ void Snake_Update(void *opaque)
snake->snakedir[0] = 0; snake->snakedir[0] = 0;
S_StartSound(NULL, sfx_gravch); S_StartSoundFromEverywhere(sfx_gravch);
break; break;
default: default:
if (snake->snakebonus != BONUS_GHOST) if (snake->snakebonus != BONUS_GHOST)
{ {
snake->gameover = true; snake->gameover = true;
S_StartSound(NULL, sfx_lose); S_StartSoundFromEverywhere(sfx_lose);
} }
} }
...@@ -582,7 +582,7 @@ boolean Snake_JoyGrabber(void *opaque, event_t *ev) ...@@ -582,7 +582,7 @@ boolean Snake_JoyGrabber(void *opaque, event_t *ev)
{ {
snake_t *snake = opaque; snake_t *snake = opaque;
if (ev->type == ev_joystick && ev->key == 0) if (snake != NULL && ev->type == ev_joystick && ev->key == 0)
{ {
snake->joyevents[snake->joyeventcount] = ev; snake->joyevents[snake->joyeventcount] = ev;
snake->joyeventcount++; snake->joyeventcount++;
......
...@@ -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.
...@@ -696,7 +696,7 @@ static void ST_drawRaceNum(INT32 time) ...@@ -696,7 +696,7 @@ static void ST_drawRaceNum(INT32 time)
{ {
height -= (2 - bounce); height -= (2 - bounce);
if (!(P_AutoPause() || paused) && !bounce) if (!(P_AutoPause() || paused) && !bounce)
S_StartSound(0, ((racenum == racego) ? sfx_s3kad : sfx_s3ka7)); S_StartSoundFromEverywhere(((racenum == racego) ? sfx_s3kad : sfx_s3ka7));
} }
V_DrawScaledPatch(((BASEVIDWIDTH - racenum->width)/2), height, V_PERPLAYER, racenum); V_DrawScaledPatch(((BASEVIDWIDTH - racenum->width)/2), height, V_PERPLAYER, racenum);
} }
...@@ -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))
...@@ -1175,8 +1178,6 @@ static void ST_drawInput(void) ...@@ -1175,8 +1178,6 @@ static void ST_drawInput(void)
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,50 +1328,46 @@ void ST_preDrawTitleCard(void) ...@@ -1334,50 +1328,46 @@ 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))
{
// tick
lt_ticker++;
if (lt_ticker >= lt_endtime)
lt_exitticker++;
// scroll to screen (level title)
if (!lt_exitticker) if (!lt_exitticker)
{ {
if (abs(lt_scroll) > FRACUNIT) if (abs(lt_scroll) > FRACUNIT)
lt_scroll -= (lt_scroll>>2); lt_scroll -= (lt_scroll>>2);
else else
lt_scroll = 0; lt_scroll = 0;
if (abs(lt_zigzag) > FRACUNIT)
lt_zigzag -= (lt_zigzag>>2);
else
lt_zigzag = 0;
} }
// scroll away from screen (level title)
else else
{ {
lt_mom -= FRACUNIT*6; lt_mom -= FRACUNIT*6;
if (lt_scroll > -BASEVIDWIDTH * FRACUNIT * 2)
{
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;
}
}
// //
// Draw the title card itself. // Draw the title card itself.
// //
...@@ -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
} }
...@@ -2619,7 +2608,7 @@ static void ST_doHuntIconsAndSound(void) ...@@ -2619,7 +2608,7 @@ static void ST_doHuntIconsAndSound(void)
} }
if (!(P_AutoPause() || paused) && interval > 0 && leveltime && leveltime % interval == 0 && renderisnewtic) if (!(P_AutoPause() || paused) && interval > 0 && leveltime && leveltime % interval == 0 && renderisnewtic)
S_StartSound(NULL, sfx_emfind); S_StartSoundFromEverywhere(sfx_emfind);
} }
static boolean ST_doItemFinderIconsAndSound(void) static boolean ST_doItemFinderIconsAndSound(void)
...@@ -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;
...@@ -2696,29 +2685,19 @@ static boolean ST_doItemFinderIconsAndSound(void) ...@@ -2696,29 +2685,19 @@ static boolean ST_doItemFinderIconsAndSound(void)
} }
if (!(P_AutoPause() || paused) && interval > 0 && leveltime && leveltime % interval == 0 && renderisnewtic) if (!(P_AutoPause() || paused) && interval > 0 && leveltime && leveltime % interval == 0 && renderisnewtic)
S_StartSound(NULL, sfx_emfind); S_StartSoundFromEverywhere(sfx_emfind);
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)))
{ {
...@@ -2766,7 +2745,7 @@ static void ST_overlayDrawer(void) ...@@ -2766,7 +2745,7 @@ static void ST_overlayDrawer(void)
} }
} }
if (i == MAXPLAYERS && deadtimer >= 0) if (i == MAXPLAYERS && deadtimer >= 0 && LUA_HudEnabled(hud_gameover))
{ {
INT32 lvlttlx = min(6*deadtimer, BASEVIDWIDTH/2); INT32 lvlttlx = min(6*deadtimer, BASEVIDWIDTH/2);
UINT32 flags = V_PERPLAYER|(stplyr->spectator ? V_HUDTRANSHALF : V_HUDTRANS); UINT32 flags = V_PERPLAYER|(stplyr->spectator ? V_HUDTRANSHALF : V_HUDTRANS);
...@@ -2817,7 +2796,7 @@ static void ST_overlayDrawer(void) ...@@ -2817,7 +2796,7 @@ static void ST_overlayDrawer(void)
ST_drawRaceHUD(); ST_drawRaceHUD();
// Emerald Hunt Indicators // Emerald Hunt Indicators
if (!ST_doItemFinderIconsAndSound()) if (!ST_doItemFinderIconsAndSound() && LUA_HudEnabled(hud_itemhunt))
ST_doHuntIconsAndSound(); ST_doHuntIconsAndSound();
if(!P_IsLocalPlayer(stplyr)) if(!P_IsLocalPlayer(stplyr))
...@@ -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,8 +2908,23 @@ void ST_Drawer(void) ...@@ -2929,8 +2908,23 @@ void ST_Drawer(void)
} }
} }
// 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; st_translucency = cv_translucenthud.value;
drawstagetitle = false;
}
if (st_overlay) if (st_overlay)
{ {
// No deadview! // No deadview!
......
...@@ -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.
...@@ -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.
...@@ -1290,6 +1290,210 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) ...@@ -1290,6 +1290,210 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
} }
} }
// lua modders best dream
void V_DrawFixedFill(fixed_t x, fixed_t y, fixed_t w, fixed_t h, INT32 c)
{
UINT8 *dest;
const UINT8 *deststop;
UINT32 alphalevel = ((c & V_ALPHAMASK) >> V_ALPHASHIFT);
UINT32 blendmode = ((c & V_BLENDMASK) >> V_BLENDSHIFT);
UINT8 perplayershuffle = 0;
if (rendermode == render_none)
return;
v_translevel = NULL;
if (alphalevel || blendmode)
{
if (alphalevel == 10) // V_HUDTRANSHALF
alphalevel = hudminusalpha[st_translucency];
else if (alphalevel == 11) // V_HUDTRANS
alphalevel = 10 - st_translucency;
else if (alphalevel == 12) // V_HUDTRANSDOUBLE
alphalevel = hudplusalpha[st_translucency];
if (alphalevel >= 10)
return; // invis
if (alphalevel || blendmode)
v_translevel = R_GetBlendTable(blendmode+1, alphalevel);
}
#ifdef HWRENDER
//if (rendermode != render_soft && !con_startup) // Not this again
if (rendermode == render_opengl)
{
HWR_DrawFixedFill(x, y, w, h, c);
return;
}
#endif
if (splitscreen && (c & V_PERPLAYER))
{
fixed_t adjusty = ((c & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)<<(FRACBITS-1);
h >>= 1;
y >>= 1;
#ifdef QUADS
if (splitscreen > 1) // 3 or 4 players
{
fixed_t adjustx = ((c & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)<<(FRACBITS-1);
w >>= 1;
x >>= 1;
if (stplyr == &players[displayplayer])
{
if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 4;
c &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT;
}
else if (stplyr == &players[secondarydisplayplayer])
{
if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 8;
x += adjustx;
c &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT;
}
else if (stplyr == &players[thirddisplayplayer])
{
if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 4;
y += adjusty;
c &= ~V_SNAPTOTOP|V_SNAPTORIGHT;
}
else //if (stplyr == &players[fourthdisplayplayer])
{
if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
perplayershuffle |= 8;
x += adjustx;
y += adjusty;
c &= ~V_SNAPTOTOP|V_SNAPTOLEFT;
}
}
else
#endif
// 2 players
{
if (stplyr == &players[displayplayer])
{
if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 1;
c &= ~V_SNAPTOBOTTOM;
}
else //if (stplyr == &players[secondarydisplayplayer])
{
if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
perplayershuffle |= 2;
y += adjusty;
c &= ~V_SNAPTOTOP;
}
}
}
deststop = screens[0] + vid.rowbytes * vid.height;
if (c & V_NOSCALESTART)
{
x >>= FRACBITS;
y >>= FRACBITS;
deststop += (y*vid.width) + x;
}
else
{
/*
if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT)
{ // Clear the entire screen, from dest to deststop. Yes, this really works.
memset(screens[0], (c&255), vid.width * vid.height * vid.bpp);
return;
}
*/
x *= vid.dup;
y *= vid.dup;
x >>= FRACBITS;
y >>= FRACBITS;
w *= vid.dup;
h *= vid.dup;
w >>= FRACBITS;
h >>= FRACBITS;
// Center it if necessary
if (vid.width != BASEVIDWIDTH * vid.dup)
{
// dup adjustments pretend that screen width is BASEVIDWIDTH * dup,
// so center this imaginary screen
if (c & V_SNAPTORIGHT)
x += (vid.width - (BASEVIDWIDTH * vid.dup));
else if (!(c & V_SNAPTOLEFT))
x += (vid.width - (BASEVIDWIDTH * vid.dup)) / 2;
if (perplayershuffle & 4)
x -= (vid.width - (BASEVIDWIDTH * vid.dup)) / 4;
else if (perplayershuffle & 8)
x += (vid.width - (BASEVIDWIDTH * vid.dup)) / 4;
}
if (vid.height != BASEVIDHEIGHT * vid.dup)
{
// same thing here
if (c & V_SNAPTOBOTTOM)
y += (vid.height - (BASEVIDHEIGHT * vid.dup));
else if (!(c & V_SNAPTOTOP))
y += (vid.height - (BASEVIDHEIGHT * vid.dup)) / 2;
if (perplayershuffle & 1)
y -= (vid.height - (BASEVIDHEIGHT * vid.dup)) / 4;
else if (perplayershuffle & 2)
y += (vid.height - (BASEVIDHEIGHT * vid.dup)) / 4;
}
}
if (x >= vid.width || y >= vid.height)
return; // off the screen
if (x < 0)
{
w += x;
x = 0;
}
if (y < 0)
{
h += y;
y = 0;
}
if (w <= 0 || h <= 0)
return; // zero width/height wouldn't draw anything
if (x + w > vid.width)
w = vid.width - x;
if (y + h > vid.height)
h = vid.height - y;
dest = screens[0] + y*vid.width + x;
c &= 255;
// borrowing this from jimitia's new hud drawing functions rq
if (alphalevel)
{
v_translevel += c<<8;
for (;(--h >= 0) && dest < deststop; dest += vid.width)
{
for (x = 0; x < w; x++)
dest[x] = v_translevel[dest[x]];
}
}
else
{
for (;(--h >= 0) && dest < deststop; dest += vid.width)
memset(dest, c, w * vid.bpp);
}
}
#ifdef HWRENDER #ifdef HWRENDER
// This is now a function since it's otherwise repeated 2 times and honestly looks retarded: // This is now a function since it's otherwise repeated 2 times and honestly looks retarded:
static UINT32 V_GetHWConsBackColor(void) static UINT32 V_GetHWConsBackColor(void)
......
...@@ -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.
...@@ -181,6 +181,7 @@ void V_DrawBlock(INT32 x, INT32 y, INT32 scrn, INT32 width, INT32 height, const ...@@ -181,6 +181,7 @@ void V_DrawBlock(INT32 x, INT32 y, INT32 scrn, INT32 width, INT32 height, const
// fill a box with a single color // fill a box with a single color
void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c); void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c);
void V_DrawFixedFill(fixed_t x, fixed_t y, fixed_t w, fixed_t h, INT32 c);
void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c); void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c);
// fill a box with a flat as a pattern // fill a box with a flat as a pattern
void V_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatnum); void V_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatnum);
......
#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];
...@@ -132,6 +134,8 @@ void W_Shutdown(void) ...@@ -132,6 +134,8 @@ void W_Shutdown(void)
Z_Free(wad->lumpinfo[wad->numlumps].longname); Z_Free(wad->lumpinfo[wad->numlumps].longname);
Z_Free(wad->lumpinfo[wad->numlumps].fullname); Z_Free(wad->lumpinfo[wad->numlumps].fullname);
} }
M_AATreeFree(wad->startfolders);
M_AATreeFree(wad->endfolders);
Z_Free(wad->lumpinfo); Z_Free(wad->lumpinfo);
Z_Free(wad); Z_Free(wad);
...@@ -306,12 +310,10 @@ static void W_LoadDehackedLumps(UINT16 wadnum, boolean mainfile) ...@@ -306,12 +310,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 +330,9 @@ static INT32 W_MakeFileMD5(const char *filename, void *resblock) ...@@ -328,9 +330,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)
...@@ -966,6 +968,8 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) ...@@ -966,6 +968,8 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup)
fseek(handle, 0, SEEK_END); fseek(handle, 0, SEEK_END);
wadfile->filesize = (unsigned)ftell(handle); wadfile->filesize = (unsigned)ftell(handle);
wadfile->type = type; wadfile->type = type;
wadfile->startfolders = M_AATreeAlloc(0);
wadfile->endfolders = M_AATreeAlloc(0);
// already generated, just copy it over // already generated, just copy it over
M_Memcpy(&wadfile->md5sum, &md5sum, 16); M_Memcpy(&wadfile->md5sum, &md5sum, 16);
...@@ -1010,6 +1014,10 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) ...@@ -1010,6 +1014,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;
} }
...@@ -1153,6 +1161,8 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) ...@@ -1153,6 +1161,8 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup)
wadfile->lumpinfo = lumpinfo; wadfile->lumpinfo = lumpinfo;
wadfile->important = important; wadfile->important = important;
wadfile->filesize = 0; wadfile->filesize = 0;
wadfile->startfolders = M_AATreeAlloc(0);
wadfile->endfolders = M_AATreeAlloc(0);
for (i = 0; i < numlumps; i++) for (i = 0; i < numlumps; i++)
wadfile->filesize += lumpinfo[i].disksize; wadfile->filesize += lumpinfo[i].disksize;
...@@ -1170,6 +1180,11 @@ UINT16 W_InitFolder(const char *path, boolean mainfile, boolean startup) ...@@ -1170,6 +1180,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;
...@@ -1314,6 +1329,16 @@ W_CheckNumForMarkerStartPwad (const char *name, UINT16 wad, UINT16 startlump) ...@@ -1314,6 +1329,16 @@ W_CheckNumForMarkerStartPwad (const char *name, UINT16 wad, UINT16 startlump)
return marker; return marker;
} }
static INT32 W_CheckFolderKeys(void* key1, void* key2)
{
return strcmp((char *)key1, (char *)key2);
}
static void W_DeallocFolderKey(void* key)
{
Z_Free(key);
}
// Look for the first lump from a folder. // Look for the first lump from a folder.
UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump) UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlump)
{ {
...@@ -1321,6 +1346,11 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum ...@@ -1321,6 +1346,11 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum
INT32 i; INT32 i;
lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump;
name_length = strlen(name); name_length = strlen(name);
void *val = M_AATreeGet(wadfiles[wad]->startfolders, Z_StrDup(name), W_CheckFolderKeys, W_DeallocFolderKey);
if (val != NULL)
return (uintptr_t)val;
for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++)
{ {
if (strnicmp(name, lump_p->fullname, name_length) == 0) if (strnicmp(name, lump_p->fullname, name_length) == 0)
...@@ -1328,10 +1358,12 @@ UINT16 W_CheckNumForFolderStartPK3(const char *name, UINT16 wad, UINT16 startlum ...@@ -1328,10 +1358,12 @@ 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; M_AATreeSet(wadfiles[wad]->startfolders, Z_StrDup(name), (void *)(uintptr_t)i, W_CheckFolderKeys, W_DeallocFolderKey);
return i;
} }
} }
return i; M_AATreeSet(wadfiles[wad]->startfolders, Z_StrDup(name), (void *)INT16_MAX, W_CheckFolderKeys, W_DeallocFolderKey);
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.
...@@ -1341,11 +1373,18 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump) ...@@ -1341,11 +1373,18 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump)
{ {
INT32 i; INT32 i;
lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump;
size_t name_length = strlen(name);
void *val = M_AATreeGet(wadfiles[wad]->endfolders, Z_StrDup(name), W_CheckFolderKeys, W_DeallocFolderKey);
if (val != NULL)
return (uintptr_t)val;
for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++)
{ {
if (strnicmp(name, lump_p->fullname, strlen(name))) if (strnicmp(name, lump_p->fullname, name_length))
break; break;
} }
M_AATreeSet(wadfiles[wad]->endfolders, Z_StrDup(name), (void *)(uintptr_t)i, W_CheckFolderKeys, W_DeallocFolderKey);
return i; return i;
} }
...@@ -1475,6 +1514,63 @@ UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump) ...@@ -1475,6 +1514,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 +1583,10 @@ lumpnum_t W_CheckNumForName(const char *name) ...@@ -1487,17 +1583,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 +1600,11 @@ lumpnum_t W_CheckNumForName(const char *name) ...@@ -1511,12 +1600,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);
lumpnumcache[lumpnumcacheindex].lumpnum = (i<<16)+check;
return lumpnumcache[lumpnumcacheindex].lumpnum; AddLumpToCache(lumpnum, name, false);
return lumpnum;
} }
} }
...@@ -1534,16 +1622,10 @@ lumpnum_t W_CheckNumForLongName(const char *name) ...@@ -1534,16 +1622,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--)
...@@ -1555,17 +1637,13 @@ lumpnum_t W_CheckNumForLongName(const char *name) ...@@ -1555,17 +1637,13 @@ 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. // Update the cache.
lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1); lumpnum_t lumpnum = (i << 16) + check;
memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', 32);
strlcpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 32); AddLumpToCache(lumpnum, name, true);
lumpnumcache[lumpnumcacheindex].lumpnum = (i << 16) + check;
}
return (i << 16) + check; return lumpnum;
} }
} }
...@@ -1647,6 +1725,193 @@ lumpnum_t W_GetNumForLongName(const char *name) ...@@ -1647,6 +1725,193 @@ lumpnum_t W_GetNumForLongName(const char *name)
return i; return i;
} }
// Checks if a given lump might be a valid patch.
// This will need to read a bit of the file, but it attempts to not load it
// in its entirety.
static boolean W_IsProbablyValidPatch(UINT16 wadnum, UINT16 lumpnum)
{
UINT8 header[MIN_PATCH_LUMP_HEADER_SIZE];
I_StaticAssert(sizeof(header) >= PNG_HEADER_SIZE);
// Check the file's size first
size_t lumplen = W_LumpLengthPwad(wadnum, lumpnum);
// Cannot be a valid Doom patch
if (lumplen < MIN_PATCH_LUMP_SIZE)
return false;
// Check if it's probably a valid PNG
if (lumplen >= PNG_MIN_SIZE)
{
// Read the PNG's header
W_ReadLumpHeaderPwad(wadnum, lumpnum, header, PNG_HEADER_SIZE, 0);
if (Picture_IsLumpPNG(header, lumplen))
{
// Assume it is if the signature matches.
return true;
}
// Otherwise, we read it as a patch
}
// Read the first 12 bytes
W_ReadLumpHeaderPwad(wadnum, lumpnum, header, sizeof(header), 0);
softwarepatch_t patch;
memcpy(&patch, header, sizeof(header));
INT16 width = SHORT(patch.width);
INT16 height = SHORT(patch.height);
// Lump size makes no sense given the width
if (!VALID_PATCH_LUMP_SIZE(lumplen, width))
return false;
// Check the dimensions.
if (width > 0 && height > 0 && width <= MAX_PATCH_DIMENSIONS && height <= MAX_PATCH_DIMENSIONS)
{
// Dimensions seem to make sense... But check at least the first column.
UINT32 ofs = LONG(patch.columnofs[0]);
// Need one byte for an empty column (but there's patches that don't know that!)
if (ofs < FIRST_PATCH_LUMP_COLUMN(width) || (size_t)ofs >= lumplen)
{
return false;
}
return true;
}
// Not valid if this point was reached
return false;
}
//
// Same as W_CheckNumForNamePwad, but handles namespaces.
//
static UINT16 W_CheckNumForPatchNamePwad(const char *name, UINT16 wad, boolean longname)
{
UINT16 i;
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);
}
lump_p = wadfiles[wad]->lumpinfo;
for (i = 0; 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))
{
// Found the patch by name, but needs to check if it is valid.
if (W_IsProbablyValidPatch(wad, i))
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
...@@ -2175,9 +2440,12 @@ static void *W_GetPatchPwad(UINT16 wad, UINT16 lump, INT32 tag) ...@@ -2175,9 +2440,12 @@ static void *W_GetPatchPwad(UINT16 wad, UINT16 lump, INT32 tag)
#endif #endif
} }
dest = Patch_CreateFromDoomPatch(ptr); dest = Patch_CreateFromDoomPatch(ptr, len);
Z_Free(ptr); Z_Free(ptr);
if (dest == NULL)
return NULL;
Z_ChangeTag(dest, tag); Z_ChangeTag(dest, tag);
Z_SetUser(dest, &lumpcache[lump]); Z_SetUser(dest, &lumpcache[lump]);
} }
...@@ -2195,7 +2463,7 @@ void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) ...@@ -2195,7 +2463,7 @@ void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag)
patch_t *patch = W_GetPatchPwad(wad, lump, tag); patch_t *patch = W_GetPatchPwad(wad, lump, tag);
#ifdef HWRENDER #ifdef HWRENDER
if (rendermode == render_opengl) if (patch != NULL && rendermode == render_opengl)
Patch_CreateGL(patch); Patch_CreateGL(patch);
#endif #endif
...@@ -2291,10 +2559,10 @@ void *W_CachePatchName(const char *name, INT32 tag) ...@@ -2291,10 +2559,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 +2570,10 @@ void *W_CachePatchLongName(const char *name, INT32 tag) ...@@ -2302,10 +2570,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
...@@ -2451,6 +2719,7 @@ static lumpchecklist_t folderblacklist[] = ...@@ -2451,6 +2719,7 @@ 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},
......