2006-02-24 04:48:15 +00:00
|
|
|
/*
|
|
|
|
** c_dispatch.cpp
|
|
|
|
** Functions for executing console commands and aliases
|
|
|
|
**
|
|
|
|
**---------------------------------------------------------------------------
|
2007-03-24 14:59:28 +00:00
|
|
|
** Copyright 1998-2007 Randy Heit
|
2006-02-24 04:48:15 +00:00
|
|
|
** 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.
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
|
2007-03-24 14:59:28 +00:00
|
|
|
// HEADER FILES ------------------------------------------------------------
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#include "templates.h"
|
|
|
|
#include "doomtype.h"
|
|
|
|
#include "cmdlib.h"
|
|
|
|
#include "c_console.h"
|
|
|
|
#include "c_dispatch.h"
|
|
|
|
#include "m_argv.h"
|
|
|
|
#include "doomstat.h"
|
|
|
|
#include "d_player.h"
|
|
|
|
#include "configfile.h"
|
|
|
|
#include "m_crc32.h"
|
|
|
|
#include "v_text.h"
|
2008-09-14 23:54:38 +00:00
|
|
|
#include "d_net.h"
|
2009-05-31 10:49:47 +00:00
|
|
|
#include "d_main.h"
|
2011-07-06 14:20:54 +00:00
|
|
|
#include "farchive.h"
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2007-03-24 14:59:28 +00:00
|
|
|
// MACROS ------------------------------------------------------------------
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2007-03-24 14:59:28 +00:00
|
|
|
// TYPES -------------------------------------------------------------------
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
class DWaitingCommand : public DThinker
|
|
|
|
{
|
|
|
|
DECLARE_CLASS (DWaitingCommand, DThinker)
|
|
|
|
public:
|
|
|
|
DWaitingCommand (const char *cmd, int tics);
|
|
|
|
~DWaitingCommand ();
|
|
|
|
void Serialize (FArchive &arc);
|
|
|
|
void Tick ();
|
|
|
|
|
|
|
|
private:
|
|
|
|
DWaitingCommand ();
|
|
|
|
|
|
|
|
char *Command;
|
|
|
|
int TicsLeft;
|
|
|
|
};
|
|
|
|
|
|
|
|
class DStoredCommand : public DThinker
|
|
|
|
{
|
|
|
|
DECLARE_CLASS (DStoredCommand, DThinker)
|
|
|
|
public:
|
|
|
|
DStoredCommand (FConsoleCommand *com, const char *cmd);
|
|
|
|
~DStoredCommand ();
|
|
|
|
void Tick ();
|
|
|
|
|
|
|
|
private:
|
|
|
|
DStoredCommand ();
|
|
|
|
|
|
|
|
FConsoleCommand *Command;
|
|
|
|
char *Text;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct FActionMap
|
|
|
|
{
|
|
|
|
FButtonStatus *Button;
|
2013-07-02 02:51:12 +00:00
|
|
|
unsigned int Key; // value from passing Name to MakeKey()
|
2006-02-24 04:48:15 +00:00
|
|
|
char Name[12];
|
|
|
|
};
|
|
|
|
|
2007-03-24 14:59:28 +00:00
|
|
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
|
|
|
|
|
|
|
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
|
|
|
|
|
|
|
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
|
|
|
|
2008-05-09 03:54:06 +00:00
|
|
|
static long ParseCommandLine (const char *args, int *argc, char **argv, bool no_escapes);
|
2006-02-24 04:48:15 +00:00
|
|
|
static FConsoleCommand *FindNameInHashTable (FConsoleCommand **table, const char *name, size_t namelen);
|
|
|
|
static FConsoleCommand *ScanChainForName (FConsoleCommand *start, const char *name, size_t namelen, FConsoleCommand **prev);
|
|
|
|
|
2007-03-24 14:59:28 +00:00
|
|
|
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2007-03-24 14:59:28 +00:00
|
|
|
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
|
|
|
|
|
|
|
CVAR (Bool, lookspring, true, CVAR_ARCHIVE); // Generate centerview when -mlook encountered?
|
|
|
|
|
|
|
|
FConsoleCommand *Commands[FConsoleCommand::HASH_SIZE];
|
2006-02-24 04:48:15 +00:00
|
|
|
FButtonStatus Button_Mlook, Button_Klook, Button_Use, Button_AltAttack,
|
|
|
|
Button_Attack, Button_Speed, Button_MoveRight, Button_MoveLeft,
|
|
|
|
Button_Strafe, Button_LookDown, Button_LookUp, Button_Back,
|
|
|
|
Button_Forward, Button_Right, Button_Left, Button_MoveDown,
|
2008-09-13 02:55:45 +00:00
|
|
|
Button_MoveUp, Button_Jump, Button_ShowScores, Button_Crouch,
|
|
|
|
Button_Zoom, Button_Reload,
|
2010-08-27 15:20:05 +00:00
|
|
|
Button_User1, Button_User2, Button_User3, Button_User4,
|
|
|
|
Button_AM_PanLeft, Button_AM_PanRight, Button_AM_PanDown, Button_AM_PanUp,
|
|
|
|
Button_AM_ZoomIn, Button_AM_ZoomOut;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2007-03-24 14:59:28 +00:00
|
|
|
bool ParsingKeyConf;
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
// To add new actions, go to the console and type "key <action name>".
|
|
|
|
// This will give you the key value to use in the first column. Then
|
|
|
|
// insert your new action into this list so that the keys remain sorted
|
|
|
|
// in ascending order. No two keys can be identical. If yours matches
|
2007-03-24 14:59:28 +00:00
|
|
|
// an existing key, change the name of your action.
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
FActionMap ActionMaps[] =
|
|
|
|
{
|
2013-07-02 02:51:12 +00:00
|
|
|
{ &Button_AM_PanLeft, 0x0d52d67b, "am_panleft"},
|
|
|
|
{ &Button_User2, 0x125f5226, "user2" },
|
|
|
|
{ &Button_Jump, 0x1eefa611, "jump" },
|
|
|
|
{ &Button_Right, 0x201f1c55, "right" },
|
|
|
|
{ &Button_Zoom, 0x20ccc4d5, "zoom" },
|
|
|
|
{ &Button_Back, 0x23a99cd7, "back" },
|
|
|
|
{ &Button_AM_ZoomIn, 0x41df90c2, "am_zoomin"},
|
|
|
|
{ &Button_Reload, 0x426b69e7, "reload" },
|
|
|
|
{ &Button_LookDown, 0x4463f43a, "lookdown" },
|
|
|
|
{ &Button_AM_ZoomOut, 0x51f7a334, "am_zoomout"},
|
|
|
|
{ &Button_User4, 0x534c30ee, "user4" },
|
|
|
|
{ &Button_Attack, 0x5622bf42, "attack" },
|
|
|
|
{ &Button_User1, 0x577712d0, "user1" },
|
|
|
|
{ &Button_Klook, 0x57c25cb2, "klook" },
|
|
|
|
{ &Button_Forward, 0x59f3e907, "forward" },
|
|
|
|
{ &Button_MoveDown, 0x6167ce99, "movedown" },
|
|
|
|
{ &Button_AltAttack, 0x676885b8, "altattack" },
|
|
|
|
{ &Button_MoveLeft, 0x6fa41b84, "moveleft" },
|
|
|
|
{ &Button_MoveRight, 0x818f08e6, "moveright" },
|
|
|
|
{ &Button_AM_PanRight, 0x8197097b, "am_panright"},
|
|
|
|
{ &Button_AM_PanUp, 0x8d89955e, "am_panup"} ,
|
|
|
|
{ &Button_Mlook, 0xa2b62d8b, "mlook" },
|
|
|
|
{ &Button_Crouch, 0xab2c3e71, "crouch" },
|
|
|
|
{ &Button_Left, 0xb000b483, "left" },
|
|
|
|
{ &Button_LookUp, 0xb62b1e49, "lookup" },
|
|
|
|
{ &Button_User3, 0xb6f8fe92, "user3" },
|
|
|
|
{ &Button_Strafe, 0xb7e6a54b, "strafe" },
|
|
|
|
{ &Button_AM_PanDown, 0xce301c81, "am_pandown"},
|
|
|
|
{ &Button_ShowScores, 0xd5897c73, "showscores" },
|
|
|
|
{ &Button_Speed, 0xe0ccb317, "speed" },
|
|
|
|
{ &Button_Use, 0xe0cfc260, "use" },
|
|
|
|
{ &Button_MoveUp, 0xfdd701c7, "moveup" },
|
2006-02-24 04:48:15 +00:00
|
|
|
};
|
2006-05-09 00:02:37 +00:00
|
|
|
#define NUM_ACTIONS countof(ActionMaps)
|
|
|
|
|
2010-08-27 15:20:05 +00:00
|
|
|
|
2007-03-24 14:59:28 +00:00
|
|
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
2006-05-09 00:02:37 +00:00
|
|
|
|
2007-03-24 14:59:28 +00:00
|
|
|
static const char *KeyConfCommands[] =
|
|
|
|
{
|
|
|
|
"alias",
|
|
|
|
"defaultbind",
|
|
|
|
"addkeysection",
|
|
|
|
"addmenukey",
|
|
|
|
"addslotdefault",
|
|
|
|
"weaponsection",
|
|
|
|
"setslot",
|
|
|
|
"addplayerclass",
|
|
|
|
"clearplayerclasses"
|
|
|
|
};
|
|
|
|
|
2014-12-26 23:21:57 +00:00
|
|
|
static TArray<FString> StoredStartupSets;
|
|
|
|
static bool RunningStoredStartups;
|
|
|
|
|
2007-03-24 14:59:28 +00:00
|
|
|
// CODE --------------------------------------------------------------------
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
IMPLEMENT_CLASS (DWaitingCommand)
|
|
|
|
|
|
|
|
void DWaitingCommand::Serialize (FArchive &arc)
|
|
|
|
{
|
|
|
|
Super::Serialize (arc);
|
|
|
|
arc << Command << TicsLeft;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWaitingCommand::DWaitingCommand ()
|
|
|
|
{
|
|
|
|
Command = NULL;
|
|
|
|
TicsLeft = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWaitingCommand::DWaitingCommand (const char *cmd, int tics)
|
|
|
|
{
|
|
|
|
Command = copystring (cmd);
|
|
|
|
TicsLeft = tics+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWaitingCommand::~DWaitingCommand ()
|
|
|
|
{
|
|
|
|
if (Command != NULL)
|
|
|
|
{
|
|
|
|
delete[] Command;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DWaitingCommand::Tick ()
|
|
|
|
{
|
|
|
|
if (--TicsLeft == 0)
|
|
|
|
{
|
|
|
|
AddCommandString (Command);
|
|
|
|
Destroy ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
IMPLEMENT_CLASS (DStoredCommand)
|
|
|
|
|
|
|
|
DStoredCommand::DStoredCommand ()
|
|
|
|
{
|
|
|
|
Text = NULL;
|
|
|
|
Destroy ();
|
|
|
|
}
|
|
|
|
|
|
|
|
DStoredCommand::DStoredCommand (FConsoleCommand *command, const char *args)
|
|
|
|
{
|
|
|
|
Command = command;
|
|
|
|
Text = copystring (args);
|
|
|
|
}
|
|
|
|
|
|
|
|
DStoredCommand::~DStoredCommand ()
|
|
|
|
{
|
|
|
|
if (Text != NULL)
|
|
|
|
{
|
|
|
|
delete[] Text;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DStoredCommand::Tick ()
|
|
|
|
{
|
|
|
|
if (Text != NULL && Command != NULL)
|
|
|
|
{
|
|
|
|
FCommandLine args (Text);
|
|
|
|
Command->Run (args, players[consoleplayer].mo, 0);
|
|
|
|
}
|
|
|
|
Destroy ();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ListActionCommands (const char *pattern)
|
|
|
|
{
|
|
|
|
char matcher[16];
|
|
|
|
unsigned int i;
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_ACTIONS; ++i)
|
|
|
|
{
|
|
|
|
if (pattern == NULL || CheckWildcards (pattern,
|
About a week's worth of changes here. As a heads-up, I wouldn't be
surprised if this doesn't build in Linux right now. The CMakeLists.txt
were checked with MinGW and NMake, but how they fair under Linux is an
unknown to me at this time.
- Converted most sprintf (and all wsprintf) calls to either mysnprintf or
FStrings, depending on the situation.
- Changed the strings in the wbstartstruct to be FStrings.
- Changed myvsnprintf() to output nothing if count is greater than INT_MAX.
This is so that I can use a series of mysnprintf() calls and advance the
pointer for each one. Once the pointer goes beyond the end of the buffer,
the count will go negative, but since it's an unsigned type it will be
seen as excessively huge instead. This should not be a problem, as there's
no reason for ZDoom to be using text buffers larger than 2 GB anywhere.
- Ripped out the disabled bit from FGameConfigFile::MigrateOldConfig().
- Changed CalcMapName() to return an FString instead of a pointer to a static
buffer.
- Changed startmap in d_main.cpp into an FString.
- Changed CheckWarpTransMap() to take an FString& as the first argument.
- Changed d_mapname in g_level.cpp into an FString.
- Changed DoSubstitution() in ct_chat.cpp to place the substitutions in an
FString.
- Fixed: The MAPINFO parser wrote into the string buffer to construct a map
name when given a Hexen map number. This was fine with the old scanner
code, but only a happy coincidence prevents it from crashing with the new
code
- Added the 'B' conversion specifier to StringFormat::VWorker() for printing
binary numbers.
- Added CMake support for building with MinGW, MSYS, and NMake. Linux support
is probably broken until I get around to booting into Linux again. Niceties
provided over the existing Makefiles they're replacing:
* All command-line builds can use the same build system, rather than having
a separate one for MinGW and another for Linux.
* Microsoft's NMake tool is supported as a target.
* Progress meters.
* Parallel makes work from a fresh checkout without needing to be primed
first with a single-threaded make.
* Porting to other architectures should be simplified, whenever that day
comes.
- Replaced the makewad tool with zipdir. This handles the dependency tracking
itself instead of generating an external makefile to do it, since I couldn't
figure out how to generate a makefile with an external tool and include it
with a CMake-generated makefile. Where makewad used a master list of files
to generate the package file, zipdir just zips the entire contents of one or
more directories.
- Added the gdtoa package from netlib's fp library so that ZDoom's printf-style
formatting can be entirely independant of the CRT.
SVN r1082 (trunk)
2008-07-23 04:57:26 +00:00
|
|
|
(mysnprintf (matcher, countof(matcher), "+%s", ActionMaps[i].Name), matcher)))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
Printf ("+%s\n", ActionMaps[i].Name);
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
if (pattern == NULL || CheckWildcards (pattern,
|
About a week's worth of changes here. As a heads-up, I wouldn't be
surprised if this doesn't build in Linux right now. The CMakeLists.txt
were checked with MinGW and NMake, but how they fair under Linux is an
unknown to me at this time.
- Converted most sprintf (and all wsprintf) calls to either mysnprintf or
FStrings, depending on the situation.
- Changed the strings in the wbstartstruct to be FStrings.
- Changed myvsnprintf() to output nothing if count is greater than INT_MAX.
This is so that I can use a series of mysnprintf() calls and advance the
pointer for each one. Once the pointer goes beyond the end of the buffer,
the count will go negative, but since it's an unsigned type it will be
seen as excessively huge instead. This should not be a problem, as there's
no reason for ZDoom to be using text buffers larger than 2 GB anywhere.
- Ripped out the disabled bit from FGameConfigFile::MigrateOldConfig().
- Changed CalcMapName() to return an FString instead of a pointer to a static
buffer.
- Changed startmap in d_main.cpp into an FString.
- Changed CheckWarpTransMap() to take an FString& as the first argument.
- Changed d_mapname in g_level.cpp into an FString.
- Changed DoSubstitution() in ct_chat.cpp to place the substitutions in an
FString.
- Fixed: The MAPINFO parser wrote into the string buffer to construct a map
name when given a Hexen map number. This was fine with the old scanner
code, but only a happy coincidence prevents it from crashing with the new
code
- Added the 'B' conversion specifier to StringFormat::VWorker() for printing
binary numbers.
- Added CMake support for building with MinGW, MSYS, and NMake. Linux support
is probably broken until I get around to booting into Linux again. Niceties
provided over the existing Makefiles they're replacing:
* All command-line builds can use the same build system, rather than having
a separate one for MinGW and another for Linux.
* Microsoft's NMake tool is supported as a target.
* Progress meters.
* Parallel makes work from a fresh checkout without needing to be primed
first with a single-threaded make.
* Porting to other architectures should be simplified, whenever that day
comes.
- Replaced the makewad tool with zipdir. This handles the dependency tracking
itself instead of generating an external makefile to do it, since I couldn't
figure out how to generate a makefile with an external tool and include it
with a CMake-generated makefile. Where makewad used a master list of files
to generate the package file, zipdir just zips the entire contents of one or
more directories.
- Added the gdtoa package from netlib's fp library so that ZDoom's printf-style
formatting can be entirely independant of the CRT.
SVN r1082 (trunk)
2008-07-23 04:57:26 +00:00
|
|
|
(mysnprintf (matcher, countof(matcher), "-%s", ActionMaps[i].Name), matcher)))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
Printf ("-%s\n", ActionMaps[i].Name);
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2007-03-29 00:35:34 +00:00
|
|
|
/* ======================================================================== */
|
|
|
|
|
|
|
|
/* By Paul Hsieh (C) 2004, 2005. Covered under the Paul Hsieh derivative
|
|
|
|
license. See:
|
|
|
|
http://www.azillionmonkeys.com/qed/weblicense.html for license details.
|
|
|
|
|
|
|
|
http://www.azillionmonkeys.com/qed/hash.html */
|
|
|
|
|
|
|
|
#undef get16bits
|
|
|
|
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|
|
|
|
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
|
|
|
|
#define get16bits(d) (*((const WORD *) (d)))
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined (get16bits)
|
|
|
|
#define get16bits(d) ((((DWORD)(((const BYTE *)(d))[1])) << 8)\
|
|
|
|
+(DWORD)(((const BYTE *)(d))[0]) )
|
|
|
|
#endif
|
|
|
|
|
|
|
|
DWORD SuperFastHash (const char *data, size_t len)
|
|
|
|
{
|
|
|
|
DWORD hash = 0, tmp;
|
|
|
|
size_t rem;
|
|
|
|
|
|
|
|
if (len == 0 || data == NULL) return 0;
|
|
|
|
|
|
|
|
rem = len & 3;
|
|
|
|
len >>= 2;
|
|
|
|
|
|
|
|
/* Main loop */
|
|
|
|
for (;len > 0; len--)
|
|
|
|
{
|
|
|
|
hash += get16bits (data);
|
|
|
|
tmp = (get16bits (data+2) << 11) ^ hash;
|
|
|
|
hash = (hash << 16) ^ tmp;
|
|
|
|
data += 2*sizeof (WORD);
|
|
|
|
hash += hash >> 11;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle end cases */
|
|
|
|
switch (rem)
|
|
|
|
{
|
|
|
|
case 3: hash += get16bits (data);
|
|
|
|
hash ^= hash << 16;
|
|
|
|
hash ^= data[sizeof (WORD)] << 18;
|
|
|
|
hash += hash >> 11;
|
|
|
|
break;
|
|
|
|
case 2: hash += get16bits (data);
|
|
|
|
hash ^= hash << 11;
|
|
|
|
hash += hash >> 17;
|
|
|
|
break;
|
|
|
|
case 1: hash += *data;
|
|
|
|
hash ^= hash << 10;
|
|
|
|
hash += hash >> 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Force "avalanching" of final 127 bits */
|
|
|
|
hash ^= hash << 3;
|
|
|
|
hash += hash >> 5;
|
|
|
|
hash ^= hash << 4;
|
|
|
|
hash += hash >> 17;
|
|
|
|
hash ^= hash << 25;
|
|
|
|
hash += hash >> 6;
|
|
|
|
|
|
|
|
return hash;
|
2007-03-24 14:59:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* A modified version to do a case-insensitive hash */
|
|
|
|
|
|
|
|
#undef get16bits
|
2007-03-29 00:35:34 +00:00
|
|
|
#define get16bits(d) ((((DWORD)tolower(((const BYTE *)(d))[1])) << 8)\
|
|
|
|
+(DWORD)tolower(((const BYTE *)(d))[0]) )
|
|
|
|
|
|
|
|
DWORD SuperFastHashI (const char *data, size_t len)
|
|
|
|
{
|
|
|
|
DWORD hash = 0, tmp;
|
|
|
|
size_t rem;
|
|
|
|
|
|
|
|
if (len <= 0 || data == NULL) return 0;
|
|
|
|
|
|
|
|
rem = len & 3;
|
|
|
|
len >>= 2;
|
|
|
|
|
|
|
|
/* Main loop */
|
|
|
|
for (;len > 0; len--)
|
|
|
|
{
|
|
|
|
hash += get16bits (data);
|
|
|
|
tmp = (get16bits (data+2) << 11) ^ hash;
|
|
|
|
hash = (hash << 16) ^ tmp;
|
|
|
|
data += 2*sizeof (WORD);
|
|
|
|
hash += hash >> 11;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle end cases */
|
|
|
|
switch (rem)
|
|
|
|
{
|
|
|
|
case 3: hash += get16bits (data);
|
|
|
|
hash ^= hash << 16;
|
|
|
|
hash ^= tolower(data[sizeof (WORD)]) << 18;
|
|
|
|
hash += hash >> 11;
|
|
|
|
break;
|
|
|
|
case 2: hash += get16bits (data);
|
|
|
|
hash ^= hash << 11;
|
|
|
|
hash += hash >> 17;
|
|
|
|
break;
|
|
|
|
case 1: hash += tolower(*data);
|
|
|
|
hash ^= hash << 10;
|
|
|
|
hash += hash >> 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Force "avalanching" of final 127 bits */
|
|
|
|
hash ^= hash << 3;
|
|
|
|
hash += hash >> 5;
|
|
|
|
hash ^= hash << 4;
|
|
|
|
hash += hash >> 17;
|
|
|
|
hash ^= hash << 25;
|
|
|
|
hash += hash >> 6;
|
|
|
|
|
|
|
|
return hash;
|
2007-03-24 14:59:28 +00:00
|
|
|
}
|
|
|
|
|
2007-03-29 00:35:34 +00:00
|
|
|
/* ======================================================================== */
|
2007-03-24 14:59:28 +00:00
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
unsigned int MakeKey (const char *s)
|
|
|
|
{
|
|
|
|
if (s == NULL)
|
|
|
|
{
|
2007-03-24 14:59:28 +00:00
|
|
|
return 0;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
2007-03-24 14:59:28 +00:00
|
|
|
return SuperFastHashI (s, strlen (s));
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int MakeKey (const char *s, size_t len)
|
|
|
|
{
|
2007-03-24 14:59:28 +00:00
|
|
|
return SuperFastHashI (s, len);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FindButton scans through the actionbits[] array
|
|
|
|
// for a matching key and returns an index or -1 if
|
|
|
|
// the key could not be found. This uses binary search,
|
|
|
|
// so actionbits[] must be sorted in ascending order.
|
|
|
|
|
|
|
|
FButtonStatus *FindButton (unsigned int key)
|
|
|
|
{
|
|
|
|
const FActionMap *bit;
|
|
|
|
|
|
|
|
bit = BinarySearch<FActionMap, unsigned int>
|
|
|
|
(ActionMaps, NUM_ACTIONS, &FActionMap::Key, key);
|
|
|
|
return bit ? bit->Button : NULL;
|
|
|
|
}
|
|
|
|
|
2009-07-26 03:25:18 +00:00
|
|
|
bool FButtonStatus::PressKey (int keynum)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
int i, open;
|
|
|
|
|
|
|
|
keynum &= KEY_DBLCLICKED-1;
|
|
|
|
|
|
|
|
if (keynum == 0)
|
|
|
|
{ // Issued from console instead of a key, so force on
|
|
|
|
Keys[0] = 0xffff;
|
|
|
|
for (i = MAX_KEYS-1; i > 0; --i)
|
|
|
|
{
|
|
|
|
Keys[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (i = MAX_KEYS-1, open = -1; i >= 0; --i)
|
|
|
|
{
|
|
|
|
if (Keys[i] == 0)
|
|
|
|
{
|
|
|
|
open = i;
|
|
|
|
}
|
|
|
|
else if (Keys[i] == keynum)
|
|
|
|
{ // Key is already down; do nothing
|
2009-07-26 03:25:18 +00:00
|
|
|
return false;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (open < 0)
|
|
|
|
{ // No free key slots, so do nothing
|
|
|
|
Printf ("More than %u keys pressed for a single action!\n", MAX_KEYS);
|
2009-07-26 03:25:18 +00:00
|
|
|
return false;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
Keys[open] = keynum;
|
|
|
|
}
|
2009-07-26 03:25:18 +00:00
|
|
|
BYTE wasdown = bDown;
|
2006-02-24 04:48:15 +00:00
|
|
|
bDown = bWentDown = true;
|
2009-07-26 03:25:18 +00:00
|
|
|
// Returns true if this key caused the button to go down.
|
|
|
|
return !wasdown;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2009-07-26 03:25:18 +00:00
|
|
|
bool FButtonStatus::ReleaseKey (int keynum)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
int i, numdown, match;
|
2009-07-26 03:25:18 +00:00
|
|
|
BYTE wasdown = bDown;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
keynum &= KEY_DBLCLICKED-1;
|
|
|
|
|
|
|
|
if (keynum == 0)
|
|
|
|
{ // Issued from console instead of a key, so force off
|
|
|
|
for (i = MAX_KEYS-1; i >= 0; --i)
|
|
|
|
{
|
|
|
|
Keys[i] = 0;
|
|
|
|
}
|
|
|
|
bWentUp = true;
|
|
|
|
bDown = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (i = MAX_KEYS-1, numdown = 0, match = -1; i >= 0; --i)
|
|
|
|
{
|
|
|
|
if (Keys[i] != 0)
|
|
|
|
{
|
|
|
|
++numdown;
|
|
|
|
if (Keys[i] == keynum)
|
|
|
|
{
|
|
|
|
match = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (match < 0)
|
|
|
|
{ // Key was not down; do nothing
|
2009-07-26 03:25:18 +00:00
|
|
|
return false;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
Keys[match] = 0;
|
|
|
|
bWentUp = true;
|
|
|
|
if (--numdown == 0)
|
|
|
|
{
|
|
|
|
bDown = false;
|
|
|
|
}
|
|
|
|
}
|
2009-07-26 03:25:18 +00:00
|
|
|
// Returns true if releasing this key caused the button to go up.
|
|
|
|
return wasdown && !bDown;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ResetButtonTriggers ()
|
|
|
|
{
|
|
|
|
for (int i = NUM_ACTIONS-1; i >= 0; --i)
|
|
|
|
{
|
|
|
|
ActionMaps[i].Button->ResetTriggers ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ResetButtonStates ()
|
|
|
|
{
|
|
|
|
for (int i = NUM_ACTIONS-1; i >= 0; --i)
|
|
|
|
{
|
|
|
|
FButtonStatus *button = ActionMaps[i].Button;
|
|
|
|
|
|
|
|
if (button != &Button_Mlook && button != &Button_Klook)
|
|
|
|
{
|
|
|
|
button->ReleaseKey (0);
|
|
|
|
}
|
|
|
|
button->ResetTriggers ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-26 23:21:57 +00:00
|
|
|
void C_ExecStoredSets()
|
|
|
|
{
|
|
|
|
assert(!RunningStoredStartups);
|
|
|
|
RunningStoredStartups = true;
|
|
|
|
for (unsigned i = 0; i < StoredStartupSets.Size(); ++i)
|
|
|
|
{
|
|
|
|
C_DoCommand(StoredStartupSets[i]);
|
|
|
|
}
|
|
|
|
StoredStartupSets.Clear();
|
|
|
|
RunningStoredStartups = false;
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
void C_DoCommand (const char *cmd, int keynum)
|
|
|
|
{
|
|
|
|
FConsoleCommand *com;
|
|
|
|
const char *end;
|
|
|
|
const char *beg;
|
|
|
|
|
|
|
|
// Skip any beginning whitespace
|
|
|
|
while (*cmd && *cmd <= ' ')
|
|
|
|
cmd++;
|
|
|
|
|
|
|
|
// Find end of the command name
|
|
|
|
if (*cmd == '\"')
|
|
|
|
{
|
|
|
|
for (end = beg = cmd+1; *end && *end != '\"'; ++end)
|
|
|
|
;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
beg = cmd;
|
|
|
|
for (end = cmd+1; *end > ' '; ++end)
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
2009-05-15 10:39:40 +00:00
|
|
|
const size_t len = end - beg;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (ParsingKeyConf)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
- Fixed compilation with mingw again.
- Added multiple-choice sound sequences. These overcome one of the major
deficiences of the Hexen-inherited SNDSEQ system while still being Hexen
compatible: Custom door sounds can now use different opening and closing
sequences, for both normal and blazing speeds.
- Added a serializer for TArray.
- Added a countof macro to doomtype.h. See the1's blog to find out why
it's implemented the way it is.
<http://blogs.msdn.com/the1/articles/210011.aspx>
- Added a new method to FRandom for getting random numbers larger than 255,
which lets me:
- Fixed: SNDSEQ delayrand commands could delay for no more than 255 tics.
- Fixed: If you're going to have sector_t.SoundTarget, then they need to
be included in the pointer cleanup scans.
- Ported back newer name code from 2.1.
- Fixed: Using -warp with only one parameter in Doom and Heretic to
select a map on episode 1 no longer worked.
- New: Loading a multiplayer save now restores the players based on
their names rather than on their connection order. Using connection
order was sensible when -net was the only way to start a network game,
but with -host/-join, it's not so nice. Also, if there aren't enough
players in the save, then the extra players will be spawned normally,
so you can continue a saved game with more players than you started it
with.
- Added some new SNDSEQ commands to make it possible to define Heretic's
ambient sounds in SNDSEQ: volumerel, volumerand, slot, randomsequence,
delayonce, and restart. With these, it is basically possible to obsolete
all of the $ambient SNDINFO commands.
- Fixed: Sound sequences would only execute one command each time they were
ticked.
- Fixed: No bounds checking was done on the volume sound sequences played at.
- Fixed: The tic parameter to playloop was useless and caused it to
act like a redundant playrepeat. I have removed all the logic that
caused playloop to play repeating sounds, and now it acts like an
infinite sequence of play/delay commands until the sequence is
stopped.
- Fixed: Sound sequences were ticked every frame, not every tic, so all
the delay commands were timed incorrectly and varied depending on your
framerate. Since this is useful for restarting looping sounds that got
cut off, I have not changed this. Instead, the delay commands now
record the tic when execution should resume, not the number of tics
left to delay.
SVN r57 (trunk)
2006-04-21 01:22:55 +00:00
|
|
|
for (i = countof(KeyConfCommands)-1; i >= 0; --i)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (strnicmp (beg, KeyConfCommands[i], len) == 0 &&
|
|
|
|
KeyConfCommands[i][len] == 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i < 0)
|
|
|
|
{
|
|
|
|
Printf ("Invalid command for KEYCONF: %s\n", beg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if this is an action
|
|
|
|
if (*beg == '+' || *beg == '-')
|
|
|
|
{
|
|
|
|
FButtonStatus *button;
|
|
|
|
|
|
|
|
button = FindButton (MakeKey (beg + 1, end - beg - 1));
|
|
|
|
if (button != NULL)
|
|
|
|
{
|
|
|
|
if (*beg == '+')
|
|
|
|
{
|
|
|
|
button->PressKey (keynum);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
button->ReleaseKey (keynum);
|
|
|
|
if (button == &Button_Mlook && lookspring)
|
|
|
|
{
|
|
|
|
Net_WriteByte (DEM_CENTERVIEW);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse it as a normal command
|
|
|
|
// Checking for matching commands follows this search order:
|
|
|
|
// 1. Check the Commands[] hash table
|
|
|
|
// 2. Check the CVars list
|
|
|
|
|
|
|
|
if ( (com = FindNameInHashTable (Commands, beg, len)) )
|
|
|
|
{
|
2014-12-26 23:21:57 +00:00
|
|
|
if (gamestate == GS_STARTUP && !RunningStoredStartups &&
|
|
|
|
len == 3 && strnicmp(beg, "set", 3) == 0)
|
|
|
|
{
|
|
|
|
// Save setting of unknown cvars for later, in case a loaded wad has a
|
|
|
|
// CVARINFO that defines it.
|
|
|
|
FCommandLine args(beg);
|
|
|
|
if (args.argc() > 1 && FindCVar(args[1], NULL) == NULL)
|
|
|
|
{
|
|
|
|
StoredStartupSets.Push(beg);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
com->Run(args, players[consoleplayer].mo, keynum);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (gamestate != GS_STARTUP || ParsingKeyConf ||
|
2006-02-24 04:48:15 +00:00
|
|
|
(len == 3 && strnicmp (beg, "set", 3) == 0) ||
|
|
|
|
(len == 7 && strnicmp (beg, "logfile", 7) == 0) ||
|
|
|
|
(len == 9 && strnicmp (beg, "unbindall", 9) == 0) ||
|
|
|
|
(len == 4 && strnicmp (beg, "bind", 4) == 0) ||
|
|
|
|
(len == 4 && strnicmp (beg, "exec", 4) == 0) ||
|
|
|
|
(len ==10 && strnicmp (beg, "doublebind", 10) == 0) ||
|
|
|
|
(len == 6 && strnicmp (beg, "pullin", 6) == 0)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
FCommandLine args (beg);
|
|
|
|
com->Run (args, players[consoleplayer].mo, keynum);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-10-10 03:11:33 +00:00
|
|
|
if (len == 4 && strnicmp(beg, "warp", 4) == 0)
|
|
|
|
{
|
|
|
|
StoredWarp = beg;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
new DStoredCommand (com, beg);
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // Check for any console vars that match the command
|
2009-05-15 10:39:40 +00:00
|
|
|
FBaseCVar *var = FindCVarSub (beg, int(len));
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
if (var != NULL)
|
|
|
|
{
|
|
|
|
FCommandLine args (beg);
|
|
|
|
|
|
|
|
if (args.argc() >= 2)
|
|
|
|
{ // Set the variable
|
|
|
|
var->CmdSet (args[1]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // Get the variable's value
|
|
|
|
UCVarValue val = var->GetGenericRep (CVAR_String);
|
|
|
|
Printf ("\"%s\" is \"%s\"\n", var->GetName(), val.String);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // We don't know how to handle this command
|
2014-12-26 23:21:57 +00:00
|
|
|
if (gamestate == GS_STARTUP && !RunningStoredStartups)
|
|
|
|
{
|
|
|
|
// Save it for later, in case a CVARINFO defines it.
|
|
|
|
StoredStartupSets.Push(beg);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-02-07 14:27:31 +00:00
|
|
|
Printf ("Unknown command \"%.*s\"\n", (int)len, beg);
|
2014-12-26 23:21:57 +00:00
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AddCommandString (char *cmd, int keynum)
|
|
|
|
{
|
|
|
|
char *brkpt;
|
|
|
|
int more;
|
|
|
|
|
|
|
|
if (cmd)
|
|
|
|
{
|
|
|
|
while (*cmd)
|
|
|
|
{
|
|
|
|
brkpt = cmd;
|
|
|
|
while (*brkpt != ';' && *brkpt != '\0')
|
|
|
|
{
|
|
|
|
if (*brkpt == '\"')
|
|
|
|
{
|
|
|
|
brkpt++;
|
|
|
|
while (*brkpt != '\0' && (*brkpt != '\"' || *(brkpt-1) == '\\'))
|
|
|
|
brkpt++;
|
|
|
|
}
|
|
|
|
if (*brkpt != '\0')
|
|
|
|
brkpt++;
|
|
|
|
}
|
|
|
|
if (*brkpt == ';')
|
|
|
|
{
|
|
|
|
*brkpt = '\0';
|
|
|
|
more = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
more = 0;
|
|
|
|
}
|
|
|
|
// Intercept wait commands here. Note: wait must be lowercase
|
|
|
|
while (*cmd && *cmd <= ' ')
|
|
|
|
cmd++;
|
|
|
|
if (*cmd)
|
|
|
|
{
|
|
|
|
if (!ParsingKeyConf &&
|
|
|
|
cmd[0] == 'w' && cmd[1] == 'a' && cmd[2] == 'i' && cmd[3] == 't' &&
|
|
|
|
(cmd[4] == 0 || cmd[4] == ' '))
|
|
|
|
{
|
|
|
|
int tics;
|
|
|
|
|
|
|
|
if (cmd[4] == ' ')
|
|
|
|
{
|
|
|
|
tics = strtol (cmd + 5, NULL, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tics = 1;
|
|
|
|
}
|
|
|
|
if (tics > 0)
|
|
|
|
{
|
|
|
|
if (more)
|
|
|
|
{ // The remainder of the command will be executed later
|
|
|
|
// Note that deferred commands lose track of which key
|
|
|
|
// (if any) they were pressed from.
|
|
|
|
*brkpt = ';';
|
2014-08-25 08:51:50 +00:00
|
|
|
new DWaitingCommand (brkpt, tics);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
C_DoCommand (cmd, keynum);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (more)
|
|
|
|
{
|
|
|
|
*brkpt = ';';
|
|
|
|
}
|
|
|
|
cmd = brkpt + more;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseCommandLine
|
|
|
|
//
|
|
|
|
// Parse a command line (passed in args). If argc is non-NULL, it will
|
|
|
|
// be set to the number of arguments. If argv is non-NULL, it will be
|
|
|
|
// filled with pointers to each argument; argv[0] should be initialized
|
|
|
|
// to point to a buffer large enough to hold all the arguments. The
|
|
|
|
// return value is the necessary size of this buffer.
|
|
|
|
//
|
2007-12-15 03:27:40 +00:00
|
|
|
// Special processing:
|
|
|
|
// Inside quoted strings, \" becomes just "
|
2007-12-26 05:03:14 +00:00
|
|
|
// \\ becomes just a single backslash
|
2007-12-15 03:27:40 +00:00
|
|
|
// \c becomes just TEXTCOLOR_ESCAPE
|
|
|
|
// $<cvar> is replaced by the contents of <cvar>
|
2006-02-24 04:48:15 +00:00
|
|
|
|
2008-05-09 03:54:06 +00:00
|
|
|
static long ParseCommandLine (const char *args, int *argc, char **argv, bool no_escapes)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
int count;
|
|
|
|
char *buffplace;
|
|
|
|
|
|
|
|
count = 0;
|
|
|
|
buffplace = NULL;
|
|
|
|
if (argv != NULL)
|
|
|
|
{
|
|
|
|
buffplace = argv[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
while (*args <= ' ' && *args)
|
|
|
|
{ // skip white space
|
|
|
|
args++;
|
|
|
|
}
|
|
|
|
if (*args == 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (*args == '\"')
|
|
|
|
{ // read quoted string
|
|
|
|
char stuff;
|
|
|
|
if (argv != NULL)
|
|
|
|
{
|
|
|
|
argv[count] = buffplace;
|
|
|
|
}
|
|
|
|
count++;
|
|
|
|
args++;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
stuff = *args++;
|
2008-05-09 03:54:06 +00:00
|
|
|
if (!no_escapes && stuff == '\\' && *args == '\"')
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
stuff = '\"', args++;
|
|
|
|
}
|
2008-05-09 03:54:06 +00:00
|
|
|
else if (!no_escapes && stuff == '\\' && *args == '\\')
|
2007-12-15 03:27:40 +00:00
|
|
|
{
|
|
|
|
args++;
|
|
|
|
}
|
2008-05-09 03:54:06 +00:00
|
|
|
else if (!no_escapes && stuff == '\\' && *args == 'c')
|
2007-12-15 03:27:40 +00:00
|
|
|
{
|
|
|
|
stuff = TEXTCOLOR_ESCAPE, args++;
|
|
|
|
}
|
2006-02-24 04:48:15 +00:00
|
|
|
else if (stuff == '\"')
|
|
|
|
{
|
|
|
|
stuff = 0;
|
|
|
|
}
|
|
|
|
else if (stuff == 0)
|
|
|
|
{
|
|
|
|
args--;
|
|
|
|
}
|
|
|
|
if (argv != NULL)
|
|
|
|
{
|
|
|
|
*buffplace = stuff;
|
|
|
|
}
|
|
|
|
buffplace++;
|
|
|
|
} while (stuff);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // read unquoted string
|
|
|
|
const char *start = args++, *end;
|
|
|
|
FBaseCVar *var;
|
|
|
|
UCVarValue val;
|
|
|
|
|
|
|
|
while (*args && *args > ' ' && *args != '\"')
|
|
|
|
args++;
|
2009-05-15 10:39:40 +00:00
|
|
|
if (*start == '$' && (var = FindCVarSub (start+1, int(args-start-1))))
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
val = var->GetGenericRep (CVAR_String);
|
|
|
|
start = val.String;
|
|
|
|
end = start + strlen (start);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
end = args;
|
|
|
|
}
|
|
|
|
if (argv != NULL)
|
|
|
|
{
|
|
|
|
argv[count] = buffplace;
|
|
|
|
while (start < end)
|
|
|
|
*buffplace++ = *start++;
|
|
|
|
*buffplace++ = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buffplace += end - start + 1;
|
|
|
|
}
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (argc != NULL)
|
|
|
|
{
|
|
|
|
*argc = count;
|
|
|
|
}
|
|
|
|
return (long)(buffplace - (char *)0);
|
|
|
|
}
|
|
|
|
|
2008-05-09 03:54:06 +00:00
|
|
|
FCommandLine::FCommandLine (const char *commandline, bool no_escapes)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
cmd = commandline;
|
|
|
|
_argc = -1;
|
|
|
|
_argv = NULL;
|
2008-05-09 03:54:06 +00:00
|
|
|
noescapes = no_escapes;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FCommandLine::~FCommandLine ()
|
|
|
|
{
|
|
|
|
if (_argv != NULL)
|
|
|
|
{
|
|
|
|
delete[] _argv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-09 03:54:06 +00:00
|
|
|
void FCommandLine::Shift()
|
|
|
|
{
|
|
|
|
// Only valid after _argv has been filled.
|
|
|
|
for (int i = 1; i < _argc; ++i)
|
|
|
|
{
|
|
|
|
_argv[i - 1] = _argv[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
int FCommandLine::argc ()
|
|
|
|
{
|
|
|
|
if (_argc == -1)
|
|
|
|
{
|
2008-05-09 03:54:06 +00:00
|
|
|
argsize = ParseCommandLine (cmd, &_argc, NULL, noescapes);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
return _argc;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *FCommandLine::operator[] (int i)
|
|
|
|
{
|
|
|
|
if (_argv == NULL)
|
|
|
|
{
|
|
|
|
int count = argc();
|
|
|
|
_argv = new char *[count + (argsize+sizeof(char*)-1)/sizeof(char*)];
|
|
|
|
_argv[0] = (char *)_argv + count*sizeof(char *);
|
2008-05-09 03:54:06 +00:00
|
|
|
ParseCommandLine (cmd, NULL, _argv, noescapes);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
return _argv[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
static FConsoleCommand *ScanChainForName (FConsoleCommand *start, const char *name, size_t namelen, FConsoleCommand **prev)
|
|
|
|
{
|
|
|
|
int comp;
|
|
|
|
|
|
|
|
*prev = NULL;
|
|
|
|
while (start)
|
|
|
|
{
|
|
|
|
comp = strnicmp (start->m_Name, name, namelen);
|
|
|
|
if (comp > 0)
|
|
|
|
return NULL;
|
|
|
|
else if (comp == 0 && start->m_Name[namelen] == 0)
|
|
|
|
return start;
|
|
|
|
|
|
|
|
*prev = start;
|
|
|
|
start = start->m_Next;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static FConsoleCommand *FindNameInHashTable (FConsoleCommand **table, const char *name, size_t namelen)
|
|
|
|
{
|
|
|
|
FConsoleCommand *dummy;
|
|
|
|
|
|
|
|
return ScanChainForName (table[MakeKey (name, namelen) % FConsoleCommand::HASH_SIZE], name, namelen, &dummy);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FConsoleCommand::AddToHash (FConsoleCommand **table)
|
|
|
|
{
|
|
|
|
unsigned int key;
|
|
|
|
FConsoleCommand *insert, **bucket;
|
|
|
|
|
|
|
|
key = MakeKey (m_Name);
|
|
|
|
bucket = &table[key % HASH_SIZE];
|
|
|
|
|
|
|
|
if (ScanChainForName (*bucket, m_Name, strlen (m_Name), &insert))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (insert)
|
|
|
|
{
|
|
|
|
m_Next = insert->m_Next;
|
|
|
|
if (m_Next)
|
|
|
|
m_Next->m_Prev = &m_Next;
|
|
|
|
insert->m_Next = this;
|
|
|
|
m_Prev = &insert->m_Next;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_Next = *bucket;
|
|
|
|
*bucket = this;
|
|
|
|
m_Prev = bucket;
|
|
|
|
if (m_Next)
|
|
|
|
m_Next->m_Prev = &m_Next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-07-28 11:58:30 +00:00
|
|
|
FConsoleCommand* FConsoleCommand::FindByName (const char* name)
|
|
|
|
{
|
|
|
|
return FindNameInHashTable (Commands, name, strlen (name));
|
|
|
|
}
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
FConsoleCommand::FConsoleCommand (const char *name, CCmdRun runFunc)
|
|
|
|
: m_RunFunc (runFunc)
|
|
|
|
{
|
|
|
|
static bool firstTime = true;
|
|
|
|
|
|
|
|
if (firstTime)
|
|
|
|
{
|
|
|
|
char tname[16];
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
firstTime = false;
|
|
|
|
|
|
|
|
// Add all the action commands for tab completion
|
|
|
|
for (i = 0; i < NUM_ACTIONS; i++)
|
|
|
|
{
|
|
|
|
strcpy (&tname[1], ActionMaps[i].Name);
|
|
|
|
tname[0] = '+';
|
|
|
|
C_AddTabCommand (tname);
|
|
|
|
tname[0] = '-';
|
|
|
|
C_AddTabCommand (tname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int ag = strcmp (name, "kill");
|
|
|
|
if (ag == 0)
|
|
|
|
ag=0;
|
|
|
|
m_Name = copystring (name);
|
|
|
|
|
|
|
|
if (!AddToHash (Commands))
|
|
|
|
Printf ("FConsoleCommand c'tor: %s exists\n", name);
|
|
|
|
else
|
|
|
|
C_AddTabCommand (name);
|
|
|
|
}
|
|
|
|
|
|
|
|
FConsoleCommand::~FConsoleCommand ()
|
|
|
|
{
|
|
|
|
*m_Prev = m_Next;
|
|
|
|
if (m_Next)
|
|
|
|
m_Next->m_Prev = m_Prev;
|
|
|
|
C_RemoveTabCommand (m_Name);
|
|
|
|
delete[] m_Name;
|
|
|
|
}
|
|
|
|
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
void FConsoleCommand::Run (FCommandLine &argv, APlayerPawn *who, int key)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
m_RunFunc (argv, who, key);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FConsoleAlias::FConsoleAlias (const char *name, const char *command, bool noSave)
|
|
|
|
: FConsoleCommand (name, NULL),
|
2006-10-05 03:30:44 +00:00
|
|
|
bRunning(false), bKill(false)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-10-05 03:30:44 +00:00
|
|
|
m_Command[noSave] = command;
|
|
|
|
m_Command[!noSave] = FString();
|
|
|
|
// If the command contains % characters, assume they are parameter markers
|
|
|
|
// for substitution when the command is executed.
|
|
|
|
bDoSubstitution = (strchr (command, '%') != NULL);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FConsoleAlias::~FConsoleAlias ()
|
|
|
|
{
|
2006-10-05 03:30:44 +00:00
|
|
|
m_Command[1] = m_Command[0] = FString();
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
|
2013-01-11 03:21:46 +00:00
|
|
|
// Given an argument vector, reconstitute the command line it could have been produced from.
|
2010-03-02 04:51:16 +00:00
|
|
|
FString BuildString (int argc, FString *argv)
|
|
|
|
{
|
|
|
|
if (argc == 1)
|
|
|
|
{
|
|
|
|
return *argv;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FString buf;
|
|
|
|
int arg;
|
|
|
|
|
|
|
|
for (arg = 0; arg < argc; arg++)
|
|
|
|
{
|
2013-01-11 03:21:46 +00:00
|
|
|
if (strchr(argv[arg], '"'))
|
|
|
|
{ // If it contains one or more quotes, we need to escape them.
|
|
|
|
buf << '"';
|
|
|
|
long substr_start = 0, quotepos;
|
|
|
|
while ((quotepos = argv[arg].IndexOf('"', substr_start)) >= 0)
|
|
|
|
{
|
|
|
|
if (substr_start < quotepos)
|
|
|
|
{
|
|
|
|
buf << argv[arg].Mid(substr_start, quotepos - substr_start);
|
|
|
|
}
|
|
|
|
buf << "\\\"";
|
|
|
|
substr_start = quotepos + 1;
|
|
|
|
}
|
|
|
|
buf << argv[arg].Mid(substr_start) << "\" ";
|
|
|
|
}
|
|
|
|
else if (strchr(argv[arg], ' '))
|
|
|
|
{ // If it contains a space, it needs to be quoted.
|
2010-03-02 04:51:16 +00:00
|
|
|
buf << '"' << argv[arg] << "\" ";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buf << argv[arg] << ' ';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-10-05 03:30:44 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// SubstituteAliasParams
|
|
|
|
//
|
|
|
|
// Given an command line and a set of arguments, replace instances of
|
|
|
|
// %x or %{x} in the command line with argument x. If argument x does not
|
|
|
|
// exist, then the empty string is substituted in its place.
|
|
|
|
//
|
|
|
|
// Substitution is not done inside of quoted strings.
|
|
|
|
//
|
|
|
|
// To avoid a substitution, use %%. The %% will be replaced by a single %.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
FString SubstituteAliasParams (FString &command, FCommandLine &args)
|
|
|
|
{
|
|
|
|
// Do substitution by replacing %x with the argument x.
|
|
|
|
// If there is no argument x, then %x is simply removed.
|
|
|
|
|
|
|
|
// For some reason, strtoul's stop parameter is non-const.
|
|
|
|
char *p = command.LockBuffer(), *start = p;
|
|
|
|
unsigned long argnum;
|
|
|
|
FString buf;
|
|
|
|
|
|
|
|
while (*p != '\0')
|
|
|
|
{
|
|
|
|
if (*p == '%' && ((p[1] >= '0' && p[1] <= '9') || p[1] == '{' || p[1] == '%'))
|
|
|
|
{
|
|
|
|
if (p[1] == '%')
|
|
|
|
{
|
|
|
|
// Do not substitute. Just collapse to a single %.
|
|
|
|
buf.AppendCStrPart (start, p - start + 1);
|
|
|
|
start = p = p + 2;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do a substitution. Output what came before this.
|
|
|
|
buf.AppendCStrPart (start, p - start);
|
|
|
|
|
|
|
|
// Extract the argument number and substitute the corresponding argument.
|
|
|
|
argnum = strtoul (p + 1 + (p[1] == '{'), &start, 10);
|
2006-10-31 00:47:05 +00:00
|
|
|
if ((p[1] != '{' || *start == '}') && argnum < (unsigned long)args.argc())
|
2006-10-05 03:30:44 +00:00
|
|
|
{
|
|
|
|
buf += args[argnum];
|
|
|
|
}
|
|
|
|
p = (start += (p[1] == '{' && *start == '}'));
|
|
|
|
}
|
|
|
|
else if (*p == '"')
|
|
|
|
{
|
|
|
|
// Don't substitute inside quoted strings.
|
|
|
|
p++;
|
|
|
|
while (*p != '\0' && (*p != '"' || *(p-1) == '\\'))
|
|
|
|
p++;
|
|
|
|
if (*p != '\0')
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Return whatever was after the final substitution.
|
|
|
|
if (p > start)
|
|
|
|
{
|
|
|
|
buf.AppendCStrPart (start, p - start);
|
|
|
|
}
|
|
|
|
command.UnlockBuffer();
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
static int DumpHash (FConsoleCommand **table, bool aliases, const char *pattern=NULL)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
int bucket, count;
|
|
|
|
FConsoleCommand *cmd;
|
|
|
|
|
|
|
|
for (bucket = count = 0; bucket < FConsoleCommand::HASH_SIZE; bucket++)
|
|
|
|
{
|
|
|
|
cmd = table[bucket];
|
|
|
|
while (cmd)
|
|
|
|
{
|
|
|
|
if (CheckWildcards (pattern, cmd->m_Name))
|
|
|
|
{
|
|
|
|
if (cmd->IsAlias())
|
|
|
|
{
|
|
|
|
if (aliases)
|
|
|
|
{
|
|
|
|
++count;
|
|
|
|
static_cast<FConsoleAlias *>(cmd)->PrintAlias ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!aliases)
|
|
|
|
{
|
|
|
|
++count;
|
|
|
|
cmd->PrintCommand ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cmd = cmd->m_Next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FConsoleAlias::PrintAlias ()
|
|
|
|
{
|
|
|
|
if (m_Command[0])
|
|
|
|
{
|
2006-10-09 15:55:47 +00:00
|
|
|
Printf (TEXTCOLOR_YELLOW "%s : %s\n", m_Name, m_Command[0].GetChars());
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
if (m_Command[1])
|
|
|
|
{
|
2006-10-09 15:55:47 +00:00
|
|
|
Printf (TEXTCOLOR_ORANGE "%s : %s\n", m_Name, m_Command[1].GetChars());
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FConsoleAlias::Archive (FConfigFile *f)
|
|
|
|
{
|
2006-10-05 03:30:44 +00:00
|
|
|
if (f != NULL && !m_Command[0].IsEmpty())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
f->SetValueForKey ("Name", m_Name, true);
|
|
|
|
f->SetValueForKey ("Command", m_Command[0], true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void C_ArchiveAliases (FConfigFile *f)
|
|
|
|
{
|
|
|
|
int bucket;
|
|
|
|
FConsoleCommand *alias;
|
|
|
|
|
|
|
|
for (bucket = 0; bucket < FConsoleCommand::HASH_SIZE; bucket++)
|
|
|
|
{
|
|
|
|
alias = Commands[bucket];
|
|
|
|
while (alias)
|
|
|
|
{
|
|
|
|
if (alias->IsAlias())
|
|
|
|
static_cast<FConsoleAlias *>(alias)->Archive (f);
|
|
|
|
alias = alias->m_Next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-12 23:52:00 +00:00
|
|
|
void C_ClearAliases ()
|
|
|
|
{
|
|
|
|
int bucket;
|
|
|
|
FConsoleCommand *alias;
|
|
|
|
|
|
|
|
for (bucket = 0; bucket < FConsoleCommand::HASH_SIZE; bucket++)
|
|
|
|
{
|
|
|
|
alias = Commands[bucket];
|
|
|
|
while (alias)
|
|
|
|
{
|
|
|
|
FConsoleCommand *next = alias->m_Next;
|
|
|
|
if (alias->IsAlias())
|
|
|
|
static_cast<FConsoleAlias *>(alias)->SafeDelete();
|
|
|
|
alias = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD(clearaliases)
|
|
|
|
{
|
|
|
|
C_ClearAliases();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-24 04:48:15 +00:00
|
|
|
// This is called only by the ini parser.
|
|
|
|
void C_SetAlias (const char *name, const char *cmd)
|
|
|
|
{
|
|
|
|
FConsoleCommand *prev, *alias, **chain;
|
|
|
|
|
|
|
|
chain = &Commands[MakeKey (name) % FConsoleCommand::HASH_SIZE];
|
|
|
|
alias = ScanChainForName (*chain, name, strlen (name), &prev);
|
|
|
|
if (alias != NULL)
|
|
|
|
{
|
|
|
|
if (!alias->IsAlias ())
|
|
|
|
{
|
|
|
|
//Printf (PRINT_BOLD, "%s is a command and cannot be an alias.\n", name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
delete alias;
|
|
|
|
}
|
|
|
|
new FConsoleAlias (name, cmd, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (alias)
|
|
|
|
{
|
|
|
|
FConsoleCommand *prev, *alias, **chain;
|
|
|
|
|
|
|
|
if (argv.argc() == 1)
|
|
|
|
{
|
|
|
|
Printf ("Current alias commands:\n");
|
|
|
|
DumpHash (Commands, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
chain = &Commands[MakeKey (argv[1]) % FConsoleCommand::HASH_SIZE];
|
|
|
|
|
|
|
|
if (argv.argc() == 2)
|
|
|
|
{ // Remove the alias
|
|
|
|
|
|
|
|
if ( (alias = ScanChainForName (*chain, argv[1], strlen (argv[1]), &prev)))
|
|
|
|
{
|
|
|
|
if (alias->IsAlias ())
|
|
|
|
{
|
|
|
|
static_cast<FConsoleAlias *> (alias)->SafeDelete ();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Printf ("%s is a normal command\n", alias->m_Name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // Add/change the alias
|
|
|
|
|
|
|
|
alias = ScanChainForName (*chain, argv[1], strlen (argv[1]), &prev);
|
|
|
|
if (alias != NULL)
|
|
|
|
{
|
|
|
|
if (alias->IsAlias ())
|
|
|
|
{
|
|
|
|
static_cast<FConsoleAlias *> (alias)->Realias (argv[2], ParsingKeyConf);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Printf ("%s is a normal command\n", alias->m_Name);
|
|
|
|
alias = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-02-07 15:35:23 +00:00
|
|
|
new FConsoleAlias (argv[1], argv[2], ParsingKeyConf);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (cmdlist)
|
|
|
|
{
|
|
|
|
int count;
|
|
|
|
const char *filter = (argv.argc() == 1 ? NULL : argv[1]);
|
|
|
|
|
|
|
|
count = ListActionCommands (filter);
|
|
|
|
count += DumpHash (Commands, false, filter);
|
|
|
|
Printf ("%d commands\n", count);
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (key)
|
|
|
|
{
|
|
|
|
if (argv.argc() > 1)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 1; i < argv.argc(); ++i)
|
|
|
|
{
|
|
|
|
unsigned int key = MakeKey (argv[i]);
|
|
|
|
Printf (" 0x%08x\n", key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Execute any console commands specified on the command line.
|
|
|
|
// These all begin with '+' as opposed to '-'.
|
|
|
|
void C_ExecCmdLineParams ()
|
|
|
|
{
|
2008-03-12 02:56:11 +00:00
|
|
|
for (int currArg = 1; currArg < Args->NumArgs(); )
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-03-12 02:56:11 +00:00
|
|
|
if (*Args->GetArg (currArg++) == '+')
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-10-05 03:30:44 +00:00
|
|
|
FString cmdString;
|
2006-02-24 04:48:15 +00:00
|
|
|
int cmdlen = 1;
|
|
|
|
int argstart = currArg - 1;
|
|
|
|
|
2008-03-12 02:56:11 +00:00
|
|
|
while (currArg < Args->NumArgs())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2008-03-12 02:56:11 +00:00
|
|
|
if (*Args->GetArg (currArg) == '-' || *Args->GetArg (currArg) == '+')
|
2006-02-24 04:48:15 +00:00
|
|
|
break;
|
|
|
|
currArg++;
|
|
|
|
cmdlen++;
|
|
|
|
}
|
|
|
|
|
2008-03-12 02:56:11 +00:00
|
|
|
cmdString = BuildString (cmdlen, Args->GetArgList (argstart));
|
2006-10-05 03:30:44 +00:00
|
|
|
if (!cmdString.IsEmpty())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
2006-10-05 03:30:44 +00:00
|
|
|
C_DoCommand (&cmdString[1]);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FConsoleCommand::IsAlias ()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FConsoleAlias::IsAlias ()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
- Added the ACS commands
ReplaceTextures (str old_texture, str new_texture, optional bool not_lower,
optional bool not_mid, optional bool not_upper, optional bool not_floor,
optional bool not_ceiling); and
SectorDamage (int tag, int amount, str type, bool players_only, bool in_air,
str protection_item, bool subclasses_okay);
- Added the vid_nowidescreen cvar to disable widescreen aspect ratio
correction. When this is enabled, the only display ratio available is 4:3
(and 5:4 if vid_tft is set).
- Added support for setting an actor's damage property to an expression
through decorate. Just enclose it within parentheses, and the expression
will be evaluated exactly as-is without the normal Doom damage calculation.
So if you want something that does exactly 6 damage, use a "Damage (6)"
property. To deal normal Doom missile damage, you can use
"Damage (random(1,8)*6)" instead of "Damage 6".
- Moved InvFirst and InvSel into APlayerPawn so that they can be consistantly
maintained by ObtainInventory.
SVN r288 (trunk)
2006-08-12 02:30:57 +00:00
|
|
|
void FConsoleAlias::Run (FCommandLine &args, APlayerPawn *who, int key)
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
if (bRunning)
|
|
|
|
{
|
|
|
|
Printf ("Alias %s tried to recurse.\n", m_Name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-10-05 03:30:44 +00:00
|
|
|
int index = !m_Command[1].IsEmpty();
|
|
|
|
FString savedcommand = m_Command[index], mycommand;
|
|
|
|
m_Command[index] = FString();
|
|
|
|
|
|
|
|
if (bDoSubstitution)
|
|
|
|
{
|
|
|
|
mycommand = SubstituteAliasParams (savedcommand, args);
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
else
|
2006-10-05 03:30:44 +00:00
|
|
|
{
|
|
|
|
mycommand = savedcommand;
|
|
|
|
}
|
|
|
|
|
|
|
|
bRunning = true;
|
|
|
|
AddCommandString (mycommand.LockBuffer(), key);
|
|
|
|
mycommand.UnlockBuffer();
|
|
|
|
bRunning = false;
|
|
|
|
if (m_Command[index].IsEmpty())
|
2006-02-24 04:48:15 +00:00
|
|
|
{ // The alias is unchanged, so put the command back so it can be used again.
|
2006-10-05 03:30:44 +00:00
|
|
|
// If the command had been non-empty, then that means that executing this
|
|
|
|
// alias caused it to realias itself, so the old command will be forgotten
|
|
|
|
// once this function returns.
|
|
|
|
m_Command[index] = savedcommand;
|
2006-02-24 04:48:15 +00:00
|
|
|
}
|
|
|
|
if (bKill)
|
|
|
|
{ // The alias wants to remove itself
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FConsoleAlias::Realias (const char *command, bool noSave)
|
|
|
|
{
|
2006-10-05 03:30:44 +00:00
|
|
|
if (!noSave && !m_Command[1].IsEmpty())
|
2006-02-24 04:48:15 +00:00
|
|
|
{
|
|
|
|
noSave = true;
|
|
|
|
}
|
2006-10-05 03:30:44 +00:00
|
|
|
m_Command[noSave] = command;
|
|
|
|
|
|
|
|
// If the command contains % characters, assume they are parameter markers
|
|
|
|
// for substitution when the command is executed.
|
|
|
|
bDoSubstitution = (strchr (command, '%') != NULL);
|
2006-02-24 04:48:15 +00:00
|
|
|
bKill = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FConsoleAlias::SafeDelete ()
|
|
|
|
{
|
|
|
|
if (!bRunning)
|
|
|
|
{
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bKill = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-14 00:02:31 +00:00
|
|
|
static BYTE PullinBad = 2;
|
2006-02-24 04:48:15 +00:00
|
|
|
static const char *PullinFile;
|
2010-01-01 12:40:47 +00:00
|
|
|
extern TArray<FString> allwads;
|
2006-02-24 04:48:15 +00:00
|
|
|
|
|
|
|
int C_ExecFile (const char *file, bool usePullin)
|
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
char cmd[4096];
|
|
|
|
int retval = 0;
|
|
|
|
|
|
|
|
BYTE pullinSaved = PullinBad;
|
|
|
|
const char *fileSaved = PullinFile;
|
|
|
|
|
|
|
|
if ( (f = fopen (file, "r")) )
|
|
|
|
{
|
|
|
|
PullinBad = 1-usePullin;
|
|
|
|
PullinFile = file;
|
|
|
|
|
|
|
|
while (fgets (cmd, 4095, f))
|
|
|
|
{
|
|
|
|
// Comments begin with //
|
|
|
|
char *stop = cmd + strlen (cmd) - 1;
|
|
|
|
char *comment = cmd;
|
|
|
|
int inQuote = 0;
|
|
|
|
|
|
|
|
if (*stop == '\n')
|
|
|
|
*stop-- = 0;
|
|
|
|
|
|
|
|
while (comment < stop)
|
|
|
|
{
|
|
|
|
if (*comment == '\"')
|
|
|
|
{
|
|
|
|
inQuote ^= 1;
|
|
|
|
}
|
|
|
|
else if (!inQuote && *comment == '/' && *(comment + 1) == '/')
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
comment++;
|
|
|
|
}
|
|
|
|
if (comment == cmd)
|
|
|
|
{ // Comment at line beginning
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (comment < stop)
|
|
|
|
{ // Comment in middle of line
|
|
|
|
*comment = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AddCommandString (cmd);
|
|
|
|
}
|
|
|
|
if (!feof (f))
|
|
|
|
{
|
|
|
|
retval = 2;
|
|
|
|
}
|
|
|
|
fclose (f);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
retval = 1;
|
|
|
|
}
|
|
|
|
PullinBad = pullinSaved;
|
|
|
|
PullinFile = fileSaved;
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCMD (pullin)
|
|
|
|
{
|
|
|
|
if (PullinBad == 2)
|
|
|
|
{
|
|
|
|
Printf ("This command is only valid from .cfg\n"
|
|
|
|
"files and only when used at startup.\n");
|
|
|
|
}
|
|
|
|
else if (argv.argc() > 1)
|
|
|
|
{
|
|
|
|
const char *lastSlash;
|
|
|
|
|
2013-07-30 09:45:42 +00:00
|
|
|
#ifdef __unix__
|
2006-02-24 04:48:15 +00:00
|
|
|
lastSlash = strrchr (PullinFile, '/');
|
|
|
|
#else
|
|
|
|
const char *lastSlash1, *lastSlash2;
|
|
|
|
|
|
|
|
lastSlash1 = strrchr (PullinFile, '/');
|
|
|
|
lastSlash2 = strrchr (PullinFile, '\\');
|
|
|
|
lastSlash = MAX (lastSlash1, lastSlash2);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (PullinBad)
|
|
|
|
{
|
|
|
|
Printf ("Not loading:");
|
|
|
|
}
|
|
|
|
for (int i = 1; i < argv.argc(); ++i)
|
|
|
|
{
|
|
|
|
if (PullinBad)
|
|
|
|
{
|
|
|
|
Printf (" %s", argv[i]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Try looking for the wad in the same directory as the .cfg
|
|
|
|
// before looking for it in the current directory.
|
|
|
|
char *path = argv[i];
|
|
|
|
|
|
|
|
if (lastSlash != NULL)
|
|
|
|
{
|
|
|
|
size_t pathlen = lastSlash - PullinFile + strlen (argv[i]) + 2;
|
|
|
|
path = new char[pathlen];
|
|
|
|
strncpy (path, PullinFile, (lastSlash - PullinFile) + 1);
|
|
|
|
strcpy (path + (lastSlash - PullinFile) + 1, argv[i]);
|
|
|
|
if (!FileExists (path))
|
|
|
|
{
|
|
|
|
delete[] path;
|
|
|
|
path = argv[i];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FixPathSeperator (path);
|
|
|
|
}
|
|
|
|
}
|
2010-01-01 12:40:47 +00:00
|
|
|
D_AddFile (allwads, path);
|
2006-02-24 04:48:15 +00:00
|
|
|
if (path != argv[i])
|
|
|
|
{
|
|
|
|
delete[] path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (PullinBad)
|
|
|
|
{
|
|
|
|
Printf ("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-05-14 14:30:13 +00:00
|
|
|
|