amxmodx/amxmodx/file.cpp
2014-08-07 01:42:58 +02:00

921 lines
17 KiB
C++
Executable File

// vim: set ts=4 sw=4 tw=99 noet:
//
// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO").
// Copyright (C) The AMX Mod X Development Team.
//
// This software is licensed under the GNU General Public License, version 3 or higher.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://alliedmods.net/amxmodx-license
// intptr_t
#ifdef _MSC_VER
typedef int intptr_t;
#define _INTPTR_T_DEFINED
#endif
#ifdef __GNUC__
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#endif
// header file for unlink()
#if defined(__linux__) || defined(__APPLE__)
#include <unistd.h>
#else
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#include <io.h>
#endif
#include "amxmodx.h"
CVector<FILE *> FileList;
class AutoFilePtr
{
FILE *m_FP;
public:
AutoFilePtr(FILE *fp) : m_FP(fp)
{}
~AutoFilePtr()
{
if (m_FP)
fclose(m_FP);
}
operator FILE* ()
{
return m_FP;
}
};
enum FileTimeType
{
FileTime_LastAccess = 0, /* Last access (not available on FAT) */
FileTime_Created = 1, /* Creation (not available on FAT) */
FileTime_LastChange = 2, /* Last modification */
};
static cell AMX_NATIVE_CALL read_dir(AMX *amx, cell *params)
{
#ifdef __GNUC__
int a;
struct dirent *ep;
DIR *dp;
char* dirname = build_pathname("%s", get_amxstring(amx, params[1], 0, a));
a = params[2];
if ((dp = opendir (dirname)) == NULL)
return 0;
seekdir(dp, a);
if ((ep = readdir (dp)) != NULL)
{
cell *length = get_amxaddr(amx, params[5]);
*length = set_amxstring(amx, params[3], ep->d_name, params[4]);
a = telldir(dp);
} else
a = 0;
closedir (dp);
return a;
#else
int tmp;
char *dirname = build_pathname("%s/*", get_amxstring(amx, params[1], 0, tmp));
tmp = params[2];
_finddata_t fd;
intptr_t handle = _findfirst(dirname, &fd);
if (handle < 0)
return 0;
++tmp;
for (int i = 0; i < tmp; ++i)
{
if (_findnext(handle, &fd) < 0)
{
tmp = 0;
break;
}
}
// current data in fd
cell *length = get_amxaddr(amx, params[5]); // pointer to the outLen parameter
*length = set_amxstring(amx, params[3], fd.name, params[4]); // set output and outLen parameters
_findclose(handle);
return tmp;
#endif // __GNUC__
}
static cell AMX_NATIVE_CALL read_file(AMX *amx, cell *params) /* 5 param */
{
int iLen;
char* szFile = get_amxstring(amx, params[1], 0, iLen);
FILE *fp;
if ((fp =fopen(build_pathname("%s", szFile), "r")) == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Couldn't read file \"%s\"", szFile);
return 0;
}
char buffor[1024];
int i = 0, iLine = params[2];
while ((i <= iLine) && fgets(buffor, 1023, fp))
i++;
fclose(fp);
if (i > iLine)
{
int len = strlen(buffor);
if (buffor[len - 1] == '\n')
buffor[--len] = 0;
if (buffor[len - 1] == '\r')
buffor[--len] = 0;
cell *length = get_amxaddr(amx, params[5]);
*length = set_amxstring_utf8(amx, params[3], buffor, len, params[4] + 1); // + EOS
return i;
}
return 0;
}
static cell AMX_NATIVE_CALL write_file(AMX *amx, cell *params) /* 3 param */
{
int i;
char* sFile = build_pathname("%s", get_amxstring(amx, params[1], 0, i));
char* sText = get_amxstring(amx, params[2], 0, i);
FILE* pFile;
int iLine = params[3];
// apending to the end
if (iLine < 0)
{
if ((pFile = fopen(sFile, "a")) == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Couldn't write file \"%s\"", sFile);
return 0;
}
fputs(sText, pFile);
fputc('\n', pFile);
fclose(pFile);
return 1;
}
// creating a new file with a line in a middle
if ((pFile = fopen(sFile, "r")) == NULL)
{
if ((pFile = fopen(sFile, "w")) == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Couldn't write file \"%s\"", sFile);
return 0;
}
for (i = 0; i < iLine; ++i)
fputc('\n', pFile);
fputs(sText, pFile);
fputc('\n', pFile);
fclose(pFile);
return 1;
}
// adding a new line in a middle of already existing file
FILE* pTemp;
char buffor[2048];
if ((pTemp = tmpfile()) == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Couldn't create temp file");
return 0;
}
for (i = 0; ; ++i)
{
if (i == iLine)
{
fgets(buffor, 2047, pFile);
fputs(sText, pTemp);
fputc('\n', pTemp);
}
else if (fgets(buffor, 2047, pFile))
{
fputs(buffor, pTemp);
}
else if (i < iLine)
{
fputc('\n', pTemp);
}
else
break;
}
fclose(pFile);
rewind(pTemp);
// now rewrite because file can be now smaller...
if ((pFile = fopen(sFile, "w")) == NULL)
{
LogError(amx, AMX_ERR_NATIVE, "Couldn't write file \"%s\"", sFile);
return 0;
}
while (fgets(buffor, 2047, pTemp))
fputs(buffor, pFile);
fclose(pTemp);
fclose(pFile);
return 1;
}
static cell AMX_NATIVE_CALL delete_file(AMX *amx, cell *params) /* 1 param */
{
int iLen;
char* sFile = get_amxstring(amx, params[1], 0, iLen);
return (unlink(build_pathname("%s", sFile)) ? 0 : 1);
}
static cell AMX_NATIVE_CALL file_exists(AMX *amx, cell *params) /* 1 param */
{
int iLen;
char *sFile = get_amxstring(amx, params[1], 0, iLen);
char *file = build_pathname("%s", sFile);
#if defined WIN32 || defined _WIN32
DWORD attr = GetFileAttributes(file);
if (attr == INVALID_FILE_ATTRIBUTES)
return 0;
if (attr == FILE_ATTRIBUTE_DIRECTORY)
return 0;
return 1;
#else
struct stat s;
if (stat(file, &s) != 0)
return 0;
if (S_ISDIR(s.st_mode))
return 0;
return 1;
#endif
}
static cell AMX_NATIVE_CALL dir_exists(AMX *amx, cell *params) /* 1 param */
{
int iLen;
char *sFile = get_amxstring(amx, params[1], 0, iLen);
char *file = build_pathname("%s", sFile);
return DirExists(file) ? 1 : 0;
}
static cell AMX_NATIVE_CALL file_size(AMX *amx, cell *params) /* 1 param */
{
int iLen;
char* sFile = get_amxstring(amx, params[1], 0, iLen);
AutoFilePtr fp(fopen(build_pathname("%s", sFile), "r"));
if (fp != NULL)
{
if (params[0] < 2 || params[2] == 0)
{
fseek(fp, 0, SEEK_END);
int size = ftell(fp);
return size;
}
else if (params[2] == 1)
{
int a = 0,lines = 0;
while (a != EOF)
{
++lines;
while ((a = fgetc(fp)) != '\n' && a != EOF);
}
//int a, b = '\n';
//while( (a = fgetc(fp)) != EOF ){
// if ( a == '\n')
// ++lines;
// b = a;
//}
//if ( b != '\n' )
// ++lines;
return lines;
}
else if (params[2] == 2)
{
fseek(fp, -1, SEEK_END);
if (fgetc(fp) == '\n')
return 1;
return 0;
}
}
return -1;
}
static cell AMX_NATIVE_CALL amx_fopen(AMX *amx, cell *params)
{
int len;
char *file = build_pathname("%s", get_amxstring(amx, params[1], 1, len));
char *flags = get_amxstring(amx, params[2], 0, len);
FILE *fp = fopen(file, flags);
return (cell)fp;
}
static cell AMX_NATIVE_CALL amx_fwrite_blocks(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 0;
cell *addr = get_amxaddr(amx, params[2]);
size_t blocks = params[3];
size_t btmp = blocks;
cell mode = params[4];
switch (mode)
{
case 1:
{
char *a = new char[blocks];
char *ptr = a;
while (btmp--)
*ptr++ = static_cast<char>(*addr++);
size_t res = fwrite(a, sizeof(char), blocks, fp);
delete [] a;
return res;
}
case 2:
{
short *a = new short[blocks];
short *ptr = a;
while (btmp--)
*ptr++ = static_cast<short>(*addr++);
size_t res = fwrite(a, sizeof(short), blocks, fp);
delete [] a;
return res;
}
case 4:
{
int *a = new int[blocks];
int *ptr = a;
while (btmp--)
*ptr++ = static_cast<int>(*addr++);
size_t res = fwrite(a, sizeof(int), blocks, fp);
delete [] a;
return res;
}
}
return 0;
}
static cell AMX_NATIVE_CALL amx_fwrite(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 0;
size_t mode = params[3];
switch (mode)
{
case 1:
{
char a = static_cast<char>(params[2]);
return fwrite(&a, sizeof(char), 1, fp);
}
case 2:
{
short b = static_cast<short>(params[2]);
return fwrite(&b, sizeof(short), 1, fp);
}
case 4:
{
int c = static_cast<int>(params[2]);
return fwrite(&c, sizeof(int), 1, fp);
}
}
return 0;
}
static cell AMX_NATIVE_CALL amx_fwrite_raw(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 0;
cell *addr = get_amxaddr(amx, params[2]);
return fwrite(addr, params[3], params[4], fp);
}
static cell AMX_NATIVE_CALL amx_fread_raw(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 0;
cell *addr = get_amxaddr(amx, params[2]);
size_t size = static_cast<cell>(params[3]);
size_t blocks = static_cast<cell>(params[4]);
return fread(addr, size, blocks, fp);
}
static cell AMX_NATIVE_CALL amx_fread(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 0;
cell *addr = get_amxaddr(amx, params[2]);
switch (params[3])
{
case 1: //char
{
char a;
size_t res = fread(&a, sizeof(char), 1, fp);
*addr = static_cast<cell>(a);
return res;
}
case 2: //short
{
short a;
size_t res = fread(&a, sizeof(short), 1, fp);
*addr = static_cast<cell>(a);
return res;
}
case 4: //int
default:
{
int a;
size_t res = fread(&a, sizeof(int), 1, fp);
*addr = static_cast<cell>(a);
return res;
}
}
return 0;
}
static cell AMX_NATIVE_CALL amx_fread_blocks(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 0;
cell *addr = get_amxaddr(amx, params[2]);
size_t blocks = params[3];
switch (params[4])
{
case 1: //char
{
char *a = new char[blocks];
char *ptr = a;
size_t res = fread(a, sizeof(char), blocks, fp);
while (blocks--)
*addr++ = static_cast<cell>(*ptr++);
delete [] a;
return res;
}
case 2: //short
{
short *a = new short[blocks];
short *ptr = a;
size_t res = fread(a, sizeof(short), blocks, fp);
while (blocks--)
*addr++ = static_cast<cell>(*ptr++);
delete [] a;
return res;
}
case 4: //int
default:
{
int *a = new int[blocks];
int *ptr = a;
size_t res = fread(a, sizeof(int), blocks, fp);
while (blocks--)
*addr++ = static_cast<cell>(*ptr++);
delete [] a;
return res;
}
}
return 0;
}
static cell AMX_NATIVE_CALL amx_fputs(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 0;
int len;
char *str = get_amxstring(amx, params[2], 0, len);
return fputs(str, fp);
}
static cell AMX_NATIVE_CALL amx_fgets(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 0;
static char buffer[4096];
buffer[0] = '\0';
fgets(buffer, sizeof(buffer)-1, fp);
return set_amxstring_utf8(amx, params[2], buffer, strlen(buffer), params[3] + 1); // + EOS
}
static cell AMX_NATIVE_CALL amx_fseek(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 0;
return fseek(fp, params[2], params[3]);
}
static cell AMX_NATIVE_CALL amx_ftell(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 0;
return ftell(fp);
}
static cell AMX_NATIVE_CALL amx_fprintf(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 0;
int len;
char *str = format_amxstring(amx, params, 2, len);
return fprintf(fp, "%s", str);
}
static cell AMX_NATIVE_CALL amx_feof(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 1;
return feof(fp);
}
static cell AMX_NATIVE_CALL amx_fclose(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 1;
fclose(fp);
return 1;
}
static cell AMX_NATIVE_CALL amx_filesize(AMX *amx, cell *params)
{
int len;
char *file = build_pathname("%s", format_amxstring(amx, params, 1, len));
long size;
AutoFilePtr fp(fopen(file, "rb"));
if (fp)
{
fseek(fp, 0, SEEK_END);
size = ftell(fp);
return size;
}
return -1;
}
static cell AMX_NATIVE_CALL amx_build_pathname(AMX *amx, cell *params)
{
int len;
char *szPath = get_amxstring(amx, params[1], 0, len);
return set_amxstring(amx, params[2], build_pathname("%s", szPath), params[3]);
}
static cell AMX_NATIVE_CALL amx_open_dir(AMX *amx, cell *params)
{
int len;
char *path = get_amxstring(amx, params[1], 0, len);
#if defined WIN32 || defined _WIN32
char *dirname = build_pathname("%s\\*", path);
WIN32_FIND_DATA fd;
HANDLE hFile = FindFirstFile(dirname, &fd);
if (hFile == INVALID_HANDLE_VALUE)
return 0;
set_amxstring(amx, params[2], fd.cFileName, params[3]);
return (DWORD)hFile;
#else
char *dirname = build_pathname("%s", path);
DIR *dp = opendir(dirname);
if (!dp)
return 0;
struct dirent *ep = readdir(dp);
if (!ep)
{
closedir(dp);
return 0;
}
set_amxstring(amx, params[2], ep->d_name, params[3]);
return (cell)dp;
#endif
}
static cell AMX_NATIVE_CALL amx_close_dir(AMX *amx, cell *params)
{
#if defined WIN32 || defined _WIN32
HANDLE hFile = (HANDLE)((DWORD)params[1]);
if (hFile == INVALID_HANDLE_VALUE || hFile == NULL)
return 0;
FindClose(hFile);
return 1;
#else
DIR *dp = (DIR *)params[1];
if (!dp)
return 0;
closedir(dp);
return 1;
#endif
}
static cell AMX_NATIVE_CALL amx_get_dir(AMX *amx, cell *params)
{
#if defined WIN32 || defined _WIN32
HANDLE hFile = (HANDLE)((DWORD)params[1]);
if (hFile == INVALID_HANDLE_VALUE || hFile == NULL)
return 0;
WIN32_FIND_DATA fd;
if (!FindNextFile(hFile, &fd))
return 0;
set_amxstring(amx, params[2], fd.cFileName, params[3]);
return 1;
#else
DIR *dp = (DIR *)params[1];
if (!dp)
return 0;
struct dirent *ep = readdir(dp);
if (!ep)
return 0;
set_amxstring(amx, params[2], ep->d_name, params[3]);
return 1;
#endif
}
//native fgetc( file );
static cell AMX_NATIVE_CALL amx_fgetc(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 0;
return fgetc(fp);
}
//native fputc( file, data );
static cell AMX_NATIVE_CALL amx_fputc(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 0;
return fputc(static_cast<int>(params[2]), fp);
}
//native ungetc( file, data );
static cell AMX_NATIVE_CALL amx_ungetc(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
if (!fp)
return 0;
return ungetc(static_cast<int>(params[2]), fp);
}
#if defined(__linux__) || defined(__APPLE__)
#define _rmdir rmdir
#endif
static cell AMX_NATIVE_CALL amx_rmdir(AMX *amx, cell *params)
{
int len;
char* sFile = build_pathname("%s", get_amxstring(amx, params[1], 0, len));
if (_rmdir(sFile) != 0)
return 0;
return 1;
}
static cell AMX_NATIVE_CALL amx_rename(AMX *amx, cell *params)
{
int len;
char f_old_r[260];
char f_new_r[260];
char *fold = get_amxstring(amx, params[1], 0, len);
char *fnew = get_amxstring(amx, params[2], 1, len);
if (params[0] / sizeof(cell) == 3 && params[3])
{
build_pathname_r(f_old_r, sizeof(f_old_r)-1, "%s", fold);
build_pathname_r(f_new_r, sizeof(f_new_r)-1, "%s", fnew);
} else {
UTIL_Format(f_old_r, sizeof(f_old_r)-1, "%s", fold);
UTIL_Format(f_new_r, sizeof(f_new_r)-1, "%s", fnew);
}
#if defined(__linux__) || defined(__APPLE__)
return (rename(f_old_r, f_new_r) == 0);
#elif defined WIN32
return MoveFileA(f_old_r, f_new_r);
#endif
}
static cell LoadFileForMe(AMX *amx, cell *params)
{
int len;
char *file = get_amxstring(amx, params[1], 0, len);
char path[256];
build_pathname_r(path, sizeof(path), "%s", file);
byte *addr = LOAD_FILE_FOR_ME(path, &len);
if (addr == NULL)
{
return -1;
}
cell *buffer = get_amxaddr(amx, params[2]);
cell maxlength = params[3];
cell *bytes_avail = get_amxaddr(amx, params[4]);
*bytes_avail = len;
cell count;
for (count = 0; count < len && count < maxlength; count++)
{
buffer[count] = addr[count];
}
FREE_FILE(addr);
return count;
}
static cell AMX_NATIVE_CALL amx_fflush(AMX *amx, cell *params)
{
FILE *fp = (FILE *)params[1];
return fflush(fp);
}
static cell AMX_NATIVE_CALL GetFileTime(AMX *amx, cell *params)
{
int len;
char *file = get_amxstring(amx, params[1], 0, len);
char path[256];
build_pathname_r(path, sizeof(path), "%s", file);
#if defined(WIN32)
struct _stat s;
if (_stat(path, &s) != 0)
#elif defined(__linux__) || defined(__APPLE__)
struct stat s;
if (stat(path, &s) != 0)
#endif
{
return -1;
}
time_t time_val = -1;
switch( params[2] )
{
case FileTime_LastAccess : time_val = s.st_atime; break;
case FileTime_Created : time_val = s.st_ctime; break;
case FileTime_LastChange : time_val = s.st_mtime; break;
}
return (cell)time_val;
}
AMX_NATIVE_INFO file_Natives[] =
{
{"delete_file", delete_file},
{"file_exists", file_exists},
{"file_size", file_size},
{"read_dir", read_dir},
{"read_file", read_file},
{"write_file", write_file},
//new, sane file natives
{"fopen", amx_fopen},
{"fclose", amx_fclose},
{"fread", amx_fread},
{"fread_blocks", amx_fread_blocks},
{"fread_raw", amx_fread_raw},
{"fwrite", amx_fwrite},
{"fwrite_blocks", amx_fwrite_blocks},
{"fwrite_raw", amx_fwrite_raw},
{"feof", amx_feof},
{"fprintf", amx_fprintf},
{"fgets", amx_fgets},
{"fseek", amx_fseek},
{"ftell", amx_ftell},
{"filesize", amx_filesize},
{"unlink", delete_file},
{"build_pathname", amx_build_pathname},
{"dir_exists", dir_exists},
{"open_dir", amx_open_dir},
{"close_dir", amx_close_dir},
{"next_file", amx_get_dir},
{"fgetc", amx_fgetc},
{"fputc", amx_fputc},
{"fungetc", amx_ungetc},
{"rmdir", amx_rmdir},
{"fputs", amx_fputs},
{"rename_file", amx_rename},
{"LoadFileForMe", LoadFileForMe},
{"fflush", amx_fflush},
{"GetFileTime", GetFileTime},
{NULL, NULL}
};