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 if (params[0] >= 2 && params[2] == 1)
293 return basefilesystem->FileExists(name) ? 1 : 0;
296 char realpath[PLATFORM_MAX_PATH];
297 g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
298 #ifdef PLATFORM_WINDOWS
300 if (_stat(realpath, &s) != 0)
304 if (s.st_mode & S_IFREG)
309 #elif defined PLATFORM_POSIX
311 if (stat(realpath, &s) != 0)
315 if (S_ISREG(s.st_mode))
323 static cell_t sm_RenameFile(IPluginContext *pContext, const cell_t *params)
325 char *newpath, *oldpath;
327 if ((err=pContext->LocalToString(params[1], &newpath)) != SP_ERROR_NONE)
329 pContext->ThrowNativeErrorEx(err, NULL);
332 if ((err=pContext->LocalToString(params[2], &oldpath)) != SP_ERROR_NONE)
334 pContext->ThrowNativeErrorEx(err, NULL);
338 char new_realpath[PLATFORM_MAX_PATH];
339 g_SourceMod.BuildPath(Path_Game, new_realpath, sizeof(new_realpath), "%s", newpath);
340 char old_realpath[PLATFORM_MAX_PATH];
341 g_SourceMod.BuildPath(Path_Game, old_realpath, sizeof(old_realpath), "%s", oldpath);
343 #ifdef PLATFORM_WINDOWS
344 return (MoveFileA(old_realpath, new_realpath)) ? 1 : 0;
345 #elif defined PLATFORM_POSIX
346 return (rename(old_realpath, new_realpath)) ? 0 : 1;
350 static cell_t sm_DirExists(IPluginContext *pContext, const cell_t *params)
354 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
356 pContext->ThrowNativeErrorEx(err, NULL);
360 char realpath[PLATFORM_MAX_PATH];
361 g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
362 #ifdef PLATFORM_WINDOWS
364 if (_stat(realpath, &s) != 0)
368 if (s.st_mode & S_IFDIR)
373 #elif defined PLATFORM_POSIX
375 if (stat(realpath, &s) != 0)
379 if (S_ISDIR(s.st_mode))
387 static cell_t sm_FileSize(IPluginContext *pContext, const cell_t *params)
391 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
393 pContext->ThrowNativeErrorEx(err, NULL);
397 char realpath[PLATFORM_MAX_PATH];
398 g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
399 #ifdef PLATFORM_WINDOWS
401 if (_stat(realpath, &s) != 0)
405 if (s.st_mode & S_IFREG)
407 return static_cast<cell_t>(s.st_size);
410 #elif defined PLATFORM_POSIX
412 if (stat(realpath, &s) != 0)
416 if (S_ISREG(s.st_mode))
418 return static_cast<cell_t>(s.st_size);
424 static cell_t sm_RemoveDir(IPluginContext *pContext, const cell_t *params)
428 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
430 pContext->ThrowNativeErrorEx(err, NULL);
434 char realpath[PLATFORM_MAX_PATH];
435 g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
437 return (rmdir(realpath)) ? 0 : 1;
440 static cell_t sm_WriteFileLine(IPluginContext *pContext, const cell_t *params)
442 Handle_t hndl = static_cast<Handle_t>(params[1]);
448 sec.pIdentity = g_pCoreIdent;
450 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
453 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
458 if ((err=pContext->LocalToString(params[2], &fmt)) != SP_ERROR_NONE)
460 pContext->ThrowNativeErrorEx(err, NULL);
466 atcprintf(buffer, sizeof(buffer), fmt, pContext, params, &arg);
467 fprintf(pFile, "%s\n", buffer);
472 static cell_t sm_FlushFile(IPluginContext *pContext, const cell_t *params)
474 Handle_t hndl = static_cast<Handle_t>(params[1]);
480 sec.pIdentity = g_pCoreIdent;
482 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
485 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
488 return (fflush(pFile) == 0) ? 1 : 0;
491 static cell_t sm_BuildPath(IPluginContext *pContext, const cell_t *params)
493 char path[PLATFORM_MAX_PATH], *fmt, *buffer;
495 pContext->LocalToString(params[2], &buffer);
496 pContext->LocalToString(params[4], &fmt);
498 atcprintf(path, sizeof(path), fmt, pContext, params, &arg);
500 return g_SourceMod.BuildPath(Path_SM_Rel, buffer, params[3], "%s", path);
503 static cell_t sm_LogToGame(IPluginContext *pContext, const cell_t *params)
505 g_SourceMod.SetGlobalTarget(LANG_SERVER);
508 size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
510 if (pContext->GetContext()->n_err != SP_ERROR_NONE)
515 if (len >= sizeof(buffer)-2)
520 buffer[len++] = '\n';
524 engine->LogPrint(buffer);
529 static cell_t sm_LogMessage(IPluginContext *pContext, const cell_t *params)
531 g_SourceMod.SetGlobalTarget(LANG_SERVER);
534 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
536 if (pContext->GetContext()->n_err != SP_ERROR_NONE)
541 CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
542 g_Logger.LogMessage("[%s] %s", pPlugin->GetFilename(), buffer);
547 static cell_t sm_LogError(IPluginContext *pContext, const cell_t *params)
549 g_SourceMod.SetGlobalTarget(LANG_SERVER);
552 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 1);
554 if (pContext->GetContext()->n_err != SP_ERROR_NONE)
559 CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
560 g_Logger.LogError("[%s] %s", pPlugin->GetFilename(), buffer);
565 static cell_t sm_GetFileTime(IPluginContext *pContext, const cell_t *params)
569 if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
571 pContext->ThrowNativeErrorEx(err, NULL);
576 char realpath[PLATFORM_MAX_PATH];
577 g_SourceMod.BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
579 if (!g_LibSys.FileTime(realpath, (FileTimeType)params[2], &time_val))
584 return (cell_t)time_val;
587 static cell_t sm_LogToOpenFile(IPluginContext *pContext, const cell_t *params)
589 Handle_t hndl = static_cast<Handle_t>(params[1]);
595 sec.pIdentity = g_pCoreIdent;
597 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
600 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
604 g_SourceMod.SetGlobalTarget(LANG_SERVER);
605 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
607 if (pContext->GetContext()->n_err != SP_ERROR_NONE)
612 CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
613 g_Logger.LogToOpenFile(pFile, "[%s] %s", pPlugin->GetFilename(), buffer);
618 static cell_t sm_LogToOpenFileEx(IPluginContext *pContext, const cell_t *params)
620 Handle_t hndl = static_cast<Handle_t>(params[1]);
626 sec.pIdentity = g_pCoreIdent;
628 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
631 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
635 g_SourceMod.SetGlobalTarget(LANG_SERVER);
636 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
638 if (pContext->GetContext()->n_err != SP_ERROR_NONE)
643 g_Logger.LogToOpenFile(pFile, "%s", buffer);
648 static cell_t sm_ReadFile(IPluginContext *pContext, const cell_t *params)
650 Handle_t hndl = static_cast<Handle_t>(params[1]);
652 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
656 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
659 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
662 if (params[4] != 1 && params[4] != 2 && params[4] != 4)
664 return pContext->ThrowNativeError("Invalid size specifier (%d is not 1, 2, or 4)", params[4]);
668 pContext->LocalToPhysAddr(params[2], &data);
672 read = fread(data, sizeof(cell_t), params[3], pFile);
674 else if (params[4] == 2)
677 for (cell_t i = 0; i < params[3]; i++)
679 if (fread(&val, sizeof(int16_t), 1, pFile) != 1)
686 else if (params[4] == 1)
689 for (cell_t i = 0; i < params[3]; i++)
691 if (fread(&val, sizeof(int8_t), 1, pFile) != 1)
699 if (read != (size_t)params[3] && ferror(pFile) != 0)
707 static cell_t sm_ReadFileString(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=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
718 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
722 pContext->LocalToString(params[2], &buffer);
727 /* If we're in stop mode, break as soon as the buffer is full. */
728 if (params[4] && (params[3] == 0 || num_read >= params[3] - 1))
732 if (fread(&val, sizeof(val), 1, pFile) != 1)
744 if (params[3] > 0 && num_read < params[3] - 1)
746 buffer[num_read++] = val;
752 buffer[num_read] = '\0';
758 static cell_t sm_WriteFile(IPluginContext *pContext, const cell_t *params)
760 Handle_t hndl = static_cast<Handle_t>(params[1]);
762 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
765 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
768 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
772 pContext->LocalToPhysAddr(params[2], &data);
774 if (params[4] != 1 && params[4] != 2 && params[4] != 4)
776 return pContext->ThrowNativeError("Invalid size specifier (%d is not 1, 2, or 4)", params[4]);
779 /* :NOTE: This really isn't compatible with big endian but we will never have to worry about that. */
783 if (fwrite(data, sizeof(cell_t), params[3], pFile) != (size_t)params[3])
788 else if (params[4] == 2)
790 for (cell_t i = 0; i < params[3]; i++)
792 if (fwrite(&data[i], sizeof(int16_t), 1, pFile) != 1)
798 else if (params[4] == 1)
800 for (cell_t i = 0; i < params[3]; i++)
802 if (fwrite(&data[i], sizeof(int8_t), 1, pFile) != 1)
812 static cell_t sm_WriteFileString(IPluginContext *pContext, const cell_t *params)
814 Handle_t hndl = static_cast<Handle_t>(params[1]);
816 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
819 if ((herr=g_HandleSys.ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
822 return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
826 pContext->LocalToString(params[2], &buffer);
828 size_t len = strlen(buffer);
835 return (fwrite(buffer, sizeof(char), len, pFile) == len) ? 1 : 0;
838 REGISTER_NATIVES(filesystem)
840 {"OpenDirectory", sm_OpenDirectory},
841 {"ReadDirEntry", sm_ReadDirEntry},
842 {"OpenFile", sm_OpenFile},
843 {"DeleteFile", sm_DeleteFile},
844 {"ReadFileLine", sm_ReadFileLine},
845 {"IsEndOfFile", sm_IsEndOfFile},
846 {"FileSeek", sm_FileSeek},
847 {"FilePosition", sm_FilePosition},
848 {"FileExists", sm_FileExists},
849 {"RenameFile", sm_RenameFile},
850 {"DirExists", sm_DirExists},
851 {"FileSize", sm_FileSize},
852 {"RemoveDir", sm_RemoveDir},
853 {"WriteFileLine", sm_WriteFileLine},
854 {"BuildPath", sm_BuildPath},
855 {"LogToGame", sm_LogToGame},
856 {"LogMessage", sm_LogMessage},
857 {"LogError", sm_LogError},
858 {"FlushFile", sm_FlushFile},
859 {"GetFileTime", sm_GetFileTime},
860 {"LogToOpenFile", sm_LogToOpenFile},
861 {"LogToOpenFileEx", sm_LogToOpenFileEx},
862 {"ReadFile", sm_ReadFile},
863 {"ReadFileString", sm_ReadFileString},
864 {"WriteFile", sm_WriteFile},
865 {"WriteFileString", sm_WriteFileString},