diff --git a/amxmodx/AMBuilder b/amxmodx/AMBuilder index 53075490..bc954f51 100644 --- a/amxmodx/AMBuilder +++ b/amxmodx/AMBuilder @@ -96,6 +96,7 @@ binary.sources = [ '../public/memtools/CDetour/asm/asm.c', 'CLibrarySys.cpp', 'CGameConfigs.cpp', + 'gameconfigs.cpp', ] if builder.target_platform == 'windows': diff --git a/amxmodx/CGameConfigs.cpp b/amxmodx/CGameConfigs.cpp index 35c07655..8678d5c6 100644 --- a/amxmodx/CGameConfigs.cpp +++ b/amxmodx/CGameConfigs.cpp @@ -14,7 +14,7 @@ CGameConfigManager ConfigManager; static CGameMasterReader MasterReader; -// +// // GAME CONFIG // @@ -23,6 +23,8 @@ enum PSTATE_NONE, PSTATE_GAMES, PSTATE_GAMEDEFS, + PSTATE_GAMEDEFS_CLASSES, + PSTATE_GAMEDEFS_CLASSES_CLASS, PSTATE_GAMEDEFS_OFFSETS, PSTATE_GAMEDEFS_OFFSETS_OFFSET, PSTATE_GAMEDEFS_KEYS, @@ -103,6 +105,8 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n m_ShouldBeReadingDefault = true; strncopy(m_Game, name, sizeof(m_Game)); + m_Class[0] = '\0'; + m_MatchedClasses = false; m_ParseState = PSTATE_GAMEDEFS; } else @@ -112,8 +116,22 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n break; } case PSTATE_GAMEDEFS: + case PSTATE_GAMEDEFS_CLASSES_CLASS: { - if (strcmp(name, "Offsets") == 0) + if (strcmp(name, "Classes") == 0) + { + if (!m_Class[0]) + { + m_ParseState = PSTATE_GAMEDEFS_CLASSES; + m_MatchedClasses = true; + } + else + { + ++m_IgnoreLevel; + } + break; + } + else if (strcmp(name, "Offsets") == 0) { m_ParseState = PSTATE_GAMEDEFS_OFFSETS; } @@ -149,14 +167,18 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n break; } - m_IgnoreLevel++; + ++m_IgnoreLevel; } break; } + case PSTATE_GAMEDEFS_CLASSES: + { + strncopy(m_Class, name, sizeof(m_Class)); + m_ParseState = PSTATE_GAMEDEFS_CLASSES_CLASS; + break; + } case PSTATE_GAMEDEFS_OFFSETS: { - m_Class[0] = '\0'; - strncopy(m_Offset, name, sizeof(m_Offset)); m_ParseState = PSTATE_GAMEDEFS_OFFSETS_OFFSET; @@ -174,7 +196,7 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n } case PSTATE_GAMEDEFS_CUSTOM: { - m_CustomLevel++; + ++m_CustomLevel; return m_CustomHandler->ReadSMC_NewSection(states, name); break; } @@ -228,11 +250,7 @@ SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key { case PSTATE_GAMEDEFS_OFFSETS_OFFSET: { - if (strcmp(key, "class") == 0) - { - strncopy(m_Class, value, sizeof(m_Class)); - } - else if (g_LibSys.IsPlatformCompatible(key, &m_MatchedPlatform)) + if (g_LibSys.IsPlatformCompatible(key, &m_MatchedPlatform)) { if (m_Class[0]) { @@ -246,7 +264,7 @@ SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key { ic->value = new OffsetClass; ic->value->list.insert(m_Offset, atoi(value)); - } + } } else { @@ -319,7 +337,7 @@ SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key } else { - AMXXLOG_Error("[SM] Error parsing Address \"%s\", does not support more than %d read offsets (gameconf \"%s\")", + AMXXLOG_Error("[SM] Error parsing Address \"%s\", does not support more than %d read offsets (gameconf \"%s\")", m_Address, limit, m_CurrentPath); } } @@ -365,6 +383,18 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states) m_ParseState = PSTATE_GAMES; break; } + case PSTATE_GAMEDEFS_CLASSES: + { + m_MatchedClasses = false; + m_ParseState = PSTATE_GAMEDEFS; + break; + } + case PSTATE_GAMEDEFS_CLASSES_CLASS: + { + m_ParseState = PSTATE_GAMEDEFS_CLASSES; + m_Class[0] = '\0'; + break; + } case PSTATE_GAMEDEFS_CUSTOM: { m_ParseState = PSTATE_GAMEDEFS; @@ -372,9 +402,13 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states) break; } case PSTATE_GAMEDEFS_KEYS: + { + m_ParseState = m_MatchedClasses ? PSTATE_GAMEDEFS_CLASSES_CLASS : PSTATE_GAMEDEFS; + break; + } case PSTATE_GAMEDEFS_OFFSETS: { - m_ParseState = PSTATE_GAMEDEFS; + m_ParseState = m_MatchedClasses ? PSTATE_GAMEDEFS_CLASSES_CLASS : PSTATE_GAMEDEFS; break; } case PSTATE_GAMEDEFS_OFFSETS_OFFSET: @@ -389,7 +423,7 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states) m_IgnoreLevel = 1; m_ParseState = PSTATE_GAMES; } - else + else { m_ParseState = PSTATE_GAMEDEFS; } @@ -397,7 +431,7 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states) } case PSTATE_GAMEDEFS_SIGNATURES: { - m_ParseState = PSTATE_GAMEDEFS; + m_ParseState = m_MatchedClasses ? PSTATE_GAMEDEFS_CLASSES_CLASS : PSTATE_GAMEDEFS; break; } case PSTATE_GAMEDEFS_SIGNATURES_SIG: @@ -413,7 +447,7 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states) { addressInBase = reinterpret_cast(MDLL_Spawn); } - else if (strcmp(TempSig.library, "engine") == 0) + else if (strcmp(TempSig.library, "engine") == 0) { addressInBase = reinterpret_cast(gpGlobals); } @@ -442,7 +476,7 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states) #elif defined PLATFORM_POSIX Dl_info info; - + if (dladdr(addressInBase, &info) != 0) { void *handle = dlopen(info.dli_fname, RTLD_NOW); @@ -457,7 +491,7 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states) AMXXLOG_Error("Unable to load library \"%s\" (gameconf \"%s\")", TempSig.library, m_File); } } - else + else { AMXXLOG_Error("Unable to find library \"%s\" in memory (gameconf \"%s\")", TempSig.library, m_File); } @@ -477,7 +511,7 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states) } case PSTATE_GAMEDEFS_ADDRESSES: { - m_ParseState = PSTATE_GAMEDEFS; + m_ParseState = m_MatchedClasses ? PSTATE_GAMEDEFS_CLASSES_CLASS : PSTATE_GAMEDEFS; break; } case PSTATE_GAMEDEFS_ADDRESSES_ADDRESS: @@ -516,13 +550,16 @@ bool CGameConfig::Reparse(char *error, size_t maxlength) if (!g_LibSys.PathExists(path)) { +#if 0 + // Single config file without master g_LibSys.PathFormat(path, sizeof(path), "%s.txt", m_File); if (!EnterFile(path, error, maxlength)) { return false; } - +#endif + // Allow customizations of default gamedata files build_pathname_r(path, sizeof(path), "%s/gamedata/custom/%s.txt", dataDir, m_File); if (g_LibSys.PathExists(path)) @@ -605,10 +642,7 @@ bool CGameConfig::Reparse(char *error, size_t maxlength) bool CGameConfig::EnterFile(const char *file, char *error, size_t maxlength) { - char path[PLATFORM_MAX_PATH]; - build_pathname_r(path, sizeof(path), "%s/gamedata/%s", get_localinfo("amxx_datadir", "addons/amxmodx/data"), file); - - strncopy(m_CurrentPath, path, sizeof(m_CurrentPath)); + build_pathname_r(m_CurrentPath, sizeof(m_CurrentPath), "%s/gamedata/%s", get_localinfo("amxx_datadir", "addons/amxmodx/data"), file); m_IgnoreLevel = 0; m_ShouldBeReadingDefault = true; @@ -698,7 +732,8 @@ bool CGameConfig::GetAddress(const char *key, void **retaddr) *retaddr = nullptr; return false; } - address = *(reinterpret_cast(reinterpret_cast(address) + offset)); + + address = reinterpret_cast(reinterpret_cast(address) + offset); } *retaddr = address; @@ -723,7 +758,7 @@ bool CGameConfig::GetMemSig(const char *key, void **addr) } -// +// // CONFIG MASTER READER // @@ -839,7 +874,7 @@ SMCResult CGameMasterReader::ReadSMC_LeavingSection(const SMCStates *states) } -// +// // CONFIG MANAGER // diff --git a/amxmodx/CGameConfigs.h b/amxmodx/CGameConfigs.h index 01ab72db..e908bdb7 100644 --- a/amxmodx/CGameConfigs.h +++ b/amxmodx/CGameConfigs.h @@ -81,6 +81,7 @@ class CGameConfig char m_Offset[64]; char m_Game[256]; + bool m_MatchedClasses; bool m_ShouldBeReadingDefault; bool m_HadGame; bool m_MatchedGame; diff --git a/amxmodx/amxmodx.h b/amxmodx/amxmodx.h index af60eaa9..d0cf8e24 100755 --- a/amxmodx/amxmodx.h +++ b/amxmodx/amxmodx.h @@ -67,6 +67,7 @@ extern AMX_NATIVE_INFO g_DataStructNatives[]; extern AMX_NATIVE_INFO g_StackNatives[]; extern AMX_NATIVE_INFO g_TextParserNatives[]; extern AMX_NATIVE_INFO g_CvarNatives[]; +extern AMX_NATIVE_INFO g_GameConfigNatives[]; #if defined(_WIN32) #define DLLOAD(path) (DLHANDLE)LoadLibrary(path) diff --git a/amxmodx/gameconfigs.cpp b/amxmodx/gameconfigs.cpp new file mode 100644 index 00000000..b8768c82 --- /dev/null +++ b/amxmodx/gameconfigs.cpp @@ -0,0 +1,174 @@ +// vim: set ts=4 sw=4 tw=99 noet: +// +// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). +// Copyright (C) The AMX Mod X Development Team. +// +// This software is licensed under the GNU General Public License, version 3 or higher. +// Additional exceptions apply. For full license details, see LICENSE.txt or visit: +// https://alliedmods.net/amxmodx-license + +#include "gameconfigs.h" +#include "amxmodx.h" +#include "CGameConfigs.h" + +Handle GameConfigHandle; + +// native GameConfig:LoadGameConfigFile(const file[]); +static cell AMX_NATIVE_CALL LoadGameConfigFile(AMX *amx, cell *params) +{ + int length; + const char *filename = get_amxstring(amx, params[1], 0, length); + + IGameConfig *config = nullptr; + char error[128]; + + if (!ConfigManager.LoadGameConfigFile(filename, &config, error, sizeof(error))) + { + LogError(amx, AMX_ERR_NATIVE, "Unable to open %s: %s", filename, error); + return 0; + } + + int handle = GameConfigHandle.create(); + + auto configHandle = GameConfigHandle.lookup(handle); + + if (!configHandle) + { + return 0; + } + + configHandle->m_config = config; + + return handle; +} + +// native GameConfGetOffset(GameConfig:handle, const key[]); +static cell AMX_NATIVE_CALL GameConfGetOffset(AMX *amx, cell *params) +{ + GameConfigNative *handle = GameConfigHandle.lookup(params[1]); + + if (!handle) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid game config handle %d", params[1]); + return 0; + } + + int length; + int value; + + const char *key = get_amxstring(amx, params[2], 0, length); + + if (!handle->m_config->GetOffset(key, &value)) + { + return -1; + } + + return value; +} + +// native GameConfGetClassOffset(GameConfig:handle, const classname[], const key[]); +static cell AMX_NATIVE_CALL GameConfGetClassOffset(AMX *amx, cell *params) +{ + GameConfigNative *handle = GameConfigHandle.lookup(params[1]); + + if (!handle) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid game config handle %d", params[1]); + return 0; + } + + int length; + int value; + + const char *classname = get_amxstring(amx, params[2], 0, length); + const char *key = get_amxstring(amx, params[3], 1, length); + + if (!handle->m_config->GetOffsetByClass(classname, key, &value)) + { + return -1; + } + + return value; +} + +// native bool:GameConfGetKeyValue(GameConfig:handle, const key[], buffer[], maxlen); +static cell AMX_NATIVE_CALL GameConfGetKeyValue(AMX *amx, cell *params) +{ + GameConfigNative *handle = GameConfigHandle.lookup(params[1]); + + if (!handle) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid game config handle %d", params[1]); + return 0; + } + + int length; + const char *value; + const char *key = get_amxstring(amx, params[2], 0, length); + + if (!(value = handle->m_config->GetKeyValue(key))) + { + return 0; + } + + set_amxstring_utf8(amx, params[3], value, strlen(value), params[4]); + + return 1; +} + +// native GameConfGetAddress(GameConfig:handle, const name[]); +static cell AMX_NATIVE_CALL GameConfGetAddress(AMX *amx, cell *params) +{ + GameConfigNative *handle = GameConfigHandle.lookup(params[1]); + + if (!handle) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid game config handle %d", params[1]); + return 0; + } + + int length; + void *value; + + const char *key = get_amxstring(amx, params[2], 0, length); + + if (!handle->m_config->GetAddress(key, &value)) + { + return 0; + } + + return reinterpret_cast(value); +} + +// native CloseGameConfigFile(&GameConfig:handle); +static cell AMX_NATIVE_CALL CloseGameConfigFile(AMX *amx, cell *params) +{ + cell *address = get_amxaddr(amx, params[1]); + + GameConfigNative *handle = GameConfigHandle.lookup(*address); + + if (!handle) + { + return 0; + } + + if (GameConfigHandle.destroy(*address)) + { + *address = 0; + return 1; + } + + return 0; +} + + +AMX_NATIVE_INFO g_GameConfigNatives[] = +{ + { "LoadGameConfigFile" , LoadGameConfigFile }, + { "GameConfGetOffset" , GameConfGetOffset }, + { "GameConfGetClassOffset", GameConfGetClassOffset }, + { "GameConfGetKeyValue" , GameConfGetKeyValue }, + { "GameConfGetAddress" , GameConfGetAddress }, + { "CloseGameConfigFile" , CloseGameConfigFile }, + { nullptr , nullptr } +}; diff --git a/amxmodx/gameconfigs.h b/amxmodx/gameconfigs.h new file mode 100644 index 00000000..aed9434f --- /dev/null +++ b/amxmodx/gameconfigs.h @@ -0,0 +1,18 @@ +// vim: set ts=4 sw=4 tw=99 noet: +// +// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). +// Copyright (C) The AMX Mod X Development Team. +// +// This software is licensed under the GNU General Public License, version 3 or higher. +// Additional exceptions apply. For full license details, see LICENSE.txt or visit: +// https://alliedmods.net/amxmodx-license + +#include "IGameConfigs.h" +#include "natives_handles.h" + +struct GameConfigNative +{ + IGameConfig *m_config; +}; + +extern Handle GameConfigHandle; diff --git a/amxmodx/meta_api.cpp b/amxmodx/meta_api.cpp index 7108b397..b56ab7c8 100755 --- a/amxmodx/meta_api.cpp +++ b/amxmodx/meta_api.cpp @@ -27,6 +27,7 @@ #include "CvarManager.h" #include "CLibrarySys.h" #include "CFileSystem.h" +#include "gameconfigs.h" plugin_info_t Plugin_info = { @@ -408,6 +409,7 @@ int C_Spawn(edict_t *pent) g_TrieSnapshotHandles.clear(); g_DataPackHandles.clear(); g_TextParsersHandles.clear(); + GameConfigHandle.clear(); char map_pluginsfile_path[256]; char prefixed_map_pluginsfile[256]; diff --git a/amxmodx/modules.cpp b/amxmodx/modules.cpp index 5d32c809..b935a459 100755 --- a/amxmodx/modules.cpp +++ b/amxmodx/modules.cpp @@ -531,6 +531,7 @@ int set_amxnatives(AMX* amx, char error[128]) amx_Register(amx, g_StackNatives, -1); amx_Register(amx, g_TextParserNatives, -1); amx_Register(amx, g_CvarNatives, -1); + amx_Register(amx, g_GameConfigNatives, -1); //we're not actually gonna check these here anymore amx->flags |= AMX_FLAG_PRENIT; diff --git a/amxmodx/msvc12/amxmodx_mm.vcxproj b/amxmodx/msvc12/amxmodx_mm.vcxproj index bd63e4a9..87d7ded9 100644 --- a/amxmodx/msvc12/amxmodx_mm.vcxproj +++ b/amxmodx/msvc12/amxmodx_mm.vcxproj @@ -278,6 +278,7 @@ AssemblyAndSourceCode + @@ -361,10 +362,12 @@ + + @@ -383,6 +386,7 @@ + diff --git a/amxmodx/msvc12/amxmodx_mm.vcxproj.filters b/amxmodx/msvc12/amxmodx_mm.vcxproj.filters index ef5c2f69..c789aa43 100644 --- a/amxmodx/msvc12/amxmodx_mm.vcxproj.filters +++ b/amxmodx/msvc12/amxmodx_mm.vcxproj.filters @@ -288,6 +288,9 @@ Memtools + + Source Files + @@ -497,6 +500,12 @@ Memtools + + Header Files + + + Header Files + @@ -594,6 +603,9 @@ Pawn Includes + + Pawn Includes + diff --git a/amxmodx/natives_handles.h b/amxmodx/natives_handles.h new file mode 100644 index 00000000..5e0b3b84 --- /dev/null +++ b/amxmodx/natives_handles.h @@ -0,0 +1,88 @@ +// vim: set ts=4 sw=4 tw=99 noet: +// +// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). +// Copyright (C) The AMX Mod X Development Team. +// +// This software is licensed under the GNU General Public License, version 3 or higher. +// Additional exceptions apply. For full license details, see LICENSE.txt or visit: +// https://alliedmods.net/amxmodx-license + +#include + +template +class Handle +{ + private: + + ke::Vector m_handles; + + public: + + Handle() {} + ~Handle() + { + this->clear(); + } + + void clear() + { + for (size_t i = 0; i < m_handles.length(); ++i) + { + if (m_handles[i]) + { + delete m_handles[i]; + } + } + + m_handles.clear(); + } + + T *lookup(int handle) + { + --handle; + + if (handle < 0 || handle >= static_cast(m_handles.length())) + { + return nullptr; + } + + return m_handles[handle]; + } + + int create() + { + for (size_t i = 0; i < m_handles.length(); ++i) + { + if (!m_handles[i]) + { + m_handles[i] = new T; + + return static_cast(i) + 1; + } + } + + m_handles.append(new T); + + return m_handles.length(); + } + + bool destroy(int handle) + { + handle--; + + if (handle < 0 || handle >= static_cast(m_handles.length())) + { + return false; + } + + if (!m_handles[handle]) + { + return false; + } + + delete m_handles[handle]; + m_handles[handle] = nullptr; + + return true; + } +}; \ No newline at end of file diff --git a/plugins/include/amxmodx.inc b/plugins/include/amxmodx.inc index b58239de..becae711 100755 --- a/plugins/include/amxmodx.inc +++ b/plugins/include/amxmodx.inc @@ -30,6 +30,7 @@ #include #include #include +#include /** * Called just after server activation. diff --git a/plugins/include/gameconfig.inc b/plugins/include/gameconfig.inc new file mode 100644 index 00000000..d3724e45 --- /dev/null +++ b/plugins/include/gameconfig.inc @@ -0,0 +1,93 @@ +// vim: set ts=4 sw=4 tw=99 noet: +// +// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). +// Copyright (C) The AMX Mod X Development Team. +// +// This software is licensed under the GNU General Public License, version 3 or higher. +// Additional exceptions apply. For full license details, see LICENSE.txt or visit: +// https://alliedmods.net/amxmodx-license + +// +// Game Config Functions +// + +#if defined _gameconfigs_included + #endinput +#endif +#define _gameconfigs_included + +enum GameConfig +{ + Invalid_GameConfig = 0 +}; + +/** + * Loads a game config file. + * + * @note The file path must be relative to the 'gamedata' folder under the data folder + * and the extension should be omitted. + * + * @param file File to load + * + * @return A handle to the game config file + */ +native GameConfig:LoadGameConfigFile(const file[]); + +/** + * Returns an offset value. + * + * @param handle Game config handle + * @param key Key to retrieve from the offset section + * + * @return An offset, or -1 on failure + * @error Invalid game config handle + */ +native GameConfGetOffset(GameConfig:handle, const key[]); + +/** + * Returns an offset value given a classname. + * + * @param handle Game config handle + * @param classname Class name to match from the offset section + * @param key Key to retrieve from the offset section + * + * @return An offset, or -1 on failure + * @error Invalid game config handle + */ +native GameConfGetClassOffset(GameConfig:handle, const classname[], const key[]); + +/** + * Gets the value of a key from the "Keys" section. + * + * @param handle Game config handle + * @param key Key to retrieve from the Keys section + * @param buffer Destination string buffer + * @param maxlen Maximum length of output string buffer + * + * @return True if key existed, false otherwise + * @error Invalid game config handle + */ +native bool:GameConfGetKeyValue(GameConfig:handle, const key[], buffer[], maxlen); + +/** + * Finds an address calculation in a GameConfig file. + * + * @param handle Game config handle + * @param name Name of the property to find + * + * @return An address calculated on success, otherwise 0 on failure. + * @error Invalid game config handle + */ +native GameConfGetAddress(GameConfig:handle, const name[]); + +/** + * Destroys a game config and frees its memory. + * + * @note The function automatically sets the variable passed to it to 0 to aid + * in preventing accidental usage after destroy. + * + * @param handle Game config handle + * + * @return 1 on success, 0 if an invalid handle was passed in + */ +native CloseGameConfigFile(&GameConfig:handle); \ No newline at end of file diff --git a/public/ITextParsers.h b/public/ITextParsers.h index 2a643d32..67e27a46 100644 --- a/public/ITextParsers.h +++ b/public/ITextParsers.h @@ -32,6 +32,8 @@ #ifndef _INCLUDE_SOURCEMOD_TEXTPARSERS_INTERFACE_H_ #define _INCLUDE_SOURCEMOD_TEXTPARSERS_INTERFACE_H_ +#include // size_t + /** * @file ITextParsers.h * @brief Defines various text/file parsing functions, as well as UTF-8 support code. diff --git a/public/memtools/MemoryUtils.cpp b/public/memtools/MemoryUtils.cpp index 99ec9a94..d99c1241 100644 --- a/public/memtools/MemoryUtils.cpp +++ b/public/memtools/MemoryUtils.cpp @@ -644,7 +644,7 @@ bool MemoryUtils::GetLibraryOfAddress(const void *libPtr, char *buffer, size_t m return false; } const char *dllpath = info.dli_fname; - UTIL_Format(buffer, maxlength, "%s", dllpath); + Format(buffer, maxlength, "%s", dllpath); if (base) { *base = (uintptr_t)info.dli_fbase; @@ -708,4 +708,20 @@ size_t MemoryUtils::DecodeHexString(unsigned char *buffer, size_t maxlength, con } return written; +} + +size_t MemoryUtils::Format(char *buffer, size_t maxlength, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + size_t len = vsnprintf(buffer, maxlength, fmt, ap); + va_end(ap); + + if (len >= maxlength) + { + buffer[maxlength - 1] = '\0'; + return (maxlength - 1); + } + + return len; } \ No newline at end of file diff --git a/public/memtools/MemoryUtils.h b/public/memtools/MemoryUtils.h index b7b49662..2f22255f 100644 --- a/public/memtools/MemoryUtils.h +++ b/public/memtools/MemoryUtils.h @@ -80,6 +80,7 @@ class MemoryUtils public: size_t DecodeHexString(unsigned char *buffer, size_t maxlength, const char *hexstr); + size_t Format(char *buffer, size_t maxlength, const char *fmt, ...); #if defined(__linux__) || defined(__APPLE__) private: diff --git a/public/sdk/amxxmodule.h b/public/sdk/amxxmodule.h index 5f0feab5..c9f9e75f 100644 --- a/public/sdk/amxxmodule.h +++ b/public/sdk/amxxmodule.h @@ -2377,7 +2377,7 @@ const char * MF_GetLocalInfo (const char *name, const char *def) { } int MF_AmxReRegister (AMX *amx, AMX_NATIVE_INFO *list, int number) { return 0; } void * MF_RegisterFunctionEx (void *pfn, const char *description) { } void * MF_MessageBlock (int mode, int msg, int *opt) { } -IGameConfigManager* MF_MessageBlock () { } +IGameConfigManager* MF_GetConfigManager (void) { } #endif // MAY_NEVER_BE_DEFINED #define MF_AddNatives g_fn_AddNatives diff --git a/support/PackageScript b/support/PackageScript index 826371de..692c043b 100644 --- a/support/PackageScript +++ b/support/PackageScript @@ -264,6 +264,7 @@ scripting_files = [ 'include/file.inc', 'include/float.inc', 'include/fun.inc', + 'include/gameconfig.inc', 'include/geoip.inc', 'include/lang.inc', 'include/ns.inc',