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>.
34 #include <IHandleSys.h>
35 #include <ILibrarySys.h>
36 #include <IPluginSys.h>
37 #include <IForwardSys.h>
38 #include <ISourceMod.h>
39 #include <ITranslator.h>
40 #include "common_logic.h"
42 HandleType_t g_FileType;
43 HandleType_t g_DirType;
44 IChangeableForward *g_pLogHook = NULL;
48 public IHandleTypeDispatch,
49 public IPluginsListener
55 virtual void OnSourceModAllInitialized()
57 g_FileType = handlesys->CreateType("File", this, 0, NULL, NULL, g_pCoreIdent, NULL);
58 g_DirType = handlesys->CreateType("Directory", this, 0, NULL, NULL, g_pCoreIdent, NULL);
59 g_pLogHook = forwardsys->CreateForwardEx(NULL, ET_Hook, 1, NULL, Param_String);
60 pluginsys->AddPluginsListener(this);
62 virtual void OnSourceModShutdown()
64 pluginsys->RemovePluginsListener(this);
65 forwardsys->ReleaseForward(g_pLogHook);
66 handlesys->RemoveType(g_DirType, g_pCoreIdent);
67 handlesys->RemoveType(g_FileType, g_pCoreIdent);
71 virtual void OnHandleDestroy(HandleType_t type, void *object)
73 if (type == g_FileType)
75 FILE *fp = (FILE *)object;
78 else if (type == g_DirType)
80 IDirectory *pDir = (IDirectory *)object;
81 libsys->CloseDirectory(pDir);
84 virtual void AddLogHook(IPluginFunction *pFunc)
86 g_pLogHook->AddFunction(pFunc);
88 virtual void RemoveLogHook(IPluginFunction *pFunc)
90 g_pLogHook->RemoveFunction(pFunc);
92 virtual bool LogPrint(const char *msg)
95 g_pLogHook->PushString(msg);
96 g_pLogHook->Execute(&result);
97 return result >= Pl_Handled;
101 bool OnLogPrint(const char *msg)
103 return s_FileNatives.LogPrint(msg);
106 static cell_t sm_OpenDirectory(IPluginContext *pContext, const cell_t *params)
110 if ((err=pContext->LocalToString(params[1], &path)) != SP_ERROR_NONE)
112 pContext->ThrowNativeErrorEx(err, NULL);
116 char realpath[PLATFORM_MAX_PATH];
117 g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", path);
119 IDirectory *pDir = libsys->OpenDirectory(realpath);
125 return handlesys->CreateHandle(g_DirType, pDir, pContext->GetIdentity(), g_pCoreIdent, NULL);
128 static cell_t sm_ReadDirEntry(IPluginContext *pContext, const cell_t *params)
130 Handle_t hndl = static_cast<Handle_t>(params[1]);
137 sec.pIdentity = g_pCoreIdent;
139 if ((herr=handlesys->ReadHandle(hndl, g_DirType, &sec, (void **)&pDir))
142 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
145 if (!pDir->MoreFiles())
151 if ((err=pContext->LocalToPhysAddr(params[4], &filetype)) != SP_ERROR_NONE)
153 pContext->ThrowNativeErrorEx(err, NULL);
157 if (pDir->IsEntryDirectory())
160 } else if (pDir->IsEntryFile()) {
166 const char *path = pDir->GetEntryName();
167 if ((err=pContext->StringToLocalUTF8(params[2], params[3], path, NULL))
170 pContext->ThrowNativeErrorEx(err, NULL);
179 static cell_t sm_OpenFile(IPluginContext *pContext, const cell_t *params)
183 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
185 pContext->ThrowNativeErrorEx(err, NULL);
188 if ((err=pContext->LocalToString(params[2], &mode)) != SP_ERROR_NONE)
190 pContext->ThrowNativeErrorEx(err, NULL);
194 char realpath[PLATFORM_MAX_PATH];
195 g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
197 FILE *pFile = fopen(realpath, mode);
203 return handlesys->CreateHandle(g_FileType, pFile, pContext->GetIdentity(), g_pCoreIdent, NULL);
206 static cell_t sm_DeleteFile(IPluginContext *pContext, const cell_t *params)
210 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
212 pContext->ThrowNativeErrorEx(err, NULL);
216 char realpath[PLATFORM_MAX_PATH];
217 g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
219 return (unlink(realpath)) ? 0 : 1;
222 static cell_t sm_ReadFileLine(IPluginContext *pContext, const cell_t *params)
224 Handle_t hndl = static_cast<Handle_t>(params[1]);
231 sec.pIdentity = g_pCoreIdent;
233 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
236 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
240 if ((err=pContext->LocalToString(params[2], &buf)) != SP_ERROR_NONE)
242 pContext->ThrowNativeErrorEx(err, NULL);
246 if (fgets(buf, params[3], pFile) == NULL)
254 static cell_t sm_IsEndOfFile(IPluginContext *pContext, const cell_t *params)
256 Handle_t hndl = static_cast<Handle_t>(params[1]);
262 sec.pIdentity = g_pCoreIdent;
264 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
267 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
270 return (feof(pFile)) ? 1 : 0;
273 static cell_t sm_FileSeek(IPluginContext *pContext, const cell_t *params)
275 Handle_t hndl = static_cast<Handle_t>(params[1]);
281 sec.pIdentity = g_pCoreIdent;
283 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
286 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
289 fseek(pFile, params[2], params[3]);
294 static cell_t sm_FilePosition(IPluginContext *pContext, const cell_t *params)
296 Handle_t hndl = static_cast<Handle_t>(params[1]);
302 sec.pIdentity = g_pCoreIdent;
304 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
307 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
313 static cell_t sm_FileExists(IPluginContext *pContext, const cell_t *params)
317 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
319 pContext->ThrowNativeErrorEx(err, NULL);
323 if (params[0] >= 2 && params[2] == 1)
325 return smcore.FileExists(name) ? 1 : 0;
328 char realpath[PLATFORM_MAX_PATH];
329 g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
330 #ifdef PLATFORM_WINDOWS
332 if (_stat(realpath, &s) != 0)
336 if (s.st_mode & S_IFREG)
341 #elif defined PLATFORM_POSIX
343 if (stat(realpath, &s) != 0)
347 if (S_ISREG(s.st_mode))
355 static cell_t sm_RenameFile(IPluginContext *pContext, const cell_t *params)
357 char *newpath, *oldpath;
359 if ((err=pContext->LocalToString(params[1], &newpath)) != SP_ERROR_NONE)
361 pContext->ThrowNativeErrorEx(err, NULL);
364 if ((err=pContext->LocalToString(params[2], &oldpath)) != SP_ERROR_NONE)
366 pContext->ThrowNativeErrorEx(err, NULL);
370 char new_realpath[PLATFORM_MAX_PATH];
371 g_pSM->BuildPath(Path_Game, new_realpath, sizeof(new_realpath), "%s", newpath);
372 char old_realpath[PLATFORM_MAX_PATH];
373 g_pSM->BuildPath(Path_Game, old_realpath, sizeof(old_realpath), "%s", oldpath);
375 #ifdef PLATFORM_WINDOWS
376 return (MoveFileA(old_realpath, new_realpath)) ? 1 : 0;
377 #elif defined PLATFORM_POSIX
378 return (rename(old_realpath, new_realpath)) ? 0 : 1;
382 static cell_t sm_DirExists(IPluginContext *pContext, const cell_t *params)
386 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
388 pContext->ThrowNativeErrorEx(err, NULL);
392 char realpath[PLATFORM_MAX_PATH];
393 g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
394 #ifdef PLATFORM_WINDOWS
396 if (_stat(realpath, &s) != 0)
400 if (s.st_mode & S_IFDIR)
405 #elif defined PLATFORM_POSIX
407 if (stat(realpath, &s) != 0)
411 if (S_ISDIR(s.st_mode))
419 static cell_t sm_FileSize(IPluginContext *pContext, const cell_t *params)
423 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
425 pContext->ThrowNativeErrorEx(err, NULL);
429 char realpath[PLATFORM_MAX_PATH];
430 g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
431 #ifdef PLATFORM_WINDOWS
433 if (_stat(realpath, &s) != 0)
437 if (s.st_mode & S_IFREG)
439 return static_cast<cell_t>(s.st_size);
442 #elif defined PLATFORM_POSIX
444 if (stat(realpath, &s) != 0)
448 if (S_ISREG(s.st_mode))
450 return static_cast<cell_t>(s.st_size);
456 static cell_t sm_CreateDirectory(IPluginContext *pContext, const cell_t *params)
459 char realpath[PLATFORM_MAX_PATH];
461 pContext->LocalToString(params[1], &name);
462 g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
464 #if defined PLATFORM_WINDOWS
465 return mkdir(realpath) == 0;
467 return mkdir(realpath, params[2]) == 0;
471 static cell_t sm_RemoveDir(IPluginContext *pContext, const cell_t *params)
475 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
477 pContext->ThrowNativeErrorEx(err, NULL);
481 char realpath[PLATFORM_MAX_PATH];
482 g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
484 return (rmdir(realpath)) ? 0 : 1;
487 static cell_t sm_WriteFileLine(IPluginContext *pContext, const cell_t *params)
489 Handle_t hndl = static_cast<Handle_t>(params[1]);
495 sec.pIdentity = g_pCoreIdent;
497 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
500 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
505 if ((err=pContext->LocalToString(params[2], &fmt)) != SP_ERROR_NONE)
507 pContext->ThrowNativeErrorEx(err, NULL);
513 smcore.atcprintf(buffer, sizeof(buffer), fmt, pContext, params, &arg);
514 fprintf(pFile, "%s\n", buffer);
519 static cell_t sm_FlushFile(IPluginContext *pContext, const cell_t *params)
521 Handle_t hndl = static_cast<Handle_t>(params[1]);
527 sec.pIdentity = g_pCoreIdent;
529 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
532 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
535 return (fflush(pFile) == 0) ? 1 : 0;
538 static cell_t sm_BuildPath(IPluginContext *pContext, const cell_t *params)
540 char path[PLATFORM_MAX_PATH], *fmt, *buffer;
542 pContext->LocalToString(params[2], &buffer);
543 pContext->LocalToString(params[4], &fmt);
545 smcore.atcprintf(path, sizeof(path), fmt, pContext, params, &arg);
547 return g_pSM->BuildPath(Path_SM_Rel, buffer, params[3], "%s", path);
550 static cell_t sm_LogToGame(IPluginContext *pContext, const cell_t *params)
552 g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
555 size_t len = g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
557 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
562 if (len >= sizeof(buffer)-2)
567 buffer[len++] = '\n';
571 smcore.LogToGame(buffer);
576 static cell_t sm_LogMessage(IPluginContext *pContext, const cell_t *params)
578 g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
581 g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
583 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
588 IPlugin *pPlugin = pluginsys->FindPluginByContext(pContext->GetContext());
589 smcore.Log("[%s] %s", pPlugin->GetFilename(), buffer);
594 static cell_t sm_LogError(IPluginContext *pContext, const cell_t *params)
596 g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
599 g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
601 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
606 IPlugin *pPlugin = pluginsys->FindPluginByContext(pContext->GetContext());
607 smcore.LogError("[%s] %s", pPlugin->GetFilename(), buffer);
612 static cell_t sm_GetFileTime(IPluginContext *pContext, const cell_t *params)
616 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
618 pContext->ThrowNativeErrorEx(err, NULL);
623 char realpath[PLATFORM_MAX_PATH];
624 g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
626 if (!libsys->FileTime(realpath, (FileTimeType)params[2], &time_val))
631 return (cell_t)time_val;
634 static cell_t sm_LogToOpenFile(IPluginContext *pContext, const cell_t *params)
636 Handle_t hndl = static_cast<Handle_t>(params[1]);
642 sec.pIdentity = g_pCoreIdent;
644 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
647 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
651 g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
652 g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
654 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
659 IPlugin *pPlugin = pluginsys->FindPluginByContext(pContext->GetContext());
660 smcore.LogToFile(pFile, "[%s] %s", pPlugin->GetFilename(), buffer);
665 static cell_t sm_LogToOpenFileEx(IPluginContext *pContext, const cell_t *params)
667 Handle_t hndl = static_cast<Handle_t>(params[1]);
673 sec.pIdentity = g_pCoreIdent;
675 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
678 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
682 g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
683 g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
685 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
690 smcore.LogToFile(pFile, "%s", buffer);
695 static cell_t sm_ReadFile(IPluginContext *pContext, const cell_t *params)
697 Handle_t hndl = static_cast<Handle_t>(params[1]);
699 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
703 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
706 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
709 if (params[4] != 1 && params[4] != 2 && params[4] != 4)
711 return pContext->ThrowNativeError("Invalid size specifier (%d is not 1, 2, or 4)", params[4]);
715 pContext->LocalToPhysAddr(params[2], &data);
719 read = fread(data, sizeof(cell_t), params[3], pFile);
721 else if (params[4] == 2)
724 for (cell_t i = 0; i < params[3]; i++)
726 if (fread(&val, sizeof(uint16_t), 1, pFile) != 1)
733 else if (params[4] == 1)
736 for (cell_t i = 0; i < params[3]; i++)
738 if (fread(&val, sizeof(uint8_t), 1, pFile) != 1)
746 if (read != (size_t)params[3] && ferror(pFile) != 0)
754 static cell_t sm_ReadFileString(IPluginContext *pContext, const cell_t *params)
756 Handle_t hndl = static_cast<Handle_t>(params[1]);
758 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
762 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
765 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
769 pContext->LocalToString(params[2], &buffer);
773 if (size_t(params[4]) > size_t(params[3]))
775 return pContext->ThrowNativeError("read_count (%u) is greater than buffer size (%u)",
780 num_read = (cell_t)fread(buffer, 1, params[4], pFile);
782 if (num_read != params[4] && ferror(pFile))
793 if (params[3] == 0 || num_read >= params[3] - 1)
797 if (fread(&val, sizeof(val), 1, pFile) != 1)
809 if (params[3] > 0 && num_read < params[3] - 1)
811 buffer[num_read++] = val;
817 buffer[num_read] = '\0';
823 static cell_t sm_WriteFile(IPluginContext *pContext, const cell_t *params)
825 Handle_t hndl = static_cast<Handle_t>(params[1]);
827 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
830 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
833 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
837 pContext->LocalToPhysAddr(params[2], &data);
839 if (params[4] != 1 && params[4] != 2 && params[4] != 4)
841 return pContext->ThrowNativeError("Invalid size specifier (%d is not 1, 2, or 4)", params[4]);
844 /* :NOTE: This really isn't compatible with big endian but we will never have to worry about that. */
848 if (fwrite(data, sizeof(cell_t), params[3], pFile) != (size_t)params[3])
853 else if (params[4] == 2)
855 for (cell_t i = 0; i < params[3]; i++)
857 if (fwrite(&data[i], sizeof(int16_t), 1, pFile) != 1)
863 else if (params[4] == 1)
865 for (cell_t i = 0; i < params[3]; i++)
867 if (fwrite(&data[i], sizeof(int8_t), 1, pFile) != 1)
877 static cell_t sm_WriteFileString(IPluginContext *pContext, const cell_t *params)
879 Handle_t hndl = static_cast<Handle_t>(params[1]);
881 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
884 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
887 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
891 pContext->LocalToString(params[2], &buffer);
893 size_t len = strlen(buffer);
900 return (fwrite(buffer, sizeof(char), len, pFile) == len) ? 1 : 0;
903 static cell_t sm_AddGameLogHook(IPluginContext *pContext, const cell_t *params)
905 IPluginFunction *pFunction;
907 if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
909 return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
912 s_FileNatives.AddLogHook(pFunction);
917 static cell_t sm_RemoveGameLogHook(IPluginContext *pContext, const cell_t *params)
919 IPluginFunction *pFunction;
921 if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
923 return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
926 s_FileNatives.RemoveLogHook(pFunction);
931 REGISTER_NATIVES(filesystem)
933 {"OpenDirectory", sm_OpenDirectory},
934 {"ReadDirEntry", sm_ReadDirEntry},
935 {"OpenFile", sm_OpenFile},
936 {"DeleteFile", sm_DeleteFile},
937 {"ReadFileLine", sm_ReadFileLine},
938 {"IsEndOfFile", sm_IsEndOfFile},
939 {"FileSeek", sm_FileSeek},
940 {"FilePosition", sm_FilePosition},
941 {"FileExists", sm_FileExists},
942 {"RenameFile", sm_RenameFile},
943 {"DirExists", sm_DirExists},
944 {"FileSize", sm_FileSize},
945 {"RemoveDir", sm_RemoveDir},
946 {"WriteFileLine", sm_WriteFileLine},
947 {"BuildPath", sm_BuildPath},
948 {"LogToGame", sm_LogToGame},
949 {"LogMessage", sm_LogMessage},
950 {"LogError", sm_LogError},
951 {"FlushFile", sm_FlushFile},
952 {"GetFileTime", sm_GetFileTime},
953 {"LogToOpenFile", sm_LogToOpenFile},
954 {"LogToOpenFileEx", sm_LogToOpenFileEx},
955 {"ReadFile", sm_ReadFile},
956 {"ReadFileString", sm_ReadFileString},
957 {"WriteFile", sm_WriteFile},
958 {"WriteFileString", sm_WriteFileString},
959 {"AddGameLogHook", sm_AddGameLogHook},
960 {"RemoveGameLogHook", sm_RemoveGameLogHook},
961 {"CreateDirectory", sm_CreateDirectory},