core/logic/smn_filesystem.cpp
author Kyle Sanderson <kyle.leet@gmail.com>
Mon Aug 12 00:42:56 2013 +0100 (2013-08-12)
changeset 3994 4bc73bac0f4c
parent 3794 b15d5b65fcda
child 4198 d6f90552ca85
permissions -rw-r--r--
Added helper stock to check if a command exists. (bug 5510, r=asherkin)
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     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.
    11  * 
    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
    15  * details.
    16  *
    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/>.
    19  *
    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>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <sys/stat.h>
    33 #include <string.h>
    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"
    41 
    42 HandleType_t g_FileType;
    43 HandleType_t g_DirType;
    44 IChangeableForward *g_pLogHook = NULL;
    45 
    46 class FileNatives : 
    47 	public SMGlobalClass,
    48 	public IHandleTypeDispatch,
    49 	public IPluginsListener
    50 {
    51 public:
    52 	FileNatives()
    53 	{
    54 	}
    55 	virtual void OnSourceModAllInitialized()
    56 	{
    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);
    61 	}
    62 	virtual void OnSourceModShutdown()
    63 	{
    64 		pluginsys->RemovePluginsListener(this);
    65 		forwardsys->ReleaseForward(g_pLogHook);
    66 		handlesys->RemoveType(g_DirType, g_pCoreIdent);
    67 		handlesys->RemoveType(g_FileType, g_pCoreIdent);
    68 		g_DirType = 0;
    69 		g_FileType = 0;
    70 	}
    71 	virtual void OnHandleDestroy(HandleType_t type, void *object)
    72 	{
    73 		if (type == g_FileType)
    74 		{
    75 			FILE *fp = (FILE *)object;
    76 			fclose(fp);
    77 		}
    78 		else if (type == g_DirType)
    79 		{
    80 			IDirectory *pDir = (IDirectory *)object;
    81 			libsys->CloseDirectory(pDir);
    82 		}
    83 	}
    84 	virtual void AddLogHook(IPluginFunction *pFunc)
    85 	{
    86 		g_pLogHook->AddFunction(pFunc);
    87 	}
    88 	virtual void RemoveLogHook(IPluginFunction *pFunc)
    89 	{
    90 		g_pLogHook->RemoveFunction(pFunc);
    91 	}
    92 	virtual bool LogPrint(const char *msg)
    93 	{
    94 		cell_t result = 0;
    95 		g_pLogHook->PushString(msg);
    96 		g_pLogHook->Execute(&result);
    97 		return result >= Pl_Handled;
    98 	}
    99 } s_FileNatives;
   100 
   101 bool OnLogPrint(const char *msg)
   102 {
   103 	return s_FileNatives.LogPrint(msg);
   104 }
   105 
   106 static cell_t sm_OpenDirectory(IPluginContext *pContext, const cell_t *params)
   107 {
   108 	char *path;
   109 	int err;
   110 	if ((err=pContext->LocalToString(params[1], &path)) != SP_ERROR_NONE)
   111 	{
   112 		pContext->ThrowNativeErrorEx(err, NULL);
   113 		return 0;
   114 	}
   115 
   116 	char realpath[PLATFORM_MAX_PATH];
   117 	g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", path);
   118 
   119 	IDirectory *pDir = libsys->OpenDirectory(realpath);
   120 	if (!pDir)
   121 	{
   122 		return 0;
   123 	}
   124 
   125 	return handlesys->CreateHandle(g_DirType, pDir, pContext->GetIdentity(), g_pCoreIdent, NULL);
   126 }
   127 
   128 static cell_t sm_ReadDirEntry(IPluginContext *pContext, const cell_t *params)
   129 {
   130 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   131 	IDirectory *pDir;
   132 	HandleError herr;
   133 	HandleSecurity sec;
   134 	int err;
   135 
   136 	sec.pOwner = NULL;
   137 	sec.pIdentity = g_pCoreIdent;
   138 
   139 	if ((herr=handlesys->ReadHandle(hndl, g_DirType, &sec, (void **)&pDir))
   140 		!= HandleError_None)
   141 	{
   142 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   143 	}
   144 
   145 	if (!pDir->MoreFiles())
   146 	{
   147 		return false;
   148 	}
   149 
   150 	cell_t *filetype;
   151 	if ((err=pContext->LocalToPhysAddr(params[4], &filetype)) != SP_ERROR_NONE)
   152 	{
   153 		pContext->ThrowNativeErrorEx(err, NULL);
   154 		return 0;
   155 	}
   156 
   157 	if (pDir->IsEntryDirectory())
   158 	{
   159 		*filetype = 1;
   160 	} else if (pDir->IsEntryFile()) {
   161 		*filetype = 2;
   162 	} else {
   163 		*filetype = 0;
   164 	}
   165 
   166 	const char *path = pDir->GetEntryName();
   167 	if ((err=pContext->StringToLocalUTF8(params[2], params[3], path, NULL))
   168 		!= SP_ERROR_NONE)
   169 	{
   170 		pContext->ThrowNativeErrorEx(err, NULL);
   171 		return 0;
   172 	}
   173 
   174 	pDir->NextEntry();
   175 
   176 	return true;
   177 }
   178 
   179 static cell_t sm_OpenFile(IPluginContext *pContext, const cell_t *params)
   180 {
   181 	char *name, *mode;
   182 	int err;
   183 	if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
   184 	{
   185 		pContext->ThrowNativeErrorEx(err, NULL);
   186 		return 0;
   187 	}
   188 	if ((err=pContext->LocalToString(params[2], &mode)) != SP_ERROR_NONE)
   189 	{
   190 		pContext->ThrowNativeErrorEx(err, NULL);
   191 		return 0;
   192 	}
   193 
   194 	char realpath[PLATFORM_MAX_PATH];
   195 	g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
   196 
   197 	FILE *pFile = fopen(realpath, mode);
   198 	if (!pFile)
   199 	{
   200 		return 0;
   201 	}
   202 
   203 	return handlesys->CreateHandle(g_FileType, pFile, pContext->GetIdentity(), g_pCoreIdent, NULL);
   204 }
   205 
   206 static cell_t sm_DeleteFile(IPluginContext *pContext, const cell_t *params)
   207 {
   208 	char *name;
   209 	int err;
   210 	if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
   211 	{
   212 		pContext->ThrowNativeErrorEx(err, NULL);
   213 		return 0;
   214 	}
   215 
   216 	char realpath[PLATFORM_MAX_PATH];
   217 	g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
   218 
   219 	return (unlink(realpath)) ? 0 : 1;
   220 }
   221 
   222 static cell_t sm_ReadFileLine(IPluginContext *pContext, const cell_t *params)
   223 {
   224 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   225 	HandleError herr;
   226 	HandleSecurity sec;
   227 	FILE *pFile;
   228 	int err;
   229 
   230 	sec.pOwner = NULL;
   231 	sec.pIdentity = g_pCoreIdent;
   232 
   233 	if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   234 		!= HandleError_None)
   235 	{
   236 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   237 	}
   238 
   239 	char *buf;
   240 	if ((err=pContext->LocalToString(params[2], &buf)) != SP_ERROR_NONE)
   241 	{
   242 		pContext->ThrowNativeErrorEx(err, NULL);
   243 		return 0;
   244 	}
   245 
   246 	if (fgets(buf, params[3], pFile) == NULL)
   247 	{
   248 		return 0;
   249 	}
   250 
   251 	return 1;
   252 }
   253 
   254 static cell_t sm_IsEndOfFile(IPluginContext *pContext, const cell_t *params)
   255 {
   256 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   257 	HandleError herr;
   258 	HandleSecurity sec;
   259 	FILE *pFile;
   260 
   261 	sec.pOwner = NULL;
   262 	sec.pIdentity = g_pCoreIdent;
   263 
   264 	if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   265 		!= HandleError_None)
   266 	{
   267 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   268 	}
   269 
   270 	return (feof(pFile)) ? 1 : 0;
   271 }
   272 
   273 static cell_t sm_FileSeek(IPluginContext *pContext, const cell_t *params)
   274 {
   275 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   276 	HandleError herr;
   277 	HandleSecurity sec;
   278 	FILE *pFile;
   279 
   280 	sec.pOwner = NULL;
   281 	sec.pIdentity = g_pCoreIdent;
   282 
   283 	if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   284 		!= HandleError_None)
   285 	{
   286 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   287 	}
   288 
   289 	fseek(pFile, params[2], params[3]);
   290 	
   291 	return 1;
   292 }
   293 
   294 static cell_t sm_FilePosition(IPluginContext *pContext, const cell_t *params)
   295 {
   296 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   297 	HandleError herr;
   298 	HandleSecurity sec;
   299 	FILE *pFile;
   300 
   301 	sec.pOwner = NULL;
   302 	sec.pIdentity = g_pCoreIdent;
   303 
   304 	if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   305 		!= HandleError_None)
   306 	{
   307 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   308 	}
   309 
   310 	return ftell(pFile);
   311 }
   312 
   313 static cell_t sm_FileExists(IPluginContext *pContext, const cell_t *params)
   314 {
   315 	char *name;
   316 	int err;
   317 	if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
   318 	{
   319 		pContext->ThrowNativeErrorEx(err, NULL);
   320 		return 0;
   321 	}
   322 
   323 	if (params[0] >= 2 && params[2] == 1)
   324 	{
   325 		return smcore.FileExists(name) ? 1 : 0;
   326 	}
   327 
   328 	char realpath[PLATFORM_MAX_PATH];
   329 	g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
   330 #ifdef PLATFORM_WINDOWS
   331 	struct _stat s;
   332 	if (_stat(realpath, &s) != 0)
   333 	{
   334 		return 0;
   335 	}
   336 	if (s.st_mode & S_IFREG)
   337 	{
   338 		return 1;
   339 	}
   340 	return 0;
   341 #elif defined PLATFORM_POSIX
   342 	struct stat s;
   343 	if (stat(realpath, &s) != 0)
   344 	{
   345 		return 0;
   346 	}
   347 	if (S_ISREG(s.st_mode))
   348 	{
   349 		return 1;
   350 	}
   351 	return 0;
   352 #endif
   353 }
   354 
   355 static cell_t sm_RenameFile(IPluginContext *pContext, const cell_t *params)
   356 {
   357 	char *newpath, *oldpath;
   358 	int err;
   359 	if ((err=pContext->LocalToString(params[1], &newpath)) != SP_ERROR_NONE)
   360 	{
   361 		pContext->ThrowNativeErrorEx(err, NULL);
   362 		return 0;
   363 	}
   364 	if ((err=pContext->LocalToString(params[2], &oldpath)) != SP_ERROR_NONE)
   365 	{
   366 		pContext->ThrowNativeErrorEx(err, NULL);
   367 		return 0;
   368 	}
   369 
   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);
   374 
   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;
   379 #endif
   380 }
   381 
   382 static cell_t sm_DirExists(IPluginContext *pContext, const cell_t *params)
   383 {
   384 	char *name;
   385 	int err;
   386 	if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
   387 	{
   388 		pContext->ThrowNativeErrorEx(err, NULL);
   389 		return 0;
   390 	}
   391 
   392 	char realpath[PLATFORM_MAX_PATH];
   393 	g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
   394 #ifdef PLATFORM_WINDOWS
   395 	struct _stat s;
   396 	if (_stat(realpath, &s) != 0)
   397 	{
   398 		return 0;
   399 	}
   400 	if (s.st_mode & S_IFDIR)
   401 	{
   402 		return 1;
   403 	}
   404 	return 0;
   405 #elif defined PLATFORM_POSIX
   406 	struct stat s;
   407 	if (stat(realpath, &s) != 0)
   408 	{
   409 		return 0;
   410 	}
   411 	if (S_ISDIR(s.st_mode))
   412 	{
   413 		return 1;
   414 	}
   415 	return 0;
   416 #endif
   417 }
   418 
   419 static cell_t sm_FileSize(IPluginContext *pContext, const cell_t *params)
   420 {
   421 	char *name;
   422 	int err;
   423 	if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
   424 	{
   425 		pContext->ThrowNativeErrorEx(err, NULL);
   426 		return -1;
   427 	}
   428 
   429 	char realpath[PLATFORM_MAX_PATH];
   430 	g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
   431 #ifdef PLATFORM_WINDOWS
   432 	struct _stat s;
   433 	if (_stat(realpath, &s) != 0)
   434 	{
   435 		return -1;
   436 	}
   437 	if (s.st_mode & S_IFREG)
   438 	{
   439 		return static_cast<cell_t>(s.st_size);
   440 	}
   441 	return -1;
   442 #elif defined PLATFORM_POSIX
   443 	struct stat s;
   444 	if (stat(realpath, &s) != 0)
   445 	{
   446 		return -1;
   447 	}
   448 	if (S_ISREG(s.st_mode))
   449 	{
   450 		return static_cast<cell_t>(s.st_size);
   451 	}
   452 	return -1;
   453 #endif
   454 }
   455 
   456 static cell_t sm_CreateDirectory(IPluginContext *pContext, const cell_t *params)
   457 {
   458 	char *name;
   459 	char realpath[PLATFORM_MAX_PATH];
   460 
   461 	pContext->LocalToString(params[1], &name);
   462 	g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
   463 
   464 #if defined PLATFORM_WINDOWS
   465 	return mkdir(realpath) == 0;
   466 #else
   467 	return mkdir(realpath, params[2]) == 0;
   468 #endif
   469 }
   470 
   471 static cell_t sm_RemoveDir(IPluginContext *pContext, const cell_t *params)
   472 {
   473 	char *name;
   474 	int err;
   475 	if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
   476 	{
   477 		pContext->ThrowNativeErrorEx(err, NULL);
   478 		return 0;
   479 	}
   480 
   481 	char realpath[PLATFORM_MAX_PATH];
   482 	g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
   483 
   484 	return (rmdir(realpath)) ? 0 : 1;
   485 }
   486 
   487 static cell_t sm_WriteFileLine(IPluginContext *pContext, const cell_t *params)
   488 {
   489 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   490 	HandleError herr;
   491 	HandleSecurity sec;
   492 	FILE *pFile;
   493 
   494 	sec.pOwner = NULL;
   495 	sec.pIdentity = g_pCoreIdent;
   496 
   497 	if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   498 		!= HandleError_None)
   499 	{
   500 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   501 	}
   502 
   503 	char *fmt;
   504 	int err;
   505 	if ((err=pContext->LocalToString(params[2], &fmt)) != SP_ERROR_NONE)
   506 	{
   507 		pContext->ThrowNativeErrorEx(err, NULL);
   508 		return 0;
   509 	}
   510 
   511 	char buffer[2048];
   512 	int arg = 3;
   513 	smcore.atcprintf(buffer, sizeof(buffer), fmt, pContext, params, &arg);
   514 	fprintf(pFile, "%s\n", buffer);
   515 
   516 	return 1;
   517 }
   518 
   519 static cell_t sm_FlushFile(IPluginContext *pContext, const cell_t *params)
   520 {
   521 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   522 	HandleError herr;
   523 	HandleSecurity sec;
   524 	FILE *pFile;
   525 
   526 	sec.pOwner = NULL;
   527 	sec.pIdentity = g_pCoreIdent;
   528 
   529 	if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   530 		!= HandleError_None)
   531 	{
   532 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   533 	}
   534 
   535 	return (fflush(pFile) == 0) ? 1 : 0;
   536 }
   537 
   538 static cell_t sm_BuildPath(IPluginContext *pContext, const cell_t *params)
   539 {
   540 	char path[PLATFORM_MAX_PATH], *fmt, *buffer;
   541 	int arg = 5;
   542 	pContext->LocalToString(params[2], &buffer);
   543 	pContext->LocalToString(params[4], &fmt);
   544 
   545 	smcore.atcprintf(path, sizeof(path), fmt, pContext, params, &arg);
   546 
   547 	return g_pSM->BuildPath(Path_SM_Rel, buffer, params[3], "%s", path);
   548 }
   549 
   550 static cell_t sm_LogToGame(IPluginContext *pContext, const cell_t *params)
   551 {
   552 	g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
   553 
   554 	char buffer[1024];
   555 	size_t len = g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
   556 
   557 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
   558 	{
   559 		return 0;
   560 	}
   561 
   562 	if (len >= sizeof(buffer)-2)
   563 	{
   564 		buffer[1022] = '\n';
   565 		buffer[1023] = '\0';
   566 	} else {
   567 		buffer[len++] = '\n';
   568 		buffer[len] = '\0';
   569 	}
   570 
   571 	smcore.LogToGame(buffer);
   572 
   573 	return 1;
   574 }
   575 
   576 static cell_t sm_LogMessage(IPluginContext *pContext, const cell_t *params)
   577 {
   578 	g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
   579 
   580 	char buffer[1024];
   581 	g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
   582 
   583 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
   584 	{
   585 		return 0;
   586 	}
   587 
   588 	IPlugin *pPlugin = pluginsys->FindPluginByContext(pContext->GetContext());
   589 	smcore.Log("[%s] %s", pPlugin->GetFilename(), buffer);
   590 
   591 	return 1;
   592 }
   593 
   594 static cell_t sm_LogError(IPluginContext *pContext, const cell_t *params)
   595 {
   596 	g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
   597 
   598 	char buffer[1024];
   599 	g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 1);
   600 
   601 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
   602 	{
   603 		return 0;
   604 	}
   605 
   606 	IPlugin *pPlugin = pluginsys->FindPluginByContext(pContext->GetContext());
   607 	smcore.LogError("[%s] %s", pPlugin->GetFilename(), buffer);
   608 
   609 	return 1;
   610 }
   611 
   612 static cell_t sm_GetFileTime(IPluginContext *pContext, const cell_t *params)
   613 {
   614 	char *name;
   615 	int err;
   616 	if ((err=pContext->LocalToString(params[1], &name)) != SP_ERROR_NONE)
   617 	{
   618 		pContext->ThrowNativeErrorEx(err, NULL);
   619 		return 0;
   620 	}
   621 
   622 	time_t time_val;
   623 	char realpath[PLATFORM_MAX_PATH];
   624 	g_pSM->BuildPath(Path_Game, realpath, sizeof(realpath), "%s", name);
   625 
   626 	if (!libsys->FileTime(realpath, (FileTimeType)params[2], &time_val))
   627 	{
   628 		return -1;
   629 	}
   630 
   631 	return (cell_t)time_val;
   632 }
   633 
   634 static cell_t sm_LogToOpenFile(IPluginContext *pContext, const cell_t *params)
   635 {
   636 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   637 	HandleError herr;
   638 	HandleSecurity sec;
   639 	FILE *pFile;
   640 
   641 	sec.pOwner = NULL;
   642 	sec.pIdentity = g_pCoreIdent;
   643 
   644 	if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   645 		!= HandleError_None)
   646 	{
   647 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   648 	}
   649 
   650 	char buffer[2048];
   651 	g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
   652 	g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
   653 
   654 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
   655 	{
   656 		return 0;
   657 	}
   658 
   659 	IPlugin *pPlugin = pluginsys->FindPluginByContext(pContext->GetContext());
   660 	smcore.LogToFile(pFile, "[%s] %s", pPlugin->GetFilename(), buffer);
   661 
   662 	return 1;
   663 }
   664 
   665 static cell_t sm_LogToOpenFileEx(IPluginContext *pContext, const cell_t *params)
   666 {
   667 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   668 	HandleError herr;
   669 	HandleSecurity sec;
   670 	FILE *pFile;
   671 
   672 	sec.pOwner = NULL;
   673 	sec.pIdentity = g_pCoreIdent;
   674 
   675 	if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   676 		!= HandleError_None)
   677 	{
   678 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   679 	}
   680 
   681 	char buffer[2048];
   682 	g_pSM->SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
   683 	g_pSM->FormatString(buffer, sizeof(buffer), pContext, params, 2);
   684 
   685 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
   686 	{
   687 		return 0;
   688 	}
   689 
   690 	smcore.LogToFile(pFile, "%s", buffer);
   691 
   692 	return 1;
   693 }
   694 
   695 static cell_t sm_ReadFile(IPluginContext *pContext, const cell_t *params)
   696 {
   697 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   698 	HandleError herr;
   699 	HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
   700 	FILE *pFile;
   701 	size_t read = 0;
   702 
   703 	if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   704 		!= HandleError_None)
   705 	{
   706 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   707 	}
   708 
   709 	if (params[4] != 1 && params[4] != 2 && params[4] != 4)
   710 	{
   711 		return pContext->ThrowNativeError("Invalid size specifier (%d is not 1, 2, or 4)", params[4]);
   712 	}
   713 
   714 	cell_t *data;
   715 	pContext->LocalToPhysAddr(params[2], &data);
   716 
   717 	if (params[4] == 4)
   718 	{
   719 		read = fread(data, sizeof(cell_t), params[3], pFile);
   720 	}
   721 	else if (params[4] == 2)
   722 	{
   723 		uint16_t val;
   724 		for (cell_t i = 0; i < params[3]; i++)
   725 		{
   726 			if (fread(&val, sizeof(uint16_t), 1, pFile) != 1)
   727 			{
   728 				break;
   729 			}
   730 			data[read++] = val;
   731 		}
   732 	}
   733 	else if (params[4] == 1)
   734 	{
   735 		uint8_t val;
   736 		for (cell_t i = 0; i < params[3]; i++)
   737 		{
   738 			if (fread(&val, sizeof(uint8_t), 1, pFile) != 1)
   739 			{
   740 				break;
   741 			}
   742 			data[read++] = val;
   743 		}
   744 	}
   745 
   746 	if (read != (size_t)params[3] && ferror(pFile) != 0)
   747 	{
   748 		return -1;
   749 	}
   750 
   751 	return read;
   752 }
   753 
   754 static cell_t sm_ReadFileString(IPluginContext *pContext, const cell_t *params)
   755 {
   756 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   757 	HandleError herr;
   758 	HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
   759 	FILE *pFile;
   760 	cell_t num_read = 0;
   761 
   762 	if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   763 		!= HandleError_None)
   764 	{
   765 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   766 	}
   767 
   768 	char *buffer;
   769 	pContext->LocalToString(params[2], &buffer);
   770 
   771 	if (params[4] != -1)
   772 	{
   773 		if (size_t(params[4]) > size_t(params[3]))
   774 		{
   775 			return pContext->ThrowNativeError("read_count (%u) is greater than buffer size (%u)",
   776 				params[4],
   777 				params[3]);
   778 		}
   779 
   780 		num_read = (cell_t)fread(buffer, 1, params[4], pFile);
   781 
   782 		if (num_read != params[4] && ferror(pFile))
   783 		{
   784 			return -1;
   785 		}
   786 
   787 		return num_read;
   788 	}
   789 
   790 	char val;
   791 	while (1)
   792 	{
   793 		if (params[3] == 0 || num_read >= params[3] - 1)
   794 		{
   795 			break;
   796 		}
   797 		if (fread(&val, sizeof(val), 1, pFile) != 1)
   798 		{
   799 			if (ferror(pFile))
   800 			{
   801 				return -1;
   802 			}
   803 			break;
   804 		}
   805 		if (val == '\0')
   806 		{
   807 			break;
   808 		}
   809 		if (params[3] > 0 && num_read < params[3] - 1)
   810 		{
   811 			buffer[num_read++] = val;
   812 		}
   813 	}
   814 
   815 	if (params[3] > 0)
   816 	{
   817 		buffer[num_read] = '\0';
   818 	}
   819 
   820 	return num_read;
   821 }
   822 
   823 static cell_t sm_WriteFile(IPluginContext *pContext, const cell_t *params)
   824 {
   825 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   826 	HandleError herr;
   827 	HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
   828 	FILE *pFile;
   829 
   830 	if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   831 		!= HandleError_None)
   832 	{
   833 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   834 	}
   835 
   836 	cell_t *data;
   837 	pContext->LocalToPhysAddr(params[2], &data);
   838 
   839 	if (params[4] != 1 && params[4] != 2 && params[4] != 4)
   840 	{
   841 		return pContext->ThrowNativeError("Invalid size specifier (%d is not 1, 2, or 4)", params[4]);
   842 	}
   843 
   844 	/* :NOTE: This really isn't compatible with big endian but we will never have to worry about that. */
   845 
   846 	if (params[4] == 4)
   847 	{
   848 		if (fwrite(data, sizeof(cell_t), params[3], pFile) != (size_t)params[3])
   849 		{
   850 			return 0;
   851 		}
   852 	}
   853 	else if (params[4] == 2)
   854 	{
   855 		for (cell_t i = 0; i < params[3]; i++)
   856 		{
   857 			if (fwrite(&data[i], sizeof(int16_t), 1, pFile) != 1)
   858 			{
   859 				return 0;
   860 			}
   861 		}
   862 	}
   863 	else if (params[4] == 1)
   864 	{
   865 		for (cell_t i = 0; i < params[3]; i++)
   866 		{
   867 			if (fwrite(&data[i], sizeof(int8_t), 1, pFile) != 1)
   868 			{
   869 				return 0;
   870 			}
   871 		}
   872 	}
   873 
   874 	return 1;
   875 }
   876 
   877 static cell_t sm_WriteFileString(IPluginContext *pContext, const cell_t *params)
   878 {
   879 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   880 	HandleError herr;
   881 	HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
   882 	FILE *pFile;
   883 
   884 	if ((herr=handlesys->ReadHandle(hndl, g_FileType, &sec, (void **)&pFile))
   885 		!= HandleError_None)
   886 	{
   887 		return pContext->ThrowNativeError("Invalid file handle %x (error %d)", hndl, herr);
   888 	}
   889 
   890 	char *buffer;
   891 	pContext->LocalToString(params[2], &buffer);
   892 
   893 	size_t len = strlen(buffer);
   894 
   895 	if (params[3])
   896 	{
   897 		len++;
   898 	}
   899 
   900 	return (fwrite(buffer, sizeof(char), len, pFile) == len) ? 1 : 0;
   901 }
   902 
   903 static cell_t sm_AddGameLogHook(IPluginContext *pContext, const cell_t *params)
   904 {
   905 	IPluginFunction *pFunction;
   906 
   907 	if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
   908 	{
   909 		return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
   910 	}
   911 
   912 	s_FileNatives.AddLogHook(pFunction);
   913 	
   914 	return 1;
   915 }
   916 
   917 static cell_t sm_RemoveGameLogHook(IPluginContext *pContext, const cell_t *params)
   918 {
   919 	IPluginFunction *pFunction;
   920 
   921 	if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
   922 	{
   923 		return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
   924 	}
   925 
   926 	s_FileNatives.RemoveLogHook(pFunction);
   927 
   928 	return 1;
   929 }
   930 
   931 REGISTER_NATIVES(filesystem)
   932 {
   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},
   962 	{NULL,						NULL},
   963 };