From 486910a9a174fd34a2d415231c6a504bd446eb73 Mon Sep 17 00:00:00 2001 From: Nicholas Hastings Date: Wed, 13 Aug 2014 14:26:18 -0700 Subject: [PATCH] Add missing files --- core/logic/Logger.cpp | 551 ++++++++++++++++++++++++++++ core/logic/Logger.h | 113 ++++++ core/logic/smn_core.cpp | 777 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1441 insertions(+) create mode 100644 core/logic/Logger.cpp create mode 100644 core/logic/Logger.h create mode 100644 core/logic/smn_core.cpp diff --git a/core/logic/Logger.cpp b/core/logic/Logger.cpp new file mode 100644 index 00000000..9cfd7246 --- /dev/null +++ b/core/logic/Logger.cpp @@ -0,0 +1,551 @@ +/** + * vim: set ts=4 sw=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include +#include "Logger.h" +#include +#include + +Logger g_Logger; + +/** + * :TODO: This should be creating the log folder if it doesn't exist + */ + +ConfigResult Logger::OnSourceModConfigChanged(const char *key, + const char *value, + ConfigSource source, + char *error, + size_t maxlength) +{ + if (strcasecmp(key, "Logging") == 0) + { + bool state; + + if (strcasecmp(value, "on") == 0) + { + state = true; + } else if (strcasecmp(value, "off") == 0) { + state = false; + } else { + smcore.Format(error, maxlength, "Invalid value: must be \"on\" or \"off\""); + return ConfigResult_Reject; + } + + if (source == ConfigSource_Console) + { + state ? EnableLogging() : DisableLogging(); + } else { + m_InitialState = state; + } + + return ConfigResult_Accept; + } else if (strcasecmp(key, "LogMode") == 0) { + if (strcasecmp(value, "daily") == 0) + { + m_Mode = LoggingMode_Daily; + } else if (strcasecmp(value, "map") == 0) { + m_Mode = LoggingMode_PerMap; + } else if (strcasecmp(value, "game") == 0) { + m_Mode = LoggingMode_Game; + } else { + smcore.Format(error, maxlength, "Invalid value: must be [daily|map|game]"); + return ConfigResult_Reject; + } + + return ConfigResult_Accept; + } + + return ConfigResult_Ignore; +} + +void Logger::OnSourceModStartup(bool late) +{ + InitLogger(m_Mode); +} + +void Logger::OnSourceModAllShutdown() +{ + CloseLogger(); +} + +void Logger::OnSourceModLevelChange(const char *mapName) +{ + MapChange(mapName); +} + +void Logger::_NewMapFile() +{ + if (!m_Active) + { + return; + } + + /* Append "Log file closed" to previous log file */ + _CloseFile(); + + char _filename[256]; + int i = 0; + + time_t t = g_pSM->GetAdjustedTime(); + tm *curtime = localtime(&t); + + while (true) + { + g_pSM->BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%02d%02d%03d.log", curtime->tm_mon + 1, curtime->tm_mday, i); + FILE *fp = fopen(_filename, "r"); + if (!fp) + { + break; + } + fclose(fp); + i++; + } + m_NrmFileName.assign(_filename); + + FILE *fp = fopen(m_NrmFileName.c_str(), "w"); + if (!fp) + { + char error[255]; + libsys->GetPlatformError(error, sizeof(error)); + LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", m_NrmFileName.c_str()); + LogFatal("[SM] Platform returned error: \"%s\"", error); + LogFatal("[SM] Logging has been disabled."); + m_Active = false; + return; + } else { + char date[32]; + strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime); + fprintf(fp, "L %s: SourceMod log file started (file \"L%02d%02d%03d.log\") (Version \"%s\")\n", date, curtime->tm_mon + 1, curtime->tm_mday, i, SOURCEMOD_VERSION); + fclose(fp); + } +} + +void Logger::_CloseFile() +{ + if (!m_Active) + { + return; + } + + FILE *fp = NULL; + if (!m_NrmFileName.empty()) + { + fp = fopen(m_NrmFileName.c_str(), "r+"); + if (fp) + { + fseek(fp, 0, SEEK_END); + LogMessage("Log file closed."); + fclose(fp); + } + m_NrmFileName.clear(); + } + + if (!m_ErrMapStart) + { + return; + } + fp = fopen(m_ErrFileName.c_str(), "r+"); + if (fp) + { + fseek(fp, 0, SEEK_END); + LogError("Error log file session closed."); + fclose(fp); + } + m_ErrFileName.clear(); +} + +void Logger::InitLogger(LoggingMode mode) +{ + m_Mode = mode; + m_Active = m_InitialState; + + time_t t = g_pSM->GetAdjustedTime(); + tm *curtime = localtime(&t); + m_NrmCurDay = curtime->tm_mday; + m_ErrCurDay = curtime->tm_mday; + + char _filename[256]; + g_pSM->BuildPath(Path_SM, _filename, sizeof(_filename), "logs/errors_%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday); + m_ErrFileName.assign(_filename); + + switch (m_Mode) + { + case LoggingMode_PerMap: + { + if (!m_Active) + { + m_DelayedStart = true; + } + break; + } + case LoggingMode_Daily: + { + g_pSM->BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday); + m_NrmFileName.assign(_filename); + m_DailyPrintHdr = true; + break; + } + default: + { + /* do nothing... */ + break; + } + } +} + +void Logger::CloseLogger() +{ + _CloseFile(); +} + +void Logger::LogToOpenFile(FILE *fp, const char *msg, ...) +{ + if (!m_Active) + { + return; + } + + va_list ap; + va_start(ap, msg); + LogToOpenFileEx(fp, msg, ap); + va_end(ap); +} + +void Logger::LogToFileOnly(FILE *fp, const char *msg, ...) +{ + if (!m_Active) + { + return; + } + + va_list ap; + va_start(ap, msg); + LogToFileOnlyEx(fp, msg, ap); + va_end(ap); +} + +void Logger::LogToOpenFileEx(FILE *fp, const char *msg, va_list ap) +{ + if (!m_Active) + { + return; + } + + static ConVar *sv_logecho = smcore.FindConVar("sv_logecho"); + + char buffer[3072]; + smcore.FormatArgs(buffer, sizeof(buffer), msg, ap); + + char date[32]; + time_t t = g_pSM->GetAdjustedTime(); + tm *curtime = localtime(&t); + strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime); + + fprintf(fp, "L %s: %s\n", date, buffer); + + if (!sv_logecho || smcore.GetCvarBool(sv_logecho)) + { + static char conBuffer[4096]; + smcore.Format(conBuffer, sizeof(conBuffer), "L %s: %s\n", date, buffer); + smcore.ConPrint(conBuffer); + } +} + +void Logger::LogToFileOnlyEx(FILE *fp, const char *msg, va_list ap) +{ + if (!m_Active) + { + return; + } + + char buffer[3072]; + smcore.FormatArgs(buffer, sizeof(buffer), msg, ap); + + char date[32]; + time_t t = g_pSM->GetAdjustedTime(); + tm *curtime = localtime(&t); + strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime); + + fprintf(fp, "L %s: %s\n", date, buffer); + fflush(fp); +} + +void Logger::LogMessage(const char *vafmt, ...) +{ + va_list ap; + va_start(ap, vafmt); + LogMessageEx(vafmt, ap); + va_end(ap); +} + +void Logger::LogMessageEx(const char *vafmt, va_list ap) +{ + if (!m_Active) + { + return; + } + + if (m_Mode == LoggingMode_Game) + { + _PrintToGameLog(vafmt, ap); + return; + } + + if (m_DelayedStart) + { + m_DelayedStart = false; + _NewMapFile(); + } + + time_t t = g_pSM->GetAdjustedTime(); + tm *curtime = localtime(&t); + + FILE *fp = NULL; + if (m_Mode == LoggingMode_PerMap) + { + fp = fopen(m_NrmFileName.c_str(), "a+"); + if (!fp) + { + _NewMapFile(); + fp = fopen(m_NrmFileName.c_str(), "a+"); + if (!fp) + { + goto print_error; + } + } + } else { + if (m_NrmCurDay != curtime->tm_mday) + { + char _filename[256]; + g_pSM->BuildPath(Path_SM, _filename, sizeof(_filename), "logs/L%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday); + m_NrmFileName.assign(_filename); + m_NrmCurDay = curtime->tm_mday; + m_DailyPrintHdr = true; + } + fp = fopen(m_NrmFileName.c_str(), "a+"); + } + + if (fp) + { + if (m_DailyPrintHdr) + { + char date[32]; + m_DailyPrintHdr = false; + strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime); + fprintf(fp, "L %s: SourceMod log file session started (file \"L%04d%02d%02d.log\") (Version \"%s\")\n", date, curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday, SOURCEMOD_VERSION); + } + LogToOpenFileEx(fp, vafmt, ap); + fclose(fp); + } else { + goto print_error; + } + + return; +print_error: + char error[255]; + libsys->GetPlatformError(error, sizeof(error)); + LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", m_NrmFileName.c_str()); + LogFatal("[SM] Platform returned error: \"%s\"", error); + LogFatal("[SM] Logging has been disabled."); + m_Active = false; +} + +void Logger::LogError(const char *vafmt, ...) +{ + va_list ap; + va_start(ap, vafmt); + LogErrorEx(vafmt, ap); + va_end(ap); +} + +void Logger::LogErrorEx(const char *vafmt, va_list ap) +{ + if (!m_Active) + { + return; + } + + time_t t = g_pSM->GetAdjustedTime(); + tm *curtime = localtime(&t); + + if (curtime->tm_mday != m_ErrCurDay) + { + char _filename[256]; + g_pSM->BuildPath(Path_SM, _filename, sizeof(_filename), "logs/errors_%04d%02d%02d.log", curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday); + m_ErrFileName.assign(_filename); + m_ErrCurDay = curtime->tm_mday; + m_ErrMapStart = false; + } + + FILE *fp = fopen(m_ErrFileName.c_str(), "a+"); + if (fp) + { + if (!m_ErrMapStart) + { + char date[32]; + strftime(date, sizeof(date), "%m/%d/%Y - %H:%M:%S", curtime); + fprintf(fp, "L %s: SourceMod error session started\n", date); + fprintf(fp, "L %s: Info (map \"%s\") (file \"errors_%04d%02d%02d.log\")\n", date, m_CurMapName.c_str(), curtime->tm_year + 1900, curtime->tm_mon + 1, curtime->tm_mday); + m_ErrMapStart = true; + } + LogToOpenFileEx(fp, vafmt, ap); + fclose(fp); + } + else + { + char error[255]; + libsys->GetPlatformError(error, sizeof(error)); + LogFatal("[SM] Unexpected fatal logging error (file \"%s\")", m_NrmFileName.c_str()); + LogFatal("[SM] Platform returned error: \"%s\"", error); + LogFatal("[SM] Logging has been disabled."); + m_Active = false; + return; + } +} + +void Logger::MapChange(const char *mapname) +{ + m_CurMapName.assign(mapname); + + switch (m_Mode) + { + case LoggingMode_Daily: + { + LogMessage("-------- Mapchange to %s --------", mapname); + break; + } + case LoggingMode_PerMap: + { + _NewMapFile(); + break; + } + default: + { + /* Do nothing... */ + break; + } + } + + if (m_ErrMapStart) + { + LogError("Error log file session closed."); + } + m_ErrMapStart = false; +} + +void Logger::_PrintToGameLog(const char *fmt, va_list ap) +{ + char msg[3072]; + size_t len; + + len = vsnprintf(msg, sizeof(msg)-2, fmt, ap); + len = (len >= sizeof(msg)) ? (sizeof(msg) - 2) : len; + + msg[len++] = '\n'; + msg[len] = '\0'; + + Engine_LogPrintWrapper(msg); +} + +const char *Logger::GetLogFileName(LogType type) const +{ + switch (type) + { + case LogType_Normal: + { + return m_NrmFileName.c_str(); + } + case LogType_Error: + { + return m_ErrFileName.c_str(); + } + default: + { + return ""; + } + } +} + +LoggingMode Logger::GetLoggingMode() const +{ + return m_Mode; +} + +void Logger::EnableLogging() +{ + if (m_Active) + { + return; + } + m_Active = true; + LogMessage("[SM] Logging enabled manually by user."); +} + +void Logger::DisableLogging() +{ + if (!m_Active) + { + return; + } + LogMessage("[SM] Logging disabled manually by user."); + m_Active = false; +} + +void Logger::LogFatal(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + LogFatalEx(msg, ap); + va_end(ap); +} + +void Logger::LogFatalEx(const char *msg, va_list ap) +{ + /* :TODO: make this print all pretty-like + * In fact, the pretty log printing function should be abstracted. + * It's already implemented twice which is bad. + */ + + char path[PLATFORM_MAX_PATH]; + + g_pSM->BuildPath(Path_Game, path, sizeof(path), "sourcemod_fatal.log"); + + FILE *fp = fopen(path, "at"); + if (fp) + { + m_Active = true; + LogToOpenFileEx(fp, msg, ap); + m_Active = false; + fclose(fp); + } +} diff --git a/core/logic/Logger.h b/core/logic/Logger.h new file mode 100644 index 00000000..e472b404 --- /dev/null +++ b/core/logic/Logger.h @@ -0,0 +1,113 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_CLOGGER_H_ +#define _INCLUDE_SOURCEMOD_CLOGGER_H_ + +#include +#include + +#include "common_logic.h" + +using namespace SourceHook; + +enum LogType +{ + LogType_Normal, + LogType_Error +}; + +enum LoggingMode +{ + LoggingMode_Daily, + LoggingMode_PerMap, + LoggingMode_Game +}; + +class Logger : public SMGlobalClass, public ILogger +{ +public: + Logger() : m_Mode(LoggingMode_Daily), m_ErrMapStart(false), + m_Active(false), m_DelayedStart(false), m_DailyPrintHdr(false), + m_InitialState(true) + { + } +public: //SMGlobalClass + ConfigResult OnSourceModConfigChanged(const char *key, + const char *value, + ConfigSource source, + char *error, + size_t maxlength); + void OnSourceModStartup(bool late); + void OnSourceModAllShutdown(); + void OnSourceModLevelChange(const char *mapName); +public: + void InitLogger(LoggingMode mode); + void CloseLogger(); + void EnableLogging(); + void DisableLogging(); + void LogMessage(const char *msg, ...); + void LogMessageEx(const char *msg, va_list ap); + void LogError(const char *msg, ...); + void LogErrorEx(const char *msg, va_list ap); + void LogFatal(const char *msg, ...); + void LogFatalEx(const char *msg, va_list ap); + void LogToOpenFile(FILE *fp, const char *msg, ...); + void LogToOpenFileEx(FILE *fp, const char *msg, va_list ap); + /* This version does not print to console, and is thus thread-safe */ + void LogToFileOnly(FILE *fp, const char *msg, ...); + void LogToFileOnlyEx(FILE *fp, const char *msg, va_list ap); + void MapChange(const char *mapname); + const char *GetLogFileName(LogType type) const; + LoggingMode GetLoggingMode() const; +private: + void _CloseFile(); + void _NewMapFile(); + void _PrintToGameLog(const char *fmt, va_list ap); +private: + String m_NrmFileName; + String m_ErrFileName; + String m_CurMapName; + LoggingMode m_Mode; + int m_NrmCurDay; + int m_ErrCurDay; + bool m_ErrMapStart; + bool m_Active; + bool m_DelayedStart; + bool m_DailyPrintHdr; + bool m_InitialState; +}; + +void Engine_LogPrintWrapper(const char *msg); + +extern Logger g_Logger; + +#endif // _INCLUDE_SOURCEMOD_CLOGGER_H_ diff --git a/core/logic/smn_core.cpp b/core/logic/smn_core.cpp new file mode 100644 index 00000000..50eb0e5d --- /dev/null +++ b/core/logic/smn_core.cpp @@ -0,0 +1,777 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include +#include +#include "common_logic.h" +#include "Logger.h" + +#include +#include + +#include +#include + +#if defined PLATFORM_WINDOWS +#include +#elif defined PLATFORM_POSIX +#include +#include +#include +#endif + +HandleType_t g_PlIter; + +IForward *g_OnLogAction = NULL; + +static ConVar *sm_datetime_format = NULL; + +class CoreNativeHelpers : + public SMGlobalClass, + public IHandleTypeDispatch +{ +public: + void OnSourceModAllInitialized() + { + HandleAccess hacc; + handlesys->InitAccessDefaults(NULL, &hacc); + hacc.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER; + + g_PlIter = handlesys->CreateType("PluginIterator", this, 0, NULL, NULL, g_pCoreIdent, NULL); + + g_OnLogAction = forwardsys->CreateForward("OnLogAction", + ET_Hook, + 5, + NULL, + Param_Cell, + Param_Cell, + Param_Cell, + Param_Cell, + Param_String); + + sm_datetime_format = smcore.FindConVar("sm_datetime_format"); + } + void OnHandleDestroy(HandleType_t type, void *object) + { + IPluginIterator *iter = (IPluginIterator *)object; + iter->Release(); + } + void OnSourceModShutdown() + { + forwardsys->ReleaseForward(g_OnLogAction); + handlesys->RemoveType(g_PlIter, g_pCoreIdent); + } +} g_CoreNativeHelpers; + +void LogAction(Handle_t hndl, int type, int client, int target, const char *message) +{ + if (g_OnLogAction->GetFunctionCount()) + { + cell_t result = 0; + g_OnLogAction->PushCell(hndl); + g_OnLogAction->PushCell(type); + g_OnLogAction->PushCell(client); + g_OnLogAction->PushCell(target); + g_OnLogAction->PushString(message); + g_OnLogAction->Execute(&result); + + if (result >= (ResultType)Pl_Handled) + { + return; + } + } + + const char *logtag = "SM"; + if (type == 2) + { + HandleError err; + IPlugin *pPlugin = scripts->FindPluginByHandle(hndl, &err); + if (pPlugin) + { + logtag = pPlugin->GetFilename(); + } + } + + g_Logger.LogMessage("[%s] %s", logtag, message); +} + + static cell_t ThrowError(IPluginContext *pContext, const cell_t *params) +{ + char buffer[512]; + + g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE); + + g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1); + + if (pContext->GetLastNativeError() == SP_ERROR_NONE) + { + pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", buffer); + } + + return 0; +} + +static cell_t GetTime(IPluginContext *pContext, const cell_t *params) +{ + time_t t = g_pSM->GetAdjustedTime(); + cell_t *addr; + pContext->LocalToPhysAddr(params[1], &addr); + + *(time_t *)addr = t; + + return static_cast(t); +} + +#if defined SUBPLATFORM_SECURECRT +void _ignore_invalid_parameter( + const wchar_t * expression, + const wchar_t * function, + const wchar_t * file, + unsigned int line, + uintptr_t pReserved + ) +{ + /* Wow we don't care, thanks Microsoft. */ +} +#endif + +static cell_t FormatTime(IPluginContext *pContext, const cell_t *params) +{ + char *format, *buffer; + pContext->LocalToString(params[1], &buffer); + pContext->LocalToStringNULL(params[3], &format); + + if (format == NULL) + { + format = const_cast(smcore.GetCvarString(sm_datetime_format)); + } + +#if defined SUBPLATFORM_SECURECRT + _invalid_parameter_handler handler = _set_invalid_parameter_handler(_ignore_invalid_parameter); +#endif + + time_t t = (params[4] == -1) ? g_pSM->GetAdjustedTime() : (time_t)params[4]; + size_t written = strftime(buffer, params[2], format, localtime(&t)); + +#if defined SUBPLATFORM_SECURECRT + _set_invalid_parameter_handler(handler); +#endif + + if (params[2] && format[0] != '\0' && !written) + { + pContext->ThrowNativeError("Invalid time format or buffer too small"); + return 0; + } + + return 1; +} + +static cell_t GetPluginIterator(IPluginContext *pContext, const cell_t *params) +{ + IPluginIterator *iter = scripts->GetPluginIterator(); + + Handle_t hndl = handlesys->CreateHandle(g_PlIter, iter, pContext->GetIdentity(), g_pCoreIdent, NULL); + + if (hndl == BAD_HANDLE) + { + iter->Release(); + } + + return hndl; +} + +static cell_t MorePlugins(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = (Handle_t)params[1]; + HandleError err; + IPluginIterator *pIter; + + HandleSecurity sec; + sec.pIdentity = g_pCoreIdent; + sec.pOwner = pContext->GetIdentity(); + + if ((err=handlesys->ReadHandle(hndl, g_PlIter, &sec, (void **)&pIter)) != HandleError_None) + { + return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err); + } + + return pIter->MorePlugins() ? 1 : 0; +} + +static cell_t ReadPlugin(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = (Handle_t)params[1]; + HandleError err; + IPluginIterator *pIter; + + HandleSecurity sec; + sec.pIdentity = g_pCoreIdent; + sec.pOwner = pContext->GetIdentity(); + + if ((err=handlesys->ReadHandle(hndl, g_PlIter, &sec, (void **)&pIter)) != HandleError_None) + { + return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err); + } + + IPlugin *pPlugin = pIter->GetPlugin(); + if (!pPlugin) + { + return BAD_HANDLE; + } + + pIter->NextPlugin(); + + return pPlugin->GetMyHandle(); +} + +IPlugin *GetPluginFromHandle(IPluginContext *pContext, Handle_t hndl) +{ + if (hndl == BAD_HANDLE) + { + return scripts->FindPluginByContext(pContext->GetContext()); + } else { + HandleError err; + IPlugin *pPlugin = scripts->FindPluginByHandle(hndl, &err); + if (!pPlugin) + { + pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err); + } + return pPlugin; + } +} + +static cell_t GetPluginStatus(IPluginContext *pContext, const cell_t *params) +{ + IPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]); + if (!pPlugin) + { + return 0; + } + + return pPlugin->GetStatus(); +} + +static cell_t GetPluginFilename(IPluginContext *pContext, const cell_t *params) +{ + IPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]); + if (!pPlugin) + { + return 0; + } + + pContext->StringToLocalUTF8(params[2], params[3], pPlugin->GetFilename(), NULL); + + return 1; +} + +static cell_t IsPluginDebugging(IPluginContext *pContext, const cell_t *params) +{ + IPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]); + if (!pPlugin) + { + return 0; + } + + return pPlugin->IsDebugging() ? 1 : 0; +} + +/* Local to plugins only */ +enum PluginInfo +{ + PlInfo_Name, /**< Plugin name */ + PlInfo_Author, /**< Plugin author */ + PlInfo_Description, /**< Plugin description */ + PlInfo_Version, /**< Plugin verison */ + PlInfo_URL, /**< Plugin URL */ +}; + +static cell_t GetPluginInfo(IPluginContext *pContext, const cell_t *params) +{ + IPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]); + if (!pPlugin) + { + return 0; + } + + const sm_plugininfo_t *info = pPlugin->GetPublicInfo(); + + if (!info) + { + return 0; + } + + const char *str = NULL; + + switch ((PluginInfo)params[2]) + { + case PlInfo_Name: + { + str = info->name; + break; + } + case PlInfo_Author: + { + str = info->author; + break; + } + case PlInfo_Description: + { + str = info->description; + break; + } + case PlInfo_Version: + { + str = info->version; + break; + } + case PlInfo_URL: + { + str = info->url; + break; + } + } + + if (!str || str[0] == '\0') + { + return 0; + } + + pContext->StringToLocalUTF8(params[3], params[4], str, NULL); + + return 1; +} + +static cell_t SetFailState(IPluginContext *pContext, const cell_t *params) +{ + char *str; + SMPlugin *pPlugin; + + pContext->LocalToString(params[1], &str); + pPlugin = scripts->FindPluginByContext(pContext->GetContext()); + + if (params[0] == 1) + { + pPlugin->SetErrorState(Plugin_Error, "%s", str); + + return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", str); + } + else + { + char buffer[2048]; + + g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1); + if (pContext->GetLastNativeError() != SP_ERROR_NONE) + { + pPlugin->SetErrorState(Plugin_Error, "%s", str); + return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "Formatting error (%s)", str); + } + else + { + pPlugin->SetErrorState(Plugin_Error, "%s", buffer); + return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", buffer); + } + } + + return 0; +} + +static cell_t GetSysTickCount(IPluginContext *pContext, const cell_t *params) +{ +#if defined PLATFORM_WINDOWS + return (cell_t)GetTickCount(); +#elif defined PLATFORM_POSIX + tms tm; + clock_t ticks = times(&tm); + long ticks_per_sec = sysconf(_SC_CLK_TCK); + double fticks = (double)ticks / (double)ticks_per_sec; + fticks *= 1000.0f; + if (fticks > INT_MAX) + { + double r = (int)(fticks / INT_MAX) * (double)INT_MAX; + fticks -= r; + } + return (cell_t)fticks; +#endif +} + +static cell_t AutoExecConfig(IPluginContext *pContext, const cell_t *params) +{ + SMPlugin *plugin = scripts->FindPluginByContext(pContext->GetContext()); + + char *cfg, *folder; + pContext->LocalToString(params[2], &cfg); + pContext->LocalToString(params[3], &folder); + + if (cfg[0] == '\0') + { + static char temp_str[255]; + static char temp_file[PLATFORM_MAX_PATH]; + char *ptr; + + libsys->GetFileFromPath(temp_str, sizeof(temp_str), plugin->GetFilename()); + if ((ptr = strstr(temp_str, ".smx")) != NULL) + { + *ptr = '\0'; + } + + /* We have the raw filename! */ + g_pSM->Format(temp_file, sizeof(temp_file), "plugin.%s", temp_str); + cfg = temp_file; + } + + plugin->AddConfig(params[1] ? true : false, cfg, folder); + + return 1; +} + +static cell_t MarkNativeAsOptional(IPluginContext *pContext, const cell_t *params) +{ + char *name; + uint32_t idx; + sp_native_t *native; + + pContext->LocalToString(params[1], &name); + if (pContext->FindNativeByName(name, &idx) != SP_ERROR_NONE) + { + /* Oops! This HAS to silently fail! */ + return 0; + } + + pContext->GetNativeByIndex(idx, &native); + + native->flags |= SP_NTVFLAG_OPTIONAL; + + return 1; +} + +static cell_t RegPluginLibrary(IPluginContext *pContext, const cell_t *params) +{ + char *name; + SMPlugin *pl = scripts->FindPluginByContext(pContext->GetContext()); + + pContext->LocalToString(params[1], &name); + + pl->AddLibrary(name); + return 1; +} + +static cell_t LibraryExists(IPluginContext *pContext, const cell_t *params) +{ + char *str; + pContext->LocalToString(params[1], &str); + + if (strcmp(str, "__CanTestFeatures__") == 0) + { + return 1; + } + + if (scripts->LibraryExists(str)) + { + return 1; + } + + if (extsys->LibraryExists(str)) + { + return 1; + } + + return 0; +} + +static cell_t sm_LogAction(IPluginContext *pContext, const cell_t *params) +{ + char buffer[2048]; + g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE); + g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 3); + + if (pContext->GetLastNativeError() != SP_ERROR_NONE) + { + return 0; + } + + IPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext()); + + LogAction(pPlugin->GetMyHandle(), 2, params[1], params[2], buffer); + + return 1; +} + +static cell_t LogToFile(IPluginContext *pContext, const cell_t *params) +{ + char *file; + pContext->LocalToString(params[1], &file); + + char path[PLATFORM_MAX_PATH]; + g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file); + + FILE *fp = fopen(path, "at"); + if (!fp) + { + return pContext->ThrowNativeError("Could not open file \"%s\"", path); + } + + char buffer[2048]; + g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE); + g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2); + + if (pContext->GetLastNativeError() != SP_ERROR_NONE) + { + fclose(fp); + return 0; + } + + IPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext()); + + g_Logger.LogToOpenFile(fp, "[%s] %s", pPlugin->GetFilename(), buffer); + + fclose(fp); + + return 1; +} + +static cell_t LogToFileEx(IPluginContext *pContext, const cell_t *params) +{ + char *file; + pContext->LocalToString(params[1], &file); + + char path[PLATFORM_MAX_PATH]; + g_pSM->BuildPath(Path_Game, path, sizeof(path), "%s", file); + + FILE *fp = fopen(path, "at"); + if (!fp) + { + return pContext->ThrowNativeError("Could not open file \"%s\"", path); + } + + char buffer[2048]; + g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE); + g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2); + + if (pContext->GetLastNativeError() != SP_ERROR_NONE) + { + fclose(fp); + return 0; + } + + g_Logger.LogToOpenFile(fp, "%s", buffer); + + fclose(fp); + + return 1; +} + +static cell_t GetExtensionFileStatus(IPluginContext *pContext, const cell_t *params) +{ + char *str; + pContext->LocalToString(params[1], &str); + + IExtension *pExtension = extsys->FindExtensionByFile(str); + + if (!pExtension) + { + return -2; + } + + if (!pExtension->IsLoaded()) + { + return -1; + } + + char *error; + pContext->LocalToString(params[2], &error); + if (!pExtension->IsRunning(error, params[3])) + { + return 0; + } + + return 1; +} + +static cell_t FindPluginByNumber(IPluginContext *pContext, const cell_t *params) +{ + IPlugin *pPlugin = scripts->FindPluginByOrder(params[1]); + + if (pPlugin == NULL) + { + return BAD_HANDLE; + } + + return pPlugin->GetMyHandle(); +} + +static cell_t VerifyCoreVersion(IPluginContext *pContext, const cell_t *params) +{ + return 4; +} + +static cell_t GetFeatureStatus(IPluginContext *pContext, const cell_t *params) +{ + FeatureType type = (FeatureType)params[1]; + char *name; + + pContext->LocalToString(params[2], &name); + + return sharesys->TestFeature(pContext->GetRuntime(), type, name); +} + +static cell_t RequireFeature(IPluginContext *pContext, const cell_t *params) +{ + FeatureType type = (FeatureType)params[1]; + char *name; + + pContext->LocalToString(params[2], &name); + + if (sharesys->TestFeature(pContext->GetRuntime(), type, name) != FeatureStatus_Available) + { + char buffer[255]; + char *msg = buffer; + char default_message[255]; + SMPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext()); + + g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 3); + if (pContext->GetLastNativeError() != SP_ERROR_NONE || buffer[0] == '\0') + { + g_pSM->Format(default_message, sizeof(default_message), "Feature \"%s\" not available", name); + msg = default_message; + } + pPlugin->SetErrorState(Plugin_Error, "%s", msg); + return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", msg); + } + + return 1; +} + +enum NumberType +{ + NumberType_Int8, + NumberType_Int16, + NumberType_Int32 +}; + +//memory addresses below 0x10000 are automatically considered invalid for dereferencing +#define VALID_MINIMUM_MEMORY_ADDRESS 0x10000 + +static cell_t LoadFromAddress(IPluginContext *pContext, const cell_t *params) +{ + void *addr = reinterpret_cast(params[1]); + + if (addr == NULL) + { + return pContext->ThrowNativeError("Address cannot be null"); + } + else if (reinterpret_cast(addr) < VALID_MINIMUM_MEMORY_ADDRESS) + { + return pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory.", addr); + } + NumberType size = static_cast(params[2]); + + switch(size) + { + case NumberType_Int8: + return *reinterpret_cast(addr); + case NumberType_Int16: + return *reinterpret_cast(addr); + case NumberType_Int32: + return *reinterpret_cast(addr); + default: + return pContext->ThrowNativeError("Invalid number types %d", size); + } +} + + +static cell_t StoreToAddress(IPluginContext *pContext, const cell_t *params) +{ + void *addr = reinterpret_cast(params[1]); + + if (addr == NULL) + { + return pContext->ThrowNativeError("Address cannot be null"); + } + else if (reinterpret_cast(addr) < VALID_MINIMUM_MEMORY_ADDRESS) + { + return pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory.", addr); + } + cell_t data = params[2]; + + NumberType size = static_cast(params[3]); + + switch(size) + { + case NumberType_Int8: + SourceHook::SetMemAccess(addr, sizeof(uint8_t), SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC); + *reinterpret_cast(addr) = data; + break; + case NumberType_Int16: + SourceHook::SetMemAccess(addr, sizeof(uint16_t), SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC); + *reinterpret_cast(addr) = data; + break; + case NumberType_Int32: + SourceHook::SetMemAccess(addr, sizeof(uint32_t), SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC); + *reinterpret_cast(addr) = data; + break; + default: + return pContext->ThrowNativeError("Invalid number types %d", size); + } + + return 0; +} + +REGISTER_NATIVES(coreNatives) +{ + {"ThrowError", ThrowError}, + {"GetTime", GetTime}, + {"FormatTime", FormatTime}, + {"GetPluginIterator", GetPluginIterator}, + {"MorePlugins", MorePlugins}, + {"ReadPlugin", ReadPlugin}, + {"GetPluginStatus", GetPluginStatus}, + {"GetPluginFilename", GetPluginFilename}, + {"IsPluginDebugging", IsPluginDebugging}, + {"GetPluginInfo", GetPluginInfo}, + {"SetFailState", SetFailState}, + {"GetSysTickCount", GetSysTickCount}, + {"AutoExecConfig", AutoExecConfig}, + {"MarkNativeAsOptional", MarkNativeAsOptional}, + {"RegPluginLibrary", RegPluginLibrary}, + {"LibraryExists", LibraryExists}, + {"LogAction", sm_LogAction}, + {"LogToFile", LogToFile}, + {"LogToFileEx", LogToFileEx}, + {"GetExtensionFileStatus", GetExtensionFileStatus}, + {"FindPluginByNumber", FindPluginByNumber}, + {"VerifyCoreVersion", VerifyCoreVersion}, + {"GetFeatureStatus", GetFeatureStatus}, + {"RequireFeature", RequireFeature}, + {"LoadFromAddress", LoadFromAddress}, + {"StoreToAddress", StoreToAddress}, + {NULL, NULL}, +}; \ No newline at end of file