466 lines
14 KiB
C++
466 lines
14 KiB
C++
// =====================================================================================================================
|
|
// fennec, a free and open source game engine
|
|
// Copyright © 2025 Medusa Slockbower
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
// =====================================================================================================================
|
|
|
|
#ifndef FENNEC_FILESYSTEM_FILE_H
|
|
#define FENNEC_FILESYSTEM_FILE_H
|
|
|
|
#include <fennec/filesystem/path.h>
|
|
#include <fennec/format/format.h>
|
|
|
|
#include <fennec/string/cstring.h>
|
|
#include <fennec/string/string.h>
|
|
#include <fennec/string/wstring.h>
|
|
|
|
namespace fennec
|
|
{
|
|
|
|
///
|
|
/// \brief Mode flags for opening a file
|
|
///
|
|
/// fmode_binary and fmode_wide are independent of the other modes
|
|
///
|
|
/// \details Valid Flag Combinations
|
|
/// <table width="100%" class="fieldtable" id="table_fennec_LANGPROC_io_fmode">
|
|
/// <tr><th style="vertical-align: top">Flags
|
|
/// <th style="vertical-align: top">Description
|
|
///
|
|
/// <tr><td style="vertical-align: top">\f$read\f$
|
|
/// <td style="vertical-align: top">Opens file as read-only, reading from start
|
|
///
|
|
/// <tr><td style="vertical-align: top">\f$write\f$
|
|
/// <td style="vertical-align: top">Opens file as write-only, writing to end
|
|
///
|
|
/// <tr><td style="vertical-align: top">`read | write`
|
|
/// <td style="vertical-align: top">Opens file as read-write, reading from start
|
|
///
|
|
/// <tr><td style="vertical-align: top">`write | trunc`
|
|
/// <td style="vertical-align: top">Opens file as write-only, destroying contents
|
|
///
|
|
/// <tr><td style="vertical-align: top">`read | write | trunc`
|
|
/// <td style="vertical-align: top">Opens file as read-write, destroying contents
|
|
/// </table>
|
|
enum fmode_ : uint8_t
|
|
{
|
|
fmode_read = 0b00000001 ///< Opens file for reading
|
|
, fmode_write = 0b00000010 ///< Opens file for writing
|
|
, fmode_trunc = 0b00000100 ///< Contents of the file will be destroyed, only compatible with write enabled modes
|
|
, fmode_exclusive = 0b00001000 ///< Generates an error if the opened file is not empty
|
|
, fmode_binary = 0b00010000 ///< Open in binary mode
|
|
, fmode_wide = 0b00100000 ///< Opens a file in wide mode
|
|
};
|
|
|
|
///
|
|
/// \brief Structure for handling streams of data
|
|
///
|
|
/// \details operations, when errored, will return a corresponding error.
|
|
/// Use file::get_error() to check if an error is present and return a corresponding string.
|
|
/// Use file::clear_error() to clear the errored state.
|
|
/// Some operations, specifically file::rename() and file::copy().
|
|
class file
|
|
{
|
|
public:
|
|
/// \brief value of an invalid position
|
|
static constexpr size_t npos = -1;
|
|
|
|
///
|
|
/// \brief Check if the provided mode bitflags are a valid combination
|
|
/// \param mode the bitfield
|
|
/// \returns true if the combination of flags is valid, false otherwise
|
|
static constexpr bool is_valid(uint8_t mode) {
|
|
const bool t = mode & fmode_trunc;
|
|
const bool x = mode & fmode_exclusive;
|
|
const bool w = mode & fmode_write;
|
|
|
|
// when x is true, t must be true
|
|
// when t is true, w must be true
|
|
return (t && x && w)
|
|
|| (t && w)
|
|
|| !(t || x);
|
|
}
|
|
|
|
///
|
|
/// \returns the c stdout
|
|
static file& cout();
|
|
|
|
///
|
|
/// \returns the c stdin
|
|
static file& cin();
|
|
|
|
///
|
|
/// \returns the c stderr
|
|
static file& cerr();
|
|
|
|
///
|
|
/// \brief default constructor, initializes an empty stream
|
|
file();
|
|
|
|
///
|
|
/// \brief path constructor, initializes a stream pointing to \f$mode\f$ opened with \f$mode\f$
|
|
/// \param path the path of the file
|
|
/// \param mode the mode to open with
|
|
file(const cstring& path, uint8_t mode)
|
|
: file() {
|
|
open(path, mode);
|
|
}
|
|
|
|
///
|
|
/// \brief path constructor, initializes a stream pointing to \f$mode\f$ opened with \f$mode\f$
|
|
/// \param path the path of the file
|
|
/// \param mode the mode to open with
|
|
file(const string& path, uint8_t mode)
|
|
: file() {
|
|
open(path, mode);
|
|
}
|
|
|
|
///
|
|
/// \brief path constructor, initializes a stream pointing to \f$mode\f$ opened with \f$mode\f$
|
|
/// \param path the path of the file
|
|
/// \param mode the mode to open with
|
|
file(const path& path, uint8_t mode)
|
|
: file() {
|
|
open(path, mode);
|
|
}
|
|
|
|
///
|
|
/// \brief default destructor, cleans up an open stream
|
|
~file();
|
|
|
|
///
|
|
/// \brief move constructor
|
|
/// \param file the stream to take ownership of
|
|
file(file&& file) noexcept;
|
|
|
|
///
|
|
/// \brief move assignment
|
|
/// \param file the stream to take ownership of
|
|
file& operator=(file&& file) noexcept;
|
|
|
|
// don't allow copying streams
|
|
file(const file&) = delete;
|
|
|
|
|
|
// Properties ==========================================================================================================
|
|
|
|
///
|
|
/// \returns the path the stream
|
|
const path& get_path() const {
|
|
return _path;
|
|
}
|
|
|
|
///
|
|
/// \returns the mode of the stream
|
|
uint8_t mode() const {
|
|
return _mode;
|
|
}
|
|
|
|
///
|
|
/// \returns true if there is a valid, open stream.
|
|
bool is_open() const {
|
|
return _handle != nullptr;
|
|
}
|
|
|
|
|
|
// File Access =========================================================================================================
|
|
|
|
///
|
|
/// \brief open a file
|
|
/// \param path the path to the file
|
|
/// \param mode the mode flags to open the file with
|
|
/// \returns false on success, true on error
|
|
bool open(const cstring& path, uint8_t mode);
|
|
|
|
///
|
|
/// \brief open a file
|
|
/// \param path the path to the file
|
|
/// \param mode the mode flags to open the file with
|
|
/// \returns false on success, true on error
|
|
bool open(const string& path, uint8_t mode);
|
|
|
|
///
|
|
/// \brief open a file
|
|
/// \param path the path to the file
|
|
/// \param mode the mode flags to open the file with
|
|
/// \returns false on success, true on error
|
|
bool open(const path& path, uint8_t mode);
|
|
|
|
///
|
|
/// \brief close a stream
|
|
/// \returns false on success, true on error
|
|
bool close();
|
|
|
|
///
|
|
/// \brief commit the streams buffer to the file
|
|
/// \returns false on success, true on error
|
|
bool commit();
|
|
|
|
|
|
// File Operations =====================================================================================================
|
|
|
|
///
|
|
/// \brief closes the stream and erases the file
|
|
/// \returns false on success, true on error
|
|
bool erase();
|
|
|
|
///
|
|
/// \brief rebinds the stream, copying contents to path, and erasing the old file
|
|
/// \param path the new path
|
|
/// \returns false on success, true on error
|
|
///
|
|
/// \details attempts to open a write-only stream at path,
|
|
/// attempts to reopen this file as read-only,
|
|
/// copies the contents of this file to the new stream,
|
|
/// reopen the new stream with the flags of this file and binds to it,
|
|
/// closes the old file.
|
|
bool rename(const cstring& path);
|
|
|
|
///
|
|
/// \brief rebinds the stream, copying contents to path, and erasing the old file
|
|
/// \param path the new path
|
|
/// \returns false on success, true on error
|
|
///
|
|
/// \details attempts to open a write-only stream at path,
|
|
/// attempts to reopen this file as read-only,
|
|
/// copies the contents of this file to the new stream,
|
|
/// reopen the new stream with the flags of this file and binds to it,
|
|
/// closes the old file.
|
|
bool rename(const string& path);
|
|
|
|
///
|
|
/// \brief rebinds the stream, copying contents to path, and erasing the old file
|
|
/// \param path the new path
|
|
/// \returns false on success, true on error
|
|
///
|
|
/// \details attempts to open a write-only stream at path,
|
|
/// attempts to reopen this file as read-only,
|
|
/// copies the contents of this file to the new stream,
|
|
/// reopen the new stream with the flags of this file and binds to it,
|
|
/// closes the old file.
|
|
bool rename(const path& path);
|
|
|
|
///
|
|
/// \brief copies the contents of this file to path.
|
|
/// \param path the path to copy to
|
|
/// \returns a file at the new path with the copied contents
|
|
file copy(const cstring& path);
|
|
|
|
///
|
|
/// \brief copies the contents of this file to path.
|
|
/// \param path the path to copy to
|
|
/// \returns a file at the new path with the copied contents
|
|
file copy(const string& path);
|
|
|
|
///
|
|
/// \brief copies the contents of this file to path.
|
|
/// \param path the path to copy to
|
|
/// \returns a file at the new path with the copied contents
|
|
file copy(const path& path);
|
|
|
|
|
|
// File Positioning ====================================================================================================
|
|
|
|
///
|
|
/// \returns the position index in the stream
|
|
size_t get_pos() const;
|
|
|
|
///
|
|
/// \param i the new index to move to
|
|
/// \returns \f$false\f$ on success, \f$true\f$ otherwise
|
|
bool set_pos(size_t i);
|
|
|
|
///
|
|
/// \brief return to the start of the stream
|
|
/// \returns \f$false\f$ on success, \f$true\f$ otherwise
|
|
bool rewind();
|
|
|
|
///
|
|
/// \returns \f$true\f$ if the stream has reached the end of the file, \f$false\f$ otherwise
|
|
bool eof() const;
|
|
|
|
|
|
// Binary Read Operations ==============================================================================================
|
|
|
|
///
|
|
/// \brief binary read
|
|
/// \param data the buffer to write to
|
|
/// \param size the size of each object in bytes
|
|
/// \param n the number of objects to read
|
|
/// \returns the number of objects successfully read
|
|
size_t read(void* data, size_t size, size_t n);
|
|
|
|
///
|
|
/// \brief type read
|
|
/// \tparam T the type to read
|
|
/// \param data the buffer to write to
|
|
/// \param n the number of objects to read
|
|
/// \returns the number of objects successfully read
|
|
template<typename T>
|
|
size_t read(T* data, size_t n) {
|
|
return read(static_cast<void*>(data), sizeof(T), n);
|
|
}
|
|
|
|
///
|
|
/// \brief type read
|
|
/// \tparam T the type to read
|
|
/// \tparam n the number of objects to read
|
|
/// \param data the buffer to write to
|
|
/// \returns the number of objects successfully read
|
|
template<typename T, size_t n>
|
|
size_t read(T (&data)[n]) {
|
|
return read(static_cast<void*>(data), sizeof(T), n);
|
|
}
|
|
|
|
|
|
// Binary Write Operations =============================================================================================
|
|
|
|
///
|
|
/// \brief put a character at the current position in the stream
|
|
/// \param c the character to put
|
|
/// \returns \f$false\f$ on success, \f$true\f$ otherwise
|
|
bool putc(char c);
|
|
|
|
///
|
|
/// \brief put a wide character at the current position in the stream
|
|
/// \param c the character to put
|
|
/// \returns \f$false\f$ on success, \f$true\f$ otherwise
|
|
bool putwc(wchar_t c);
|
|
|
|
///
|
|
/// \brief write a buffer to at the current position in the stream
|
|
/// \param data the buffer to read from
|
|
/// \param size the size of each object in bytes
|
|
/// \param n the number of objects to write
|
|
/// \returns the number of objects successfully written
|
|
size_t write(const void* data, size_t size, size_t n);
|
|
|
|
///
|
|
/// \brief write a character buffer to at the current position in the stream
|
|
/// \tparam n the number of characters to write
|
|
/// \param data the buffer to read from
|
|
/// \returns the number of objects successfully written
|
|
template<size_t n>
|
|
size_t write(const char (&data)[n]) {
|
|
return write(data, sizeof(char), n - 1);
|
|
}
|
|
|
|
///
|
|
/// \brief write a wide character buffer to at the current position in the stream
|
|
/// \tparam n the number of characters to write
|
|
/// \param data the buffer to read from
|
|
/// \returns the number of objects successfully written
|
|
template<size_t n>
|
|
size_t write(const wchar_t (&data)[n]) {
|
|
return write(data, sizeof(wchar_t), n - 1);
|
|
}
|
|
|
|
///
|
|
/// \brief write a buffer to at the current position in the stream
|
|
/// \tparam T the object type to write
|
|
/// \param data the buffer to read from
|
|
/// \param n the number of objects to write
|
|
/// \returns the number of objects successfully written
|
|
template<typename T>
|
|
size_t write(const T* data, size_t n) {
|
|
return write(static_cast<const void*>(data), sizeof(T), n);
|
|
}
|
|
|
|
///
|
|
/// \brief write a buffer to at the current position in the stream
|
|
/// \tparam T the object type to write
|
|
/// \tparam n the number of objects to write
|
|
/// \param data the buffer to read from
|
|
/// \returns the number of objects successfully written
|
|
template<typename T, size_t n>
|
|
size_t write(const T (&data)[n]) {
|
|
return write(static_cast<const void*>(data), sizeof(T), n);
|
|
}
|
|
|
|
|
|
// Read Operations =====================================================================================================
|
|
|
|
///
|
|
/// \returns the character read at the current position in the stream
|
|
char getc();
|
|
|
|
///
|
|
/// \returns the wide character read at the current position in the stream
|
|
wchar_t getwc();
|
|
|
|
///
|
|
/// \returns a string containing all characters from the current position in the stream to the next newline character
|
|
string getline();
|
|
|
|
///
|
|
/// \returns a string containing all characters from the current position in the stream to the next newline character
|
|
wstring getwline();
|
|
|
|
|
|
// Printing Operations =================================================================================================
|
|
|
|
///
|
|
/// \param str the string to print
|
|
void print(const cstring& str);
|
|
|
|
///
|
|
/// \brief print a string to the stream
|
|
/// \param str the string to print
|
|
void print(const string& str);
|
|
|
|
///
|
|
/// \param str the string to print
|
|
void println(const cstring& str);
|
|
|
|
///
|
|
/// \brief print a string to the stream followed by a newline character
|
|
/// \param str the string to print
|
|
void println(const string& str);
|
|
|
|
///
|
|
/// \brief print a formatted string to the stream
|
|
/// \tparam ArgsT the argument types
|
|
/// \param str the format string
|
|
/// \param args the argument values
|
|
template<typename...ArgsT>
|
|
void printf(const cstring& str, ArgsT&&...args) {
|
|
string fmt = fennec::format(str, fennec::forward<ArgsT>(args)...);
|
|
this->print(cstring(fmt.cstr(), fmt.length()));
|
|
}
|
|
|
|
|
|
|
|
|
|
// Error Handling ======================================================================================================
|
|
|
|
///
|
|
/// \returns the current error state of the file
|
|
const char* get_error() const { return _error; }
|
|
|
|
///
|
|
/// \brief clears the errored state
|
|
void clear_error() { _error = nullptr; }
|
|
|
|
private:
|
|
FILE* _handle;
|
|
path _path;
|
|
uint8_t _mode;
|
|
char* _error;
|
|
};
|
|
|
|
}
|
|
|
|
#endif // FENNEC_FILESYSTEM_FILE_H
|