From f7d7a1a9fb68fb3e0ca3d193ed0b5c940ceda60a Mon Sep 17 00:00:00 2001 From: Spoike Date: Tue, 15 Nov 2016 22:22:04 +0000 Subject: [PATCH] fix some search_begin to not fail just because no files were found, fixing a crash reported by eukara. fix Sys_EnumerateFiles on linux to enumerate more loosely, so "*/*/*/*.tga" or whatever can now be searched for, instead of giving no hits. be slightly more promiscuous when loading audio files. try to deal with denormals-are-zero without bugging out. be slightly more verbose about nan origins/velocities. parse custom cvar descriptions specified via the set command. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5023 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/snd_mem.c | 59 ++++++++++-------- engine/client/sys_linux.c | 98 ++++++++++++++++++++++-------- engine/common/cmd.c | 71 +++++++++++++++++----- engine/common/net_ssl_gnutls.c | 36 ++++++----- engine/common/pr_bgcmd.c | 60 ++++++++++++++----- engine/qclib/execloop.h | 21 ++++--- engine/server/sv_phys.c | 4 +- engine/server/sv_sys_unix.c | 105 +++++++++++++++++++++++---------- 8 files changed, 318 insertions(+), 136 deletions(-) diff --git a/engine/client/snd_mem.c b/engine/client/snd_mem.c index e483e7088..a03eca850 100644 --- a/engine/client/snd_mem.c +++ b/engine/client/snd_mem.c @@ -851,6 +851,11 @@ static void S_LoadSoundWorker (void *ctx, void *ctxdata, size_t a, size_t b) { //Con_Printf ("S_LoadSound: %x\n", (int)stackbuf); // load it in + const char *prefixes[] = {"sound/", ""}; + const char *extensions[] = {".wav", ".ogg"}; + char altname[sizeof(namebuffer)]; + char orig[16]; + size_t pre, ex; data = NULL; filesize = 0; @@ -860,38 +865,46 @@ static void S_LoadSoundWorker (void *ctx, void *ctxdata, size_t a, size_t b) s->loadstate = SLS_FAILED; return; } - else if (name[0] == '.' && name[1] == '.' && name[2] == '/') + + for (pre = 0; !data && pre < countof(prefixes); pre++) { - //not relative to sound/ - Q_strcpy(namebuffer, name+3); - } - else - { - //q1 behaviour, relative to sound/ - Q_strcpy(namebuffer, "sound/"); - Q_strcat(namebuffer, name); + if (name[0] == '.' && name[1] == '.' && name[2] == '/') + { //someone's being specific. disable prefixes entirely. + if (pre) + break; + //not relative to sound/ + Q_snprintfz(namebuffer, sizeof(namebuffer), "%s", name+3); + } + else + Q_snprintfz(namebuffer, sizeof(namebuffer), "%s%s", prefixes[pre], name); + data = COM_LoadFile(namebuffer, 5, &filesize); - } - - // Con_Printf ("loading %s\n",namebuffer); - - if (!data) - data = COM_LoadFile(name, 5, &filesize); - if (!data) - { - char altname[sizeof(namebuffer)]; - COM_StripExtension(namebuffer, altname, sizeof(altname)); - COM_DefaultExtension(altname, ".ogg", sizeof(altname)); - data = COM_LoadFile(altname, 5, &filesize); if (data) - Con_DPrintf("found a mangled name\n"); + break; + COM_FileExtension(namebuffer, orig, sizeof(orig)); + COM_StripExtension(namebuffer, altname, sizeof(altname)); + for (ex = 0; ex < countof(extensions); ex++) + { + if (!strcmp(orig, extensions[ex]+1)) + continue; + Q_snprintfz(namebuffer, sizeof(namebuffer), "%s%s", altname, extensions[ex]); + data = COM_LoadFile(namebuffer, 5, &filesize); + if (data) + { + Con_DPrintf("found a mangled name: %s\n", namebuffer); + break; + } + } } } if (!data) { //FIXME: check to see if queued for download. - Con_DPrintf ("Couldn't load %s\n", namebuffer); + if (name[0] == '.' && name[1] == '.' && name[2] == '/') + Con_DPrintf ("Couldn't load %s\n", name+3); + else + Con_DPrintf ("Couldn't load sound/%s\n", name); COM_AddWork(WG_MAIN, S_LoadedOrFailed, s, NULL, SLS_FAILED, 0); return; } diff --git a/engine/client/sys_linux.c b/engine/client/sys_linux.c index f31f560f7..cc275cf3b 100644 --- a/engine/client/sys_linux.c +++ b/engine/client/sys_linux.c @@ -415,43 +415,65 @@ int Sys_DebugLog(char *file, char *fmt, ...) return 1; } -int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t modtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath) +int Sys_EnumerateFiles2 (const char *truepath, int apathofs, const char *match, int (*func)(const char *, qofs_t, time_t modtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath) { DIR *dir; - char apath[MAX_OSPATH]; char file[MAX_OSPATH]; - char truepath[MAX_OSPATH]; - char *s; + const char *s; struct dirent *ent; struct stat st; + const char *wild; + const char *apath = truepath+apathofs; -//printf("path = %s\n", gpath); -//printf("match = %s\n", match); - - if (!gpath) - gpath = ""; - *apath = '\0'; - - Q_strncpyz(apath, match, sizeof(apath)); - for (s = apath+strlen(apath)-1; s >= apath; s--) + //if there's a * in a system path, then we need to scan its parent directory to figure out what the * expands to. + //we can just recurse quicklyish to try to handle it. + wild = strchr(apath, '*'); + if (!wild) + wild = strchr(apath, '?'); + if (wild) { - if (*s == '/') + char subdir[MAX_OSPATH]; + for (s = wild+1; *s && *s != '/'; s++) + ; + while (wild > truepath) { - s[1] = '\0'; - match += s - apath+1; - break; + if (*(wild-1) == '/') + break; + wild--; } + memcpy(file, truepath, wild-truepath); + file[wild-truepath] = 0; + + dir = opendir(file); + memcpy(subdir, wild, s-wild); + subdir[s-wild] = 0; + if (dir) + { + do + { + ent = readdir(dir); + if (!ent) + break; + if (*ent->d_name != '.') + { + if (wildcmp(subdir, ent->d_name)) + { + memcpy(file, truepath, wild-truepath); + Q_snprintfz(file+(wild-truepath), sizeof(file)-(wild-truepath), "%s%s", ent->d_name, s); + if (!Sys_EnumerateFiles2(file, apathofs, match, func, parm, spath)) + { + closedir(dir); + return false; + } + } + } + } while(1); + closedir(dir); + } + return true; } - if (s < apath) //didn't find a '/' - *apath = '\0'; - - Q_snprintfz(truepath, sizeof(truepath), "%s/%s", gpath, apath); -//printf("truepath = %s\n", truepath); -//printf("gamepath = %s\n", gpath); -//printf("apppath = %s\n", apath); -//printf("match = %s\n", match); dir = opendir(truepath); if (!dir) { @@ -486,10 +508,34 @@ int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const } } while(1); closedir(dir); - return true; } +int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t modtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath) +{ + char apath[MAX_OSPATH]; + char truepath[MAX_OSPATH]; + char *s; + if (!gpath) + gpath = ""; + *apath = '\0'; + + Q_strncpyz(apath, match, sizeof(apath)); + for (s = apath+strlen(apath)-1; s >= apath; s--) + { + if (*s == '/') + { + s[1] = '\0'; + match += s - apath+1; + break; + } + } + if (s < apath) //didn't find a '/' + *apath = '\0'; + + Q_snprintfz(truepath, sizeof(truepath), "%s/%s", gpath, apath); + return Sys_EnumerateFiles2(truepath, strlen(gpath)+1, match, func, parm, spath); +} int secbase; diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 86ab6b7bd..6e007c76e 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -3130,6 +3130,7 @@ void Cmd_set_f(void) int forceflags = 0; qboolean docalc; char name[256]; + const char *desc = NULL; if (Cmd_Argc()<3) { @@ -3147,7 +3148,7 @@ void Cmd_set_f(void) Q_strncpyz(name, Cmd_Argv(1), sizeof(name)); - if (!strcmp(Cmd_Argv(0), "setfl") || Cmd_FromGamecode()) //AAHHHH!!! Q2 set command is different + if (!strcmp(Cmd_Argv(0), "setfl") || Cmd_FromGamecode()) //AARGHHHH!!! Q2 set command is different { text = Cmd_Argv(3); while(*text) @@ -3169,41 +3170,81 @@ void Cmd_set_f(void) } text = Cmd_Argv(2); - /*desc = Cmd_Argv(4)*/ + if (Cmd_Argc()>=5) + desc = Cmd_Argv(4); } else if (dpcompat_set.ival && !docalc) { text = Cmd_Argv(2); - /*desc = Cmd_Argv(3)*/ + if (Cmd_Argc()>=4) + desc = Cmd_Argv(3); } else { Cmd_ShiftArgs(1, false); text = Cmd_Args(); - if (!docalc && (*text == '\"' || (*text == '\\' && text[1] == '\"'))) //if it's already quoted, dequote it, and ignore trailing stuff, for q2/q3 compatability - text = Cmd_Argv(1); - else + if (!docalc && Cmd_Argc()==2 && (*text == '\"' || (*text == '\\' && text[1] == '\"'))) //if it's already quoted, dequote it, and ignore trailing stuff, for q2/q3 compatability { - end = strstr(text, "//"); - if (end) + desc = COM_StringParse (text, com_token, sizeof(com_token), false, false); + while (*desc == ' ' || *desc == '\t') + desc++; + if (desc[0] == '/' && desc[1] == '/') { - end--; - while (end >= text) + desc+=2; + while (*desc == ' ' || *desc == '\t') + desc++; + end = desc + strlen(desc); + while (end > desc) { + end--; if (*end == ' ' || *end == '\t' || *end == '\r') - end--; + *(char*)end = 0; + else + break; + } + } + else + desc = NULL; + text = Cmd_Argv(1); + } + else + { + desc = strstr(text, "//"); + if (desc) + end = desc; + else + end = text+strlen(text); + end--; + while (end >= text) + { + if (*end == ' ' || *end == '\t' || *end == '\r') + end--; + else + break; + } + end++; + *(char*)end = 0; + if (desc) + { + desc+=2; + while(*desc == ' ' || *desc == '\t') + desc++; + end = desc + strlen(desc); + while (end > desc) + { + end--; + if (*end == ' ' || *end == '\t' || *end == '\r') + *(char*)end = 0; else break; } - end++; - *(char*)end = 0; - } } + //fixme: should peek onto the next line to see if that's an indented // too, or something. forceflags |= 0; } - var = Cvar_Get (name, text, CVAR_TEAMPLAYTAINT, "Custom variables"); + var = Cvar_Get2 (name, text, CVAR_TEAMPLAYTAINT, desc, "Custom variables"); mark = If_Token_GetMark(); diff --git a/engine/common/net_ssl_gnutls.c b/engine/common/net_ssl_gnutls.c index fb44dc055..f3e023d15 100644 --- a/engine/common/net_ssl_gnutls.c +++ b/engine/common/net_ssl_gnutls.c @@ -3,7 +3,10 @@ //named functions, this makes it *really* easy to port plugins from one engine to annother. #include "quakedef.h" - + +#define GNUTLS_DYNAMIC //statically linking is bad, because that just dynamically links to a .so that probably won't exist. + //on the other hand, it does validate that the function types are correct. + #ifdef HAVE_GNUTLS #if defined(_WIN32) && !defined(MINGW) @@ -116,12 +119,13 @@ typedef int (VARGS gnutls_certificate_verify_function)(gnutls_session_t session) #endif #if GNUTLS_VERSION_MAJOR >= 3 - #define GNUTLS_VERSION_3_0_0_PLUS + #define GNUTLS_HAVE_SYSTEMTRUST #endif #if GNUTLS_VERSION_MAJOR >= 4 || (GNUTLS_VERSION_MAJOR == 3 && (GNUTLS_VERSION_MINOR > 1 || (GNUTLS_VERSION_MINOR == 1 && GNUTLS_VERSION_PATCH >= 1))) - #define GNUTLS_VERSION_3_1_4_PLUS + #define GNUTLS_HAVE_VERIFY3 #endif + static int (VARGS *qgnutls_bye)(gnutls_session_t session, gnutls_close_request_t how); static void (VARGS *qgnutls_perror)(int error); static int (VARGS *qgnutls_handshake)(gnutls_session_t session); @@ -138,18 +142,18 @@ static int (VARGS *qgnutls_certificate_allocate_credentials)(gnutls_certificate_ static int (VARGS *qgnutls_certificate_type_set_priority)(gnutls_session_t session, const int*); static int (VARGS *qgnutls_anon_allocate_client_credentials)(gnutls_anon_client_credentials_t *sc); static int (VARGS *qgnutls_global_init)(void); -static int (VARGS *qgnutls_record_send)(gnutls_session_t session, const void *data, size_t sizeofdata); -static int (VARGS *qgnutls_record_recv)(gnutls_session_t session, void *data, size_t sizeofdata); +static ssize_t (VARGS *qgnutls_record_send)(gnutls_session_t session, const void *data, size_t sizeofdata); +static ssize_t (VARGS *qgnutls_record_recv)(gnutls_session_t session, void *data, size_t sizeofdata); -static int (VARGS *qgnutls_certificate_set_verify_function)(gnutls_certificate_credentials_t cred, gnutls_certificate_verify_function *func); +static void (VARGS *qgnutls_certificate_set_verify_function)(gnutls_certificate_credentials_t cred, gnutls_certificate_verify_function *func); static void *(VARGS *qgnutls_session_get_ptr)(gnutls_session_t session); static void (VARGS *qgnutls_session_set_ptr)(gnutls_session_t session, void *ptr); -#ifdef GNUTLS_VERSION_3_0_0_PLUS +#ifdef GNUTLS_HAVE_SYSTEMTRUST static int (VARGS *qgnutls_certificate_set_x509_system_trust)(gnutls_certificate_credentials_t cred); #else static int (VARGS *qgnutls_certificate_set_x509_trust_file)(gnutls_certificate_credentials_t cred, const char * cafile, gnutls_x509_crt_fmt_t type); #endif -#ifdef GNUTLS_VERSION_3_1_4_PLUS +#ifdef GNUTLS_HAVE_VERIFY3 static int (VARGS *qgnutls_certificate_verify_peers3)(gnutls_session_t session, const char* hostname, unsigned int * status); static int (VARGS *qgnutls_certificate_verification_status_print)(unsigned int status, gnutls_certificate_type_t type, gnutls_datum_t * out, unsigned int flags); #else @@ -165,12 +169,12 @@ static int (VARGS *qgnutls_server_name_set)(gnutls_session_t session, gnutls_ser static qboolean Init_GNUTLS(void) { -#ifdef GNUTLS_VERSION_3_0_0_PLUS +#ifdef GNUTLS_HAVE_SYSTEMTRUST #define GNUTLS_TRUSTFUNCS GNUTLS_FUNC(gnutls_certificate_set_x509_system_trust) #else #define GNUTLS_TRUSTFUNCS GNUTLS_FUNC(gnutls_certificate_set_x509_trust_file) #endif -#ifdef GNUTLS_VERSION_3_1_4_PLUS +#ifdef GNUTLS_HAVE_VERIFY3 #define GNUTLS_VERIFYFUNCS \ GNUTLS_FUNC(gnutls_certificate_verify_peers3) \ GNUTLS_FUNC(gnutls_certificate_verification_status_print) @@ -212,7 +216,7 @@ static qboolean Init_GNUTLS(void) GNUTLS_FUNC(gnutls_free) \ GNUTLS_FUNC(gnutls_server_name_set) \ -#if 1 //GNUTLS_DYNAMIC +#ifdef GNUTLS_DYNAMIC dllhandle_t *hmod; dllfunction_t functable[] = @@ -242,12 +246,12 @@ static qboolean Init_GNUTLS(void) {(void**)&qgnutls_certificate_set_verify_function, "gnutls_certificate_set_verify_function"}, {(void**)&qgnutls_session_get_ptr, "gnutls_session_get_ptr"}, {(void**)&qgnutls_session_set_ptr, "gnutls_session_set_ptr"}, -#ifdef GNUTLS_VERSION_3_0_0_PLUS +#ifdef GNUTLS_HAVE_SYSTEMTRUST {(void**)&qgnutls_certificate_set_x509_system_trust, "gnutls_certificate_set_x509_system_trust"}, #else {(void**)&qgnutls_certificate_set_x509_trust_file, "gnutls_certificate_set_x509_trust_file"}, #endif -#ifdef GNUTLS_VERSION_3_1_4_PLUS +#ifdef GNUTLS_HAVE_VERIFY3 {(void**)&qgnutls_certificate_verify_peers3, "gnutls_certificate_verify_peers3"}, {(void**)&qgnutls_certificate_verification_status_print, "gnutls_certificate_verification_status_print"}, #else @@ -326,7 +330,7 @@ static int QDECL SSL_CheckCert(gnutls_session_t session) unsigned int certstatus; cvar_t *tls_ignorecertificateerrors; -#ifdef GNUTLS_VERSION_3_1_4_PLUS +#ifdef GNUTLS_HAVE_VERIFY3 if (qgnutls_certificate_verify_peers3(session, file->certname, &certstatus) >= 0) { { @@ -339,7 +343,7 @@ static int QDECL SSL_CheckCert(gnutls_session_t session) if (qgnutls_certificate_verification_status_print(certstatus, type, &out, 0) >= 0) { Con_Printf("%s: %s\n", file->certname, out.data); - qgnutls_free(out.data); +//looks like its static anyway. qgnutls_free(out.data); #else if (qgnutls_certificate_verify_peers2(session, &certstatus) >= 0) @@ -591,7 +595,7 @@ vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server, qgnutls_anon_allocate_client_credentials (&anoncred); qgnutls_certificate_allocate_credentials (&xcred); -#ifdef GNUTLS_VERSION_3_0_0_PLUS +#ifdef GNUTLS_HAVE_SYSTEMTRUST qgnutls_certificate_set_x509_system_trust (xcred); #else qgnutls_certificate_set_x509_trust_file (xcred, CAFILE, GNUTLS_X509_FMT_PEM); diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index c957cb82a..7d884dfca 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -30,6 +30,17 @@ void skel_info_f(void); void skel_generateragdoll_f(void); void PF_Common_RegisterCvars(void) { + volatile union + { + int i; + float f; + } a, b; + a.i = 1; + b.i = 1; + if (!(a.f && b.f)) + Con_Printf("WARNING: denormalised floats are disabled. Mods may malfunction\n"); + + Cvar_Register (&sv_gameplayfix_blowupfallenzombies, cvargroup_progs); Cvar_Register (&pr_droptofloorunits, cvargroup_progs); Cvar_Register (&pr_brokenfloatconvert, cvargroup_progs); @@ -1429,7 +1440,11 @@ void QCBUILTIN PF_registercvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_ void QCBUILTIN PF_memalloc (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int size = G_INT(OFS_PARM0); - void *ptr = prinst->AddressableAlloc(prinst, size); + void *ptr; + if (size <= 0 || size > 0x01000000) + ptr = NULL; //don't let them abuse things too much. values that are too large might overflow. + else + ptr = prinst->AddressableAlloc(prinst, size); if (ptr) { memset(ptr, 0, size); @@ -2411,24 +2426,26 @@ int QDECL search_enumerate(const char *name, qofs_t fsize, time_t mtime, void *p //float search_begin(string pattern, float caseinsensitive, float quiet) = #74; void QCBUILTIN PF_search_begin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) -{ //< 0 for error, > 0 for handle. +{ //< 0 for error, >= 0 for handle. + //error includes bad search patterns, but not no files const char *pattern = PR_GetStringOfs(prinst, OFS_PARM0); // qboolean caseinsensitive = G_FLOAT(OFS_PARM1); // qboolean quiet = G_FLOAT(OFS_PARM2); prvmsearch_t *s; + if (!*pattern || (*pattern == '.' && pattern[1] == '.') || *pattern == '/' || *pattern == '\\' || strchr(pattern, ':')) + { + PF_Warningf(prinst, "PF_search_begin: bad search pattern \"%s\"\n", pattern); + G_FLOAT(OFS_RETURN) = -1; + return; + } + s = Z_Malloc(sizeof(*s)); s->fromprogs = prinst; s->handle = prvm_nextsearchhandle++; COM_EnumerateFiles(pattern, search_enumerate, s); - if (s->entries==0) - { - BZ_Free(s); - G_FLOAT(OFS_RETURN) = -1; - return; - } s->next = prvmsearches; prvmsearches = s; G_FLOAT(OFS_RETURN) = s->handle; @@ -2444,7 +2461,7 @@ void QCBUILTIN PF_search_getsize (pubprogfuncs_t *prinst, struct globalvars_s *p { int handle = G_FLOAT(OFS_PARM0); prvmsearch_t *s; - G_FLOAT(OFS_RETURN) = -1; + G_FLOAT(OFS_RETURN) = 0; for (s = prvmsearches; s; s = s->next) { if (s->handle == handle) @@ -6022,6 +6039,7 @@ lh_extension_t QSG_Extensions[] = { // {"DP_ENT_COLORMOD"}, {"DP_ENT_CUSTOMCOLORMAP"}, {"DP_ENT_EXTERIORMODELTOCLIENT"}, + {"DP_ENT_SCALE"}, {"DP_ENT_TRAILEFFECTNUM", 1, NULL, {"particleeffectnum"}, "self.traileffectnum=particleeffectnum(\"myeffectname\"); can be used to attach a particle trail to the given server entity. This is equivelent to calling trailparticles each frame."}, //only in dp6 currently {"DP_ENT_GLOW"}, {"DP_ENT_VIEWMODEL"}, @@ -6111,8 +6129,8 @@ lh_extension_t QSG_Extensions[] = { {"DP_TE_EXPLOSIONRGB", 1, NULL, {"te_explosionrgb"}}, {"_DP_TE_FLAMEJET", 1, NULL, {"te_flamejet"}}, {"DP_TE_PARTICLECUBE", 1, NULL, {"te_particlecube"}}, - {"_DP_TE_PARTICLERAIN", 1, NULL, {"te_particlerain"}}, - {"_DP_TE_PARTICLESNOW", 1, NULL, {"te_particlesnow"}}, + {"DP_TE_PARTICLERAIN", 1, NULL, {"te_particlerain"}}, + {"DP_TE_PARTICLESNOW", 1, NULL, {"te_particlesnow"}}, {"_DP_TE_PLASMABURN", 1, NULL, {"te_plasmaburn"}}, {"_DP_TE_QUADEFFECTS1", 4, NULL, {"te_gunshotquad", "te_spikequad", "te_superspikequad", "te_explosionquad"}}, {"DP_TE_SMALLFLASH", 1, NULL, {"te_smallflash"}}, @@ -6128,7 +6146,6 @@ lh_extension_t QSG_Extensions[] = { {"FTE_CALLTIMEOFDAY", 1, NULL, {"calltimeofday"}, "Replication of mvdsv functionality (call calltimeofday to cause 'timeofday' to be called, with arguments that can be saved off to a global). Generally strftime is simpler to use."}, {"FTE_CSQC_ALTCONSOLES", 4, NULL, {"con_getset", "con_printf", "con_draw", "con_input"}, "The engine tracks multiple consoles. These may or may not be directly visible to the user."}, {"FTE_CSQC_BASEFRAME", 0, NULL, {NULL}, "Specifies that .basebone, .baseframe2, .baselerpfrac, baseframe1time, etc exist in csqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations."}, - {"FTE_QC_BASEFRAME", 0, NULL, {NULL}, "Specifies that .basebone and .baseframe exist in ssqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations, from ssqc."}, #ifdef HALFLIFEMODELS {"FTE_CSQC_HALFLIFE_MODELS"}, //hl-specific skeletal model control #endif @@ -6138,7 +6155,10 @@ lh_extension_t QSG_Extensions[] = { {"FTE_CSQC_SKELETONOBJECTS", 15, NULL, { "skel_create", "skel_build", "skel_get_numbones", "skel_get_bonename", "skel_get_boneparent", "skel_find_bone", "skel_get_bonerel", "skel_get_boneabs", "skel_set_bone", "skel_mul_bone", "skel_mul_bones", "skel_copybones", "skel_delete", "frameforname", "frameduration"}, "Provides container objects for skeletal bone data, which can be modified on a per bone basis if needed. This allows you to dynamically generate animations (or just blend them with greater customisation) instead of being limited to a single animation or two."}, + {"FTE_CSQC_RAWIMAGES", 2, NULL, {"r_uploadimage","r_readimage"}, "Provides raw rgba image access to csqc. With this, the csprogs can read textures into qc-accessible memory, modify it, and then upload it to the renderer."}, {"FTE_CSQC_RENDERTARGETS", 0, NULL, {NULL}, "VF_RT_DESTCOLOUR exists and can be used to redirect any rendering to a texture instead of the screen."}, + {"FTE_CSQC_REVERB", 1, NULL, {"setup_reverb"}, "Specifies that the mod can create custom reverb effects. Whether they will actually be used or not depends upon the sound driver."}, + {"FTE_CSQC_WINDOWCAPTION", 1, NULL, {"setwindowcaption"}, "Provides csqc with the ability to change the window caption as displayed when running windowed or in the task bar when switched out."}, {"FTE_ENT_SKIN_CONTENTS", 0, NULL, {NULL}, "self.skin = CONTENTS_WATER; makes a brush entity into water. use -16 for a ladder."}, {"FTE_ENT_UNIQUESPAWNID"}, {"FTE_EXTENDEDTEXTCODES"}, @@ -6175,18 +6195,25 @@ lh_extension_t QSG_Extensions[] = { {"FTE_PART_NAMESPACE_EFFECTINFO", 0, NULL, {NULL}, "Specifies that effectinfo.bar can load effects from effectinfo.txt for DP compatibility."}, #endif #endif + {"FTE_QC_BASEFRAME", 0, NULL, {NULL}, "Specifies that .basebone and .baseframe exist in ssqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations, from ssqc."}, + {"FTE_QC_FILE_BINARY", 4, NULL, {"fread","fwrite","fseek","fsize"}, "Extends FRIK_FILE with binary read+write, as well as allowing seeking. Requires pointers."}, + {"FTE_QC_CHANGELEVEL_HUB", 0, NULL, {NULL}, "Adds an extra argument to changelevel which is carried over to the next map in the 'spawnspot' global. Maps will be saved+reloaded until the extra argument is omitted again, purging all saved maps. Saved games will contain a copy of each preserved map. parm1-parm64 globals can be used, giving more space to transfer more player data."}, {"FTE_QC_CHECKCOMMAND", 1, NULL, {"checkcommand"}, "Provides a way to test if a console command exists, and whether its a command/alias/cvar. Does not say anything about the expected meanings of any arguments or values."}, {"FTE_QC_CHECKPVS", 1, NULL, {"checkpvs"}}, - {"FTE_QC_HARDWARECURSORS", 0, NULL, {NULL}, "setcursormode exists in both csqc+menuqc, and accepts additional arguments to specify a cursor image to use when this module has focus. If the image exceeds hardware limits, it will be emulated using regular draws - this at least still avoids conflicting cursors."}, + {"FTE_QC_CROSSPRODUCT", 1, NULL, {"crossproduct"}}, + {"FTE_QC_FS_SEARCH_SIZEMTIME", 2, NULL, {"search_getfilesize", "search_getfilemtime"}}, + {"FTE_QC_HARDWARECURSORS", 0, NULL, {NULL}, "setcursormode exists in both csqc+menuqc, and accepts additional arguments to specify a cursor image to use when this module has focus. If the image exceeds hardware limits (or hardware cursors are unsupported), it will be emulated using regular draws - this at least still avoids conflicting cursors as only one will ever be used, even if console+menu+csqc are all overlayed."}, {"FTE_QC_HASHTABLES", 6, NULL, {"hash_createtab", "hash_destroytab", "hash_add", "hash_get", "hash_delete", "hash_getkey"}, "Provides efficient string-based lookups."}, - {"FTE_QC_INTCONV", 4, NULL, {"stoi", "itos", "stoh", "htos"}, "Provides string<>int conversions, including hex representations."}, + {"FTE_QC_INTCONV", 6, NULL, {"stoi", "itos", "stoh", "htos", "itof", "ftoi"}, "Provides string<>int conversions, including hex representations."}, {"FTE_QC_MATCHCLIENTNAME", 1, NULL, {"matchclientname"}}, +// {"FTE_QC_MESHOBJECTS", 0, NULL, {"mesh_create", "mesh_build", "mesh_getvertex", "mesh_getindex", "mesh_setvertex", "mesh_setindex", "mesh_destroy"}, "Provides qc with the ability to create its own meshes."}, {"FTE_QC_PAUSED"}, #ifdef QCGC {"FTE_QC_PERSISTENTTEMPSTRINGS", NOBI "Supersedes DP_QC_MULTIPLETEMPSTRINGS. Temp strings are garbage collected automatically, and do not expire while they're still in use. This makes strzone redundant."}, #endif {"FTE_QC_RAGDOLL_WIP", 1, NULL, {"ragupdate", "skel_set_bone_world", "skel_mmap"}}, {"FTE_QC_SENDPACKET", 1, NULL, {"sendpacket"}, "Allows the use of out-of-band udp packets to/from other hosts. Includes the SV_ParseConnectionlessPacket event."}, + {"FTE_QC_STUFFCMDFLAGS", 1, NULL, {"stuffcmdflags"}, "Variation on regular stuffcmd that gives control over how spectators/mvds should be treated."}, {"FTE_QC_TRACETRIGGER"}, #ifdef Q2CLIENT {"FTE_QUAKE2_CLIENT", 0, NULL, {NULL}, "This engine is able to act as a quake2 client"}, @@ -6223,10 +6250,11 @@ lh_extension_t QSG_Extensions[] = { {"FTE_TE_STANDARDEFFECTBUILTINS", 14, NULL, {"te_gunshot", "te_spike", "te_superspike", "te_explosion", "te_tarexplosion", "te_wizspike", "te_knightspike", "te_lavasplash", "te_teleport", "te_lightning1", "te_lightning2", "te_lightning3", "te_lightningblood", "te_bloodqw"}, "Provides builtins to replace writebytes, with a QW compatible twist."}, #ifdef TERRAIN - {"FTE_TERRAIN_MAP", 0, NULL, {NULL}, "This engine supports .hmp files, as well as terrain embedded within bsp files."}, - {"FTE_RAW_MAP", 0, NULL, {NULL}, "This engine supports directly loading .map files, as well as realtime editing of the various brushes."}, + {"FTE_TERRAIN_MAP", 1, NULL, {"terrain_edit"}, "This engine supports .hmp files, as well as terrain embedded within bsp files."}, + {"FTE_RAW_MAP", 7, NULL, {"brush_get","brush_create","brush_delete","brush_selected","brush_getfacepoints","brush_calcfacepoints","brush_findinvolume"}, "This engine supports directly loading .map files, as well as realtime editing of the various brushes."}, #endif + {"KRIMZON_SV_PARSECLIENTCOMMAND", 3, NULL, {"clientcommand", "tokenize", "argv"}, "SSQC's SV_ParseClientCommand function is able to handle client 'cmd' commands. The tokenizing parts also work in csqc."}, //very very similar to the mvdsv system. {"NEH_CMD_PLAY2"}, {"NEH_RESTOREGAME"}, diff --git a/engine/qclib/execloop.h b/engine/qclib/execloop.h index 0e921a9fe..e3249917d 100644 --- a/engine/qclib/execloop.h +++ b/engine/qclib/execloop.h @@ -38,6 +38,7 @@ #define QCFAULT return (pr_xstatement=(st-pr_statements)-1),PR_HandleFault +#define EVAL_FLOATISTRUE(ev) ((ev)->_int & 0x7fffffff) //mask away sign bit. This avoids using denormalized floats. #ifdef __GNUC__ #define errorif(x) if(__builtin_expect(x,0)) @@ -223,14 +224,18 @@ reeval: break; case OP_AND_F: - OPC->_float = (float)(OPA->_float && OPB->_float); + //original logic + //OPC->_float = (float)(OPA->_float && OPB->_float); + //deal with denormalized floats by ensuring that they're not 0 (ignoring sign bit). + //this avoids issues where the fpu treats denormalised floats as 0, or fpus that don't support denormals. + OPC->_float = (float)(EVAL_FLOATISTRUE(OPA) && EVAL_FLOATISTRUE(OPB)); break; case OP_OR_F: - OPC->_float = (float)(OPA->_float || OPB->_float); + OPC->_float = (float)(EVAL_FLOATISTRUE(OPA) || EVAL_FLOATISTRUE(OPB)); break; case OP_NOT_F: - OPC->_float = (float)(!OPA->_float); + OPC->_float = (float)(!EVAL_FLOATISTRUE(OPA)); break; case OP_NOT_V: OPC->_float = (float)(!OPA->_vector[0] && !OPA->_vector[1] && !OPA->_vector[2]); @@ -549,7 +554,7 @@ reeval: OPC->_vector[2] = ptr->_vector[2]; } break; - + //================== case OP_IFNOT_S: @@ -560,10 +565,11 @@ reeval: case OP_IFNOT_F: RUNAWAYCHECK(); - if (!OPA->_float) + if (!EVAL_FLOATISTRUE(OPA)) st += (sofs)st->b - 1; // offset the s++ break; + //WARNING: vanilla uses this for floats too, which results in a discrepancy with -0 case OP_IFNOT_I: RUNAWAYCHECK(); if (!OPA->_int) @@ -578,16 +584,17 @@ reeval: case OP_IF_F: RUNAWAYCHECK(); - if (OPA->_float) + if (EVAL_FLOATISTRUE(OPA)) st += (sofs)st->b - 1; // offset the s++ break; + //WARNING: vanilla uses this for floats too, which results in a discrepancy with -0 case OP_IF_I: RUNAWAYCHECK(); if (OPA->_int) st += (sofs)st->b - 1; // offset the s++ break; - + case OP_GOTO: RUNAWAYCHECK(); st += (sofs)st->a - 1; // offset the s++ diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index c92ab6cb5..46e784b08 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -148,12 +148,12 @@ void WPhys_CheckVelocity (world_t *w, wedict_t *ent) { if (IS_NAN(ent->v->velocity[i])) { - Con_DPrintf ("Got a NaN velocity on %s\n", PR_GetString(w->progs, ent->v->classname)); + Con_DPrintf ("Got a NaN velocity on entity %i (%s)\n", ent->entnum, PR_GetString(w->progs, ent->v->classname)); ent->v->velocity[i] = 0; } if (IS_NAN(ent->v->origin[i])) { - Con_Printf ("Got a NaN origin on %s\n", PR_GetString(w->progs, ent->v->classname)); + Con_Printf ("Got a NaN origin on entity %i (%s)\n", ent->entnum, PR_GetString(w->progs, ent->v->classname)); ent->v->origin[i] = 0; } } diff --git a/engine/server/sv_sys_unix.c b/engine/server/sv_sys_unix.c index fe805841a..8603590ed 100644 --- a/engine/server/sv_sys_unix.c +++ b/engine/server/sv_sys_unix.c @@ -769,48 +769,65 @@ int main(int argc, char *argv[]) return 0; } - - - - - -int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath) +int Sys_EnumerateFiles2 (const char *truepath, int apathofs, const char *match, int (*func)(const char *, qofs_t, time_t modtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath) { DIR *dir; - char apath[MAX_OSPATH]; char file[MAX_OSPATH]; - char truepath[MAX_OSPATH]; - char *s; + const char *s; struct dirent *ent; struct stat st; + const char *wild; + const char *apath = truepath+apathofs; -//printf("path = %s\n", gpath); -//printf("match = %s\n", match); - - if (!gpath) - gpath = ""; - *apath = '\0'; - - Q_strncpyz(apath, match, sizeof(apath)); - for (s = apath+strlen(apath)-1; s >= apath; s--) + //if there's a * in a system path, then we need to scan its parent directory to figure out what the * expands to. + //we can just recurse quicklyish to try to handle it. + wild = strchr(apath, '*'); + if (!wild) + wild = strchr(apath, '?'); + if (wild) { - if (*s == '/') + char subdir[MAX_OSPATH]; + for (s = wild+1; *s && *s != '/'; s++) + ; + while (wild > truepath) { - s[1] = '\0'; - match += s - apath+1; - break; + if (*(wild-1) == '/') + break; + wild--; } + memcpy(file, truepath, wild-truepath); + file[wild-truepath] = 0; + + dir = opendir(file); + memcpy(subdir, wild, s-wild); + subdir[s-wild] = 0; + if (dir) + { + do + { + ent = readdir(dir); + if (!ent) + break; + if (*ent->d_name != '.') + { + if (wildcmp(subdir, ent->d_name)) + { + memcpy(file, truepath, wild-truepath); + Q_snprintfz(file+(wild-truepath), sizeof(file)-(wild-truepath), "%s%s", ent->d_name, s); + if (!Sys_EnumerateFiles2(file, apathofs, match, func, parm, spath)) + { + closedir(dir); + return false; + } + } + } + } while(1); + closedir(dir); + } + return true; } - if (s < apath) //didn't find a '/' - *apath = '\0'; - - Q_snprintfz(truepath, sizeof(truepath), "%s/%s", gpath, apath); -//printf("truepath = %s\n", truepath); -//printf("gamepath = %s\n", gpath); -//printf("apppath = %s\n", apath); -//printf("match = %s\n", match); dir = opendir(truepath); if (!dir) { @@ -834,6 +851,7 @@ int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const if (!func(file, st.st_size, st.st_mtime, parm, spath)) { + Con_DPrintf("giving up on search after finding %s\n", file); closedir(dir); return false; } @@ -844,9 +862,34 @@ int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const } } while(1); closedir(dir); - return true; } +int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t modtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath) +{ + char apath[MAX_OSPATH]; + char truepath[MAX_OSPATH]; + char *s; + + if (!gpath) + gpath = ""; + *apath = '\0'; + + Q_strncpyz(apath, match, sizeof(apath)); + for (s = apath+strlen(apath)-1; s >= apath; s--) + { + if (*s == '/') + { + s[1] = '\0'; + match += s - apath+1; + break; + } + } + if (s < apath) //didn't find a '/' + *apath = '\0'; + + Q_snprintfz(truepath, sizeof(truepath), "%s/%s", gpath, apath); + return Sys_EnumerateFiles2(truepath, strlen(gpath)+1, match, func, parm, spath); +}