123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563 |
- /**
- * Copyright (c) 2006-2010 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 <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)
- {
- }
- 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;
- }
- 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_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);
- 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;
- }
- 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%).
- if(!mkdir(save_path_relative.c_str()))
- {
- 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, 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];
- 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_HOM");
- if (!xdgdatahome)
- appdata = getUserDirectory();
- 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, file->getSize());
- // 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(lua_State * L)
- {
- File * file;
- if(lua_isstring(L, 1))
- {
- file = newFile(lua_tostring(L, 1));
- if(!file->open(File::READ))
- return luaL_error(L, "Could not open file %s.\n", lua_tostring(L, 1));
- lua_pop(L, 1);
- luax_newtype(L, "File", FILESYSTEM_FILE_T, file, false);
- lua_pushboolean(L, 1); // 1 = autoclose.
- }
- else
- return luaL_error(L, "Expected filename.");
- // Reset the file position.
- if(!file->seek(0))
- return luaL_error(L, "File does not appear to be open.\n");
- lua_pushcclosure(L, lines_i, 2);
- return 1;
- }
- int Filesystem::lines_i(lua_State * L)
- {
- // We're using a 1k buffer.
- const static int bufsize = 8;
- static char buf[bufsize];
- File * file = luax_checktype<File>(L, lua_upvalueindex(1), "File", FILESYSTEM_FILE_T);
- int close = (int)lua_tointeger(L, lua_upvalueindex(2));
- // Find the next newline.
- // pos must be at the start of the line we're trying to find.
- int pos = file->tell();
- int newline = -1;
- int totalread = 0;
- while(!file->eof())
- {
- int current = file->tell();
- int read = file->read(buf, bufsize);
- totalread += read;
- if(read < 0)
- return luaL_error(L, "Readline failed!");
- for(int i = 0;i<read;i++)
- {
- if(buf[i] == '\n')
- {
- newline = current+i;
- break;
- }
- }
- if(newline > 0)
- break;
- }
- // Special case for the last "line".
- if(newline <= 0 && file->eof() && totalread > 0)
- newline = pos + totalread;
- // We've got a newline.
- if(newline > 0)
- {
- // Ok, we've got a line.
- int linesize = (newline-pos);
- // Allocate memory for the string.
- char * str = new char[linesize];
- // Read it.
- file->seek(pos);
- if(file->read(str, linesize) == -1)
- return luaL_error(L, "Read error.");
- if(str[linesize-1]=='\r')
- linesize -= 1;
- lua_pushlstring(L, str, linesize);
- // Free the memory. Lua has a copy now.
- delete[] str;
- // Set the beginning of the next line.
- if(!file->eof())
- file->seek(newline+1);
- return 1;
- }
- if(close)
- {
- file->close();
- file->release();
- }
- // else: (newline <= 0)
- 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.");
- const char * filename = lua_tostring(L, -1);
- // The file must exist.
- if(!exists(filename))
- return luaL_error(L, "File %s does not exist.", filename);
- // Create the file.
- File * file = newFile(filename);
- // Get the data from the file.
- Data * data = file->read();
- int status = luaL_loadbuffer(L, (const char *)data->getData(), data->getSize(), filename);
- 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;
- }
- }
- } // physfs
- } // filesystem
- } // love
|