diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 9f2dbf60b..f71076d17 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -637,6 +637,7 @@ file( GLOB HEADER_FILES mact/src/*.h common/*.h common/utility/*.h + common/console/*.h build/src/*.h thirdparty/include/*.h @@ -787,6 +788,8 @@ set (PCH_SOURCES common/rts.cpp common/gameconfigfile.cpp + common/console/c_cvars.cpp + common/utility/cmdlib.cpp common/utility/m_argv.cpp common/utility/files.cpp diff --git a/source/build/src/osd.cpp b/source/build/src/osd.cpp index 2bc01d60b..decf98e6c 100644 --- a/source/build/src/osd.cpp +++ b/source/build/src/osd.cpp @@ -10,6 +10,7 @@ #include "osd.h" #include "scancodes.h" #include "common.h" +#include "c_cvars.h" #define XXH_STATIC_LINKING_ONLY #include "xxhash.h" @@ -126,6 +127,7 @@ static int OSD_CvarModified(const osdcvar_t * const pCvar) return rv || ((pCvar->pData->flags & CVAR_MODIFIED) == CVAR_MODIFIED); } + // color code format is as follows: // ^## sets a color, where ## is the palette number // ^S# sets a shade, range is 0-7 equiv to shades 0-14 @@ -1864,6 +1866,51 @@ void OSD_Dispatch(const char *cmd) if (symbol == NULL) { + // Quick hack to make ZDoom CVARs accessible. This isn't fully functional and only meant as a transitional helper. + auto cv = FindCVar(token, nullptr); + if (cv) + { + token = osd_strtoken(NULL, &wtp, &restart); + + if (token == nullptr) + { + if (cv->GetDescription()) + OSD_Printf("%s: %s\n", cv->GetName(), cv->GetDescription()); + ECVarType type; + auto val = cv->GetFavoriteRep(&type); + switch (type) + { + case CVAR_String: + OSD_Printf("%s is %s\n", cv->GetName(), val.String); + break; + + case CVAR_Int: + OSD_Printf("%s is %d\n", cv->GetName(), val.Int); + break; + + case CVAR_Float: + OSD_Printf("%s is %2.5f\n", cv->GetName(), val.Float); + break; + + case CVAR_Bool: + OSD_Printf("%s is %s\n", cv->GetName(), val.Bool? "true" : "false"); + break; + + default: + break; + } + } + else + { + UCVarValue val; + val.String = token; + cv->SetGenericRep(val, CVAR_String); + } + + + return; + } + static char const s_gamefunc_[] = "gamefunc_"; size_t constexpr strlen_gamefunc_ = ARRAY_SIZE(s_gamefunc_) - 1; size_t const strlen_token = Bstrlen(token); diff --git a/source/common/console/c_console.h b/source/common/console/c_console.h new file mode 100644 index 000000000..2eedd9407 --- /dev/null +++ b/source/common/console/c_console.h @@ -0,0 +1,101 @@ +/* +** c_console.h +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** 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. +**--------------------------------------------------------------------------- +** +*/ + +#ifndef __C_CONSOLE__ +#define __C_CONSOLE__ + +#include +//#include "basictypes.h" + +// Dummies for missing features to make the code compile. + +inline void C_AddTabCommand (const char *name) {} +inline void C_RemoveTabCommand (const char *name) {} + + +#if 0 + +struct event_t; + +#define C_BLINKRATE (TICRATE/2) + +typedef enum cstate_t +{ + c_up=0, c_down=1, c_falling=2, c_rising=3 +} +constate_e; + +enum +{ + PRINTLEVELS = 5 +}; +extern int PrintColors[PRINTLEVELS + 2]; + +extern constate_e ConsoleState; + +// Initialize the console +void C_InitConsole (int width, int height, bool ingame); +void C_DeinitConsole (); +void C_InitConback(); + +// Adjust the console for a new screen mode +void C_NewModeAdjust (void); + +void C_Ticker (void); + +void AddToConsole (int printlevel, const char *string); +int PrintString (int printlevel, const char *string); +int PrintStringHigh (const char *string); +int VPrintf (int printlevel, const char *format, va_list parms) GCCFORMAT(2); + +void C_DrawConsole (); +void C_ToggleConsole (void); +void C_FullConsole (void); +void C_HideConsole (void); +void C_AdjustBottom (void); +void C_FlushDisplay (void); + +class FFont; +void C_MidPrint (FFont *font, const char *message, bool bold = false); + +bool C_Responder (event_t *ev); + +void C_AddTabCommand (const char *name); +void C_RemoveTabCommand (const char *name); +void C_ClearTabCommands(); // Removes all tab commands + +extern const char *console_bar; + +#endif + +#endif diff --git a/source/common/console/c_cvars.cpp b/source/common/console/c_cvars.cpp new file mode 100644 index 000000000..92abb0199 --- /dev/null +++ b/source/common/console/c_cvars.cpp @@ -0,0 +1,1586 @@ +/* +** c_cvars.cpp +** Defines all the different console variable types +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** 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. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include + +#include "c_cvars.h" +#include "configfile.h" + +#include "c_console.h" + +//#include "cmdlib.h" +//#include "c_dispatch.h" + +//#include "g_game.h" +//#include "d_player.h" +//#include "v_video.h" +//#include "d_netinf.h" + +//#include "menu/menu.h" +//#include "vm.h" + +#define CVAR_NOSAVE 0x100 // duplicate name, put in header later + +struct FLatchedValue +{ + FBaseCVar *Variable; + UCVarValue Value; + ECVarType Type; + bool UnsafeContext; +}; + +static TArray LatchedValues; + +bool FBaseCVar::m_DoNoSet = false; +bool FBaseCVar::m_UseCallback = false; + +FBaseCVar *CVars = NULL; + +int cvar_defflags; + + +FBaseCVar::FBaseCVar (const char *var_name, uint32_t flags, void (*callback)(FBaseCVar &), const char *descr) +{ + if (var_name != nullptr && (flags & CVAR_SERVERINFO)) + { + // This limitation is imposed by network protocol which uses only 6 bits + // for name's length with terminating null character + static const size_t NAME_LENGHT_MAX = 63; + + /* + if (strlen(var_name) > NAME_LENGHT_MAX) + { + I_FatalError("Name of the server console variable \"%s\" is too long.\n" + "Its length should not exceed %zu characters.\n", var_name, NAME_LENGHT_MAX); + } + */ + } + + FBaseCVar *var; + + var = FindCVar (var_name, NULL); + + m_Callback = callback; + Flags = 0; + VarName = ""; + Description = descr; + + if (var_name) + { + C_AddTabCommand (var_name); + VarName = var_name; + m_Next = CVars; + CVars = this; + } + + if (var) + { + ECVarType type; + UCVarValue value; + + value = var->GetFavoriteRep (&type); + ForceSet (value, type); + + if (var->Flags & CVAR_AUTO) + delete var; + else + var->~FBaseCVar(); + + Flags = flags; + } + else + { + Flags = flags | CVAR_ISDEFAULT; + } +} + +FBaseCVar::~FBaseCVar () +{ + if (VarName.IsNotEmpty()) + { + FBaseCVar *var, *prev; + + var = FindCVar (VarName, &prev); + + if (var == this) + { + if (prev) + prev->m_Next = m_Next; + else + CVars = m_Next; + } + C_RemoveTabCommand(VarName); + } +} + +const char *FBaseCVar::GetHumanString(int precision) const +{ + return GetGenericRep(CVAR_String).String; +} + +void FBaseCVar::ForceSet (UCVarValue value, ECVarType type, bool nouserinfosend) +{ + DoSet (value, type); + if (m_UseCallback) + Callback (); + + if ((Flags & CVAR_ARCHIVE)) + { + SafeValue = GetGenericRep(CVAR_String).String; + } + + Flags &= ~(CVAR_ISDEFAULT); +} + +void FBaseCVar::SetGenericRep (UCVarValue value, ECVarType type) +{ + if ((Flags & CVAR_NOSET) && m_DoNoSet) + { + return; + } +#if 0 + else if ((Flags & CVAR_LATCH) && gamestate != GS_FULLCONSOLE && gamestate != GS_STARTUP) + { + FLatchedValue latch; + + latch.Variable = this; + latch.Type = type; + if (type != CVAR_String) + latch.Value = value; + else + latch.Value.String = copystring(value.String); + latch.UnsafeContext = !!(Flags & CVAR_UNSAFECONTEXT); + LatchedValues.Push (latch); + + Flags &= ~CVAR_UNSAFECONTEXT; + } + else if ((Flags & CVAR_SERVERINFO) && gamestate != GS_STARTUP && !demoplayback) + { + if (netgame && !players[consoleplayer].settings_controller) + { + Printf ("Only setting controllers can change %s\n", Name); + Flags &= ~CVAR_UNSAFECONTEXT; + return; + } + D_SendServerInfoChange (this, value, type); + } +#endif + else + { + ForceSet (value, type); + } +} + +bool FBaseCVar::ToBool (UCVarValue value, ECVarType type) +{ + switch (type) + { + case CVAR_Bool: + return value.Bool; + + case CVAR_Int: + return !!value.Int; + + case CVAR_Float: + return value.Float != 0.f; + + case CVAR_String: + if (stricmp (value.String, "true") == 0) + return true; + else if (stricmp (value.String, "false") == 0) + return false; + else + return !!strtoll (value.String, NULL, 0); + + default: + return false; + } +} + +int FBaseCVar::ToInt (UCVarValue value, ECVarType type) +{ + int res; +#if __GNUC__ <= 2 + float tmp; +#endif + + switch (type) + { + case CVAR_Bool: res = (int)value.Bool; break; + case CVAR_Int: res = value.Int; break; +#if __GNUC__ <= 2 + case CVAR_Float: tmp = value.Float; res = (int)tmp; break; +#else + case CVAR_Float: res = (int)value.Float; break; +#endif + case CVAR_String: + { + if (stricmp (value.String, "true") == 0) + res = 1; + else if (stricmp (value.String, "false") == 0) + res = 0; + else + res = (int)strtoll (value.String, NULL, 0); + break; + } + default: res = 0; break; + } + return res; +} + +float FBaseCVar::ToFloat (UCVarValue value, ECVarType type) +{ + switch (type) + { + case CVAR_Bool: + return (float)value.Bool; + + case CVAR_Int: + return (float)value.Int; + + case CVAR_Float: + return value.Float; + + case CVAR_String: + return (float)strtod (value.String, NULL); + + default: + return 0.f; + } +} + +static char cstrbuf[40]; +static char truestr[] = "true"; +static char falsestr[] = "false"; + +const char *FBaseCVar::ToString (UCVarValue value, ECVarType type) +{ + switch (type) + { + case CVAR_Bool: + return value.Bool ? truestr : falsestr; + + case CVAR_String: + return value.String; + + case CVAR_Int: + snprintf (cstrbuf, sizeof(cstrbuf), "%i", value.Int); + break; + + case CVAR_Float: + IGNORE_FORMAT_PRE + snprintf (cstrbuf, sizeof(cstrbuf), "%g", value.Float); + IGNORE_FORMAT_POST + break; + + default: + strcpy (cstrbuf, ""); + break; + } + return cstrbuf; +} + +UCVarValue FBaseCVar::FromBool (bool value, ECVarType type) +{ + UCVarValue ret; + + switch (type) + { + case CVAR_Bool: + ret.Bool = value; + break; + + case CVAR_Int: + ret.Int = value; + break; + + case CVAR_Float: + ret.Float = value; + break; + + case CVAR_String: + ret.String = value ? truestr : falsestr; + break; + + default: + break; + } + + return ret; +} + +UCVarValue FBaseCVar::FromInt (int value, ECVarType type) +{ + UCVarValue ret; + + switch (type) + { + case CVAR_Bool: + ret.Bool = value != 0; + break; + + case CVAR_Int: + ret.Int = value; + break; + + case CVAR_Float: + ret.Float = (float)value; + break; + + case CVAR_String: + snprintf (cstrbuf, sizeof(cstrbuf), "%i", value); + ret.String = cstrbuf; + break; + + default: + break; + } + + return ret; +} + +UCVarValue FBaseCVar::FromFloat (float value, ECVarType type) +{ + UCVarValue ret; + + switch (type) + { + case CVAR_Bool: + ret.Bool = value != 0.f; + break; + + case CVAR_Int: + ret.Int = (int)value; + break; + + case CVAR_Float: + ret.Float = value; + break; + + case CVAR_String: + IGNORE_FORMAT_PRE + snprintf (cstrbuf, sizeof(cstrbuf), "%g", value); + IGNORE_FORMAT_POST + ret.String = cstrbuf; + break; + + default: + break; + } + + return ret; +} + +static uint8_t HexToByte (const char *hex) +{ + uint8_t v = 0; + for (int i = 0; i < 2; ++i) + { + v <<= 4; + if (hex[i] >= '0' && hex[i] <= '9') + { + v += hex[i] - '0'; + } + else if (hex[i] >= 'A' && hex[i] <= 'F') + { + v += hex[i] - 'A'; + } + else // The string is already verified to contain valid hexits + { + v += hex[i] - 'a'; + } + } + return v; +} + +UCVarValue FBaseCVar::FromString (const char *value, ECVarType type) +{ + UCVarValue ret; + + switch (type) + { + case CVAR_Bool: + if (stricmp (value, "true") == 0) + ret.Bool = true; + else if (stricmp (value, "false") == 0) + ret.Bool = false; + else + ret.Bool = strtoll (value, NULL, 0) != 0; + break; + + case CVAR_Int: + if (stricmp (value, "true") == 0) + ret.Int = 1; + else if (stricmp (value, "false") == 0) + ret.Int = 0; + else + ret.Int = (int)strtoll (value, NULL, 0); + break; + + case CVAR_Float: + ret.Float = (float)strtod (value, NULL); + break; + + case CVAR_String: + ret.String = const_cast(value); + break; + + default: + break; + } + + return ret; +} + +FBaseCVar *cvar_set (const char *var_name, const char *val) +{ + FBaseCVar *var; + + if ( (var = FindCVar (var_name, NULL)) ) + { + UCVarValue value; + value.String = const_cast(val); + var->SetGenericRep (value, CVAR_String); + } + + return var; +} + +FBaseCVar *cvar_forceset (const char *var_name, const char *val) +{ + FBaseCVar *var; + UCVarValue vval; + + if ( (var = FindCVar (var_name, NULL)) ) + { + vval.String = const_cast(val); + var->ForceSet (vval, CVAR_String); + } + + return var; +} + +void FBaseCVar::EnableNoSet () +{ + m_DoNoSet = true; +} + +void FBaseCVar::EnableCallbacks () +{ + m_UseCallback = true; + FBaseCVar *cvar = CVars; + + while (cvar) + { + if (!(cvar->Flags & CVAR_NOINITCALL)) + { + cvar->Callback (); + } + cvar = cvar->m_Next; + } +} + +void FBaseCVar::DisableCallbacks () +{ + m_UseCallback = false; +} + +// +// Boolean cvar implementation +// + +FBoolCVar::FBoolCVar (const char *name, bool def, uint32_t flags, void (*callback)(FBoolCVar &), const char* descr) +: FBaseCVar (name, flags, reinterpret_cast(callback), descr) +{ + DefaultValue = def; + if (Flags & CVAR_ISDEFAULT) + Value = def; +} + +ECVarType FBoolCVar::GetRealType () const +{ + return CVAR_Bool; +} + +UCVarValue FBoolCVar::GetGenericRep (ECVarType type) const +{ + return FromBool (Value, type); +} + +UCVarValue FBoolCVar::GetFavoriteRep (ECVarType *type) const +{ + UCVarValue ret; + *type = CVAR_Bool; + ret.Bool = Value; + return ret; +} + +UCVarValue FBoolCVar::GetGenericRepDefault (ECVarType type) const +{ + return FromBool (DefaultValue, type); +} + +UCVarValue FBoolCVar::GetFavoriteRepDefault (ECVarType *type) const +{ + UCVarValue ret; + *type = CVAR_Bool; + ret.Bool = DefaultValue; + return ret; +} + +void FBoolCVar::SetGenericRepDefault (UCVarValue value, ECVarType type) +{ + DefaultValue = ToBool (value, type); + if (Flags & CVAR_ISDEFAULT) + { + SetGenericRep (value, type); + Flags |= CVAR_ISDEFAULT; + } +} + +void FBoolCVar::DoSet (UCVarValue value, ECVarType type) +{ + Value = ToBool (value, type); +} + +// +// Integer cvar implementation +// + +FIntCVar::FIntCVar (const char *name, int def, uint32_t flags, void (*callback)(FIntCVar &), const char* descr) +: FBaseCVar (name, flags, reinterpret_cast(callback), descr) +{ + DefaultValue = def; + if (Flags & CVAR_ISDEFAULT) + Value = def; +} + +ECVarType FIntCVar::GetRealType () const +{ + return CVAR_Int; +} + +UCVarValue FIntCVar::GetGenericRep (ECVarType type) const +{ + return FromInt (Value, type); +} + +UCVarValue FIntCVar::GetFavoriteRep (ECVarType *type) const +{ + UCVarValue ret; + *type = CVAR_Int; + ret.Int = Value; + return ret; +} + +UCVarValue FIntCVar::GetGenericRepDefault (ECVarType type) const +{ + return FromInt (DefaultValue, type); +} + +UCVarValue FIntCVar::GetFavoriteRepDefault (ECVarType *type) const +{ + UCVarValue ret; + *type = CVAR_Int; + ret.Int = DefaultValue; + return ret; +} + +void FIntCVar::SetGenericRepDefault (UCVarValue value, ECVarType type) +{ + DefaultValue = ToInt (value, type); + if (Flags & CVAR_ISDEFAULT) + { + SetGenericRep (value, type); + Flags |= CVAR_ISDEFAULT; + } +} + +void FIntCVar::DoSet (UCVarValue value, ECVarType type) +{ + Value = ToInt (value, type); +} + +// +// Floating point cvar implementation +// + +FFloatCVar::FFloatCVar (const char *name, float def, uint32_t flags, void (*callback)(FFloatCVar &), const char* descr) +: FBaseCVar (name, flags, reinterpret_cast(callback), descr) +{ + DefaultValue = def; + if (Flags & CVAR_ISDEFAULT) + Value = def; +} + +ECVarType FFloatCVar::GetRealType () const +{ + return CVAR_Float; +} + +const char *FFloatCVar::GetHumanString(int precision) const +{ + if (precision < 0) + { + precision = 6; + } + snprintf(cstrbuf, sizeof(cstrbuf), "%.*g", precision, Value); + return cstrbuf; +} + +UCVarValue FFloatCVar::GetGenericRep (ECVarType type) const +{ + return FromFloat (Value, type); +} + +UCVarValue FFloatCVar::GetFavoriteRep (ECVarType *type) const +{ + UCVarValue ret; + *type = CVAR_Float; + ret.Float = Value; + return ret; +} + +UCVarValue FFloatCVar::GetGenericRepDefault (ECVarType type) const +{ + return FromFloat (DefaultValue, type); +} + +UCVarValue FFloatCVar::GetFavoriteRepDefault (ECVarType *type) const +{ + UCVarValue ret; + *type = CVAR_Float; + ret.Float = DefaultValue; + return ret; +} + +void FFloatCVar::SetGenericRepDefault (UCVarValue value, ECVarType type) +{ + DefaultValue = ToFloat (value, type); + if (Flags & CVAR_ISDEFAULT) + { + SetGenericRep (value, type); + Flags |= CVAR_ISDEFAULT; + } +} + +void FFloatCVar::DoSet (UCVarValue value, ECVarType type) +{ + Value = ToFloat (value, type); +} + +// +// String cvar implementation +// + +FStringCVar::FStringCVar (const char *name, const char *def, uint32_t flags, void (*callback)(FStringCVar &), const char* descr) +: FBaseCVar (name, flags, reinterpret_cast(callback), descr) +{ + mDefaultValue = def; + if (Flags & CVAR_ISDEFAULT) + mValue = def; + else + mValue = ""; +} + +FStringCVar::~FStringCVar () +{ +} + +ECVarType FStringCVar::GetRealType () const +{ + return CVAR_String; +} + +UCVarValue FStringCVar::GetGenericRep (ECVarType type) const +{ + return FromString (mValue, type); +} + +UCVarValue FStringCVar::GetFavoriteRep (ECVarType *type) const +{ + UCVarValue ret; + *type = CVAR_String; + ret.String = mValue; + return ret; +} + +UCVarValue FStringCVar::GetGenericRepDefault (ECVarType type) const +{ + return FromString (mDefaultValue, type); +} + +UCVarValue FStringCVar::GetFavoriteRepDefault (ECVarType *type) const +{ + UCVarValue ret; + *type = CVAR_String; + ret.String = mDefaultValue; + return ret; +} + +void FStringCVar::SetGenericRepDefault (UCVarValue value, ECVarType type) +{ + mDefaultValue = ToString(value, type); + if (Flags & CVAR_ISDEFAULT) + { + SetGenericRep (value, type); + Flags |= CVAR_ISDEFAULT; + } +} + +void FStringCVar::DoSet (UCVarValue value, ECVarType type) +{ + mValue = ToString (value, type); +} + +// +// Color cvar implementation +// +#if 0 +#define APART(c) (((c)>>24)&0xff) +#define RPART(c) (((c)>>16)&0xff) +#define GPART(c) (((c)>>8)&0xff) +#define BPART(c) ((c)&0xff) + +FColorCVar::FColorCVar (const char *name, int def, uint32_t flags, void (*callback)(FColorCVar &), const char* descr) +: FIntCVar (name, def, flags, reinterpret_cast(callback), descr) +{ +} + +ECVarType FColorCVar::GetRealType () const +{ + return CVAR_Color; +} + +UCVarValue FColorCVar::GetGenericRep (ECVarType type) const +{ + return FromInt2 (Value, type); +} + +UCVarValue FColorCVar::GetGenericRepDefault (ECVarType type) const +{ + return FromInt2 (DefaultValue, type); +} + +void FColorCVar::SetGenericRepDefault (UCVarValue value, ECVarType type) +{ + DefaultValue = ToInt2 (value, type); + if (Flags & CVAR_ISDEFAULT) + { + SetGenericRep (value, type); + Flags |= CVAR_ISDEFAULT; + } +} + +UCVarValue FColorCVar::FromInt2 (int value, ECVarType type) +{ + if (type == CVAR_String) + { + UCVarValue ret; + snprintf (cstrbuf, sizeof(cstrbuf), "%02x %02x %02x", + RPART(value), GPART(value), BPART(value)); + ret.String = cstrbuf; + return ret; + } + return FromInt (value, type); +} + +int FColorCVar::ToInt2 (UCVarValue value, ECVarType type) +{ + int ret; + +#if 0 + if (type == CVAR_String) + { + FString string; + // Only allow named colors after the screen exists (i.e. after + // we've got some lumps loaded, so X11R6RGB can be read). Since + // the only time this might be called before that is when loading + // zdoom.ini, this shouldn't be a problem. + if (screen && !(string = V_GetColorStringByName (value.String)).IsEmpty() ) + { + ret = V_GetColorFromString (NULL, string); + } + else + { + ret = V_GetColorFromString (NULL, value.String); + } + } + else +#endif + { + ret = ToInt (value, type); + } + return ret; +} +#endif + +// +// More base cvar stuff +// + +void FBaseCVar::ResetColors () +{ + FBaseCVar *var = CVars; + + while (var) + { + if (var->GetRealType () == CVAR_Color) + { + var->DoSet (var->GetGenericRep (CVAR_Int), CVAR_Int); + } + var = var->m_Next; + } +} + +void FBaseCVar::ResetToDefault () +{ + if (!(Flags & CVAR_ISDEFAULT)) + { + UCVarValue val; + ECVarType type; + + val = GetFavoriteRepDefault (&type); + SetGenericRep (val, type); + Flags |= CVAR_ISDEFAULT; + } +} + +void FBaseCVar::MarkUnsafe() +{ +} + +// +// Flag cvar implementation +// +// This type of cvar is not a "real" cvar. Instead, it gets and sets +// the value of a FIntCVar, modifying it bit-by-bit. As such, it has +// no default, and is not written to the .cfg or transferred around +// the network. The "host" cvar is responsible for that. +// + +FFlagCVar::FFlagCVar (const char *name, FIntCVar &realvar, uint32_t bitval, const char* descr) +: FBaseCVar (name, 0, NULL, descr), +ValueVar (realvar), +BitVal (bitval) +{ + int bit; + + Flags &= ~CVAR_ISDEFAULT; + + assert (bitval != 0); + + bit = 0; + while ((bitval >>= 1) != 0) + { + ++bit; + } + BitNum = bit; + + assert ((1u << BitNum) == BitVal); +} + +ECVarType FFlagCVar::GetRealType () const +{ + return CVAR_DummyBool; +} + +UCVarValue FFlagCVar::GetGenericRep (ECVarType type) const +{ + return FromBool ((ValueVar & BitVal) != 0, type); +} + +UCVarValue FFlagCVar::GetFavoriteRep (ECVarType *type) const +{ + UCVarValue ret; + *type = CVAR_Bool; + ret.Bool = (ValueVar & BitVal) != 0; + return ret; +} + +UCVarValue FFlagCVar::GetGenericRepDefault (ECVarType type) const +{ + ECVarType dummy; + UCVarValue def; + def = ValueVar.GetFavoriteRepDefault (&dummy); + return FromBool ((def.Int & BitVal) != 0, type); +} + +UCVarValue FFlagCVar::GetFavoriteRepDefault (ECVarType *type) const +{ + ECVarType dummy; + UCVarValue def; + def = ValueVar.GetFavoriteRepDefault (&dummy); + def.Bool = (def.Int & BitVal) != 0; + *type = CVAR_Bool; + return def; +} + +void FFlagCVar::SetGenericRepDefault (UCVarValue value, ECVarType type) +{ + bool newdef = ToBool (value, type); + ECVarType dummy; + UCVarValue def; + def = ValueVar.GetFavoriteRepDefault (&dummy); + if (newdef) + def.Int |= BitVal; + else + def.Int &= ~BitVal; + ValueVar.SetGenericRepDefault (def, CVAR_Int); +} + +void FFlagCVar::DoSet (UCVarValue value, ECVarType type) +{ + bool newval = ToBool (value, type); + +#if 0 + // Server cvars that get changed by this need to use a special message, because + // changes are not processed until the next net update. This is a problem with + // exec scripts because all flags will base their changes off of the value of + // the "master" cvar at the time the script was run, overriding any changes + // another flag might have made to the same cvar earlier in the script. + if ((ValueVar.GetFlags() & CVAR_SERVERINFO) && gamestate != GS_STARTUP && !demoplayback) + { + if (netgame && !players[consoleplayer].settings_controller) + { + Printf ("Only setting controllers can change %s\n", Name); + return; + } + D_SendServerFlagChange (&ValueVar, BitNum, newval); + } + else +#endif + { + int val = *ValueVar; + if (newval) + val |= BitVal; + else + val &= ~BitVal; + ValueVar = val; + } +} + +// +// Mask cvar implementation +// +// Similar to FFlagCVar but can have multiple bits +// + +FMaskCVar::FMaskCVar (const char *name, FIntCVar &realvar, uint32_t bitval, const char* descr) +: FBaseCVar (name, 0, NULL, descr), +ValueVar (realvar), +BitVal (bitval) +{ + int bit; + + Flags &= ~CVAR_ISDEFAULT; + + assert (bitval != 0); + + bit = 0; + while ((bitval & 1) == 0) + { + ++bit; + bitval >>= 1; + } + BitNum = bit; +} + +ECVarType FMaskCVar::GetRealType () const +{ + return CVAR_DummyInt; +} + +UCVarValue FMaskCVar::GetGenericRep (ECVarType type) const +{ + return FromInt ((ValueVar & BitVal) >> BitNum, type); +} + +UCVarValue FMaskCVar::GetFavoriteRep (ECVarType *type) const +{ + UCVarValue ret; + *type = CVAR_Int; + ret.Int = (ValueVar & BitVal) >> BitNum; + return ret; +} + +UCVarValue FMaskCVar::GetGenericRepDefault (ECVarType type) const +{ + ECVarType dummy; + UCVarValue def; + def = ValueVar.GetFavoriteRepDefault (&dummy); + return FromInt ((def.Int & BitVal) >> BitNum, type); +} + +UCVarValue FMaskCVar::GetFavoriteRepDefault (ECVarType *type) const +{ + ECVarType dummy; + UCVarValue def; + def = ValueVar.GetFavoriteRepDefault (&dummy); + def.Int = (def.Int & BitVal) >> BitNum; + *type = CVAR_Int; + return def; +} + +void FMaskCVar::SetGenericRepDefault (UCVarValue value, ECVarType type) +{ + int val = ToInt(value, type) << BitNum; + ECVarType dummy; + UCVarValue def; + def = ValueVar.GetFavoriteRepDefault (&dummy); + def.Int &= ~BitVal; + def.Int |= val; + ValueVar.SetGenericRepDefault (def, CVAR_Int); +} + +void FMaskCVar::DoSet (UCVarValue value, ECVarType type) +{ + int val = ToInt(value, type) << BitNum; + +#if 0 + // Server cvars that get changed by this need to use a special message, because + // changes are not processed until the next net update. This is a problem with + // exec scripts because all flags will base their changes off of the value of + // the "master" cvar at the time the script was run, overriding any changes + // another flag might have made to the same cvar earlier in the script. + if ((ValueVar.GetFlags() & CVAR_SERVERINFO) && gamestate != GS_STARTUP && !demoplayback) + { + if (netgame && !players[consoleplayer].settings_controller) + { + Printf ("Only setting controllers can change %s\n", Name); + return; + } + // Ugh... + for(int i = 0; i < 32; i++) + { + if (BitVal & (1<GetName(), ((*(FBaseCVar **)b))->GetName()); +} + +void FilterCompactCVars (TArray &cvars, uint32_t filter) +{ + // Accumulate all cvars that match the filter flags. + for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->m_Next) + { + if ((cvar->Flags & filter)) + cvars.Push(cvar); + } + // Now sort them, so they're in a deterministic order and not whatever + // order the linker put them in. + if (cvars.Size() > 0) + { + qsort(&cvars[0], cvars.Size(), sizeof(FBaseCVar *), sortcvars); + } +} + +void C_WriteCVars (uint8_t **demo_p, uint32_t filter, bool compact) +{ + FString dump = C_GetMassCVarString(filter, compact); + size_t dumplen = dump.Len() + 1; // include terminating \0 + memcpy(*demo_p, dump.GetChars(), dumplen); + *demo_p += dumplen; +} + +FString C_GetMassCVarString (uint32_t filter, bool compact) +{ + FBaseCVar *cvar; + FString dump; + + if (compact) + { + TArray cvars; + dump.AppendFormat("\\\\%ux", filter); + FilterCompactCVars(cvars, filter); + while (cvars.Pop (cvar)) + { + UCVarValue val = cvar->GetGenericRep(CVAR_String); + dump << '\\' << val.String; + } + } + else + { + for (cvar = CVars; cvar != NULL; cvar = cvar->m_Next) + { + if ((cvar->Flags & filter) && !(cvar->Flags & (CVAR_NOSAVE))) + { + UCVarValue val = cvar->GetGenericRep(CVAR_String); + dump << '\\' << cvar->GetName() << '\\' << val.String; + } + } + } + return dump; +} + +void C_ReadCVars (uint8_t **demo_p) +{ + char *ptr = *((char **)demo_p); + char *breakpt; + + if (*ptr++ != '\\') + return; + + if (*ptr == '\\') + { // compact mode + TArray cvars; + FBaseCVar *cvar; + uint32_t filter; + + ptr++; + breakpt = strchr (ptr, '\\'); + *breakpt = 0; + filter = strtoul (ptr, NULL, 16); + *breakpt = '\\'; + ptr = breakpt + 1; + + FilterCompactCVars (cvars, filter); + + while (cvars.Pop (cvar)) + { + UCVarValue val; + breakpt = strchr (ptr, '\\'); + if (breakpt) + *breakpt = 0; + val.String = ptr; + cvar->ForceSet (val, CVAR_String); + if (breakpt) + { + *breakpt = '\\'; + ptr = breakpt + 1; + } + else + break; + } + } + else + { + char *value; + + while ( (breakpt = strchr (ptr, '\\')) ) + { + *breakpt = 0; + value = breakpt + 1; + if ( (breakpt = strchr (value, '\\')) ) + *breakpt = 0; + + cvar_set (ptr, value); + + *(value - 1) = '\\'; + if (breakpt) + { + *breakpt = '\\'; + ptr = breakpt + 1; + } + else + { + break; + } + } + } + *demo_p += strlen (*((char **)demo_p)) + 1; +} + +struct FCVarBackup +{ + FString Name, String; +}; +static TArray CVarBackups; + +void C_BackupCVars (void) +{ + assert(CVarBackups.Size() == 0); + CVarBackups.Clear(); + + FCVarBackup backup; + + for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->m_Next) + { + if ((cvar->Flags & (CVAR_SERVERINFO|CVAR_DEMOSAVE)) && !(cvar->Flags & CVAR_LATCH)) + { + backup.Name = cvar->GetName(); + backup.String = cvar->GetGenericRep(CVAR_String).String; + CVarBackups.Push(backup); + } + } +} + +void C_RestoreCVars (void) +{ + for (unsigned int i = 0; i < CVarBackups.Size(); ++i) + { + cvar_set(CVarBackups[i].Name, CVarBackups[i].String); + } + C_ForgetCVars(); +} + +void C_ForgetCVars (void) +{ + CVarBackups.Clear(); +} + +FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev) +{ + FBaseCVar *var; + FBaseCVar *dummy; + + if (var_name == NULL) + return NULL; + + if (prev == NULL) + prev = &dummy; + + var = CVars; + *prev = NULL; + while (var) + { + if (stricmp (var->GetName (), var_name) == 0) + break; + *prev = var; + var = var->m_Next; + } + return var; +} + +FBaseCVar *FindCVarSub (const char *var_name, int namelen) +{ + FBaseCVar *var; + + if (var_name == NULL) + return NULL; + + var = CVars; + while (var) + { + const char *probename = var->GetName (); + + if (strnicmp (probename, var_name, namelen) == 0 && + probename[namelen] == 0) + { + break; + } + var = var->m_Next; + } + return var; +} + + +//=========================================================================== +// +// C_CreateCVar +// +// Create a new cvar with the specified name and type. It should not already +// exist. +// +//=========================================================================== + +FBaseCVar *C_CreateCVar(const char *var_name, ECVarType var_type, uint32_t flags) +{ + assert(FindCVar(var_name, NULL) == NULL); + flags |= CVAR_AUTO; + switch (var_type) + { + case CVAR_Bool: return new FBoolCVar(var_name, 0, flags); + case CVAR_Int: return new FIntCVar(var_name, 0, flags); + case CVAR_Float: return new FFloatCVar(var_name, 0, flags); + case CVAR_String: return new FStringCVar(var_name, NULL, flags); + //case CVAR_Color: return new FColorCVar(var_name, 0, flags); + default: return NULL; + } +} + +void UnlatchCVars (void) +{ + for (const FLatchedValue& var : LatchedValues) + { + uint32_t oldflags = var.Variable->Flags; + var.Variable->Flags &= ~(CVAR_LATCH | CVAR_SERVERINFO); + var.Variable->SetGenericRep (var.Value, var.Type); + if (var.Type == CVAR_String) + delete[] var.Value.String; + var.Variable->Flags = oldflags; + } + + LatchedValues.Clear(); +} + +void DestroyCVarsFlagged (uint32_t flags) +{ + FBaseCVar *cvar = CVars; + FBaseCVar *next = cvar; + + while(cvar) + { + next = cvar->m_Next; + + if(cvar->Flags & flags) + delete cvar; + + cvar = next; + } +} + +void C_SetCVarsToDefaults (void) +{ + FBaseCVar *cvar = CVars; + + while (cvar) + { + // Only default save-able cvars + if (cvar->Flags & CVAR_ARCHIVE) + { + UCVarValue val; + ECVarType type; + val = cvar->GetFavoriteRepDefault (&type); + cvar->SetGenericRep (val, type); + } + cvar = cvar->m_Next; + } +} + +void C_ArchiveCVars (FConfigFile *f, uint32_t filter) +{ + FBaseCVar *cvar = CVars; + + while (cvar) + { + if ((cvar->Flags & + (CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_AUTO|CVAR_SERVERINFO|CVAR_NOSAVE)) + == filter) + { + const char *const value = (cvar->Flags & CVAR_ISDEFAULT) + ? cvar->GetGenericRep(CVAR_String).String + : cvar->SafeValue.GetChars(); + f->SetValueForKey(cvar->GetName(), value); + } + cvar = cvar->m_Next; + } +} + +#if 0 +EXTERN_CVAR(Bool, sv_cheats); + +void FBaseCVar::CmdSet (const char *newval) +{ + MarkUnsafe(); + + UCVarValue val; + + // Casting away the const is safe in this case. + val.String = const_cast(newval); + SetGenericRep (val, CVAR_String); + + if (GetFlags() & CVAR_NOSET) + Printf ("%s is write protected.\n", GetName()); + else if (GetFlags() & CVAR_LATCH) + Printf ("%s will be changed for next game.\n", GetName()); +} + +CCMD (set) +{ + if (argv.argc() != 3) + { + Printf ("usage: set \n"); + } + else + { + FBaseCVar *var; + + var = FindCVar (argv[1], NULL); + if (var == NULL) + var = new FStringCVar (argv[1], NULL, CVAR_AUTO | CVAR_UNSETTABLE | cvar_defflags); + + var->CmdSet (argv[2]); + } +} + +CCMD (unset) +{ + if (argv.argc() != 2) + { + Printf ("usage: unset \n"); + } + else + { + FBaseCVar *var = FindCVar (argv[1], NULL); + if (var != NULL) + { + if (var->GetFlags() & CVAR_UNSETTABLE) + { + delete var; + } + else + { + Printf ("Cannot unset %s\n", argv[1]); + } + } + } +} + +CCMD (get) +{ + FBaseCVar *var, *prev; + + if (argv.argc() >= 2) + { + if ( (var = FindCVar (argv[1], &prev)) ) + { + UCVarValue val; + val = var->GetGenericRep (CVAR_String); + Printf ("\"%s\" is \"%s\"\n", var->GetName(), val.String); + } + else + { + Printf ("\"%s\" is unset\n", argv[1]); + } + } + else + { + Printf ("get: need variable name\n"); + } +} + +CCMD (toggle) +{ + FBaseCVar *var, *prev; + UCVarValue val; + + if (argv.argc() > 1) + { + if ( (var = FindCVar (argv[1], &prev)) ) + { + var->MarkUnsafe(); + + val = var->GetGenericRep (CVAR_Bool); + val.Bool = !val.Bool; + var->SetGenericRep (val, CVAR_Bool); + Printf ("\"%s\" = \"%s\"\n", var->GetName(), + val.Bool ? "true" : "false"); + } + } +} + +void FBaseCVar::ListVars (const char *filter, bool plain) +{ + FBaseCVar *var = CVars; + int count = 0; + + while (var) + { + if (CheckWildcards (filter, var->GetName())) + { + uint32_t flags = var->GetFlags(); + if (plain) + { // plain formatting does not include user-defined cvars + if (!(flags & CVAR_UNSETTABLE)) + { + ++count; + Printf ("%s : %s\n", var->GetName(), var->GetHumanString()); + } + } + else + { + ++count; + Printf ("%c%c%c%c%c %s = %s\n", + flags & CVAR_ARCHIVE ? 'A' : ' ', + flags & CVAR_USERINFO ? 'U' : + flags & CVAR_SERVERINFO ? 'S' : + flags & CVAR_AUTO ? 'C' : ' ', + flags & CVAR_NOSET ? '-' : + flags & CVAR_LATCH ? 'L' : + flags & CVAR_UNSETTABLE ? '*' : ' ', + flags & CVAR_MOD ? 'M' : ' ', + flags & CVAR_IGNORE ? 'X' : ' ', + var->GetName(), + var->GetHumanString()); + } + } + var = var->m_Next; + } + Printf ("%d cvars\n", count); +} + +CCMD (cvarlist) +{ + if (argv.argc() == 1) + { + FBaseCVar::ListVars (NULL, false); + } + else + { + FBaseCVar::ListVars (argv[1], false); + } +} + +CCMD (cvarlistplain) +{ + FBaseCVar::ListVars (NULL, true); +} + +CCMD (archivecvar) +{ + + if (argv.argc() == 1) + { + Printf ("Usage: archivecvar \n"); + } + else + { + FBaseCVar *var = FindCVar (argv[1], NULL); + + if (var != NULL && (var->GetFlags() & CVAR_AUTO)) + { + var->SetArchiveBit (); + } + } +} +#endif \ No newline at end of file diff --git a/source/common/console/c_cvars.h b/source/common/console/c_cvars.h new file mode 100644 index 000000000..6b506b29e --- /dev/null +++ b/source/common/console/c_cvars.h @@ -0,0 +1,436 @@ +/* +** c_cvars.h +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** 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. +**--------------------------------------------------------------------------- +** +*/ + +#ifndef __C_CVARS_H__ +#define __C_CVARS_H__ +#include "zstring.h" +#include "tarray.h" + +/* +========================================================== + +CVARS (console variables) + +========================================================== +*/ + +enum +{ + CVAR_ARCHIVE = 1, // set to cause it to be saved to config + //CVAR_USERINFO = 2, // added to userinfo when changed + CVAR_SERVERINFO = 4, // added to serverinfo when changed + CVAR_NOSET = 8, // don't allow change from console at all, + // but can be set from the command line + CVAR_LATCH = 16, // save changes until server restart + CVAR_UNSETTABLE = 32, // can unset this var from console + CVAR_DEMOSAVE = 64, // save the value of this cvar in a demo + CVAR_ISDEFAULT = 128, // is cvar unchanged since creation? + //CVAR_NOSAVE = 256, // when used with CVAR_SERVERINFO, do not save var to savegame + CVAR_NOINITCALL = 512, // don't call callback at game start + CVAR_GLOBALCONFIG = 1024, // cvar is saved to global config section + CVAR_VIDEOCONFIG = 2048, // cvar is saved to video config section (not implemented) + CVAR_AUTO = 4096, // allocated; needs to be freed when destroyed + //CVAR_MOD = 8192, // cvar was defined by a mod + //CVAR_IGNORE = 16384,// do not send cvar across the network/inaccesible from ACS (dummy mod cvar) + //CVAR_CHEAT = 32768,// can be set only when sv_cheats is enabled + //CVAR_UNSAFECONTEXT = 65536,// cvar value came from unsafe context +}; + +union UCVarValue +{ + bool Bool; + int Int; + float Float; + const char *String; +}; + +enum ECVarType +{ + CVAR_Bool, + CVAR_Int, + CVAR_Float, + CVAR_String, + CVAR_Color, // stored as CVAR_Int + CVAR_DummyBool, // just redirects to another cvar + CVAR_DummyInt, // just redirects to another cvar + CVAR_Dummy // Unknown +}; + +class FConfigFile; + +class FxCVar; + +class FBaseCVar +{ +public: + FBaseCVar (const char *name, uint32_t flags, void (*callback)(FBaseCVar &), const char *descr); + virtual ~FBaseCVar (); + + inline void Callback () { if (m_Callback) m_Callback (*this); } + + inline const char *GetName () const { return VarName.GetChars(); } + inline uint32_t GetFlags () const { return Flags; } + inline FBaseCVar *GetNext() const { return m_Next; } + + void CmdSet (const char *newval); + void ForceSet (UCVarValue value, ECVarType type, bool nouserinfosend=false); + void SetGenericRep (UCVarValue value, ECVarType type); + void ResetToDefault (); + void SetArchiveBit () { Flags |= CVAR_ARCHIVE; } + void MarkUnsafe(); + + virtual ECVarType GetRealType () const = 0; + + virtual const char *GetHumanString(int precision=-1) const; + virtual UCVarValue GetGenericRep (ECVarType type) const = 0; + virtual UCVarValue GetFavoriteRep (ECVarType *type) const = 0; + + virtual UCVarValue GetGenericRepDefault (ECVarType type) const = 0; + virtual UCVarValue GetFavoriteRepDefault (ECVarType *type) const = 0; + virtual void SetGenericRepDefault (UCVarValue value, ECVarType type) = 0; + + FBaseCVar &operator= (const FBaseCVar &var) + { UCVarValue val; ECVarType type; val = var.GetFavoriteRep (&type); SetGenericRep (val, type); return *this; } + + static void EnableNoSet (); // enable the honoring of CVAR_NOSET + static void EnableCallbacks (); + static void DisableCallbacks (); + static void ResetColors (); // recalc color cvars' indices after screen change + + static void ListVars (const char *filter, bool plain); + + const char *GetDescription() const { return Description; }; + +protected: + virtual void DoSet (UCVarValue value, ECVarType type) = 0; + + static bool ToBool (UCVarValue value, ECVarType type); + static int ToInt (UCVarValue value, ECVarType type); + static float ToFloat (UCVarValue value, ECVarType type); + static const char *ToString (UCVarValue value, ECVarType type); + static UCVarValue FromBool (bool value, ECVarType type); + static UCVarValue FromInt (int value, ECVarType type); + static UCVarValue FromFloat (float value, ECVarType type); + static UCVarValue FromString (const char *value, ECVarType type); + + FString VarName; + FString SafeValue; + FString Description; + uint32_t Flags; + +private: + FBaseCVar (const FBaseCVar &var) = delete; + FBaseCVar (const char *name, uint32_t flags); + + void (*m_Callback)(FBaseCVar &); + FBaseCVar *m_Next; + + static bool m_UseCallback; + static bool m_DoNoSet; + + friend FString C_GetMassCVarString (uint32_t filter, bool compact); + friend void C_ReadCVars (uint8_t **demo_p); + friend void C_BackupCVars (void); + friend FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev); + friend FBaseCVar *FindCVarSub (const char *var_name, int namelen); + friend void UnlatchCVars (void); + friend void DestroyCVarsFlagged (uint32_t flags); + friend void C_ArchiveCVars (FConfigFile *f, uint32_t filter); + friend void C_SetCVarsToDefaults (void); + friend void FilterCompactCVars (TArray &cvars, uint32_t filter); + friend void C_DeinitConsole(); +}; + +// Returns a string with all cvars whose flags match filter. In compact mode, +// the cvar names are omitted to save space. +FString C_GetMassCVarString (uint32_t filter, bool compact=false); + +// Writes all cvars that could effect demo sync to *demo_p. These are +// cvars that have either CVAR_SERVERINFO or CVAR_DEMOSAVE set. +void C_WriteCVars (uint8_t **demo_p, uint32_t filter, bool compact=false); + +// Read all cvars from *demo_p and set them appropriately. +void C_ReadCVars (uint8_t **demo_p); + +// Backup demo cvars. Called before a demo starts playing to save all +// cvars the demo might change. +void C_BackupCVars (void); + +// Finds a named cvar +FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev); +FBaseCVar *FindCVarSub (const char *var_name, int namelen); + +// Create a new cvar with the specified name and type +FBaseCVar *C_CreateCVar(const char *var_name, ECVarType var_type, uint32_t flags); + +// Called from G_InitNew() +void UnlatchCVars (void); + +// Destroy CVars with the matching flags; called from CCMD(restart) +void DestroyCVarsFlagged (uint32_t flags); + +// archive cvars to FILE f +void C_ArchiveCVars (FConfigFile *f, uint32_t filter); + +// initialize cvars to default values after they are created +void C_SetCVarsToDefaults (void); + +void FilterCompactCVars (TArray &cvars, uint32_t filter); + +void C_DeinitConsole(); + +class FBoolCVar : public FBaseCVar +{ + friend class FxCVar; +public: + FBoolCVar (const char *name, bool def, uint32_t flags, void (*callback)(FBoolCVar &)=NULL, const char* descr = nullptr); + + virtual ECVarType GetRealType () const; + + virtual UCVarValue GetGenericRep (ECVarType type) const; + virtual UCVarValue GetFavoriteRep (ECVarType *type) const; + virtual UCVarValue GetGenericRepDefault (ECVarType type) const; + virtual UCVarValue GetFavoriteRepDefault (ECVarType *type) const; + virtual void SetGenericRepDefault (UCVarValue value, ECVarType type); + + inline bool operator= (bool boolval) + { UCVarValue val; val.Bool = boolval; SetGenericRep (val, CVAR_Bool); return boolval; } + inline operator bool () const { return Value; } + inline bool operator *() const { return Value; } + +protected: + virtual void DoSet (UCVarValue value, ECVarType type); + + bool Value; + bool DefaultValue; +}; + +class FIntCVar : public FBaseCVar +{ + friend class FxCVar; +public: + FIntCVar (const char *name, int def, uint32_t flags, void (*callback)(FIntCVar &)=NULL, const char* descr = nullptr); + + virtual ECVarType GetRealType () const; + + virtual UCVarValue GetGenericRep (ECVarType type) const; + virtual UCVarValue GetFavoriteRep (ECVarType *type) const; + virtual UCVarValue GetGenericRepDefault (ECVarType type) const; + virtual UCVarValue GetFavoriteRepDefault (ECVarType *type) const; + virtual void SetGenericRepDefault (UCVarValue value, ECVarType type); + + int operator= (int intval) + { UCVarValue val; val.Int = intval; SetGenericRep (val, CVAR_Int); return intval; } + inline operator int () const { return Value; } + inline int operator *() const { return Value; } + +protected: + virtual void DoSet (UCVarValue value, ECVarType type); + + int Value; + int DefaultValue; + + friend class FFlagCVar; +}; + +class FFloatCVar : public FBaseCVar +{ + friend class FxCVar; +public: + FFloatCVar (const char *name, float def, uint32_t flags, void (*callback)(FFloatCVar &)=NULL, const char* descr = nullptr); + + virtual ECVarType GetRealType () const; + + virtual UCVarValue GetGenericRep (ECVarType type) const; + virtual UCVarValue GetFavoriteRep (ECVarType *type) const; + virtual UCVarValue GetGenericRepDefault (ECVarType type) const; + virtual UCVarValue GetFavoriteRepDefault (ECVarType *type) const; + virtual void SetGenericRepDefault (UCVarValue value, ECVarType type); + const char *GetHumanString(int precision) const override; + + float operator= (float floatval) + { UCVarValue val; val.Float = floatval; SetGenericRep (val, CVAR_Float); return floatval; } + inline operator float () const { return Value; } + inline float operator *() const { return Value; } + +protected: + virtual void DoSet (UCVarValue value, ECVarType type); + + float Value; + float DefaultValue; +}; + +class FStringCVar : public FBaseCVar +{ + friend class FxCVar; +public: + FStringCVar (const char *name, const char *def, uint32_t flags, void (*callback)(FStringCVar &)=NULL, const char* descr = nullptr); + ~FStringCVar (); + + virtual ECVarType GetRealType () const; + + virtual UCVarValue GetGenericRep (ECVarType type) const; + virtual UCVarValue GetFavoriteRep (ECVarType *type) const; + virtual UCVarValue GetGenericRepDefault (ECVarType type) const; + virtual UCVarValue GetFavoriteRepDefault (ECVarType *type) const; + virtual void SetGenericRepDefault (UCVarValue value, ECVarType type); + + const char *operator= (const char *stringrep) + { UCVarValue val; val.String = const_cast(stringrep); SetGenericRep (val, CVAR_String); return stringrep; } + inline operator const char * () const { return mValue; } + inline const char *operator *() const { return mValue; } + +protected: + virtual void DoSet (UCVarValue value, ECVarType type); + + FString mValue; + FString mDefaultValue; +}; + +#if 0 +class FColorCVar : public FIntCVar +{ + friend class FxCVar; +public: + FColorCVar (const char *name, int def, uint32_t flags, void (*callback)(FColorCVar &)=NULL, const char* descr = nullptr); + + virtual ECVarType GetRealType () const; + + virtual UCVarValue GetGenericRep (ECVarType type) const; + virtual UCVarValue GetGenericRepDefault (ECVarType type) const; + virtual void SetGenericRepDefault (UCVarValue value, ECVarType type); + + inline operator uint32_t () const { return Value; } + inline uint32_t operator *() const { return Value; } + +protected: + virtual void DoSet (UCVarValue value, ECVarType type); + + static UCVarValue FromInt2 (int value, ECVarType type); + static int ToInt2 (UCVarValue value, ECVarType type); +}; +#endif + +class FFlagCVar : public FBaseCVar +{ + friend class FxCVar; +public: + FFlagCVar (const char *name, FIntCVar &realvar, uint32_t bitval, const char* descr = nullptr); + + virtual ECVarType GetRealType () const; + + virtual UCVarValue GetGenericRep (ECVarType type) const; + virtual UCVarValue GetFavoriteRep (ECVarType *type) const; + virtual UCVarValue GetGenericRepDefault (ECVarType type) const; + virtual UCVarValue GetFavoriteRepDefault (ECVarType *type) const; + virtual void SetGenericRepDefault (UCVarValue value, ECVarType type); + + bool operator= (bool boolval) + { UCVarValue val; val.Bool = boolval; SetGenericRep (val, CVAR_Bool); return boolval; } + bool operator= (FFlagCVar &flag) + { UCVarValue val; val.Bool = !!flag; SetGenericRep (val, CVAR_Bool); return val.Bool; } + inline operator int () const { return (ValueVar & BitVal); } + inline int operator *() const { return (ValueVar & BitVal); } + +protected: + virtual void DoSet (UCVarValue value, ECVarType type); + + FIntCVar &ValueVar; + uint32_t BitVal; + int BitNum; +}; + +class FMaskCVar : public FBaseCVar +{ + friend class FxCVar; +public: + FMaskCVar (const char *name, FIntCVar &realvar, uint32_t bitval, const char* descr = nullptr); + + virtual ECVarType GetRealType () const; + + virtual UCVarValue GetGenericRep (ECVarType type) const; + virtual UCVarValue GetFavoriteRep (ECVarType *type) const; + virtual UCVarValue GetGenericRepDefault (ECVarType type) const; + virtual UCVarValue GetFavoriteRepDefault (ECVarType *type) const; + virtual void SetGenericRepDefault (UCVarValue value, ECVarType type); + + inline operator int () const { return (ValueVar & BitVal) >> BitNum; } + inline int operator *() const { return (ValueVar & BitVal) >> BitNum; } + +protected: + virtual void DoSet (UCVarValue value, ECVarType type); + + FIntCVar &ValueVar; + uint32_t BitVal; + int BitNum; +}; + +extern int cvar_defflags; + +FBaseCVar *cvar_set (const char *var_name, const char *value); +FBaseCVar *cvar_forceset (const char *var_name, const char *value); + +inline FBaseCVar *cvar_set (const char *var_name, const uint8_t *value) { return cvar_set (var_name, (const char *)value); } +inline FBaseCVar *cvar_forceset (const char *var_name, const uint8_t *value) { return cvar_forceset (var_name, (const char *)value); } + + + +// Restore demo cvars. Called after demo playback to restore all cvars +// that might possibly have been changed during the course of demo playback. +void C_RestoreCVars (void); + +void C_ForgetCVars (void); + + +#define CUSTOM_CVAR(type,name,def,flags) \ + static void cvarfunc_##name(F##type##CVar &); \ + F##type##CVar name (#name, def, flags, cvarfunc_##name); \ + static void cvarfunc_##name(F##type##CVar &self) + +#define CVAR(type,name,def,flags) \ + F##type##CVar name (#name, def, flags); + +#define EXTERN_CVAR(type,name) extern F##type##CVar name; + +#define CUSTOM_CVARD(type,name,def,flags,descr) \ + static void cvarfunc_##name(F##type##CVar &); \ + F##type##CVar name (#name, def, flags, cvarfunc_##name, descr); \ + static void cvarfunc_##name(F##type##CVar &self) + +#define CVARD(type,name,def,flags, descr) \ + F##type##CVar name (#name, def, flags, descr); + + +extern FBaseCVar *CVars; + +#endif //__C_CVARS_H__