- refactor of the quote storage.

This was consolidated for both EDuke and RedNukem frontends, put into a class with strict access control and the length limit was lifted.
The new class will eventually allow better localization control.
This commit is contained in:
Christoph Oelckers 2019-12-04 00:28:28 +01:00
parent c561255018
commit 72857db17b
26 changed files with 418 additions and 596 deletions

View file

@ -752,6 +752,7 @@ set (PCH_SOURCES
common/secrets.cpp common/secrets.cpp
common/compositesavegame.cpp common/compositesavegame.cpp
common/savegamehelp.cpp common/savegamehelp.cpp
common/quotes.cpp
common/2d/v_2ddrawer.cpp common/2d/v_2ddrawer.cpp
common/2d/v_draw.cpp common/2d/v_draw.cpp

View file

@ -384,12 +384,6 @@ void ctrlGetInput(void)
strafe = ClipRange(strafe-(info.dx<<5), -2048, 2048); strafe = ClipRange(strafe-(info.dx<<5), -2048, 2048);
#if 0
if (info.dz < 0)
gInput.mlook = ClipRange((info.dz+127)>>7, -127, 127);
else
gInput.mlook = ClipRange(info.dz>>7, -127, 127);
#endif
if (g_MyAimMode) if (g_MyAimMode)
gInput.q16mlook = fix16_clamp(fix16_div(fix16_from_int(info.mousey), F16(128)), F16(-127)>>2, F16(127)>>2); gInput.q16mlook = fix16_clamp(fix16_div(fix16_from_int(info.mousey), F16(128)), F16(-127)>>2, F16(127)>>2);
else else

65
source/common/quotemgr.h Normal file
View file

@ -0,0 +1,65 @@
#pragma once
#include "zstring.h"
#include "gstrings.h"
// Reimplementation of the Duke quote string buffer.
// Needed because these strings need a level of abstraction from the engine to allow localization
// and because some of the quotes are really status messages that need to be shared between the different games.
// Also a good opportunity to consolidate the data buffers from Duke and Redneck frontends.
enum
{
MAXQUOTES = 16384,
};
class Quotes
{
FString quotes[MAXQUOTES];
FString exquotes[MAXQUOTES];
void MakeStringLabel(FString &quote);
public:
void InitializeQuote(int num, const char *text, bool fromscript = false);
void InitializeExQuote(int num, const char *text, bool fromscript = false);
const char *GetQuote(int num)
{
return GStrings.localize(quotes[num]);
}
const char *GetExQuote(int num)
{
return GStrings.localize(exquotes[num]);
}
const char *GetRawQuote(int num)
{
return quotes[num];
}
const char *GetRawExQuote(int num)
{
return exquotes[num];
}
void CopyQuote(int dst, int src)
{
quotes[dst] = quotes[src];
}
void CopyExQuote(int dst, int src)
{
quotes[dst] = exquotes[src];
}
void AppendQuote(int dst, int src, int len = -1);
void AppendExQuote(int dst, int src, int len = -1);
void FormatQuote(int dst, const char* fmt, ...);
void Substitute(int num, const char* text, const char* replc);
void ReadFromSavegame();
void WriteToSavegame();
};
extern Quotes quoteMgr;

180
source/common/quotes.cpp Normal file
View file

@ -0,0 +1,180 @@
/*
**
** quotes.cpp
** Duke-Nukem-style quote buffer
**
**---------------------------------------------------------------------------
** Copyright 2019 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
** This is actually a VERY inefficient way to manage strings
** but needs to be preserved because the CON VM depends on it.
*/
#include "quotemgr.h"
#include "savegamehelp.h"
#include "sjson.h"
void Quotes::MakeStringLabel(FString &quote)
{
}
void Quotes::InitializeQuote(int num, const char *text, bool fromscript)
{
quotes[num] = num;
if (fromscript) // means this is the initial setup from the source data.
{
MakeStringLabel(quotes[num]);
}
}
void Quotes::InitializeExQuote(int num, const char *text, bool fromscript)
{
exquotes[num] = num;
if (fromscript) // means this is the initial setup from the source data.
{
MakeStringLabel(quotes[num]);
}
}
void Quotes::AppendQuote(int dst, int src, int len)
{
// This needs to apply the localization because the combined string is not localizable anymore.
if (quotes[dst][0] == '$') quotes[dst] = GStrings.localize(quotes[dst]);
if (len < 0) quotes[dst] << GStrings.localize(quotes[src]);
else quotes[dst] += FString(GStrings.localize(quotes[src]), len);
}
void Quotes::AppendExQuote(int dst, int src, int len)
{
// This needs to apply the localization because the combined string is not localizable anymore.
if (quotes[dst][0] == '$') quotes[dst] = GStrings.localize(quotes[dst]);
if (len < 0) quotes[dst] << GStrings.localize(exquotes[src]);
else quotes[dst] += FString(GStrings.localize(exquotes[src]), len);
}
void Quotes::FormatQuote(int dst, const char* fmt, ...)
{
va_list ap;
va_start(ap, fmt);
quotes[dst].VFormat(fmt, ap);
}
void Quotes::Substitute(int dst, const char* text, const char* replc)
{
if (quotes[dst][0] == '$') quotes[dst] = GStrings.localize(quotes[dst]);
quotes[dst].Substitute(text, replc);
}
void Quotes::ReadFromSavegame()
{
for (auto& q : quotes) q = "";
for (auto& q : exquotes) q = "";
auto fil = ReadSavegameChunk("quotes.json");
if (!fil.isOpen())
{
return;
}
auto text = fil.ReadPadded(1);
fil.Close();
if (text.Size() == 0)
{
return;
}
sjson_context* ctx = sjson_create_context(0, 0, NULL);
sjson_node* root = sjson_decode(ctx, (const char*)text.Data());
auto qs = sjson_find_member(root, "quotes");
auto xs = sjson_find_member(root, "exquotes");
sjson_node* q;
sjson_foreach(q, qs)
{
int index = (int)strtoll(q->key, nullptr, 10);
quotes[index] = q->string_;
}
sjson_foreach(q, xs)
{
int index = (int)strtoll(q->key, nullptr, 10);
exquotes[index] = q->string_;
}
sjson_destroy_context(ctx);
}
void Quotes::WriteToSavegame()
{
sjson_context* ctx = sjson_create_context(0, 0, NULL);
if (!ctx)
{
return;
}
sjson_node* root = sjson_mkobject(ctx);
sjson_node* qs = sjson_mkobject(ctx);
sjson_node* xs = sjson_mkobject(ctx);
for (unsigned i = 0; i < MAXQUOTES; i++)
{
if (quotes[i].IsNotEmpty())
{
char buff[10];
snprintf(buff, 10, "%d", i);
sjson_append_member(ctx, qs, buff, sjson_mkstring(ctx, quotes[i]));
}
if (exquotes[i].IsNotEmpty())
{
char buff[10];
snprintf(buff, 10, "%d", i);
sjson_append_member(ctx, xs, buff, sjson_mkstring(ctx, exquotes[i]));
}
}
sjson_append_member(ctx, root, "quotes", qs);
sjson_append_member(ctx, root, "exquotes", xs);
char* encoded = sjson_stringify(ctx, root, " ");
FileWriter* fil = WriteSavegameChunk("quotes.json");
if (!fil)
{
sjson_destroy_context(ctx);
return;
}
fil->Write(encoded, strlen(encoded));
sjson_free_string(ctx, encoded);
sjson_destroy_context(ctx);
return;
}
Quotes quoteMgr;

View file

@ -44,6 +44,7 @@
#include "statistics.h" #include "statistics.h"
#include "secrets.h" #include "secrets.h"
#include "s_music.h" #include "s_music.h"
#include "quotemgr.h"
static CompositeSavegameWriter savewriter; static CompositeSavegameWriter savewriter;
static FResourceFile *savereader; static FResourceFile *savereader;
@ -79,6 +80,7 @@ bool OpenSaveGameForRead(const char *name)
ReadStatistics(); ReadStatistics();
SECRET_Load(); SECRET_Load();
MUS_Restore(); MUS_Restore();
quoteMgr.ReadFromSavegame();
} }
return savereader != nullptr; return savereader != nullptr;
@ -155,6 +157,7 @@ void G_WriteSaveHeader(const char *name, const char*mapname, const char *maptitl
SaveStatistics(); SaveStatistics();
SECRET_Save(); SECRET_Save();
MUS_Save(); MUS_Save();
quoteMgr.WriteToSavegame();
} }
//============================================================================= //=============================================================================

View file

@ -499,7 +499,7 @@ void G_DoCheats(void)
} }
else else
{ {
Bstrcpy(apStrings[QUOTE_RESERVED4], "Come Get Some!"); quoteMgr.InitializeQuote(QUOTE_RESERVED4, "Come Get Some!");
S_PlaySound(DUKE_GETWEAPON2); S_PlaySound(DUKE_GETWEAPON2);
P_DoQuote(QUOTE_RESERVED4, pPlayer); P_DoQuote(QUOTE_RESERVED4, pPlayer);
@ -654,7 +654,7 @@ void G_DoCheats(void)
case CHEAT_TODD: case CHEAT_TODD:
if (NAM) if (NAM)
{ {
Bstrcpy(apStrings[QUOTE_RESERVED4], CheatDescriptions[CHEAT_TODD]); quoteMgr.InitializeQuote(QUOTE_RESERVED4, CheatDescriptions[CHEAT_TODD]);
P_DoQuote(QUOTE_RESERVED4, pPlayer); P_DoQuote(QUOTE_RESERVED4, pPlayer);
} }
else else
@ -690,7 +690,7 @@ void G_DoCheats(void)
if (++g_noEnemies == 3) if (++g_noEnemies == 3)
g_noEnemies = 0; g_noEnemies = 0;
Bsprintf(apStrings[QUOTE_RESERVED4], "Monsters: %s", s[g_noEnemies]); quoteMgr.InitializeQuote(QUOTE_RESERVED4, "Monsters: %s", s[g_noEnemies]);
P_DoQuote(QUOTE_RESERVED4, pPlayer); P_DoQuote(QUOTE_RESERVED4, pPlayer);
end_cheat(pPlayer); end_cheat(pPlayer);

View file

@ -143,7 +143,7 @@ void G_OpenDemoWrite(void)
if ((g_player[myconnectindex].ps->gm&MODE_GAME) && g_player[myconnectindex].ps->dead_flag) if ((g_player[myconnectindex].ps->gm&MODE_GAME) && g_player[myconnectindex].ps->dead_flag)
{ {
Bstrcpy(apStrings[QUOTE_RESERVED4], "CANNOT START DEMO RECORDING WHEN DEAD!"); quoteMgr.InitializeQuote(QUOTE_RESERVED4, "CANNOT START DEMO RECORDING WHEN DEAD!");
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps); P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
ud.recstat = m_recstat = 0; ud.recstat = m_recstat = 0;
return; return;
@ -180,7 +180,7 @@ void G_OpenDemoWrite(void)
delete g_demo_filePtr; delete g_demo_filePtr;
g_demo_filePtr = nullptr; g_demo_filePtr = nullptr;
error_wopen_demo: error_wopen_demo:
Bstrcpy(apStrings[QUOTE_RESERVED4], "FAILED STARTING DEMO RECORDING. SEE CONSOLE FOR DETAILS."); quoteMgr.InitializeQuote(QUOTE_RESERVED4, "FAILED STARTING DEMO RECORDING. SEE CONSOLE FOR DETAILS.");
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps); P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
ud.recstat = m_recstat = 0; ud.recstat = m_recstat = 0;
return; return;
@ -190,7 +190,7 @@ error_wopen_demo:
demorec_diffs = demorec_diffs_cvar; demorec_diffs = demorec_diffs_cvar;
demorec_difftics = demorec_difftics_cvar; demorec_difftics = demorec_difftics_cvar;
Bsprintf(apStrings[QUOTE_RESERVED4], "DEMO %d RECORDING STARTED", demonum-1); quoteMgr.InitializeQuote(QUOTE_RESERVED4, "DEMO %d RECORDING STARTED", demonum-1);
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps); P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
ud.reccnt = 0; ud.reccnt = 0;
@ -285,7 +285,7 @@ void G_CloseDemoWrite(void)
sv_freemem(); sv_freemem();
Bstrcpy(apStrings[QUOTE_RESERVED4], "DEMO RECORDING STOPPED"); quoteMgr.InitializeQuote(QUOTE_RESERVED4, "DEMO RECORDING STOPPED");
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps); P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
} }
#if KRANDDEBUG #if KRANDDEBUG

