From 5445d59d574670bfdfff3996ac2a20656c82234b Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 19 Mar 2024 01:23:51 -0700 Subject: [PATCH] Replays: larger buffers for player name, skin name, color name; bump demo version 0x0009 -> 0x000A - Name buffers are the same size as everywhere else - Billiards Cactus no longer gets cut short - Long player names no longer get cut short - System in place to allow easily changing the buffer sizes across all the demo code --- src/doomstat.h | 2 +- src/g_demo.cpp | 172 +++++++++++++++++++++---------------------------- src/g_demo.h | 12 +++- 3 files changed, 86 insertions(+), 100 deletions(-) diff --git a/src/doomstat.h b/src/doomstat.h index c09e7d96f..011d21c78 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -448,7 +448,7 @@ struct staffbrief_t { UINT16 wad; UINT16 lump; - char name[16]; + char name[MAXPLAYERNAME+1]; tic_t time; tic_t lap; }; diff --git a/src/g_demo.cpp b/src/g_demo.cpp index c01474580..10f8ac01b 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -93,6 +93,25 @@ boolean nodrawers; // for comparative timing purposes boolean noblit; // for comparative timing purposes tic_t demostarttime; // for comparative timing purposes +static constexpr DemoBufferSizes get_buffer_sizes(UINT16 version) +{ + (void)version; + // These sizes are compatible as of version 0x000A + static_assert(MAXPLAYERNAME == 21); + static_assert(SKINNAMESIZE == 16); + static_assert(MAXCOLORNAME == 32); + return {21, 16, 32}; +} + +static DemoBufferSizes g_buffer_sizes; + +size_t copy_fixed_buf(void* p, const void* s, size_t n) +{ + strncpy((char*)p, (const char*)s, n); + ((char*)p)[n] = '\0'; + return n; +} + static char demoname[MAX_WADPATH]; static savebuffer_t demobuf = {0}; static UINT8 *demotime_p, *demoinfo_p; @@ -135,7 +154,7 @@ demoghost *ghosts = NULL; // DEMO RECORDING // -#define DEMOVERSION 0x0009 +#define DEMOVERSION 0x000A #define DEMOHEADER "\xF0" "KartReplay" "\x0F" #define DF_ATTACKMASK (ATTACKING_TIME|ATTACKING_LAP|ATTACKING_SPB) // This demo contains time/lap data @@ -221,7 +240,8 @@ static mobj_t oldghost[MAXPLAYERS]; void G_ReadDemoExtraData(void) { INT32 p, extradata, i; - char name[17]; + char name[64]; + static_assert(sizeof name >= std::max({MAXPLAYERNAME+1u, SKINNAMESIZE+1u, MAXCOLORNAME+1u})); if (leveltime > starttime) { @@ -233,7 +253,7 @@ void G_ReadDemoExtraData(void) } } - memset(name, '\0', 17); + memset(name, '\0', sizeof name); p = READUINT8(demobuf.p); @@ -327,8 +347,7 @@ void G_ReadDemoExtraData(void) if (extradata & DXD_COLOR) { // Color - M_Memcpy(name, demobuf.p, 16); - demobuf.p += 16; + demobuf.p += copy_fixed_buf(name, demobuf.p, g_buffer_sizes.color_name); for (i = 0; i < numskincolors; i++) if (!stricmp(skincolors[i].name, name)) // SRB2kart { @@ -341,19 +360,16 @@ void G_ReadDemoExtraData(void) if (extradata & DXD_NAME) { // Name - M_Memcpy(player_names[p],demobuf.p,16); - demobuf.p += 16; + demobuf.p += copy_fixed_buf(player_names[p], demobuf.p, g_buffer_sizes.player_name); } if (extradata & DXD_FOLLOWER) { // Set our follower - M_Memcpy(name, demobuf.p, 16); - demobuf.p += 16; + demobuf.p += copy_fixed_buf(name, demobuf.p, g_buffer_sizes.skin_name); K_SetFollowerByName(p, name); // Follower's color - M_Memcpy(name, demobuf.p, 16); - demobuf.p += 16; + demobuf.p += copy_fixed_buf(name, demobuf.p, g_buffer_sizes.color_name); for (i = 0; i < numskincolors +2; i++) // +2 because of Match and Opposite { if (!stricmp(Followercolor_cons_t[i].strvalue, name)) @@ -411,7 +427,8 @@ void G_ReadDemoExtraData(void) void G_WriteDemoExtraData(void) { INT32 i, j; - char name[16]; + char name[64]; + static_assert(sizeof name >= std::max({MAXPLAYERNAME+1u, SKINNAMESIZE+1u, MAXCOLORNAME+1u})); for (i = 0; i < MAXPLAYERS; i++) { @@ -464,41 +481,28 @@ void G_WriteDemoExtraData(void) if (demo_extradata[i] & DXD_COLOR) { // Color - memset(name, 0, 16); - strlcpy(name, skincolors[players[i].skincolor].name, 16); - M_Memcpy(demobuf.p,name,16); - demobuf.p += 16; + demobuf.p += copy_fixed_buf(demobuf.p, skincolors[players[i].skincolor].name, g_buffer_sizes.color_name); } if (demo_extradata[i] & DXD_NAME) { // Name - memset(name, 0, 16); - strlcpy(name, player_names[i], 16); - M_Memcpy(demobuf.p,name,16); - demobuf.p += 16; + demobuf.p += copy_fixed_buf(demobuf.p, player_names[i], g_buffer_sizes.player_name); } if (demo_extradata[i] & DXD_FOLLOWER) { // write follower - memset(name, 0, 16); if (players[i].followerskin == -1) - strncpy(name, "None", 16); + demobuf.p += copy_fixed_buf(demobuf.p, "None", g_buffer_sizes.skin_name); else - strlcpy(name, followers[players[i].followerskin].name, 16); - M_Memcpy(demobuf.p, name, 16); - demobuf.p += 16; + demobuf.p += copy_fixed_buf(demobuf.p, followers[players[i].followerskin].name, g_buffer_sizes.skin_name); // write follower color - memset(name, 0, 16); for (j = (numskincolors+2)-1; j > 0; j--) { if (Followercolor_cons_t[j].value == players[i].followercolor) break; } - strlcpy(name, Followercolor_cons_t[j].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" - M_Memcpy(demobuf.p,name,16); - demobuf.p += 16; - + demobuf.p += copy_fixed_buf(demobuf.p, Followercolor_cons_t[j].strvalue, g_buffer_sizes.color_name); // Not KartColor_Names because followercolor has extra values such as "Match" } if (demo_extradata[i] & DXD_WEAPONPREF) { @@ -1293,11 +1297,11 @@ readghosttic: if (ziptic & DXD_SKIN) g->p++; // We _could_ read this info, but it shouldn't change anything in record attack... if (ziptic & DXD_COLOR) - g->p += 16; // Same tbh + g->p += g->sizes.color_name; // Same tbh if (ziptic & DXD_NAME) - g->p += 16; // yea + g->p += g->sizes.player_name; // yea if (ziptic & DXD_FOLLOWER) - g->p += 32; // ok (32 because there's both the skin and the colour) + g->p += g->sizes.skin_name + g->sizes.color_name; if (ziptic & DXD_WEAPONPREF) g->p++; // ditto if (ziptic & DXD_START) @@ -2019,9 +2023,8 @@ static UINT8 G_CheckDemoExtraFiles(savebuffer_t *info, boolean quick) return error; } -static void G_SaveDemoSkins(UINT8 **pp) +static void G_SaveDemoSkins(UINT8 **pp, const DemoBufferSizes &psizes) { - char skin[16]; UINT8 i; UINT8 *availabilitiesbuffer = R_GetSkinAvailabilities(true, -1); @@ -2029,9 +2032,7 @@ static void G_SaveDemoSkins(UINT8 **pp) for (i = 0; i < numskins; i++) { // Skinname, for first attempt at identification. - memset(skin, 0, 16); - strlcpy(skin, skins[i].name, 16); - WRITEMEM((*pp), skin, 16); + (*pp) += copy_fixed_buf((*pp), skins[i].name, psizes.skin_name); // Backup information for second pass. WRITEUINT8((*pp), skins[i].kartspeed); @@ -2045,7 +2046,7 @@ static void G_SaveDemoSkins(UINT8 **pp) } } -static democharlist_t *G_LoadDemoSkins(savebuffer_t *info, UINT8 *worknumskins, boolean getclosest) +static democharlist_t *G_LoadDemoSkins(const DemoBufferSizes &psizes, savebuffer_t *info, UINT8 *worknumskins, boolean getclosest) { UINT8 i, byte, shif; democharlist_t *skinlist = NULL; @@ -2069,14 +2070,13 @@ static democharlist_t *G_LoadDemoSkins(savebuffer_t *info, UINT8 *worknumskins, { INT32 result = -1; - if (P_SaveBufferRemaining(info) < 16+1+1+4) + if (P_SaveBufferRemaining(info) < psizes.skin_name+1+1+4) { Z_Free(skinlist); return NULL; } - READMEM(info->p, skinlist[i].name, 16); - skinlist[i].name[16] = '\0'; + info->p += copy_fixed_buf(skinlist[i].name, info->p, psizes.skin_name); skinlist[i].namehash = quickncasehash(skinlist[i].name, SKINNAMESIZE); skinlist[i].kartspeed = READUINT8(info->p); skinlist[i].kartweight = READUINT8(info->p); @@ -2128,7 +2128,7 @@ static democharlist_t *G_LoadDemoSkins(savebuffer_t *info, UINT8 *worknumskins, return skinlist; } -static void G_SkipDemoSkins(UINT8 **pp) +static void G_SkipDemoSkins(UINT8 **pp, const DemoBufferSizes& psizes) { UINT8 demonumskins; UINT8 i; @@ -2136,7 +2136,7 @@ static void G_SkipDemoSkins(UINT8 **pp) demonumskins = READUINT8((*pp)); for (i = 0; i < demonumskins; ++i) { - (*pp) += 16; // name + (*pp) += psizes.skin_name; // name (*pp)++; // kartspeed (*pp)++; // kartweight (*pp) += 4; // flags @@ -2148,12 +2148,10 @@ static void G_SkipDemoSkins(UINT8 **pp) void G_BeginRecording(void) { UINT8 i, j, p; - char name[MAXCOLORNAME+1]; player_t *player = &players[consoleplayer]; if (demobuf.p) return; - memset(name,0,sizeof(name)); demobuf.p = demobuf.buffer; @@ -2186,6 +2184,7 @@ void G_BeginRecording(void) WRITEUINT16(demobuf.p,DEMOVERSION); demo.version = DEMOVERSION; + g_buffer_sizes = get_buffer_sizes(DEMOVERSION); // Full replay title demobuf.p += 64; @@ -2213,7 +2212,7 @@ void G_BeginRecording(void) G_SaveDemoExtraFiles(&demobuf.p); // character list - G_SaveDemoSkins(&demobuf.p); + G_SaveDemoSkins(&demobuf.p, g_buffer_sizes); if ((demoflags & DF_ATTACKMASK)) { @@ -2293,10 +2292,7 @@ void G_BeginRecording(void) } // Name - memset(name, 0, 16); - strlcpy(name, player_names[p], 16); - M_Memcpy(demobuf.p,name,16); - demobuf.p += 16; + demobuf.p += copy_fixed_buf(demobuf.p, player_names[p], g_buffer_sizes.player_name); for (j = 0; j < MAXAVAILABILITY; j++) { @@ -2308,34 +2304,24 @@ void G_BeginRecording(void) WRITEUINT8(demobuf.p, player->lastfakeskin); // Color - memset(name, 0, 16); - strncpy(name, skincolors[player->skincolor].name, 16); - M_Memcpy(demobuf.p,name,16); - demobuf.p += 16; + demobuf.p += copy_fixed_buf(demobuf.p, skincolors[player->skincolor].name, g_buffer_sizes.color_name); // Save follower's skin name // PS: We must check for 'follower' to determine if the followerskin is valid. It's going to be 0 if we don't have a follower, but 0 is also absolutely a valid follower! // Doesn't really matter if the follower mobj is valid so long as it exists in a way or another. - memset(name, 0, 16); if (player->follower) - strncpy(name, followers[player->followerskin].name, 16); + demobuf.p += copy_fixed_buf(demobuf.p, followers[player->followerskin].name, g_buffer_sizes.skin_name); else - strncpy(name, "None", 16); // Say we don't have one, then. - - M_Memcpy(demobuf.p,name,16); - demobuf.p += 16; + demobuf.p += copy_fixed_buf(demobuf.p, "None", g_buffer_sizes.skin_name); // Say we don't have one, then. // Save follower's colour - memset(name, 0, 16); for (j = (numskincolors+2)-1; j > 0; j--) { if (Followercolor_cons_t[j].value == players[i].followercolor) break; } - strncpy(name, Followercolor_cons_t[j].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" - M_Memcpy(demobuf.p, name, 16); - demobuf.p += 16; + demobuf.p += copy_fixed_buf(demobuf.p, Followercolor_cons_t[j].strvalue, g_buffer_sizes.color_name); // Not KartColor_Names because followercolor has extra values such as "Match" // Score, since Kart uses this to determine where you start on the map WRITEUINT32(demobuf.p, player->score); @@ -2466,7 +2452,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) p++; // numlaps G_SkipDemoExtraFiles(&p); - G_SkipDemoSkins(&p); + G_SkipDemoSkins(&p, get_buffer_sizes(DEMOVERSION)); aflags = flags & DF_ATTACKMASK; I_Assert(aflags); @@ -2531,7 +2517,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) return UINT8_MAX; } - G_SkipDemoSkins(&p); + G_SkipDemoSkins(&p, get_buffer_sizes(oldversion)); if (flags & ATTACKING_TIME) oldtime = READUINT32(p); @@ -2581,7 +2567,7 @@ static bool load_ubjson_standing(menudemo_t* pdemo, tcb::span slice, StandingJson& jsstanding = js.standings[i]; auto& memstanding = pdemo->standings[i]; memstanding.ranking = jsstanding.ranking; - strlcpy(memstanding.name, jsstanding.name.c_str(), 17); + strlcpy(memstanding.name, jsstanding.name.c_str(), sizeof memstanding.name); if (jsstanding.demoskin >= demoskins.size()) { memstanding.skin = demoskins[0].mapping; @@ -2713,7 +2699,7 @@ void G_LoadDemoInfo(menudemo_t *pdemo, boolean allownonmultiplayer) pdemo->addonstatus = G_CheckDemoExtraFiles(&info, true); - skinlist = G_LoadDemoSkins(&info, &worknumskins, false); + skinlist = G_LoadDemoSkins(get_buffer_sizes(pdemoversion), &info, &worknumskins, false); if (!skinlist) { @@ -2807,8 +2793,7 @@ void G_LoadDemoInfo(menudemo_t *pdemo, boolean allownonmultiplayer) pdemo->standings[legacystandingplayercount].ranking = READUINT8(info.p); // Name - M_Memcpy(pdemo->standings[legacystandingplayercount].name, info.p, 16); - info.p += 16; + info.p += copy_fixed_buf(pdemo->standings[legacystandingplayercount].name, info.p, 16); // Skin skinid = READUINT8(info.p); @@ -2817,8 +2802,7 @@ void G_LoadDemoInfo(menudemo_t *pdemo, boolean allownonmultiplayer) pdemo->standings[legacystandingplayercount].skin = skinlist[skinid].mapping; // Color - M_Memcpy(temp,info.p,16); - info.p += 16; + info.p += copy_fixed_buf(temp, info.p, 16); for (i = 0; i < numskincolors; i++) if (!stricmp(skincolors[i].name,temp)) // SRB2kart { @@ -2911,7 +2895,7 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) INT32 i; UINT8 p, numslots = 0; lumpnum_t l; - char color[MAXCOLORNAME+1],follower[17],mapname[MAXMAPLUMPNAME],gtname[MAXGAMETYPELENGTH]; + char color[MAXCOLORNAME+1],follower[SKINNAMESIZE+1],mapname[MAXMAPLUMPNAME],gtname[MAXGAMETYPELENGTH]; char *pdemoname; UINT8 availabilities[MAXPLAYERS][MAXAVAILABILITY]; UINT8 version,subversion; @@ -2931,8 +2915,6 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) G_InitDemoRewind(); - follower[16] = '\0'; - color[MAXCOLORNAME] = '\0'; gtname[MAXGAMETYPELENGTH-1] = '\0'; if (deflumpnum != LUMPERROR) @@ -3093,6 +3075,8 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) return; } + g_buffer_sizes = get_buffer_sizes(demo.version); + // demo title M_Memcpy(demo.titlename, demobuf.p, 64); demobuf.p += 64; @@ -3190,7 +3174,7 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) G_SetGametype(i); // character list - demo.skinlist = G_LoadDemoSkins(&demobuf, &demo.numskins, true); + demo.skinlist = G_LoadDemoSkins(g_buffer_sizes, &demobuf, &demo.numskins, true); if (!demo.skinlist) { snprintf(msg, 1024, M_GetText("%s has an invalid skin list and cannot be played.\n"), pdemoname); @@ -3379,8 +3363,7 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) K_UpdateShrinkCheat(&players[p]); // Name - M_Memcpy(player_names[p],demobuf.p,16); - demobuf.p += 16; + demobuf.p += copy_fixed_buf(player_names[p], demobuf.p, g_buffer_sizes.player_name); for (i = 0; i < MAXAVAILABILITY; i++) { @@ -3395,8 +3378,7 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) lastfakeskin[p] = READUINT8(demobuf.p); // Color - M_Memcpy(color,demobuf.p,16); - demobuf.p += 16; + demobuf.p += copy_fixed_buf(color, demobuf.p, g_buffer_sizes.color_name); for (i = 0; i < numskincolors; i++) if (!stricmp(skincolors[i].name,color)) // SRB2kart { @@ -3405,13 +3387,11 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) } // Follower - M_Memcpy(follower, demobuf.p, 16); - demobuf.p += 16; + demobuf.p += copy_fixed_buf(follower, demobuf.p, g_buffer_sizes.skin_name); K_SetFollowerByName(p, follower); // Follower colour - M_Memcpy(color, demobuf.p, 16); - demobuf.p += 16; + demobuf.p += copy_fixed_buf(color, demobuf.p, g_buffer_sizes.color_name); for (i = 0; i < numskincolors +2; i++) // +2 because of Match and Opposite { if (!stricmp(Followercolor_cons_t[i].strvalue, color)) @@ -3508,7 +3488,7 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) void G_AddGhost(savebuffer_t *buffer, const char *defdemoname) { INT32 i; - char name[17], color[MAXCOLORNAME+1], md5[16]; + char name[MAXPLAYERNAME+1], color[MAXCOLORNAME+1], md5[16]; demoghost *gh; UINT16 flags; UINT8 *p; @@ -3518,9 +3498,6 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname) UINT8 worknumskins; democharlist_t *skinlist = NULL; - name[16] = '\0'; - color[16] = '\0'; - p = buffer->buffer; // read demo header @@ -3546,6 +3523,8 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname) return; } + const DemoBufferSizes ghostsizes = get_buffer_sizes(ghostversion); + p += 64; // title M_Memcpy(md5, p, 16); p += 16; // demo checksum @@ -3590,7 +3569,7 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname) { // FIXME: the rest of this function is not modifying buffer->p directly so fuck it buffer->p = p; - skinlist = G_LoadDemoSkins(buffer, &worknumskins, true); + skinlist = G_LoadDemoSkins(ghostsizes, buffer, &worknumskins, true); p = buffer->p; } if (!skinlist) @@ -3653,8 +3632,7 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname) } // Player name (TODO: Display this somehow if it doesn't match cv_playername!) - M_Memcpy(name, p, 16); - p += 16; + p += copy_fixed_buf(name, p, ghostsizes.player_name); p += MAXAVAILABILITY; @@ -3665,11 +3643,10 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname) p++; // lastfakeskin // Color - M_Memcpy(color, p, 16); - p += 16; + p += copy_fixed_buf(color, p, ghostsizes.color_name); // Follower data was here, skip it, we don't care about it for ghosts. - p += 32; // followerskin (16) + followercolor (16) + p += ghostsizes.skin_name + ghostsizes.color_name; p += 4; // score p += 2; // powerlevel @@ -3689,6 +3666,7 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname) gh = static_cast(Z_Calloc(sizeof(demoghost), PU_LEVEL, NULL)); + gh->sizes = ghostsizes; gh->next = ghosts; gh->buffer = buffer->buffer; M_Memcpy(gh->checksum, md5, 16); @@ -3821,7 +3799,7 @@ staffbrief_t *G_GetStaffGhostBrief(UINT8 *buffer) p++; // numlaps G_SkipDemoExtraFiles(&p); - G_SkipDemoSkins(&p); + G_SkipDemoSkins(&p, get_buffer_sizes(ghostversion)); if (flags & ATTACKING_TIME) temp.time = READUINT32(p); @@ -3861,7 +3839,7 @@ staffbrief_t *G_GetStaffGhostBrief(UINT8 *buffer) if (READUINT8(p) & (DEMO_SPECTATOR|DEMO_BOT)) goto fail; - M_Memcpy(temp.name, p, 16); + copy_fixed_buf(temp.name, p, get_buffer_sizes(ghostversion).player_name); ret = static_cast(Z_Malloc(sizeof(staffbrief_t), PU_STATIC, NULL)); if (ret) diff --git a/src/g_demo.h b/src/g_demo.h index 3decd2059..ac354dadc 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -69,7 +69,7 @@ extern consvar_t cv_recordmultiplayerdemos, cv_netdemosyncquality; extern tic_t demostarttime; struct democharlist_t { - char name[17]; + char name[SKINNAMESIZE+1]; UINT32 namehash; UINT8 mapping; // No, this isn't about levels. It maps to loaded character ID. UINT8 kartspeed; @@ -128,7 +128,7 @@ struct menudemo_t { struct { UINT8 ranking; - char name[17]; + char name[MAXPLAYERNAME+1]; UINT8 skin, color; UINT32 timeorscore; } standings[MAXPLAYERS]; @@ -189,6 +189,13 @@ void G_StoreRewindInfo(void); void G_PreviewRewind(tic_t previewtime); void G_ConfirmRewind(tic_t rewindtime); +struct DemoBufferSizes +{ + size_t player_name; + size_t skin_name; + size_t color_name; +}; + // Your naming conventions are stupid and useless. // There is no conflict here. struct demoghost { @@ -201,6 +208,7 @@ struct demoghost { boolean done; democharlist_t *skinlist; mobj_t oldmo, *mo; + struct DemoBufferSizes sizes; struct demoghost *next; }; extern demoghost *ghosts;