diff --git a/src/byteptr.h b/src/byteptr.h index 933c2af34..01a6293b4 100644 --- a/src/byteptr.h +++ b/src/byteptr.h @@ -150,15 +150,15 @@ FUNCINLINE static ATTRINLINE UINT32 readulong(void *ptr) #undef DEALIGNED -#define WRITESTRINGN(p,s,n) { size_t tmp_i = 0; for (; tmp_i < n && s[tmp_i] != '\0'; tmp_i++) WRITECHAR(p, s[tmp_i]); if (tmp_i < n) WRITECHAR(p, '\0');} -#define WRITESTRING(p,s) { size_t tmp_i = 0; for (; s[tmp_i] != '\0'; tmp_i++) WRITECHAR(p, s[tmp_i]); WRITECHAR(p, '\0');} -#define WRITEMEM(p,s,n) { memcpy(p, s, n); p += n; } +#define WRITESTRINGN(p,s,n) do { size_t tmp_i = 0; for (; tmp_i < n && s[tmp_i] != '\0'; tmp_i++) WRITECHAR(p, s[tmp_i]); if (tmp_i < n) WRITECHAR(p, '\0');} while (0) +#define WRITESTRING(p,s) do { size_t tmp_i = 0; for (; s[tmp_i] != '\0'; tmp_i++) WRITECHAR(p, s[tmp_i]); WRITECHAR(p, '\0');} while (0) +#define WRITEMEM(p,s,n) do { memcpy(p, s, n); p += n; } while (0) #define SKIPSTRING(p) while (READCHAR(p) != '\0') -#define READSTRINGN(p,s,n) { size_t tmp_i = 0; for (; tmp_i < n && (s[tmp_i] = READCHAR(p)) != '\0'; tmp_i++); s[tmp_i] = '\0';} -#define READSTRING(p,s) { size_t tmp_i = 0; for (; (s[tmp_i] = READCHAR(p)) != '\0'; tmp_i++); s[tmp_i] = '\0';} -#define READMEM(p,s,n) { memcpy(s, p, n); p += n; } +#define READSTRINGN(p,s,n) ({ size_t tmp_i = 0; for (; tmp_i < n && (s[tmp_i] = READCHAR(p)) != '\0'; tmp_i++); s[tmp_i] = '\0';}) +#define READSTRING(p,s) ({ size_t tmp_i = 0; for (; (s[tmp_i] = READCHAR(p)) != '\0'; tmp_i++); s[tmp_i] = '\0';}) +#define READMEM(p,s,n) ({ memcpy(s, p, n); p += n; }) #if 0 // old names #define WRITEBYTE(p,b) WRITEUINT8(p,b) diff --git a/src/command.c b/src/command.c index 091dbd8f1..6fd8c11f9 100644 --- a/src/command.c +++ b/src/command.c @@ -56,7 +56,13 @@ static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr); static boolean CV_Command(void); consvar_t *CV_FindVar(const char *name); static const char *CV_StringValue(const char *var_name); + static consvar_t *consvar_vars; // list of registered console variables +static UINT16 consvar_number_of_netids = 0; + +#ifdef OLD22DEMOCOMPAT +static old_demo_var_t *consvar_old_demo_vars; +#endif static char com_token[1024]; static char *COM_Parse(char *data); @@ -1121,14 +1127,16 @@ consvar_t *CV_FindVar(const char *name) return NULL; } -/** Builds a unique Net Variable identifier number, which is used - * in network packets instead of the full name. +#ifdef OLD22DEMOCOMPAT +/** Builds a unique Net Variable identifier number, which was used + * in network packets and demos instead of the full name. + * + * This function only still exists to keep compatibility with old demos. * * \param s Name of the variable. * \return A new unique identifier. - * \sa CV_FindNetVar */ -static inline UINT16 CV_ComputeNetid(const char *s) +static inline UINT16 CV_ComputeOldDemoID(const char *s) { UINT16 ret = 0, i = 0; static UINT16 premiers[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53}; @@ -1142,16 +1150,47 @@ static inline UINT16 CV_ComputeNetid(const char *s) return ret; } +/** Finds a net variable based on its old style hash. If a hash collides, a + * warning is printed and this function returns NULL. + * + * \param chk The variable's old style hash. + * \return A pointer to the variable itself if found, or NULL. + */ +static old_demo_var_t *CV_FindOldDemoVar(UINT16 chk) +{ + old_demo_var_t *demovar; + + for (demovar = consvar_old_demo_vars; demovar; demovar = demovar->next) + { + if (demovar->checksum == chk) + { + if (demovar->collides) + { + CONS_Alert(CONS_WARNING, + "Old demo netvar id %hu is a collision\n", chk); + return NULL; + } + + return demovar; + } + } + + return NULL; +} +#endif/*OLD22DEMOCOMPAT*/ + /** Finds a net variable based on its identifier number. * * \param netid The variable's identifier number. * \return A pointer to the variable itself if found, or NULL. - * \sa CV_ComputeNetid */ static consvar_t *CV_FindNetVar(UINT16 netid) { consvar_t *cvar; + if (netid >= consvar_number_of_netids) + return NULL; + for (cvar = consvar_vars; cvar; cvar = cvar->next) if (cvar->netid == netid) return cvar; @@ -1161,6 +1200,32 @@ static consvar_t *CV_FindNetVar(UINT16 netid) static void Setvalue(consvar_t *var, const char *valstr, boolean stealth); +#ifdef OLD22DEMOCOMPAT +/* Sets up a netvar for compatibility with old demos. */ +static void CV_RegisterOldDemoVar(consvar_t *variable) +{ + old_demo_var_t *demovar; + UINT16 old_demo_id; + + old_demo_id = CV_ComputeOldDemoID(variable->name); + + demovar = CV_FindOldDemoVar(old_demo_id); + + if (demovar) + demovar->collides = true; + else + { + demovar = ZZ_Calloc(sizeof *demovar); + + demovar->checksum = old_demo_id; + demovar->cvar = variable; + + demovar->next = consvar_old_demo_vars; + consvar_old_demo_vars = demovar; + } +} +#endif + /** Registers a variable for later use from the console. * * \param variable The variable to register. @@ -1184,11 +1249,15 @@ void CV_RegisterVar(consvar_t *variable) // check net variables if (variable->flags & CV_NETVAR) { - const consvar_t *netvar; - variable->netid = CV_ComputeNetid(variable->name); - netvar = CV_FindNetVar(variable->netid); - if (netvar) - I_Error("Variables %s and %s have same netid\n", variable->name, netvar->name); + variable->netid = consvar_number_of_netids++; + + /* in case of overflow... */ + if (variable->netid > consvar_number_of_netids) + I_Error("Way too many netvars"); + +#ifdef OLD22DEMOCOMPAT + CV_RegisterOldDemoVar(variable); +#endif } // link the variable in @@ -1448,12 +1517,100 @@ badinput: static boolean serverloading = false; +static consvar_t * +ReadNetVar (UINT8 **p, char **return_value, boolean *return_stealth) +{ + UINT16 netid; + char *val; + boolean stealth; + + consvar_t *cvar; + + netid = READUINT16 (*p); + val = (char *)*p; + SKIPSTRING (*p); + stealth = READUINT8 (*p); + + cvar = CV_FindNetVar(netid); + + if (cvar) + { + (*return_value) = val; + (*return_stealth) = stealth; + + DEBFILE(va("Netvar received: %s [netid=%d] value %s\n", cvar->name, netid, val)); + } + else + CONS_Alert(CONS_WARNING, "Netvar not found with netid %hu\n", netid); + + return cvar; +} + +#ifdef OLD22DEMOCOMPAT +static consvar_t * +ReadOldDemoVar (UINT8 **p, char **return_value, boolean *return_stealth) +{ + UINT16 id; + char *val; + boolean stealth; + + old_demo_var_t *demovar; + + id = READUINT16 (*p); + val = (char *)*p; + SKIPSTRING (*p); + stealth = READUINT8 (*p); + + demovar = CV_FindOldDemoVar(id); + + if (demovar) + { + (*return_value) = val; + (*return_stealth) = stealth; + + return demovar->cvar; + } + else + { + CONS_Alert(CONS_WARNING, "Netvar not found with old demo id %hu\n", id); + return NULL; + } +} +#endif/*OLD22DEMOCOMPAT*/ + +static consvar_t * +ReadDemoVar (UINT8 **p, char **return_value, boolean *return_stealth) +{ + char *name; + char *val; + boolean stealth; + + consvar_t *cvar; + + name = (char *)*p; + SKIPSTRING (*p); + val = (char *)*p; + SKIPSTRING (*p); + stealth = READUINT8 (*p); + + cvar = CV_FindVar(name); + + if (cvar) + { + (*return_value) = val; + (*return_stealth) = stealth; + } + else + CONS_Alert(CONS_WARNING, "Netvar not found with name %s\n", name); + + return cvar; +} + static void Got_NetVar(UINT8 **p, INT32 playernum) { consvar_t *cvar; - UINT16 netid; char *svalue; - UINT8 stealth = false; + boolean stealth; if (playernum != serverplayer && !IsPlayerAdmin(playernum) && !serverloading) { @@ -1463,23 +1620,14 @@ static void Got_NetVar(UINT8 **p, INT32 playernum) SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); return; } - netid = READUINT16(*p); - cvar = CV_FindNetVar(netid); - svalue = (char *)*p; - SKIPSTRING(*p); - stealth = READUINT8(*p); - if (!cvar) - { - CONS_Alert(CONS_WARNING, "Netvar not found with netid %hu\n", netid); - return; - } - DEBFILE(va("Netvar received: %s [netid=%d] value %s\n", cvar->name, netid, svalue)); + cvar = ReadNetVar(p, &svalue, &stealth); - Setvalue(cvar, svalue, stealth); + if (cvar) + Setvalue(cvar, svalue, stealth); } -void CV_SaveNetVars(UINT8 **p) +void CV_SaveVars(UINT8 **p, boolean in_demo) { consvar_t *cvar; UINT8 *count_p = *p; @@ -1491,7 +1639,10 @@ void CV_SaveNetVars(UINT8 **p) for (cvar = consvar_vars; cvar; cvar = cvar->next) if ((cvar->flags & CV_NETVAR) && !CV_IsSetToDefault(cvar)) { - WRITEUINT16(*p, cvar->netid); + if (in_demo) + WRITESTRING(*p, cvar->name); + else + WRITEUINT16(*p, cvar->netid); WRITESTRING(*p, cvar->string); WRITEUINT8(*p, false); ++count; @@ -1499,11 +1650,15 @@ void CV_SaveNetVars(UINT8 **p) WRITEUINT16(count_p, count); } -void CV_LoadNetVars(UINT8 **p) +static void CV_LoadVars(UINT8 **p, + consvar_t *(*got)(UINT8 **p, char **ret_value, boolean *ret_stealth)) { consvar_t *cvar; UINT16 count; + char *val; + boolean stealth; + // prevent "invalid command received" serverloading = true; @@ -1513,11 +1668,33 @@ void CV_LoadNetVars(UINT8 **p) count = READUINT16(*p); while (count--) - Got_NetVar(p, 0); + { + cvar = (*got)(p, &val, &stealth); + + if (cvar) + Setvalue(cvar, val, stealth); + } serverloading = false; } +void CV_LoadNetVars(UINT8 **p) +{ + CV_LoadVars(p, ReadNetVar); +} + +#ifdef OLD22DEMOCOMPAT +void CV_LoadOldDemoVars(UINT8 **p) +{ + CV_LoadVars(p, ReadOldDemoVar); +} +#endif + +void CV_LoadDemoVars(UINT8 **p) +{ + CV_LoadVars(p, ReadDemoVar); +} + static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth); void CV_ResetCheatNetVars(void) diff --git a/src/command.h b/src/command.h index 404052ce4..b39153a65 100644 --- a/src/command.h +++ b/src/command.h @@ -144,6 +144,19 @@ typedef struct consvar_s //NULL, NULL, 0, NULL, NULL |, 0, NULL, NULL, 0, 0, NUL struct consvar_s *next; } consvar_t; +#ifdef OLD22DEMOCOMPAT +typedef struct old_demo_var old_demo_var_t; + +struct old_demo_var +{ + UINT16 checksum; + boolean collides;/* this var is a collision of multiple hashes */ + + consvar_t *cvar; + old_demo_var_t *next; +}; +#endif/*OLD22DEMOCOMPAT*/ + extern CV_PossibleValue_t CV_OnOff[]; extern CV_PossibleValue_t CV_YesNo[]; extern CV_PossibleValue_t CV_Unsigned[]; @@ -184,9 +197,18 @@ void CV_AddValue(consvar_t *var, INT32 increment); void CV_SaveVariables(FILE *f); // load/save gamesate (load and save option and for network join in game) -void CV_SaveNetVars(UINT8 **p); +void CV_SaveVars(UINT8 **p, boolean in_demo); + +#define CV_SaveNetVars(p) CV_SaveVars(p, false) void CV_LoadNetVars(UINT8 **p); +#define CV_SaveDemoVars(p) CV_SaveVars(p, true) +void CV_LoadDemoVars(UINT8 **p); + +#ifdef OLD22DEMOCOMPAT +void CV_LoadOldDemoVars(UINT8 **p); +#endif + // reset cheat netvars after cheats is deactivated void CV_ResetCheatNetVars(void); diff --git a/src/doomdef.h b/src/doomdef.h index 380adf781..8853b4aae 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -657,4 +657,7 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// Render flats on walls #define WALLFLATS +/// Maintain compatibility with older 2.2 demos +#define OLD22DEMOCOMPAT + #endif // __DOOMDEF__ diff --git a/src/g_demo.c b/src/g_demo.c index 4dad85a3c..6c58f12fb 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -1525,7 +1525,7 @@ void G_BeginRecording(void) } // Save netvar data - CV_SaveNetVars(&demo_p); + CV_SaveDemoVars(&demo_p); memset(&oldcmd,0,sizeof(oldcmd)); memset(&oldghost,0,sizeof(oldghost)); @@ -1756,6 +1756,9 @@ void G_DoPlayDemo(char *defdemoname) UINT32 randseed, followitem; fixed_t camerascale,shieldscale,actionspd,mindash,maxdash,normalspeed,runspeed,jumpfactor,height,spinheight; char msg[1024]; +#ifdef OLD22DEMOCOMPAT + boolean use_old_demo_vars = false; +#endif skin[16] = '\0'; color[MAXCOLORNAME] = '\0'; @@ -1818,10 +1821,13 @@ void G_DoPlayDemo(char *defdemoname) case DEMOVERSION: // latest always supported cnamelen = MAXCOLORNAME; break; +#ifdef OLD22DEMOCOMPAT // all that changed between then and now was longer color name case 0x000c: cnamelen = 16; + use_old_demo_vars = true; break; +#endif // too old, cannot support. default: snprintf(msg, 1024, M_GetText("%s is an incompatible replay format and cannot be played.\n"), pdemoname); @@ -1923,7 +1929,13 @@ void G_DoPlayDemo(char *defdemoname) } // net var data - CV_LoadNetVars(&demo_p); +#ifdef OLD22DEMOCOMPAT + if (use_old_demo_vars) + CV_LoadOldDemoVars(&demo_p); + else +#else + CV_LoadDemoVars(&demo_p); +#endif // Sigh ... it's an empty demo. if (*demo_p == DEMOMARKER)