mirror of
https://git.do.srb2.org/KartKrew/Kart-Public.git
synced 2025-01-13 05:11:01 +00:00
Merge branch 'map-by-name' of https://git.do.srb2.org/KartKrew/Kart into internal16fixes
This commit is contained in:
commit
60b8916a96
10 changed files with 596 additions and 99 deletions
|
@ -353,6 +353,40 @@ size_t COM_CheckParm(const char *check)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \brief COM_CheckParm, but checks only the start of each argument.
|
||||||
|
* E.g. checking for "-no" would match "-noerror" too.
|
||||||
|
*/
|
||||||
|
size_t COM_CheckPartialParm(const char *check)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
len = strlen(check);
|
||||||
|
|
||||||
|
for (i = 1; i < com_argc; i++)
|
||||||
|
{
|
||||||
|
if (strncasecmp(check, com_argv[i], len) == 0)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Find the first argument that starts with a hyphen (-).
|
||||||
|
* \return The index of the argument, or 0
|
||||||
|
* if there are no such arguments.
|
||||||
|
*/
|
||||||
|
size_t COM_FirstOption(void)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 1; i < com_argc; i++)
|
||||||
|
{
|
||||||
|
if (com_argv[i][0] == '-')/* options start with a hyphen */
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/** Parses a string into command-line tokens.
|
/** Parses a string into command-line tokens.
|
||||||
*
|
*
|
||||||
* \param ptext A null-terminated string. Does not need to be
|
* \param ptext A null-terminated string. Does not need to be
|
||||||
|
|
|
@ -37,6 +37,8 @@ size_t COM_Argc(void);
|
||||||
const char *COM_Argv(size_t arg); // if argv > argc, returns empty string
|
const char *COM_Argv(size_t arg); // if argv > argc, returns empty string
|
||||||
char *COM_Args(void);
|
char *COM_Args(void);
|
||||||
size_t COM_CheckParm(const char *check); // like M_CheckParm :)
|
size_t COM_CheckParm(const char *check); // like M_CheckParm :)
|
||||||
|
size_t COM_CheckPartialParm(const char *check);
|
||||||
|
size_t COM_FirstOption(void);
|
||||||
|
|
||||||
// match existing command or NULL
|
// match existing command or NULL
|
||||||
const char *COM_CompleteCommand(const char *partial, INT32 skips);
|
const char *COM_CompleteCommand(const char *partial, INT32 skips);
|
||||||
|
|
37
src/d_main.c
37
src/d_main.c
|
@ -1247,26 +1247,6 @@ void D_SRB2Main(void)
|
||||||
if (M_CheckParm("-server") || dedicated)
|
if (M_CheckParm("-server") || dedicated)
|
||||||
netgame = server = true;
|
netgame = server = true;
|
||||||
|
|
||||||
if (M_CheckParm("-warp") && M_IsNextParm())
|
|
||||||
{
|
|
||||||
const char *word = M_GetNextParm();
|
|
||||||
char ch; // use this with sscanf to catch non-digits with
|
|
||||||
if (fastncmp(word, "MAP", 3)) // MAPxx name
|
|
||||||
pstartmap = M_MapNumber(word[3], word[4]);
|
|
||||||
else if (sscanf(word, "%d%c", &pstartmap, &ch) != 1) // a plain number
|
|
||||||
I_Error("Cannot warp to map %s (invalid map name)\n", word);
|
|
||||||
// Don't check if lump exists just yet because the wads haven't been loaded!
|
|
||||||
// Just do a basic range check here.
|
|
||||||
if (pstartmap < 1 || pstartmap > NUMMAPS)
|
|
||||||
I_Error("Cannot warp to map %d (out of range)\n", pstartmap);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!M_CheckParm("-server"))
|
|
||||||
G_SetGameModified(true, true);
|
|
||||||
autostart = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
|
CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
|
||||||
Z_Init();
|
Z_Init();
|
||||||
|
|
||||||
|
@ -1446,6 +1426,23 @@ void D_SRB2Main(void)
|
||||||
|
|
||||||
//------------------------------------------------ COMMAND LINE PARAMS
|
//------------------------------------------------ COMMAND LINE PARAMS
|
||||||
|
|
||||||
|
// this must be done after loading gamedata,
|
||||||
|
// to avoid setting off the corrupted gamedata code in G_LoadGameData if a SOC with custom gamedata is added
|
||||||
|
// -- Monster Iestyn 20/02/20
|
||||||
|
if (M_CheckParm("-warp") && M_IsNextParm())
|
||||||
|
{
|
||||||
|
const char *word = M_GetNextParm();
|
||||||
|
pstartmap = G_FindMapByNameOrCode(word, 0);
|
||||||
|
if (! pstartmap)
|
||||||
|
I_Error("Cannot find a map remotely named '%s'\n", word);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!M_CheckParm("-server"))
|
||||||
|
G_SetGameModified(true, true);
|
||||||
|
autostart = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize CD-Audio
|
// Initialize CD-Audio
|
||||||
if (M_CheckParm("-usecd") && !dedicated)
|
if (M_CheckParm("-usecd") && !dedicated)
|
||||||
I_InitCD();
|
I_InitCD();
|
||||||
|
|
244
src/d_netcmd.c
244
src/d_netcmd.c
|
@ -2519,27 +2519,67 @@ void D_PickVote(void)
|
||||||
SendNetXCmd(XD_PICKVOTE, &buf, 2);
|
SendNetXCmd(XD_PICKVOTE, &buf, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
ConcatCommandArgv (int start, int end)
|
||||||
|
{
|
||||||
|
char *final;
|
||||||
|
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
size = 0;
|
||||||
|
|
||||||
|
for (i = start; i < end; ++i)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
one space after each argument, but terminating
|
||||||
|
character on final argument
|
||||||
|
*/
|
||||||
|
size += strlen(COM_Argv(i)) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
final = ZZ_Alloc(size);
|
||||||
|
p = final;
|
||||||
|
|
||||||
|
--end;/* handle the final argument separately */
|
||||||
|
for (i = start; i < end; ++i)
|
||||||
|
{
|
||||||
|
p += sprintf(p, "%s ", COM_Argv(i));
|
||||||
|
}
|
||||||
|
/* at this point "end" is actually the last argument's position */
|
||||||
|
strcpy(p, COM_Argv(end));
|
||||||
|
|
||||||
|
return final;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
// Warp to map code.
|
// Warp to map code.
|
||||||
// Called either from map <mapname> console command, or idclev cheat.
|
// Called either from map <mapname> console command, or idclev cheat.
|
||||||
//
|
//
|
||||||
|
// Largely rewritten by James.
|
||||||
|
//
|
||||||
static void Command_Map_f(void)
|
static void Command_Map_f(void)
|
||||||
{
|
{
|
||||||
const char *mapname;
|
size_t first_option;
|
||||||
size_t i;
|
size_t option_force;
|
||||||
INT32 newmapnum;
|
size_t option_gametype;
|
||||||
boolean newresetplayers, newencoremode;
|
size_t option_encore;
|
||||||
INT32 newgametype = gametype;
|
const char *gametypename;
|
||||||
|
boolean newresetplayers;
|
||||||
|
|
||||||
// max length of command: map map03 -gametype race -noresetplayers -force -encore
|
boolean mustmodifygame;
|
||||||
// 1 2 3 4 5 6 7
|
|
||||||
// = 8 arg max
|
INT32 newmapnum;
|
||||||
// i don't know whether this is intrinsic to the system or just someone being weird but
|
|
||||||
// "noresetplayers" is pretty useless for kart if it turns out this is too close to the limit
|
char * mapname;
|
||||||
if (COM_Argc() < 2 || COM_Argc() > 8)
|
char *realmapname = NULL;
|
||||||
{
|
|
||||||
CONS_Printf(M_GetText("map <mapname> [-gametype <type> [-force]: warp to map\n"));
|
INT32 newgametype = gametype;
|
||||||
return;
|
boolean newencoremode = cv_kartencore.value;
|
||||||
}
|
|
||||||
|
INT32 d;
|
||||||
|
|
||||||
if (client && !IsPlayerAdmin(consoleplayer))
|
if (client && !IsPlayerAdmin(consoleplayer))
|
||||||
{
|
{
|
||||||
|
@ -2547,99 +2587,129 @@ static void Command_Map_f(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// internal wad lump always: map command doesn't support external files as in doom legacy
|
option_force = COM_CheckPartialParm("-f");
|
||||||
if (W_CheckNumForName(COM_Argv(1)) == LUMPERROR)
|
option_gametype = COM_CheckPartialParm("-g");
|
||||||
|
option_encore = COM_CheckPartialParm("-e");
|
||||||
|
newresetplayers = ! COM_CheckParm("-noresetplayers");
|
||||||
|
|
||||||
|
mustmodifygame = !( netgame || multiplayer || majormods );
|
||||||
|
|
||||||
|
if (mustmodifygame && !option_force)
|
||||||
{
|
{
|
||||||
CONS_Alert(CONS_ERROR, M_GetText("Internal game level '%s' not found\n"), COM_Argv(1));
|
/* May want to be more descriptive? */
|
||||||
|
CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(netgame || multiplayer) && !majormods)
|
|
||||||
{
|
|
||||||
if (COM_CheckParm("-force"))
|
|
||||||
{
|
|
||||||
G_SetGameModified(false, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newresetplayers = !COM_CheckParm("-noresetplayers");
|
|
||||||
|
|
||||||
if (!newresetplayers && !cv_debug)
|
if (!newresetplayers && !cv_debug)
|
||||||
{
|
{
|
||||||
CONS_Printf(M_GetText("DEVMODE must be enabled.\n"));
|
CONS_Printf(M_GetText("DEVMODE must be enabled.\n"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mapname = COM_Argv(1);
|
if (option_gametype)
|
||||||
if (strlen(mapname) != 5
|
|
||||||
|| (newmapnum = M_MapNumber(mapname[3], mapname[4])) == 0)
|
|
||||||
{
|
{
|
||||||
CONS_Alert(CONS_ERROR, M_GetText("Invalid level name %s\n"), mapname);
|
if (!multiplayer)
|
||||||
|
{
|
||||||
|
CONS_Printf(M_GetText(
|
||||||
|
"You can't switch gametypes in single player!\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (COM_Argc() < option_gametype + 2)/* no argument after? */
|
||||||
|
{
|
||||||
|
CONS_Alert(CONS_ERROR,
|
||||||
|
"No gametype name follows parameter '%s'.\n",
|
||||||
|
COM_Argv(option_gametype));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!( first_option = COM_FirstOption() ))
|
||||||
|
first_option = COM_Argc();
|
||||||
|
|
||||||
|
if (first_option < 2)
|
||||||
|
{
|
||||||
|
/* I'm going over the fucking lines and I DON'T CAREEEEE */
|
||||||
|
CONS_Printf("map <name / [MAP]code / number> [-gametype <type>] [-encore] [-force]:\n");
|
||||||
|
CONS_Printf(M_GetText(
|
||||||
|
"Warp to a map, by its name, two character code, with optional \"MAP\" prefix, or by its number (though why would you).\n"
|
||||||
|
"All parameters are case-insensitive and may be abbreviated.\n"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mapname = ConcatCommandArgv(1, first_option);
|
||||||
|
|
||||||
|
newmapnum = G_FindMapByNameOrCode(mapname, &realmapname);
|
||||||
|
|
||||||
|
if (newmapnum == 0)
|
||||||
|
{
|
||||||
|
CONS_Alert(CONS_ERROR, M_GetText("Could not find any map described as '%s'.\n"), mapname);
|
||||||
|
Z_Free(mapname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mustmodifygame && option_force)
|
||||||
|
{
|
||||||
|
G_SetGameModified(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
// new gametype value
|
// new gametype value
|
||||||
// use current one by default
|
// use current one by default
|
||||||
i = COM_CheckParm("-gametype");
|
if (option_gametype)
|
||||||
if (i)
|
|
||||||
{
|
{
|
||||||
if (!multiplayer)
|
gametypename = COM_Argv(option_gametype + 1);
|
||||||
{
|
|
||||||
CONS_Printf(M_GetText("You can't switch gametypes in single player!\n"));
|
newgametype = G_GetGametypeByName(gametypename);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
newgametype = G_GetGametypeByName(COM_Argv(i+1));
|
|
||||||
if (newgametype == -1) // reached end of the list with no match
|
if (newgametype == -1) // reached end of the list with no match
|
||||||
{
|
{
|
||||||
INT32 j = atoi(COM_Argv(i+1)); // assume they gave us a gametype number, which is okay too
|
/* Did they give us a gametype number? That's okay too! */
|
||||||
if (j >= 0 && j < NUMGAMETYPES)
|
if (isdigit(gametypename[0]))
|
||||||
newgametype = (INT16)j;
|
{
|
||||||
|
d = atoi(gametypename);
|
||||||
|
if (d >= 0 && d < NUMGAMETYPES)
|
||||||
|
newgametype = d;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CONS_Alert(CONS_ERROR,
|
||||||
|
"Gametype number %d is out of range. Use a number between"
|
||||||
|
" 0 and %d inclusive. ...Or just use the name. :v\n",
|
||||||
|
d,
|
||||||
|
NUMGAMETYPES-1);
|
||||||
|
Z_Free(realmapname);
|
||||||
|
Z_Free(mapname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CONS_Alert(CONS_ERROR,
|
||||||
|
"'%s' is not a gametype.\n",
|
||||||
|
gametypename);
|
||||||
|
Z_Free(realmapname);
|
||||||
|
Z_Free(mapname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// new encoremode value
|
if (!option_force && newgametype == gametype) // SRB2Kart
|
||||||
// use cvar by default
|
|
||||||
|
|
||||||
newencoremode = (boolean)cv_kartencore.value;
|
|
||||||
|
|
||||||
if (COM_CheckParm("-encore"))
|
|
||||||
{
|
|
||||||
if (!M_SecretUnlocked(SECRET_ENCORE) && !newencoremode)
|
|
||||||
{
|
|
||||||
CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
newencoremode = !newencoremode;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(i = COM_CheckParm("-force")) && newgametype == gametype) // SRB2Kart
|
|
||||||
newresetplayers = false; // if not forcing and gametypes is the same
|
newresetplayers = false; // if not forcing and gametypes is the same
|
||||||
|
|
||||||
// don't use a gametype the map doesn't support
|
// don't use a gametype the map doesn't support
|
||||||
if (cv_debug || i || cv_skipmapcheck.value)
|
if (cv_debug || option_force || cv_skipmapcheck.value)
|
||||||
; // The player wants us to trek on anyway. Do so.
|
fromlevelselect = false; // The player wants us to trek on anyway. Do so.
|
||||||
// G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer
|
// G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer
|
||||||
// Alternatively, bail if the map header is completely missing anyway.
|
else
|
||||||
else if (!mapheaderinfo[newmapnum-1]
|
|
||||||
|| !(mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)))
|
|
||||||
{
|
{
|
||||||
char gametypestring[32] = "Single Player";
|
if (!(mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype)))
|
||||||
|
|
||||||
if (multiplayer)
|
|
||||||
{
|
{
|
||||||
if (newgametype >= 0 && newgametype < NUMGAMETYPES
|
CONS_Alert(CONS_WARNING, M_GetText("Course %s (%s) doesn't support %s mode!\n(Use -force to override)\n"), realmapname, G_BuildMapName(newmapnum),
|
||||||
&& Gametype_Names[newgametype])
|
(multiplayer ? gametype_cons_t[newgametype].strvalue : "Single Player"));
|
||||||
strcpy(gametypestring, Gametype_Names[newgametype]);
|
Z_Free(realmapname);
|
||||||
|
Z_Free(mapname);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CONS_Alert(CONS_WARNING, M_GetText("%s doesn't support %s mode!\n(Use -force to override)\n"), mapname, gametypestring);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent warping to locked levels
|
// Prevent warping to locked levels
|
||||||
|
@ -2649,11 +2719,25 @@ static void Command_Map_f(void)
|
||||||
if (!dedicated && M_MapLocked(newmapnum))
|
if (!dedicated && M_MapLocked(newmapnum))
|
||||||
{
|
{
|
||||||
CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
|
CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
|
||||||
|
Z_Free(realmapname);
|
||||||
|
Z_Free(mapname);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (option_encore)
|
||||||
|
{
|
||||||
|
newencoremode = ! newencoremode;
|
||||||
|
if (! M_SecretUnlocked(SECRET_ENCORE) && newencoremode)
|
||||||
|
{
|
||||||
|
CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fromlevelselect = false;
|
fromlevelselect = false;
|
||||||
D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false);
|
D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false);
|
||||||
|
|
||||||
|
Z_Free(realmapname);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Receives a map command and changes the map.
|
/** Receives a map command and changes the map.
|
||||||
|
@ -2702,7 +2786,9 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
|
||||||
lastgametype = gametype;
|
lastgametype = gametype;
|
||||||
gametype = READUINT8(*cp);
|
gametype = READUINT8(*cp);
|
||||||
|
|
||||||
if (gametype != lastgametype)
|
if (gametype < 0 || gametype >= NUMGAMETYPES)
|
||||||
|
gametype = lastgametype;
|
||||||
|
else if (gametype != lastgametype)
|
||||||
D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
|
D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
|
||||||
|
|
||||||
if (!G_RaceGametype())
|
if (!G_RaceGametype())
|
||||||
|
|
|
@ -532,6 +532,7 @@ extern boolean capslock;
|
||||||
|
|
||||||
// if we ever make our alloc stuff...
|
// if we ever make our alloc stuff...
|
||||||
#define ZZ_Alloc(x) Z_Malloc(x, PU_STATIC, NULL)
|
#define ZZ_Alloc(x) Z_Malloc(x, PU_STATIC, NULL)
|
||||||
|
#define ZZ_Calloc(x) Z_Calloc(x, PU_STATIC, NULL)
|
||||||
|
|
||||||
// i_system.c, replace getchar() once the keyboard has been appropriated
|
// i_system.c, replace getchar() once the keyboard has been appropriated
|
||||||
INT32 I_GetKey(void);
|
INT32 I_GetKey(void);
|
||||||
|
|
|
@ -139,6 +139,9 @@ typedef long ssize_t;
|
||||||
#define strlwr _strlwr
|
#define strlwr _strlwr
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
char *strcasestr(const char *in, const char *what);
|
||||||
|
#define stristr strcasestr
|
||||||
|
|
||||||
#if defined (macintosh) //|| defined (__APPLE__) //skip all boolean/Boolean crap
|
#if defined (macintosh) //|| defined (__APPLE__) //skip all boolean/Boolean crap
|
||||||
#define true 1
|
#define true 1
|
||||||
#define false 0
|
#define false 0
|
||||||
|
|
236
src/g_game.c
236
src/g_game.c
|
@ -4866,6 +4866,242 @@ char *G_BuildMapTitle(INT32 mapnum)
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void measurekeywords(mapsearchfreq_t *fr,
|
||||||
|
struct searchdim **dimp, UINT8 *cuntp,
|
||||||
|
const char *s, const char *q, boolean wanttable)
|
||||||
|
{
|
||||||
|
char *qp;
|
||||||
|
char *sp;
|
||||||
|
if (wanttable)
|
||||||
|
(*dimp) = Z_Realloc((*dimp), 255 * sizeof (struct searchdim),
|
||||||
|
PU_STATIC, NULL);
|
||||||
|
for (qp = strtok(va("%s", q), " ");
|
||||||
|
qp && fr->total < 255;
|
||||||
|
qp = strtok(0, " "))
|
||||||
|
{
|
||||||
|
if (( sp = strcasestr(s, qp) ))
|
||||||
|
{
|
||||||
|
if (wanttable)
|
||||||
|
{
|
||||||
|
(*dimp)[(*cuntp)].pos = sp - s;
|
||||||
|
(*dimp)[(*cuntp)].siz = strlen(qp);
|
||||||
|
}
|
||||||
|
(*cuntp)++;
|
||||||
|
fr->total++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (wanttable)
|
||||||
|
(*dimp) = Z_Realloc((*dimp), (*cuntp) * sizeof (struct searchdim),
|
||||||
|
PU_STATIC, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writesimplefreq(mapsearchfreq_t *fr, INT32 *frc,
|
||||||
|
INT32 mapnum, UINT8 pos, UINT8 siz)
|
||||||
|
{
|
||||||
|
fr[(*frc)].mapnum = mapnum;
|
||||||
|
fr[(*frc)].matchd = ZZ_Alloc(sizeof (struct searchdim));
|
||||||
|
fr[(*frc)].matchd[0].pos = pos;
|
||||||
|
fr[(*frc)].matchd[0].siz = siz;
|
||||||
|
fr[(*frc)].matchc = 1;
|
||||||
|
fr[(*frc)].total = 1;
|
||||||
|
(*frc)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
INT32 G_FindMap(const char *mapname, char **foundmapnamep,
|
||||||
|
mapsearchfreq_t **freqp, INT32 *freqcp)
|
||||||
|
{
|
||||||
|
INT32 newmapnum = 0;
|
||||||
|
INT32 mapnum;
|
||||||
|
INT32 apromapnum = 0;
|
||||||
|
|
||||||
|
size_t mapnamelen;
|
||||||
|
char *realmapname = NULL;
|
||||||
|
char *newmapname = NULL;
|
||||||
|
char *apromapname = NULL;
|
||||||
|
char *aprop = NULL;
|
||||||
|
|
||||||
|
mapsearchfreq_t *freq;
|
||||||
|
boolean wanttable;
|
||||||
|
INT32 freqc;
|
||||||
|
UINT8 frequ;
|
||||||
|
|
||||||
|
INT32 i;
|
||||||
|
|
||||||
|
mapnamelen = strlen(mapname);
|
||||||
|
|
||||||
|
/* Count available maps; how ugly. */
|
||||||
|
for (i = 0, freqc = 0; i < NUMMAPS; ++i)
|
||||||
|
{
|
||||||
|
if (mapheaderinfo[i])
|
||||||
|
freqc++;
|
||||||
|
}
|
||||||
|
|
||||||
|
freq = ZZ_Calloc(freqc * sizeof (mapsearchfreq_t));
|
||||||
|
|
||||||
|
wanttable = !!( freqp );
|
||||||
|
|
||||||
|
freqc = 0;
|
||||||
|
for (i = 0, mapnum = 1; i < NUMMAPS; ++i, ++mapnum)
|
||||||
|
if (mapheaderinfo[i])
|
||||||
|
{
|
||||||
|
if (!( realmapname = G_BuildMapTitle(mapnum) ))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
aprop = realmapname;
|
||||||
|
|
||||||
|
/* Now that we found a perfect match no need to fucking guess. */
|
||||||
|
if (strnicmp(realmapname, mapname, mapnamelen) == 0)
|
||||||
|
{
|
||||||
|
if (wanttable)
|
||||||
|
{
|
||||||
|
writesimplefreq(freq, &freqc, mapnum, 0, mapnamelen);
|
||||||
|
}
|
||||||
|
if (newmapnum == 0)
|
||||||
|
{
|
||||||
|
newmapnum = mapnum;
|
||||||
|
newmapname = realmapname;
|
||||||
|
realmapname = 0;
|
||||||
|
Z_Free(apromapname);
|
||||||
|
if (!wanttable)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (apromapnum == 0 || wanttable)
|
||||||
|
{
|
||||||
|
/* LEVEL 1--match keywords verbatim */
|
||||||
|
if (( aprop = strcasestr(realmapname, mapname) ))
|
||||||
|
{
|
||||||
|
if (wanttable)
|
||||||
|
{
|
||||||
|
writesimplefreq(freq, &freqc,
|
||||||
|
mapnum, aprop - realmapname, mapnamelen);
|
||||||
|
}
|
||||||
|
if (apromapnum == 0)
|
||||||
|
{
|
||||||
|
apromapnum = mapnum;
|
||||||
|
apromapname = realmapname;
|
||||||
|
realmapname = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else/* ...match individual keywords */
|
||||||
|
{
|
||||||
|
freq[freqc].mapnum = mapnum;
|
||||||
|
measurekeywords(&freq[freqc],
|
||||||
|
&freq[freqc].matchd, &freq[freqc].matchc,
|
||||||
|
realmapname, mapname, wanttable);
|
||||||
|
if (freq[freqc].total)
|
||||||
|
freqc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Z_Free(realmapname);/* leftover old name */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newmapnum == 0)/* no perfect match--try a substring */
|
||||||
|
{
|
||||||
|
newmapnum = apromapnum;
|
||||||
|
newmapname = apromapname;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newmapnum == 0)/* calculate most queries met! */
|
||||||
|
{
|
||||||
|
frequ = 0;
|
||||||
|
for (i = 0; i < freqc; ++i)
|
||||||
|
{
|
||||||
|
if (freq[i].total > frequ)
|
||||||
|
{
|
||||||
|
frequ = freq[i].total;
|
||||||
|
newmapnum = freq[i].mapnum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newmapnum)
|
||||||
|
{
|
||||||
|
newmapname = G_BuildMapTitle(newmapnum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (freqp)
|
||||||
|
(*freqp) = freq;
|
||||||
|
else
|
||||||
|
Z_Free(freq);
|
||||||
|
|
||||||
|
if (freqcp)
|
||||||
|
(*freqcp) = freqc;
|
||||||
|
|
||||||
|
if (foundmapnamep)
|
||||||
|
(*foundmapnamep) = newmapname;
|
||||||
|
else
|
||||||
|
Z_Free(newmapname);
|
||||||
|
|
||||||
|
return newmapnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc)
|
||||||
|
{
|
||||||
|
INT32 i;
|
||||||
|
for (i = 0; i < freqc; ++i)
|
||||||
|
{
|
||||||
|
Z_Free(freq[i].matchd);
|
||||||
|
}
|
||||||
|
Z_Free(freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep)
|
||||||
|
{
|
||||||
|
boolean usemapcode = false;
|
||||||
|
|
||||||
|
INT32 newmapnum;
|
||||||
|
|
||||||
|
size_t mapnamelen;
|
||||||
|
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
mapnamelen = strlen(mapname);
|
||||||
|
|
||||||
|
if (mapnamelen == 2)/* maybe two digit code */
|
||||||
|
{
|
||||||
|
if (( newmapnum = M_MapNumber(mapname[0], mapname[1]) ))
|
||||||
|
usemapcode = true;
|
||||||
|
}
|
||||||
|
else if (mapnamelen == 5 && strnicmp(mapname, "MAP", 3) == 0)
|
||||||
|
{
|
||||||
|
if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) ))
|
||||||
|
usemapcode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!usemapcode)
|
||||||
|
{
|
||||||
|
/* Now detect map number in base 10, which no one asked for. */
|
||||||
|
newmapnum = strtol(mapname, &p, 10);
|
||||||
|
if (*p == '\0')/* we got it */
|
||||||
|
{
|
||||||
|
if (newmapnum < 1 || newmapnum > NUMMAPS)
|
||||||
|
{
|
||||||
|
CONS_Alert(CONS_ERROR, M_GetText("Invalid map number %d.\n"), newmapnum);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
usemapcode = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newmapnum = G_FindMap(mapname, realmapnamep, NULL, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usemapcode)
|
||||||
|
{
|
||||||
|
/* we can't check mapheaderinfo for this hahahaha */
|
||||||
|
if (W_CheckNumForName(G_BuildMapName(newmapnum)) == LUMPERROR)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (realmapnamep)
|
||||||
|
(*realmapnamep) = G_BuildMapTitle(newmapnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newmapnum;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// DEMO RECORDING
|
// DEMO RECORDING
|
||||||
//
|
//
|
||||||
|
|
24
src/g_game.h
24
src/g_game.h
|
@ -173,6 +173,30 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer,
|
||||||
boolean skipprecutscene);
|
boolean skipprecutscene);
|
||||||
char *G_BuildMapTitle(INT32 mapnum);
|
char *G_BuildMapTitle(INT32 mapnum);
|
||||||
|
|
||||||
|
struct searchdim
|
||||||
|
{
|
||||||
|
UINT8 pos;
|
||||||
|
UINT8 siz;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
INT16 mapnum;
|
||||||
|
UINT8 matchc;
|
||||||
|
struct searchdim *matchd;/* offset that a pattern was matched */
|
||||||
|
UINT8 keywhc;
|
||||||
|
struct searchdim *keywhd;/* ...in KEYWORD */
|
||||||
|
UINT8 total;/* total hits */
|
||||||
|
}
|
||||||
|
mapsearchfreq_t;
|
||||||
|
|
||||||
|
INT32 G_FindMap(const char *query, char **foundmapnamep,
|
||||||
|
mapsearchfreq_t **freqp, INT32 *freqc);
|
||||||
|
void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc);
|
||||||
|
|
||||||
|
/* Match map name by search + 2 digit map code or map number. */
|
||||||
|
INT32 G_FindMapByNameOrCode(const char *query, char **foundmapnamep);
|
||||||
|
|
||||||
// XMOD spawning
|
// XMOD spawning
|
||||||
mapthing_t *G_FindCTFStart(INT32 playernum);
|
mapthing_t *G_FindCTFStart(INT32 playernum);
|
||||||
mapthing_t *G_FindMatchStart(INT32 playernum);
|
mapthing_t *G_FindMatchStart(INT32 playernum);
|
||||||
|
|
111
src/strcasestr.c
Normal file
111
src/strcasestr.c
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
strcasestr -- case insensitive substring searching function.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
Copyright 2019 James R.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source forms, with or without modification, is
|
||||||
|
permitted provided that the following condition is met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
condition and the following disclaimer.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
trycmp (char **pp, char *cp,
|
||||||
|
const char *q, size_t qn)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
p = (*pp);
|
||||||
|
if (strncasecmp(p, q, qn) == 0)
|
||||||
|
return 0;
|
||||||
|
(*pp) = strchr(&p[1], (*cp));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
swapp (char ***ppap, char ***ppbp, char **cpap, char **cpbp)
|
||||||
|
{
|
||||||
|
char **pp;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
pp = *ppap;
|
||||||
|
*ppap = *ppbp;
|
||||||
|
*ppbp = pp;
|
||||||
|
|
||||||
|
p = *cpap;
|
||||||
|
*cpap = *cpbp;
|
||||||
|
*cpbp = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
strcasestr (const char *s, const char *q)
|
||||||
|
{
|
||||||
|
size_t qn;
|
||||||
|
|
||||||
|
char uc;
|
||||||
|
char lc;
|
||||||
|
|
||||||
|
char *up;
|
||||||
|
char *lp;
|
||||||
|
|
||||||
|
char **ppa;
|
||||||
|
char **ppb;
|
||||||
|
|
||||||
|
char *cpa;
|
||||||
|
char *cpb;
|
||||||
|
|
||||||
|
uc = toupper(*q);
|
||||||
|
lc = tolower(*q);
|
||||||
|
|
||||||
|
up = strchr(s, uc);
|
||||||
|
lp = strchr(s, lc);
|
||||||
|
|
||||||
|
if (!( (intptr_t)up|(intptr_t)lp ))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!lp || ( up && up < lp ))
|
||||||
|
{
|
||||||
|
ppa = &up;
|
||||||
|
ppb = &lp;
|
||||||
|
|
||||||
|
cpa = &uc;
|
||||||
|
cpb = &lc;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ppa = &lp;
|
||||||
|
ppb = &up;
|
||||||
|
|
||||||
|
cpa = &lc;
|
||||||
|
cpb = &uc;
|
||||||
|
}
|
||||||
|
|
||||||
|
qn = strlen(q);
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (trycmp(ppa, cpa, q, qn) == 0)
|
||||||
|
return (*ppa);
|
||||||
|
|
||||||
|
if (!( (intptr_t)up|(intptr_t)lp ))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!(*ppa) || ( (*ppb) && (*ppb) < (*ppa) ))
|
||||||
|
swapp(&ppa, &ppb, &cpa, &cpb);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Copyright (C) 2006 by Graue.
|
// Copyright (C) 2006 by Graue.
|
||||||
// Copyright (C) 2006-2018 by Sonic Team Junior.
|
// Copyright (C) 2006-2018 by Sonic Team Junior.
|
||||||
|
// Copyright (C) 2019 by James R.
|
||||||
//
|
//
|
||||||
// This program is free software distributed under the
|
// This program is free software distributed under the
|
||||||
// terms of the GNU General Public License, version 2.
|
// terms of the GNU General Public License, version 2.
|
||||||
|
@ -50,3 +51,5 @@ size_t strlcpy(char *dst, const char *src, size_t siz)
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "strcasestr.c"
|
||||||
|
|
Loading…
Reference in a new issue