View file

@ -5396,12 +5396,6 @@ static void G_Cleanup(void)
G_FreeMapState(i); G_FreeMapState(i);
} }
for (i=MAXQUOTES-1; i>=0; i--)
{
Xfree(apStrings[i]);
Xfree(apXStrings[i]);
}
for (i=MAXPLAYERS-1; i>=0; i--) for (i=MAXPLAYERS-1; i>=0; i--)
{ {
Xfree(g_player[i].ps); Xfree(g_player[i].ps);
@ -6347,7 +6341,7 @@ int G_DoMoveThings(void)
{ {
if (ldist(&sprite[pPlayer->i], &sprite[hitData.sprite]) < 9216) if (ldist(&sprite[pPlayer->i], &sprite[hitData.sprite]) < 9216)
{ {
Bsprintf(apStrings[QUOTE_RESERVED3], "%s", &g_player[playerNum].user_name[0]); quoteMgr.InitializeQuote(QUOTE_RESERVED3, "%s", &g_player[playerNum].user_name[0]);
pPlayer->fta = 12, pPlayer->ftq = QUOTE_RESERVED3; pPlayer->fta = 12, pPlayer->ftq = QUOTE_RESERVED3;
} }
} }

View file

@ -2166,129 +2166,31 @@ LUNATIC_EXTERN void C_DefineProjectile(int32_t j, int32_t what, int32_t val)
int32_t C_AllocQuote(int32_t qnum) int32_t C_AllocQuote(int32_t qnum)
{ {
Bassert((unsigned)qnum < MAXQUOTES); Bassert((unsigned)qnum < MAXQUOTES);
// No longer needed, quotes are now FStrings.
if (apStrings[qnum] == NULL) return 1;
{
apStrings[qnum] = (char *)Xcalloc(MAXQUOTELEN,sizeof(uint8_t));
return 1;
}
return 0;
} }
#ifndef EDUKE32_TOUCH_DEVICES
static void C_ReplaceQuoteSubstring(const size_t q, char const * const query, char const * const replacement)
{
size_t querylength = Bstrlen(query);
for (bssize_t i = MAXQUOTELEN - querylength - 2; i >= 0; i--)
if (Bstrncmp(&apStrings[q][i], query, querylength) == 0)
{
Bmemset(tempbuf, 0, sizeof(tempbuf));
Bstrncpy(tempbuf, apStrings[q], i);
Bstrcat(tempbuf, replacement);
Bstrcat(tempbuf, &apStrings[q][i + querylength]);
Bstrncpy(apStrings[q], tempbuf, MAXQUOTELEN - 1);
i = MAXQUOTELEN - querylength - 2;
}
}
#endif
void C_InitQuotes(void) void C_InitQuotes(void)
{ {
for (int i = 0; i < 128; i++) C_AllocQuote(i);
#ifdef EDUKE32_TOUCH_DEVICES
apStrings[QUOTE_DEAD] = 0;
#else
auto openkeys = Bindings.GetKeysForCommand("+open"); auto openkeys = Bindings.GetKeysForCommand("+open");
if (openkeys.Size()) if (openkeys.Size())
{ {
auto OpenGameFunc = C_NameKeys(openkeys.Data(), 1); auto OpenGameFunc = C_NameKeys(openkeys.Data(), 1);
C_ReplaceQuoteSubstring(QUOTE_DEAD, "SPACE", OpenGameFunc); quoteMgr.Substitute(QUOTE_DEAD, "SPACE", OpenGameFunc);
C_ReplaceQuoteSubstring(QUOTE_DEAD, "OPEN", OpenGameFunc); quoteMgr.Substitute(QUOTE_DEAD, "OPEN", OpenGameFunc);
C_ReplaceQuoteSubstring(QUOTE_DEAD, "USE", OpenGameFunc); quoteMgr.Substitute(QUOTE_DEAD, "USE", OpenGameFunc);
} }
#endif
// most of these are based on Blood, obviously g_numObituaries = 48;
const char *PlayerObituaries[] =
{
"^02%s^02 beat %s^02 like a cur",
"^02%s^02 broke %s",
"^02%s^02 body bagged %s",
"^02%s^02 boned %s^02 like a fish",
"^02%s^02 castrated %s",
"^02%s^02 creamed %s",
"^02%s^02 crushed %s",
"^02%s^02 destroyed %s",
"^02%s^02 diced %s",
"^02%s^02 disemboweled %s",
"^02%s^02 erased %s",
"^02%s^02 eviscerated %s",
"^02%s^02 flailed %s",
"^02%s^02 flattened %s",
"^02%s^02 gave AnAl MaDnEsS to %s",
"^02%s^02 gave %s^02 Anal Justice",
"^02%s^02 hosed %s",
"^02%s^02 hurt %s^02 real bad",
"^02%s^02 killed %s",
"^02%s^02 made dog meat out of %s",
"^02%s^02 made mincemeat out of %s",
"^02%s^02 manhandled %s",
"^02%s^02 massacred %s",
"^02%s^02 mutilated %s",
"^02%s^02 murdered %s",
"^02%s^02 neutered %s",
"^02%s^02 punted %s",
"^02%s^02 reamed %s",
"^02%s^02 ripped %s^02 a new orifice",
"^02%s^02 rocked %s",
"^02%s^02 sent %s^02 to hell",
"^02%s^02 shredded %s",
"^02%s^02 slashed %s",
"^02%s^02 slaughtered %s",
"^02%s^02 sliced %s",
"^02%s^02 smacked %s around",
"^02%s^02 smashed %s",
"^02%s^02 snuffed %s",
"^02%s^02 sodomized %s",
"^02%s^02 splattered %s",
"^02%s^02 sprayed %s",
"^02%s^02 squashed %s",
"^02%s^02 throttled %s",
"^02%s^02 toasted %s",
"^02%s^02 vented %s",
"^02%s^02 ventilated %s",
"^02%s^02 wasted %s",
"^02%s^02 wrecked %s",
};
const char *PlayerSelfObituaries[] =
{
"^02%s^02 is excrement",
"^02%s^02 is hamburger",
"^02%s^02 suffered scrotum separation",
"^02%s^02 volunteered for population control",
"^02%s^02 has suicided",
"^02%s^02 bled out",
};
EDUKE32_STATIC_ASSERT(OBITQUOTEINDEX + ARRAY_SIZE(PlayerObituaries)-1 < MAXQUOTES);
EDUKE32_STATIC_ASSERT(SUICIDEQUOTEINDEX + ARRAY_SIZE(PlayerSelfObituaries)-1 < MAXQUOTES);
g_numObituaries = ARRAY_SIZE(PlayerObituaries);
for (bssize_t i = g_numObituaries - 1; i >= 0; i--) for (bssize_t i = g_numObituaries - 1; i >= 0; i--)
{ {
if (C_AllocQuote(i + OBITQUOTEINDEX)) quoteMgr.FormatQuote(i + OBITQUOTEINDEX, "$TXT_OBITUARY%d", i + 1);
Bstrcpy(apStrings[i + OBITQUOTEINDEX], PlayerObituaries[i]);
} }
g_numSelfObituaries = ARRAY_SIZE(PlayerSelfObituaries); g_numSelfObituaries = 6;
for (bssize_t i = g_numSelfObituaries - 1; i >= 0; i--) for (bssize_t i = g_numSelfObituaries - 1; i >= 0; i--)
{ {
if (C_AllocQuote(i + SUICIDEQUOTEINDEX)) quoteMgr.FormatQuote(i + SUICIDEQUOTEINDEX, "$TXT_SELFOBIT%d", i + 1);
Bstrcpy(apStrings[i + SUICIDEQUOTEINDEX], PlayerSelfObituaries[i]);
} }
} }
@ -3167,7 +3069,7 @@ DO_DEFSTATE:
case CON_QUOTE: case CON_QUOTE:
C_GetNextValue(LABEL_DEFINE); C_GetNextValue(LABEL_DEFINE);
if (EDUKE32_PREDICT_FALSE(((unsigned)g_scriptPtr[-1] >= MAXQUOTES) || apStrings[g_scriptPtr[-1]] == NULL)) if (EDUKE32_PREDICT_FALSE(((unsigned)g_scriptPtr[-1] >= MAXQUOTES)))
{ {
g_errorCnt++; g_errorCnt++;
C_ReportError(-1); C_ReportError(-1);
@ -5329,6 +5231,7 @@ repeatcase:
case CON_DEFINEQUOTE: case CON_DEFINEQUOTE:
case CON_REDEFINEQUOTE: case CON_REDEFINEQUOTE:
{
if (tw == CON_DEFINEQUOTE) if (tw == CON_DEFINEQUOTE)
{ {
g_scriptPtr--; g_scriptPtr--;
@ -5353,53 +5256,27 @@ repeatcase:
if (tw == CON_DEFINEQUOTE) if (tw == CON_DEFINEQUOTE)
g_scriptPtr--; g_scriptPtr--;
i = 0;
scriptSkipSpaces(); scriptSkipSpaces();
if (tw == CON_REDEFINEQUOTE) TArray<char> buffer;
{
if (apXStrings[g_numXStrings] == NULL)
apXStrings[g_numXStrings] = (char *)Xcalloc(MAXQUOTELEN,sizeof(uint8_t));
}
while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0) while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
{ {
/* buffer.Push(*textptr);
if (*textptr == '%' && *(textptr+1) == 's') textptr++;
{
initprintf("%s:%d: error: quote text contains string identifier.\n",g_szScriptFileName,g_lineNumber);
g_numCompilerErrors++;
while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0) textptr++;
break;
}
*/
if (tw == CON_DEFINEQUOTE)
*(apStrings[k]+i) = *textptr;
else
*(apXStrings[g_numXStrings]+i) = *textptr;
textptr++,i++;
if (EDUKE32_PREDICT_FALSE(i >= MAXQUOTELEN-1))
{
initprintf("%s:%d: warning: truncating quote text to %d characters.\n",g_scriptFileName,g_lineNumber,MAXQUOTELEN-1);
g_warningCnt++;
scriptSkipLine();
break;
}
} }
buffer.Push(0);
if (tw == CON_DEFINEQUOTE)
quoteMgr.InitializeQuote(k, buffer.Data(), true);
else
quoteMgr.InitializeExQuote(k, buffer.Data(), true);
if (tw == CON_DEFINEQUOTE)
if (tw != CON_DEFINEQUOTE)
{ {
if ((unsigned)k < MAXQUOTES)
*(apStrings[k]+i) = '\0';
}
else
{
*(apXStrings[g_numXStrings]+i) = '\0';
scriptWriteValue(g_numXStrings++); scriptWriteValue(g_numXStrings++);
} }
continue; continue;
}
case CON_DEFINECHEATDESCRIPTION: case CON_DEFINECHEATDESCRIPTION:
g_scriptPtr--; g_scriptPtr--;
@ -5981,14 +5858,7 @@ void C_PrintStats(void)
MAXSPRITES * sizeof(spritetype)/(1<<6)), MAXSPRITES * sizeof(spritetype)/(1<<6)),
g_gameVarCount, MAXGAMEVARS, g_gameArrayCount, MAXGAMEARRAYS); g_gameVarCount, MAXGAMEVARS, g_gameArrayCount, MAXGAMEARRAYS);
int cnt = g_numXStrings; int cnt = 0;
for (auto &ptr : apStrings)
if (ptr)
cnt++;
if (cnt) initprintf("%d strings, ", cnt);
cnt = 0;
for (auto & apScriptEvent : apScriptEvents) for (auto & apScriptEvent : apScriptEvents)
if (apScriptEvent) if (apScriptEvent)

View file

@ -39,6 +39,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "version.h" #include "version.h"
#include "menu/menu.h" #include "menu/menu.h"
#include "c_dispatch.h" #include "c_dispatch.h"
#include "quotemgr.h"
#include "debugbreak.h" #include "debugbreak.h"
extern bool rotatesprite_2doverride; extern bool rotatesprite_2doverride;
@ -2772,7 +2773,7 @@ badindex:
int const strIndex = *insptr++; int const strIndex = *insptr++;
int const XstrIndex = *insptr++; int const XstrIndex = *insptr++;
Bstrcpy(apStrings[strIndex], apXStrings[XstrIndex]); quoteMgr.CopyExQuote(strIndex, XstrIndex);
dispatch(); dispatch();
} }
@ -3547,9 +3548,9 @@ badindex:
int const gameVar = *insptr++; int const gameVar = *insptr++;
int const quoteNum = Gv_GetVar(*insptr++); int const quoteNum = Gv_GetVar(*insptr++);
VM_ASSERT((unsigned)quoteNum < MAXQUOTES && apStrings[quoteNum], "invalid quote %d\n", quoteNum); VM_ASSERT((unsigned)quoteNum < MAXQUOTES, "invalid quote %d\n", quoteNum);
Gv_SetVar(gameVar, Bstrlen(apStrings[quoteNum])); Gv_SetVar(gameVar, strlen(quoteMgr.GetQuote(quoteNum)));
dispatch(); dispatch();
} }
@ -3572,11 +3573,11 @@ badindex:
if (EDUKE32_PREDICT_FALSE(v.tileNum < 0 || v.tileNum + 127 >= MAXTILES)) if (EDUKE32_PREDICT_FALSE(v.tileNum < 0 || v.tileNum + 127 >= MAXTILES))
CON_ERRPRINTF("invalid base tilenum %d\n", v.tileNum); CON_ERRPRINTF("invalid base tilenum %d\n", v.tileNum);
else if (EDUKE32_PREDICT_FALSE((unsigned)v.quoteNum >= MAXQUOTES || apStrings[v.quoteNum] == NULL)) else if ((unsigned)v.quoteNum >= MAXQUOTES)
CON_ERRPRINTF("invalid quote %d\n", v.quoteNum); CON_ERRPRINTF("invalid quote %d\n", v.quoteNum);
else else
{ {
vec2_t dim = G_ScreenTextSize(v.tileNum, v.vect.x, v.vect.y, v.vect.z, v.blockAngle, apStrings[v.quoteNum], 2 | v.orientation, vec2_t dim = G_ScreenTextSize(v.tileNum, v.vect.x, v.vect.y, v.vect.z, v.blockAngle, quoteMgr.GetQuote(v.quoteNum), 2 | v.orientation,
v.offset.x, v.offset.y, v.between.x, v.between.y, v.f, v.bound[0].x, v.bound[0].y, v.bound[1].x, v.offset.x, v.offset.y, v.between.x, v.between.y, v.f, v.bound[0].x, v.bound[0].y, v.bound[1].x,
v.bound[1].y); v.bound[1].y);
@ -3668,12 +3669,12 @@ badindex:
int const quoteIndex = Gv_GetVar(*insptr++); int const quoteIndex = Gv_GetVar(*insptr++);
int const gameFunc = Gv_GetVar(*insptr++); int const gameFunc = Gv_GetVar(*insptr++);
int funcPos = Gv_GetVar(*insptr++); int funcPos = Gv_GetVar(*insptr++);
VM_ASSERT((unsigned)quoteIndex < MAXQUOTES && apStrings[quoteIndex], "invalid quote %d\n", quoteIndex); VM_ASSERT((unsigned)quoteIndex < MAXQUOTES, "invalid quote %d\n", quoteIndex);
VM_ASSERT((unsigned)gameFunc < NUMGAMEFUNCTIONS, "invalid function %d\n", gameFunc); VM_ASSERT((unsigned)gameFunc < NUMGAMEFUNCTIONS, "invalid function %d\n", gameFunc);
auto bindings = Bindings.GetKeysForCommand(C_CON_GetButtonFunc(gameFunc)); auto bindings = Bindings.GetKeysForCommand(C_CON_GetButtonFunc(gameFunc));
if ((unsigned)funcPos >= bindings.Size()) funcPos = 0; if ((unsigned)funcPos >= bindings.Size()) funcPos = 0;
Bstrcpy(apStrings[quoteIndex], KB_ScanCodeToString(bindings[funcPos])); quoteMgr.InitializeQuote(quoteIndex, KB_ScanCodeToString(bindings[funcPos]));
dispatch(); dispatch();
} }
@ -3683,14 +3684,11 @@ badindex:
int const quoteIndex = Gv_GetVar(*insptr++); int const quoteIndex = Gv_GetVar(*insptr++);
int const gameFunc = Gv_GetVar(*insptr++); int const gameFunc = Gv_GetVar(*insptr++);
VM_ASSERT((unsigned)quoteIndex < MAXQUOTES && apStrings[quoteIndex], "invalid quote %d\n", quoteIndex); VM_ASSERT((unsigned)quoteIndex < MAXQUOTES, "invalid quote %d\n", quoteIndex);
VM_ASSERT((unsigned)gameFunc < NUMGAMEFUNCTIONS, "invalid function %d\n", gameFunc); VM_ASSERT((unsigned)gameFunc < NUMGAMEFUNCTIONS, "invalid function %d\n", gameFunc);
static char const s_KeyboardFormat[] = "[%s]";
static char const s_JoystickFormat[] = "(%s)";
auto binding = C_CON_GetBoundKeyForLastInput(gameFunc); auto binding = C_CON_GetBoundKeyForLastInput(gameFunc);
if (binding.Len()) snprintf(apStrings[quoteIndex], MAXQUOTELEN, "(%s)", binding.GetChars()); if (binding.Len()) quoteMgr.FormatQuote(quoteIndex, "(%s)", binding.GetChars());
dispatch(); dispatch();
} }
@ -3703,17 +3701,11 @@ badindex:
} v; } v;
Gv_FillWithVars(v); Gv_FillWithVars(v);
if (EDUKE32_PREDICT_FALSE((unsigned)v.outputQuote >= MAXQUOTES || apStrings[v.outputQuote] == NULL if (EDUKE32_PREDICT_FALSE((unsigned)v.outputQuote >= MAXQUOTES
|| (unsigned)v.inputQuote >= MAXQUOTES || (unsigned)v.inputQuote >= MAXQUOTES
|| apStrings[v.inputQuote] == NULL)) ))
{ {
CON_ERRPRINTF("invalid quote %d\n", apStrings[v.outputQuote] ? v.inputQuote : v.outputQuote); CON_ERRPRINTF("invalid quote %d\n", v.inputQuote >= MAXQUOTES ? v.inputQuote : v.outputQuote);
abort_after_error();
}
if (EDUKE32_PREDICT_FALSE((unsigned)v.quotePos >= MAXQUOTELEN))
{
CON_ERRPRINTF("invalid position %d\n", v.quotePos);
abort_after_error(); abort_after_error();
} }
@ -3723,18 +3715,18 @@ badindex:
abort_after_error(); abort_after_error();
} }
char * pOutput = apStrings[v.outputQuote]; TArray<char> output;
char const *pInput = apStrings[v.inputQuote]; char const *pInput = quoteMgr.GetQuote(v.inputQuote);
while (*pInput && v.quotePos--) while (*pInput && v.quotePos--)
pInput++; pInput++;
while ((*pOutput = *pInput) && v.quoteLength--) while ((*pInput) && v.quoteLength--)
{ {
pOutput++; output.Push(*pInput);
pInput++; pInput++;
} }
*pOutput = '\0'; output.Push(0);
quoteMgr.InitializeQuote(v.outputQuote, output.Data());
dispatch(); dispatch();
} }
@ -3745,13 +3737,7 @@ badindex:
int const quote2 = Gv_GetVar(*insptr++); int const quote2 = Gv_GetVar(*insptr++);
int const gameVar = *insptr++; int const gameVar = *insptr++;
if (EDUKE32_PREDICT_FALSE(apStrings[quote1] == NULL || apStrings[quote2] == NULL)) Gv_SetVar(gameVar, strcmp(quoteMgr.GetQuote(quote1), quoteMgr.GetQuote(quote2)));
{
CON_ERRPRINTF("null quote %d\n", apStrings[quote1] ? quote2 : quote1);
abort_after_error();
}
Gv_SetVar(gameVar, strcmp(apStrings[quote1], apStrings[quote2]));
dispatch(); dispatch();
} }
@ -3775,14 +3761,14 @@ badindex:
switch (VM_DECODE_INST(tw)) switch (VM_DECODE_INST(tw))
{ {
case CON_GETPNAME: case CON_GETPNAME:
VM_ASSERT((unsigned)q < MAXQUOTES && apStrings[q], "invalid quote %d\n", q); VM_ASSERT((unsigned)q < MAXQUOTES, "invalid quote %d\n", q);
if (g_player[j].user_name[0]) if (g_player[j].user_name[0])
Bstrcpy(apStrings[q], g_player[j].user_name); quoteMgr.InitializeQuote(q, g_player[j].user_name);
else else
Bsprintf(apStrings[q], "%d", j); quoteMgr.FormatQuote(q, "%d", j);
break; break;
case CON_QGETSYSSTR: case CON_QGETSYSSTR:
VM_ASSERT((unsigned)q < MAXQUOTES && apStrings[q], "invalid quote %d\n", q); VM_ASSERT((unsigned)q < MAXQUOTES, "invalid quote %d\n", q);
switch (j) switch (j)
{ {
case STR_MAPNAME: case STR_MAPNAME:
@ -3790,7 +3776,7 @@ badindex:
{ {
if (G_HaveUserMap()) if (G_HaveUserMap())
{ {
snprintf(apStrings[q], MAXQUOTELEN, "%s", boardfilename); quoteMgr.FormatQuote(q, "%s", boardfilename);
break; break;
} }
@ -3812,22 +3798,22 @@ badindex:
abort_after_error(); abort_after_error();
} }
Bstrcpy(apStrings[q], j == STR_MAPNAME ? g_mapInfo[levelNum].name : g_mapInfo[levelNum].filename); quoteMgr.InitializeQuote(q, j == STR_MAPNAME ? g_mapInfo[levelNum].name : g_mapInfo[levelNum].filename);
break; break;
} }
case STR_PLAYERNAME: case STR_PLAYERNAME:
VM_ASSERT((unsigned)vm.playerNum < (unsigned)g_mostConcurrentPlayers, "invalid player %d\n", vm.playerNum); VM_ASSERT((unsigned)vm.playerNum < (unsigned)g_mostConcurrentPlayers, "invalid player %d\n", vm.playerNum);
Bstrcpy(apStrings[q], g_player[vm.playerNum].user_name); quoteMgr.InitializeQuote(q, g_player[vm.playerNum].user_name);
break; break;
case STR_VERSION: case STR_VERSION:
Bsprintf(tempbuf, HEAD2 " %s", GetGitDescription()); Bsprintf(tempbuf, HEAD2 " %s", GetGitDescription());
Bstrcpy(apStrings[q], tempbuf); quoteMgr.InitializeQuote(q, tempbuf);
break; break;
case STR_GAMETYPE: Bstrcpy(apStrings[q], g_gametypeNames[ud.coop]); break; case STR_GAMETYPE: quoteMgr.InitializeQuote(q, g_gametypeNames[ud.coop]); break;
case STR_VOLUMENAME: case STR_VOLUMENAME:
if (G_HaveUserMap()) if (G_HaveUserMap())
{ {
apStrings[q][0] = '\0'; quoteMgr.InitializeQuote(q, "");
break; break;
} }
@ -3837,36 +3823,28 @@ badindex:
abort_after_error(); abort_after_error();
} }
// length is no longer limited so a check is needed. // length is no longer limited so a check is needed.
Bstrncpy(apStrings[q], gVolumeNames[ud.volume_number], MAXQUOTELEN); quoteMgr.InitializeQuote(q, gVolumeNames[ud.volume_number]);
apStrings[q][MAXQUOTELEN-1] = 0;
break; break;
case STR_YOURTIME: Bstrcpy(apStrings[q], G_PrintYourTime()); break; case STR_YOURTIME: quoteMgr.InitializeQuote(q, G_PrintYourTime()); break;
case STR_PARTIME: Bstrcpy(apStrings[q], G_PrintParTime()); break; case STR_PARTIME: quoteMgr.InitializeQuote(q, G_PrintParTime()); break;
case STR_DESIGNERTIME: Bstrcpy(apStrings[q], G_PrintDesignerTime()); break; case STR_DESIGNERTIME: quoteMgr.InitializeQuote(q, G_PrintDesignerTime()); break;
case STR_BESTTIME: Bstrcpy(apStrings[q], G_PrintBestTime()); break; case STR_BESTTIME: quoteMgr.InitializeQuote(q, G_PrintBestTime()); break;
case STR_USERMAPFILENAME: snprintf(apStrings[q], MAXQUOTELEN, "%s", boardfilename); break; case STR_USERMAPFILENAME: quoteMgr.FormatQuote(q, "%s", boardfilename); break;
default: CON_ERRPRINTF("invalid string index %d or %d\n", q, j); abort_after_error(); default: CON_ERRPRINTF("invalid string index %d or %d\n", q, j); abort_after_error();
} }
break; break;
case CON_QSTRCAT: case CON_QSTRCAT:
if (EDUKE32_PREDICT_FALSE(apStrings[q] == NULL || apStrings[j] == NULL)) quoteMgr.AppendQuote(q, j);
goto nullquote;
Bstrncat(apStrings[q], apStrings[j], (MAXQUOTELEN - 1) - Bstrlen(apStrings[q]));
break; break;
case CON_QSTRNCAT: case CON_QSTRNCAT:
if (EDUKE32_PREDICT_FALSE(apStrings[q] == NULL || apStrings[j] == NULL)) quoteMgr.AppendQuote(q, j, Gv_GetVar(*insptr++));
goto nullquote;
Bstrncat(apStrings[q], apStrings[j], Gv_GetVar(*insptr++));
break; break;
case CON_QSTRCPY: case CON_QSTRCPY:
if (EDUKE32_PREDICT_FALSE(apStrings[q] == NULL || apStrings[j] == NULL)) if (q != j)
goto nullquote; quoteMgr.CopyQuote(q, j);
if (q != j)
Bstrcpy(apStrings[q], apStrings[j]);
break; break;
default: default:
nullquote: CON_ERRPRINTF("invalid quote %d\n", q < MAXQUOTES? j : q);
CON_ERRPRINTF("invalid quote %d\n", apStrings[q] ? j : q);
abort_after_error(); abort_after_error();
} }
dispatch(); dispatch();
@ -4188,18 +4166,18 @@ badindex:
{ {
int const nQuote = Gv_GetVar(*insptr++); int const nQuote = Gv_GetVar(*insptr++);
VM_ASSERT((unsigned)nQuote < MAXQUOTES && apStrings[nQuote], "invalid quote %d\n", nQuote); VM_ASSERT((unsigned)nQuote < MAXQUOTES, "invalid quote %d\n", nQuote);
if (VM_DECODE_INST(tw) == CON_IFCUTSCENE) if (VM_DECODE_INST(tw) == CON_IFCUTSCENE)
{ {
insptr--; insptr--;
VM_CONDITIONAL(g_animPtr == Anim_Find(apStrings[nQuote])); VM_CONDITIONAL(g_animPtr == Anim_Find(quoteMgr.GetQuote(nQuote)));
dispatch(); dispatch();
} }
tw = vm.pPlayer->palette; tw = vm.pPlayer->palette;
I_ClearAllInput(); I_ClearAllInput();
Anim_Play(apStrings[nQuote]); Anim_Play(quoteMgr.GetQuote(nQuote));
P_SetGamePalette(vm.pPlayer, tw, 2 + 16); P_SetGamePalette(vm.pPlayer, tw, 2 + 16);
dispatch(); dispatch();
} }
@ -4342,9 +4320,9 @@ badindex:
abort_after_error(); abort_after_error();
} }
VM_ASSERT((unsigned)v.nQuote < MAXQUOTES && apStrings[v.nQuote], "invalid quote %d\n", v.nQuote); VM_ASSERT((unsigned)v.nQuote < MAXQUOTES, "invalid quote %d\n", v.nQuote);
G_PrintGameText(v.tilenum, v.pos.x >> 1, v.pos.y, apStrings[v.nQuote], v.shade, v.pal, v.orientation & (ROTATESPRITE_MAX - 1), G_PrintGameText(v.tilenum, v.pos.x >> 1, v.pos.y, quoteMgr.GetQuote(v.nQuote), v.shade, v.pal, v.orientation & (ROTATESPRITE_MAX - 1),
v.bound[0].x, v.bound[0].y, v.bound[1].x, v.bound[1].y, z, 0); v.bound[0].x, v.bound[0].y, v.bound[1].x, v.bound[1].y, z, 0);
dispatch(); dispatch();
} }
@ -4386,9 +4364,9 @@ badindex:
} v; } v;
Gv_FillWithVars(v); Gv_FillWithVars(v);
VM_ASSERT((unsigned)v.nQuote < MAXQUOTES && apStrings[v.nQuote], "invalid quote %d\n", v.nQuote); VM_ASSERT((unsigned)v.nQuote < MAXQUOTES, "invalid quote %d\n", v.nQuote);
minitextshade(v.pos.x, v.pos.y, apStrings[v.nQuote], v.shade, v.pal, 2 + 8 + 16); minitextshade(v.pos.x, v.pos.y, quoteMgr.GetQuote(v.nQuote), v.shade, v.pal, 2 + 8 + 16);
dispatch(); dispatch();
} }
@ -4412,9 +4390,9 @@ badindex:
abort_after_error(); abort_after_error();
} }
VM_ASSERT((unsigned)v.nQuote < MAXQUOTES && apStrings[v.nQuote], "invalid quote %d\n", v.nQuote); VM_ASSERT((unsigned)v.nQuote < MAXQUOTES, "invalid quote %d\n", v.nQuote);
G_ScreenText(v.tilenum, v.v.x, v.v.y, v.v.z, v.blockangle, v.charangle, apStrings[v.nQuote], v.shade, v.pal, G_ScreenText(v.tilenum, v.v.x, v.v.y, v.v.z, v.blockangle, v.charangle, quoteMgr.GetQuote(v.nQuote), v.shade, v.pal,
2 | (v.orientation & (ROTATESPRITE_MAX - 1)), v.alpha, v.spacing.x, v.spacing.y, v.between.x, v.between.y, v.nFlags, 2 | (v.orientation & (ROTATESPRITE_MAX - 1)), v.alpha, v.spacing.x, v.spacing.y, v.between.x, v.between.y, v.nFlags,
v.bound[0].x, v.bound[0].y, v.bound[1].x, v.bound[1].y); v.bound[0].x, v.bound[0].y, v.bound[1].x, v.bound[1].y);
dispatch(); dispatch();
@ -4814,9 +4792,9 @@ badindex:
insptr++; insptr++;
int const nQuote = Gv_GetVar(*insptr++); int const nQuote = Gv_GetVar(*insptr++);
VM_ASSERT((unsigned)nQuote < MAXQUOTES && apStrings[nQuote], "invalid quote %d\n", nQuote); VM_ASSERT((unsigned)nQuote < MAXQUOTES, "invalid quote %d\n", nQuote);
//communityapiUnlockAchievement(apStrings[nQuote]); //communityapiUnlockAchievement(quoteMgr.GetQuote(v.nQuote));
dispatch(); dispatch();
} }
@ -4826,9 +4804,9 @@ badindex:
int const nQuote = Gv_GetVar(*insptr++); int const nQuote = Gv_GetVar(*insptr++);
int const value = Gv_GetVar(*insptr++); int const value = Gv_GetVar(*insptr++);
VM_ASSERT((unsigned)nQuote < MAXQUOTES && apStrings[nQuote], "invalid quote %d\n", nQuote); VM_ASSERT((unsigned)nQuote < MAXQUOTES, "invalid quote %d\n", nQuote);
//communityapiSetStat(apStrings[nQuote], value); //communityapiSetStat(quoteMgr.GetQuote(nQuote), value);
dispatch(); dispatch();
} }
@ -5103,16 +5081,10 @@ badindex:
int const outputQuote = Gv_GetVar(*insptr++); int const outputQuote = Gv_GetVar(*insptr++);
int const inputQuote = Gv_GetVar(*insptr++); int const inputQuote = Gv_GetVar(*insptr++);
if (EDUKE32_PREDICT_FALSE(apStrings[inputQuote] == NULL || apStrings[outputQuote] == NULL)) auto inBuf = quoteMgr.GetQuote(inputQuote);
{
CON_ERRPRINTF("null quote %d\n", apStrings[inputQuote] ? outputQuote : inputQuote);
abort_after_error();
}
auto &inBuf = apStrings[inputQuote];
int32_t arg[32]; int32_t arg[32];
char outBuf[MAXQUOTELEN]; TArray<char> outBuf;
int const quoteLen = Bstrlen(inBuf); int const quoteLen = Bstrlen(inBuf);
@ -5131,8 +5103,8 @@ badindex:
do do
{ {
while (inputPos < quoteLen && outputPos < MAXQUOTELEN && inBuf[inputPos] != '%') while (inputPos < quoteLen && inBuf[inputPos] != '%')
outBuf[outputPos++] = inBuf[inputPos++]; outBuf.Push(inBuf[inputPos++]);
if (inBuf[inputPos] == '%') if (inBuf[inputPos] == '%')
{ {
@ -5143,8 +5115,8 @@ badindex:
if (inBuf[inputPos + 1] != 'd') if (inBuf[inputPos + 1] != 'd')
{ {
// write the % and l // write the % and l
outBuf[outputPos++] = inBuf[inputPos - 1]; outBuf.Push(inBuf[inputPos - 1]);
outBuf[outputPos++] = inBuf[inputPos++]; outBuf.Push(inBuf[inputPos++]);
break; break;
} }
inputPos++; inputPos++;
@ -5158,7 +5130,8 @@ badindex:
Bsprintf(buf, "%d", arg[argIdx++]); Bsprintf(buf, "%d", arg[argIdx++]);
int const bufLen = Bstrlen(buf); int const bufLen = Bstrlen(buf);
Bmemcpy(&outBuf[outputPos], buf, bufLen); outputPos = outBuf.Reserve(bufLen);
memcpy(&outBuf[outputPos], buf, bufLen);
outputPos += bufLen; outputPos += bufLen;
inputPos++; inputPos++;
} }
@ -5169,22 +5142,24 @@ badindex:
if (argIdx >= numArgs) if (argIdx >= numArgs)
goto finish_qsprintf; goto finish_qsprintf;
int const argLen = Bstrlen(apStrings[arg[argIdx]]); auto quoteArg = quoteMgr.GetQuote(arg[argIdx]);
int const argLen = (int)strlen(quoteArg);
Bmemcpy(&outBuf[outputPos], apStrings[arg[argIdx]], argLen); outputPos = outBuf.Reserve(argLen);
memcpy(&outBuf[outputPos], quoteArg, argLen);
outputPos += argLen; outputPos += argLen;
argIdx++; argIdx++;
inputPos++; inputPos++;
} }
break; break;
default: outBuf[outputPos++] = inBuf[inputPos - 1]; break; default: outBuf.Push(inBuf[inputPos - 1]); break;
} }
} }
} while (inputPos < quoteLen && outputPos < MAXQUOTELEN); } while (inputPos < quoteLen);
finish_qsprintf: finish_qsprintf:
outBuf[outputPos] = '\0'; outBuf.Push(0);
Bstrncpyz(apStrings[outputQuote], outBuf, MAXQUOTELEN); quoteMgr.InitializeQuote(outputQuote, outBuf.Data());
dispatch(); dispatch();
} }
@ -5600,9 +5575,9 @@ badindex:
int const arrayNum = *insptr++; int const arrayNum = *insptr++;
int const quoteFilename = *insptr++; int const quoteFilename = *insptr++;
VM_ASSERT((unsigned)quoteFilename < MAXQUOTES && apStrings[quoteFilename], "invalid quote %d\n", quoteFilename); VM_ASSERT((unsigned)quoteFilename < MAXQUOTES, "invalid quote %d\n", quoteFilename);
FStringf IniSection("%s.UserStrings", LumpFilter.GetChars()); FStringf IniSection("%s.UserStrings", LumpFilter.GetChars());
auto IniKey = apStrings[quoteFilename]; auto IniKey = quoteMgr.GetQuote(quoteFilename);
if (!GameConfig->SetSection(IniSection)) if (!GameConfig->SetSection(IniSection))
{ {
dispatch(); dispatch();
@ -5676,12 +5651,12 @@ badindex:
int const arrayNum = *insptr++; int const arrayNum = *insptr++;
int const quoteFilename = *insptr++; int const quoteFilename = *insptr++;
VM_ASSERT((unsigned)quoteFilename < MAXQUOTES && apStrings[quoteFilename], "invalid quote %d\n", quoteFilename); VM_ASSERT((unsigned)quoteFilename < MAXQUOTES, "invalid quote %d\n", quoteFilename);
// No, we are not writing stuff to an arbitrary file on the hard drive! This is a first grade exploit for doing serious damage. // No, we are not writing stuff to an arbitrary file on the hard drive! This is a first grade exploit for doing serious damage.
// Instead, encode the data as BASE64 and write it to the config file, // Instead, encode the data as BASE64 and write it to the config file,
// which doesn't create a wide open door for exploits. // which doesn't create a wide open door for exploits.
FStringf IniSection("%s.UserStrings", LumpFilter.GetChars()); FStringf IniSection("%s.UserStrings", LumpFilter.GetChars());
auto IniKey = apStrings[quoteFilename]; auto IniKey = quoteMgr.GetQuote(quoteFilename);
BufferWriter bw; BufferWriter bw;
switch (aGameArrays[arrayNum].flags & GAMEARRAY_SIZE_MASK) switch (aGameArrays[arrayNum].flags & GAMEARRAY_SIZE_MASK)
@ -6198,18 +6173,18 @@ badindex:
insptr++; insptr++;
tw = Gv_GetVar(*insptr++); tw = Gv_GetVar(*insptr++);
VM_ASSERT((unsigned)tw < MAXQUOTES && apStrings[tw], "invalid quote %d\n", (int)tw); VM_ASSERT((unsigned)tw < MAXQUOTES, "invalid quote %d\n", (int)tw);
G_AddUserQuote(apStrings[tw]); G_AddUserQuote(quoteMgr.GetQuote(tw));
dispatch(); dispatch();
vInstruction(CON_ECHO): vInstruction(CON_ECHO):
insptr++; insptr++;
tw = Gv_GetVar(*insptr++); tw = Gv_GetVar(*insptr++);
VM_ASSERT((unsigned)tw < MAXQUOTES && apStrings[tw], "invalid quote %d\n", (int)tw); VM_ASSERT((unsigned)tw < MAXQUOTES, "invalid quote %d\n", (int)tw);
OSD_Printf("%s\n", apStrings[tw]); OSD_Printf("%s\n", quoteMgr.GetQuote(tw));
dispatch(); dispatch();
vInstruction(CON_RESPAWNHITAG): vInstruction(CON_RESPAWNHITAG):

View file

@ -67,7 +67,6 @@ G_EXTERN actor_t actor[MAXSPRITES];
// g_tile: tile-specific data THAT DOES NOT CHANGE during the course of a game // g_tile: tile-specific data THAT DOES NOT CHANGE during the course of a game
G_EXTERN tiledata_t g_tile[MAXTILES]; G_EXTERN tiledata_t g_tile[MAXTILES];
G_EXTERN animwalltype animwall[MAXANIMWALLS]; G_EXTERN animwalltype animwall[MAXANIMWALLS];
G_EXTERN char *apStrings[MAXQUOTES],*apXStrings[MAXQUOTES];
G_EXTERN char *label; G_EXTERN char *label;
G_EXTERN int32_t g_musicIndex; G_EXTERN int32_t g_musicIndex;
G_EXTERN char g_loadFromGroupOnly; G_EXTERN char g_loadFromGroupOnly;

View file

@ -1517,7 +1517,7 @@ static void P_RemovePlayer(int32_t p)
voting = -1; voting = -1;
} }
Bstrcpy(apStrings[QUOTE_RESERVED2],recbuf); quoteMgr.InitializeQuote(QUOTE_RESERVED2 ,recbuf);
g_player[myconnectindex].ps->ftq = QUOTE_RESERVED2; g_player[myconnectindex].ps->ftq = QUOTE_RESERVED2;
g_player[myconnectindex].ps->fta = 180; g_player[myconnectindex].ps->fta = 180;
} }

