#include "file.h" #include "cfile/cfilesystem.h" extern int cfread_lua_number(double *buf, CFILE *cfile); namespace scripting { namespace api { cfile_h::cfile_h(CFILE* cfp) : _cfp(cfp) {} cfile_h::~cfile_h() { if (_cfp && cf_is_valid(_cfp.get())) { Error(LOCATION, "Lua file handle has been left open!\n\nSorry, I can't say anything more than that..."); } } bool cfile_h::isValid() const { return _cfp != nullptr; } CFILE* cfile_h::get() const { return _cfp.get(); } void cfile_h::close() { _cfp = nullptr; // Automatically closes the handle } //**********HANDLE: File ADE_OBJ(l_File, cfile_h, "file", "File handle"); ADE_FUNC(isValid, l_File, NULL, "Detects whether handle is valid", "boolean", "true if valid, false if handle is invalid, nil if a syntax/type error occurs") { cfile_h* cfp = nullptr; if (!ade_get_args(L, "o", l_File.GetPtr(&cfp))) return ADE_RETURN_NIL; if (cfp == nullptr || !cfp->isValid()) return ADE_RETURN_FALSE; return ADE_RETURN_TRUE; } ADE_FUNC(close, l_File, NULL, "Instantly closes file and invalidates all file handles", NULL, NULL) { cfile_h* cfp = nullptr; if (!ade_get_args(L, "o", l_File.GetPtr(&cfp))) return ADE_RETURN_FALSE; if (cfp == nullptr || !cfp->isValid()) return ADE_RETURN_FALSE; cfp->close(); return ADE_RETURN_TRUE; } ADE_FUNC(flush, l_File, NULL, "Flushes file buffer to disk.", "boolean", "True for success, false on failure") { cfile_h* cfp = nullptr; if (!ade_get_args(L, "o", l_File.GetPtr(&cfp))) return ADE_RETURN_FALSE; if (cfp == nullptr || !cfp->isValid()) return ADE_RETURN_FALSE; //WMC - this looks reversed, yes, it's right. Look at cflush. int cf_result = cflush(cfp->get()); return ade_set_args(L, "b", cf_result ? false : true); } ADE_FUNC(getPath, l_File, NULL, "Determines path of the given file", "string", "Path string of the file handle, or an empty string if it doesn't have one, or the handle is invalid") { cfile_h* cfp = nullptr; if (!ade_get_args(L, "o", l_File.GetPtr(&cfp))) return ade_set_error(L, "s", ""); if (cfp == nullptr || !cfp->isValid()) return ade_set_error(L, "s", ""); int id = cf_get_dir_type(cfp->get()); if (Pathtypes[id].path != NULL) return ade_set_args(L, "s", Pathtypes[id].path); else return ade_set_args(L, "s", ""); } ADE_FUNC(read, l_File, "number|string, ...", "Reads part of or all of a file, depending on arguments passed. Based on basic Lua file:read function." "Returns nil when the end of the file is reached." "
", ade_type_info({"number|string", "..."}), "Requested data, or nil if the function fails") { cfile_h* cfp = nullptr; if (!ade_get_args(L, "o", l_File.GetPtr(&cfp))) return ADE_RETURN_NIL; if (cfp == nullptr || !cfp->isValid()) return ADE_RETURN_NIL; int i; int num_returned = 0; int type = LUA_TNONE; //WMC - Since we push stuff onto the stack, we must get //the original arguments NOW. int lastarg = lua_gettop(L); for (i = 2; i <= lastarg; i++) { type = lua_type(L, i); const char* fmt = nullptr; //int num = 0; if (type == LUA_TSTRING) { fmt = lua_tostring(L, i); if (!stricmp(fmt, "*n")) { double d = 0.0f; if (cfread_lua_number(&d, cfp->get()) == EOF) return ADE_RETURN_NIL; lua_pushnumber(L, d); num_returned++; } else if (!stricmp(fmt, "*a")) { int tell_res = cftell(cfp->get()); if (tell_res < 0) { Error(LOCATION, "Critical error reading Lua file; could not cftell."); } int read_len = cfilelength(cfp->get()) - tell_res; char *buf = (char *)vm_malloc(read_len + 1); int final_len = cfread(buf, 1, read_len, cfp->get()); buf[final_len] = '\0'; lua_pushstring(L, buf); vm_free(buf); num_returned++; } else if (!stricmp(fmt, "*l")) { char buf[10240]; size_t idx; if (cfgets(buf, (int)(sizeof(buf) / sizeof(char)), cfp->get()) == nullptr) { lua_pushnil(L); } else { // Strip all newlines so this works like the Lua original // http://www.lua.org/source/5.1/liolib.c.html#g_read // Note: we also strip carriage return in WMC's implementation for (idx = 0; idx < strlen(buf); idx++) { if (buf[idx] == '\n' || buf[idx] == '\r') buf[idx] = '\0'; } lua_pushstring(L, buf); } num_returned++; } } if (type == LUA_TNUMBER || (type == LUA_TSTRING && strpbrk(fmt, "1234567890"))) { int num = 0; if (type == LUA_TSTRING) num = atoi(fmt); else num = fl2i(lua_tonumber(L, i)); if (num < 1) { if (cfeof(cfp->get())) lua_pushstring(L, ""); else lua_pushnil(L); } char *buf = (char*)vm_malloc(num + 1); int total_read = cfread(buf, 1, num, cfp->get()); if (total_read) { buf[total_read] = '\0'; lua_pushstring(L, buf); } else { lua_pushnil(L); } vm_free(buf); num_returned++; } if (type != LUA_TNUMBER && type != LUA_TSTRING) LuaError(L, "Invalid argument passed to file:read"); } return num_returned; } ADE_FUNC(seek, l_File, "[string Whence=\"cur\", number Offset=0]", "Changes position of file, or gets location." "Whence can be:" "
  • \"set\" - File start.
  • " "
  • \"cur\" - Current position in file.
  • " "
  • \"end\" - File end.
  • ", "number", "new offset, or false or nil on failure") { const char* w = nullptr; int o = 0; cfile_h* cfp = nullptr; if (!ade_get_args(L, "o|si", l_File.GetPtr(&cfp), &w, &o)) return ADE_RETURN_NIL; if (cfp == nullptr || !cfp->isValid()) return ADE_RETURN_NIL; if (!(w == NULL || (!stricmp(w, "cur") && o != 0))) { int seek_type = CF_SEEK_CUR; if (!stricmp(w, "set")) seek_type = CF_SEEK_SET; else if (!stricmp(w, "cur")) seek_type = CF_SEEK_CUR; else if (!stricmp(w, "end")) seek_type = CF_SEEK_END; else LuaError(L, "Invalid where argument passed to seek() - '%s'", w); if (cfseek(cfp->get(), o, seek_type)) return ADE_RETURN_FALSE; } int res = cftell(cfp->get()); if (res >= 0) return ade_set_args(L, "i", res); else return ADE_RETURN_FALSE; } ADE_FUNC(write, l_File, "string or number, ...", "Writes a series of Lua strings or numbers to the current file.", "number", "Number of items successfully written.") { cfile_h* cfp = nullptr; if (!ade_get_args(L, "o", l_File.GetPtr(&cfp))) return ade_set_error(L, "i", 0); if (cfp == nullptr || !cfp->isValid()) return ade_set_error(L, "i", 0); int l_pos = 2; int type = LUA_TNONE; int num_successful = 0; while ((type = lua_type(L, l_pos)) != LUA_TNONE) { if (type == LUA_TSTRING) { auto s = lua_tostring(L, l_pos); if (cfwrite(s, (int)sizeof(char), (int)strlen(s), cfp->get())) num_successful++; } else if (type == LUA_TNUMBER) { double d = lua_tonumber(L, l_pos); char buf[32] = { 0 }; sprintf(buf, LUA_NUMBER_FMT, d); if (cfwrite(buf, (int)sizeof(char), (int)strlen(buf), cfp->get())) num_successful++; } l_pos++; } return ade_set_args(L, "i", num_successful); } } }