From 0eeb5942a170cee6a1991451f2bc8b87dd43a7ab Mon Sep 17 00:00:00 2001 From: Arkshine Date: Sun, 8 Mar 2015 23:46:33 +0100 Subject: [PATCH] VFS: Reflect changes in natives and documentation --- amxmodx/CLang.cpp | 2 + amxmodx/amxmodx.cpp | 14 - amxmodx/file.cpp | 1464 ++++++++++++++++++++++---------------- plugins/include/file.inc | 657 +++++++++++++---- 4 files changed, 1376 insertions(+), 761 deletions(-) diff --git a/amxmodx/CLang.cpp b/amxmodx/CLang.cpp index e01cf76c..31da9576 100755 --- a/amxmodx/CLang.cpp +++ b/amxmodx/CLang.cpp @@ -12,6 +12,8 @@ #include "CLang.h" #include "format.h" +CVector FileList; + #define LITIDX_NONE 0 #define LITIDX_BRACKET 1 #define LITIDX_DEFINITION 2 diff --git a/amxmodx/amxmodx.cpp b/amxmodx/amxmodx.cpp index c50595ca..321a991f 100755 --- a/amxmodx/amxmodx.cpp +++ b/amxmodx/amxmodx.cpp @@ -3784,19 +3784,6 @@ cell AMX_NATIVE_CALL require_module(AMX *amx, cell *params) return 1; } -static cell AMX_NATIVE_CALL amx_mkdir(AMX *amx, cell *params) -{ - int len = 0; - char *path = get_amxstring(amx, params[1], 0, len); - char *realpath = build_pathname("%s", path); - -#if defined(__linux__) || defined(__APPLE__) - return mkdir(realpath, 0700); -#else - return mkdir(realpath); -#endif -} - static cell AMX_NATIVE_CALL find_plugin_byfile(AMX *amx, cell *params) { typedef int (*STRCOMPARE)(const char*, const char*); @@ -4561,7 +4548,6 @@ AMX_NATIVE_INFO amxmodx_Natives[] = {"hash_string", amx_hash_string}, {"hash_file", amx_hash_file}, {"module_exists", module_exists}, - {"mkdir", amx_mkdir}, {"next_hudchannel", next_hudchannel}, {"num_to_word", num_to_word}, {"parse_loguser", parse_loguser}, diff --git a/amxmodx/file.cpp b/amxmodx/file.cpp index 39ba4ab4..1341851b 100755 --- a/amxmodx/file.cpp +++ b/amxmodx/file.cpp @@ -7,484 +7,503 @@ // 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 -#include -#include -#include -#endif - -// header file for unlink() -#if defined(__linux__) || defined(__APPLE__) -#include -#else -#define WINDOWS_LEAN_AND_MEAN -#include -#include -#endif - #include "amxmodx.h" +#include "CFileSystem.h" +#include "CLibrarySys.h" -CVector 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 */ -}; +using namespace ke; +// native read_dir(const dirname[], pos, output[], len, &outlen = 0); 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) + int length; + const char* path = get_amxstring(amx, params[1], 0, length); + const char* realpath = build_pathname("%s", path); + + AutoPtr dir(g_LibSys.OpenDirectory(realpath)); + + if (!dir) { - 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__ + cell offset = Max(0, params[2]); + + dir->NextEntry(&offset); + + if (!dir->IsValid()) + { + return 0; + } + + const char* entry = dir->GetEntryName(); + + size_t entrylen = set_amxstring_utf8(amx, params[3], entry, strlen(entry), params[4] + 1); + *get_amxaddr(amx, params[5]) = entrylen; + + return offset; } -static cell AMX_NATIVE_CALL read_file(AMX *amx, cell *params) /* 5 param */ +// native read_file(const file[], line, text[], len, &txtlen = 0); +static cell AMX_NATIVE_CALL read_file(AMX *amx, cell *params) { - int iLen; - char* szFile = get_amxstring(amx, params[1], 0, iLen); - FILE *fp; - - if ((fp =fopen(build_pathname("%s", szFile), "r")) == NULL) + int length; + const char* path = get_amxstring(amx, params[1], 0, length); + const char* realpath = build_pathname("%s", path); + + AutoPtr fp(SystemFile::Open(realpath, "r")); + + if (!fp) { - LogError(amx, AMX_ERR_NATIVE, "Couldn't read file \"%s\"", szFile); + LogError(amx, AMX_ERR_NATIVE, "Couldn't read file \"%s\"", path); return 0; } - char buffor[1024]; - int i = 0, iLine = params[2]; - - while ((i <= iLine) && fgets(buffor, 1023, fp)) - i++; - - fclose(fp); + static char buffer[2048]; - if (i > iLine) + size_t currentLine = 0; + size_t targetLine = Max(0, params[2]); + + while (currentLine <= targetLine && fp->ReadLine(buffer, sizeof(buffer) - 1)) { - 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; + ++currentLine; } - + + if (currentLine > targetLine) + { + length = strlen(buffer); + + if (buffer[length - 1] == '\n') + buffer[--length] = '\0'; + + if (buffer[length - 1] == '\r') + buffer[--length] = '\0'; + + length = set_amxstring_utf8(amx, params[3], buffer, length, params[4] + 1); // + EOS + *get_amxaddr(amx, params[5]) = length; + + return currentLine; + } + return 0; } -static cell AMX_NATIVE_CALL write_file(AMX *amx, cell *params) /* 3 param */ +// native write_file(const file[], const text[], line = -1); +static cell AMX_NATIVE_CALL write_file(AMX *amx, cell *params) { - 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]; + int length; + const char* path = get_amxstring(amx, params[1], 0, length); + const char* text = get_amxstring(amx, params[2], 1, length); + int targetLine = params[3]; - // apending to the end - if (iLine < 0) + const char* realpath = build_pathname("%s", path); + + AutoPtrfp; + + if (targetLine < 0) { - if ((pFile = fopen(sFile, "a")) == NULL) + if (!(fp = SystemFile::Open(realpath, "a"))) { - LogError(amx, AMX_ERR_NATIVE, "Couldn't write file \"%s\"", sFile); + LogError(amx, AMX_ERR_NATIVE, "Couldn't write file \"%s\"", realpath); return 0; } - - fputs(sText, pFile); - fputc('\n', pFile); - fclose(pFile); - + + fp->Write(text, length); + fp->Write("\n", 1); + return 1; } - // creating a new file with a line in a middle - if ((pFile = fopen(sFile, "r")) == NULL) + if (!(fp = SystemFile::Open(realpath, "r"))) { - if ((pFile = fopen(sFile, "w")) == NULL) + if (!(fp = SystemFile::Open(realpath, "w"))) { - LogError(amx, AMX_ERR_NATIVE, "Couldn't write file \"%s\"", sFile); + LogError(amx, AMX_ERR_NATIVE, "Couldn't write file \"%s\"", realpath); return 0; } - - for (i = 0; i < iLine; ++i) - fputc('\n', pFile); - fputs(sText, pFile); - fputc('\n', pFile); - fclose(pFile); - + for (int i = 0; i < targetLine; ++i) + { + fp->Write("\n", 1); + } + + fp->Write(text, length); + fp->Write("\n", 1); + return 1; } - // adding a new line in a middle of already existing file - FILE* pTemp; - char buffor[2048]; + SystemFile fptemp(tmpfile()); - if ((pTemp = tmpfile()) == NULL) + if (!fptemp.handle()) { LogError(amx, AMX_ERR_NATIVE, "Couldn't create temp file"); return 0; } - for (i = 0; ; ++i) + static char buffer[2048]; + + for (int i = 0;; ++i) { - if (i == iLine) + if (i == targetLine) { - fgets(buffor, 2047, pFile); - fputs(sText, pTemp); - fputc('\n', pTemp); + fp->ReadLine(buffer, sizeof(buffer) - 1); + + fptemp.Write(text, length); + fptemp.Write("\n", 1); } - else if (fgets(buffor, 2047, pFile)) + else if (fp->ReadLine(buffer, sizeof(buffer) - 1)) { - fputs(buffor, pTemp); + fptemp.Write(buffer, strlen(buffer)); } - else if (i < iLine) + else if (i < targetLine) { - fputc('\n', pTemp); + fptemp.Write("\n", 1); } 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) + } + + rewind(fptemp.handle()); + + if (!(fp = AutoPtr(SystemFile::Open(realpath, "w")))) + { + LogError(amx, AMX_ERR_NATIVE, "Couldn't write file \"%s\"", realpath); + return 0; + } + + while (fptemp.ReadLine(buffer, sizeof(buffer) - 1)) + { + fp->Write(buffer, strlen(buffer)); + } + + return 1; +} + +// native delete_file(const file[], bool:use_valve_fs = false, const valve_path_id[] = "GAMECONFIG"); +static cell AMX_NATIVE_CALL delete_file(AMX *amx, cell *params) +{ + int length; + const char* file = get_amxstring(amx, params[1], 0, length); + + if (*params / sizeof(cell) >= 2 && params[2] > 0) + { + const char* pathID = get_amxstring(amx, params[3], 1, length); + + if (!strcmp(pathID, "ALL")) { - int a = 0,lines = 0; - - while (a != EOF) + pathID = nullptr; + } + + return ValveFile::Delete(file, pathID); + } + + return SystemFile::Delete(build_pathname("%s", file)); +} + +// native file_exists(const file[], bool:use_valve_fs = false); +static cell AMX_NATIVE_CALL file_exists(AMX *amx, cell *params) +{ + int length; + const char* file = get_amxstring(amx, params[1], 0, length); + + if (*params / sizeof(cell) >= 2 && params[2] > 0) + { + return g_FileSystem->FileExists(file); + } + + return g_LibSys.IsPathFile(build_pathname("%s", file)); +} + +// native dir_exists(const dir[], bool:use_valve_fs = false); +static cell AMX_NATIVE_CALL dir_exists(AMX *amx, cell *params) +{ + int length; + const char *path = get_amxstring(amx, params[1], 0, length); + + if (*params / sizeof(cell) >= 2 && params[2] > 0) + { + return g_FileSystem->IsDirectory(path); + } + + return g_LibSys.IsPathDirectory(build_pathname("%s", path)); +} + +#define FSOPT_BYTES_COUNT 0 +#define FSOPT_LINES_COUNT 1 +#define FSOPT_END_WITH_LF 2 + +// native file_size(const file[], flag = 0, bool:use_valve_fs = false, const valve_path_id[] = "GAME"); +static cell AMX_NATIVE_CALL file_size(AMX *amx, cell *params) +{ + int length; + const char* path = get_amxstring(amx, params[1], 0, length); + int flag = params[2]; + + AutoPtr fp; + + size_t numParams = *params / sizeof(cell); + + if (numParams >= 3 && params[3] > 0) + { + const char* pathID = get_amxstring(amx, params[4], 1, length); + + if (!strcmp(pathID, "ALL")) + { + pathID = nullptr; + } + + fp = ValveFile::Open(path, "r", pathID); + } + else + { + fp = SystemFile::Open(build_pathname("%s", path), "r"); + } + + if (!fp) + { + return -1; + } + + switch (flag) + { + default: + case FSOPT_BYTES_COUNT: + { + fp->Seek(0, SEEK_END); + + return fp->Tell(); + } + case FSOPT_LINES_COUNT: + { + int8_t ch = 0; + size_t lines = 0; + + while (!fp->EndOfFile() && !fp->HasError()) { ++lines; - while ((a = fgetc(fp)) != '\n' && a != EOF); + while (fp->Read(&ch, 1) == 1 && ch != '\n' && ch != 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) + case FSOPT_END_WITH_LF: { - fseek(fp, -1, SEEK_END); - - if (fgetc(fp) == '\n') - return 1; - - return 0; + int8_t ch = 0; + + fp->Seek(-1, SEEK_END); + fp->Read(&ch, 1); + + return ch == '\n'; } } - + return -1; } +// native fopen(const filename[], const mode[], bool:use_valve_fs = false, const valve_path_id[] = "GAME"); 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); + int length; + const char* file = get_amxstring(amx, params[1], 0, length); + const char* flags = get_amxstring(amx, params[2], 1, length); - FILE *fp = fopen(file, flags); + FileObject* fp = nullptr; - return (cell)fp; + if (*params / sizeof(cell) >= 3 && params[3] > 0) + { + const char* pathID = get_amxstring(amx, params[4], 2, length); + + if (!strcmp(pathID, "ALL")) + { + pathID = nullptr; + } + + fp = ValveFile::Open(file, flags, pathID); + } + else + { + fp = SystemFile::Open(build_pathname("%s", file), flags); + } + + if (!fp) + { + return 0; + } + + return reinterpret_cast(fp); } +#define BLOCK_INT 4 +#define BLOCK_SHORT 2 +#define BLOCK_CHAR 1 + +// native fwrite_blocks(file, const data[], blocks, mode); static cell AMX_NATIVE_CALL amx_fwrite_blocks(AMX *amx, cell *params) { - FILE *fp = (FILE *)params[1]; + FileObject* fp = reinterpret_cast(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: + return 0; + } + + cell* data = get_amxaddr(amx, params[2]); + cell blocks = params[3]; + cell size = params[4]; + + size_t read = 0; + + switch (size) + { + case BLOCK_CHAR: { - char *a = new char[blocks]; - char *ptr = a; - while (btmp--) - *ptr++ = static_cast(*addr++); - size_t res = fwrite(a, sizeof(char), blocks, fp); - delete [] a; - return res; + for (cell i = 0; i < blocks; ++i) + { + char value = data[i]; + + if (fp->Write(&value, sizeof(value)) != sizeof(value)) + { + break; + } + + read += sizeof(value); + } + break; } - case 2: + case BLOCK_SHORT: { - short *a = new short[blocks]; - short *ptr = a; - while (btmp--) - *ptr++ = static_cast(*addr++); - size_t res = fwrite(a, sizeof(short), blocks, fp); - delete [] a; - return res; + for (cell i = 0; i < blocks; ++i) + { + short value = data[i]; + + if (fp->Write(&value, sizeof(value)) != sizeof(value)) + { + break; + } + + read += sizeof(value); + } + break; } - case 4: + default: { - int *a = new int[blocks]; - int *ptr = a; - while (btmp--) - *ptr++ = static_cast(*addr++); - size_t res = fwrite(a, sizeof(int), blocks, fp); - delete [] a; - return res; + if (size != BLOCK_INT) + { + size = BLOCK_INT; + } + } + case BLOCK_INT: + { + read = fp->Write(data, sizeof(cell) * blocks); } } - return 0; + return read / size; } +// native fwrite(file, data, mode); static cell AMX_NATIVE_CALL amx_fwrite(AMX *amx, cell *params) { - FILE *fp = (FILE *)params[1]; + FileObject* fp = reinterpret_cast(params[1]); if (!fp) - return 0; - - size_t mode = params[3]; - switch (mode) { - case 1: + return 0; + } + + cell data = params[2]; + size_t size = params[3]; + + switch (size) + { + case BLOCK_CHAR: { - char a = static_cast(params[2]); - return fwrite(&a, sizeof(char), 1, fp); + char value = static_cast(data); + return fp->Write(&value, sizeof(value)); } - case 2: + case BLOCK_SHORT: { - short b = static_cast(params[2]); - return fwrite(&b, sizeof(short), 1, fp); + short value = static_cast(data); + return fp->Write(&value, sizeof(value)); } - case 4: + default: + case BLOCK_INT: { - int c = static_cast(params[2]); - return fwrite(&c, sizeof(int), 1, fp); + int value = static_cast(data); + return fp->Write(&value, sizeof(value)); } } return 0; } +// native fwrite_raw(file, const stream[], blocksize, mode); static cell AMX_NATIVE_CALL amx_fwrite_raw(AMX *amx, cell *params) { - FILE *fp = (FILE *)params[1]; + FileObject* fp = reinterpret_cast(params[1]); if (!fp) + { return 0; + } - cell *addr = get_amxaddr(amx, params[2]); - return fwrite(addr, params[3], params[4], fp); + cell* data = get_amxaddr(amx, params[2]); + + return fp->Write(&data, params[3] * params[4]); } +// native fread_raw(file, stream[], blocksize, blocks); static cell AMX_NATIVE_CALL amx_fread_raw(AMX *amx, cell *params) { - FILE *fp = (FILE *)params[1]; + FileObject* fp = reinterpret_cast(params[1]); if (!fp) + { return 0; + } - cell *addr = get_amxaddr(amx, params[2]); - size_t size = static_cast(params[3]); - size_t blocks = static_cast(params[4]); - - return fread(addr, size, blocks, fp); + cell* data = get_amxaddr(amx, params[2]); + + return fp->Read(data, params[3] * params[4]); } +// native fread(file, &data, mode); static cell AMX_NATIVE_CALL amx_fread(AMX *amx, cell *params) { - FILE *fp = (FILE *)params[1]; + FileObject* fp = reinterpret_cast(params[1]); if (!fp) + { return 0; + } + + cell *data = get_amxaddr(amx, params[2]); - cell *addr = get_amxaddr(amx, params[2]); switch (params[3]) { - case 1: //char + case BLOCK_CHAR: { - char a; - size_t res = fread(&a, sizeof(char), 1, fp); - *addr = static_cast(a); + char value; + size_t res = fp->Read(&value, sizeof(value)); + *data = static_cast(value); return res; } - case 2: //short + case BLOCK_SHORT: { - short a; - size_t res = fread(&a, sizeof(short), 1, fp); - *addr = static_cast(a); + short value; + size_t res = fp->Read(&value, sizeof(value)); + *data = static_cast(value); return res; } - case 4: //int - default: + case BLOCK_INT: + default: { - int a; - size_t res = fread(&a, sizeof(int), 1, fp); - *addr = static_cast(a); + int value; + size_t res = fp->Read(&value, sizeof(value)); + *data = static_cast(value); return res; } } @@ -492,333 +511,545 @@ static cell AMX_NATIVE_CALL amx_fread(AMX *amx, cell *params) return 0; } +// native fread_blocks(file, data[], blocks, mode); static cell AMX_NATIVE_CALL amx_fread_blocks(AMX *amx, cell *params) { - FILE *fp = (FILE *)params[1]; + FileObject* fp = reinterpret_cast(params[1]); if (!fp) - return 0; - - cell *addr = get_amxaddr(amx, params[2]); - size_t blocks = params[3]; - switch (params[4]) { - case 1: //char + return 0; + } + + cell *data = get_amxaddr(amx, params[2]); + cell blocks = params[3]; + cell size = params[4]; + + size_t read = 0; + + switch (size) + { + case BLOCK_CHAR: { - char *a = new char[blocks]; - char *ptr = a; - size_t res = fread(a, sizeof(char), blocks, fp); - while (blocks--) - *addr++ = static_cast(*ptr++); - delete [] a; - return res; + for (cell i = 0; i < blocks; ++i) + { + char value; + + if (fp->Read(&value, sizeof(value)) != sizeof(value)) + { + break; + } + + read += sizeof(value); + *data++ = value; + } + break; } - case 2: //short + case BLOCK_SHORT: { - short *a = new short[blocks]; - short *ptr = a; - size_t res = fread(a, sizeof(short), blocks, fp); - while (blocks--) - *addr++ = static_cast(*ptr++); - delete [] a; - return res; + for (cell i = 0; i < blocks; ++i) + { + short value; + + if (fp->Read(&value, sizeof(value)) != sizeof(value)) + { + break; + } + + read += sizeof(value); + *data++ = value; + } + break; } - case 4: //int - default: + default: { - int *a = new int[blocks]; - int *ptr = a; - size_t res = fread(a, sizeof(int), blocks, fp); - while (blocks--) - *addr++ = static_cast(*ptr++); - delete [] a; - return res; + if (size != BLOCK_INT) + { + size = BLOCK_INT; + } } + case BLOCK_INT: + { + read = fp->Read(data, sizeof(cell) * blocks); + break; + } + } + + return read / size; +} + +// native fputs(file, const text[], bool:null_term = false); +static cell AMX_NATIVE_CALL amx_fputs(AMX *amx, cell *params) +{ + FileObject* fp = reinterpret_cast(params[1]); + + if (!fp) + { + return 0; + } + + int length; + char *string = get_amxstring(amx, params[2], 0, length); + + if (*params / sizeof(cell) >= 3 && params[3] > 0) + { + ++length; + } + + if (fp->Write(string, length) != length) + { + return -1; } 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); -} - +// native fgets(file, buffer[], maxlength); static cell AMX_NATIVE_CALL amx_fgets(AMX *amx, cell *params) { - FILE *fp = (FILE *)params[1]; + FileObject* fp = reinterpret_cast(params[1]); if (!fp) + { return 0; + } static char buffer[4096]; buffer[0] = '\0'; - fgets(buffer, sizeof(buffer)-1, fp); + + if (!fp->ReadLine(buffer, sizeof(buffer) - 1)) + { + return 0; + } + return set_amxstring_utf8(amx, params[2], buffer, strlen(buffer), params[3] + 1); // + EOS } +// native fseek(file, position, start); static cell AMX_NATIVE_CALL amx_fseek(AMX *amx, cell *params) { - FILE *fp = (FILE *)params[1]; + FileObject* fp = reinterpret_cast(params[1]); if (!fp) + { return 0; + } - return fseek(fp, params[2], params[3]); + return !fp->Seek(params[2], params[3]); } +// native ftell(file); static cell AMX_NATIVE_CALL amx_ftell(AMX *amx, cell *params) { - FILE *fp = (FILE *)params[1]; + FileObject* fp = reinterpret_cast(params[1]); if (!fp) + { return 0; + } - return ftell(fp); + return fp->Tell(); } +// native fprintf(file, const fmt[], any:...); static cell AMX_NATIVE_CALL amx_fprintf(AMX *amx, cell *params) { - FILE *fp = (FILE *)params[1]; + FileObject* fp = reinterpret_cast(params[1]); if (!fp) + { return 0; + } - int len; - char *str = format_amxstring(amx, params, 2, len); - return fprintf(fp, "%s", str); + int length; + const char* string = format_amxstring(amx, params, 2, length); + + if (ValveFile *vfile = fp->AsValveFile()) + { + return g_FileSystem->FPrintf(vfile->handle(), const_cast("%s"), string); + } + else if (SystemFile *sysfile = fp->AsSystemFile()) + { + return fprintf(sysfile->handle(), "%s", string); + } + else + { + assert(false); + } + + return 0; } +// native feof(file); static cell AMX_NATIVE_CALL amx_feof(AMX *amx, cell *params) { - FILE *fp = (FILE *)params[1]; + FileObject* fp = reinterpret_cast(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); + if (fp) + { + return fp->EndOfFile(); + } return 1; } -static cell AMX_NATIVE_CALL amx_filesize(AMX *amx, cell *params) +// native fclose(file); +static cell AMX_NATIVE_CALL amx_fclose(AMX *amx, cell *params) { - int len; - char *file = build_pathname("%s", format_amxstring(amx, params, 1, len)); - long size; - - AutoFilePtr fp(fopen(file, "rb")); - + FileObject* fp = reinterpret_cast(params[1]); + if (fp) { - fseek(fp, 0, SEEK_END); - size = ftell(fp); - - return size; + fp->Close(); } - + + return 1; +} + +// native filesize(const filename[], any:...); +static cell AMX_NATIVE_CALL amx_filesize(AMX *amx, cell *params) +{ + int length; + const char *realpath = build_pathname("%s", format_amxstring(amx, params, 1, length)); + + AutoPtr fp(SystemFile::Open(realpath, "rb")); + + if (fp) + { + fp->Seek(0, SEEK_END); + + return fp->Tell(); + } + return -1; } +// Undocumented. 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]); + int length; + const char* path = get_amxstring(amx, params[1], 0, length); + + return set_amxstring(amx, params[2], build_pathname("%s", path), params[3]); } +enum FileType +{ + FileType_Unknown, /* Unknown file type (device/socket) */ + FileType_Directory, /* File is a directory */ + FileType_File, /* File is a file */ +}; + +struct DirectoryHandle +{ + DirectoryHandle(void* handle_, bool valvefs_) : handle(handle_), valvefs(valvefs_) {} + + void* handle; + bool valvefs; +}; + +// native open_dir(dir[], firstfile[], length, &FileType:type = FileType_Unknown, bool:use_valve_fs=false, const valve_path_id[] = "GAME"); static cell AMX_NATIVE_CALL amx_open_dir(AMX *amx, cell *params) { - int len; - char *path = get_amxstring(amx, params[1], 0, len); + int length; + const char* path = get_amxstring(amx, params[1], 0, length); -#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) + if (!*path) { - closedir(dp); return 0; } - - set_amxstring(amx, params[2], ep->d_name, params[3]); - - return (cell)dp; -#endif + + size_t numParams = *params / sizeof(cell); + + if (*params / sizeof(cell) >= 4 && params[5] > 0) + { + const char* wildcardedPath = g_LibSys.PathFormat("%s%s*", path, (path[length - 1] != '/' && path[length - 1] != '\\') ? "/" : ""); + const char* pathID = get_amxstring(amx, params[6], 1, length); + + if (!strcmp(pathID, "ALL")) + { + pathID = nullptr; + } + + static FileFindHandle_t handle; + const char* pFirst = g_FileSystem->FindFirst(wildcardedPath, &handle, pathID); + + if (!pFirst) + { + return 0; + } + + set_amxstring_utf8(amx, params[2], pFirst, strlen(pFirst), params[3] + 1); + + *get_amxaddr(amx, params[4]) = g_FileSystem->FindIsDirectory(handle) ? FileType_Directory : FileType_File; + + return reinterpret_cast(new DirectoryHandle(reinterpret_cast(&handle), true)); + } + + CDirectory* dir = g_LibSys.OpenDirectory(build_pathname("%s", path)); + + if (!dir) + { + return 0; + } + + if (numParams >= 4) + { + *get_amxaddr(amx, params[4]) = dir->IsEntryDirectory() ? FileType_Directory : FileType_File; + } + + const char* entry = dir->GetEntryName(); + set_amxstring_utf8(amx, params[2], entry, strlen(entry), params[3] + 1); + + return reinterpret_cast(new DirectoryHandle(reinterpret_cast(dir), false)); } +// native close_dir(dirh); 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 -} + AutoPtr p(reinterpret_cast(params[1])); -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(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(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]) + if (!p) { - 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); + return 0; } -#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); + if (p->valvefs) + { + FileFindHandle_t* handle = reinterpret_cast(p->handle); + g_FileSystem->FindClose(*handle); + } + else + { + CDirectory* handle = reinterpret_cast(p->handle); + g_LibSys.CloseDirectory(handle); + } + + return 1; +} + +// native next_file(dirh, buffer[], length, &FileType:type = FileType_Unknown); +static cell AMX_NATIVE_CALL amx_get_dir(AMX *amx, cell *params) +{ + DirectoryHandle* p = reinterpret_cast(params[1]); + + if (!p) + { + return 0; + } + + size_t numParams = *params / sizeof(cell); + + if (p->valvefs) + { + FileFindHandle_t* handle = reinterpret_cast(p->handle); + + if (!handle) + { + return 0; + } + + const char* entry = g_FileSystem->FindNext(*handle); + + if (!entry) + { + return 0; + } + + if (numParams >= 4) + { + *get_amxaddr(amx, params[4]) = g_FileSystem->FindIsDirectory(*handle) ? FileType_Directory : FileType_File; + } + + set_amxstring_utf8(amx, params[2], entry, strlen(entry), params[3] + 1); + } + else + { + CDirectory* handle = reinterpret_cast(p->handle); + + if (!handle) + { + return 0; + } + + handle->NextEntry(); + + if (!handle->MoreFiles()) + { + return 0; + } + + if (numParams >= 4) + { + *get_amxaddr(amx, params[4]) = handle->IsEntryDirectory() ? FileType_Directory : FileType_File; + } + + const char* entry = handle->GetEntryName(); + + set_amxstring_utf8(amx, params[2], entry, strlen(entry), params[3] + 1); + } + + return 1; +} + +//native fgetc(file); +static cell AMX_NATIVE_CALL amx_fgetc(AMX *amx, cell *params) +{ + FileObject* fp = reinterpret_cast(params[1]); + + if (!fp) + { + return 0; + } + + uint8_t val; + + if (fp->Read(&val, sizeof(val)) != sizeof(val)) + { + return -1; + } + + return static_cast(val); +} + +//native fputc(file, data); +static cell AMX_NATIVE_CALL amx_fputc(AMX *amx, cell *params) +{ + FileObject* fp = reinterpret_cast(params[1]); + + if (!fp) + { + return 0; + } + + uint8_t val = static_cast(params[2]); + + if (fp->Write(&val, sizeof(val)) != sizeof(val)) + { + return -1; + } + + return val; +} + +//native ungetc(file, data); +static cell AMX_NATIVE_CALL amx_ungetc(AMX *amx, cell *params) +{ + FileObject* fp = reinterpret_cast(params[1]); + + if (!fp) + { + return 0; + } + + SystemFile* sysfile = fp->AsSystemFile(); + + if (!sysfile) + { + LogError(amx, AMX_ERR_NATIVE, "Can not ungetc to file in the Valve file system"); + return 0; + } + + return ungetc(static_cast(params[2]), sysfile->handle()); +} + +// native rmdir(const path[]); +static cell AMX_NATIVE_CALL amx_rmdir(AMX *amx, cell *params) +{ + int length; + const char* realpath = build_pathname("%s", get_amxstring(amx, params[1], 0, length)); + + return rmdir(realpath) == 0; +} + +// native mkdir(const dirname[], mode, bool:use_valve_fs = false, const valve_path_id[] = "GAMECONFIG"); +static cell AMX_NATIVE_CALL amx_mkdir(AMX *amx, cell *params) +{ + int length; + char *path = get_amxstring(amx, params[1], 0, length); + + size_t numParams = *params / sizeof(cell); + + if (numParams >= 3 && params[3] > 0) + { + const char* pathID = get_amxstring(amx, params[4], 1, length); + + if (g_FileSystem->IsDirectory(path)) + { + return -1; + } + + if (!strcmp(pathID, "ALL")) + { + pathID = nullptr; + } + + g_FileSystem->CreateDirHierarchy(path, pathID); + + if (g_FileSystem->IsDirectory(path)) + { + return 0; + } + } + + const char* realpath = build_pathname("%s", path); + +#if defined PLATFORM_WINDOWS + + return mkdir(realpath); +#else + if (numParams >= 2) + { + return mkdir(realpath, params[2]); + } + + return mkdir(realpath, 0755); #endif } +// native rename_file(const oldname[], const newname[], relative = 0); +static cell AMX_NATIVE_CALL rename_file(AMX *amx, cell *params) +{ + int length; + char file_old_r[PLATFORM_MAX_PATH]; + char file_new_r[PLATFORM_MAX_PATH]; + + const char* file_old = get_amxstring(amx, params[1], 0, length); + const char* file_new = get_amxstring(amx, params[2], 1, length); + + if (params[0] / sizeof(cell) >= 3 && params[3] > 0) + { + build_pathname_r(file_old_r, sizeof(file_old_r) - 1, "%s", file_old); + build_pathname_r(file_new_r, sizeof(file_new_r) - 1, "%s", file_new); + } + else + { + g_LibSys.PathFormat(file_old_r, sizeof(file_old_r) - 1, "%s", file_old); + g_LibSys.PathFormat(file_new_r, sizeof(file_new_r) - 1, "%s", file_new); + } + +#if defined PLATFORM_POSIX + + return (rename(file_old_r, file_new_r) == 0); + +#elif defined PLATFORM_WINDOWS + + return MoveFileA(file_old_r, file_new_r); +#endif +} + +// native LoadFileForMe(const file[], buffer[], maxlength, &length = 0); static cell LoadFileForMe(AMX *amx, cell *params) { - int len; - char *file = get_amxstring(amx, params[1], 0, len); - char path[256]; + int length; + char *file = get_amxstring(amx, params[1], 0, length); + char path[PLATFORM_MAX_PATH]; build_pathname_r(path, sizeof(path), "%s", file); - byte *addr = LOAD_FILE_FOR_ME(path, &len); - if (addr == NULL) + byte* addr = LOAD_FILE_FOR_ME(path, &length); + + if (!addr) { return -1; } @@ -827,10 +1058,11 @@ static cell LoadFileForMe(AMX *amx, cell *params) cell maxlength = params[3]; cell *bytes_avail = get_amxaddr(amx, params[4]); - *bytes_avail = len; + *bytes_avail = length; cell count; - for (count = 0; count < len && count < maxlength; count++) + + for (count = 0; count < length && count < maxlength; count++) { buffer[count] = addr[count]; } @@ -840,81 +1072,81 @@ static cell LoadFileForMe(AMX *amx, cell *params) return count; } +// native fflush(file); static cell AMX_NATIVE_CALL amx_fflush(AMX *amx, cell *params) { - FILE *fp = (FILE *)params[1]; + FileObject* fp = reinterpret_cast(params[1]); - return fflush(fp); + if (fp) + { + return fp->Flush(); + } + + return -1; } +// native GetFileTime(const file[], FileTimeType:tmode); static cell AMX_NATIVE_CALL GetFileTime(AMX *amx, cell *params) { - int len; - char *file = get_amxstring(amx, params[1], 0, len); + int length; + const char* file = get_amxstring(amx, params[1], 0, length); - char path[256]; - build_pathname_r(path, sizeof(path), "%s", file); + time_t time_val; -#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 + if (!g_LibSys.FileTime(build_pathname("%s", file), static_cast(params[2]), &time_val)) { 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; + return static_cast(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 + {"rename_file", rename_file}, + {"delete_file", delete_file}, + {"unlink", delete_file}, + + {"file_exists", file_exists}, + {"file_size", file_size}, + {"filesize", amx_filesize}, + {"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}, + {"fgetc", amx_fgetc}, + {"fputc", amx_fputc}, + {"fungetc", amx_ungetc}, + {"fputs", amx_fputs}, + {"fflush", amx_fflush}, + {"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}, + {"mkdir", amx_mkdir}, + {"LoadFileForMe", LoadFileForMe}, - {"fflush", amx_fflush}, {"GetFileTime", GetFileTime}, {NULL, NULL} }; + diff --git a/plugins/include/file.inc b/plugins/include/file.inc index cd509a68..c617538a 100755 --- a/plugins/include/file.inc +++ b/plugins/include/file.inc @@ -12,140 +12,23 @@ // #if defined _file_included - #endinput + #endinput #endif #define _file_included -/* Reads content from directory. -* Returns index of next element or 0 when end of dir. is reached. */ -native read_dir(const dirname[],pos,output[],len,&outlen); - -/* Reads line from file. Returns index of next line or 0 when end of file is reached. */ -native read_file(const file[],line,text[],len,&txtlen); - -/* Writes text to file. Function returns 0 on failure. -* When line is set to -1, the text is added at the end of file. */ -native write_file(const file[],const text[],line = -1); - -/* Deletes file. Function returns 1 on success, 0 on failure. */ -native delete_file(const file[]); - -/* Checks for file. If file exists function returns 1, in other case 0. */ -native file_exists(const file[]); - -/* renames a file. returns 0 on failure, 1 on success. - * if relative true, rename_file will act like other natives which - * use the moddir as a base directory. otherwise, the current directory is - * undefined (but assumed to be hlds). +/** + * File inode types for use with open_dir() and next_file(). */ -native rename_file(const oldname[], const newname[], relative=0); - -/* Checks if a directory exists */ -native dir_exists(const dir[]); - -/* Returns a file size in bytes if flag is set to 0. -* When flag is set to 1 returns number of lines in the file, -* and when flags is 2, function returns 1 if the file ends -* with line feed. If file doesn't exist returns -1. */ -native file_size(const file[], flag=0); - -#define SEEK_SET 0 -#define SEEK_CUR 1 -#define SEEK_END 2 - -//Open a file, returns a handle or 0 on failure -native fopen(const filename[],const mode[]); - -//Closes a file handle -native fclose(file); - -#define BLOCK_INT 4 -#define BLOCK_SHORT 2 -#define BLOCK_CHAR 1 -#define BLOCK_BYTE 1 - -//The following functions work as such: -// RAW - means the array you pass is a raw bytestream, for experts only -// BLOCK - means you are passing in an array where each element will be written -// NORMAL - means you are writing only one element -// RAW and BLOCK return the number of blocks acted upon successfully -// NORMAL returns 1 on success - -native fread(file, &data, mode); -native fread_blocks(file, data[], blocks, mode); -native fread_raw(file, stream[], blocksize, blocks); -native fwrite(file, data, mode); -native fwrite_blocks(file, const data[], blocks, mode); -native fwrite_raw(file, const stream[], blocksize, mode); - -//Returns 1 if the file is ended, 0 otherwise -native feof(file); - -//Reads a line from a text file -- includes newline! -native fgets(file, buffer[], maxlength); - -//Writes a line to a text file. Returns # of characters written. -native fputs(file, const text[]); - -//Writes a line to the file -native fprintf(file, const fmt[], any:...); - -//Sets the current position in a file (see SEEK_ values above) -native fseek(file, position, start); - -//Returns the current position in a file -native ftell(file); - -//These are straight from the C standard. -native fgetc(file); -native fputc(file, data); -native fungetc(file, data); - -//Return the size of a file -native filesize(const filename[], any:...); - -//Attempts to remove a directory. -//Note that you cannot remove a directory that has files on most -// operating systems. -native rmdir(const path[]); - -/* Returns 0 on success, like the POSIX specification */ -native mkdir(const dirname[]); - - -//Delete a file (delete_file macro) -native unlink(const filename[]); - -//Returns a handle to a directory -native open_dir(dir[], firstfile[], length); -native next_file(dirh, buffer[], length); -native close_dir(dirh); +enum FileType +{ + FileType_Unknown, /* Unknown file type (device/socket) */ + FileType_Directory, /* File is a directory */ + FileType_File, /* File is a file */ +}; /** - * Loads a file using the LoadFileForMe engine function. - * - * The data is truncated if there is not enough space. No null-terminator - * is applied; the data is the raw contents of the file. - * - * @param file File to load (may be a file from the GCF). - * @param buffer Buffer to store file contents. - * @param maxlength Maximum size of the file buffer. - * @param length Variable to store the file length. This may return - * a number larger than the buffer size. - * @return -1 if the file could not be loaded. Otherwise, - * the number of cells actually written to the buffer - * are returned. + * File time modes for use with GetFileTime(). */ -native LoadFileForMe(const file[], buffer[], maxlength, &length=0); - -/** - * Flushes a buffered output stream. - * - * @param file File handle, or 0 for all open streams. - * @return 0 on success, -1 on failure. - */ -native fflush(file); - enum FileTimeType { FileTime_LastAccess, /* Last access (not available on FAT) */ @@ -153,11 +36,523 @@ enum FileTimeType FileTime_LastChange, /* Last modification */ }; +/** + * File position modes for use with fseek(). + */ +#define SEEK_SET 0 /* Seek from start */ +#define SEEK_CUR 1 /* Seek from current position */ +#define SEEK_END 2 /* Seek from end position */ + +/** + * Options for use with file_size() flag parameter. + */ +#define FSOPT_BYTES_COUNT 0 /* Returns the file size in number of bytes */ +#define FSOPT_LINES_COUNT 1 /* Returns how many lines there are in this file */ +#define FSOPT_END_WITH_LF 2 /* Returns whether the last line is '\n' */ + +/** + * Data block modes for use with fread*() and fwrite*(). + */ +#define BLOCK_INT 4 +#define BLOCK_SHORT 2 +#define BLOCK_CHAR 1 +#define BLOCK_BYTE 1 + +/** + * File permissions flags for use with mkdir(). + */ +#define FPERM_U_READ 0x0100 /* User can read. */ +#define FPERM_U_WRITE 0x0080 /* User can write. */ +#define FPERM_U_EXEC 0x0040 /* User can exec. */ +#define FPERM_U_RWX FPERM_U_READ | FPERM_U_WRITE | FPERM_U_EXEC + +#define FPERM_G_READ 0x0020 /* Group can read. */ +#define FPERM_G_WRITE 0x0010 /* Group can write. */ +#define FPERM_G_EXEC 0x0008 /* Group can exec. */ +#define FPERM_G_RWX FPERM_G_READ | FPERM_G_WRITE | FPERM_G_EXEC + +#define FPERM_O_READ 0x0004 /* Anyone can read. */ +#define FPERM_O_WRITE 0x0002 /* Anyone can write. */ +#define FPERM_O_EXEC 0x0001 /* Anyone can exec. */ +#define FPERM_O_RWX FPERM_O_READ | FPERM_O_WRITE | FPERM_O_EXEC + +#define FPERM_DIR_DEFAULT FPERM_U_RWX | FPERM_G_RWX | FPERM_O_RWX /* rwx r-x r-x (0755) */ + + +/** + * Reads content from directory + * + * @note This native is expensive. Consider the use of open_dir(), next_file() and close_dir() instead. + * @note Both the '.' and '..' automatic directory entries will be retrieved for Windows and Linux. + * + * @param dirname Path to open + * @param pos Index the element + * @param output String buffer to hold content + * @param len Maximum size of string buffer + * @param outlen Number of characters written to the buffer + * + * @return Returns index of next element, otherwiwe 0 when end of dir is reached + */ +native read_dir(const dirname[], pos, output[], len, &outlen = 0); + +/** + * Reads line from file. + * + * @note This native is expensive. Consider the use of new file natives (fopen(), fgets(), etc.) + * if purpose is to read several lines of a file. + * + * @param file Path to open + * @param line Index of the line, starting to 0 + * @param text String buffer to hold line read + * @param len Maximum size of string buffer + * @param txtlen Number of characters written to the buffer + * + * @return Returns index of next line, otherwise 0 when end of file is reached + * @error Unable to read the file + */ +native read_file(const file[], line, text[], len, &txtlen = 0); + +/** + * Writes text to file. + * + * @note This native is expensive. Consider the use of new file natives (fopen(), fputs(), etc.) + * if purpose is to write several lines of a file. + * + * @param file Path to open + * @param text String to write to + * @param line Index of the line, starting to 0 + * If < 0, content will be appended + * + * @noreturn + * @error Unable to write [temporary] file + */ +native write_file(const file[], const text[], line = -1); + +/** + * Deletes a file. + * + * @param file Path of the file to delete + * @param use_valve_fs If true, the Valve file system will be used instead. + * This can be used to delete files existing in the Valve + * search path, rather than solely files existing directly + * in the gamedir. + * @param valve_path_id If use_valve_fs, a search path from gameinfo or "ALL" for all search paths. + * + * @return 1 on success, 0 on failure or if file not immediately removed. + */ +native delete_file(const file[], bool:use_valve_fs = false, const valve_path_id[] = "GAMECONFIG"); + +/** + * Checks if a file exists. + * + * @param file Path to the file + * @param use_valve_fs If true, the Valve file system will be used instead. + * This can be used to find files existing in any of + * the Valve search paths, rather than solely files + * existing directly in the gamedir. + * + * @return 1 if the file exists, 0 otherwise + */ +native file_exists(const file[], bool:use_valve_fs = false); + + /** + * Renames a file. + * + * @param oldname New path to the file + * @param newname Path to the existing file + * @param relative If true, native will act like other natives which + * use the moddir as a base directory. Otherwise, the + * current directory is undefined (but assumed to be hlds). + * + * @return 1 on success, 0 otherwise + */ +native rename_file(const oldname[], const newname[], relative = 0); + +/** + * Checks if a directory exists. + * + * @param dir Path to the directory + * @param use_valve_fs If true, the Valve file system will be used instead. + * This can be used to find files existing in any of + * the Valve search paths, rather than solely files + * existing directly in the gamedir. + * + * @return 1 if the directory exists, 0 otherwise + */ +native dir_exists(const dir[], bool:use_valve_fs = false); + +/** + * Get the file size in bytes. + * + * @param file Path to the file + * @param flag Flag options, see FSOPT_* constants + * @param use_valve_fs If true, the Valve file system will be used instead. + * This can be used to find files existing in any of + * the Valve search paths, rather than solely files + * existing directly in the gamedir. + * If used, flag option is ignored. + * @param valve_path_id If use_valve_fs, a search path from gameinfo or "ALL" for all search paths + * + * @return If flag is FSOPT_BYTES_COUNT or use_valve_fs to true, the file size in bytes + * If flag is FSOPT_LINES_COUNT, the number of lines in the file + * If flag is FSOPT_END_WITH_LF, 1 is returned if file ends with line feed + * If file doesn't exist, -1 + */ +native file_size(const file[], flag = FSOPT_BYTES_COUNT, bool:use_valve_fs = false, const valve_path_id[] = "GAME"); + +/** + * Opens or creates a file, returning a file handle on success. File handles + * should be closed with fclose(). + * + * @note The open mode may be one of the following strings: + * "r": Open an existing file for reading. + * "w": Create a file for writing, or truncate (delete the contents of) an + * existing file and then open it for writing. + * "a": Create a file for writing, or open an existing file such that writes + * will be appended to the end. + * "r+": Open an existing file for both reading and writing. + * "w+": Create a file for reading and writing, or truncate an existing file + * and then open it for reading and writing. + * "a+": Create a file for both reading and writing, or open an existing file + * such that writes will be appended to the end. + * + * @note The open mode may also contain an additional character after "r", "w", or "a", + * but before any "+" sign. This character may be "b" (indicating binary mode) or + * "t" (indicating text mode). By default, "text" mode is implied. On Linux and + * Mac, this has no distinction from binary mode. On Windows, it causes the '\n' + * character (0xA) to be written as "\r\n" (0xD, 0xA). + * + * Example: "rb" opens a binary file for writing; "at" opens a text file for + * appending. + * + * @note Registered paths ID are (in priority order) : + * GAME All paths related to current mod, including fallback + * Depending settings, it includes: _lv/_addon/_/_hd + * and itself + * GAMECONFIG The default writable directory () + * GAMEDOWNLOAD The download directory (_download) + * GAME_FALLBACK All paths related to fallback game, same as GAME + * DEFAULTGAME All paths related to the default game which is "valve", same as GAME + * BASE The base path where server is installed + * + * Note that some paths are non-writable. It includes all _* (expect _download) + * and DEFAULTGAME. Any file inside a non-writable path will be ignored if you try to open + * it in writing mode. + * + * @param filename File to open + * @param mode Open mode + * @param use_valve_fs If true, the Valve file system will be used instead + * This can be used to finred files existing in valve + * search paths, rather than solely files existing directly + * in the gamedir. + * @param valve_path_id If use_valve_fs, a search path from gameinfo or "ALL" for all search paths + * + * @return A file handle, or null if the file could not be opened. + */ +native fopen(const filename[], const mode[], bool:use_valve_fs = false, const valve_path_id[] = "GAME"); + +/** + * Closes a file handle. + * + * @param file File handle + */ +native fclose(file); + +/** + * Reads a single binary data from a file. + * + * @param file Handle to the file + * @param data Variable to store item read + * @param mode Size of each element, in bytes, to be read + * See BLOCK_* constants + * + * @return Number of elements read + */ +native fread(file, &any:data, mode); + +/** + * Reads binary data from a file. + * + * @param file Handle to the file + * @param data Array to store each item read + * @param blocks Number of items to read into the array + * @param mode Size of each element, in bytes, to be read + * Valid sizes are 1, 2, or 4. See BLOCK_* constants. + * + * @return Number of elements read + */ +native fread_blocks(file, any:data[], blocks, mode); + +/** + * Reads raw binary data from a file. + * + * @param file Handle to the file + * @param stream Array to store each item read + * @param blocksize Number of items to read into the array + * @param blocks Size of each element, in bytes. The data is read directly. + * That is, in 1 or 2-byte mode, the lower byte(s) in + * each cell are used directly, rather than performing + * any casts from a 4-byte number to a smaller number. + * + * @return Number of elements read + */ +native fread_raw(file, any:stream[], blocksize, blocks); + +/** + * Writes a single binary data to a file. + * + * @param file Handle to the file + * @param data Item to write + * @param mode Size of each item in the array in bytes + * Valid sizes are 1, 2, or 4. See BLOCK_* constants + * + * @return Number of elements written + */ +native fwrite(file, any:data, mode); + +/** + * Writes binary data to a file. + * + * @param file Handle to the file + * @param data Array of items to write + * @param blocks Number of items in the array + * @param mode Size of each item in the array in bytes + * Valid sizes are 1, 2, or 4. See BLOCK_* constants + * + * @return Number of elements written + */ +native fwrite_blocks(file, const any:data[], blocks, mode); + +/** + * Writes raw binary data to a file. + * + * @param file Handle to the file. + * @param stream Array of items to write. The data is written directly. + * That is, in 1 or 2-byte mode, the lower byte(s) in + * each cell are used directly, rather than performing + * any casts from a 4-byte number to a smaller number. + * @param blocks Size of each item in the array in bytes. + * @param mode Number of items in the array. + * + * @return Number of elements written + */ +native fwrite_raw(file, const any:stream[], blocks, mode); + +/** + * Tests if the end of file has been reached. + * + * @param file Handle to the file + * + * @return 1 if end of file has been reached, 0 otherwise. + */ +native feof(file); + +/** + * Reads a line from a text file. + * + * @param file Handle to the file. + * @param buffer String buffer to hold the line + * @param maxlength Maximum size of string buffer + * + * @return Total number of characters written on success, 0 otherwise + */ +native fgets(file, buffer[], maxlength); + +/** + * Writes a line of text to a text file. + * + * @param file Handle to the file + * @param text String to write + * @param null_term True to append NULL terminator, false otherwise + * + * @return 0 on success, -1 otherwise + */ +native fputs(file, const text[], bool:null_term = false); + +/** + * Writes a line of formatted text to a text file. + * + * @param file Handle to the file + * @param format Formatting rules + * @param ... Variable number of format parameters + * + * @return Total number of characters written on success, 0 otherwise + */ +native fprintf(file, const fmt[], any:...); + +/** + * Sets the file position indicator. + * + * @param file Handle to the file + * @param position Position relative to what is specified in whence + * @param start SEEK_ constant value of where to see from + * + * @return 0 on success, a non-zero value otherwise + */ +native fseek(file, position, start); + +/** + * Gets current position in the file. + * + * @param file Handle to the file + * + * @return Value for the file position indicator + */ +native ftell(file); + +/** + * Gets character from file. + * + * @param file Handle to the file + * + * @return Character read on success, -1 otherwise + */ +native fgetc(file); + +/** + * Writes character to file + * + * @param file Handle to the file + * @param data Character to put + * + * @return Character written on success, -1 otherwise + */ +native fputc(file, data); + +/** + * Ungets character from file. + * + * @param file Handle to the file + * @param data Character to unget + * + * @return On success, the character put back is returned, -1 otherwise + */ +native fungetc(file, data); + +/** + * Flushes a buffered output stream. + * + * @param file File handle, or 0 for all open streams + * + * @return 0 on success, -1 on failure + */ +native fflush(file); + +/** + * Gets the formatted file size in bytes. + * + * @param filename Path to the file + * @param ... Variable number of format parameters + * + * @return File size in bytes, otherwise -1 if file not found + */ +native filesize(const filename[], any:...); + +/** + * Removes a directory. + * + * @note On most Operating Systems you cannot remove a directory which has files inside it. + * + * @param path Path to the directory + * + * @return 1 on success, 0 otherwise + */ +native rmdir(const path[]); + +/** + * Creates a directory. + * + * @param path Path to create + * @param mode Permissions (default is o=rx,g=rx,u=rwx). Note that folders must have + * the execute bit set on Linux. On Windows, the mode is ignored. + * @param use_valve_fs If true, the Valve file system will be used instead + * This can be used to create folders in the game's + * Valve search paths, rather than directly in the gamedir. + * @param valve_path_id If use_valve_fs, a search path from gameinfo or "ALL" for default + * In this case, mode is ignored + * + * @return 0 on success, -1 otherwise + */ +native mkdir(const dirname[], mode = FPERM_DIR_DEFAULT, bool:use_valve_fs = false, const valve_path_id[] = "GAMECONFIG"); + +/** + * Deletes a file (delete_file macro) + * + * @param filename Path of the file to delete + * @param use_valve_fs If true, the Valve file system will be used instead. + * This can be used to delete files existing in the Valve + * search path, rather than solely files existing directly + * in the gamedir. + * @param valve_path_id If use_valve_fs, a search path from gameinfo or "ALL" for all search paths + * + * @return 1 on success, 0 on failure or if file not immediately removed + */ +native unlink(const filename[], bool:use_valve_fs = false, const valve_path_id[] = "GAMECONFIG"); + +/** + * Opens a directory/folder for contents enumeration. + * + * @note Directories are closed with close_dir(). + * + * @param dir Path to open. + * @param firstfile String buffer to hold first file name + * @param length Maximum size of the string buffer + * @param type Optional variable to store the file type + * @param use_valve_fs If true, the Valve file system will be used instead. + * This can be used to find files existing in any of + * the Valve search paths, rather than solely files + * existing directly in the gamedir. + * @param valve_path_id If use_valve_fs, a search path from gameinfo or "ALL" for all search paths. + * + * @return Handle to the directory, 0 otherwise + */ +native open_dir(dir[], firstfile[], length, &FileType:type = FileType_Unknown, bool:use_valve_fs = false, const valve_path_id[] = "GAME"); + +/** + * Reads the next directory entry as a local filename. + * + * @note Contents of buffers are undefined when returning false. + * @note Both the '.' and '..' automatic directory entries will be retrieved for Windows and Linux. + * + * @param dirh Handle to a directory + * @param buffer String buffer to hold directory name + * @param length Maximum size of string buffer + * @param type Optional variable to store the file type. FileType_* constants + * + * @return 1 on success, 0 if there are no more files to read. + */ +native next_file(dirh, buffer[], length, &FileType:type = FileType_Unknown); + +/** + * Closes the directory. + * + * @param dirh Handle to a directory + */ +native close_dir(dirh); + +/** + * Loads a file using the LoadFileForMe engine function. + * + * The data is truncated if there is not enough space. No null-terminator + * is applied; the data is the raw contents of the file. + * + * @param file File to load (may be a file from the GCF) + * @param buffer Buffer to store file contents + * @param maxlength Maximum size of the file buffer + * @param length Variable to store the file length. This may return + * a number larger than the buffer size + * @return -1 if the file could not be loaded. Otherwise, + * the number of cells actually written to the buffer + * are returned. + */ +native LoadFileForMe(const file[], buffer[], maxlength, &length = 0); + /** * Returns a file timestamp as a unix timestamp. * - * @param file File name. - * @param tmode Time mode. See FileTime_* constants. - * @return Returns a file timestamp as a unix timestamp. + * @param file File name + * @param tmode Time mode, see FileTime_* constants + * + * @return Returns a file timestamp as a unix timestamp */ -native GetFileTime( const file[], FileTimeType:tmode ); +native GetFileTime(const file[], FileTimeType:tmode);