View file

@ -3768,18 +3768,18 @@ void P_FragPlayer(int playerNum)
if (playerNum == screenpeek) if (playerNum == screenpeek)
{ {
Bsprintf(apStrings[QUOTE_RESERVED], "Killed by %s", &g_player[pPlayer->frag_ps].user_name[0]); quoteMgr.FormatQuote(QUOTE_RESERVED, "Killed by %s", &g_player[pPlayer->frag_ps].user_name[0]);
P_DoQuote(QUOTE_RESERVED, pPlayer); P_DoQuote(QUOTE_RESERVED, pPlayer);
} }
else else
{ {
Bsprintf(apStrings[QUOTE_RESERVED2], "Killed %s", &g_player[playerNum].user_name[0]); quoteMgr.FormatQuote(QUOTE_RESERVED2, "Killed %s", &g_player[playerNum].user_name[0]);
P_DoQuote(QUOTE_RESERVED2, g_player[pPlayer->frag_ps].ps); P_DoQuote(QUOTE_RESERVED2, g_player[pPlayer->frag_ps].ps);
} }
if (cl_obituaries) if (cl_obituaries)
{ {
Bsprintf(tempbuf, apStrings[OBITQUOTEINDEX + (krand() % g_numObituaries)], Bsprintf(tempbuf, quoteMgr.GetQuote(OBITQUOTEINDEX + (krand() % g_numObituaries)),
&g_player[pPlayer->frag_ps].user_name[0], &g_player[playerNum].user_name[0]); &g_player[pPlayer->frag_ps].user_name[0], &g_player[playerNum].user_name[0]);
G_AddUserQuote(tempbuf); G_AddUserQuote(tempbuf);
} }
@ -3792,14 +3792,14 @@ void P_FragPlayer(int playerNum)
{ {
pPlayer->fraggedself++; pPlayer->fraggedself++;
if ((unsigned)pPlayer->wackedbyactor < MAXTILES && A_CheckEnemyTile(sprite[pPlayer->wackedbyactor].picnum)) if ((unsigned)pPlayer->wackedbyactor < MAXTILES && A_CheckEnemyTile(sprite[pPlayer->wackedbyactor].picnum))
Bsprintf(tempbuf, apStrings[OBITQUOTEINDEX + (krand() % g_numObituaries)], "A monster", Bsprintf(tempbuf, quoteMgr.GetQuote(OBITQUOTEINDEX + (krand() % g_numObituaries)), "A monster",
&g_player[playerNum].user_name[0]); &g_player[playerNum].user_name[0]);
else if (actor[pPlayer->i].picnum == NUKEBUTTON) else if (actor[pPlayer->i].picnum == NUKEBUTTON)
Bsprintf(tempbuf, "^02%s^02 tried to leave", &g_player[playerNum].user_name[0]); Bsprintf(tempbuf, "^02%s^02 tried to leave", &g_player[playerNum].user_name[0]);
else else
{ {
// random suicide death string // random suicide death string
Bsprintf(tempbuf, apStrings[SUICIDEQUOTEINDEX + (krand() % g_numSelfObituaries)], Bsprintf(tempbuf, quoteMgr.GetQuote(SUICIDEQUOTEINDEX + (krand() % g_numSelfObituaries)),
&g_player[playerNum].user_name[0]); &g_player[playerNum].user_name[0]);
} }
} }

