Compare commits

...

50 commits

Author SHA1 Message Date
Shpoike
247556928f Misc tweaks. 2025-01-26 07:21:39 +00:00
Shpoike
12721c764b Nano seems intent on having mvds with a few more twiddles in them - enable some of that stuff in qtv so as to not be caught out.
also fix a couple of bugs.
2025-01-26 07:21:38 +00:00
Shpoike
f101a82066 Let qc make websocket connections. 2025-01-26 07:21:38 +00:00
Shpoike
7339ddd47a Fix up imgtool a bit regarding palettes.
Fix issues turning rgba images into alpha-tested stuff like conchars.
Viewing a wad2 file will use the embedded palette if present (and different).
--genwad3 now attempts to generate per-mip palettes (and doesn't bug out when viewing them).
Allows converting to .lmp(qpic).
Rejects dupe input files with different extensions in a more deterministic way.
2025-01-26 07:21:38 +00:00
Shpoike
ad5366ae54 Make getinfo responses consistent, whether actual getinfos or the broker equivelent. 2025-01-26 07:21:38 +00:00
Shpoike
e5e7dde336 Fix dlights getting drawn despite being switched off. 2025-01-26 07:21:38 +00:00
Shpoike
8cc2fdd591 Fix OpenAL_GetChannelPos so it handles streaming sources too. 2025-01-26 07:21:38 +00:00
Shpoike
f767d952e3 fteqcc was failing to generate reflection info for nested arrays. 2025-01-26 07:21:38 +00:00
Shpoike
f1b76e4832 Enable multiprogs in menuqc too.
Add gettimed builtin (clone of gettime, but won't screw up precision from long uptimes).
Add 64bit versions of fseek+fsize+ftell, just in case it ever matters.
Tweak sendevent builtin to allow passing blobs (ptr arg, with previous int arg for size). Beware MTU!
2025-01-26 07:21:38 +00:00
Shpoike
64f805980a Tweaks to try to make Paradoks happy. 2025-01-26 07:21:38 +00:00
Shpoike
edda391be5 New qc builtin to allow menuqc+csqc to stream their own audio. 2025-01-26 07:21:38 +00:00
Shpoike
3837beb84e Add 'DECLAMP' to gl's default2d, so mods can display render targets a bit more easily without needing custom glsl.
Fix dumb issues with the recent gltf2 extensions.
Fix r_fog_linear compile issues in the vulkan renderer.
2025-01-26 07:21:38 +00:00
Shpoike
f35c156800 Fix some build option compile errors. Strip a small bit of redundant code. 2025-01-26 07:21:38 +00:00
Shpoike
46636fd08e Demolist wasn't listing .qwd for some reason. 2025-01-26 06:55:31 +00:00
Shpoike
f38f27e388 Add 'auto' keyword, this works around ptr-to-local in a C compatible way. Unlike C, can also be used for globals where it serves to reduce bss in favour of loadtime allocs.
Fix some other C-related qcc bugs.
Add 'extern "QC"' and 'extern "C"', probably doesn't quite tweak enough compiler settings like type keywords, but should otherwise be fine for *extensions.qc.
Avoid using popen on weird systems (for git info).
Add emulation for pointers to char/short, so they can be used with older/limited opcode targets (eg DP - char/short ptr maths is still an issue there though).
2025-01-26 06:55:31 +00:00
64f4e2a10a glsl: unbreak lightmapped surfaces 2024-12-30 02:22:35 -08:00
Shpoike
247f186a68 Fix up some more C compat issues. And some general qcc bugfixes that would have affected qc code too. 2024-12-28 16:30:05 +00:00
Shpoike
0df247d4c1 Add setwatchpoint builtin, for debugging stuff that would otherwise be hard to monitor. 2024-12-28 16:30:05 +00:00
Shpoike
1ea029f789 Fix up some misc issues. 2024-12-28 16:29:58 +00:00
Shpoike
778bbefc8f something complained about alignment. 2024-12-28 16:29:14 +00:00
Shpoike
0c5e912397 Add a couple of extra options for jumping around demos. 2024-12-28 16:29:14 +00:00
Shpoike
59c0c8857a Parse gltf2's KHR_materials_transmission and KHR_materials_volume extensions, so eukara can figure out the glsl for it. 2024-12-28 16:29:14 +00:00
Shpoike
249ff8777b Make apropos command a bit more clickable. 2024-12-28 16:29:14 +00:00
Shpoike
62ff790114 Fix possible loophole. 2024-12-28 16:29:14 +00:00
Shpoike
b1b0f5e654 Small compile fixes. 2024-12-28 16:29:14 +00:00
Shpoike
c9de6f4bff Fix https://github.com/fte-team/fteqw/issues/299 2024-12-28 16:29:14 +00:00
Shpoike
6e35299bf8 Rewrite fork/resume logic to not bug out when the qc used alloca. 2024-12-28 16:28:29 +00:00
Shpoike
affb96a3b3 Add support for char/short/bitfields. Fix up some other stuff for better C compat. 2024-12-28 16:24:00 +00:00
Shpoike
0650610667 Add the fork+sleep builtins to the menuqc and csqc VMs too. 2024-12-28 16:24:00 +00:00
Shpoike
39e921624f Allow mods to provide raw 8bit paletted image data. 2024-12-28 16:24:00 +00:00
Shpoike
0a7f0cd7d4 Undo the damage from 2e51fb74be 2024-12-28 16:24:00 +00:00
Shpoike
dc010d6ec8 fteqw: add memrealloc builtin.
fteqw: make it a bit clearer when there's no tls drivers compiled/loaded.
fteqcc: fix decompiler code to not crash nor misbehave on 64bit cpus.
fteqcc: add -TDP_20241108 to target dp's most recent additions.
fteqcc: add -Fundefwordsize (autoenabled when targetting dp) to tell the compiler to not make any assumptions about runtime pointer types. this skips some optimisations, blocks sizeof(float), casts between pointer and string,  and a few other sizing things, unsafe operations will become errors.
fteqcc: add -FILP32 - changes 'long' datatype to int32_t, which should match common C assumptions around long/size_t/intptr_t
fteqcc: add -Fpointerrelocs (autoenabled with a recent enough fte target). this finally allows pointer globals to be preinitialised with the addresses of other globals.
fteqcc: add -Fomitinternals. this omits the reflection data in the progs roughly equivelent to visibility=hidden om linux. This WILL break saved games, and probably a few other things too, but will greatly reduce stringtable sizes.
fteqcc: improve compat when compiling C code
qclib: fix op_push
2024-12-28 16:24:00 +00:00
Shpoike
7e9d138d5f Try to fix up some q2 protocol/ice quirks.
Add r_imagelist_wad command to show lumps in the current map's wad list.
Hide the gpu utilisation info unless developer. its misleading on account of power profiles.
Make the menu not flash/animate so much when something else has focus.
Add QTV streaming option when browsing servers (assuming servers are configured properly).
Show people's health+armour+weapons on the scoreboard, if we have that information.
"enemyskin solid; enemycolor 0x00ff00" will make enemies full green without needing any external texture files.
Add ENGINE_HAS_ZIP build option, to have the engine look for concated(self-extracter style) zips for use in single-file games.
Small speedup for hl2bsp load times.
ftemaster now supports bad-word filters. Basic, probably easy enough to hack around, but at least we tried, admins can extra words as needed. People should at least realise they're being naughty.
Misc fixes for ftemaster's html generation.
Add support for a couple of quirky hlbsps.
Don't attempt to auto-use setangles_delta when sv_nqplayerphysics is active, to avoid compat quirks with AD.
2024-12-28 16:24:00 +00:00
Shpoike
e82f61256a Add 'setrenderer sv' for linux, without needing to be started from a terminal. 2024-12-28 16:23:59 +00:00
Shpoike
777b4b1fd9 Add _some_ support for the rerelease's waypoints. 2024-12-28 16:23:59 +00:00
Shpoike
9bc9700506 Add json-formatted framegroups files. Allow for ragdolls to be used with hlmdl. Try to be smarter/higher with gpu bone limits. 2024-12-28 16:23:59 +00:00
Shpoike
28a880c56e Misc small fixups/cleanups. 2024-12-12 13:42:05 +00:00
Shpoike
d26b741e4b Update the doom3 map support. Still not enabled on account of severe material issues. 2024-12-12 13:42:05 +00:00
Shpoike
dd4ff8d530 Fix up some -std=C issues. 2024-12-12 13:42:05 +00:00
Shpoike
fe6ef90c44 Better gmqcc compat. 2024-12-12 13:42:05 +00:00
Shpoike
3b554f3742 Fix a couple of issues with the hl2 plugin. 2024-12-12 13:42:04 +00:00
Shpoike
3b140e9cf8 Fix https://github.com/fte-team/fteqw/issues/287 2024-12-12 13:25:56 +00:00
Shpoike
9c32640189 Fix https://github.com/fte-team/fteqw/issues/286 2024-12-12 13:25:56 +00:00
Shpoike
0ac7268cc5 Include other proxies in viewer counts, on account of all the people misusing it... 2024-12-12 13:25:56 +00:00
Shpoike
3df06cd519 qtv: strcpy with overlapping dest/source was resulting in serverinfo corruption with glibc. 2024-12-12 13:25:56 +00:00
Shpoike
ee534acb8c ftemaster Fixups. Should actually work again now. 2024-12-12 13:25:56 +00:00
Shpoike
84438b5f9b vk: Add lit water support to the spir-v. 2024-12-12 13:25:56 +00:00
Shpoike
6f579815d2 Hopefully fix https://github.com/fte-team/fteqw/issues/275 though the specified input is still optimised too much for this usage to be useful. 2024-12-12 13:25:56 +00:00
Shpoike
a1bf9dd60a Fix bug with randomv intrinsic. 2024-12-12 13:25:56 +00:00
Shpoike
f0e57311d6 Fix silly timing bug. 2024-12-12 13:25:55 +00:00
158 changed files with 13404 additions and 7694 deletions

View file

@ -124,7 +124,7 @@ ENDIF()
# libepoll-shim needs to be installed on the BSDs and Mac OSX to get
# some of the server code to compile and work correctly on those platforms - Brad
IF(UNIX AND NOT LINUX AND NOT CYGWIN)
IF(CMAKE_SYSTEM_NAME MATCHES "BSD" OR CMAKE_SYSTEM_NAME MATCHES "Darwin")
INCLUDE(FetchContent)
FetchContent_Declare(
epoll-shim
@ -161,6 +161,7 @@ IF(ZLIB_FOUND)
SET(FTE_LIBS ${FTE_LIBS} ${ZLIB_LIBRARIES})
SET(FTESV_LIBS ${FTESV_LIBS} ${ZLIB_LIBRARIES})
SET(FTEQCC_LIBS ${FTEQCC_LIBS} ${ZLIB_LIBRARIES})
SET(FTEQTV_LIBS ${FTEQTV_LIBS} ${ZLIB_LIBRARIES})
ELSE()
MESSAGE(WARNING "libz library NOT available. compressed pk3, ICE, Q2E, etc etc, yada yada, blah blah will not be available.")
SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_ZLIB)
@ -202,16 +203,18 @@ ELSE()
SET(JPEG_LIBRARIES)
ENDIF()
SET(FTE_DEP_DBUS true CACHE BOOL "Link against libdbus.")
IF(FTE_DEP_DBUS)
FIND_PACKAGE(DBus1)
ENDIF()
IF(DBUS1_FOUND)
INCLUDE_DIRECTORIES( ${DBus1_INCLUDE_DIRS} )
SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};HAVE_DBUS)
SET(FTE_LIBS ${FTE_LIBS} ${DBus1_LIBRARIES})
ELSE()
MESSAGE(WARNING "libdbus-1 library NOT available. Who cares?")
IF(NOT ${WIN32})
SET(FTE_DEP_DBUS true CACHE BOOL "Link against libdbus.")
IF(FTE_DEP_DBUS)
FIND_PACKAGE(DBus1)
ENDIF()
IF(DBUS1_FOUND)
INCLUDE_DIRECTORIES( ${DBus1_INCLUDE_DIRS} )
SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};HAVE_DBUS)
SET(FTE_LIBS ${FTE_LIBS} ${DBus1_LIBRARIES})
ELSE()
MESSAGE(WARNING "libdbus-1 library NOT available. Who cares?")
ENDIF()
ENDIF()
SET(FTE_DEP_PNG true CACHE BOOL "Link against libpng.")
@ -520,10 +523,11 @@ ELSEIF(UNIX AND NOT FTE_USE_SDL) #linux(ish)
ENDIF()
ENDIF()
IF(NOT LINUX AND NOT CYGWIN)
IF(CMAKE_SYSTEM_NAME MATCHES "BSD" OR CMAKE_SYSTEM_NAME MATCHES "Darwin")
FIND_LIBRARY(epoll-shim REQUIRED)
INCLUDE_DIRECTORIES(${EPOLL_INC_DIR})
SET(FTESV_LIBS ${FTESV_LIBS} epoll-shim)
SET(FTESV_DEFINES ${FTESV_DEFINES};HAVE_EPOLL)
ENDIF()
SET(FTESV_DEFINES ${FTESV_DEFINES};MULTITHREAD)
@ -781,6 +785,7 @@ SET(FTE_COMMON_FILES
engine/client/pr_skelobj.c
engine/client/m_download.c
engine/client/net_master.c
engine/client/r_d3.c
#these are here because of hitmodel etc
engine/gl/gl_heightmap.c
@ -1087,10 +1092,11 @@ ELSE()
${FTE_CLIENT_FILES}
${FTE_SERVER_FILES}
)
IF(UNIX AND NOT LINUX AND NOT CYGWIN)
IF(CMAKE_SYSTEM_NAME MATCHES "BSD" OR CMAKE_SYSTEM_NAME MATCHES "Darwin")
FIND_LIBRARY(epoll-shim REQUIRED)
SET(FTE_INCLUDES ${FTE_INCLUDES} "${EPOLL_INC_DIR}")
SET(FTE_LIBS ${FTE_LIBS} epoll-shim)
SET(FTE_DEFINES ${FTE_DEFINES};HAVE_EPOLL)
ENDIF()
SET_TARGET_PROPERTIES(fteqw PROPERTIES COMPILE_DEFINITIONS "${FTE_LIB_DEFINES};${FTE_DEFINES};${FTE_REVISON}")
TARGET_INCLUDE_DIRECTORIES(fteqw PUBLIC ${FTE_INCLUDES})
@ -1191,16 +1197,17 @@ ELSE()
fteqtv/libqtvc/glibc_sucks.c
engine/common/sha1.c
)
SET_TARGET_PROPERTIES(qtv PROPERTIES COMPILE_DEFINITIONS "${FTE_REVISON}")
SET_TARGET_PROPERTIES(qtv PROPERTIES COMPILE_DEFINITIONS "${FTE_REVISON};${FTE_LIB_DEFINES}")
IF(WIN32)
TARGET_LINK_LIBRARIES(qtv ws2_32 winmm ${SYS_LIBS} ${ZLIB_LIBRARIES})
ELSEIF(LINUX)
TARGET_LINK_LIBRARIES(qtv ${SYS_LIBS} ${ZLIB_LIBRARIES})
# Add Epoll-shim for the Unixes here - Brad
ELSE()
TARGET_LINK_LIBRARIES(qtv ws2_32 winmm ${SYS_LIBS} ${FTEQTV_LIBS})
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "BSD" OR CMAKE_SYSTEM_NAME MATCHES "Darwin")
# Add Epoll-shim for the kqueue Unixes here - Brad
FIND_LIBRARY(epoll-shim REQUIRED)
TARGET_INCLUDE_DIRECTORIES(qtv PUBLIC "${EPOLL_INC_DIR}")
TARGET_LINK_LIBRARIES(qtv epoll-shim ${SYS_LIBS} ${ZLIB_LIBRARIES})
TARGET_LINK_LIBRARIES(qtv epoll-shim ${SYS_LIBS} ${FTEQTV_LIBS})
SET_TARGET_PROPERTIES(qtv PROPERTIES COMPILE_DEFINITIONS "${FTE_REVISON};${FTE_LIB_DEFINES};HAVE_EPOLL")
ELSE()
TARGET_LINK_LIBRARIES(qtv ${SYS_LIBS} ${FTEQTV_LIBS})
ENDIF()
SET(INSTALLTARGS ${INSTALLTARGS} qtv)
ENDIF()
@ -1243,14 +1250,28 @@ ELSE()
SET_TARGET_PROPERTIES(httpserver PROPERTIES COMPILE_DEFINITIONS "WEBSERVER;WEBSVONLY;${FTE_REVISON}")
IF(WIN32)
TARGET_LINK_LIBRARIES(httpserver ws2_32)
ELSEIF(UNIX AND NOT LINUX AND NOT CYGWIN)
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "BSD" OR CMAKE_SYSTEM_NAME MATCHES "Darwin")
FIND_LIBRARY(epoll-shim REQUIRED)
TARGET_INCLUDE_DIRECTORIES(httpserver PUBLIC "${EPOLL_INC_DIR}")
TARGET_LINK_LIBRARIES(httpserver epoll-shim)
SET_TARGET_PROPERTIES(httpserver PROPERTIES COMPILE_DEFINITIONS "WEBSERVER;WEBSVONLY;${FTE_REVISON};HAVE_EPOLL")
ENDIF()
#SET(INSTALLTARGS ${INSTALLTARGS} httpserver)
ENDIF()
SET(FTE_TOOL_QCVM false CACHE BOOL "Compile standalone qcvm.")
IF(FTE_TOOL_QCVM)
ADD_EXECUTABLE(qcvm
engine/qclib/test.c
engine/qclib/hash.c
${FTE_QCVM_FILES}
)
SET_TARGET_PROPERTIES(qcvm PROPERTIES COMPILE_DEFINITIONS "${FTE_LIB_DEFINES};${FTE_REVISON}")
TARGET_LINK_LIBRARIES(qcvm ${FTEQCC_LIBS} ${SYS_LIBS})
SET(INSTALLTARGS ${INSTALLTARGS} qcvm)
ENDIF()
SET(FTE_TOOL_QCC true CACHE BOOL "Compile commandline qc compiler.")
IF(FTE_TOOL_QCC)
ADD_EXECUTABLE(fteqcc
@ -1479,6 +1500,7 @@ IF(FTE_PLUG_IRC)
EMBED_PLUGIN_META(irc "IRC Plugin" "Allows you to chat on IRC without tabbing out.")
ENDIF()
IF(ZLIB_FOUND)
#mpq package format plugin (blizzard games)
SET(FTE_PLUG_MPQ false CACHE BOOL "Compile mpq junk.")
IF(FTE_PLUG_MPQ)
@ -1492,6 +1514,7 @@ IF(FTE_PLUG_MPQ)
EMBED_PLUGIN_META(irc "MPQ Archive Plugin" "Adds support for reading .mpq files. Not very useful...")
ENDIF()
ENDIF()
#vpk package format plugin (halflife2)
SET(FTE_PLUG_HL2 true CACHE BOOL "Compile support for hl2 file formats.")
@ -1705,7 +1728,6 @@ IF(FTE_PLUG_CEF)
ENDIF()
ENDIF()
IF(NOT ANDROID) #libresolv issues.
SET(FTE_PLUG_XMPP true CACHE BOOL "Compile xmpp/jabber instant-messenger plugin.")
IF(FTE_PLUG_XMPP)
#XMPP/jabber client plugin
@ -1720,14 +1742,16 @@ IF(FTE_PLUG_XMPP)
plugins/emailnot/md5.c
)
SET_TARGET_PROPERTIES(plug_xmpp PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;${FTE_LIB_DEFINES}")
IF(${WIN32})
ELSEIF(LINUX OR APPLE)
#for SRV lookups, so we actually get the right server from account names/etc.
IF(ANDROID) #libresolv issues.
ELSEIF(${WIN32}) #softlinks a dll
ELSE()
TARGET_LINK_LIBRARIES(plug_xmpp ${SYS_LIBS} resolv)
ENDIF()
EMBED_PLUGIN_META(xmpp "XMPP Plugin" "XMPP/Jabber instant messenger plugin for chatting without tabbing out.")
ENDIF()
ENDIF() #android
INSTALL(TARGETS ${INSTALLTARGS}
RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/${FTE_INSTALL_BINDIR}"

View file

@ -955,7 +955,7 @@ void Cam_SetModAutoTrack(int userid)
return;
}
}
Con_Printf("//at: invalid userid\n");
Con_Printf("//at: invalid userid %i\n", userid);
}
/*static void Cam_TrackCrosshairedPlayer(playerview_t *pv)

View file

@ -76,7 +76,7 @@ void CL_StopPlayback (void)
cls.demoinfile = NULL;
cls.state = ca_disconnected;
cls.demoplayback = DPB_NONE;
cls.demoseeking = false; //just in case
cls.demoseeking = DEMOSEEK_NOT; //just in case
cls.demotrack = -1;
cls.demoeztv_ext = 0;
@ -393,7 +393,7 @@ void CL_ProgressDemoTime(void)
void CL_DemoJump_f(void)
{
float newtime;
char *s = Cmd_Argv(1);
char *s = (!strncmp(Cmd_Argv(0), "demo_jump_", 10))?Cmd_Argv(0)+10:Cmd_Argv(1);
char *colon = strchr(s, ':');
if (!cls.demoplayback)
@ -414,6 +414,17 @@ void CL_DemoJump_f(void)
return; //can't seek live streams...
}
if (!strcmp(s, "intermission") || !strcmp(s, "end"))
{ //seeks until we see an svc_intermission
cls.demoseeking = DEMOSEEK_INTERMISSION;
return;
}
if (!strcmp(s, "mark"))
{ //seeks until we see an svc_stufftext `//demomark`
cls.demoseeking = DEMOSEEK_MARK;
return;
}
if (*s == '+' || *s == '-')
{
if (colon)
@ -457,7 +468,7 @@ void CL_DemoJump_f(void)
//now fastparse it.
cls.demoseektime = newtime;
}
cls.demoseeking = true;
cls.demoseeking = DEMOSEEK_TIME;
}
void CL_DemoNudge_f(void)
@ -610,11 +621,11 @@ qboolean CL_GetDemoMessage (void)
return 0;
}*/
cls.netchan.last_received = realtime;
if (cls.demoseeking)
if (cls.demoseeking == DEMOSEEK_TIME)
{
if (cl.gametime > cls.demoseektime)
{
cls.demoseeking = false;
cls.demoseeking = DEMOSEEK_NOT;
return 0;
}
}
@ -731,11 +742,11 @@ readnext:
// decide if it is time to grab the next message
if (cls.demoseeking)
if (cls.demoseeking != DEMOSEEK_NOT)
{
demtime = demotime; //warp
if (demtime >= cls.demoseektime)
cls.demoseeking = false;
if (cls.demoseeking == DEMOSEEK_TIME && demtime >= cls.demoseektime)
cls.demoseeking = DEMOSEEK_NOT;
}
else if (cls.timedemo)
{
@ -1986,7 +1997,7 @@ void CL_Record_f (void)
n = 0;
s = cl.sound_name[n+1];
while (*s)
while (s && *s)
{
MSG_WriteString (&buf, s);
if (buf.cursize > buf.maxsize/2 && (n&0xff))
@ -2024,7 +2035,7 @@ void CL_Record_f (void)
n = 0;
s = cl.model_name[n+1];
while (*s)
while (s && *s)
{
MSG_WriteString (&buf, s);
if (buf.cursize > buf.maxsize/2 && (n&0xff))
@ -3037,7 +3048,7 @@ fail:
MC_AddCenterPicture(sourcesmenu, 4, 24, "gfx/p_option.lmp");
}
if (init_numplayers == true && init_numviewers == true)
MC_AddConsoleCommand(sourcesmenu, 42, 170, (sourcenum++)*8 + 32, va("%s (p%i, v%i)", srchost, numplayers, numviewers), va("qtvplay %s@%s\n", streamid, qtv->hostname));
MC_AddConsoleCommand(sourcesmenu, 42, 170, (sourcenum++)*8 + 32, va("%s (p%i, v%i)", *srchost?srchost:streamid, numplayers, numviewers), va("qtvplay %s@%s\n", streamid, qtv->hostname));
//else
// FIXME: add error message here
#else
@ -3060,7 +3071,7 @@ fail:
if (streamavailable)
{
if (*streamavailable)
Con_Printf("streaming \"%s\" from qtv\n", streamavailable);
Con_Printf("streaming \"%s\" via \"%s\"\n", streamavailable, qtv->hostname);
else
Con_Printf("qtv connection established to %s\n", qtv->hostname);
CL_PlayDemoStream(qtv->stream, NULL, false, DPB_MVD, BUFFERTIME, iseztv);
@ -3138,6 +3149,7 @@ void CL_QTVPlay_Establish (const char *host, const char *password, const char *c
return;
}
Q_strncpyz(qtv->hostname, host, sizeof(qtv->hostname));
Q_strncpyz(qtv->password, password, sizeof(qtv->password));
if (qtvcl_forceversion1.ival)

View file

@ -854,8 +854,8 @@ qboolean IN_DrawWeaponWheel(int pnum)
}
void IN_WWheelDown (void)
{
#ifdef CSQC_DAT
int pnum = CL_TargettedSplit(false);
#ifdef CSQC_DAT
if (CSQC_ConsoleCommand(pnum, Cmd_Argv(0)))
return;
#endif

View file

@ -736,8 +736,8 @@ static void CL_SendConnectPacket (netadr_t *to)
}
connectinfo.ext.fte1 &= (PEXT_MODELDBL|PEXT_SOUNDDBL|PEXT_SPLITSCREEN);
connectinfo.ext.fte2 = 0;
connectinfo.ext.ez1 = 0;
connectinfo.ext.fte2 &= PEXT2_STUNAWARE;
connectinfo.ext.ez1 &= 0;
}
#endif
else
@ -2632,6 +2632,14 @@ void CL_Disconnect_f (void)
(void)CSQC_UnconnectedInit();
}
void CL_Disconnect2_f (void)
{
char *reason = Cmd_Argv(1);
if (*reason)
Cvar_Set(&cl_disconnectreason, reason);
CL_Disconnect_f();
}
/*
====================
CL_User_f
@ -3786,7 +3794,7 @@ void CL_Changing_f (void)
if (cls.download && cls.download->method <= DL_QWPENDING) // don't change when downloading
return;
cls.demoseeking = false; //don't seek over it
cls.demoseeking = DEMOSEEK_NOT; //don't seek over it
if (*mapname)
SCR_ImageName(mapname);
@ -5553,6 +5561,8 @@ void CL_Status_f(void)
}
Con_Printf("Server address : %s\n", NET_AdrToString(adr, sizeof(adr), &cls.netchan.remote_address)); //not relevent as a limit.
Con_Printf("Server cert fp : %s\n", b64); //not relevent as a limit.
Con_Printf("Network MTU : %u (max %u) %s\n", cls.netchan.mtu_cur, cls.netchan.mtu_max, (cls.netchan.flags&NCF_FRAGABLE)?"":" (strict)"); //not relevent as a limit.
switch(cls.protocol)
{
default:
@ -5715,6 +5725,7 @@ static void CL_UserinfoChanged(void *ctx, const char *keyname)
void CL_Skygroup_f(void);
void WAD_ImageList_f(void);
/*
=================
CL_Init
@ -5956,7 +5967,9 @@ void CL_Init (void)
Cmd_AddCommand ("qtvplay", CL_QTVPlay_f);
Cmd_AddCommand ("qtvlist", CL_QTVList_f);
Cmd_AddCommand ("qtvdemos", CL_QTVDemos_f);
Cmd_AddCommandD ("demo_jump", CL_DemoJump_f, "Jump to a specified time in a demo. Prefix with a + or - for a relative offset. Seeking backwards will restart the demo and the fast forward, which can take some time in long demos.");
Cmd_AddCommandD ("demo_jump", CL_DemoJump_f, "Jump to a specified time in a demo. Prefix with a + or - for a relative offset. Seeking backwards will restart the demo and the fast forward, which can take some time in long demos.");
Cmd_AddCommandD ("demo_jump_mark", CL_DemoJump_f, "Jump to the next '//demomark' marker.");
Cmd_AddCommandD ("demo_jump_end", CL_DemoJump_f, "Jump to the next intermission message.");
Cmd_AddCommandD ("demo_nudge", CL_DemoNudge_f, "Nudge the demo by one frame. Argument should be +1 or -1. Nudging backwards is limited.");
Cmd_AddCommandAD ("timedemo", CL_TimeDemo_f, CL_DemoList_c, NULL);
#ifdef _DEBUG
@ -6090,6 +6103,7 @@ void CL_Init (void)
Cmd_AddCommandD ("waterfog", CL_Fog_f, "waterfog <density> <red> <green> <blue> <alpha> <depthbias>");
Cmd_AddCommandD ("skyroomfog", CL_Fog_f, "skyroomfog <density> <red> <green> <blue> <alpha> <depthbias>");
Cmd_AddCommandD ("skygroup", CL_Skygroup_f, "Provides a way to associate a skybox name with a series of maps, so that the requested skybox will override on a per-map basis.");
Cmd_AddCommandD ("r_imagelist_wad", WAD_ImageList_f, "displays the available wad images.");
//
// Windows commands
//
@ -6214,7 +6228,7 @@ void Host_WriteConfiguration (void)
}
Key_WriteBindings (f);
Cvar_WriteVariables (f, false);
Cvar_WriteVariables (f, false, false);
VFS_CLOSE (f);
@ -7229,7 +7243,7 @@ double Host_Frame (double time)
}
else
spare = 0;
host_frametime = (realtime - oldrealtime)*cl.gamespeed;
host_frametime = (realtime-spare - oldrealtime)*cl.gamespeed;
oldrealtime = realtime-spare;
if (host_speeds.ival)
@ -7650,8 +7664,6 @@ void CL_ExecInitialConfigs(char *resetcommand, qboolean fullvidrestart)
Cbuf_Execute (); //make sure any pending console commands are done with. mostly, anyway...
Cbuf_AddText("unbindall\nshowpic_removeall\n", RESTRICT_LOCAL);
Cbuf_AddText("bind volup \"inc volume 0.1\"\n", RESTRICT_LOCAL);
Cbuf_AddText("bind voldown \"inc volume -0.1\"\n", RESTRICT_LOCAL);
Cbuf_AddText("alias restart_ents \"changelevel . .\"\n",RESTRICT_LOCAL);
Cbuf_AddText("alias restart map_restart\n",RESTRICT_LOCAL);
Cbuf_AddText("alias startmap_sp \"map start\"\n", RESTRICT_LOCAL);

View file

@ -37,7 +37,6 @@ static char *CLNQ_ParseProQuakeMessage (char *s);
static void DLC_Poll(qdownload_t *dl);
static void CL_ProcessUserInfo (int slot, player_info_t *player);
static void CL_ParseStuffCmd(char *msg, int destsplit);
void Con_HexDump(qbyte *packet, size_t len, size_t badoffset);
#define MSG_ReadBigIndex() ((cls.fteprotocolextensions2&PEXT2_LONGINDEXES)?(unsigned int)MSG_ReadUInt64():MSG_ReadByte ())
#define MSG_ReadPlayer() MSG_ReadBigIndex()
@ -734,8 +733,11 @@ static void CL_WebDownloadFinished(struct dl_download *dl)
}
#endif
static void CL_SendDownloadStartRequest(char *filename, char *localname, unsigned int flags)
static void CL_SendDownloadStartRequest(downloadlist_t *pending)
{
char *filename = pending->rname;
char *localname = pending->localname;
unsigned int flags = pending->flags;
static int dlsequence;
qdownload_t *dl;
@ -1671,7 +1673,6 @@ void CL_RequestNextDownload (void)
if (cl.downloadlist)
{
downloadlist_t *dl;
unsigned int fl;
//download required downloads first
for (dl = cl.downloadlist; dl; dl = dl->next)
@ -1689,14 +1690,13 @@ void CL_RequestNextDownload (void)
if (!dl)
dl = cl.downloadlist;
}
fl = dl->flags;
/*if we don't require downloads don't queue requests until we're actually on the server, slightly more deterministic*/
if (cls.state == ca_active || (requiredownloads.value && !(cls.demoplayback && !(fl&DLLF_TRYWEB))) || (fl & DLLF_REQUIRED))
if (cls.state == ca_active || (requiredownloads.value && !(cls.demoplayback && !(dl->flags&DLLF_TRYWEB))) || (dl->flags & DLLF_REQUIRED))
{
if ((fl & DLLF_OVERWRITE) || !CL_CheckFile (dl->localname))
if ((dl->flags & DLLF_OVERWRITE) || !CL_CheckFile (dl->localname))
{
CL_SendDownloadStartRequest(dl->rname, dl->localname, fl);
CL_SendDownloadStartRequest(dl);
return;
}
else
@ -4219,7 +4219,7 @@ static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caut
CL_CheckServerInfo();
#if _MSC_VER > 1200
Sys_RecentServer("+nqconnect", cls.servername, cls.servername, "Join NQ Server");
Sys_RecentServer("+connectnq", cls.servername, cls.servername, "Join NQ Server");
#endif
if (CPNQ_IS_DP) //DP's protocol requires client+server to have exactly the same data files. this is shit, but in the interests of compatibility...
@ -7040,13 +7040,13 @@ static void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds
if (developer.ival)
{
Con_DPrintf("Proquake Message:\n");
Con_HexDump(msg, strlen(msg), 1);
Con_HexDump(msg, strlen(msg), 1, 16);
}
msg = CLNQ_ParseProQuakeMessage(msg);
}
#endif
strncat(stufftext, msg, sizeof(stufftext)-1);
Q_strncatz(stufftext, msg, sizeof(stufftext)-1);
while((msg = strchr(stufftext, '\n')))
{
*msg = '\0';
@ -7313,6 +7313,11 @@ static void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds
Plug_Command_f();
}
#endif
else if (!strncmp(stufftext, "//demomark", 10) && (stufftext[10]==0||stufftext[10]==' ') && cls.demoseeking == DEMOSEEK_MARK)
{ //found the next marker. we're done seeking.
cls.demoseeking = DEMOSEEK_NOT;
//FIXME: pause it.
}
else
{
if (!strncmp(stufftext, "cmd ", 4))
@ -7378,7 +7383,7 @@ static void CL_ParsePrecache(void)
}
}
void Con_HexDump(qbyte *packet, size_t len, size_t badoffset)
void Con_HexDump(qbyte *packet, size_t len, size_t badoffset, size_t stride)
{
int i;
int pos;
@ -7387,7 +7392,7 @@ void Con_HexDump(qbyte *packet, size_t len, size_t badoffset)
while(pos < len)
{
Con_Printf("%5i ", pos);
for (i = 0; i < 16; i++)
for (i = 0; i < stride; i++)
{
if (pos >= len)
Con_Printf(" - ");
@ -7397,8 +7402,8 @@ void Con_HexDump(qbyte *packet, size_t len, size_t badoffset)
Con_Printf("%2x ", packet[pos]);
pos++;
}
pos-=16;
for (i = 0; i < 16; i++)
pos-=stride;
for (i = 0; i < stride; i++)
{
if (pos >= len)
Con_Printf("X");
@ -7424,7 +7429,7 @@ void Con_HexDump(qbyte *packet, size_t len, size_t badoffset)
}
void CL_DumpPacket(void)
{
Con_HexDump(net_message.data, net_message.cursize, MSG_GetReadCount()-1);
Con_HexDump(net_message.data, net_message.cursize, MSG_GetReadCount()-1, 16);
}
static void CL_ParsePortalState(void)
@ -7838,11 +7843,18 @@ void CLQW_ParseServerMessage (void)
else
{
inframe_t *inf = &cl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK];
int fixtype = 2;
if (cls.ezprotocolextensions1 & EZPEXT1_SETANGLEREASON)
MSG_ReadByte(); //0=unknown, 1=tele, 2=spawn
fixtype = MSG_ReadByte(); //0=unknown, 1=tele, 2=spawn
for (i=0 ; i<3 ; i++)
ang[i] = MSG_ReadAngle();
if (!CSQC_Parse_SetAngles(destsplit, ang, false))
if (fixtype == 1)
{ //relative
VectorAdd(ang, cl.playerview[destsplit].viewangles, ang);
VectorSubtract(ang, cl.outframes[cls.netchan.incoming_sequence&UPDATE_MASK].cmd->angles, ang);
}
else fixtype = 2; //snap
if (!CSQC_Parse_SetAngles(destsplit, ang, fixtype==1))
{
inf->packet_entities.fixangles[destsplit] = true;
VectorCopy (ang, cl.playerview[destsplit].viewangles);
@ -8061,6 +8073,12 @@ void CLQW_ParseServerMessage (void)
cl.playerview[destsplit].simorg[i] = MSG_ReadCoord ();
for (i=0 ; i<3 ; i++)
cl.playerview[destsplit].intermissionangles[i] = MSG_ReadAngle ();
if (cls.demoseeking == DEMOSEEK_INTERMISSION)
{
cls.demoseeking = DEMOSEEK_NOT; //reached it.
//FIXME: pause it.
}
break;
case svc_finale:
@ -9863,6 +9881,12 @@ void CLNQ_ParseServerMessage (void)
cl.completed_time = cl.gametime;
}
cl.intermissionmode = IM_NQSCORES;
if (cls.demoseeking == DEMOSEEK_INTERMISSION)
{
cls.demoseeking = DEMOSEEK_NOT; //reached it.
//FIXME: pause it.
}
break;
case svc_finale:

View file

@ -2003,7 +2003,10 @@ void SCR_DrawFPS (void)
fps_count = 0;
lastupdatetime = t;
R_GetGPUUtilisation(&gpu, &gpumem); //not all that accurate, but oh well.
if (developer.ival)
R_GetGPUUtilisation(&gpu, &gpumem); //not all that accurate, but oh well.
else
gpu=gpumem=-1;
}
frametime = t - lastsystemtime;
lastsystemtime = t;

View file

@ -310,8 +310,9 @@ cvar_t r_bluelight_colour = CVARFD("r_bluelight_colour", "0.5 0.5 3.0 200", CVA
cvar_t r_rocketlight_colour = CVARFD("r_rocketlight_colour", "2.0 1.0 0.25 200", CVAR_ARCHIVE, "This controls the RGB+radius values of MF_ROCKET effects.");
cvar_t r_muzzleflash_colour = CVARFD("r_muzzleflash_colour", "1.5 1.3 1.0 200", CVAR_ARCHIVE, "This controls the initial RGB+radius of EF_MUZZLEFLASH/svc_muzzleflash effects.");
cvar_t r_muzzleflash_fade = CVARFD("r_muzzleflash_fade", "1.5 0.75 0.375 1000", CVAR_ARCHIVE, "This controls the per-second RGB+radius decay of EF_MUZZLEFLASH/svc_muzzleflash effects.");
cvar_t cl_truelightning = CVARF("cl_truelightning", "0", CVAR_SEMICHEAT);
static cvar_t cl_beam_trace = CVAR("cl_beam_trace", "0");
cvar_t cl_truelightning = CVARFD("cl_truelightning", "0", CVAR_SEMICHEAT, "Manipulate the end position of the player's own beams, to hide lag. Can be set to fractional values to reduce the effect.");
static cvar_t cl_beam_trace = CVARD("cl_beam_trace", "0", "Clips the length of any beams according to any walls they may have impacted.");
static cvar_t cl_beam_alpha = CVARAFD("cl_beam_alpha", "1", "r_shaftalpha", 0, "Specifies the translucency of the lightning beam segments.");
static cvar_t cl_legacystains = CVARD("cl_legacystains", "1", "WARNING: this cvar will default to 0 and later removed at some point"); //FIXME: do as the description says!
static cvar_t cl_shaftlight = CVAR("gl_shaftlight", "0.8");
static cvar_t cl_part_density_fade_start = CVARD("cl_part_density_fade_start", "1024", "Specifies the distance at which ssqc's pointparticles will start to get less dense.");
@ -454,6 +455,7 @@ void CL_InitTEnts (void)
Cvar_Register (&cl_expsprite, "Temporary entity control");
Cvar_Register (&cl_truelightning, "Temporary entity control");
Cvar_Register (&cl_beam_trace, "Temporary entity control");
Cvar_Register (&cl_beam_alpha, "Temporary entity control");
Cvar_Register (&r_explosionlight, "Temporary entity control");
Cvar_Register (&r_explosionlight_colour, "Temporary entity control");
Cvar_Register (&r_explosionlight_fade, "Temporary entity control");
@ -905,7 +907,9 @@ beam_t *CL_AddBeam (enum beamtype_e tent, int ent, vec3_t start, vec3_t end) //f
b->tag = -1;
b->bflags |= /*STREAM_ATTACHED|*/STREAM_ATTACHTOPLAYER;
b->endtime = cl.time + 0.2;
b->alpha = 1;
b->alpha = bound(0, cl_beam_alpha.value, 1);
if(b->alpha < 1)
b->rflags |= RF_TRANSLUCENT;
b->skin = 0;
VectorCopy (start, b->start);
VectorCopy (end, b->end);

View file

@ -533,7 +533,13 @@ typedef struct
#define EZTV_SETINFO (1u<<1) //proxy wants setinfo + ptrack commands
#define EZTV_QTVUSERLIST (1u<<2) //'//qul cmd id [name]' commands from proxy.
qboolean demohadkeyframe; //q2 needs to wait for a packet with a key frame, supposedly.
qboolean demoseeking;
enum
{
DEMOSEEK_NOT,
DEMOSEEK_TIME, //stops one we reach demoseektime
DEMOSEEK_MARK, //stops once we reach a '//demomark'
DEMOSEEK_INTERMISSION, //stops once we reach an svc_intermission
} demoseeking;
float demoseektime;
int demotrack;
qboolean timedemo;
@ -1197,7 +1203,7 @@ extern scenetris_t *cl_stris;
extern vecV_t *fte_restrict cl_strisvertv;
extern vec4_t *fte_restrict cl_strisvertc;
extern vec2_t *fte_restrict cl_strisvertt;
extern vec3_t *fte_restrict cl_strisvertn[3];
//extern vec3_t *fte_restrict cl_strisvertn[3];
extern index_t *fte_restrict cl_strisidx;
extern unsigned int cl_numstrisidx;
extern unsigned int cl_maxstrisidx;
@ -1579,6 +1585,7 @@ int TP_CategorizeMessage (char *s, int *offset, player_info_t **plr);
void TP_ExecTrigger (char *s, qboolean indemos); //executes one of the user's f_foo aliases from some engine-defined event.
qboolean TP_FilterMessage (char *s);
void TP_Init(void);
qboolean TP_HaveLocations(void);
char* TP_LocationName (const vec3_t location);
void TP_NewMap (void);
qboolean TP_CheckSoundTrigger (char *str); //plays sound files when some substring exists in chat.

View file

@ -336,7 +336,7 @@ static qbyte *CIN_ReadNextFrame (cinematics_t *cin)
else if (cin->s_width == 2)
COM_SwapLittleShortBlock((short *)samples, count*cin->s_channels);
S_RawAudio (0, samples, cin->s_rate, count, cin->s_channels, cin->s_width, volume.value );
S_RawAudio (SOURCEID_CINEMATIC, samples, cin->s_rate, count, cin->s_channels, cin->s_width, volume.value );
in.data = compressed;
in.count = size;

View file

@ -8819,6 +8819,34 @@ static void Image_Tr_RGBX8toPaletted(struct pendingtextureinfo *mips, int args)
*out++ = GetPaletteIndexRange(first, stop, in[0], in[1], in[2]);
}
}
static void Image_Tr_RGBA8toPaletted(struct pendingtextureinfo *mips, int args)
{
unsigned int mip;
int first=args&0xffff;
int stop=(args>>16)&0xffff;
int tr = first?0:255;
for (mip = 0; mip < mips->mipcount; mip++)
{
qbyte *in = mips->mip[mip].data;
qbyte *out = mips->mip[mip].data;
unsigned int p = mips->mip[mip].width*mips->mip[mip].height*mips->mip[mip].depth;
unsigned short tmp;
if (!mips->mip[mip].needfree && !mips->extrafree)
{
mips->mip[mip].needfree = true;
mips->mip[mip].data = out = BZ_Malloc(sizeof(tmp)*p);
}
mips->mip[mip].datasize = p*sizeof(*out);
for(; p-->0; in += 4)
{
if (in[3] < 128)
*out++ = tr;
else
*out++ = GetPaletteIndexRange(first, stop, in[0], in[1], in[2]);
}
}
}
//may operate in place
static void Image_Tr_8888toLuminence(struct pendingtextureinfo *mips, int channels)
@ -12184,7 +12212,9 @@ static struct
{PTI_RG8, PTI_RGBX8, Image_Tr_RG8ToRGXX8},
{PTI_RGBX8, PTI_P8, Image_Tr_RGBX8toPaletted, 0|(256<<16)},
{PTI_RGBX8, TF_H2_TRANS8_0, Image_Tr_RGBX8toPaletted, 1|(256<<16)},
{PTI_RGBA8, TF_H2_TRANS8_0, Image_Tr_RGBA8toPaletted, 1|(256<<16)},
{PTI_RGBX8, TF_TRANS8, Image_Tr_RGBX8toPaletted, 0|(255<<16)},
{PTI_RGBA8, TF_TRANS8, Image_Tr_RGBA8toPaletted, 0|(255<<16)},
{PTI_P8, PTI_RGBX8, Image_Tr_PalettedtoRGBX8, -1},
{TF_SOLID8, PTI_RGBX8, Image_Tr_PalettedtoRGBX8, -1},
{TF_H2_TRANS8_0,PTI_RGBA8, Image_Tr_PalettedtoRGBX8, 0},

View file

@ -31,8 +31,8 @@ void IN_ActivateMouse(void)
SDL_ShowCursor(0);
#if SDL_MAJOR_VERSION >= 2
SDL_SetRelativeMouseMode(true);
SDL_SetWindowGrab(sdlwindow, true);
SDL_SetRelativeMouseMode(SDL_TRUE);
SDL_SetWindowGrab(sdlwindow, SDL_TRUE);
#else
SDL_WM_GrabInput(SDL_GRAB_ON);
#endif
@ -46,8 +46,8 @@ void IN_DeactivateMouse(void)
mouseactive = false;
SDL_ShowCursor(1);
#if SDL_MAJOR_VERSION >= 2
SDL_SetRelativeMouseMode(false);
SDL_SetWindowGrab(sdlwindow, false);
SDL_SetRelativeMouseMode(SDL_FALSE);
SDL_SetWindowGrab(sdlwindow, SDL_FALSE);
#else
SDL_WM_GrabInput(SDL_GRAB_OFF);
#endif

View file

@ -2964,8 +2964,9 @@ void Key_Event (unsigned int devid, int key, unsigned int unicode, qboolean down
}
}
//yes, csqc is allowed to steal the escape key.
if (key != '`' && (!down || key != K_ESCAPE || (!Key_Dest_Has(~kdm_game) && !shift_down)) &&
//yes, csqc is allowed to steal the escape key (unless shift).
//it is blocked from blocking backtick (unless shift).
if ((key != '`'||shift_down) && (!down || key != K_ESCAPE || (!Key_Dest_Has(~kdm_game) && !shift_down)) &&
!Key_Dest_Has(~kdm_game))
{
#ifdef CSQC_DAT

View file

@ -239,7 +239,7 @@ void Draw_Hexen2BigFontString(int x, int y, const char *text)
}
#endif
mpic_t *QBigFontWorks(void)
void *QBigFontWorks(void)
{
mpic_t *p;
int i;
@ -250,14 +250,15 @@ mpic_t *QBigFontWorks(void)
"textures/mcharset.lmp",
NULL
};
if (font_menu)
return font_menu;
for (i = 0; names[i]; i++)
{
p = R2D_SafeCachePic (names[i]);
if (p && R_GetShaderSizes(p, NULL, NULL, true))
return p;
}
return (mpic_t*)font_menu;
return NULL;
}
void Draw_BigFontString(int x, int y, const char *text)
{
@ -598,7 +599,7 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *men
}
}
if (&menu->menu == topmenu && menu->mouseitem == option && option->common.type != mt_frameend)
if (&menu->menu == topmenu && menu->mouseitem == option && option->common.type != mt_frameend && !Key_Dest_Has_Higher(kdm_menu))
{
float alphamax = 0.5, alphamin = 0.2;
R2D_ImageColours(.5,.4,0,(sin(realtime*2)+1)*0.5*(alphamax-alphamin)+alphamin);
@ -608,12 +609,16 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *men
switch(option->common.type)
{
case mt_menucursor:
if (Key_Dest_Has_Higher(kdm_menu))
break;
if ((int)(realtime*4)&1)
Draw_FunString(xpos+option->common.posx, ypos+option->common.posy, "^Ue00d");
break;
case mt_text:
if (!option->text.text)
{ //blinking cursor image hack (FIXME)
if (Key_Dest_Has_Higher(kdm_menu))
break;
if ((int)(realtime*4)&1)
Draw_FunString(xpos+option->common.posx, ypos+option->common.posy, "^Ue00d");
}
@ -636,16 +641,18 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *men
Draw_BigFontString(xpos+option->common.posx, ypos+option->common.posy, option->button.text);
break;
case mt_menudot:
if (Key_Dest_Has_Higher(kdm_menu))
break;
i = (int)(realtime * 10)%maxdots;
p = R2D_SafeCachePic(va(menudotstyle, i+mindot ));
if (R_GetShaderSizes(p, NULL, NULL, false)>0)
R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy+dotofs, option->common.width, option->common.height, p);
if (R_GetShaderSizes(p, &pw, &ph, false)>0)
R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy+dotofs, (pw/(float)ph)*option->common.width, option->common.height, p);
else if ((int)(realtime*4)&1)
Draw_FunString(xpos+option->common.posx, ypos+option->common.posy + (option->common.height-8)/2, "^a^Ue00d");
break;
case mt_picturesel:
p = NULL;
if (menu->selecteditem && menu->selecteditem->common.posx == option->common.posx && menu->selecteditem->common.posy == option->common.posy)
if (menu->selecteditem && menu->selecteditem->common.posx == option->common.posx && menu->selecteditem->common.posy == option->common.posy && !Key_Dest_Has_Higher(kdm_menu))
{
char selname[MAX_QPATH];
Q_strncpyz(selname, option->picture.picturename, sizeof(selname));
@ -816,7 +823,7 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *men
Draw_ApproxTextBox(x, y, 16*8, 8);
Draw_FunString(x, y, option->edit.text);
if (menu->selecteditem == option && (int)(realtime*4) & 1)
if (menu->selecteditem == option && (int)(realtime*4) & 1 && !Key_Dest_Has_Higher(kdm_menu))
{
vid.ime_allow = true;
vid.ime_position[0] = x;
@ -904,7 +911,7 @@ static void MenuDraw(emenu_t *menu)
menu->menu.showosk = false;
MenuDrawItems(menu->xpos, menu->ypos, menu->options, menu);
// draw tooltip
if (menu->mouseitem && menu->tooltip && realtime > menu->tooltiptime)
if (menu->mouseitem && menu->tooltip && realtime > menu->tooltiptime && !Key_Dest_Has_Higher(kdm_menu))
{
// menuoption_t *option = menu->mouseitem;

View file

@ -29,7 +29,7 @@ static cvar_t sb_showtimelimit = CVARF("sb_showtimelimit", "0", CVAR_ARCHIVE);
static cvar_t sb_alpha = CVARF("sb_alpha", "0.7", CVAR_ARCHIVE);
vrect_t joinbutton, specbutton;
vrect_t joinbutton, streambutton, specbutton;
static float refreshedtime;
static int isrefreshing;
static enum
@ -372,7 +372,7 @@ static qboolean SL_ServerKey (menucustom_t *ths, emenu_t *menu, int key, unsigne
return true;
}
if (oldselection == info->selectedpos)
serverpreview = 1;
serverpreview = (server->adr.prot>=NP_STREAM)?SVPV_RULES:1;
return true;
}
@ -466,6 +466,7 @@ static void SL_PostDraw (emenu_t *menu)
serverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL;
int h = 0;
int w = 240;
char *qtv;
#ifdef HAVE_PACKET
if (server && selectedserver.refreshtime < realtime)
{
@ -706,6 +707,35 @@ static void SL_PostDraw (emenu_t *menu)
Draw_FunStringWidth(lx, y + (bh-8)/2, localtext("Observe"), bw, 2, active);y+=8;
}
qtv = Info_ValueForKey(server->moreinfo->info, "qtvstream");
if (server && *qtv)
{
int lx = vid.width/2 - w/2;
int y = vid.height/2 - h/2 - 4 + h;
int bh, bw;
qboolean active = false;
bw = w+16+12;
bh = 24;
// lx += bw-12;
bw = strlen(localtext("Stream"))*8 + 24;
bw = ((bw+15)/16) * 16; //width must be a multiple of 16
// lx -= bw;
streambutton.x = lx;
streambutton.y = y;
streambutton.width = bw + 16;
streambutton.height = bh + 16;
R2D_ImageColours(1,1,1,1);
y += 8;
Draw_ApproxTextBox(lx, y, bw, bh);
if (mousecursor_x >= streambutton.x && mousecursor_x < streambutton.x+streambutton.width)
if (mousecursor_y >= streambutton.y && mousecursor_y < streambutton.y+streambutton.height)
active = true;
Draw_FunStringWidth(lx, y + (bh-8)/2, localtext("Stream"), bw, 2, active);y+=8;
}
{
int lx = vid.width/2 - w/2;
int y = vid.height/2 - h/2 - 4 + h;
@ -794,6 +824,12 @@ static qboolean SL_Key (emenu_t *menu, int key, unsigned int unicode)
serverpreview = SVPV_NO;
goto dospec;
}
if (mousecursor_x >= streambutton.x && mousecursor_x < streambutton.x+streambutton.width)
if (mousecursor_y >= streambutton.y && mousecursor_y < streambutton.y+streambutton.height)
{
serverpreview = SVPV_NO;
goto dostream;
}
return true;
}
#ifdef HAVE_PACKET
@ -839,6 +875,13 @@ static qboolean SL_Key (emenu_t *menu, int key, unsigned int unicode)
return true;
}
#endif
else if (key == 't')
{
dostream:
Cbuf_AddText(va("qtvplay \"%s\"\n", Info_ValueForKey(server->moreinfo->info, "qtvstream")), RESTRICT_LOCAL);
M_RemoveAllMenus(true);
return true;
}
else if (key == 'b' || key == 'o' || key == 'j' || key == K_ENTER || key == K_KP_ENTER || key == K_GP_DIAMOND_CONFIRM || key == K_GP_DIAMOND_ALTCONFIRM) //join
{
if (key == 's' || key == 'o' || key == K_GP_DIAMOND_ALTCONFIRM)

View file

@ -1801,10 +1801,10 @@ static qboolean Media_WinAvi_DecodeFrame (cin_t *cin, qboolean nosound, qboolean
qacmStreamConvert(cin->avi.audiodecoder, &strhdr, ACM_STREAMCONVERTF_BLOCKALIGN);
qacmStreamUnprepareHeader(cin->avi.audiodecoder, &strhdr, 0);
S_RawAudio(-1, strhdr.pbDst, cin->avi.pWaveFormat->nSamplesPerSec, strhdr.cbDstLengthUsed/4, cin->avi.pWaveFormat->nChannels, 2, volume.value);
S_RawAudio(SOURCEID_CINEMATIC, strhdr.pbDst, cin->avi.pWaveFormat->nSamplesPerSec, strhdr.cbDstLengthUsed/4, cin->avi.pWaveFormat->nChannels, 2, volume.value);
}
else
S_RawAudio(-1, pBuffer, cin->avi.pWaveFormat->nSamplesPerSec, samples, cin->avi.pWaveFormat->nChannels, 2, volume.value);
S_RawAudio(SOURCEID_CINEMATIC, pBuffer, cin->avi.pWaveFormat->nSamplesPerSec, samples, cin->avi.pWaveFormat->nChannels, 2, volume.value);
}
return true;
}
@ -2223,7 +2223,7 @@ static qboolean Media_Roq_DecodeFrame (cin_t *cin, qboolean nosound, qboolean fo
while (cin->roq.roqfilm->audio_channels && S_HaveOutput() && cin->roq.roqfilm->aud_pos < cin->roq.roqfilm->vid_pos)
{
if (roq_read_audio(cin->roq.roqfilm)>0)
S_RawAudio(-1, cin->roq.roqfilm->audio, 22050, cin->roq.roqfilm->audio_size/cin->roq.roqfilm->audio_channels, cin->roq.roqfilm->audio_channels, 2, volume.value );
S_RawAudio(SOURCEID_CINEMATIC, cin->roq.roqfilm->audio, 22050, cin->roq.roqfilm->audio_size/cin->roq.roqfilm->audio_channels, cin->roq.roqfilm->audio_channels, 2, volume.value );
else
break;
}
@ -2589,7 +2589,7 @@ qboolean Media_StopFilm(qboolean all)
R_UnloadShader(videoshader);
videoshader = NULL;
S_RawAudio(-1, NULL, 0, 0, 0, 0, 0);
S_RawAudio(SOURCEID_CINEMATIC, NULL, 0, 0, 0, 0, 0);
}
while (pendingfilms && !videoshader)
@ -3524,7 +3524,7 @@ void Media_RecordFrame (void)
Draw_FunString(0, y, capturemessage.string);
}
//time for annother frame?
//time for another frame?
if (!captureframeforce)
{
if (capturelastvideotime > realtime+1)

View file

@ -3377,8 +3377,8 @@ typedef struct
int boneidx;
int bonebias; //shift the bones menu down to ensure the boneidx stays visible
int textype;
double framechangetime;
double skinchangetime;
double frametime;
double skintime;
float pitch;
float yaw;
@ -3394,6 +3394,7 @@ typedef struct
char shaderfile[MAX_QPATH];
char *shadertext;
qboolean paused;
#ifdef RAGDOLL
lerpents_t ragent;
world_t ragworld;
@ -3505,7 +3506,9 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu
modelview_t *mods = c->dptr;
skinfile_t *skin;
texnums_t *texnums;
#ifdef SKELETALMODELS
qboolean boneanimsonly;
#endif
model_t *animmodel = NULL;
if (R2D_Flush)
@ -3523,7 +3526,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu
r_refdef.grect.height = vid.height;
r_refdef.grect.x = 0;
r_refdef.grect.y = 0;
r_refdef.time = realtime;
r_refdef.time = mods->skintime;
r_refdef.flags = RDF_NOWORLDMODEL;
@ -3620,7 +3623,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu
ent.shaderTime = 0;//realtime;
ent.framestate.g[FS_REG].lerpweight[0] = 1;
ent.framestate.g[FS_REG].frame[0] = mods->framegroup;
ent.framestate.g[FS_REG].frametime[0] = ent.framestate.g[FS_REG].frametime[1] = realtime - mods->framechangetime;
ent.framestate.g[FS_REG].frametime[0] = ent.framestate.g[FS_REG].frametime[1] = mods->frametime;
ent.framestate.g[FS_REG].endbone = 0x7fffffff;
if (*mods->skinname)
{
@ -3651,6 +3654,12 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu
#endif
V_ApplyRefdef();
if (!mods->paused)
{
mods->frametime += host_frametime;
mods->skintime += host_frametime;
}
/*
{
trace_t tr;
@ -3707,9 +3716,8 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu
ent.framestate.g[FS_REG].frame[0] |= 0x8000;
if (ent.model->dollinfo && mods->ragworld.rbe)
{
float rate = 1.0/60;
float rate = 1.0/60; //try a fixed tick rate...
rag_doallanimations(&mods->ragworld);
mods->fixedrate += host_frametime;
if (mods->fixedrate > 1)
mods->fixedrate = 1;
while (mods->fixedrate >= rate)
@ -3717,11 +3725,14 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu
mods->ragworld.rbe->RunFrame(&mods->ragworld, rate, 800);
mods->fixedrate -= rate;
}
if (!mods->paused)
mods->fixedrate += host_frametime;
rag_updatedeltaent(&mods->ragworld, &ent, &mods->ragent);
}
#endif
#ifdef SKELETALMODELS
if (animmodel)// && Mod_GetNumBones(ent.model, false)==Mod_GetNumBones(animmodel, false))
{
int numbones = Mod_GetNumBones(ent.model, false);
@ -3732,6 +3743,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu
ent.framestate.skeltype = SKEL_RELATIVE;
}
else
#endif
animmodel = ent.model; //not using it. sorry. warn?
@ -3842,6 +3854,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu
}
#endif
}
#ifdef SKELETALMODELS
boneanimsonly = false;
if (ent.model && ent.model->loadstate == MLS_LOADED && ent.model->type == mod_alias)
{ //some models don't actually contain any mesh data, but exist as containers for skeletal animations that can be skel_built into a different model's anims.
@ -3900,6 +3913,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu
}
}
}
#endif
V_AddAxisEntity(&ent);
@ -4250,10 +4264,18 @@ static qboolean M_ModelViewerKey(struct menucustom_s *c, struct emenu_s *m, int
case MV_NORMALS: mods->mode = MV_NONE; break;
}
}
else if (key >= '1' && key <= '7')
{
Z_Free(mods->shadertext);
mods->shadertext = NULL;
mods->mode = MV_NONE + (key - '1');
}
else if (key == 'p')
mods->paused = !mods->paused;
else if (key == 'r')
{
mods->framechangetime = realtime;
mods->skinchangetime = realtime;
mods->frametime = 0;
mods->skintime = 0;
}
#ifdef RAGDOLL
else if (key == 'f')
@ -4290,28 +4312,28 @@ static qboolean M_ModelViewerKey(struct menucustom_s *c, struct emenu_s *m, int
else if (key == K_END)
{
mods->skingroup = max(0, mods->skingroup-1);
mods->skinchangetime = realtime;
mods->skintime = 0;
Z_Free(mods->shadertext);
mods->shadertext = NULL;
}
else if (key == K_HOME)
{
mods->skingroup += 1;
mods->skinchangetime = realtime;
mods->skintime = 0;
Z_Free(mods->shadertext);
mods->shadertext = NULL;
}
else if (key == K_PGDN)
{
mods->framegroup = max(0, mods->framegroup-1);
mods->framechangetime = realtime;
mods->frametime = 0;
Z_Free(mods->shadertext);
mods->shadertext = NULL;
}
else if (key == K_PGUP)
{
mods->framegroup += 1;
mods->framechangetime = realtime;
mods->frametime = 0;
}
else if (key == K_DEL)
{
@ -4393,8 +4415,8 @@ void M_Menu_ModelViewer_f(void)
Q_strncpyz(mv->skinname, Cmd_Argv(2), sizeof(mv->skinname));
Q_strncpyz(mv->animname, Cmd_Argv(3), sizeof(mv->animname));
mv->framechangetime = realtime;
mv->skinchangetime = realtime;
mv->frametime = 0;
mv->skintime = 0;
#ifdef RAGDOLL
menu->menu.videoreset = M_Modelviewer_Reset;
menu->remove = M_Modelviewer_Shutdown;

View file

@ -1183,7 +1183,8 @@ void M_Menu_Demos_f (void)
{
char *demoexts[] = {
".mvd", ".mvd.gz",
".qwz", ".qwz.gz",
".qwd", ".qwd.gz",
//".qwz", ".qwz.gz",
#ifdef NQPROT
".dem", ".dem.gz",
#endif
@ -1191,7 +1192,7 @@ void M_Menu_Demos_f (void)
".dm2", ".dm2.gz"
#endif
//there are also qizmo demos (.qwz) out there...
//we don't support them, but if we were to ask quizmo to decode them for us, we could do.
//we don't support them, but if we were to ask qizmo to decode them for us, we could do.
};
char *archiveexts[] = {
#ifdef PACKAGE_PK3

View file

@ -1283,7 +1283,10 @@ void M_Menu_Quit_f (void)
else if (!strcmp(arg, "forcesave") || cfg_save_auto.ival)
{
Cmd_ExecuteString("cfg_save", RESTRICT_LOCAL);
mode = 0;
if (!strcmp(arg, "prompt"))
mode = 1;
else
mode = 0;
}
else if (!strcmp(arg, "save"))
mode = 2;

View file

@ -138,6 +138,7 @@ void Menu_Prompt (void (*callback)(void *, promptbutton_t), void *ctx, const cha
#define Menu_PromptOrPrint(messages,optioncancel,highpri) Con_Printf("%s", messages)
#endif
void M_Window_ClosePrompt (void); //called when the window was requested to be closed. displays whatever quit menu is appropriate.
#ifndef NOBUILTINMENUS
//
@ -150,8 +151,6 @@ void M_Menu_Mods_f (void); //used at startup if the current gamedirs look dodgy.
void M_Menu_Installer (void); //given an embedded manifest, this displays an install menu for said game.
mpic_t *M_CachePic (char *path);
void M_Menu_Quit_f (void);
void M_Window_ClosePrompt (void); //called when the window was requested to be closed. displays whatever quit menu is appropriate.
void menufixme(void); //REMOVE REMOVE REMOVE
typedef struct emenu_s emenu_t;
@ -365,7 +364,7 @@ menucheck_t *MC_AddCheckBox(emenu_t *menu, int tx, int cx, int y, const char *te
menucheck_t *MC_AddCheckBoxFunc(emenu_t *menu, int tx, int cx, int y, const char *text, qboolean (*func) (menucheck_t *option, emenu_t *menu, chk_set_t set), int bits);
menubutton_t *MC_AddConsoleCommand(emenu_t *menu, int lhs, int rhs, int y, const char *text, const char *command);
menubutton_t *MC_AddConsoleCommandQBigFont(emenu_t *menu, int x, int y, const char *text, const char *command);
mpic_t *QBigFontWorks(void);
void *QBigFontWorks(void); //treat as a boolean.
menubutton_t *MC_AddConsoleCommandHexen2BigFont(emenu_t *menu, int x, int y, const char *text, const char *command);
menubutton_t *VARGS MC_AddConsoleCommandf(emenu_t *menu, int lhs, int rhs, int y, int rightalign, const char *text, char *command, ...);
menubutton_t *MC_AddCommand(emenu_t *menu, int lhs, int rhs, int y, const char *text, qboolean (*command) (union menuoption_s *,struct emenu_s *,int));

View file

@ -388,6 +388,8 @@ typedef struct texnums_s {
texid_t reflectmask; //2d, defines how reflective it is (for cubemap reflections)
texid_t displacement; //2d, alternative to bump.a, eg R16[F] for offsetmapping or tessellation
texid_t occlusion; //2d, occlusion map... only red is used.
texid_t transmission; //2d, multiplier for transmissionfactor... only red is used.
texid_t thickness; //2d, multiplier for thicknessfactor... only green is used.
//the material's pushconstants. vulkan guarentees only 128 bytes. so 8 vec4s. note that lmscales should want 4 of them...
/*struct

View file

@ -2965,7 +2965,7 @@ static void MasterInfo_Request(master_t *mast)
break;
#endif
case MP_QUAKEWORLD:
NET_SendPollPacket (14, va("%c%c%c%cstatus 23\n", 255, 255, 255, 255), mast->adr);
NET_SendPollPacket (14, va("%c%c%c%cstatus %i\n", 255, 255, 255, 255, STATUS_QTVLIST|STATUS_SHOWTEAMS|STATUS_SPECTATORS|STATUS_PLAYERS|STATUS_SERVERINFO), mast->adr);
break;
#ifdef NQPROT
case MP_NETQUAKE:
@ -3151,7 +3151,7 @@ void Master_QueryServer(serverinfo_t *server)
return;
#endif
case SS_QUAKEWORLD:
Q_snprintfz(data, sizeof(data), "%c%c%c%cstatus 23\n", 255, 255, 255, 255);
Q_snprintfz(data, sizeof(data), "%c%c%c%cstatus %i\n", 255, 255, 255, 255, STATUS_QTVLIST|STATUS_SHOWTEAMS|STATUS_SPECTATORS|STATUS_PLAYERS|STATUS_SERVERINFO);
break;
case SS_QUAKE2:
Q_snprintfz(data, sizeof(data), "%c%c%c%cstatus\n", 255, 255, 255, 255);
@ -3487,8 +3487,10 @@ static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolea
if (!MasterInfo_ReadProtocol(info, msg))
{ //try and guess.
if (0)
;
#ifdef Q2CLIENT
if (prototype == MP_QUAKE2)
else if (prototype == MP_QUAKE2)
info->special |= SS_QUAKE2;
#endif
#ifdef Q3CLIENT
@ -3499,9 +3501,7 @@ static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolea
else if (prototype == MP_NETQUAKE)
info->special |= SS_NETQUAKE;
#endif
#if defined(Q2CLIENT) || defined(Q3CLIENT) || defined(NQPROT)
else
#endif
info->special |= SS_QUAKEWORLD;
}
if (favorite) //was specifically named, not retrieved from a master.
@ -3577,13 +3577,35 @@ static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolea
{
int clnum;
for (clnum=0; clnum < MAX_CLIENTS; clnum++)
for (clnum=0; ; clnum++)
{
nl = strchr(msg, '\n');
if (!nl)
break;
*nl = '\0';
if (!strncmp(msg, "qtv ", 4))
{ //qtv destnum "proxyname" "stream@host:port" viewercount
char proxstream[128];
char tokval[256];
token = msg+4;
token = COM_ParseOut(token, tokval,sizeof(tokval));
//destnum = atoi(tokval);
token = COM_ParseOut(token, tokval,sizeof(tokval));
//proxy name...
token = COM_ParseOut(token, proxstream,sizeof(proxstream));
token = COM_ParseOut(token, tokval,sizeof(tokval));
//viewercount = atoi(tokval);
if (*proxstream)
Info_SetValueForKey(details.info, "qtvstream", proxstream, sizeof(details.info));
continue;
}
if (clnum == MAX_CLIENTS)
break;
details.players[clnum].isspec = 0;
details.players[clnum].team[0] = 0;
details.players[clnum].skin[0] = 0;

View file

@ -567,6 +567,20 @@ void QCBUILTIN PF_cl_runningserver (pubprogfuncs_t *prinst, struct globalvars_s
#endif
}
void QCBUILTIN PF_cl_addprogs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
const char *s = PR_GetStringOfs(prinst, OFS_PARM0);
int newp;
if (!s || !*s)
newp = -1;
else
{
newp = PR_LoadProgs(prinst, s);
if (newp >= 0)
PR_ProgsAdded(prinst, newp, s);
}
G_FLOAT(OFS_RETURN) = newp;
}
#ifdef HAVE_MEDIA_DECODER
@ -1050,6 +1064,55 @@ void QCBUILTIN PF_cl_localsound(pubprogfuncs_t *prinst, struct globalvars_s *pr_
S_LocalSound2(s, chan, vol);
}
void QCBUILTIN PF_cl_queueaudio(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *world = prinst->parms->user;
int sourceid = (world->keydestmask == kdm_menu)?SOURCEID_MENUQC:SOURCEID_CSQC;
int hz = G_INT(OFS_PARM0);
int channels = G_INT(OFS_PARM1);
int type = G_INT(OFS_PARM2);
int qcptr = G_INT(OFS_PARM3);
int numframes = G_INT(OFS_PARM4);
int i;
const float volume = 1;
qaudiofmt_t fmt;
const void *data;
void *ndata;
if (hz < 1 || (channels != 1 && channels != 2) || numframes <= 0)
{ //dumb arg.
G_FLOAT(OFS_RETURN) = -1;
return;
}
switch(type)
{
case 8: fmt = QAF_S8; data = PR_GetReadQCPtr(prinst, qcptr, numframes*channels*QAF_BYTES(fmt));
ndata = alloca(sizeof(char)*numframes*channels);
for (i = numframes*channels; i --> 0; )
((char*)ndata)[i] = ((const qbyte*)data)[i]-128;
data = ndata; break;
case -8: fmt = QAF_S8; data = PR_GetReadQCPtr(prinst, qcptr, numframes*channels*QAF_BYTES(fmt)); break;
case -16: fmt = QAF_S16; data = PR_GetReadQCPtr(prinst, qcptr, numframes*channels*QAF_BYTES(fmt)); break;
// case 16: fmt = QAF_U16: data = PR_GetReadQCPtr(prinst, qcptr, numframes*channels*QAF_BYTES(fmt)); break;
#ifdef MIXER_F32
case 256|32: fmt = QAF_F32; data = PR_GetReadQCPtr(prinst, qcptr, numframes*channels*QAF_BYTES(fmt)); break;
#endif
default:
G_FLOAT(OFS_RETURN) = -2; //unsupported width.
return;
}
S_RawAudio(sourceid, data, hz, numframes*channels, channels, fmt, volume);
G_FLOAT(OFS_RETURN) = 1;
}
void QCBUILTIN PF_cl_getqueuedaudiotime(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *world = prinst->parms->user;
int sourceid = (world->keydestmask == kdm_menu)?SOURCEID_MENUQC:SOURCEID_CSQC;
G_FLOAT(OFS_RETURN) = S_RawAudioQueued(sourceid);
}
void QCBUILTIN PF_cl_getlocaluserinfoblob (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{

View file

@ -42,20 +42,10 @@ extern cvar_t sv_demo_write_csqc;
static pubprogfuncs_t *csqcprogs;
typedef struct csqctreadstate_s {
float resumetime;
struct qcthread_s *thread;
int self;
int other;
struct csqctreadstate_s *next;
} csqctreadstate_t;
static qboolean csprogs_promiscuous;
static unsigned int csprogs_checksum;
static size_t csprogs_checksize;
static char csprogs_checkname[MAX_QPATH];
static csqctreadstate_t *csqcthreads;
qboolean csqc_resortfrags;
world_t csqc_world;
@ -127,7 +117,7 @@ static csqcglobals_t csqcg;
playerview_t csqc_nullview;
static void VARGS CSQC_Abort (char *format, ...); //an error occured.
static void VARGS CSQC_Abort (const char *format, ...); //an error occured.
static void cs_set_input_state (usercmd_t *cmd);
//fixme: we should be using entity numbers, not view numbers.
@ -348,7 +338,7 @@ static void CSQC_FindGlobals(qboolean nofuncs)
if (!csqc_world.g.physics_mode)
{
csphysicsmode = 0; /*note: dp handles think functions as part of addentity rather than elsewhere. if we're in a compat mode, we don't want to have to duplicate work*/
csphysicsmode = csqc_isdarkplaces?1:0; /*note: dp handles think functions as part of addentity rather than elsewhere. if we're in a compat mode, we don't want to have to duplicate work*/
csqc_world.g.physics_mode = &csphysicsmode;
}
@ -2342,9 +2332,12 @@ uploadfmt_t PR_TranslateTextureFormat(int qcformat)
case 13: return PTI_RG8;
case 14: return PTI_RGB32F;
case 15: return TF_8PAL24;
case 16: return TF_8PAL32;
default:
qcformat = -qcformat;
if (qcformat < PTI_MAX)
if ((unsigned int)qcformat < PTI_MAX)
return qcformat;
return PTI_INVALID;
}
@ -2371,6 +2364,9 @@ int PR_UnTranslateTextureFormat(uploadfmt_t pixelformat)
case PTI_RG8: return 13;
case PTI_RGB32F: return 14;
case TF_8PAL24: return 15;
case TF_8PAL32: return 16;
default:return -pixelformat;
}
}
@ -2615,7 +2611,7 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_
Q_strncpyz(r_refdef.rt_destcolour[i].texname, PR_GetStringOfs(prinst, OFS_PARM1), sizeof(r_refdef.rt_destcolour[i].texname));
if (prinst->callargc >= 4 && *r_refdef.rt_destcolour[i].texname)
{
float fmt = G_FLOAT(OFS_PARM2);
int fmt = G_FLOAT(OFS_PARM2);
float *size = G_VECTOR(OFS_PARM3);
if (fmt < 0)
R2D_RT_Configure(r_refdef.rt_destcolour[i].texname, size[0], size[1], PR_TranslateTextureFormat(-fmt), (RT_IMAGEFLAGS&~IF_LINEAR)|IF_NEAREST);
@ -2629,7 +2625,7 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_
Q_strncpyz(r_refdef.rt_sourcecolour.texname, PR_GetStringOfs(prinst, OFS_PARM1), sizeof(r_refdef.rt_sourcecolour));
if (prinst->callargc >= 4 && *r_refdef.rt_sourcecolour.texname)
{
float fmt = G_FLOAT(OFS_PARM2);
int fmt = G_FLOAT(OFS_PARM2);
float *size = G_VECTOR(OFS_PARM3);
if (fmt < 0)
R2D_RT_Configure(r_refdef.rt_sourcecolour.texname, size[0], size[1], PR_TranslateTextureFormat(-fmt), (RT_IMAGEFLAGS&~IF_LINEAR)|IF_NEAREST);
@ -2642,7 +2638,7 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_
Q_strncpyz(r_refdef.rt_depth.texname, PR_GetStringOfs(prinst, OFS_PARM1), sizeof(r_refdef.rt_depth.texname));
if (prinst->callargc >= 4 && *r_refdef.rt_depth.texname)
{
float fmt = G_FLOAT(OFS_PARM2);
int fmt = G_FLOAT(OFS_PARM2);
float *size = G_VECTOR(OFS_PARM3);
if (fmt < 0)
R2D_RT_Configure(r_refdef.rt_depth.texname, size[0], size[1], PR_TranslateTextureFormat(-fmt), (RT_IMAGEFLAGS&~IF_LINEAR)|IF_NEAREST);
@ -2655,7 +2651,7 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_
Q_strncpyz(r_refdef.rt_ripplemap.texname, PR_GetStringOfs(prinst, OFS_PARM1), sizeof(r_refdef.rt_ripplemap.texname));
if (prinst->callargc >= 4 && *r_refdef.rt_ripplemap.texname)
{
float fmt = G_FLOAT(OFS_PARM2);
int fmt = G_FLOAT(OFS_PARM2);
float *size = G_VECTOR(OFS_PARM3);
if (fmt < 0)
R2D_RT_Configure(r_refdef.rt_ripplemap.texname, size[0], size[1], PR_TranslateTextureFormat(-fmt), (RT_IMAGEFLAGS&~IF_LINEAR)|IF_NEAREST);
@ -3814,6 +3810,16 @@ static void QCBUILTIN PF_cs_sendevent (pubprogfuncs_t *prinst, struct globalvars
MSG_WriteByte(&cls.netchan.message, ev_string);
MSG_WriteString(&cls.netchan.message, PR_GetStringOfs(prinst, OFS_PARM2+i*3));
}
else if (argtypes[i] == 'p' && i>0)
{
unsigned int len = G_UINT(OFS_PARM1+i*3);
unsigned int qcptr = G_UINT(OFS_PARM2+i*3);
char *p = prinst->stringtable + qcptr;
if (len >= prinst->stringtablesize || qcptr > prinst->stringtablesize-len)
break;
MSG_WriteByte(&cls.netchan.message, ev_pointer);
SZ_Write(&cls.netchan.message, p, len);
}
else if (argtypes[i] == 'f')
{
MSG_WriteByte(&cls.netchan.message, ev_float);
@ -3842,7 +3848,7 @@ static void QCBUILTIN PF_cs_sendevent (pubprogfuncs_t *prinst, struct globalvars
else if (argtypes[i] == 'U')
{
MSG_WriteByte(&cls.netchan.message, ev_uint64);
MSG_WriteInt64(&cls.netchan.message, G_UINT64(OFS_PARM2+i*3));
MSG_WriteUInt64(&cls.netchan.message, G_UINT64(OFS_PARM2+i*3));
}
else if (argtypes[i] == 'v')
{
@ -4839,7 +4845,13 @@ static void QCBUILTIN PF_getlightstylergb (pubprogfuncs_t *prinst, struct global
return;
}
if (stnum >= cl_max_lightstyles || !cl_lightstyle[stnum].length)
if (stnum >= cl_max_lightstyles)
{
value = ('m'-'a')*22 * r_lightstylescale.value;
G_VECTOR(OFS_RETURN)[0] = G_VECTOR(OFS_RETURN)[1] = G_VECTOR(OFS_RETURN)[2] = value*(1.0/256);
return;
}
else if (!cl_lightstyle[stnum].length)
value = ('m'-'a')*22 * r_lightstylescale.value;
else if (cl_lightstyle[stnum].map[0] == '=')
value = atof(cl_lightstyle[stnum].map+1)*256*r_lightstylescale.value;
@ -4847,7 +4859,7 @@ static void QCBUILTIN PF_getlightstylergb (pubprogfuncs_t *prinst, struct global
{
int v1, v2, vd, i;
float f;
f = (cl.time*r_lightstylespeed.value);
if (f < 0)
f = 0;
@ -5227,50 +5239,6 @@ static void QCBUILTIN PF_cl_te_particlesnow (pubprogfuncs_t *prinst, struct glob
P_RunParticleWeather(min, max, vel, howmany, colour, "snow");
}
void CSQC_RunThreads(void)
{
csqctreadstate_t *state = csqcthreads, *next;
csqcthreads = NULL;
while(state)
{
next = state->next;
if (state->resumetime > cl.servertime)
{ //not time yet, reform original list.
state->next = csqcthreads;
csqcthreads = state;
}
else
{ //call it and forget it ever happened. The Sleep biltin will recreate if needed.
*csqcg.self = EDICT_TO_PROG(csqcprogs, EDICT_NUM_UB(csqcprogs, state->self));
*csqcg.other = EDICT_TO_PROG(csqcprogs, EDICT_NUM_UB(csqcprogs, state->other));
csqcprogs->RunThread(csqcprogs, state->thread);
csqcprogs->parms->memfree(state->thread);
csqcprogs->parms->memfree(state);
}
state = next;
}
}
static void QCBUILTIN PF_cs_addprogs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
const char *s = PR_GetStringOfs(prinst, OFS_PARM0);
int newp;
if (!s || !*s)
newp = -1;
else
{
newp = PR_LoadProgs(prinst, s);
if (newp >= 0)
PR_ProgsAdded(csqcprogs, newp, s);
}
G_FLOAT(OFS_RETURN) = newp;
}
static void QCBUILTIN PF_cs_OpenPortal (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int state = G_FLOAT(OFS_PARM1)!=0;
@ -6793,8 +6761,10 @@ static struct {
{"fputs", PF_fputs, 113}, // #113 void(float fnum, string str) fputs (FRIK_FILE)
{"fread", PF_fread, 0},
{"fwrite", PF_fwrite, 0},
{"fseek", PF_fseek, 0},
{"fsize", PF_fsize, 0},
{"fseek", PF_fseek32, 0},
{"fsize", PF_fsize32, 0},
{"fseek64", PF_fseek64, 0},
{"fsize64", PF_fsize64, 0},
{"strlen", PF_strlen, 114}, // #114 float(string str) strlen (FRIK_FILE)
{"strcat", PF_strcat, 115}, // #115 string(string str1, string str2, ...) strcat (FRIK_FILE)
@ -6812,7 +6782,7 @@ static struct {
{"getmodelindex", PF_cs_getmodelindex, 200},
{"getsoundindex", PF_cs_getsoundindex, 0},
{"externcall", PF_externcall, 201},
{"addprogs", PF_cs_addprogs, 202},
{"addprogs", PF_cl_addprogs, 202},
{"externvalue", PF_externvalue, 203},
{"externset", PF_externset, 204},
@ -6822,9 +6792,9 @@ static struct {
{"registertempent", PF_NoCSQC, 208},//{"RegisterTempEnt", PF_RegisterTEnt, 0, 0, 0, 208},
{"customtempent", PF_NoCSQC, 209},//{"CustomTempEnt", PF_CustomTEnt, 0, 0, 0, 209},
//210
{"fork", PF_Fixme, 210},//{"fork", PF_Fork, 0, 0, 0, 210},
{"fork", PF_Fork, 210},//{"fork", PF_Fork, 0, 0, 0, 210},
{"abort", PF_Abort, 211}, //#211 void() abort (FTE_MULTITHREADED)
{"sleep", PF_Fixme, 212},//{"sleep", PF_Sleep, 0, 0, 0, 212},
{"sleep", PF_Sleep, 212},//{"sleep", PF_Sleep, 0, 0, 0, 212},
{"forceinfokey", PF_NoCSQC, 213},//{"forceinfokey", PF_ForceInfoKey, 0, 0, 0, 213},
{"forceinfokeyblob", PF_NoCSQC, 0},//{"forceinfokey", PF_ForceInfoKey, 0, 0, 0, 213},
{"chat", PF_NoCSQC, 214},//{"chat", PF_chat, 0, 0, 0, 214},// #214 void(string filename, float starttag, entity edict) SV_Chat (FTE_NPCCHAT)
@ -6905,6 +6875,8 @@ static struct {
{"htos", PF_htos, 262},
{"ftoi", PF_ftoi, 0},
{"itof", PF_itof, 0},
{"ftou", PF_ftou, 0},
{"utof", PF_utof, 0},
{"skel_create", PF_skel_create, 263},//float(float modlindex) skel_create = #263; // (FTE_CSQC_SKELETONOBJECTS)
{"skel_build", PF_skel_build, 264},//float(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone, optional float addition) skel_build = #264; // (FTE_CSQC_SKELETONOBJECTS)
@ -7043,6 +7015,7 @@ static struct {
{"cprint", PF_cl_cprint, 338}, // #338 void(string s) cprint (EXT_CSQC)
{"print", PF_print, 339}, // #339 void(string s) print (EXT_CSQC)
{"setwatchpoint", PF_setwatchpoint, 0},
//340
{"keynumtostring", PF_cl_keynumtostring, 340}, // #340 string(float keynum) keynumtostring (EXT_CSQC)
@ -7076,6 +7049,8 @@ static struct {
{"SetListener", PF_cs_setlistener, 351}, // #351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC)
{"setup_reverb", PF_cs_setupreverb, 0},
{"queueaudio", PF_cl_queueaudio, 0},
{"getqueuedaudiotime", PF_cl_getqueuedaudiotime, 0},
{"registercommand", PF_cs_registercommand, 352}, // #352 void(string cmdname) registercommand (EXT_CSQC)
{"wasfreed", PF_WasFreed, 353}, // #353 float(entity ent) wasfreed (EXT_CSQC) (should be availabe on server too)
@ -7120,6 +7095,7 @@ static struct {
{"releasecustomskin", PF_cs_releasecustomskin, 379},
{"memalloc", PF_memalloc, 384},
{"memrealloc", PF_memrealloc, 0},
{"memfree", PF_memfree, 385},
{"memcmp", PF_memcmp, 0},
{"memcpy", PF_memcpy, 386},
@ -7335,7 +7311,8 @@ static struct {
{"buf_cvarlist", PF_buf_cvarlist, 517},
{"cvar_description", PF_cvar_description, 518},
{"gettime", PF_gettime, 519},
{"gettimef", PF_gettimef, 519},
{"gettimed", PF_gettimed, 0},
{"keynumtostring_omgwtf", PF_cl_keynumtostring, 520},
{"findkeysforcommand", PF_cl_findkeysforcommand, 521},
@ -7509,7 +7486,7 @@ static progparms_t csqcprogparms;
//Any menu builtin error or anything like that will come here.
static void VARGS CSQC_Abort (char *format, ...) //an error occured.
static void VARGS CSQC_Abort (const char *format, ...) //an error occured.
{
va_list argptr;
char string[1024];
@ -7532,21 +7509,6 @@ static void VARGS CSQC_Abort (char *format, ...) //an error occured.
Host_EndGame("csqc error");
}
static void CSQC_ForgetThreads(void)
{
csqctreadstate_t *state = csqcthreads, *next;
csqcthreads = NULL;
while(state)
{
next = state->next;
csqcprogs->parms->memfree(state->thread);
csqcprogs->parms->memfree(state);
state = next;
}
}
static void PDECL CSQC_EntSpawn (struct edict_s *e, int loading)
{
struct csqcedict_s *ent = (csqcedict_t*)e;
@ -7706,7 +7668,6 @@ void CSQC_Shutdown(void)
Material_RegisterLoader(&csqc_world, NULL);
key_dest_absolutemouse &= ~kdm_game;
CSQC_ForgetThreads();
PR_ReleaseFonts(kdm_game);
PR_Common_Shutdown(csqcprogs, false);
World_Destroy(&csqc_world);
@ -7714,7 +7675,10 @@ void CSQC_Shutdown(void)
csqc_world.progs = csqcprogs = NULL;
}
else
{
PR_Route_Shutdown (&csqc_world);
World_Destroy(&csqc_world);
}
Cmd_RemoveCommands(CS_ConsoleCommand_f);
@ -8663,7 +8627,7 @@ void CSQC_WatchPoint_f(void)
Con_Printf("csqc not running\n");
return;
}
if (csqcprogs->SetWatchPoint(csqcprogs, variable))
if (csqcprogs->SetWatchPoint(csqcprogs, variable, variable))
Con_Printf("Watchpoint set\n");
else
Con_Printf("Watchpoint cleared\n");
@ -8773,11 +8737,27 @@ qboolean CSQC_DrawView(void)
csqc_dp_lastwas3d = false;
RSpeedRemark();
if (csqc_isdarkplaces && *csqc_world.g.physics_mode == 1)
if (*csqc_world.g.physics_mode == 0)
{
csqc_world.physicstime = cl.servertime;
// let the progs know that a new frame has started
if (csqcg.StartFrame)
{
*csqc_world.g.self = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.other = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.time = csqc_world.physicstime;
PR_ExecuteProgram (csqcprogs, csqcg.StartFrame);
}
PR_RunThreads(&csqc_world);
if (csqcg.EndFrame)
{ //consistency. or something.
*csqc_world.g.self = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.other = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.time = csqc_world.physicstime;
PR_ExecuteProgram (csqcprogs, csqcg.EndFrame);
}
}
else
else if (*csqc_world.g.physics_mode)
{
#ifdef USERBE
if (csqc_world.rbe)
@ -8796,6 +8776,15 @@ qboolean CSQC_DrawView(void)
if (host_frametime > maxtic)
host_frametime = maxtic;
// let the progs know that a new frame has started
if (csqcg.StartFrame)
{
*csqc_world.g.self = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.other = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.time = csqc_world.physicstime;
PR_ExecuteProgram (csqcprogs, csqcg.StartFrame);
}
#ifdef USERBE
if (csqc_world.rbe)
{
@ -8806,8 +8795,18 @@ qboolean CSQC_DrawView(void)
}
#endif
PR_RunThreads(&csqc_world);
World_Physics_Frame(&csqc_world);
csqc_world.physicstime += host_frametime;
//all thinks done. for consistency with the ssqc extension.
if (csqcg.EndFrame)
{
*csqc_world.g.self = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.other = EDICT_TO_PROG(csqcprogs, csqc_world.edicts);
*csqc_world.g.time = csqc_world.physicstime;
PR_ExecuteProgram (csqcprogs, csqcg.EndFrame);
}
}
}
RSpeedEnd(RSPEED_CSQCPHYSICS);
@ -8866,8 +8865,6 @@ qboolean CSQC_DrawView(void)
if (cl.worldmodel)
Surf_LessenStains();
CSQC_RunThreads(); //wake up any qc threads
#ifdef HAVE_LEGACY
if (csqcg.autocvar_vid_conwidth)
*csqcg.autocvar_vid_conwidth = vid.width;

View file

@ -901,17 +901,25 @@ void QCBUILTIN PF_CL_uploadimage (pubprogfuncs_t *prinst, struct globalvars_s *p
}
else
{
void *palette = NULL;
unsigned int blockbytes, blockwidth, blockheight, blockdepth;
//get format info
Image_BlockSizeForEncoding(format, &blockbytes, &blockwidth, &blockheight, &blockdepth);
//round up as appropriate
blockwidth = ((width+blockwidth-1)/blockwidth)*blockwidth;
blockheight = ((height+blockheight-1)/blockheight)*blockheight;
//we do allow palettes on the end.
if (format == TF_8PAL24)
size -= 768, palette = (qbyte*)imgptr+size, blockbytes=1;
else if (format == TF_8PAL32)
size -= 1024, palette = (qbyte*)imgptr+size, blockbytes=1;
if (size != blockwidth*blockheight*blockbytes)
G_INT(OFS_RETURN) = 0; //size isn't right. which means the pointer might be invalid too.
else
{
Image_Upload(tid, format, imgptr, NULL, width, height, 1, RT_IMAGEFLAGS);
Image_Upload(tid, format, imgptr, palette, width, height, 1, RT_IMAGEFLAGS);
tid->width = width;
tid->height = height;
G_INT(OFS_RETURN) = 1;
@ -2450,6 +2458,8 @@ static struct {
{"htos", PF_htos, 0},
{"ftoi", PF_ftoi, 0},
{"itof", PF_itof, 0},
{"ftou", PF_ftou, 0},
{"utof", PF_utof, 0},
{"spawn", PF_Spawn, 22},
{"remove", PF_Remove_, 23},
@ -2475,7 +2485,7 @@ static struct {
{"sin", PF_Sin, 38},
{"cos", PF_Cos, 39},
{"sqrt", PF_Sqrt, 40},
{"randomvector", PF_randomvector, 41},
{"randomvec", PF_randomvector, 41},
{"registercvar", PF_registercvar, 42},
{"min", PF_min, 43},
{"max", PF_max, 44},
@ -2490,8 +2500,10 @@ static struct {
{"fputs", PF_fputs, 51},
{"fread", PF_fread, 0},
{"fwrite", PF_fwrite, 0},
{"fseek", PF_fseek, 0},
{"fsize", PF_fsize, 0},
{"fseek", PF_fseek32, 0},
{"fsize", PF_fsize32, 0},
{"fseek64", PF_fseek64, 0},
{"fsize64", PF_fsize64, 0},
{"strlen", PF_strlen, 52},
{"strcat", PF_strcat, 53},
{"substring", PF_substring, 54},
@ -2510,7 +2522,8 @@ static struct {
{"changelevel", PF_cl_changelevel, 64}, //void changelevel(string map) = #64;
{"localsound", PF_cl_localsound, 65},
{"getmousepos", PF_cl_getmousepos, 66},
{"gettime", PF_gettime, 67},
{"gettime", PF_gettimef, 67},
{"gettimed", PF_gettimed, 0},
{"loadfromdata", PF_loadfromdata, 68},
{"loadfromfile", PF_loadfromfile, 69},
{"mod", PF_mod, 70},
@ -2542,8 +2555,14 @@ static struct {
{"setorigin", PF_m_setorigin, 92},
//gap
{"getmodelindex", PF_m_getmodelindex, 200},
{"externcall", PF_externcall, 201},
{"addprogs", PF_cl_addprogs, 202},
{"externvalue", PF_externvalue, 203},
{"externset", PF_externset, 204},
//gap
{"fork", PF_Fork, 210},
{"abort", PF_Abort, 211},
{"sleep", PF_Sleep, 212},
//gap
{"strstrofs", PF_strstrofs, 221},
{"str2chr", PF_str2chr, 222},
@ -2621,6 +2640,7 @@ static struct {
{"print_csqc", PF_print, 339},
{"setwatchpoint", PF_setwatchpoint, 0},
{"keynumtostring_csqc", PF_cl_keynumtostring, 340},
{"stringtokeynum_csqc", PF_cl_stringtokeynum, 341},
{"getkeybind", PF_cl_getkeybind, 342},
@ -2644,6 +2664,8 @@ static struct {
{"isdemo", PF_isdemo, 349},
// {NULL, PF_Fixme, 350},
// {NULL, PF_Fixme, 351},
{"queueaudio", PF_cl_queueaudio, 0},
{"getqueuedaudiotime", PF_cl_getqueuedaudiotime, 0},
{"registercommand", PF_menu_registercommand, 352},
{"wasfreed", PF_WasFreed, 353},
{"serverkey", PF_cl_serverkey, 354}, // #354 string(string key) serverkey;
@ -2662,6 +2684,7 @@ static struct {
{"setcustomskin", PF_m_setcustomskin, 376},
//gap
{"memalloc", PF_memalloc, 384},
{"memrealloc", PF_memrealloc, 0},
{"memfree", PF_memfree, 385},
{"memcmp", PF_memcmp, 0},
{"memcpy", PF_memcpy, 386},
@ -3120,7 +3143,7 @@ void *VARGS PR_CB_Malloc(int size); //these functions should be tracked by the l
void VARGS PR_CB_Free(void *mem);
//Any menu builtin error or anything like that will come here.
void VARGS Menu_Abort (char *format, ...)
void VARGS Menu_Abort (const char *format, ...)
{
va_list argptr;
char string[1024];
@ -3290,7 +3313,7 @@ qboolean MP_Init (void)
menu_world.Get_FrameState = MP_Get_FrameState;
menu_world.progs = InitProgs(&menuprogparms);
PR_Configure(menu_world.progs, PR_ReadBytesString(pr_menu_memsize.string), 1, pr_enable_profiling.ival);
PR_Configure(menu_world.progs, PR_ReadBytesString(pr_menu_memsize.string), 16, pr_enable_profiling.ival);
mprogs = PR_LoadProgs(menu_world.progs, "menu.dat");
if (mprogs < 0) //no per-progs builtins.
{
@ -3408,9 +3431,14 @@ qboolean MP_ConsoleCommand(const char *cmdtext)
void MP_CoreDump_f(void)
{
if (Cmd_IsInsecure())
{
Con_TPrintf("Refusing to execute insecure %s\n", Cmd_Argv(0));
return;
}
if (!menu_world.progs)
{
Con_Printf("Can't core dump, you need to be running the CSQC progs first.");
Con_Printf("Can't core dump, you need to be running the MenuQC progs first.");
return;
}
@ -3425,21 +3453,28 @@ void MP_CoreDump_f(void)
static void MP_Poke_f(void)
{
/*if (!SV_MayCheat())
Con_TPrintf ("Please set sv_cheats 1 and restart the map first.\n");
else */if (menu_world.progs && menu_world.progs->EvaluateDebugString)
if (Cmd_IsInsecure())
Con_TPrintf("Refusing to execute insecure %s\n", Cmd_Argv(0));
/*else if (!SV_MayCheat())
Con_TPrintf ("Please set sv_cheats 1 and restart the map first.\n");*/
else if (menu_world.progs && menu_world.progs->EvaluateDebugString)
Con_TPrintf("Result: %s\n", menu_world.progs->EvaluateDebugString(menu_world.progs, Cmd_Args()));
else
Con_TPrintf ("not supported.\n");
}
void MP_Breakpoint_f(void)
static void MP_Breakpoint_f(void)
{
int wasset;
int isset;
char *filename = Cmd_Argv(1);
int line = atoi(Cmd_Argv(2));
if (Cmd_IsInsecure())
{
Con_TPrintf("Refusing to execute insecure %s\n", Cmd_Argv(0));
return;
}
if (!menu_world.progs)
{
Con_Printf("Menu not running\n");
@ -3457,17 +3492,51 @@ void MP_Breakpoint_f(void)
Cvar_Set(Cvar_FindVar("pr_debugger"), "1");
}
static void MP_Watchpoint_f(void)
{
char *variable = Cmd_Argv(1);
if (!*variable)
variable = NULL;
if (Cmd_IsInsecure())
{
Con_TPrintf("Refusing to execute insecure %s\n", Cmd_Argv(0));
return;
}
if (!menu_world.progs)
{
Con_Printf("menuqc not running\n");
return;
}
if (menu_world.progs->SetWatchPoint(menu_world.progs, variable, variable))
Con_Printf("Watchpoint set\n");
else
Con_Printf("Watchpoint cleared\n");
}
static void MP_Profile_f(void)
{
if (Cmd_IsInsecure())
{
Con_TPrintf("Refusing to execute insecure %s\n", Cmd_Argv(0));
return;
}
if (menu_world.progs && menu_world.progs->DumpProfile)
if (!menu_world.progs->DumpProfile(menu_world.progs, !atof(Cmd_Argv(1))))
Con_Printf("Enabled menuqc Profiling.\n");
}
void MP_RegisterCvarsAndCmds(void)
{
Cmd_AddCommand("coredump_menuqc", MP_CoreDump_f);
Cmd_AddCommand("menu_cmd", MP_GameCommand_f);
Cmd_AddCommand("breakpoint_menu", MP_Breakpoint_f);
Cmd_AddCommand("breakpoint_menuqc", MP_Breakpoint_f);
Cmd_AddCommand("watchpoint_menuqc", MP_Watchpoint_f);
#ifdef HAVE_LEGACY
Cmd_AddCommand("loadfont", CL_LoadFont_f);
#endif
Cmd_AddCommand("poke_menuqc", MP_Poke_f);
Cmd_AddCommand("profile_menuqc", MP_Profile_f);
Cvar_Register(&forceqmenu, MENUPROGSGROUP);
@ -3546,6 +3615,8 @@ void MP_Draw(void)
*menu_world.g.frametime = host_frametime;
inmenuprogs++;
PR_RunThreads(&menu_world);
pr_globals = PR_globals(menu_world.progs, PR_CURRENT);
if (scr_drawloading||scr_disabled_for_loading)

View file

@ -52,6 +52,8 @@ typedef struct doll_s
struct doll_s *next;
qboolean drawn:1;
int refanim; //-1 for skining pose. otherwise the first pose of the specified anim. probaly 0.
float refanimtime; //usually just 0
int numdefaultanimated;
int numbodies;
int numjoints;
@ -349,6 +351,7 @@ static dollcreatectx_t *rag_createdoll(model_t *mod, const char *fname, int numb
ctx->d->next = dolllist;
ctx->d->name = strdup(fname);
ctx->d->model = mod;
ctx->d->refanim = -1; //skin pose.
ctx->d->numbodies = 0;
ctx->d->body = NULL;
ctx->d->numjoints = 0;
@ -377,6 +380,13 @@ static qboolean rag_dollline(dollcreatectx_t *ctx, int linenum)
if (!argc)
{
}
else if (argc == 2 && !stricmp(cmd, "refpose") && !strcmp(val, "skin"))
ctx->d->refanim = -1;
else if (argc == 3 && !stricmp(cmd, "refpose"))
{
ctx->d->refanim = atoi(val);
ctx->d->refanimtime = atoi(Cmd_Argv(2));
}
//create a new body
else if (argc == 3 && !stricmp(cmd, "body"))
{
@ -911,7 +921,13 @@ void skel_generateragdoll_f(void)
if (i == 0)
VFS_PUTS(f, "//NO FRAME INFO\n");
//print background frame info.
VFS_PUTS(f, "\n//reference pose that offsets are defined in terms of\n");
if (mod->type == mod_halflife)
VFS_PUTS(f, "refpose 0 0.0 //use first anim's first pose\n");
else
VFS_PUTS(f, "refpose skin\n");
//print background frame info.
VFS_PUTS(f, "\n//skins are as follows:\n");
for (i = 0; i < 32768; i++)
{
@ -1248,11 +1264,34 @@ static qboolean rag_instanciate(skelobject_t *sko, doll_t *doll, float *emat, we
int bone;
rbebody_t *body1, *body2;
rbejointinfo_t *j;
float *absolutes;
sko->numbodies = doll->numbodies;
sko->body = BZ_Malloc(sizeof(*sko->body) * sko->numbodies);
sko->doll = doll;
doll->uses++;
sko->numanimated = 0;
if (bones && doll->refanim)
{
framestate_t fstate = {0};
float *relatives = alloca(sizeof(float)*12*numbones*2);
fstate.g[FS_REG].frame[0] = doll->refanim; //which anim we're using as the reference
fstate.g[FS_REG].frametime[0] = doll->refanimtime; //first pose of the anim
fstate.g[FS_REG].lerpweight[0] = 1;
fstate.g[FS_REG].endbone = numbones;
absolutes = relatives+numbones*12;
numbones = Mod_GetBoneRelations(sko->model, 0, numbones, bones, &fstate, relatives);
for (i = 0; i < numbones; i++)
{ //compute the absolutes. not gonna make a bg3 reference here.
if (bones[i].parent>=0)
R_ConcatTransforms((void*)(absolutes+12*bones[i].parent), (void*)(relatives+12*i), (void*)(absolutes+12*i));
else
memcpy(absolutes+12*i, relatives+12*i, sizeof(float)*12);
}
}
else absolutes = NULL;
for (i = 0; i < sko->numbodies; i++)
{
memset(&sko->body[i], 0, sizeof(sko->body[i]));
@ -1262,7 +1301,9 @@ static qboolean rag_instanciate(skelobject_t *sko, doll_t *doll, float *emat, we
sko->numanimated++;
//spawn the body in the base pose, so we can add joints etc (also ignoring the entity matrix, we'll fix all that up later).
if (1)
if (absolutes) //we have a reference pose
memcpy(bodymat, absolutes+12*doll->body[i].bone, sizeof(float)*12);
else if (1)
Matrix3x4_Invert_Simple(bones[doll->body[i].bone].inverse, bodymat);
else
rag_genbodymatrix(sko, &doll->body[i], emat, bodymat);
@ -1281,8 +1322,10 @@ static qboolean rag_instanciate(skelobject_t *sko, doll_t *doll, float *emat, we
bone = j->bonepivot;
bmat = sko->bonematrix + bone*12;
if (1)
{
if (absolutes) //we have a reference pose
memcpy(worldmat, absolutes+12*doll->body[i].bone, sizeof(float)*12);
else if (1)
{ //FIXME: j->offset isn't actually used?!?
Matrix3x4_Invert_Simple(bones[j->bonepivot].inverse, worldmat);
}
else

View file

@ -53,21 +53,21 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#pragma warning(2:4032) // function arg has different type from declaration
#pragma warning(2:4092) // 'sizeof' value too big
#pragma warning(2:4132 4268)// const object not initialized
//#pragma warning(2:4152) // pointer conversion between function and data
//#pragma warning(2:4152) // pointer conversion between function and data
#pragma warning(2:4239) // standard doesn't allow this conversion
#pragma warning(2:4701) // local variable used without being initialized
//#pragma warning(2:4706) // if (a=b) instead of (if a==b)
//#pragma warning(2:4706) // if (a=b) instead of (if a==b)
#pragma warning(2:4709) // comma in array subscript
#pragma warning(3:4061) // not all enum values tested in switch statement
#pragma warning(3:4710) // inline function was not inlined
#pragma warning(3:4121) // space added for structure alignment
#pragma warning(3:4505) // unreferenced local function removed
#pragma warning(3:4019) // empty statement at global scope
//#pragma warning(3:4057) // pointers refer to different base types
//#pragma warning(3:4057) // pointers refer to different base types
#pragma warning(3:4125) // decimal digit terminates octal escape
#pragma warning(2:4131) // old-style function declarator
#pragma warning(3:4211) // extern redefined as static
//#pragma warning(3:4213) // cast on left side of = is non-standard
//#pragma warning(3:4213) // cast on left side of = is non-standard
#pragma warning(3:4222) // member function at file scope shouldn't be static
#pragma warning(3:4234 4235)// keyword not supported or reserved for future
#pragma warning(3:4504) // type ambiguous; simplify code
@ -132,10 +132,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#endif
#include <time.h>
#if defined(__unix__) && !defined(__CYGWIN__)
#include <sys/epoll.h>
#endif
#ifdef USE_MSVCRT_DEBUG
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
@ -408,7 +404,7 @@ void COM_AssertMainThread(const char *msg);
#endif
extern qboolean msg_suppress_1; // suppresses resolution and cache size console output
// an fullscreen DIB focus gain/loss
// an fullscreen DIB focus gain/loss
#ifndef HAVE_CLIENT
#define isDedicated true

View file

@ -1,7 +1,7 @@
#include "quakedef.h"
#ifdef MAP_PROC
#ifndef SERVERONLY
#ifdef HAVE_CLIENT
#include "shader.h"
#endif
#include "com_mesh.h"
@ -10,15 +10,9 @@
//fixme: merge areas and static ents too somehow.
void Mod_SetParent (mnode_t *node, mnode_t *parent);
static int D3_ClusterForPoint (struct model_s *model, vec3_t point);
#ifndef SERVERONLY
void ModD3_GenAreaVBO(void *ctx, void *data, size_t a, size_t b)
{
model_t *sub = ctx;
BE_GenBrushModelVBO(sub);
}
static int D3_ClusterForPoint (struct model_s *model, const vec3_t point, int *areaout);
#ifdef HAVE_CLIENT
static void R_BuildDefaultTexnums_Doom3(shader_t *shader)
{
extern qboolean r_loadbumpmapping;
@ -140,6 +134,37 @@ static void R_BuildDefaultTexnums_Doom3(shader_t *shader)
}
}
}
static void ModD3_GenAreaVBO(void *ctx, void *data, size_t a, size_t barg)
{
model_t *sub = ctx;
batch_t *b = sub->batches[0];
int surf;
sub->batches[0] = NULL;
for (surf = 0; surf < sub->numbatches; surf++)
sub->numsurfaces += b[surf].meshes;
sub->texinfo = ZG_Malloc(&sub->memgroup, sizeof(*sub->texinfo)*sub->numsurfaces);
sub->surfaces = ZG_Malloc(&sub->memgroup, sizeof(*sub->surfaces)*sub->numsurfaces);
sub->firstmodelsurface = sub->nummodelsurfaces = 0;
for (surf = 0; surf < sub->numbatches; surf++)
{
b[surf].shader = R_RegisterShader_Vertex(sub, b[surf].texture->name);
R_BuildDefaultTexnums_Doom3(b[surf].shader);
//now we know its sort key, we can link it properly. *sigh*
b[surf].next = sub->batches[b[surf].shader->sort];
sub->batches[b[surf].shader->sort] = &b[surf];
//all this extra stuff so r_showshaders works. *sigh*
sub->surfaces[sub->nummodelsurfaces].texinfo = &sub->texinfo[sub->nummodelsurfaces];
sub->surfaces[sub->nummodelsurfaces].texinfo->texture = b[surf].texture;
sub->surfaces[sub->nummodelsurfaces].mesh = b[surf].mesh[0];
sub->nummodelsurfaces++;
}
BE_GenBrushModelVBO(sub);
}
static qboolean Mod_LoadMap_Proc(model_t *model, char *data)
{
@ -186,8 +211,14 @@ static qboolean Mod_LoadMap_Proc(model_t *model, char *data)
if (strcmp(token, "{"))
return false;
data = COM_ParseOut(data, token, sizeof(token));
sub = Mod_FindName(va("*%s", token));
*token = '*';
data = COM_ParseOut(data, token+1, sizeof(token)-1);
Q_strncatz(token, ":", sizeof(token));
Q_strncatz(token, model->publicname, sizeof(token));
sub = Mod_FindName(token);
if (sub->loadstate != MLS_NOTLOADED)
return false;
data = COM_ParseOut(data, token, sizeof(token));
numsurfs = atoi(token);
@ -241,16 +272,14 @@ static qboolean Mod_LoadMap_Proc(model_t *model, char *data)
b[surf].lmlightstyle[3] = INVALID_LIGHTSTYLE;
data = COM_ParseOut(data, token, sizeof(token));
b[surf].shader = R_RegisterShader_Vertex(token);
R_BuildDefaultTexnums_Doom3(b[surf].shader);
b[surf].texture = ZG_Malloc(&sub->memgroup, sizeof(*b[surf].texture));
Q_strncpyz(b[surf].texture->name, token, sizeof(b[surf].texture->name));
data = COM_ParseOut(data, token, sizeof(token));
numverts = atoi(token);
data = COM_ParseOut(data, token, sizeof(token));
numindicies = atoi(token);
b[surf].next = sub->batches[b[surf].shader->sort];
sub->batches[b[surf].shader->sort] = &b[surf];
m[surf].numvertexes = numverts;
m[surf].numindexes = numindicies;
vdata = ZG_Malloc(&sub->memgroup, numverts * (sizeof(vecV_t) + sizeof(vec2_t) + sizeof(vec3_t)*3 + sizeof(vec4_t)) + numindicies * sizeof(index_t));
@ -334,10 +363,14 @@ static qboolean Mod_LoadMap_Proc(model_t *model, char *data)
if (strcmp(token, "}"))
return false;
// sub->loadstate = MLS_LOADED;
sub->fromgame = fg_doom3;
sub->fromgame = fg_new;
sub->type = mod_brush;
sub->lightmaps.surfstyles = 1;
memset(sub->batches, 0, sizeof(sub->batches));
sub->batches[0] = b;
sub->numbatches = numsurfs;
COM_AddWork(WG_MAIN, ModD3_GenAreaVBO, sub, NULL, MLS_LOADED, 0);
COM_AddWork(WG_MAIN, Mod_ModelLoaded, sub, NULL, MLS_LOADED, 0);
}
@ -536,7 +569,7 @@ static void D3_WalkPortal(model_t *mod, int start, vec_t bounds[4], unsigned cha
}
}
unsigned char *D3_CalcVis(model_t *mod, vec3_t org)
static void D3_PrepareFrame(model_t *mod, refdef_t *refdef, int inarea, int inclusters[2], pvsbuffer_t *vis, qbyte **entvis_out, qbyte **surfvis_out)
{
int start;
static qbyte visbuf[256];
@ -546,7 +579,7 @@ unsigned char *D3_CalcVis(model_t *mod, vec3_t org)
int area;
entity_t ent;
start = D3_ClusterForPoint(mod, org);
start = D3_ClusterForPoint(mod, refdef->vieworg, NULL);
/*figure out which area we're in*/
if (start < 0)
{
@ -588,13 +621,25 @@ unsigned char *D3_CalcVis(model_t *mod, vec3_t org)
V_AddEntity(&ent);
}
}
return usevis;
*entvis_out = *surfvis_out = usevis;
}
static void D3_StainNode (struct model_s *model, float *parms)
{
}
static void D3_LightPointValues (struct model_s *model, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)
{
/*basically require rtlighting for any light*/
VectorClear(res_diffuse);
VectorClear(res_ambient);
VectorClear(res_dir);
res_dir[2] = 1;
}
#endif
//edict system as opposed to q2 game dll system.
static void D3_FindTouchedLeafs (struct model_s *model, struct pvscache_s *ent, vec3_t cullmins, vec3_t cullmaxs)
static void D3_FindTouchedLeafs (struct model_s *model, struct pvscache_s *ent, const vec3_t cullmins, const vec3_t cullmaxs)
{
}
static qbyte *D3_ClusterPVS (struct model_s *model, int num, pvsbuffer_t *buffer, pvsmerge_t merge)
@ -602,12 +647,14 @@ static qbyte *D3_ClusterPVS (struct model_s *model, int num, pvsbuffer_t *buffer
memset(buffer->buffer, 0xff, buffer->buffersize);
return buffer->buffer;
}
static int D3_ClusterForPoint (struct model_s *model, vec3_t point)
static int D3_ClusterForPoint (struct model_s *model, const vec3_t point, int *areaout)
{
float p;
int c;
mnode_t *node;
node = model->nodes;
if (areaout)
*areaout = 0;
while(1)
{
p = DotProduct(point, node->plane->normal) + node->plane->dist;
@ -618,26 +665,12 @@ static int D3_ClusterForPoint (struct model_s *model, vec3_t point)
}
return 0;
}
static unsigned int D3_FatPVS (struct model_s *model, vec3_t org, pvsbuffer_t *pvsbuffer, qboolean merge)
static unsigned int D3_FatPVS (struct model_s *model, const vec3_t org, pvsbuffer_t *pvsbuffer, qboolean merge)
{
return 0;
}
static void D3_StainNode (struct mnode_s *node, float *parms)
{
}
static void D3_LightPointValues (struct model_s *model, vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)
{
/*basically require rtlighting for any light*/
VectorClear(res_diffuse);
VectorClear(res_ambient);
VectorClear(res_dir);
res_dir[2] = 1;
}
static qboolean D3_EdictInFatPVS (struct model_s *model, struct pvscache_s *edict, qbyte *pvsbuffer)
static qboolean D3_EdictInFatPVS (struct model_s *model, const struct pvscache_s *edict, const qbyte *pvsbuffer, const int *areas)
{
int i;
for (i = 0; i < edict->num_leafs; i++)
@ -879,7 +912,7 @@ static void D3_InsertClipBrush(cm_node_t *node, cm_brush_t *brush)
node->brushlist = brush;
}
static void D3_RecursiveSurfCheck (cm_node_t *node, float p1f, float p2f, vec3_t p1, vec3_t p2)
static void D3_RecursiveSurfCheck (cm_node_t *node, float p1f, float p2f, const vec3_t p1, const vec3_t p2)
{
float t1, t2, offset;
float frac, frac2;
@ -973,7 +1006,7 @@ return;
D3_RecursiveSurfCheck (node->child[side^1], midf, p2f, mid, p2);
}
static qboolean D3_Trace (struct model_s *model, int hulloverride, framestate_t *framestate, vec3_t axis[3], vec3_t p1, vec3_t p2, vec3_t mins, vec3_t maxs, qboolean capsule, unsigned int hitcontentsmask, struct trace_s *trace)
static qboolean D3_Trace (struct model_s *model, int hulloverride, const framestate_t *framestate, const vec3_t axis[3], const vec3_t p1, const vec3_t p2, const vec3_t mins, const vec3_t maxs, qboolean capsule, unsigned int hitcontentsmask, struct trace_s *trace)
{
int i;
float e1,e2;
@ -1055,21 +1088,21 @@ static qboolean D3_Trace (struct model_s *model, int hulloverride, framestate_t
return false;
}
static unsigned int D3_PointContents (struct model_s *model, vec3_t axis[3], vec3_t p)
static unsigned int D3_PointContents (struct model_s *model, const vec3_t axis[3], const vec3_t p)
{
cm_node_t *node = model->cnodes;
cm_brush_t *brush;
float t1;
unsigned int contents = 0;
int i;
vec3_t np;
if (axis)
{
vec3_t tmp;
VectorCopy(p, tmp);
p[0] = DotProduct(tmp, axis[0]);
p[1] = DotProduct(tmp, axis[1]);
p[2] = DotProduct(tmp, axis[2]);
np[0] = DotProduct(p, axis[0]);
np[1] = DotProduct(p, axis[1]);
np[2] = DotProduct(p, axis[2]);
p = np;
}
while(node)
@ -1106,7 +1139,7 @@ static unsigned int D3_PointContents (struct model_s *model, vec3_t axis[3], vec
return contents;
}
#define ensurenewtoken(t) buf = COM_ParseOut(buf, token, sizeof(token)); if (strcmp(token, t)) break;
#define ensurenewtoken(t) buf = COM_ParseOut(buf, token, sizeof(token)); if (strcmp(token, t)) break
static int D3_ParseContents(char *str)
{
@ -1173,7 +1206,13 @@ qboolean QDECL D3_LoadMap_CollisionMap(model_t *mod, void *buf, size_t bufsize)
if (!strcmp(token, "worldMap"))
cmod = mod;
else
{
Q_strncatz(token, ":", sizeof(token));
Q_strncatz(token, mod->publicname, sizeof(token));
cmod = Mod_FindName(token);
if (cmod->loadstate != MLS_NOTLOADED)
return false;
}
if (filever == 3)
{
@ -1298,7 +1337,7 @@ qboolean QDECL D3_LoadMap_CollisionMap(model_t *mod, void *buf, size_t bufsize)
ensurenewtoken(")");
buf = COM_ParseOut(buf, token, sizeof(token));
#ifndef SERVERONLY
#ifdef HAVE_CLIENT
// surf->shader = R_RegisterShader_Vertex(token);
// R_BuildDefaultTexnums_Doom3(NULL, surf->shader);
#endif
@ -1429,32 +1468,37 @@ qboolean QDECL D3_LoadMap_CollisionMap(model_t *mod, void *buf, size_t bufsize)
/*load up the .map so we can get some entities (anyone going to bother making a qc mod compatible with this?)*/
COM_StripExtension(mod->name, token, sizeof(token));
Mod_SetEntitiesString(mod, FS_LoadMallocFile(va("%s.map", token), NULL), true);
Q_strncatz(token, ".map", sizeof(token));
Mod_SetEntitiesString(mod, FS_LoadMallocFile(token, NULL), true);
mod->funcs.FindTouchedLeafs = D3_FindTouchedLeafs;
mod->funcs.NativeTrace = D3_Trace;
mod->funcs.PointContents = D3_PointContents;
mod->funcs.FatPVS = D3_FatPVS;
mod->funcs.ClusterForPoint = D3_ClusterForPoint;
mod->funcs.StainNode = D3_StainNode;
mod->funcs.LightPointValues = D3_LightPointValues;
mod->funcs.EdictInFatPVS = D3_EdictInFatPVS;
mod->funcs.ClusterPVS = D3_ClusterPVS;
#ifdef HAVE_CLIENT
mod->funcs.StainNode = D3_StainNode;
mod->funcs.LightPointValues = D3_LightPointValues;
mod->funcs.PrepareFrame = D3_PrepareFrame;
#endif
mod->fromgame = fg_doom3;
mod->type = mod_brush; //err, kinda, sorta, maybe.
mod->fromgame = fg_new;
/*that's the physics sorted*/
#ifndef SERVERONLY
#ifdef HAVE_CLIENT
if (!isDedicated)
{
COM_StripExtension(mod->name, token, sizeof(token));
buf = FS_LoadMallocFile(va("%s.proc", token), NULL);
Q_strncatz(token, ".proc", sizeof(token));
buf = FS_LoadMallocFile(token, NULL);
Mod_LoadMap_Proc(mod, buf);
BZ_Free(buf);
}
#endif
return true;
}

View file

@ -36,6 +36,7 @@ int webo_blocklightmapupdates; //0 no webo, &1=using threadedworld, &2=already u
#ifdef BEF_PUSHDEPTH
qboolean r_pushdepth;
#endif
qboolean r_dlightlightmaps; //updated each frame, says whether to do lightmap hack dlights.
extern cvar_t r_ambient;
@ -2314,7 +2315,7 @@ void Surf_GenBrushBatches(batch_t **batches, entity_t *ent)
currententity = ent;
currentmodel = ent->model;
if (model->nummodelsurfaces != 0 && r_dynamic.ival > 0)
if (model->nummodelsurfaces != 0 && r_dlightlightmaps && model->funcs.MarkLights)
{
for (k=rtlights_first; k<RTL_FIRST; k++)
{
@ -2999,7 +3000,7 @@ void Surf_DrawWorld (void)
currentmodel = cl.worldmodel;
currententity = &r_worldentity;
r_dynamic.ival = r_dynamic.value;
r_dlightlightmaps = !!r_dynamic.ival;
{
#ifdef THREADEDWORLD
@ -3026,8 +3027,8 @@ void Surf_DrawWorld (void)
{
r_dynamic.modified = false;
r_temporalscenecache.modified = false;
#ifdef RTLIGHT
Sh_CheckSettings(); //fiddle with r_dynamic vs r_shadow_realtime_dlight.
#ifdef RTLIGHTS
// Sh_CheckSettings(); //fiddle with r_dynamic vs r_shadow_realtime_dlight.
#endif
COM_WorkerPartialSync(webogenerating, &webogeneratingstate, true);
while (webostates)
@ -3219,7 +3220,7 @@ void Surf_DrawWorld (void)
if (webostate->cluster[0] == r_viewcluster && webostate->cluster[1] == r_viewcluster2)
VectorCopy(r_refdef.vieworg, webostate->lastpos);
r_dynamic.ival = -1; //don't waste time on dlighting models.
r_dlightlightmaps = false; //don't waste time on dlighting bmodels.
RSpeedEnd(RSPEED_WORLDNODE);
@ -3242,9 +3243,9 @@ void Surf_DrawWorld (void)
}
#endif
#ifdef RTLIGHT
#ifdef RTLIGHTS
if (r_shadow_realtime_dlight.ival || currentmodel->type != mod_brush || !(currentmodel->fromgame == fg_quake || currentmodel->fromgame == fg_halflife) || !currentmodel->funcs.MarkLights)
r_dynamic.ival = -1;
r_dlightlightmaps = false; //don't do double lighting.
#endif
Surf_PushChains(currentmodel->batches);
@ -3256,10 +3257,6 @@ void Surf_DrawWorld (void)
}
else if (currentmodel->type != mod_brush)
entvis = surfvis = NULL;
#ifdef MAP_PROC
else if (currentmodel->fromgame == fg_doom3)
entvis = surfvis = D3_CalcVis(currentmodel, r_origin);
#endif
#ifdef MAP_DOOM
else if (currentmodel->fromgame == fg_doom)
{

View file

@ -688,6 +688,7 @@ extern cvar_t r_lavastyle;
extern cvar_t r_slimestyle;
extern cvar_t r_telestyle;
extern cvar_t r_dynamic;
extern qboolean r_dlightlightmaps;
extern cvar_t r_temporalscenecache;
extern cvar_t r_novis;
extern cvar_t r_netgraph;

View file

@ -1802,7 +1802,7 @@ TRACE(("dbg: R_ApplyRenderer: starting on client state\n"));
memcpy(&currentrendererstate, newr, sizeof(currentrendererstate));
TRACE(("dbg: R_ApplyRenderer: S_Restart_f\n"));
if (!isDedicated)
if (!isDedicated && newr)
S_DoRestart(true);
#ifdef VM_UI
@ -1936,7 +1936,7 @@ TRACE(("dbg: R_ApplyRenderer: efrags\n"));
void R_ReloadRenderer_f (void)
{
#if !defined(CLIENTONLY) && (defined(Q2BSPS) || defined(Q3BSPS))
#if !defined(CLIENTONLY)
void *portalblob = NULL;
size_t portalsize = 0;
#endif
@ -2200,7 +2200,7 @@ static int QDECL R_SortRenderers(const void *av, const void *bv)
void R_RestartRenderer (rendererstate_t *newr)
{
#if !defined(CLIENTONLY) && (defined(Q2BSPS) || defined(Q3BSPS))
#if !defined(CLIENTONLY)
void *portalblob = NULL;
size_t portalsize = 0;
#endif
@ -2766,7 +2766,6 @@ int SignbitsForPlane (mplane_t *out)
}
return bits;
}
#if 1
void R_SetFrustum (float projmat[16], float viewmat[16])
{
float scale;
@ -2873,47 +2872,6 @@ void R_SetFrustum (float projmat[16], float viewmat[16])
p->signbits = SignbitsForPlane (p);
}
}
#else
void R_SetFrustum (void)
{
int i;
if (r_novis.ival & 4)
return;
/* removed - assumes fov_x == fov_y
if (r_refdef.fov_x == 90)
{
// front side is visible
VectorAdd (vpn, vright, frustum[0].normal);
VectorSubtract (vpn, vright, frustum[1].normal);
VectorAdd (vpn, vup, frustum[2].normal);
VectorSubtract (vpn, vup, frustum[3].normal);
}
else
*/
{
// rotate VPN right by FOV_X/2 degrees
RotatePointAroundVector( frustum[0].normal, vup, vpn, -(90-r_refdef.fov_x / 2 ) );
// rotate VPN left by FOV_X/2 degrees
RotatePointAroundVector( frustum[1].normal, vup, vpn, 90-r_refdef.fov_x / 2 );
// rotate VPN up by FOV_X/2 degrees
RotatePointAroundVector( frustum[2].normal, vright, vpn, 90-r_refdef.fov_y / 2 );
// rotate VPN down by FOV_X/2 degrees
RotatePointAroundVector( frustum[3].normal, vright, vpn, -( 90 - r_refdef.fov_y / 2 ) );
}
for (i=0 ; i<4 ; i++)
{
frustum[i].type = PLANE_ANYZ;
frustum[i].dist = DotProduct (r_origin, frustum[i].normal);
frustum[i].signbits = SignbitsForPlane (&frustum[i]);
}
}
#endif

View file

@ -33,6 +33,11 @@ static cvar_t scr_scoreboard_drawtitle = CVARD("scr_scoreboard_drawtitle", "1",
static cvar_t scr_scoreboard_forcecolors = CVARD("scr_scoreboard_forcecolors", "0", "Makes the scoreboard colours obey enemycolor/teamcolor rules."); //damn americans
static cvar_t scr_scoreboard_newstyle = CVARD("scr_scoreboard_newstyle", "1", "Display team colours and stuff in a style popularised by Electro. Looks more modern, but might not quite fit classic huds."); // New scoreboard style ported from Electro, by Molgrum
static cvar_t scr_scoreboard_showfrags = CVARD("scr_scoreboard_showfrags", "0", "Display kills+deaths+teamkills, as determined by fragfile.dat-based conprint parsing. These may be inaccurate if you join mid-game.");
#ifdef QUAKESTATS
static cvar_t scr_scoreboard_showlocation = CVARD("scr_scoreboard_showlocation", "1", "Display player location names when playing mvd/qtv streams, if available.");
static cvar_t scr_scoreboard_showhealth = CVARD("scr_scoreboard_showhealth", "3", "Display health information when playing mvd/qtv streams.\n0: off\n1: on\n2: show armour too. 3: combined health ('+' says more armour than health allows).");
static cvar_t scr_scoreboard_showweapon = CVARD("scr_scoreboard_showweapon", "1", "Display weapon information when playing mvd/qtv streams.");
#endif
static cvar_t scr_scoreboard_showflags = CVARD("scr_scoreboard_showflags", "2", "Display flag caps+touches on the scoreboard, where our fragfile.dat supports them.\n0: off\n1: on\n2: on only if someone appears to have interacted with a flag.");
static cvar_t scr_scoreboard_fillalpha = CVARD("scr_scoreboard_fillalpha", "0.7", "Transparency amount for newstyle scoreboard.");
static cvar_t scr_scoreboard_backgroundalpha = CVARD("scr_scoreboard_backgroundalpha", "0.5", "Further multiplier for the background alphas.");
@ -1178,6 +1183,11 @@ void Sbar_Init (void)
Cvar_Register(&scr_scoreboard_forcecolors, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_newstyle, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_showfrags, "Scoreboard settings");
#ifdef QUAKESTATS
Cvar_Register(&scr_scoreboard_showlocation, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_showhealth, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_showweapon, "Scoreboard settings");
#endif
Cvar_Register(&scr_scoreboard_showflags, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_showruleset, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_afk, "Scoreboard settings");
@ -3619,6 +3629,12 @@ ping time frags name
code \
} \
}, fill)
#define COLUMN_STAT2(title, width, code, fill) COLUMN(title, width, { \
if (!s->spectator) \
{ \
code \
} \
}, fill)
#define COLUMN_RULESET COLUMN(ruleset, 8*8, {Draw_FunStringWidth(x, y, s->ruleset, 8*8+4, false, false);},NOFILL)
#define COLUMN_NAME COLUMN(name, namesize, {Draw_FunStringWidth(x, y, s->name, namesize, false, highlight);},NOFILL)
#define COLUMN_KILLS COLUMN_STAT(kils, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetKills(k)), 4*8+4, false, false);},NOFILL)
@ -3626,11 +3642,46 @@ ping time frags name
#define COLUMN_DEATHS COLUMN_STAT(dths, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetDeaths(k)), 4*8+4, false, false);},NOFILL)
#define COLUMN_TOUCHES COLUMN_STAT(tchs, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetTouches(k)), 4*8+4, false, false);},NOFILL)
#define COLUMN_CAPS COLUMN_STAT(caps, 4*8, {Draw_FunStringWidth(x, y, va("%4i", Stats_GetCaptures(k)), 4*8+4, false, false);},NOFILL)
#ifdef QUAKESTATS
#define COLUMN_HEALTH COLUMN_STAT2(hlth, (scr_scoreboard_showhealth.ival==2)?7*8:4*8, { \
float t;int c; \
int a = cl.players[k].stats[STAT_ARMOR]; \
int h = cl.players[k].stats[STAT_HEALTH]; \
if (cl.players[k].stats[STAT_ITEMS] & IT_ARMOR3) {c='1';t = 0.8f;}\
else if (cl.players[k].stats[STAT_ITEMS] & IT_ARMOR2) {c='3';t = 0.6f;}\
else if (cl.players[k].stats[STAT_ITEMS] & IT_ARMOR1) {c='2';t = 0.3f;}\
else {c='7';t = 0.f ;}\
if (h <= 0) /*draw nothing*/ ; \
else if (scr_scoreboard_showhealth.ival==3&&a>0) { int m = h/(1-t); Draw_FunStringWidth(x, y, va(a>m?"^%c%+4i":"^%c%4i", c,h + bound(0, m, a)), 4*8+4, false, false); } \
else if (scr_scoreboard_showhealth.ival==2) Draw_FunStringWidth(x, y, (a>0)?va("^%c%3i^7/%-3i", c,a,h):va("---/%-3i", h), 7*8+4, false, false); \
else Draw_FunStringWidth(x, y, va("%4i", h), 4*8+4, false, false); \
},NOFILL)
#define COLUMN_BESTWEAPON COLUMN_STAT2(wep, 4*8, { \
Draw_FunStringWidth(x, y, va("%s%s%s", \
(cl.players[k].stats[STAT_ITEMS] & IT_LIGHTNING)?"^5L":" ", \
(cl.players[k].stats[STAT_ITEMS] & IT_ROCKET_LAUNCHER)?"^1R":" ", \
(cl.players[k].stats[STAT_ITEMS] & IT_GRENADE_LAUNCHER)?"^2G":" " \
), 4*8+4, false, false); \
},NOFILL)
#define COLUMN_LOCATION COLUMN_STAT2(loc, 8*8, { \
lerpents_t *le; \
const char *loc; \
if (k+1 < cl.maxlerpents && cl.lerpentssequence && cl.lerpents[k+1].sequence == cl.lerpentssequence) le = &cl.lerpents[k+1]; \
else if (cl.lerpentssequence && cl.lerpplayers[k].sequence == cl.lerpentssequence) le = &cl.lerpplayers[k];\
else le = NULL;\
loc = le?TP_LocationName(le->origin):""; \
Draw_FunStringWidth(x, y, loc, 8*8+4, false, false); \
},NOFILL)
#else
#define COLUMN_HEALTH
#define COLUMN_BESTWEAPON
#define COLUMN_LOCATION
#endif
#define COLUMN_AFK COLUMN(afk, 0, {int cs = atoi(InfoBuf_ValueForKey(&s->userinfo, "chat")); if (cs)Draw_FunStringWidth(x+4, y, (cs&2)?"afk":"msg", 4*8, false, false);},NOFILL)
//columns are listed here in display order
#define ALLCOLUMNS COLUMN_PING COLUMN_PL COLUMN_TIME COLUMN_RULESET COLUMN_FRAGS COLUMN_TEAMNAME COLUMN_NAME COLUMN_KILLS COLUMN_TKILLS COLUMN_DEATHS COLUMN_TOUCHES COLUMN_CAPS COLUMN_AFK
#define ALLCOLUMNS COLUMN_PING COLUMN_PL COLUMN_TIME COLUMN_RULESET COLUMN_FRAGS COLUMN_TEAMNAME COLUMN_NAME COLUMN_HEALTH COLUMN_BESTWEAPON COLUMN_LOCATION COLUMN_KILLS COLUMN_TKILLS COLUMN_DEATHS COLUMN_TOUCHES COLUMN_CAPS COLUMN_AFK
enum
{
@ -3674,12 +3725,15 @@ void Sbar_DeathmatchOverlay (playerview_t *pv, int start)
cl.last_ping_request = realtime;
CL_SendClientCommand(true, "pings");
}
#ifdef NQPROT
else if (cls.protocol == CP_NETQUAKE && !cls.qex)
{
cl.last_ping_request = realtime;
CL_SendClientCommand(true, "ping");
}
#endif
}
#ifdef NQPROT
if (cls.protocol == CP_NETQUAKE && !cls.qex)
{
if (cl.nqplayernamechanged && cl.nqplayernamechanged < realtime)
@ -3689,6 +3743,7 @@ void Sbar_DeathmatchOverlay (playerview_t *pv, int start)
CL_SendClientCommand(true, "status");
}
}
#endif
if (start)
y = start;
@ -3768,6 +3823,20 @@ void Sbar_DeathmatchOverlay (playerview_t *pv, int start)
COLUMN_TKILLS
}
}
#ifdef QUAKESTATS
if (scr_scoreboard_showhealth.ival && cls.demoplayback==DPB_MVD)
{
COLUMN_HEALTH
}
if (scr_scoreboard_showweapon.ival && cls.demoplayback==DPB_MVD)
{
COLUMN_BESTWEAPON
}
if (scr_scoreboard_showlocation.ival && cls.demoplayback==DPB_MVD && TP_HaveLocations())
{
COLUMN_LOCATION
}
#endif
if (scr_scoreboard_showflags.ival && cl.teamplay && Stats_HaveFlags(scr_scoreboard_showflags.ival&1))
{
COLUMN_TOUCHES

View file

@ -322,7 +322,7 @@ void Skin_WorkerLoad(void *skinptr, void *data, size_t a, size_t b)
{
qwskin_t *skin = skinptr;
char name[MAX_QPATH];
qbyte *out;
qbyte *out = NULL;
int srcw = 0, srch = 0;
size_t pcxsize = 0;
@ -362,9 +362,14 @@ void Skin_WorkerLoad(void *skinptr, void *data, size_t a, size_t b)
Q_snprintfz (name, sizeof(name), "skins/%s.pcx", skin->name);
pcxfiledata = FS_LoadMallocFileFlags (name, FSLF_IGNOREPURE, &pcxsize);
if (!pcxfiledata)
{
//use 24bit skins even if gl_load24bit is failed
if (strcmp(skin->name, baseskin.string))
{ //FIXME: use 24bit skins even if gl_load24bit is failed
if (!strcmp(skin->name, "solid") || !strcmp(skin->name, "block"))
{ //allow block colour, even if the file isn't found.
srcw = srch = 1;
out = BZ_Malloc(srcw*srch);
memset(out, BOTTOM_DEFAULT | 15, srcw*srch);
}
else if (strcmp(skin->name, baseskin.string))
{
//if its not already the base skin, try the base (and warn if anything not base couldn't load).
Con_Printf ("Couldn't load skin %s\n", name);
@ -374,15 +379,12 @@ void Skin_WorkerLoad(void *skinptr, void *data, size_t a, size_t b)
pcxfiledata = FS_LoadMallocFileFlags (name, FSLF_IGNOREPURE, &pcxsize);
}
}
if (!pcxfiledata)
{
Skin_WorkerDone(skin, NULL, 0, 0);
return;
}
}
}
if (pcxfiledata)
if (out)
;
else if (pcxfiledata)
{
out = Skin_ParsePCX(name, pcxfiledata, pcxsize, &srcw, &srch);
FS_FreeFile(pcxfiledata);

View file

@ -434,7 +434,8 @@ typedef struct
ALuint handle;
qbyte allocated; //there is no guarenteed-unused handle (and I don't want to have to keep spamming alIsSource).
qbyte queuesize;
ALuint queue[3];
ALuint queue_b[3];
usamplepos_t queue_f[3];
} *source;
size_t max_sources;
@ -746,7 +747,7 @@ static qboolean OpenAL_ReclaimASource(soundcardinfo_t *sc)
{
palDeleteSources(1, &src);
if (oali->source[i].queuesize)
palDeleteBuffers(oali->source[i].queuesize, oali->source[i].queue);
palDeleteBuffers(oali->source[i].queuesize, oali->source[i].queue_b);
oali->source[i].queuesize = 0;
oali->source[i].handle = 0;
oali->source[i].allocated = false;
@ -778,7 +779,7 @@ static qboolean OpenAL_ReclaimASource(soundcardinfo_t *sc)
i = furthest;
palDeleteSources(1, &oali->source[i].handle);
if (oali->source[i].queuesize)
palDeleteBuffers(oali->source[i].queuesize, oali->source[i].queue);
palDeleteBuffers(oali->source[i].queuesize, oali->source[i].queue_b);
oali->source[i].queuesize = 0;
oali->source[i].handle = 0;
oali->source[i].allocated = false;
@ -802,7 +803,32 @@ static ssamplepos_t OpenAL_GetChannelPos(soundcardinfo_t *sc, channel_t *chan)
//alcMakeContextCurrent
palGetSourcei(src, AL_SAMPLE_OFFSET, &spos);
if (oali->source[chnum].queuesize)
{ //we're streaming, for whatever reason.
ssamplepos_t pos;
ALuint processed;
int i;
//reclaim any queued buffers
palGetSourcei(src, AL_BUFFERS_PROCESSED, &processed); //get number of buffers
palGetSourcei(src, AL_SAMPLE_OFFSET, &spos); //get our position within the current one.
if (processed)
{
palSourceUnqueueBuffers(src, processed, oali->source[chnum].queue_b);
palDeleteBuffers(processed, oali->source[chnum].queue_b);
oali->source[chnum].queuesize -= processed;
memmove(oali->source[chnum].queue_b, oali->source[chnum].queue_b+processed, oali->source[chnum].queuesize*sizeof(*oali->source[chnum].queue_b));
memmove(oali->source[chnum].queue_f, oali->source[chnum].queue_f+processed, oali->source[chnum].queuesize*sizeof(*oali->source[chnum].queue_f));
}
pos = chan->pos>>PITCHSHIFT; //this is the point of thedata that was already submitted to openal.
for (i = 0; i < oali->source[chnum].queuesize; i++)
pos -= oali->source[chnum].queue_f[i];
//pos is now 'chan->pos at start of current buffer'
pos += spos; //current playback position (should always be smaller than chan->pos originally was...)
return pos;
}
else
palGetSourcei(src, AL_SAMPLE_OFFSET, &spos);
return spos; //FIXME: result is probably going to be wrong when streaming
}
@ -860,7 +886,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, chanupdat
palSourceStop(src);
palSourcei(src, AL_BUFFER, 0);
if (oali->source[chnum].queuesize)
palDeleteBuffers(oali->source[chnum].queuesize, oali->source[chnum].queue);
palDeleteBuffers(oali->source[chnum].queuesize, oali->source[chnum].queue_b);
oali->source[chnum].queuesize = 0;
}
@ -870,10 +896,11 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, chanupdat
palGetSourcei(src, AL_BUFFERS_PROCESSED, &processed);
if (processed)
{
palSourceUnqueueBuffers(src, processed, oali->source[chnum].queue);
palDeleteBuffers(processed, oali->source[chnum].queue);
palSourceUnqueueBuffers(src, processed, oali->source[chnum].queue_b);
palDeleteBuffers(processed, oali->source[chnum].queue_b);
oali->source[chnum].queuesize -= processed;
memmove(oali->source[chnum].queue, oali->source[chnum].queue+processed, oali->source[chnum].queuesize*sizeof(*oali->source[chnum].queue));
memmove(oali->source[chnum].queue_b, oali->source[chnum].queue_b+processed, oali->source[chnum].queuesize*sizeof(*oali->source[chnum].queue_b));
memmove(oali->source[chnum].queue_f, oali->source[chnum].queue_f+processed, oali->source[chnum].queuesize*sizeof(*oali->source[chnum].queue_f));
}
}
@ -902,7 +929,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, chanupdat
#else
palDeleteSources(1, &src);
if (oali->source[chnum].queuesize)
palDeleteBuffers(oali->source[chnum].queuesize, oali->source[chnum].queue);
palDeleteBuffers(oali->source[chnum].queuesize, oali->source[chnum].queue_b);
oali->source[chnum].queuesize = 0;
oali->source[chnum].handle = 0;
oali->source[chnum].allocated = false;
@ -947,7 +974,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, chanupdat
{
int offset;
sfxcache_t sbuf, *sc;
while (oali->source[chnum].queuesize < countof(oali->source[chnum].queue))
while (oali->source[chnum].queuesize < countof(oali->source[chnum].queue_b))
{ //decode periodically instead of all at the start.
int tryduration = snd_speed*0.5;
ssamplepos_t pos = chan->pos>>PITCHSHIFT;
@ -985,7 +1012,9 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, chanupdat
if (OpenAL_LoadCache(oali, &buf, &sbuf, max(1,cvolume), 0))
{
palSourceQueueBuffers(src, 1, &buf);
oali->source[chnum].queue[oali->source[chnum].queuesize++] = buf;
oali->source[chnum].queue_b[oali->source[chnum].queuesize] = buf;
oali->source[chnum].queue_f[oali->source[chnum].queuesize] = sbuf.length;
oali->source[chnum].queuesize++;
}
}
else
@ -1000,7 +1029,9 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, chanupdat
if (OpenAL_LoadCache(oali, &buf, &silence, 1, 0))
{
palSourceQueueBuffers(src, 1, &buf);
oali->source[chnum].queue[oali->source[chnum].queuesize++] = buf;
oali->source[chnum].queue_b[oali->source[chnum].queuesize] = buf;
oali->source[chnum].queue_f[oali->source[chnum].queuesize] = 0; //don't count silence.
oali->source[chnum].queuesize++;
}
}
@ -1031,7 +1062,9 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, chanupdat
if (OpenAL_LoadCache(oali, &buf, &silence, 1, 0))
{
palSourceQueueBuffers(src, 1, &buf);
oali->source[chnum].queue[oali->source[chnum].queuesize++] = buf;
oali->source[chnum].queue_b[oali->source[chnum].queuesize] = buf;
oali->source[chnum].queue_f[oali->source[chnum].queuesize] = 0; //don't count silence.
oali->source[chnum].queuesize++;
if (oali->can_source_spatialise) //force spacialisation as desired, if supported (this solves browsers forcing stereo on mono files which should mean static audio is full volume...)
palSourcei(src, AL_SOURCE_SPATIALIZE_SOFT, !srcrel);
}

View file

@ -4214,7 +4214,7 @@ typedef struct {
int length;
void *data;
} streaming_t;
#define MAX_RAW_SOURCES (MAX_CLIENTS+1)
#define MAX_RAW_SOURCES (MAX_CLIENTS+3)
streaming_t s_streamers[MAX_RAW_SOURCES];
void S_ClearRaw(void)
@ -4256,8 +4256,47 @@ void QDECL S_Raw_Purge(sfx_t *sfx)
memset(&sfx->decoder, 0, sizeof(sfx->decoder));
}
float S_RawAudioQueued(int sourceid) //returns in seconds. we don't know what the original sample count was.
{
soundcardinfo_t *si;
streaming_t *s;
int i;
float r;
ssamplepos_t highest, pos;
for (s = s_streamers, i = 0; i < MAX_RAW_SOURCES; i++, s++)
{
if (s->inuse && s->id == sourceid)
{
S_LockMixer();
highest = ((~(usamplepos_t)0)>>1);
for (si = sndcardinfo; si; si=si->next) //make sure all cards are playing, and that we still get a prepad if just one is.
{
for (i = 0; i < si->total_chans; i++)
if (si->channel[i].sfx == s->sfx)
{
if (si->GetChannelPos)
pos = si->GetChannelPos(si, &si->channel[i]);
else
pos = si->channel[i].pos>>PITCHSHIFT;
if (highest > pos)
highest = pos;
break;
}
}
if (highest == ((~(usamplepos_t)0)>>1))
r = 0; //nothing playing it... needs to be woken up. pretend nothing is there so it gets poked a bit.
else
r = (s->length - highest) / (float)snd_speed;
S_UnlockMixer();
return r;
}
}
return 0; //not found
}
//streaming audio. //this is useful when there is one source, and the sound is to be played with no attenuation
void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels, qaudiofmt_t format, float volume)
void S_RawAudio(int sourceid, const qbyte *data, int speed, int samples, int channels, qaudiofmt_t format, float volume)
{
soundcardinfo_t *si;
int i;

View file

@ -291,11 +291,11 @@ static wavinfo_t GetWavinfo (char *name, qbyte *wav, int wavlength);
// SND_ResampleStream: takes a sound stream and converts with given parameters. Limited to
// 8-16-bit signed conversions and mono-to-mono/stereo-to-stereo conversions.
// Not an in-place algorithm.
void SND_ResampleStream (void *in, int inrate, qaudiofmt_t informat, int inchannels, int insamps, void *out, int outrate, qaudiofmt_t outformat, int outchannels, int resampstyle)
void SND_ResampleStream (const void *in, int inrate, qaudiofmt_t informat, int inchannels, int insamps, void *out, int outrate, qaudiofmt_t outformat, int outchannels, int resampstyle)
{
double scale;
signed char *in8 = (signed char *)in;
short *in16 = (short *)in;
const signed char *in8 = (const signed char *)in;
const short *in16 = (const short *)in;
signed char *out8 = (signed char *)out;
short *out16 = (short *)out;
int outsamps, outnlsamps, outsampleft, outsampright;

View file

@ -262,7 +262,7 @@ qboolean S_IsPlayingSomewhere(sfx_t *s);
// picks a channel based on priorities, empty slots, number of channels
channel_t *SND_PickChannel(soundcardinfo_t *sc, int entnum, int entchannel);
void SND_ResampleStream (void *in, int inrate, qaudiofmt_t inwidth, int inchannels, int insamps, void *out, int outrate, qaudiofmt_t outwidth, int outchannels, int resampstyle);
void SND_ResampleStream (const void *in, int inrate, qaudiofmt_t inwidth, int inchannels, int insamps, void *out, int outrate, qaudiofmt_t outwidth, int outchannels, int resampstyle);
// restart entire sound subsystem (doesn't flush old sounds, so make sure that happens)
void S_DoRestart (qboolean onlyifneeded);
@ -270,7 +270,13 @@ void S_DoRestart (qboolean onlyifneeded);
void S_Restart_f (void);
//plays streaming audio
void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels, qaudiofmt_t width, float volume);
#define SOURCEID_MENUQC -3
#define SOURCEID_CSQC -2
#define SOURCEID_CINEMATIC -1
#define SOURCEID_VOIP_FIRST 0
#define SOURCEID_VOIP_MAX MAX_CLIENTS-1
void S_RawAudio(int sourceid, const qbyte *data, int speed, int samples, int channels, qaudiofmt_t width, float volume);
float S_RawAudioQueued(int sourceid);
void CLVC_Poll (void);

View file

@ -66,13 +66,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#undef malloc
#if defined(__unix__) && !defined(__CYGWIN__)
#include <sys/epoll.h>
#endif
static int noconinput = 0;
static int nostdout = 0;
static FILE *dedcon; //replaces our stdin/out when set.
static int dedconproc = -1;
extern int isPlugin;
int sys_parentleft;
int sys_parenttop;
@ -86,10 +85,77 @@ static void Sys_InitClock(void);
qboolean Sys_InitTerminal (void) //we either have one or we don't.
{
return isatty(STDIN_FILENO);
if (isatty(STDIN_FILENO))
return true; //already attached to a terminal in some form. don't need another.
if (dedcon) //already got one open... shouldn't really happen. future paranoia.
return true;
else
{
int pty = posix_openpt(O_RDWR|O_NOCTTY);
if (pty >= 0)
{
int fd;
char *slavename, window[64], buf[64];
grantpt(pty);
unlockpt(pty);
slavename = ptsname(pty);
dedcon = fopen(slavename, "r+e");
if (dedcon)
{
snprintf(buf, sizeof buf, "-S%s/%d", strrchr(slavename,'/')+1, pty);
dedconproc = fork();
if(!dedconproc)
{ //oh hey, we're the child.
execlp("xterm", "xterm", buf, (char *)0);
_exit(1);
}
close(pty); //can close it now, we'll keep track of it with our slave fd
if (dedconproc >= 0)
{ //if the xterm fails, does this EPIPE properly?
fgets(window, sizeof window, dedcon);
//printf("window: %s\n", window);
//switch input to non-blocking, so we can actually still do stuff...
fd = fileno(dedcon);
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
#ifdef HAVE_EPOLL
{
extern int epoll_fd;
if (epoll_fd >= 0)
{
struct epoll_event event = {EPOLLIN, {NULL}};
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event);
}
}
#else
//FIXME: NET_Sleep needs to wake up on dedcon input
#endif
return true;
}
//else fork failed.
fclose(dedcon);
dedcon = NULL;
}
close(pty);
}
}
return false; //nope, soz
}
void Sys_CloseTerminal (void)
{
if (dedcon)
{
#ifdef HAVE_EPOLL
extern int epoll_fd;
if (epoll_fd >= 0)
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fileno(dedcon), NULL); //don't get confused if the FD# is later reopened as something else...
#endif
fclose(dedcon);
dedcon = NULL;
}
dedconproc = -1;
}
void Sys_RecentServer(char *command, char *target, char *title, char *desc)
@ -186,7 +252,9 @@ void Sys_Printf (char *fmt, ...)
}
#endif
if (nostdout)
if (dedcon)
out = dedcon;
else if (nostdout)
{
#ifdef _DEBUG
out = stderr;
@ -1137,6 +1205,27 @@ char *Sys_ConsoleInput(void)
static char text[256];
char *nl;
if (dedcon)
{
int e;
if (fgets(text, sizeof(text), dedcon))
return text;
e = errno;
switch(e)
{
case EAGAIN:
case EINTR: //not meant to be blocking, but can still be interrupted.
break; //cos we made it non-blocking.
case EPIPE: //not seen, but possible I guess.
case EIO: //can happen if the other end dies.
Sys_CloseTerminal();
return "quit"; //kill the server if we were actually using the terminal... or at least try not to run silently.
default:
Sys_Printf(CON_WARNING "fgets errno %i\n", e);
break;
}
}
if (noconinput)
return NULL;
@ -1227,7 +1316,9 @@ static void DoSign(const char *fname, int signtype)
}
else if (f)
{
hashfunc_t *h = (signtype==1)?&hash_sha2_256:&hash_sha2_512;
hashfunc_t *h = (signtype==1)?&hash_sha1:
(signtype==256)?&hash_sha2_256:
&hash_sha2_512;
size_t l, ts = 0;
void *ctx = alloca(h->contextsize);
qbyte data[65536*16];
@ -1350,6 +1441,29 @@ static void SigCont(int code)
fcntl(STDIN_FILENO, F_SETFL, fl | FNDELAY);
noconinput &= ~2;
}
static void SigChldTerminalDied(int code, void *data)
{ //our terminal dieded? restart again (this shouldn't loop infinitely...)
Sys_CloseTerminal();
Cbuf_AddText ("vid_renderer \"\"; vid_restart\n", RESTRICT_LOCAL);
}
static void SigChld(int code)
{
int wstat;
pid_t pid;
for(;;)
{
pid = wait3 (&wstat, WNOHANG, (struct rusage *)NULL);
if (pid == -1)
return; //error
else if (pid == 0)
return; //nothing left to report (linux seems to like errors instead)
else if (pid == dedconproc)
Cmd_AddTimer(0, SigChldTerminalDied, 0, NULL, 0);
// else //forked subserver? we use pipes to track when it dies.
// printf ("Return code: %d\n", wstat);
}
}
#endif
int main (int c, const char **v)
{
@ -1362,7 +1476,7 @@ int main (int c, const char **v)
#ifdef _POSIX_C_SOURCE
signal(SIGTTIN, SIG_IGN); //have to ignore this if we want to not lock up when running backgrounded.
signal(SIGCONT, SigCont);
signal(SIGCHLD, SIG_IGN); //mapcluster stuff might leak zombie processes if we don't do this.
signal(SIGCHLD, SigChld); //mapcluster stuff might leak zombie processes if we don't do this.
#endif

View file

@ -2072,9 +2072,12 @@ void R_DrawNameTags(void)
else
{
shader = NULL;
#ifdef TERRAIN
if (cl.worldmodel->terrain && trace.brush_id && (shader = Terr_GetShader(cl.worldmodel, &trace)))
shadername = shader->name;
else if ((surf = (trace.fraction == 1)?NULL:Mod_GetSurfaceNearPoint(cl.worldmodel, trace.endpos)))
else
#endif
if ((surf = (trace.fraction == 1)?NULL:Mod_GetSurfaceNearPoint(cl.worldmodel, trace.endpos)))
{
shadername = surf->texinfo->texture->name;
shader = surf->texinfo->texture->shader;

View file

@ -626,6 +626,24 @@ miptex_t *W_GetMipTex(const char *name)
return NULL;
}
void WAD_ImageList_f(void)
{
wadfile_t *wad;
int i;
char *match = Cmd_Argv(1);
Sys_LockMutex(wadmutex);
for (i = 0;i < numwadtextures;i++)
{
if (*match && !wildcmp(match, texwadlump[i].name))
continue;
for (wad = openwadfiles; wad; wad = wad->next)
if (wad->file == texwadlump[i].file)
break;
Con_Printf("^[\\img\\%s\\s\\%i\\tip\\From inside %s^] %s\n", texwadlump[i].name, 64, wad?wad->name:"<unknown>", texwadlump[i].name);
}
Sys_UnlockMutex(wadmutex);
}
typedef struct mapgroup_s {
char *mapname;
char *skyname;

View file

@ -1529,16 +1529,16 @@ TP_ParseFunChars
Doesn't check for overflows, so strlen(s) should be < MAX_MACRO_STRING
==============
*/
char *TP_ParseFunChars (char *s)
const char *TP_ParseFunChars (const char *s)
{
static char buf[MAX_MACRO_STRING];
char *out = buf;
int c;
if (!cl_parseFunChars.ival)
if (!cl_parseFunChars.ival || com_parseutf8.ival != 0)
return s;
while (*s) {
while (*s && out < buf+countof(buf)-1) {
if (*s == '$' && s[1] == 'x') {
int i;
// check for $x10, $x8a, etc
@ -1788,7 +1788,10 @@ static void TP_LoadLocFile_f (void)
TP_LoadLocFile (Cmd_Argv(1), false);
}
qboolean TP_HaveLocations(void)
{
return loc_numentries>0;
}
char *TP_LocationName (const vec3_t location)
{
int i, j, minnum;
@ -3885,7 +3888,11 @@ void CL_Say (qboolean team, char *extra)
//messagemode always adds quotes. the console command never did.
//the server is expected to use Cmd_Args and to strip first+last chars if the first is a quote. this is annoying and clumsy for mods to parse.
#ifdef HAVE_LEGACY
if (!dpcompat_console.ival && !cls.qex)
if (!dpcompat_console.ival
#ifdef NQPROT
&& !cls.qex
#endif
)
CL_SendSeatClientCommand(true, split, "%s \"%s%s\"", team ? "say_team" : "say", extra?extra:"", sendtext);
else
#endif

View file

@ -254,8 +254,7 @@ void Cmd_AddTimer(float delay, void(*callback)(int iarg, void *data), int iarg,
n->timer = realtime + delay;
n->next = cmdtimers;
cmdtimers = n;
FTE_Atomic_Insert(cmdtimers, n, n->next);
}
static void Cmd_In_Callback(int iarg, void *data)
{
@ -715,6 +714,9 @@ static const char *replacementq1binds =
"bind F10 menu_quit\n"
// "bind F11 +zoom\n"
"bind F12 screenshot\n"
"bind volup \"if $volume < 0.9 then inc volume 0.1 else if $volume < 1.0 then set volume 1\"\n"
"bind voldown \"inc volume -0.1; if $volume < 0 then set volume 0\"\n"
;
static const char *defaulttouchcfg =
"showpic_removeall\n"
@ -1038,11 +1040,11 @@ Cmd_Echo_f
Just prints the rest of the line to the console
===============
*/
char *TP_ParseFunChars (char *s);
static void Cmd_Echo_f (void)
{
char text[4096];
char extext[4096], *t;
char extext[4096];
const char *t;
int level = Cmd_ExecLevel;
int i;
*text = 0;
@ -1465,9 +1467,9 @@ static void Cmd_AliasList_f (void)
if (!num)
Con_TPrintf("Alias list:\n");
if (cmd->execlevel)
Con_Printf("(%2i)(%2i) %s\n", (int)(cmd->restriction?cmd->restriction:rcon_level.ival), cmd->execlevel, cmd->name);
Con_Printf(S_COLOR_TRANS"(%2i)(%2i) "S_COLOR_WHITE"^[%s\\type\\/%s^]\n", (int)(cmd->restriction?cmd->restriction:rcon_level.ival), cmd->execlevel, cmd->name, cmd->name);
else
Con_Printf("(%2i) %s\n", (int)(cmd->restriction?cmd->restriction:rcon_level.ival), cmd->name);
Con_Printf(S_COLOR_TRANS "(%2i) "S_COLOR_WHITE"^[%s\\type\\/%s^]\n", (int)(cmd->restriction?cmd->restriction:rcon_level.ival), cmd->name, cmd->name);
num++;
}
if (num)
@ -2733,16 +2735,16 @@ static void Cmd_Apropos_f (void)
{
COM_QuotedString(var->latched_string, latchedvalue, sizeof(latchedvalue), false);
if (d)
Con_TPrintf("cvar ^2%s^7: %s (effective %s): ^3%s\n", name, latchedvalue, escapedvalue, d);
Con_TPrintf("cvar ^[^2%s\\type\\%s^]: %s (effective %s): ^3%s\n", name,name, latchedvalue, escapedvalue, d);
else
Con_TPrintf("cvar ^2%s^7: %s (effective %s): ^3no description\n", name, latchedvalue, escapedvalue);
Con_TPrintf("cvar ^[^2%s\\type\\%s^]: %s (effective %s): ^3no description\n", name,name, latchedvalue, escapedvalue);
}
else
{
if (d)
Con_TPrintf("cvar ^2%s^7: %s : ^3%s\n", name, escapedvalue, d);
Con_TPrintf("cvar ^[^2%s\\type\\%s^]: %s : ^3%s\n", name,name, escapedvalue, d);
else
Con_TPrintf("cvar ^2%s^7: %s : ^3no description\n", name, escapedvalue);
Con_TPrintf("cvar ^[^2%s\\type\\%s^]: %s : ^3no description\n", name,name, escapedvalue);
}
}
@ -2757,9 +2759,9 @@ static void Cmd_Apropos_f (void)
continue;
if (d)
Con_TPrintf("command ^2%s^7: ^3%s\n", cmd->name, d);
Con_TPrintf("command ^[^2%s\\type\\%s^]: ^3%s\n", cmd->name,cmd->name, d);
else
Con_TPrintf("command ^2%s^7: ^3no description\n", cmd->name);
Con_TPrintf("command ^[^2%s\\type\\%s^]: ^3no description\n", cmd->name,cmd->name);
}
//FIXME: add aliases.
}
@ -3739,7 +3741,7 @@ skipblock:
if (trueblock)
goto skipblock; //we've had our true, all others are assumed to be false.
else
goto elseif; //and have annother go.
goto elseif; //and have another go.
}
else
{ //we got an else. This is the last block. Don't go through the normal way, cos that would let us follow up with a second else.
@ -4139,6 +4141,7 @@ static void Cmd_WriteConfig_f(void)
char fname[MAX_QPATH];
char displayname[MAX_OSPATH];
qboolean all = true;
qboolean nohidden = false;
//special variation that only saves if an archived cvar was actually modified.
if (!Q_strcasecmp(Cmd_Argv(0), "cfg_save_ifmodified"))
@ -4167,7 +4170,9 @@ static void Cmd_WriteConfig_f(void)
Q_snprintfz(fname, sizeof(fname), "%s", filename);
COM_RequireExtension(fname, ".cfg", sizeof(fname));
if (Cmd_IsInsecure() && strncmp(fname, "data/", 5))
if (!strncmp(fname, "data/", 5))
nohidden = true; //we're writing to the data/ dir, which mods may potentially read. don't write any settings they're not allowed to see.
else if (Cmd_IsInsecure())
{
Con_Printf ("%s %s: not allowed\n", Cmd_Argv(0), Cmd_Args());
return;
@ -4235,7 +4240,7 @@ static void Cmd_WriteConfig_f(void)
#endif
if (cfg_save_aliases.ival)
Alias_WriteAliases (f);
Cvar_WriteVariables (f, all);
Cvar_WriteVariables (f, all, nohidden);
VFS_CLOSE(f);
Cvar_Saved();

View file

@ -251,6 +251,7 @@ void Cmd_MessageTrigger (char *message, int type);
void Cmd_ShiftArgs (int ammount, qboolean expandstring);
const char *TP_ParseFunChars (const char *s);
char *Cmd_ExpandString (const char *data, char *dest, int destlen, int *accesslevel, qboolean expandargs, qboolean expandcvars, qboolean expandmacros);
qboolean If_EvaluateBoolean(const char *text, int restriction);

View file

@ -896,7 +896,7 @@ static void BIH_RecursiveTrace (struct bihtrace_s *fte_restrict tr, const struct
VectorSubtract (tr->startpos, node->data.mesh.tr->origin, start_l);
VectorSubtract (tr->endpos, node->data.mesh.tr->origin, end_l);
node->data.mesh.model->funcs.NativeTrace(node->data.mesh.model, 0, NULLFRAMESTATE, node->data.mesh.tr->axis, start_l, end_l, tr->size.min, tr->size.max, tr->shape==shape_iscapsule, tr->hitcontents, &sub);
submod->funcs.NativeTrace(submod, 0, NULLFRAMESTATE, node->data.mesh.tr->axis, start_l, end_l, tr->size.min, tr->size.max, tr->shape==shape_iscapsule, tr->hitcontents, &sub);
if (sub.truefraction < tr->trace.truefraction)
{
@ -1850,6 +1850,7 @@ void BIH_Build (model_t *mod, struct bihleaf_s *leafs, size_t numleafs)
mod->funcs.PointContents = BIH_PointContents;
mod->funcs.NativeContents = BIH_NativeContents;
}
#ifdef SKELETALMODELS
void BIH_BuildAlias (model_t *mod, galiasinfo_t *meshes)
{
size_t numleafs, i;
@ -1883,3 +1884,4 @@ void BIH_BuildAlias (model_t *mod, galiasinfo_t *meshes)
}
BIH_Build(mod, leafs, leaf-leafs);
}
#endif

View file

@ -1160,6 +1160,9 @@ static int Alias_FindRawSkelData(galiasinfo_t *inf, const framestate_t *fstate,
int endbone;
int numbonegroups=0;
if (lastbone > inf->numbones)
lastbone = inf->numbones;
for (bonegroup = 0; bonegroup < FS_COUNT; bonegroup++)
{
endbone = fstate->g[bonegroup].endbone;
@ -1811,8 +1814,8 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in
#endif
mesh->xyz_array = meshcache.coords;
//we don't support meshes with one pose skeletal and annother not.
//we don't support meshes with one group skeletal and annother not.
//we don't support meshes with one pose skeletal and another not.
//we don't support meshes with one group skeletal and another not.
#ifdef SKELETALMODELS
meshcache.vbop = NULL;
@ -1878,7 +1881,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in
else
{
if (meshcache.bonecachetype != SKEL_ABSOLUTE)
meshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, MAX_BONES, NULL);
meshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, inf->numbones, NULL);
#ifndef SERVERONLY
if (inf->shares_bones != surfnum && qrenderer)
Alias_DrawSkeletalBones(inf->ofsbones, (const float *)meshcache.usebonepose, inf->numbones, e->framestate.g[0].endbone);
@ -1888,7 +1891,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in
else
{
if (meshcache.bonecachetype != SKEL_INVERSE_ABSOLUTE)
meshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_INVERSE_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, MAX_BONES, NULL);
meshcache.usebonepose = Alias_GetBoneInformation(inf, &e->framestate, meshcache.bonecachetype=SKEL_INVERSE_ABSOLUTE, meshcache.boneposebuffer1, meshcache.boneposebuffer2, inf->numbones, NULL);
//hardware bone animation
mesh->xyz_array = inf->ofs_skel_xyz;
@ -2903,11 +2906,14 @@ typedef struct
float fps;
qboolean loop;
int action;
int actionweight;
galiasevent_t *events;
float actionweight;
char name[MAX_QPATH];
} frameinfo_t;
static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups)
static frameinfo_t *ParseFrameInfo(model_t *mod, int *numgroups)
{
const char *modelname = mod->name;
int count = 0;
int maxcount = 0;
char *line, *eol;
@ -2917,11 +2923,84 @@ static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups)
char tok[MAX_FRAMEINFO_POSES * 4];
size_t fsize;
com_tokentype_t ttype;
json_t *rootjson;
Q_snprintfz(fname, sizeof(fname), "%s.framegroups", modelname);
line = file = FS_LoadMallocFile(fname, &fsize);
if (!file)
return NULL;
while(line && *line)
rootjson = JSON_Parse(file); //must be a fully valid json file, so any space-separated tokens from the dp format will return NULL here just fine.
if (rootjson)
{
json_t *framegroups = JSON_FindChild(rootjson, "framegroups");
maxcount = JSON_GetCount(framegroups);
frames = realloc(frames, sizeof(*frames)*maxcount);
for(count = 0; count < maxcount; count++)
{
galiasevent_t *ev, **link;
char eventdata[65536];
unsigned int posecount;
json_t *arr, *c;
json_t *in = JSON_GetIndexed(framegroups, count);
if (!in)
break; //erk? shouldn't really happen. not an issue though.
frames[count].firstpose = JSON_GetInteger(in, "firstpose", 0);
frames[count].posecount = JSON_GetInteger(in, "numposes", 1);
frames[count].posesarray = false;
arr = JSON_FindChild(in, "poses");
if (arr)
{ //override with explicit poses, if specified.
for (posecount = 0; posecount < countof(frames[count].poses); posecount++)
{
c = JSON_GetIndexed(arr, posecount);
if (!c) //ran out of elements...
break;
frames[count].poses[posecount] = JSON_GetUInteger(c, NULL, 0);
}
if (posecount>0)
{
frames[count].posecount = posecount;
frames[count].posesarray = true;
}
}
frames[count].fps = JSON_GetFloat(in, "fps", 20);
if (frames[count].fps <= 0)
frames[count].fps = 10;
frames[count].loop = JSON_GetUInteger(in, "loop", false);
Q_snprintfz(frames[count].name,sizeof(frames[count].name), "%s[%d]", fname, count);
JSON_GetString(in, "name", frames[count].name, sizeof(frames[count].name), NULL);
frames[count].action = JSON_GetInteger(in, "action", -1);
frames[count].actionweight = JSON_GetFloat(in, "actionweight", 0);
frames[count].events = NULL;
arr = JSON_FindChild(in, "events");
for (posecount = 0; (c=JSON_GetIndexed(arr, posecount))!=NULL; posecount++)
{
*eventdata = 0;
JSON_GetString(c, "value", eventdata,sizeof(eventdata), NULL);
ev = ZG_Malloc(&mod->memgroup, sizeof(*ev) + strlen(eventdata)+1);
ev->code = JSON_GetInteger(c, "code", 0);
ev->timestamp = JSON_GetFloat(c, "timestamp", JSON_GetFloat(c, "pose", 0)/frames[count].fps);
ev->data = strcpy((char*)(ev+1), eventdata);
link = &frames[count].events;
while (*link && (*link)->timestamp <= ev->timestamp)
link = &(*link)->next;
ev->next = *link;
*link = ev;
}
}
mod->flags = JSON_GetUInteger(rootjson, "modelflags", mod->flags);
JSON_Destroy(rootjson);
}
else while(line && *line)
{
unsigned int posecount = 0;
@ -2961,6 +3040,7 @@ static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups)
else
frames[count].loop = !!atoi(tok);
frames[count].events = NULL;
frames[count].action = -1;
frames[count].actionweight = 0;
Q_snprintfz(frames[count].name, sizeof(frames[count].name), "groupified_%d_anim", count); //to match DP. frameforname cares.
@ -2990,6 +3070,11 @@ static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups)
BZ_Free(file);
*numgroups = count;
if (!count)
{
free(frames);
frames = NULL;
}
return frames;
}
@ -3985,10 +4070,10 @@ static void *Q1MDL_LoadSkins_GL (galiasinfo_t *galias, dmdl_t *pq1inmodel, model
#endif
static void Mesh_HandleFramegroupsFile(model_t *mod, galiasinfo_t *galias)
{
{ //use ONLY with vertex models.
unsigned int numanims, a, p, g, oldnumanims = galias->numanimations, targpose;
galiasanimation_t *o, *oldanims = galias->ofsanimations, *frame;
frameinfo_t *framegroups = ParseFrameInfo(mod->name, &numanims);
frameinfo_t *framegroups = ParseFrameInfo(mod, &numanims);
if (framegroups)
{
galias->ofsanimations = o = ZG_Malloc(&mod->memgroup, sizeof(*galias->ofsanimations) * numanims);
@ -4014,8 +4099,9 @@ static void Mesh_HandleFramegroupsFile(model_t *mod, galiasinfo_t *galias)
o->numposes = p;
o->rate = framegroups[a].fps;
o->loop = framegroups[a].loop;
o->action = -1;
o->actionweight = 0;
o->events = framegroups[a].events;
o->action = framegroups[a].action;
o->actionweight = framegroups[a].actionweight;
Q_strncpyz(o->name, framegroups[a].name, sizeof(o->name));
}
galias->numanimations = numanims;
@ -5128,25 +5214,23 @@ int Mod_GetBoneRelations(model_t *model, int firstbone, int lastbone, const gali
galiasbone_t *Mod_GetBoneInfo(model_t *model, int *numbones)
{
#ifdef SKELETALMODELS
galiasbone_t *bone;
galiasinfo_t *inf;
if (!model || model->type != mod_alias)
if (model && model->type == mod_alias)
{
*numbones = 0;
return NULL;
galiasinfo_t *inf = Mod_Extradata(model);
*numbones = inf->numbones;
return inf->ofsbones;
}
inf = Mod_Extradata(model);
bone = inf->ofsbones;
*numbones = inf->numbones;
return bone;
#else
#endif
#ifdef HALFLIFEMODELS
if (model && model->type == mod_halflife)
{
hlmodel_t *hlmod = Mod_Extradata(model);
*numbones = hlmod->header->numbones;
return hlmod->compatbones;
}
#endif
*numbones = 0;
return NULL;
#endif
}
int Mod_GetBoneParent(model_t *model, int bonenum)
@ -6014,7 +6098,7 @@ static galiasinfo_t *Mod_LoadQ3ModelLod(model_t *mod, int *surfcount, void *buff
externalskins = Mod_CountSkinFiles(mod);
#endif
framegroups = ParseFrameInfo(mod->name, &numgroups);
framegroups = ParseFrameInfo(mod, &numgroups);
ClearBounds(min, max);
@ -6087,46 +6171,6 @@ static galiasinfo_t *Mod_LoadQ3ModelLod(model_t *mod, int *surfcount, void *buff
tvector = (vec3_t*)(svector + numverts*numposes);
#endif
if (framegroups)
{ //group the poses into animations.
for (i = 0; i < numgroups; i++)
{
int first = framegroups[i].firstpose, count = framegroups[i].posecount;
if (first >= numposes) //bound the numbers.
first = numposes-1;
if (first < 0)
first = 0;
if (count > numposes-first)
count = numposes-first;
if (count < 0)
count = 0;
Q_snprintfz(group->name, sizeof(group->name), "%s", framegroups[i].name);
group->numposes = count;
group->rate = framegroups[i].fps;
group->poseofs = pose + first;
group->loop = framegroups[i].loop;
group->events = NULL;
group->action = -1;
group->actionweight = 0;
group++;
}
}
else
{ //raw poses, no animations.
for (i = 0; i < numgroups; i++)
{
Q_snprintfz(group->name, sizeof(group->name), "frame%i", i);
group->numposes = 1;
group->rate = 1;
group->poseofs = pose + i;
group->loop = false;
group->events = NULL;
group->action = -1;
group->actionweight = 0;
group++;
}
}
//load in that per-pose data
invert = (md3XyzNormal_t *)((qbyte*)surf + LittleLong(surf->ofsXyzNormals));
for (i = 0; i < numposes; i++)
@ -6172,6 +6216,64 @@ static galiasinfo_t *Mod_LoadQ3ModelLod(model_t *mod, int *surfcount, void *buff
#endif
pose++;
}
pose -= numposes;
if (framegroups)
{ //group the poses into animations.
for (i = 0; i < numgroups; i++)
{
Q_snprintfz(group->name, sizeof(group->name), "%s", framegroups[i].name);
if (framegroups[i].posesarray)
{
unsigned int p, targpose;
group->poseofs = ZG_Malloc(&mod->memgroup, sizeof(*group->poseofs) * framegroups[i].posecount);
group->numposes = 0;
for (p = 0; p < framegroups[i].posecount; p++)
{
targpose = framegroups[i].poses[p];
if (targpose < numposes)
group->poseofs[group->numposes++] = pose[targpose];
}
}
else
{
int first = framegroups[i].firstpose, count = framegroups[i].posecount;
if (first >= numposes) //bound the numbers.
first = numposes-1;
if (first < 0)
first = 0;
if (count > numposes-first)
count = numposes-first;
if (count < 0)
count = 0;
group->poseofs = pose + first;
group->numposes = count;
}
group->rate = framegroups[i].fps;
group->loop = framegroups[i].loop;
group->events = framegroups[i].events;
group->action = framegroups[i].action;
group->actionweight = framegroups[i].actionweight;
group++;
}
}
else
{ //raw poses, no animations.
for (i = 0; i < numgroups; i++)
{
Q_snprintfz(group->name, sizeof(group->name), "frame%i", i);
group->numposes = 1;
group->rate = 1;
group->poseofs = pose + i;
group->loop = false;
group->events = NULL;
group->action = -1;
group->actionweight = 0;
group++;
}
}
#ifndef SERVERONLY
if (externalskins<LittleLong(surf->numShaders))
@ -7160,7 +7262,7 @@ static qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize)
if (animinfo && animkeys)
{
int numgroups = 0;
frameinfo_t *frameinfo = ParseFrameInfo(mod->name, &numgroups);
frameinfo_t *frameinfo = ParseFrameInfo(mod, &numgroups);
if (numgroups)
{
/*externally supplied listing of frames. ignore all framegroups in the model and use only the pose info*/
@ -7169,6 +7271,8 @@ static qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize)
for (j = 0; j < numgroups; j++)
{
/*bound check*/
if (frameinfo[j].posesarray)
Con_Printf(CON_WARNING"Mod_LoadPSKModel(%s): framegroup[%i] poses array not suppported\n", mod->name, j);
if (frameinfo[j].firstpose+frameinfo[j].posecount > num_animkeys)
frameinfo[j].posecount = num_animkeys - frameinfo[j].firstpose;
if (frameinfo[j].firstpose >= num_animkeys)
@ -7186,8 +7290,9 @@ static qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize)
group[j].loop = frameinfo[j].loop;
group[j].rate = frameinfo[j].fps;
group[j].skeltype = SKEL_RELATIVE;
group[j].action = -1;
group[j].actionweight = 0;
group[j].events = frameinfo[j].events;
group[j].action = frameinfo[j].action;
group[j].actionweight = frameinfo[j].actionweight;
}
num_animinfo = numgroups;
}
@ -7537,7 +7642,7 @@ static qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t
//throw away the flags.
}
framegroups = ParseFrameInfo(mod->name, &numgroups);
framegroups = ParseFrameInfo(mod, &numgroups);
if (!framegroups)
{ //use the dpm's poses directly.
numgroups = header->num_frames;
@ -7596,14 +7701,16 @@ static qboolean QDECL Mod_LoadDarkPlacesModel(model_t *mod, void *buffer, size_t
numposes = 0;
if (firstpose + numposes > header->num_frames)
numposes = header->num_frames - firstpose;
if (framegroups[i].posesarray)
Con_Printf(CON_WARNING"Mod_LoadDarkPlacesModel(%s): No support for explicit pose lists\n", mod->name);
outgroups[i].skeltype = SKEL_RELATIVE;
outgroups[i].boneofs = outposedata + firstpose*header->num_bones*12;
outgroups[i].numposes = numposes;
outgroups[i].loop = framegroups[i].loop;
outgroups[i].rate = framegroups[i].fps;
outgroups[i].events = NULL;
outgroups[i].action = -1;
outgroups[i].actionweight = 0;
outgroups[i].events = framegroups[i].events;
outgroups[i].action = framegroups[i].action;
outgroups[i].actionweight = framegroups[i].actionweight;
Q_strncpyz(outgroups[i].name, framegroups[i].name, sizeof(outgroups[i].name));
}
}
@ -8406,7 +8513,7 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz
numgroups = 0;
framegroups = NULL;
if (!numgroups)
framegroups = ParseFrameInfo(mod->name, &numgroups);
framegroups = ParseFrameInfo(mod, &numgroups);
if (!numgroups && h->num_anims)
{
/*use the model's framegroups*/
@ -8421,6 +8528,7 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz
framegroups[i].posecount = LittleLong(anim[i].num_frames);
framegroups[i].fps = LittleFloat(anim[i].framerate);
framegroups[i].loop = !!(LittleLong(anim[i].flags) & IQM_LOOP);
framegroups[i].events = NULL;
framegroups[i].action = -1;
framegroups[i].actionweight = 0;
Q_strncpyz(framegroups[i].name, Mod_IQMString(&strings, anim[i].name), sizeof(fgroup[i].name));
@ -8437,6 +8545,7 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz
framegroups->posecount = 1;
framegroups->fps = 10;
framegroups->loop = 1;
framegroups->events = NULL;
framegroups->action = -1;
framegroups->actionweight = 0;
strcpy(framegroups->name, "base");
@ -8606,6 +8715,8 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz
//now generate the animations.
for (i = 0; i < numgroups; i++)
{
if (framegroups[i].posesarray)
Con_Printf(CON_WARNING"Mod_ParseIQMMeshModel(%s): framegroup[%i] poses array not suppported\n", mod->name, i);
if (framegroups[i].firstpose + framegroups[i].posecount > h->num_frames)
framegroups[i].posecount = h->num_frames - framegroups[i].firstpose;
if (framegroups[i].firstpose >= h->num_frames)
@ -8629,6 +8740,7 @@ static galiasinfo_t *Mod_ParseIQMMeshModel(model_t *mod, const char *buffer, siz
if (fgroup[i].rate <= 0)
fgroup[i].rate = 10;
fgroup[i].events = framegroups[i].events;
fgroup[i].action = framegroups[i].action;
fgroup[i].actionweight = framegroups[i].actionweight;
}

View file

@ -5970,6 +5970,7 @@ static void COM_Version_f (void)
#endif
#endif
#if defined(HAVE_SERVER) || defined(HAVE_CLIENT)
Con_Printf("^3Games:^7");
#if defined(Q3SERVER) && defined(Q3CLIENT)
#ifdef BOTLIB_STATIC
@ -6030,6 +6031,7 @@ static void COM_Version_f (void)
Con_Printf(" ssqc");
#endif
Con_Printf("\n");
#endif
Con_Printf("^3Networking:^7");
#ifdef WEBCLIENT
@ -6043,14 +6045,16 @@ static void COM_Version_f (void)
#endif
#if (defined(SUPPORT_ICE)&&defined(HAVE_DTLS)) || defined(FTE_TARGET_WEB)
Con_Printf(" WebRTC");
#elif defined(SUPPORT_ICE)
Con_Printf(" ICE");
#endif
#ifdef FTE_TARGET_WEB
Con_Printf(" WebSocket/WSS");
#else
#if defined(HAVE_TCP)
#ifdef TCPCONNECT
Con_Printf(" TCPConnect");
#endif
#ifdef TCPCONNECT
Con_Printf(" TCPConnect");
#endif
#else
Con_Printf(" ^h(disabled: TCP)");
#endif
@ -6060,9 +6064,6 @@ static void COM_Version_f (void)
#endif
#ifdef HAVE_WINSSPI //on windows
Con_Printf(" WINSSPI");
#endif
#ifdef SUPPORT_ICE
Con_Printf(" ICE");
#endif
Con_Printf("\n");
}
@ -6364,6 +6365,9 @@ static int COM_WorkerThread(void *arg)
}
static void Sys_ErrorThread(void *ctx, void *data, size_t a, size_t b)
{
if (ctx)
COM_WorkerSync_WorkerStopped(ctx, NULL, a, b);
//posted to main thread from a worker.
Sys_Error("%s", (const char*)data);
}
@ -6383,12 +6387,12 @@ void COM_WorkerAbort(char *message)
if (com_worker[us].thread && Sys_IsThread(com_worker[us].thread))
{
group = WG_LOADER;
COM_AddWork(WG_MAIN, COM_WorkerSync_WorkerStopped, &com_worker[us], NULL, 0, group);
COM_InsertWork(WG_MAIN, Sys_ErrorThread, &com_worker[us], Z_StrDup(message), 0, group);
break;
}
//now tell the main thread that it should be crashing, and why.
COM_AddWork(WG_MAIN, Sys_ErrorThread, NULL, Z_StrDup(message), 0, 0);
if (us == WORKERTHREADS) //don't know who it was.
COM_AddWork(WG_MAIN, Sys_ErrorThread, NULL, Z_StrDup(message), 0, 0);
Sys_ThreadAbort();
}
@ -6416,11 +6420,10 @@ void COM_DestroyWorkerThread(void)
while(COM_DoWork(WG_LOADER, false)) //finish any work that got posted to it that it neglected to finish.
;
COM_WorkerFullSync();
while(COM_DoWork(WG_MAIN, false))
;
COM_WorkerFullSync();
for (i = 0; i < WG_COUNT; i++)
{
if (com_workercondition[i])
@ -6435,33 +6438,35 @@ void COM_DestroyWorkerThread(void)
//Dangerous: stops workers WITHOUT flushing their queue. Be SURE to 'unlock' to start them up again.
void COM_WorkerLock(void)
{
#define NOFLUSH 0x40000000
int i;
if (!com_liveworkers[WG_LOADER])
return; //nothing to do.
//add a fake worker and ask workers to die
//don't let liveworkers become 0 (so the main thread doesn't flush any pending work) and ask workers to die
Sys_LockConditional(com_workercondition[WG_LOADER]);
com_liveworkers[WG_LOADER] += 1;
com_liveworkers[WG_LOADER] |= NOFLUSH;
for (i = 0; i < WORKERTHREADS; i++)
com_worker[i].request = WR_DIE; //flag them all to die
Sys_ConditionBroadcast(com_workercondition[WG_LOADER]); //and make sure they ALL wake up to check their new death values.
Sys_UnlockConditional(com_workercondition[WG_LOADER]);
//wait for the workers to stop (leaving their work, because of our fake worker)
while(com_liveworkers[WG_LOADER]>1)
while((com_liveworkers[WG_LOADER]&~NOFLUSH)>0)
{
if (!COM_DoWork(WG_MAIN, false)) //need to check this to know they're done.
COM_DoWork(WG_LOADER, false); //might as well, while we're waiting.
}
//remove our fake worker now...
//remove our flush-blocker now...
Sys_LockConditional(com_workercondition[WG_LOADER]);
com_liveworkers[WG_LOADER] -= 1;
com_liveworkers[WG_LOADER] &= ~NOFLUSH;
Sys_UnlockConditional(com_workercondition[WG_LOADER]);
}
//called after COM_WorkerLock
void COM_WorkerUnlock(void)
{
qboolean restarted = false;
int i;
for (i = 0; i < WORKERTHREADS; i++)
{
@ -6473,8 +6478,14 @@ void COM_WorkerUnlock(void)
{
com_worker[i].request = WR_NONE;
com_worker[i].thread = Sys_CreateThread(va("loadworker_%i", i), COM_WorkerThread, &com_worker[i], 0, 256*1024);
if (com_worker[i].thread)
restarted = true;
}
}
if (!restarted)
while (COM_DoWork(WG_LOADER, false))
;
}
//fully flushes ALL pending work.
@ -6709,6 +6720,19 @@ static void COM_InitWorkerThread(void)
Cvar_ForceCallback(&worker_count);
}
qboolean FTE_AtomicPtr_ConditionalReplace(qint32_t *ptr, qint32_t old, qint32_t new)
{
Sys_LockMutex(com_resourcemutex);
if (*ptr == old)
{
*ptr = new;
Sys_UnlockMutex(com_resourcemutex);
return true;
}
Sys_UnlockMutex(com_resourcemutex);
return false;
}
qint32_t FTE_Atomic32Mutex_Add(qint32_t *ptr, qint32_t change)
{
qint32_t r;
@ -6724,6 +6748,15 @@ qint32_t FTE_Atomic32Mutex_Add(qint32_t *ptr, qint32_t change)
r = (*ptr += change);
return r;
}
qboolean FTE_AtomicPtr_ConditionalReplace(qint32_t *ptr, qint32_t old, qint32_t new)
{ //hope it ain't threaded
if (*ptr == old)
{
*ptr = new;
return true;
}
return false;
}
#endif
/*

View file

@ -201,11 +201,11 @@ typedef enum {
} sbpacking_t;
typedef struct sizebuf_s
{
qboolean allowoverflow; // if false, do a Sys_Error
qboolean overflowed; // set to true if the buffer size failed
qbyte *data;
int maxsize; //storage size of data
int cursize; //assigned size of data
qboolean allowoverflow; // if false, do a Sys_Error
qboolean overflowed; // set to true if the buffer size failed
sbpacking_t packing; //required for q3
int currentbit; //ignored for rawbytes

View file

@ -21,6 +21,7 @@
//#define GAME_DOWNLOADSURL NULL //url for the package manger to update from
//#define GAME_DEFAULTCMDS NULL //a string containing the things you want to exec in order to override default.cfg
//#define ENGINE_HAS_ZIP //when defined, the engine is effectively a self-extrating zip with the gamedata zipped onto the end (if it in turn contains nested packages then they should probably be STOREd pk3s)
// Allowed renderers... There should ONLY be undefs here (other C files won't be pulled in automatically)
//#undef GLQUAKE

View file

@ -44,6 +44,7 @@
#define MULTITHREAD //misc basic multithreading - dsound, downloads, basic stuff that's unlikely to have race conditions.
#define LOADERTHREAD //worker threads for loading misc stuff. falls back on main thread if not supported.
#define AVAIL_DINPUT
#define MAX_CLIENTS 32 //32 for vanilla qw. max 255.
//#define SIDEVIEWS 4 //enable secondary/reverse views.
//#define MAX_SPLITS 4u
#define VERTEXINDEXBYTES 2 //16bit indexes work everywhere but may break some file types, 32bit indexes are optional in gles<=2 and d3d<=9 and take more memory/copying but allow for bigger batches/models. Plugins need to be compiled the same way so this is no longer set per-renderer.

View file

@ -21,13 +21,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// console
//
// undefine this to solve build issues with epoll-shim - Brad
#if defined(__unix__) && !defined(__linux__) && !defined(__CYGWIN__)
#ifdef close
#undef close
#endif
#endif
#define MAXCONCOLOURS 16
typedef struct {
float fr, fg, fb;
@ -251,6 +244,9 @@ char *Con_CopyConsole(console_t *con, qboolean nomarkup, qboolean onlyiflink, qb
void Con_Print (const char *txt);
void Con_CenterPrint(const char *txt);
void Con_PrintFlags(const char *text, unsigned int setflags, unsigned int clearflags);
#ifdef HAVE_CLIENT
void Con_HexDump(qbyte *packet, size_t len, size_t badoffset, size_t stride);
#endif
void VARGS Con_Printf (const char *fmt, ...) LIKEPRINTF(1);
void VARGS Con_TPrintf (translation_t text, ...);
void VARGS Con_DPrintf (const char *fmt, ...) LIKEPRINTF(1); //developer>=1, for stuff that's probably actually slightly useful

View file

@ -998,9 +998,10 @@ static cvar_t *Cvar_SetCore (cvar_t *var, const char *value, qboolean force)
if (var->flags & CVAR_USERINFO)
{
char *old = InfoBuf_ValueForKey(&cls.userinfo[0], var->name);
if (strcmp(old, value)) //only spam the server if it actually changed
const char *corruptval = TP_ParseFunChars(value);
if (strcmp(old, corruptval)) //only spam the server if it actually changed
{ //this helps with config execs
InfoBuf_SetKey (&cls.userinfo[0], var->name, value);
InfoBuf_SetKey (&cls.userinfo[0], var->name, corruptval);
}
}
#endif
@ -1687,7 +1688,7 @@ Writes lines containing "set variable value" for all variables
with the archive flag set to true.
============
*/
void Cvar_WriteVariables (vfsfile_t *f, qboolean all)
void Cvar_WriteVariables (vfsfile_t *f, qboolean all, qboolean nohidden)
{
qboolean writtengroupheader;
cvar_group_t *grp;
@ -1705,6 +1706,8 @@ void Cvar_WriteVariables (vfsfile_t *f, qboolean all)
//yeah, don't force-save readonly cvars.
if (var->flags & (CVAR_NOSET|CVAR_NOSAVE))
continue;
if (nohidden && (var->flags & CVAR_NOUNSAFEEXPAND))
continue;
if (!writtengroupheader)
{

View file

@ -213,7 +213,7 @@ qboolean Cvar_Command (cvar_t *v, int level);
// command. Returns true if the command was a variable reference that
// was handled. (print or change)
void Cvar_WriteVariables (vfsfile_t *f, qboolean all);
void Cvar_WriteVariables (vfsfile_t *f, qboolean all, qboolean nohidden);
// Writes lines containing "set variable value" for all variables
// with the archive flag set to true.

View file

@ -66,7 +66,7 @@ static char *vidfilenames[] = //list of filenames to check to see if graphics st
/*set some stuff so our regular qw client appears more like hexen2. sv_mintic must be 0.015 to 'fix' the ravenstaff so that its projectiles don't impact upon each other, or even 0.05 to exactly match the hardcoded assumptions in obj_push. There's maps that depend on a low framerate via waterjump framerate-dependance too.*/
#define HEX2CFG "//schemes hexen2\n" "set v_gammainverted 1\nset com_parseutf8 -1\nset gl_font gfx/hexen2\nset in_builtinkeymap 0\nset_calc cl_playerclass int (random * 5) + 1\nset cl_forwardspeed 200\nset cl_backspeed 200\ncl_sidespeed 225\nset sv_maxspeed 640\ncl_run 0\nset watervis 1\nset r_lavaalpha 1\nset r_lavastyle -2\nset r_wateralpha 0.5\nset sv_pupglow 1\ngl_shaftlight 0.5\nsv_mintic 0.05\nset r_meshpitch -1\nset r_meshroll -1\nr_sprite_backfacing 1\nset mod_warnmodels 0\nset cl_model_bobbing 1\nsv_sound_watersplash \"misc/hith2o.wav\"\nsv_sound_land \"fx/thngland.wav\"\nset sv_walkpitch 0\n"
/*yay q2!*/
#define Q2CFG "//schemes quake2\n" "set v_gammainverted 1\nset com_parseutf8 0\ncom_gamedirnativecode 1\nset sv_bigcoords 0\nsv_port "STRINGIFY(PORT_Q2SERVER)" "STRINGIFY(PORT_Q2EXSERVER)"\ncl_defaultport "STRINGIFY(PORT_Q2SERVER)"\n" \
#define Q2CFG "//schemes quake2\n" "set com_protocolversion "STRINGIFY(PROTOCOL_VERSION_Q2)"\nset v_gammainverted 1\nset com_parseutf8 0\ncom_gamedirnativecode 1\nset sv_bigcoords 0\nsv_port "STRINGIFY(PORT_Q2SERVER)" "STRINGIFY(PORT_Q2EXSERVER)"\ncl_defaultport "STRINGIFY(PORT_Q2SERVER)"\n" \
"set r_replacemodels " IFMINIMAL("","md3 md5mesh")"\n" \
"set r_glsl_emissive 0\n" /*work around the _glow textures not being meant to glow*/
/*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/
@ -364,7 +364,7 @@ char *VFS_GETS(vfsfile_t *vf, char *buffer, size_t buflen)
void VARGS VFS_PRINTF(vfsfile_t *vf, const char *format, ...)
{
va_list argptr;
char string[1024];
char string[2048];
va_start (argptr, format);
vsnprintf (string,sizeof(string)-1, format,argptr);
@ -1119,7 +1119,7 @@ static qboolean FS_Manifest_ParseTokens(ftemanifest_t *man)
char *sl = strchr(newdir+6, '/');
if (!sl)
break; //malformed steam link
man->gamepath[i].flags |= GAMEDIR_STEAMGAME;
man->gamepath[i].flags |= GAMEDIR_PRIVATE|GAMEDIR_STEAMGAME;
*sl = 0;
if (!FS_GamedirIsOkay(sl+1))
break;
@ -1464,6 +1464,9 @@ static void COM_Path_f (void)
return;
}
if (fs_hidesyspaths.ival)
Con_Printf("External paths are hidden, ^[click to unhide\\type\\set fs_hidesyspaths 0;path^]\n");
if (com_purepaths || fs_puremode)
{
Con_Printf ("Pure paths:\n");
@ -1915,6 +1918,51 @@ static void FS_FlushFSHashReally(qboolean domutexes)
}
}
static void QDECL FS_AddFileHashUnsafe(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle)
{
//threading stuff is fucked.
fsbucket_t *old;
old = Hash_GetInsensitiveBucket(&filesystemhash, fname);
if (old)
{
fs_hash_dups++;
if (depth >= old->depth)
{
return;
}
//remove the old version
//FIXME: needs to be atomic. just live with multiple in there.
//Hash_RemoveBucket(&filesystemhash, fname, &old->buck);
}
if (!filehandle)
{
int nlen = strlen(fname)+1;
int plen = sizeof(*filehandle)+nlen;
plen = (plen+fte_alignof(fsbucket_t)-1) & ~(fte_alignof(fsbucket_t)-1);
if (!fs_hash_filebuckets || fs_hash_filebuckets->used+plen > fs_hash_filebuckets->total)
{
void *o = fs_hash_filebuckets;
fs_hash_filebuckets = Z_Malloc(65536);
fs_hash_filebuckets->total = 65536 - sizeof(*fs_hash_filebuckets);
fs_hash_filebuckets->prev = o;
}
filehandle = (fsbucket_t*)(fs_hash_filebuckets->data+fs_hash_filebuckets->used);
fs_hash_filebuckets->used += plen;
if (!filehandle)
return; //eep!
memcpy((char*)(filehandle+1), fname, nlen);
fname = (char*)(filehandle+1);
}
filehandle->depth = depth;
Hash_AddInsensitive(&filesystemhash, fname, pathhandle, &filehandle->buck);
fs_hash_files++;
}
static void QDECL FS_AddFileHash(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle)
{
fsbucket_t *old;
@ -2677,7 +2725,7 @@ static qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, ch
Q_snprintfz(out, outlen, "$bindir/%s", fname+strlen(host_parms.binarydir));
#ifdef FTE_LIBRARY_PATH
else if (!strncmp(fname, STRINGIFY(FTE_LIBRARY_PATH)"/", strlen(STRINGIFY(FTE_LIBRARY_PATH)"/"))) //FS_LIBRARYDIR
Q_snprintfz(out, outlen, "$libdir/%s", fname+strlen(host_parms.binarydir));
Q_snprintfz(out, outlen, "$libdir/%s", fname+strlen(STRINGIFY(FTE_LIBRARY_PATH)"/"));
#endif
else //should try bindir
Q_snprintfz(out, outlen, "$system/%s", COM_SkipPath(fname)); //FS_SYSTEM :(
@ -2685,11 +2733,13 @@ static qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, ch
else
Q_snprintfz(out, outlen, "%s", fname);
#ifdef _WIN32
for (; *out; out++)
{
if (*out == '\\')
*out = '/';
}
#endif
return true;
}
@ -4250,20 +4300,37 @@ static searchpath_t *FS_AddPathHandle(searchpath_t **oldpaths, const char *purep
if (flags & (SPF_TEMPORARY|SPF_SERVER))
{
int depth = 1;
searchpath_t *s;
//add at end. pureness will reorder if needed.
link = &com_searchpaths;
while(*link)
{
link = &(*link)->next;
}
if (com_purepaths)
{ //go for the pure paths first.
for (s = com_purepaths; s; s = s->nextpure)
depth++;
}
if (fs_puremode < 2)
{
for (s = com_searchpaths ; s ; s = s->next)
depth++;
}
*link = search;
if (filesystemhash.numbuckets)
search->handle->BuildHash(search->handle, depth, FS_AddFileHashUnsafe);
}
else
{
search->next = com_searchpaths;
com_searchpaths = search;
com_fschanged = true; //depth values are screwy
}
com_fschanged = true;
return search;
}
@ -5185,6 +5252,19 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags)
com_base_searchpaths = NULL;
gameonly_gamedir = gameonly_homedir = NULL;
#if defined(ENGINE_HAS_ZIP) && defined(PACKAGE_PK3)
{
searchpathfuncs_t *pak;
vfsfile_t *vfs;
vfs = VFSOS_Open(com_argv[0], "rb");
pak = FSZIP_LoadArchive(vfs, NULL, com_argv[0], com_argv[0], "");
if (pak) //logically should have SPF_EXPLICIT set, but that would give it a worse gamedir depth
{
FS_AddPathHandle(&oldpaths, "", com_argv[0], pak, "", SPF_COPYPROTECTED, reloadflags);
}
}
#endif
#if defined(HAVE_LEGACY) && defined(PACKAGE_PK3)
{
searchpathfuncs_t *pak;
@ -6509,6 +6589,41 @@ static ftemanifest_t *FS_ReadDefaultManifest(char *newbasedir, size_t newbasedir
man->security = MANIFEST_SECURITY_DEFAULT;
}
#if defined(ENGINE_HAS_ZIP) && defined(PACKAGE_PK3)
if (!man && game == -1)
{
searchpathfuncs_t *pak;
vfsfile_t *vfs;
vfs = VFSOS_Open(com_argv[0], "rb");
pak = FSZIP_LoadArchive(vfs, NULL, com_argv[0], com_argv[0], "");
if (pak)
{
flocation_t loc;
if (pak->FindFile(pak, &loc, "default.fmf", NULL))
{
f = pak->OpenVFS(pak, &loc, "rb");
if (f)
{
size_t len = VFS_GETLEN(f);
char *fdata = BZ_Malloc(len+1);
if (fdata)
{
VFS_READ(f, fdata, len);
fdata[len] = 0;
man = FS_Manifest_ReadMem(NULL, NULL, fdata);
if (man)
man->security = MANIFEST_SECURITY_DEFAULT;
BZ_Free(fdata);
}
VFS_CLOSE(f);
}
}
pak->ClosePath(pak);
}
}
#endif
//-basepack is primarily an android feature
i = COM_CheckParm ("-basepack");
while (!man && game == -1 && i && i < com_argc-1)

View file

@ -2197,7 +2197,7 @@ static qboolean FSZIP_FindEndCentralDirectory(zipfile_t *zip, struct zipinfo *in
}
if (!result)
Con_Printf("zip: unable to find end-of-central-directory\n");
Con_Printf("%s: unable to find end-of-central-directory (not a zip?)\n", zip->filename); //usually just not a zip.
else
//now look for a zip64 header.
@ -2249,13 +2249,13 @@ static qboolean FSZIP_FindEndCentralDirectory(zipfile_t *zip, struct zipinfo *in
}
else
{
Con_Printf("zip: zip64 end-of-central directory at unknown offset.\n");
Con_Printf("%s: zip64 end-of-central directory at unknown offset.\n", zip->filename);
result = false;
}
if (info->diskcount < 1 || info->zip64_centraldirend_disk != info->thisdisk)
{
Con_Printf("zip: archive is spanned\n");
{ //must read the segment with the central directory.
Con_Printf("%s: archive is spanned\n", zip->filename);
return false;
}
@ -2264,13 +2264,13 @@ static qboolean FSZIP_FindEndCentralDirectory(zipfile_t *zip, struct zipinfo *in
}
if (info->thisdisk != info->centraldir_startdisk || info->centraldir_numfiles_disk != info->centraldir_numfiles_all)
{
Con_Printf("zip: archive is spanned\n");
{ //must read the segment with the central directory.
Con_Printf("%s: archive is spanned\n", zip->filename);
result = false;
}
if (info->centraldir_compressionmethod || info->centraldir_algid)
{
Con_Printf("zip: encrypted centraldir\n");
Con_Printf("%s: encrypted centraldir\n", zip->filename);
result = false;
}

View file

@ -416,8 +416,7 @@ void Netchan_Setup (unsigned int flags, netchan_t *chan, netadr_t *adr, int qpor
#endif
chan->incoming_unreliable = -1;
if (flags&NCF_CLIENT)
chan->outgoing_sequence = 1; //so the first one doesn't get dropped.
chan->outgoing_sequence = 1; //so the first one doesn't get dropped.
if (adr->prot == NP_KEXLAN)
chan->qportsize = 0;
@ -472,7 +471,11 @@ void Netchan_Setup (unsigned int flags, netchan_t *chan, netadr_t *adr, int qpor
chan->message.data = chan->message_buf;
chan->message.allowoverflow = true;
chan->message.maxsize = min(chan->mtu_cur, sizeof(chan->message_buf));
if ((flags&NCF_FRAGABLE) && NET_AddrIsReliable(adr))
chan->message.maxsize = sizeof(chan->message_buf); //something big. might as well if its all tcp anyway.
else
chan->message.maxsize = min(chan->mtu_cur, sizeof(chan->message_buf));
}
@ -733,7 +736,7 @@ A 0 length will still generate a packet and deal with the reliable messages.
*/
int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate)
{
sizebuf_t send = {false};
sizebuf_t send = {NULL};
qbyte send_buf[MAX_OVERALLMSGLEN + PACKET_HEADER];
qboolean send_reliable;
char remote_adr[MAX_ADR_SIZE];

View file

@ -504,7 +504,6 @@ static qboolean TURN_AddXorAddressAttrib(sizebuf_t *buf, unsigned int attr, neta
MSG_WriteByte(buf, ((qbyte*)&to->address)[aofs+i] ^ (buf->data+4)[i]);
return true;
}
void Con_HexDump(qbyte *packet, size_t len, size_t badoffset);
static qboolean TURN_AddAuth(sizebuf_t *buf, struct iceserver_s *srv)
{ //adds auth info to a stun packet
unsigned short len;
@ -5373,59 +5372,8 @@ static void FTENET_ICE_Heartbeat(ftenet_ice_connection_t *b)
#ifdef HAVE_SERVER
if (b->generic.islisten)
{
extern cvar_t maxclients;
char info[2048];
int i;
client_t *cl;
int numclients = 0;
for (i=0 ; i<svs.allocated_client_slots ; i++)
{
cl = &svs.clients[i];
if ((cl->state == cs_connected || cl->state == cs_spawned || cl->name[0]) && !cl->spectator)
numclients++;
}
*info = 0;
//first line contains the serverinfo, or some form of it
{
char *resp = info;
const char *ignorekeys[] = {
"maxclients", "map", "*gamedir", "*z_ext", //this is a DP protocol query, so some QW fields are not needed
"gamename", "modname", "protocol", "clients", "sv_maxclients", "mapname", "qcstatus", "challenge", NULL}; //and we need to add some
const char *prioritykeys[] = {"hostname", NULL}; //make sure we include these before we start overflowing
char protocolname[64];
const char *gamestatus;
COM_ParseOut(com_protocolname.string, protocolname, sizeof(protocolname)); //we can only report one, so report the first.
if (svprogfuncs)
{
eval_t *v = PR_FindGlobal(svprogfuncs, "worldstatus", PR_ANY, NULL);
if (v)
gamestatus = PR_GetString(svprogfuncs, v->string);
else
gamestatus = "";
}
else
gamestatus = "";
Info_SetValueForKey(resp, "gamename", protocolname, sizeof(info) - (resp-info));//distinguishes it from other types of games
Info_SetValueForKey(resp, "protocol", SV_GetProtocolVersionString(), sizeof(info) - (resp-info));
Info_SetValueForKey(resp, "modname", FS_GetGamedir(true), sizeof(info) - (resp-info));
Info_SetValueForKey(resp, "clients", va("%d", numclients), sizeof(info) - (resp-info));
Info_SetValueForKey(resp, "sv_maxclients", maxclients.string, sizeof(info) - (resp-info));
Info_SetValueForKey(resp, "mapname", InfoBuf_ValueForKey(&svs.info, "map"), sizeof(info) - (resp-info));
resp += strlen(resp);
//now include the full/regular serverinfo
resp += InfoBuf_ToString(&svs.info, resp, sizeof(info) - (resp-info), prioritykeys, ignorekeys, NULL, NULL, NULL);
*resp = 0;
//and any possibly-long qc status string
if (*gamestatus)
Info_SetValueForKey(resp, "qcstatus", gamestatus, sizeof(info) - (resp-info));
resp += strlen(resp);
*resp++ = 0;
}
SV_GeneratePublicServerinfo(info, info+sizeof(info));
FTENET_ICE_SplurgeCmd(b, ICEMSG_SERVERINFO, -1, info);
}
#endif

View file

@ -1,6 +1,6 @@
//This file should be easily portable.
//The biggest strength of this plugin system is that ALL interactions are performed via
//named functions, this makes it *really* easy to port plugins from one engine to annother.
//named functions, this makes it *really* easy to port plugins from one engine to another.
#include "quakedef.h"
#include "netinc.h"

View file

@ -26,10 +26,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <sys/stat.h> //to delete the file/socket.
#endif
#if defined(__unix__) && !defined(__linux__) && !defined(__APPLE__) && !defined(__CYGWIN__)
#include <sys/epoll.h>
#endif
extern ftemanifest_t *fs_manifest;
// Eww, eww. This is hacky but so is netinc.h, so bite me
@ -93,7 +89,7 @@ static cvar_t net_enable_kexlobby = CVARD("net_enable_"KEXLOBBY, "0", "If en
//#endif
#ifdef HAVE_EPOLL
static int epoll_fd = -1;
int epoll_fd = -1;
#endif
void NET_GetLocalAddress (int socket, netadr_t *out);
@ -223,10 +219,16 @@ static void NET_TLS_Provider_Changed(struct cvar_s *var, char *oldvalue)
}
if (host_initialized && !var->ival)
{
Con_Printf("%s: \"%s\" not loaded, valid values are:", var->name, var->string);
int found = 0;
for (i = 0; i < cryptolib_count; i++)
if (cryptolib[i])
{
if (!found++)
Con_Printf("%s: \"%s\" not loaded, valid values are:", var->name, var->string);
Con_Printf(" ^[%s\\type\\%s %s^]", cryptolib[i]->drivername, var->name, cryptolib[i]->drivername);
}
if (!found)
Con_Printf("%s: no tls plugins loaded", var->name);
Con_Printf("\n");
}
@ -2899,6 +2901,7 @@ static ftenet_generic_connection_t *FTENET_Loop_EstablishConnection(ftenet_conne
#endif
newcon->islisten = col->islisten;
newcon->prot = adr.prot;
newcon->addrtype[0] = NA_LOOPBACK;
newcon->addrtype[1] = NA_INVALID;
@ -4387,6 +4390,7 @@ ftenet_generic_connection_t *FTENET_Datagram_EstablishConnection(ftenet_connecti
newcon->owner = col;
newcon->islisten = isserver;
newcon->prot = adr.prot;
if (hybrid)
{
newcon->addrtype[0] = NA_IP;
@ -5475,7 +5479,7 @@ enum
WCATTR_ACCEPT_ENCODING,
WCATTR_TRANSFER_ENCODING
};
typedef char httparg_t[64];
typedef char httparg_t[256];
#include "fs.h"
#ifdef _WIN32
#include "resource.h"
@ -5711,7 +5715,7 @@ qboolean FTENET_TCP_HTTPResponse(ftenet_tcp_stream_t *st, httparg_t arg[WCATTR_C
"{"
"if (Module['sched'] === undefined)"
"{" //our main function failed to set up the main loop. ie: main didn't get called. panic.
"alert('Unable to initialise. You may need to restart your browser. If you get this often and inconsistently, consider using a 64bit browser instead.');"
"alert('Unable to initialise. You may need to restart your browser.');"
"Module.setStatus('Initialisation Failure');"
"}"
"}"
@ -6117,7 +6121,7 @@ static const char *FTENET_TCP_ParseHTTPRequest(ftenet_tcp_connection_t *con, fte
int websocketver = 0;
qboolean acceptsgzip = false;
qboolean sendingweirdness = false;
char arg[WCATTR_COUNT][64];
httparg_t arg[WCATTR_COUNT];
if (!net_enable_http.ival && !net_enable_websockets.ival && !net_enable_rtcbroker.ival)
@ -6134,7 +6138,7 @@ static const char *FTENET_TCP_ParseHTTPRequest(ftenet_tcp_connection_t *con, fte
arg[i][0] = 0;
for (i = 0; i < st->inlen; i++)
{
if (alen == 63)
if (alen >= sizeof(arg[attr])-1)
{
Con_Printf("http request overflow from %s\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr));
//we need to respond, firefox will create 10 different connections if we just close it
@ -6285,7 +6289,7 @@ static const char *FTENET_TCP_ParseHTTPRequest(ftenet_tcp_connection_t *con, fte
websocketver = atoi(&st->inbuffer[j]);
break;
default:
Q_strncpyz(arg[attr], &st->inbuffer[j], (i-j > 63)?64:(i - j + 1));
Q_strncpyz(arg[attr], &st->inbuffer[j], (i-j >= sizeof(arg[attr]))?sizeof(arg[attr]):(i - j + 1));
break;
}
}
@ -7867,6 +7871,7 @@ static qboolean FTENET_TCP_ChangeLocalAddress(struct ftenet_generic_connection_s
n.scopeid = adr->scopeid;
addrsize2 = NetadrToSockadr(&n, &cur);
con->prot = NP_STREAM;
if ((bind(newsocket, (struct sockaddr *)&cur, addrsize2) != INVALID_SOCKET) &&
(listen(newsocket, 2) != INVALID_SOCKET) &&
ioctlsocket (newsocket, FIONBIO, &_true) != -1)
@ -8026,6 +8031,7 @@ ftenet_generic_connection_t *FTENET_TCP_EstablishConnection(ftenet_connections_t
newcon = Z_Malloc(sizeof(*newcon));
newcon->generic.thesocket = newsocket = INVALID_SOCKET;
newcon->generic.prot = adr.prot;
newcon->generic.addrtype[0] = adr.type;
newcon->generic.addrtype[1] = NA_INVALID;
@ -8732,6 +8738,7 @@ struct ftenet_generic_connection_s *FTENET_IRCConnect_EstablishConnection(qboole
newcon->generic.Close = FTENET_IRCConnect_Close;
newcon->generic.islisten = isserver;
newcon->generic.prot = adr.prot;
newcon->generic.addrtype[0] = NA_IRC;
newcon->generic.addrtype[1] = NA_INVALID;
@ -8824,30 +8831,11 @@ static void FTENET_WebRTC_Heartbeat(ftenet_websocket_connection_t *b)
#ifdef HAVE_SERVER
if (b->generic.islisten)
{
extern cvar_t maxclients;
char info[2048];
int i;
client_t *cl;
int numclients = 0;
for (i=0 ; i<svs.allocated_client_slots ; i++)
{
cl = &svs.clients[i];
if ((cl->state == cs_connected || cl->state == cs_spawned || cl->name[0]) && !cl->spectator)
numclients++;
}
info[0] = ICEMSG_SERVERINFO;
info[1] =
info[2] = 0xff; //to the broker rather than any actual client
info[3] = 0;
Info_SetValueForKey(info+3, "protocol", SV_GetProtocolVersionString(), sizeof(info)-3);
Info_SetValueForKey(info+3, "maxclients", maxclients.string, sizeof(info)-3);
Info_SetValueForKey(info+3, "clients", va("%i", numclients), sizeof(info)-3);
Info_SetValueForKey(info+3, "hostname", hostname.string, sizeof(info)-3);
Info_SetValueForKey(info+3, "modname", FS_GetGamedir(true), sizeof(info)-3);
Info_SetValueForKey(info+3, "mapname", InfoBuf_ValueForKey(&svs.info, "map"), sizeof(info)-3);
Info_SetValueForKey(info+3, "needpass", InfoBuf_ValueForKey(&svs.info, "needpass"), sizeof(info)-3);
SV_GeneratePublicServerinfo(info+3, info+sizeof(info));
if (emscriptenfte_ws_send(b->brokersock, info, 3+strlen(info+3)) <= 0)
return;
}
@ -9309,6 +9297,7 @@ static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(ftenet_
newcon->generic.Close = FTENET_WebSocket_Close;
newcon->generic.islisten = isserver;
newcon->generic.prot = adr.prot;
newcon->generic.addrtype[0] = NA_WEBSOCKET;
newcon->generic.addrtype[1] = NA_INVALID;
@ -9360,6 +9349,7 @@ static ftenet_generic_connection_t *FTENET_WebRTC_EstablishConnection(ftenet_con
newcon->generic.Close = FTENET_WebSocket_Close;
newcon->generic.islisten = isserver;
newcon->generic.prot = adr.prot;
newcon->generic.addrtype[0] = NA_WEBSOCKET;
newcon->generic.addrtype[1] = NA_INVALID;
@ -10486,7 +10476,8 @@ NET_Init
void NET_Init (void)
{
#ifdef HAVE_EPOLL
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd < 0)
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
#endif
Cvar_Register(&net_enabled, "networking");
@ -10885,7 +10876,7 @@ void NET_Shutdown (void)
#ifdef HAVE_EPOLL
if (epoll_fd >= 0)
close(epoll_fd);
epoll_close(epoll_fd);
epoll_fd = -1;
stdin_epolling = false;
#endif
@ -11171,12 +11162,513 @@ vfsfile_t *FS_WrapTCPSocket(SOCKET sock, qboolean conpending, const char *peerna
return &newf->funcs;
}
vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls)
typedef struct {
vfsfile_t funcs;
vfsfile_t *stream;
int conpending; //waiting for the proper handshake response, don't write past this.
unsigned int mask; //xor masking, to make it harder to exploit buggy shit that's parsing streams (like magic packets or w/e).
char readbuffer[65536];
int readbufferofs;
int readbuffered;
char *pending;
int pendingofs;
int pendingsize;
int pendingmax;
int err;
} websocketfile_t;
static void VFSWS_Flush (websocketfile_t *f)
{
//try flushing it now. note: tls packet sizes can leak.
int i = f->conpending?f->conpending:f->pendingsize;
if (i == f->pendingofs)
return; //nothing to flush.
i = VFS_WRITE(f->stream, f->pending+f->pendingofs, i-f->pendingofs);
if (i > 0)
f->pendingofs += i;
else if (i < 0)
{
f->err = i;
VFS_CLOSE(f->stream); //close it.
f->stream = NULL;
}
}
static void VFSWS_Append (websocketfile_t *f, unsigned packettype, const unsigned char *data, size_t length)
{
union
{
unsigned char b[4];
int i;
} mask;
unsigned short ctrl = 0x8000 | (packettype<<8);
quint64_t paylen = 0;
unsigned int payoffs = f->pendingsize;
// int i;
if (!f->stream)
return; //can't do anything anyway...
switch((ctrl>>8) & 0xf)
{
/*case WS_PACKETTYPE_TEXTFRAME:
for (i = 0; i < length; i++)
{
paylen += (data[i] == 0 || data[i] >= 0x80)?2:1;
}
break;*/
default:
paylen = length;
break;
}
payoffs = 2; //ctrl header
if (paylen >= (1<<16))
ctrl |= 127, payoffs+=8; //64bit len... overkill
else if (paylen >= 126)
ctrl |= 126, payoffs+=2; //16bit len.
else
ctrl |= paylen; //smol
if (ctrl&0x80)
payoffs += 4; //mask
payoffs += paylen;
if (f->pendingmax < f->pendingsize+payoffs)
{ //oh noes. wouldn't be space
if (f->pendingofs && !f->conpending/*don't get confused*/)
{ //move it down, we already sent that bit.
f->pendingsize -= f->pendingofs;
memmove(f->pending, f->pending + f->pendingofs, f->pendingsize);
f->pendingofs = 0;
}
if (f->pendingmax < f->pendingsize + payoffs)
{ //still too big. make the buffer bigger.
f->pendingmax = f->pendingsize + payoffs;
f->pending = realloc(f->pending, f->pendingmax);
}
}
payoffs = f->pendingsize;
f->pending[payoffs++] = ctrl>>8;
f->pending[payoffs++] = ctrl&0xff;
if ((ctrl&0x7f) == 127)
{
f->pending[payoffs++] = (paylen>>56)&0xff;
f->pending[payoffs++] = (paylen>>48)&0xff;
f->pending[payoffs++] = (paylen>>40)&0xff;
f->pending[payoffs++] = (paylen>>32)&0xff;
f->pending[payoffs++] = (paylen>>24)&0xff;
f->pending[payoffs++] = (paylen>>16)&0xff;
f->pending[payoffs++] = (paylen>> 8)&0xff;
f->pending[payoffs++] = (paylen>> 0)&0xff;
}
else if ((ctrl&0x7f) == 126)
{
f->pending[payoffs++] = (paylen>>8)&0xff;
f->pending[payoffs++] = (paylen>>0)&0xff;
}
if (ctrl&0x80)
{
mask.i = f->mask;
//'re-randomise' it a bit
f->mask = (f->mask<<4) | (f->mask>>(32-4));
f->mask += (payoffs<<16) + paylen;
f->pending[payoffs++] = mask.b[0];
f->pending[payoffs++] = mask.b[1];
f->pending[payoffs++] = mask.b[2];
f->pending[payoffs++] = mask.b[3];
}
switch((ctrl>>8) & 0xf)
{
#if 0
case WS_PACKETTYPE_TEXTFRAME:/*utf8ify the data*/
for (i = 0; i < length; i++)
{
if (!data[i])
{ /*0 is encoded as 0x100 to avoid safety checks*/
f->pending[payoffs++] = 0xc0 | (0x100>>6);
f->pending[payoffs++] = 0x80 | (0x100&0x3f);
}
else if (data[i] >= 0x80)
{ /*larger bytes require markup*/
f->pending[payoffs++] = 0xc0 | (data[i]>>6);
f->pending[payoffs++] = 0x80 | (data[i]&0x3f);
}
else
{ /*lower 7 bits are as-is*/
f->pending[payoffs++] = data[i];
}
}
break;
#endif
default: //raw data
memcpy(f->pending+payoffs, data, length);
payoffs += length;
break;
}
if (ctrl&0x80)
{
unsigned char *buf = f->pending+payoffs-paylen;
int i;
for (i = 0; i < paylen; i++)
buf[i] ^= mask.b[i&3];
}
f->pendingsize = payoffs;
//try flushing it now. note: tls packet sizes can leak.
VFSWS_Flush(f);
}
static qboolean QDECL VFSWS_Close (struct vfsfile_s *file)
{
websocketfile_t *f = (websocketfile_t *)file;
qboolean success = f->stream != NULL;
if (f->stream != NULL)
{ //still open? o.O
VFSWS_Append(f, WS_PACKETTYPE_CLOSE, NULL, 0); //let the other side know it was intended
VFS_WRITE(f->stream, f->pending+f->pendingofs, f->pendingsize-f->pendingofs); //final flush
success = VFS_CLOSE(f->stream); //close it.
f->stream = NULL;
}
free(f->pending);
f->pending = NULL;
Z_Free(f);
return success;
}
static int QDECL VFSWS_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)
{
websocketfile_t *f = (websocketfile_t *)file;
int r;
int t;
VFSWS_Flush(f); //flush any pending writes.
for(t = 0; t < 2; t++)
{
if (t==1 && !f->err)
{
if (f->readbufferofs >= 1024)
{
f->readbuffered -= f->readbufferofs;
memmove(f->readbuffer, f->readbuffer+f->readbufferofs, f->readbuffered);
f->readbufferofs = 0;
}
r = f->stream?VFS_READ(f->stream, f->readbuffer+f->readbuffered, sizeof(f->readbuffer)-f->readbuffered):-1;
if (r > 0)
f->readbuffered += r;
if (r < 0 && f->stream)
f->err = r;
if (r <= 0)
return f->err; //needed more, couldn't get it.
}
if (f->conpending)
{ //look for \r\n\r\n
char *l, *e, *le;
char *upg=NULL, *con=NULL, *accept=NULL, *prot=NULL;
char tok[128];
if (!t)
continue; //read the size
if (f->readbuffered < 13)
continue; //not nuff data for the basic header
if (strncmp(f->readbuffer, "HTTP/1.1 101 ", 13))
{
f->err = VFS_ERROR_UNSPECIFIED;
break;
}
l = f->readbuffer;
e = f->readbuffer+f->readbuffered;
for(;;)
{
for (le = l; le < e && *le != '\n'; le++)
;
if (le == e)
break; //failed.
//track interesting lines as we parse.
if (!strncmp(l, "Upgrade:", 8))
upg = l+8;
else if (!strncmp(l, "Connection:", 11))
con = l+11;
else if (!strncmp(l, "Sec-WebSocket-Accept:", 21))
accept = l+21;
else if (!strncmp(l, "Sec-WebSocket-Protocol:", 23))
prot = l+23;
if (le[0] == '\n' && le[1] == '\r' && le[2] == '\n')
{
le += 3;
if (!con || !COM_ParseTokenOut(con, NULL, tok,sizeof(tok),NULL) || Q_strcasecmp(tok, "Upgrade"))
f->err = VFS_ERROR_UNSPECIFIED; //wrong connection state...
else if (!upg || !COM_ParseTokenOut(upg, NULL, tok,sizeof(tok),NULL) || Q_strcasecmp(tok, "websocket"))
f->err = VFS_ERROR_UNSPECIFIED; //wrong type of upgrade...
else if (!accept)
f->err = VFS_ERROR_UNSPECIFIED; //wrong hash
else
{
COM_ParseTokenOut(prot, NULL, tok,sizeof(tok),NULL);
Con_Printf("websocket connection using protocol %s\n", prot);
}
f->conpending = false;
f->readbufferofs = le-f->readbuffer;
break;
}
l = le+1;
}
if (f->conpending)
continue;
if (f->err)
break;
//try and read the next thing.
t = 0;
continue;
}
else if (f->readbuffered-f->readbufferofs >= 2)
{ //try to make sense of the packet..
unsigned char *inbuffer = f->readbuffer + f->readbufferofs;
size_t inlen = f->readbuffered-f->readbufferofs;
unsigned short ctrl = inbuffer[0]<<8 | inbuffer[1];
unsigned long paylen;
unsigned int payoffs = 2;
unsigned int mask = 0;
if (ctrl & 0x7000)
{
f->err = VFS_ERROR_UNSPECIFIED; //reserved bits set
break;
}
else if ((ctrl & 0x7f) == 127)
{
quint64_t ullpaylen;
//as a payload is not allowed to be encoded as too large a type, and quakeworld never used packets larger than 1450 bytes anyway, this code isn't needed (65k is the max even without this)
if (sizeof(ullpaylen) < 8)
{
f->err = VFS_ERROR_UNSPECIFIED; //wut...
break;
}
if (payoffs + 8 > inlen)
continue; //not enough buffered
ullpaylen =
(quint64_t)inbuffer[payoffs+0]<<56u |
(quint64_t)inbuffer[payoffs+1]<<48u |
(quint64_t)inbuffer[payoffs+2]<<40u |
(quint64_t)inbuffer[payoffs+3]<<32u |
(quint64_t)inbuffer[payoffs+4]<<24u |
(quint64_t)inbuffer[payoffs+5]<<16u |
(quint64_t)inbuffer[payoffs+6]<< 8u |
(quint64_t)inbuffer[payoffs+7]<< 0u;
if (ullpaylen < 0x10000)
{
f->err = VFS_ERROR_UNSPECIFIED; //should have used a smaller encoding...
break;
}
if (ullpaylen > 0x40000)
{
f->err = VFS_ERROR_UNSPECIFIED; //abusively large...
break;
}
paylen = ullpaylen;
payoffs += 8;
}
else if ((ctrl & 0x7f) == 126)
{
if (payoffs + 2 > inlen)
continue; //not enough buffered
paylen =
inbuffer[payoffs+0]<<8 |
inbuffer[payoffs+1]<<0;
if (paylen < 126)
{
f->err = VFS_ERROR_UNSPECIFIED; //should have used a smaller encoding...
break;
}
payoffs += 2;
}
else
{
paylen = ctrl & 0x7f;
}
if (ctrl & 0x80)
{
if (payoffs + 4 > inlen)
continue;
/*this might read data that isn't set yet, but should be safe*/
((unsigned char*)&mask)[0] = inbuffer[payoffs+0];
((unsigned char*)&mask)[1] = inbuffer[payoffs+1];
((unsigned char*)&mask)[2] = inbuffer[payoffs+2];
((unsigned char*)&mask)[3] = inbuffer[payoffs+3];
payoffs += 4;
}
/*if there isn't space, try again next time around*/
if (payoffs + paylen > inlen)
{
if (payoffs + paylen >= sizeof(inbuffer)-1)
{
f->err = VFS_ERROR_UNSPECIFIED; //payload is too big for out in buffer
break;
}
continue; //need more data
}
if (mask)
{
int i;
for (i = 0; i < paylen; i++)
{
inbuffer[i + payoffs] ^= ((unsigned char*)&mask)[i&3];
}
}
t = 0; //allow checking for new data again.
f->readbufferofs += payoffs + paylen; //skip to end...
switch((ctrl>>8) & 0xf)
{
case WS_PACKETTYPE_CLOSE:
if (!f->err)
{
VFSWS_Flush(f);
f->err = VFS_ERROR_EOF;
if (f->pendingofs < f->pendingsize)
return VFS_ERROR_EOF; //nothing more to read (might still have some to flush).
}
break; //will kill it.
case WS_PACKETTYPE_CONTINUATION:
f->err = VFS_ERROR_UNSPECIFIED; //a prior packet lacked the 'fin' flag. we don't support fragmentation though.
break;
case WS_PACKETTYPE_TEXTFRAME: //we don't distinguish. use utf-8 data if you wanted that.
case WS_PACKETTYPE_BINARYFRAME: //actual data
if (bytestoread >= paylen)
{ //caller passed a big enough buffer
memcpy(buffer, f->readbuffer+f->readbufferofs-paylen, paylen);
return paylen;
}
else
Con_Printf("websocket connection received %u-byte package. only %i requested\n", (unsigned)paylen, bytestoread);
continue; //buffer too small... sorry
case WS_PACKETTYPE_PING:
VFSWS_Append(f, WS_PACKETTYPE_PONG, f->readbuffer+f->readbufferofs-paylen, paylen); //send it back
continue; //and look for more.
case WS_PACKETTYPE_PONG: //wut? we didn't ask for this
default:
break;
}
}
else
continue; //need more data
break; //oops?
}
if (f->err)
{ //something bad happened
if (f->stream)
VFS_CLOSE(f->stream);
f->stream = NULL;
return f->err;
}
return VFS_ERROR_TRYLATER;
}
static int QDECL VFSWS_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestowrite)
{ //websockets are a pseudo-packet protocol, so queue one packet at a time. there may still be extra data queued at a lower level.
websocketfile_t *f = (websocketfile_t *)file;
if (!f->stream)
return f->err;
if (f->pendingsize > 8192)
return VFS_ERROR_TRYLATER; //something pending... don't queue excessively.
//okay, we're taking this packet. all or nothing.
VFSWS_Append(f, WS_PACKETTYPE_BINARYFRAME, buffer, bytestowrite);
return bytestowrite;
}
static vfsfile_t *Websocket_WrapStream(vfsfile_t *stream, const char *host, const char *resource, const char *proto)
{ //this is kinda messy. Websocket_WrapStream(FS_OpenSSL(FS_WrapTCPSocket(TCP_OpenStream())))... *sigh*. wss uris kinda require all the extra layers.
websocketfile_t *newf;
char *hello;
if (!stream)
return NULL;
hello = va("GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Connection: Upgrade\r\n"
"Upgrade: websocket\r\n"
"Sec-WebSocket-Version: 13\r\n"
"Sec-WebSocket-Protocol: %s\r\n"
"\r\n", resource, host, proto);
newf = Z_Malloc(sizeof(*newf) + strlen(host));
Sys_RandomBytes((void*)&newf->mask, sizeof(newf->mask));
newf->stream = stream;
newf->funcs.Close = VFSWS_Close;
newf->funcs.ReadBytes = VFSWS_ReadBytes;
newf->funcs.WriteBytes = VFSWS_WriteBytes;
newf->funcs.Flush = NULL;
newf->funcs.GetLen = VFSTCP_GetLen;
newf->funcs.Seek = VFSTCP_Seek;
newf->funcs.Tell = VFSTCP_Tell;
newf->funcs.seekstyle = SS_UNSEEKABLE;
//send the hello, the weird way.
newf->pending = strdup(hello);
newf->conpending = newf->pendingsize = newf->pendingmax = strlen(newf->pending);
VFSWS_Flush(newf);
return &newf->funcs;
}
vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls/*used when no scheme specified*/)
{
netadr_t adr = {0};
if (NET_StringToAdr(name, defaultport, &adr))
const char *resource = "/";
const char *host = name;
const char *proto = NULL;
if (!strncmp(name, "ws:", 3))
assumetls = false, host += 3, defaultport=defaultport?defaultport:80;
else if (!strncmp(name, "wss:", 4))
assumetls = true, host += 4, defaultport=defaultport?defaultport:443;
else
host = name;
if (host != name && host[0] == '/' && host[1] == '/')
{
qboolean wanttls = (adr.prot == NP_TLS || (adr.prot != NP_STREAM && assumetls));
host += 2;
proto = ""; //not specified
}
else
{
proto = host;
host = strstr(host, "://");
if (host)
{
char *t = alloca(1+host-proto);
t[host-proto] = 0;
proto = memcpy(t, proto, host-proto);
host+=3;
}
else
{
host = name;
proto = "";
}
}
resource = strchr(host, '/');
if (!resource)
resource = "/";
else
{
char *t = alloca(1+resource-host);
t[resource-host] = 0;
host = memcpy(t, host, resource-host);
}
if (NET_StringToAdr(host, defaultport, &adr))
{
qboolean wanttls = (adr.prot == NP_WSS || adr.prot == NP_TLS || (adr.prot != NP_STREAM && assumetls));
vfsfile_t *f;
#ifndef HAVE_SSL
if (wanttls)
@ -11185,8 +11677,11 @@ vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls)
f = FS_WrapTCPSocket(TCP_OpenStream(&adr, name), true, name);
#ifdef HAVE_SSL
if (f && wanttls)
f = FS_OpenSSL(name, f, false);
f = FS_OpenSSL(host, f, false);
#endif
if (proto)
f = Websocket_WrapStream(f, host, resource, proto);
return f;
}
else
@ -11196,6 +11691,7 @@ vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls)
typedef struct {
vfsfile_t funcs;
qboolean packetmode;
int id;
int readbuffered;
char readbuffer[65536];
@ -11206,6 +11702,14 @@ int QDECL VFSWS_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread
int len;
int trying;
if (f->packetmode)
{ //just grab the next packet.
len = emscriptenfte_ws_recv(f->id, buffer, bytestoread);
if (len < 0)
len = VFS_ERROR_EOF;
return len;
}
//websockets are pseudo-packetised. tcp isn't.
while (f->readbuffered < bytestoread)
{
@ -11266,6 +11770,7 @@ vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls)
{
wsfile_t *newf;
int id;
const char *proto = "faketcp";
if (!strncmp(name, "./", 2) || //relative-to-page
!strncmp(name, "/", 1) || //relative-to-host
!strncmp(name, "wss://", 6) || //what we'd rather be using...
@ -11273,22 +11778,50 @@ vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls)
;
else
{
const char *host = name;
if (!strncmp(name, "ws:", 3))
assumetls = false, host += 3, defaultport=defaultport?defaultport:80;
else if (!strncmp(name, "wss:", 4))
assumetls = true, host += 4, defaultport=defaultport?defaultport:443;
else
host = name;
if (host == name)
; //no scheme
else if (host != name && host[0] == '/' && host[1] == '/')
name = host+2; //just ws:// without protocol name
else
{
proto = host;
host = strstr(host, "://");
if (host)
{
char *t = alloca(1+host-proto);
t[host-proto] = 0;
proto = memcpy(t, proto, host-proto);
name = host+3;
}
else
return NULL; //something screwy.
}
//bad prefix... probably just a real hostname. don't get confused with relative-to-page uris.
//FIXME: we should probably be trying to handle the defaultport. oh well.
if (assumetls)
name = va("wss://%s", name);
else
{
Con_Printf(CON_WARNING"FS_OpenTCP(%s): Assuming insecure\n", name);
if (host == name)
Con_Printf(CON_WARNING"FS_OpenTCP(%s): Assuming insecure\n", name);
name = va("ws://%s", name); //urgh. will probably fail when browsers block it on https pages.
}
}
id = emscriptenfte_ws_connect(name, "faketcp");
id = emscriptenfte_ws_connect(name, proto);
if (id < 0)
return NULL;
newf = Z_Malloc(sizeof(*newf));
newf->id = id;
newf->packetmode = strcmp(proto, "faketcp");
newf->funcs.Close = VFSWS_Close;
newf->funcs.Flush = NULL;
newf->funcs.GetLen = VFSWS_GetLen;

View file

@ -140,7 +140,7 @@
#include <libc.h>
#endif
#if defined(__unix__) && !defined(__CYGWIN__)
#if defined(__linux__) || defined(HAVE_EPOLL)
//requires linux 2.6.27 up (and equivelent libc)
//note that BSD does tend to support the api, but emulated.
//this works around the select FD limit, and supposedly has better performance.
@ -149,6 +149,13 @@
#define HAVE_EPOLL
//#else too old, probably android...
#endif
#if defined(close) //epoll-shim? stop it from breaking shit
#undef close
#undef fcntl
#define epoll_close epoll_shim_close
#else
#define epoll_close close
#endif
#endif
#if defined(__MORPHOS__) && !defined(ixemul)

View file

@ -1,6 +1,6 @@
//This file should be easily portable.
//The biggest strength of this plugin system is that ALL interactions are performed via
//named functions, this makes it *really* easy to port plugins from one engine to annother.
//named functions, this makes it *really* easy to port plugins from one engine to another.
#include "quakedef.h"
#include "fs.h"

View file

@ -826,7 +826,7 @@ void QCBUILTIN PF_json_get_name(pubprogfuncs_t *prinst, struct globalvars_s *pr_
void QCBUILTIN PF_json_get_integer(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
qcjson_t *handle = JSONFromQC(G_INT(OFS_PARM0));
switch (handle->type)
safeswitch (handle->type)
{
case json_type_number:
case json_type_true:
@ -836,7 +836,10 @@ void QCBUILTIN PF_json_get_integer(pubprogfuncs_t *prinst, struct globalvars_s *
case json_type_string:
G_INT(OFS_RETURN) = atoi(PR_GetString(prinst, handle->u.strofs));
break;
default:
case json_type_object:
case json_type_array:
case json_type_null:
safedefault:
G_INT(OFS_RETURN) = 0;
break;
}
@ -844,7 +847,7 @@ void QCBUILTIN PF_json_get_integer(pubprogfuncs_t *prinst, struct globalvars_s *
void QCBUILTIN PF_json_get_float(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
qcjson_t *handle = JSONFromQC(G_INT(OFS_PARM0));
switch (handle->type)
safeswitch (handle->type)
{
case json_type_number:
case json_type_true:
@ -854,7 +857,10 @@ void QCBUILTIN PF_json_get_float(pubprogfuncs_t *prinst, struct globalvars_s *pr
case json_type_string:
G_FLOAT(OFS_RETURN) = atof(PR_GetString(prinst, handle->u.strofs));
break;
default:
case json_type_object:
case json_type_array:
case json_type_null:
safedefault:
G_FLOAT(OFS_RETURN) = 0;
break;
}
@ -2040,6 +2046,8 @@ void QCBUILTIN PF_memalloc (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
{
int size = G_INT(OFS_PARM0);
void *ptr;
if (!size)
size = 1; //return something free can free.
if (size <= 0 || size > 0x01000000)
ptr = NULL; //don't let them abuse things too much. values that are too large might overflow.
else
@ -2050,7 +2058,29 @@ void QCBUILTIN PF_memalloc (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
G_INT(OFS_RETURN) = (char*)ptr - prinst->stringtable;
}
else
{
G_INT(OFS_RETURN) = 0;
PR_BIError(prinst, "PF_memalloc: failure (size %i)\n", size);
}
}
void QCBUILTIN PF_memrealloc (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
void *oldcptr = prinst->stringtable + G_INT(OFS_PARM0);
int size = G_INT(OFS_PARM1);
void *ptr;
if (!size)
size = 1; //return something free can free.
if (size <= 0 || size > 0x01000000)
ptr = NULL; //don't let them abuse things too much. values that are too large might overflow.
else
ptr = prinst->AddressableRealloc(prinst, oldcptr, size);
if (ptr)
G_INT(OFS_RETURN) = (char*)ptr - prinst->stringtable;
else
{
G_INT(OFS_RETURN) = 0;
PR_BIError(prinst, "PF_memrealloc: failure (size %i)\n", size);
}
}
void QCBUILTIN PF_memfree (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@ -2089,15 +2119,15 @@ void QCBUILTIN PF_memcpy (pubprogfuncs_t *prinst, struct globalvars_s *pr_global
int srcoffset = (prinst->callargc>3)?G_INT(OFS_PARM3):0;
int dstoffset = (prinst->callargc>4)?G_INT(OFS_PARM4):0;
if (size < 0)
PR_BIError(prinst, "PF_memcpy: invalid size\n");
PR_BIError(prinst, "PF_memcpy: invalid size %#x\n", size);
else if (size)
{
void *dst = PR_PointerToNative_Resize(prinst, qcdst, dstoffset, size);
void *src = PR_PointerToNative_MoInvalidate(prinst, qcsrc, srcoffset, size);
if (!dst)
PR_BIError(prinst, "PF_memcpy: invalid dest\n");
PR_BIError(prinst, "PF_memcpy: invalid dest (%#x[%#x...%#x]\n", qcdst, dstoffset, dstoffset+size-1);
else if (!src)
PR_BIError(prinst, "PF_memcpy: invalid source\n");
PR_BIError(prinst, "PF_memcpy: invalid source (%#x[%#x...%#x]\n", qcsrc, srcoffset, srcoffset+size-1);
else
memmove(dst, src, size);
}
@ -2632,16 +2662,24 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
if (!pf_fopen_files[i].prinst)
break;
G_FLOAT(OFS_RETURN) = -1; //assume an error
if (i == MAX_QC_FILES) //too many already open
{
Con_Printf("qcfopen(\"%s\"): too many files open\n", name);
G_FLOAT(OFS_RETURN) = -1;
return;
}
if (fmode < 0 && (!strncmp(name, "tcp://", 6) || !strncmp(name, "tls://", 6)))
if (fmode < 0 && (!strncmp(name, "tcp://", 6) || !strncmp(name, "tls://", 6) || !strncmp(name, "ws:", 3) || !strncmp(name, "wss:", 4)))
{
G_FLOAT(OFS_RETURN) = -1;
extern cvar_t pr_enable_uriget;
#if defined(CSQC_DAT) && !defined(SERVERONLY)
extern world_t csqc_world;
if (prinst == csqc_world.progs) //menuqc will refuse to load from untrusted sources. ssqc is the admin's choice. csqc is fundamentally untrusted though.
return;
#endif
if (!pr_enable_uriget.ival) //same cvar to block http requests.
return;
Q_strncpyz(pf_fopen_files[i].name, name, sizeof(pf_fopen_files[i].name));
pf_fopen_files[i].accessmode = FRIK_FILE_STREAM;
pf_fopen_files[i].bufferlen = 0;
@ -2664,7 +2702,6 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
if (!QC_FixFileName(name, &name, &fallbackread))
{
Con_Printf("qcfopen(\"%s\"): Access denied\n", name);
G_FLOAT(OFS_RETURN) = -1;
return;
}
@ -2700,10 +2737,7 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
}
if (!pf_fopen_files[i].data)
{
G_FLOAT(OFS_RETURN) = -1;
break;
}
pf_fopen_files[i].len = pf_fopen_files[i].bufferlen;
pf_fopen_files[i].ofs = 0;
@ -2729,8 +2763,6 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
G_FLOAT(OFS_RETURN) = i + FIRST_QC_FILE_INDEX;
pf_fopen_files[i].prinst = prinst;
}
else
G_FLOAT(OFS_RETURN) = -1;
}
break;
@ -2748,8 +2780,6 @@ void QCBUILTIN PF_fopen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
G_FLOAT(OFS_RETURN) = i + FIRST_QC_FILE_INDEX;
pf_fopen_files[i].prinst = prinst;
}
else
G_FLOAT(OFS_RETURN) = -1;
pf_fopen_files[i].bufferlen = pf_fopen_files[i].len = fsize;
pf_fopen_files[i].ofs = 0;
@ -3213,23 +3243,23 @@ void QCBUILTIN PF_fread (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
G_INT(OFS_RETURN) = PF_fread_internal (prinst, fnum, ptr, size);
}
void QCBUILTIN PF_fseek (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
void QCBUILTIN PF_fseek64 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int fnum = G_FLOAT(OFS_PARM0) - FIRST_QC_FILE_INDEX;
G_INT(OFS_RETURN) = -1;
G_INT64(OFS_RETURN) = -1;
if (fnum < 0 || fnum >= MAX_QC_FILES)
{
PF_Warningf(prinst, "PF_fread: File out of range\n");
PF_Warningf(prinst, "PF_fseek: File out of range\n");
return; //out of range
}
if (!pf_fopen_files[fnum].prinst)
{
PF_Warningf(prinst, "PF_fread: File is not open\n");
PF_Warningf(prinst, "PF_fseek: File is not open\n");
return; //not open
}
if (pf_fopen_files[fnum].prinst != prinst)
{
PF_Warningf(prinst, "PF_fread: File is from wrong instance\n");
PF_Warningf(prinst, "PF_fseek: File is from wrong instance\n");
return; //this just isn't ours.
}
@ -3245,23 +3275,29 @@ void QCBUILTIN PF_fseek (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
if (pf_fopen_files[fnum].file)
{
G_INT(OFS_RETURN) = VFS_TELL(pf_fopen_files[fnum].file);
if (prinst->callargc>1 && G_INT(OFS_PARM1) >= 0)
VFS_SEEK(pf_fopen_files[fnum].file, G_INT(OFS_PARM1));
G_INT64(OFS_RETURN) = VFS_TELL(pf_fopen_files[fnum].file);
if (prinst->callargc>1 && G_INT64(OFS_PARM1) >= 0)
VFS_SEEK(pf_fopen_files[fnum].file, G_INT64(OFS_PARM1));
}
else
{
G_INT(OFS_RETURN) = pf_fopen_files[fnum].ofs;
if (prinst->callargc>1 && G_INT(OFS_PARM1) >= 0)
G_INT64(OFS_RETURN) = pf_fopen_files[fnum].ofs;
if (prinst->callargc>1 && G_INT64(OFS_PARM1) >= 0)
{
pf_fopen_files[fnum].ofs = G_INT(OFS_PARM1);
pf_fopen_files[fnum].ofs = G_INT64(OFS_PARM1);
}
}
}
void QCBUILTIN PF_fsize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
void QCBUILTIN PF_fseek32 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
G_INT64(OFS_PARM1) = G_INT(OFS_PARM1);
PF_fseek64(prinst, pr_globals);
G_INT(OFS_RETURN) = G_INT64(OFS_RETURN);
}
void QCBUILTIN PF_fsize64 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int fnum = G_FLOAT(OFS_PARM0) - FIRST_QC_FILE_INDEX;
G_INT(OFS_RETURN) = -1;
G_INT64(OFS_RETURN) = -1;
if (fnum < 0 || fnum >= MAX_QC_FILES)
{
PF_Warningf(prinst, "PF_fsize: File out of range\n");
@ -3290,21 +3326,27 @@ void QCBUILTIN PF_fsize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
if (pf_fopen_files[fnum].file)
{
G_INT(OFS_RETURN) = VFS_GETLEN(pf_fopen_files[fnum].file);
if (prinst->callargc>1 && G_INT(OFS_PARM1) >= 0)
G_INT64(OFS_RETURN) = VFS_GETLEN(pf_fopen_files[fnum].file);
if (prinst->callargc>1 && G_INT64(OFS_PARM1) >= 0)
PF_Warningf(prinst, "PF_fsize: truncation/extension is not supported for stream file types\n");
}
else
{
G_INT(OFS_RETURN) = pf_fopen_files[fnum].len;
if (prinst->callargc>1 && G_INT(OFS_PARM1) >= 0)
G_INT64(OFS_RETURN) = pf_fopen_files[fnum].len;
if (prinst->callargc>1 && G_INT64(OFS_PARM1) >= 0)
{
size_t newlen = G_INT(OFS_PARM1);
size_t newlen = G_INT64(OFS_PARM1);
PF_fresizebuffer_internal(&pf_fopen_files[fnum], newlen);
pf_fopen_files[fnum].len = min(pf_fopen_files[fnum].bufferlen, newlen);
}
}
}
void QCBUILTIN PF_fsize32 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
G_INT64(OFS_PARM1) = G_INT(OFS_PARM1);
PF_fsize64(prinst, pr_globals);
G_INT(OFS_RETURN) = G_INT64(OFS_RETURN);
}
void PF_fcloseall (pubprogfuncs_t *prinst)
{
@ -3414,7 +3456,7 @@ void QCBUILTIN PF_fexists (pubprogfuncs_t *prinst, struct globalvars_s *pr_globa
void QCBUILTIN PF_rmtree (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
const char *fname = PR_GetStringOfs(prinst, OFS_PARM0);
Con_Printf("rmtree(\"%s\"): rmtree is not implemented at this\n", fname);
Con_Printf("rmtree(\"%s\"): rmtree is not implemented at this time\n", fname);
/*flocation_t loc;
G_FLOAT(OFS_RETURN) = -1; //error
@ -4623,6 +4665,26 @@ void QCBUILTIN PF_ftos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
RETURN_TSTRING(pr_string_temp);
}
void QCBUILTIN PF_ftou (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
G_UINT(OFS_RETURN) = G_FLOAT(OFS_PARM0);
}
void QCBUILTIN PF_utof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
if (prinst->callargc > 1)
{
unsigned int value = G_UINT(OFS_PARM0);
unsigned int shift = G_FLOAT(OFS_PARM1);
unsigned int count = G_FLOAT(OFS_PARM2);
value >>= shift;
if (count != 32)
value &= ((1u<<count)-1u);
G_FLOAT(OFS_RETURN) = value;
}
else
G_FLOAT(OFS_RETURN) = G_UINT(OFS_PARM0);
}
void QCBUILTIN PF_ftoi (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
G_INT(OFS_RETURN) = G_FLOAT(OFS_PARM0);
@ -4634,7 +4696,10 @@ void QCBUILTIN PF_itof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
unsigned int value = G_INT(OFS_PARM0);
unsigned int shift = G_FLOAT(OFS_PARM1);
unsigned int count = G_FLOAT(OFS_PARM2);
G_FLOAT(OFS_RETURN) = (value >> shift) & ((1u<<count)-1u);
value >>= shift;
if (count != 32)
value &= ((1u<<count)-1u);
G_FLOAT(OFS_RETURN) = value;
}
else
G_FLOAT(OFS_RETURN) = G_INT(OFS_PARM0);
@ -5032,7 +5097,6 @@ void QCBUILTIN PF_strftime (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
{
const char *in = PF_VarString(prinst, 1, pr_globals);
char result[8192];
char uresult[8192];
time_t ctime;
struct tm *tm;
@ -5051,9 +5115,8 @@ void QCBUILTIN PF_strftime (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
in = "%Y-%m-%d";
strftime(result, sizeof(result), in, tm);
unicode_strtoupper(result, uresult, sizeof(uresult), VMUTF8MARKUP);
RETURN_TSTRING(uresult);
RETURN_TSTRING(result);
}
//String functions
@ -6846,12 +6909,135 @@ void QCBUILTIN PF_rotatevectorsbymatrix (pubprogfuncs_t *prinst, struct globalva
////////////////////////////////////////////////////
//Progs internals
qcstate_t *PR_CreateThread(pubprogfuncs_t *prinst, float retval, float resumetime, qboolean wait)
{
world_t *world = prinst->parms->user;
qcstate_t *state;
edict_t *ed;
state = prinst->parms->memalloc(sizeof(qcstate_t));
state->next = world->qcthreads;
world->qcthreads = state;
state->resumetime = resumetime;
if (prinst->edicttable_length)
{
ed = PROG_TO_EDICT(prinst, world->g.self?*world->g.self:0);
state->self = NUM_FOR_EDICT(prinst, ed);
state->selfid = (prinst==svprogfuncs&&ed->ereftype==ER_ENTITY)?ed->xv->uniquespawnid:0;
ed = PROG_TO_EDICT(prinst, world->g.self?*world->g.self:0);
state->other = NUM_FOR_EDICT(prinst, ed);
state->otherid = (prinst==svprogfuncs&&ed->ereftype==ER_ENTITY)?ed->xv->uniquespawnid:0;
}
else //allows us to call this during init().
state->self = state->other = state->selfid = state->otherid = 0;
state->thread = prinst->Fork(prinst);
state->waiting = wait;
state->returnval = retval;
return state;
}
void PR_RunThreads(world_t *world)
{
struct globalvars_s *pr_globals;
edict_t *ed;
qcstate_t *state = world->qcthreads, *next;
world->qcthreads = NULL;
while(state)
{
pubprogfuncs_t *prinst = world->progs;
next = state->next;
if (state->resumetime > (world->g.time?*world->g.time:0) || state->waiting)
{ //not time yet, reform original list.
state->next = world->qcthreads;
world->qcthreads = state;
}
else
{ //call it and forget it ever happened. The Sleep biltin will recreate if needed.
pr_globals = PR_globals(prinst, PR_CURRENT);
if (world->g.self)
{
//restore the thread's self variable, if applicable.
ed = PROG_TO_EDICT(prinst, state->self);
if ((prinst==svprogfuncs?ed->xv->uniquespawnid:0) != state->selfid)
ed = prinst->edicttable[0];
*world->g.self = EDICT_TO_PROG(prinst, ed);
}
if (world->g.other)
{
//restore the thread's other variable, if applicable
ed = PROG_TO_EDICT(prinst, state->other);
if ((prinst==svprogfuncs?ed->xv->uniquespawnid:0) != state->otherid)
ed = prinst->edicttable[0];
*world->g.other = EDICT_TO_PROG(prinst, ed);
}
G_FLOAT(OFS_RETURN) = state->returnval; //return value of fork or sleep
prinst->RunThread(prinst, state->thread);
prinst->parms->memfree(state->thread);
prinst->parms->memfree(state);
}
state = next;
}
}
void PR_ClearThreads(pubprogfuncs_t *prinst)
{
world_t *world;
qcstate_t *state, *next;
if (!prinst)
return; //shoo!
world = prinst->parms->user;
state = world->qcthreads;
world->qcthreads = NULL;
while(state)
{
next = state->next;
//free the memory.
prinst->parms->memfree(state->thread);
prinst->parms->memfree(state);
state = next;
}
}
void QCBUILTIN PF_Abort(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
prinst->AbortStack(prinst);
}
void QCBUILTIN PF_Sleep(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *world = prinst->parms->user;
float sleeptime;
//this func calls a function in annother progs
sleeptime = G_FLOAT(OFS_PARM0);
PR_CreateThread(prinst, 1, (world->g.time?*world->g.time:0) + sleeptime, false);
prinst->AbortStack(prinst);
}
void QCBUILTIN PF_Fork(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *world = prinst->parms->user;
float sleeptime;
if (svprogfuncs->callargc >= 1)
sleeptime = G_FLOAT(OFS_PARM0);
else
sleeptime = 0;
PR_CreateThread(prinst, 1, (world->g.time?*world->g.time:0) + sleeptime, false);
// PRSV_RunThreads();
G_FLOAT(OFS_RETURN) = 0;
}
//this func calls a function in another progs
//it works in the same way as the above func, except that it calls by reference to a function, as opposed to by it's name
//used for entity function variables - not actually needed anymore
void QCBUILTIN PF_externrefcall (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
@ -6868,20 +7054,28 @@ void QCBUILTIN PF_externrefcall (pubprogfuncs_t *prinst, struct globalvars_s *pr
PR_ExecuteProgram(prinst, f);
}
void QCBUILTIN PF_externset (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) //set a value in annother progs
void QCBUILTIN PF_externset (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) //set a value in another progs
{
int n = G_PROG(OFS_PARM0);
int v = G_INT(OFS_PARM1);
eval_t *v = (eval_t*)&G_INT(OFS_PARM1);
const char *varname = PF_VarString(prinst, 2, pr_globals);
eval_t *var;
etype_t t = ev_void;
var = PR_FindGlobal(prinst, varname, n, NULL);
var = PR_FindGlobal(prinst, varname, n, &t);
if (var)
var->_int = v;
{
if (t == ev_vector)
VectorCopy(v->_vector, var->_vector);
else if (t == ev_int64 || t == ev_uint64 || t == ev_double)
var->i64 = v->i64;
else
var->_int = v->_int;
}
}
void QCBUILTIN PF_externvalue (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) //return a value in annother progs
void QCBUILTIN PF_externvalue (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) //return a value in another progs
{
int n = G_PROG(OFS_PARM0);
const char *varname = PF_VarString(prinst, 1, pr_globals);
@ -6895,6 +7089,8 @@ void QCBUILTIN PF_externvalue (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
G_INT(OFS_RETURN) = (char*)var - prinst->stringtable;
else
G_INT(OFS_RETURN) = 0;
G_INT(OFS_RETURN+1) = 0;
G_INT(OFS_RETURN+2) = 0;
}
else
{
@ -6910,11 +7106,13 @@ void QCBUILTIN PF_externvalue (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
{
n = prinst->FindFunction(prinst, varname, n);
G_INT(OFS_RETURN) = n;
G_INT(OFS_RETURN+1) = 0;
G_INT(OFS_RETURN+2) = 0;
}
}
}
void QCBUILTIN PF_externcall (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) //this func calls a function in annother progs (by name)
void QCBUILTIN PF_externcall (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) //this func calls a function in another progs (by name)
{
int progsnum;
const char *funcname;
@ -6950,6 +7148,26 @@ void QCBUILTIN PF_externcall (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl
}
}
void QCBUILTIN PF_setwatchpoint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
const char *desc = PR_GetStringOfs(prinst, OFS_PARM0);
int type = G_FLOAT(OFS_PARM1);
int qcptr = G_INT(OFS_PARM2);
char variable[64];
if (type == ev_float)
Q_snprintfz(variable,sizeof(variable), "*(float*)%#x", qcptr);
else if (type == ev_string)
Q_snprintfz(variable,sizeof(variable), "*(string*)%#x", qcptr);
else
Q_snprintfz(variable,sizeof(variable), "*(int*)%#x", qcptr);
if (prinst->SetWatchPoint(prinst, desc, variable))
Con_DPrintf("Watchpoint set\n");
else
Con_Printf("Watchpoint failure\n");
}
void QCBUILTIN PF_traceon (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
prinst->debug_trace = DEBUG_TRACE_INTO;
@ -7031,28 +7249,34 @@ void QCBUILTIN PF_localcmd (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
Cbuf_AddText (str, RESTRICT_INSECURE);
}
void QCBUILTIN PF_gettime (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int timer = (prinst->callargc > 0)?G_FLOAT(OFS_PARM0):0;
void QCBUILTIN PF_gettimed (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{ //usng doubles is for longer uptimes rather than extra precision. we artificially limit precision to reduce spectre sidebands, though I'm not sure how useful it'd be.
int timer = (prinst->callargc > 0)?G_INT(OFS_PARM0):0;
switch(timer)
{
default:
case 0: //cached time at start of frame
G_FLOAT(OFS_RETURN) = realtime;
G_DOUBLE(OFS_RETURN) = realtime;
break;
case 1: //actual time, ish. we round to milliseconds to reduce spectre exposure
G_FLOAT(OFS_RETURN) = (qint64_t)Sys_Milliseconds()/1000.0;
G_DOUBLE(OFS_RETURN) = (qint64_t)Sys_Milliseconds()/1000.0;
break;
//case 2: //highres.. looks like time into the frame
//case 3: //uptime
//case 4: //cd track
#ifndef SERVERONLY
case 5: //sim time
G_FLOAT(OFS_RETURN) = cl.time;
G_DOUBLE(OFS_RETURN) = cl.time;
break;
#endif
}
}
void QCBUILTIN PF_gettimef (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
G_INT(OFS_PARM0) = G_FLOAT(OFS_PARM0);
PF_gettimed (prinst, pr_globals);
G_FLOAT(OFS_RETURN) = G_DOUBLE(OFS_RETURN);
}
void QCBUILTIN PF_calltimeofday (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@ -7097,7 +7321,7 @@ void QCBUILTIN PF_sprintf_internal (pubprogfuncs_t *prinst, struct globalvars_s
formatbuf[0] = '%';
#define GETARG_FLOAT(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_FLOAT(OFS_PARM0 + 3 * (a))) : 0)
#define GETARG_DOUBLE(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_FLOAT(OFS_PARM0 + 3 * (a))) : 0)
#define GETARG_DOUBLE(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_DOUBLE(OFS_PARM0 + 3 * (a))) : 0)
#define GETARG_VECTOR(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyvec)
#define GETARG_INT(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_INT(OFS_PARM0 + 3 * (a))) : 0)
#define GETARG_INT64(a) (((a)>=firstarg && (a)<prinst->callargc) ? (G_INT64(OFS_PARM0 + 3 * (a))) : 0)
@ -7707,6 +7931,7 @@ void QCBUILTIN PF_pushmove (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
void PR_Common_Shutdown(pubprogfuncs_t *progs, qboolean errored)
{
PR_ClearThreads(progs);
#if defined(SKELETALOBJECTS) || defined(RAGDOLLS)
skel_reset(progs->parms->user);
#endif
@ -8092,7 +8317,7 @@ qc_extension_t QSG_Extensions[] = {
{"DP_QC_TRACETOSS"},
{"DP_QC_TRACE_MOVETYPE_HITMODEL"},
{"DP_QC_TRACE_MOVETYPE_WORLDONLY"},
{"DP_QC_TRACE_MOVETYPES"}, //this one is just a lame excuse to add annother extension...
{"DP_QC_TRACE_MOVETYPES"}, //this one is just a lame excuse to add another extension...
{"DP_QC_UNLIMITEDTEMPSTRINGS", NULL, 0,{NULL}, "Supersedes DP_QC_MULTIPLETEMPSTRINGS, superseded by FTE_QC_PERSISTENTTEMPSTRINGS. Specifies that all temp strings will be valid at least until the QCVM returns."},
{"DP_QC_URI_ESCAPE", NULL, 2,{"uri_escape", "uri_unescape"}},
#ifdef WEBCLIENT

View file

@ -181,8 +181,10 @@ void QCBUILTIN PF_fputs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals
void QCBUILTIN PF_fgets (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fwrite (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fread (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fseek (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fsize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fseek32 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fsize32 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fseek64 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_fsize64 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_normalize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_vlen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_vhlen (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
@ -195,6 +197,7 @@ void QCBUILTIN PF_rotatevectorsbymatrix (pubprogfuncs_t *prinst, struct globalva
void QCBUILTIN PF_findchain (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_findchainfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_coredump (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_setwatchpoint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_traceon (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_traceoff (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_eprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
@ -237,6 +240,8 @@ void QCBUILTIN PF_stoh (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
void QCBUILTIN PF_htos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_ftoi (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_itof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_ftou (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_utof (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void PR_fclose_progs (pubprogfuncs_t *prinst);
const char *PF_VarString (pubprogfuncs_t *prinst, int first, struct globalvars_s *pr_globals);
void PR_ProgsAdded(pubprogfuncs_t *prinst, int newprogs, const char *modulename);
@ -275,6 +280,7 @@ void QCBUILTIN PF_setattachment(pubprogfuncs_t *prinst, struct globalvars_s *pr_
#define PF_frameduration PF_Fixme
#define PF_modelframecount PF_Fixme
#define PF_frameforname PF_Fixme
#define PF_frameforaction PF_Fixme
#define PF_skel_delete PF_Fixme
#define PF_skel_copybones PF_Fixme
#define PF_skel_premul_bones PF_Fixme
@ -370,7 +376,6 @@ void QCBUILTIN PF_cvars_haveunsaved (pubprogfuncs_t *prinst, struct globalvars_s
void QCBUILTIN PF_cvar_set (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cvar_setf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_ArgC (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_randomvec (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_strreplace (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_strireplace (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_randomvector (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
@ -392,7 +397,12 @@ void QCBUILTIN PF_findchainfloat (pubprogfuncs_t *prinst, struct globalvars_s *p
void QCBUILTIN PF_findchainflags (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_bitshift(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
struct qcstate_s *PR_CreateThread(pubprogfuncs_t *prinst, float retval, float resumetime, qboolean wait);
void PR_ClearThreads(pubprogfuncs_t *prinst);
void PR_RunThreads(world_t *world);
void QCBUILTIN PF_Abort(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_Fork(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_Sleep(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_externcall (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_externrefcall (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_externvalue (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
@ -505,6 +515,7 @@ void QCBUILTIN PF_cl_playingdemo (pubprogfuncs_t *prinst, struct globalvars_s *p
void QCBUILTIN PF_cl_runningserver (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_getgamedirinfo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_getpackagemanagerinfo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_addprogs (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cs_media_create (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cs_media_destroy (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cs_media_command (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
@ -535,6 +546,8 @@ void QCBUILTIN PF_cl_sprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo
void QCBUILTIN PF_cl_bprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_clientcount (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_localsound(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_queueaudio(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_getqueuedaudiotime(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_SendPacket(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_getlocaluserinfoblob (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_getlocaluserinfostring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
@ -594,6 +607,7 @@ void QCBUILTIN PF_base64encode(pubprogfuncs_t *prinst, struct globalvars_s *pr_g
void QCBUILTIN PF_base64decode(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_memalloc (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_memrealloc(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_memfree (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_memcmp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_memcpy (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
@ -605,7 +619,8 @@ void QCBUILTIN PF_memstrsize(pubprogfuncs_t *prinst, struct globalvars_s *pr_glo
void QCBUILTIN PF_soundlength (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_calltimeofday (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_gettime (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_gettimef (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_gettimed (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_whichpack (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
@ -1094,6 +1109,8 @@ enum
globalfunction(CSQC_Parse_TempEntity, "float()")/*EXT_CSQC_ABSOLUTLY_VILE*/ \
\
globalfunction(CSQC_MapEntityEdited, "void(int entidx, string newentdata)")\
globalfunction(StartFrame, "void()")\
globalfunction(EndFrame, "void()")\
\
/*These are pointers to the csqc's globals.*/ \
globalfloat (time) /*float The simulation(aka: smoothed server) time, speed drifts based upon latency*/ \
@ -1273,6 +1290,19 @@ typedef struct csqcedict_s
int skinobject;
} csqcedict_t;
typedef struct qcstate_s
{
float resumetime;
qboolean waiting;
struct qcthread_s *thread;
int self;
int selfid;
int other;
int otherid;
float returnval;
struct qcstate_s *next;
} qcstate_t;
#ifdef __cplusplus
};

View file

@ -206,6 +206,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define C2M_MASTER_REQUEST 'c'
#define M2C_MASTER_REPLY 'd' // + \n + qw server port list
//for S2C 'status' packets.
#define STATUS_OLDSTYLE 0 //equivelent to STATUS_SERVERINFO|STATUS_PLAYERS
#define STATUS_SERVERINFO 1
#define STATUS_PLAYERS 2
#define STATUS_SPECTATORS 4
#define STATUS_SPECTATORS_AS_PLAYERS 8 //for ASE - change only frags: show as "S"
#define STATUS_SHOWTEAMS 16
#define STATUS_QTVLIST 32 //qtv destid "name" "streamid@host:port" numviewers
#define STATUS_LOGININFO 64
//==================
// note that there are some defs.qc that mirror to these numbers
// also related to svc_strings[] in cl_parse
@ -1170,7 +1181,9 @@ enum {
==========================================================
*/
#ifndef MAX_CLIENTS
#define MAX_CLIENTS 255 /*max 255, min 32*/
#endif
#define QWMAX_CLIENTS 32 /*QW's standard max. clients might have issues above this value*/
#define NQMAX_CLIENTS 16 /*NQ's standard max. clients might have issues above this value*/

View file

@ -108,14 +108,17 @@ qboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refres
#define qatomic32_t qint32_t
#define FTE_Atomic32_Inc(ptr) __sync_add_and_fetch(ptr, 1) //returns the AFTER the operation.
#define FTE_Atomic32_Dec(ptr) __sync_add_and_fetch(ptr, -1) //returns the AFTER the operation.
#define FTE_Atomic_Insert(head, newnode, newnodenext) do newnodenext = head; while(!__sync_bool_compare_and_swap(&head, newnodenext, newnode)) //atomically insert into a linked list, being sure to not corrupt the pointers
#elif defined(_WIN32)
#define qatomic32_t long
#define FTE_Atomic32_Inc(ptr) _InterlockedIncrement(ptr)
#define FTE_Atomic32_Dec(ptr) _InterlockedDecrement(ptr)
#define FTE_Atomic_Insert(head, newnode, newnodenext) do newnodenext = head; while(newnodenext != _InterlockedCompareExchangePointer(&head, newnode, newnodenext))
#else
#define qatomic32_t qint32_t
#define FTE_Atomic32_Inc(ptr) FTE_Atomic32Mutex_Add(ptr, 1)
#define FTE_Atomic32_Dec(ptr) FTE_Atomic32Mutex_Add(ptr, -1)
#define FTE_Atomic_Insert(head, newnode, newnodenext) do newnodenext = head; while(!FTE_AtomicPtr_ConditionalReplace(&head, newnodenext, newnode))
#endif

View file

@ -1,4 +1,5 @@
#include "quakedef.h"
#include <wctype.h>
//#define COLOURMISSINGSTRINGS //for english people to more easily see what's not translatable (text still white)
//#define COLOURUNTRANSLATEDSTRINGS //show empty translations as alt-text versions of the original string
@ -27,8 +28,9 @@ cvar_t language = CVARAFCD("lang", sys_language, "prvm_language", CVAR_USERINFO|
static void Filter_Reload_f(void)
{
// FilterInit(
char *file = FS_MallocFile("filter.txt", FS_ROOT, NULL);
FilterInit(file?file:"");
FS_FreeFile(file);
}
void TranslateInit(void)
{
@ -630,6 +632,7 @@ void TL_Reformat(int language, char *out, size_t outsize, size_t numargs, const
const char *fmt;
const char *a;
size_t alen;
unsigned int lastindex = 0;
fmt = (numargs>0&&arg[0])?arg[0]:"";
fmt = TL_Translate(language, fmt);
@ -645,8 +648,11 @@ void TL_Reformat(int language, char *out, size_t outsize, size_t numargs, const
*out++ = '}', fmt+=2, outsize--;
else if (*fmt == '{')
{
unsigned int index = strtoul(fmt+1, (char**)&fmt, 10)+1;
const char *idxstr = fmt+1;
unsigned int index = strtoul(idxstr, (char**)&fmt, 10)+1;
int size = 0;
if (idxstr == fmt) //when no index value was specified, just go for the next one
index = lastindex+1;
if (*fmt == ',')
size = strtol(fmt+1, (char**)&fmt, 10);
if (*fmt == ':')
@ -665,6 +671,8 @@ void TL_Reformat(int language, char *out, size_t outsize, size_t numargs, const
else
a = TL_Translate(language, arg[index]);
lastindex = index;
alen = strlen(a);
if (alen > outsize)
alen = outsize;
@ -708,8 +716,8 @@ static void FilterPurge(void)
}
static void FilterInit(const char *file)
{
qbyte *tempmem = malloc(strlen(file)+1);
qbyte *tempmemstart = tempmem;
qbyte *tempmemstart = malloc(strlen(file)+1);
qbyte *tempmem = tempmemstart;
const char **words;
size_t count = 1, i, l;
size_t bytes;
@ -737,28 +745,29 @@ static void FilterInit(const char *file)
*tempmem++ = tolower(*c);
}
*tempmem++ = 0;
count++;
if (*words[count])
count++;
}
qsort(words, count, sizeof(words[0]), FilterCompareWords); //sort by lead byte... and longest first...
i = 0;
for (i = 0, bytes = 0; i < count; i++)
bytes += strlen(words[i]);
bytes += strlen(words[i])+1;
bytes += countof(filter);
filtermem = malloc(bytes);
filtermem = tempmem = malloc(bytes);
for (l = countof(filter), i = 0; l-- > 0; )
{
if (i < count && words[i][0] == l)
{
filter[l] = filtermem;
filter[l] = tempmem;
while (i < count && *words[i] == l)
{ //second copy... urgh. can forget the first char and replace with a length.
*filtermem++ = strlen(words[i]+1);
memcpy(filtermem, words[i]+1, filtermem[-1]); //just the text, no null needed. tighly packed.
filtermem += filtermem[-1];
*tempmem++ = strlen(words[i]+1);
memcpy(tempmem, words[i]+1, tempmem[-1]); //just the text, no null needed. tighly packed.
tempmem += tempmem[-1];
i++;
}
*filtermem++ = 0;
*tempmem++ = 0;
}
else
filter[l] = NULL;
@ -772,6 +781,8 @@ char *FilterObsceneString(const qbyte *in, char *outbuf, size_t bufsize)
char *ret = outbuf;
if (strlen(in) >= bufsize)
Sys_Error("output buffer too small!");
if (!filtermem)
Filter_Reload_f();
restart:
while (*in)
{
@ -827,3 +838,75 @@ restart:
*outbuf++ = 0; //make sure its null terminated.
return ret;
}
qboolean TL_FilterObsceneCCStringInplace(conchar_t *in, conchar_t *end)
{ //FIXME: filters are meant to be utf-8, but our strings are not.
qboolean obscene = false;
// conchar_t *start = in;
conchar_t *next;
if (!filtermem)
Filter_Reload_f();
restart:
while(in < end)
{
unsigned int c, cflags;
next = Font_Decode(in, &cflags, &c);
c = towlower(c);
if (c < 255 && filter[c])
{
qbyte *m = filter[c];
while (*m)
{ //for each word starting with this letter...
conchar_t *test = next;
qbyte len = *m;
const qbyte *match = m+1;
int err;
m += 1+len;
while(*match && test < end)
{ //don't let 'foo bar' through when 'foobar' is a bad word.
test = Font_Decode(test, &cflags, &c);
if (whiteish(c))
continue;
if (towlower(c) == utf8_decode(&err, match, (char const**)&match))
{
if (--len == 0)
{ //a match.
//peek the next and reject it if we're still mid word
if (test < end)
Font_Decode(test, &cflags, &c);
else
c = 0;
if (c && !whiteish(c))
break; //assassinate!
//okay, not mid-word, obuscate the swears.
while (test > in)
{ //censor it.
if (*in & CON_LONGCHAR && !(*in & CON_RICHFORECOLOUR))
*in = CON_LONGCHAR; //no other flags here.
else
{
//*in = "#@*$"[(in-start)&3] | CON_WHITEMASK;
*in = 0x26a0 | CON_WHITEMASK | (*in&CON_HIDDEN);
obscene = true;
}
in++;
}
goto restart; //double breaks suck
}
continue;
}
break;
}
}
}
for(; next < end; next = Font_Decode(next, &cflags, &c))
{
if (whiteish(c))
break;
}
in = next;
}
return obscene;
}

View file

@ -21,4 +21,6 @@ extern cvar_t language;
#define localtext(t) PO_GetText(languages[com_language].po, t)
int TL_FindLanguage(const char *lang);
qboolean TL_FilterObsceneCCStringInplace(conchar_t *in, conchar_t *end);
#endif

View file

@ -130,7 +130,7 @@ typedef struct q2trace_s
#define MOVE_WORLDONLY (MOVE_NOMONSTERS|MOVE_MISSILE) //use MOVE_OTHERONLY instead
#endif
#define MOVE_HITMODEL (1<<2)
#define MOVE_RESERVED (1<<3) //so we are less likly to get into tricky situations when we want to steal annother future DP extension.
#define MOVE_RESERVED (1<<3) //so we are less likly to get into tricky situations when we want to steal another future DP extension.
#define MOVE_TRIGGERS (1<<4) //triggers must be marked with FINDABLE_NONSOLID (an alternative to solid-corpse)
#define MOVE_EVERYTHING (1<<5) //can return triggers and non-solid items if they're marked with FINDABLE_NONSOLID (works even if the items are not properly linked)
#define MOVE_LAGGED (1<<6) //trace touches current last-known-state, instead of actual ents (just affects players for now)
@ -263,6 +263,7 @@ struct world_s
pvec_t *drawfont;
pvec_t *drawfontscale;
} g;
struct qcstate_s *qcthreads;
#ifdef USERBE
qboolean rbe_hasphysicsents;

View file

@ -1393,7 +1393,7 @@ qboolean R_CalcModelLighting(entity_t *e, model_t *clmodel)
if ((clmodel->engineflags & MDLF_FLAME) || //stuff on fire should generally have enough light...
r_fullbright.ival || //vanila cheat
(e->flags & RF_FULLBRIGHT) || //DP feature
(r_fb_models.ival == 1 && ruleset_allow_fbmodels.ival && (clmodel->engineflags & MDLF_EZQUAKEFBCHEAT) && cls.protocol == CP_QUAKEWORLD && cl.deathmatch)) //ezquake cheat
(r_fb_models.ival == 1 && ruleset_allow_fbmodels.ival && (clmodel->engineflags & MDLF_EZQUAKEFBCHEAT) && cls.protocol == CP_QUAKEWORLD && cl.deathmatch && cls.allow_fbskins>0)) //ezquake cheat
{
e->light_avg[0] = e->light_avg[1] = e->light_avg[2] = 1;
e->light_range[0] = e->light_range[1] = e->light_range[2] = 0;
@ -1453,7 +1453,7 @@ qboolean R_CalcModelLighting(entity_t *e, model_t *clmodel)
// VectorMA(vec3_origin, 0.5, shadelight, ambientlight);
// VectorCopy(ambientlight, shadelight);
if (!r_vertexdlights.ival && r_dynamic.ival > 0)
if (!r_vertexdlights.ival && r_dlightlightmaps)
{
float *org = e->origin;
if (e->flags & RF_WEAPONMODEL)
@ -1500,7 +1500,7 @@ qboolean R_CalcModelLighting(entity_t *e, model_t *clmodel)
}
else
{
if (!r_vertexdlights.ival && r_dynamic.ival > 0)
if (!r_vertexdlights.ival && r_dlightlightmaps)
{
float *org = e->origin;
if (e->flags & RF_WEAPONMODEL)

View file

@ -1307,6 +1307,18 @@ static void Shader_BindTextureForPass(int tmu, const shaderpass_t *pass)
else
t = r_whiteimage;
break;
case T_GEN_TRANSMISSION:
if (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->transmission))
t = shaderstate.curtexnums->transmission;
else
t = r_whiteimage;
break;
case T_GEN_THICKNESS:
if (shaderstate.curtexnums && TEXLOADED(shaderstate.curtexnums->thickness))
t = shaderstate.curtexnums->thickness;
else
t = r_whiteimage;
break;
case T_GEN_SHADOWMAP:
t = shaderstate.curshadowmap;
break;

View file

@ -387,13 +387,12 @@ void R_BloomBlend (texid_t source, int x, int y, int w, int h)
intex = pingtex[0][i];
}
r_worldentity.glowmod[0] = 0;
r_worldentity.glowmod[1] = 0;
if (R2D_Flush)
R2D_Flush();
GL_Set2D(false);
r_worldentity.glowmod[0] = 0;
r_worldentity.glowmod[1] = 0;
bloomfinal->defaulttextures->base = intex;
bloomfinal->defaulttextures->loweroverlay = (i >= 2)?pingtex[0][i-2]:0;
@ -402,6 +401,7 @@ void R_BloomBlend (texid_t source, int x, int y, int w, int h)
/*combine them onto the screen*/
GLBE_FBO_Pop(oldfbo);
GLBE_FBO_Sources(source, r_nulltex);
GL_Set2D(false);
R2D_ScalePic(x, y + h, w, -h, bloomfinal);
}
void R_BloomShutdown(void)

View file

@ -24,7 +24,7 @@
fmt=k -- quake-style raster font with koi8-u codepage (apparently its somewhat common in the quake community)
fmt=h -- halflife-style all-on-one-line raster font
aspect=0.5 -- raster font is squished horizontally
style -- list of modifiets for inexact family font matching for system fonts
style -- list of modifiers for inexact family font matching for system fonts
*/
void Font_Init(void);
@ -404,6 +404,7 @@ static struct
} trackerimages[256];
static int numtrackerimages;
#define TRACKERFIRST 0xe200
#define TRACKERCOUNT 0x100 //an upper bound. so misused codepoints won't go weird.
int Font_RegisterTrackerImage(const char *image)
{
int i;
@ -412,7 +413,7 @@ int Font_RegisterTrackerImage(const char *image)
if (!strcmp(trackerimages[i].name, image))
return TRACKERFIRST + i;
}
if (numtrackerimages == 256)
if (numtrackerimages == TRACKERCOUNT)
return 0;
trackerimages[i].image = NULL; //actually load it elsewhere, because we're lazy.
Q_strncpyz(trackerimages[i].name, image, sizeof(trackerimages[i].name));
@ -1546,7 +1547,7 @@ static struct charcache_s *Font_GetChar(font_t *f, unsigned int codepoint)
c = Font_GetCharIfLoaded(f, charidx);
if (!c)
{
if (charidx >= TRACKERFIRST && charidx < TRACKERFIRST+100)
if (charidx >= TRACKERFIRST && charidx < TRACKERFIRST+TRACKERCOUNT)
{
static struct charcache_s tc;
tc.texplane = TRACKERIMAGE;

View file

@ -5873,7 +5873,7 @@ void Terr_Brush_Draw(heightmap_t *hm, batch_t **batches, entity_t *e)
{
for (s = 0; s < br->faces[j].lmextents[0]; s++)
{
*(unsigned int*)out = (0x3<<30) | (in[2]<<22) | (in[1]<<12) | (in[0]<<2);
*(unsigned int*)out = (0x3u<<30) | (in[2]<<22) | (in[1]<<12) | (in[0]<<2);
out+=4;
in+=3;
}
@ -6709,6 +6709,9 @@ static qboolean Brush_Deserialise(heightmap_t *hm, brushes_t *br, void *mem)
br->faces[i].stdir[1][1] = MSG_ReadFloat();
br->faces[i].stdir[1][2] = MSG_ReadFloat();
br->faces[i].stdir[1][3] = MSG_ReadFloat();
br->faces[i].surfaceflags = 0; //used by q2
br->faces[i].surfacevalue = 0; //used by q2 (generally light levels)
}
return true;
}

View file

@ -371,6 +371,31 @@ qboolean QDECL Mod_LoadHLModel (model_t *mod, void *buffer, size_t fsize)
model->bonectls = bonectls;
#ifndef SERVERONLY
model->compatbones = ZG_Malloc(&mod->memgroup, header->numbones * sizeof(*model->compatbones));
for (i = 0; i < header->numbones; i++)
{
float matrix[12];
Q_strncpyz(model->compatbones[i].name, model->bones[i].name, sizeof(model->bones[i].name));
model->compatbones[i].parent = model->bones[i].parent;
model->compatbones[i].ref.org[0] = model->bones[i].value[0];
model->compatbones[i].ref.org[1] = model->bones[i].value[1];
model->compatbones[i].ref.org[2] = model->bones[i].value[2];
QuaternionGLAngle(model->bones[i].value+3, model->compatbones[i].ref.quat);
model->compatbones[i].ref.scale[0] = 1.0f;
model->compatbones[i].ref.scale[1] = 1.0f;
model->compatbones[i].ref.scale[2] = 1.0f;
//compute rel matrix
GenMatrixPosQuat4Scale(model->compatbones[i].ref.org, model->compatbones[i].ref.quat, model->compatbones[i].ref.scale, matrix);
//compute abs matrix.
if(model->bones[i].parent>=0)
R_ConcatTransforms((void*)transform_matrix[model->bones[i].parent], (void*)matrix, transform_matrix[i]);
else
memcpy(transform_matrix[i], matrix, 12 * sizeof(float));
//keep the ragdoll code happy with its insistance on using inverses.
Matrix3x4_Invert_Simple((const float*)transform_matrix[i], model->compatbones[i].inverse);
}
tex = (hlmdl_tex_t *) ((qbyte *) texheader + texheader->textures);
shaders = ZG_Malloc(&mod->memgroup, texheader->numtextures*sizeof(shader_t));
@ -1192,7 +1217,7 @@ static int HLMDL_GetBoneData_Internal(hlmodel_t *model, int firstbone, int lastb
lastbone = model->header->numbones;
if (cbone >= lastbone)
continue;
HL_SetupBones(model, fstate->g[bgroup].frame[0], cbone, lastbone, fstate->g[bgroup].subblendfrac, fstate->g[bgroup].subblend2frac, fstate->g[bgroup].frametime[0], result); /* Setup the bones */
HL_SetupBones(model, fstate->g[bgroup].frame[0] & ~0x8000, cbone, lastbone, fstate->g[bgroup].subblendfrac, fstate->g[bgroup].subblend2frac, fstate->g[bgroup].frametime[0], result); /* Setup the bones */
cbone = lastbone;
}
return cbone;

View file

@ -1031,27 +1031,28 @@ void Mod_ModelLoaded(void *ctx, void *data, size_t a, size_t b)
{
if (qrenderer != QR_NONE)
Mod_LoadAliasShaders(mod);
}
#ifdef RAGDOLL
if (mod->type == mod_alias || mod->type == mod_halflife)
{
int numbones = Mod_GetNumBones(mod, false);
if (numbones)
{
int numbones = Mod_GetNumBones(mod, false);
if (numbones)
size_t filesize;
char *buf;
char dollname[MAX_QPATH];
Q_snprintfz(dollname, sizeof(dollname), "%s.doll", mod->name);
buf = FS_LoadMallocFile(dollname, &filesize);
if (buf)
{
size_t filesize;
char *buf;
char dollname[MAX_QPATH];
Q_snprintfz(dollname, sizeof(dollname), "%s.doll", mod->name);
buf = FS_LoadMallocFile(dollname, &filesize);
if (buf)
{
mod->dollinfo = rag_createdollfromstring(mod, dollname, numbones, buf);
BZ_Free(buf);
}
mod->dollinfo = rag_createdollfromstring(mod, dollname, numbones, buf);
BZ_Free(buf);
}
}
#endif
}
#endif
#endif
switch(verbose)
@ -3595,8 +3596,8 @@ static void Mod_LoadMiptex(model_t *loadmodel, texture_t *tx, miptex_t *mt, int
mt->offsets[3] == mt->offsets[2]+(mt->width>>2)*(mt->height>>2))
{
extofs = mt->offsets[3]+(mt->width>>3)*(mt->height>>3);
if (loadmodel->fromgame == fg_halflife && *(short*)(ptr + mt->offsets[3] + (mt->width>>3)*(mt->height>>3)) == 256)
{
if (extofs + 2+256*3 <= miptexsize && *(short*)(ptr + extofs) == 256)
{ //space for a halflife paletted texture, with the right signature (note: usually padded).
pal = ptr + extofs+2;
extofs += 2+256*3;
}
@ -4313,6 +4314,59 @@ static qboolean Mod_LoadFaces (model_t *loadmodel, bspx_header_t *bspx, qbyte *m
loadmodel->surfaces = out;
loadmodel->numsurfaces = count;
//dodgy guesses time...
if (loadmodel->fromgame == fg_quake //some halflife maps are misidentified as quake...
&& loadmodel->submodels[0].headnode[3] /*these do have crouch hulls. this'll save a LOT of modulo expense*/
&& subbsp == sb_none/*don't bother with bsp2... maybe halflife will get a remaster that uses/supports it?*/
&& ins && count /*yeah... just in case*/
&& !overrides.shifts /*would break expectations. fix your maps.*/
&& lightlump->filelen%3==0 /*hlbsp has rgb lighting so MUST be a multiple of 3*/
)
{
for (surfnum=0; surfnum<count; surfnum++)
{
lofs = LittleLong(ins[surfnum].lightofs);
if (lofs%3)
break; //not a byte offset within rgb data
if (lofs != (unsigned int)-1 && ins[surfnum].styles[0]!=255)
{
//count styles
for (i = 0; i < countof(ins[surfnum].styles); i++)
if (ins[surfnum].styles[i] == 255)
break;
if (!i)
continue; //no lightmap data here...
tn = LittleShort (ins->texinfo);
if (tn < 0 || tn >= loadmodel->numtexinfo)
break;
out->texinfo = loadmodel->texinfo + tn;
out->firstedge = LittleLong(ins->firstedge);
out->numedges = LittleShort(ins->numedges);
out->lmshift = lmshift;
CalcSurfaceExtents (loadmodel, out);
i *= (out->extents[0]>>out->lmshift)+1; //width
i *= (out->extents[1]>>out->lmshift)+1; //height
i *= 3; //for rgb
//'i' is now the size of our lightmap data, in bytes. phew.
lend = lofs + i;
//we now have a reference surface.
for (surfnum++; surfnum<count; surfnum++)
{
unsigned int checklofs = LittleLong(ins[surfnum].lightofs);
if (checklofs%3)
break; //can't be hl
if (checklofs > lofs && checklofs < lend)
break; //started before reference surf ended... reference surface can't have been using RGB lighting. so not a mislabled hlbsp.
}
break;
}
}
if (surfnum==count)
loadmodel->fromgame = fg_halflife;
}
Mod_LoadVertexNormals(loadmodel, bspx, mod_base, NULL);
Mod_LoadLighting (loadmodel, bspx, mod_base, lightlump, false, &overrides, subbsp);

View file

@ -35,20 +35,24 @@ typedef struct bspx_header_s bspx_header_t;
typedef enum {
SHADER_SORT_NONE,
SHADER_SORT_RIPPLE,
SHADER_SORT_DEFERREDLIGHT,
SHADER_SORT_RIPPLE, //new
SHADER_SORT_DEFERREDLIGHT, //new
SHADER_SORT_PORTAL,
SHADER_SORT_SKY,
SHADER_SORT_SKY, //aka environment
SHADER_SORT_OPAQUE,
//fixme: occlusion tests
SHADER_SORT_DECAL,
SHADER_SORT_SEETHROUGH,
//then rtlights are drawn
SHADER_SORT_UNLITDECAL,
SHADER_SORT_UNLITDECAL, //new
SHADER_SORT_BANNER,
//fog
SHADER_SORT_UNDERWATER,
SHADER_SORT_BLEND,
SHADER_SORT_ADDITIVE,
//blend2,3,6
//stencilshadow
//almostnearest
SHADER_SORT_NEAREST,
@ -894,7 +898,7 @@ typedef struct {
//
typedef enum {mod_brush, mod_sprite, mod_alias, mod_dummy, mod_halflife, mod_heightmap} modtype_t;
typedef enum {fg_quake, fg_quake2, fg_quake3, fg_halflife, fg_new, fg_doom, fg_doom3} fromgame_t; //useful when we have very similar model types. (eg quake/halflife bsps)
typedef enum {fg_quake, fg_quake2, fg_quake3, fg_halflife, fg_new, fg_doom} fromgame_t; //useful when we have very similar model types. (eg quake/halflife bsps)
typedef enum {sb_none, sb_quake64, sb_long1, sb_long2} subbsp_t; // used to denote bsp specifics for load processing only (no runtime changes)
#define MF_ROCKET (1u<<0) // leave a trail

View file

@ -735,7 +735,7 @@ void R_PushDlights (void)
return;
#endif
if (r_dynamic.ival <= 0|| !r_worldentity.model)
if (!r_dlightlightmaps || !r_worldentity.model)
return;
if (r_worldentity.model->loadstate != MLS_LOADED)

View file

@ -1165,6 +1165,21 @@ static void Shader_SurfaceParm (parsestate_t *ps, const char **ptr)
Con_DLPrintf(2, "Shader %s, Unknown surface parm \"%s\"\n", ps->s->name, token); //note that there are game-specific names used to override mod surfaceflags+contents
}
static void Shader_DP_Sort (parsestate_t *ps, const char **ptr)
{
shader_t *shader = ps->s;
char *token;
token = Shader_ParseString ( ptr );
if (!Q_stricmp(token, "sky"))
shader->sort = SHADER_SORT_SKY;
else if (!Q_stricmp(token, "hud"))
shader->sort = SHADER_SORT_NEAREST;
// else if (!Q_stricmp(token, "distance"))
// shader->sort = SHADER_SORT_NONE; //not really immplemented, could maybe force v_depthsortentities. just let q3 rules take over.
}
static void Shader_Sort (parsestate_t *ps, const char **ptr)
{
shader_t *shader = ps->s;
@ -1176,13 +1191,19 @@ static void Shader_Sort (parsestate_t *ps, const char **ptr)
Con_DPrintf("Shader %s, ignoring 'sort %s'\n", ps->s->name, token);
return; //dp ignores 'sort' entirely.
}
// else if ( !Q_stricmp( token, "none" ) )
// shader->sort = SHADER_SORT_NONE; //default, overwritten with an automatic choice.
else if ( !Q_stricmp( token, "ripple" ) ) //fte, weird. drawn only to the ripplemap.
shader->sort = SHADER_SORT_RIPPLE;
else if ( !Q_stricmp( token, "deferredlight" ) ) //fte, weird. drawn only to prelight buffer.
shader->sort = SHADER_SORT_DEFERREDLIGHT;
else if ( !Q_stricmp( token, "portal" ) )
shader->sort = SHADER_SORT_PORTAL;
else if( !Q_stricmp( token, "sky" ) )
shader->sort = SHADER_SORT_SKY;
else if( !Q_stricmp( token, "opaque" ) )
shader->sort = SHADER_SORT_OPAQUE;
else if( !Q_stricmp( token, "decal" ) || !Q_stricmp( token, "litdecal" ) )
else if( !Q_stricmp( token, "decal" ) || !Q_stricmp( token, "litdecal" ) )
shader->sort = SHADER_SORT_DECAL;
else if( !Q_stricmp( token, "seethrough" ) )
shader->sort = SHADER_SORT_SEETHROUGH;
@ -1190,21 +1211,41 @@ static void Shader_Sort (parsestate_t *ps, const char **ptr)
shader->sort = SHADER_SORT_UNLITDECAL;
else if( !Q_stricmp( token, "banner" ) )
shader->sort = SHADER_SORT_BANNER;
else if( !Q_stricmp( token, "additive" ) )
shader->sort = SHADER_SORT_ADDITIVE;
else if( !Q_stricmp( token, "underwater" ) )
shader->sort = SHADER_SORT_UNDERWATER;
else if( !Q_stricmp( token, "blend" ))
shader->sort = SHADER_SORT_BLEND;
else if( !Q_stricmp( token, "additive" ) )
shader->sort = SHADER_SORT_ADDITIVE;
else if( !Q_stricmp( token, "nearest" ) )
shader->sort = SHADER_SORT_NEAREST;
else if( !Q_stricmp( token, "blend" ) )
shader->sort = SHADER_SORT_BLEND;
else if ( !Q_stricmp( token, "deferredlight" ) )
shader->sort = SHADER_SORT_DEFERREDLIGHT;
else if ( !Q_stricmp( token, "ripple" ) )
shader->sort = SHADER_SORT_RIPPLE;
else
{
shader->sort = atoi ( token );
int q3 = atoi ( token );
shadersort_t q3sorttofte[] =
{
/* 0*/SHADER_SORT_NONE,
/* 1*/SHADER_SORT_PORTAL,
/* 2*/SHADER_SORT_SKY, //aka environment in q3
/* 3*/SHADER_SORT_OPAQUE,
/* 4*/SHADER_SORT_DECAL,
/* 5*/SHADER_SORT_SEETHROUGH,
/* 6*/SHADER_SORT_BANNER,
/* 7*/SHADER_SORT_UNDERWATER/*SHADER_SORT_FOG*/,
/* 8*/SHADER_SORT_UNDERWATER,
/* 9*/SHADER_SORT_BLEND, //blend0 in q3
/*10*/SHADER_SORT_ADDITIVE, //blend1 in q3
/*11*/SHADER_SORT_ADDITIVE/*SHADER_SORT_BLEND2*/,
/*12*/SHADER_SORT_ADDITIVE/*SHADER_SORT_BLEND3*/,
/*13*/SHADER_SORT_ADDITIVE/*SHADER_SORT_BLEND6*/, //yes, 4+5 missing in q3...
/*14*/SHADER_SORT_ADDITIVE/*SHADER_SORT_STENCIL*/,
/*15*/SHADER_SORT_NEAREST/*SHADER_SORT_ALMOSTNEAREST*/,
/*16*/SHADER_SORT_NEAREST
};
if (q3 >= 0 && q3 < countof(q3sorttofte))
shader->sort = q3sorttofte[q3];
else
shader->sort = SHADER_SORT_NONE; // :(
clamp ( shader->sort, SHADER_SORT_NONE, SHADER_SORT_NEAREST );
}
}
@ -1374,6 +1415,8 @@ const struct sh_defaultsamplers_s sh_defaultsamplers[] =
{"s_reflectmask", 1u<<S_REFLECTMASK},
{"s_displacement", 1u<<S_DISPLACEMENT},
{"s_occlusion", 1u<<S_OCCLUSION},
{"s_transmission", 1u<<S_TRANSMISSION},
{"s_thickness", 1u<<S_THICKNESS},
{"s_lightmap", 1u<<S_LIGHTMAP0},
{"s_deluxemap", 1u<<S_DELUXEMAP0},
#if MAXRLIGHTMAPS > 1
@ -2007,12 +2050,12 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip
nopermutation |= PERMUTATION_SKELETAL;
//multiple lightmaps is kinda hacky. if any are set, all must be.
#define ALTLIGHTMAPSAMP 14
if (prog->defaulttextures & ((1u<<(ALTLIGHTMAPSAMP+0)) | (1u<<(ALTLIGHTMAPSAMP+1)) | (1u<<(ALTLIGHTMAPSAMP+2))))
prog->defaulttextures |=((1u<<(ALTLIGHTMAPSAMP+0)) | (1u<<(ALTLIGHTMAPSAMP+1)) | (1u<<(ALTLIGHTMAPSAMP+2)));
#define ALTDELUXMAPSAMP 17
if (prog->defaulttextures & ((1u<<(ALTDELUXMAPSAMP+0)) | (1u<<(ALTDELUXMAPSAMP+1)) | (1u<<(ALTDELUXMAPSAMP+2))))
prog->defaulttextures |=((1u<<(ALTDELUXMAPSAMP+0)) | (1u<<(ALTDELUXMAPSAMP+1)) | (1u<<(ALTDELUXMAPSAMP+2)));
#if MAXRLIGHTMAPS > 1
if (prog->defaulttextures & ((1u<<S_LIGHTMAP1 ) | (1u<<S_LIGHTMAP2 ) | (1u<<S_LIGHTMAP3 )))
prog->defaulttextures |=((1u<<S_LIGHTMAP1 ) | (1u<<S_LIGHTMAP2 ) | (1u<<S_LIGHTMAP3 ));
if (prog->defaulttextures & ((1u<<S_DELUXEMAP1) | (1u<<S_DELUXEMAP2) | (1u<<S_DELUXEMAP3)))
prog->defaulttextures |=((1u<<S_DELUXEMAP1) | (1u<<S_DELUXEMAP2) | (1u<<S_DELUXEMAP3));
#endif
for (end = *name?strchr(name+1, '#'):NULL; end && *end; )
{
@ -2169,6 +2212,11 @@ static void Shader_LoadGeneric(sgeneric_t *g, int qrtype)
if (strchr(basicname, '/') || strchr(basicname, '.'))
{ //explicit path
FS_LoadFile(basicname, &file);
if (!file)
{ //well that failed. try fixing up the extension in case they omitted that.
Q_snprintfz(blobname, sizeof(blobname), COM_SkipPath(sh_config.progpath), basicname);
FS_LoadFile(blobname, &file);
}
*blobname = 0;
}
else if (ruleset_allow_shaders.ival)
@ -2581,7 +2629,7 @@ static void Shader_ReflectCube(parsestate_t *ps, const char **ptr)
static void Shader_ReflectMask(parsestate_t *ps, const char **ptr)
{
char *token = Shader_ParseSensString(ptr);
unsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, 0);
unsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, IF_NOSRGB);
ps->s->defaulttextures->reflectmask = Shader_FindImage(ps, token, flags);
}
@ -2629,6 +2677,18 @@ static void Shader_DisplacementMap(parsestate_t *ps, const char **ptr)
unsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, IF_NOSRGB);
ps->s->defaulttextures->displacement = Shader_FindImage(ps, token, flags);
}
static void Shader_TransmissionMap(parsestate_t *ps, const char **ptr)
{
char *token = Shader_ParseSensString(ptr);
unsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, IF_NOSRGB);
ps->s->defaulttextures->transmission = Shader_FindImage(ps, token, flags);
}
static void Shader_ThicknessMap(parsestate_t *ps, const char **ptr)
{
char *token = Shader_ParseSensString(ptr);
unsigned int flags = Shader_SetImageFlags (ps, ps->pass, &token, IF_NOSRGB);
ps->s->defaulttextures->thickness = Shader_FindImage(ps, token, flags);
}
static void Shaderpass_QF_Material(parsestate_t *ps, const char **ptr)
{ //qf_material BASETEXTURE NORMALMAP SPECULARMAP
@ -2789,6 +2849,23 @@ static void Shader_FactorEmit(parsestate_t *ps, const char **ptr)
shader->factors[MATERIAL_FACTOR_EMIT][2] = Shader_ParseFloat(shader, ptr, 1);
shader->factors[MATERIAL_FACTOR_EMIT][3] = Shader_ParseFloat(shader, ptr, 1);
}
static void Shader_FactorTransmission(parsestate_t *ps, const char **ptr)
{
shader_t *shader = ps->s;
shader->factors[MATERIAL_FACTOR_TRANSMISSION][0] = Shader_ParseFloat(shader, ptr, 1);
// shader->factors[MATERIAL_FACTOR_TRANSMISSION][1] = the volume distance;
shader->factors[MATERIAL_FACTOR_TRANSMISSION][2] = 0;
shader->factors[MATERIAL_FACTOR_TRANSMISSION][3] = 0;
}
static void Shader_FactorVolume(parsestate_t *ps, const char **ptr)
{
shader_t *shader = ps->s;
shader->factors[MATERIAL_FACTOR_VOLUME][0] = Shader_ParseFloat(shader, ptr, 1); //r
shader->factors[MATERIAL_FACTOR_VOLUME][1] = Shader_ParseFloat(shader, ptr, 1); //g
shader->factors[MATERIAL_FACTOR_VOLUME][2] = Shader_ParseFloat(shader, ptr, 1); //b
shader->factors[MATERIAL_FACTOR_VOLUME][3] = Shader_ParseFloat(shader, ptr, 1); //factor
shader->factors[MATERIAL_FACTOR_TRANSMISSION][1] = Shader_ParseFloat(shader, ptr, 1); //distance
}
static void Shader_BEMode(parsestate_t *ps, const char **ptr)
{
@ -2919,11 +2996,15 @@ static shaderkey_t shaderkeys[] =
{"lowermap", Shader_LowerMap, "fte"},
{"reflectmask", Shader_ReflectMask, "fte"},
{"displacementmap", Shader_DisplacementMap, "fte"},
{"transmissionmap", Shader_TransmissionMap, "fte"},
{"thicknessmap", Shader_ThicknessMap, "fte"},
{"portalfboscale", Shader_PortalFBOScale, "fte"}, //portal/mirror/refraction/reflection FBOs are resized by this scale
{"basefactor", Shader_FactorBase, "fte"}, //material scalers for glsl
{"specularfactor", Shader_FactorSpec, "fte"}, //material scalers for glsl
{"fullbrightfactor", Shader_FactorEmit, "fte"}, //material scalers for glsl
{"fte_transmissionfactor",Shader_FactorTransmission,"fte"}, //material scalers for glsl
{"fte_volumefactor", Shader_FactorVolume, "fte"}, //material scalers for glsl
//TODO: PBR textures...
// {"albedomap", Shader_DiffuseMap, "fte"}, //rgb(a)
@ -2952,6 +3033,7 @@ static shaderkey_t shaderkeys[] =
{"polygonoffset", NULL, "dp"},
{"glossintensitymod", Shader_DP_GlossScale, "dp"}, //scales r_shadow_glossintensity(=1), aka: gl_specular
{"glossexponentmod", Shader_DP_GlossExponent, "dp"}, //scales r_shadow_glossexponent(=32)
{"transparentsort", Shader_DP_Sort, "dp"}, //urgh...
/*doom3 compat*/
{"diffusemap", Shader_DiffuseMap, "doom3"}, //macro for "{\nstage diffusemap\nmap <map>\n}"
@ -3940,7 +4022,7 @@ static void Shaderpass_Scroll (parsestate_t *ps, const char **ptr)
}
else
{
Con_Printf("Bad shader scale\n");
Con_DPrintf("Bad shader scroll value\n");
return;
}
@ -3952,7 +4034,7 @@ static void Shaderpass_Scroll (parsestate_t *ps, const char **ptr)
}
else
{
Con_Printf("Bad shader scale\n");
Con_DPrintf("Bad shader scroll value\n");
return;
}
@ -4307,7 +4389,9 @@ qboolean Shader_Init (void)
#ifdef FTE_TARGET_WEB
sh_config.max_gpu_bones = 0; //webgl tends to crap out if this is too high, so 32 is a good enough value to play safe. some browsers have really shitty uniform performance too, so lets just default to pure-cpu transforms. in javascript. yes, its that bad.
#else
sh_config.max_gpu_bones = 64; //ATI drivers bug out and start to crash if you put this at 128.
//some of our APIs will set their own guesses from queries. don't stomp on that.
if (!sh_config.max_gpu_bones)
sh_config.max_gpu_bones = 64; //ATI drivers bug out and start to crash if you put this at 128.
#endif
}
else
@ -4726,16 +4810,18 @@ static void Shader_FixupProgPasses(parsestate_t *ps, shaderpass_t *pass)
{T_GEN_REFLECTMASK, 0}, //10
{T_GEN_DISPLACEMENT, SHADER_HASDISPLACEMENT},//11
{T_GEN_OCCLUSION, 0}, //12
{T_GEN_TRANSMISSION, 0}, //13
{T_GEN_THICKNESS, 0}, //14
// {T_GEN_REFLECTION, SHADER_HASREFLECT}, //
// {T_GEN_REFRACTION, SHADER_HASREFRACT}, //
// {T_GEN_REFRACTIONDEPTH, SHADER_HASREFRACTDEPTH},//
// {T_GEN_RIPPLEMAP, SHADER_HASRIPPLEMAP}, //
//batch
{T_GEN_LIGHTMAP, SHADER_HASLIGHTMAP}, //13
{T_GEN_DELUXMAP, 0}, //14
//more lightmaps //15,16,17
//mode deluxemaps //18,19,20
{T_GEN_LIGHTMAP, SHADER_HASLIGHTMAP}, //15
{T_GEN_DELUXMAP, 0}, //16
//more lightmaps //17,18,19
//mode deluxemaps //20,21,22
};
#ifdef HAVE_MEDIA_DECODER
@ -4815,7 +4901,9 @@ struct scondinfo_s
static qboolean Shader_Conditional_Read(parsestate_t *ps, struct scondinfo_s *cond, const char *token, const char **ptr)
{
shader_t *shader = ps->s;
if (!Q_stricmp(token, "if"))
if (ps->parseflags & SPF_DOOM3)
return false; //doom materials have conditionals that remove passes, without endifs. don't misparse here.
else if (!Q_stricmp(token, "if"))
{
if (cond->depth+1 == countof(cond->level))
{
@ -5925,16 +6013,18 @@ done:;
{T_GEN_REFLECTMASK, 0}, //10
{T_GEN_DISPLACEMENT, SHADER_HASDISPLACEMENT},//11
{T_GEN_OCCLUSION, 0}, //12
{T_GEN_TRANSMISSION, 0}, //13
{T_GEN_THICKNESS, 0}, //14
// {T_GEN_REFLECTION, SHADER_HASREFLECT}, //
// {T_GEN_REFRACTION, SHADER_HASREFRACT}, //
// {T_GEN_REFRACTIONDEPTH, SHADER_HASREFRACTDEPTH},//
// {T_GEN_RIPPLEMAP, SHADER_HASRIPPLEMAP}, //
//batch
{T_GEN_LIGHTMAP, SHADER_HASLIGHTMAP}, //13
{T_GEN_DELUXMAP, 0}, //14
//more lightmaps //15,16,17
//mode deluxemaps //18,19,20
{T_GEN_LIGHTMAP, SHADER_HASLIGHTMAP}, //15
{T_GEN_DELUXMAP, 0}, //16
//more lightmaps //17,18,19
//mode deluxemaps //20,21,22
};
#ifdef HAVE_MEDIA_DECODER
@ -8035,6 +8125,8 @@ static char *Shader_DecomposeSubPass(char *o, shader_t *s, shaderpass_t *p, qboo
case T_GEN_REFLECTMASK: Shader_DecomposeSubPassMap(o, s, "map $reflectmask", s->defaulttextures[0].reflectmask); break;
case T_GEN_DISPLACEMENT: Shader_DecomposeSubPassMap(o, s, "map $displacement", s->defaulttextures[0].displacement); break;
case T_GEN_OCCLUSION: Shader_DecomposeSubPassMap(o, s, "map $occlusion", s->defaulttextures[0].occlusion); break;
case T_GEN_TRANSMISSION: Shader_DecomposeSubPassMap(o, s, "map $transmission", s->defaulttextures[0].transmission); break;
case T_GEN_THICKNESS: Shader_DecomposeSubPassMap(o, s, "map $thickness", s->defaulttextures[0].thickness); break;
case T_GEN_CURRENTRENDER: sprintf(o, "map $currentrender "); break;
case T_GEN_SOURCECOLOUR: sprintf(o, "map $sourcecolour"); break;
case T_GEN_SOURCEDEPTH: sprintf(o, "map $sourcedepth"); break;

View file

@ -3847,7 +3847,7 @@ void Sh_PreGenerateLights(void)
{
r_shadow_realtime_world_lightmaps.value = 1;
if (!r_shadow_realtime_world_importlightentitiesfrommap.ival)
Con_Printf(CON_WARNING "No lights detected in map.\n");
Con_Printf(CON_WARNING "No lights detected in map, ^[[and importing is disabled]\\type\\r_shadow_realtime_world_importlightentitiesfrommap 1^].\n");
else
Con_DPrintf("No lights detected in map.\n");
}
@ -4098,7 +4098,8 @@ void Sh_DrawLights(qbyte *vis)
r_shadow_realtime_world_importlightentitiesfrommap.modified ||
r_shadow_realtime_dlight.modified ||
r_shadow_realtime_dlight_shadows.modified ||
r_shadow_shadowmapping.modified || r_shadows.modified)
r_shadow_shadowmapping.modified || r_shadows.modified ||
r_shadow_raytrace.modified)
{
r_shadow_realtime_world.modified =
r_shadow_realtime_world_shadows.modified =
@ -4106,6 +4107,7 @@ void Sh_DrawLights(qbyte *vis)
r_shadow_realtime_dlight_shadows.modified =
r_shadow_shadowmapping.modified =
r_shadows.modified =
r_shadow_raytrace.modified =
false;
Sh_CheckSettings();
//make sure the lighting is reloaded
@ -4116,9 +4118,8 @@ void Sh_DrawLights(qbyte *vis)
return;
ignoreflags = (r_shadow_realtime_world.ival?LFLAG_REALTIMEMODE:0)
| (r_shadow_realtime_dlight.ival?LFLAG_NORMALMODE:0);
if (r_dynamic.ival == -1 && r_dynamic.value > 0)
ignoreflags |= LFLAG_LIGHTMAP; //if we're using scenecache then we cannot use lightmap hacks for dlights, so draw them via rtlight code instead.
| (r_shadow_realtime_dlight.ival?LFLAG_NORMALMODE:0)
| ((r_dynamic.ival&&!r_dlightlightmaps)?LFLAG_LIGHTMAP:0); //if we're using scenecache and won't be updating lightmaps then we should still respect r_dynamic.
if (!ignoreflags)
return;

View file

@ -1491,14 +1491,24 @@ static const char *glsl_hdrs[] =
#endif
#endif
"#if defined(ORM) || defined(SG)\n"
"uniform vec4 factors[3];\n"
"uniform vec4 factors[5];\n"
"#define factor_base factors[0]\n"
"#define factor_spec factors[1]\n"
"#define factor_emit factors[2]\n"
"#define factor_transmission factors[3].r\n"
"#define factor_volume_distance factors[3].g\n"
// "#define factor_? factors[3].b\n"
// "#define factor_? factors[3].a\n"
"#define factor_volume_rgb factors[4].rgb\n"
"#define factor_volume_thickness factors[4].a\n"
"#else\n"
"#define factor_base vec4(1.0)\n"
"#define factor_spec vec4(1.0)\n"
"#define factor_emit vec4(1.0)\n"
/*"#define factor_transmission 0.0\n"
"#define factor_volume_distance 0.0\n"
"#define factor_volume_rgb vec3(1.0)\n"
"#define factor_volume_thickness 0.0\n"*/
"#endif\n"
"#ifdef USEUBOS\n"
"layout(std140) uniform u_lightinfo\n"
@ -1541,7 +1551,7 @@ static const char *glsl_hdrs[] =
"float e_time;"
"};\n"
"#ifdef SKELETAL\n"
"layout(std140) unform u_bones\n"
"layout(std140) uniform u_bones\n"
"{\n"
"#ifdef PACKEDBONES\n"
"vec4 m_bones_packed[3*MAX_GPU_BONES];\n"
@ -1648,7 +1658,7 @@ static const char *glsl_hdrs[] =
"wmat[2] += m_bones_packed[2+3*int(v_bone.y)] * v_weight.y;"
"wmat[2] += m_bones_packed[2+3*int(v_bone.z)] * v_weight.z;"
"wmat[2] += m_bones_packed[2+3*int(v_bone.w)] * v_weight.w;"
"wmat[3] = vec4(0.0,0.0,0.0,1.0);\n"
"wmat[3] = vec4(0.0,0.0,0.0,1.0);"
"return m_modelviewprojection * (vec4(v_position.xyz, 1.0) * wmat);"
"}\n"
"vec4 skeletaltransform_nst(out vec3 n, out vec3 t, out vec3 b)"
@ -2422,6 +2432,8 @@ static GLuint GLSlang_CreateShader (program_t *prog, const char *name, int ver,
"uniform sampler2D s_reflectmask;\n",
"uniform sampler2D s_displacement;\n",
"uniform sampler2D s_occlusion;\n",
"uniform sampler2D s_transmission;\n",
"uniform sampler2D s_thickness;\n",
"uniform sampler2D s_lightmap;\n#define s_lightmap0 s_lightmap\n",
"uniform sampler2D s_deluxemap;\n#define s_deluxemap0 s_deluxemap\n",
@ -3664,6 +3676,21 @@ qboolean GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name))
sh_config.progs_supported = gl_config.arb_shader_objects;
sh_config.progs_required = gl_config_nofixedfunc;
if (gl_config.glversion >= 4.1)
{
GLint maxuniformvecs = 256; //minimum value... or 128 on webgl1(grr)
if (gl_config.glversion >= 4.1)
qglGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxuniformvecs);
else if (gl_config.glversion >= 2.0)
{
qglGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &maxuniformvecs); //at least 1024, supposedly.
maxuniformvecs /= 4;
}
sh_config.max_gpu_bones = (maxuniformvecs-64)/3; //we don't know how many we're actually going to use for any specific bit of glsl, so make a generous guess and throw in a bit more for drivers that lie. we need 3 per bone matrix.
sh_config.max_gpu_bones = bound(0, sh_config.max_gpu_bones, MAX_BONES); //o.O
}
if (sh_config.progs_supported)
{
sh_config.pDeleteProg = GLSlang_DeleteProg;

View file

@ -582,7 +582,7 @@ static qboolean EGLHeadless_Init (rendererstate_t *info, unsigned char *palette)
if (GL_Init(info, &EGL_Proc))
return true;
Con_Printf(CON_ERROR "Unable to initialise opengl-on-wayland.\n");
Con_Printf(CON_ERROR "Unable to initialise egl_headless.\n");
return false;
}
static void EGLHeadless_DeInit(void)

View file

@ -311,7 +311,7 @@ static void SDLVID_EnumerateVideoModes (const char *driver, const char *output,
static qboolean SDLVID_Init (rendererstate_t *info, unsigned char *palette, r_qrenderer_t qrenderer)
{
int flags = 0;
#if SDL_MAJOR_VERSION >= 2
#if SDL_VERSION_ATLEAST(2,0,0)
int display = -1;
SDL_DisplayMode modeinfo, *usemode;
@ -361,7 +361,6 @@ static qboolean SDLVID_Init (rendererstate_t *info, unsigned char *palette, r_qr
if (info->stereo)
SDL_GL_SetAttribute(SDL_GL_STEREO, 1);
#if SDL_MAJOR_VERSION >= 2
if (info->srgb)
SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, 1);
@ -393,7 +392,6 @@ static qboolean SDLVID_Init (rendererstate_t *info, unsigned char *palette, r_qr
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
else
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
#endif
if (info->multisample)
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, info->multisample);

View file

@ -398,8 +398,6 @@ void R_DoomWorld();
#endif
#ifdef MAP_PROC
qboolean QDECL D3_LoadMap_CollisionMap(model_t *mod, void *buf, size_t bufsize);
unsigned char *D3_CalcVis(model_t *mod, vec3_t org);
void D3_GenerateAreas(model_t *mod);
#endif
//gl_bloom.c

View file

@ -349,9 +349,12 @@ typedef void (APIENTRY * PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face);
#endif
#ifndef GL_VERSION_4_1
#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB
#endif
#ifndef GL_VERSION_2_0
#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A
#endif
#ifndef GL_ARB_vertex_program

View file

@ -298,6 +298,7 @@ typedef struct //this is stored as the cache. an hlmodel_t is generated when dra
hlmdl_header_t *header;
hlmdl_bone_t *bones;
struct galiasbone_s *compatbones;
hlmdl_bonecontroller_t *bonectls;
hlmdl_sequencefile_t *animcache[MAX_ANIM_GROUPS];
zonegroup_t *memgroup;

File diff suppressed because it is too large Load diff

View file

@ -274,6 +274,8 @@ typedef struct shaderpass_s {
T_GEN_REFLECTMASK, //dpreflectcube mask
T_GEN_DISPLACEMENT, //displacement texture (probably half-float or something so higher precision than normalmap.a)
T_GEN_OCCLUSION, //occlusion mask (instead of baking it into the texture itself, required for correct pbr)
T_GEN_TRANSMISSION, //.r fancy opacity mask (still contributes its own colour over the top, for KHR_materials_transmission)
T_GEN_THICKNESS, //.g depth mask (could be replaced with raytracing, for KHR_materials_volume)
T_GEN_CURRENTRENDER,//copy the current screen to a texture, and draw that
@ -698,7 +700,9 @@ struct shader_s
#define MATERIAL_FACTOR_BASE 0
#define MATERIAL_FACTOR_SPEC 1
#define MATERIAL_FACTOR_EMIT 2
#define MATERIAL_FACTOR_COUNT 3
#define MATERIAL_FACTOR_TRANSMISSION 3
#define MATERIAL_FACTOR_VOLUME 4
#define MATERIAL_FACTOR_COUNT 5
vec4_t factors[MATERIAL_FACTOR_COUNT];
//arranged as a series of vec4s
@ -877,15 +881,17 @@ enum
S_REFLECTMASK = 10,
S_DISPLACEMENT = 11,
S_OCCLUSION = 12,
S_LIGHTMAP0 = 13,
S_DELUXEMAP0 = 14,
S_TRANSMISSION = 13,
S_THICKNESS = 14,
S_LIGHTMAP0 = 15,
S_DELUXEMAP0 = 16,
#if MAXRLIGHTMAPS > 1
S_LIGHTMAP1 = 15,
S_LIGHTMAP2 = 16,
S_LIGHTMAP3 = 17,
S_DELUXEMAP1 = 18,
S_DELUXEMAP2 = 19,
S_DELUXEMAP3 = 20,
S_LIGHTMAP1 = 17,
S_LIGHTMAP2 = 18,
S_LIGHTMAP3 = 19,
S_DELUXEMAP1 = 20,
S_DELUXEMAP2 = 21,
S_DELUXEMAP3 = 22,
#endif
};
extern const struct sh_defaultsamplers_s

View file

@ -36,10 +36,6 @@ struct sockaddr;
struct sockaddr_qstorage;
int NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s);
#if defined(__unix__) && !defined(__CYGWIN__)
#include <sys/epoll.h>
#endif
typedef qboolean iwboolean;
typedef struct {

View file

@ -201,7 +201,7 @@ int COM_CheckParm(const char *parm)
#include <signal.h>
#endif
#ifndef _WIN32
#ifdef HAVE_EPOLL
struct stun_ctx
{
struct epollctx_s pub;
@ -338,7 +338,9 @@ int main(int argc, char **argv)
WSADATA pointlesscrap;
WSAStartup(2, &pointlesscrap);
#else
#ifdef HAVE_EPOLL
int ep = epoll_create1(0);
#endif
signal(SIGPIPE, SIG_IGN); //so we don't crash out if a peer closes the socket half way through.
#endif
@ -416,7 +418,7 @@ int main(int argc, char **argv)
else
printf("Server is read only\n");
#ifdef _WIN32
#ifndef HAVE_EPOLL
while(1)
{
if (ftpport)
@ -430,7 +432,6 @@ int main(int argc, char **argv)
#endif
}
#else
while (!HTTP_ServerInit(ep, httpport))
sleep(5);
PrepareStun(ep, httpport);

View file

@ -1,5 +1,5 @@
COMMON_OBJS=comprout.o hash.o qcc_cmdlib.o qcd_main.o
QCC_OBJS=qccmain.o qcc_pr_comp.o qcc_pr_lex.o packager.o
QCC_OBJS=qccmain.o qcc_pr_comp.o qcc_pr_lex.o packager.o decomp.o
VM_OBJS=pr_exec.o pr_edict.o pr_multi.o initlib.o qcdecomp.o
GTKGUI_OBJS=qcc_gtk.o qccguistuff.o
WIN32GUI_OBJS=qccgui.o qccguistuff.o packager.o
@ -102,10 +102,10 @@ qcvm.a: $(QCC_OBJS) $(VM_OBJS) $(COMMON_OBJS)
test.o: test.c
$(DO_CC)
testapp.bin: test.o qcvm.a
$(CC) $(BASE_CFLAGS) $(CFLAGS) -o testapp.bin -O3 $(BASE_LDFLAGS) $^ -lm -lz
qcvm: test.o qcvm.a
$(CC) $(BASE_CFLAGS) $(CFLAGS) -o qcvm -O3 $(BASE_LDFLAGS) $^ -lm -lz -ggdb
tests: testapp.bin
tests: qcvm
@echo Running Tests...
@$(foreach a,$(wildcard tests/*.src), echo TEST: $a; rm progs.dat; ./testapp.bin progs.dat -srcfile $a; echo; echo)
@echo Tests run.

View file

@ -65,6 +65,7 @@ void *qccHunkAlloc(size_t mem)
void qccClearHunk(void)
{
struct qcchunk_s *t;
QCC_PurgeTemps();
while (qcc_hunk)
{
t = qcc_hunk;

File diff suppressed because it is too large Load diff

View file

@ -40,6 +40,8 @@
#define QCFAULT return (prinst.pr_xstatement=(st-pr_statements)-1),PR_HandleFault
#define EVAL_FLOATISTRUE(ev) ((ev)->_int & 0x7fffffff) //mask away sign bit. This avoids using denormalized floats.
#define A_RSHIFT_I(x,y) ((x < 0) ? ~(~(x) >> (y)) : ((x) >> (y))) //C leaves it undefined whether signed rshift is arithmatic or logical. gcc should be smart enough to fold this to the proper signed instruction at least on x86.
#ifdef __GNUC__
#define errorif(x) if(__builtin_expect(x,0))
#else
@ -57,9 +59,9 @@
//this will fire on the next instruction after the variable got changed.
prinst.pr_xstatement = s;
if (current_progstate->linenums)
externs->Printf("Watch point hit in %s:%u, \"%s\" changed", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), current_progstate->linenums[s-1], prinst.watch_name);
externs->Printf("^b^3Watch point hit in %s:%u, \"%s\" changed", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), current_progstate->linenums[s-1], prinst.watch_name);
else
externs->Printf("Watch point hit in %s, \"%s\" changed", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), prinst.watch_name);
externs->Printf("^b^3Watch point hit in %s, \"%s\" changed", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), prinst.watch_name);
switch(prinst.watch_type)
{
case ev_float:
@ -468,7 +470,7 @@ reeval:
ptr = QCPOINTERM(i);
*(unsigned char *)ptr = (char)OPA->_float;
break;
case OP_STOREP_B: //store (byte) character in a string
case OP_STOREP_I8: //store (byte) character in a string
i = OPB->_int + (OPC->_int)*sizeof(pbyte);
errorif (QCPOINTERWRITEFAIL(i, sizeof(pbyte)))
{
@ -483,6 +485,21 @@ reeval:
ptr = QCPOINTERM(i);
*(pbyte *)ptr = (pbyte)OPA->_int;
break;
case OP_STOREP_I16: //store short to a pointer
i = OPB->_int + (OPC->_int)*sizeof(short);
errorif (QCPOINTERWRITEFAIL(i, sizeof(short)))
{
if (!(ptr=PR_GetWriteTempStringPtr(progfuncs, OPB->_int, OPC->_int*sizeof(short), sizeof(short))))
{
if (i == -1)
break;
QCFAULT(&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, prinst.addressableused);
}
}
else
ptr = QCPOINTERM(i);
*(short *)ptr = (short)OPA->_int;
break;
case OP_STOREF_F:
case OP_STOREF_I:
@ -860,7 +877,6 @@ reeval:
case OP_CALL1:
case OP_CALL0:
{
int callerprogs;
int newpr;
unsigned int fnum;
RUNAWAYCHECK();
@ -874,7 +890,7 @@ reeval:
glob = NULL; //try to derestrict it.
callerprogs=prinst.pr_typecurrent; //so we can revert to the right caller.
progfuncs->funcs.callprogs=prinst.pr_typecurrent; //so we can revert to the right caller.
newpr = (fnum & 0xff000000)>>24; //this is the progs index of the callee
fnum &= ~0xff000000; //the callee's function index.
@ -882,7 +898,7 @@ reeval:
errorif (!PR_SwitchProgsParms(progfuncs, newpr) || !fnum || fnum > pr_progs->numfunctions)
{
char *msg = fnum?"OP_CALL references invalid function in %s\n":"NULL function from qc (inside %s).\n";
PR_SwitchProgsParms(progfuncs, callerprogs);
PR_SwitchProgsParms(progfuncs, progfuncs->funcs.callprogs);
glob = pr_globals;
if (!progfuncs->funcs.debug_trace)
@ -922,12 +938,12 @@ reeval:
}
else
PR_RunError (&progfuncs->funcs, "Bad builtin call number - %i", -newf->first_statement);
PR_SwitchProgsParms(progfuncs, (progsnum_t)callerprogs);
PR_SwitchProgsParms(progfuncs, (progsnum_t)progfuncs->funcs.callprogs);
//decide weather non debugger wants to start debugging.
return prinst.pr_xstatement;
}
s = PR_EnterFunction (progfuncs, newf, callerprogs);
s = PR_EnterFunction (progfuncs, newf, progfuncs->funcs.callprogs);
st = &pr_statements[s];
}
@ -1018,6 +1034,8 @@ reeval:
case OP_DIV_I:
if (OPB->_int == 0) //no division by zero allowed...
OPC->_int = 0;
else if (OPB->_int == -1 && OPA->_int==(int)0x80000000)
OPC->_int = 0x7fffffff;
else
OPC->_int = OPA->_int / OPB->_int;
break;
@ -1106,7 +1124,7 @@ reeval:
ptr = QCPOINTERM(i);
OPC->_float = *(unsigned char *)ptr;
break;
case OP_LOADP_B: //load character from a string/pointer
case OP_LOADP_U8: //load character from a string/pointer
i = (unsigned int)OPA->_int + (int)OPB->_int;
errorif (QCPOINTERREADFAIL(i, sizeof(pbyte)))
{
@ -1124,6 +1142,60 @@ reeval:
ptr = QCPOINTERM(i);
OPC->_int = *(pbyte *)ptr;
break;
case OP_LOADP_I8: //load character from a string/pointer
i = (unsigned int)OPA->_int + (int)OPB->_int;
errorif (QCPOINTERREADFAIL(i, sizeof(pbyte)))
{
if (!(ptr=PR_GetReadTempStringPtr(progfuncs, OPA->_int, OPB->_int, sizeof(pbyte))))
{
if (i == -1)
{
OPC->_int = 0;
break;
}
QCFAULT(&progfuncs->funcs, "bad pointer read in %s (%i bytes into %s)", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, ptr);
}
}
else
ptr = QCPOINTERM(i);
OPC->_int = *(char *)ptr;
break;
case OP_LOADP_U16: //load character from a string/pointer
i = (unsigned int)OPA->_int + (int)OPB->_int*2;
errorif (QCPOINTERREADFAIL(i, sizeof(short)))
{
if (!(ptr=PR_GetReadTempStringPtr(progfuncs, OPA->_int, OPB->_int*2, sizeof(short))))
{
if (i == -1)
{
OPC->_int = 0;
break;
}
QCFAULT(&progfuncs->funcs, "bad pointer read in %s (%i bytes into %s)", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, ptr);
}
}
else
ptr = QCPOINTERM(i);
OPC->_int = *(unsigned short *)ptr;
break;
case OP_LOADP_I16: //load character from a string/pointer
i = (unsigned int)OPA->_int + (int)OPB->_int*2;
errorif (QCPOINTERREADFAIL(i, sizeof(short)))
{
if (!(ptr=PR_GetReadTempStringPtr(progfuncs, OPA->_int, OPB->_int*2, sizeof(short))))
{
if (i == -1)
{
OPC->_int = 0;
break;
}
QCFAULT(&progfuncs->funcs, "bad pointer read in %s (%i bytes into %s)", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), i, ptr);
}
}
else
ptr = QCPOINTERM(i);
OPC->_int = *(short *)ptr;
break;
case OP_LOADP_I:
case OP_LOADP_F:
case OP_LOADP_FLD:
@ -1194,7 +1266,7 @@ reeval:
OPC->_int = OPA->_int ^ OPB->_int;
break;
case OP_RSHIFT_I:
OPC->_int = OPA->_int >> OPB->_int;
OPC->_int = A_RSHIFT_I(OPA->_int, OPB->_int);
break;
case OP_RSHIFT_U:
OPC->_uint = OPA->_uint >> OPB->_uint;
@ -1602,18 +1674,19 @@ reeval:
return s;
*/ }
break;
case OP_PUSH:
case OP_PUSH: //note: OPA is words, not bytes.
OPC->_int = ENGINEPOINTER(&prinst.localstack[prinst.localstack_used+prinst.spushed]);
prinst.spushed += OPA->_int;
prinst.spushed += OPA->_uint;
if (prinst.spushed + prinst.localstack_used >= LOCALSTACK_SIZE)
{
i = prinst.spushed;
prinst.spushed = 0;
prinst.pr_xstatement = st-pr_statements;
PR_RunError(&progfuncs->funcs, "Progs pushed too much");
PR_RunError(&progfuncs->funcs, "Progs pushed too much (%i bytes, %i parents, max %i)", i, prinst.localstack_used, LOCALSTACK_SIZE);
}
break;
/* case OP_POP:
pr_spushed -= OPA->_int;
pr_spushed -= OPA->_uint;
if (pr_spushed < 0)
{
pr_spushed = 0;
@ -1633,7 +1706,7 @@ reeval:
case OP_BITOR_I64: OPC->i64 = OPA->i64 | OPB->i64; break;
case OP_BITXOR_I64: OPC->i64 = OPA->i64 ^ OPB->i64; break;
case OP_LSHIFT_I64I: OPC->i64 = OPA->i64 << OPB->_int; break;
case OP_RSHIFT_I64I: OPC->i64 = OPA->i64 >> OPB->_int; break;
case OP_RSHIFT_I64I: OPC->i64 = A_RSHIFT_I(OPA->i64, OPB->_int); break;
case OP_LT_I64: OPC->_int = OPA->i64 < OPB->i64; break;
case OP_LE_I64: OPC->_int = OPA->i64 <= OPB->i64; break;
case OP_EQ_I64: OPC->_int = OPA->i64 == OPB->i64; break;
@ -1652,6 +1725,10 @@ reeval:
case OP_CONV_FI64: OPC->i64 = OPA->_float; break;
case OP_CONV_I64D: OPC->_double = OPA->i64; break;
case OP_CONV_DI64: OPC->i64 = OPA->_double; break;
case OP_CONV_U64D: OPC->_double = OPA->u64; break;
case OP_CONV_DU64: OPC->u64 = OPA->_double; break;
case OP_CONV_U64F: OPC->_float = OPA->u64; break;
case OP_CONV_FU64: OPC->u64 = OPA->_float; break;
case OP_ADD_D: OPC->_double = OPA->_double + OPB->_double; break;
case OP_SUB_D: OPC->_double = OPA->_double - OPB->_double; break;
case OP_MUL_D: OPC->_double = OPA->_double * OPB->_double; break;
@ -1661,8 +1738,11 @@ reeval:
case OP_EQ_D: OPC->_int = OPA->_double == OPB->_double; break;
case OP_NE_D: OPC->_int = OPA->_double != OPB->_double; break;
case OP_BITEXTEND_I: OPC->_int = A_RSHIFT_I(( signed int)(OPA->_int << (32-(OPB->_uint&0xff)-(OPB->_uint>>8))), (signed)(32-(OPB->_uint&0xff))); break; //shift it up and down. should sign extend.
case OP_BITEXTEND_U: OPC->_uint = (unsigned int)(OPA->_uint << (32-(OPB->_uint&0xff)-(OPB->_uint>>8))) >> (32-(OPB->_uint&0xff)); break; //shift it up and down. should clear the bits.
case OP_BITCOPY_I: i=((1<<(OPB->_uint&0xff))-1);OPC->_uint=(OPC->_uint&~(i<<(OPB->_uint>>8)))|(((OPA->_uint&i)<<(OPB->_uint>>8)));break; //replaces the specified bits (uses the same format bitextend uses to select its input to extend)
case OP_CONV_UF: OPC->_float = OPA->_uint; break;
case OP_CONV_FU: OPC->_uint = OPA->_float; break;
case OP_UNUSED:
case OP_POP:

View file

@ -3,7 +3,7 @@ int Grep(const char *filename, const char *string);
void EditFile(const char *name, int line, pbool setcontrol);
void GUI_SetDefaultOpts(void);
int GUI_BuildParms(const char *args, const char **argv, pbool quick);
int GUI_BuildParms(const char *args, const char **argv, int argv_size, pbool quick);
//unsigned char *PDECL QCC_ReadFile (const char *fname, void *buffer, int len, size_t *sz);
int QCC_RawFileSize (const char *fname);

View file

@ -445,6 +445,56 @@ static void PDECL PR_memfree (pubprogfuncs_t *ppf, void *memptr)
PR_memvalidate(progfuncs);
}
static void *PDECL PR_memrealloc (pubprogfuncs_t *ppf, void *memptr, unsigned int newsize)
{
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
qcmemusedblock_t *ub;
unsigned int ptr = memptr?((char*)memptr - progfuncs->funcs.stringtable):0;
void *newptr;
unsigned int oldsize;
/*freeing NULL is ignored*/
if (!ptr) //realloc instead of malloc is accepted.
return PR_memalloc(ppf, newsize);
PR_memvalidate(progfuncs);
ptr -= sizeof(qcmemusedblock_t);
if (/*ptr < 0 ||*/ ptr >= prinst.addressableused)
{
ptr += sizeof(qcmemusedblock_t);
if (ptr < prinst.addressableused && !*(char*)memptr)
{
//the empty string is a point of contention. while we can detect it from fteqcc, its best to not give any special favours (other than nicer debugging, where possible)
//we might not actually spot it from other qccs, so warning about it where possible is probably a very good thing.
externs->Printf("PR_memrealloc: unable to free the non-null empty string constant at %x\n", ptr);
}
else
externs->Printf("PR_memrealloc: pointer invalid - out of range (%x >= %x)\n", ptr, (unsigned int)prinst.addressableused);
PR_StackTrace(&progfuncs->funcs, false);
return NULL;
}
//this is the used block that we're trying to free
ub = (qcmemusedblock_t*)(progfuncs->funcs.stringtable + ptr);
if (ub->marker != MARKER_USED || ub->size <= sizeof(*ub) || ptr + ub->size > (unsigned int)prinst.addressableused)
{
externs->Printf("PR_memrealloc: pointer lacks marker - double-freed?\n");
PR_StackTrace(&progfuncs->funcs, false);
return NULL;
}
oldsize = ub->size;
oldsize -= sizeof(qcmemusedblock_t); //ignore the header.
newptr = PR_memalloc(ppf, newsize);
if (oldsize > newsize)
oldsize = newsize; //don't copy it all.
memcpy(newptr, memptr, oldsize);
newsize -= oldsize;
memset((char*)newptr+oldsize, 0, newsize); //clear out any extended part.
PR_memfree(ppf, memptr); //free the old.
return newptr;
}
void PRAddressableFlush(progfuncs_t *progfuncs, size_t totalammount)
{
prinst.addressableused = 0;
@ -1169,7 +1219,7 @@ void *PR_PointerToNative_MoInvalidate(pubprogfuncs_t *inst, pint_t ptr, size_t o
else
{ //regular pointer
offset += ptr;
if (datasize > inst->stringtablesize || offset >= inst->stringtablesize-datasize || !offset)
if (datasize > inst->stringtablesize || offset >= inst->stringtablesize-datasize || (!offset && datasize>1))
return NULL; //can't autoresize these. just fail.
return inst->stringtable + ptr;
}
@ -1375,6 +1425,8 @@ static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str,
prinst.nexttempstring = i;
prinst.livetemps++;
len = ((len+3)&~3); //round up, primarily so its safe to use loadp_i to read the last few chars of the string
prinst.tempstrings[i] = progfuncs->funcs.parms->memalloc(sizeof(tempstr_t) - sizeof(((tempstr_t*)NULL)->value) + len);
prinst.tempstrings[i]->size = len;
*str = prinst.tempstrings[i]->value;
@ -1667,6 +1719,7 @@ static pubprogfuncs_t deffuncs = {
QC_Decompile,
#endif
0, //callargc
0,
0, //string table(pointer base address)
0, //string table size
@ -1685,6 +1738,7 @@ static pubprogfuncs_t deffuncs = {
ED_NewString,
QC_HunkAlloc,
PR_memalloc,
PR_memrealloc,
PR_memfree,
PR_AllocTempString,
PR_AllocTempStringLen,

View file

@ -247,7 +247,7 @@ enum qcop_e {
OP_LOADA_FNC, //150
OP_LOADA_I,
OP_STORE_P, //152... erm.. wait...
OP_STORE_P,
OP_LOAD_P,
OP_LOADP_F,
@ -339,13 +339,13 @@ enum qcop_e {
OP_STOREF_I, //1 non-string reference/int
//r5744+
OP_STOREP_B,//((char*)b)[(int)c] = (int)a
OP_LOADP_B, //(int)c = *(char*)
OP_STOREP_I8, //((char*)b)[(int)c] = (int)a
OP_LOADP_U8, //(int)c = *(unsigned char*)
//r5768+
//opcodes for 32bit uints
OP_LE_U, //aka GT
OP_LT_U, //aka GE
OP_LE_U, //aka GE
OP_LT_U, //aka GT
OP_DIV_U, //don't need mul+add+sub
OP_RSHIFT_U, //lshift is the same for signed+unsigned
@ -359,13 +359,13 @@ enum qcop_e {
OP_BITXOR_I64,
OP_LSHIFT_I64I,
OP_RSHIFT_I64I,
OP_LE_I64, //aka GT
OP_LT_I64, //aka GE
OP_LE_I64, //aka GE
OP_LT_I64, //aka GT
OP_EQ_I64,
OP_NE_I64,
//extra opcodes for 64bit uints
OP_LE_U64, //aka GT
OP_LT_U64, //aka GE
OP_LE_U64, //aka GE
OP_LT_U64, //aka GT
OP_DIV_U64,
OP_RSHIFT_U64I,
@ -397,6 +397,20 @@ enum qcop_e {
OP_EQ_D,
OP_NE_D,
//r6614+
OP_STOREP_I16, //((short*)b)[(int)c] = (int)a
OP_LOADP_I16, //(int)c = *(signed short*)a (sign extends)
OP_LOADP_U16, //(unsigned int)c = *(unsigned short*)a
OP_LOADP_I8, //(unsigned int)c = *(signed char*)a (sign extends)
OP_BITEXTEND_I, //sign extend (for signed bitfields)
OP_BITEXTEND_U, //zero extend (for unsigned bitfields)
OP_BITCOPY_I, //copy lower bits from the input to some part of the output
OP_CONV_UF, //OPC.f=(float)OPA.i -- 0xffffffffu*0.5=0 otherwise.
OP_CONV_FU, //OPC.i=(int)OPA.f
OP_CONV_U64D, //OPC.d=(double)OPA.u64 -- useful mostly so decompilers don't do weird stuff.
OP_CONV_DU64, //OPC.u64=(uint64_t)OPA.d
OP_CONV_U64F, //OPC.f=(float)OPA.u64 -- useful mostly so decompilers don't do weird stuff.
OP_CONV_FU64, //OPC.u64=(uint64_t)OPA.f
OP_NUMREALOPS,
@ -463,10 +477,13 @@ enum qcop_e {
OP_ADD_FP,
OP_ADD_PI,
OP_ADD_IP,
OP_ADD_PU,
OP_ADD_UP,
OP_SUB_SI,
OP_SUB_PF,
OP_SUB_PI,
OP_SUB_PU,
OP_SUB_PP,
@ -531,8 +548,8 @@ enum qcop_e {
//uint64 opcodes. they match the int32 ones so emulation is basically swapping them over.
OP_BITNOT_I64, //BITXOR ~0
OP_BITCLR_I64,
OP_GE_I64, //LT_I64
OP_GT_I64, //LE_I64
OP_GE_I64, //LE_I64
OP_GT_I64, //LT_I64
OP_ADD_U64,
OP_SUB_U64,
@ -544,8 +561,8 @@ enum qcop_e {
OP_BITNOT_U64, //BITXOR ~0
OP_BITCLR_U64,
OP_LSHIFT_U64I,
OP_GE_U64, //LT_U64
OP_GT_U64, //LE_U64
OP_GE_U64, //LE_U64
OP_GT_U64, //LT_U64
OP_EQ_U64,
OP_NE_U64,
@ -557,6 +574,8 @@ enum qcop_e {
OP_BITCLR_D,
OP_LSHIFT_DI,
OP_RSHIFT_DI,
OP_GE_D, //LE_D
OP_GT_D, //LT_D
OP_WSTATE, //for the 'w' part of CWSTATE. will probably never be used, but hey, hexen2...
@ -618,13 +637,19 @@ typedef struct statement32_s
typedef struct
{
struct QCC_def_s *sym;
unsigned int ofs;
union
{
unsigned int ofs;
// unsigned int bofs;
signed int jumpofs;
};
struct QCC_type_s *cast; //the entire sref is considered null if there is no cast, although it *MAY* have an ofs specified if its part of a jump instruction
} QCC_sref_t;
typedef struct qcc_statement_s
{
unsigned short op;
#define STF_LOGICOP (1u<<0) //do not bother following when looking for uninitialised variables.
#define STF_NOFOLD (1u<<1) //do not allow changing its var_c to fold the following store.
unsigned short flags;
QCC_sref_t a, b, c;
unsigned int linenum;

View file

@ -1036,6 +1036,20 @@ char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs)
return line;
}
char *PR_GlobalStringImmediate (progfuncs_t *progfuncs, int ofs)
{
int i;
static char line[128];
sprintf (line,"%i", ofs);
i = strlen(line);
for ( ; i<20 ; i++)
strcat (line," ");
strcat (line," ");
return line;
}
/*
=============
ED_Print
@ -2742,6 +2756,7 @@ pbool PR_ReallyLoadProgs (progfuncs_t *progfuncs, const char *filename, progstat
int reorg = prinst.reorganisefields || prinst.numfields;
int stringadjust;
int pointeradjust;
int *basictypetable;
@ -3027,9 +3042,15 @@ retry:
glob = pr_globals = (float *)s;
if (progfuncs->funcs.stringtable)
{
stringadjust = pr_strings - progfuncs->funcs.stringtable;
pointeradjust = (char*)glob - progfuncs->funcs.stringtable;
}
else
{
stringadjust = 0;
pointeradjust = (char*)glob - pr_strings;
}
if (!pr_linenums)
{
@ -3531,12 +3552,20 @@ retry:
if (reorg && !basictypetable)
QC_AddSharedFieldVar(&progfuncs->funcs, i, pr_strings - stringadjust);
break;
case ev_pointer:
if (((int *)glob)[gd16[i].ofs] & 0x80000000)
{
((int *)glob)[gd16[i].ofs] &= ~0x80000000;
((int *)glob)[gd16[i].ofs] += pointeradjust;
break;
}
FALLTHROUGH
case ev_string:
if (((unsigned int *)glob)[gd16[i].ofs]>=progstate->progs->numstrings)
externs->Printf("PR_LoadProgs: invalid string value (%x >= %x) in '%s'\n", ((unsigned int *)glob)[gd16[i].ofs], progstate->progs->numstrings, gd16[i].s_name+pr_strings-stringadjust);
else if (isfriked != -1)
{
if (pr_strings[((int *)glob)[gd16[i].ofs]]) //quakec uses string tables. 0 must remain null, or 'if (s)' can break.
if (((int *)glob)[gd16[i].ofs]) //quakec uses string tables. 0 must remain null, or 'if (s)' can break.
{
((int *)glob)[gd16[i].ofs] += stringadjust;
isfriked = false;
@ -3578,8 +3607,18 @@ retry:
case ev_field:
QC_AddSharedFieldVar(&progfuncs->funcs, i, pr_strings - stringadjust);
break;
case ev_pointer:
if (((int *)glob)[pr_globaldefs32[i].ofs] & 0x80000000)
{
((int *)glob)[pr_globaldefs32[i].ofs] &= ~0x80000000;
((int *)glob)[pr_globaldefs32[i].ofs] += pointeradjust;
break;
}
FALLTHROUGH
case ev_string:
if (pr_strings[((int *)glob)[pr_globaldefs32[i].ofs]]) //quakec uses string tables. 0 must remain null, or 'if (s)' can break.
if (((unsigned int *)glob)[pr_globaldefs32[i].ofs]>=progstate->progs->numstrings)
externs->Printf("PR_LoadProgs: invalid string value (%x >= %x) in '%s'\n", ((unsigned int *)glob)[pr_globaldefs32[i].ofs], progstate->progs->numstrings, pr_globaldefs32[i].s_name+pr_strings-stringadjust);
else if (((int *)glob)[pr_globaldefs32[i].ofs]) //quakec uses string tables. 0 must remain null, or 'if (s)' can break.
{
((int *)glob)[pr_globaldefs32[i].ofs] += stringadjust;
isfriked = false;
@ -3628,6 +3667,10 @@ retry:
if (progfuncs->funcs.stringtablesize + progfuncs->funcs.stringtable < pr_strings + pr_progs->numstrings)
progfuncs->funcs.stringtablesize = (pr_strings + pr_progs->numstrings) - progfuncs->funcs.stringtable;
//make sure the localstack is addressable to the qc, so we can OP_PUSH okay.
if (!prinst.localstack)
prinst.localstack = PRAddressableExtend(progfuncs, NULL, 0, sizeof(float)*LOCALSTACK_SIZE);
if (externs->MapNamedBuiltin)
{
for (i=0,fnc2=pr_cp_functions; i<pr_progs->numfunctions; i++, fnc2++)

View file

@ -128,8 +128,8 @@ static void PR_PrintStatement (progfuncs_t *progfuncs, int statementnum)
if ( (unsigned)op < OP_NUMOPS)
{
int i;
externs->Printf ("%s ", pr_opcodes[op].name);
i = strlen(pr_opcodes[op].name);
externs->Printf ("%s ", pr_opcodes[op].opname);
i = strlen(pr_opcodes[op].opname);
for ( ; i<10 ; i++)
externs->Printf (" ");
}
@ -141,16 +141,22 @@ static void PR_PrintStatement (progfuncs_t *progfuncs, int statementnum)
#define TYPEHINT(a) NULL
#endif
if (op == OP_IF_F || op == OP_IFNOT_F)
if (op == OP_IF_F || op == OP_IFNOT_F || op == OP_IF_I || op == OP_IFNOT_I || op == OP_IF_S || op == OP_IFNOT_S)
externs->Printf ("%sbranch %i",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)),arg[1]);
else if (op == OP_GOTO)
{
externs->Printf ("branch %i",arg[0]);
}
else if (op == OP_BOUNDCHECK)
{
externs->Printf ("%s",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)));
externs->Printf ("%s",PR_GlobalStringImmediate(progfuncs, arg[1]));
externs->Printf ("%s",PR_GlobalStringImmediate(progfuncs, arg[2]));
}
else if ( (unsigned)(op - OP_STORE_F) < 6)
{
externs->Printf ("%s",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)));
externs->Printf ("%s", PR_GlobalStringNoContents(progfuncs, arg[1]));
externs->Printf ("%s",PR_GlobalStringNoContents(progfuncs, arg[1]));
}
else
{
@ -159,7 +165,7 @@ static void PR_PrintStatement (progfuncs_t *progfuncs, int statementnum)
if (arg[1])
externs->Printf ("%s",PR_GlobalString(progfuncs, arg[1], TYPEHINT(b)));
if (arg[2])
externs->Printf ("%s", PR_GlobalStringNoContents(progfuncs, arg[2]));
externs->Printf ("%s",PR_GlobalStringNoContents(progfuncs, arg[2]));
}
externs->Printf ("\n");
}
@ -363,9 +369,9 @@ static void PDECL PR_PrintRelevantLocals(progfuncs_t *progfuncs)
else
fdef = ED_FieldAtOfs(progfuncs, ((eval_t *)&pr_globals[st16[st].b])->_int);
if (fdef)
externs->Printf(" %s.%s: %s\n", PR_StringToNative(&progfuncs->funcs, ent->s_name), PR_StringToNative(&progfuncs->funcs, fld->s_name), PR_ValueString(progfuncs, fdef->type, ptr, false));
externs->Printf(" %s.%s: %s\n", PR_StringToNative(&progfuncs->funcs, ent->s_name), PR_StringToNative(&progfuncs->funcs, fld->s_name), PR_ValueString(progfuncs, fdef->type, ptr, false));
else
externs->Printf(" %s.%s: BAD FIELD DEF - %#x\n", PR_StringToNative(&progfuncs->funcs, ent->s_name), PR_StringToNative(&progfuncs->funcs, fld->s_name), ptr->_int);
externs->Printf(" %s.%s: BAD FIELD DEF - %#x\n", PR_StringToNative(&progfuncs->funcs, ent->s_name), PR_StringToNative(&progfuncs->funcs, fld->s_name), ptr->_int);
}
}
}
@ -454,20 +460,20 @@ void PDECL PR_StackTrace (pubprogfuncs_t *ppf, int showlocals)
{
if (f->parm_size[arg] == 3)
{ //looks like a vector. print it as such
externs->Printf(" arg%i(%i): [%g, %g, %g]\n", arg, f->parm_start+ofs, *(float *)(globalbase+ofs), *(float *)(globalbase+ofs+1), *(float *)(globalbase+ofs+2));
externs->Printf(" arg%i(%i): [%g, %g, %g]\n", arg, f->parm_start+ofs, *(float *)(globalbase+ofs), *(float *)(globalbase+ofs+1), *(float *)(globalbase+ofs+2));
ofs += 2;
}
else
externs->Printf(" arg%i(%i): %g===%i\n", arg, f->parm_start+ofs, *(float *)(globalbase+ofs), *(int *)(globalbase+ofs) );
externs->Printf(" arg%i(%i): %g===%i\n", arg, f->parm_start+ofs, *(float *)(globalbase+ofs), *(int *)(globalbase+ofs) );
}
else
{
externs->Printf(" unk(%i): %g===%i\n", f->parm_start+ofs, *(float *)(globalbase+ofs), *(int *)(globalbase+ofs) );
externs->Printf(" unk(%i): %g===%i\n", f->parm_start+ofs, *(float *)(globalbase+ofs), *(int *)(globalbase+ofs) );
}
}
else
{
externs->Printf(" %s: %s\n", PR_StringToNative(ppf, local->s_name), PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase+ofs), false));
externs->Printf(" %s: %s\n", PR_StringToNative(ppf, local->s_name), PR_ValueString(progfuncs, local->type, (eval_t*)(globalbase+ofs), false));
if (local->type == ev_vector)
ofs+=2;
}
@ -556,6 +562,7 @@ static int ASMCALL PR_EnterFunction (progfuncs_t *progfuncs, mfunction_t *f, int
}
prinst.pr_xfunction = f;
prinst.spushed = 0;
return f->first_statement - 1; // offset the s++
}
@ -708,6 +715,34 @@ pbool LocateDebugTerm(progfuncs_t *progfuncs, const char *key, eval_t **result,
struct edictrun_s *ed;
// etype_t ptrtype = ev_void;
if (!strncmp(key, "*(float*)", 9))
{
fofs = strtoul(key+9, NULL, 0);
if (fofs < 0 || fofs+3 >= prinst.addressableused)
return false;
*result = (eval_t*)(pr_strings + fofs);
*rettype = ev_float;
return true;
}
if (!strncmp(key, "*(int*)", 7))
{
fofs = strtoul(key+7, NULL, 0);
if (fofs < 0 || fofs+3 >= prinst.addressableused)
return false;
*result = (eval_t*)(pr_strings + fofs);
*rettype = ev_integer;
return true;
}
if (!strncmp(key, "*(string*)", 7))
{
fofs = strtoul(key+7, NULL, 0);
if (fofs < 0 || fofs+3 >= prinst.addressableused)
return false;
*result = (eval_t*)(pr_strings + fofs);
*rettype = ev_string;
return true;
}
c = strchr(key, '.');
if (c) *c = '\0';
def = ED_FindLocalOrGlobal(progfuncs, key, &val);
@ -790,14 +825,14 @@ pbool LocateDebugTerm(progfuncs_t *progfuncs, const char *key, eval_t **result,
return true;
}
pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *ppf, const char *key)
pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *ppf, const char *desc, const char *location)
{
progfuncs_t *progfuncs = (progfuncs_t *)ppf;
eval_t *val;
eval_t fakeval;
etype_t type;
if (!key)
if (!location)
{
free(prinst.watch_name);
prinst.watch_name = NULL;
@ -805,9 +840,9 @@ pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *ppf, const char *key)
prinst.watch_type = ev_void;
return false;
}
if (!LocateDebugTerm(progfuncs, key, &val, &type, &fakeval))
if (!LocateDebugTerm(progfuncs, location, &val, &type, &fakeval))
{
externs->Printf("Unable to evaluate watch term \"%s\"\n", key);
externs->Printf("Unable to evaluate watch term \"%s\"\n", location);
return false;
}
if (val == &fakeval)
@ -822,7 +857,7 @@ pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *ppf, const char *key)
}
free(prinst.watch_name);
prinst.watch_name = strdup(key);
prinst.watch_name = strdup(desc);
prinst.watch_ptr = val;
prinst.watch_old = *prinst.watch_ptr;
prinst.watch_type = type &~ DEF_SAVEGLOBAL;
@ -1354,7 +1389,10 @@ static const char *lastfile = NULL;
{
PR_PrintStatement(progfuncs, statement);
if (fatal)
{
progfuncs->funcs.debug_trace = DEBUG_TRACE_ABORTERROR;
progfuncs->funcs.parms->Abort ("%s", fault?fault:"Debugger Abort");
}
return statement;
}
@ -1602,20 +1640,20 @@ static casecmprange_t casecmprange[] =
casecmprange_i //func
};
#define RUNAWAYCHECK() \
#define RUNAWAYCHECK() \
if (!--*runaway) \
{ \
prinst.pr_xstatement = st-pr_statements; \
{ \
prinst.pr_xstatement = st-pr_statements; \
PR_RunError (&progfuncs->funcs, "runaway loop error\n");\
PR_StackTrace(&progfuncs->funcs,false); \
externs->Printf ("runaway loop error\n"); \
while(prinst.pr_depth > prinst.exitdepth) \
PR_LeaveFunction(progfuncs); \
PR_StackTrace(&progfuncs->funcs,false); \
externs->Printf ("runaway loop error\n"); \
while(prinst.pr_depth > prinst.exitdepth) \
PR_LeaveFunction(progfuncs); \
prinst.spushed = 0; \
return -1; \
return -1; \
}
#if defined(FTE_TARGET_WEB) || defined(SIMPLE_QCVM)
#if defined(SIMPLE_QCVM)
static int PR_NoDebugVM(progfuncs_t *fte_restrict progfuncs)
{
char stack[4*1024];
@ -1653,7 +1691,7 @@ static int PR_ExecuteCode16 (progfuncs_t *fte_restrict progfuncs, int s, int *ft
st = &pr_statements16[s];
while (progfuncs->funcs.debug_trace || prinst.watch_ptr || prinst.profiling)
{
#if defined(FTE_TARGET_WEB) || defined(SIMPLE_QCVM)
#if defined(SIMPLE_QCVM)
reeval16:
//this can generate huge functions, so disable it on systems that can't realiably cope with such things (IE initiates an unwanted denial-of-service attack when pointed our javascript, and firefox prints a warning too)
prinst.pr_xstatement = st-pr_statements16;
@ -1678,7 +1716,7 @@ static int PR_ExecuteCode16 (progfuncs_t *fte_restrict progfuncs, int s, int *ft
static int PR_ExecuteCode32 (progfuncs_t *fte_restrict progfuncs, int s, int *fte_restrict runaway)
{
#if defined(FTE_TARGET_WEB) ||defined(SIMPLE_QCVM)
#if defined(SIMPLE_QCVM)
//this can generate huge functions, so disable it on systems that can't realiably cope with such things (IE initiates an unwanted denial-of-service attack when pointed our javascript, and firefox prints a warning too)
prinst.pr_xstatement = s;
PR_RunError (&progfuncs->funcs, "32bit qc statement support was disabled for this platform.\n");
@ -1801,6 +1839,10 @@ static void PR_ExecuteCode (progfuncs_t *progfuncs, int s)
}
}
#if defined(__GNUC__) && defined(__GLIBC__)
#define __USE_GNU
#include <dlfcn.h>
#endif
void PDECL PR_ExecuteProgram (pubprogfuncs_t *ppf, func_t fnum)
{
@ -1833,7 +1875,12 @@ void PDECL PR_ExecuteProgram (pubprogfuncs_t *ppf, func_t fnum)
{
// if (pr_global_struct->self)
// ED_Print (PROG_TO_EDICT(pr_global_struct->self));
#if defined(__GNUC__) && !defined(FTE_TARGET_WEB)
#if defined(__GNUC__) && defined(__GLIBC__)
Dl_info info;
void *caller = __builtin_return_address(0);
dladdr(caller, &info);
externs->Printf("PR_ExecuteProgram: NULL function from %s+%p(%s)\n", info.dli_fname, (void*)((intptr_t)caller - (intptr_t)info.dli_fbase), info.dli_sname?info.dli_sname:"<function not known>");
#elif defined(__GNUC__) && !defined(FTE_TARGET_WEB)
externs->Printf("PR_ExecuteProgram: NULL function from exe (address %p)\n", __builtin_return_address(0));
#else
externs->Printf("PR_ExecuteProgram: NULL function from exe\n");
@ -1902,104 +1949,138 @@ typedef struct {
int fnum;
int progsnum;
int statement;
int spushed;
} qcthreadstack_t;
typedef struct qcthread_s {
int fstackdepth;
qcthreadstack_t fstack[MAX_STACK_DEPTH];
int lstackused;
int lstack[LOCALSTACK_SIZE];
int xstatement;
int xfunction;
progsnum_t xprogs;
} qcthread_t;
struct qcthread_s *PDECL PR_ForkStack(pubprogfuncs_t *ppf)
{ //QC code can call builtins that call qc code.
//to get around the problems of restoring the builtins we simply don't save the thread over the builtin.
//this may be an error when OP_PUSH has been used.
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
int i, l;
int i, pushed;
int ed = prinst.exitdepth;
int localsoffset, baselocalsoffset;
qcthread_t *thread = externs->memalloc(sizeof(qcthread_t));
const mfunction_t *f;
int curprogs = ppf->callprogs;
//notes:
//pr_stack[prinst.exitdepth] is a dummy entry, with null function refs. we don't care about it.
//pr_stack[pr_depth] is technically invalid but logically required - it refers to the current function instead. stoopid extra indirection.
//[pr_depth] is the qc function that called whichever builtin we're executing right now so we want that (logical) one. [0] is irrelevant though.
//entering a function copys its locals into the local stack for restoration on return, any OP_PUSHED stuff within the frame is then after that.
//OP_PUSH/'pushed' is the PARENT function's pushes. f->locals stuff is the CHILD function's pushes.
//copy out the functions stack.
for (i = 0,localsoffset=0; i < ed; i++)
for (i = 1,localsoffset=0; i <= ed; i++)
{
if (i+1 == prinst.pr_depth)
if (i == prinst.pr_depth)
{
localsoffset += prinst.spushed;
f = prinst.pr_xfunction;
localsoffset += f->locals;
}
else
f = prinst.pr_stack[i+1].f;
localsoffset += f->locals; //this is where it crashes
{
localsoffset += prinst.pr_stack[i].pushed;
f = prinst.pr_stack[i].f;
if (f)
localsoffset += f->locals;
}
}
//now we can start copying the stack.
baselocalsoffset = localsoffset;
for (i = ed; i < prinst.pr_depth; i++)
thread->fstackdepth = 0;
for (; i <= prinst.pr_depth; i++)
{
thread->fstack[i-ed].fnum = prinst.pr_stack[i].f - pr_progstate[prinst.pr_stack[i].progsnum].functions;
thread->fstack[i-ed].progsnum = prinst.pr_stack[i].progsnum;
thread->fstack[i-ed].statement = prinst.pr_stack[i].s;
if (i == prinst.pr_depth)
{ //top of the stack. whichever function called the builtin we're executing.
thread->fstack[thread->fstackdepth].fnum = prinst.pr_xfunction - pr_progstate[curprogs].functions;
thread->fstack[thread->fstackdepth].progsnum = curprogs;
thread->fstack[thread->fstackdepth].statement = prinst.pr_xstatement;
thread->fstack[thread->fstackdepth].spushed = prinst.spushed;
thread->fstackdepth++;
if (i+1 == prinst.pr_depth)
localsoffset += prinst.spushed;
f = prinst.pr_xfunction;
localsoffset += f->locals;
}
else
f = prinst.pr_stack[i+1].f;
localsoffset += f->locals;
{
thread->fstack[thread->fstackdepth].fnum = prinst.pr_stack[i].f - pr_progstate[prinst.pr_stack[i].progsnum].functions;
thread->fstack[thread->fstackdepth].progsnum = prinst.pr_stack[i].progsnum;
thread->fstack[thread->fstackdepth].statement = prinst.pr_stack[i].s;
thread->fstack[thread->fstackdepth].spushed = prinst.pr_stack[i].pushed;
thread->fstackdepth++;
localsoffset += prinst.pr_stack[i].pushed;
f = prinst.pr_stack[i].f;
localsoffset += f->locals;
}
}
thread->fstackdepth = prinst.pr_depth - ed;
for (i = prinst.pr_depth - 1; i >= ed ; i--)
//we now know how many locals we need... but life is not easy.
//preserving on entry means the definitive location is the pr_globals - and they'll have been 'corrupted' by any child functions.
//so we need to unwind(rewind) them here to find their proper 'current' values, so that resumption can rebuild the execution stack on resume to revert them properly to their prior values. messy.
for (i = prinst.pr_depth; i > ed ; i--)
{
if (i+1 == prinst.pr_depth)
f = prinst.pr_xfunction;
if (i == prinst.pr_depth)
f = prinst.pr_xfunction, pushed = prinst.spushed;
else
f = prinst.pr_stack[i+1].f;
f = prinst.pr_stack[i].f, pushed = prinst.pr_stack[i].pushed;
//preseve any OP_PUSH stuff
localsoffset -= pushed;
memcpy(&thread->lstack[localsoffset-baselocalsoffset], prinst.localstack+localsoffset, pushed*sizeof(int));
//preserve the current locals
localsoffset -= f->locals;
for (l = 0; l < f->locals; l++)
{
thread->lstack[localsoffset-baselocalsoffset + l ] = ((int *)pr_globals)[f->parm_start + l];
((int *)pr_globals)[f->parm_start + l] = prinst.localstack[localsoffset+l]; //copy the old value into the globals (so the older functions have the correct locals.
}
memcpy(&thread->lstack[localsoffset-baselocalsoffset], ((int *)pr_globals)+f->parm_start, f->locals*sizeof(int));
//unwind the locals so parent functions we preserve have the proper current values.
memcpy(((int *)pr_globals)+f->parm_start, prinst.localstack+localsoffset, f->locals*sizeof(int));
}
for (i = ed; i < prinst.pr_depth ; i++) //we need to get the locals back to how they were.
//rewind the locals so they don't get corrupt when returning from fork etc.
for (i = ed+1; i <= prinst.pr_depth ; i++) //we need to get the locals back to how they were.
{
if (i+1 == prinst.pr_depth)
f = prinst.pr_xfunction;
if (i == prinst.pr_depth)
f = prinst.pr_xfunction, pushed = prinst.spushed;
else
f = prinst.pr_stack[i+1].f;
f = prinst.pr_stack[i].f, pushed = prinst.pr_stack[i].pushed;
for (l = 0; l < f->locals; l++)
{
((int *)pr_globals)[f->parm_start + l] = thread->lstack[localsoffset-baselocalsoffset + l];
}
memcpy(((int *)pr_globals)+f->parm_start, &thread->lstack[localsoffset-baselocalsoffset], f->locals*sizeof(int));
localsoffset += f->locals;
localsoffset += pushed; //we didn't need to clobber this, just skip it to avoid corrupting.
}
thread->lstackused = localsoffset - baselocalsoffset;
thread->xstatement = prinst.pr_xstatement;
thread->xfunction = prinst.pr_xfunction - current_progstate->functions;
thread->xprogs = prinst.pr_typecurrent;
return thread;
}
void PDECL PR_ResumeThread (pubprogfuncs_t *ppf, struct qcthread_s *thread)
{
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
mfunction_t *f, *oldf;
int i,l,ls, olds;
progsnum_t initial_progs;
mfunction_t *f;
int i;
progsnum_t initial_progs = prinst.pr_typecurrent;
unsigned int initial_stack;
int oldexitdepth;
int *glob;
int s;
#ifndef QCGC
int tempdepth;
#endif
progsnum_t prnum = thread->xprogs;
int fnum = thread->xfunction;
if (prinst.localstack_used + thread->lstackused > LOCALSTACK_SIZE)
PR_RunError(&progfuncs->funcs, "Too many locals on resumtion of QC thread\n");
@ -2008,87 +2089,57 @@ void PDECL PR_ResumeThread (pubprogfuncs_t *ppf, struct qcthread_s *thread)
//do progs switching stuff as appropriate. (fteqw only)
initial_progs = prinst.pr_typecurrent;
PR_SwitchProgsParms(progfuncs, prnum);
oldexitdepth = prinst.exitdepth;
prinst.exitdepth = prinst.pr_depth;
ls = 0;
initial_stack = prinst.localstack_used;
//add on the callstack.
for (i = 0; i < thread->fstackdepth; i++)
{
if (prinst.pr_depth == prinst.exitdepth)
{
prinst.pr_stack[prinst.pr_depth].f = prinst.pr_xfunction;
prinst.pr_stack[prinst.pr_depth].s = prinst.pr_xstatement;
prinst.pr_stack[prinst.pr_depth].progsnum = initial_progs;
}
else
{
prinst.pr_stack[prinst.pr_depth].progsnum = thread->fstack[i].progsnum;
prinst.pr_stack[prinst.pr_depth].f = pr_progstate[thread->fstack[i].progsnum].functions + thread->fstack[i].fnum;
prinst.pr_stack[prinst.pr_depth].s = thread->fstack[i].statement;
}
if (i+1 == thread->fstackdepth)
{
f = &pr_cp_functions[fnum];
glob = (int*)pr_globals;
}
else
{
f = pr_progstate[thread->fstack[i+1].progsnum].functions + thread->fstack[i+1].fnum;
glob = (int*)pr_progstate[thread->fstack[i+1].progsnum].globals;
}
for (l = 0; l < f->locals; l++)
{
prinst.localstack[prinst.localstack_used++] = glob[f->parm_start + l];
glob[f->parm_start + l] = thread->lstack[ls++];
}
//make the new stack frame from the working values so stuff gets restored properly...
prinst.pr_stack[prinst.pr_depth].f = prinst.pr_xfunction;
prinst.pr_stack[prinst.pr_depth].s = prinst.pr_xstatement;
prinst.pr_stack[prinst.pr_depth].progsnum = prinst.pr_typecurrent;
prinst.pr_stack[prinst.pr_depth].pushed = prinst.spushed;
prinst.pr_depth++;
//restore the OP_PUSH data
memcpy(&prinst.localstack[prinst.localstack_used], &thread->lstack[prinst.localstack_used-initial_stack], prinst.spushed*sizeof(int));
prinst.localstack_used += prinst.spushed;
//and refill the working values from the inbound stack
PR_SwitchProgs(progfuncs, thread->fstack[i].progsnum);
prinst.pr_xfunction = pr_progstate[thread->fstack[i].progsnum].functions + thread->fstack[i].fnum;
prinst.pr_xstatement = thread->fstack[i].statement;
prinst.spushed = thread->fstack[i].spushed;
f = pr_progstate[thread->fstack[i].progsnum].functions + thread->fstack[i].fnum;
glob = (int*)pr_progstate[thread->fstack[i].progsnum].globals;
//copy the 'new' function's current globals into the local stack for restoration
memcpy(&prinst.localstack[prinst.localstack_used], &glob[f->parm_start], sizeof(int)*f->locals);
//and overwrite them with the saved values.
memcpy(&glob[f->parm_start], &thread->lstack[prinst.localstack_used-initial_stack], sizeof(int)*f->locals);
prinst.localstack_used += f->locals;
}
if (ls != thread->lstackused)
if (prinst.localstack_used-initial_stack != thread->lstackused)
PR_RunError(&progfuncs->funcs, "Thread stores incorrect locals count\n");
f = &pr_cp_functions[fnum];
// thread->lstackused -= f->locals; //the current function is the odd one out.
//add on the locals stack
memcpy(prinst.localstack+prinst.localstack_used, thread->lstack, sizeof(int)*thread->lstackused);
prinst.localstack_used += thread->lstackused;
//bung the locals of the current function on the stack.
// for (i=0 ; i < f->locals ; i++)
// ((int *)pr_globals)[f->parm_start + i] = 0xff00ff00;//thread->lstack[thread->lstackused+i];
// PR_EnterFunction (progfuncs, f, initial_progs);
oldf = prinst.pr_xfunction;
olds = prinst.pr_xstatement;
prinst.pr_xfunction = f;
s = thread->xstatement;
#ifndef QCGC
tempdepth = prinst.numtempstringsstack;
#endif
PR_ExecuteCode(progfuncs, s);
PR_ExecuteCode(progfuncs, prinst.pr_xstatement);
PR_SwitchProgsParms(progfuncs, initial_progs);
PR_SwitchProgsParms(progfuncs, initial_progs); //just in case
#ifndef QCGC
PR_FreeTemps(progfuncs, tempdepth);
prinst.numtempstringsstack = tempdepth;
#endif
prinst.exitdepth = oldexitdepth;
prinst.pr_xfunction = oldf;
prinst.pr_xstatement = olds;
}
void PDECL PR_AbortStack (pubprogfuncs_t *ppf)

View file

@ -331,7 +331,7 @@ int PDECL QC_RegisterFieldVar(pubprogfuncs_t *ppf, unsigned int type, const char
{ //we just found a new fieldname inside a progs
prinst.field[fnum].ofs = ofs = prinst.fields_size/sizeof(pvec_t); //add on the end
//if the progs field offset matches annother offset in the same progs, make it match up with the earlier one.
//if the progs field offset matches another offset in the same progs, make it match up with the earlier one.
if (progsofs>=0)
{
unsigned otherofs;

View file

@ -167,12 +167,12 @@ typedef struct prinst_s
#define MAX_STACK_DEPTH 1024 //insanely high value requried for xonotic.
prstack_t pr_stack[MAX_STACK_DEPTH];
int pr_depth;
int spushed;
//locals
#define LOCALSTACK_SIZE 16384
int localstack[LOCALSTACK_SIZE];
#define LOCALSTACK_SIZE (65536*16) //in words
int *localstack;
int localstack_used;
int spushed; //extra
//step-by-step debug state
int debugstatement;
@ -328,7 +328,7 @@ typedef struct edictrun_s
int PDECL Comp_Begin(pubprogfuncs_t *progfuncs, int nump, const char **parms);
int PDECL Comp_Continue(pubprogfuncs_t *progfuncs);
pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *progfuncs, const char *key);
pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *progfuncs, const char *desc, const char *location);
char *PDECL PR_EvaluateDebugString(pubprogfuncs_t *progfuncs, const char *key);
char *PDECL PR_SaveEnts(pubprogfuncs_t *progfuncs, char *mem, size_t *size, size_t maxsize, int mode);
int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PDECL *memoryreset) (pubprogfuncs_t *progfuncs, void *ctx), void (PDECL *entspawned) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend), pbool(PDECL *extendedterm)(pubprogfuncs_t *progfuncs, void *ctx, const char **extline));
@ -557,6 +557,7 @@ const char *ASMCALL PR_StringToNative (pubprogfuncs_t *inst, string_t str);
char *PR_GlobalString (progfuncs_t *progfuncs, int ofs, struct QCC_type_s **typehint);
char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs);
char *PR_GlobalStringImmediate (progfuncs_t *progfuncs, int ofs);
pbool CompileFile(progfuncs_t *progfuncs, const char *filename);

View file

@ -26,6 +26,16 @@
#define VARGS
#endif
#if __STDC_VERSION__ >= 202311L // c23
#define FALLTHROUGH [[fallthrough]];
#elif defined(__GNUC__) && __GNUC__ >= 7
#define FALLTHROUGH __attribute__((fallthrough));
#elif defined(__clang__) && __clang_major__ >= 7
#define FALLTHROUGH __attribute__((fallthrough));
#else
#define FALLTHROUGH
#endif
#if defined(_M_IX86) || defined(__i386__) //supported arch
#if defined(__GNUC__) || defined(_MSC_VER) //supported compilers (yay for inline asm)
//#define QCJIT
@ -95,7 +105,8 @@ ev_union, //not really sure why this is separate from struct
ev_accessor,//some weird type to provide class-like functions over a basic type.
ev_enum, //just a numeric type
ev_typedef, //so typedefs can refer to their original type (primarily for structs).
ev_boolean, //exists to optimise if(-0) workarounds. engine just sees int/float.
ev_boolean, //exists to optimise if(-0) workarounds. engine just sees int/float. uses parentclass
ev_bitfld, //erk... structs only... converted to their parentclass on read.
} etype_t;
enum {
DEBUG_TRACE_OFF, //debugging should be off.
@ -180,6 +191,7 @@ struct pubprogfuncs_s
pbool (PDECL *Decompile) (pubprogfuncs_t *prinst, const char *fname);
int callargc; //number of args of built-in call
int callprogs; //which progs it was called from...
char *stringtable; //qc strings are all relative. add to a qc string. this is required for support of frikqcc progs that strip string immediates.
unsigned int stringtablesize;
@ -200,6 +212,7 @@ struct pubprogfuncs_s
char *(PDECL *AddString) (pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup); //dump a string into the progs memory (for setting globals and whatnot)
void *(PDECL *Tempmem) (pubprogfuncs_t *prinst, int ammount, char *whatfor); //grab some mem for as long as the progs stays loaded
void *(PDECL *AddressableAlloc) (pubprogfuncs_t *progfuncs, unsigned int ammount); /*returns memory within the qc block, use stringtoprogs to get a usable qc pointer/string*/
void *(PDECL *AddressableRealloc) (pubprogfuncs_t *progfuncs, void *oldptr, unsigned int newammount); /*returns memory within the qc block, use stringtoprogs to get a usable qc pointer/string*/
void (PDECL *AddressableFree) (pubprogfuncs_t *progfuncs, void *mem); /*frees a block of addressable memory*/
string_t (PDECL *TempString) (pubprogfuncs_t *prinst, const char *str);
string_t (PDECL *AllocTempString) (pubprogfuncs_t *prinst, char **str, unsigned int len);
@ -211,7 +224,7 @@ struct pubprogfuncs_s
void (PDECL *EntClear) (pubprogfuncs_t *progfuncs, struct edict_s *e);
void (PDECL *FindPrefixGlobals) (pubprogfuncs_t *progfuncs, int prnum, char *prefix, void (PDECL *found) (pubprogfuncs_t *progfuncs, char *name, union eval_s *val, etype_t type, void *ctx), void *ctx); //calls the callback for each named global found
pbool (PDECL *SetWatchPoint) (pubprogfuncs_t *prinst, const char *key);
pbool (PDECL *SetWatchPoint) (pubprogfuncs_t *prinst, const char *desc, const char *location);
void (PDECL *AddSharedVar) (pubprogfuncs_t *progfuncs, int start, int size);
void (PDECL *AddSharedFieldVar) (pubprogfuncs_t *progfuncs, int num, char *relstringtable);
@ -244,7 +257,7 @@ typedef struct progexterns_s {
int (VARGS *Printf) (const char *, ...) LIKEPRINTF(1);
int (VARGS *DPrintf) (const char *, ...) LIKEPRINTF(1);
void (VARGS *Sys_Error) (const char *, ...) LIKEPRINTF(1);
void (VARGS *Abort) (char *, ...) LIKEPRINTF(1);
void (VARGS *Abort) (const char *, ...) LIKEPRINTF(1);
pbool (PDECL *CheckHeaderCrc) (pubprogfuncs_t *inst, progsnum_t idx, int crc, const char *filename);
void (PDECL *entspawn) (struct edict_s *ent, int loading); //ent has been spawned, but may not have all the extra variables (that may need to be set) set

Some files were not shown because too many files have changed in this diff Show more