mirror of https://github.com/bsnes-emu/bsnes.git
262 lines
6.8 KiB
C++
262 lines
6.8 KiB
C++
|
#ifndef NALL_FILE_HPP
|
||
|
#define NALL_FILE_HPP
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#if !defined(_WIN32)
|
||
|
#include <unistd.h>
|
||
|
#else
|
||
|
#include <io.h>
|
||
|
#endif
|
||
|
|
||
|
#include <nall/stdint.hpp>
|
||
|
#include <nall/string.hpp>
|
||
|
#include <nall/utf8.hpp>
|
||
|
#include <nall/utility.hpp>
|
||
|
|
||
|
namespace nall {
|
||
|
inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) {
|
||
|
#if !defined(_WIN32)
|
||
|
return fopen(utf8_filename, mode);
|
||
|
#else
|
||
|
return _wfopen(utf16_t(utf8_filename), utf16_t(mode));
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
class file {
|
||
|
public:
|
||
|
enum FileMode { mode_read, mode_write, mode_readwrite, mode_writeread };
|
||
|
enum SeekMode { seek_absolute, seek_relative };
|
||
|
|
||
|
uint8_t read() {
|
||
|
if(!fp) return 0xff; //file not open
|
||
|
if(file_mode == mode_write) return 0xff; //reads not permitted
|
||
|
if(file_offset >= file_size) return 0xff; //cannot read past end of file
|
||
|
buffer_sync();
|
||
|
return buffer[(file_offset++) & buffer_mask];
|
||
|
}
|
||
|
|
||
|
uintmax_t readl(unsigned length = 1) {
|
||
|
uintmax_t data = 0;
|
||
|
for(int i = 0; i < length; i++) {
|
||
|
data |= (uintmax_t)read() << (i << 3);
|
||
|
}
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
uintmax_t readm(unsigned length = 1) {
|
||
|
uintmax_t data = 0;
|
||
|
while(length--) {
|
||
|
data <<= 8;
|
||
|
data |= read();
|
||
|
}
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
void read(uint8_t *buffer, unsigned length) {
|
||
|
while(length--) *buffer++ = read();
|
||
|
}
|
||
|
|
||
|
void write(uint8_t data) {
|
||
|
if(!fp) return; //file not open
|
||
|
if(file_mode == mode_read) return; //writes not permitted
|
||
|
buffer_sync();
|
||
|
buffer[(file_offset++) & buffer_mask] = data;
|
||
|
buffer_dirty = true;
|
||
|
if(file_offset > file_size) file_size = file_offset;
|
||
|
}
|
||
|
|
||
|
void writel(uintmax_t data, unsigned length = 1) {
|
||
|
while(length--) {
|
||
|
write(data);
|
||
|
data >>= 8;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void writem(uintmax_t data, unsigned length = 1) {
|
||
|
for(int i = length - 1; i >= 0; i--) {
|
||
|
write(data >> (i << 3));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void write(const uint8_t *buffer, unsigned length) {
|
||
|
while(length--) write(*buffer++);
|
||
|
}
|
||
|
|
||
|
template<typename... Args> void print(Args... args) {
|
||
|
string data(args...);
|
||
|
const char *p = data;
|
||
|
while(*p) write(*p++);
|
||
|
}
|
||
|
|
||
|
void flush() {
|
||
|
buffer_flush();
|
||
|
fflush(fp);
|
||
|
}
|
||
|
|
||
|
void seek(int offset, SeekMode mode = seek_absolute) {
|
||
|
if(!fp) return; //file not open
|
||
|
buffer_flush();
|
||
|
|
||
|
uintmax_t req_offset = file_offset;
|
||
|
switch(mode) {
|
||
|
case seek_absolute: req_offset = offset; break;
|
||
|
case seek_relative: req_offset += offset; break;
|
||
|
}
|
||
|
|
||
|
if(req_offset < 0) req_offset = 0; //cannot seek before start of file
|
||
|
if(req_offset > file_size) {
|
||
|
if(file_mode == mode_read) { //cannot seek past end of file
|
||
|
req_offset = file_size;
|
||
|
} else { //pad file to requested location
|
||
|
file_offset = file_size;
|
||
|
while(file_size < req_offset) write(0x00);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
file_offset = req_offset;
|
||
|
}
|
||
|
|
||
|
int offset() {
|
||
|
if(!fp) return -1; //file not open
|
||
|
return file_offset;
|
||
|
}
|
||
|
|
||
|
int size() {
|
||
|
if(!fp) return -1; //file not open
|
||
|
return file_size;
|
||
|
}
|
||
|
|
||
|
bool truncate(unsigned size) {
|
||
|
if(!fp) return false; //file not open
|
||
|
#if !defined(_WIN32)
|
||
|
return ftruncate(fileno(fp), size) == 0;
|
||
|
#else
|
||
|
return _chsize(fileno(fp), size) == 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
bool end() {
|
||
|
if(!fp) return true; //file not open
|
||
|
return file_offset >= file_size;
|
||
|
}
|
||
|
|
||
|
static bool exists(const char *fn) {
|
||
|
#if !defined(_WIN32)
|
||
|
FILE *fp = fopen(fn, "rb");
|
||
|
#else
|
||
|
FILE *fp = _wfopen(utf16_t(fn), L"rb");
|
||
|
#endif
|
||
|
if(fp) {
|
||
|
fclose(fp);
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static unsigned size(const char *fn) {
|
||
|
#if !defined(_WIN32)
|
||
|
FILE *fp = fopen(fn, "rb");
|
||
|
#else
|
||
|
FILE *fp = _wfopen(utf16_t(fn), L"rb");
|
||
|
#endif
|
||
|
unsigned filesize = 0;
|
||
|
if(fp) {
|
||
|
fseek(fp, 0, SEEK_END);
|
||
|
filesize = ftell(fp);
|
||
|
fclose(fp);
|
||
|
}
|
||
|
return filesize;
|
||
|
}
|
||
|
|
||
|
bool open() {
|
||
|
return fp;
|
||
|
}
|
||
|
|
||
|
bool open(const char *fn, FileMode mode) {
|
||
|
if(fp) return false;
|
||
|
|
||
|
switch(file_mode = mode) {
|
||
|
#if !defined(_WIN32)
|
||
|
case mode_read: fp = fopen(fn, "rb"); break;
|
||
|
case mode_write: fp = fopen(fn, "wb+"); break; //need read permission for buffering
|
||
|
case mode_readwrite: fp = fopen(fn, "rb+"); break;
|
||
|
case mode_writeread: fp = fopen(fn, "wb+"); break;
|
||
|
#else
|
||
|
case mode_read: fp = _wfopen(utf16_t(fn), L"rb"); break;
|
||
|
case mode_write: fp = _wfopen(utf16_t(fn), L"wb+"); break;
|
||
|
case mode_readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break;
|
||
|
case mode_writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break;
|
||
|
#endif
|
||
|
}
|
||
|
if(!fp) return false;
|
||
|
buffer_offset = -1; //invalidate buffer
|
||
|
file_offset = 0;
|
||
|
fseek(fp, 0, SEEK_END);
|
||
|
file_size = ftell(fp);
|
||
|
fseek(fp, 0, SEEK_SET);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void close() {
|
||
|
if(!fp) return;
|
||
|
buffer_flush();
|
||
|
fclose(fp);
|
||
|
fp = 0;
|
||
|
}
|
||
|
|
||
|
file() {
|
||
|
memset(buffer, 0, sizeof buffer);
|
||
|
buffer_offset = -1;
|
||
|
buffer_dirty = false;
|
||
|
fp = 0;
|
||
|
file_offset = 0;
|
||
|
file_size = 0;
|
||
|
file_mode = mode_read;
|
||
|
}
|
||
|
|
||
|
~file() {
|
||
|
close();
|
||
|
}
|
||
|
|
||
|
file& operator=(const file&) = delete;
|
||
|
file(const file&) = delete;
|
||
|
|
||
|
private:
|
||
|
enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 };
|
||
|
char buffer[buffer_size];
|
||
|
int buffer_offset;
|
||
|
bool buffer_dirty;
|
||
|
FILE *fp;
|
||
|
unsigned file_offset;
|
||
|
unsigned file_size;
|
||
|
FileMode file_mode;
|
||
|
|
||
|
void buffer_sync() {
|
||
|
if(!fp) return; //file not open
|
||
|
if(buffer_offset != (file_offset & ~buffer_mask)) {
|
||
|
buffer_flush();
|
||
|
buffer_offset = file_offset & ~buffer_mask;
|
||
|
fseek(fp, buffer_offset, SEEK_SET);
|
||
|
unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask);
|
||
|
if(length) unsigned unused = fread(buffer, 1, length, fp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void buffer_flush() {
|
||
|
if(!fp) return; //file not open
|
||
|
if(file_mode == mode_read) return; //buffer cannot be written to
|
||
|
if(buffer_offset < 0) return; //buffer unused
|
||
|
if(buffer_dirty == false) return; //buffer unmodified since read
|
||
|
fseek(fp, buffer_offset, SEEK_SET);
|
||
|
unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask);
|
||
|
if(length) unsigned unused = fwrite(buffer, 1, length, fp);
|
||
|
buffer_offset = -1; //invalidate buffer
|
||
|
buffer_dirty = false;
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
#endif
|