View file

@ -23,8 +23,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef quotes_h_ #ifndef quotes_h_
#define quotes_h_ #define quotes_h_
#define MAXQUOTES 16384 #include "quotemgr.h"
#define MAXQUOTELEN 128
#define OBITQUOTEINDEX (MAXQUOTES-128) #define OBITQUOTEINDEX (MAXQUOTES-128)
#define SUICIDEQUOTEINDEX (MAXQUOTES-32) #define SUICIDEQUOTEINDEX (MAXQUOTES-32)

View file

@ -617,7 +617,7 @@ bool G_SavePlayer(FSaveGameNode *sv)
if (!g_netServer && ud.multimode < 2) if (!g_netServer && ud.multimode < 2)
{ {
OSD_Printf("Saved: %s\n", fn.GetChars()); OSD_Printf("Saved: %s\n", fn.GetChars());
strcpy(apStrings[QUOTE_RESERVED4], "Game Saved"); quoteMgr.InitializeQuote(QUOTE_RESERVED4, "Game Saved");
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps); P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
} }
@ -638,7 +638,7 @@ bool GameInterface::LoadGame(FSaveGameNode *sv)
{ {
if (g_netServer || ud.multimode > 1) if (g_netServer || ud.multimode > 1)
{ {
Bstrcpy(apStrings[QUOTE_RESERVED4], "Multiplayer Loading Not Yet Supported"); quoteMgr.InitializeQuote(QUOTE_RESERVED4, "Multiplayer Loading Not Yet Supported");
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps); P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
// g_player[myconnectindex].ps->gm = MODE_GAME; // g_player[myconnectindex].ps->gm = MODE_GAME;
@ -657,7 +657,7 @@ bool GameInterface::SaveGame(FSaveGameNode* sv)
{ {
if (g_netServer || ud.multimode > 1) if (g_netServer || ud.multimode > 1)
{ {
Bstrcpy(apStrings[QUOTE_RESERVED4], "Multiplayer Saving Not Yet Supported"); quoteMgr.InitializeQuote(QUOTE_RESERVED4, "Multiplayer Saving Not Yet Supported");
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps); P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
return false; return false;
} }
@ -1145,9 +1145,6 @@ static void sv_create_lua_state(void)
static void sv_postactordata(); static void sv_postactordata();
static void sv_preanimateptrsave(); static void sv_preanimateptrsave();
static void sv_postanimateptr(); static void sv_postanimateptr();
static void sv_prequote();
static void sv_quotesave();
static void sv_quoteload();
static void sv_prequoteredef(); static void sv_prequoteredef();
static void sv_quoteredefsave(); static void sv_quoteredefsave();
static void sv_quoteredefload(); static void sv_quoteredefload();
@ -1167,9 +1164,6 @@ static int32_t savegame_projectilecnt = 0;
((sizeof(g_player[0].user_name)+sizeof(g_player[0].pcolor)+sizeof(g_player[0].pteam) \ ((sizeof(g_player[0].user_name)+sizeof(g_player[0].pcolor)+sizeof(g_player[0].pteam) \
+sizeof(g_player[0].frags)+sizeof(DukePlayer_t))*MAXPLAYERS) +sizeof(g_player[0].frags)+sizeof(DukePlayer_t))*MAXPLAYERS)
static uint8_t savegame_quotedef[MAXQUOTES >> 3];
static char (*savegame_quotes)[MAXQUOTELEN];
static char (*savegame_quoteredefs)[MAXQUOTELEN];
static uint8_t savegame_restdata[SVARDATALEN]; static uint8_t savegame_restdata[SVARDATALEN];
static char svgm_udnetw_string [] = "blK:udnt"; static char svgm_udnetw_string [] = "blK:udnt";
@ -1309,20 +1303,6 @@ static const dataspec_t svgm_anmisc[] =
{ 0, &g_pskyidx, sizeof(g_pskyidx), 1 }, // DS_NOCHK? { 0, &g_pskyidx, sizeof(g_pskyidx), 1 }, // DS_NOCHK?
{ 0, &g_earthquakeTime, sizeof(g_earthquakeTime), 1 }, { 0, &g_earthquakeTime, sizeof(g_earthquakeTime), 1 },
{ DS_SAVEFN|DS_LOADFN|DS_NOCHK, (void *)sv_prequote, 0, 1 },
{ DS_SAVEFN, (void *)&sv_quotesave, 0, 1 },
{ DS_NOCHK, &savegame_quotedef, sizeof(savegame_quotedef), 1 }, // quotes can change during runtime, but new quote numbers cannot be allocated
{ DS_DYNAMIC, &savegame_quotes, MAXQUOTELEN, MAXQUOTES },
{ DS_LOADFN, (void *)&sv_quoteload, 0, 1 },
{ DS_NOCHK|DS_SAVEFN|DS_LOADFN, (void *)&sv_prequoteredef, 0, 1 },
{ DS_NOCHK|DS_SAVEFN, (void *)&sv_quoteredefsave, 0, 1 }, // quote redefinitions replace quotes at runtime, but cannot be changed after CON compilation
{ DS_NOCHK|DS_DYNAMIC|DS_CNT(g_numXStrings), &savegame_quoteredefs, MAXQUOTELEN, (intptr_t)&g_numXStrings },
{ DS_NOCHK|DS_LOADFN, (void *)&sv_quoteredefload, 0, 1 },
{ DS_NOCHK|DS_SAVEFN|DS_LOADFN, (void *)&sv_postquoteredef, 0, 1 },
#ifdef LUNATIC
{ 0, g_playerWeapon, sizeof(weapondata_t), MAXPLAYERS*MAX_WEAPONS },
#endif
{ DS_SAVEFN, (void *)&sv_restsave, 0, 1 }, { DS_SAVEFN, (void *)&sv_restsave, 0, 1 },
{ 0, savegame_restdata, 1, sizeof(savegame_restdata) }, // sz/cnt swapped for kdfread { 0, savegame_restdata, 1, sizeof(savegame_restdata) }, // sz/cnt swapped for kdfread
{ DS_LOADFN, (void *)&sv_restload, 0, 1 }, { DS_LOADFN, (void *)&sv_restload, 0, 1 },
@ -1798,34 +1778,6 @@ static void sv_postanimateptr()
{ {
G_Util_PtrToIdx(g_animatePtr, g_animateCnt, sector, P2I_BACK); G_Util_PtrToIdx(g_animatePtr, g_animateCnt, sector, P2I_BACK);
} }
static void sv_prequote()
{
if (!savegame_quotes)
{
void *ptr = Xcalloc(MAXQUOTES, MAXQUOTELEN);
savegame_quotes = (char(*)[MAXQUOTELEN])ptr;
}
}
static void sv_quotesave()
{
Bmemset(savegame_quotedef, 0, sizeof(savegame_quotedef));
for (int i = 0; i < MAXQUOTES; i++)
if (apStrings[i])
{
savegame_quotedef[i>>3] |= 1<<(i&7);
Bmemcpy(savegame_quotes[i], apStrings[i], MAXQUOTELEN);
}
}
static void sv_quoteload()
{
for (int i = 0; i < MAXQUOTES; i++)
if (savegame_quotedef[i>>3] & pow2char[i&7])
{
C_AllocQuote(i);
Bmemcpy(apStrings[i], savegame_quotes[i], MAXQUOTELEN);
}
}
static void sv_preprojectilesave() static void sv_preprojectilesave()
{ {
savegame_projectilecnt = 0; savegame_projectilecnt = 0;
@ -1881,31 +1833,6 @@ static void sv_postprojectileload()
DO_FREE_AND_NULL(savegame_projectiledata); DO_FREE_AND_NULL(savegame_projectiledata);
} }
static void sv_prequoteredef()
{
// "+1" needed for dfwrite which doesn't handle the src==NULL && cnt==0 case
void *ptr = Xcalloc(g_numXStrings+1, MAXQUOTELEN);
savegame_quoteredefs = (decltype(savegame_quoteredefs))ptr;
}
static void sv_quoteredefsave()
{
for (int i = 0; i < g_numXStrings; i++)
if (apXStrings[i])
Bmemcpy(savegame_quoteredefs[i], apXStrings[i], MAXQUOTELEN);
}
static void sv_quoteredefload()
{
for (int i = 0; i < g_numXStrings; i++)
{
if (!apXStrings[i])
apXStrings[i] = (char *)Xcalloc(1,MAXQUOTELEN);
Bmemcpy(apXStrings[i], savegame_quoteredefs[i], MAXQUOTELEN);
}
}
static void sv_postquoteredef()
{
Xfree(savegame_quoteredefs), savegame_quoteredefs=NULL;
}
static void sv_restsave() static void sv_restsave()
{ {
uint8_t * mem = savegame_restdata; uint8_t * mem = savegame_restdata;

View file

@ -27,6 +27,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "sbar.h" #include "sbar.h"
#include "menus.h" #include "menus.h"
#include "gstrings.h" #include "gstrings.h"
#include "quotemgr.h"
BEGIN_DUKE_NS BEGIN_DUKE_NS
@ -1069,12 +1070,6 @@ void G_PrintGameQuotes(int32_t snum)
if (k <= 1) if (k <= 1)
break; break;
if (EDUKE32_PREDICT_FALSE(apStrings[ps->ftq] == NULL))
{
OSD_Printf(OSD_ERROR "%s %d null quote %d\n", "screentext:", __LINE__, ps->ftq);
break;
}
int32_t y = ybase; int32_t y = ybase;
if (reserved_quote) if (reserved_quote)
{ {
@ -1104,7 +1099,7 @@ void G_PrintGameQuotes(int32_t snum)
} }
#endif #endif
height = gametext_(x, y, apStrings[ps->ftq], textsh(k), pal, texto(k), texta(k), TEXT_XCENTER).y + (1<<16); height = gametext_(x, y, quoteMgr.GetQuote(ps->ftq), textsh(k), pal, texto(k), texta(k), TEXT_XCENTER).y + (1<<16);
} }
while (0); while (0);
@ -1143,12 +1138,6 @@ void P_DoQuote(int32_t q, DukePlayer_t *p)
q &= ~MAXQUOTES; q &= ~MAXQUOTES;
} }
if (EDUKE32_PREDICT_FALSE(apStrings[q] == NULL))
{
OSD_Printf(OSD_ERROR "%s %d null quote %d\n", "screentext:", __LINE__, q);
return;
}
if (p->fta > 0 && q != QUOTE_RESERVED && q != QUOTE_RESERVED2) if (p->fta > 0 && q != QUOTE_RESERVED && q != QUOTE_RESERVED2)
if (p->ftq == QUOTE_RESERVED || p->ftq == QUOTE_RESERVED2) return; if (p->ftq == QUOTE_RESERVED || p->ftq == QUOTE_RESERVED2) return;
@ -1156,8 +1145,9 @@ void P_DoQuote(int32_t q, DukePlayer_t *p)
if (p->ftq != q) if (p->ftq != q)
{ {
if (p == g_player[screenpeek].ps && apStrings[q][0] != '\0') auto qu = quoteMgr.GetQuote(q);
OSD_Printf(cq ? OSDTEXT_DEFAULT "%s\n" : "%s\n", apStrings[q]); if (p == g_player[screenpeek].ps && qu[0] != '\0')
OSD_Printf(cq ? OSDTEXT_DEFAULT "%s\n" : "%s\n", qu);
p->ftq = q; p->ftq = q;
} }

View file

@ -490,7 +490,7 @@ void G_DoCheats(void)
//} //}
//else //else
//{ //{
// Bstrcpy(apStrings[QUOTE_RESERVED4], "Come Get Some!"); // Bstrcpy(pStrings[QUOTE_RESERVED4], "Come Get Some!");
// //
// S_PlaySound(DUKE_GETWEAPON2); // S_PlaySound(DUKE_GETWEAPON2);
// P_DoQuote(QUOTE_RESERVED4, pPlayer); // P_DoQuote(QUOTE_RESERVED4, pPlayer);
@ -661,7 +661,7 @@ void G_DoCheats(void)
case CHEAT_TODD: case CHEAT_TODD:
if (NAM) if (NAM)
{ {
Bstrcpy(apStrings[QUOTE_RESERVED4], g_NAMMattCheatQuote); quoteMgr.InitializeQuote(QUOTE_RESERVED4, g_NAMMattCheatQuote);
P_DoQuote(QUOTE_RESERVED4, pPlayer); P_DoQuote(QUOTE_RESERVED4, pPlayer);
} }
else else
@ -698,7 +698,7 @@ void G_DoCheats(void)
if (++g_noEnemies == 3) if (++g_noEnemies == 3)
g_noEnemies = 0; g_noEnemies = 0;
Bsprintf(apStrings[QUOTE_RESERVED4], "Monsters: %s", s[g_noEnemies]); quoteMgr.FormatQuote(QUOTE_RESERVED4, "Monsters: %s", s[g_noEnemies]);
P_DoQuote(QUOTE_RESERVED4, pPlayer); P_DoQuote(QUOTE_RESERVED4, pPlayer);
end_cheat(pPlayer); end_cheat(pPlayer);

View file

@ -143,7 +143,7 @@ void G_OpenDemoWrite(void)
if ((g_player[myconnectindex].ps->gm&MODE_GAME) && g_player[myconnectindex].ps->dead_flag) if ((g_player[myconnectindex].ps->gm&MODE_GAME) && g_player[myconnectindex].ps->dead_flag)
{ {
Bstrcpy(apStrings[QUOTE_RESERVED4], "CANNOT START DEMO RECORDING WHEN DEAD!"); quoteMgr.InitializeQuote(QUOTE_RESERVED4, "CANNOT START DEMO RECORDING WHEN DEAD!");
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps); P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
ud.recstat = m_recstat = 0; ud.recstat = m_recstat = 0;
return; return;
@ -180,7 +180,7 @@ void G_OpenDemoWrite(void)
delete g_demo_filePtr; delete g_demo_filePtr;
g_demo_filePtr = nullptr; g_demo_filePtr = nullptr;
error_wopen_demo: error_wopen_demo:
Bstrcpy(apStrings[QUOTE_RESERVED4], "FAILED STARTING DEMO RECORDING. SEE CONSOLE FOR DETAILS."); quoteMgr.InitializeQuote(QUOTE_RESERVED4, "FAILED STARTING DEMO RECORDING. SEE CONSOLE FOR DETAILS.");
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps); P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
ud.recstat = m_recstat = 0; ud.recstat = m_recstat = 0;
return; return;
@ -190,7 +190,7 @@ error_wopen_demo:
demorec_diffs = demorec_diffs_cvar; demorec_diffs = demorec_diffs_cvar;
demorec_difftics = demorec_difftics_cvar; demorec_difftics = demorec_difftics_cvar;
Bsprintf(apStrings[QUOTE_RESERVED4], "DEMO %d RECORDING STARTED", demonum-1); quoteMgr.FormatQuote(QUOTE_RESERVED4, "DEMO %d RECORDING STARTED", demonum-1);
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps); P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
ud.reccnt = 0; ud.reccnt = 0;
@ -285,7 +285,7 @@ void G_CloseDemoWrite(void)
sv_freemem(); sv_freemem();
Bstrcpy(apStrings[QUOTE_RESERVED4], "DEMO RECORDING STOPPED"); quoteMgr.InitializeQuote(QUOTE_RESERVED4, "DEMO RECORDING STOPPED");
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps); P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
} }
#if KRANDDEBUG #if KRANDDEBUG

