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
This commit is contained in:
Spoike 2016-11-15 22:22:04 +00:00
parent 018aed2c19
commit f7d7a1a9fb
8 changed files with 318 additions and 136 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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();

View File

@ -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);

View File

@ -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"},

View File

@ -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++

View File

@ -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;
}
}

View File

@ -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);
}