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