View file

@ -6805,12 +6805,6 @@ static void G_Cleanup(void)
G_FreeMapState(i); G_FreeMapState(i);
} }
for (i=MAXQUOTES-1; i>=0; i--)
{
Bfree(apStrings[i]);
Bfree(apXStrings[i]);
}
for (i=MAXPLAYERS-1; i>=0; i--) for (i=MAXPLAYERS-1; i>=0; i--)
{ {
Bfree(g_player[i].ps); Bfree(g_player[i].ps);
@ -7794,7 +7788,7 @@ int G_DoMoveThings(void)
{ {
if (ldist(&sprite[pPlayer->i], &sprite[hitData.sprite]) < 9216) if (ldist(&sprite[pPlayer->i], &sprite[hitData.sprite]) < 9216)
{ {
Bsprintf(apStrings[QUOTE_RESERVED3], "%s", &g_player[playerNum].user_name[0]); quoteMgr.FormatQuote(QUOTE_RESERVED3, "%s", &g_player[playerNum].user_name[0]);
pPlayer->fta = 12, pPlayer->ftq = QUOTE_RESERVED3; pPlayer->fta = 12, pPlayer->ftq = QUOTE_RESERVED3;
} }
} }

View file

@ -902,129 +902,30 @@ void C_DefineVolumeFlags(int32_t vol, int32_t flags)
int32_t C_AllocQuote(int32_t qnum) int32_t C_AllocQuote(int32_t qnum)
{ {
Bassert((unsigned)qnum < MAXQUOTES); Bassert((unsigned)qnum < MAXQUOTES);
return 1;
if (apStrings[qnum] == NULL)
{
apStrings[qnum] = (char *)Xcalloc(MAXQUOTELEN,sizeof(uint8_t));
return 1;
}
return 0;
} }
#ifndef EDUKE32_TOUCH_DEVICES
static void C_ReplaceQuoteSubstring(const size_t q, char const * const query, char const * const replacement)
{
size_t querylength = Bstrlen(query);
for (bssize_t i = MAXQUOTELEN - querylength - 2; i >= 0; i--)
if (Bstrncmp(&apStrings[q][i], query, querylength) == 0)
{
Bmemset(tempbuf, 0, sizeof(tempbuf));
Bstrncpy(tempbuf, apStrings[q], i);
Bstrcat(tempbuf, replacement);
Bstrcat(tempbuf, &apStrings[q][i + querylength]);
Bstrncpy(apStrings[q], tempbuf, MAXQUOTELEN - 1);
i = MAXQUOTELEN - querylength - 2;
}
}
#endif
void C_InitQuotes(void) void C_InitQuotes(void)
{ {
for (bssize_t i = 0; i < 128; i++) C_AllocQuote(i);
#ifdef EDUKE32_TOUCH_DEVICES
apStrings[QUOTE_DEAD] = 0;
#else
auto openkeys = Bindings.GetKeysForCommand("+open"); auto openkeys = Bindings.GetKeysForCommand("+open");
if (openkeys.Size()) if (openkeys.Size())
{ {
auto OpenGameFunc = C_NameKeys(openkeys.Data(), 1); auto OpenGameFunc = C_NameKeys(openkeys.Data(), 1);
C_ReplaceQuoteSubstring(QUOTE_DEAD, "SPACE", OpenGameFunc); quoteMgr.Substitute(QUOTE_DEAD, "SPACE", OpenGameFunc);
C_ReplaceQuoteSubstring(QUOTE_DEAD, "OPEN", OpenGameFunc); quoteMgr.Substitute(QUOTE_DEAD, "OPEN", OpenGameFunc);
C_ReplaceQuoteSubstring(QUOTE_DEAD, "USE", OpenGameFunc); quoteMgr.Substitute(QUOTE_DEAD, "USE", OpenGameFunc);
} }
#endif
// most of these are based on Blood, obviously g_numObituaries = 48;
const char *PlayerObituaries[] =
{
"^02%s^02 beat %s^02 like a cur",
"^02%s^02 broke %s",
"^02%s^02 body bagged %s",
"^02%s^02 boned %s^02 like a fish",
"^02%s^02 castrated %s",
"^02%s^02 creamed %s",
"^02%s^02 crushed %s",
"^02%s^02 destroyed %s",
"^02%s^02 diced %s",
"^02%s^02 disemboweled %s",
"^02%s^02 erased %s",
"^02%s^02 eviscerated %s",
"^02%s^02 flailed %s",
"^02%s^02 flattened %s",
"^02%s^02 gave AnAl MaDnEsS to %s",
"^02%s^02 gave %s^02 Anal Justice",
"^02%s^02 hosed %s",
"^02%s^02 hurt %s^02 real bad",
"^02%s^02 killed %s",
"^02%s^02 made dog meat out of %s",
"^02%s^02 made mincemeat out of %s",
"^02%s^02 manhandled %s",
"^02%s^02 massacred %s",
"^02%s^02 mutilated %s",
"^02%s^02 murdered %s",
"^02%s^02 neutered %s",
"^02%s^02 punted %s",
"^02%s^02 reamed %s",
"^02%s^02 ripped %s^02 a new orifice",
"^02%s^02 rocked %s",
"^02%s^02 sent %s^02 to hell",
"^02%s^02 shredded %s",
"^02%s^02 slashed %s",
"^02%s^02 slaughtered %s",
"^02%s^02 sliced %s",
"^02%s^02 smacked %s around",
"^02%s^02 smashed %s",
"^02%s^02 snuffed %s",
"^02%s^02 sodomized %s",
"^02%s^02 splattered %s",
"^02%s^02 sprayed %s",
"^02%s^02 squashed %s",
"^02%s^02 throttled %s",
"^02%s^02 toasted %s",
"^02%s^02 vented %s",
"^02%s^02 ventilated %s",
"^02%s^02 wasted %s",
"^02%s^02 wrecked %s",
};
const char *PlayerSelfObituaries[] =
{
"^02%s^02 is excrement",
"^02%s^02 is hamburger",
"^02%s^02 suffered scrotum separation",
"^02%s^02 volunteered for population control",
"^02%s^02 has suicided",
"^02%s^02 bled out",
};
EDUKE32_STATIC_ASSERT(OBITQUOTEINDEX + ARRAY_SIZE(PlayerObituaries)-1 < MAXQUOTES);
EDUKE32_STATIC_ASSERT(SUICIDEQUOTEINDEX + ARRAY_SIZE(PlayerSelfObituaries)-1 < MAXQUOTES);
g_numObituaries = ARRAY_SIZE(PlayerObituaries);
for (bssize_t i = g_numObituaries - 1; i >= 0; i--) for (bssize_t i = g_numObituaries - 1; i >= 0; i--)
{ {
if (C_AllocQuote(i + OBITQUOTEINDEX)) quoteMgr.FormatQuote(i + OBITQUOTEINDEX, "$TXT_OBITUARY%d", i + 1);
Bstrcpy(apStrings[i + OBITQUOTEINDEX], PlayerObituaries[i]);
} }
g_numSelfObituaries = ARRAY_SIZE(PlayerSelfObituaries); g_numSelfObituaries = 6;
for (bssize_t i = g_numSelfObituaries - 1; i >= 0; i--) for (bssize_t i = g_numSelfObituaries - 1; i >= 0; i--)
{ {
if (C_AllocQuote(i + SUICIDEQUOTEINDEX)) quoteMgr.FormatQuote(i + SUICIDEQUOTEINDEX, "$TXT_SELFOBIT%d", i + 1);
Bstrcpy(apStrings[i + SUICIDEQUOTEINDEX], PlayerSelfObituaries[i]);
} }
} }
@ -2028,6 +1929,7 @@ static int32_t C_ParseCommand(int32_t loop)
continue; continue;
case CON_DEFINEQUOTE: case CON_DEFINEQUOTE:
{
g_scriptPtr--; g_scriptPtr--;
C_GetNextValue(LABEL_DEFINE); C_GetNextValue(LABEL_DEFINE);
@ -2050,33 +1952,16 @@ static int32_t C_ParseCommand(int32_t loop)
C_SkipSpace(); C_SkipSpace();
while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0) TArray<char> buffer;
{ while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0)
/* {
if (*textptr == '%' && *(textptr+1) == 's') buffer.Push(*textptr);
{ textptr++;
initprintf("%s:%d: error: quote text contains string identifier.\n",g_szScriptFileName,g_lineNumber); }
g_numCompilerErrors++; buffer.Push(0);
while (*textptr != 0x0a && *textptr != 0x0d && *textptr != 0) textptr++; quoteMgr.InitializeQuote(k, buffer.Data(), true);
break; continue;
} }
*/
*(apStrings[k]+i) = *textptr;
textptr++,i++;
if (EDUKE32_PREDICT_FALSE(i >= MAXQUOTELEN-1))
{
initprintf("%s:%d: warning: truncating quote text to %d characters.\n",g_scriptFileName,g_lineNumber,MAXQUOTELEN-1);
g_warningCnt++;
C_NextLine();
break;
}
}
if ((unsigned)k < MAXQUOTES)
*(apStrings[k]+i) = '\0';
continue;
case CON_DEFINESOUND: case CON_DEFINESOUND:
g_scriptPtr--; g_scriptPtr--;
C_GetNextValue(LABEL_DEFINE); C_GetNextValue(LABEL_DEFINE);
@ -2313,14 +2198,6 @@ void C_PrintStats(void)
int i, j; int i, j;
for (i=MAXQUOTES-1, j=0; i>=0; i--)
{
if (apStrings[i])
j++;
}
if (j) initprintf("%d strings, ", j);
for (i=MAXTILES-1, j=0; i>=0; i--) for (i=MAXTILES-1, j=0; i>=0; i--)
{ {
if (g_tile[i].execPtr) if (g_tile[i].execPtr)

View file

@ -2457,7 +2457,7 @@ GAMEEXEC_STATIC void VM_Execute(native_t loop)
case CON_QUOTE: case CON_QUOTE:
insptr++; insptr++;
if (EDUKE32_PREDICT_FALSE((unsigned)(*insptr) >= MAXQUOTES) || apStrings[*insptr] == NULL) if (EDUKE32_PREDICT_FALSE((unsigned)(*insptr) >= MAXQUOTES))
{ {
CON_ERRPRINTF("invalid quote %d\n", (int32_t)(*insptr)); CON_ERRPRINTF("invalid quote %d\n", (int32_t)(*insptr));
insptr++; insptr++;

View file

@ -72,7 +72,6 @@ G_EXTERN actor_t actor[MAXSPRITES];
// g_tile: tile-specific data THAT DOES NOT CHANGE during the course of a game // g_tile: tile-specific data THAT DOES NOT CHANGE during the course of a game
G_EXTERN tiledata_t g_tile[MAXTILES]; G_EXTERN tiledata_t g_tile[MAXTILES];
G_EXTERN animwalltype animwall[MAXANIMWALLS]; G_EXTERN animwalltype animwall[MAXANIMWALLS];
G_EXTERN char *apStrings[MAXQUOTES],*apXStrings[MAXQUOTES];
G_EXTERN char *label; G_EXTERN char *label;
G_EXTERN int32_t g_musicIndex; G_EXTERN int32_t g_musicIndex;
G_EXTERN char g_loadFromGroupOnly; G_EXTERN char g_loadFromGroupOnly;

View file

@ -4478,18 +4478,18 @@ void P_FragPlayer(int playerNum)
if (playerNum == screenpeek) if (playerNum == screenpeek)
{ {
Bsprintf(apStrings[QUOTE_RESERVED], "Killed by %s", &g_player[pPlayer->frag_ps].user_name[0]); quoteMgr.InitializeQuote(QUOTE_RESERVED, "Killed by %s", &g_player[pPlayer->frag_ps].user_name[0]);
P_DoQuote(QUOTE_RESERVED, pPlayer); P_DoQuote(QUOTE_RESERVED, pPlayer);
} }
else else
{ {
Bsprintf(apStrings[QUOTE_RESERVED2], "Killed %s", &g_player[playerNum].user_name[0]); quoteMgr.InitializeQuote(QUOTE_RESERVED2, "Killed %s", &g_player[playerNum].user_name[0]);
P_DoQuote(QUOTE_RESERVED2, g_player[pPlayer->frag_ps].ps); P_DoQuote(QUOTE_RESERVED2, g_player[pPlayer->frag_ps].ps);
} }
if (cl_obituaries) if (cl_obituaries)
{ {
Bsprintf(tempbuf, apStrings[OBITQUOTEINDEX + (krand2() % g_numObituaries)], Bsprintf(tempbuf, quoteMgr.GetQuote(OBITQUOTEINDEX + (krand2() % g_numObituaries)),
&g_player[pPlayer->frag_ps].user_name[0], &g_player[playerNum].user_name[0]); &g_player[pPlayer->frag_ps].user_name[0], &g_player[playerNum].user_name[0]);
G_AddUserQuote(tempbuf); G_AddUserQuote(tempbuf);
} }
@ -4502,14 +4502,14 @@ void P_FragPlayer(int playerNum)
{ {
pPlayer->fraggedself++; pPlayer->fraggedself++;
if ((unsigned)pPlayer->wackedbyactor < MAXTILES && A_CheckEnemyTile(sprite[pPlayer->wackedbyactor].picnum)) if ((unsigned)pPlayer->wackedbyactor < MAXTILES && A_CheckEnemyTile(sprite[pPlayer->wackedbyactor].picnum))
Bsprintf(tempbuf, apStrings[OBITQUOTEINDEX + (krand2() % g_numObituaries)], "A monster", Bsprintf(tempbuf, quoteMgr.GetQuote(OBITQUOTEINDEX + (krand2() % g_numObituaries)), "A monster",
&g_player[playerNum].user_name[0]); &g_player[playerNum].user_name[0]);
else if (actor[pPlayer->i].picnum == NUKEBUTTON) else if (actor[pPlayer->i].picnum == NUKEBUTTON)
Bsprintf(tempbuf, "^02%s^02 tried to leave", &g_player[playerNum].user_name[0]); Bsprintf(tempbuf, "^02%s^02 tried to leave", &g_player[playerNum].user_name[0]);
else else
{ {
// random suicide death string // random suicide death string
Bsprintf(tempbuf, apStrings[SUICIDEQUOTEINDEX + (krand2() % g_numSelfObituaries)], Bsprintf(tempbuf, quoteMgr.GetQuote(SUICIDEQUOTEINDEX + (krand2() % g_numSelfObituaries)),
&g_player[playerNum].user_name[0]); &g_player[playerNum].user_name[0]);
} }
} }

View file

@ -23,8 +23,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef quotes_h_ #ifndef quotes_h_
#define quotes_h_ #define quotes_h_
#define MAXQUOTES 16384 #include "quotemgr.h"
#define MAXQUOTELEN 128
#define OBITQUOTEINDEX (MAXQUOTES-128) #define OBITQUOTEINDEX (MAXQUOTES-128)
#define SUICIDEQUOTEINDEX (MAXQUOTES-32) #define SUICIDEQUOTEINDEX (MAXQUOTES-32)

View file

@ -362,7 +362,7 @@ bool G_SavePlayer(FSaveGameNode *sv)
if (!g_netServer && ud.multimode < 2) if (!g_netServer && ud.multimode < 2)
{ {
OSD_Printf("Saved: %s\n", fn.GetChars()); OSD_Printf("Saved: %s\n", fn.GetChars());
Bstrcpy(apStrings[QUOTE_RESERVED4], "Game Saved"); quoteMgr.InitializeQuote(QUOTE_RESERVED4, "Game Saved");
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps); P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
} }
@ -380,7 +380,7 @@ bool GameInterface::LoadGame(FSaveGameNode* sv)
{ {
if (g_netServer || ud.multimode > 1) if (g_netServer || ud.multimode > 1)
{ {
Bstrcpy(apStrings[QUOTE_RESERVED4], "Multiplayer Loading Not Yet Supported"); quoteMgr.InitializeQuote(QUOTE_RESERVED4, "Multiplayer Loading Not Yet Supported");
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps); P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
// g_player[myconnectindex].ps->gm = MODE_GAME; // g_player[myconnectindex].ps->gm = MODE_GAME;
@ -399,7 +399,7 @@ bool GameInterface::SaveGame(FSaveGameNode* sv)
{ {
if (g_netServer || ud.multimode > 1) if (g_netServer || ud.multimode > 1)
{ {
Bstrcpy(apStrings[QUOTE_RESERVED4], "Multiplayer Saving Not Yet Supported"); quoteMgr.InitializeQuote(QUOTE_RESERVED4, "Multiplayer Saving Not Yet Supported");
P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps); P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
return false; return false;
} }
@ -881,8 +881,6 @@ static void sv_rrrafog();
((sizeof(g_player[0].user_name)+sizeof(g_player[0].pcolor)+sizeof(g_player[0].pteam) \ ((sizeof(g_player[0].user_name)+sizeof(g_player[0].pcolor)+sizeof(g_player[0].pteam) \
+sizeof(g_player[0].frags)+sizeof(DukePlayer_t))*MAXPLAYERS) +sizeof(g_player[0].frags)+sizeof(DukePlayer_t))*MAXPLAYERS)
static uint8_t savegame_quotedef[MAXQUOTES>>3];
static char (*savegame_quotes)[MAXQUOTELEN];
static uint8_t savegame_restdata[SVARDATALEN]; static uint8_t savegame_restdata[SVARDATALEN];
static char svgm_udnetw_string [] = "blK:udnt"; static char svgm_udnetw_string [] = "blK:udnt";
@ -1082,12 +1080,6 @@ static const dataspec_t svgm_anmisc[] =
{ 0, &g_fogType, sizeof(g_fogType), 1 }, { 0, &g_fogType, sizeof(g_fogType), 1 },
{ DS_LOADFN, (void *)sv_rrrafog, 0, 1 }, { DS_LOADFN, (void *)sv_rrrafog, 0, 1 },
{ DS_SAVEFN|DS_LOADFN|DS_NOCHK, (void *)sv_prequote, 0, 1 },
{ DS_SAVEFN, (void *)&sv_quotesave, 0, 1 },
{ DS_NOCHK, &savegame_quotedef, sizeof(savegame_quotedef), 1 }, // quotes can change during runtime, but new quote numbers cannot be allocated
{ DS_DYNAMIC, &savegame_quotes, MAXQUOTELEN, MAXQUOTES },
{ DS_LOADFN, (void *)&sv_quoteload, 0, 1 },
{ DS_SAVEFN, (void *)&sv_restsave, 0, 1 }, { DS_SAVEFN, (void *)&sv_restsave, 0, 1 },
{ 0, savegame_restdata, 1, sizeof(savegame_restdata) }, // sz/cnt swapped for kdfread { 0, savegame_restdata, 1, sizeof(savegame_restdata) }, // sz/cnt swapped for kdfread
{ DS_LOADFN, (void *)&sv_restload, 0, 1 }, { DS_LOADFN, (void *)&sv_restload, 0, 1 },
@ -1482,33 +1474,6 @@ static void sv_postanimateptr()
{ {
G_Util_PtrToIdx(g_animatePtr, g_animateCnt, sector, P2I_BACK); G_Util_PtrToIdx(g_animatePtr, g_animateCnt, sector, P2I_BACK);
} }
static void sv_prequote()
{
if (!savegame_quotes)
{
void *ptr = Xcalloc(MAXQUOTES, MAXQUOTELEN);
savegame_quotes = (char(*)[MAXQUOTELEN])ptr;
}
}
static void sv_quotesave()
{
Bmemset(savegame_quotedef, 0, sizeof(savegame_quotedef));
for (int i = 0; i < MAXQUOTES; i++)
if (apStrings[i])
{
savegame_quotedef[i>>3] |= 1<<(i&7);
Bmemcpy(savegame_quotes[i], apStrings[i], MAXQUOTELEN);
}
}
static void sv_quoteload()
{
for (int i = 0; i < MAXQUOTES; i++)
if (savegame_quotedef[i>>3] & (1<<(i&7)))
{
C_AllocQuote(i);
Bmemcpy(apStrings[i], savegame_quotes[i], MAXQUOTELEN);
}
}
static void sv_restsave() static void sv_restsave()
{ {
uint8_t * mem = savegame_restdata; uint8_t * mem = savegame_restdata;

View file

@ -1071,12 +1071,6 @@ void G_PrintGameQuotes(int32_t snum)
if (k <= 1) if (k <= 1)
break; break;
if (EDUKE32_PREDICT_FALSE(apStrings[ps->ftq] == NULL))
{
OSD_Printf(OSD_ERROR "%s %d null quote %d\n", __FILE__, __LINE__, ps->ftq);
break;
}
int32_t y = ybase; int32_t y = ybase;
if (reserved_quote) if (reserved_quote)
{ {
@ -1106,7 +1100,7 @@ void G_PrintGameQuotes(int32_t snum)
} }
#endif #endif
height = gametext_(x, y, apStrings[ps->ftq], textsh(k), pal, texto(k), texta(k), TEXT_XCENTER).y + (1<<16); height = gametext_(x, y, quoteMgr.GetQuote(ps->ftq), textsh(k), pal, texto(k), texta(k), TEXT_XCENTER).y + (1<<16);
} }
while (0); while (0);
@ -1145,12 +1139,6 @@ void P_DoQuote(int32_t q, DukePlayer_t *p)
q &= ~MAXQUOTES; q &= ~MAXQUOTES;
} }
if (EDUKE32_PREDICT_FALSE(apStrings[q] == NULL))
{
OSD_Printf(OSD_ERROR "%s %d null quote %d\n", __FILE__, __LINE__, q);
return;
}
if (p->fta > 0 && q != QUOTE_RESERVED && q != QUOTE_RESERVED2) if (p->fta > 0 && q != QUOTE_RESERVED && q != QUOTE_RESERVED2)
if (p->ftq == QUOTE_RESERVED || p->ftq == QUOTE_RESERVED2) return; if (p->ftq == QUOTE_RESERVED || p->ftq == QUOTE_RESERVED2) return;
@ -1158,8 +1146,9 @@ void P_DoQuote(int32_t q, DukePlayer_t *p)
if (p->ftq != q) if (p->ftq != q)
{ {
if (p == g_player[screenpeek].ps && apStrings[q][0] != '\0') auto qu = quoteMgr.GetQuote(q);
OSD_Printf(cq ? OSDTEXT_DEFAULT "%s\n" : "%s\n", apStrings[q]); if (p == g_player[screenpeek].ps && qu[0] != '\0')
OSD_Printf(cq ? OSDTEXT_DEFAULT "%s\n" : "%s\n", qu);
p->ftq = q; p->ftq = q;
} }