diff --git a/include/Spectre/System/File.h b/include/Spectre/System/File.h index 210f12d..5adb044 100644 --- a/include/Spectre/System/File.h +++ b/include/Spectre/System/File.h @@ -2,12 +2,115 @@ #ifndef SPECTRE_SYSTEM_FILE_H #define SPECTRE_SYSTEM_FILE_H -#include +#include #include +#include -namespace sp { namespace file +namespace sp { + +class File { - std::vector read(const std::string& path); -} } +public : + + enum struct Access { + READ = 1 << 0, + WRITE = 1 << 1, + READ_WRITE = READ | WRITE, + }; + + // NOTE: These are ignored atm. as implementation uses stdio.h + enum OpenMode { + // Only open if file exists and + // in overwrite mode if write is enabled. + DEFAULT = 0, + // Create file if it does not exist (ignored in read-only mode) + CREATE = 1 << 0, + // If file exists any write calls will + // append to the file. (ignored in read-only mode) + APPEND = 1 << 1, + // If file exists it's content are discarded + // resulting in an empty file. (ignored in read-only mode) + TRUNCATE = 1 << 2 + }; + +// Methods. +public : + File(); + + // see `open()` + File(const std::string& filename, + Access access = Access::READ, + unsigned int mode = DEFAULT); + + ~File(); + + // Opens a file (default in read-only) + bool open(const std::string& filename, + Access access = Access::READ, + unsigned int mode = DEFAULT); + + // Returns true if the file is open, false otherwise. + bool isOpen() const; + + // Close the file. + void close(); + + std::string getErrorMessage() const; + + // Get the current position in the file. + size_t pos(); + + // Set the file stream position. + // if `from_end` is true. `offset` will be applied from the end of file, otherwise from beginning. + // Returns true if the position was updated, false otherwise. + bool set(long int pos = 0, bool from_end = false); + + // Move the current file stream position with `offset` bytes. + // This is equivalent to calling set(pos() + offset) + // Returns true if the position was updated, false otherwise. + bool seek(long int offset); + + // Get the size of the file (in bytes) + size_t size() const; + + // Read `size` data from file at the current position. + // Content is stored in `ptr` and must be alteast `size` bytes. + // Returns -1 if an error occured. Otherwice the number of bytes read is returned. + size_t read(void *ptr, size_t size); + + // Read all remaining data from file at the current position. Content is stored in `buffer`. + // Returns true if exactly all data could be read from file. false otherwise. + template + inline bool read(buffer_t& buffer) + { + size_t s = size(); + buffer.resize(s); + return read((void*) &buffer[0], s) == s; + } + + // Write data to file at the current position. + // Returns -1 if an error occured. Otherwice the number of bytes written is returned. + size_t write(const void *ptr, size_t size); + + // Write data to file at the current position. + // Returns true if exactly all data could be written from `buffer`. false otherwise. + template + inline bool write(const buffer_t& buffer) + { + return write((const void*) &buffer[0], buffer.size()) == buffer.size(); + } + + // Flush any buffered data to the file. + bool flush(); + +private : + + FILE *m_handle; + + // Error + mutable std::error_condition m_error; +}; + +} //namespace sp #endif /* SPECTRE_SYSTEM_FILE_H */ diff --git a/source/Graphics/ImageLoader.cpp b/source/Graphics/ImageLoader.cpp index 3f7cac5..fc44b3f 100644 --- a/source/Graphics/ImageLoader.cpp +++ b/source/Graphics/ImageLoader.cpp @@ -1,4 +1,5 @@ +#include #include #include #include @@ -18,22 +19,17 @@ #include #include -#include #include namespace sp { bool ImageLoader::loadFromFile(const char *filename, Image& img) { - FILE *fd = fopen(filename, "rb"); + File file(filename); - if (fd) { + if (file.isOpen()) { std::vector buf; - fseek(fd, 0, SEEK_END); - buf.resize(ftell(fd)); - rewind(fd); - fread(&buf[0], 1, buf.size(), fd); - fclose(fd); + file.read(buf); if (loadFromMemory(&buf[0], buf.size(), img) == false) { Log::warn("ImageLoader: could not load file '%s'. Reason: %s", @@ -44,7 +40,7 @@ bool ImageLoader::loadFromFile(const char *filename, Image& img) } Log::warn("ImageLoader: could not open file '%s'. Reason: %s", - filename, strerror(errno)); + filename, file.getErrorMessage().c_str()); return false; } @@ -91,10 +87,9 @@ bool ImageLoader::saveToFile(const Image& img, const char *filename) if (encoded_data.size() > 0) { - FILE *fd = fopen(filename, "wb"); - if (fd) { - fwrite(&encoded_data[0], 1, encoded_data.size(), fd); - fclose(fd); + File file(filename, File::Access::WRITE); + if (file.isOpen()) { + file.write(encoded_data); } return true; } diff --git a/source/Graphics/Shader.cpp b/source/Graphics/Shader.cpp index 47f1016..e24eb1f 100644 --- a/source/Graphics/Shader.cpp +++ b/source/Graphics/Shader.cpp @@ -1,6 +1,6 @@ -#include #include +#include #include #include @@ -30,26 +30,26 @@ const std::string& Shader::getName() const return m_name; } -bool Shader::loadFromFile(const std::string& file) +bool Shader::loadFromFile(const std::string& filename) { std::string src; - std::ifstream strm; + File file; // If this shader does not have a name, pick the filename. if (m_name.length() < 1) { - m_name = Path::getBasename(file); + m_name = Path::getBasename(filename); } - // Load file into memory. - strm.open(file.c_str(), std::fstream::in); - if (!strm.is_open()) { - m_error = "Can't open file: " + file; + if (!file.open(filename)) { + m_error = "Can't open file: " + filename; return false; } - src.assign(std::istreambuf_iterator(strm), std::istreambuf_iterator()); - - strm.close(); + // Load file into memory. + if (!file.read(src)) { + m_error = "Could not read file: " + filename; + return false; + } return loadFromMemory(src); } diff --git a/source/System/File.cpp b/source/System/File.cpp index f2d82d7..2cc24d3 100644 --- a/source/System/File.cpp +++ b/source/System/File.cpp @@ -1,22 +1,159 @@ #include #include +#include #include -namespace sp { namespace file +namespace sp { + +File::File() : +m_handle (NULL) { - std::vector read(const std::string& path) { +} - std::vector buf; - FILE *fd = fopen(path.c_str(), "rb"); +File::File(const std::string& filename, Access access, unsigned int mode) : +m_handle (NULL) +{ + open(filename, access, mode); +} - if (fd) { - fseek(fd, 0, SEEK_END); - buf.resize(ftell(fd)); - rewind(fd); - fread(&buf[0], 1, buf.size(), fd); - fclose(fd); +File::~File() +{ + close(); +} + +bool File::open(const std::string& filename, Access access, unsigned int mode) +{ + std::string m; + + close(); + + if (access == Access::READ) { + m = "rb"; + } else if (access == Access::WRITE) { + if (mode & TRUNCATE) { + m = "wb"; + } else { + // NOTE: this is append mode. but stdio does only + // have "a" and "w". and "w" will truncate. + // So we need to handle append with fseek() later. + m = "ab"; } - return buf; } -} } // namespace sp::file + // Read + Write + else if (mode & TRUNCATE) { + m = "wb+"; + } else if (mode & APPEND) { + m = "ab+"; + } else { + m = "rb+"; + } + + // HACK. stdio does not have a mode for strictly create new files + // independant of access (read/write). So we will fake it by open the + // file in append mode and close it. creating the file if did not exist. + if (mode & CREATE && (access == Access::WRITE || access == Access::READ_WRITE)) { + m_handle = fopen(filename.c_str(), "a"); + if (!m_handle) { + m_error.assign(errno, std::generic_category()); + return false; + } + fclose(m_handle); + } + + m_handle = fopen(filename.c_str(), m.c_str()); + + if (m_handle) { + + // Hack, if we opened the file in "a" mode, but did not + // have the append flag. seek to the beginning. + if (m == "ab" && mode & ~APPEND) { + fseek(m_handle, 0, SEEK_SET); + } + return true; + } + + m_error.assign(errno, std::generic_category()); + return false; +} + +bool File::isOpen() const +{ + if (m_handle == NULL) { + m_error = std::errc::bad_file_descriptor; + return false; + } + return true; +} + +void File::close() +{ + if (m_handle) { + fclose(m_handle); + m_handle = NULL; + } + m_error.clear(); +} + +std::string File::getErrorMessage() const +{ + return m_error.message(); +} + +size_t File::pos() +{ + if (isOpen()) { + return ftell(m_handle); + } + return -1; +} + +bool File::set(long int offset, bool from_end) +{ + if (isOpen()) { + return fseek(m_handle, offset, from_end ? SEEK_END : SEEK_SET) == 0; + } + return false; +} + +bool File::seek(long int offset) +{ + if (isOpen()) { + return fseek(m_handle, offset, SEEK_CUR) == 0; + } + return false; +} + +size_t File::size() const +{ + size_t s = 0; + while(::getc(m_handle) != EOF) s++; + ::rewind(m_handle); + return s; +} + +size_t File::read(void *ptr, size_t size) +{ + if (isOpen()) { + return fread(ptr, 1, size, m_handle); + } + return -1; +} + +size_t File::write(const void *ptr, size_t size) +{ + if (isOpen()) { + return fwrite(ptr, 1, size, m_handle); + } + return -1; +} + +bool File::flush() +{ + if (isOpen()) { + return fflush(m_handle) == 0; + } + return false; +} + +} // namespace sp