Mercurial > love-experiments
diff src/modules/filesystem/physfs/Filesystem.cpp @ 1096:86a44f2bc38f
CRLF to LF
| author | Bart van Strien <bart.bes+projects@gmail.com> |
|---|---|
| date | Wed, 04 Jul 2012 14:40:24 +0200 |
| parents | 73f4863ee5b7 |
| children | a4d516668953 |
line wrap: on
line diff
--- a/src/modules/filesystem/physfs/Filesystem.cpp Wed Jul 04 13:54:15 2012 +0200 +++ b/src/modules/filesystem/physfs/Filesystem.cpp Wed Jul 04 14:40:24 2012 +0200 @@ -1,610 +1,610 @@ -/** - * Copyright (c) 2006-2012 LOVE Development Team - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - **/ - -#include "common/config.h" - -#include <iostream> - -#include "common/utf8.h" -#include "common/b64.h" - -#include "Filesystem.h" - -namespace love -{ -namespace filesystem -{ -namespace physfs -{ - -Filesystem::Filesystem() - : open_count(0) - , buffer(0) - , isInited(false) - , release(false) - , releaseSet(false) -{ -} - -Filesystem::~Filesystem() -{ - if (isInited) - { - isInited = false; - PHYSFS_deinit(); - } -} - -const char *Filesystem::getName() const -{ - return "love.filesystem.physfs"; -} - -void Filesystem::init(const char *arg0) -{ - if (!PHYSFS_init(arg0)) - throw Exception(PHYSFS_getLastError()); - isInited = true; -} - -void Filesystem::setRelease(bool release) -{ - if (releaseSet) - return; - this->release = release; - releaseSet = true; -} - -bool Filesystem::isRelease() const -{ - if (!releaseSet) - return false; - return release; -} - -bool Filesystem::setIdentity(const char *ident) -{ - if (!isInited) - return false; - - // Store the save directory. - save_identity = std::string(ident); - - // Generate the relative path to the game save folder. - save_path_relative = std::string(LOVE_APPDATA_PREFIX LOVE_APPDATA_FOLDER LOVE_PATH_SEPARATOR) + save_identity; - - // Generate the full path to the game save folder. - save_path_full = std::string(getAppdataDirectory()) + std::string(LOVE_PATH_SEPARATOR); - if (release) - save_path_full += std::string(LOVE_APPDATA_PREFIX) + save_identity; - else - save_path_full += save_path_relative; - - // We now have something like: - // save_identity: game - // save_path_relative: ./LOVE/game - // save_path_full: C:\Documents and Settings\user\Application Data/LOVE/game - - // Try to add the save directory to the search path. - // (No error on fail, it means that the path doesn't exist). - PHYSFS_addToSearchPath(save_path_full.c_str(), 0); - - return true; -} - -const char *Filesystem::getIdentity() const -{ - return save_identity.c_str(); -} - -bool Filesystem::setSource(const char *source) -{ - if (!isInited) - return false; - - // Check whether directory is already set. - if (!game_source.empty()) - return false; - - // Add the directory. - if (!PHYSFS_addToSearchPath(source, 1)) - return false; - - // Save the game source. - game_source = std::string(source); - - return true; -} - -bool Filesystem::setupWriteDirectory() -{ - if (!isInited) - return false; - - // These must all be set. - if (save_identity.empty() || save_path_full.empty() || save_path_relative.empty()) - return false; - - // Set the appdata folder as writable directory. - // (We must create the save folder before mounting it). - if (!PHYSFS_setWriteDir(getAppdataDirectory())) - return false; - - // Create the save folder. (We're now "at" %APPDATA%). - bool success = false; - if (release) - success = mkdir(save_identity.c_str()); - else - success = mkdir(save_path_relative.c_str()); - - if (!success) - { - PHYSFS_setWriteDir(0); // Clear the write directory in case of error. - return false; - } - - // Set the final write directory. - if (!PHYSFS_setWriteDir(save_path_full.c_str())) - return false; - - // Add the directory. (Will not be readded if already present). - if (!PHYSFS_addToSearchPath(save_path_full.c_str(), 0)) - { - PHYSFS_setWriteDir(0); // Clear the write directory in case of error. - return false; - } - - return true; -} - -File *Filesystem::newFile(const char *filename) -{ - return new File(filename); -} - -FileData *Filesystem::newFileData(void *data, unsigned int size, const char *filename) -{ - FileData *fd = new FileData(size, std::string(filename)); - - // Copy the data into FileData. - memcpy(fd->getData(), data, size); - - return fd; -} - -FileData *Filesystem::newFileData(const char *b64, const char *filename) -{ - int size = strlen(b64); - int outsize = 0; - char *dst = b64_decode(b64, size, outsize); - FileData *fd = new FileData(outsize, std::string(filename)); - - // Copy the data into FileData. - memcpy(fd->getData(), dst, outsize); - delete [] dst; - - return fd; -} - -const char *Filesystem::getWorkingDirectory() -{ - if (cwd.empty()) - { -#ifdef LOVE_WINDOWS - - WCHAR w_cwd[LOVE_MAX_PATH]; - _wgetcwd(w_cwd, LOVE_MAX_PATH); - cwd = to_utf8(w_cwd); - replace_char(cwd, '\\', '/'); -#else - char *cwd_char = new char[LOVE_MAX_PATH]; - - if (getcwd(cwd_char, LOVE_MAX_PATH)) - cwd = cwd_char; // if getcwd fails, cwd_char (and thus cwd) will still be empty - - delete [] cwd_char; -#endif - } - - return cwd.c_str(); -} - -const char *Filesystem::getUserDirectory() -{ - return PHYSFS_getUserDir(); -} - -const char *Filesystem::getAppdataDirectory() -{ -#ifdef LOVE_WINDOWS - if (appdata.empty()) - { - wchar_t *w_appdata = _wgetenv(TEXT("APPDATA")); - appdata = to_utf8(w_appdata); - replace_char(appdata, '\\', '/'); - } - return appdata.c_str(); -#elif defined(LOVE_MACOSX) - if (appdata.empty()) - { - std::string udir = getUserDirectory(); - udir.append("/Library/Application Support"); - appdata = udir; - } - return appdata.c_str(); -#elif defined(LOVE_LINUX) - if (appdata.empty()) - { - char *xdgdatahome = getenv("XDG_DATA_HOME"); - if (!xdgdatahome) - appdata = std::string(getUserDirectory()) + "/.local/share/"; - else - appdata = xdgdatahome; - } - return appdata.c_str(); -#else - return getUserDirectory(); -#endif -} - - -const char *Filesystem::getSaveDirectory() -{ - return save_path_full.c_str(); -} - -bool Filesystem::exists(const char *file) -{ - if (PHYSFS_exists(file)) - return true; - return false; -} - -bool Filesystem::isDirectory(const char *file) -{ - if (PHYSFS_isDirectory(file)) - return true; - return false; -} - -bool Filesystem::isFile(const char *file) -{ - return exists(file) && !isDirectory(file); -} - -bool Filesystem::mkdir(const char *file) -{ - if (PHYSFS_getWriteDir() == 0 && !setupWriteDirectory()) - return false; - - if (!PHYSFS_mkdir(file)) - return false; - return true; -} - -bool Filesystem::remove(const char *file) -{ - if (PHYSFS_getWriteDir() == 0 && !setupWriteDirectory()) - return false; - - if (!PHYSFS_delete(file)) - return false; - return true; -} - -int Filesystem::read(lua_State *L) -{ - // The file to read from. The file must either be created - // on-the-fly, or passed as a parameter. - File *file; - - if (lua_isstring(L, 1)) - { - // Create the file. - file = newFile(lua_tostring(L, 1)); - } - else - return luaL_error(L, "Expected filename."); - - // Optionally, the caller can specify whether to read - // the whole file, or just a part of it. - int count = luaL_optint(L, 2, (lua_Integer)file->getSize()); // FIXME - - // Read the data. - Data *data = file->read(count); - - // Error check. - if (data == 0) - return luaL_error(L, "File could not be read."); - - // Close and delete the file, if we created it. - // (I.e. if the first parameter is a string). - if (lua_isstring(L, 1)) - file->release(); - - // Push the string. - lua_pushlstring(L, (char *)data->getData(), data->getSize()); - - // Push the size. - lua_pushinteger(L, data->getSize()); - - // Lua has a copy now, so we can free it. - data->release(); - - return 2; -} - -int Filesystem::write(lua_State *L) -{ - // The file to write to. The file must either be created - // on-the-fly, or passed as a parameter. - File *file; - - // We know for sure that we need a second parameter, so - // let's check that first. - if (lua_isnoneornil(L, 2)) - return luaL_error(L, "Second argument needed."); - - if (lua_isstring(L, 1)) - { - // Create the file. - file = newFile(lua_tostring(L, 1)); - } - else - return luaL_error(L, "Expected filename."); - - // Get the current mode of the file. - File::Mode mode = file->getMode(); - - if (mode == File::CLOSED) - { - // It should be possible to use append mode, but - // normal File::Mode::Write is the default. - int mode = luaL_optint(L, 4, File::WRITE); - - // Open the file. - if (!file->open((File::Mode)mode)) - return luaL_error(L, "Could not open file."); - } - - size_t length = 0; - const char *input; - if (lua_isstring(L, 2)) - { - input = lua_tolstring(L, 2, &length); - } - else if (luax_istype(L, 2, DATA_T)) - { - love::Data *data = luax_totype<love::Data>(L, 2, "Data", DATA_T); - length = data->getSize(); - input = (char *)data->getData(); - } - else - { - return luaL_error(L, "Expected string or data for argument #2."); - } - - // Get how much we should write. Length of string default. - length = luaL_optint(L, 3, length); - - // Write the data. - bool success = file->write(input, length); - - // Close and delete the file, if we created - // it in this function. - if (lua_isstring(L, 1)) - { - // Kill the file if "we" created it. - file->close(); - file->release(); - } - - if (!success) - return luaL_error(L, "Data could not be written."); - - lua_pushboolean(L, success); - return 1; -} - -int Filesystem::enumerate(lua_State *L) -{ - int n = lua_gettop(L); - - if (n != 1) - return luaL_error(L, "Function requires a single parameter."); - - int type = lua_type(L, 1); - - if (type != LUA_TSTRING) - return luaL_error(L, "Function requires parameter of type string."); - - const char *dir = lua_tostring(L, 1); - char **rc = PHYSFS_enumerateFiles(dir); - char **i; - int index = 1; - - lua_newtable(L); - - for (i = rc; *i != 0; i++) - { - lua_pushinteger(L, index); - lua_pushstring(L, *i); - lua_settable(L, -3); - index++; - } - - PHYSFS_freeList(rc); - - return 1; -} - -int Filesystem::lines_i(lua_State *L) -{ - const int bufsize = 1024; - char buf[bufsize]; - int linesize = 0; - bool newline = false; - - File *file = luax_checktype<File>(L, lua_upvalueindex(1), "File", FILESYSTEM_FILE_T); - - // Only accept read mode at this point. - if (file->getMode() != File::READ) - return luaL_error(L, "File needs to stay in read mode."); - - int64 pos = file->tell(); - int64 userpos = -1; - - if (lua_isnoneornil(L, lua_upvalueindex(2)) == 0) - { - // User may have changed the file position. - userpos = pos; - pos = (int64) lua_tonumber(L, lua_upvalueindex(2)); - if (userpos != pos) - file->seek(pos); - } - - while (!newline && !file->eof()) - { - // This 64-bit to 32-bit integer cast should be safe as it never exceeds bufsize. - int read = (int) file->read(buf, bufsize); - if (read < 0) - return luaL_error(L, "Could not read from file."); - - linesize += read; - - for (int i = 0; i < read; i++) - { - if (buf[i] == '\n') - { - linesize -= read - i; - newline = true; - break; - } - } - } - - if (newline || (file->eof() && linesize > 0)) - { - if (linesize < bufsize) - { - // We have the line in the buffer on the stack. No 'new' and 'read' needed. - lua_pushlstring(L, buf, linesize > 0 && buf[linesize - 1] == '\r' ? linesize - 1 : linesize); - if (userpos < 0) - file->seek(pos + linesize + 1); - } - else - { - char *str; - try - { - str = new char[linesize + 1]; - } - catch(std::bad_alloc &) - { - return luaL_error(L, "Out of memory"); - } - file->seek(pos); - - // Read the \n anyway and save us a call to seek. - if (file->read(str, linesize + 1) == -1) - { - delete [] str; - return luaL_error(L, "Could not read from file."); - } - - lua_pushlstring(L, str, str[linesize - 1] == '\r' ? linesize - 1 : linesize); - delete [] str; - } - - if (userpos >= 0) - { - // Save new position in upvalue. - lua_pushnumber(L, (lua_Number)(pos + linesize + 1)); - lua_replace(L, lua_upvalueindex(2)); - file->seek(userpos); - } - - return 1; - } - - // EOF reached. - if (userpos >= 0 && luax_toboolean(L, lua_upvalueindex(3))) - file->seek(userpos); - else - file->close(); - - return 0; -} - -int Filesystem::load(lua_State *L) -{ - // Need only one arg. - luax_assert_argc(L, 1, 1); - - // Must be string. - if (!lua_isstring(L, -1)) - return luaL_error(L, "The argument must be a string."); - - std::string filename = lua_tostring(L, -1); - - // The file must exist. - if (!exists(filename.c_str())) - return luaL_error(L, "File %s does not exist.", filename.c_str()); - - // Create the file. - File *file = newFile(filename.c_str()); - - // Get the data from the file. - Data *data = file->read(); - - int status = luaL_loadbuffer(L, (const char *)data->getData(), data->getSize(), ("@" + filename).c_str()); - - data->release(); - file->release(); - - // Load the chunk, but don't run it. - switch (status) - { - case LUA_ERRMEM: - return luaL_error(L, "Memory allocation error: %s\n", lua_tostring(L, -1)); - case LUA_ERRSYNTAX: - return luaL_error(L, "Syntax error: %s\n", lua_tostring(L, -1)); - default: // success - return 1; - } -} - -int Filesystem::getLastModified(lua_State *L) -{ - const char *filename = luaL_checkstring(L, 1); - PHYSFS_sint64 time = PHYSFS_getLastModTime(filename); - if (time == -1) - { - lua_pushnil(L); - lua_pushstring(L, "Could not determine file modification date."); - return 2; - } - lua_pushnumber(L, static_cast<lua_Number>(time)); - return 1; -} - -} // physfs -} // filesystem -} // love +/** + * Copyright (c) 2006-2012 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#include "common/config.h" + +#include <iostream> + +#include "common/utf8.h" +#include "common/b64.h" + +#include "Filesystem.h" + +namespace love +{ +namespace filesystem +{ +namespace physfs +{ + +Filesystem::Filesystem() + : open_count(0) + , buffer(0) + , isInited(false) + , release(false) + , releaseSet(false) +{ +} + +Filesystem::~Filesystem() +{ + if (isInited) + { + isInited = false; + PHYSFS_deinit(); + } +} + +const char *Filesystem::getName() const +{ + return "love.filesystem.physfs"; +} + +void Filesystem::init(const char *arg0) +{ + if (!PHYSFS_init(arg0)) + throw Exception(PHYSFS_getLastError()); + isInited = true; +} + +void Filesystem::setRelease(bool release) +{ + if (releaseSet) + return; + this->release = release; + releaseSet = true; +} + +bool Filesystem::isRelease() const +{ + if (!releaseSet) + return false; + return release; +} + +bool Filesystem::setIdentity(const char *ident) +{ + if (!isInited) + return false; + + // Store the save directory. + save_identity = std::string(ident); + + // Generate the relative path to the game save folder. + save_path_relative = std::string(LOVE_APPDATA_PREFIX LOVE_APPDATA_FOLDER LOVE_PATH_SEPARATOR) + save_identity; + + // Generate the full path to the game save folder. + save_path_full = std::string(getAppdataDirectory()) + std::string(LOVE_PATH_SEPARATOR); + if (release) + save_path_full += std::string(LOVE_APPDATA_PREFIX) + save_identity; + else + save_path_full += save_path_relative; + + // We now have something like: + // save_identity: game + // save_path_relative: ./LOVE/game + // save_path_full: C:\Documents and Settings\user\Application Data/LOVE/game + + // Try to add the save directory to the search path. + // (No error on fail, it means that the path doesn't exist). + PHYSFS_addToSearchPath(save_path_full.c_str(), 0); + + return true; +} + +const char *Filesystem::getIdentity() const +{ + return save_identity.c_str(); +} + +bool Filesystem::setSource(const char *source) +{ + if (!isInited) + return false; + + // Check whether directory is already set. + if (!game_source.empty()) + return false; + + // Add the directory. + if (!PHYSFS_addToSearchPath(source, 1)) + return false; + + // Save the game source. + game_source = std::string(source); + + return true; +} + +bool Filesystem::setupWriteDirectory() +{ + if (!isInited) + return false; + + // These must all be set. + if (save_identity.empty() || save_path_full.empty() || save_path_relative.empty()) + return false; + + // Set the appdata folder as writable directory. + // (We must create the save folder before mounting it). + if (!PHYSFS_setWriteDir(getAppdataDirectory())) + return false; + + // Create the save folder. (We're now "at" %APPDATA%). + bool success = false; + if (release) + success = mkdir(save_identity.c_str()); + else + success = mkdir(save_path_relative.c_str()); + + if (!success) + { + PHYSFS_setWriteDir(0); // Clear the write directory in case of error. + return false; + } + + // Set the final write directory. + if (!PHYSFS_setWriteDir(save_path_full.c_str())) + return false; + + // Add the directory. (Will not be readded if already present). + if (!PHYSFS_addToSearchPath(save_path_full.c_str(), 0)) + { + PHYSFS_setWriteDir(0); // Clear the write directory in case of error. + return false; + } + + return true; +} + +File *Filesystem::newFile(const char *filename) +{ + return new File(filename); +} + +FileData *Filesystem::newFileData(void *data, unsigned int size, const char *filename) +{ + FileData *fd = new FileData(size, std::string(filename)); + + // Copy the data into FileData. + memcpy(fd->getData(), data, size); + + return fd; +} + +FileData *Filesystem::newFileData(const char *b64, const char *filename) +{ + int size = strlen(b64); + int outsize = 0; + char *dst = b64_decode(b64, size, outsize); + FileData *fd = new FileData(outsize, std::string(filename)); + + // Copy the data into FileData. + memcpy(fd->getData(), dst, outsize); + delete [] dst; + + return fd; +} + +const char *Filesystem::getWorkingDirectory() +{ + if (cwd.empty()) + { +#ifdef LOVE_WINDOWS + + WCHAR w_cwd[LOVE_MAX_PATH]; + _wgetcwd(w_cwd, LOVE_MAX_PATH); + cwd = to_utf8(w_cwd); + replace_char(cwd, '\\', '/'); +#else + char *cwd_char = new char[LOVE_MAX_PATH]; + + if (getcwd(cwd_char, LOVE_MAX_PATH)) + cwd = cwd_char; // if getcwd fails, cwd_char (and thus cwd) will still be empty + + delete [] cwd_char; +#endif + } + + return cwd.c_str(); +} + +const char *Filesystem::getUserDirectory() +{ + return PHYSFS_getUserDir(); +} + +const char *Filesystem::getAppdataDirectory() +{ +#ifdef LOVE_WINDOWS + if (appdata.empty()) + { + wchar_t *w_appdata = _wgetenv(TEXT("APPDATA")); + appdata = to_utf8(w_appdata); + replace_char(appdata, '\\', '/'); + } + return appdata.c_str(); +#elif defined(LOVE_MACOSX) + if (appdata.empty()) + { + std::string udir = getUserDirectory(); + udir.append("/Library/Application Support"); + appdata = udir; + } + return appdata.c_str(); +#elif defined(LOVE_LINUX) + if (appdata.empty()) + { + char *xdgdatahome = getenv("XDG_DATA_HOME"); + if (!xdgdatahome) + appdata = std::string(getUserDirectory()) + "/.local/share/"; + else + appdata = xdgdatahome; + } + return appdata.c_str(); +#else + return getUserDirectory(); +#endif +} + + +const char *Filesystem::getSaveDirectory() +{ + return save_path_full.c_str(); +} + +bool Filesystem::exists(const char *file) +{ + if (PHYSFS_exists(file)) + return true; + return false; +} + +bool Filesystem::isDirectory(const char *file) +{ + if (PHYSFS_isDirectory(file)) + return true; + return false; +} + +bool Filesystem::isFile(const char *file) +{ + return exists(file) && !isDirectory(file); +} + +bool Filesystem::mkdir(const char *file) +{ + if (PHYSFS_getWriteDir() == 0 && !setupWriteDirectory()) + return false; + + if (!PHYSFS_mkdir(file)) + return false; + return true; +} + +bool Filesystem::remove(const char *file) +{ + if (PHYSFS_getWriteDir() == 0 && !setupWriteDirectory()) + return false; + + if (!PHYSFS_delete(file)) + return false; + return true; +} + +int Filesystem::read(lua_State *L) +{ + // The file to read from. The file must either be created + // on-the-fly, or passed as a parameter. + File *file; + + if (lua_isstring(L, 1)) + { + // Create the file. + file = newFile(lua_tostring(L, 1)); + } + else + return luaL_error(L, "Expected filename."); + + // Optionally, the caller can specify whether to read + // the whole file, or just a part of it. + int count = luaL_optint(L, 2, (lua_Integer)file->getSize()); // FIXME + + // Read the data. + Data *data = file->read(count); + + // Error check. + if (data == 0) + return luaL_error(L, "File could not be read."); + + // Close and delete the file, if we created it. + // (I.e. if the first parameter is a string). + if (lua_isstring(L, 1)) + file->release(); + + // Push the string. + lua_pushlstring(L, (char *)data->getData(), data->getSize()); + + // Push the size. + lua_pushinteger(L, data->getSize()); + + // Lua has a copy now, so we can free it. + data->release(); + + return 2; +} + +int Filesystem::write(lua_State *L) +{ + // The file to write to. The file must either be created + // on-the-fly, or passed as a parameter. + File *file; + + // We know for sure that we need a second parameter, so + // let's check that first. + if (lua_isnoneornil(L, 2)) + return luaL_error(L, "Second argument needed."); + + if (lua_isstring(L, 1)) + { + // Create the file. + file = newFile(lua_tostring(L, 1)); + } + else + return luaL_error(L, "Expected filename."); + + // Get the current mode of the file. + File::Mode mode = file->getMode(); + + if (mode == File::CLOSED) + { + // It should be possible to use append mode, but + // normal File::Mode::Write is the default. + int mode = luaL_optint(L, 4, File::WRITE); + + // Open the file. + if (!file->open((File::Mode)mode)) + return luaL_error(L, "Could not open file."); + } + + size_t length = 0; + const char *input; + if (lua_isstring(L, 2)) + { + input = lua_tolstring(L, 2, &length); + } + else if (luax_istype(L, 2, DATA_T)) + { + love::Data *data = luax_totype<love::Data>(L, 2, "Data", DATA_T); + length = data->getSize(); + input = (char *)data->getData(); + } + else + { + return luaL_error(L, "Expected string or data for argument #2."); + } + + // Get how much we should write. Length of string default. + length = luaL_optint(L, 3, length); + + // Write the data. + bool success = file->write(input, length); + + // Close and delete the file, if we created + // it in this function. + if (lua_isstring(L, 1)) + { + // Kill the file if "we" created it. + file->close(); + file->release(); + } + + if (!success) + return luaL_error(L, "Data could not be written."); + + lua_pushboolean(L, success); + return 1; +} + +int Filesystem::enumerate(lua_State *L) +{ + int n = lua_gettop(L); + + if (n != 1) + return luaL_error(L, "Function requires a single parameter."); + + int type = lua_type(L, 1); + + if (type != LUA_TSTRING) + return luaL_error(L, "Function requires parameter of type string."); + + const char *dir = lua_tostring(L, 1); + char **rc = PHYSFS_enumerateFiles(dir); + char **i; + int index = 1; + + lua_newtable(L); + + for (i = rc; *i != 0; i++) + { + lua_pushinteger(L, index); + lua_pushstring(L, *i); + lua_settable(L, -3); + index++; + } + + PHYSFS_freeList(rc); + + return 1; +} + +int Filesystem::lines_i(lua_State *L) +{ + const int bufsize = 1024; + char buf[bufsize]; + int linesize = 0; + bool newline = false; + + File *file = luax_checktype<File>(L, lua_upvalueindex(1), "File", FILESYSTEM_FILE_T); + + // Only accept read mode at this point. + if (file->getMode() != File::READ) + return luaL_error(L, "File needs to stay in read mode."); + + int64 pos = file->tell(); + int64 userpos = -1; + + if (lua_isnoneornil(L, lua_upvalueindex(2)) == 0) + { + // User may have changed the file position. + userpos = pos; + pos = (int64) lua_tonumber(L, lua_upvalueindex(2)); + if (userpos != pos) + file->seek(pos); + } + + while (!newline && !file->eof()) + { + // This 64-bit to 32-bit integer cast should be safe as it never exceeds bufsize. + int read = (int) file->read(buf, bufsize); + if (read < 0) + return luaL_error(L, "Could not read from file."); + + linesize += read; + + for (int i = 0; i < read; i++) + { + if (buf[i] == '\n') + { + linesize -= read - i; + newline = true; + break; + } + } + } + + if (newline || (file->eof() && linesize > 0)) + { + if (linesize < bufsize) + { + // We have the line in the buffer on the stack. No 'new' and 'read' needed. + lua_pushlstring(L, buf, linesize > 0 && buf[linesize - 1] == '\r' ? linesize - 1 : linesize); + if (userpos < 0) + file->seek(pos + linesize + 1); + } + else + { + char *str; + try + { + str = new char[linesize + 1]; + } + catch(std::bad_alloc &) + { + return luaL_error(L, "Out of memory"); + } + file->seek(pos); + + // Read the \n anyway and save us a call to seek. + if (file->read(str, linesize + 1) == -1) + { + delete [] str; + return luaL_error(L, "Could not read from file."); + } + + lua_pushlstring(L, str, str[linesize - 1] == '\r' ? linesize - 1 : linesize); + delete [] str; + } + + if (userpos >= 0) + { + // Save new position in upvalue. + lua_pushnumber(L, (lua_Number)(pos + linesize + 1)); + lua_replace(L, lua_upvalueindex(2)); + file->seek(userpos); + } + + return 1; + } + + // EOF reached. + if (userpos >= 0 && luax_toboolean(L, lua_upvalueindex(3))) + file->seek(userpos); + else + file->close(); + + return 0; +} + +int Filesystem::load(lua_State *L) +{ + // Need only one arg. + luax_assert_argc(L, 1, 1); + + // Must be string. + if (!lua_isstring(L, -1)) + return luaL_error(L, "The argument must be a string."); + + std::string filename = lua_tostring(L, -1); + + // The file must exist. + if (!exists(filename.c_str())) + return luaL_error(L, "File %s does not exist.", filename.c_str()); + + // Create the file. + File *file = newFile(filename.c_str()); + + // Get the data from the file. + Data *data = file->read(); + + int status = luaL_loadbuffer(L, (const char *)data->getData(), data->getSize(), ("@" + filename).c_str()); + + data->release(); + file->release(); + + // Load the chunk, but don't run it. + switch (status) + { + case LUA_ERRMEM: + return luaL_error(L, "Memory allocation error: %s\n", lua_tostring(L, -1)); + case LUA_ERRSYNTAX: + return luaL_error(L, "Syntax error: %s\n", lua_tostring(L, -1)); + default: // success + return 1; + } +} + +int Filesystem::getLastModified(lua_State *L) +{ + const char *filename = luaL_checkstring(L, 1); + PHYSFS_sint64 time = PHYSFS_getLastModTime(filename); + if (time == -1) + { + lua_pushnil(L); + lua_pushstring(L, "Could not determine file modification date."); + return 2; + } + lua_pushnumber(L, static_cast<lua_Number>(time)); + return 1; +} + +} // physfs +} // filesystem +} // love
