#pragma region File Handling typedef u32 FileMode; enum FileModeFlag { EFileMode_READ = bit( 0 ), EFileMode_WRITE = bit( 1 ), EFileMode_APPEND = bit( 2 ), EFileMode_RW = bit( 3 ), GEN_FILE_MODES = EFileMode_READ | EFileMode_WRITE | EFileMode_APPEND | EFileMode_RW, }; // NOTE: Only used internally and for the file operations enum SeekWhenceType { ESeekWhence_BEGIN = 0, ESeekWhence_CURRENT = 1, ESeekWhence_END = 2, }; enum FileError { EFileError_NONE, EFileError_INVALID, EFileError_INVALID_FILENAME, EFileError_EXISTS, EFileError_NOT_EXISTS, EFileError_PERMISSION, EFileError_TRUNCATION_FAILURE, EFileError_NOT_EMPTY, EFileError_NAME_TOO_LONG, EFileError_UNKNOWN, }; union FileDescriptor { void* p; sptr i; uptr u; }; typedef struct FileOperations FileOperations; #define GEN_FILE_OPEN_PROC( name ) FileError name( FileDescriptor* fd, FileOperations* ops, FileMode mode, char const* filename ) #define GEN_FILE_READ_AT_PROC( name ) b32 name( FileDescriptor fd, void* buffer, sw size, s64 offset, sw* bytes_read, b32 stop_at_newline ) #define GEN_FILE_WRITE_AT_PROC( name ) b32 name( FileDescriptor fd, void const* buffer, sw size, s64 offset, sw* bytes_written ) #define GEN_FILE_SEEK_PROC( name ) b32 name( FileDescriptor fd, s64 offset, SeekWhenceType whence, s64* new_offset ) #define GEN_FILE_CLOSE_PROC( name ) void name( FileDescriptor fd ) typedef GEN_FILE_OPEN_PROC( file_open_proc ); typedef GEN_FILE_READ_AT_PROC( FileReadProc ); typedef GEN_FILE_WRITE_AT_PROC( FileWriteProc ); typedef GEN_FILE_SEEK_PROC( FileSeekProc ); typedef GEN_FILE_CLOSE_PROC( FileCloseProc ); struct FileOperations { FileReadProc* read_at; FileWriteProc* write_at; FileSeekProc* seek; FileCloseProc* close; }; extern FileOperations const default_file_operations; typedef u64 FileTime; enum DirType { GEN_DIR_TYPE_FILE, GEN_DIR_TYPE_FOLDER, GEN_DIR_TYPE_UNKNOWN, }; struct DirInfo; struct DirEntry { char const* filename; struct DirInfo* dir_info; u8 type; }; struct DirInfo { char const* fullpath; DirEntry* entries; // zpl_array // Internals char** filenames; // zpl_array String buf; }; struct FileInfo { FileOperations ops; FileDescriptor fd; b32 is_temp; char const* filename; FileTime last_write_time; DirEntry* dir; }; enum FileStandardType { EFileStandard_INPUT, EFileStandard_OUTPUT, EFileStandard_ERROR, EFileStandard_COUNT, }; /** * Get standard file I/O. * @param std Check zpl_file_standard_type * @return File handle to standard I/O */ FileInfo* file_get_standard( FileStandardType std ); /** * Closes the file * @param file */ FileError file_close( FileInfo* file ); /** * Returns the currently opened file's name * @param file */ inline char const* file_name( FileInfo* file ) { return file->filename ? file->filename : ""; } /** * Opens a file * @param file * @param filename */ FileError file_open( FileInfo* file, char const* filename ); /** * Opens a file using a specified mode * @param file * @param mode Access mode to use * @param filename */ FileError file_open_mode( FileInfo* file, FileMode mode, char const* filename ); /** * Reads from a file * @param file * @param buffer Buffer to read to * @param size Size to read */ GEN_DEF_INLINE b32 file_read( FileInfo* file, void* buffer, sw size ); /** * Reads file at a specific offset * @param file * @param buffer Buffer to read to * @param size Size to read * @param offset Offset to read from * @param bytes_read How much data we've actually read */ GEN_DEF_INLINE b32 file_read_at( FileInfo* file, void* buffer, sw size, s64 offset ); /** * Reads file safely * @param file * @param buffer Buffer to read to * @param size Size to read * @param offset Offset to read from * @param bytes_read How much data we've actually read */ GEN_DEF_INLINE b32 file_read_at_check( FileInfo* file, void* buffer, sw size, s64 offset, sw* bytes_read ); struct FileContents { AllocatorInfo allocator; void* data; sw size; }; constexpr b32 zero_terminate = true; constexpr b32 no_zero_terminate = false; /** * Reads the whole file contents * @param a Allocator to use * @param zero_terminate End the read data with null terminator * @param filepath Path to the file * @return File contents data */ FileContents file_read_contents( AllocatorInfo a, b32 zero_terminate, char const* filepath ); /** * Returns a size of the file * @param file * @return File size */ s64 file_size( FileInfo* file ); /** * Seeks the file cursor from the beginning of file to a specific position * @param file * @param offset Offset to seek to */ GEN_DEF_INLINE s64 file_seek( FileInfo* file, s64 offset ); /** * Seeks the file cursor to the end of the file * @param file */ GEN_DEF_INLINE s64 file_seek_to_end( FileInfo* file ); /** * Returns the length from the beginning of the file we've read so far * @param file * @return Our current position in file */ GEN_DEF_INLINE s64 file_tell( FileInfo* file ); /** * Writes to a file * @param file * @param buffer Buffer to read from * @param size Size to read */ GEN_DEF_INLINE b32 file_write( FileInfo* file, void const* buffer, sw size ); /** * Writes to file at a specific offset * @param file * @param buffer Buffer to read from * @param size Size to write * @param offset Offset to write to * @param bytes_written How much data we've actually written */ GEN_DEF_INLINE b32 file_write_at( FileInfo* file, void const* buffer, sw size, s64 offset ); /** * Writes to file safely * @param file * @param buffer Buffer to read from * @param size Size to write * @param offset Offset to write to * @param bytes_written How much data we've actually written */ GEN_DEF_INLINE b32 file_write_at_check( FileInfo* file, void const* buffer, sw size, s64 offset, sw* bytes_written ); GEN_IMPL_INLINE s64 file_seek( FileInfo* f, s64 offset ) { s64 new_offset = 0; if ( ! f->ops.read_at ) f->ops = default_file_operations; f->ops.seek( f->fd, offset, ESeekWhence_BEGIN, &new_offset ); return new_offset; } GEN_IMPL_INLINE s64 file_seek_to_end( FileInfo* f ) { s64 new_offset = 0; if ( ! f->ops.read_at ) f->ops = default_file_operations; f->ops.seek( f->fd, 0, ESeekWhence_END, &new_offset ); return new_offset; } GEN_IMPL_INLINE s64 file_tell( FileInfo* f ) { s64 new_offset = 0; if ( ! f->ops.read_at ) f->ops = default_file_operations; f->ops.seek( f->fd, 0, ESeekWhence_CURRENT, &new_offset ); return new_offset; } GEN_IMPL_INLINE b32 file_read( FileInfo* f, void* buffer, sw size ) { s64 cur_offset = file_tell( f ); b32 result = file_read_at( f, buffer, size, file_tell( f ) ); file_seek( f, cur_offset + size ); return result; } GEN_IMPL_INLINE b32 file_read_at( FileInfo* f, void* buffer, sw size, s64 offset ) { return file_read_at_check( f, buffer, size, offset, NULL ); } GEN_IMPL_INLINE b32 file_read_at_check( FileInfo* f, void* buffer, sw size, s64 offset, sw* bytes_read ) { if ( ! f->ops.read_at ) f->ops = default_file_operations; return f->ops.read_at( f->fd, buffer, size, offset, bytes_read, false ); } GEN_IMPL_INLINE b32 file_write( FileInfo* f, void const* buffer, sw size ) { s64 cur_offset = file_tell( f ); b32 result = file_write_at( f, buffer, size, file_tell( f ) ); file_seek( f, cur_offset + size ); return result; } GEN_IMPL_INLINE b32 file_write_at( FileInfo* f, void const* buffer, sw size, s64 offset ) { return file_write_at_check( f, buffer, size, offset, NULL ); } GEN_IMPL_INLINE b32 file_write_at_check( FileInfo* f, void const* buffer, sw size, s64 offset, sw* bytes_written ) { if ( ! f->ops.read_at ) f->ops = default_file_operations; return f->ops.write_at( f->fd, buffer, size, offset, bytes_written ); } enum FileStreamFlags : u32 { /* Allows us to write to the buffer directly. Beware: you can not append a new data! */ EFileStream_WRITABLE = bit( 0 ), /* Clones the input buffer so you can write (zpl_file_write*) data into it. */ /* Since we work with a clone, the buffer size can dynamically grow as well. */ EFileStream_CLONE_WRITABLE = bit( 1 ), }; /** * Opens a new memory stream * @param file * @param allocator */ b8 file_stream_new( FileInfo* file, AllocatorInfo allocator ); /** * Opens a memory stream over an existing buffer * @param file * @param allocator * @param buffer Memory to create stream from * @param size Buffer's size * @param flags */ b8 file_stream_open( FileInfo* file, AllocatorInfo allocator, u8* buffer, sw size, FileStreamFlags flags ); /** * Retrieves the stream's underlying buffer and buffer size. * @param file memory stream * @param size (Optional) buffer size */ u8* file_stream_buf( FileInfo* file, sw* size ); extern FileOperations const memory_file_operations; #pragma endregion File Handling