diff --git a/src/modules/filesystem/File.cpp b/src/modules/filesystem/File.cpp index 806cbd6d8..49f0b3414 100644 --- a/src/modules/filesystem/File.cpp +++ b/src/modules/filesystem/File.cpp @@ -34,11 +34,21 @@ bool File::getConstant(const char *in, Mode &out) return modes.find(in, out); } -bool File::getConstant(Mode in, const char *&out) +bool File::getConstant(Mode in, const char *&out) { return modes.find(in, out); } +bool File::getConstant(const char *in, BufferMode &out) +{ + return bufferModes.find(in, out); +} + +bool File::getConstant(BufferMode in, const char *&out) +{ + return bufferModes.find(in, out); +} + StringMap::Entry File::modeEntries[] = { {"c", File::CLOSED}, @@ -49,5 +59,14 @@ StringMap::Entry File::modeEntries[] = StringMap File::modes(File::modeEntries, sizeof(File::modeEntries)); +StringMap::Entry File::bufferModeEntries[] = +{ + {"none", File::BUFFER_NONE}, + {"line", File::BUFFER_LINE}, + {"full", File::BUFFER_FULL}, +}; + +StringMap File::bufferModes(File::bufferModeEntries, sizeof(File::bufferModeEntries)); + } // filesystem } // love diff --git a/src/modules/filesystem/File.h b/src/modules/filesystem/File.h index dacf3edd4..4c5bbdc75 100644 --- a/src/modules/filesystem/File.h +++ b/src/modules/filesystem/File.h @@ -56,6 +56,14 @@ public: MODE_MAX_ENUM }; + enum BufferMode + { + BUFFER_NONE, + BUFFER_LINE, + BUFFER_FULL, + BUFFER_MAX_ENUM + }; + /** * Used to indicate ALL data in a file. **/ @@ -128,6 +136,12 @@ public: **/ virtual bool write(const Data *data, int64 size = ALL) = 0; + /** + * Flushes the currently buffered file data to disk. Only applicable in + * write mode. + **/ + virtual bool flush() = 0; + /** * Checks whether we are currently at end-of-file. * @@ -150,6 +164,24 @@ public: **/ virtual bool seek(uint64 pos) = 0; + /** + * Sets the buffering mode for the file. When buffering is enabled, the file + * will not write to disk (or will pre-load data if in read mode) until the + * buffer's capacity is reached. + * In the BUFFER_LINE mode, the file will also write to disk if a newline is + * written. + * + * @param bufmode The buffer mode. + * @param size The size in bytes of the buffer. + **/ + virtual bool setBuffer(BufferMode bufmode, int64 size) = 0; + + /** + * @param[out] size The size in bytes of the buffer. + * @return The current buffer mode. + **/ + virtual BufferMode getBuffer(int64 &size) const = 0; + /** * Gets the current mode of the File. * @return The current mode of the File; CLOSED, READ, WRITE or APPEND. @@ -169,13 +201,19 @@ public: virtual std::string getExtension() const = 0; static bool getConstant(const char *in, Mode &out); - static bool getConstant(Mode in, const char *&out); + static bool getConstant(Mode in, const char *&out); + + static bool getConstant(const char *in, BufferMode &out); + static bool getConstant(BufferMode in, const char *&out); private: static StringMap::Entry modeEntries[]; static StringMap modes; + static StringMap::Entry bufferModeEntries[]; + static StringMap bufferModes; + }; // File } // filesystem diff --git a/src/modules/filesystem/physfs/File.cpp b/src/modules/filesystem/physfs/File.cpp index 08f1f1aa4..32713886a 100644 --- a/src/modules/filesystem/physfs/File.cpp +++ b/src/modules/filesystem/physfs/File.cpp @@ -39,7 +39,9 @@ extern bool hack_setupWriteDirectory(); File::File(const std::string &filename) : filename(filename) , file(0) - , mode(filesystem::File::CLOSED) + , mode(CLOSED) + , bufferMode(BUFFER_NONE) + , bufferSize(0) { } @@ -83,6 +85,13 @@ bool File::open(Mode mode) break; } + if (file != 0 && !setBuffer(bufferMode, bufferSize)) + { + // Revert to buffer defaults if we don't successfully set the buffer. + bufferMode = BUFFER_NONE; + bufferSize = 0; + } + return (file != 0); } @@ -169,7 +178,10 @@ int64 File::read(void *dst, int64 size) // Sadly, we'll have to clamp to 32 bits here size = (size > LOVE_UINT32_MAX) ? LOVE_UINT32_MAX : size; - int64 read = (int64)PHYSFS_read(file, dst, 1, (int) size); + if (size < 0) + throw love::Exception("Invalid read size."); + + int64 read = (int64)PHYSFS_read(file, dst, 1, (PHYSFS_uint32) size); return read; } @@ -182,13 +194,23 @@ bool File::write(const void *data, int64 size) // Another clamp, for the time being. size = (size > LOVE_UINT32_MAX) ? LOVE_UINT32_MAX : size; + if (size < 0) + throw love::Exception("Invalid write size."); + // Try to write. - int64 written = static_cast(PHYSFS_write(file, data, 1, (int) size)); + int64 written = static_cast(PHYSFS_write(file, data, 1, (PHYSFS_uint32) size)); // Check that correct amount of data was written. if (written != size) return false; + // Manually flush the buffer in BUFFER_LINE mode if we find a newline. + if (bufferMode == BUFFER_LINE && bufferSize > size) + { + if (memchr(data, '\n', (size_t) size) != NULL) + flush(); + } + return true; } @@ -197,6 +219,14 @@ bool File::write(const Data *data, int64 size) return write(data->getData(), (size == ALL) ? data->getSize() : size); } +bool File::flush() +{ + if (!file || (mode != WRITE && mode != APPEND)) + throw love::Exception("File is not opened for writing."); + + return PHYSFS_flush(file) != 0; +} + #ifdef LOVE_WINDOWS // MSVC doesn't like the 'this' keyword // well, we'll use 'that'. @@ -226,7 +256,7 @@ int64 File::tell() if (file == 0) return -1; - return (int64)PHYSFS_tell(file); + return (int64) PHYSFS_tell(file); } bool File::seek(uint64 pos) @@ -234,11 +264,56 @@ bool File::seek(uint64 pos) if (file == 0) return false; - if (!PHYSFS_seek(file, (PHYSFS_uint64)pos)) + if (!PHYSFS_seek(file, (PHYSFS_uint64) pos)) return false; return true; } +bool File::setBuffer(BufferMode bufmode, int64 size) +{ + // No negativity allowed! + if (size < 0) + return false; + + // If the file isn't open, we'll make sure the buffer values are set in + // File::open. + if (file == 0 || mode == CLOSED) + { + bufferMode = bufmode; + bufferSize = size; + return true; + } + + int ret = 1; + + switch (bufmode) + { + case BUFFER_NONE: + default: + ret = PHYSFS_setBuffer(file, 0); + size = 0; + break; + case BUFFER_LINE: + case BUFFER_FULL: + ret = PHYSFS_setBuffer(file, size); + break; + } + + if (ret == 0) + return false; + + bufferMode = bufmode; + bufferSize = size; + + return true; +} + +File::BufferMode File::getBuffer(int64 &size) const +{ + size = bufferSize; + return bufferMode; +} + std::string File::getFilename() const { return filename; diff --git a/src/modules/filesystem/physfs/File.h b/src/modules/filesystem/physfs/File.h index 084fce02d..ac72e2476 100644 --- a/src/modules/filesystem/physfs/File.h +++ b/src/modules/filesystem/physfs/File.h @@ -43,17 +43,6 @@ namespace physfs class File : public love::filesystem::File { -private: - - // filename - std::string filename; - - // PHYSFS File handle. - PHYSFS_file *file; - - // The current mode of the file. - Mode mode; - public: /** @@ -74,13 +63,30 @@ public: int64 read(void *dst, int64 size); bool write(const void *data, int64 size); bool write(const Data *data, int64 size = ALL); + bool flush(); bool eof(); int64 tell(); bool seek(uint64 pos); + bool setBuffer(BufferMode bufmode, int64 size); + BufferMode getBuffer(int64 &size) const; Mode getMode() const; std::string getFilename() const; std::string getExtension() const; +private: + + // filename + std::string filename; + + // PHYSFS File handle. + PHYSFS_File *file; + + // The current mode of the file. + Mode mode; + + BufferMode bufferMode; + int64 bufferSize; + }; // File } // physfs diff --git a/src/modules/filesystem/physfs/wrap_File.cpp b/src/modules/filesystem/physfs/wrap_File.cpp index 16f73fa89..4488adb2c 100644 --- a/src/modules/filesystem/physfs/wrap_File.cpp +++ b/src/modules/filesystem/physfs/wrap_File.cpp @@ -122,7 +122,7 @@ int w_File_read(lua_State *L) int w_File_write(lua_State *L) { File *file = luax_checkfile(L, 1); - bool result; + bool result = false; if (lua_isstring(L, 2)) { @@ -156,6 +156,22 @@ int w_File_write(lua_State *L) return 1; } +int w_File_flush(lua_State *L) +{ + File *file = luax_checkfile(L, 1); + bool success = false; + try + { + success = file->flush(); + } + catch (love::Exception &e) + { + return ioError(L, "%s", e.what()); + } + luax_pushboolean(L, success); + return 1; +} + int w_File_eof(lua_State *L) { File *file = luax_checkfile(L, 1); @@ -191,16 +207,10 @@ int w_File_seek(lua_State *L) int w_File_lines(lua_State *L) { - File *file; + File *file = luax_checkfile(L, 1); - if (luax_istype(L, 1, FILESYSTEM_FILE_T)) - { - file = luax_checktype(L, 1, "File", FILESYSTEM_FILE_T); - lua_pushnumber(L, 0); // File position. - luax_pushboolean(L, file->getMode() != File::CLOSED); // Save current file mode. - } - else - return luaL_error(L, "Expected File."); + lua_pushnumber(L, 0); // File position. + luax_pushboolean(L, file->getMode() != File::CLOSED); // Save current file mode. if (file->getMode() != File::READ) { @@ -222,6 +232,59 @@ int w_File_lines(lua_State *L) return 1; } +int w_File_setBuffer(lua_State *L) +{ + File *file = luax_checkfile(L, 1); + const char *str = luaL_checkstring(L, 2); + int64 size = (int64) luaL_optnumber(L, 3, 0.0); + + File::BufferMode bufmode; + if (!File::getConstant(str, bufmode)) + return luaL_error(L, "Incorrect file buffer mode: %s", str); + + bool success = false; + try + { + success = file->setBuffer(bufmode, size); + } + catch (love::Exception &e) + { + return ioError(L, "%s", e.what()); + } + + luax_pushboolean(L, success); + return 1; +} + +int w_File_getBuffer(lua_State *L) +{ + File *file = luax_checkfile(L, 1); + int64 size = 0; + File::BufferMode bufmode = file->getBuffer(size); + const char *str = 0; + + if (!File::getConstant(bufmode, str)) + return ioError(L, "Unknown file buffer mode."); + + lua_pushstring(L, str); + lua_pushnumber(L, (lua_Number) size); + return 2; +} + +int w_File_getMode(lua_State *L) +{ + File *file = luax_checkfile(L, 1); + + File::Mode mode = file->getMode(); + const char *str = 0; + + if (!File::getConstant(mode, str)) + return ioError(L, "Unknown file mode."); + + lua_pushstring(L, str); + return 1; +} + static const luaL_Reg functions[] = { { "getSize", w_File_getSize }, @@ -230,10 +293,14 @@ static const luaL_Reg functions[] = { "isOpen", w_File_isOpen }, { "read", w_File_read }, { "write", w_File_write }, + { "flush", w_File_flush }, { "eof", w_File_eof }, { "tell", w_File_tell }, { "seek", w_File_seek }, { "lines", w_File_lines }, + { "setBuffer", w_File_setBuffer }, + { "getBuffer", w_File_getBuffer }, + { "getMode", w_File_getMode }, { 0, 0 } }; diff --git a/src/modules/filesystem/physfs/wrap_File.h b/src/modules/filesystem/physfs/wrap_File.h index 3eed83f73..538fbd316 100644 --- a/src/modules/filesystem/physfs/wrap_File.h +++ b/src/modules/filesystem/physfs/wrap_File.h @@ -40,10 +40,14 @@ int w_File_close(lua_State *L); int w_File_isOpen(lua_State *L); int w_File_read(lua_State *L); int w_File_write(lua_State *L); +int w_File_flush(lua_State *L); int w_File_eof(lua_State *L); int w_File_tell(lua_State *L); int w_File_seek(lua_State *L); int w_File_lines(lua_State *L); +int w_File_setBuffer(lua_State *L); +int w_File_getBuffer(lua_State *L); +int w_File_getMode(lua_State *L); extern "C" int luaopen_file(lua_State *L); } // physfs