3 * =============================================================================
5 * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
6 * =============================================================================
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, version 3.0, as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to link the
21 * code of this program (as well as its derivative works) to "Half-Life 2," the
22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 * by the Valve Corporation. You must obey the GNU General Public License in
24 * all respects for all other code used. Additionally, AlliedModders LLC grants
25 * this exception to all derivative works. AlliedModders LLC defines further
26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 * or <http://www.sourcemod.net/license.php>.
35 #include "sm_stringutil.h"
36 #include "sm_globals.h"
37 #include "sourcemod.h"
38 #include "LibrarySys.h"
41 #include <sh_memory.h>
42 #include "logic_bridge.h"
44 #if defined PLATFORM_WINDOWS
46 #elif defined PLATFORM_POSIX
49 #include <sys/times.h>
52 HandleType_t g_PlIter;
53 ConVar sm_datetime_format("sm_datetime_format", "%m/%d/%Y - %H:%M:%S", 0, "Default formatting time rules");
54 IForward *g_OnLogAction = NULL;
56 class CoreNativeHelpers :
58 public IHandleTypeDispatch
61 void OnSourceModAllInitialized()
64 handlesys->InitAccessDefaults(NULL, &hacc);
65 hacc.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
67 g_PlIter = handlesys->CreateType("PluginIterator", this, 0, NULL, NULL, g_pCoreIdent, NULL);
69 g_OnLogAction = forwardsys->CreateForward("OnLogAction",
79 void OnHandleDestroy(HandleType_t type, void *object)
81 IPluginIterator *iter = (IPluginIterator *)object;
84 void OnSourceModShutdown()
86 forwardsys->ReleaseForward(g_OnLogAction);
87 handlesys->RemoveType(g_PlIter, g_pCoreIdent);
89 } g_CoreNativeHelpers;
91 void LogAction(Handle_t hndl, int type, int client, int target, const char *message)
93 if (g_OnLogAction->GetFunctionCount())
96 g_OnLogAction->PushCell(hndl);
97 g_OnLogAction->PushCell(type);
98 g_OnLogAction->PushCell(client);
99 g_OnLogAction->PushCell(target);
100 g_OnLogAction->PushString(message);
101 g_OnLogAction->Execute(&result);
103 if (result >= (ResultType)Pl_Handled)
109 const char *logtag = "SM";
113 IPlugin *pPlugin = scripts->FindPluginByHandle(hndl, &err);
116 logtag = pPlugin->GetFilename();
120 g_Logger.LogMessage("[%s] %s", logtag, message);
123 static cell_t ThrowError(IPluginContext *pContext, const cell_t *params)
127 g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
129 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
131 if (pContext->GetLastNativeError() == SP_ERROR_NONE)
133 pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", buffer);
139 static cell_t GetTime(IPluginContext *pContext, const cell_t *params)
141 time_t t = GetAdjustedTime();
143 pContext->LocalToPhysAddr(params[1], &addr);
147 return static_cast<cell_t>(t);
150 #if defined SUBPLATFORM_SECURECRT
151 void _ignore_invalid_parameter(
152 const wchar_t * expression,
153 const wchar_t * function,
154 const wchar_t * file,
159 /* Wow we don't care, thanks Microsoft. */
163 static cell_t FormatTime(IPluginContext *pContext, const cell_t *params)
165 char *format, *buffer;
166 pContext->LocalToString(params[1], &buffer);
167 pContext->LocalToStringNULL(params[3], &format);
171 format = const_cast<char *>(sm_datetime_format.GetString());
174 #if defined SUBPLATFORM_SECURECRT
175 _invalid_parameter_handler handler = _set_invalid_parameter_handler(_ignore_invalid_parameter);
178 time_t t = (params[4] == -1) ? GetAdjustedTime() : (time_t)params[4];
179 size_t written = strftime(buffer, params[2], format, localtime(&t));
181 #if defined SUBPLATFORM_SECURECRT
182 _set_invalid_parameter_handler(handler);
185 if (params[2] && format[0] != '\0' && !written)
187 pContext->ThrowNativeError("Invalid time format or buffer too small");
194 static cell_t GetPluginIterator(IPluginContext *pContext, const cell_t *params)
196 IPluginIterator *iter = scripts->GetPluginIterator();
198 Handle_t hndl = handlesys->CreateHandle(g_PlIter, iter, pContext->GetIdentity(), g_pCoreIdent, NULL);
200 if (hndl == BAD_HANDLE)
208 static cell_t MorePlugins(IPluginContext *pContext, const cell_t *params)
210 Handle_t hndl = (Handle_t)params[1];
212 IPluginIterator *pIter;
215 sec.pIdentity = g_pCoreIdent;
216 sec.pOwner = pContext->GetIdentity();
218 if ((err=handlesys->ReadHandle(hndl, g_PlIter, &sec, (void **)&pIter)) != HandleError_None)
220 return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
223 return pIter->MorePlugins() ? 1 : 0;
226 static cell_t ReadPlugin(IPluginContext *pContext, const cell_t *params)
228 Handle_t hndl = (Handle_t)params[1];
230 IPluginIterator *pIter;
233 sec.pIdentity = g_pCoreIdent;
234 sec.pOwner = pContext->GetIdentity();
236 if ((err=handlesys->ReadHandle(hndl, g_PlIter, &sec, (void **)&pIter)) != HandleError_None)
238 return pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
241 IPlugin *pPlugin = pIter->GetPlugin();
249 return pPlugin->GetMyHandle();
252 IPlugin *GetPluginFromHandle(IPluginContext *pContext, Handle_t hndl)
254 if (hndl == BAD_HANDLE)
256 return scripts->FindPluginByContext(pContext->GetContext());
259 IPlugin *pPlugin = scripts->FindPluginByHandle(hndl, &err);
262 pContext->ThrowNativeError("Could not read Handle %x (error %d)", hndl, err);
268 static cell_t GetPluginStatus(IPluginContext *pContext, const cell_t *params)
270 IPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]);
276 return pPlugin->GetStatus();
279 static cell_t GetPluginFilename(IPluginContext *pContext, const cell_t *params)
281 IPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]);
287 pContext->StringToLocalUTF8(params[2], params[3], pPlugin->GetFilename(), NULL);
292 static cell_t IsPluginDebugging(IPluginContext *pContext, const cell_t *params)
294 IPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]);
300 return pPlugin->IsDebugging() ? 1 : 0;
303 /* Local to plugins only */
306 PlInfo_Name, /**< Plugin name */
307 PlInfo_Author, /**< Plugin author */
308 PlInfo_Description, /**< Plugin description */
309 PlInfo_Version, /**< Plugin verison */
310 PlInfo_URL, /**< Plugin URL */
313 static cell_t GetPluginInfo(IPluginContext *pContext, const cell_t *params)
315 IPlugin *pPlugin = GetPluginFromHandle(pContext, params[1]);
321 const sm_plugininfo_t *info = pPlugin->GetPublicInfo();
328 const char *str = NULL;
330 switch ((PluginInfo)params[2])
342 case PlInfo_Description:
344 str = info->description;
359 if (!str || str[0] == '\0')
364 pContext->StringToLocalUTF8(params[3], params[4], str, NULL);
369 static cell_t SetFailState(IPluginContext *pContext, const cell_t *params)
374 pContext->LocalToString(params[1], &str);
375 pPlugin = scripts->FindPluginByContext(pContext->GetContext());
379 pPlugin->SetErrorState(Plugin_Error, "%s", str);
381 return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", str);
387 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
388 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
390 pPlugin->SetErrorState(Plugin_Error, "%s", str);
391 return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "Formatting error (%s)", str);
395 pPlugin->SetErrorState(Plugin_Error, "%s", buffer);
396 return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", buffer);
403 static cell_t GetSysTickCount(IPluginContext *pContext, const cell_t *params)
405 #if defined PLATFORM_WINDOWS
406 return (cell_t)GetTickCount();
407 #elif defined PLATFORM_POSIX
409 clock_t ticks = times(&tm);
410 long ticks_per_sec = sysconf(_SC_CLK_TCK);
411 double fticks = (double)ticks / (double)ticks_per_sec;
413 if (fticks > INT_MAX)
415 double r = (int)(fticks / INT_MAX) * (double)INT_MAX;
418 return (cell_t)fticks;
422 static cell_t AutoExecConfig(IPluginContext *pContext, const cell_t *params)
424 SMPlugin *plugin = scripts->FindPluginByContext(pContext->GetContext());
427 pContext->LocalToString(params[2], &cfg);
428 pContext->LocalToString(params[3], &folder);
432 static char temp_str[255];
433 static char temp_file[PLATFORM_MAX_PATH];
436 g_LibSys.GetFileFromPath(temp_str, sizeof(temp_str), plugin->GetFilename());
437 if ((ptr = strstr(temp_str, ".smx")) != NULL)
442 /* We have the raw filename! */
443 UTIL_Format(temp_file, sizeof(temp_file), "plugin.%s", temp_str);
447 plugin->AddConfig(params[1] ? true : false, cfg, folder);
452 static cell_t MarkNativeAsOptional(IPluginContext *pContext, const cell_t *params)
458 pContext->LocalToString(params[1], &name);
459 if (pContext->FindNativeByName(name, &idx) != SP_ERROR_NONE)
461 /* Oops! This HAS to silently fail! */
465 pContext->GetNativeByIndex(idx, &native);
467 native->flags |= SP_NTVFLAG_OPTIONAL;
472 static cell_t RegPluginLibrary(IPluginContext *pContext, const cell_t *params)
475 SMPlugin *pl = scripts->FindPluginByContext(pContext->GetContext());
477 pContext->LocalToString(params[1], &name);
479 pl->AddLibrary(name);
483 static cell_t LibraryExists(IPluginContext *pContext, const cell_t *params)
486 pContext->LocalToString(params[1], &str);
488 if (strcmp(str, "__CanTestFeatures__") == 0)
493 if (scripts->LibraryExists(str))
498 if (extsys->LibraryExists(str))
506 static cell_t sm_LogAction(IPluginContext *pContext, const cell_t *params)
509 g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
510 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 3);
512 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
517 IPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext());
519 LogAction(pPlugin->GetMyHandle(), 2, params[1], params[2], buffer);
524 static cell_t LogToFile(IPluginContext *pContext, const cell_t *params)
527 pContext->LocalToString(params[1], &file);
529 char path[PLATFORM_MAX_PATH];
530 g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "%s", file);
532 FILE *fp = fopen(path, "at");
535 return pContext->ThrowNativeError("Could not open file \"%s\"", path);
539 g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
540 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
542 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
548 IPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext());
550 g_Logger.LogToOpenFile(fp, "[%s] %s", pPlugin->GetFilename(), buffer);
557 static cell_t LogToFileEx(IPluginContext *pContext, const cell_t *params)
560 pContext->LocalToString(params[1], &file);
562 char path[PLATFORM_MAX_PATH];
563 g_SourceMod.BuildPath(Path_Game, path, sizeof(path), "%s", file);
565 FILE *fp = fopen(path, "at");
568 return pContext->ThrowNativeError("Could not open file \"%s\"", path);
572 g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
573 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
575 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
581 g_Logger.LogToOpenFile(fp, "%s", buffer);
588 static cell_t GetExtensionFileStatus(IPluginContext *pContext, const cell_t *params)
591 pContext->LocalToString(params[1], &str);
593 IExtension *pExtension = extsys->FindExtensionByFile(str);
600 if (!pExtension->IsLoaded())
606 pContext->LocalToString(params[2], &error);
607 if (!pExtension->IsRunning(error, params[3]))
615 static cell_t FindPluginByNumber(IPluginContext *pContext, const cell_t *params)
617 IPlugin *pPlugin = scripts->FindPluginByOrder(params[1]);
624 return pPlugin->GetMyHandle();
627 static cell_t VerifyCoreVersion(IPluginContext *pContext, const cell_t *params)
632 static cell_t GetFeatureStatus(IPluginContext *pContext, const cell_t *params)
634 FeatureType type = (FeatureType)params[1];
637 pContext->LocalToString(params[2], &name);
639 return sharesys->TestFeature(pContext->GetRuntime(), type, name);
642 static cell_t RequireFeature(IPluginContext *pContext, const cell_t *params)
644 FeatureType type = (FeatureType)params[1];
647 pContext->LocalToString(params[2], &name);
649 if (sharesys->TestFeature(pContext->GetRuntime(), type, name) != FeatureStatus_Available)
653 char default_message[255];
654 SMPlugin *pPlugin = scripts->FindPluginByContext(pContext->GetContext());
656 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 3);
657 if (pContext->GetLastNativeError() != SP_ERROR_NONE || buffer[0] == '\0')
659 UTIL_Format(default_message, sizeof(default_message), "Feature \"%s\" not available", name);
660 msg = default_message;
662 pPlugin->SetErrorState(Plugin_Error, "%s", msg);
663 return pContext->ThrowNativeErrorEx(SP_ERROR_ABORTED, "%s", msg);
676 //memory addresses below 0x10000 are automatically considered invalid for dereferencing
677 #define VALID_MINIMUM_MEMORY_ADDRESS 0x10000
679 static cell_t LoadFromAddress(IPluginContext *pContext, const cell_t *params)
681 void *addr = reinterpret_cast<void*>(params[1]);
685 pContext->ThrowNativeError("Address cannot be null");
687 else if (reinterpret_cast<uintptr_t>(addr) < VALID_MINIMUM_MEMORY_ADDRESS)
689 pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory.", addr);
691 NumberType size = static_cast<NumberType>(params[2]);
695 case NumberType_Int8:
696 return *reinterpret_cast<uint8_t*>(addr);
697 case NumberType_Int16:
698 return *reinterpret_cast<uint16_t*>(addr);
699 case NumberType_Int32:
700 return *reinterpret_cast<uint32_t*>(addr);
702 pContext->ThrowNativeError("Invalid number types %d", size);
709 static cell_t StoreToAddress(IPluginContext *pContext, const cell_t *params)
711 void *addr = reinterpret_cast<void*>(params[1]);
715 pContext->ThrowNativeError("Address cannot be null");
717 else if (reinterpret_cast<uintptr_t>(addr) < VALID_MINIMUM_MEMORY_ADDRESS)
719 pContext->ThrowNativeError("Invalid address 0x%x is pointing to reserved memory.", addr);
721 cell_t data = params[2];
723 NumberType size = static_cast<NumberType>(params[3]);
727 case NumberType_Int8:
728 SourceHook::SetMemAccess(addr, sizeof(uint8_t), SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC);
729 *reinterpret_cast<uint8_t*>(addr) = data;
731 case NumberType_Int16:
732 SourceHook::SetMemAccess(addr, sizeof(uint16_t), SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC);
733 *reinterpret_cast<uint16_t*>(addr) = data;
735 case NumberType_Int32:
736 SourceHook::SetMemAccess(addr, sizeof(uint32_t), SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC);
737 *reinterpret_cast<uint32_t*>(addr) = data;
740 pContext->ThrowNativeError("Invalid number types %d", size);
746 REGISTER_NATIVES(coreNatives)
748 {"AutoExecConfig", AutoExecConfig},
749 {"GetPluginFilename", GetPluginFilename},
750 {"GetPluginInfo", GetPluginInfo},
751 {"GetPluginIterator", GetPluginIterator},
752 {"GetPluginStatus", GetPluginStatus},
753 {"GetSysTickCount", GetSysTickCount},
754 {"GetTime", GetTime},
755 {"IsPluginDebugging", IsPluginDebugging},
756 {"MorePlugins", MorePlugins},
757 {"ReadPlugin", ReadPlugin},
758 {"ThrowError", ThrowError},
759 {"SetFailState", SetFailState},
760 {"FormatTime", FormatTime},
761 {"MarkNativeAsOptional", MarkNativeAsOptional},
762 {"RegPluginLibrary", RegPluginLibrary},
763 {"LibraryExists", LibraryExists},
764 {"LogAction", sm_LogAction},
765 {"LogToFile", LogToFile},
766 {"LogToFileEx", LogToFileEx},
767 {"GetExtensionFileStatus", GetExtensionFileStatus},
768 {"FindPluginByNumber", FindPluginByNumber},
769 {"VerifyCoreVersion", VerifyCoreVersion},
770 {"GetFeatureStatus", GetFeatureStatus},
771 {"RequireFeature", RequireFeature},
772 {"LoadFromAddress", LoadFromAddress},
773 {"StoreToAddress", StoreToAddress},