Merge branch 'master' into joyfix

This commit is contained in:
Marco Cawthorne 2023-04-16 23:17:35 -07:00 committed by GitHub
commit 73a71a2ce1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
152 changed files with 7101 additions and 2602 deletions

View File

@ -95,6 +95,10 @@ SET(FTE_BUILD_CONFIG ${CMAKE_HOME_DIRECTORY}/engine/common/config_fteqw.h CACHE
SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};CONFIG_FILE_NAME=${FTE_BUILD_CONFIG})
SET(FTE_USE_SDL false CACHE BOOL "Force the use of SDL instead of using native builds.")
INCLUDE(GNUInstallDirs)
SET(FTE_INSTALL_BINDIR games CACHE STRING "Binary dir to install to.")
SET(FTE_INSTALL_LIBDIR fteqw CACHE STRING "Binary dir to install to.")
IF(NOT WIN32)
SET(SYS_LIBS ${SYS_LIBS} m)
ELSE()
@ -131,8 +135,12 @@ ELSE()
SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_OPENGL)
ENDIF()
FIND_PACKAGE(JPEG)
SET(FTE_DEP_JPEG true CACHE BOOL "Link against libjpeg.")
IF(FTE_DEP_JPEG)
FIND_PACKAGE(JPEG)
ENDIF()
IF(JPEG_FOUND)
INCLUDE_DIRECTORIES( ${JPEG_INCLUDE_DIRS} )
SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};LIBJPEG_STATIC)
SET(FTE_LIBS ${FTE_LIBS} ${JPEG_LIBRARIES})
ELSE()
@ -142,6 +150,7 @@ ENDIF()
FIND_PACKAGE(PNG)
IF(PNG_FOUND)
INCLUDE_DIRECTORIES( ${PNG_INCLUDE_DIRS} )
SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};LIBPNG_STATIC)
SET(FTE_LIBS ${FTE_LIBS} ${PNG_LIBRARIES})
ELSE()
@ -169,6 +178,7 @@ ENDIF()
FIND_LIBRARY(VORBISFILE_LIBRARY NAMES vorbisfile)
IF(NOT VORBISFILE_LIBRARY)
INCLUDE_DIRECTORIES( ${VORBISFILE_INCLUDE_DIRS} )
MESSAGE(WARNING "libvorbisfile library NOT available. Who listens to the bgm anyway?")
SET(FTE_LIB_DEFINES ${FTE_LIB_DEFINES};NO_OGG)
ENDIF()
@ -215,11 +225,13 @@ IF(CMAKE_BUILD_TYPE MATCHES "Debug")
ENDIF()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FILE_OFFSET_BITS=64")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DFTE_LIBRARY_PATH=${CMAKE_INSTALL_FULL_LIBDIR}/${FTE_INSTALL_LIBDIR}")
FUNCTION(EMBED_PLUGIN_META PLUGNAME PLUGTITLE PLUGDESC)
SET_TARGET_PROPERTIES(plug_${PLUGNAME} PROPERTIES OUTPUT_NAME "${PLUGNAME}")
SET_TARGET_PROPERTIES(plug_${PLUGNAME} PROPERTIES PREFIX "fteplug_")
SET_TARGET_PROPERTIES(plug_${PLUGNAME} PROPERTIES LINK_FLAGS "-Wl,--no-undefined")
SET(INSTALLTARGS ${INSTALLTARGS} "plug_${PLUGNAME}")
SET(INSTALLTARGS ${INSTALLTARGS} "plug_${PLUGNAME}" PARENT_SCOPE)
#sadly we need to use a temp zip file, because otherwise zip insists on using zip64 extensions which breaks zip -A (as well as any attempts to read any files).
ADD_CUSTOM_COMMAND(
TARGET plug_${PLUGNAME} POST_BUILD
@ -1020,6 +1032,7 @@ ELSE()
engine/common/cvar.c
engine/common/cmd.c
engine/common/sha1.c #for websockets
engine/common/sha2.c #for fingerprints
engine/http/httpclient.c #for the pipe stuff
engine/common/log.c
engine/common/fs.c
@ -1157,8 +1170,7 @@ SET(FTE_PLUG_QI true CACHE BOOL "Compile Quake-Injnector plugin.")
IF(FTE_PLUG_QI)
ADD_LIBRARY(plug_qi MODULE
plugins/plugin.c
plugins/qi/qi.c
plugins/emailnot/md5.c
plugins/qi/qi.c
plugins/jabber/xml.c
)
SET_TARGET_PROPERTIES(plug_qi PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;${FTE_LIB_DEFINES}")
@ -1241,11 +1253,7 @@ IF(FTE_PLUG_NAMEMAKER)
plugins/namemaker/namemaker.c
)
SET_TARGET_PROPERTIES(plug_namemaker PROPERTIES COMPILE_DEFINITIONS "${FTE_LIB_DEFINES}")
SET_TARGET_PROPERTIES(plug_namemaker PROPERTIES OUTPUT_NAME "namemaker")
SET_TARGET_PROPERTIES(plug_namemaker PROPERTIES PREFIX "fteplug_")
SET_TARGET_PROPERTIES(plug_namemaker PROPERTIES LINK_FLAGS "-Wl,--no-undefined")
TARGET_LINK_LIBRARIES(plug_namemaker ${SYS_LIBS})
SET(INSTALLTARGS ${INSTALLTARGS} plug_namemaker)
EMBED_PLUGIN_META(namemaker "Name Maker Plugin" "Provides a lame UI for selecting arbitrary non-ascii glyphs as part of your nickname.")
ENDIF()
@ -1525,13 +1533,16 @@ IF(FTE_PLUG_XMPP)
ENDIF()
ENDIF() #android
INCLUDE(GNUInstallDirs)
SET(FTE_INSTALL_BINDIR games CACHE STRING "Binary dir to install to.")
INSTALL(TARGETS ${INSTALLTARGS}
RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/${FTE_INSTALL_BINDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/${FTE_INSTALL_LIBDIR}"
)
INSTALL(FILES
fteqw.desktop
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications/")
SET(FTE_MENU_SYS true CACHE BOOL "Compile System Menu.")
IF(FTE_MENU_SYS)
ADD_CUSTOM_TARGET(menusys ALL

View File

@ -84,10 +84,10 @@ MAKE:=$(MAKE) --no-print-directory SVNREVISION="$(SVNREVISION)" SVN_VERSION="$(S
#update these to download+build a different version. this assumes that the url+subdirs etc contain a consistant version everywhere.
JPEGVER=9c
ZLIBVER=1.2.13
PNGVER=1.6.37
PNGVER=1.6.39
OGGVER=1.3.4
VORBISVER=1.3.6
SDL2VER=2.0.10
SDL2VER=2.26.4
SCINTILLAVER=373
OPUSVER=1.3.1
SPEEXVER=1.2.0
@ -1044,6 +1044,14 @@ endif
ifeq (1,$(LINK_ZLIB))
CLIENTLIBFLAGS+=-DZLIB_STATIC
CLIENTLDDEPS+=-lz
#and deflate64, because why not.
ifneq ("$(wildcard $(ARCHLIBS)/infback9.h)","")
CLIENTLIBFLAGS+=-DZLIB_DEFLATE64
CLIENTLDDEPS+=-lz9
QCC_CFLAGS+=-DZLIB_DEFLATE64
QCC_LDFLAGS+=-lz9
endif
endif
ifeq (1,$(LINK_ODE))
ALL_CFLAGS+=$(shell $(PKGCONFIG) ode --cflags --silence-errors) -DODE_STATIC
@ -2124,7 +2132,7 @@ m-profile:
_qcc-tmp: $(REQDIR)
@$(MAKE) $(TYPE) EXE_NAME="$(EXE_NAME)$(EXEPOSTFIX)" PRECOMPHEADERS="" OUT_DIR="$(OUT_DIR)" WCFLAGS="$(CLIENT_ONLY_CFLAGS) $(WCFLAGS)" LDFLAGS="$(LDFLAGS) $(QCC_LDFLAGS)" OBJS="QCC_OBJS SOBJS"
@$(MAKE) $(TYPE) EXE_NAME="$(EXE_NAME)$(EXEPOSTFIX)" PRECOMPHEADERS="" OUT_DIR="$(OUT_DIR)" WCFLAGS="$(QCC_CFLAGS) $(WCFLAGS)" LDFLAGS="$(LDFLAGS) $(QCC_LDFLAGS)" OBJS="QCC_OBJS SOBJS"
qcc-rel:
@$(MAKE) _qcc-tmp TYPE=_out-rel REQDIR=reldir EXE_NAME="../fteqcc$(BITS)" OUT_DIR="$(RELEASE_DIR)/$(NCDIRPREFIX)$(QCC_DIR)" SOBJS="qcctui.o packager.o $(if $(findstring win,$(FTE_TARGET)),fteqcc.o)"
qccgui-rel:
@ -2429,6 +2437,13 @@ libs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc:
test -f zlib-$(ZLIBVER).tar.gz || wget http://zlib.net/zlib-$(ZLIBVER).tar.gz
-test -f libs-$(ARCH)/libz.a || (mkdir -p libs-$(ARCH) && cd libs-$(ARCH) && tar -xvzf ../zlib-$(ZLIBVER).tar.gz && cd zlib-$(ZLIBVER) && $(TOOLOVERRIDES) ./configure --static && $(TOOLOVERRIDES) $(MAKE) libz.a CC="$(CC) $(W32_CFLAGS) -fPIC" && cp libz.a ../ && $(TOOLOVERRIDES) $(AR) -s ../libz.a && cp zlib.h zconf.h zutil.h zlib.pc ../ )
endif
libs-$(ARCH)/libz9.a: libs-$(ARCH)/libz.a
(cd libs-$(ARCH)/zlib-$(ZLIBVER) && \
$(CC) -o contrib/infback9/infback9.o -c contrib/infback9/infback9.c -I. && \
$(CC) -o contrib/infback9/inftree9.o -c contrib/infback9/inftree9.c -I. && \
cp contrib/infback9/infback9.h .. && \
$(AR) rcs ../libz9.a contrib/infback9/infback9.o contrib/infback9/inftree9.o)
libs-$(ARCH)/libpng.a libs-$(ARCH)/libpng.pc: libs-$(ARCH)/libz.a libs-$(ARCH)/libz.pc
test -f libpng-$(PNGVER).tar.gz || wget http://prdownloads.sourceforge.net/libpng/libpng-$(PNGVER).tar.gz?download -O libpng-$(PNGVER).tar.gz
@ -2465,7 +2480,7 @@ libs-$(ARCH)/libBulletDynamics.a:
ifeq ($(FTE_TARGET),web)
makelibs: libs-$(ARCH)/libz.a $(MAKELIBS)
else
makelibs: libs-$(ARCH)/libjpeg.a libs-$(ARCH)/libz.a libs-$(ARCH)/libpng.a libs-$(ARCH)/libogg.a libs-$(ARCH)/libvorbis.a libs-$(ARCH)/libopus.a libs-$(ARCH)/libspeex.a libs-$(ARCH)/libspeexdsp.a libs-$(ARCH)/libfreetype.a $(MAKELIBS)
makelibs: libs-$(ARCH)/libjpeg.a libs-$(ARCH)/libz9.a libs-$(ARCH)/libz.a libs-$(ARCH)/libpng.a libs-$(ARCH)/libogg.a libs-$(ARCH)/libvorbis.a libs-$(ARCH)/libopus.a libs-$(ARCH)/libspeex.a libs-$(ARCH)/libspeexdsp.a libs-$(ARCH)/libfreetype.a $(MAKELIBS)
endif
HTTP_OBJECTS=http/httpserver.c http/iwebiface.c common/fs_stdio.c http/ftpserver.c

View File

@ -3009,9 +3009,9 @@ fail:
else if (!strcmp(auth, "SHA1"))
hashfunc = &hash_sha1;
else if (!strcmp(auth, "SHA2_256"))
hashfunc = &hash_sha256;
hashfunc = &hash_sha2_256;
else if (!strcmp(auth, "SHA2_512"))
hashfunc = &hash_sha512;
hashfunc = &hash_sha2_512;
else if (*auth)
Con_Printf("Server requires unsupported auth method: %s\n", auth);

View File

@ -3231,7 +3231,7 @@ void CLQ1_AddShadow(entity_t *ent)
scenetris_t *t;
cl_adddecal_ctx_t ctx;
if (!r_blobshadows || !ent->model || (ent->model->type != mod_alias && ent->model->type != mod_halflife))
if (!r_blobshadows || !ent->model || (ent->model->type != mod_alias && ent->model->type != mod_halflife) || (ent->flags & RF_NOSHADOW))
return;
s = R_RegisterShader("shadowshader", SUF_NONE,
@ -3295,7 +3295,7 @@ void CLQ1_AddShadow(entity_t *ent)
}
ctx.t = t;
Vector4Set(ctx.rgbavalue, 0, 0, 0, r_blobshadows);
Vector4Set(ctx.rgbavalue, 0, 0, 0, r_blobshadows*((ent->flags & RF_TRANSLUCENT)?ent->shaderRGBAf[3]:1));
Mod_ClipDecal(cl.worldmodel, shadoworg, ctx.axis[0], ctx.axis[1], ctx.axis[2], radius, 0,0, CL_AddDecal_Callback, &ctx);
if (!t->numidx)
cl_numstris--;

View File

@ -460,10 +460,13 @@ static void Ignoreteam_f(void)
}
if (j == MAX_TEAMIGNORELIST)
Con_Printf("You cannot ignore more than %d teams\n", MAX_TEAMIGNORELIST);
Q_strncpyz(ignoreteamlist[j], arg, sizeof(ignoreteamlist[j]));
if (j + 1 < MAX_TEAMIGNORELIST)
ignoreteamlist[j + 1][0] = 0;
Con_Printf("Added team %s to ignore list\n", arg);
else
{
Q_strncpyz(ignoreteamlist[j], arg, sizeof(ignoreteamlist[j]));
if (j + 1 < MAX_TEAMIGNORELIST)
ignoreteamlist[j + 1][0] = 0;
Con_Printf("Added team %s to ignore list\n", arg);
}
return;
}
}

View File

@ -37,7 +37,7 @@ cvar_t cl_c2sdupe = CVARD("cl_c2sdupe", "0", "Send duplicate copies of packets t
cvar_t cl_c2spps = CVARD("cl_c2spps", "0", "Reduces outgoing packet rates by dropping up to a third of outgoing packets.");
cvar_t cl_c2sImpulseBackup = CVARD("cl_c2sImpulseBackup","3", "Prevents the cl_c2spps setting from dropping redundant packets that contain impulses, in an attempt to keep impulses more reliable.");
static cvar_t cl_c2sMaxRedundancy = CVARD("cl_c2sMaxRedundancy","5", "This is the maximum number of input frames to send in each input packet. Values greater than 1 provide redundancy and avoid prediction misses, though you might find cl_c2sdupe provides equivelent result and at lower latency. It is locked at 3 for vanilla quakeworld, and locked at 1 for vanilla netquake.");
cvar_t cl_netfps = CVARD("cl_netfps", "150", "Send up to this many packets to the server per second. The rate used is also limited by the server which usually forces a cap to this setting of 77. Low packet rates can result in extra extrapolation to try to hide the resulting latencies.");
cvar_t cl_netfps = CVARFD("cl_netfps", "150", CVAR_ARCHIVE, "Send up to this many packets to the server per second. The rate used is also limited by the server which usually forces a cap to this setting of 77. Low packet rates can result in extra extrapolation to try to hide the resulting latencies.");
cvar_t cl_queueimpulses = CVARD("cl_queueimpulses", "0", "Queues unsent impulses instead of replacing them. This avoids the need for extra wait commands (and the timing issues of such commands), but potentially increases latency and can cause scripts to be desynced with regard to buttons and impulses.");
cvar_t cl_smartjump = CVARD("cl_smartjump", "1", "Makes the jump button act as +moveup when in water. This is typically quieter and faster.");
cvar_t cl_iDrive = CVARFD("cl_iDrive", "1", CVAR_SEMICHEAT, "Effectively releases movement keys when the opposing key is pressed. This avoids dead-time when both keys are pressed. This can be emulated with various scripts, but that's messy.");
@ -1393,6 +1393,9 @@ void CL_ClampPitch (int pnum, float frametime)
VectorAngles(view[0], view[2], pv->viewangles, false);
VectorClear(pv->viewanglechange);
//fixme: in_vraim stuff
VectorCopy(pv->viewangles, pv->aimangles);
return;
}
#if 1
@ -1450,7 +1453,10 @@ void CL_ClampPitch (int pnum, float frametime)
if (!vang[ROLL])
{
if (!pv->viewanglechange[PITCH] && !pv->viewanglechange[YAW] && !pv->viewanglechange[ROLL])
{
VectorCopy(pv->viewangles, pv->aimangles);
return;
}
}
else
{
@ -1490,6 +1496,9 @@ void CL_ClampPitch (int pnum, float frametime)
pv->viewangles[ROLL] += 360;
if (pv->viewangles[PITCH] < -180)
pv->viewangles[PITCH] += 360;
//fixme: in_vraim stuff
VectorCopy(pv->viewangles, pv->aimangles);
return;
}
#endif

View File

@ -187,23 +187,20 @@ cvar_t cl_gunanglex = CVAR("cl_gunanglex", "0");
cvar_t cl_gunangley = CVAR("cl_gunangley", "0");
cvar_t cl_gunanglez = CVAR("cl_gunanglez", "0");
#ifdef HAVE_DTLS
extern cvar_t net_enable_dtls;
#endif
cvar_t cl_proxyaddr = CVAR("cl_proxyaddr", "");
cvar_t cl_sendguid = CVARD("cl_sendguid", "", "Send a randomly generated 'globally unique' id to servers, which can be used by servers for score rankings and stuff. Different servers will see different guids. Delete the 'qkey' file in order to appear as a different user.\nIf set to 2, all servers will see the same guid. Be warned that this can show other people the guid that you're using.");
cvar_t cl_downloads = CVARAFD("cl_downloads", "1", /*q3*/"cl_allowDownload", CVAR_NOTFROMSERVER, "Allows you to block all automatic downloads.");
cvar_t cl_download_csprogs = CVARFD("cl_download_csprogs", "1", CVAR_NOTFROMSERVER, "Download updated client gamecode if available. Warning: If you clear this to avoid downloading vm code, you should also clear cl_download_packages.");
cvar_t cl_download_redirection = CVARFD("cl_download_redirection", "2", CVAR_NOTFROMSERVER, "Follow download redirection to download packages instead of individual files. Also allows the server to send nearly arbitary download commands.\n2: allows redirection only to named packages files (and demos/*.mvd), which is a bit safer.");
cvar_t cl_download_mapsrc = CVARFD("cl_download_mapsrc", "", CVAR_ARCHIVE, "Specifies an http location prefix for map downloads. EG: \"http://example.com/path/quakemaps/\"");
cvar_t cl_downloads = CVARAFD("cl_downloads", "1", /*q3*/"cl_allowDownload", CVAR_NOTFROMSERVER|CVAR_ARCHIVE, "Allows you to block all automatic downloads.");
cvar_t cl_download_csprogs = CVARFD("cl_download_csprogs", "1", CVAR_NOTFROMSERVER|CVAR_ARCHIVE, "Download updated client gamecode if available. Warning: If you clear this to avoid downloading vm code, you should also clear cl_download_packages.");
cvar_t cl_download_redirection = CVARFD("cl_download_redirection", "2", CVAR_NOTFROMSERVER|CVAR_ARCHIVE, "Follow download redirection to download packages instead of individual files. Also allows the server to send nearly arbitary download commands.\n2: allows redirection only to named packages files (and demos/*.mvd), which is a bit safer.");
cvar_t cl_download_mapsrc = CVARFD("cl_download_mapsrc", "", CVAR_ARCHIVE, "Specifies an http location prefix for map downloads. EG: \"http://example.com/path/gamemaps/\"");
cvar_t cl_download_packages = CVARFD("cl_download_packages", "1", CVAR_NOTFROMSERVER, "0=Do not download packages simply because the server is using them. 1=Download and load packages as needed (does not affect games which do not use this package). 2=Do download and install permanently (use with caution!)");
cvar_t requiredownloads = CVARFD("requiredownloads","1", CVAR_ARCHIVE, "0=join the game before downloads have even finished (might be laggy). 1=wait for all downloads to complete before joining.");
cvar_t requiredownloads = CVARAFD("cl_download_wait", "1", /*old*/"requiredownloads", CVAR_ARCHIVE, "0=join the game before downloads have even finished (might be laggy). 1=wait for all downloads to complete before joining.");
cvar_t mod_precache = CVARD("mod_precache","1", "Controls when models are loaded.\n0: Load them only when they're actually needed.\n1: Load them upfront.\n2: Lazily load them to shorten load times at the risk of brief stuttering during only the start of the map.");
cvar_t cl_muzzleflash = CVAR("cl_muzzleflash", "1");
cvar_t gl_simpleitems = CVARF("gl_simpleitems", "0", CVAR_ARCHIVE);
cvar_t cl_item_bobbing = CVARF("cl_model_bobbing", "0", CVAR_ARCHIVE);
cvar_t gl_simpleitems = CVARFD("gl_simpleitems", "0", CVAR_ARCHIVE, "Replace models with simpler sprites.");
cvar_t cl_item_bobbing = CVARFD("cl_model_bobbing", "0", CVAR_ARCHIVE, "Makes rotating pickup items bob too.");
cvar_t cl_countpendingpl = CVARD("cl_countpendingpl", "0", "If set to 1, packet loss percentages will show packets still in transit as lost, even if they might still be received.");
cvar_t cl_standardchat = CVARFD("cl_standardchat", "0", CVAR_ARCHIVE, "Disables auto colour coding in chat messages.");
@ -213,7 +210,7 @@ cvar_t msg_filter_pickups = CVARD("msg_filter_pickups", "0", "Prevents pickup m
cvar_t cl_standardmsg = CVARFD("cl_standardmsg", "0", CVAR_ARCHIVE, "Disables auto colour coding in console prints.");
cvar_t cl_parsewhitetext = CVARD("cl_parsewhitetext", "1", "When parsing chat messages, enable support for messages like: red{white}red");
cvar_t cl_dlemptyterminate = CVAR("cl_dlemptyterminate", "1");
cvar_t cl_dlemptyterminate = CVARD("cl_dlemptyterminate", "1", "Terminate downloads when reciving an empty download packet. This should help work around buggy mvdsv servers.");
static void QDECL Cvar_CheckServerInfo(struct cvar_s *var, char *oldvalue)
{ //values depend upon the serverinfo, so reparse for overrides.
@ -289,15 +286,6 @@ static struct
int numadr;
int nextadr;
netadr_t adr[8]; //addresses that we're trying to transfer to, one entry per dns result, eg both ::1 AND 127.0.0.1
#ifdef HAVE_DTLS
enum
{ //not relevant when given a direct dtls address.
DTLS_DISABLE,
DTLS_TRY,
DTLS_REQUIRE,
DTLS_ACTIVE,
} dtlsupgrade;
#endif
int protocol; //nq/qw/q2/q3. guessed based upon server replies
int subprotocol; //the monkeys are trying to eat me.
struct
@ -320,13 +308,20 @@ static struct
enum coninfomode_e
{
CIM_DEFAULT, //sends both a qw getchallenge and nq connect (also with postfixed getchallenge so modified servers can force getchallenge)
CIM_NQONLY, //disables getchallenge (so fte servers treat us as an nq server). should not be used for dpp7 servers.
CIM_NQONLY, //disables getchallenge (so fte servers treat us as an nq client). should not be used for dpp7 servers.
CIM_QEONLY, //forces dtls and uses a different nq netchan version
} mode;
enum coninfospec_e
{
CIS_DEFAULT, //default
CIS_JOIN, //force join
CIS_OBSERVE, //force observe
} spec;
int defaultport;
int tries; //increased each try, every fourth trys nq connect packets.
unsigned char guid[64]; //client->server guid (so doesn't change with transfers)
// qbyte fingerprint[5*4]; //sha1 hash of accepted dtls certs
struct dtlspeercred_s peercred;
} connectinfo;
qboolean nomaster;
@ -536,10 +531,9 @@ char *CL_GUIDString(netadr_t *adr)
{
static qbyte buf[2048];
static int buflen;
unsigned int digest[4];
qbyte digest[DIGEST_MAXSIZE];
char serveraddr[256];
void *blocks[2];
int lens[2];
void *ctx;
if (!*cl_sendguid.string && *connectinfo.ext.guidsalt)
{
@ -587,14 +581,50 @@ char *CL_GUIDString(netadr_t *adr)
}
}
blocks[0] = buf;lens[0] = buflen;
blocks[1] = serveraddr;lens[1] = strlen(serveraddr);
Com_BlocksChecksum(2, blocks, lens, (void*)digest);
Q_snprintfz(connectinfo.guid, sizeof(connectinfo.guid), "%08x%08x%08x%08x", digest[0], digest[1], digest[2], digest[3]);
ctx = alloca(hash_md4.contextsize);
hash_md4.init(ctx);
hash_md4.process(ctx, buf, buflen);
hash_md4.process(ctx, serveraddr, strlen(serveraddr));
hash_md4.terminate(digest, ctx);
Base16_EncodeBlock(digest, hash_md4.digestsize, connectinfo.guid, sizeof(connectinfo.guid));
return connectinfo.guid;
}
static void CL_ConnectAbort(const char *format, ...)
{ //stops trying to connect, doesn't affect the _current_ connection, so usable for transfers.
va_list argptr;
char reason[1024];
if (format)
{
va_start (argptr, format);
Q_vsnprintfz (reason, sizeof(reason), format,argptr);
va_end (argptr);
Cvar_Set(&cl_disconnectreason, reason);
Con_Printf (CON_ERROR"%s\n", reason);
}
#ifdef HAVE_DTLS
while (connectinfo.numadr)
NET_DTLS_Disconnect(cls.sockets, &connectinfo.adr[--connectinfo.numadr]);
#endif
connectinfo.numadr = 0;
SCR_EndLoadingPlaque();
connectinfo.trying = false;
if (format)
{
//try and force the menu to show again. this should force the disconnectreason to show.
if (!Key_Dest_Has(kdm_console))
{
#ifdef MENU_DAT
if (!MP_Toggle(1))
#endif
Menu_Prompt(NULL, NULL, reason, NULL, NULL, "Okay", true);
}
}
}
/*
=======================
CL_SendConnectPacket
@ -658,13 +688,29 @@ static void CL_SendConnectPacket (netadr_t *to)
t1 = Sys_DoubleTime ();
#ifdef HAVE_DTLS
if (connectinfo.peercred.hash && net_enable_dtls.ival>0)
{
char cert[8192];
char digest[DIGEST_MAXSIZE];
int sz = NET_GetConnectionCertificate(cls.sockets, to, QCERT_PEERCERTIFICATE, cert, sizeof(cert));
if (sz <= 0 || memcmp(connectinfo.peercred.digest, digest, CalcHash(connectinfo.peercred.hash, digest, sizeof(digest), cert, sz)))
{ //FIXME: we may have already pinned the bad cert, which may cause issues when reconnecting without FP info later.
if (NET_GetConnectionCertificate(cls.sockets, to, QCERT_ISENCRYPTED, NULL, 0)<0)
CL_ConnectAbort ("Fingerprint specified, but server did not report any certificate\n");
else
CL_ConnectAbort ("Server certificate does not match specified fingerprint\n");
return;
}
}
#endif
if (!to)
{
to = &addr;
if (!NET_StringToAdr (cls.servername, PORT_DEFAULTSERVER, to))
{
Con_TPrintf ("CL_SendConnectPacket: Bad server address \"%s\"\n", cls.servername);
connectinfo.trying = false;
CL_ConnectAbort ("CL_SendConnectPacket: Bad server address \"%s\"\n", cls.servername);
return;
}
}
@ -675,8 +721,7 @@ static void CL_SendConnectPacket (netadr_t *to)
if (!NET_IsClientLegal(to))
{
Con_TPrintf ("Illegal server address\n");
connectinfo.trying = false;
CL_ConnectAbort("Illegal server address\n");
return;
}
@ -715,10 +760,12 @@ static void CL_SendConnectPacket (netadr_t *to)
Q_strncatz(data, va("\\prx\\%s", cls.servername), sizeof(data));
*a = '@';
}
if (connectinfo.spec==CIS_OBSERVE)
Q_strncatz(data, "\\spectator\\1", sizeof(data));
//the info itself
{
static const char *prioritykeys[] = {"name", "password", "spectator", "lang", "rate", "team", "topcolor", "bottomcolor", "skin", "_", "*", NULL};
static const char *ignorekeys[] = {"prx", "*z_ext", NULL};
const char *ignorekeys[] = {"prx", "*z_ext", (connectinfo.spec!=CIS_DEFAULT)?"spectator":NULL, NULL};
InfoBuf_ToString(&cls.userinfo[0], data+strlen(data), sizeof(data)-strlen(data), prioritykeys, ignorekeys, NULL, &cls.userinfosync, &cls.userinfo[0]);
}
if (connectinfo.protocol == CP_QUAKEWORLD) //zquake extension info.
@ -808,32 +855,18 @@ static void CL_ResolvedServer(void *vctx, void *data, size_t a, size_t b)
if (!ctx->found)
{
Cvar_Set(&cl_disconnectreason, va("Bad server address \"%s\"", ctx->servername));
Con_TPrintf ("Bad server address \"%s\"\n", ctx->servername);
connectinfo.trying = false;
SCR_EndLoadingPlaque();
CL_ConnectAbort("Unable to resolve server address \"%s\"\n", ctx->servername);
return;
}
#ifdef HAVE_DTLS
for (i = 0; i < ctx->found; i++)
{
if (connectinfo.dtlsupgrade == DTLS_ACTIVE || connectinfo.mode==CIM_QEONLY)
if (net_enable_dtls.ival>=4 || connectinfo.mode==CIM_QEONLY)// || (connectinfo.peercred.hash && net_enable_dtls.ival >= 1))
{ //if we've already established a dtls connection, stick with it
if (ctx->adr[i].prot == NP_DGRAM)
ctx->adr[i].prot = NP_DTLS;
}
else if (connectinfo.adr[i].prot == NP_DTLS)
{ //dtls connections start out with regular udp, and upgrade to dtls once its established that the server supports it.
//FIXME: remove this block once our new netcode is better established.
connectinfo.dtlsupgrade = DTLS_REQUIRE;
ctx->adr[i].prot = NP_DGRAM;
}
else
{
//hostname didn't specify dtls. upgrade if we're allowed, but don't mandate it.
//connectinfo.dtlsupgrade = DTLS_TRY;
}
}
#endif
@ -846,11 +879,11 @@ static void CL_ResolvedServer(void *vctx, void *data, size_t a, size_t b)
static void CL_ResolveServer(void *vctx, void *data, size_t a, size_t b)
{
struct resolvectx_s *ctx = vctx;
const char *host = strrchr(cls.servername+1, '@');
const char *host = strrchr(ctx->servername+1, '@');
if (host)
host++;
else
host = cls.servername;
host = ctx->servername;
ctx->found = NET_StringToAdr2 (host, connectinfo.defaultport, ctx->adr, countof(ctx->adr), NULL);
@ -1109,9 +1142,7 @@ void CL_CheckForResend (void)
connectinfo.nextadr = 0;
if (!connectinfo.numadr)
{
Con_TPrintf ("CL_CheckForResend: Bad server address \"%s\"\n", cls.servername);
connectinfo.trying = false;
SCR_EndLoadingPlaque();
CL_ConnectAbort("CL_CheckForResend: Bad server address \"%s\"\n", cls.servername);
return;
}
NET_AdrToString(data, sizeof(data), &connectinfo.adr[connectinfo.nextadr]);
@ -1200,6 +1231,9 @@ void CL_CheckForResend (void)
else
connectinfo.clogged = false; //do the prints and everything.
if (!cls.sockets) //only if its needed... we don't want to keep using a new port unless we have to
NET_InitClient(false);
#ifdef HAVE_DTLS
if (connectinfo.numadr>0 && connectinfo.adr[0].prot == NP_DTLS)
{ //get through the handshake first, instead of waiting for a 5-sec timeout between polls.
@ -1208,17 +1242,16 @@ void CL_CheckForResend (void)
case NETERR_CLOGGED: //temporary failure
connectinfo.clogged = true;
return;
case NETERR_DISCONNECTED:
CL_ConnectAbort("DTLS Certificate Verification Failure\n");
break;
case NETERR_NOROUTE: //not an error here, just means we need to send a new handshake.
break;
default:
break;
}
}
if (connectinfo.dtlsupgrade != DTLS_ACTIVE)
#endif
{
if (!cls.sockets) //only if its needed... we don't want to keep using a new port unless we have to
NET_InitClient(false);
}
t1 = Sys_DoubleTime ();
if (!connectinfo.istransfer)
@ -1246,10 +1279,7 @@ void CL_CheckForResend (void)
to = &connectinfo.adr[connectinfo.nextadr%connectinfo.numadr];
if (!NET_IsClientLegal(to))
{
Cvar_Set(&cl_disconnectreason, va("Illegal server address"));
Con_TPrintf ("Illegal server address\n");
SCR_EndLoadingPlaque();
connectinfo.trying = false;
CL_ConnectAbort ("Illegal server address\n");
return;
}
@ -1274,12 +1304,9 @@ void CL_CheckForResend (void)
connectinfo.clogged = false;
if (connectinfo.tries == 0 && connectinfo.nextadr < connectinfo.numadr)
if (!NET_EnsureRoute(cls.sockets, "conn", cls.servername, to))
if (!NET_EnsureRoute(cls.sockets, "conn", &connectinfo.peercred, to, true))
{
Cvar_Set(&cl_disconnectreason, va("Unable to establish connection to %s\n", cls.servername));
Con_Printf ("Unable to establish connection to %s\n", cls.servername);
connectinfo.trying = false;
SCR_EndLoadingPlaque();
CL_ConnectAbort ("Unable to establish connection to %s\n", cls.servername);
return;
}
@ -1377,32 +1404,92 @@ void CL_CheckForResend (void)
}
else
{
Cvar_Set(&cl_disconnectreason, va("No route to \"%s\", giving up\n", cls.servername));
Con_TPrintf ("No route to host, giving up\n");
connectinfo.trying = false;
SCR_EndLoadingPlaque();
CL_ConnectAbort ("Unable to connect to %s, giving up\n", cls.servername);
NET_CloseClient();
}
}
}
static void CL_BeginServerConnect(const char *host, int port, qboolean noproxy, enum coninfomode_e mode)
static void CL_BeginServerConnect(char *host, int port, qboolean noproxy, enum coninfomode_e mode, enum coninfospec_e spec)
{
if (!strncmp(host, "localhost", 9))
noproxy = true; //FIXME: resolve the address here or something so that we don't end up using a proxy for lan addresses.
const char *schemeend = strstr(host, "://");
char *arglist;
if (strstr(host, "://") || !*cl_proxyaddr.string || noproxy)
Q_strncpyz (cls.servername, host, sizeof(cls.servername));
Q_strncpyz(cls.serverurl, host, sizeof(cls.serverurl));
if (schemeend)
{
const char *schemestart = strchr(host, ':');
int schemelen;
//if its one of our explicit protocols then use the url as-is
const char *netschemes[] = {"udp", "udp4", "udp6", "ipx", "tcp", "tcp4", "tcp6", /*ipx*/"spx", "ws", "wss", "tls", "dtls", "ice", "rtc", "ices", "rtcs", "irc", "udg", "unix"};
int i;
size_t slen;
if (!schemestart || schemestart==schemeend)
schemestart = host;
else
schemestart++;
schemelen = schemeend-schemestart;
Q_strncpyz (cls.servername, "", sizeof(cls.servername));
for (i = 0; i < countof(netschemes); i++)
{
slen = strlen(netschemes[i]);
if (schemelen == slen && !strncmp(schemestart, netschemes[i], slen))
{
Q_strncpyz (cls.servername, host, sizeof(cls.servername)); //oh. will probably be okay then
break;
}
}
if (!*cls.servername)
{ //not some '/foo' name, not rtc:// either...
char *sl = strchr(schemeend+3, '/');
if (sl)
{
if (!strncmp(sl, "/observe", 8))
{
if (spec == CIS_DEFAULT)
spec = CIS_OBSERVE;
else if (spec != CIS_OBSERVE)
Con_Printf("Ignoring 'observe'\n");
memmove(sl, sl+8, strlen(sl+8)+1);
}
else if (!strncmp(sl, "/join", 5))
{
if (spec == CIS_DEFAULT)
spec = CIS_JOIN;
else if (spec != CIS_OBSERVE)
Con_Printf("Ignoring 'join'\n");
memmove(sl, sl+5, strlen(sl+5)+1);
}
else if (!strncmp(sl, "/", 1) && (sl[1] == 0 || sl[1]=='?'))
{
//current spectator mode
memmove(sl, sl+1, strlen(sl+1)+1);
}
}
Q_strncpyz (cls.servername, schemeend+3, sizeof(cls.servername)); //probably some game-specific mess that we don't know
}
}
else
Q_snprintfz(cls.servername, sizeof(cls.servername), "%s@%s", host, cl_proxyaddr.string);
{
if (!strncmp(host, "localhost", 9))
noproxy = true; //FIXME: resolve the address here or something so that we don't end up using a proxy for lan addresses.
if (strstr(host, "://") || !*cl_proxyaddr.string || noproxy)
Q_strncpyz (cls.servername, host, sizeof(cls.servername));
else
Q_snprintfz(cls.servername, sizeof(cls.servername), "%s@%s", host, cl_proxyaddr.string);
}
arglist = strchr(cls.servername, '?');
if (!port)
port = cl_defaultport.value;
#ifdef HAVE_DTLS
while (connectinfo.numadr)
NET_DTLS_Disconnect(cls.sockets, &connectinfo.adr[--connectinfo.numadr]);
#endif
CL_ConnectAbort(NULL);
memset(&connectinfo, 0, sizeof(connectinfo));
if (*cl_disconnectreason.string)
Cvar_Set(&cl_disconnectreason, "");
@ -1410,15 +1497,38 @@ static void CL_BeginServerConnect(const char *host, int port, qboolean noproxy,
connectinfo.defaultport = port;
connectinfo.protocol = CP_UNKNOWN;
connectinfo.mode = mode;
connectinfo.spec = spec;
#ifdef HAVE_DTLS
if (net_enable_dtls.ival >= 3)
connectinfo.dtlsupgrade = DTLS_REQUIRE;
else if (net_enable_dtls.ival >= 2)
connectinfo.dtlsupgrade = DTLS_TRY;
else
connectinfo.dtlsupgrade = DTLS_DISABLE;
#endif
connectinfo.peercred.name = cls.servername;
if (arglist)
{
*arglist++ = 0;
while (*arglist)
{
char *e = strchr(arglist, '&');
if (e)
*e=0;
if (!strncasecmp(arglist, "fp=", 3))
{
size_t l = 8*Base64_DecodeBlock(arglist+3, arglist+strlen(arglist), connectinfo.peercred.digest, sizeof(connectinfo.peercred.digest));
if (l <= 160)
connectinfo.peercred.hash = &hash_sha1;
else if (l <= 256)
connectinfo.peercred.hash = &hash_sha2_256;
else if (l <= 512)
connectinfo.peercred.hash = &hash_sha2_512;
else
connectinfo.peercred.hash = NULL;
}
else
Con_Printf(CON_WARNING"uri arg not known: \"%s\"\n", arglist);
if (e)
arglist=e+1;
else
break;
}
}
SCR_SetLoadingStage(LS_CONNECTION);
CL_CheckForResend();
@ -1434,9 +1544,11 @@ void CL_BeginServerReconnect(void)
}
#endif
#ifdef HAVE_DTLS
if (connectinfo.numadr>0)
NET_DTLS_Disconnect(cls.sockets, &connectinfo.adr[0]);
connectinfo.dtlsupgrade = 0;
{
int i;
for (i = 0; i < connectinfo.numadr; i++)
NET_DTLS_Disconnect(cls.sockets, &connectinfo.adr[i]);
}
#endif
#ifdef SUPPORT_ICE
while (connectinfo.numadr) //remove any ICE addresses. probably we'll end up with no addresses left leaving us free to re-resolve giving us the original(ish) rtc connection.
@ -1474,11 +1586,11 @@ void CL_Transfer_f(void)
return;
}
CL_ConnectAbort(NULL);
server = Cmd_Argv (1);
if (!*server)
{
//if they didn't specify a server, abort any active transfer/connection.
connectinfo.trying = false;
return;
}
@ -1529,7 +1641,7 @@ void CL_Connect_f (void)
#endif
CL_Disconnect_f ();
CL_BeginServerConnect(server, 0, false, CIM_DEFAULT);
CL_BeginServerConnect(server, 0, false, CIM_DEFAULT, CIS_DEFAULT);
}
#if defined(CL_MASTER) && defined(HAVE_PACKET)
static void CL_ConnectBestRoute_f (void)
@ -1564,7 +1676,7 @@ static void CL_ConnectBestRoute_f (void)
else
#endif
CL_Disconnect_f ();
CL_BeginServerConnect(server, 0, true, CIM_DEFAULT);
CL_BeginServerConnect(server, 0, true, CIM_DEFAULT, CIS_DEFAULT);
}
#endif
@ -1591,9 +1703,7 @@ static void CL_Join_f (void)
CL_Disconnect_f ();
Cvar_Set(&spectator, "0");
CL_BeginServerConnect(server, 0, false, CIM_DEFAULT);
CL_BeginServerConnect(server, 0, false, CIM_DEFAULT, CIS_JOIN);
}
void CL_Observe_f (void)
@ -1621,7 +1731,7 @@ void CL_Observe_f (void)
Cvar_Set(&spectator, "1");
CL_BeginServerConnect(server, 0, false, CIM_DEFAULT);
CL_BeginServerConnect(server, 0, false, CIM_DEFAULT, CIS_OBSERVE);
}
#ifdef NQPROT
@ -1646,7 +1756,7 @@ void CLNQ_Connect_f (void)
CL_Disconnect_f ();
CL_BeginServerConnect(server, 26000, true, mode);
CL_BeginServerConnect(server, 26000, true, mode, CIS_DEFAULT/*doesn't really do spec/join stuff, but if the server asks for our info later...*/);
}
#endif
@ -2242,9 +2352,7 @@ void CL_Disconnect_f (void)
#endif
CL_Disconnect (NULL);
connectinfo.trying = false;
CL_ConnectAbort(NULL);
NET_CloseClient();
(void)CSQC_UnconnectedInit();
@ -2570,6 +2678,7 @@ void CL_CheckServerInfo(void)
cl.bunnyspeedcap = Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, "pm_bunnyspeedcap"));
movevars.slidefix = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, "pm_slidefix")) != 0);
movevars.slidyslopes = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, "pm_slidyslopes")) != 0);
movevars.bunnyfriction = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, "pm_bunnyfriction")) != 0);
movevars.airstep = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, "pm_airstep")) != 0);
movevars.pground = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, "pm_pground")) != 0);
movevars.stepdown = (Q_atof(InfoBuf_ValueForKey(&cl.serverinfo, "pm_stepdown")) != 0);
@ -2929,6 +3038,7 @@ void CL_Packet_f (void)
int i, l;
char *in, *out;
netadr_t adr;
struct dtlspeercred_s cred = {Cmd_Argv(1)};
if (Cmd_Argc() != 3)
{
@ -3018,7 +3128,7 @@ void CL_Packet_f (void)
if (!cls.sockets)
NET_InitClient(false);
if (!NET_EnsureRoute(cls.sockets, "packet", Cmd_Argv(1), &adr))
if (!NET_EnsureRoute(cls.sockets, "packet", &cred, &adr, true))
return;
NET_SendPacket (cls.sockets, out-send, send, &adr);
@ -3339,7 +3449,8 @@ void CL_ConnectionlessPacket (void)
{
if (CL_IsPendingServerAddress(&net_from))
{
if (!NET_EnsureRoute(cls.sockets, "redir", cls.servername, &adr))
struct dtlspeercred_s cred = {cls.servername}; //FIXME
if (!NET_EnsureRoute(cls.sockets, "redir", &cred, &adr, true))
Con_Printf (CON_ERROR"Unable to redirect to %s\n", data);
else
{
@ -3357,29 +3468,20 @@ void CL_ConnectionlessPacket (void)
else if (!strcmp(s, "reject"))
{ //generic rejection. stop trying.
char *data = MSG_ReadStringLine();
Con_Printf ("reject\n%s\n", data);
Con_Printf ("reject\n");
if (CL_IsPendingServerAddress(&net_from))
{
Cvar_Set(&cl_disconnectreason, va("%s\n", data));
connectinfo.trying = false;
}
CL_ConnectAbort("%s\n", data);
return;
}
else if (!strcmp(s, "badname"))
{ //rejected purely because of player name
if (CL_IsPendingServerAddress(&net_from))
{
Cvar_Set(&cl_disconnectreason, va("bad player name\n"));
connectinfo.trying = false;
}
CL_ConnectAbort("bad player name\n");
}
else if (!strcmp(s, "badaccount"))
{ //rejected because username or password is wrong
if (CL_IsPendingServerAddress(&net_from))
{
Cvar_Set(&cl_disconnectreason, va("invalid username or password\n"));
connectinfo.trying = false;
}
CL_ConnectAbort("invalid username or password\n");
}
Con_Printf ("f%s\n", s);
@ -3605,40 +3707,50 @@ void CL_ConnectionlessPacket (void)
}
#ifdef HAVE_DTLS
if (candtls && net_from.prot == NP_DGRAM && (connectinfo.dtlsupgrade || candtls > 1) && !NET_IsEncrypted(&net_from))
if ((candtls && net_enable_dtls.ival) && net_from.prot == NP_DGRAM && (net_enable_dtls.ival>1 || candtls > 1) && !NET_IsEncrypted(&net_from))
{
//c2s getchallenge
//s2c c%u\0DTLS=$candtls
//c2s getchallenge <no client details
//s2c c%u\0DTLS=$candtls <may leak server details>
//<<YOU ARE HERE>>
//c2s dtlsconnect %u
//s2c dtlsopened
//c2s dtlsconnect %u [REALTARGET] <FIXME: target server is plain text, not entirely unlike tls1.2, but still worse than a vpn and could be improved>
//s2c dtlsopened <no details at all, other than that the server is now willing to accept dtls handshakes etc>
//c2s DTLS(getchallenge)
//DTLS(etc)
//NOTE: the dtlsconnect/dtlsopened parts are redundant and the non-dtls parts are entirely optional (and should be skipped the client requries/knows the server supports dtls)
//NOTE: the dtlsconnect/dtlsopened parts are redundant and the non-dtls parts are now entirely optional (and should be skipped if the client requries/knows the server supports dtls)
//the challenge response includes server capabilities, so we still need the getchallenge/response part of the handshake despite dtls making the actual challenge part redundant.
//getchallenge has to be done twice, with the outer one only reporting whether dtls can/should be used.
//this means the actual connect packet is already over dtls, which protects the user's userinfo.
//FIXME: do rcon via dtls too, but requires tracking pending rcon packets until the handshake completes.
//server says it can do dtls, but will still need to ask it to allocate extra resources for us.
//server says it can do dtls, but will still need to ask it to allocate extra resources for us (I hadn't gotten dtls cookies working properly at that point).
char *pkt;
//qwfwd proxy routing
char *at;
if ((at = strrchr(cls.servername, '@')))
if (net_enable_dtls.ival>0)
{
*at = 0;
pkt = va("%c%c%c%c""dtlsconnect %i %s", 255, 255, 255, 255, connectinfo.challenge, cls.servername);
*at = '@';
char *pkt;
//qwfwd proxy routing. it doesn't support it yet, but hey, if its willing to forward the dtls packets its all good.
char *at;
if ((at = strrchr(cls.servername, '@')))
{
*at = 0;
pkt = va("%c%c%c%c""dtlsconnect %i %s", 255, 255, 255, 255, connectinfo.challenge, cls.servername);
*at = '@';
}
else
pkt = va("%c%c%c%c""dtlsconnect %i", 255, 255, 255, 255, connectinfo.challenge);
NET_SendPacket (cls.sockets, strlen(pkt), pkt, &net_from);
return;
}
else if (candtls >= 3)
{
Cvar_Set(&cl_disconnectreason, va("DTLS is disabled, but server requires it. not connecting\n"));
connectinfo.trying = false;
Con_Printf("DTLS is disabled, but server requires it. Set ^[/net_enable_dtls 1^] before connecting again.\n");
return;
}
else
pkt = va("%c%c%c%c""dtlsconnect %i", 255, 255, 255, 255, connectinfo.challenge);
NET_SendPacket (cls.sockets, strlen(pkt), pkt, &net_from);
return;
}
if (connectinfo.dtlsupgrade == DTLS_REQUIRE && !NET_IsEncrypted(&net_from))
if (net_enable_dtls.ival>=3 && !NET_IsEncrypted(&net_from))
{
Cvar_Set(&cl_disconnectreason, va("Server does not support/allow dtls. not connecting\n"));
connectinfo.trying = false;
@ -3801,10 +3913,9 @@ void CL_ConnectionlessPacket (void)
return;
memset(&cred, 0, sizeof(cred));
cred.peer.name = cls.servername;
if (NET_DTLS_Create(cls.sockets, &net_from, &cred))
cred.peer = connectinfo.peercred;
if (NET_DTLS_Create(cls.sockets, &net_from, &cred, true))
{
connectinfo.dtlsupgrade = DTLS_ACTIVE;
connectinfo.numadr = 1; //fixate on this resolved address.
connectinfo.adr[0] = net_from;
connectinfo.adr[0].prot = NP_DTLS;
@ -3812,11 +3923,7 @@ void CL_ConnectionlessPacket (void)
connectinfo.time = 0; //send a new challenge NOW.
}
else
{
if (connectinfo.dtlsupgrade == DTLS_TRY)
connectinfo.dtlsupgrade = DTLS_DISABLE;
Con_Printf ("unable to establish dtls route\n");
}
CL_ConnectAbort("Unable to initialise dtls driver. You may need to adjust tls_provider or disable dtls with ^[/net_enable_dtls 0^]\n"); //this is a local issue, and not a result on remote packets.
#else
Con_Printf ("dtlsopened (unsupported)\n");
#endif
@ -3853,15 +3960,6 @@ client_connect: //fixme: make function
Con_TPrintf ("ignoring connection\n");
return;
}
if (net_from.type != NA_LOOPBACK)
{
Con_TPrintf (S_COLOR_GRAY"connection\n");
#ifdef HAVE_SERVER
if (sv.state && sv.state != ss_clustermode)
SV_UnspawnServer();
#endif
}
if (cls.state >= ca_connected)
{
@ -3879,6 +3977,15 @@ client_connect: //fixme: make function
return;
}
}
if (net_from.type != NA_LOOPBACK)
{
// Con_TPrintf (S_COLOR_GRAY"connection\n");
#ifdef HAVE_SERVER
if (sv.state && sv.state != ss_clustermode)
SV_UnspawnServer();
#endif
}
connectinfo.trying = false;
cl.splitclients = 0;
cls.protocol = connectinfo.protocol;
@ -4111,7 +4218,7 @@ void CLNQ_ConnectionlessPacket(void)
else
{
//send a dummy packet.
//this makes our local nat think we initialised the conversation, so that we can receive the.
//this makes our local firewall think we initialised the conversation, so that we can receive their packets. however this only works if our nat uses the same public port for private ports.
Netchan_Transmit(&cls.netchan, 1, "\x01", 2500);
}
return;
@ -4294,10 +4401,6 @@ void CL_ReadPackets (void)
else
NET_ReadPackets(cls.sockets);
#ifdef HAVE_DTLS
NET_DTLS_Timeouts(cls.sockets);
#endif
//
// check timeout
//
@ -5421,9 +5524,9 @@ NORETURN void VARGS Host_EndGame (const char *message, ...)
SCR_EndLoadingPlaque();
CL_Disconnect (string);
CL_ConnectAbort(NULL);
SV_UnspawnServer();
connectinfo.trying = false;
Cvar_Set(&cl_shownet, "0");
@ -6214,7 +6317,7 @@ qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file)
// "quake2:rtc://broker:port/game"
// "qw://[stream@]host[:port]/COMMAND" join, spectate, qtvplay
//we'll chop off any non-auth prefix, its just so we can handle multiple protocols via a single uri scheme.
char *t, *cmd;
char *t, *cmd, *args;
const char *url;
char buffer[8192];
const char *schemestart = strchr(fname, ':');
@ -6260,6 +6363,15 @@ qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file)
t[urilen] = 0;
url = t+schemelen;
*buffer = 0;
for (args = t+schemelen; *args; args++)
{
if (*args == '?')
{
*args++ = 0;
break;
}
}
for (cmd = t+schemelen; *cmd; cmd++)
{
if (*cmd == '/')

View File

@ -35,6 +35,7 @@ static char *CLNQ_ParseProQuakeMessage (char *s);
#endif
static void DLC_Poll(qdownload_t *dl);
static void CL_ProcessUserInfo (int slot, player_info_t *player);
static void Con_HexDump(qbyte *packet, size_t len, size_t badoffset);
#ifdef NQPROT
char *cl_dp_packagenames;
@ -978,10 +979,9 @@ qboolean CL_CheckOrEnqueDownloadFile (const char *filename, const char *localnam
if (flags & DLLF_ALLOWWEB)
{
extern cvar_t sv_dlURL;
const char *dlURL = InfoBuf_ValueForKey(&cl.serverinfo, "sv_dlURL");
if (!*dlURL)
dlURL = sv_dlURL.string;
dlURL = fs_dlURL.string;
flags &= ~(DLLF_TRYWEB|DLLF_ALLOWWEB);
if (*dlURL && (flags & DLLF_NONGAME) && !strncmp(filename, "package/", 8))
{ //filename is something like: package/GAMEDIR/foo.pk3
@ -1000,10 +1000,10 @@ qboolean CL_CheckOrEnqueDownloadFile (const char *filename, const char *localnam
{
char base[MAX_QPATH];
COM_FileBase(filename, base, sizeof(base));
#ifndef FTE_TARGET_WEB
if (strncmp(cl_download_mapsrc.string, "http://", 7) && !strncmp(cl_download_mapsrc.string, "https://", 8))
#ifndef FTE_TARGET_WEB //don't care about prefixes in the web build, for site-relative uris.
if (strncmp(cl_download_mapsrc.string, "http://", 7) && strncmp(cl_download_mapsrc.string, "https://", 8))
{
Con_Printf("%s: Scheme not specified.\n", cl_download_mapsrc.name);
Con_Printf("%s: Scheme not specified, assuming https.\n", cl_download_mapsrc.name);
filename = va("https://%s/%s", cl_download_mapsrc.string, filename+5);
}
else
@ -1487,6 +1487,7 @@ static int CL_LoadModels(int stage, qboolean dontactuallyload)
SCR_SetLoadingFile("external textures");
if (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADING)
COM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING);
CL_CheckServerInfo(); //some serverinfo rules can change with map type, so make sure they're updated now we're sure we know it properly.
if (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADED)
Mod_NowLoadExternal(cl.worldmodel);
@ -1498,6 +1499,7 @@ static int CL_LoadModels(int stage, qboolean dontactuallyload)
if (atstage())
{
SCR_SetLoadingFile("newmap");
// if (!cl.worldmodel || cl.worldmodel->type == mod_dummy)
// Host_EndGame("No worldmodel was loaded\n");
Surf_NewMap (cl.worldmodel);
@ -2238,6 +2240,7 @@ static void DL_Completed(qdownload_t *dl, qofs_t start, qofs_t end)
static float chunkrate;
static int CL_CountQueuedDownloads(void);
static void CL_ParseChunkedDownload(qdownload_t *dl)
{
qbyte *svname;
@ -2261,7 +2264,41 @@ static void CL_ParseChunkedDownload(qdownload_t *dl)
svname = MSG_ReadString();
if (cls.demoplayback)
return;
{ //downloading in demos is allowed ONLY for csprogs.dat
extern cvar_t cl_downloads, cl_download_csprogs;
if (!cls.download && !dl &&
!strcmp(svname, "csprogs.dat") && filesize && filesize == strtoul(InfoBuf_ValueForKey(&cl.serverinfo, "*csprogssize"), NULL, 0) &&
cl_downloads.ival && cl_download_csprogs.ival)
{
//FIXME: should probably save this to memory instead of bloating it on disk.
dl = Z_Malloc(sizeof(*dl));
Q_strncpyz(dl->remotename, svname, sizeof(dl->remotename));
Q_strncpyz(dl->localname, va("csprogsvers/%x.dat", (unsigned int)strtoul(InfoBuf_ValueForKey(&cl.serverinfo, "*csprogs"), NULL, 0)), sizeof(dl->localname));
// download to a temp name, and only rename
// to the real name when done, so if interrupted
// a runt file wont be left
COM_StripExtension (dl->localname, dl->tempname, sizeof(dl->tempname)-5);
Q_strncatz (dl->tempname, ".tmp", sizeof(dl->tempname));
dl->method = DL_QWPENDING;
dl->percent = 0;
dl->sizeunknown = true;
dl->flags = DLLF_OVERWRITE;
if (COM_FCheckExists(dl->localname))
{
Con_DPrintf("Demo embeds redundant %s\n", dl->localname);
Z_Free(dl);
return;
}
cls.download = dl;
Con_Printf("Saving recorded file %s (%lu bytes)\n", dl->localname, (unsigned long)filesize);
}
else
return;
}
if (!*svname)
{
@ -2365,6 +2402,7 @@ static void CL_ParseChunkedDownload(qdownload_t *dl)
dl->method = DL_QWCHUNKS;
dl->percent = 0;
dl->size = filesize;
dl->sizeunknown = false;
dl->starttime = Sys_DoubleTime();
@ -2389,7 +2427,8 @@ static void CL_ParseChunkedDownload(qdownload_t *dl)
if (!dl)
{
Con_Printf("ignoring download data packet\n");
if (!cls.demoplayback) //mute it in demos.
Con_Printf("ignoring download data packet\n");
return;
}
@ -2399,11 +2438,6 @@ static void CL_ParseChunkedDownload(qdownload_t *dl)
if (!dl->file)
return;
if (cls.demoplayback)
{ //err, yeah, when playing demos we don't actually pay any attention to this.
return;
}
VFS_SEEK(dl->file, chunknum*DLBLOCKSIZE);
if (dl->size - chunknum*DLBLOCKSIZE < DLBLOCKSIZE) //final block is actually meant to be smaller than we recieve.
VFS_WRITE(dl->file, data, dl->size - chunknum*DLBLOCKSIZE);
@ -2415,6 +2449,9 @@ static void CL_ParseChunkedDownload(qdownload_t *dl)
dl->percent = dl->completedbytes/(float)dl->size*100;
chunkrate += 1;
if (dl->completedbytes == dl->size)
CL_DownloadFinished(dl);
}
static int CL_CountQueuedDownloads(void)
@ -3706,6 +3743,7 @@ static void CLQ2_ParseServerData (void)
Cvar_ForceCallback(Cvar_FindVar("r_particlesdesc"));
Surf_PreNewMap();
CL_CheckServerInfo();
}
#endif
@ -3738,7 +3776,7 @@ void CL_ParseEstablished(void)
else
security = "^["S_COLOR_RED"plain-text\\tip\\"CON_WARNING"Do not type passwords as they can potentially be seen by network sniffers^]";
Con_TPrintf ("Connected to ^["S_COLOR_BLUE"%s\\type\\connect %s^] (%s).\n", cls.servername, cls.servername, security);
Con_TPrintf ("\rConnected to ^["S_COLOR_BLUE"%s\\type\\connect %s^] (%s).\n", cls.servername, cls.servername, security);
}
}
@ -5549,6 +5587,8 @@ static void CL_ProcessUserInfo (int slot, player_info_t *player)
player->rbottomcolor = 13;
*/
player->chatstate = atoi(InfoBuf_ValueForKey (&player->userinfo, "chat"));
#ifdef HEXEN2
/*if we're running hexen2, they have to be some class...*/
player->h2playerclass = atoi(InfoBuf_ValueForKey (&player->userinfo, "cl_playerclass"));
@ -5691,7 +5731,10 @@ static void CL_ParseSetInfo (void)
{
player = &cl.players[slot];
Con_DLPrintf(strcmp(key, "chat")?1:2,"SETINFO %s: %s=%s\n", player->name, key, val);
if (cl_shownet.value == 3)
Con_Printf("\t%i(%s): %s=\"%s\"\n", slot, player->name, key, val);
else
Con_DLPrintf(strcmp(key, "chat")?1:2,"SETINFO %s: %s=%s\n", player->name, key, val);
InfoBuf_SetStarKey(&player->userinfo, key, val);
player->userinfovalid = true;
@ -5715,7 +5758,10 @@ static void CL_ServerInfo (void)
Q_strncpyz (key, MSG_ReadString(), sizeof(key));
Q_strncpyz (value, MSG_ReadString(), sizeof(value));
Con_DPrintf("SERVERINFO: %s=%s\n", key, value);
if (cl_shownet.value == 3)
Con_Printf("\t%s=%s\n", key, value);
else
Con_DPrintf("SERVERINFO: %s=%s\n", key, value);
InfoBuf_SetStarKey(&cl.serverinfo, key, value);
@ -5874,6 +5920,9 @@ static void CL_SetStatNumeric (int pnum, int stat, int ivalue, float fvalue)
cl.players[cls_lastto].stats[stat]=ivalue;
cl.players[cls_lastto].statsf[stat]=fvalue;
if (cl_shownet.value == 3)
Con_Printf("\t%i: %i=%g\n", cls_lastto, stat, fvalue);
for (pnum = 0; pnum < cl.splitclients; pnum++)
if (cl.playerview[pnum].cam_spec_track == cls_lastto && cl.playerview[pnum].cam_state != CAM_FREECAM)
CL_SetStat_Internal(pnum, stat, ivalue, fvalue);
@ -5887,6 +5936,9 @@ static void CL_SetStatNumeric (int pnum, int stat, int ivalue, float fvalue)
cl.players[pl].statsf[stat]=fvalue;
}
if (cl_shownet.value == 3)
Con_Printf("\t%i(%i): %i=%g\n", pnum, pl, stat, fvalue);
CL_SetStat_Internal(pnum, stat, ivalue, fvalue);
}
@ -6693,9 +6745,13 @@ static void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds
{
int cbuflevel;
#ifdef NQPROT
if (!*stufftext && *msg == 1 && !cls.allow_csqc)
if (!*stufftext && *msg == 1)
{
Con_DPrintf("Proquake: %s\n", msg);
if (developer.ival)
{
Con_DPrintf("Proquake Message:\n");
Con_HexDump(msg, strlen(msg), 1);
}
msg = CLNQ_ParseProQuakeMessage(msg);
}
#endif
@ -7396,7 +7452,10 @@ void CLQW_ParseServerMessage (void)
i = MSG_ReadByte ();
if (i >= MAX_NET_LIGHTSTYLES)
Host_EndGame ("svc_lightstyle > MAX_LIGHTSTYLES");
R_UpdateLightStyle(i, MSG_ReadString(), 1, 1, 1);
s = MSG_ReadString();
if (cl_shownet.value == 3)
Con_Printf("\t%i=\"%s\"\n", i, s);
R_UpdateLightStyle(i, s, 1, 1, 1);
break;
#ifdef PEXT_LIGHTSTYLECOL
case svcfte_lightstylecol:
@ -7847,7 +7906,7 @@ void CLQ2_ParseServerMessage (void)
//
if (cl_shownet.value == 1)
Con_Printf ("%i ",net_message.cursize);
else if (cl_shownet.value == 2)
else if (cl_shownet.value >= 2)
Con_Printf ("------------------\n");
@ -8237,7 +8296,7 @@ void CLNQ_ParseServerMessage (void)
//
if (cl_shownet.value == 1)
Con_Printf ("%i ",net_message.cursize);
else if (cl_shownet.value == 2)
else if (cl_shownet.value >= 2)
Con_Printf ("------------------\n");
//

View File

@ -615,7 +615,8 @@ void CL_CalcClientTime(void)
{
if (!cls.state)
{
cl.servertime += host_frametime;
if (!cl.implicitpause)
cl.servertime += host_frametime;
cl.time = cl.servertime;
return;
}
@ -958,6 +959,8 @@ float CL_GetPredictionRealtime(playerview_t *pv)
return simtime;
}
qboolean CSQC_GetSSQCEntityOrigin(unsigned int ssqcent, float *out);
/*
==============
CL_PredictMove
@ -1059,7 +1062,8 @@ void CL_PredictMovePNum (int seat)
if (pv->cam_state == CAM_PENDING && pv->cam_spec_track >= 0 && pv->cam_spec_track < cl.allocated_client_slots && pv->viewentity != pv->cam_spec_track+1)
{
if ((cl.inframes[cl.validsequence & UPDATE_MASK].playerstate[pv->cam_spec_track].messagenum == cl.validsequence) ||
(pv->cam_spec_track+1 < cl.maxlerpents && cl.lerpents[pv->cam_spec_track+1].sequence == cl.lerpentssequence))
(pv->cam_spec_track+1 < cl.maxlerpents && cl.lerpents[pv->cam_spec_track+1].sequence == cl.lerpentssequence) ||
CSQC_GetSSQCEntityOrigin(pv->cam_spec_track+1, NULL))
{
pv->cam_state = CAM_EYECAM;
pv->viewentity = pv->cam_spec_track+1;

View File

@ -78,6 +78,7 @@ void RSpeedShow(void)
RSpNames[RSPEED_PROTOCOL] = "Client Protocol";
RSpNames[RSPEED_SERVER] = "Server";
RSpNames[RSPEED_AUDIO] = "Audio";
memset(RQntNames, 0, sizeof(RQntNames));
RQntNames[RQUANT_MSECS] = "Microseconds";
@ -1399,7 +1400,7 @@ const char *SCR_ShowPics_ClickCommand(float cx, float cy, qboolean istouch)
float x, y, w, h;
showpic_t *sp;
mpic_t *p;
qboolean tryload = !showpics_touchtime;
qboolean tryload = istouch && !showpics_touchtime;
float bestdist = istouch?16:1;
const char *best = NULL;
showpics_touchtime = realtime;
@ -1876,9 +1877,9 @@ void R_GetGPUUtilisation(float *gpu, float *mem)
typedef void *nvmlDevice_t;
struct nvmlUtilization_s
{
unsigned int cpu;
unsigned int gpu;
unsigned int mem;
} util = {-1,-1};
} util = {~0u,~0u};
static int (*nvmlDeviceGetUtilizationRates) (nvmlDevice_t device, struct nvmlUtilization_s *utilization);
static nvmlDevice_t dev;
if (!tried)
@ -1906,8 +1907,8 @@ void R_GetGPUUtilisation(float *gpu, float *mem)
if (dev)
nvmlDeviceGetUtilizationRates(dev, &util);
*gpu = util.cpu/100.0;
*mem = util.mem/100.0;
*gpu = (util.gpu == ~0u)?-1:(util.gpu/100.0);
*mem = (util.mem == ~0u)?-1:(util.mem/100.0);
#else
*gpu = *mem = -1;
#endif
@ -1954,6 +1955,11 @@ void SCR_DrawFPS (void)
sprintf(str, "%.0f%% GPU", gpu*100);
SCR_StringXY(str, show_fps_x.value, (show_fps_y.value>=0)?(show_fps_y.value+8):(show_fps_y.value-1));
}
/* if (gpumem>=0)
{
sprintf(str, "%.0f%% VRAM Bus", gpumem*100);
SCR_StringXY(str, show_fps_x.value, (show_fps_y.value>=0)?(show_fps_y.value+16):(show_fps_y.value-2));
}*/
}
void SCR_DrawClock(void)

View File

@ -2870,7 +2870,7 @@ entity_t *CL_NewTempEntity (void)
return ent;
}
void CSQC_GetEntityOrigin(unsigned int csqcent, float *out);
qboolean CSQC_GetEntityOrigin(unsigned int csqcent, float *out);
/*
=================

View File

@ -187,6 +187,7 @@ typedef struct player_info_s
qboolean ignored;
qboolean vignored;
unsigned int chatstate;
// skin information
unsigned int rtopcolor; //real, according to their userinfo
@ -491,7 +492,8 @@ typedef struct
infobuf_t userinfo[MAX_SPLITS];
infosync_t userinfosync;
char servername[MAX_OSPATH]; // name of server from original connect
char serverurl[MAX_OSPATH*4]; // eg qw://foo:27500/join?fp=blah
char servername[MAX_OSPATH]; // internal parsing, eg dtls://foo:27500
struct ftenet_connections_s *sockets;
@ -1450,7 +1452,7 @@ qboolean CL_MayLerp(void);
//
#ifdef CSQC_DAT
qboolean CSQC_Inited(void);
void CSQC_RendererRestarted(void);
void CSQC_RendererRestarted(qboolean initing);
qboolean CSQC_UnconnectedOkay(qboolean inprinciple);
qboolean CSQC_UnconnectedInit(void);
qboolean CSQC_CheckDownload(const char *name, unsigned int checksum, size_t checksize); //reports whether we already have a usable csprogs.dat

View File

@ -3294,7 +3294,7 @@ void Con_DrawConsole (int lines, qboolean noback)
}
selactive = Key_GetConsoleSelectionBox(con_current, &selsx, &selsy, &selex, &seley);
if ((con_current->flags & CONF_KEEPSELECTION) && con_current->selstartline && con_current->selendline && con_current->buttonsdown != CB_SELECTED)
if ((con_current->flags & CONF_KEEPSELECTION) && con_current->selstartline && con_current->selendline && con_current->buttonsdown != CB_SELECTED && con_current->buttonsdown != CB_TAPPED)
selactive = -1;
Font_BeginString(font_console, x, y, &x, &y);
@ -3329,14 +3329,18 @@ void Con_DrawConsole (int lines, qboolean noback)
mouseconsole = con_mouseover?con_mouseover:con_current;
if (con_current->buttonsdown == CB_SELECTED)
if (con_current->buttonsdown == CB_SELECTED || con_current->buttonsdown == CB_TAPPED)
{ //select was released...
console_t *con = con_current;
char *buffer;
qboolean tapped = con->buttonsdown==CB_TAPPED;
con->buttonsdown = CB_NONE;
if (con->selstartline)
{
con->flags |= CONF_KEEPSELECTION;
if (tapped)
con->flags &= ~CONF_KEEPSELECTION;
else
con->flags |= CONF_KEEPSELECTION;
if (con->userline)
{
if (con->flags & CONF_BACKSELECTION)
@ -3427,6 +3431,57 @@ void Con_DrawOneConsole(console_t *con, qboolean focused, struct font_s *font, f
con->display = con->current;
Con_DrawConsoleLines(con, con->display, con->displayscroll, x, sx, sy, y, selactive, selsx, selex, selsy, seley, lineagelimit);
if (con->buttonsdown == CB_SELECTED || con->buttonsdown == CB_TAPPED)
{ //select was released...
char *buffer;
qboolean tapped = con->buttonsdown==CB_TAPPED;
con->buttonsdown = CB_NONE;
if (con->selstartline)
{
if (tapped)
con->flags &= ~CONF_KEEPSELECTION;
else
con->flags |= CONF_KEEPSELECTION;
if (con->userline)
{
if (con->flags & CONF_BACKSELECTION)
{
con->userline = con->selendline;
con->useroffset = con->selendoffset;
}
else
{
con->userline = con->selstartline;
con->useroffset = con->selstartoffset;
}
}
if (con->selstartline == con->selendline && con->selendoffset <= con->selstartoffset+1)
{
if (keydown[K_LSHIFT] || keydown[K_RSHIFT])
;
else
{
buffer = Con_CopyConsole(con, false, true, false);
if (buffer)
{
Key_HandleConsoleLink(con, buffer);
Z_Free(buffer);
}
}
}
else
{
buffer = Con_CopyConsole(con, true, false, true); //don't keep markup if we're copying to the clipboard
if (buffer)
{
Sys_SaveClipboard(CBT_SELECTION, buffer);
Z_Free(buffer);
}
}
}
}
Font_EndString(font);
}

View File

@ -4633,8 +4633,10 @@ static void *ReadEXRFile(qbyte *buf, size_t len, const char *fname, int *outwidt
fd = mkstemp(tname); //bsd4.3/posix1-2001
if (fd >= 0)
{
write(fd, buf, len);
ctx = exr.OpenInputFile(tname);
if (write(fd, buf, len) == len)
ctx = exr.OpenInputFile(tname);
else
ctx = NULL;
close(fd); //we don't need the input file now.
unlink(tname);
#endif
@ -14162,18 +14164,18 @@ image_t *Image_FindTexture(const char *identifier, const char *subdir, unsigned
image_t *tex;
if (!subdir)
subdir = "";
tex = Hash_Get(&imagetable, identifier);
tex = Hash_GetInsensitive(&imagetable, identifier);
while(tex)
{
if (!((tex->flags ^ flags) & (IF_CLAMP|IF_PALETTIZE|IF_PREMULTIPLYALPHA)))
{
if (r_ignoremapprefixes.ival || !strcmp(subdir, tex->subpath?tex->subpath:"") || ((flags|tex->flags) & IF_INEXACT))
if (r_ignoremapprefixes.ival || !Q_strcasecmp(subdir, tex->subpath?tex->subpath:"") || ((flags|tex->flags) & IF_INEXACT))
{
tex->regsequence = r_regsequence;
return tex;
}
}
tex = Hash_GetNext(&imagetable, identifier, tex);
tex = Hash_GetNextInsensitive(&imagetable, identifier, tex);
}
return NULL;
}
@ -14212,7 +14214,7 @@ static image_t *Image_CreateTexture_Internal (const char *identifier, const char
tex->fallbackheight = 0;
tex->fallbackfmt = TF_INVALID;
if (*tex->ident)
Hash_Add(&imagetable, tex->ident, tex, buck);
Hash_AddInsensitive(&imagetable, tex->ident, tex, buck);
return tex;
}
@ -14549,7 +14551,7 @@ void Image_DestroyTexture(image_t *tex)
Sys_UnlockMutex(com_resourcemutex);
#endif
if (*tex->ident)
Hash_RemoveData(&imagetable, tex->ident, tex);
Hash_RemoveDataInsensitive(&imagetable, tex->ident, tex);
Z_Free(tex);
}
@ -14787,7 +14789,7 @@ void Image_Shutdown(void)
{
tex = imagelist;
if (*tex->ident)
Hash_RemoveData(&imagetable, tex->ident, tex);
Hash_RemoveDataInsensitive(&imagetable, tex->ident, tex);
imagelist = tex->next;
if (tex->status == TEX_LOADED)
j++;

View File

@ -751,8 +751,10 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum, float frame
mx *= 1.75;
my *= 1.75;
#ifdef QUAKESTATS
if (IN_WeaponWheelAccumulate(pnum, mx, my, 0))
mx = my = 0;
mx = my = 0;
#endif
}
}
else
@ -1023,10 +1025,12 @@ void IN_MoveJoystick(struct joy_s *joy, float *movements, int pnum, float framet
}
}
#ifdef QUAKESTATS
if (IN_WeaponWheelAccumulate(joy->qdeviceid, jstrafe[1]*50, -jstrafe[0]*50, 20))
jstrafe[0] = jstrafe[1] = 0;
if (IN_WeaponWheelAccumulate(joy->qdeviceid, jlook[1]*50, jlook[0]*50, 20))
jlook[0] = jlook[1] = 0;
#endif
if (Key_Dest_Has(~kdm_game))
{

View File

@ -506,7 +506,7 @@ static void J_ControllerSensor(SDL_JoystickID jid, SDL_SensorType sensor, float
if (joy->qdevid == DEVID_UNSET)
return;
safeswitch(sensor)
switch(sensor)
{
case SDL_SENSOR_ACCEL:
IN_Accelerometer(joy->qdevid, data[0], data[1], data[2]);
@ -520,7 +520,7 @@ static void J_ControllerSensor(SDL_JoystickID jid, SDL_SensorType sensor, float
case SDL_SENSOR_GYRO_R:
case SDL_SENSOR_INVALID:
case SDL_SENSOR_UNKNOWN:
safedefault:
default:
break;
}
}

View File

@ -761,7 +761,7 @@ qboolean Key_GetConsoleSelectionBox(console_t *con, int *sx, int *sy, int *ex, i
*ey = con->mousecursor[1];
return true;
}
else if (con->buttonsdown == CB_SELECT || con->buttonsdown == CB_SELECTED)
else if (con->buttonsdown == CB_SELECT || con->buttonsdown == CB_SELECTED || con->buttonsdown == CB_TAPPED)
{
//right-mouse
//select. copy-to-clipboard on release.
@ -1219,7 +1219,10 @@ void Key_ConsoleRelease(console_t *con, int key, unsigned int unicode)
if ((key == K_TOUCHTAP || key == K_MOUSE1) && con->buttonsdown == CB_SELECT)
{
con->buttonsdown = CB_SELECTED;
if (fabs(con->mousedown[0] - con->mousecursor[0]) < 5 && fabs(con->mousedown[1] - con->mousecursor[1]) < 5 && realtime < con->mousedowntime + 0.4)
con->buttonsdown = CB_TAPPED; //don't leave it selected.
else
con->buttonsdown = CB_SELECTED;
return;
}
if ((key == K_TOUCHSLIDE || key == K_MOUSE1) && con->buttonsdown == CB_SCROLL)// || (key == K_MOUSE2 && con->buttonsdown == CB_SCROLL_R))
@ -1276,7 +1279,7 @@ void Key_ConsoleRelease(console_t *con, int key, unsigned int unicode)
return;
}
}
if (con->buttonsdown == CB_SELECTED)
if (con->buttonsdown == CB_SELECTED || con->buttonsdown == CB_TAPPED)
; //will time out in the drawing code.
else
// if (con->buttonsdown == CB_MOVE) //window title(move)
@ -1408,6 +1411,8 @@ void Key_EmojiCompletion_c(int argn, const char *partial, struct xcommandargcomp
char guess[256];
char repl[256];
size_t ofs, len;
if (*partial != ':')
return; //don't show annoying completion crap.
if (!emojidata)
Key_LoadEmojiList();
len = strlen(partial);

View File

@ -176,9 +176,9 @@ typedef enum {
#define K_GP_DIAMOND_CONFIRM K_GP_DIAMOND_DOWN //roughly equivelent to k_return for menu behaviours
#define K_GP_DIAMOND_CANCEL K_GP_DIAMOND_RIGHT //roughly like escape, at least in menus
#define K_GP_DIAMOND_ALTCONFIRM K_GP_DIAMOND_UP //for more negative confirmations.
K_GP_VIEW,
K_GP_VIEW, //aka back (near left stick)
K_GP_GUIDE,
K_GP_MENU,
K_GP_MENU, //aka options/start (near right stick)
K_GP_LEFT_STICK,
K_GP_RIGHT_STICK,
K_GP_LEFT_SHOULDER,

File diff suppressed because it is too large Load Diff

View File

@ -110,7 +110,8 @@ menuoption_t *M_NextSelectableItem(emenu_t *m, menuoption_t *old, qboolean wrap)
//but we're lazy so we don't consider the next char. italic fonts are annoying like that. feel free to refudge it.
void Draw_Hexen2BigFontString(int x, int y, const char *text)
{
int c;
conchar_t *w, buffer[256];
unsigned int codeflags, oldflags=CON_WHITEMASK, c;
int sx, sy;
mpic_t *p;
p = R_RegisterShader ("gfx/menu/bigfont.lmp", SUF_2D,
@ -144,9 +145,45 @@ void Draw_Hexen2BigFontString(int x, int y, const char *text)
p->defaulttextures->base = R_LoadHiResTexture("gfx/menu/bigfont.lmp", NULL, IF_PREMULTIPLYALPHA|IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP);
}
while(*text)
COM_ParseFunString(oldflags, text, buffer, sizeof(buffer), false);
for (w = buffer; *w; )
{
c = *text++;
w = Font_Decode(w, &codeflags, &c);
if (codeflags & CON_HIDDEN)
continue;
if (c >= 0xe020 && c <= 0xe07f)
c &= 0x00ff; //convert to quake glyph to unicode/ascii...
if (codeflags != oldflags)
{
vec4_t rgba;
unsigned int col;
oldflags = codeflags;
col = (codeflags&CON_FGMASK)>>CON_FGSHIFT;
rgba[0] = consolecolours[col].fr;
rgba[1] = consolecolours[col].fg;
rgba[2] = consolecolours[col].fb;
if(codeflags & CON_HALFALPHA)
rgba[3] = 0.5;
else
rgba[3] = 1;
if (vid.flags&VID_SRGBAWARE)
{
rgba[0] = M_SRGBToLinear(rgba[0], 1);
rgba[1] = M_SRGBToLinear(rgba[1], 1);
rgba[2] = M_SRGBToLinear(rgba[2], 1);
}
if (codeflags & CON_BLINKTEXT)
{
float a = (sin(realtime*3)+1)*0.3 + 0.4;
VectorScale(rgba, a, rgba);
}
R2D_ImageColours(rgba[0], rgba[1], rgba[2], rgba[3]);
}
if (c >= 'a' && c <= 'z')
{
sx = ((c-'a')%8)*20;
@ -197,6 +234,8 @@ void Draw_Hexen2BigFontString(int x, int y, const char *text)
default: x+=20; break;
}
}
R2D_ImageColours(1, 1, 1, 1);
}
#endif
@ -248,6 +287,8 @@ void Draw_BigFontString(int x, int y, const char *text)
for (w = buffer; *w; )
{
w = Font_Decode(w, &codeflags, &codepoint);
if (codeflags & CON_HIDDEN)
continue;
if (codepoint >= 0xe020 && codepoint <= 0xe07f)
codepoint &= 0x00ff; //convert to quake glyph to unicode/ascii...
@ -323,7 +364,7 @@ int dotofs;
static void MenuTooltipChange(emenu_t *menu, const char *text)
{
unsigned int MAX_CHARS=1024;
unsigned int MAX_CHARS=2048;
menutooltip_t *mtt;
if (menu->tooltip)
{
@ -412,7 +453,7 @@ static qboolean M_MouseMoved(emenu_t *menu)
{
menu->selecteditem = option;
if (menu->cursoritem)
menu->cursoritem->common.posy = menu->selecteditem->common.posy;
menu->cursoritem->common.posy = menu->selecteditem->common.posy + (menu->selecteditem->common.height-menu->cursoritem->common.height)/2;
}
menu->tooltiptime = realtime + 1;
MenuTooltipChange(menu, menu->mouseitem->common.tooltip);
@ -765,7 +806,7 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *men
int y = ypos+option->common.posy;
if (!option->edit.slim)
y += (16-8)/2; //fat ones are twice the height on account of the text box's borders.
y += (option->common.height-8)/2; //fat ones are twice the height on account of the text box's borders.
Draw_FunStringWidth(x, y, option->edit.caption, option->edit.captionwidth, true, !menu->cursoritem && menu->selecteditem == option);
x += option->edit.captionwidth + 3*8;
@ -923,6 +964,7 @@ menutext_t *MC_AddWhiteText(emenu_t *menu, int lhs, int rhs, int y, const char *
n->common.posx = lhs;
n->common.posy = y;
n->common.width = (rhs)?rhs-lhs:0;
n->common.height = 8;
n->rightalign = rightalign;
if (text)
{
@ -1068,7 +1110,7 @@ menupicture_t *MC_AddCenterPicture(emenu_t *menu, int y, int height, char *picna
return MC_AddPicture(menu, x, y, width, height, picname);
}
menuoption_t *MC_AddCursorSmall(emenu_t *menu, menuresel_t *reselection, int x, int y)
menuoption_t *MC_AddCursorSmall(emenu_t *menu, menuresel_t *reselection, int x)
{
menuoption_t *n = Z_Malloc(sizeof(menucommon_t));
if (reselection)
@ -1076,7 +1118,11 @@ menuoption_t *MC_AddCursorSmall(emenu_t *menu, menuresel_t *reselection, int x,
n->common.type = mt_menucursor;
n->common.iszone = true;
n->common.posx = x;
n->common.posy = y;
n->common.height = 8;
if (!menu->selecteditem)
n->common.posy = -8;
else
n->common.posy = menu->selecteditem->common.posy + (menu->selecteditem->common.height-n->common.height)/2;
n->common.next = menu->options;
menu->options = (menuoption_t *)n;
@ -1090,7 +1136,7 @@ menuoption_t *MC_AddCursorSmall(emenu_t *menu, menuresel_t *reselection, int x,
if (sel->common.posx == menu->reselection->x && sel->common.posy == menu->reselection->y)
{
menu->selecteditem = sel;
n->common.posy = sel->common.posy;
n->common.posy = sel->common.posy + (sel->common.height-n->common.height)/2;
break;
}
sel = M_NextSelectableItem(menu, sel, false);
@ -1182,7 +1228,7 @@ menuedit_t *MC_AddEdit(emenu_t *menu, int cx, int ex, int y, char *text, char *d
n->common.posx = cx;
n->common.posy = y;
n->common.width = ex-cx+(17)*8;
n->common.height = n->slim?8:16;
n->common.height = 8 + (n->slim?0:(8*2)); //the 8bit artwork has 8*8 borders - only 4 pixels of that contains any actual data, but replacement images don't stick to that. so just treat them as the full +/- 8 extents here.
n->modified = true;
n->captionwidth = ex-cx;
n->caption = (char *)(n+1);
@ -2035,13 +2081,14 @@ menuoption_t *M_PrevSelectableItem(emenu_t *m, menuoption_t *old, qboolean wrap)
void M_Complex_Key(emenu_t *currentmenu, int key, int unicode)
{
menuoption_t *mi;
if (!currentmenu)
return; //erm...
M_CheckMouseMove(currentmenu);
if (currentmenu->key)
if (currentmenu->key(key, currentmenu))
if (currentmenu->key(currentmenu, key, unicode))
return;
if (currentmenu->selecteditem && currentmenu->selecteditem->common.type == mt_custom && (key == K_DOWNARROW || key == K_KP_DOWNARROW || key == K_GP_DPAD_DOWN || key == K_GP_LEFT_THUMB_DOWN || key == K_GP_DIAMOND_CONFIRM || key == K_GP_DIAMOND_ALTCONFIRM || key == K_UPARROW || key == K_KP_UPARROW || key == K_GP_DPAD_UP || key == K_GP_LEFT_THUMB_UP || key == K_TAB || key == K_MWHEELUP || key == K_MWHEELDOWN || key == K_PGUP || key == K_PGDN))
@ -2154,34 +2201,37 @@ void M_Complex_Key(emenu_t *currentmenu, int key, int unicode)
S_LocalSound ("misc/menu1.wav");
if (currentmenu->cursoritem)
currentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy;
currentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy + (currentmenu->selecteditem->common.height-currentmenu->cursoritem->common.height)/2;
}
break;
case K_MWHEELUP:
case K_MWHEELDOWN:
if (currentmenu->mouseitem)
mi = currentmenu->mouseitem;
if (!mi)
mi = currentmenu->selecteditem;
if (mi)
{
qboolean handled = false;
switch(currentmenu->mouseitem->common.type)
switch(mi->common.type)
{
case mt_combo:
if (mousecursor_x >= currentmenu->xpos + currentmenu->mouseitem->common.posx + currentmenu->mouseitem->combo.captionwidth + 3*8)
if (mousecursor_x >= currentmenu->xpos + mi->common.posx + mi->combo.captionwidth + 3*8)
{
MC_Combo_Key(&currentmenu->mouseitem->combo, key);
MC_Combo_Key(&mi->combo, key);
handled = true;
}
break;
case mt_checkbox:
if (mousecursor_x >= currentmenu->xpos + currentmenu->mouseitem->common.posx + currentmenu->mouseitem->check.textwidth + 3*8)
if (mousecursor_x >= currentmenu->xpos + mi->common.posx + mi->check.textwidth + 3*8)
{
MC_CheckBox_Key(&currentmenu->mouseitem->check, currentmenu, key);
MC_CheckBox_Key(&mi->check, currentmenu, key);
handled = true;
}
break;
case mt_custom:
if (currentmenu->mouseitem->custom.key)
handled = currentmenu->mouseitem->custom.key(&currentmenu->mouseitem->custom, currentmenu, key, unicode);
if (mi->custom.key)
handled = mi->custom.key(&mi->custom, currentmenu, key, unicode);
break;
default:
break;
@ -2189,9 +2239,9 @@ void M_Complex_Key(emenu_t *currentmenu, int key, int unicode)
if (handled)
{
currentmenu->selecteditem = currentmenu->mouseitem;
currentmenu->selecteditem = mi;
if (currentmenu->cursoritem)
currentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy;
currentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy + (currentmenu->selecteditem->common.height-currentmenu->cursoritem->common.height)/2;
break;
}
else if (key == K_MWHEELUP)
@ -2220,7 +2270,7 @@ void M_Complex_Key(emenu_t *currentmenu, int key, int unicode)
S_LocalSound ("misc/menu1.wav");
if (currentmenu->cursoritem)
currentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy;
currentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy + (currentmenu->selecteditem->common.height-currentmenu->cursoritem->common.height)/2;
break; //require a double-click when selecting...
}
//fall through
@ -2280,7 +2330,7 @@ void M_Complex_Key(emenu_t *currentmenu, int key, int unicode)
qboolean MC_Main_Key (int key, emenu_t *menu) //here purly to restart demos.
qboolean MC_Main_Key (emenu_t *menu, int key, unsigned int unicode) //here purly to restart demos.
{
if (key == K_ESCAPE || key == K_GP_BACK || key == K_GP_DIAMOND_CANCEL || key == K_MOUSE2)
{
@ -2621,13 +2671,13 @@ void M_Menu_Main_f (void)
if (b)
{
mainm->selecteditem = (menuoption_t*)b;
mainm->cursoritem->common.posy = mainm->selecteditem->common.posy;
mainm->cursoritem->common.posy = mainm->selecteditem->common.posy + (mainm->selecteditem->common.height-mainm->cursoritem->common.height)/2;
}
}
int MC_AddBulk(struct emenu_s *menu, menuresel_t *resel, menubulk_t *bulk, int xstart, int xtextend, int y)
{
int selectedy = y, last_y = y;
int last_y = y;
menuoption_t *selected = NULL;
while (bulk)
@ -2755,9 +2805,7 @@ int MC_AddBulk(struct emenu_s *menu, menuresel_t *resel, menubulk_t *bulk, int x
}
menu->selecteditem = selected;
if (selected)
selectedy = selected->common.posy;
menu->cursoritem = (menuoption_t*)MC_AddCursorSmall(menu, resel, xtextend + 8, selectedy);
menu->cursoritem = (menuoption_t*)MC_AddCursorSmall(menu, resel, xtextend + 8);
return y;
}
#endif

View File

@ -765,7 +765,7 @@ static void SL_PostDraw (emenu_t *menu)
}
}
}
static qboolean SL_Key (int key, emenu_t *menu)
static qboolean SL_Key (emenu_t *menu, int key, unsigned int unicode)
{
serverlist_t *info = (serverlist_t*)(menu + 1);
@ -1370,7 +1370,7 @@ static void M_QuickConnect_PreDraw(emenu_t *menu)
}
}
static qboolean M_QuickConnect_Key (int key, emenu_t *menu)
static qboolean M_QuickConnect_Key (emenu_t *menu, int key, unsigned int unicode)
{
return false;
}

View File

@ -1183,7 +1183,7 @@ void Com_CompleateOSFileName(char *name)
strcpy(name, compleatenamename);
}
qboolean M_Media_Key (int key, emenu_t *menu)
qboolean M_Media_Key (emenu_t *menu, int key, unsigned int unicode)
{
int dir;
if (key == K_ESCAPE || key == K_GP_BACK || key == K_MOUSE2)

View File

@ -439,6 +439,7 @@ void M_Menu_Setup_f (void)
menucustom_t *ci;
menubutton_t *b;
static menuresel_t resel;
int y;
#ifdef Q2CLIENT
if (M_GameType() == MGT_QUAKE2) //quake2 main menu.
@ -469,7 +470,7 @@ void M_Menu_Setup_f (void)
cu->draw = MSetupQ2_TransDraw;
cu->key = MSetupQ2_ChangeSkin;
menu->cursoritem = (menuoption_t*)MC_AddCursorSmall(menu, &resel, 54, 32);
menu->cursoritem = (menuoption_t*)MC_AddCursorSmall(menu, &resel, 54);
return;
}
#endif
@ -480,9 +481,10 @@ void M_Menu_Setup_f (void)
// MC_AddPicture(menu, 72, 32, Draw_CachePic ("gfx/mp_menu.lmp") );
y = 40;
menu->selecteditem = (menuoption_t*)
(info->nameedit = MC_AddEdit(menu, 64, 160, 40, "Your name", name.string));
(info->teamedit = MC_AddEdit(menu, 64, 160, 56, "Your team", team.string));
(info->nameedit = MC_AddEdit(menu, 64, 160, y, "Your name", name.string)); y+= info->nameedit->common.height;
(info->teamedit = MC_AddEdit(menu, 64, 160, y, "Your team", team.string)); y+= info->teamedit->common.height;
#ifdef HEXEN2
info->ticlass = -1;
if (M_GameType() == MGT_HEXEN2)
@ -497,7 +499,11 @@ void M_Menu_Setup_f (void)
NULL
};
cvar_t *pc = Cvar_Get("cl_playerclass", "1", CVAR_USERINFO|CVAR_ARCHIVE, "Hexen2");
(info->classedit = MC_AddCombo(menu, 64, 160, 72, "Your class", (const char **)classnames, pc->ival-1));
(info->classedit = MC_AddCombo(menu, 64, 160, y, "Your class", (const char **)classnames, pc->ival-1)); y+= info->classedit->common.height;
//trim options if the artwork is missing.
while (info->classedit->numoptions && !COM_FCheckExists(va("gfx/menu/netp%i.lmp", info->classedit->numoptions)))
info->classedit->numoptions--;
}
else
#endif
@ -505,21 +511,25 @@ void M_Menu_Setup_f (void)
MC_AddPicture(menu, 16, 4, 32, 144, "gfx/qplaque.lmp");
MC_AddCenterPicture(menu, 4, 24, "gfx/p_multi.lmp");
(info->skinedit = MC_AddEdit(menu, 64, 160, 72, "Your skin", skin.string));
(info->skinedit = MC_AddEdit(menu, 64, 160, y, "Your skin", skin.string)); y+= info->skinedit->common.height;
}
ci = MC_AddCustom(menu, 172+32, 88, NULL, 0, NULL);
ci = MC_AddCustom(menu, 172+32, y, NULL, 0, NULL);
ci->draw = MSetup_TransDraw;
ci->key = NULL;
MC_AddCommand(menu, 64, 160, 96, "Top colour", SetupMenuColour);
MC_AddCommand(menu, 64, 160, 120, "Lower colour", SetupMenuColour);
MC_AddCommand(menu, 64, 160, y+8, "Top colour", SetupMenuColour);
MC_AddCommand(menu, 64, 160, y+32, "Lower colour", SetupMenuColour);
y+= 16;
y+=4;
b = MC_AddConsoleCommand(menu, 64, 204, 168, "Network Settings", "menu_network\n");
b->common.tooltip = "Change network and client prediction settings.";
y += b->common.height;
b = MC_AddConsoleCommand(menu, 64, 204, 176, "Teamplay Settings", "menu_teamplay\n");
b->common.tooltip = "Change teamplay macro settings.";
menu->cursoritem = (menuoption_t*)MC_AddCursorSmall(menu, &resel, 54, 32);
y += b->common.height;
menu->cursoritem = (menuoption_t*)MC_AddCursorSmall(menu, &resel, 54);
info->lowercolour = bottomcolor.value;
@ -637,14 +647,19 @@ static int QDECL M_Menu_GameOptions_AddMap(const char *fname, qofs_t fsize, time
struct mapopts_s *ctx = parm;
size_t i;
char *ext;
char trimmedfname[MAX_QPATH];
if (Q_strncasecmp(fname, "maps/", 5))
return true; //o.O
fname += 5;
if (*fname == 'b' && *fname == '_')
if (fname[0] == 'b' && fname[1] == '_')
return true; //stoopid ammo boxes.
ext = strrchr(fname, '.');
if (ext && !strcmp(ext, ".bsp"))
*ext = 0;
if (ext && !strcmp(ext, ".bsp") && ext-fname<sizeof(trimmedfname))
{
memcpy(trimmedfname, fname, ext-fname);
trimmedfname[ext-fname] = 0;
fname = trimmedfname;
}
for (i = 0; i < ctx->count; i++)
if (!Q_strcasecmp(ctx->maps[i], fname))
@ -761,8 +776,21 @@ void M_Menu_GameOptions_f (void)
MC_AddCommand (menu, 64, 160, y, "Start game", MultiBeginGame);y+=16;
y+=4;
info->hostnameedit = MC_AddEdit (menu, 64, 160, y, "Hostname", name.string);y+=16;
info->hostnameedit = MC_AddEdit (menu, 64, 160, y, "Hostname", name.string);y+=info->hostnameedit->common.height;
info->publicgame = MC_AddCombo (menu, 64, 160, y, "Public", publicoptions, bound(0, sv_public.ival+1, 4));y+=8;
#if !defined(FTE_TARGET_WEB) && defined(HAVE_DTLS)
{
static const char *encoptions[] =
{
"Disabled",
"Accept",
"Request",
"Require",
NULL
};
MC_AddCvarCombo (menu, 64, 160, y, "DTLS Encryption", &net_enable_dtls, encoptions, NULL);y+=8;
}
#endif
y+=4;
for (players = 0; players < sizeof(numplayeroptions)/ sizeof(numplayeroptions[0]); players++)
@ -782,7 +810,21 @@ void M_Menu_GameOptions_f (void)
info->fraglimit = MC_AddCombo (menu, 64, 160, y, "Frag Limit", (const char **)fraglimitoptions, fraglimit.value/10);y+=8;
y+=8;
M_Menu_GameOptions_AddMap((mgt == MGT_QUAKE2)?"maps/base1":"maps/start", 0, 0, &mapopts, NULL);
//populate it with an appropriate default. its a shame it won't change with the deathmatch/coop options
switch(mgt)
{
case MGT_QUAKE2:
M_Menu_GameOptions_AddMap("maps/base1.bsp", 0, 0, &mapopts, NULL);
break;
case MGT_HEXEN2:
M_Menu_GameOptions_AddMap("maps/demo1.bsp", 0, 0, &mapopts, NULL);
break;
case MGT_QUAKE1:
M_Menu_GameOptions_AddMap("maps/start.bsp", 0, 0, &mapopts, NULL);
break;
default:
break;
}
COM_EnumerateFiles("maps/*.bsp", M_Menu_GameOptions_AddMap, &mapopts);
COM_EnumerateFiles("maps/*.bsp.gz", M_Menu_GameOptions_AddMap, &mapopts);
COM_EnumerateFiles("maps/*.bsp.xz", M_Menu_GameOptions_AddMap, &mapopts);
@ -1099,6 +1141,15 @@ void M_Menu_Network_f (void)
"Smooth Demos Only",
NULL
};
#ifdef HAVE_DTLS
static const char *dtlsopts[] = {
"Disabled",
"Accept",
"Request",
"Require",
NULL
};
#endif
static const char *smoothingvalues[] = {"0", "1", "2", NULL};
extern cvar_t cl_download_csprogs, cl_download_redirection, requiredownloads, cl_solid_players;
extern cvar_t cl_predict_players, cl_lerp_smooth, cl_predict_extrapolate;
@ -1111,8 +1162,11 @@ void M_Menu_Network_f (void)
MB_EDITCVARSLIM("Network FPS", "cl_netfps", "Sets ammount of FPS used to communicate with server (sent and received)"),
MB_EDITCVARSLIM("Rate", "rate", "Maximum bytes per second that the server should send to the client"),
MB_EDITCVARSLIM("Download Rate", "drate", "Maximum bytes per second that the server should send maps and demos to the client"),
#ifdef HAVE_DTLS
MB_COMBOCVAR("DTLS Encryption", net_enable_dtls, dtlsopts, NULL, "Use this to avoid snooping. Certificates will be pinned."),
#endif
MB_SPACING(4),
MB_CHECKBOXCVARTIP("Require Download", requiredownloads, 0, "Ignore downloaded content sent to the client and connect immediately"),
MB_CHECKBOXCVARTIP("Wait for Downloads", requiredownloads, 0, "Ignore downloaded content sent to the client and connect immediately"),
MB_CHECKBOXCVARTIP("Redirect Download", cl_download_redirection, 0, "Whether the client will ignore download redirection from servers"),
MB_CHECKBOXCVARTIP("Download CSQC", cl_download_csprogs, 0, "Whether to allow the client to download CSQC (client-side QuakeC) progs from servers"),
MB_SPACING(4),

View File

@ -421,17 +421,20 @@ void M_Menu_Options_f (void)
MC_AddFrameEnd(menu, framey);
menu->predraw = M_Options_Predraw;
o = NULL;
if (!o && !m_preset_chosen.ival)
o = M_FindButton(menu, "fps_preset\n");
#ifdef PACKAGEMANAGER
if (!o && PM_AreSourcesNew(false))
o = M_FindButton(menu, "menu_download\n");
#endif
if (o)
if (!resel.x)
{
menu->selecteditem = (menuoption_t*)o;
menu->cursoritem->common.posy = o->common.posy;
o = NULL;
if (!o && !m_preset_chosen.ival)
o = M_FindButton(menu, "fps_preset\n");
#ifdef PACKAGEMANAGER
if (!o && PM_AreSourcesNew(false))
o = M_FindButton(menu, "menu_download\n");
#endif
if (o)
{
menu->selecteditem = (menuoption_t*)o;
menu->cursoritem->common.posy = o->common.posy + (o->common.height-menu->cursoritem->common.height)/2;
}
}
}
@ -446,7 +449,7 @@ typedef struct {
soundcardinfo_t *card;
} audiomenuinfo_t;
qboolean M_Audio_Key (int key, struct emenu_s *menu)
qboolean M_Audio_Key (struct emenu_s *menu, int key, unsigned int unicode)
{
int i;
audiomenuinfo_t *info = menu->data;
@ -1078,17 +1081,18 @@ const char *presetexec[] =
struct
{
const char *name;
qboolean dorestart;
const char *desc;
const char *settings;
} builtinpresets[] =
{
{ "hdr",
{ "hdr", true,
"Don't let colour depth stop you!",
"set vid_srgb 2\n"
"set r_hdr_irisadaptation 1\n"
},
{ "shib",
{ "shib", true,
"Performance optimisations for large/detailed maps.",
"set r_temporalscenecache 1\n" //the main speedup.
@ -1096,7 +1100,7 @@ struct
"set sv_autooffload 1\n" //Needs polish still.
"set gl_pbolightmaps 1\n" //FIXME: this needs to be the default eventually.
},
{ "dm",
{ "dm", false,
"Various settings to make you more competitive."
"set cl_yieldcpu 0\n"
@ -1110,20 +1114,42 @@ struct
"set sys_clockprecision 1\n" //windows kinda sucks otherwise
#endif
},
{ "qw",
"Enable QuakeWorld-isms, for better gameplay.",
{ "qw", false,
"Enable QuakeWorld physics, for better gameplay.",
"set sv_nqplayerphysics 0\n"
"set sv_gameplayfix_multiplethinks 1\n"
"cvarreset pm_bunnyfriction\n"
"cvarreset pm_edgefriction\n"
"cvarreset pm_slidefix\n"
"cvarreset pm_slidyslopes\n"
},
{ "nq"
"Disable QuakeWorld-isms, for nq mod compat.",
{ "hybridphysics", false,
"Tweak QuakeWorld player physics to feel like nq physics, while still supporting prediction.",
"set sv_nqplayerphysics 0\n"
"set sv_gameplayfix_multiplethinks 1\n"
"set pm_bunnyfriction 1\n" //don't need bunnyspeedcap with this.
"set pm_edgefriction 2\n" //forces traceline instead of tracebox, to match nq (applies earlier, making it more aggressive)
"set pm_slidefix 1\n" //smoother running down slopes
"set pm_slidyslopes 1\n" //*sigh*
"set pm_noround 1\n" //lame
"set sv_maxtic 0\n" //fixed tick rates.
},
{ "nq", false,
"Disable QuakeWorld physics, for nq mod compat.",
"set sv_nqplayerphysics 1\n"
"set sv_gameplayfix_multiplethinks 0\n"
//*also* set these, in case they use nqplayerphysics 2 after, which should give better hints.
"set pm_bunnyfriction 1\n" //don't need bunnyspeedcap with this.
"set pm_edgefriction 2\n" //forces traceline instead of tracebox, to match nq (applies earlier, making it more aggressive)
"set pm_slidefix 1\n" //smoother running down slopes
"set pm_slidyslopes 1\n" //*sigh*
"set pm_noround 1\n" //lame
},
{ "dp",
{ "dp", false,
"Reconfigures FTE to mimic DP for compat reasons.",
"if $server then echo Be sure to restart your server\n"
@ -1153,7 +1179,7 @@ struct
// "sv_listen_dp 1\nsv_listen_nq 0\nsv_listen_qw 0\ncl_loopbackprotocol dpp7\ndpcompat_nopreparse 1\n"
},
{ "tenebrae",
{ "tenebrae", true,
"Reconfigures FTE to mimic Tenebrae for compat/style reasons.",
//for the luls. combine with the tenebrae mod for maximum effect.
"fps_preset nq\n"
@ -1169,7 +1195,7 @@ struct
"set r_nolerp 1\n" //well, that matches tenebrae. for the luls, right?
},
{ "timedemo",
{ "timedemo", false,
"Reconfigure some stuff to get through timedemos really fast. Some people might consider this cheating.",
//some extra things to pwn timedemos.
"fps_preset fast\n"
@ -1321,7 +1347,7 @@ static void M_Menu_Preset_Predraw(emenu_t *menu)
}
}
M_Menu_ApplyGravity(menu->options);
menu->cursoritem->common.posy = menu->selecteditem->common.posy; //make sure it shows the right place still
menu->cursoritem->common.posy = menu->selecteditem->common.posy + (menu->selecteditem->common.height-menu->cursoritem->common.height)/2;
if (forcereload)
Cbuf_InsertText("\nfs_restart\nvid_reload\n", RESTRICT_LOCAL, true);
@ -1404,7 +1430,7 @@ void M_Menu_Preset_f (void)
if (presetoption[item])
{
menu->selecteditem = presetoption[item];
menu->cursoritem->common.posy = menu->selecteditem->common.posy;
menu->cursoritem->common.posy = menu->selecteditem->common.posy + (menu->selecteditem->common.height-menu->cursoritem->common.height)/2;
}
//so they can actually see the preset they're picking.
@ -1457,7 +1483,7 @@ void FPS_Preset_f (void)
{
if (!stricmp(builtinpresets[i].name, arg))
{
if (doreload)
if (doreload && builtinpresets[i].dorestart)
Cbuf_InsertText("\nfs_restart\nvid_reload\n", RESTRICT_LOCAL, false);
Cbuf_InsertText(builtinpresets[i].settings, RESTRICT_LOCAL, false);
return;
@ -4419,7 +4445,8 @@ void M_Menu_Mods_f (void)
MC_AddFrameStart(menu, 32);
for (i = 0; i<1 || Mods_GetMod(i); i++)
{
c = MC_AddCustom(menu, 64, 32+i*8, menu->data, i, NULL);
struct modlist_s *mod = Mods_GetMod(i);
c = MC_AddCustom(menu, 64, 32+i*8, menu->data, i, (mod&&mod->manifest)?mod->manifest->basedir:NULL);
// if (!menu->selecteditem)
// menu->selecteditem = (menuoption_t*)c;
c->common.height = 8;

View File

@ -43,7 +43,7 @@ void M_Script_Option (emenu_t *menu, char *optionvalue, qboolean isexplicit)
}
}
Cmd_TokenizeString(buf, false, false);
Cmd_ExpandString(scriptname, buf, sizeof(buf), &expandlevel, true, true);
Cmd_ExpandString(scriptname, buf, sizeof(buf), &expandlevel, true, false, false);
//and execute it as-is
Cbuf_AddText(buf, execlevel);
@ -57,7 +57,7 @@ void M_Script_Remove (emenu_t *menu)
M_Script_Option(menu, "cancel", false);
}
qboolean M_Script_Key (int key, emenu_t *menu)
qboolean M_Script_Key (struct emenu_s *menu, int key, unsigned int unicode)
{
if (menu->selecteditem && menu->selecteditem->common.type == mt_edit)
return false;

View File

@ -1081,7 +1081,7 @@ void M_Help_Draw (emenu_t *m)
R2D_ScalePic ((vid.width-width)/2, (vid.height-height)/2, width, height, pic);
}
}
qboolean M_Help_Key (int key, emenu_t *m)
qboolean M_Help_Key (struct emenu_s *m, int key, unsigned int unicode)
{
switch (key)
{

View File

@ -329,7 +329,7 @@ struct emenu_s {
void (*reset) (struct emenu_s *); //called after a video mode switch / shader reload.
void (*remove) (struct emenu_s *);
qboolean (*key) (int key, struct emenu_s *); //true if key was handled
qboolean (*key) (struct emenu_s *, int key, unsigned int unicode); //true if key was handled
void (*predraw) (struct emenu_s *);
void (*postdraw) (struct emenu_s *);
menuoption_t *options;
@ -353,7 +353,7 @@ menupicture_t *MC_AddPicture(emenu_t *menu, int x, int y, int width, int height,
menupicture_t *MC_AddSelectablePicture(emenu_t *menu, int x, int y, int height, char *picname);
menupicture_t *MC_AddCenterPicture(emenu_t *menu, int y, int height, char *picname);
menupicture_t *MC_AddCursor(emenu_t *menu, menuresel_t *resel, int x, int y);
menuoption_t *MC_AddCursorSmall(emenu_t *menu, menuresel_t *reselection, int x, int y);
menuoption_t *MC_AddCursorSmall(emenu_t *menu, menuresel_t *reselection, int x);
menuslider_t *MC_AddSlider(emenu_t *menu, int tx, int sx, int y, const char *text, cvar_t *var, float min, float max, float delta);
menucheck_t *MC_AddCheckBox(emenu_t *menu, int tx, int cx, int y, const char *text, cvar_t *var, int cvarbitmask);
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);

View File

@ -184,6 +184,8 @@ void Mod_ParseEntities(struct model_s *mod);
void Mod_LoadMapArchive(struct model_s *mod, void *archivedata, size_t archivesize);
extern void Mod_ClearAll (void);
extern void Mod_Purge (enum mod_purge_e type);
extern void Mod_SetModifier (const char *modifier);
extern char mod_modifier[];
extern qboolean Mod_PurgeModel (struct model_s *mod, enum mod_purge_e ptype);
extern struct model_s *Mod_FindName (const char *name); //find without loading. needload should be set.
extern struct model_s *Mod_ForName (const char *name, enum mlverbosity_e verbosity); //finds+loads

View File

@ -5,6 +5,7 @@ clientside master queries and server ping/polls
#include "quakedef.h"
#include "cl_master.h"
#include "netinc.h"
#define FAVOURITESFILE "favourites.txt"
@ -351,7 +352,7 @@ static void SV_Master_SingleHeartbeat(net_masterlist_t *master)
//Note that Darkplaces clients are supposed to be able to use the qw protocol, so it should be okay to heartbeat as Darkplaces-Quake here even when not doing any nq protocols.
//either way, custom protocols tend to require ftemaster/dpmaster so we want to heartbeat regardless.
#if defined(NQPROT) && !defined(QUAKETC)
if (sv_listen_dp.value || sv_listen_nq.value || strcasecmp(com_protocolname.string, "FTE-Quake"))
// if (sv_listen_dp.value || sv_listen_nq.value || strcasecmp(com_protocolname.string, "FTE-Quake"))
#endif
{
//darkplaces here refers to the master server protocol, rather than the game protocol
@ -374,7 +375,7 @@ static void SV_Master_SingleHeartbeat(net_masterlist_t *master)
if (sv_reportheartbeats.ival != 2 || !master->announced)
{
COM_Parse(master->cv.string);
Con_TPrintf ("Sending heartbeat to %s (%s)\n", NET_AdrToString (adr, sizeof(adr), na), com_token);
Con_TPrintf (S_COLOR_GRAY"Sending heartbeat to %s (%s)\n", NET_AdrToString (adr, sizeof(adr), na), com_token);
}
master->announced = true;
}
@ -413,7 +414,7 @@ static void SV_Master_SingleHeartbeat(net_masterlist_t *master)
struct thr_res
{
qboolean success;
netadr_t na[8];
netadr_t na[MAX_MASTER_ADDRESSES];
char str[1]; //trailing
};
static void SV_Master_Worker_Resolved(void *ctx, void *data, size_t a, size_t b)
@ -496,7 +497,10 @@ static void SV_Master_Worker_Resolved(void *ctx, void *data, size_t a, size_t b)
{
//tcp masters require a route
if (NET_AddrIsReliable(na))
NET_EnsureRoute(svs.sockets, master->cv.name, master->cv.string, na);
{
struct dtlspeercred_s cred = {master->cv.string};
NET_EnsureRoute(svs.sockets, master->cv.name, &cred, na, true);
}
//q2+qw masters are given a ping to verify that they're still up
switch (master->protocol)
@ -523,6 +527,39 @@ static void SV_Master_Worker_Resolved(void *ctx, void *data, size_t a, size_t b)
}
Z_Free(work);
}
#if defined(SUPPORT_ICE)
struct stunheader_s
{
unsigned short msgtype;
unsigned short msglen;
unsigned int magiccookie;
unsigned int transactid[3];
};
static void SV_Master_Worker_Resolved_Broker(void *ctx, void *data, size_t a, size_t b)
{
struct thr_res *work = data;
if (svs.sockets && work->na[0].type != NA_INVALID) //something resolved...
{
struct stunheader_s msg = {htons(1), htons(sizeof(msg)-20), BigLong(0x2112a442), {42,42,42}};
//randomize the transaction id to avoid poisoning.
if (!Sys_RandomBytes((qbyte*)msg.transactid, sizeof(msg.transactid)))
{ //FIXME: not really random enough to avoid hacks. oh well.
msg.transactid[0] = rand();
msg.transactid[1] = rand();
msg.transactid[2] = rand();
}
svs.sockets->srflx_tid[0] = msg.transactid[0];
svs.sockets->srflx_tid[1] = msg.transactid[1];
svs.sockets->srflx_tid[2] = msg.transactid[2];
NET_SendPacket(svs.sockets, sizeof(msg), &msg, &work->na[0]);
}
Z_Free(work);
}
#endif
//worker thread
static void SV_Master_Worker_Resolve(void *ctx, void *data, size_t a, size_t b)
{
@ -536,13 +573,19 @@ static void SV_Master_Worker_Resolve(void *ctx, void *data, size_t a, size_t b)
{
str = COM_ParseOut(str, token, sizeof(token));
if (*token)
found += NET_StringToAdr2(token, 0, &work->na[found], MAX_MASTER_ADDRESSES-found, NULL);
found += NET_StringToAdr2(token, 0, &work->na[found], countof(work->na)-found, NULL);
if (first && found)
break; //if we found one by name, don't try any fallback ip addresses.
first = false;
}
work->success = !!found;
COM_AddWork(WG_MAIN, SV_Master_Worker_Resolved, NULL, work, a, b);
#if defined(SUPPORT_ICE)
if (a==~(size_t)0)
COM_AddWork(WG_MAIN, SV_Master_Worker_Resolved_Broker, NULL, work, a, b);
else
#endif
COM_AddWork(WG_MAIN, SV_Master_Worker_Resolved, NULL, work, a, b);
}
/*
@ -556,7 +599,7 @@ let it know we are alive, and log information
void SV_Master_Heartbeat (void)
{
int i;
int interval = bound(90, sv_heartbeat_interval.ival, 600);
int interval = bound(85, sv_heartbeat_interval.ival, 600);
if (sv_public.ival<=0 || SSV_IsSubServer())
return;
@ -601,6 +644,18 @@ void SV_Master_Heartbeat (void)
else
SV_Master_SingleHeartbeat(&net_masterlist[i]);
}
#if defined(SUPPORT_ICE)
if (*net_ice_broker.string)
{
const char *s = net_ice_broker.string;
struct thr_res *work = Z_Malloc(sizeof(*work) + strlen(s));
if (!strncmp(s, "tls://", 6) || !strncmp(s, "tcp://", 6))
s+=6; //ignore weird prefixes here
strcpy(work->str, s);
COM_AddWork(WG_MAIN, SV_Master_Worker_Resolve, NULL, work, ~(size_t)0, 0);
}
#endif
}
#ifdef HAVE_LEGACY
@ -2517,26 +2572,88 @@ void SListOptionChanged(serverinfo_t *newserver)
}
}
static qboolean MasterInfo_ReadProtocol(serverinfo_t *info, const char *infostring)
{
char *token = Info_ValueForKey(infostring, "protocol");
if (*token)
{
//read the protocol number
info->protocol = strtoul(token, &token, 0);
//and try to figure out which filter it should be under.
info->special &= ~SS_PROTOCOLMASK;
if (*token)
{
while (*token)
{
if (*token == 'w')
info->special |= SS_QUAKEWORLD;
else if (*token == 'n' || *token == 'd')
info->special |= SS_NETQUAKE;
else if (*token == 'x')
info->special |= SS_QEPROT;
else
continue;
break;
}
}
else switch(info->protocol)
{
case PROTOCOL_VERSION_QW: info->special |= SS_QUAKEWORLD; break;
#ifdef NQPROT
case PROTOCOL_VERSION_NQ: info->special |= SS_NETQUAKE; break;
case PROTOCOL_VERSION_H2: info->special |= SS_NETQUAKE; break; //erk
case PROTOCOL_VERSION_NEHD: info->special |= SS_NETQUAKE; break;
case PROTOCOL_VERSION_FITZ: info->special |= SS_NETQUAKE; break;
case PROTOCOL_VERSION_RMQ: info->special |= SS_NETQUAKE; break;
case PROTOCOL_VERSION_DP5: info->special |= SS_NETQUAKE; break; //dp actually says 3... but hey, that's dp being WEIRD.
case PROTOCOL_VERSION_DP6: info->special |= SS_NETQUAKE; break;
case PROTOCOL_VERSION_DP7: info->special |= SS_NETQUAKE; break;
case NQ_NETCHAN_VERSION_QEX:info->special |= SS_QEPROT; break;
case NQ_NETCHAN_VERSION:
#endif
default:
if ((info->special&SS_PROTOCOLMASK) == SS_UNKNOWN)
{ //guesses...
if (PROTOCOL_VERSION_Q2 >= info->protocol && info->protocol >= PROTOCOL_VERSION_Q2_MIN)
info->special |= SS_QUAKE2; //q2 has a range!
else if (info->protocol > 60)
info->special |= SS_QUAKE3;
else if (!strcmp(Info_ValueForKey(infostring, "gamename"), "DarkPlaces-Quake") || *Info_ValueForKey(infostring, "nqprotocol"))
info->special |= SS_NETQUAKE;
else
info->special |= SS_QUAKEWORLD;
}
break;
}
return true;
}
info->protocol = 0;
return false;
}
#ifdef WEBCLIENT
static void MasterInfo_ProcessHTTPInfo(serverinfo_t *srv, const char *info)
{
char adrbuf[MAX_ADR_SIZE];
if (info && (!(srv->status & SRVSTATUS_ALIVE) || srv->ping == PING_UNKNOWN))
{
if (srv->adr.prot == NP_RTC_TLS || srv->adr.prot == NP_RTC_TCP)
if (srv->adr.prot != NP_DGRAM)
{
srv->sends = 0; //no point pinging it, it won't work.
srv->ping = PING_UNKNOWN;
srv->status |= SRVSTATUS_ALIVE; //or at least wouldn't have been reported this time around.
}
else
srv->sends = 1; //no point pinging it, it won't work.
Q_strncpyz(srv->name, Info_ValueForKey(info, "hostname"), sizeof(srv->name));
Q_strncpyz(srv->gamedir, Info_ValueForKey(info, "modname"), sizeof(srv->gamedir));
Q_strncpyz(srv->map, Info_ValueForKey(info, "mapname"), sizeof(srv->map));
srv->players = atoi(Info_ValueForKey(info, "clients"));
srv->maxplayers = atoi(Info_ValueForKey(info, "maxclients"));
if (!MasterInfo_ReadProtocol(srv, info))
srv->special = (srv->special&~SS_PROTOCOLMASK)|SS_QUAKEWORLD; //assume its an older fteqw server.
srv->numbots = 0;
srv->numhumans = srv->players - srv->numbots;
srv->freeslots = srv->maxplayers - srv->players;
@ -2560,7 +2677,7 @@ static void MasterInfo_ProcessHTTP(struct dl_download *dl)
char *el;
serverinfo_t *info;
char linebuffer[2048];
char *brokerid;
const char *brokerid;
char *infostring;
netadr_t brokeradr;
@ -2607,12 +2724,19 @@ static void MasterInfo_ProcessHTTP(struct dl_download *dl)
if (*s == '#') //hash is a comment, apparently.
continue;
for (infostring = s; *infostring && *infostring != ' '; )
for (infostring = s; *infostring && *infostring != ' ' && *infostring != '\t'; )
infostring++;
if (*infostring == ' ')
*infostring++ = 0;
if (*infostring == ' ' || *infostring == '\t')
{
*infostring++ = 0; //null terminate the address
while(*infostring == ' ' || *infostring == '\t')
infostring++; //skip over any whitespace...
if (*infostring != '\\')
infostring = NULL; //err... no. not an info string. probably a comment.
}
else
infostring = NULL;
if (!strncmp(s, "ice:///", 7) || !strncmp(s, "ices:///", 8) || !strncmp(s, "rtc:///", 7) || !strncmp(s, "rtcs:///", 8))
{
brokerid = s+((s[4]==':')?7:6);
@ -2620,10 +2744,14 @@ static void MasterInfo_ProcessHTTP(struct dl_download *dl)
if (!*brokerid)
continue; //invalid...
}
else if (*s == '/')
{
brokerid = s;
adr = brokeradr;
}
else
{
brokerid = "";
if (!NET_StringToAdr(s, 80, &adr))
if (!NET_StringToAdr2(s, 80, &adr, 1, &brokerid))
continue;
}
@ -2644,8 +2772,8 @@ static void MasterInfo_ProcessHTTP(struct dl_download *dl)
info->special = 0;
if (protocoltype == MP_QUAKEWORLD)
info->special |= SS_QUAKEWORLD;
else if (protocoltype == MP_DPMASTER)
info->special |= SS_GETINFO;
else if (protocoltype == MP_DPMASTER) //actually ftemaster... so assume fteqw servers not ftenq ones unless otherwise indicated.
info->special |= SS_QUAKEWORLD|SS_GETINFO;
#if defined(Q2CLIENT) || defined(Q2SERVER)
else if (protocoltype == MP_QUAKE2)
info->special |= SS_QUAKE2;
@ -2885,18 +3013,18 @@ void MasterInfo_Refresh(qboolean doreset)
Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_Q3SERVER), MT_BCAST, MP_QUAKE3, "Nearby Quake3 UDP servers.");
#endif
if (!fs_manifest->rtcbroker || !*fs_manifest->rtcbroker)
if (!*net_ice_broker.string)
; //nope, sorry, not configured.
else
{
char *url;
COM_Parse(com_protocolname.string);
if (!strncmp(fs_manifest->rtcbroker, "tls://", 6))
url = va("https://%s/raw/%s", fs_manifest->rtcbroker+6, com_token);
else if (!strncmp(fs_manifest->rtcbroker, "tcp://", 6))
url = va("http://%s/raw/%s", fs_manifest->rtcbroker+6, com_token);
if (!strncmp(net_ice_broker.string, "tls://", 6))
url = va("https://%s/raw/%s", net_ice_broker.string+6, com_token);
else if (!strncmp(net_ice_broker.string, "tcp://", 6))
url = va("http://%s/raw/%s", net_ice_broker.string+6, com_token);
else
url = va("http://%s/raw/%s", fs_manifest->rtcbroker, com_token);
url = va("http://%s/raw/%s", net_ice_broker.string, com_token);
Master_AddMasterHTTP(url, MT_MASTERHTTP, MP_DPMASTER, "Public Servers Potentially Behind A NAT.");
}
@ -3245,65 +3373,23 @@ static int CL_ReadServerInfo(char *msg, enum masterprotocol_e prototype, qboolea
else if (!strncmp(DISTRIBUTION, Info_ValueForKey(msg, "*version"), strlen(DISTRIBUTION)))
info->special |= SS_FTESERVER;
info->protocol = strtoul(Info_ValueForKey(msg, "protocol"), &token, 0);
if (info->protocol)
{
switch(info->protocol)
{
case PROTOCOL_VERSION_QW: info->special |= SS_QUAKEWORLD; break;
#ifdef NQPROT
case PROTOCOL_VERSION_NQ: info->special |= SS_NETQUAKE; break;
case PROTOCOL_VERSION_H2: info->special |= SS_NETQUAKE; break; //erk
case PROTOCOL_VERSION_NEHD: info->special |= SS_NETQUAKE; break;
case PROTOCOL_VERSION_FITZ: info->special |= SS_NETQUAKE; break;
case PROTOCOL_VERSION_RMQ: info->special |= SS_NETQUAKE; break;
case PROTOCOL_VERSION_DP5: info->special |= SS_NETQUAKE; break; //dp actually says 3... but hey, that's dp being WEIRD.
case PROTOCOL_VERSION_DP6: info->special |= SS_NETQUAKE; break;
case PROTOCOL_VERSION_DP7: info->special |= SS_NETQUAKE; break;
case NQ_NETCHAN_VERSION_QEX:info->special |= SS_QEPROT; break;
case NQ_NETCHAN_VERSION:
#endif
default:
while (*token)
{
if (*token == 'w')
info->special |= SS_QUAKEWORLD;
else if (*token == 'n' || *token == 'd')
info->special |= SS_NETQUAKE;
else if (*token == 'x')
info->special |= SS_QEPROT;
else
continue;
break;
}
if ((info->special&SS_PROTOCOLMASK) == SS_UNKNOWN)
{ //guesses...
if (PROTOCOL_VERSION_Q2 >= info->protocol && info->protocol >= PROTOCOL_VERSION_Q2_MIN)
info->special |= SS_QUAKE2; //q2 has a range!
else if (info->protocol > 60)
info->special |= SS_QUAKE3;
else if (!strcmp(Info_ValueForKey(msg, "gamename"), "DarkPlaces-Quake") || *Info_ValueForKey(msg, "nqprotocol"))
info->special |= SS_NETQUAKE;
else
info->special |= SS_QUAKEWORLD;
}
break;
}
}
if (!MasterInfo_ReadProtocol(info, msg))
{ //try and guess.
#ifdef Q2CLIENT
else if (prototype == MP_QUAKE2)
info->special |= SS_QUAKE2;
if (prototype == MP_QUAKE2)
info->special |= SS_QUAKE2;
#endif
#ifdef Q3CLIENT
else if (prototype == MP_QUAKE3 || prototype == MP_DPMASTER/*if no protocol, assume q3 behaviours*/)
info->special |= SS_QUAKE3;
else if (prototype == MP_QUAKE3 || prototype == MP_DPMASTER/*if no protocol, assume q3 behaviours*/)
info->special |= SS_QUAKE3;
#endif
#ifdef NQPROT
else if (prototype == MP_NETQUAKE)
info->special |= SS_NETQUAKE;
else if (prototype == MP_NETQUAKE)
info->special |= SS_NETQUAKE;
#endif
else
info->special |= SS_QUAKEWORLD;
else
info->special |= SS_QUAKEWORLD;
}
if (favorite) //was specifically named, not retrieved from a master.
info->special |= SS_FAVORITE;
@ -3782,16 +3868,19 @@ static void NetQ3_GlobalServers_Request(size_t masternum, int protocol, const ch
const char *url;
struct dl_download *dl;
COM_Parse(com_protocolname.string);
if (!strncmp(fs_manifest->rtcbroker, "tls://", 6))
url = va("https://%s/raw/%s", fs_manifest->rtcbroker+6, com_token);
else if (!strncmp(fs_manifest->rtcbroker, "tcp://", 6))
url = va("http://%s/raw/%s", fs_manifest->rtcbroker+6, com_token);
else
url = va("http://%s/raw/%s", fs_manifest->rtcbroker, com_token);
if (*net_ice_broker.string)
{
if (!strncmp(net_ice_broker.string, "tls://", 6))
url = va("https://%s/raw/%s", net_ice_broker.string+6, com_token);
else if (!strncmp(net_ice_broker.string, "tcp://", 6))
url = va("http://%s/raw/%s", net_ice_broker.string+6, com_token);
else
url = va("http://%s/raw/%s", net_ice_broker.string, com_token);
dl = HTTP_CL_Get(url, NULL, MasterInfo_ProcessHTTP);
if (dl)
dl->isquery = true;
dl = HTTP_CL_Get(url, NULL, MasterInfo_ProcessHTTP);
if (dl)
dl->isquery = true;
}
}
#endif
#if POLLTOTALSOCKETS>0

View File

@ -793,7 +793,7 @@ static model_t *CSQC_GetModelForIndex(int index)
else if (index < 0 && index > -MAX_CSMODELS)
{
if (!cl.model_csqcprecache[-index])
cl.model_csqcprecache[-index] = Mod_ForName(Mod_FixName(cl.model_csqcname[-index], csqc_world.worldmodel->publicname), MLV_WARN);
cl.model_csqcprecache[-index] = Mod_ForName(Mod_FixName(cl.model_csqcname[-index], csqc_world.worldmodel?csqc_world.worldmodel->publicname:NULL), MLV_WARN);
return cl.model_csqcprecache[-index];
}
else
@ -2770,7 +2770,8 @@ static void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars
{
csqc_worldchanged = false;
cl.worldmodel = r_worldentity.model = csqc_world.worldmodel;
FS_LoadMapPackFile(cl.worldmodel->name, cl.worldmodel->archive);
if (cl.worldmodel)
FS_LoadMapPackFile(cl.worldmodel->name, cl.worldmodel->archive);
Surf_NewMap(csqc_world.worldmodel);
CL_UpdateWindowTitle();
@ -7208,7 +7209,7 @@ static struct {
{"patch_getcp", PF_patch_getcp, 0},
{"patch_getmesh", PF_patch_getmesh, 0},
{"patch_create", PF_patch_create, 0},
// {"patch_calculate", PF_patch_calculate, 0},
{"patch_evaluate", PF_patch_evaluate, 0},
#endif
#ifdef ENGINE_ROUTING
@ -7997,7 +7998,7 @@ static qboolean CSQC_ValidateMainCSProgs(void *file, size_t filesize, unsigned i
}
else
{ //FTE uses folded-md4. yeah, its broken but at least its still more awkward
if (LittleLong(Com_BlockChecksum(file, filesize)) != checksum)
if (LittleLong(CalcHashInt(&hash_md4, file, filesize)) != checksum)
return false;
}
return true;
@ -8309,7 +8310,11 @@ qboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int chec
}
if (cl_nocsqc.value)
{
if (checksum || progssize)
Con_Printf(CON_WARNING"Server is using csqc, but its disabled via %s\n", cl_nocsqc.name);
return false;
}
if (cls.state == ca_disconnected)
{
@ -8324,6 +8329,7 @@ qboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int chec
movevars.stepdown = true;
movevars.walljump = false;//(pm_walljump.value);
movevars.slidyslopes = false;//(pm_slidyslopes.value!=0);
movevars.bunnyfriction = false;
movevars.autobunny = false; //pm_autobunny.value!=0
movevars.watersinkspeed = 60;//*pm_watersinkspeed.string?pm_watersinkspeed.value:60;
movevars.flyfriction = 4;//*pm_flyfriction.string?pm_flyfriction.value:4;
@ -8428,9 +8434,13 @@ qboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int chec
csprogsnum = PR_LoadProgs(csqcprogs, csprogs_checkname);
if (csprogsnum >= 0)
Con_DPrintf("Loaded csprogs.dat\n");
else if (csprogs_checksum || csprogs_checksize)
Con_Printf(CON_WARNING"Unable to load \"csprogsvers/%x.dat\"\n", csprogs_checksum);
}
if (csqc_singlecheats || anycsqc)
if (csprogsnum >= 0 && !Q_strcasecmp(csprogs_checkname, "csaddon.dat"))
; //using csaddon directly... map editor mode?
else if (csqc_singlecheats || anycsqc)
{
csaddonnum = PR_LoadProgs(csqcprogs, "csaddon.dat");
if (csaddonnum >= 0)
@ -8551,7 +8561,7 @@ qboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int chec
csqc_world.physicstime = 0.1;
CSQC_RendererRestarted();
CSQC_RendererRestarted(true);
if (cls.state == ca_disconnected)
CSQC_WorldLoaded();
@ -8560,17 +8570,36 @@ qboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int chec
return true; //success!
}
void CSQC_RendererRestarted(void)
void CSQC_RendererRestarted(qboolean initing)
{
int i;
if (!csqcprogs)
return;
csqc_world.worldmodel = cl.worldmodel;
for (i = 0; i < MAX_CSMODELS; i++)
if (initing)
{
cl.model_csqcprecache[i] = NULL;
//called at startup
if (csqc_worldchanged)
{
csqc_worldchanged = false;
cl.worldmodel = r_worldentity.model = csqc_world.worldmodel;
if (cl.worldmodel)
FS_LoadMapPackFile(cl.worldmodel->name, cl.worldmodel->archive);
Surf_NewMap(csqc_world.worldmodel);
CL_UpdateWindowTitle();
World_RBE_Shutdown(&csqc_world);
World_RBE_Start(&csqc_world);
}
}
else
{ //FIXME: this might be awkward in the purecsqc case.
csqc_world.worldmodel = cl.worldmodel;
for (i = 0; i < MAX_CSMODELS; i++)
{
cl.model_csqcprecache[i] = NULL;
}
}
//FIXME: registered shaders
@ -9704,13 +9733,29 @@ int CSQC_StartSound(int entnum, int channel, char *soundname, vec3_t pos, float
return false;
}
void CSQC_GetEntityOrigin(unsigned int csqcent, float *out)
qboolean CSQC_GetEntityOrigin(unsigned int csqcent, float *out)
{
wedict_t *ent;
if (!csqcprogs)
return;
return false;
ent = WEDICT_NUM_UB(csqcprogs, csqcent);
VectorCopy(ent->v->origin, out);
return true;
}
qboolean CSQC_GetSSQCEntityOrigin(unsigned int ssqcent, float *out)
{
csqcedict_t *ent;
if (csqcprogs && ssqcent < maxcsqcentities)
{
ent = csqcent[ssqcent];
if (ent)
{
if (out)
VectorCopy(ent->v->origin, out);
return true;
}
}
return false;
}
void CSQC_ParseEntities(qboolean sized)
@ -9723,7 +9768,23 @@ void CSQC_ParseEntities(qboolean sized)
qboolean removeflag;
if (!csqcprogs)
Host_EndGame("CSQC needs to be initialized for this server.\n");
{
const char *fname = va("csprogsvers/%x.dat", (unsigned int)strtoul(InfoBuf_ValueForKey(&cl.serverinfo, "*csprogs"), NULL, 0));
const char *msg;
if (cl_nocsqc.value)
msg = "blocked by cl_nocsqc.\n";
else if (!COM_FCheckExists(fname))
{
extern cvar_t cl_downloads, cl_download_csprogs;
if (!cl_downloads.ival || !cl_download_csprogs.ival)
msg = "downloading blocked by cl_downloads or cl_download_csprogs.\n";
else
msg = "unable to download.\n";
}
else
msg = "not initialised.\n";
Host_EndGame("%s required, but %s", fname, msg);
}
if (!csqcg.CSQC_Ent_Update || !csqcg.self)
Host_EndGame("CSQC has no CSQC_Ent_Update function\n");
@ -9782,7 +9843,9 @@ void CSQC_ParseEntities(qboolean sized)
CSQC_EntityCheck(entnum);
if (cl_csqcdebug.ival)
if (cl_shownet.ival == 3)
Con_Printf("%3i: Remove %i\n", MSG_GetReadCount(), entnum);
else if (cl_csqcdebug.ival)
Con_Printf("Remove %i\n", entnum);
ent = csqcent[entnum];
@ -9828,15 +9891,26 @@ void CSQC_ParseEntities(qboolean sized)
G_FLOAT(OFS_PARM0) = true;
if (cl_csqcdebug.ival)
if (cl_shownet.ival == 3)
Con_Printf("%3i: Added %i (%i)\n", MSG_GetReadCount(), entnum, packetsize);
else if (cl_csqcdebug.ival)
Con_Printf("Add %i\n", entnum);
}
else
{
G_FLOAT(OFS_PARM0) = false;
if (cl_csqcdebug.ival)
if (cl_shownet.ival == 3)
Con_Printf("%3i: Update %i (%i)\n", MSG_GetReadCount(), entnum, packetsize);
else if (cl_csqcdebug.ival)
Con_Printf("Update %i\n", entnum);
}
#ifdef QUAKESTATS
if (entnum-1 < cl.allocated_client_slots && cls.findtrack && cl.players[entnum-1].stats[STAT_HEALTH] > 0)
{ //FIXME: is this still needed with the autotrack stuff?
Cam_Lock(&cl.playerview[0], entnum-1);
cls.findtrack = false;
}
#endif
*csqcg.self = EDICT_TO_PROG(csqcprogs, (void*)ent);
csqc_mayread = true;

View File

@ -105,6 +105,7 @@ struct {
char facename[MAX_OSPATH];
float scale; //poop
int outline; //argh
unsigned int fontflags; //erk
int sizes;
int size[FONT_SIZES];
struct font_s *font[FONT_SIZES];
@ -233,7 +234,7 @@ void PR_ReloadFonts(qboolean reload)
{ //otherwise load it.
for (j = 0; j < fontslot[i].sizes; j++)
{
fontslot[i].font[j] = Font_LoadFont(fontslot[i].facename, fontslot[i].size[j], fontslot[i].scale, fontslot[i].outline);
fontslot[i].font[j] = Font_LoadFont(fontslot[i].facename, fontslot[i].size[j], fontslot[i].scale, fontslot[i].outline, fontslot[i].fontflags);
}
}
}
@ -333,7 +334,7 @@ void QCBUILTIN PF_CL_loadfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
if (qrenderer > QR_NONE)
{
for (i = 0; i < fontslot[slotnum].sizes; i++)
fontslot[slotnum].font[i] = Font_LoadFont(facename, fontslot[slotnum].size[i], fontslot[slotnum].scale, fontslot[slotnum].outline);
fontslot[slotnum].font[i] = Font_LoadFont(facename, fontslot[slotnum].size[i], fontslot[slotnum].scale, fontslot[slotnum].outline, fontslot[slotnum].fontflags);
}
G_FLOAT(OFS_RETURN) = slotnum;
@ -342,7 +343,7 @@ void QCBUILTIN PF_CL_loadfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
#ifdef HAVE_LEGACY
void CL_LoadFont_f(void)
{
extern cvar_t r_font_postprocess_outline;
extern cvar_t r_font_postprocess_outline, r_font_postprocess_mono;
//console command for compat with dp/debug.
if (Cmd_Argc() == 1)
{
@ -418,6 +419,8 @@ void CL_LoadFont_f(void)
fontslot[slotnum].scale = 1;
fontslot[slotnum].sizes = 0;
fontslot[slotnum].outline = r_font_postprocess_outline.ival; //locked in at definition, so different fonts can have different settings even with vid_reload going on.
fontslot[slotnum].fontflags = 0 |
(r_font_postprocess_mono.ival?FONT_MONO:0);
}
if (!*facename)
return;
@ -437,6 +440,14 @@ void CL_LoadFont_f(void)
fontslot[slotnum].outline = atoi(Cmd_Argv(sizenum++));
continue;
}
if (!strcmp(a, "mono"))
{
if (atoi(Cmd_Argv(sizenum++)))
fontslot[slotnum].fontflags |= FONT_MONO;
else
fontslot[slotnum].fontflags &= ~FONT_MONO;
continue;
}
if (!strcmp(a, "blur"))
{
//fontslot[slotnum].blur = atoi(Cmd_Argv(sizenum++));
@ -471,7 +482,7 @@ void CL_LoadFont_f(void)
if (qrenderer > QR_NONE)
{
for (i = 0; i < fontslot[slotnum].sizes; i++)
fontslot[slotnum].font[i] = Font_LoadFont(facename, fontslot[slotnum].size[i], fontslot[slotnum].scale, fontslot[slotnum].outline);
fontslot[slotnum].font[i] = Font_LoadFont(facename, fontslot[slotnum].size[i], fontslot[slotnum].scale, fontslot[slotnum].outline, fontslot[slotnum].fontflags);
}
//FIXME: slotnum0==default is problematic.

View File

@ -56,7 +56,7 @@ struct
extern cvar_t scr_conalpha;
extern cvar_t gl_conback;
extern cvar_t gl_font, con_textfont;
extern cvar_t r_font_postprocess_outline;
extern cvar_t r_font_postprocess_outline, r_font_postprocess_mono;
extern cvar_t gl_screenangle;
extern cvar_t vid_minsize;
extern cvar_t vid_conautoscale;
@ -167,6 +167,7 @@ void R2D_Shutdown(void)
Cvar_Unhook(&con_textfont);
Cvar_Unhook(&gl_font);
Cvar_Unhook(&r_font_postprocess_outline);
Cvar_Unhook(&r_font_postprocess_mono);
Cvar_Unhook(&vid_conautoscale);
Cvar_Unhook(&gl_screenangle);
Cvar_Unhook(&vid_conheight);
@ -444,6 +445,7 @@ void R2D_Init(void)
Cvar_Hook(&con_textfont, R2D_Font_Callback);
Cvar_Hook(&gl_font, R2D_Font_Callback);
Cvar_Hook(&r_font_postprocess_outline, R2D_Font_Callback);
Cvar_Hook(&r_font_postprocess_mono, R2D_Font_Callback);
Cvar_Hook(&vid_conautoscale, R2D_Conautoscale_Callback);
Cvar_Hook(&gl_screenangle, R2D_ScreenAngle_Callback);
Cvar_Hook(&vid_conheight, R2D_Conheight_Callback);
@ -1137,6 +1139,7 @@ void R2D_Font_Changed(void)
{
float tsize;
const char *con_font_name = con_textfont.string;
unsigned int flags;
if (!con_textsize.modified)
return;
if (!*con_font_name)
@ -1170,6 +1173,10 @@ void R2D_Font_Changed(void)
if (qrenderer == QR_NONE)
return;
flags = 0;
if (r_font_postprocess_mono.ival)
flags |= FONT_MONO;
if (!strcmp(gl_font.string, "?"))
{
#ifndef AVAIL_FREETYPE
@ -1184,9 +1191,9 @@ void R2D_Font_Changed(void)
LOGFONTW lf = {0};
CHOOSEFONTW cf = {sizeof(cf)};
extern HWND mainwindow;
font_default = Font_LoadFont("", 8, 1, r_font_postprocess_outline.ival);
font_default = Font_LoadFont("", 8, 1, r_font_postprocess_outline.ival, flags);
if (tsize != 8)
font_console = Font_LoadFont("", tsize, 1, r_font_postprocess_outline.ival);
font_console = Font_LoadFont("", tsize, 1, r_font_postprocess_outline.ival, flags);
if (!font_console)
font_console = font_default;
@ -1230,19 +1237,19 @@ void R2D_Font_Changed(void)
}
if (COM_FCheckExists("fonts/qfont.kfont"))
font_menu = Font_LoadFont("qfont", 20, 1, r_font_postprocess_outline.ival);
font_menu = Font_LoadFont("qfont", 20, 1, r_font_postprocess_outline.ival, flags);
else
font_menu = NULL;
font_default = Font_LoadFont(gl_font.string, 8, 1, r_font_postprocess_outline.ival);
font_default = Font_LoadFont(gl_font.string, 8, 1, r_font_postprocess_outline.ival, flags);
if (!font_default && *gl_font.string)
font_default = Font_LoadFont("", 8, 1, r_font_postprocess_outline.ival);
font_default = Font_LoadFont("", 8, 1, r_font_postprocess_outline.ival, flags);
if (tsize != 8 || strcmp(gl_font.string, con_font_name))
{
font_console = Font_LoadFont(con_font_name, tsize, 1, r_font_postprocess_outline.ival);
font_console = Font_LoadFont(con_font_name, tsize, 1, r_font_postprocess_outline.ival, flags);
if (!font_console)
font_console = Font_LoadFont("", tsize, 1, r_font_postprocess_outline.ival);
font_console = Font_LoadFont("", tsize, 1, r_font_postprocess_outline.ival, flags);
}
if (!font_console)
font_console = font_default;

View File

@ -4098,7 +4098,10 @@ TRACE(("dbg: Surf_NewMap: tp\n"));
void Surf_PreNewMap(void)
{
extern cvar_t gl_specular;
r_loadbumpmapping = r_deluxemapping || r_glsl_offsetmapping.ival;
r_loadbumpmapping |= gl_specular.value>0;
#ifdef RTLIGHTS
r_loadbumpmapping |= r_shadow_realtime_world.ival || r_shadow_realtime_dlight.ival;
#endif

View File

@ -753,6 +753,7 @@ enum {
RSPEED_PALETTEFLASHES,
RSPEED_2D,
RSPEED_SERVER,
RSPEED_AUDIO,
RSPEED_SETUP,
RSPEED_SUBMIT,
RSPEED_PRESENT,

View File

@ -117,6 +117,7 @@ cvar_t gl_shadeq1_name = CVARD ("gl_shadeq1_name", "*", "Rename all surfac
extern cvar_t r_vertexlight;
extern cvar_t r_forceprogramify;
extern cvar_t r_glsl_precache;
extern cvar_t r_halfrate;
extern cvar_t dpcompat_nopremulpics;
#ifdef PSKMODELS
cvar_t dpcompat_psa_ungroup = CVAR ("dpcompat_psa_ungroup", "0");
@ -375,7 +376,7 @@ cvar_t r_tessellation = CVARAFD ("r_tessellation", "0", "gl_ati_truform",
cvar_t gl_ati_truform_type = CVAR ("gl_ati_truform_type", "1");
cvar_t r_tessellation_level = CVAR ("r_tessellation_level", "5");
cvar_t gl_blend2d = CVAR ("gl_blend2d", "1");
cvar_t gl_blendsprites = CVARD ("gl_blendsprites", "0", "Specifies how sprites are blended.\n0: Alpha tested.\n1: Premultiplied blend.\n2: Additive blend.");
cvar_t gl_blendsprites = CVARFD ("gl_blendsprites", "0", CVAR_SHADERSYSTEM, "Specifies how sprites are blended.\n0: Alpha tested.\n1: Premultiplied blend.\n2: Additive blend.");
cvar_t r_deluxemapping_cvar = CVARAFD ("r_deluxemapping", "1", "r_glsl_deluxemapping",
CVAR_ARCHIVE|CVAR_RENDERERLATCH, "Enables bumpmapping based upon precomputed light directions.\n0=off\n1=use if available\n2=auto-generate (if possible)");
cvar_t mod_loadsurfenvmaps = CVARD ("r_loadsurfenvmaps", "1", "Load local reflection environment-maps, where available. These are normally defined via env_cubemap entities dotted around the place.");
@ -457,6 +458,7 @@ cvar_t gl_texturemode2d = CVARFCD("gl_texturemode2d", "GL_LINEAR",
"Specifies how 2d images are sampled. format is a 3-tupple ");
cvar_t r_font_linear = CVARF("r_font_linear", "1", CVAR_ARCHIVE);
cvar_t r_font_postprocess_outline = CVARFD("r_font_postprocess_outline", "0", 0, "Controls the number of pixels of dark borders to use around fonts.");
cvar_t r_font_postprocess_mono = CVARFD("r_font_postprocess_mono", "0", 0, "Disables anti-aliasing on fonts.");
#if defined(HAVE_LEGACY) && defined(AVAIL_FREETYPE)
cvar_t dpcompat_smallerfonts = CVARFD("dpcompat_smallerfonts", "0", 0, "Mimics DP's behaviour of using a smaller font size than was actually requested.");
@ -983,6 +985,7 @@ void Renderer_Init(void)
Cvar_Register (&gl_texturemode2d, GLRENDEREROPTIONS);
Cvar_Register (&r_font_linear, GLRENDEREROPTIONS);
Cvar_Register (&r_font_postprocess_outline, GLRENDEREROPTIONS);
Cvar_Register (&r_font_postprocess_mono, GLRENDEREROPTIONS);
#if defined(HAVE_LEGACY) && defined(AVAIL_FREETYPE)
Cvar_Register (&dpcompat_smallerfonts, GLRENDEREROPTIONS);
#endif
@ -1020,6 +1023,7 @@ void Renderer_Init(void)
Cvar_Register (&r_forceprogramify, GLRENDEREROPTIONS);
Cvar_Register (&r_glsl_precache, GLRENDEREROPTIONS);
Cvar_Register (&r_halfrate, GRAPHICALNICETIES);
#ifdef HAVE_LEGACY
Cvar_Register (&dpcompat_nopremulpics, GLRENDEREROPTIONS);
#endif
@ -1807,6 +1811,8 @@ TRACE(("dbg: R_ApplyRenderer: starting on client state\n"));
#endif
if (cl.worldmodel)
{
int wmidx = 0;
model_t *oldwm = cl.worldmodel;
cl.worldmodel = NULL;
CL_ClearEntityLists(); //shouldn't really be needed, but we're paranoid
@ -1819,6 +1825,9 @@ TRACE(("dbg: R_ApplyRenderer: reloading ALL models\n"));
TRACE(("dbg: R_ApplyRenderer: reloading model %s\n", cl.model_name[i]));
if (oldwm == cl.model_precache[i])
wmidx = i;
#ifdef Q2CLIENT //skip vweps
if (cls.protocol == CP_QUAKE2 && *cl.model_name[i] == '#')
cl.model_precache[i] = NULL;
@ -1846,14 +1855,19 @@ TRACE(("dbg: R_ApplyRenderer: reloading ALL models\n"));
if (!cl.model_csqcname[i][0])
break;
if (oldwm == cl.model_csqcprecache[i])
wmidx = -i;
cl.model_csqcprecache[i] = NULL;
TRACE(("dbg: R_ApplyRenderer: reloading csqc model %s\n", cl.model_csqcname[i]));
cl.model_csqcprecache[i] = Mod_ForName (Mod_FixName(cl.model_csqcname[i], cl.model_name[1]), MLV_SILENT);
}
#endif
//fixme: worldmodel could be ssqc or csqc.
cl.worldmodel = cl.model_precache[1];
if (wmidx < 0)
cl.worldmodel = cl.model_csqcprecache[-wmidx];
else
#endif
cl.worldmodel = cl.model_precache[wmidx];
if (cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADING)
COM_WorkerPartialSync(cl.worldmodel, &cl.worldmodel->loadstate, MLS_LOADING);
@ -1861,14 +1875,14 @@ TRACE(("dbg: R_ApplyRenderer: reloading ALL models\n"));
TRACE(("dbg: R_ApplyRenderer: done the models\n"));
if (!cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)
{
// Con_Printf ("\nThe required model file '%s' could not be found.\n\n", cl.model_name[i]);
// Con_Printf ("You may need to download or purchase a client pack in order to play on this server.\n\n");
// Con_Printf ("\nThe required model file '%s' could not be found.\n\n", cl.model_name[i]);
// Con_Printf ("You may need to download or purchase a client pack in order to play on this server.\n\n");
CL_Disconnect ("Worldmodel missing after video reload");
CL_Disconnect ("Worldmodel missing after video reload");
if (newr)
memcpy(&currentrendererstate, newr, sizeof(currentrendererstate));
return true;
if (newr)
memcpy(&currentrendererstate, newr, sizeof(currentrendererstate));
return true;
}
TRACE(("dbg: R_ApplyRenderer: checking any wad textures\n"));
@ -1891,7 +1905,7 @@ TRACE(("dbg: R_ApplyRenderer: efrags\n"));
#endif
#ifdef CSQC_DAT
Shader_DoReload();
CSQC_RendererRestarted();
CSQC_RendererRestarted(false);
#endif
#ifdef MENU_DAT
MP_RendererRestarted();

View File

@ -29,20 +29,22 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
extern cvar_t *hud_tracking_show;
extern cvar_t *hud_miniscores_show;
cvar_t scr_scoreboard_drawtitle = CVARD("scr_scoreboard_drawtitle", "1", "Wastes screen space when looking at the scoreboard.");
cvar_t scr_scoreboard_forcecolors = CVARD("scr_scoreboard_forcecolors", "0", "Makes the scoreboard colours obey enemycolor/teamcolor rules."); //damn americans
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
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.");
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.");
cvar_t scr_scoreboard_fillalpha = CVARD("scr_scoreboard_fillalpha", "0.7", "Transparency amount for newstyle scoreboard.");
cvar_t scr_scoreboard_backgroundalpha = CVARD("scr_scoreboard_backgroundalpha", "0.5", "Further multiplier for the background alphas.");
cvar_t scr_scoreboard_teamscores = CVARD("scr_scoreboard_teamscores", "1", "Makes +showscores act as +showteamscores. Because reasons.");
cvar_t scr_scoreboard_teamsort = CVARD("scr_scoreboard_teamsort", "0", "On the scoreboard, sort players by their team BEFORE their personal score.");
cvar_t scr_scoreboard_titleseperator = CVAR("scr_scoreboard_titleseperator", "1");
cvar_t scr_scoreboard_showruleset = CVAR("scr_scoreboard_showruleset", "1");
cvar_t sbar_teamstatus = CVARD("sbar_teamstatus", "1", "Display the last team say from each of your team members just above the sbar area.");
static cvar_t scr_scoreboard_drawtitle = CVARD("scr_scoreboard_drawtitle", "1", "Wastes screen space when looking at the scoreboard.");
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.");
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.");
static cvar_t scr_scoreboard_teamscores = CVARD("scr_scoreboard_teamscores", "1", "Makes +showscores act as +showteamscores. Because reasons.");
static cvar_t scr_scoreboard_teamsort = CVARD("scr_scoreboard_teamsort", "0", "On the scoreboard, sort players by their team BEFORE their personal score.");
static cvar_t scr_scoreboard_titleseperator = CVAR("scr_scoreboard_titleseperator", "1");
static cvar_t scr_scoreboard_showruleset = CVAR("scr_scoreboard_showruleset", "1");
static cvar_t scr_scoreboard_afk = CVARD("scr_scoreboard_afk", "1", "Show 'afk' in the packetloss column when they're afk.");
static cvar_t scr_scoreboard_ping_status = CVARD("scr_scoreboard_ping_status", "25 50 100 150", "Threshholds required to switch ping display from green to white, yellow, megenta and red.");
static cvar_t sbar_teamstatus = CVARD("sbar_teamstatus", "1", "Display the last team say from each of your team members just above the sbar area.");
cvar_t cl_sbaralpha = CVARAFD("cl_sbaralpha", "0.75", "scr_sbaralpha", CVAR_ARCHIVE, "Specifies the transparency of the status bar. Only Takes effect when cl_sbar is set to 2."); //with premultiplied alpha, this needs to affect the RGB values too.
static cvar_t cl_sbaralpha = CVARAFD("cl_sbaralpha", "0.75", "scr_sbaralpha", CVAR_ARCHIVE, "Specifies the transparency of the status bar. Only Takes effect when cl_sbar is set to 2."); //with premultiplied alpha, this needs to affect the RGB values too.
//===========================================
//rogue changed and added defines
@ -1165,6 +1167,8 @@ void Sbar_Init (void)
Cvar_Register(&scr_scoreboard_showfrags, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_showflags, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_showruleset, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_afk, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_ping_status, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_fillalpha, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_backgroundalpha, "Scoreboard settings");
Cvar_Register(&scr_scoreboard_teamscores, "Scoreboard settings");
@ -1279,7 +1283,7 @@ void Draw_TinyString (float x, float y, const qbyte *str)
if (!font_tiny)
{
font_tiny = Font_LoadFont("gfx/tinyfont", 8, 1, 0);
font_tiny = Font_LoadFont("gfx/tinyfont", 8, 1, 0, 0);
if (!font_tiny)
return;
}
@ -3385,7 +3389,16 @@ ping time frags name
{ \
int p = s->ping; \
if (p < 0 || p > 999) p = 999; \
sprintf(num, "%4i", p); \
if (p >= scr_scoreboard_ping_status.vec4[3] && scr_scoreboard_ping_status.vec4[3] && *scr_scoreboard_ping_status.string) \
sprintf(num, S_COLOR_RED"%4i", p); \
else if (p >= scr_scoreboard_ping_status.vec4[2] && scr_scoreboard_ping_status.vec4[2]) \
sprintf(num, S_COLOR_MAGENTA"%4i", p); \
else if (p >= scr_scoreboard_ping_status.vec4[1] && scr_scoreboard_ping_status.vec4[1]) \
sprintf(num, S_COLOR_YELLOW"%4i", p); \
else if (p >= scr_scoreboard_ping_status.vec4[0] || !scr_scoreboard_ping_status.vec4[0]) \
sprintf(num, S_COLOR_WHITE"%4i", p); \
else \
sprintf(num, S_COLOR_GREEN"%4i", p); \
Draw_FunStringWidth(x, y, num, 4*8, false, false); \
},NOFILL)
@ -3397,9 +3410,14 @@ ping time frags name
},NOFILL)
#define COLUMN_TIME COLUMN(time, 4*8, \
{ \
total = realtime - s->realentertime; \
minutes = (int)total/60; \
sprintf (num, "%4i", minutes); \
if (scr_scoreboard_afk.ival && s->chatstate&2) \
sprintf (num, S_COLOR_RED"afk"); \
else \
{ \
total = realtime - s->realentertime; \
minutes = (int)total/60; \
sprintf (num, "%4i", minutes); \
} \
Draw_FunStringWidth(x, y, num, 4*8, false, false); \
},NOFILL)
#define COLUMN_FRAGS COLUMN(frags, 5*8, \

View File

@ -321,7 +321,8 @@ void Font_Init(void);
void Font_Shutdown(void);
int Font_RegisterTrackerImage(const char *image); //returns a unicode char value that can be used to embed the char within a line of text.
qboolean Font_TrackerValid(unsigned int imid);
struct font_s *Font_LoadFont(const char *fontfilename, float height, float scale, int outline);
struct font_s *Font_LoadFont(const char *fontfilename, float height, float scale, int outline, unsigned int flags);
#define FONT_MONO 1
void Font_Free(struct font_s *f);
void Font_BeginString(struct font_s *font, float vx, float vy, int *px, int *py);
void Font_BeginScaledString(struct font_s *font, float vx, float vy, float szx, float szy, float *px, float *py); /*avoid using*/

View File

@ -3889,7 +3889,7 @@ static void S_UpdateCard(soundcardinfo_t *sc)
if (ch->sfx && (ch->vol[0] || ch->vol[1]) )
{
if (snd_show.ival > 1)
Con_Printf ("%i, %i %i %i %i %i %i %s\n", i, ch->vol[0], ch->vol[1], ch->vol[2], ch->vol[3], ch->vol[4], ch->vol[5], ch->sfx->name);
Con_Printf ("%i, %i/%i/%i/%i/%i/%i %s\n", i, ch->vol[0], ch->vol[1], ch->vol[2], ch->vol[3], ch->vol[4], ch->vol[5], ch->sfx->name);
active++;
}
else if (ch->sfx)
@ -3961,11 +3961,12 @@ int S_GetMixerTime(soundcardinfo_t *sc)
void S_Update (void)
{
soundcardinfo_t *sc;
RSpeedMark();
S_LockMixer();
for (sc = sndcardinfo; sc; sc = sc->next)
S_UpdateCard(sc);
S_UnlockMixer();
RSpeedEnd(RSPEED_AUDIO);
}
void S_ExtraUpdate (void)

View File

@ -213,7 +213,7 @@ void Sys_Printf (char *fmt, ...)
if (w >= 0xe000 && w < 0xe100)
{
/*not all quake chars are ascii compatible, so map those control chars to safe ones so we don't mess up anyone's xterm*/
if ((w & 0x7f) > 0x20)
if ((w & 0x7f) >= 0x20)
putc(w&0x7f, out);
else if (w & 0x80)
{
@ -993,10 +993,10 @@ dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)
lib = NULL;
if (!lib)
lib = dlopen (name, RTLD_LOCAL|RTLD_LAZY);
if (!lib && !strstr(name, ".so"))
lib = dlopen (va("%s.so", name), RTLD_LOCAL|RTLD_LAZY);
if (!lib && !strstr(name, ".so") && !strncmp(name, "./", 2) && host_parms.binarydir)
lib = dlopen (va("%s%s.so", host_parms.binarydir, name+2), RTLD_LOCAL|RTLD_LAZY);
if (!lib && !strstr(name, ARCH_DL_POSTFIX))
lib = dlopen (va("%s"ARCH_DL_POSTFIX, name), RTLD_LOCAL|RTLD_LAZY);
if (!lib && !strstr(name, ARCH_DL_POSTFIX) && !strncmp(name, "./", 2) && host_parms.binarydir)
lib = dlopen (va("%s%s"ARCH_DL_POSTFIX, host_parms.binarydir, name+2), RTLD_LOCAL|RTLD_LAZY);
if (!lib)
{
Con_DLPrintf(2,"%s\n", dlerror());
@ -1219,7 +1219,7 @@ static void DoSign(const char *fname, int signtype)
}
else if (f)
{
hashfunc_t *h = (signtype==1)?&hash_sha256:&hash_sha512;
hashfunc_t *h = (signtype==1)?&hash_sha2_256:&hash_sha2_512;
size_t l, ts = 0;
void *ctx = alloca(h->contextsize);
qbyte data[65536*16];

View File

@ -11,7 +11,7 @@
#ifndef WIN32
#include <fcntl.h>
#include <sys/stat.h>
#ifdef __unix__
#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) //apple make everything painful.
#include <unistd.h>
#endif
#else
@ -729,7 +729,7 @@ static int Sys_EnumerateFiles2 (const char *truepath, int apathofs, const char *
{
Q_snprintfz(file, sizeof(file), "%s/%s", truepath, ent->d_name);
if (stat(file, &st) == 0)
if (stat(file, &st) == 0 || lstat(file, &st) == 0)
{
Q_snprintfz(file, sizeof(file), "%s%s%s", apath, ent->d_name, S_ISDIR(st.st_mode)?"/":"");
@ -740,8 +740,8 @@ static int Sys_EnumerateFiles2 (const char *truepath, int apathofs, const char *
return false;
}
}
else
printf("Stat failed for \"%s\"\n", file);
// else
// printf("Stat failed for \"%s\"\n", file);
}
}
} while(1);

View File

@ -1512,20 +1512,32 @@ static int QDECL V_DepthSortTwoEntities(const void *p1,const void *p2)
}
void V_DepthSortEntities(float *vieworg)
{
int i;
int i, j;
vec3_t disp;
for (i = 0; i < cl_numvisedicts; i++)
{
if (cl_visedicts[i].flags & RF_WEAPONMODEL)
{ //weapon models have their own extra matrix thing going on. don't mess up because of it.
cl_visedicts[i].angles[0] = 0;
//however, qsort is not stable so hide ordering in here so they still come out with the same ordering, at least with respect to each other.
cl_visedicts[i].angles[0] = -1-i;
continue;
}
if (cl_visedicts[i].rtype == RT_MODEL && cl_visedicts[i].model && cl_visedicts[i].model->type == mod_brush)
{
VectorAdd(cl_visedicts[i].model->maxs, cl_visedicts[i].model->mins, disp);
VectorMA(cl_visedicts[i].origin, 0.5, disp, disp);
VectorSubtract(disp, vieworg, disp);
if (1)
{ //by nearest point.
for (j=0 ; j<3 ; j++)
{
disp[j] = vieworg[j] - cl_visedicts[i].origin[j];
disp[j] -= bound(cl_visedicts[i].model->mins[j], disp[j], cl_visedicts[i].model->maxs[j]);
}
}
else
{ //by midpoint...
VectorAdd(cl_visedicts[i].model->maxs, cl_visedicts[i].model->mins, disp);
VectorMA(cl_visedicts[i].origin, 0.5, disp, disp);
VectorSubtract(disp, vieworg, disp);
}
}
else
{
@ -2181,7 +2193,7 @@ void R_DrawNameTags(void)
}
else
#endif
if (w && w->progs && svs.gametype == GT_PROGS)
if (w && w->progs && w->progs->saveent)
{
int best = 0;
float bestscore = 0, score = 0;

View File

@ -1830,7 +1830,7 @@ char *TP_LocationName (const vec3_t location)
recursive = true;
level = Cmd_ExecLevel;
Cmd_ExpandString (locdata[minnum].name, buf, sizeof(buf), &level, true, false);
Cmd_ExpandString (locdata[minnum].name, buf, sizeof(buf), &level, false, true, false);
recursive = false;
return buf;
@ -3427,7 +3427,7 @@ void TP_UpdateAutoStatus(void)
vars.autoteamstatus_time = realtime + 3;
level = tp_autostatus.restriction;
newstatus = Cmd_ExpandString(tp_autostatus.string, newstatusbuf, sizeof(newstatusbuf), &level, true, true);
newstatus = Cmd_ExpandString(tp_autostatus.string, newstatusbuf, sizeof(newstatusbuf), &level, false, true, true);
newstatus = TP_ParseMacroString(newstatus);
if (!strcmp(newstatus, vars.autoteamstatus))
@ -3789,7 +3789,7 @@ void CL_Say (qboolean team, char *extra)
{
char buf[1024];
int level = Cmd_ExecLevel;
Cmd_ExpandString (cl_fakename.string, buf, sizeof(buf), &level, true, true);
Cmd_ExpandString (cl_fakename.string, buf, sizeof(buf), &level, false, true, true);
strcpy (buf, TP_ParseMacroString (buf));
Q_snprintfz (sendtext, sizeof(sendtext), "\x0d%s: ", TP_ParseFunChars(buf));
}

View File

@ -429,9 +429,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//FIXME: HAVE_WINSSPI does not work as a server.
//FIXME: advertising dtls without a valid certificate will probably bug out if a client tries to auto-upgrade.
//FIXME: we don't cache server certs
#ifndef MASTERONLY
#define HAVE_DTLS
#endif
#define HAVE_DTLS
#endif
#if defined(USE_SQLITE) || defined(USE_MYSQL)
@ -690,7 +688,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define ARCH_CPU_POSTFIX "x86"
#elif defined(__powerpc__) || defined(__ppc__)
#define ARCH_CPU_POSTFIX "ppc"
#elif defined(__aarch64__)
#elif defined(__aarch64__) || defined(__arm64__)
#define ARCH_CPU_POSTFIX "arm64"
#elif defined(__arm__)
#ifdef __SOFTFP__

View File

@ -150,7 +150,23 @@ static void Cmd_MacroList_f (void)
}
static void Cmd_MacroCompletion_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx)
{
size_t i, len;
const char *end = partial;
if (*end++ != '$')
return;
if (*end == '{')
end++;
len = strlen(end);
for (i = 0; i < macro_count; i++)
{
if (len <= strlen(macro_commands[i].name))
if (!strncmp(end, macro_commands[i].name, len))
ctx->cb(va("${%s}", macro_commands[i].name), NULL, NULL, ctx);
}
}
@ -900,7 +916,7 @@ static void Cmd_Exec_f (void)
Cbuf_InsertText (fs_manifest->defaultoverrides, level, false);
#if defined(HAVE_LEGACY) && defined(HAVE_CLIENT)
if (l == 1914 && Com_BlockChecksum(f, l) == 0x2d7b72b9)
if (l == 1914 && CalcHashInt(&hash_md4, f, l) == 0x2d7b72b9)
s = (char*)replacementq1binds;
#endif
}
@ -1026,7 +1042,7 @@ static void Cmd_Echo_f (void)
Q_strncatz(text, "\n", sizeof(text));
//echo text is often quoted, so expand the text again now that we're no longer in quotes.
t = Cmd_ExpandString(text, extext, sizeof(extext), &level, !Cmd_IsInsecure()?true:false, true);
t = Cmd_ExpandString(text, extext, sizeof(extext), &level, false, !Cmd_IsInsecure()?true:false, true);
#ifndef HAVE_CLIENT
Con_Printf ("%s", t);
@ -1174,9 +1190,8 @@ static void Cmd_Alias_f (void)
// check for overlap with a command
if (Cmd_Exists (s))
{ //commands always take precedence over aliases (so mods can't clobber 'quit' etc), so creating an alias with one of these names is stupid. always try to rename them.
if (Cmd_IsInsecure())
if (Cmd_IsInsecure() && snprintf(cmd, sizeof(cmd), "%s_a", s) < sizeof(cmd))
{
snprintf(cmd, sizeof(cmd), "%s_a", s);
if (Cmd_Exists (cmd))
{
Con_Printf (S_COLOR_RED"Can't register alias, %s is a command\n", s);
@ -1195,9 +1210,8 @@ static void Cmd_Alias_f (void)
{ //aliases take precedence over cvars (while cvars can be set via 'set'), so user's choice.
if (Cvar_FindVar (s))
{
if (Cmd_IsInsecure())
if (Cmd_IsInsecure() && snprintf(cmd, sizeof(cmd), "%s_a", s) < sizeof(cmd))
{
snprintf(cmd, sizeof(cmd), "%s_a", s);
Con_Printf (S_COLOR_RED"alias %s: renamed to %s due to cvar conflict\n", s, cmd);
s = cmd;
}
@ -1652,7 +1666,7 @@ static const char *Cmd_ExpandCvar(char *cvarterm, int maxaccesslevel, int *newac
quotetype = 2;
}
else if (fixup-cvarterm > 2 && !strncmp(fixup-2, " !", 2))
{ //abort is not defined
{ //abort if not defined
pl = 2;
quotetype = 3;
}
@ -1684,8 +1698,9 @@ static const char *Cmd_ExpandCvar(char *cvarterm, int maxaccesslevel, int *newac
else
cvarname = cvarterm;
result = strtoul(cvarname, &t, 10);
if ((dpcompat_console.ival||fixval) && (*t == 0 || (*t == '-' && t[1] == 0))) //only expand $0 if its actually ${0} - this avoids conflicting with the $0 macro
if (!cvarname)
;
else if ((result = strtoul(cvarname, &t, 10)), (dpcompat_console.ival||fixval) && (*t == 0 || (*t == '-' && t[1] == 0))) //only expand $0 if its actually ${0} - this avoids conflicting with the $0 macro
{
if (*t == '-') //pure number with a trailing minus means
{ //args starting after that.
@ -1755,7 +1770,7 @@ If not SERVERONLY, also expands $macro expressions
Note: dest must point to a 1024 byte buffer
================
*/
char *Cmd_ExpandString (const char *data, char *dest, int destlen, int *accesslevel, qboolean expandcvars, qboolean expandmacros)
char *Cmd_ExpandString (const char *data, char *dest, int destlen, int *accesslevel, qboolean expandargs, qboolean expandcvars, qboolean expandmacros)
{
unsigned int c;
char buf[255];
@ -1763,7 +1778,7 @@ char *Cmd_ExpandString (const char *data, char *dest, int destlen, int *accessle
int quotes = 0;
const char *str;
const char *bestvar;
int name_length, var_length;
int name_length, var_length, best_length;
qboolean striptrailing;
int maxaccesslevel = *accesslevel;
@ -1774,7 +1789,7 @@ char *Cmd_ExpandString (const char *data, char *dest, int destlen, int *accessle
if (c == '"')
quotes++;
if (c == '%' && !(quotes&1) && !dpcompat_console.ival)
if (c == '%' && !(quotes&1) && !dpcompat_console.ival && expandargs)
{ //QW262/ezquake does this. kinda annoying.
char *end;
if (data[1] == '%')
@ -1792,7 +1807,7 @@ char *Cmd_ExpandString (const char *data, char *dest, int destlen, int *accessle
str = Cmd_Args();
data+=2;
}
else if ((i=strtol(data+1, &end, 10)))
else if ((i=strtol(data+1, &end, 10)) || (end!=data+1&&(!*end||*end==' '||*end=='\t')))
{
data = end;
str = Cmd_Argv(i);
@ -1857,7 +1872,7 @@ char *Cmd_ExpandString (const char *data, char *dest, int destlen, int *accessle
buf[0] = 0;
buf[1] = 0;
bestvar = NULL;
var_length = 0;
var_length = best_length = 0;
while((c = *data))
{
if (c < ' ' || c == '$')
@ -1868,15 +1883,15 @@ char *Cmd_ExpandString (const char *data, char *dest, int destlen, int *accessle
buf[i++] = c;
buf[i] = 0;
if ((str = Cmd_ExpandCvar(buf+striptrailing, expandcvars?maxaccesslevel:-999, accesslevel, false, &var_length)))
bestvar = str;
bestvar = str, best_length=var_length;
if (expandmacros && (str = TP_MacroString (buf+striptrailing, accesslevel, &var_length)))
bestvar = str;
bestvar = str, best_length=var_length;
}
if (bestvar)
{
str = bestvar;
name_length = var_length;
name_length = best_length;
}
else
{
@ -1919,7 +1934,7 @@ char *Cmd_ExpandString (const char *data, char *dest, int destlen, int *accessle
if (len >= destlen-1)
break;
}
};
}
dest[len] = 0;
@ -2498,8 +2513,9 @@ cmd_completion_t *Cmd_Complete(const char *partial, qboolean caseinsens)
cvar_group_t *grp;
cvar_t *cvar;
const char *sp;
const char *sp, *e;
qboolean quoted = false;
int arg = 0;
static cmd_completion_t c;
@ -2515,30 +2531,37 @@ cmd_completion_t *Cmd_Complete(const char *partial, qboolean caseinsens)
c.partial = Z_StrDup(partial);
c.caseinsens = caseinsens;
for (sp = partial; *sp; sp++)
len = 0;
for(e = partial;;)
{
if (*sp == ' ' || *sp == '\t')
sp = e; //the start of where we're trying to complete...
while (*sp == ' ' || *sp == '\t')
sp++; //leading spaces are annoying...
e = COM_Parse(sp);
if (!arg && e)
len = e - partial;
if (e && (*e == ' ' || *e == '\t'))
{ //there seems to be whitespace after it.
arg++;
while (*sp == ' ' || *sp == '\t')
sp++;
//try to handle quotes
if (*sp == '\\' && sp[1] == '\"')
{
sp+=2;
quoted = true;
}
else if (*sp == '\"')
{
sp++;
quoted = true;
}
else
quoted = false;
}
else
break;
}
len = sp - partial;
if (*sp)
{
while (*sp == ' ' || *sp == '\t')
sp++;
//try to handle quotes
if (*sp == '\\' && sp[1] == '\"')
{
sp+=2;
quoted = true;
}
else if (*sp == '\"')
{
sp++;
quoted = true;
}
}
else
sp = NULL;
// if (len)
{
@ -2547,7 +2570,7 @@ cmd_completion_t *Cmd_Complete(const char *partial, qboolean caseinsens)
for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
if (!Q_strncasecmp (partial,cmd->name, len) && (!partial[len] || strlen(cmd->name) == len))
{
if (sp && cmd->argcompletion)
if (arg)
{
struct cmdargcompletion_ctx_s ctx;
ctx.cb.cb = Cmd_Complete_CheckArg;
@ -2557,7 +2580,10 @@ cmd_completion_t *Cmd_Complete(const char *partial, qboolean caseinsens)
ctx.res = &c;
ctx.desc = cmd->description;
ctx.quoted = quoted;
cmd->argcompletion(1, sp, &ctx.cb);
Cmd_MacroCompletion_c(arg, sp, &ctx.cb);
if (cmd->argcompletion)
cmd->argcompletion(arg, sp, &ctx.cb);
}
else
Cmd_Complete_Check(cmd->name, &c, cmd->description);
@ -2931,7 +2957,7 @@ void Cmd_ExecuteString (const char *text, int level)
if (dpcompat_console.ival && !strncmp(text, "alias", 5) && (text[5] == ' ' || text[5] == '\t'))
; //certain commands don't get pre-expanded in dp. evil hack. quote them to pre-expand anyway. double evil.
else
text = Cmd_ExpandString(text, dest, sizeof(dest), &level, true/*!Cmd_IsInsecure()?true:false*/, true);
text = Cmd_ExpandString(text, dest, sizeof(dest), &level, false, true/*!Cmd_IsInsecure()?true:false*/, true);
Cmd_TokenizeString (text, (level == RESTRICT_LOCAL&&!dpcompat_console.ival)?true:false, false);
// execute the command line
@ -2976,14 +3002,14 @@ void Cmd_ExecuteString (const char *text, int level)
execlevel = level;
}
Cbuf_InsertText ("\n", execlevel, false);
// if the alias value is a command or cvar and
// the alias is called with parameters, add them
//unless we're mimicing dp, or the alias has explicit expansions (or macros) in which case it can do its own damn args
{
if (dpcompat_console.ival)
{ //defective double escaping. the following line should sum it up nicely...
//set foo 3; alias test "set foo 2; echo $foo==1"; set foo 1; test
char *ignoringquoteswasstupid;
Cmd_ExpandString(a->value, dest, sizeof(dest), &execlevel, !Cmd_IsInsecure()?true:false, true);
Cmd_ExpandString(a->value, dest, sizeof(dest), &execlevel, true, !Cmd_IsInsecure()?true:false, true);
for (ignoringquoteswasstupid = dest; *ignoringquoteswasstupid; )
{ //double up dollars, to prevent expansion when its actually execed.
if (*ignoringquoteswasstupid == '$')
@ -2993,9 +3019,17 @@ void Cmd_ExecuteString (const char *text, int level)
}
ignoringquoteswasstupid++;
}
if ((a->restriction?a->restriction:rcon_level.ival) > execlevel)
return;
}
else
{ //more sane (and ezquake-like)
//set foo 3; alias test "set foo 2; echo $foo==2"; set foo 1; test
//alias test "echo Args were $qt${* q}$qt"; set foo 1; test Test Args Here
Cmd_ExpandString(a->value, dest, sizeof(dest), &execlevel, true, false, false); //expand args, but not other stuff.
}
if ((a->restriction?a->restriction:rcon_level.ival) > execlevel)
return; //we expanded something it wasn't meant to see.
Cbuf_InsertText ("\n", execlevel, false);
if (!dpcompat_console.ival)
{
if (Cmd_Argc() > 1 && (!strncmp(a->value, "cmd ", 4) || (!strchr(a->value, ' ') && !strchr(a->value, '\t') &&
@ -4416,7 +4450,7 @@ void Cmd_Init (void)
Cmd_AddCommandAD ("seta_calc", Cmd_set_f, Cmd_Set_c, "Sets the named cvar to the result of a (complex) expression. Also forces the archive flag so that the cvar will always be written into any saved configs.");
Cmd_AddCommandD ("vstr", Cmd_Vstr_f, "Executes the string value of the cvar, much like if it were an alias. For compatibility with q3.");
Cmd_AddCommandAD ("inc", Cvar_Inc_f, Cmd_Set_c, "Adds a value to the named cvar. Use a negative value if you wish to decrease the cvar's value.");
Cmd_AddCommand ("if", Cmd_if_f);
Cmd_AddCommandD ("if", Cmd_if_f, "For conditionally executing console commands.");
Cmd_AddCommand ("cmdlist", Cmd_List_f);
Cmd_AddCommand ("aliaslist", Cmd_AliasList_f);

View File

@ -251,7 +251,7 @@ void Cmd_MessageTrigger (char *message, int type);
void Cmd_ShiftArgs (int ammount, qboolean expandstring);
char *Cmd_ExpandString (const char *data, char *dest, int destlen, int *accesslevel, qboolean expandcvars, qboolean expandmacros);
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);
extern cvar_t rcon_level;

View File

@ -1718,6 +1718,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in
bytecolours = !!inf->ofs_rgbaub;
#endif
mesh->st_array = inf->ofs_st_array;
mesh->lmst_array[0] = inf->ofs_lmst_array; //some formats allow for two.
#endif
mesh->trneighbors = inf->ofs_trineighbours;
@ -1831,6 +1832,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in
meshcache.vbo.indexcount = inf->numindexes;
meshcache.vbo.vertcount = inf->numverts;
meshcache.vbo.texcoord = inf->vbotexcoords;
meshcache.vbo.lmcoord[0] = inf->vbolmtexcoords;
meshcache.vbo.coord = inf->vbo_skel_verts;
memset(&meshcache.vbo.coord2, 0, sizeof(meshcache.vbo.coord2));
meshcache.vbo.normals = inf->vbo_skel_normals;
@ -1897,6 +1899,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in
meshcache.vbo.indexcount = inf->numindexes;
meshcache.vbo.vertcount = inf->numverts;
meshcache.vbo.texcoord = inf->vbotexcoords;
meshcache.vbo.lmcoord[0] = inf->vbolmtexcoords;
meshcache.vbo.coord = inf->vbo_skel_verts;
memset(&meshcache.vbo.coord2, 0, sizeof(meshcache.vbo.coord2));
meshcache.vbo.normals = inf->vbo_skel_normals;
@ -2030,6 +2033,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in
meshcache.vbo.indexcount = inf->numindexes;
meshcache.vbo.vertcount = inf->numverts;
meshcache.vbo.texcoord = inf->vbotexcoords;
meshcache.vbo.lmcoord[0] = inf->vbolmtexcoords;
#ifdef SKELETALMODELS
memset(&meshcache.vbo.bonenums, 0, sizeof(meshcache.vbo.bonenums));
@ -3083,6 +3087,8 @@ static void Mod_GenerateMeshVBO(model_t *mod, galiasinfo_t *galias)
//determine the amount of space we need for our vbos.
if (galias->ofs_st_array)
vbospace += sizeof(*galias->ofs_st_array) * galias->numverts;
if (galias->ofs_lmst_array)
vbospace += sizeof(*galias->ofs_lmst_array) * galias->numverts;
if (galias->ofs_rgbaf)
vbospace += sizeof(*galias->ofs_rgbaf) * galias->numverts;
else if (galias->ofs_rgbaub)
@ -3112,6 +3118,8 @@ static void Mod_GenerateMeshVBO(model_t *mod, galiasinfo_t *galias)
BE_VBO_Begin(&vboctx, vbospace);
if (galias->ofs_st_array)
BE_VBO_Data(&vboctx, galias->ofs_st_array, sizeof(*galias->ofs_st_array) * galias->numverts, &galias->vbotexcoords);
if (galias->ofs_lmst_array)
BE_VBO_Data(&vboctx, galias->ofs_lmst_array, sizeof(*galias->ofs_lmst_array) * galias->numverts, &galias->vbolmtexcoords);
if (galias->ofs_rgbaf)
BE_VBO_Data(&vboctx, galias->ofs_rgbaf, sizeof(*galias->ofs_rgbaf) * galias->numverts, &galias->vborgba);
else if (galias->ofs_rgbaub)

View File

@ -205,6 +205,7 @@ typedef struct galiasinfo_s
#endif
vboarray_t vboindicies;
vboarray_t vbotexcoords;
vboarray_t vbolmtexcoords;
vboarray_t vborgba; //yeah, just you try reading THAT as an actual word.
void *vbomem;
void *ebomem;

View File

@ -6891,9 +6891,9 @@ static int Base64_Decode(char inp)
return (inp-'a') + 26;
if (inp >= '0' && inp <= '9')
return (inp-'0') + 52;
if (inp == '+')
if (inp == '+' || inp == '-')
return 62;
if (inp == '/')
if (inp == '/' || inp == '_')
return 63;
//if (inp == '=') //padding char
return 0; //invalid
@ -6929,6 +6929,23 @@ size_t Base64_EncodeBlock(const qbyte *in, size_t length, char *out, size_t outs
*out = 0;
return out-start;
}
size_t Base64_EncodeBlockURI(const qbyte *in, size_t length, char *out, size_t outsize)
{ //special uri-safe version (also trims)
outsize = Base64_EncodeBlock(in, length, out, outsize);
for (length = 0; length < outsize; length++)
{
if (out[length] == '+')
out[length] = '-';
else if (out[length] == '/')
out[length] = '_';
else if (out[length] == '=')
{ //truncate it here.
out[length] = 0;
return length;
}
}
return outsize;
}
size_t Base64_DecodeBlock(const char *in, const char *in_end, qbyte *out, size_t outsize)
{
qbyte *start = out;

View File

@ -692,7 +692,7 @@ vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls);
vfsfile_t *FS_OpenWithFriends(const char *fname, char *sysname, size_t sysnamesize, int numfriends, ...);
#define countof(array) (sizeof(array)/sizeof(array[0]))
#define countof(array) (sizeof(array)/sizeof((array)[0]))
#ifdef _WIN32
//windows doesn't support utf-8. Which is a shame really, because that's the charset we expect from everything.
char *narrowen(char *out, size_t outlen, wchar_t *wide);
@ -767,7 +767,6 @@ typedef struct
char *defaultexec; //execed after cvars are reset, to give game-specific engine-defaults.
char *defaultoverrides; //execed after default.cfg, to give usable defaults even when the mod the user is running is shit.
char *eula; //when running as an installer, the user will be presented with this as a prompt
char *rtcbroker; //the broker to use for webrtc connections.
char *basedir; //this is where we expect to find the data.
char *iconname; //path we can find the icon (relative to the fmf's location)
@ -790,13 +789,16 @@ typedef struct
} gamepath[8];
struct manpack_s //FIXME: this struct should be replaced with packagemanager info instead.
{
int type;
enum manifestdeptype_e type;
char *path; //the 'pure' name
char *prefix;
qboolean crcknown; //if the crc was specified
unsigned int crc; //the public crc
char *mirrors[8]; //a randomized (prioritized-on-load) list of mirrors to use. (may be 'prompt:game,package', 'unzip:file,url', 'xz:url', 'gz:url'
char *condition; //only downloaded if this cvar is set | delimited allows multiple cvars.
char *sha512; //package must match this hash, if specified
char *signature; //signs the hash
qofs_t filesize;
int mirrornum; //the index we last tried to download from, so we still work even if mirrors are down.
} package[64];
} ftemanifest_t;
@ -930,14 +932,12 @@ void InfoBuf_WriteToFile(vfsfile_t *f, infobuf_t *info, const char *commandname,
void InfoBuf_Enumerate (infobuf_t *info, void *ctx, void(*cb)(void *ctx, const char *key, const char *value));
void Com_BlocksChecksum (int blocks, void **buffer, int *len, unsigned char *outbuf);
unsigned int Com_BlockChecksum (const void *buffer, int length);
void Com_BlockFullChecksum (const void *buffer, int len, unsigned char *outbuf);
qbyte COM_BlockSequenceCheckByte (qbyte *base, int length, int sequence, unsigned mapchecksum);
qbyte COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence);
qbyte Q2COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence);
size_t Base64_EncodeBlock(const qbyte *in, size_t length, char *out, size_t outsize); //tries to null terminate, but returns length without termination.
size_t Base64_EncodeBlockURI(const qbyte *in, size_t length, char *out, size_t outsize); //slightly different chars for uri safety. also trims.
size_t Base64_DecodeBlock(const char *in, const char *in_end, qbyte *out, size_t outsize); // +/ and =
size_t Base16_EncodeBlock(const char *in, size_t length, qbyte *out, size_t outsize);
size_t Base16_DecodeBlock(const char *in, qbyte *out, size_t outsize);
@ -951,15 +951,16 @@ typedef struct
void (*process) (void *context, const void *data, size_t datasize);
void (*terminate) (unsigned char *digest, void *context);
} hashfunc_t;
extern hashfunc_t hash_sha1;
extern hashfunc_t hash_sha224;
extern hashfunc_t hash_sha256;
extern hashfunc_t hash_sha384;
extern hashfunc_t hash_sha512;
extern hashfunc_t hash_crc16;
extern hashfunc_t hash_md4; //required for vanilla qw mapchecks
extern hashfunc_t hash_sha1; //required for websockets, and ezquake's crypted rcon
extern hashfunc_t hash_sha2_224;
extern hashfunc_t hash_sha2_256; //required for webrtc
extern hashfunc_t hash_sha2_384;
extern hashfunc_t hash_sha2_512;
extern hashfunc_t hash_crc16; //aka ccitt, required for qw's clc_move and various bits of dp compat
extern hashfunc_t hash_crc16_lower;
unsigned int hashfunc_terminate_uint(const hashfunc_t *hash, void *context); //terminate, except returning the digest as a uint instead of a blob. folds the digest if longer than 4 bytes.
unsigned int CalcHashInt(const hashfunc_t *hash, const unsigned char *data, size_t datasize);
unsigned int CalcHashInt(const hashfunc_t *hash, const void *data, size_t datasize);
size_t CalcHash(const hashfunc_t *hash, unsigned char *digest, size_t maxdigestsize, const unsigned char *data, size_t datasize);
size_t CalcHMAC(const hashfunc_t *hashfunc, unsigned char *digest, size_t maxdigestsize, const unsigned char *data, size_t datalen, const unsigned char *key, size_t keylen);

View File

@ -124,7 +124,8 @@ enum
CB_MOVE = 4,
CB_ACTIONBAR = 5,
CB_SELECT = 6,
CB_SELECTED = 7,
CB_SELECTED = 7, //selection ended (deferred until drawing to ensure selections happen properly)
CB_TAPPED = 8, //quick-tap ended (deferred until drawing to ensure selections happen properly)
//the flags part
CB_STALE = (1u<<28), //WAS held last frame - to make sure we still do stuff when released on the same frame.

View File

@ -897,6 +897,7 @@ Cvar_Set
static cvar_t *Cvar_SetCore (cvar_t *var, const char *value, qboolean force)
{ //fixme: force should probably be a latch bitmask
char *latch=NULL;
qboolean changed;
COM_AssertMainThread("Cvar_SetCore");
@ -912,6 +913,9 @@ static cvar_t *Cvar_SetCore (cvar_t *var, const char *value, qboolean force)
return NULL;
}
if (!value)
value = var->defaultstr;
if (force)
;
else if (0)//var->flags & CVAR_SERVEROVERRIDE && !force)
@ -999,33 +1003,33 @@ static cvar_t *Cvar_SetCore (cvar_t *var, const char *value, qboolean force)
latch = var->string;//save off the old value (so cvar_set(var, var->string) works)
var->string = (char*)Z_Malloc (Q_strlen(value)+1);
Q_strcpy (var->string, value);
var->value = Q_atof (var->string);
var->ival = Q_atoi (var->string);
changed = (!latch) || strcmp(latch, value);
var->flags &= ~CVAR_TEAMPLAYTAINT;
if (changed)
{
char *str = COM_Parse(var->string);
var->vec4[0] = atof(com_token);
str = COM_Parse(str);
var->vec4[1] = atof(com_token);
str = COM_Parse(str);
var->vec4[2] = atof(com_token);
if (!str || !*str)
var->vec4[3] = 1;
else
var->string = Z_StrDup (value);
var->value = Q_atof (var->string);
var->ival = Q_atoi (var->string);
{
char *str = COM_Parse(var->string);
var->vec4[0] = atof(com_token);
str = COM_Parse(str);
var->vec4[3] = atof(com_token);
var->vec4[1] = atof(com_token);
str = COM_Parse(str);
var->vec4[2] = atof(com_token);
if (!str || !*str)
var->vec4[3] = 1;
else
{
str = COM_Parse(str);
var->vec4[3] = atof(com_token);
}
}
}
if (latch)
{
if (strcmp(latch, value))
{
if (latch)
{ //don't do this on registration.
var->modified=true; //only modified if it changed.
var->modifiedcount++;
if (var->callback)
@ -1046,6 +1050,7 @@ static cvar_t *Cvar_SetCore (cvar_t *var, const char *value, qboolean force)
#endif
}
}
if ((var->flags & CVAR_ARCHIVE) && !(var->flags & CVAR_SERVEROVERRIDE) && cl_warncmd.ival)
{
if (var->latched_string)
@ -1055,12 +1060,11 @@ static cvar_t *Cvar_SetCore (cvar_t *var, const char *value, qboolean force)
}
else
{
if (strcmp(latch, value))
if (!latch || strcmp(latch, value))
Cvar_ConfigChanged();
}
}
Z_Free (latch); // free the old value string
}

View File

@ -33,6 +33,11 @@ static cvar_t fs_gamepath = CVARAFD ("fs_gamepath"/*q3ish*/, "", "fs_gamedir"/
static cvar_t fs_basepath = CVARAFD ("fs_basepath"/*q3*/, "", "fs_basedir"/*q2*/, CVAR_NOUNSAFEEXPAND|CVAR_NOSET|CVAR_NOSAVE, "Provided for Q2/Q3 compat. System path of the base directory.");
static cvar_t fs_homepath = CVARAFD ("fs_homepath"/*q3ish*/, "", "fs_homedir"/*q2ish*/, CVAR_NOUNSAFEEXPAND|CVAR_NOSET|CVAR_NOSAVE, "Provided for Q2/Q3 compat. System path of the base directory.");
static cvar_t dpcompat_ignoremodificationtimes = CVARAFD("fs_packageprioritisation", "1", "dpcompat_ignoremodificationtimes", CVAR_NOUNSAFEEXPAND|CVAR_NOSAVE, "Favours the package that is:\n0: Most recently modified\n1: Is alphabetically last (favour z over a, 9 over 0).");
#ifdef FTE_TARGET_WEB
cvar_t fs_dlURL = CVARAFD(/*ioq3*/"sv_dlURL", "", /*dp*/"sv_curl_defaulturl", CVAR_SERVERINFO|CVAR_NOSAVE, "Provides clients with an external url from which they can obtain pk3s/packages from an external http server instead of having to download over udp.");
#else
cvar_t fs_dlURL = CVARAFD(/*ioq3*/"sv_dlURL", "", /*dp*/"sv_curl_defaulturl", CVAR_SERVERINFO|CVAR_ARCHIVE, "Provides clients with an external url from which they can obtain pk3s/packages from an external http server instead of having to download over udp.");
#endif
int active_fs_cachetype;
static int fs_referencetype;
int fs_finds;
@ -189,7 +194,7 @@ qboolean Sys_ResolveFileURL(const char *inurl, int inlen, char *out, int outlen)
{ //has an authority field...
i+=2;
//except we don't support authorities other than ourself...
if (i < inend || *i != '/')
if (i >= inend || *i != '/')
return false; //must be an absolute path...
#ifdef _WIN32
i++; //on windows, (full)absolute paths start with a drive name...
@ -308,7 +313,6 @@ void FS_Manifest_Free(ftemanifest_t *man)
Z_Free(man->eula);
Z_Free(man->defaultexec);
Z_Free(man->defaultoverrides);
Z_Free(man->rtcbroker);
Z_Free(man->basedir);
Z_Free(man->iconname);
for (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++)
@ -320,6 +324,8 @@ void FS_Manifest_Free(ftemanifest_t *man)
Z_Free(man->package[i].path);
Z_Free(man->package[i].prefix);
Z_Free(man->package[i].condition);
Z_Free(man->package[i].sha512);
Z_Free(man->package[i].signature);
for (j = 0; j < sizeof(man->package[i].mirrors) / sizeof(man->package[i].mirrors[0]); j++)
Z_Free(man->package[i].mirrors[j]);
}
@ -354,8 +360,6 @@ static ftemanifest_t *FS_Manifest_Clone(ftemanifest_t *oldm)
newm->defaultexec = Z_StrDup(oldm->defaultexec);
if (oldm->defaultoverrides)
newm->defaultoverrides = Z_StrDup(oldm->defaultoverrides);
if (oldm->rtcbroker)
newm->rtcbroker = Z_StrDup(oldm->rtcbroker);
if (oldm->iconname)
newm->iconname = Z_StrDup(oldm->iconname);
if (oldm->basedir)
@ -372,12 +376,20 @@ static ftemanifest_t *FS_Manifest_Clone(ftemanifest_t *oldm)
}
for (i = 0; i < sizeof(newm->package) / sizeof(newm->package[0]); i++)
{
newm->package[i].type = oldm->package[i].type;
newm->package[i].crc = oldm->package[i].crc;
newm->package[i].crcknown = oldm->package[i].crcknown;
if (oldm->package[i].path)
newm->package[i].path = Z_StrDup(oldm->package[i].path);
if (oldm->package[i].prefix)
newm->package[i].prefix = Z_StrDup(oldm->package[i].prefix);
if (oldm->package[i].condition)
newm->package[i].condition = Z_StrDup(oldm->package[i].condition);
if (oldm->package[i].sha512)
newm->package[i].sha512 = Z_StrDup(oldm->package[i].sha512);
if (oldm->package[i].signature)
newm->package[i].signature = Z_StrDup(oldm->package[i].signature);
newm->package[i].filesize = oldm->package[i].filesize;
for (j = 0; j < sizeof(newm->package[i].mirrors) / sizeof(newm->package[i].mirrors[0]); j++)
if (oldm->package[i].mirrors[j])
newm->package[i].mirrors[j] = Z_StrDup(oldm->package[i].mirrors[j]);
@ -450,8 +462,6 @@ static void FS_Manifest_Print(ftemanifest_t *man)
}
//Con_Printf("%s", man->defaultoverrides);
}
if (man->rtcbroker)
Con_Printf("rtcbroker %s\n", COM_QuotedString(man->rtcbroker, buffer, sizeof(buffer), false));
if (man->iconname)
Con_Printf("icon %s\n", COM_QuotedString(man->iconname, buffer, sizeof(buffer), false));
if (man->basedir)
@ -461,11 +471,10 @@ static void FS_Manifest_Print(ftemanifest_t *man)
{
if (man->gamepath[i].path)
{
size_t bufsize = strlen(man->gamepath[i].path) + 16;
char *str = Z_Malloc(bufsize);
if (man->gamepath[i].flags & GAMEDIR_PRIVATE)
Q_strncatz(str, "*", bufsize);
Q_strncatz(str, man->gamepath[i].path, bufsize);
char *str = va("%s%s%s",
(man->gamepath[i].flags & GAMEDIR_QSHACK)?"/":"",
(man->gamepath[i].flags & GAMEDIR_PRIVATE)?"*":"",
man->gamepath[i].path);
if (man->gamepath[i].flags & GAMEDIR_BASEGAME)
Con_Printf("basegame %s\n", COM_QuotedString(str, buffer, sizeof(buffer), false));
@ -483,10 +492,16 @@ static void FS_Manifest_Print(ftemanifest_t *man)
else
Con_Printf("package ");
Con_Printf("%s", COM_QuotedString(man->package[i].path, buffer, sizeof(buffer), false));
if (man->package[i].condition)
Con_Printf(" prefix %s", COM_QuotedString(man->package[i].condition, buffer, sizeof(buffer), false));
if (man->package[i].prefix)
Con_Printf(" prefix %s", COM_QuotedString(man->package[i].prefix, buffer, sizeof(buffer), false));
if (man->package[i].condition)
Con_Printf(" condition %s", COM_QuotedString(man->package[i].condition, buffer, sizeof(buffer), false));
if (man->package[i].filesize)
Con_Printf(" filesize %"PRIuQOFS, man->package[i].filesize);
if (man->package[i].sha512)
Con_Printf(" sha512 %s", COM_QuotedString(man->package[i].sha512, buffer, sizeof(buffer), false));
if (man->package[i].signature)
Con_Printf(" signature %s", COM_QuotedString(man->package[i].signature, buffer, sizeof(buffer), false));
if (man->package[i].crcknown)
Con_Printf(" crc 0x%x", man->package[i].crc);
for (j = 0; j < sizeof(man->package[i].mirrors) / sizeof(man->package[i].mirrors[0]); j++)
@ -538,8 +553,6 @@ static ftemanifest_t *FS_Manifest_Create(const char *syspath, const char *basedi
#else
man->mainconfig = Z_StrDup("fte.cfg");
#endif
man->rtcbroker = Z_StrDup("tls://master.frag-net.com:27950"); //This is eukara's server. fixme: this really ought to be a cvar instead.
return man;
}
@ -552,6 +565,9 @@ static qboolean FS_Manifest_ParsePackage(ftemanifest_t *man, int packagetype)
char *condition = NULL;
char *prefix = NULL;
char *arch = NULL;
char *signature = NULL;
char *sha512 = NULL;
qofs_t filesize = 0;
unsigned int arg = 1;
unsigned int mirrors = 0;
char *mirror[countof(man->package[0].mirrors)];
@ -598,6 +614,12 @@ static qboolean FS_Manifest_ParsePackage(ftemanifest_t *man, int packagetype)
prefix = Cmd_Argv(arg++);
else if (!strcmp(a, "arch"))
arch = Cmd_Argv(arg++);
else if (!strcmp(a, "signature"))
signature = Cmd_Argv(arg++);
else if (!strcmp(a, "sha512"))
sha512 = Cmd_Argv(arg++);
else if (!strcmp(a, "filesize")||!strcmp(a, "size"))
filesize = strtoull(Cmd_Argv(arg++), NULL, 0);
else if (!strcmp(a, "mirror"))
{
a = Cmd_Argv(arg++);
@ -642,6 +664,9 @@ mirror:
man->package[i].path = Z_StrDup(path);
man->package[i].prefix = prefix?Z_StrDup(prefix):NULL;
man->package[i].condition = condition?Z_StrDup(condition):NULL;
man->package[i].sha512 = sha512?Z_StrDup(sha512):NULL;
man->package[i].signature = signature?Z_StrDup(signature):NULL;
man->package[i].filesize = filesize;
man->package[i].crcknown = crcknown;
man->package[i].crc = crc;
for (j = 0; j < mirrors; j++)
@ -784,11 +809,12 @@ static qboolean FS_Manifest_ParseTokens(ftemanifest_t *man)
{
Z_StrCat(&man->defaultoverrides, va("%s %s\n", Cmd_Argv(0), Cmd_Args()));
}
#ifdef HAVE_LEGACY
else if (!Q_strcasecmp(cmd, "rtcbroker"))
{
Z_Free(man->rtcbroker);
man->rtcbroker = Z_StrDup(Cmd_Argv(1));
Z_StrCat(&man->defaultexec, va("set %s %s\n", net_ice_broker.name, Cmd_Args()));
}
#endif
else if (!Q_strcasecmp(cmd, "updateurl"))
{
Z_Free(man->updateurl);
@ -1330,10 +1356,10 @@ static void COM_CalcHash_Thread(void *ctx, void *fname, size_t a, size_t b)
// {"crc16", &hash_crc16},
{"sha1", &hash_sha1},
#if defined(HAVE_SERVER) || defined(HAVE_CLIENT)
// {"sha224", &hash_sha224},
{"sha256", &hash_sha256},
// {"sha384", &hash_sha384},
// {"sha512", &hash_sha512},
// {"sha224", &hash_sha2_224},
{"sha256", &hash_sha2_256},
// {"sha384", &hash_sha2_384},
// {"sha512", &hash_sha2_512},
#endif
};
qbyte digest[DIGEST_MAXSIZE];
@ -2277,6 +2303,7 @@ qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out
char cleanname[MAX_QPATH];
char *last;
qboolean wasbase; //to handle out-of-order base/game dirs.
int nlen;
if (relativeto == FS_SYSTEM)
{
@ -2295,7 +2322,7 @@ qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out
{
//this is sometimes used to query the actual path.
//don't alow it for other stuff though.
if (relativeto != FS_ROOT && relativeto != FS_BINARYPATH && relativeto != FS_GAMEONLY)
if (relativeto != FS_ROOT && relativeto != FS_BINARYPATH && relativeto != FS_LIBRARYPATH && relativeto != FS_GAMEONLY)
return false;
}
else
@ -2310,29 +2337,36 @@ qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out
case FS_GAME: //this is really for diagnostic type stuff...
if (FS_FLocateFile(fname, FSLF_IFFOUND, &loc))
{
snprintf(out, outlen, "%s/%s", loc.search->logicalpath, fname);
nlen = snprintf(out, outlen, "%s/%s", loc.search->logicalpath, fname);
break;
}
//fallthrough
case FS_GAMEONLY:
if (com_homepathenabled)
snprintf(out, outlen, "%s%s/%s", com_homepath, gamedirfile, fname);
nlen = snprintf(out, outlen, "%s%s/%s", com_homepath, gamedirfile, fname);
else
snprintf(out, outlen, "%s%s/%s", com_gamepath, gamedirfile, fname);
nlen = snprintf(out, outlen, "%s%s/%s", com_gamepath, gamedirfile, fname);
break;
case FS_LIBRARYPATH:
#ifdef FTE_LIBRARY_PATH
nlen = snprintf(out, outlen, STRINGIFY(FTE_LIBRARY_PATH)"/%s", fname);
break;
#else
return false;
#endif
case FS_BINARYPATH:
if (host_parms.binarydir && *host_parms.binarydir)
snprintf(out, outlen, "%s%s", host_parms.binarydir, fname);
nlen = snprintf(out, outlen, "%s%s", host_parms.binarydir, fname);
else
snprintf(out, outlen, "%s%s", host_parms.basedir, fname);
nlen = snprintf(out, outlen, "%s%s", host_parms.basedir, fname);
break;
case FS_ROOT:
if (com_installer)
return false;
if (com_homepathenabled)
snprintf(out, outlen, "%s%s", com_homepath, fname);
nlen = snprintf(out, outlen, "%s%s", com_homepath, fname);
else
snprintf(out, outlen, "%s%s", com_gamepath, fname);
nlen = snprintf(out, outlen, "%s%s", com_gamepath, fname);
break;
case FS_BASEGAMEONLY: // fte/
@ -2349,9 +2383,9 @@ qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out
if (!last)
return false; //eep?
if (com_homepathenabled)
snprintf(out, outlen, "%s%s/%s", com_homepath, last, fname);
nlen = snprintf(out, outlen, "%s%s/%s", com_homepath, last, fname);
else
snprintf(out, outlen, "%s%s/%s", com_gamepath, last, fname);
nlen = snprintf(out, outlen, "%s%s/%s", com_gamepath, last, fname);
break;
case FS_PUBGAMEONLY: // $gamedir/ or qw/ but not fte/
last = NULL;
@ -2372,9 +2406,9 @@ qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out
if (!last)
return false; //eep?
if (com_homepathenabled)
snprintf(out, outlen, "%s%s/%s", com_homepath, last, fname);
nlen = snprintf(out, outlen, "%s%s/%s", com_homepath, last, fname);
else
snprintf(out, outlen, "%s%s/%s", com_gamepath, last, fname);
nlen = snprintf(out, outlen, "%s%s/%s", com_gamepath, last, fname);
break;
case FS_PUBBASEGAMEONLY: // qw/ (fixme: should be the last non-private basedir)
last = NULL;
@ -2390,14 +2424,14 @@ qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out
if (!last)
return false; //eep?
if (com_homepathenabled)
snprintf(out, outlen, "%s%s/%s", com_homepath, last, fname);
nlen = snprintf(out, outlen, "%s%s/%s", com_homepath, last, fname);
else
snprintf(out, outlen, "%s%s/%s", com_gamepath, last, fname);
nlen = snprintf(out, outlen, "%s%s/%s", com_gamepath, last, fname);
break;
default:
Sys_Error("FS_NativePath case not handled\n");
}
return true;
return nlen < outlen;
}
//returns false to stop the enumeration. check the return value of the fs enumerator to see if it was canceled by this return value.
@ -3529,6 +3563,7 @@ void FS_AddHashedPackage(searchpath_t **oldpaths, const char *parentpath, const
static void FS_AddManifestPackages(searchpath_t **oldpaths, const char *purepath, const char *logicalpaths, searchpath_t *search, unsigned int loadstuff)
{
#ifndef PACKAGEMANAGER
int i;
int ptlen, palen;
@ -3552,6 +3587,7 @@ static void FS_AddManifestPackages(searchpath_t **oldpaths, const char *purepath
);
}
}
#endif
}
static void FS_AddDownloadManifestPackages(searchpath_t **oldpaths, unsigned int loadstuff)//, const char *purepath, searchpath_t *search, const char *extension, searchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, const char *desc))
@ -3803,6 +3839,68 @@ qboolean FS_Restarted(unsigned int *since)
return false;
}
#ifdef __WIN32 //already assumed to be case insensitive. let the OS keep fixing up the paths itself.
static void FS_FixupFileCase(char *out, size_t outsize, const char *basedir, const char *entry)
{
Q_snprintfz(out, outsize, "%s%s", basedir, entry);
}
#else
struct fixupcase_s
{
char *out;
size_t outsize;
const char *match;
size_t matchlen;
qboolean isdir; //directory results have a trailing /
};
static int FS_FixupFileCaseResult(const char *name, qofs_t sz, time_t modtime, void *vparm, searchpathfuncs_t *spath)
{
struct fixupcase_s *parm = vparm;
if (strlen(name) != parm->matchlen+parm->isdir)
return true;
if (parm->isdir && name[parm->matchlen] != '/')
return true;
if (Q_strncasecmp(name, parm->match, parm->matchlen))
return true;
memcpy(parm->out, name, parm->matchlen);
return !!Q_strncmp(name, parm->match, parm->matchlen); //stop if we find the exact path case. otherwise keep looking
}
//like snprintf("%s%s") but fixes up 'gamedir' case to a real file
static qboolean FS_FixupFileCase(char *out, size_t outsize, const char *basedir, const char *entry, qboolean isdir)
{
char *s;
struct fixupcase_s parm = {out+strlen(basedir), outsize-strlen(basedir), entry, strlen(entry), isdir};
if (Q_snprintfz(out, outsize, "%s%s", basedir, entry) >= outsize || outsize < strlen(basedir)+1 || parm.outsize < parm.matchlen+1)
return false; //over sized?...
if (strchr(entry, '/')) for(;;)
{
parm.match = entry;
s = strchr(entry, '/');
if (s)
{
parm.isdir = true;
parm.matchlen = s-entry;
Sys_EnumerateFiles(basedir, "*", FS_FixupFileCaseResult, &parm, NULL);
parm.out += parm.matchlen+1;
parm.outsize -= parm.matchlen+1;
entry += (s-entry)+1;
}
else
{
parm.isdir = isdir;
parm.matchlen = strlen(entry);
parm.out[-1] = 0;
Sys_EnumerateFiles(out, "*", FS_FixupFileCaseResult, &parm, NULL);
parm.out[-1] = '/';
break;
}
}
else
Sys_EnumerateFiles(basedir, "*", FS_FixupFileCaseResult, &parm, NULL);
return true;
}
#endif
/*
================
FS_AddGameDirectory
@ -3854,13 +3952,12 @@ static searchpath_t *FS_AddSingleGameDirectory (searchpath_t **oldpaths, const c
static void FS_AddGameDirectory (searchpath_t **oldpaths, const char *puredir, unsigned int loadstuff, unsigned int flags)
{
char syspath[MAX_OSPATH];
Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_gamepath, puredir);
gameonly_gamedir = FS_AddSingleGameDirectory(oldpaths, puredir, syspath, loadstuff, flags&~(com_homepathenabled?SPF_WRITABLE:0u));
if (com_homepathenabled)
{
Q_snprintfz(syspath, sizeof(syspath), "%s%s", com_homepath, puredir);
if (FS_FixupFileCase(syspath, sizeof(syspath), com_gamepath, puredir, true))
gameonly_gamedir = FS_AddSingleGameDirectory(oldpaths, puredir, syspath, loadstuff, flags&~(com_homepathenabled?SPF_WRITABLE:0u));
else
gameonly_gamedir = NULL;
if (com_homepathenabled && FS_FixupFileCase(syspath, sizeof(syspath), com_homepath, puredir, true))
gameonly_homedir = FS_AddSingleGameDirectory(oldpaths, puredir, syspath, loadstuff, flags);
}
else
gameonly_homedir = NULL;
}
@ -4136,7 +4233,7 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
#define QCFG "//schemes quake qw\n" QUAKEOVERRIDES "set com_parseutf8 0\n" QRPCOMPAT
#define KEXCFG "//schemes quake_r2\n" QUAKEOVERRIDES "set com_parseutf8 1\nset campaign 0\nset net_enable_dtls 1\nset sv_mintic 0.016666667\nset sv_maxtic $sv_mintic\nset cl_netfps 60\n"
/*NetQuake reconfiguration, to make certain people feel more at home...*/
#define NQCFG "//disablehomedir 1\n//mainconfig ftenq\n" QCFG "cfg_save_auto 1\nset sv_nqplayerphysics 1\nset cl_loopbackprotocol auto\ncl_sbar 1\nset plug_sbar 0\nset sv_port "STRINGIFY(PORT_NQSERVER)"\ncl_defaultport "STRINGIFY(PORT_NQSERVER)"\nset m_preset_chosen 1\nset vid_wait 1\nset cl_demoreel 1\n"
#define NQCFG "//disablehomedir 1\n//mainconfig ftenq\n" QCFG "cfg_save_auto 1\nset pm_bunnyfriction 1\nset sv_nqplayerphysics 1\nset cl_loopbackprotocol auto\ncl_sbar 1\nset plug_sbar 0\nset sv_port "STRINGIFY(PORT_NQSERVER)"\ncl_defaultport "STRINGIFY(PORT_NQSERVER)"\nset m_preset_chosen 1\nset vid_wait 1\nset cl_demoreel 1\n"
#define SPASMCFG NQCFG "fps_preset builtin_spasm\nset cl_demoreel 0\ncl_sbar 2\nset gl_load24bit 1\n"
#define FITZCFG NQCFG "fps_preset builtin_spasm\ncl_sbar 2\nset gl_load24bit 1\n"
#define TENEBRAECFG NQCFG "fps_preset builtin_tenebrae\n"
@ -4152,11 +4249,13 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
/*set some stuff so our regular qw client appears more like hexen2. sv_mintic is required to 'fix' the ravenstaff so that its projectiles don't impact upon each other*/
#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.015\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 allow_skybox 1\nset v_gammainverted 1\nset com_parseutf8 0\ncom_gamedirnativecode 1\nset sv_bigcoords 0\nsv_port "STRINGIFY(PORT_Q2SERVER)"\ncl_defaultport "STRINGIFY(PORT_Q2SERVER)"\n"
#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)"\ncl_defaultport "STRINGIFY(PORT_Q2SERVER)"\n"
/*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/
#define Q3CFG "//schemes quake3\n" "set v_gammainverted 0\nset snd_ignorecueloops 1\nsetfl g_gametype 0 s\nset gl_clear 1\nset r_clearcolour 0 0 0\nset com_parseutf8 0\ngl_overbright "FORWEB("0","2")"\nseta model sarge\nseta headmodel sarge\nseta handicap 100\ncom_gamedirnativecode 1\nsv_port "STRINGIFY(PORT_Q3SERVER)"\ncl_defaultport "STRINGIFY(PORT_Q3SERVER)"\ncom_protocolversion 68\n"
//#define RMQCFG "sv_bigcoords 1\n"
#define HLCFG "plug_load ffmpeg\n"
#ifndef UPDATEURL
#ifdef HAVE_SSL
#define UPDATEURL(g) "/downloadables.php?game=" #g
@ -4225,7 +4324,7 @@ static const gamemode_info_t gamemode_info[] = {
//because we can. 'fps_preset spasm' is hopefully close enough...
{"-fitz", "nq", QUAKEPROT, {"id1/pak0.pak","id1/quake.rc"},FITZCFG,{"id1"}, "FauxFitz", UPDATEURL(Q1)},
//because we can
{"-tenebrae", NULL, QUAKEPROT, {"id1/pak0.pak","id1/quake.rc"},TENEBRAECFG,{"id1", "tenebrae"}, "FauxTenebrae", UPDATEURL(Q1)},
{"-tenebrae", NULL, QUAKEPROT, {"tenebrae/Pak0.pak","id1/quake.rc"},TENEBRAECFG,{"id1", "tenebrae"}, "FauxTenebrae", UPDATEURL(Q1)},
//quake's mission packs should not be favoured over the base game nor autodetected
//third part mods also tend to depend upon the mission packs for their huds, even if they don't use any other content.
@ -4281,10 +4380,8 @@ static const gamemode_info_t gamemode_info[] = {
//for the luls
// {"-diablo2", NULL, "FTE-Diablo2", {"d2music.mpq"}, NULL, {"*", "*fted2"}, "Diablo 2"},
#endif
#if defined(HLSERVER) || defined(HLCLIENT)
//can run in windows, needs hl gamecode enabled. maps can always be viewed, but meh.
{"-halflife", "halflife", "FTE-HalfLife", {"valve/liblist.gam"}, NULL, {"valve", "*ftehl"}, "Half-Life"},
#endif
/* maintained by FreeHL ~eukara */
{"-halflife", "halflife", "FTE-HalfLife", {"valve/liblist.gam"}, HLCFG, {"logos", "valve"}, "Half-Life"},
#endif
{NULL}
@ -4872,6 +4969,8 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags)
}
else
{
if (!FS_GamedirIsOkay(dir))
continue;
FS_AddGameDirectory(&oldpaths, dir, reloadflags, fl);
}
}
@ -4888,15 +4987,6 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags)
char *dir = fs_manifest->gamepath[i].path;
if (dir && !(fs_manifest->gamepath[i].flags&GAMEDIR_BASEGAME))
{
//don't allow leading dots, hidden files are evil.
//don't allow complex paths. those are evil too.
if (!*dir || *dir == '.' || !strcmp(dir, ".") || strstr(dir, "..") || strstr(dir, "/")
|| strstr(dir, "\\") || strstr(dir, ":") )
{
Con_Printf ("Gamedir should be a single filename, not a path\n");
continue;
}
for (j = 0; j < countof(fs_manifest->gamepath); j++)
{
char *dir2 = fs_manifest->gamepath[j].path;
@ -4906,12 +4996,28 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags)
if (j < countof(fs_manifest->gamepath))
continue; //already loaded above. don't mess up gameonly_gamedir.
fl = SPF_EXPLICIT;
if (!(fs_manifest->gamepath[i].flags&GAMEDIR_READONLY))
fl |= SPF_WRITABLE;
if (fs_manifest->gamepath[i].flags&GAMEDIR_PRIVATE)
fl |= SPF_PRIVATE;
if (fs_manifest->gamepath[i].flags&GAMEDIR_QSHACK)
fl |= SPF_QSHACK;
if (*dir == '*')
{
{ //just in case... shouldn't be needed.
dir++;
fl |= GAMEDIR_PRIVATE;
}
if (fs_manifest->gamepath[i].flags & GAMEDIR_SPECIAL)
; //don't.
else
{
FS_AddGameDirectory(&oldpaths, dir, reloadflags, SPF_EXPLICIT|SPF_WRITABLE);
//don't use evil gamedir names.
if (!FS_GamedirIsOkay(dir))
continue;
FS_AddGameDirectory(&oldpaths, dir, reloadflags, fl);
}
}
}
@ -5535,6 +5641,8 @@ qboolean Sys_FindGameData(const char *poshname, const char *gamename, char *base
return true;
if (!strcmp(gamename, "quake") || !strcmp(gamename, "afterquake") || !strcmp(gamename, "netquake") || !strcmp(gamename, "spasm") || !strcmp(gamename, "fitz") || !strcmp(gamename, "tenebrae"))
{
if (Sys_SteamHasFile(basepath, basepathlen, "Quake", "Id1/PAK0.PAK")) //dos legacies need to die.
return true;
if (Sys_SteamHasFile(basepath, basepathlen, "Quake", "id1/PAK0.PAK")) //dos legacies need to die.
return true;
if (Sys_SteamHasFile(basepath, basepathlen, "Quake", "id1/pak0.pak")) //people may have tried to sanitise it already.
@ -5671,16 +5779,19 @@ static qboolean FS_DirHasAPackage(char *basedir, ftemanifest_t *man)
return defaultret;
}
#ifdef _WIN32
//false stops the search (and returns that value to FS_DirHasGame)
static int QDECL FS_DirDoesHaveGame(const char *fname, qofs_t fsize, time_t modtime, void *ctx, searchpathfuncs_t *subdir)
{
return false;
}
#endif
//just check each possible file, see if one is there.
static qboolean FS_DirHasGame(const char *basedir, int gameidx)
{
int j;
char realpath[MAX_OSPATH];
//none listed, just assume its correct.
if (!gamemode_info[gameidx].auniquefile[0])
@ -5690,8 +5801,13 @@ static qboolean FS_DirHasGame(const char *basedir, int gameidx)
{
if (!gamemode_info[gameidx].auniquefile[j])
continue; //no more
#ifdef _WIN32
if (!Sys_EnumerateFiles(basedir, gamemode_info[gameidx].auniquefile[j], FS_DirDoesHaveGame, NULL, NULL))
return true; //search was cancelled by the callback, so it actually got called.
#else
if (FS_FixupFileCase(realpath, sizeof(realpath), basedir, gamemode_info[gameidx].auniquefile[j], false) && access(realpath, R_OK) == 0)
return true; //something readable.
#endif
}
return false;
}
@ -5746,7 +5862,7 @@ static int FS_IdentifyDefaultGame(char *newbase, int sizeof_newbase, qboolean fi
if (gamenum != -1)
Q_strncpyz(newbase, host_parms.binarydir, sizeof_newbase);
}
if (gamenum == -1 && *com_homepath && !fixedbase)
if (gamenum == -1 && *com_homepath && com_homepathusable && !fixedbase)
{
gamenum = FS_IdentifyDefaultGameFromDir(com_homepath);
if (gamenum != -1)
@ -6141,6 +6257,8 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
char *vidfile[] = {"gfx.wad", "gfx/conback.lmp", //misc stuff
"gfx/palette.lmp", "pics/colormap.pcx", "gfx/conchars.png"}; //palettes
searchpathfuncs_t *vidpath[countof(vidfile)];
char *menufile[] = {"menu.dat"/*mods*/, "gfx/ttl_main.lmp"/*q1*/, "pics/m_main_quit.pcx"/*q2*/, "gfx/menu/title0.lmp"/*h2*/};
searchpathfuncs_t *menupath[countof(menufile)];
#endif
//if any of these files change location, the configs will be re-execed.
@ -6159,6 +6277,16 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
else
vidpath[i] = NULL;
}
for (i = 0; i < countof(menufile); i++)
{
if (allowreloadconfigs)
{
FS_FLocateFile(menufile[i], FSLF_IFFOUND|FSLF_SECUREONLY, &loc);
menupath[i] = loc.search?loc.search->handle:NULL;
}
else
menupath[i] = NULL;
}
#endif
if (allowreloadconfigs && fs_noreexec.ival)
@ -6439,6 +6567,12 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
}
#endif
//our basic filesystem should be okay, but no packages loaded yet.
#ifdef MANIFESTDOWNLOADS
//make sure the package manager knows what its meant to know...
PM_AddManifestPackages(man);
#endif
if (Sys_LockMutex(fs_thread_mutex))
{
#ifdef HAVE_CLIENT
@ -6537,6 +6671,20 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
Cbuf_AddText ("vid_reload\n", RESTRICT_LOCAL);
vidrestart = false;
}
if (qrenderer != QR_NONE && allowreloadconfigs)
{
for (i = 0; i < countof(menufile); i++)
{
FS_FLocateFile(menufile[i], FSLF_IFFOUND, &loc);
if (menupath[i] != (loc.search?loc.search->handle:NULL))
{
Cbuf_AddText ("menu_restart\n", RESTRICT_LOCAL);
break;
}
}
}
#endif
//rebuild the cache now, should be safe to waste some cycles on it
@ -6701,7 +6849,7 @@ int FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man),
Q_strncpyz(basedir, com_gamepath, sizeof(basedir));
if (gamemode_info[i].manifestfile ||
((gamemode_info[i].exename || (i>0&&gamemode_info[i].customexec&&gamemode_info[i-1].customexec&&strcmp(gamemode_info[i].customexec,gamemode_info[i-1].customexec))) && FS_DirHasGame(com_gamepath, i)) ||
(e.anygamedir&&Sys_FindGameData(NULL, gamemode_info[i].argname+1, basedir, sizeof(basedir), true)))
(e.anygamedir&&Sys_FindGameData(NULL, gamemode_info[i].argname+1, basedir, sizeof(basedir), false)))
{
man = FS_GenerateLegacyManifest(i, basedir);
if (e.callback(e.usr, man))
@ -7203,6 +7351,7 @@ static void FS_ChangeMod_f(void)
int packages = 0;
const char *arg = "?";
qboolean okay = false;
char *dir = NULL;
if (Cmd_IsInsecure())
return;
@ -7246,6 +7395,11 @@ static void FS_ChangeMod_f(void)
arg = Cmd_Argv(i++);
packagespaths[packages-1].subpath = Z_StrDup(arg);
}
else if (!strcmp(arg, "dir"))
{
arg = Cmd_Argv(i++);
Z_StrDupPtr(&dir, arg);
}
else if (!strcmp(arg, "map"))
{
Z_Free(fs_loadedcommand);
@ -7268,13 +7422,14 @@ static void FS_ChangeMod_f(void)
}
if (okay)
COM_Gamedir("", packagespaths);
COM_Gamedir(dir?dir:"", packagespaths);
else
{
Con_Printf("unsupported args: %s\n", arg);
Z_Free(fs_loadedcommand);
fs_loadedcommand = NULL;
}
Z_Free(dir);
for (i = 0; i < packages; i++)
{
@ -7645,6 +7800,7 @@ void COM_InitFilesystem (void)
Cvar_Register(&fs_gamepath, "Filesystem");
Cvar_Register(&fs_basepath, "Filesystem");
Cvar_Register(&fs_homepath, "Filesystem");
Cvar_Register(&fs_dlURL, "Filesystem");
COM_InitHomedir(NULL);

View File

@ -27,6 +27,7 @@ extern int fs_hash_files; //for tracking efficiency. no functional use.
extern qboolean fs_readonly; //if true, fopen(, "w") should always fail.
extern void *fs_thread_mutex;
extern float fs_accessed_time;
extern cvar_t fs_dlURL;
struct searchpath_s;
struct searchpathfuncs_s
@ -90,7 +91,7 @@ void Menu_Download_Update(void);
typedef struct
{
char *description;
void (*Update) (const char *url, vfsfile_t *out);
void (*Update) (const char *url, vfsfile_t *out, qboolean favourcache);
#define plugupdatesourcefuncs_name "UpdateSource"
} plugupdatesourcefuncs_t;
qboolean PM_RegisterUpdateSource(void *module, plugupdatesourcefuncs_t *funcs);

View File

@ -1235,9 +1235,9 @@ static int QDECL FSDZ_GeneratePureCRC(searchpathfuncs_t *handle, int seed, int c
}
if (crctype)
result = Com_BlockChecksum(filecrcs, numcrcs*sizeof(int));
result = CalcHashInt(&hash_md4, filecrcs, numcrcs*sizeof(int));
else
result = Com_BlockChecksum(filecrcs+1, (numcrcs-1)*sizeof(int));
result = CalcHashInt(&hash_md4, filecrcs+1, (numcrcs-1)*sizeof(int));
BZ_Free(filecrcs);
return result;

View File

@ -176,9 +176,9 @@ static int QDECL FSPAK_GeneratePureCRC(searchpathfuncs_t *handle, int seed, int
}
if (crctype)
result = Com_BlockChecksum(filecrcs, numcrcs*sizeof(int));
result = CalcHashInt(&hash_md4, filecrcs, numcrcs*sizeof(int));
else
result = Com_BlockChecksum(filecrcs+1, (numcrcs-1)*sizeof(int));
result = CalcHashInt(&hash_md4, filecrcs+1, (numcrcs-1)*sizeof(int));
BZ_Free(filecrcs);
return result;

View File

@ -607,6 +607,9 @@ typedef struct
#define ZFL_SYMLINK (1u<<3) //file is a symlink
#define ZFL_CORRUPT (1u<<4) //file is corrupt or otherwise unreadable (usually just means we don't support reading it rather than actually corrupt, but hey).
#define ZFL_WEAKENCRYPT (1u<<5) //traditional zip encryption
#define ZFL_DEFLATE64D (1u<<6) //need to use zlib's 'inflateBack9' stuff.
#define ZFL_COMPRESSIONTYPE (ZFL_STORED|ZFL_CORRUPT|ZFL_DEFLATED|ZFL_DEFLATE64D|ZFL_BZIP2)
typedef struct zipfile_s
@ -788,9 +791,9 @@ static int QDECL FSZIP_GeneratePureCRC(searchpathfuncs_t *handle, int seed, int
}
if (crctype || numcrcs < 1)
result = Com_BlockChecksum(filecrcs, numcrcs*sizeof(int));
result = CalcHashInt(&hash_md4, filecrcs, numcrcs*sizeof(int));
else
result = Com_BlockChecksum(filecrcs+1, (numcrcs-1)*sizeof(int));
result = CalcHashInt(&hash_md4, filecrcs+1, (numcrcs-1)*sizeof(int));
BZ_Free(filecrcs);
return result;
@ -1030,6 +1033,95 @@ static struct decompressstate *FSZIP_Deflate_Init(zipfile_t *source, qofs_t star
}
#endif
#if defined(ZLIB_DEFLATE64) && !defined(AVAIL_ZLIB)
#undef ZLIB_DEFLATE64 //don't be silly.
#endif
#if defined(ZLIB_DEFLATE64)
#include "infback9.h" //an obscure compile-your-own part of zlib.
struct def64ctx
{
vfsfile_t *src;
vfsfile_t *dst;
qofs_t csize;
qofs_t usize;
unsigned int crc;
qbyte inbuf[0x8000];
};
static unsigned int FSZIP_Deflate64_Grab(void *vctx, unsigned char **bufptr)
{
struct def64ctx *ctx = vctx;
int avail;
avail = sizeof(ctx->inbuf);
if (avail > ctx->csize)
avail = ctx->csize; //don't over-read.
if (avail <= 0)
{
*bufptr = NULL;
return 0;
}
avail = VFS_READ(ctx->src, ctx->inbuf, avail);
*bufptr = ctx->inbuf;
if (avail < 0)
avail = 0; //treated as eof...
ctx->csize -= avail;
return avail;
}
static int FSZIP_Deflate64_Spew(void *vctx, unsigned char *buf, unsigned int buflen)
{
struct def64ctx *ctx = vctx;
//update the crc
ctx->crc = crc32(ctx->crc, buf, buflen);
ctx->usize += buflen;
if (VFS_WRITE(ctx->dst, buf, buflen) != buflen)
return 1; //failure returns non-zero.
return 0;
}
//inflateBack stuff is apparently not restartable, and must read the entire file
static vfsfile_t *FSZIP_Deflate64(vfsfile_t *src, qofs_t csize, qofs_t usize, unsigned int crc32)
{
z_stream strm = {NULL};
qbyte window[65536];
struct def64ctx ctx;
ctx.src = src;
ctx.dst = VFSPIPE_Open(1, true);
ctx.csize = csize;
ctx.usize = 0;
ctx.crc = 0;
strm.data_type = Z_UNKNOWN;
inflateBack9Init(&strm, window);
//getting inflateBack9 to
if (Z_STREAM_END != inflateBack9(&strm, FSZIP_Deflate64_Grab, &ctx, FSZIP_Deflate64_Spew, &ctx))
{ //some stream error?
Con_Printf("Decompression error\n");
VFS_CLOSE(ctx.dst);
ctx.dst = NULL;
}
else if (ctx.csize != 0 || ctx.usize != usize)
{ //corrupt file table?
Con_Printf("Decompression size error\n");
Con_Printf("read %i of %i bytes\n", (unsigned)ctx.csize, (unsigned)csize);
Con_Printf("wrote %i of %i bytes\n", (unsigned)ctx.usize, (unsigned)usize);
VFS_CLOSE(ctx.dst);
ctx.dst = NULL;
}
else if (ctx.crc != crc32)
{ //corrupt file table?
Con_Printf("CRC32 error\n");
VFS_CLOSE(ctx.dst);
ctx.dst = NULL;
}
inflateBack9End(&strm);
return ctx.dst;
}
#endif
#ifdef AVAIL_BZLIB
//if the offset is still within our decompressed block then we can just rewind a smidge
static qboolean FSZIP_BZip2_Seek(struct decompressstate *st, qofs_t *offset, qofs_t newoffset)
@ -1403,13 +1495,40 @@ static vfsfile_t *QDECL FSZIP_OpenVFS(searchpathfuncs_t *handle, flocation_t *lo
vfsz->funcs.WriteBytes = NULL;
vfsz->funcs.seekstyle = SS_SLOW;
//NOTE: pf->name is the quakeified name, and may have an extra prefix/stripped prefix for certain zips - different from what you'd see if you opened the zip yourself. this is only relevant for debugging, whuch might be misleading but is not fatal.
if (!FSZIP_ValidateLocalHeader(zip, pf, &vfsz->startpos, &datasize))
{
Con_Printf("file %s:%s is incompatible or inconsistent with zip central directory\n", zip->filename, pf->name);
Con_Printf(CON_WARNING"file %s:%s is incompatible or inconsistent with zip central directory\n", zip->filename, pf->name);
Z_Free(vfsz);
return NULL;
}
if (flags & ZFL_DEFLATE64D)
{ //crap
#if defined(ZLIB_DEFLATE64)
vfsfile_t *tmp = NULL;
qofs_t startpos = vfsz->startpos;
qofs_t usize = vfsz->length;
qofs_t csize = datasize;
Z_Free(vfsz);
Con_Printf(CON_WARNING"file %s:%s was compressed with deflate64\n", zip->filename, pf->name);
if (Sys_LockMutex(zip->mutex))
{
VFS_SEEK(vfsz->parent->raw, startpos);
tmp = FSZIP_Deflate64(zip->raw, csize, usize, pf->crc);
Sys_UnlockMutex(zip->mutex);
}
return tmp;
#else
Con_Printf(CON_WARNING"%s:%s: deflate64 not supported\n", COM_SkipPath(zip->filename), pf->name);
Z_Free(vfsz);
return NULL;
#endif
}
if (flags & ZFL_DEFLATED)
{
#ifdef AVAIL_ZLIB
@ -1670,11 +1789,19 @@ static qboolean FSZIP_ValidateLocalHeader(zipfile_t *zip, zpackfile_t *zfile, qo
return false; //FIXME: proper spanned zips fragment compressed data over multiple spans, but we don't support that
if (local.cmethod == 0)
return (zfile->flags & (ZFL_STORED|ZFL_CORRUPT|ZFL_DEFLATED|ZFL_BZIP2)) == ZFL_STORED;
return (zfile->flags & ZFL_COMPRESSIONTYPE) == ZFL_STORED;
#if defined(AVAIL_ZLIB)
if (local.cmethod == 8)
return (zfile->flags & (ZFL_STORED|ZFL_CORRUPT|ZFL_DEFLATED|ZFL_BZIP2)) == ZFL_DEFLATED;
return (zfile->flags & ZFL_COMPRESSIONTYPE) == ZFL_DEFLATED;
#endif
#if defined(ZLIB_DEFLATE64)
if (local.cmethod == 9)
return (zfile->flags & ZFL_COMPRESSIONTYPE) == ZFL_DEFLATE64D;
#endif
#ifdef AVAIL_BZLIB
if (local.cmethod == 12)
return (zfile->flags & (ZFL_STORED|ZFL_CORRUPT|ZFL_DEFLATED|ZFL_BZIP2)) == ZFL_BZIP2;
return (zfile->flags & ZFL_COMPRESSIONTYPE) == ZFL_BZIP2;
#endif
return false; //some other method that we don't know.
}
@ -1850,7 +1977,8 @@ static qboolean FSZIP_ReadCentralEntry(zipfile_t *zip, qbyte *data, struct zipce
//7: tokenize
else if (entry->cmethod == 8) //8: deflate
entry->flags |= ZFL_DEFLATED;
//9: deflate64 - patented. sometimes written by microsoft's crap, so this might be problematic. only minor improvements.
else if (entry->cmethod == 9) //9: deflate64 - patented. sometimes written by microsoft's crap, so this might be problematic. only minor improvements.
entry->flags |= ZFL_DEFLATE64D;
//10: implode
else if (entry->cmethod == 12) //12: bzip2
entry->flags |= ZFL_BZIP2;

View File

@ -69,7 +69,6 @@ struct cminfo_s;
void CM_Init(void);
static qboolean CM_HeadnodeVisible (struct model_s *mod, int nodenum, const qbyte *visbits);
static qboolean VARGS CM_AreasConnected (struct model_s *mod, unsigned int area1, unsigned int area2);
static size_t CM_WriteAreaBits (struct model_s *mod, qbyte *buffer, size_t buffersize, int area, qboolean merge);
static qbyte *CM_ClusterPVS (struct model_s *mod, int cluster, pvsbuffer_t *buffer, pvsmerge_t merge);
@ -82,9 +81,12 @@ static void CM_SetAreaPortalState (model_t *mod, unsigned int portalnum, unsigne
static size_t CM_SaveAreaPortalBlob (model_t *mod, void **data);
static size_t CM_LoadAreaPortalBlob (model_t *mod, void *ptr, size_t ptrsize);
#ifdef HAVE_SERVER
static unsigned int Q23BSP_FatPVS(model_t *mod, const vec3_t org, pvsbuffer_t *buffer, qboolean merge);
static qboolean Q23BSP_EdictInFatPVS(model_t *mod, const struct pvscache_s *ent, const qbyte *pvs, const int *areas);
static void Q23BSP_FindTouchedLeafs(model_t *mod, struct pvscache_s *ent, const float *mins, const float *maxs);
static qboolean CM_HeadnodeVisible (struct model_s *mod, int nodenum, const qbyte *visbits);
#endif
#ifdef HAVE_CLIENT
static void CM_PrepareFrame(model_t *mod, refdef_t *refdef, int area, int viewclusters[2], pvsbuffer_t *vis, qbyte **entvis_out, qbyte **surfvis_out);
@ -557,7 +559,8 @@ static void Patch_Evaluate_QuadricBezier( float t, const vec_t *point0, const ve
Patch_Evaluate
===============
*/
static void Patch_Evaluate( const vec_t *p, const unsigned short *numcp, const int *tess, vec_t *dest, int comp )
#define Patch_Evaluate(p,numcp,tess,dest, comp) Patch_EvaluateStride(p,comp,numcp,tess,dest,comp,comp)
static void Patch_EvaluateStride(const vec_t *p, int pstride, const unsigned short *numcp, const int *tess, vec_t *dest, int deststride, int comp)
{
int num_patches[2], num_tess[2];
int index[3], dstpitch, i, u, v, x, y;
@ -568,14 +571,15 @@ static void Patch_Evaluate( const vec_t *p, const unsigned short *numcp, const i
if (!tess[0] || !tess[1])
{ //not really a patch
for( i = 0; i < comp*numcp[1]*numcp[0]; i++ )
dest[i] = p[i];
for( u = 0; u < numcp[1]*numcp[0]; u++, dest += deststride, p += pstride)
for( i = 0; i < comp; i++ )
dest[i] = p[i];
return;
}
num_patches[0] = numcp[0] / 2;
num_patches[1] = numcp[1] / 2;
dstpitch = ( num_patches[0] * tess[0] + 1 ) * comp;
dstpitch = ( num_patches[0] * tess[0] + 1 ) * deststride;
step[0] = 1.0f / (float)tess[0];
step[1] = 1.0f / (float)tess[1];
@ -603,24 +607,113 @@ static void Patch_Evaluate( const vec_t *p, const unsigned short *numcp, const i
// current 3x3 patch control points
for( i = 0; i < 3; i++ )
{
pv[i][0] = &p[( index[0]+i ) * comp];
pv[i][1] = &p[( index[1]+i ) * comp];
pv[i][2] = &p[( index[2]+i ) * comp];
pv[i][0] = &p[( index[0]+i ) * pstride];
pv[i][1] = &p[( index[1]+i ) * pstride];
pv[i][2] = &p[( index[2]+i ) * pstride];
}
tvec = dest + v * tess[1] * dstpitch + u * tess[0] * comp;
tvec = dest + v * tess[1] * dstpitch + u * tess[0] * deststride;
for( y = 0, t = 0.0f; y < num_tess[1]; y++, t += step[1], tvec += dstpitch )
{
Patch_Evaluate_QuadricBezier( t, pv[0][0], pv[0][1], pv[0][2], v1, comp );
Patch_Evaluate_QuadricBezier( t, pv[1][0], pv[1][1], pv[1][2], v2, comp );
Patch_Evaluate_QuadricBezier( t, pv[2][0], pv[2][1], pv[2][2], v3, comp );
for( x = 0, tvec2 = tvec, s = 0.0f; x < num_tess[0]; x++, s += step[0], tvec2 += comp )
for( x = 0, tvec2 = tvec, s = 0.0f; x < num_tess[0]; x++, s += step[0], tvec2 += deststride )
Patch_Evaluate_QuadricBezier( s, v1, v2, v3, tvec2, comp );
}
}
}
}
#ifdef TERRAIN
#include "gl_terrain.h"
patchtessvert_t *PatchInfo_Evaluate(const qcpatchvert_t *cp, const unsigned short patch_cp[2], const short subdiv[2], unsigned short *size)
{
int step[2], flat[2];
float subdivlevel;
unsigned int numverts;
patchtessvert_t *out;
int i;
if (subdiv[0]>=0 && subdiv[1]>=0)
{ //fixed
step[0] = subdiv[0];
step[1] = subdiv[1];
}
else
{
// find the degree of subdivision in the u and v directions
subdivlevel = bound(1, r_subdivisions.ival, 15);
Patch_GetFlatness ( subdivlevel, cp->v, sizeof(*cp)/sizeof(vec_t), patch_cp, flat );
step[0] = 1 << flat[0];
step[1] = 1 << flat[1];
}
if (!step[0] || !step[1])
{
size[0] = patch_cp[0];
size[1] = patch_cp[1];
}
else
{
size[0] = ( patch_cp[0] >> 1 ) * step[0] + 1;
size[1] = ( patch_cp[1] >> 1 ) * step[1] + 1;
}
if( size[0] <= 0 || size[1] <= 0 )
return NULL;
numverts = (unsigned int)size[0] * size[1];
// fill in
out = BZ_Malloc(sizeof(*out) * numverts);
for (i = 0; i < numverts*sizeof(*out)/sizeof(vec_t); i++)
((vec_t *)out)[i] = -1;
Patch_EvaluateStride ( cp->v, sizeof(*cp)/sizeof(vec_t), patch_cp, step, out->v, sizeof(*out)/sizeof(vec_t), countof(cp->v));
Patch_EvaluateStride ( cp->rgba, sizeof(*cp)/sizeof(vec_t), patch_cp, step, out->rgba, sizeof(*out)/sizeof(vec_t), countof(cp->rgba));
Patch_EvaluateStride ( cp->tc, sizeof(*cp)/sizeof(vec_t), patch_cp, step, out->tc, sizeof(*out)/sizeof(vec_t), countof(cp->tc));
return out;
}
unsigned int PatchInfo_EvaluateIndexes(const unsigned short *size, index_t *out_indexes)
{
int i, u, v, p;
// compute new indexes avoiding adding invalid triangles
unsigned int numindexes = 0;
index_t *indexes = out_indexes;
for (v = 0, i = 0; v < size[1]-1; v++)
{
for (u = 0; u < size[0]-1; u++, i += 6)
{
indexes[0] = p = v * size[0] + u;
indexes[1] = p + size[0];
indexes[2] = p + 1;
// if ( !VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[1]]) &&
// !VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[2]]) &&
// !VectorEquals(mesh->xyz_array[indexes[1]], mesh->xyz_array[indexes[2]]) )
{
indexes += 3;
numindexes += 3;
}
indexes[0] = p + 1;
indexes[1] = p + size[0];
indexes[2] = p + size[0] + 1;
// if ( !VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[1]]) &&
// !VectorEquals(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[2]]) &&
// !VectorEquals(mesh->xyz_array[indexes[1]], mesh->xyz_array[indexes[2]]) )
{
indexes += 3;
numindexes += 3;
}
}
}
return numindexes;
}
#endif
#define PLANE_NORMAL_EPSILON 0.00001
@ -4123,7 +4216,7 @@ static void CM_OpenAllPortals(model_t *mod, char *ents) //this is a compleate ha
#endif
#if defined(HAVE_SERVER) && defined(Q3BSPS)
#if defined(Q3BSPS)
static void CalcClusterPHS(cminfo_t *prv, int cluster)
{
int j, k, l, index;
@ -4167,6 +4260,8 @@ static void CalcClusterPHS(cminfo_t *prv, int cluster)
}
prv->phscalced[cluster>>3] |= 1<<(cluster&7);
}
#endif
#if defined(HAVE_SERVER) && defined(Q3BSPS)
static void CMQ3_CalcPHS (model_t *mod)
{
cminfo_t *prv = (cminfo_t*)mod->meshinfo;
@ -4517,7 +4612,7 @@ static cmodel_t *CM_LoadMap (model_t *mod, qbyte *filein, size_t filelen, qboole
return NULL;
}
checksum = LittleLong (Com_BlockChecksum (buf, length));
checksum = LittleLong (CalcHashInt(&hash_md4, buf, length));
header = *(q2dheader_t *)(buf);
header.ident = LittleLong(header.ident);
@ -5197,12 +5292,14 @@ static int CM_PointLeafnum_r (model_t *mod, const vec3_t p, int num)
return -1 - num;
}
#ifdef HAVE_SERVER
static int CM_PointLeafnum (model_t *mod, const vec3_t p)
{
if (!mod || mod->loadstate != MLS_LOADED)
return 0; // sound may call this without map loaded
return CM_PointLeafnum_r (mod, p, 0);
}
#endif
static int CM_PointCluster (model_t *mod, const vec3_t p, int *area)
{
@ -6884,6 +6981,7 @@ static qbyte *CM_ClusterPHS (model_t *mod, int cluster, pvsbuffer_t *buffer)
return buffer->buffer;
}
#ifdef HAVE_SERVER
static unsigned int SV_Q2BSP_FatPVS (model_t *mod, const vec3_t org, pvsbuffer_t *result, qboolean merge)
{
int leafs[64];
@ -7053,6 +7151,7 @@ static void Q23BSP_FindTouchedLeafs(model_t *model, struct pvscache_s *ent, cons
}
}
}
#endif
/*
===============================================================================
@ -7314,6 +7413,7 @@ static size_t CM_LoadAreaPortalBlob (model_t *mod, void *ptr, size_t ptrsize)
return 0;
}
#ifdef HAVE_SERVER
/*
=============
CM_HeadnodeVisible
@ -7344,6 +7444,7 @@ static qboolean CM_HeadnodeVisible (model_t *mod, int nodenum, const qbyte *visb
return true;
return CM_HeadnodeVisible(mod, node->childnum[1], visbits);
}
#endif
static unsigned int Q2BSP_PointContents(model_t *mod, const vec3_t axis[3], const vec3_t p)
{

View File

@ -695,7 +695,10 @@ static void CertLog_Write(void)
VFS_PUTS(f, certhex);
VFS_PRINTF(f, "\" %i\n", l->trusted?true:false);
}
VFS_CLOSE(f);
}
else
Con_Printf(CON_ERROR"Unable to write %s\n", CERTLOG_FILENAME);
}
static void CertLog_Purge(void)
{
@ -753,6 +756,7 @@ static void CertLog_Import(const char *filename)
}
CertLog_Update(addressstring, certdata, certsize, atoi(trusted));
}
VFS_CLOSE(f);
}
static void CertLog_UntrustAll_f(void)
{
@ -792,9 +796,10 @@ static void CertLog_Add_Prompted(void *vctx, promptbutton_t button)
}
qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize, unsigned int certlogproblems)
{ //this is specifically for dtls certs.
extern cvar_t net_enable_dtls;
struct certlog_s *l;
qboolean trusted = (net_enable_dtls.ival >= 2);
char digest[DIGEST_MAXSIZE];
char fp[DIGEST_MAXSIZE*2+1];
if (certlog_curprompt)
return false;
@ -808,11 +813,20 @@ qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize,
{ //cert is new, but we don't care about full trust. don't bother to prompt when the user doesn't much care.
//(but do pin so we at least know when its MITMed after the fact)
Con_Printf(CON_WARNING"Auto-Pinning certificate for %s."CON_DEFAULT" ^[/seta %s 2^]+ for actual security.\n", hostname, net_enable_dtls.name);
if (certsize)
Base64_EncodeBlockURI(digest, CalcHash(&hash_sha1, digest, sizeof(digest), cert, certsize), fp, sizeof(fp));
else
strcpy(fp, "<No Certificate>");
Con_Printf(S_COLOR_GRAY" fp: %s\n", fp);
CertLog_Update(hostname, cert, certsize, false);
CertLog_Write();
}
else if (!l || l->certsize != certsize || memcmp(l->cert, cert, certsize) || (trusted && !l->trusted))
{ //new or different
if (certsize)
Base64_EncodeBlockURI(digest, CalcHash(&hash_sha1, digest, sizeof(digest), cert, certsize), fp, sizeof(fp));
else
strcpy(fp, "<No Certificate>");
if (qrenderer)
{
unsigned int i;
@ -820,7 +834,7 @@ qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize,
char *text;
const char *accepttext;
const char *lines[] = {
va(localtext("Certificate for %s\n"), hostname),
va(localtext("Certificate for %s\n(fp:"S_COLOR_GRAY"%s"S_COLOR_WHITE")\n"), hostname, fp),
(certlogproblems&CERTLOG_WRONGHOST)?localtext("^1Certificate does not match host\n"):"",
((certlogproblems&(CERTLOG_MISSINGCA|CERTLOG_WRONGHOST))==CERTLOG_MISSINGCA)?localtext("^1Certificate authority is untrusted.\n"):"",
(certlogproblems&CERTLOG_EXPIRED)?localtext("^1Expired Certificate\n"):"",

View File

@ -51,7 +51,7 @@ typedef struct {
} MD4_CTX;
void MD4Init (MD4_CTX *);
void MD4Update (MD4_CTX *, unsigned char *, unsigned int);
void MD4Update (MD4_CTX *, const unsigned char *, size_t);
void MD4Final (unsigned char [16], MD4_CTX *);
@ -84,9 +84,9 @@ These notices must be retained in any copies of any part of this documentation a
#define S33 11
#define S34 15
static void MD4Transform (UINT4 [4], unsigned char [64]);
static void MD4Transform (UINT4 [4], const unsigned char [64]);
static void Encode (unsigned char *, UINT4 *, unsigned int);
static void Decode (UINT4 *, unsigned char *, unsigned int);
static void Decode (UINT4 *, const unsigned char *, unsigned int);
static unsigned char PADDING[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
@ -122,7 +122,7 @@ context->state[3] = 0x10325476;
}
/* MD4 block update operation. Continues an MD4 message-digest operation, processing another message block, and updating the context. */
void MD4Update (MD4_CTX *context, unsigned char *input, unsigned int inputLen)
void MD4Update (MD4_CTX *context, const unsigned char *input, size_t inputLen)
{
unsigned int i, index, partLen;
@ -182,7 +182,7 @@ void MD4Final (unsigned char digest[16], MD4_CTX *context)
/* MD4 basic transformation. Transforms state based on block. */
static void MD4Transform (UINT4 state[4], unsigned char block[64])
static void MD4Transform (UINT4 state[4], const unsigned char block[64])
{
UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
@ -267,7 +267,7 @@ static void Encode (unsigned char *output, UINT4 *input, unsigned int len)
/* Decodes input (unsigned char) into output (UINT4). Assumes len is a multiple of 4. */
static void Decode (UINT4 *output, unsigned char *input, unsigned int len)
static void Decode (UINT4 *output, const unsigned char *input, unsigned int len)
{
unsigned int i, j;
@ -276,40 +276,13 @@ for (i = 0, j = 0; j < len; i++, j += 4)
}
//===================================================================
unsigned int Com_BlockChecksum (void *buffer, int length)
#include "quakedef.h"
hashfunc_t hash_md4 =
{
unsigned int digest[4];
unsigned int val;
MD4_CTX ctx;
16, //digest size
sizeof(MD4_CTX),
(void(*)(void*ctx))MD4Init,
(void(*)(void*ctx,const void*in,size_t))MD4Update,
(void(*)(qbyte*out,void*ctx))MD4Final,
};
MD4Init (&ctx);
MD4Update (&ctx, (unsigned char *)buffer, length);
MD4Final ( (unsigned char *)digest, &ctx);
val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3];
return val;
}
void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf)
{
MD4_CTX ctx;
MD4Init (&ctx);
MD4Update (&ctx, (unsigned char *)buffer, len);
MD4Final ( outbuf, &ctx);
}
void Com_BlocksChecksum (int blocks, void **buffer, int *len, unsigned char *outbuf)
{
MD4_CTX ctx;
MD4Init (&ctx);
while(blocks --> 0)
{
MD4Update (&ctx, (unsigned char *)*buffer++, *len++);
}
MD4Final (outbuf, &ctx);
}

View File

@ -151,17 +151,18 @@ neterr_t NET_SendPacket (struct ftenet_connections_s *col, int length, const voi
int NET_LocalAddressForRemote(struct ftenet_connections_s *collection, netadr_t *remote, netadr_t *local, int idx);
void NET_PrintAddresses(struct ftenet_connections_s *collection);
qboolean NET_AddressSmellsFunny(netadr_t *a);
qboolean NET_EnsureRoute(struct ftenet_connections_s *collection, char *routename, char *host, netadr_t *adr);
struct dtlspeercred_s;
qboolean NET_EnsureRoute(struct ftenet_connections_s *collection, char *routename, const struct dtlspeercred_s *peerinfo, netadr_t *adr, qboolean outgoing);
void NET_TerminateRoute(struct ftenet_connections_s *collection, netadr_t *adr);
void NET_PrintConnectionsStatus(struct ftenet_connections_s *collection);
enum addressscope_e
{
ASCOPE_PROCESS=0,
ASCOPE_HOST=1,
ASCOPE_LINK=2,
ASCOPE_LAN=3,
ASCOPE_NET=4
ASCOPE_PROCESS=0, //unusable
ASCOPE_HOST=1, //unroutable
ASCOPE_LINK=2, //unpredictable
ASCOPE_LAN=3, //private
ASCOPE_NET=4 //aka hopefully globally routable
};
enum addressscope_e NET_ClassifyAddress(netadr_t *adr, const char **outdesc);
@ -174,6 +175,7 @@ char *NET_AdrToString (char *s, int len, netadr_t *a);
char *NET_SockadrToString (char *s, int len, struct sockaddr_qstorage *a, size_t sizeofa);
char *NET_BaseAdrToString (char *s, int len, netadr_t *a);
size_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhint, struct sockaddr_qstorage *sadr, int *addrfamily, int *addrsize, size_t addrcount);
qboolean NET_StringToAdr_NoDNS(const char *address, int port, netadr_t *out);
#define NET_StringToSockaddr(s,p,a,f,z) (NET_StringToSockaddr2(s,p,NA_INVALID,a,f,z,1)>0)
size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t addrcount, const char **pathstart);
#define NET_StringToAdr(s,p,a) NET_StringToAdr2(s,p,a,1,NULL)
@ -191,23 +193,27 @@ qboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *na
enum certprops_e
{
QCERT_PEERFINGERPRINT
QCERT_ISENCRYPTED, //0 or error
QCERT_PEERSUBJECT, //null terminated. should be a hash of the primary cert, ignoring chain.
QCERT_PEERCERTIFICATE, //should be the primary cert, ignoring chain. no fixed maximum size required, mostly 2k but probably best to allow at leasy 5k.. or 8k.
QCERT_LOCALCERTIFICATE, //the cert we're using/advertising. may have no context. to tell people what fp to expect.
};
size_t NET_GetConnectionCertificate(struct ftenet_connections_s *col, netadr_t *a, enum certprops_e prop, char *out, size_t outsize);
int NET_GetConnectionCertificate(struct ftenet_connections_s *col, netadr_t *a, enum certprops_e prop, char *out, size_t outsize);
#ifdef HAVE_DTLS
struct dtlscred_s;
struct dtlsfuncs_s;
qboolean NET_DTLS_Create(struct ftenet_connections_s *col, netadr_t *to, const struct dtlscred_s *cred);
qboolean NET_DTLS_Create(struct ftenet_connections_s *col, netadr_t *to, const struct dtlscred_s *cred, qboolean outgoing);
qboolean NET_DTLS_Decode(struct ftenet_connections_s *col);
qboolean NET_DTLS_Disconnect(struct ftenet_connections_s *col, netadr_t *to);
void NET_DTLS_Timeouts(struct ftenet_connections_s *col);
extern cvar_t dtls_psk_hint, dtls_psk_user, dtls_psk_key;
extern cvar_t net_enable_dtls;
#endif
#ifdef SUPPORT_ICE
neterr_t ICE_SendPacket(size_t length, const void *data, netadr_t *to);
void ICE_Terminate(netadr_t *to); //if we kicked the client/etc, kill their ICE too.
qboolean ICE_IsEncrypted(netadr_t *to);
int ICE_GetPeerCertificate(netadr_t *to, enum certprops_e prop, char *out, size_t outsize);
void ICE_Init(void);
#endif
extern cvar_t timeout;

View File

@ -203,6 +203,7 @@ unsigned int Net_PextMask(unsigned int protover, qboolean fornq)
#ifdef PEXT_Q3BSP
PEXT_Q3BSP |
#endif
PEXT_TE_BULLET | //qw's gunshot+explosions etc.
PEXT_FLOATCOORDS | PEXT_HLBSP;
//these all depend fully upon the player/entity deltas, and don't make sense for NQ. Implement PEXT2_REPLACEMENTDELTAS instead.

File diff suppressed because it is too large Load Diff

View File

@ -99,6 +99,7 @@
#define GNUTLS_X509_STUFF \
GNUTLS_FUNC(gnutls_certificate_server_set_request,void,(gnutls_session_t session, gnutls_certificate_request_t req)) \
GNUTLS_FUNC(gnutls_sec_param_to_pk_bits,unsigned int,(gnutls_pk_algorithm_t algo, gnutls_sec_param_t param)) \
GNUTLS_FUNC(gnutls_x509_crt_init,int,(gnutls_x509_crt_t * cert)) \
GNUTLS_FUNC(gnutls_x509_crt_deinit,void,(gnutls_x509_crt_t cert)) \
@ -108,6 +109,7 @@
GNUTLS_FUNC(gnutls_x509_crt_set_expiration_time,int,(gnutls_x509_crt_t cert, time_t exp_time)) \
GNUTLS_FUNC(gnutls_x509_crt_set_serial,int,(gnutls_x509_crt_t cert, const void *serial, size_t serial_size)) \
GNUTLS_FUNC(gnutls_x509_crt_set_dn,int,(gnutls_x509_crt_t crt, const char *dn, const char **err)) \
GNUTLS_FUNC(gnutls_x509_crt_get_dn3,int,(gnutls_x509_crt_t crt, gnutls_datum_t * dn, unsigned flags)) \
GNUTLS_FUNC(gnutls_x509_crt_set_issuer_dn,int,(gnutls_x509_crt_t crt,const char *dn, const char **err)) \
GNUTLS_FUNC(gnutls_x509_crt_set_key,int,(gnutls_x509_crt_t crt, gnutls_x509_privkey_t key)) \
GNUTLS_FUNC(gnutls_x509_crt_export2,int,(gnutls_x509_crt_t cert, gnutls_x509_crt_fmt_t format, gnutls_datum_t * out)) \
@ -123,7 +125,9 @@
GNUTLS_FUNC(gnutls_pubkey_init,int,(gnutls_pubkey_t * key)) \
GNUTLS_FUNC(gnutls_pubkey_deinit,void,(gnutls_pubkey_t key)) \
GNUTLS_FUNC(gnutls_pubkey_import_x509,int,(gnutls_pubkey_t key, gnutls_x509_crt_t crt, unsigned int flags)) \
GNUTLS_FUNC(gnutls_pubkey_verify_hash2,int,(gnutls_pubkey_t key, gnutls_sign_algorithm_t algo, unsigned int flags, const gnutls_datum_t * hash, const gnutls_datum_t * signature))
GNUTLS_FUNC(gnutls_pubkey_verify_hash2,int,(gnutls_pubkey_t key, gnutls_sign_algorithm_t algo, unsigned int flags, const gnutls_datum_t * hash, const gnutls_datum_t * signature)) \
GNUTLS_FUNC(gnutls_certificate_get_ours,const gnutls_datum_t*,(gnutls_session_t session)) \
GNUTLS_FUNC(gnutls_certificate_get_crt_raw,int,(gnutls_certificate_credentials_t sc, unsigned idx1, unsigned idx2, gnutls_datum_t * cert))
#define GNUTLS_FUNCS \
@ -548,7 +552,7 @@ static int QDECL SSL_CheckFingerprint(gnutls_session_t session)
if (!memcmp(digest, file->peerdigest, file->peerhashfunc->digestsize))
return 0;
}
Con_DPrintf(CON_ERROR "%s: rejecting certificate\n", file->certname);
Con_Printf(CON_ERROR "%s: rejecting certificate\n", file->certname);
return GNUTLS_E_CERTIFICATE_ERROR;
}
#endif
@ -1084,7 +1088,10 @@ qboolean SSL_InitGlobal(qboolean isserver)
#endif
}
else
{
qgnutls_certificate_set_verify_function (xcred[isserver], SSL_CheckCert);
// qgnutls_certificate_set_retrieve_function (xcred[isserver], SSL_FindClientCert);
}
#endif
}
else
@ -1151,7 +1158,7 @@ static int GetPSKForServer(gnutls_session_t sess, char **username, gnutls_datum_
if (strcmp(svhint, dtls_psk_user.string) || CalcHashInt(&hash_sha1, dtls_psk_key.string, strlen(dtls_psk_key.string)) != 0x3dd348e4)
{
Con_Printf(CON_WARNING "Possible QEx Server, please set your ^[%s\\type\\%s^] and ^[%s\\type\\%s^] cvars correctly, their current values are likely to crash the server.\n", dtls_psk_user.name,dtls_psk_user.name, dtls_psk_key.name,dtls_psk_key.name);
return 0; //don't report anything.
return -1; //don't report anything.
}
}
}
@ -1174,7 +1181,10 @@ static int GetPSKForServer(gnutls_session_t sess, char **username, gnutls_datum_
static qboolean SSL_InitConnection(gnutlsfile_t *newf, qboolean isserver, qboolean datagram)
{
// Initialize TLS session
qgnutls_init (&newf->session, GNUTLS_NONBLOCK|(isserver?GNUTLS_SERVER:GNUTLS_CLIENT)|(datagram?GNUTLS_DATAGRAM:0));
qgnutls_init (&newf->session, ((newf->certcred)?GNUTLS_FORCE_CLIENT_CERT:0)
|GNUTLS_NONBLOCK
|(isserver?GNUTLS_SERVER:GNUTLS_CLIENT)
|(datagram?GNUTLS_DATAGRAM:0));
if (!isserver)
qgnutls_server_name_set(newf->session, GNUTLS_NAME_DNS, newf->certname, strlen(newf->certname));
@ -1182,6 +1192,7 @@ static qboolean SSL_InitConnection(gnutlsfile_t *newf, qboolean isserver, qboole
if (newf->certcred)
{
qgnutls_certificate_server_set_request(newf->session, GNUTLS_CERT_REQUIRE); //we will need to validate their fingerprint.
qgnutls_credentials_set (newf->session, GNUTLS_CRD_CERTIFICATE, newf->certcred);
qgnutls_set_default_priority (newf->session);
}
@ -1192,6 +1203,8 @@ static qboolean SSL_InitConnection(gnutlsfile_t *newf, qboolean isserver, qboole
qgnutls_credentials_set (newf->session, GNUTLS_CRD_ANON, anoncred[isserver]);
#else
#ifdef HAVE_DTLS
qgnutls_certificate_server_set_request(newf->session, GNUTLS_CERT_REQUEST); //request a cert, we'll use it for fingerprints.
if (datagram && !isserver)
{ //do psk as needed. we can still do the cert stuff if the server isn't doing psk.
gnutls_psk_client_credentials_t pskcred;
@ -1402,16 +1415,22 @@ static void *GNUDTLS_CreateContext(const dtlscred_t *credinfo, void *cbctx, nete
// Sys_Printf("DTLS_CreateContext: server=%i\n", isserver);
if (credinfo && credinfo->local.cert && credinfo->local.key && credinfo->peer.hash)
if (credinfo && ((credinfo->local.cert && credinfo->local.key) || credinfo->peer.hash))
{
gnutls_datum_t pub = {credinfo->local.cert, credinfo->local.certsize},
priv = {credinfo->local.key, credinfo->local.keysize};
qgnutls_certificate_allocate_credentials (&newf->certcred);
qgnutls_certificate_set_x509_key_mem(newf->certcred, &pub, &priv, GNUTLS_X509_FMT_DER);
if (credinfo->local.cert && credinfo->local.key)
{
gnutls_datum_t pub = {credinfo->local.cert, credinfo->local.certsize},
priv = {credinfo->local.key, credinfo->local.keysize};
qgnutls_certificate_set_x509_key_mem(newf->certcred, &pub, &priv, GNUTLS_X509_FMT_DER);
}
newf->peerhashfunc = credinfo->peer.hash;
memcpy(newf->peerdigest, credinfo->peer.digest, newf->peerhashfunc->digestsize);
qgnutls_certificate_set_verify_function (newf->certcred, SSL_CheckFingerprint);
if (credinfo->peer.hash)
{
newf->peerhashfunc = credinfo->peer.hash;
memcpy(newf->peerdigest, credinfo->peer.digest, newf->peerhashfunc->digestsize);
qgnutls_certificate_set_verify_function (newf->certcred, SSL_CheckFingerprint);
}
}
SSL_SetCertificateName(newf, credinfo?credinfo->peer.name:NULL);
@ -1442,6 +1461,9 @@ static neterr_t GNUDTLS_Transmit(void *ctx, const qbyte *data, size_t datasize)
return NETERR_DISCONNECTED;
}
if (!datasize)
return NETERR_SENT;
ret = qgnutls_record_send(f->session, data, datasize);
if (ret < 0)
{
@ -1462,17 +1484,39 @@ static neterr_t GNUDTLS_Received(void *ctx, sizebuf_t *message)
if (f->challenging)
{
int cli_addr = 0xdeadbeef; //FIXME: replace with client's IP:port.
size_t asize;
safeswitch (net_from.type)
{
case NA_LOOPBACK: asize = 0; break;
case NA_IP: asize = sizeof(net_from.address.ip); break;
case NA_IPV6: asize = sizeof(net_from.address.ip6); break;
case NA_IPX: asize = sizeof(net_from.address.ipx); break;
#ifdef UNIXSOCKETS
case NA_UNIX: asize = (qbyte*)&net_from.address.un.path[net_from.address.un.len]-(qbyte*)&net_from.address; break; //unlikely to be spoofed...
#endif
#ifdef IRCCONNECT
//case NA_IRC:
#endif
#ifdef HAVE_WEBSOCKCL
//case NA_WEBSOCKET: //basically web browser.
#endif
#ifdef SUPPORT_ICE
case NA_ICE: asize = strlen(net_from.address.icename); break;
#endif
case NA_INVALID:
safedefault: return NETERR_NOROUTE;
}
memset(&f->prestate, 0, sizeof(f->prestate));
ret = qgnutls_dtls_cookie_verify(&cookie_key,
&cli_addr, sizeof(cli_addr),
&net_from.address, asize,
message->data, message->cursize,
&f->prestate);
if (ret == GNUTLS_E_BAD_COOKIE)
{
qgnutls_dtls_cookie_send(&cookie_key,
&cli_addr, sizeof(cli_addr),
&net_from.address, asize,
&f->prestate,
(gnutls_transport_ptr_t)f, DTLS_Push);
return NETERR_CLOGGED;
@ -1599,6 +1643,74 @@ static neterr_t GNUDTLS_Timeouts(void *ctx)
return NETERR_SENT;
}
static int GNUDTLS_GetPeerCertificate(void *ctx, enum certprops_e prop, char *out, size_t outsize)
{
gnutlsfile_t *f = (gnutlsfile_t *)ctx;
if (f && (f->challenging || f->handshaking))
return -1; //no cert locked down yet...
safeswitch(prop)
{
case QCERT_ISENCRYPTED:
return 0; //well, should be...
case QCERT_PEERSUBJECT:
{
unsigned int certcount;
const gnutls_datum_t *const certlist = qgnutls_certificate_get_peers(f->session, &certcount);
if (certlist)
{
gnutls_x509_crt_t cert = NULL;
gnutls_datum_t dn={NULL};
qgnutls_x509_crt_init(&cert);
qgnutls_x509_crt_import(cert, certlist, GNUTLS_X509_FMT_DER);
qgnutls_x509_crt_get_dn3(cert, &dn, 0);
if (dn.size >= outsize)
dn.size = -1; //too big...
else
{
memcpy(out, dn.data, dn.size);
out[dn.size] = 0;
}
(*qgnutls_free)(dn.data);
qgnutls_x509_crt_deinit(cert);
return (int)dn.size;
}
}
return -1;
case QCERT_PEERCERTIFICATE:
{
unsigned int certcount;
const gnutls_datum_t *const certlist = qgnutls_certificate_get_peers(f->session, &certcount);
if (certlist && certlist->size <= outsize)
{
memcpy(out, certlist->data, certlist->size);
return certlist->size;
}
}
return -1;
case QCERT_LOCALCERTIFICATE:
{
const gnutls_datum_t *cert;
gnutls_datum_t d;
if (f)
cert = qgnutls_certificate_get_ours(f->session);
else //no actual context? get our default dtls server cert.
{
qgnutls_certificate_get_crt_raw (xcred[true], 0/*first chain*/, 0/*primary one*/, &d);
cert = &d;
}
if (cert->size <= outsize)
{
memcpy(out, cert->data, cert->size);
return cert->size;
}
}
return -1;
safedefault:
return -1; //dunno what you want from me.
}
}
static qboolean GNUDTLS_GenTempCertificate(const char *subject, struct dtlslocalcred_s *qcred)
{
gnutls_datum_t priv = {NULL}, pub = {NULL};
@ -1681,7 +1793,7 @@ static const dtlsfuncs_t dtlsfuncs_gnutls =
GNUDTLS_Transmit,
GNUDTLS_Received,
GNUDTLS_Timeouts,
NULL,
GNUDTLS_GetPeerCertificate,
GNUDTLS_GenTempCertificate
};
static const dtlsfuncs_t *GNUDTLS_InitServer(void)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -223,6 +223,9 @@
#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0 //available on linux, no idea about other unixes. don't bug out too much... (d)tls needs this to not get constant SIGPIPE errors
#endif
#ifndef INADDR_LOOPBACK
#define INADDR_LOOPBACK 0x7f000001
@ -289,10 +292,11 @@ typedef struct
void (QDECL *AddRCandidateInfo)(struct icestate_s *con, struct icecandinfo_s *cand); //stuff that came from the peer.
void (QDECL *Close)(struct icestate_s *con, qboolean force); //bye then.
void (QDECL *CloseModule)(void *module); //closes all unclosed connections, with warning.
// struct icestate_s *(QDECL *Find)(void *module, const char *conname);
qboolean (QDECL *GetLCandidateSDP)(struct icestate_s *con, char *out, size_t valuesize); //retrieves candidates that need reporting to the peer.
struct icestate_s *(QDECL *Find)(void *module, const char *conname);
} icefuncs_t;
extern icefuncs_t iceapi;
extern cvar_t net_ice_broker;
#endif
#ifdef HAVE_EPOLL
@ -372,7 +376,7 @@ typedef struct dtlsfuncs_s
neterr_t (*Transmit)(void *ctx, const qbyte *data, size_t datasize);
neterr_t (*Received)(void *ctx, sizebuf_t *message); //operates in-place...
neterr_t (*Timeouts)(void *ctx);
void (*GetPeerCertificate)(void *ctx);
int (*GetPeerCertificate)(void *ctx, enum certprops_e prop, char *out, size_t outsize);
qboolean (*GenTempCertificate)(const char *subject, struct dtlslocalcred_s *cred);
} dtlsfuncs_t;
#ifdef HAVE_DTLS
@ -431,6 +435,9 @@ typedef struct ftenet_connections_s
size_t cursize;
qbyte data[1];
} *delayed_packets;
netadr_t srflx[2]; //ipv4, ipv6
unsigned int srflx_tid[3]; //to verify the above.
} ftenet_connections_t;
void ICE_Tick(void);

View File

@ -173,6 +173,7 @@ static plugin_t *Plug_Load(const char *file)
static enum fs_relative prefixes[] =
{
FS_BINARYPATH,
FS_LIBRARYPATH,
#ifndef ANDROID
FS_ROOT,
#endif
@ -514,7 +515,11 @@ static int QDECL Plug_Cmd_Argc(void)
//void Cvar_SetString (char *name, char *value);
static void QDECL Plug_Cvar_SetString(const char *name, const char *value)
{
cvar_t *var = Cvar_Get(name, value, 0, "Plugin vars");
cvar_t *var;
if (!value)
var = Cvar_FindVar(name);
else
var = Cvar_Get(name, value, 0, "Plugin vars");
if (var)
Cvar_Set(var, value);
}
@ -967,6 +972,7 @@ static qhandle_t QDECL Plug_FS_Open(const char *fname, qhandle_t *outhandle, int
#ifndef WEBCLIENT
f = NULL;
#else
Con_Printf("Plugin %s requesting %s\n", currentplug->name, fname);
handle = Plug_NewStreamHandle(STREAM_WEB);
pluginstreamarray[handle].dl = HTTP_CL_Get(fname, NULL, Plug_DownloadComplete);
pluginstreamarray[handle].dl->user_num = handle;
@ -1187,6 +1193,11 @@ void QDECL Plug_FS_EnumerateFiles(enum fs_relative fsroot, const char *match, in
}
}
unsigned int Plug_BlockChecksum(const void *data, size_t datasize)
{ //convienience function. we use md4 for legacy reasons (every qw engine must have an implementation)
return CalcHashInt(&hash_md4, data, datasize);
}
#if defined(HAVE_SERVER) && defined(HAVE_CLIENT)
static qboolean QDECL Plug_MapLog_Query(const char *packagename, const char *mapname, float *vals)
{
@ -1249,9 +1260,16 @@ void Plug_Initialise(qboolean fromgamedir)
{
if (!fromgamedir)
{
FS_NativePath("", FS_BINARYPATH, nat, sizeof(nat));
Con_DPrintf("Loading plugins from \"%s\"\n", nat);
Sys_EnumerateFiles(nat, PLUGINPREFIX"*" ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, Plug_EnumeratedRoot, NULL, NULL);
if (FS_NativePath("", FS_BINARYPATH, nat, sizeof(nat)))
{
Con_DPrintf("Loading plugins from \"%s\"\n", nat);
Sys_EnumerateFiles(nat, PLUGINPREFIX"*" ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, Plug_EnumeratedRoot, NULL, NULL);
}
if (FS_NativePath("", FS_LIBRARYPATH, nat, sizeof(nat)))
{
Con_DPrintf("Loading plugins from \"%s\"\n", nat);
Sys_EnumerateFiles(nat, PLUGINPREFIX"*" ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, Plug_EnumeratedRoot, NULL, NULL);
}
}
}
if (plug_loaddefault.ival & 1)
@ -1743,6 +1761,7 @@ int QDECL Plug_List_Print(const char *fname, qofs_t fsize, time_t modtime, void
void Plug_List_f(void)
{
char binarypath[MAX_OSPATH];
char librarypath[MAX_OSPATH];
char rootpath[MAX_OSPATH];
unsigned int u;
plugin_t *plug;
@ -1765,9 +1784,22 @@ void Plug_List_f(void)
while ((mssuck=strchr(binarypath, '\\')))
*mssuck = '/';
#endif
Con_DPrintf("Scanning for plugins at %s:\n", binarypath);
Con_Printf("Scanning for plugins at %s:\n", binarypath);
Sys_EnumerateFiles(binarypath, PLUGINPREFIX"*" ARCH_DL_POSTFIX, Plug_List_Print, binarypath, NULL);
}
if (FS_NativePath("", FS_LIBRARYPATH, librarypath, sizeof(librarypath)))
{
#ifdef _WIN32
char *mssuck;
while ((mssuck=strchr(librarypath, '\\')))
*mssuck = '/';
#endif
if (strcmp(librarypath, rootpath))
{
Con_Printf("Scanning for plugins at %s:\n", librarypath);
Sys_EnumerateFiles(librarypath, PLUGINPREFIX"*" ARCH_DL_POSTFIX, Plug_List_Print, librarypath, NULL);
}
}
if (FS_NativePath("", FS_ROOT, rootpath, sizeof(rootpath)))
{
#ifdef _WIN32
@ -1926,7 +1958,7 @@ static void *QDECL PlugBI_GetEngineInterface(const char *interfacename, size_t s
COM_GetFileExtension,
COM_FileBase,
COM_CleanUpPath,
Com_BlockChecksum,
Plug_BlockChecksum,
FS_LoadMallocFile,
FS_GetPackHashes,
@ -2023,10 +2055,15 @@ static void *QDECL PlugBI_GetEngineInterface(const char *interfacename, size_t s
InfoBuf_ValueForKey,
Info_ValueForKey,
Info_SetValueForKey,
#ifdef HAVE_SERVER
SV_DropClient,
SV_ExtractFromUserinfo,
SV_ChallengePasses,
#else
NULL,
NULL,
NULL,
#endif
};
if (structsize == sizeof(funcs))
return &funcs;

View File

@ -1473,8 +1473,9 @@ void PM_PlayerMove (float gamespeed)
pmove.jump_msec = 0;
}
PM_CheckJump ();
if (!movevars.bunnyfriction)
PM_CheckJump (); //qw-style bunny
PM_Friction ();
if (pmove.waterlevel >= 2)
@ -1486,6 +1487,9 @@ void PM_PlayerMove (float gamespeed)
else
PM_AirMove ();
if (movevars.bunnyfriction)
PM_CheckJump (); //nq-style bunny. note tick rate differences too.
/* //round to network precision
for (i = 0; i < 3; i++)
{

View File

@ -119,6 +119,7 @@ typedef struct {
qboolean stepdown;
qboolean slidyslopes;
qboolean autobunny;
qboolean bunnyfriction; //force at least one frame of friction when bunnying.
int stepheight;
qbyte coordtype; //FIXME: EZPEXT1_FLOATENTCOORDS should mean 4, but the result does not match ezquake/mvdsv's round-towards-origin which would result in inconsistencies. so player coords are rounded inconsistently.

View File

@ -3942,7 +3942,7 @@ void QCBUILTIN PF_findradius (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl
for (j=0 ; j<3 ; j++)
{
eorg[j] = org[j] - ent->v->origin[j];
eorg[j] -= bound(ent->v->mins[j], org[j], ent->v->maxs[j]);
eorg[j] -= bound(ent->v->mins[j], eorg[j], ent->v->maxs[j]);
}
}
else
@ -3972,7 +3972,7 @@ void QCBUILTIN PF_findradius (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl
for (j=0 ; j<3 ; j++)
{
eorg[j] = org[j] - ent->v->origin[j];
eorg[j] -= bound(ent->v->mins[j], org[j], ent->v->maxs[j]);
eorg[j] -= bound(ent->v->mins[j], eorg[j], ent->v->maxs[j]);
}
}
else
@ -5665,21 +5665,18 @@ static void QCBUILTIN PF_digest_internal (pubprogfuncs_t *prinst, struct globalv
unsigned char hexdig[sizeof(digest)*2+1];
if (!strcmp(hashtype, "MD4"))
{
digestsize = 16;
Com_BlockFullChecksum(str, len, digest);
}
digestsize = CalcHash(&hash_md4, digest, sizeof(digest), str, len);
//md5?
else if (!strcmp(hashtype, "SHA1"))
digestsize = CalcHash(&hash_sha1, digest, sizeof(digest), str, len);
else if (!strcmp(hashtype, "SHA224"))
digestsize = CalcHash(&hash_sha224, digest, sizeof(digest), str, len);
else if (!strcmp(hashtype, "SHA256"))
digestsize = CalcHash(&hash_sha256, digest, sizeof(digest), str, len);
else if (!strcmp(hashtype, "SHA384"))
digestsize = CalcHash(&hash_sha384, digest, sizeof(digest), str, len);
else if (!strcmp(hashtype, "SHA512"))
digestsize = CalcHash(&hash_sha512, digest, sizeof(digest), str, len);
else if (!strcmp(hashtype, "SHA2-224") || !strcmp(hashtype, "SHA224"))
digestsize = CalcHash(&hash_sha2_224, digest, sizeof(digest), str, len);
else if (!strcmp(hashtype, "SHA2-256") || !strcmp(hashtype, "SHA256"))
digestsize = CalcHash(&hash_sha2_256, digest, sizeof(digest), str, len);
else if (!strcmp(hashtype, "SHA2-384") || !strcmp(hashtype, "SHA384"))
digestsize = CalcHash(&hash_sha2_384, digest, sizeof(digest), str, len);
else if (!strcmp(hashtype, "SHA2-512") || !strcmp(hashtype, "SHA512"))
digestsize = CalcHash(&hash_sha2_512, digest, sizeof(digest), str, len);
else if (!strcmp(hashtype, "CRC16"))
digestsize = CalcHash(&hash_crc16, digest, sizeof(digest), str, len);
else

View File

@ -356,6 +356,7 @@ void QCBUILTIN PF_brush_findinvolume(pubprogfuncs_t *prinst, struct globalvars_s
void QCBUILTIN PF_patch_getcp(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_patch_getmesh(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_patch_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_patch_evaluate(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
#endif
void QCBUILTIN PF_touchtriggers(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);

View File

@ -2625,7 +2625,7 @@ bspx_header_t *BSPX_Setup(model_t *mod, char *filebase, size_t filelen, lump_t *
}
}
if (offs < filelen && mod && !mod->archive && mod_loadmappackages.ival)
if (offs < filelen && mod && !mod->archive && mod_loadmappackages.ival && filelen-offs > 22)//end-of-central-dir being 22 bytes sets a minimum zip size, which should slightly reduce false-positives.
{ //we have some sort of trailing junk... is it a zip?...
Mod_LoadMapArchive(mod, filebase+offs, filelen-offs);
}

View File

@ -11,7 +11,7 @@ struct q3gamecode_s
void (*SendAuthPacket)(struct ftenet_connections_s *socket, netadr_t *gameserver);
void (*SendConnectPacket)(struct ftenet_connections_s *socket, netadr_t *to, int challenge, int qport, infobuf_t *userinfo);
void (*Established)(void);
void (*VARGS SendClientCommand)(const char *fmt, ...) LIKEPRINTF(1);
void (VARGS *SendClientCommand)(const char *fmt, ...) LIKEPRINTF(1);
void (*SendCmd)(struct ftenet_connections_s *socket, struct usercmd_s *cmd, unsigned int movesequence, double gametime);
int (*ParseServerMessage) (sizebuf_t *msg);
void (*Disconnect) (struct ftenet_connections_s *socket); //disconnects from the server, killing all connection+cgame state.

View File

@ -194,7 +194,7 @@ unsigned int hashfunc_terminate_uint(const hashfunc_t *func, void *context)
r ^= digest[l]<<((l%sizeof(r))*8);
return r;
}
unsigned int CalcHashInt(const hashfunc_t *func, const unsigned char *data, size_t datasize)
unsigned int CalcHashInt(const hashfunc_t *func, const void *data, size_t datasize)
{
void *ctx = alloca(func->contextsize);
func->init(ctx);

View File

@ -515,7 +515,7 @@ static void sha256_finish (qbyte *digest, void *context)
memcpy(digest, hd->buf, 256/8);
}
hashfunc_t hash_sha224 =
hashfunc_t hash_sha2_224 =
{
224/8,
sizeof(SHA2_CONTEXT),
@ -523,7 +523,7 @@ hashfunc_t hash_sha224 =
sha2_write,
sha224_finish
};
hashfunc_t hash_sha256 =
hashfunc_t hash_sha2_256 =
{
256/8,
sizeof(SHA2_CONTEXT),
@ -547,7 +547,7 @@ static void sha512_finish (qbyte *digest, void *context)
memcpy(digest, hd->buf, 512/8);
}
hashfunc_t hash_sha384 =
hashfunc_t hash_sha2_384 =
{
384/8,
sizeof(SHA2_CONTEXT),
@ -555,7 +555,7 @@ hashfunc_t hash_sha384 =
sha2_write,
sha384_finish
};
hashfunc_t hash_sha512 =
hashfunc_t hash_sha2_512 =
{
512/8,
sizeof(SHA2_CONTEXT),

View File

@ -254,6 +254,16 @@ char *Z_StrDupf(const char *format, ...)
return string;
}
void Z_StrCatLen(char **ptr, const char *append, size_t newlen)
{
size_t oldlen = *ptr?strlen(*ptr):0;
char *newptr = BZ_Malloc(oldlen+newlen+1);
memcpy(newptr, *ptr, oldlen);
memcpy(newptr+oldlen, append, newlen);
newptr[oldlen+newlen] = 0;
BZ_Free(*ptr);
*ptr = newptr;
}
void Z_StrCat(char **ptr, const char *append)
{
size_t oldlen = *ptr?strlen(*ptr):0;

View File

@ -151,6 +151,7 @@ void QDECL ZG_FreeGroup(zonegroup_t *ctx);
char *Z_StrDupf(const char *format, ...);
void Z_StrCat(char **ptr, const char *append);
void Z_StrCatLen(char **ptr, const char *append, size_t newlen); //still doesn't allow nulls, but src doesn't need null termination.
/*
void *Hunk_Alloc (int size); // returns 0 filled memory

View File

@ -15,7 +15,7 @@
void Font_Init(void);
void Font_Shutdown(void);
struct font_s *Font_LoadFont(const char *fontfilename, float height, float scale, int outline);
struct font_s *Font_LoadFont(const char *fontfilename, float height, float scale, int outline, unsigned int flags);
void Font_Free(struct font_s *f);
void Font_BeginString(struct font_s *font, float vx, float vy, int *px, int *py);
void Font_BeginScaledString(struct font_s *font, float vx, float vy, float szx, float szy, float *px, float *py); /*avoid using*/
@ -80,6 +80,9 @@ const char *(VARGS *pFT_Error_String) (FT_Error error_code);
typedef unsigned int FT_Pixel_Mode; //for consistency even without freetype support.
#endif
#ifndef FT_PIXEL_MODE_MONO
#define FT_PIXEL_MODE_MONO 1
#endif
#ifndef FT_PIXEL_MODE_GRAY
#define FT_PIXEL_MODE_GRAY 2
#endif
@ -322,6 +325,7 @@ typedef struct font_s
unsigned short truecharheight; //what you actually got, for compat with dp's lets-use-the-wrong-size-for-double-padding-between-lines thing.
float scale; //some sort of poop
short outline;
unsigned int flags;
unsigned short faces;
fontface_t *face[MAX_FACES];
@ -473,6 +477,7 @@ void Font_Init(void)
fontplanes.shader = R_RegisterShader("ftefont", SUF_2D,
"{\n"
"fullrate\n" //don't hurt readability of text.
"if $nofixed\n"
"program default2d\n"
"endif\n"
@ -720,6 +725,32 @@ static struct charcache_s *Font_LoadGlyphData(font_t *f, CHARIDXTYPE charidx, FT
out += PLANEWIDTH;
}
}
else if (pixelmode == FT_PIXEL_MODE_MONO)
{ //1bit font (
for (y = -pad; y < 0; y++)
{
for (x = -pad; x < (int)bmw+pad; x++)
out[x].c = BORDERCOLOUR;
out += PLANEWIDTH;
}
for (; y < bmh; y++)
{
for (x = -pad; x < 0; x++)
out[x].c = BORDERCOLOUR;
for (; x < bmw; x++)
out[x].c = (((unsigned char*)data)[x>>3]&(1<<(7-(x&7))))?0xffffffff:0;
for (; x < bmw+pad; x++)
out[x].c = BORDERCOLOUR;
data = (char*)data + pitch;
out += PLANEWIDTH;
}
for (; y < bmh+pad; y++)
{
for (x = -pad; x < (int)bmw+pad; x++)
out[x].c = BORDERCOLOUR;
out += PLANEWIDTH;
}
}
else if ((unsigned int)pixelmode == FT_PIXEL_MODE_RGBA_SA)
{ //rgba source using standard alpha.
//(we'll multiply out the alpha for the gpu)
@ -821,9 +852,6 @@ static struct charcache_s *Font_LoadGlyphData(font_t *f, CHARIDXTYPE charidx, FT
if (outline)
{
int bytes = (pixelmode == FT_PIXEL_MODE_GRAY)?1:4;
qbyte *alpha = (char*)data + bytes-1 - pitch*bmh;
static int filter_outline;
static unsigned char filter_highest[MAXOUTLINE*2+1][MAXOUTLINE*2+1];
if (outline != filter_outline)
@ -836,23 +864,57 @@ static struct charcache_s *Font_LoadGlyphData(font_t *f, CHARIDXTYPE charidx, FT
//expand it to out full(ish) size
alpha -= pitch*outline + bytes*outline;
out = &fontplanes.plane[c->bmx+((int)c->bmy-outline)*PLANEHEIGHT];
for (y = -outline; y < (int)bmh+outline; y++, out += PLANEWIDTH)
for (x = -outline; x < (int)bmw+outline; x++)
{
int xn, x1 = max(outline-x, 0), x2 = min(2*outline, (int)bmw-1-x+outline);
int yn, y1 = max(outline-y, 0), y2 = min(2*outline, (int)bmh-1-y+outline);
int v, m = out[x].rgba[3]*255;
for (yn = y1; yn <= y2; yn++)
for (xn = x1; xn <= x2; xn++)
{
v = filter_highest[yn][xn] * alpha[(xn+x)*bytes+(yn+y)*pitch];
m = max(m, v);
}
//out[x].c = 0;
out[x].rgba[3] = m/255;
}
if (pixelmode == FT_PIXEL_MODE_MONO)
{
qbyte *alpha = (char*)data - pitch*bmh;
qbyte a;
int bit;
alpha -= pitch*outline;
out = &fontplanes.plane[c->bmx+((int)c->bmy-outline)*PLANEHEIGHT];
for (y = -outline; y < (int)bmh+outline; y++, out += PLANEWIDTH)
for (x = -outline; x < (int)bmw+outline; x++)
{
int xn, x1 = max(outline-x, 0), x2 = min(2*outline, (int)bmw-1-x+outline);
int yn, y1 = max(outline-y, 0), y2 = min(2*outline, (int)bmh-1-y+outline);
int v, m = out[x].rgba[3]*255;
for (yn = y1; yn <= y2; yn++)
for (xn = x1; xn <= x2; xn++)
{
bit = (xn+x)-outline;
a = alpha[(bit>>3)+(yn+y)*pitch];
a = (a&(1<<(7-(bit&7))))?0xff:0;
v = filter_highest[yn][xn] * a;
m = max(m, v);
}
//out[x].c = 0;
out[x].rgba[3] = m/255;
}
}
else
{
int bytes = (pixelmode == FT_PIXEL_MODE_GRAY)?1:4;
qbyte *alpha = (char*)data + bytes-1 - pitch*bmh;
alpha -= pitch*outline + bytes*outline;
out = &fontplanes.plane[c->bmx+((int)c->bmy-outline)*PLANEHEIGHT];
for (y = -outline; y < (int)bmh+outline; y++, out += PLANEWIDTH)
for (x = -outline; x < (int)bmw+outline; x++)
{
int xn, x1 = max(outline-x, 0), x2 = min(2*outline, (int)bmw-1-x+outline);
int yn, y1 = max(outline-y, 0), y2 = min(2*outline, (int)bmh-1-y+outline);
int v, m = out[x].rgba[3]*255;
for (yn = y1; yn <= y2; yn++)
for (xn = x1; xn <= x2; xn++)
{
v = filter_highest[yn][xn] * alpha[(xn+x)*bytes+(yn+y)*pitch];
m = max(m, v);
}
//out[x].c = 0;
out[x].rgba[3] = m/255;
}
}
c->bmx -= outline;
@ -1316,7 +1378,7 @@ static struct charcache_s *Font_TryLoadGlyph(font_t *f, CHARIDXTYPE charidx)
return NULL; //some sort of error.
}
if (charidx == 0xfffe || pFT_Get_Char_Index(face, charidx)) //ignore glyph 0 (undefined)
if (pFT_Load_Char(face, charidx, FT_LOAD_RENDER|FT_LOAD_COLOR) == 0)
if (pFT_Load_Char(face, charidx, FT_LOAD_RENDER|(((f->flags&FONT_MONO)&&qface->ft.activeheight==qface->ft.actualsize/*FIXME*/)?FT_LOAD_TARGET_MONO:FT_LOAD_TARGET_NORMAL)|FT_LOAD_COLOR) == 0)
{
FT_GlyphSlot slot;
FT_Bitmap *bm;
@ -1348,6 +1410,12 @@ static struct charcache_s *Font_TryLoadGlyph(font_t *f, CHARIDXTYPE charidx)
Image_ResampleTexture(PTI_L8, (void*)bm->buffer, bm->width, bm->rows, out, nw, nh);
c = Font_LoadGlyphData(f, charidx, bm->pixel_mode, out, nw, nh, nw*sizeof(*out));
}
/*else if (bm->pixel_mode == FT_PIXEL_MODE_MONO)
{
unsigned char *out = alloca(nw*nh*sizeof(*out));
Image_ResampleTexture(PTI_L1, (void*)bm->buffer, bm->width, bm->rows, out, nw, nh);
c = Font_LoadGlyphData(f, charidx, bm->pixel_mode, out, nw, nh, nw*sizeof(*out));
}*/
else
c = NULL;
if (c)
@ -2208,7 +2276,7 @@ static qboolean Font_LoadFontLump(font_t *f, const char *facename)
//creates a new font object from the given file, with each text row with the given height.
//width is implicit and scales with height and choice of font.
struct font_s *Font_LoadFont(const char *fontfilename, float vheight, float scale, int outline)
struct font_s *Font_LoadFont(const char *fontfilename, float vheight, float scale, int outline, unsigned int flags)
{
struct font_s *f;
int i = 0;
@ -2236,6 +2304,7 @@ struct font_s *Font_LoadFont(const char *fontfilename, float vheight, float scal
f->scale = scale;
f->charheight = height;
f->truecharheight = height;
f->flags = flags;
Q_strncpyz(f->name, fontfilename, sizeof(f->name));
switch(M_GameType())
@ -2453,7 +2522,7 @@ struct font_s *Font_LoadFont(const char *fontfilename, float vheight, float scal
}
else
{
f->alt = Font_LoadFont(aname, vheight, scale, outline);
f->alt = Font_LoadFont(aname, vheight, scale, outline, flags);
if (f->alt)
{
VectorCopy(f->alt->tint, f->alttint);

View File

@ -18,12 +18,6 @@ See gl_terrain.h for terminology, networking notes, etc.
#include "gl_terrain.h"
static plugterrainfuncs_t terrainfuncs;
typedef struct
{
vec3_t v;
vec2_t tc;
vec4_t rgba;
} qcpatchvert_t;
cvar_t mod_terrain_networked = CVARD("mod_terrain_networked", "0", "Terrain edits are networked. Clients will download sections on demand, and servers will notify clients of changes.");
@ -3321,6 +3315,8 @@ unsigned int Heightmap_PointContents(model_t *model, const vec3_t axis[3], const
for (i = 0; i < hm->numbrushes; i++)
{
br = &hm->wbrushes[i];
if (br->patch)
continue; //infinitely thin...
for (j = 0; j < br->numplanes; j++)
{
@ -3785,7 +3781,7 @@ static qboolean Heightmap_Trace_Patch(hmtrace_t *tr, brushes_t *brushinfo)
if (!patch->tessvert)
{
const struct patchcpvert_s *r1 = patch->cp, *r2;
const struct qcpatchvert_s *r1 = patch->cp, *r2;
w = patch->numcp[0];
h = patch->numcp[1];
@ -6255,6 +6251,8 @@ static brushes_t *Terr_Brush_Insert(model_t *model, heightmap_t *hm, brushes_t *
out->planes = NULL;
out->faces = NULL;
out->numplanes = 0;
out->ispatch = !!brush->patch;
out->selected = false;
ClearBounds(out->mins, out->maxs);
if (brush->numplanes)
{
@ -6426,6 +6424,9 @@ static brushes_t *Terr_Brush_Insert(model_t *model, heightmap_t *hm, brushes_t *
AddPointToBounds(out->mins, model->mins, model->maxs);
AddPointToBounds(out->maxs, model->mins, model->maxs);
if (out->patch && (out->patch->subdiv[0] || out->patch->subdiv[1]))
out->patch->tessvert = PatchInfo_Evaluate(out->patch->cp, out->patch->numcp, out->patch->subdiv, out->patch->tesssize);
return out;
}
@ -6481,6 +6482,11 @@ static void Terr_Brush_DeleteIdx(heightmap_t *hm, size_t idx)
}
BZ_Free(br->planes);
if (br->patch)
{
BZ_Free(br->patch->tessvert);
BZ_Free(br->patch);
}
hm->numbrushes--;
hm->brushesedited = true;
//plug the hole with some other brush.
@ -6506,6 +6512,104 @@ static qboolean Terr_Brush_DeleteId(heightmap_t *hm, unsigned int brushid)
return false;
}
static void Patch_Serialise(sizebuf_t *sb, brushes_t *br)
{
qbyte flags = 0;
unsigned int i, m = br->patch->numcp[0]*br->patch->numcp[1];
for (i = 0; i < m; i++)
{
if (br->patch->cp[i].rgba[0] != 1)
flags |= 1;
if (br->patch->cp[i].rgba[1] != 1)
flags |= 2;
if (br->patch->cp[i].rgba[2] != 1)
flags |= 4;
if (br->patch->cp[i].rgba[3] != 1)
flags |= 8;
}
MSG_WriteLong(sb, br->id);
MSG_WriteLong(sb, br->contents);
MSG_WriteShort(sb, br->patch->numcp[0]);
MSG_WriteShort(sb, br->patch->numcp[1]);
MSG_WriteByte(sb, flags);
MSG_WriteString(sb, br->patch->tex->shadername);
MSG_WriteShort(sb, br->patch->subdiv[0]);
MSG_WriteShort(sb, br->patch->subdiv[1]);
for (i = 0; i < m; i++)
{
MSG_WriteFloat(sb, br->patch->cp[i].v[0]);
MSG_WriteFloat(sb, br->patch->cp[i].v[1]);
MSG_WriteFloat(sb, br->patch->cp[i].v[2]);
MSG_WriteFloat(sb, br->patch->cp[i].tc[0]);
MSG_WriteFloat(sb, br->patch->cp[i].tc[1]);
if (flags&1)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[0]);
if (flags&2)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[1]);
if (flags&4)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[2]);
if (flags&8)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[3]);
}
}
static size_t Patch_DeserialiseHeader(brushes_t *br)
{
unsigned int numcp[2];
br->id = MSG_ReadLong();
br->contents = MSG_ReadLong();
br->numplanes = numcp[0] = (unsigned short)MSG_ReadShort();
br->axialplanes = numcp[1] = (unsigned short)MSG_ReadShort();
if (numcp[0]*numcp[1] > 8192)
return 0; //too many. reject it as bad.
return sizeof(*br->patch) + sizeof(*br->patch->cp)*(numcp[0]*numcp[1]-countof(br->patch->cp));
}
static qboolean Patch_Deserialise(heightmap_t *hm, brushes_t *br, void *mem)
{
struct qcpatchvert_s vert;
qboolean flags;
unsigned int i, m;
flags = MSG_ReadByte();
br->patch = mem;
br->patch->numcp[0] = br->numplanes;
br->patch->numcp[1] = br->axialplanes;
br->numplanes = br->axialplanes = 0;
m = br->patch->numcp[0]*br->patch->numcp[1];
//FIXME: as a server, we probably want to reject the brush if we exceed some texnum/memory limitation, so clients can't just spam new textures endlessly.
br->patch->tex = Terr_Brush_FindTexture(hm, MSG_ReadString());
br->patch->subdiv[0] = MSG_ReadShort();
br->patch->subdiv[1] = MSG_ReadShort();
for (i = 0; i < m; i++)
{
vert.v[0] = MSG_ReadFloat();
vert.v[1] = MSG_ReadFloat();
vert.v[2] = MSG_ReadFloat();
vert.tc[0] = MSG_ReadFloat();
vert.tc[1] = MSG_ReadFloat();
vert.rgba[0] = (flags&1)?MSG_ReadFloat():1;
vert.rgba[1] = (flags&2)?MSG_ReadFloat():1;
vert.rgba[2] = (flags&4)?MSG_ReadFloat():1;
vert.rgba[3] = (flags&8)?MSG_ReadFloat():1;
br->patch->cp[i] = vert;
}
return true;
}
static void Brush_Serialise(sizebuf_t *sb, brushes_t *br)
{
unsigned int i;
@ -6533,16 +6637,30 @@ static void Brush_Serialise(sizebuf_t *sb, brushes_t *br)
MSG_WriteFloat(sb, br->faces[i].stdir[1][3]);
}
}
static qboolean Brush_Deserialise(heightmap_t *hm, brushes_t *br)
static size_t Brush_DeserialiseHeader(brushes_t *br, qboolean ispatch)
{
unsigned int i;
unsigned int maxplanes = br->numplanes;
br->ispatch = ispatch;
if (br->ispatch)
return Patch_DeserialiseHeader(br);
br->id = MSG_ReadLong();
br->contents = MSG_ReadLong();
br->numplanes = MSG_ReadLong();
if (br->numplanes > maxplanes)
return false;
if (br->numplanes > 8192)
return 0; //abusive
return sizeof(*br->faces) * br->numplanes
+ sizeof(*br->planes) * br->numplanes;
}
static qboolean Brush_Deserialise(heightmap_t *hm, brushes_t *br, void *mem)
{
unsigned int i;
if (br->ispatch)
return Patch_Deserialise(hm, br, mem);
br->faces = mem;
br->planes = (vec4_t*)(br->faces + br->numplanes);
for (i = 0; i < br->numplanes; i++)
{
@ -6568,88 +6686,6 @@ static qboolean Brush_Deserialise(heightmap_t *hm, brushes_t *br)
return true;
}
static void Patch_Serialise(sizebuf_t *sb, brushes_t *br)
{
qbyte flags = 0;
unsigned int i, m = br->patch->numcp[0]*br->patch->numcp[1];
for (i = 0; i < m; i++)
{
if (br->patch->cp[i].rgba[0] != 1)
flags |= 1;
if (br->patch->cp[i].rgba[1] != 1)
flags |= 2;
if (br->patch->cp[i].rgba[2] != 1)
flags |= 4;
if (br->patch->cp[i].rgba[3] != 1)
flags |= 8;
}
MSG_WriteLong(sb, br->id);
MSG_WriteByte(sb, flags);
MSG_WriteLong(sb, br->contents);
MSG_WriteString(sb, br->patch->tex->shadername);
MSG_WriteShort(sb, br->patch->numcp[0]);
MSG_WriteShort(sb, br->patch->numcp[1]);
MSG_WriteShort(sb, br->patch->subdiv[0]);
MSG_WriteShort(sb, br->patch->subdiv[1]);
for (i = 0; i < m; i++)
{
MSG_WriteFloat(sb, br->patch->cp[i].v[0]);
MSG_WriteFloat(sb, br->patch->cp[i].v[1]);
MSG_WriteFloat(sb, br->patch->cp[i].v[2]);
MSG_WriteFloat(sb, br->patch->cp[i].tc[0]);
MSG_WriteFloat(sb, br->patch->cp[i].tc[1]);
if (flags&1)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[0]);
if (flags&2)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[1]);
if (flags&4)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[2]);
if (flags&8)
MSG_WriteFloat(sb, br->patch->cp[i].rgba[3]);
}
}
static qboolean Patch_Deserialise(heightmap_t *hm, brushes_t *br)
{
struct patchcpvert_s vert;
qboolean flags;
unsigned int i, maxverts = br->patch->numcp[0]*br->patch->numcp[1];
br->id = MSG_ReadLong();
flags = MSG_ReadByte();
br->contents = MSG_ReadLong();
//FIXME: as a server, we probably want to reject the brush if we exceed some texnum/memory limitation, so clients can't just spam new textures endlessly.
br->patch->tex = Terr_Brush_FindTexture(hm, MSG_ReadString());
br->patch->numcp[0] = MSG_ReadShort();
br->patch->numcp[1] = MSG_ReadShort();
br->patch->subdiv[0] = MSG_ReadShort();
br->patch->subdiv[1] = MSG_ReadShort();
for (i = 0; i < br->patch->numcp[0]*br->patch->numcp[1]; i++)
{
vert.v[0] = MSG_ReadFloat();
vert.v[1] = MSG_ReadFloat();
vert.v[2] = MSG_ReadFloat();
vert.tc[0] = MSG_ReadFloat();
vert.tc[1] = MSG_ReadFloat();
vert.rgba[0] = (flags&1)?MSG_ReadFloat():1;
vert.rgba[1] = (flags&2)?MSG_ReadFloat():1;
vert.rgba[2] = (flags&4)?MSG_ReadFloat():1;
vert.rgba[3] = (flags&8)?MSG_ReadFloat():1;
if (i < maxverts)
br->patch->cp[i] = vert;
}
return i <= maxverts;
}
#ifndef SERVERONLY
heightmap_t *CL_BrushEdit_ForceContext(model_t *mod)
@ -6699,45 +6735,36 @@ void CL_Parse_BrushEdit(void)
else if (cmd == hmcmd_brush_insert || cmd == hmcmd_patch_insert) //1=create/replace
{
brushes_t brush;
size_t tempmemsize;
hm = CL_BrushEdit_ForceContext(mod); //do this early, to ensure that the textures are correct
memset(&brush, 0, sizeof(brush));
if (cmd == hmcmd_patch_insert)
tempmemsize = Brush_DeserialiseHeader(&brush, (cmd == hmcmd_patch_insert));
if (!tempmemsize)
Host_EndGame("CL_Parse_BrushEdit: unparsable %s\n", brush.ispatch?"patch":"brush");
if (!Brush_Deserialise(hm, &brush, alloca(tempmemsize)))
Host_EndGame("CL_Parse_BrushEdit: unparsable %s\n", brush.ispatch?"patch":"brush");
if (!ignore) //ignore if we're the server, we should already have it anyway (but might need it for demos, hence why its still sent).
{
const unsigned int maxpoints = 64*64;
brush.patch = alloca(sizeof(*brush.patch) + sizeof(*brush.patch->cp)*(maxpoints-countof(brush.patch->cp)));
brush.patch->numcp[0] = 1;
brush.patch->numcp[1] = maxpoints;
if (!Patch_Deserialise(hm, &brush))
Host_EndGame("CL_Parse_BrushEdit: unparsable patch\n");
}
else
{
brush.numplanes = 128;
brush.planes = alloca(sizeof(*brush.planes) * brush.numplanes);
brush.faces = alloca(sizeof(*brush.faces) * brush.numplanes);
if (!Brush_Deserialise(hm, &brush))
Host_EndGame("CL_Parse_BrushEdit: unparsable brush\n");
}
if (ignore)
return; //ignore if we're the server, we should already have it anyway (but might need it for demos, hence why its still sent).
if (brush.id)
{
int i;
if (cls.demoplayback)
Terr_Brush_DeleteId(hm, brush.id);
else
if (brush.id)
{
for (i = 0; i < hm->numbrushes; i++)
int i;
if (cls.demoplayback)
Terr_Brush_DeleteId(hm, brush.id);
else
{
brushes_t *br = &hm->wbrushes[i];
if (br->id == brush.id)
return; //we already have it. assume we just edited it.
for (i = 0; i < hm->numbrushes; i++)
{
brushes_t *br = &hm->wbrushes[i];
if (br->id == brush.id)
return; //we already have it. assume we just edited it.
}
}
}
Terr_Brush_Insert(mod, hm, &brush);
}
Terr_Brush_Insert(mod, hm, &brush);
}
else if (cmd == hmcmd_prespawning)
{ //delete all
@ -6899,31 +6926,22 @@ qboolean SV_Parse_BrushEdit(void)
else if (cmd == hmcmd_brush_insert || cmd == hmcmd_patch_insert)
{
brushes_t brush;
size_t tempmemsize;
memset(&brush, 0, sizeof(brush));
if (cmd == hmcmd_patch_insert)
brush.ispatch = (cmd == hmcmd_patch_insert);
tempmemsize = Brush_DeserialiseHeader(&brush, cmd == hmcmd_patch_insert);
if (!tempmemsize)
{
const unsigned int maxpoints = 64*64;
brush.patch = alloca(sizeof(*brush.patch) + sizeof(*brush.patch->cp)*(maxpoints-countof(brush.patch->cp)));
memset(brush.patch, 0, sizeof(*brush.patch));
brush.patch->numcp[0] = maxpoints;
brush.patch->numcp[1] = 1;
if (!Patch_Deserialise(hm, &brush))
{
Con_Printf("SV_Parse_BrushEdit: %s sent an unparsable patch\n", host_client->name);
return false;
}
Con_Printf("SV_Parse_BrushEdit: %s sent an abusive %s\n", host_client->name, brush.ispatch?"patch":"brush");
return false;
}
else
if (!Brush_Deserialise(hm, &brush, alloca(tempmemsize)))
{
brush.numplanes = 128;
brush.planes = alloca(sizeof(*brush.planes) * brush.numplanes);
brush.faces = alloca(sizeof(*brush.faces) * brush.numplanes);
if (!Brush_Deserialise(hm, &brush))
{
Con_Printf("SV_Parse_BrushEdit: %s sent an unparsable brush\n", host_client->name);
return false;
}
Con_Printf("SV_Parse_BrushEdit: %s sent an unparsable brush\n", host_client->name);
return false;
}
if (!authorise)
{
SV_PrintToClient(host_client, PRINT_MEDIUM, "Brush editing ignored: you are not a mapper\n");
@ -6980,28 +6998,6 @@ qboolean SV_Parse_BrushEdit(void)
}
#endif
typedef struct
{
string_t shadername;
vec3_t planenormal;
float planedist;
vec3_t sdir;
float sbias;
vec3_t tdir;
float tbias;
} qcbrushface_t;
typedef struct
{
string_t shadername;
unsigned int contents;
unsigned int cp_width;
unsigned int cp_height;
unsigned int subdiv_x;
unsigned int subdiv_y;
vec3_t texinfo;
} qcpatchinfo_t;
static void *validateqcpointer(pubprogfuncs_t *prinst, size_t qcptr, size_t elementsize, size_t elementcount, qboolean allownull)
{
//make sure that the sizes can't overflow
@ -7066,9 +7062,9 @@ void QCBUILTIN PF_patch_getcp(pubprogfuncs_t *prinst, struct globalvars_s *pr_gl
G_INT(OFS_RETURN) = br->patch->numcp[0]*br->patch->numcp[1];
else
{
maxverts = min(br->numplanes, maxverts);
maxverts = min(br->patch->numcp[0]*br->patch->numcp[1], maxverts);
for (j = 0; j < br->patch->numcp[0]*br->patch->numcp[1]; j++)
for (j = 0; j < maxverts; j++)
{
VectorCopy(br->patch->cp[j].v, out_verts->v);
Vector2Copy(br->patch->cp[j].tc, out_verts->tc);
@ -7082,7 +7078,43 @@ void QCBUILTIN PF_patch_getcp(pubprogfuncs_t *prinst, struct globalvars_s *pr_gl
}
}
}
// {"patch_getmesh", PF_patch_getmesh, 0, 0, 0, 0, D("int(float modelidx, int patchid, patchvert_t *out_verts, int maxverts, __out patchinfo_t out_info)", "Queries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error.")},
// {"patch_evaluate", PF_patch_evaluate, 0, 0, 0, 0, D("int(patchvert_t *in_controlverts, patchvert_t *out_renderverts, int maxout, patchinfo_t *inout_info)", "Calculates the geometry of a hyperthetical patch.")},
void QCBUILTIN PF_patch_evaluate(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
qcpatchinfo_t *inout_info = validateqcpointer(prinst, G_INT(OFS_PARM3), sizeof(*inout_info), 1, false);
unsigned int maxverts = G_INT(OFS_PARM2);
qcpatchvert_t *out_verts = validateqcpointer(prinst, G_INT(OFS_PARM1), sizeof(*out_verts), maxverts, true);
qcpatchvert_t *in_cp = validateqcpointer(prinst, G_INT(OFS_PARM0), sizeof(*in_cp), inout_info->cp_width*inout_info->cp_height, false);
unsigned short numcp[] = {inout_info->cp_width, inout_info->cp_height}, size[2];
short subdiv[] = {inout_info->subdiv_x, inout_info->subdiv_y};
patchtessvert_t *working_verts = PatchInfo_Evaluate(in_cp, numcp, subdiv, size);
unsigned int i;
if (working_verts)
{
if (out_verts)
{
maxverts = min(maxverts, size[0]*size[1]);
for (i = 0; i < maxverts; i++)
{
VectorCopy(working_verts[i].v, out_verts[i].v);
Vector4Copy(working_verts[i].rgba, out_verts[i].rgba);
Vector2Copy(working_verts[i].tc, out_verts[i].tc);
}
}
BZ_Free(working_verts);
inout_info->cp_width = size[0]; //not really controlpoints, but the data works the same.
inout_info->cp_height = size[1];
}
else
inout_info->cp_width = inout_info->cp_height = 0; //erk...
inout_info->subdiv_x = inout_info->subdiv_y = 0; //make it as explicit tessellation, so we can maybe skip this.
G_INT(OFS_RETURN) = maxverts;
}
// {"patch_getmesh", PF_patch_getmesh, 0, 0, 0, 0, D("int(float modelidx, int patchid, patchvert_t *out_verts, int maxverts, patchinfo_t *out_info)", "Queries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error.")},
void QCBUILTIN PF_patch_getmesh(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
world_t *vmw = prinst->parms->user;
@ -7113,10 +7145,10 @@ void QCBUILTIN PF_patch_getmesh(pubprogfuncs_t *prinst, struct globalvars_s *pr_
if (out_info)
{
out_info->contents = br->contents;
out_info->cp_width = br->patch->numcp[0];
out_info->cp_height = br->patch->numcp[1];
out_info->subdiv_x = br->patch->subdiv[0];
out_info->subdiv_y = br->patch->subdiv[1];
out_info->cp_width = br->patch->tesssize[0];
out_info->cp_height = br->patch->tesssize[1];
out_info->subdiv_x = 0;
out_info->subdiv_y = 0;
out_info->shadername = PR_TempString(prinst, br->patch->tex->shadername);
}
@ -7124,9 +7156,9 @@ void QCBUILTIN PF_patch_getmesh(pubprogfuncs_t *prinst, struct globalvars_s *pr_
G_INT(OFS_RETURN) = br->patch->tesssize[0]*br->patch->tesssize[1];
else
{
maxverts = min(br->numplanes, maxverts);
maxverts = min(br->patch->tesssize[0]*br->patch->tesssize[1], maxverts);
for (j = 0; j < br->patch->tesssize[0]*br->patch->tesssize[1]; j++)
for (j = 0; j < maxverts; j++)
{
VectorCopy(br->patch->tessvert[j].v, out_verts->v);
Vector2Copy(br->patch->tessvert[j].tc, out_verts->tc);
@ -7408,8 +7440,8 @@ void QCBUILTIN PF_patch_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_g
{
MSG_WriteByte(&sv.multicast, svcfte_brushedit);
MSG_WriteShort(&sv.multicast, modelindex);
MSG_WriteByte(&sv.multicast, hmcmd_brush_insert);
Brush_Serialise(&sv.multicast, nb);
MSG_WriteByte(&sv.multicast, hmcmd_patch_insert);
Patch_Serialise(&sv.multicast, nb);
SV_MulticastProtExt(vec3_origin, MULTICAST_ALL_R, ~0, 0, 0);
return;
}
@ -7419,8 +7451,8 @@ void QCBUILTIN PF_patch_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_g
{
MSG_WriteByte(&cls.netchan.message, clcfte_brushedit);
MSG_WriteShort(&cls.netchan.message, modelindex);
MSG_WriteByte(&cls.netchan.message, hmcmd_brush_insert);
Brush_Serialise(&cls.netchan.message, nb);
MSG_WriteByte(&cls.netchan.message, hmcmd_patch_insert);
Patch_Serialise(&cls.netchan.message, nb);
return;
}
#endif
@ -7930,6 +7962,7 @@ void Mod_Terrain_Save_f(void)
{
//warning: brushes are not saved unless its a .map
COM_StripExtension(mod->name, fname, sizeof(fname));
Q_strncatz(fname, mod_modifier, sizeof(fname));
Q_strncatz(fname, ".ent", sizeof(fname));
FS_CreatePath(fname, FS_GAMEONLY);

View File

@ -36,7 +36,7 @@ cvar_t mod_loadentfiles = CVAR("sv_loadentfiles", "1");
cvar_t mod_loadentfiles_dir = CVAR("sv_loadentfiles_dir", "");
cvar_t mod_external_vis = CVARD("mod_external_vis", "1", "Attempt to load .vis patches for quake maps, allowing transparent water to work properly.");
cvar_t mod_warnmodels = CVARD("mod_warnmodels", "1", "Warn if any models failed to load. Set to 0 if your mod is likely to lack optional models (like its in development)."); //set to 0 for hexen2 and its otherwise-spammy-as-heck demo.
cvar_t mod_litsprites_force = CVARD("mod_litsprites_force", "0", "If set to 1, sprites will be lit according to world lighting (including rtlights), like Tenebrae. Ideally use EF_ADDITIVE or EF_FULLBRIGHT to make emissive sprites instead.");
cvar_t mod_litsprites_force = CVARFD("mod_litsprites_force", "0", CVAR_RENDERERLATCH, "If set to 1, sprites will be lit according to world lighting (including rtlights), like Tenebrae. Ideally use EF_ADDITIVE or EF_FULLBRIGHT to make emissive sprites instead.");
cvar_t mod_loadmappackages = CVARD ("mod_loadmappackages", "1", "Load additional content embedded within bsp files.");
cvar_t mod_lightscale_broken = CVARFD("mod_lightscale_broken", "0", CVAR_RENDERERLATCH, "When active, replicates a bug from vanilla - the radius of r_dynamic lights is scaled by per-surface texture scale rather than using actual distance.");
cvar_t mod_lightpoint_distance = CVARD("mod_lightpoint_distance", "8192", "This is the maximum distance to trace when searching for a ground surface for lighting info on map formats without light more fancy lighting info. Use 2048 for full compat with Quake.");
@ -81,6 +81,7 @@ void Mod_LoadDoomSprite (model_t *mod);
#define MAX_MOD_KNOWN 8192
model_t *mod_known;
int mod_numknown;
char mod_modifier[MAX_QPATH]; //postfix for ent files
extern cvar_t r_loadlits;
#ifdef SPECULAR
@ -620,6 +621,17 @@ void Mod_Purge(enum mod_purge_e ptype)
}
}
void Mod_SetModifier(const char *modifier)
{
if (!modifier || strlen(modifier) >= sizeof(mod_modifier)) modifier = "";
if (strcmp(modifier, mod_modifier))
{ //if the modifier changed, force all models to reset.
COM_WorkerFullSync(); //sync all the workers, just in case.
strcpy(mod_modifier, modifier);
Mod_Purge(MP_RESET); //nuke it now
}
}
#ifndef SERVERONLY
void Mod_FindCubemaps_f(void);
void Mod_Realign_f(void);
@ -1449,7 +1461,7 @@ static const char *Mod_RemapBuggyTexture(const char *name, const qbyte *data, un
{
if (!strcmp(name, buggytextures[i].oldname))
{
unsigned int sum = Com_BlockChecksum(data, datalen);
unsigned int sum = CalcHashInt(&hash_md4, data, datalen);
for (; i < sizeof(buggytextures)/sizeof(buggytextures[0]); i++)
{
if (strcmp(name, buggytextures[i].oldname))
@ -2252,11 +2264,13 @@ static void Mod_SaveEntFile_f(void)
{
Q_snprintfz(fname, sizeof(fname), "maps/%s/%s", mod_loadentfiles_dir.string, mod->name+5);
COM_StripExtension(fname, fname, sizeof(fname));
Q_strncatz(fname, mod_modifier, sizeof(fname));
Q_strncatz(fname, ".ent", sizeof(fname));
}
else
{
COM_StripExtension(mod->name, fname, sizeof(fname));
Q_strncatz(fname, mod_modifier, sizeof(fname));
Q_strncatz(fname, ".ent", sizeof(fname));
}
@ -2293,6 +2307,7 @@ qboolean Mod_LoadEntitiesBlob(struct model_s *mod, const char *entdata, size_t e
{
Q_snprintfz(fname, sizeof(fname), "maps/%s/%s", mod_loadentfiles_dir.string, mod->name+5);
COM_StripExtension(fname, fname, sizeof(fname));
Q_strncatz(fname, mod_modifier, sizeof(fname));
Q_strncatz(fname, ".ent", sizeof(fname));
ents = FS_LoadMallocFile(fname, &sz);
}
@ -2300,12 +2315,14 @@ qboolean Mod_LoadEntitiesBlob(struct model_s *mod, const char *entdata, size_t e
if (mod_loadentfiles.value && !ents)
{
COM_StripExtension(mod->name, fname, sizeof(fname));
Q_strncatz(fname, mod_modifier, sizeof(fname));
Q_strncatz(fname, ".ent", sizeof(fname));
ents = FS_LoadMallocFile(fname, &sz);
}
if (mod_loadentfiles.value && !ents)
{ //tenebrae compat
COM_StripExtension(mod->name, fname, sizeof(fname));
Q_strncatz(fname, mod_modifier, sizeof(fname));
Q_strncatz(fname, ".edo", sizeof(fname));
ents = FS_LoadMallocFile(fname, &sz);
}
@ -3720,6 +3737,12 @@ TRACE(("dbg: Mod_LoadTextures: inittexturedescs\n"));
m->nummiptex = LittleLong (m->nummiptex);
if ((1+m->nummiptex)*sizeof(int) > l->filelen)
{
Con_Printf(CON_WARNING "warning: %s contains corrupt texture lump\n", loadmodel->name);
return false;
}
loadmodel->numtextures = m->nummiptex;
loadmodel->textures = ZG_Malloc(&loadmodel->memgroup, m->nummiptex * sizeof(*loadmodel->textures));
sizes = alloca(sizeof(*sizes)*m->nummiptex);
@ -5348,7 +5371,28 @@ static qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsi
else if (!memcmp(&header.version, BSPVERSION_LONG2))
mod->engineflags |= MDLF_NEEDOVERBRIGHT, subbsp = sb_long2;
else if (!memcmp(&header.version, BSPVERSIONHL))
{
char tmp[64];
mod->fromgame = fg_halflife;
//special hack to work around blueshit bugs - we need to swap LUMP_ENTITIES and LUMP_PLANES over
if (COM_ParseOut(mod_base + header.lumps[LUMP_PLANES].fileofs, tmp, sizeof(tmp)) && !strcmp(tmp, "{"))
{
COM_ParseOut(mod_base + header.lumps[LUMP_ENTITIES].fileofs, tmp, sizeof(tmp));
if (strcmp(tmp, "{"))
{
int i;
for (i = 0; i < header.lumps[LUMP_ENTITIES].filelen && i < sizeof(dplane_t); i++)
if (mod_base[header.lumps[LUMP_ENTITIES].fileofs + i] == 0)
{ //yeah, looks screwy in the way we expect. swap em over.
lump_t tmp = header.lumps[LUMP_ENTITIES];
header.lumps[LUMP_ENTITIES] = header.lumps[LUMP_PLANES];
header.lumps[LUMP_PLANES] = tmp;
break;
}
}
}
}
else
{
Con_Printf (CON_ERROR "Mod_LoadBrushModel: %s has wrong version number (%i)\n", mod->name, i);
@ -5375,7 +5419,7 @@ static qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsi
}
if (i == LUMP_ENTITIES)
continue;
chksum = Com_BlockChecksum(mod_base + header.lumps[i].fileofs, header.lumps[i].filelen);
chksum = CalcHashInt(&hash_md4, mod_base + header.lumps[i].fileofs, header.lumps[i].filelen);
mod->checksum ^= chksum;
if (i == LUMP_VISIBILITY || i == LUMP_LEAFS || i == LUMP_NODES)

View File

@ -46,6 +46,7 @@ cvar_t r_forceprogramify = CVARAFD("r_forceprogramify", "0", "dpcompat_makeshitu
cvar_t dpcompat_nopremulpics = CVARFD("dpcompat_nopremulpics", "0", CVAR_SHADERSYSTEM, "By default FTE uses premultiplied alpha for hud/2d images, while DP does not (which results in halos with low-res content). Unfortunately DDS files would need to be recompressed, resulting in visible issues.");
#endif
cvar_t r_glsl_precache = CVARFD("r_glsl_precache", "0", CVAR_SHADERSYSTEM, "Force all relevant glsl permutations to load upfront.");
cvar_t r_halfrate = CVARFD("r_halfrate", "0", CVAR_SHADERSYSTEM, "Use half-rate shading (where supported by gpu).");
extern cvar_t r_glsl_offsetmapping_reliefmapping;
extern cvar_t r_drawflat;
@ -964,9 +965,17 @@ static void Shader_NoMipMaps (parsestate_t *ps, const char **ptr)
static void Shader_Affine (parsestate_t *ps, const char **ptr)
{
shader_t *shader = ps->s;
shader->flags |= SBITS_AFFINE;
int i;
for (i = 0; i < countof(shader->passes); i++)
shader->passes[i].shaderbits |= SBITS_AFFINE;
}
static void Shader_FullRate (parsestate_t *ps, const char **ptr)
{
shader_t *shader = ps->s;
int i;
for (i = 0; i < countof(shader->passes); i++)
shader->passes[i].shaderbits |= SBITS_MISC_FULLRATE;
}
static void Shader_NoPicMip (parsestate_t *ps, const char **ptr)
{
@ -2003,6 +2012,7 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip
for (end = *name?strchr(name+1, '#'):NULL; end && *end; )
{
size_t startoffset=offset;
char *start = end+1;
end = strchr(start, '#');
if (!end)
@ -2018,11 +2028,26 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip
{
if (*start == '=')
{
if (offset == startoffset+8)
break;
start++;
prescript[offset++] = ' ';
break;
}
prescript[offset++] = toupper(*start++);
if ((*start >='a'&&*start<='z')||(*start >='A'&&*start<='Z')||*start=='_'||(*start >='0'&&*start<='9'&&offset>startoffset+8))
prescript[offset++] = toupper(*start++);
else
{ ///invalid symbol name...
offset = startoffset+8;
prescript[offset] = 0;
break;
}
}
if (offset == startoffset+8)
{ ///invalid symbol name...
offset = startoffset;
prescript[offset] = 0;
break;
}
while (offset < sizeof(prescript) && start < end)
prescript[offset++] = toupper(*start++);
@ -2872,6 +2897,7 @@ static shaderkey_t shaderkeys[] =
{"deferredlight", Shader_Deferredlight, "fte"}, //(sort = prelight)
// {"lpp_light", Shader_Deferredlight, "fte"}, //(sort = prelight)
{"affine", Shader_Affine, "fte"}, //some hardware is horribly slow, and can benefit from certain hints.
{"fullrate", Shader_FullRate, "fte"}, //blocks half-rate shading on this surface.
{"bemode", Shader_BEMode, "fte"},
@ -5854,6 +5880,14 @@ done:;
}
}
}
if (!r_halfrate.ival)
{
for (i = 0; i < s->numpasses; i++)
{
s->passes[i].shaderbits |= SBITS_MISC_FULLRATE;
}
}
}
/*
static void Shader_UpdateRegistration (void)

View File

@ -259,13 +259,32 @@ typedef struct brushtex_s
struct brushtex_s *next;
} brushtex_t;
typedef struct patchtessvert_s
{
vec3_t v;
vec4_t rgba;
vec2_t tc;
// vec3_t norm;
// vec3_t sdir;
// vec3_t tdir;
} patchtessvert_t;
typedef struct qcpatchvert_s
{
vec3_t v;
vec4_t rgba;
vec2_t tc;
} qcpatchvert_t;
patchtessvert_t *PatchInfo_Evaluate(const qcpatchvert_t *cp, const unsigned short patch_cp[2], const short subdiv[2], unsigned short *size);
unsigned int PatchInfo_EvaluateIndexes(const unsigned short *size, index_t *out_indexes);
typedef struct
{
unsigned int contents;
unsigned int contents; //bitmask
unsigned int id; //networked/gamecode id.
unsigned int axialplanes; //+x,+y,+z,-x,-y,-z. used for bevel stuff.
unsigned int numplanes;
unsigned char selected:1; //different shader stuff
unsigned char ispatch:1; //just for parsing really
vec4_t *planes;
vec3_t mins, maxs; //for optimisation and stuff
struct patchdata_s
@ -275,23 +294,10 @@ typedef struct
short subdiv[2]; //<0=regular q3 patch, 0=cp-only, >0=fixed-tessellation.
unsigned short tesssize[2];
struct patchtessvert_s
{
vec3_t v;
vec2_t tc;
vec4_t rgba;
// vec3_t norm;
// vec3_t sdir;
// vec3_t tdir;
} *tessvert; //x+(y*tesssize[0])
patchtessvert_t *tessvert; //x+(y*tesssize[0])
//control points
struct patchcpvert_s
{
vec3_t v;
vec2_t tc;
vec4_t rgba;
} cp[1]; //x+(y*numcp[0]) extends past end of patchdata_s
qcpatchvert_t cp[1]; //x+(y*numcp[0]) extends past end of patchdata_s
} *patch; //if this is NULL, then its a regular brush. otherwise its a patch.
struct brushface_s
{
@ -311,6 +317,28 @@ typedef struct
qbyte *lightdata;
} *faces;
} brushes_t;
typedef struct
{
string_t shadername;
vec3_t planenormal;
float planedist;
vec3_t sdir;
float sbias;
vec3_t tdir;
float tbias;
} qcbrushface_t;
typedef struct
{
string_t shadername;
unsigned int contents;
unsigned int cp_width;
unsigned int cp_height;
unsigned int subdiv_x;
unsigned int subdiv_y;
vec3_t texinfo;
} qcpatchinfo_t;
typedef struct heightmap_s
{
char path[MAX_QPATH];

View File

@ -182,12 +182,12 @@ qboolean EGL_LoadLibrary(char *driver)
#ifndef _WIN32
if (!eslibrary)
{
eslibrary = dlopen("libGL.so.1.2", RTLD_NOW|RTLD_GLOBAL);
eslibrary = dlopen("libGL"ARCH_DL_POSTFIX".1.2", RTLD_NOW|RTLD_GLOBAL);
if (eslibrary) Sys_Printf("Loaded libGL.so.1.2\n");
}
if (!eslibrary)
{
eslibrary = dlopen("libGL.so.1", RTLD_NOW|RTLD_GLOBAL);
eslibrary = dlopen("libGL"ARCH_DL_POSTFIX".1", RTLD_NOW|RTLD_GLOBAL);
if (eslibrary) Sys_Printf("Loaded libGL.so.1\n");
}
if (!eslibrary)

View File

@ -1562,12 +1562,22 @@ static int GLVID_SetMode (rendererstate_t *info, unsigned char *palette)
stat = CreateMainWindow(info, true);
if (stat)
{
EGLConfig cfg;
maindc = GetDC(mainwindow);
stat = EGL_Init (info, palette, EGL_PLATFORM_WIN32, mainwindow, maindc, (EGLNativeWindowType)mainwindow, (EGLNativeDisplayType)maindc);
if (stat)
if (!GL_Init(info, &EGL_Proc))
return false;
if (!EGL_InitDisplay(info, EGL_PLATFORM_WIN32, maindc, (EGLNativeDisplayType)maindc, &cfg))
{
Con_Printf("couldn't find suitable EGL config\n");
return false;
}
if (!EGL_InitWindow(info, EGL_PLATFORM_WIN32, mainwindow, (EGLNativeWindowType)mainwindow, cfg))
{
Con_Printf("couldn't initialise EGL context\n");
return false;
}
if (!GL_Init(info, &EGL_Proc))
return false;
}
break;
#endif
@ -3369,7 +3379,7 @@ rendererinfo_t eglrendererinfo =
GLBE_Init,
GLBE_GenBrushModelVBO,
GLBE_ClearVBO,
GLBE_UploadAllLightmaps,
GLBE_UpdateLightmaps,
GLBE_SelectEntity,
GLBE_SelectDLight,
GLBE_Scissor,

View File

@ -806,10 +806,7 @@ static qboolean VKVID_Init (rendererstate_t *info, unsigned char *palette)
vkGetInstanceProcAddr = SDL_Vulkan_GetVkGetInstanceProcAddr();
if (!VK_Init(info, extnames, VKSDL_CreateSurface, NULL))
{
SDL_ShowSimpleMessageBox(0, "FTEQuake", extnames[1], sdlwindow);
return false;
}
return true;
}
rendererinfo_t vkrendererinfo =

View File

@ -74,7 +74,10 @@ void R_SetSky(const char *sky)
int i;
const char *shadername;
extern cvar_t r_skyboxname;
Q_strncpyz(cl.skyname, sky, sizeof(cl.skyname));
if (sky)
Q_strncpyz(cl.skyname, sky, sizeof(cl.skyname));
else
sky = cl.skyname;
if (qrenderer <= QR_NONE)
return; //not ready yet...
if (*r_skyboxname.string) //override it with the user's preference
@ -259,7 +262,7 @@ static void R_ForceSky_f(void)
}
void QDECL R_SkyBox_Changed (struct cvar_s *var, char *oldvalue)
{
R_SetSky(var->string);
R_SetSky(NULL);
// Shader_NeedReload(false);
}

View File

@ -62,7 +62,7 @@ void ModBrush_LoadGLStuff(void *ctx, void *data, size_t a, size_t b); //data ===
#define GLdouble GLfloat
#else
#ifdef _WIN32 //windows might use the standard header filename, but it still requires that we manually include windows.h first.
#ifndef WIN32_BLOATED
#if !defined(WIN32_BLOATED) && !defined(WIN32_LEAN_AND_MEAN)
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>

View File

@ -169,6 +169,7 @@ enum
SBITS_TESSELLATION = 0x00100000,
SBITS_AFFINE = 0x00200000,
SBITS_MISC_FULLRATE = 0x00400000, //don't use half-rate shading (for text/ui)
//provided for the backend to hack about with
SBITS_LINES = 0x80000000
@ -653,7 +654,6 @@ struct shader_s
SHADER_HASLIGHTMAP = 1 << 16,
SHADER_HASTOPBOTTOM = 1 << 17,
SHADER_HASREFLECTCUBE = 1 << 18, //shader has a T_GEN_REFLECTCUBE pass (otherwise we can skip surf envmaps for better batching)
// SHADER_STATICDATA = 1 << 18, //set if true: no deforms, no tcgen, rgbgen=identitylighting, alphagen=identity, tmu0=st + tmu1=lm(if available) for every pass, no norms
SHADER_HASREFLECT = 1 << 19, //says that we need to generate a reflection image first
SHADER_HASREFRACT = 1 << 20, //says that we need to generate a refraction image first
SHADER_HASREFRACTDEPTH = 1 << 21, //refraction generation needs to generate a depth texture too.

View File

@ -1570,7 +1570,10 @@ void DL_Close(struct dl_download *dl)
#ifdef MULTITHREAD
dl->threadenable = false;
if (dl->threadctx)
{
Sys_WaitOnThread(dl->threadctx);
dl->threadctx = NULL;
}
#endif
if (dl->file && dl->file->seekstyle < SS_PIPE)
VFS_SEEK(dl->file, 0);

View File

@ -305,7 +305,7 @@ const char *HTTP_RunClient (HTTP_active_connections_t *cl)
char *content;
char *msg, *nl;
char buf2[2560]; //short lived temp buffer.
char resource[2560];
char resource[2560], *args;
char host[256];
char mode[80];
qboolean hostspecified;
@ -389,6 +389,10 @@ const char *HTTP_RunClient (HTTP_active_connections_t *cl)
}
}
args = strchr(resource, '?');
if (args)
*args++=0;
if (!strcmp(resource, "/"))
strcpy(resource, "/index.html");

View File

@ -136,7 +136,7 @@ struct dl_download
/*not used internally by the backend, but used by HTTP_CL_Get/thread wrapper*/
struct dl_download *next;
qboolean (*notifystarted) (struct dl_download *dl, char *mimetype); //mime can be null for some protocols, read dl->totalsize for size. false if the mime just isn't acceptable.
void (*notifycomplete) (struct dl_download *dl);
void (*notifycomplete) (struct dl_download *dl); //lets the requester know that the download context is complete and the handle is no longer valid.
};
vfsfile_t *VFSPIPE_Open(int refs, qboolean seekable); //refs should be 1 or 2, to say how many times it must be closed before its actually closed, so both ends can close separately

View File

@ -265,10 +265,7 @@ void PrepareStun(int epfd, int reportport)
#if 0
char *stunserver = "localhost";
int stunport = 27500;
#elif 1
char *stunserver = "stun.l.google.com";
int stunport = 19302;
#else
#else //sorry about hardcoding a server, but probably few people are gonna care enough.
char *stunserver = "master.frag-net.com";
int stunport = 27950;
#endif

View File

@ -558,6 +558,8 @@ enum qcop_e {
OP_LSHIFT_DI,
OP_RSHIFT_DI,
OP_WSTATE, //for the 'w' part of CWSTATE. will probably never be used, but hey, hexen2...
//special/fake opcodes used by the decompiler.
OPD_GOTO_FORSTART,
OPD_GOTO_WHILE1,

View File

@ -944,6 +944,7 @@ enum {
ERR_BADPLUSPLUSOPERATOR,
ERR_BADNOTTYPE,
ERR_BADTYPECAST,
ERR_BADMEMBER,
ERR_MULTIPLEDEFAULTS,
ERR_CASENOTIMMEDIATE,
ERR_BADSWITCHTYPE,

View File

@ -890,6 +890,8 @@ QCC_opcode_t pr_opcodes[] =
{7, "<<", "LSHIFT_DI", PC_SHIFT, ASSOC_LEFT, &type_double, &type_integer, &type_double, OPF_STD},
{7, ">>", "RSHIFT_DI", PC_SHIFT, ASSOC_LEFT, &type_double, &type_integer, &type_double, OPF_STD},
{7, "<WSTATE>", "WSTATE", PC_NONE, ASSOC_LEFT, &type_float, &type_float, &type_void},
{0, NULL, "OPD_GOTO_FORSTART"},
{0, NULL, "OPD_GOTO_WHILE1"},
@ -968,6 +970,7 @@ static int OpAssignsCount(unsigned int op)
case OP_CALL8H:
return 0; //also, eep.
case OP_STATE:
case OP_WSTATE:
case OP_CSTATE:
case OP_CWSTATE:
case OP_THINKTIME:
@ -2253,7 +2256,7 @@ void QCC_FreeTemp(QCC_sref_t t)
if (t.sym && t.sym->symbolheader)
{
if (--t.sym->symbolheader->refcount < 0)
QCC_PR_ParseWarning(WARN_DEBUGGING, "INTERNAL: over-freed refcount to %s", t.sym->name);
QCC_PR_ParseWarning(WARN_DEBUGGING, "INTERNAL: over-freed refcount to %s", QCC_VarAtOffset(t));
}
}
@ -2277,7 +2280,7 @@ static void QCC_UnFreeTemp(QCC_sref_t t)
if (t.sym && t.sym->symbolheader)
{
if (!t.sym->symbolheader->refcount++)
QCC_PR_ParseWarning(WARN_DEBUGGING, "INTERNAL: %s+%i@%i was already fully freed.", t.sym->name, t.ofs, t.sym->ofs);
QCC_PR_ParseWarning(WARN_DEBUGGING, "INTERNAL: %s+%i@%i was already fully freed.", QCC_VarAtOffset(t), t.ofs, t.sym->ofs);
}
}
@ -3625,21 +3628,56 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_
QCC_sref_t fldthink = QCC_PR_GetSRef(QCC_PR_FieldType(type_function), "think", NULL, true, 0, false);
QCC_sref_t fldnextthink = QCC_PR_GetSRef(type_floatfield, "nextthink", NULL, true, 0, false);
QCC_UnFreeTemp(self);
QCC_UnFreeTemp(self);
//self.frame = var_a;
QCC_StoreSRefToRef(QCC_PR_BuildRef(&tempref, REF_FIELD, self,
fldframe, fldframe.cast->aux_type,
true), var_a, false, false);
false), var_a, false, false);
//self.think = var_b;
QCC_StoreSRefToRef(QCC_PR_BuildRef(&tempref, REF_FIELD, self,
fldthink, fldthink.cast->aux_type,
true), var_b, false, false);
false), var_b, false, false);
//self.frame = time + interval;
time = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], time, QCC_MakeFloatConst(1/qcc_framerate), NULL);
QCC_StoreSRefToRef(QCC_PR_BuildRef(&tempref, REF_FIELD, self,
fldnextthink, fldnextthink.cast->aux_type,
true), time, false, false);
false), time, false, false);
return nullsref;
}
break;
case OP_WSTATE:
{ //there is no normal opcode.
QCC_ref_t tempref;
QCC_sref_t self = QCC_PR_GetSRef(type_entity, "self", NULL, true, 0, false);
QCC_sref_t time = QCC_PR_GetSRef(type_float, "time", NULL, true, 0, false);
QCC_sref_t fldframe = QCC_PR_GetSRef(type_floatfield, "weaponframe", NULL, true, 0, false);
QCC_sref_t fldthink = QCC_PR_GetSRef(QCC_PR_FieldType(type_function), "think", NULL, true, 0, false);
QCC_sref_t fldnextthink = QCC_PR_GetSRef(type_floatfield, "nextthink", NULL, true, 0, false);
float framerate = (qcc_framerate>0)?qcc_framerate:(qcc_targetformat_ishexen2()?20:10);
QCC_UnFreeTemp(self);
QCC_UnFreeTemp(self);
//self.frame = var_a;
QCC_StoreSRefToRef(QCC_PR_BuildRef(&tempref, REF_FIELD, self,
fldframe, fldframe.cast->aux_type,
false), var_a, false, false);
//self.think = var_b;
QCC_StoreSRefToRef(QCC_PR_BuildRef(&tempref, REF_FIELD, self,
fldthink, fldthink.cast->aux_type,
false), var_b, false, false);
//self.frame = time + interval;
time = QCC_PR_Statement(&pr_opcodes[OP_ADD_F], time, QCC_MakeFloatConst(1/framerate), NULL);
QCC_StoreSRefToRef(QCC_PR_BuildRef(&tempref, REF_FIELD, self,
fldnextthink, fldnextthink.cast->aux_type,
false), time, false, false);
return nullsref;
}
break;
@ -4562,6 +4600,12 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_
return var_c;
}
break;
case OP_BITNOT_I64:
op = &pr_opcodes[OP_SUB_I64];
var_b = var_a;
var_a = QCC_MakeInt64Const(~(longlong)0);
var_a.sym->referenced = true;
break;
case OP_BITNOT_F:
op = &pr_opcodes[OP_SUB_F];
var_b = var_a;
@ -5867,7 +5911,7 @@ static void QCC_PrecacheFile (const char *n, int ch)
static void QCC_VerifyFormatString (const char *funcname, QCC_ref_t **arglist, unsigned int argcount)
{
const char *s = "%s";
const char *s = "%s", *reqtype;
int firstarg = 1;
const char *s0;
char *err;
@ -6034,6 +6078,7 @@ noflags:
//case 'll': //long long
case 'l': isfloat = 0; break; //long
case 'L': isfloat = 0; break; //long double
//case 'q': break; //long long in c
case 'j': //[u]intmax_t
case 'z': //size_t
case 't': //ptrdiff_t
@ -6071,6 +6116,7 @@ nolength:
memcpy(formatbuf, s0, s+1-s0);
formatbuf[s+1-s0] = 0;
reqtype = NULL;
switch(*s)
{
//fixme: should we validate char ranges?
@ -6085,9 +6131,7 @@ nolength:
case ev_variant:
break;
default:
{
QCC_PR_ParseWarning(WARN_FORMATSTRING, "%s: %s%s%s requires float at arg %i (got %s%s%s)", funcname, col_name, formatbuf, col_none, thisarg+1, col_type, TypeName(ARGCTYPE(thisarg), temp, sizeof(temp)), col_none);
}
reqtype = "float";
break;
}
}
@ -6101,7 +6145,7 @@ nolength:
case ev_variant:
break;
default:
QCC_PR_ParseWarning(WARN_FORMATSTRING, "%s: %s%s%s requires pointer at arg %i (got %s%s%s)", funcname, col_name, formatbuf, col_none, thisarg+1, col_type, TypeName(ARGCTYPE(thisarg), temp, sizeof(temp)), col_none);
reqtype = "pointer";
break;
}
}
@ -6118,6 +6162,7 @@ nolength:
break;
//fallthrough
default:
reqtype = "int";
QCC_PR_ParseWarning(WARN_FORMATSTRING, "%s: %s%s%s requires int at arg %i (got %s%s%s)", funcname, col_name, formatbuf, col_none, thisarg+1, col_type, TypeName(ARGCTYPE(thisarg), temp, sizeof(temp)), col_none);
break;
}
@ -6133,12 +6178,12 @@ nolength:
case ev_variant:
break;
default:
QCC_PR_ParseWarning(WARN_FORMATSTRING, "%s: %s%s%s requires vector at arg %i (got %s%s%s)", funcname, col_name, formatbuf, col_none, thisarg+1, col_type, TypeName(ARGCTYPE(thisarg), temp, sizeof(temp)), col_none);
reqtype = "vector";
break;
}
}
else
QCC_PR_ParseWarning(WARN_FORMATSTRING, "%s: %s%s%s requires intvector at arg %i (got %s%s%s)", funcname, col_name, formatbuf, col_none, thisarg+1, col_type, TypeName(ARGCTYPE(thisarg), temp, sizeof(temp)), col_none);
reqtype = "intvector";
break;
case 's':
case 'S':
@ -6148,7 +6193,7 @@ nolength:
case ev_variant:
break;
default:
QCC_PR_ParseWarning(WARN_FORMATSTRING, "%s: %s%s%s requires string at arg %i (got %s%s%s)", funcname, col_name, formatbuf, col_none, thisarg+1, col_type, TypeName(ARGCTYPE(thisarg), temp, sizeof(temp)), col_none);
reqtype = "string";
break;
}
break;
@ -6156,6 +6201,52 @@ nolength:
QCC_PR_ParseWarning(WARN_FORMATSTRING, "%s: invalid format string: %s%s%s", funcname, col_name, s0, col_none);
return;
}
if (reqtype)
{
QCC_PR_ParseWarning(WARN_FORMATSTRING, "%s: %s%s%s requires %s at arg %i (got %s%s%s)", funcname, col_name, formatbuf, col_none, reqtype, thisarg+1, col_type, TypeName(ARGCTYPE(thisarg), temp, sizeof(temp)), col_none);
switch(ARGCTYPE(thisarg)->type)
{
case ev_string:
QCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, "%s", "use %s or %S for strings");
break;
case ev_float:
QCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, "%s", "use %g or %f or %hx for floats");
break;
case ev_vector:
QCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, "%s", "use %v for vectors");
break;
case ev_entity:
QCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, "%s", "use %i for entities");
break;
case ev_pointer:
QCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, "%s", "use %p for pointer types");
break;
case ev_integer:
QCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, "%s", "use %i or %lx for 32bit ints");
break;
case ev_uint:
QCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, "%s", "use %lu or or %lx for 32bit ints");
break;
case ev_void: //coder's problem
case ev_field: //cast to int
case ev_function: //cast to int
case ev_variant: //should be accepted by anything...
case ev_int64: //specification problem
case ev_uint64: //specification problem
case ev_double: //specification problem
case ev_struct: //coder's problem
case ev_union: //coder's problem
case ev_accessor: //should be unreachable
case ev_enum: //should be unreachable
case ev_typedef: //should be unreachable
case ev_boolean: //should be unreachable
QCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, "%s", "cast to something else");
break;
}
}
s++;
break;
default:
@ -8925,9 +9016,9 @@ static QCC_ref_t *QCC_PR_ParseField(QCC_ref_t *refbuf, QCC_ref_t *lhs)
return refbuf;
}
if (t->parentclass)
QCC_PR_ParseError(ERR_INTERNAL, "%s is not a field of class %s", QCC_GetSRefName(QCC_RefToDef(field, false)), t->name);
QCC_PR_ParseError(ERR_BADMEMBER, "%s is not a field of class %s", QCC_GetSRefName(QCC_RefToDef(field, false)), t->name);
else
QCC_PR_ParseError(ERR_INTERNAL, "%s is not a field", QCC_GetSRefName(QCC_RefToDef(field, false)));
QCC_PR_ParseError(ERR_BADMEMBER, "%s is not a field", QCC_GetSRefName(QCC_RefToDef(field, false)));
}
lhs = QCC_PR_ParseField(refbuf, lhs);
@ -8935,7 +9026,12 @@ static QCC_ref_t *QCC_PR_ParseField(QCC_ref_t *refbuf, QCC_ref_t *lhs)
lhs = QCC_PR_ParseRefArrayPointer (refbuf, lhs, false, false);
}
else
QCC_PR_ParseError(ERR_INTERNAL, "%s is not a member of %s", QCC_PR_ParseName(), t->name);
{
QCC_PR_ParseWarning(ERR_BADMEMBER, "%s is not a member of %s", QCC_PR_ParseName(), t->name);
if (t->filen)
QCC_PR_Note(ERR_BADMEMBER, t->filen, t->line, "%s is defined here", t->name);
return QCC_PR_BuildRef(refbuf, REF_GLOBAL, QCC_MakeIntConst(0), nullsref, type_void, false);
}
}
else if (flag_qccx && t->type == ev_entity && QCC_PR_CheckToken("["))
{ //p[%0] gives a regular array reference. except that p is probably a float, and we're expecting OP_LOAD_F
@ -9345,7 +9441,11 @@ fieldarrayindex:
return QCC_PR_ParseRefArrayPointer(retbuf, r, allowarrayassign, makearraypointers);
}
}
QCC_PR_ParseError(0, "%s is not a member of %s", mname, tname);
QCC_PR_ParseWarning(ERR_BADMEMBER, "%s is not a member of %s", mname, t->name);
if (t->filen)
QCC_PR_Note(ERR_BADMEMBER, t->filen, t->line, "%s is defined here", t->name);
QCC_PR_ParseError(ERR_BADMEMBER, NULL);
}
if (!ofs && idx.cast)
;
@ -14427,77 +14527,145 @@ void QCC_PR_ParseState (void)
{
QCC_sref_t s1, def;
pbool isinc;
//FIXME: this is ambiguous with pre-inc and post-inc logic.
if (QCC_PR_CheckToken("++") || QCC_PR_CheckToken("--"))
if ((isinc=QCC_PR_CheckToken("++")) || QCC_PR_CheckToken("--"))
{
s1 = QCC_PR_ParseImmediate ();
const QCC_eval_t *first, *last;
int dir = 0;
int op = OP_CSTATE;
if (QCC_PR_CheckToken("("))
{
op = OP_CWSTATE;
if (!QCC_PR_CheckToken("w"))
QCC_PR_Expect("W");
QCC_PR_Expect(")");
}
// s1 = QCC_PR_ParseImmediate ();
s1 = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA);
s1 = QCC_SupplyConversion(s1, ev_float, true);
QCC_PR_Expect("..");
def = QCC_PR_ParseImmediate ();
// def = QCC_PR_ParseImmediate ();
def = QCC_PR_Expression (TOP_PRIORITY, EXPR_DISALLOW_COMMA);
def = QCC_SupplyConversion(def, ev_float, true);
QCC_PR_Expect ("]");
if (s1.cast->type != ev_float || def.cast->type != ev_float)
QCC_PR_ParseError(ERR_STATETYPEMISMATCH, "state type mismatch");
first = QCC_SRef_EvalConst(s1);
last = QCC_SRef_EvalConst(def);
if (first&&last)
{ //whether its a ++ or -- doesn't really matter, but hcc generates an error so we should at least generate a warning.
dir = (last->_float >= first->_float)?1:-1;
if (isinc)
{
if (first->_float > last->_float)
QCC_PR_ParseWarning(ERR_STATETYPEMISMATCH, "Forwards State Cycle with backwards range");
}
else
{
if (first->_float < last->_float)
QCC_PR_ParseWarning(ERR_STATETYPEMISMATCH, "Forwards State Cycle with backwards range");
}
}
if (QCC_OPCodeValid(&pr_opcodes[OP_CSTATE]))
QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_CSTATE], s1, def, NULL));
if (QCC_OPCodeValid(&pr_opcodes[op]))
QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[op], s1, def, NULL));
else
{
QCC_statement_t *patch1, *entercyc, *fwd, *back;
QCC_statement_t *patch1, *entercycf, *entercycb, *fwd, *back;
QCC_sref_t t1, t2;
QCC_sref_t framef, frame;
QCC_sref_t self;
QCC_sref_t cycle_wrapped;
self = QCC_PR_GetSRef(type_entity, "self", NULL, false, 0, false);
framef = QCC_PR_GetSRef(NULL, "frame", NULL, false, 0, false);
framef = QCC_PR_GetSRef(NULL, (op==OP_CWSTATE)?"weaponframe":"frame", NULL, false, 0, false);
cycle_wrapped = QCC_PR_GetSRef(type_float, "cycle_wrapped", NULL, false, 0, false);
frame = QCC_PR_Statement(&pr_opcodes[OP_LOAD_F], self, framef, NULL);
frame = QCC_PR_StatementFlags(&pr_opcodes[OP_LOAD_F], self, framef, NULL, 0);
if (cycle_wrapped.cast)
QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(0), cycle_wrapped, NULL));
QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(0), cycle_wrapped, NULL, STFL_PRESERVEB));
//make sure the frame is within the bounds given.
t1 = QCC_PR_StatementFlags(&pr_opcodes[OP_LT_F], frame, s1, NULL, STFL_PRESERVEA);
t2 = QCC_PR_StatementFlags(&pr_opcodes[OP_GT_F], frame, def, NULL, STFL_PRESERVEA);
t1 = QCC_PR_Statement(&pr_opcodes[OP_OR_F], t1, t2, NULL);
patch1 = QCC_Generate_OP_IFNOT(t1, false);
QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], s1, frame, NULL, STFL_PRESERVEB));
entercyc = QCC_Generate_OP_GOTO();
patch1->b.ofs = &statements[numstatements] - patch1;
t1 = QCC_PR_Statement(&pr_opcodes[OP_GE_F], def, s1, NULL);
fwd = QCC_Generate_OP_IFNOT(t1, false); //this block is the 'it's in a forwards direction'
if (dir)
fwd = NULL; //can skip the checks
else
{
t1 = QCC_PR_StatementFlags(&pr_opcodes[OP_GE_F], def, s1, NULL, STFL_PRESERVEA|STFL_PRESERVEB);
fwd = QCC_Generate_OP_IFNOT(t1, false);
}
if (dir >= 0)
{ //this block is the 'it's in a forwards direction'
//make sure the frame is within the bounds given.
t1 = QCC_PR_StatementFlags(&pr_opcodes[OP_LT_F], frame, s1, NULL, STFL_PRESERVEA|STFL_PRESERVEB);
t2 = QCC_PR_StatementFlags(&pr_opcodes[OP_GT_F], frame, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB);
t1 = QCC_PR_Statement(&pr_opcodes[OP_OR_F], t1, t2, NULL);
patch1 = QCC_Generate_OP_IFNOT(t1, false);
{
QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], s1, frame, NULL, STFL_PRESERVEA|STFL_PRESERVEB));
entercycf = QCC_Generate_OP_GOTO();
}
patch1->b.ofs = &statements[numstatements] - patch1;
QCC_PR_SimpleStatement(&pr_opcodes[OP_ADD_F], frame, QCC_MakeFloatConst(1), frame, false);
t1 = QCC_PR_Statement(&pr_opcodes[OP_GT_F], frame, def, NULL);
t1 = QCC_PR_StatementFlags(&pr_opcodes[OP_GT_F], frame, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB);
patch1 = QCC_Generate_OP_IFNOT(t1, false);
{
QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], s1, frame, NULL, STFL_PRESERVEA|STFL_PRESERVEB));
if (cycle_wrapped.cast)
QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(1), cycle_wrapped, NULL, STFL_PRESERVEB));
}
patch1->b.ofs = &statements[numstatements] - patch1;
}
else entercycf = NULL;
if (fwd)
{
back = QCC_Generate_OP_GOTO();
fwd->b.ofs = &statements[numstatements] - fwd;
}
else
back = NULL;
if (dir <= 0)
{ //reverse animation.
//make sure the frame is within the bounds given.
t1 = QCC_PR_StatementFlags(&pr_opcodes[OP_GT_F], frame, s1, NULL, STFL_PRESERVEA|STFL_PRESERVEB);
t2 = QCC_PR_StatementFlags(&pr_opcodes[OP_LT_F], frame, def, NULL, STFL_PRESERVEA|STFL_PRESERVEB);
t1 = QCC_PR_Statement(&pr_opcodes[OP_OR_F], t1, t2, NULL);
patch1 = QCC_Generate_OP_IFNOT(t1, false);
{
QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], s1, frame, NULL, STFL_PRESERVEA|STFL_PRESERVEB));
entercycb = QCC_Generate_OP_GOTO();
}
patch1->b.ofs = &statements[numstatements] - patch1;
QCC_PR_SimpleStatement(&pr_opcodes[OP_SUB_F], frame, QCC_MakeFloatConst(1), frame, false);
t1 = QCC_PR_StatementFlags(&pr_opcodes[OP_LT_F], frame, def, NULL, STFL_PRESERVEA);
patch1 = QCC_Generate_OP_IFNOT(t1, false);
{
QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], s1, frame, NULL, STFL_PRESERVEB));
if (cycle_wrapped.cast)
QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(1), cycle_wrapped, NULL));
QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(1), cycle_wrapped, NULL, 0));
}
patch1->b.ofs = &statements[numstatements] - patch1;
}
back = QCC_Generate_OP_GOTO();
fwd->b.ofs = &statements[numstatements] - fwd;
{
//reverse animation.
QCC_PR_SimpleStatement(&pr_opcodes[OP_SUB_F], frame, QCC_MakeFloatConst(1), frame, false);
t1 = QCC_PR_StatementFlags(&pr_opcodes[OP_LT_F], frame, s1, NULL, STFL_PRESERVEA);
patch1 = QCC_Generate_OP_IFNOT(t1, false);
{
QCC_FreeTemp(QCC_PR_StatementFlags(&pr_opcodes[OP_STORE_F], def, frame, NULL, STFL_PRESERVEB));
if (cycle_wrapped.cast)
QCC_FreeTemp(QCC_PR_Statement(&pr_opcodes[OP_STORE_F], QCC_MakeFloatConst(1), cycle_wrapped, NULL));
}
patch1->b.ofs = &statements[numstatements] - patch1;
}
back->b.ofs = &statements[numstatements] - back;
else entercycb = NULL;
/*out of range*/entercyc->b.ofs = &statements[numstatements] - entercyc;
if (back)
back->a.ofs = &statements[numstatements] - back;
if (entercycf)
/*out of range*/entercycf->a.ofs = &statements[numstatements] - entercycf;
if (entercycb)
/*out of range*/entercycb->a.ofs = &statements[numstatements] - entercycb;
//self.frame = frame happens with the normal state opcode.
QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_STATE], frame, QCC_MakeSRef(pr_scope->def, 0, pr_scope->type), NULL));
QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[(op==OP_CWSTATE)?OP_WSTATE:OP_STATE], frame, QCC_MakeSRef(pr_scope->def, 0, pr_scope->type), NULL));
}
return;
}

View File

@ -1139,6 +1139,8 @@ static pbool QCC_PR_Precompiler(void)
else if (!QC_strcasecmp(qcc_token, "framerate"))
{
qcc_framerate = atof(msg);
if (qcc_framerate < 0)
qcc_framerate = 0;
}
else if (!QC_strcasecmp(qcc_token, "once"))
{
@ -4072,11 +4074,14 @@ NORETURN void VARGS QCC_PR_ParseError (int errortype, const char *error, ...)
editbadfile(s_filen, pr_source_line);
#endif
QCC_PR_PrintScope();
if (flag_msvcstyle)
externs->Printf ("%s%s(%i) : %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string);
else
externs->Printf ("%s%s:%i: %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string);
if (error)
{
QCC_PR_PrintScope();
if (flag_msvcstyle)
externs->Printf ("%s%s(%i) : %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string);
else
externs->Printf ("%s%s:%i: %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string);
}
longjmp (pr_parse_abort, 1);
}
@ -5826,11 +5831,13 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail)
//swap the class out for the appropriate function type...
newparm = QCC_PR_GenFunctionType(type_void, basetype->params, basetype->num_parms);
parmname = classname;
arraysize = 0;
}
else if (!flag_qcfuncs && basetype == newt && QCC_PR_CheckToken("("))
{
newparm = QCC_PR_ParseFunctionType(false, type_void);
parmname = classname;
arraysize = 0;
}
else
{
@ -6095,6 +6102,50 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail)
}
}
}
/* iterate over every parent-class and see if our method already exists in conflicting nonvirtual type form */
if (isvirt)
{
for(pc = newt; pc && !found; pc = pc->parentclass)
{
struct QCC_typeparam_s *pp;
int numpc;
int i;
found = false;
if (pc == newt)
continue;
pp = parms;
numpc = numparms;
/* iterate over all of the virtual methods */
for (i = 0; i < numpc; i++)
{
if (pp[i].type->type == newparm->type)
{
/* if we found it, abandon this loop - we still have to check the other classes however */
if (!strcmp(pp[i].paramname, parmname))
{
found = true;
break;
}
}
}
/* we didn't find it as a field... so check if it exists as a nonvirtual method */
if (found == false)
{
QC_snprintfz(membername, sizeof(membername), "%s::%s", pc->name, parmname);
/* if we found it, game over */
if (QCC_PR_GetDef(NULL, membername, NULL, false, 0, 0))
QCC_PR_ParseError(0, "%s defined as virtual in %s, but nonvirtual in %s\n", parmname, newt->name, pc->name);
}
}
}
parms[numparms].ofs = basicindex; //ulp, its new
numparms++;

View File

@ -4782,6 +4782,10 @@ static void QCC_PR_CommandLinePrecompilerOptions (void)
else
QCC_PR_Warning(WARN_BADPARAMS, "cmdline", 0, "Unrecognised std parameter (%s)", myargv[i]);
}
else if (!strnicmp(myargv[i], "-state-fps=", 11))
{
qcc_framerate = atof(myargv[i]+11);
}
else if ( !strnicmp(myargv[i], "-F", 2) || WINDOWSARG(!strnicmp(myargv[i], "/F", 2)) )
{
pbool state;
@ -4816,6 +4820,13 @@ static void QCC_PR_CommandLinePrecompilerOptions (void)
flag_ifstring = state;
else if (!stricmp(arg, "true-empty-strings"))
flag_brokenifstring = state;
else if (!stricmp(arg, "emulate-state"))
{
if (qcc_framerate>0 && state)
;//already on, don't force if they already gave it an actual rate.
else
qcc_framerate = state?10:0;
}
else if (!stricmp(arg, "arithmetic-exceptions"))
qccwarningaction[WARN_DIVISIONBY0] = state?WA_ERROR:WA_IGNORE;
else if (!stricmp(arg, "lno"))

View File

@ -120,7 +120,16 @@ static void QCC_FileList(const char *name, const void *compdata, size_t compsize
{
totalsize += plainsize;
filecount += 1;
if (!method && compsize==plainsize)
if (method < 0)
{
if (method == -1-9)
externs->Printf("%s%8u DF64 %s%s\n", col_error, (unsigned)plainsize, name, col_none);
else if (method == -1) //general error
externs->Printf("%s%8u ERR %s%s\n", col_error, (unsigned)plainsize, name, col_none);
else
externs->Printf("%s%8u m%-3i %s%s\n", col_error, (unsigned)plainsize, -1-method, name, col_none);
}
else if (!method && compsize==plainsize)
externs->Printf("%8u %s\n", (unsigned)plainsize, name);
else
externs->Printf("%8u %3u%% %s\n", (unsigned)plainsize, plainsize?(unsigned)((100*compsize)/plainsize):100u, name);
@ -191,10 +200,13 @@ int qcc_wildcmp(const char *wild, const char *string)
static const char *extractonly;
static pbool extractonlyfound;
static const char *extractonly; //the file we're looking for
static pbool extractonlyfound; //for errors.
static pbool extractecho; //print the file to stdout instead of writing it.
static void QCC_FileExtract(const char *name, const void *compdata, size_t compsize, int method, size_t plainsize)
{
if (method < 0)
return; //QC_decode will fail. provided for enumeration reasons.
if (extractonly)
{
const char *sl = strrchr(extractonly, '/');
@ -216,11 +228,19 @@ static void QCC_FileExtract(const char *name, const void *compdata, size_t comps
void *buffer = malloc(plainsize);
if (buffer && QC_decode(progfuncs, compsize, plainsize, method, compdata, buffer))
{
QCC_Mkdir(name);
if (!QCC_WriteFile(name, buffer, plainsize))
externs->Printf(" write failure\n");
if (extractecho)
{
externs->Printf("\n");
fwrite(buffer, 1, plainsize, stdout);
}
else
externs->Printf(" done\n");
{
QCC_Mkdir(name);
if (!QCC_WriteFile(name, buffer, plainsize))
externs->Printf(" write failure\n");
else
externs->Printf(" done\n");
}
}
else
externs->Printf(" read failure\n");
@ -253,6 +273,7 @@ int main (int argc, const char **argv)
pbool writelog = false; //other systems are sane.
#endif
int colours = 2; //auto
int ziparg = -1;
progexterns_t ext;
progfuncs_t funcs;
progfuncs = &funcs;
@ -265,52 +286,6 @@ int main (int argc, const char **argv)
funcs.funcs.parms->Printf = logprintf;
funcs.funcs.parms->Sys_Error = Sys_Error;
if ((argc == 3 && !strcmp(argv[1], "-l")) || (argc >= 3 && !strcmp(argv[1], "-x")))
{
size_t blobsize;
void *blob = QCC_ReadFile(argv[2], NULL, NULL, &blobsize, false);
if (!blob)
{
logprintf("Unable to read %s\n", argv[2]);
return EXIT_FAILURE;
}
if (argc > 3)
{
for (i = 3; i < argc; i++)
{
extractonly = argv[i];
extractonlyfound = false;
QC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileExtract);
if (!extractonlyfound)
externs->Printf("Unable to find file %s\n", extractonly);
}
extractonly = NULL;
}
else if (argv[1][1] == 'x')
QC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileExtract);
else
{
QC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileList);
externs->Printf("Total size %u bytes, %u files\n", (unsigned)totalsize, (unsigned)filecount);
}
free(blob);
return EXIT_SUCCESS;
}
if (argc == 3 && (!strncmp(argv[1], "-z", 2) || !strcmp(argv[1], "-0") || !strcmp(argv[1], "-9")))
{ //exe -0 foo.pk3dir
enum pkgtype_e t;
if (argv[1][1] == '9')
t = PACKAGER_PK3;
else if (argv[1][1] == '0')
t = PACKAGER_PAK; //not really any difference but oh well
else
t = PACKAGER_PK3_SPANNED;
if (Packager_CompressDir(argv[2], t, QCC_PR_PackagerMessage, NULL))
return EXIT_SUCCESS;
else
return EXIT_FAILURE;
}
for (i = 0; i < argc; i++)
{
if (!argv[i])
@ -327,7 +302,20 @@ int main (int argc, const char **argv)
colours = 0;
else if (!strcmp(argv[i], "--color=auto"))
colours = 2;
else if (!strcmp(argv[i], "-l") ||
!strcmp(argv[i], "-x") ||
!strcmp(argv[i], "-p") ||
!strcmp(argv[i], "-z") ||
!strcmp(argv[i], "-0") ||
!strcmp(argv[i], "-9"))
{
ziparg = i;
break; //other args are all filenames. don't misinterpret stuff.
}
}
for (i = 0; i < COL_MAX; i++)
qcccol[i] = "";
#if defined(__linux__) || defined(__unix__)
if (colours == 2)
colours = isatty(STDOUT_FILENO);
@ -346,6 +334,86 @@ int main (int argc, const char **argv)
(void)colours;
#endif
if (ziparg >= 0)
{
if (ziparg+1 >= argc)
{
logprintf("archive name not specified\n");
return EXIT_FAILURE;
}
switch(argv[ziparg][1])
{
case 'l': //list all files.
{
size_t blobsize;
void *blob = QCC_ReadFile(argv[ziparg+1], NULL, NULL, &blobsize, false);
if (blob)
{
QC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileList);
externs->Printf("Total size %lu bytes, %u files\n", (unsigned long)totalsize, (unsigned)filecount);
free(blob);
return EXIT_SUCCESS;
}
logprintf("Unable to read %s\n", argv[ziparg+1]);
}
break;
case 'p': //print (named) files to stdout.
extractecho = true;
//fall through
case 'x': //extract (named) files to working directory.
{ //list/extract/view
size_t blobsize;
void *blob = QCC_ReadFile(argv[ziparg+1], NULL, NULL, &blobsize, false);
int ret = EXIT_FAILURE;
if (!blob)
logprintf("Unable to read %s\n", argv[ziparg+1]);
else if (ziparg+2 < argc)
{
for (i = ziparg+2; i < argc; i++)
{
extractonly = argv[i];
extractonlyfound = false;
QC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileExtract);
if (!extractonlyfound)
externs->Printf("Unable to find file %s\n", extractonly);
else
ret = EXIT_SUCCESS;
}
extractonly = NULL;
}
else
{
QC_EnumerateFilesFromBlob(blob, blobsize, QCC_FileExtract);
ret = EXIT_SUCCESS;
}
free(blob);
return ret;
}
case 'z': //fancy spanned stuff
case '0': //store-only (pak)
case '9': //best compression (pk3)
{ //exe -0 foo.pk3dir
enum pkgtype_e t;
if (argv[1][1] == '9')
t = PACKAGER_PK3;
else if (argv[1][1] == '0')
t = PACKAGER_PAK; //not really any difference but oh well
else
t = PACKAGER_PK3_SPANNED;
if (Packager_CompressDir(argv[ziparg+1], t, QCC_PR_PackagerMessage, NULL))
return EXIT_SUCCESS;
}
break;
default:
//should be unreachable.
break;
}
return EXIT_FAILURE;
}
logfile = writelog?fopen("fteqcc.log", "wt"):false;
if (logfile)

View File

@ -41,6 +41,44 @@ pbool QC_decodeMethodSupported(int method)
return false;
}
#ifdef ZLIB_DEFLATE64
#include "infback9.h" //an obscure compile-your-own part of zlib.
struct def64ctx
{
const char *in;
char *out;
size_t csize;
size_t usize;
char window[65536];
};
static unsigned int QC_Deflate64_Grab(void *vctx, unsigned char **bufptr)
{
struct def64ctx *ctx = vctx;
unsigned int avail = ctx->csize;
*bufptr = (unsigned char *)ctx->in;
ctx->csize = 0;
ctx->in += avail;
return avail;
}
static int QC_Deflate64_Spew(void *vctx, unsigned char *buf, unsigned int buflen)
{
struct def64ctx *ctx = vctx;
if (buflen > ctx->usize)
return 1; //over the size of our buffer...
memcpy(ctx->out, buf, buflen);
ctx->out += buflen;
ctx->usize -= buflen;
return 0;
}
#endif
char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const void *info, char *buffer)
{
int i;
@ -87,6 +125,35 @@ char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const
externs->Sys_Error("Failed block decompression\n");
inflateEnd(&strm);
}
#endif
#ifdef ZLIB_DEFLATE64
else if (method == 9)
{
z_stream strm = {NULL};
struct def64ctx ctx;
ctx.in = info;
ctx.csize = complen;
ctx.out = buffer;
ctx.usize = len;
strm.data_type = Z_UNKNOWN;
inflateBack9Init(&strm, ctx.window);
//getting inflateBack9 to
if (Z_STREAM_END != inflateBack9(&strm, QC_Deflate64_Grab, &ctx, QC_Deflate64_Spew, &ctx))
{ //some stream error?
externs->Printf("Decompression error\n");
buffer = NULL;
}
else if (ctx.csize != 0 || ctx.usize != 0)
{ //corrupt file table?
externs->Printf("Decompression size error\n");
externs->Printf("read %i of %i bytes\n", (unsigned)ctx.csize, (unsigned)complen);
externs->Printf("wrote %i of %i bytes\n", (unsigned)ctx.usize, (unsigned)len);
buffer = NULL;
}
inflateBack9End(&strm);
return buffer;
}
#endif
//add your decryption/decompression routine here.
else
@ -191,8 +258,17 @@ int QC_EnumerateFilesFromBlob(const void *blob, size_t blobsize, void (*cb)(cons
unsigned int cdlen;
const unsigned char *eocd;
const unsigned char *cd;
int nl,el,cl;
unsigned int ofs_le;
unsigned int cd_nl,cd_el,cd_cl;
int ret = 0;
const unsigned char *le;
unsigned int csize, usize, method;
unsigned int le_nl,le_el;
char name[256];
const void *data;
if (blobsize < 22)
return ret;
if (!strncmp(blob, "PACK", 4))
@ -221,13 +297,20 @@ int QC_EnumerateFilesFromBlob(const void *blob, size_t blobsize, void (*cb)(cons
return ret;
for(; cdentries --> 0; cd += 46 + nl+el+cl)
for(; cdentries --> 0 && (QC_ReadRawInt(cd+0) == 0x02014b50); cd += 46 + cd_nl+cd_el+cd_cl)
{
if (QC_ReadRawInt(cd+0) != 0x02014b50)
break;
nl = QC_ReadRawShort(cd+28);
el = QC_ReadRawShort(cd+30);
cl = QC_ReadRawShort(cd+32);
data = NULL, csize=usize=0, method=-1;
cd_nl = QC_ReadRawShort(cd+28); //name length
cd_el = QC_ReadRawShort(cd+30); //extras length
cd_cl = QC_ReadRawShort(cd+32); //comment length
ofs_le = QC_ReadRawInt(cd+42);
if (cd_nl < sizeof(name)) //make can't be too long...
QC_strlcpy(name, cd+46, (cd_nl+1<sizeof(name))?cd_nl+1:sizeof(name));
else
QC_strlcpy(name, "?", sizeof(name));
//1=encrypted
//2,4=encoder flags
@ -240,35 +323,41 @@ int QC_EnumerateFilesFromBlob(const void *blob, size_t blobsize, void (*cb)(cons
//1000=enh comp
//2000=masked localheader
//4000,8000=reserved
if (QC_ReadRawShort(cd+8) & ~0x80e)
continue;
if (!(QC_ReadRawShort(cd+8) & ~0x80e)) //only accept known cd general purpose flags
{
const unsigned char *le = (const unsigned char*)blob + QC_ReadRawInt(cd+42);
unsigned int csize, usize, method;
char name[256];
if (ofs_le+46 < blobsize)
{
le = (const unsigned char*)blob + QC_ReadRawInt(cd+42);
if (QC_ReadRawInt(le+0) != 0x04034b50)
continue;
if (QC_ReadRawShort(le+6) & ~0x80e) //general purpose flags
continue;
method = QC_ReadRawShort(le+8);
if (method != 0 && method != 8)
continue;
if (nl != QC_ReadRawShort(le+26))
continue; //name is weird...
// if (el != QC_ReadRawShort(le+28))
// continue; //extradata is weird...
if (QC_ReadRawInt(le+0) == 0x04034b50) //needs proper local entry tag
if (!(QC_ReadRawShort(le+6) & ~0x80e)) //ignore unsupported general purpose flags
{
le_nl = QC_ReadRawShort(le+26);
le_el = QC_ReadRawShort(le+28);
if (cd_nl == le_nl) //name (length) must match...
// if (cd_el != le_el) //extras does NOT match
{
csize = QC_ReadRawInt(le+18);
usize = QC_ReadRawInt(le+22);
csize = QC_ReadRawInt(le+18);
usize = QC_ReadRawInt(le+22);
if (nl >= sizeof(name))
continue; //name is too long
QC_strlcpy(name, cd+46, (nl+1<sizeof(name))?nl+1:sizeof(name));
data = le+30+le_nl+le_el;
cb(name, le+30+QC_ReadRawShort(le+26)+QC_ReadRawShort(le+28), csize, method, usize);
ret++;
method = QC_ReadRawShort(le+8);
if (method != 0
#ifdef AVAIL_ZLIB
&& method != 8
#endif
#ifdef ZLIB_DEFLATE64
&& method != 9
#endif
)
method=-1-method;
}
}
}
}
cb(name, data, csize, method, usize);
ret++;
}
return ret;
}

View File

@ -835,6 +835,7 @@ void NPP_NQFlush(void)
break;
case svcfte_cgamepacket:
requireextension = PEXT_CSQC;
if (sv_csqcdebug.ival || writedest != &sv.multicast)
{
if (writedest != &sv.multicast)
@ -1919,6 +1920,7 @@ void NPP_QWFlush(void)
break;
case svcfte_cgamepacket:
requireextension = PEXT_CSQC;
if (sv_csqcdebug.ival || writedest != &sv.nqmulticast)
{
if (writedest != &sv.nqmulticast)

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