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.filesystem->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 if (params[0] >= 2 && params[2] == 1)
431 if (smcore.filesystem->FileExists(name))
433 return smcore.filesystem->Size(name);
441 char realpath[PLATFORM_MAX_PATH];
442 g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
443 #ifdef PLATFORM_WINDOWS
445 if (_stat(realpath, &s) != 0)
449 if (s.st_mode & S_IFREG)
451 return static_cast<cell_t>(s.st_size);
454 #elif defined PLATFORM_POSIX
456 if (stat(realpath, &s) != 0)
460 if (S_ISREG(s.st_mode))
462 return static_cast<cell_t>(s.st_size);
468 static cell_t sm_CreateDirectory(IPluginContext *pContext, const cell_t *params)
471 char realpath[PLATFORM_MAX_PATH];
473 pContext->LocalToString(params[1], &name);
474 g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
476 #if defined PLATFORM_WINDOWS
477 return mkdir(realpath) == 0;
479 return mkdir(realpath, params[2]) == 0;
483 static cell_t sm_RemoveDir(IPluginContext *pContext, const cell_t *params)
487 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
489 pContext->ThrowNativeErrorEx(err, NULL);
493 char realpath[PLATFORM_MAX_PATH];
494 g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
496 return (rmdir(realpath)) ? 0 : 1;
499 static cell_t sm_WriteFileLine(IPluginContext *pContext, const cell_t *params)
501 Handle_t hndl = static_cast<Handle_t>(params[1]);
507 sec.pIdentity = g_pCoreIdent;
509 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
512 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
517 if ((err=pContext->LocalToString(params[2], &fmt)) != SP_ERROR_NONE)
519 pContext->ThrowNativeErrorEx(err, NULL);
525 smcore.atcprintf(buffer, sizeof(buffer), fmt, pContext, params, &arg);
526 fprintf(pFile, "%s\n", buffer);
531 static cell_t sm_FlushFile(IPluginContext *pContext, const cell_t *params)
533 Handle_t hndl = static_cast<Handle_t>(params[1]);
539 sec.pIdentity = g_pCoreIdent;
541 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
544 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
547 return (fflush(pFile) == 0) ? 1 : 0;
550 static cell_t sm_BuildPath(IPluginContext *pContext, const cell_t *params)
552 char path[PLATFORM_MAX_PATH], *fmt, *buffer;
554 pContext->LocalToString(params[2], &buffer);
555 pContext->LocalToString(params[4], &fmt);
557 smcore.atcprintf(path, sizeof(path), fmt, pContext, params, &arg);
559 return g_pSM->BuildPath(Path_SM_Rel, buffer, params[3], "%s", path);
562 static cell_t sm_LogToGame(IPluginContext *pContext, const cell_t *params)
564 g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
567 size_t len = g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
569 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
574 if (len >= sizeof(buffer)-2)
579 buffer[len++] = '\n';
583 smcore.LogToGame(buffer);
588 static cell_t sm_LogMessage(IPluginContext *pContext, const cell_t *params)
590 g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
593 g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
595 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
600 IPlugin *pPlugin = pluginsys->FindPluginByContext(pContext->GetContext());
601 smcore.Log("[%s] %s", pPlugin->GetFilename(), buffer);
606 static cell_t sm_LogError(IPluginContext *pContext, const cell_t *params)
608 g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
611 g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
613 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
618 IPlugin *pPlugin = pluginsys->FindPluginByContext(pContext->GetContext());
619 smcore.LogError("[%s] %s", pPlugin->GetFilename(), buffer);
624 static cell_t sm_GetFileTime(IPluginContext *pContext, const cell_t *params)
628 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
630 pContext->ThrowNativeErrorEx(err, NULL);
635 char realpath[PLATFORM_MAX_PATH];
636 g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
638 if (!libsys->FileTime(realpath, (FileTimeType)params[2], &time_val))
643 return (cell_t)time_val;
646 static cell_t sm_LogToOpenFile(IPluginContext *pContext, const cell_t *params)
648 Handle_t hndl = static_cast<Handle_t>(params[1]);
654 sec.pIdentity = g_pCoreIdent;
656 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
659 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
663 g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
664 g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
666 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
671 IPlugin *pPlugin = pluginsys->FindPluginByContext(pContext->GetContext());
672 smcore.LogToFile(pFile, "[%s] %s", pPlugin->GetFilename(), buffer);
677 static cell_t sm_LogToOpenFileEx(IPluginContext *pContext, const cell_t *params)
679 Handle_t hndl = static_cast<Handle_t>(params[1]);
685 sec.pIdentity = g_pCoreIdent;
687 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
690 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
694 g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
695 g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
697 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
702 smcore.LogToFile(pFile, "%s", buffer);
707 static cell_t sm_ReadFile(IPluginContext *pContext, const cell_t *params)
709 Handle_t hndl = static_cast<Handle_t>(params[1]);
711 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
715 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
718 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
721 if (params[4] != 1 && params[4] != 2 && params[4] != 4)
723 return pContext->ThrowNativeError("Invalid size specifier (%d is not 1, 2, or 4)", params[4]);
727 pContext->LocalToPhysAddr(params[2], &data);
731 read = fread(data, sizeof(cell_t), params[3], pFile);
733 else if (params[4] == 2)
736 for (cell_t i = 0; i < params[3]; i++)
738 if (fread(&val, sizeof(uint16_t), 1, pFile) != 1)
745 else if (params[4] == 1)
748 for (cell_t i = 0; i < params[3]; i++)
750 if (fread(&val, sizeof(uint8_t), 1, pFile) != 1)
758 if (read != (size_t)params[3] && ferror(pFile) != 0)
766 static cell_t sm_ReadFileString(IPluginContext *pContext, const cell_t *params)
768 Handle_t hndl = static_cast<Handle_t>(params[1]);
770 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
774 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
777 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
781 pContext->LocalToString(params[2], &buffer);
785 if (size_t(params[4]) > size_t(params[3]))
787 return pContext->ThrowNativeError("read_count (%u) is greater than buffer size (%u)",
792 num_read = (cell_t)fread(buffer, 1, params[4], pFile);
794 if (num_read != params[4] && ferror(pFile))
805 if (params[3] == 0 || num_read >= params[3] - 1)
809 if (fread(&val, sizeof(val), 1, pFile) != 1)
821 if (params[3] > 0 && num_read < params[3] - 1)
823 buffer[num_read++] = val;
829 buffer[num_read] = '\0';
835 static cell_t sm_WriteFile(IPluginContext *pContext, const cell_t *params)
837 Handle_t hndl = static_cast<Handle_t>(params[1]);
839 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
842 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
845 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
849 pContext->LocalToPhysAddr(params[2], &data);
851 if (params[4] != 1 && params[4] != 2 && params[4] != 4)
853 return pContext->ThrowNativeError("Invalid size specifier (%d is not 1, 2, or 4)", params[4]);
856 /* :NOTE: This really isn't compatible with big endian but we will never have to worry about that. */
860 if (fwrite(data, sizeof(cell_t), params[3], pFile) != (size_t)params[3])
865 else if (params[4] == 2)
867 for (cell_t i = 0; i < params[3]; i++)
869 if (fwrite(&data[i], sizeof(int16_t), 1, pFile) != 1)
875 else if (params[4] == 1)
877 for (cell_t i = 0; i < params[3]; i++)
879 if (fwrite(&data[i], sizeof(int8_t), 1, pFile) != 1)
889 static cell_t sm_WriteFileString(IPluginContext *pContext, const cell_t *params)
891 Handle_t hndl = static_cast<Handle_t>(params[1]);
893 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
896 if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
899 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
903 pContext->LocalToString(params[2], &buffer);
905 size_t len = strlen(buffer);
912 return (fwrite(buffer, sizeof(char), len, pFile) == len) ? 1 : 0;
915 static cell_t sm_AddGameLogHook(IPluginContext *pContext, const cell_t *params)
917 IPluginFunction *pFunction;
919 if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
921 return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
924 s_FileNatives.AddLogHook(pFunction);
929 static cell_t sm_RemoveGameLogHook(IPluginContext *pContext, const cell_t *params)
931 IPluginFunction *pFunction;
933 if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
935 return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
938 s_FileNatives.RemoveLogHook(pFunction);
943 REGISTER_NATIVES(filesystem)
945 {"OpenDirectory", sm_OpenDirectory},
946 {"ReadDirEntry", sm_ReadDirEntry},
947 {"OpenFile", sm_OpenFile},
948 {"DeleteFile", sm_DeleteFile},
949 {"ReadFileLine", sm_ReadFileLine},
950 {"IsEndOfFile", sm_IsEndOfFile},
951 {"FileSeek", sm_FileSeek},
952 {"FilePosition", sm_FilePosition},
953 {"FileExists", sm_FileExists},
954 {"RenameFile", sm_RenameFile},
955 {"DirExists", sm_DirExists},
956 {"FileSize", sm_FileSize},
957 {"RemoveDir", sm_RemoveDir},
958 {"WriteFileLine", sm_WriteFileLine},
959 {"BuildPath", sm_BuildPath},
960 {"LogToGame", sm_LogToGame},
961 {"LogMessage", sm_LogMessage},
962 {"LogError", sm_LogError},
963 {"FlushFile", sm_FlushFile},
964 {"GetFileTime", sm_GetFileTime},
965 {"LogToOpenFile", sm_LogToOpenFile},
966 {"LogToOpenFileEx", sm_LogToOpenFileEx},
967 {"ReadFile", sm_ReadFile},
968 {"ReadFileString", sm_ReadFileString},
969 {"WriteFile", sm_WriteFile},
970 {"WriteFileString", sm_WriteFileString},
971 {"AddGameLogHook", sm_AddGameLogHook},
972 {"RemoveGameLogHook", sm_RemoveGameLogHook},
973 {"CreateDirectory", sm_CreateDirectory},