3 * =============================================================================
5 * Copyright (C) 2004-2007 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>.
33 #include "sm_globals.h"
34 #include "HandleSys.h"
35 #include "LibrarySys.h"
36 #include "sm_stringutil.h"
38 #include "PluginSys.h"
39 #include "sourcemm_api.h"
41 HandleType_t g_FileType;
42 HandleType_t g_DirType;
46 public IHandleTypeDispatch
49 virtual void OnSourceModAllInitialized()
51 g_FileType = g_HandleSys.CreateType("File", this, 0, NULL, NULL, g_pCoreIdent, NULL);
52 g_DirType = g_HandleSys.CreateType("Directory", this, 0, NULL, NULL, g_pCoreIdent, NULL);
54 virtual void OnSourceModShutdown()
56 g_HandleSys.RemoveType(g_DirType, g_pCoreIdent);
57 g_HandleSys.RemoveType(g_FileType, g_pCoreIdent);
61 virtual void OnHandleDestroy(HandleType_t type, void *object)
63 if (type == g_FileType)
65 FILE *fp = (FILE *)object;
67 } else if (type == g_DirType) {
68 IDirectory *pDir = (IDirectory *)object;
69 g_LibSys.CloseDirectory(pDir);
74 static cell_t sm_OpenDirectory(IPluginContext *pContext, const cell_t *params)
78 if ((err=pContext->LocalToString(params[1], &path)) != SP_ERROR_NONE)
80 pContext->ThrowNativeErrorEx(err, NULL);
84 char realpath[PLATFORM_MAX_PATH];
85 g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", path);
87 IDirectory *pDir = g_LibSys.OpenDirectory(realpath);
93 return g_HandleSys.CreateHandle(g_DirType, pDir, pContext->GetIdentity(), g_pCoreIdent, NULL);
96 static cell_t sm_ReadDirEntry(IPluginContext *pContext, const cell_t *params)
98 Handle_t hndl = static_cast<Handle_t>(params[1]);
105 sec.pIdentity = g_pCoreIdent;
107 if ((herr=g_HandleSys.ReadHandle(hndl, g_DirType, &sec, (void **)&pDir))
110 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
113 if (!pDir->MoreFiles())
119 if ((err=pContext->LocalToPhysAddr(params[4], &filetype)) != SP_ERROR_NONE)
121 pContext->ThrowNativeErrorEx(err, NULL);
125 if (pDir->IsEntryDirectory())
128 } else if (pDir->IsEntryFile()) {
134 const char *path = pDir->GetEntryName();
135 if ((err=pContext->StringToLocalUTF8(params[2], params[3], path, NULL))
138 pContext->ThrowNativeErrorEx(err, NULL);
147 static cell_t sm_OpenFile(IPluginContext *pContext, const cell_t *params)
151 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
153 pContext->ThrowNativeErrorEx(err, NULL);
156 if ((err=pContext->LocalToString(params[2], &mode)) != SP_ERROR_NONE)
158 pContext->ThrowNativeErrorEx(err, NULL);
162 char realpath[PLATFORM_MAX_PATH];
163 g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
165 FILE *pFile = fopen(realpath, mode);
171 return g_HandleSys.CreateHandle(g_FileType, pFile, pContext->GetIdentity(), g_pCoreIdent, NULL);
174 static cell_t sm_DeleteFile(IPluginContext *pContext, const cell_t *params)
178 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
180 pContext->ThrowNativeErrorEx(err, NULL);
184 char realpath[PLATFORM_MAX_PATH];
185 g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
187 return (unlink(realpath)) ? 0 : 1;
190 static cell_t sm_ReadFileLine(IPluginContext *pContext, const cell_t *params)
192 Handle_t hndl = static_cast<Handle_t>(params[1]);
199 sec.pIdentity = g_pCoreIdent;
201 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
204 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
208 if ((err=pContext->LocalToString(params[2], &buf)) != SP_ERROR_NONE)
210 pContext->ThrowNativeErrorEx(err, NULL);
214 if (fgets(buf, params[3], pFile) == NULL)
222 static cell_t sm_IsEndOfFile(IPluginContext *pContext, const cell_t *params)
224 Handle_t hndl = static_cast<Handle_t>(params[1]);
230 sec.pIdentity = g_pCoreIdent;
232 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
235 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
238 return (feof(pFile)) ? 1 : 0;
241 static cell_t sm_FileSeek(IPluginContext *pContext, const cell_t *params)
243 Handle_t hndl = static_cast<Handle_t>(params[1]);
249 sec.pIdentity = g_pCoreIdent;
251 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
254 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
257 fseek(pFile, params[2], params[3]);
262 static cell_t sm_FilePosition(IPluginContext *pContext, const cell_t *params)
264 Handle_t hndl = static_cast<Handle_t>(params[1]);
270 sec.pIdentity = g_pCoreIdent;
272 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
275 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
281 static cell_t sm_FileExists(IPluginContext *pContext, const cell_t *params)
285 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
287 pContext->ThrowNativeErrorEx(err, NULL);
291 char realpath[PLATFORM_MAX_PATH];
292 g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
293 #ifdef PLATFORM_WINDOWS
295 if (_stat(realpath, &s) != 0)
299 if (s.st_mode & S_IFREG)
304 #elif defined PLATFORM_POSIX
306 if (stat(realpath, &s) != 0)
310 if (S_ISREG(s.st_mode))
318 static cell_t sm_RenameFile(IPluginContext *pContext, const cell_t *params)
320 char *newpath, *oldpath;
322 if ((err=pContext->LocalToString(params[1], &newpath)) != SP_ERROR_NONE)
324 pContext->ThrowNativeErrorEx(err, NULL);
327 if ((err=pContext->LocalToString(params[2], &oldpath)) != SP_ERROR_NONE)
329 pContext->ThrowNativeErrorEx(err, NULL);
333 char new_realpath[PLATFORM_MAX_PATH];
334 g_SourceMod.BuildPath(Path_Game, new_realpath, sizeof(new_realpath), "%s", newpath);
335 char old_realpath[PLATFORM_MAX_PATH];
336 g_SourceMod.BuildPath(Path_Game, old_realpath, sizeof(old_realpath), "%s", oldpath);
338 #ifdef PLATFORM_WINDOWS
339 return (MoveFileA(old_realpath, new_realpath)) ? 1 : 0;
340 #elif defined PLATFORM_POSIX
341 return (rename(old_realpath, new_realpath)) ? 0 : 1;
345 static cell_t sm_DirExists(IPluginContext *pContext, const cell_t *params)
349 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
351 pContext->ThrowNativeErrorEx(err, NULL);
355 char realpath[PLATFORM_MAX_PATH];
356 g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
357 #ifdef PLATFORM_WINDOWS
359 if (_stat(realpath, &s) != 0)
363 if (s.st_mode & S_IFDIR)
368 #elif defined PLATFORM_POSIX
370 if (stat(realpath, &s) != 0)
374 if (S_ISDIR(s.st_mode))
382 static cell_t sm_FileSize(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_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
394 #ifdef PLATFORM_WINDOWS
396 if (_stat(realpath, &s) != 0)
400 if (s.st_mode & S_IFREG)
402 return static_cast<cell_t>(s.st_size);
405 #elif defined PLATFORM_POSIX
407 if (stat(realpath, &s) != 0)
411 if (S_ISREG(s.st_mode))
413 return static_cast<cell_t>(s.st_size);
419 static cell_t sm_RemoveDir(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_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
432 return (rmdir(realpath)) ? 0 : 1;
435 static cell_t sm_WriteFileLine(IPluginContext *pContext, const cell_t *params)
437 Handle_t hndl = static_cast<Handle_t>(params[1]);
443 sec.pIdentity = g_pCoreIdent;
445 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
448 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
453 if ((err=pContext->LocalToString(params[2], &fmt)) != SP_ERROR_NONE)
455 pContext->ThrowNativeErrorEx(err, NULL);
461 atcprintf(buffer, sizeof(buffer), fmt, pContext, params, &arg);
462 fprintf(pFile, "%s\n", buffer);
467 static cell_t sm_FlushFile(IPluginContext *pContext, const cell_t *params)
469 Handle_t hndl = static_cast<Handle_t>(params[1]);
475 sec.pIdentity = g_pCoreIdent;
477 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
480 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
483 return (fflush(pFile) == 0) ? 1 : 0;
486 static cell_t sm_BuildPath(IPluginContext *pContext, const cell_t *params)
488 char path[PLATFORM_MAX_PATH], *fmt, *buffer;
490 pContext->LocalToString(params[2], &buffer);
491 pContext->LocalToString(params[4], &fmt);
493 atcprintf(path, sizeof(path), fmt, pContext, params, &arg);
495 return g_SourceMod.BuildPath(Path_SM_Rel, buffer, params[3], "%s", path);
498 static cell_t sm_LogToGame(IPluginContext *pContext, const cell_t *params)
500 g_SourceMod.SetGlobalTarget(LANG_SERVER);
503 size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
505 if (pContext->GetContext()->n_err != SP_ERROR_NONE)
510 if (len >= sizeof(buffer)-2)
515 buffer[len++] = '\n';
519 engine->LogPrint(buffer);
524 static cell_t sm_LogMessage(IPluginContext *pContext, const cell_t *params)
526 g_SourceMod.SetGlobalTarget(LANG_SERVER);
529 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
531 if (pContext->GetContext()->n_err != SP_ERROR_NONE)
536 CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
537 g_Logger.LogMessage("[%s] %s", pPlugin->GetFilename(), buffer);
542 static cell_t sm_LogError(IPluginContext *pContext, const cell_t *params)
544 g_SourceMod.SetGlobalTarget(LANG_SERVER);
547 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
549 if (pContext->GetContext()->n_err != SP_ERROR_NONE)
554 CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
555 g_Logger.LogError("[%s] %s", pPlugin->GetFilename(), buffer);
560 static cell_t sm_GetFileTime(IPluginContext *pContext, const cell_t *params)
564 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
566 pContext->ThrowNativeErrorEx(err, NULL);
571 char realpath[PLATFORM_MAX_PATH];
572 g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
574 if (!g_LibSys.FileTime(realpath, (FileTimeType)params[2], &time_val))
579 return (cell_t)time_val;
582 static cell_t sm_LogToOpenFile(IPluginContext *pContext, const cell_t *params)
584 Handle_t hndl = static_cast<Handle_t>(params[1]);
590 sec.pIdentity = g_pCoreIdent;
592 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
595 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
599 g_SourceMod.SetGlobalTarget(LANG_SERVER);
600 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
602 if (pContext->GetContext()->n_err != SP_ERROR_NONE)
607 CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
608 g_Logger.LogToOpenFile(pFile, "[%s] %s", pPlugin->GetFilename(), buffer);
613 static cell_t sm_LogToOpenFileEx(IPluginContext *pContext, const cell_t *params)
615 Handle_t hndl = static_cast<Handle_t>(params[1]);
621 sec.pIdentity = g_pCoreIdent;
623 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
626 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
630 g_SourceMod.SetGlobalTarget(LANG_SERVER);
631 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
633 if (pContext->GetContext()->n_err != SP_ERROR_NONE)
638 g_Logger.LogToOpenFile(pFile, "%s", buffer);
643 static cell_t sm_ReadFile(IPluginContext *pContext, const cell_t *params)
645 Handle_t hndl = static_cast<Handle_t>(params[1]);
647 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
651 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
654 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
657 if (params[4] != 1 && params[4] != 2 && params[4] != 4)
659 return pContext->ThrowNativeError("Invalid size specifier (%d is not 1, 2, or 4)", params[4]);
663 pContext->LocalToPhysAddr(params[2], &data);
667 read = fread(data, sizeof(cell_t), params[3], pFile);
669 else if (params[4] == 2)
672 for (cell_t i = 0; i < params[3]; i++)
674 if (fread(&val, sizeof(int16_t), 1, pFile) != 1)
681 else if (params[4] == 1)
684 for (cell_t i = 0; i < params[3]; i++)
686 if (fread(&val, sizeof(int8_t), 1, pFile) != 1)
694 if (read != (size_t)params[3] && ferror(pFile) != 0)
702 static cell_t sm_ReadFileString(IPluginContext *pContext, const cell_t *params)
704 Handle_t hndl = static_cast<Handle_t>(params[1]);
706 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
710 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
713 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
717 pContext->LocalToString(params[2], &buffer);
722 /* If we're in stop mode, break as soon as the buffer is full. */
723 if (params[4] && (params[3] == 0 || num_read >= params[3] - 1))
727 if (fread(&val, sizeof(val), 1, pFile) != 1)
739 if (params[3] > 0 && num_read < params[3] - 1)
741 buffer[num_read++] = val;
747 buffer[num_read] = '\0';
753 static cell_t sm_WriteFile(IPluginContext *pContext, const cell_t *params)
755 Handle_t hndl = static_cast<Handle_t>(params[1]);
757 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
761 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
764 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
768 pContext->LocalToPhysAddr(params[2], &data);
770 if (params[4] != 1 && params[4] != 2 && params[4] != 4)
772 return pContext->ThrowNativeError("Invalid size specifier (%d is not 1, 2, or 4)", params[4]);
775 /* :NOTE: This really isn't compatible with big endian but we will never have to worry about that. */
779 if (fwrite(data, sizeof(cell_t), params[3], pFile) != (size_t)params[3])
784 else if (params[4] == 2)
786 for (cell_t i = 0; i < params[3]; i++)
788 if (fwrite(&data[i], sizeof(int16_t), 1, pFile) != 1)
794 else if (params[4] == 1)
796 for (cell_t i = 0; i < params[3]; i++)
798 if (fwrite(&data[i], sizeof(int8_t), 1, pFile) != 1)
808 static cell_t sm_WriteFileString(IPluginContext *pContext, const cell_t *params)
810 Handle_t hndl = static_cast<Handle_t>(params[1]);
812 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
815 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
818 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
822 pContext->LocalToString(params[2], &buffer);
824 size_t len = strlen(buffer);
831 return (fwrite(buffer, sizeof(char), len, pFile) == len) ? 1 : 0;
834 REGISTER_NATIVES(filesystem)
836 {"OpenDirectory", sm_OpenDirectory},
837 {"ReadDirEntry", sm_ReadDirEntry},
838 {"OpenFile", sm_OpenFile},
839 {"DeleteFile", sm_DeleteFile},
840 {"ReadFileLine", sm_ReadFileLine},
841 {"IsEndOfFile", sm_IsEndOfFile},
842 {"FileSeek", sm_FileSeek},
843 {"FilePosition", sm_FilePosition},
844 {"FileExists", sm_FileExists},
845 {"RenameFile", sm_RenameFile},
846 {"DirExists", sm_DirExists},
847 {"FileSize", sm_FileSize},
848 {"RemoveDir", sm_RemoveDir},
849 {"WriteFileLine", sm_WriteFileLine},
850 {"BuildPath", sm_BuildPath},
851 {"LogToGame", sm_LogToGame},
852 {"LogMessage", sm_LogMessage},
853 {"LogError", sm_LogError},
854 {"FlushFile", sm_FlushFile},
855 {"GetFileTime", sm_GetFileTime},
856 {"LogToOpenFile", sm_LogToOpenFile},
857 {"LogToOpenFileEx", sm_LogToOpenFileEx},
858 {"ReadFile", sm_ReadFile},
859 {"ReadFileString", sm_ReadFileString},
860 {"WriteFile", sm_WriteFile},
861 {"WriteFileString", sm_WriteFileString},