From 75166b0815a788869d945754731dd3cf746e5644 Mon Sep 17 00:00:00 2001 From: Shpoike Date: Tue, 1 Aug 2023 12:22:38 +0100 Subject: [PATCH] Fix up an __int64 networking issue. allow qc's sprintf builtin to print them ('q' modifier, also works for doubles when paired with the 'float' modifier as appropriate). --- engine/common/common.c | 4 +- engine/common/pr_bgcmd.c | 50 +++++++++++++++++---- engine/qclib/qcc_pr_comp.c | 92 +++++++++++++++++++++++++++----------- 3 files changed, 110 insertions(+), 36 deletions(-) diff --git a/engine/common/common.c b/engine/common/common.c index 1da765f34..07d083b1c 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -2264,9 +2264,9 @@ quint64_t MSG_ReadUInt64 (void) v-=l; b++; } - r = v<<(b*8); + r = (quint64_t)v<<(b*8); while(b --> 0) - r |= MSG_ReadByte()<<(b*8); + r |= (quint64_t)MSG_ReadByte()<<(b*8); return r; } qint64_t MSG_ReadInt64 (void) diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index f88c4d4c4..a89185002 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -7011,7 +7011,7 @@ void QCBUILTIN PF_sprintf_internal (pubprogfuncs_t *prinst, struct globalvars_s char formatbuf[16]; char *f; int argpos = firstarg; - int isfloat; + int isfloat, is64bit; static int dummyivec[3] = {0, 0, 0}; static float dummyvec[3] = {0, 0, 0}; @@ -7024,11 +7024,18 @@ void QCBUILTIN PF_sprintf_internal (pubprogfuncs_t *prinst, struct globalvars_s formatbuf[0] = '%'; #define GETARG_FLOAT(a) (((a)>=firstarg && (a)callargc) ? (G_FLOAT(OFS_PARM0 + 3 * (a))) : 0) +#define GETARG_DOUBLE(a) (((a)>=firstarg && (a)callargc) ? (G_FLOAT(OFS_PARM0 + 3 * (a))) : 0) #define GETARG_VECTOR(a) (((a)>=firstarg && (a)callargc) ? (G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyvec) #define GETARG_INT(a) (((a)>=firstarg && (a)callargc) ? (G_INT(OFS_PARM0 + 3 * (a))) : 0) +#define GETARG_INT64(a) (((a)>=firstarg && (a)callargc) ? (G_INT64(OFS_PARM0 + 3 * (a))) : 0) +#define GETARG_UINT(a) (((a)>=firstarg && (a)callargc) ? (G_UINT(OFS_PARM0 + 3 * (a))) : 0) +#define GETARG_UINT64(a) (((a)>=firstarg && (a)callargc) ? (G_UINT64(OFS_PARM0 + 3 * (a))) : 0) #define GETARG_INTVECTOR(a) (((a)>=firstarg && (a)callargc) ? ((int*) G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyivec) #define GETARG_STRING(a) (((a)>=firstarg && (a)callargc) ? (PR_GetStringOfs(prinst, OFS_PARM0 + 3 * (a))) : "") +#define GETARG_SNUMERIC(t, a) (is64bit?(isfloat ? (t) GETARG_DOUBLE(a) : (t) GETARG_INT64 (a)):(isfloat ? (t) GETARG_FLOAT(a) : (t) GETARG_INT (a))) +#define GETARG_UNUMERIC(t, a) (is64bit?(isfloat ? (t) GETARG_DOUBLE(a) : (t) GETARG_UINT64(a)):(isfloat ? (t) GETARG_FLOAT(a) : (t) GETARG_UINT(a))) + for(;;) { s0 = s; @@ -7050,6 +7057,7 @@ void QCBUILTIN PF_sprintf_internal (pubprogfuncs_t *prinst, struct globalvars_s thisarg = -1; flags = 0; isfloat = -1; + is64bit = 0; // is number following? if(*s >= '0' && *s <= '9') @@ -7176,10 +7184,18 @@ noflags: { switch(*s) { + /* note: this is technically a dp extension, so for our inputs: + dp defined %lx for entity numbers (it not having actual ints) + it later defined %llx for Actual ints, not that it makes a difference in DP. + This leaves us unable to use C's 'l' for our int64 type (aka long), nor 'll' either. + So lean on BSD's 'q'. + + for our outputs, %llx is standard for int64 with MS violating c99 and requiring %I64x instead. + */ case 'h': isfloat = 1; break; //short in C, interpreted as float here. doubled for char. case 'l': isfloat = 0; break; //long, twice for long long... we interpret as int32. case 'L': isfloat = 0; break; //'long double' - //case 'q': isfloat = 0; break; //synonym for long long. + case 'q': is64bit = 1; break; //BSD synonym for long long. or more specifically int64 and NOT int128. case 'j': break; //intmax_t //case 'Z': //synonym for 'z'. do not use case 'z': break; //size_t @@ -7229,6 +7245,24 @@ nolength: *f++ = '.'; *f++ = '*'; } + + switch(*s) + { + case 'd': case 'i': case 'I': + case 'o': case 'u': case 'x': case 'X': case 'p': case 'P': +#ifdef _WIN32 //not c99 + *f++ = 'I'; + *f++ = '6'; + *f++ = '4'; +#else //c99 + *f++ = 'l'; + if (sizeof(long) == 4) + *f++ = 'l'; //go for long long instead +#endif + break; + } + + if (*s == 'p') *f++ = 'x'; else if (*s == 'P') @@ -7246,23 +7280,23 @@ nolength: { case 'd': case 'i': case 'I': if(precision < 0) // not set - Q_snprintfz(o, end - o, formatbuf, width, (isfloat ? (int) GETARG_FLOAT(thisarg) : (int) GETARG_INT(thisarg))); + Q_snprintfz(o, end - o, formatbuf, width, GETARG_SNUMERIC(qint64_t, thisarg)); else - Q_snprintfz(o, end - o, formatbuf, width, precision, (isfloat ? (int) GETARG_FLOAT(thisarg) : (int) GETARG_INT(thisarg))); + Q_snprintfz(o, end - o, formatbuf, width, precision, GETARG_SNUMERIC(qint64_t, thisarg)); o += strlen(o); break; case 'o': case 'u': case 'x': case 'X': case 'p': case 'P': if(precision < 0) // not set - Q_snprintfz(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg))); + Q_snprintfz(o, end - o, formatbuf, width, GETARG_UNUMERIC(quint64_t, thisarg)); else - Q_snprintfz(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg))); + Q_snprintfz(o, end - o, formatbuf, width, precision, GETARG_UNUMERIC(quint64_t, thisarg)); o += strlen(o); break; case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': if(precision < 0) // not set - Q_snprintfz(o, end - o, formatbuf, width, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg))); + Q_snprintfz(o, end - o, formatbuf, width, GETARG_SNUMERIC(double, thisarg)); else - Q_snprintfz(o, end - o, formatbuf, width, precision, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg))); + Q_snprintfz(o, end - o, formatbuf, width, precision, GETARG_SNUMERIC(double, thisarg)); o += strlen(o); break; case 'v': case 'V': diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index bc91e61ba..e330844a0 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -5919,7 +5919,7 @@ static void QCC_VerifyFormatString (const char *funcname, QCC_ref_t **arglist, u char formatbuf[16]; char temp[256]; int argpos = firstarg, argn_last = firstarg; - int isfloat; + int isfloat, is64bit; const QCC_eval_t *formatstring = QCC_SRef_EvalConst(arglist[0]->base); if (!formatstring) //can't check variables. return; @@ -5952,6 +5952,7 @@ static void QCC_VerifyFormatString (const char *funcname, QCC_ref_t **arglist, u width = -1; thisarg = -1; isfloat = -1; + is64bit = 0; // is number following? if(*s >= '0' && *s <= '9') @@ -6078,7 +6079,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 'q': is64bit = 1; break; //BSD's int64 case 'j': //[u]intmax_t case 'z': //size_t case 't': //ptrdiff_t @@ -6125,21 +6126,38 @@ nolength: case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': if (isfloat) { - switch(ARGTYPE(thisarg)) + if (is64bit) { - case ev_float: - case ev_variant: - break; - default: - reqtype = "float"; - break; + switch(ARGTYPE(thisarg)) + { + case ev_double: + case ev_variant: + break; + default: + reqtype = "double"; + break; + } + } + else + { + switch(ARGTYPE(thisarg)) + { + case ev_float: + case ev_variant: + break; + default: + reqtype = "float"; + break; + } } } else { if (*s == 'p' || *s == 'P') { - switch(ARGTYPE(thisarg)) + if (is64bit) + reqtype = "some kind of double-size pointer! oh noes!"; + else switch(ARGTYPE(thisarg)) { case ev_pointer: case ev_variant: @@ -6151,20 +6169,37 @@ nolength: } else { - switch(ARGTYPE(thisarg)) + if (is64bit) { - case ev_integer: - case ev_uint: - case ev_variant: - break; - case ev_entity: //accept ents ONLY for %i - if (*s == 'i') + switch(ARGTYPE(thisarg)) + { + case ev_int64: + case ev_uint64: + case ev_variant: 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; + default: + reqtype = "__int64"; + QCC_PR_ParseWarning(WARN_FORMATSTRING, "%s: %s%s%s requires __int64 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; + } + } + else + { + switch(ARGTYPE(thisarg)) + { + case ev_integer: + case ev_uint: + case ev_variant: + break; + case ev_entity: //accept ents ONLY for %i + if (*s == 'i') + 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; + } } } } @@ -6228,14 +6263,19 @@ nolength: 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_int64: + QCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, "%s", "use %qi or %lqx for 64bit ints"); + break; + case ev_uint64: + QCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, "%s", "use %lqu or or %lqx for 64bit ints"); + break; + case ev_double: + QCC_PR_Note(WARN_FORMATSTRING, s_filen, pr_source_line, "%s", "use %qg or %qf or %hqx for doubles"); + 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