2016-03-20 17:00:00 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* REminiscence - Flashback interpreter
|
2019-10-27 17:00:00 +01:00
|
|
|
* Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net)
|
2015-08-02 18:00:00 +02:00
|
|
|
*/
|
|
|
|
|
2016-03-20 17:00:00 +01:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include "file.h"
|
2015-08-02 18:00:00 +02:00
|
|
|
#include "fs.h"
|
2016-03-20 17:00:00 +01:00
|
|
|
#include "util.h"
|
2015-08-02 18:00:00 +02:00
|
|
|
#ifdef USE_ZLIB
|
|
|
|
#include "zlib.h"
|
|
|
|
#endif
|
2017-06-07 18:00:00 +02:00
|
|
|
#ifdef USE_RWOPS
|
|
|
|
#include <SDL_filesystem.h>
|
|
|
|
#include <SDL_rwops.h>
|
|
|
|
#endif
|
2015-08-02 18:00:00 +02:00
|
|
|
|
|
|
|
struct File_impl {
|
|
|
|
bool _ioErr;
|
|
|
|
File_impl() : _ioErr(false) {}
|
|
|
|
virtual ~File_impl() {}
|
|
|
|
virtual bool open(const char *path, const char *mode) = 0;
|
|
|
|
virtual void close() = 0;
|
|
|
|
virtual uint32_t size() = 0;
|
|
|
|
virtual void seek(int32_t off) = 0;
|
|
|
|
virtual uint32_t read(void *ptr, uint32_t len) = 0;
|
2017-11-03 17:00:00 +01:00
|
|
|
virtual uint32_t write(const void *ptr, uint32_t len) = 0;
|
2015-08-02 18:00:00 +02:00
|
|
|
};
|
|
|
|
|
2016-05-09 18:00:00 +02:00
|
|
|
struct StdioFile : File_impl {
|
2015-08-02 18:00:00 +02:00
|
|
|
FILE *_fp;
|
2016-05-09 18:00:00 +02:00
|
|
|
StdioFile() : _fp(0) {}
|
2015-08-02 18:00:00 +02:00
|
|
|
bool open(const char *path, const char *mode) {
|
|
|
|
_ioErr = false;
|
|
|
|
_fp = fopen(path, mode);
|
|
|
|
return (_fp != 0);
|
|
|
|
}
|
|
|
|
void close() {
|
|
|
|
if (_fp) {
|
|
|
|
fclose(_fp);
|
|
|
|
_fp = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
uint32_t size() {
|
|
|
|
uint32_t sz = 0;
|
|
|
|
if (_fp) {
|
|
|
|
int pos = ftell(_fp);
|
|
|
|
fseek(_fp, 0, SEEK_END);
|
|
|
|
sz = ftell(_fp);
|
|
|
|
fseek(_fp, pos, SEEK_SET);
|
|
|
|
}
|
|
|
|
return sz;
|
|
|
|
}
|
|
|
|
void seek(int32_t off) {
|
|
|
|
if (_fp) {
|
|
|
|
fseek(_fp, off, SEEK_SET);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
uint32_t read(void *ptr, uint32_t len) {
|
|
|
|
if (_fp) {
|
|
|
|
uint32_t r = fread(ptr, 1, len, _fp);
|
|
|
|
if (r != len) {
|
|
|
|
_ioErr = true;
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2017-11-03 17:00:00 +01:00
|
|
|
uint32_t write(const void *ptr, uint32_t len) {
|
2015-08-02 18:00:00 +02:00
|
|
|
if (_fp) {
|
|
|
|
uint32_t r = fwrite(ptr, 1, len, _fp);
|
|
|
|
if (r != len) {
|
|
|
|
_ioErr = true;
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef USE_ZLIB
|
2016-05-09 18:00:00 +02:00
|
|
|
struct GzipFile : File_impl {
|
2015-08-02 18:00:00 +02:00
|
|
|
gzFile _fp;
|
2016-05-09 18:00:00 +02:00
|
|
|
GzipFile() : _fp(0) {}
|
2015-08-02 18:00:00 +02:00
|
|
|
bool open(const char *path, const char *mode) {
|
|
|
|
_ioErr = false;
|
|
|
|
_fp = gzopen(path, mode);
|
|
|
|
return (_fp != 0);
|
|
|
|
}
|
|
|
|
void close() {
|
|
|
|
if (_fp) {
|
|
|
|
gzclose(_fp);
|
|
|
|
_fp = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
uint32_t size() {
|
|
|
|
uint32_t sz = 0;
|
|
|
|
if (_fp) {
|
|
|
|
int pos = gztell(_fp);
|
|
|
|
gzseek(_fp, 0, SEEK_END);
|
|
|
|
sz = gztell(_fp);
|
|
|
|
gzseek(_fp, pos, SEEK_SET);
|
|
|
|
}
|
|
|
|
return sz;
|
|
|
|
}
|
|
|
|
void seek(int32_t off) {
|
|
|
|
if (_fp) {
|
|
|
|
gzseek(_fp, off, SEEK_SET);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
uint32_t read(void *ptr, uint32_t len) {
|
|
|
|
if (_fp) {
|
|
|
|
uint32_t r = gzread(_fp, ptr, len);
|
|
|
|
if (r != len) {
|
|
|
|
_ioErr = true;
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2017-11-03 17:00:00 +01:00
|
|
|
uint32_t write(const void *ptr, uint32_t len) {
|
2015-08-02 18:00:00 +02:00
|
|
|
if (_fp) {
|
|
|
|
uint32_t r = gzwrite(_fp, ptr, len);
|
|
|
|
if (r != len) {
|
|
|
|
_ioErr = true;
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2017-06-07 18:00:00 +02:00
|
|
|
#ifdef USE_RWOPS
|
|
|
|
struct AssetFile: File_impl {
|
|
|
|
SDL_RWops *_rw;
|
|
|
|
AssetFile() : _rw(0) {}
|
2017-11-03 17:00:00 +01:00
|
|
|
bool prefixedOpen(const char *prefix, const char *name) {
|
|
|
|
char path[MAXPATHLEN];
|
|
|
|
snprintf(path, sizeof(path), "%s%s", prefix, name);
|
2017-06-07 18:00:00 +02:00
|
|
|
_rw = SDL_RWFromFile(path, "rb");
|
|
|
|
if (!_rw) {
|
|
|
|
// try uppercase
|
|
|
|
char fixedPath[MAXPATHLEN];
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
for (; path[i] && i < MAXPATHLEN - 1; ++i) {
|
|
|
|
fixedPath[i] = path[i];
|
2017-11-03 17:00:00 +01:00
|
|
|
if (i < strlen(prefix)) {
|
|
|
|
continue;
|
|
|
|
}
|
2017-06-07 18:00:00 +02:00
|
|
|
if (fixedPath[i] >= 'a' && fixedPath[i] <= 'z') {
|
|
|
|
fixedPath[i] += 'A' - 'a';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fixedPath[i] = 0;
|
|
|
|
}
|
|
|
|
_rw = SDL_RWFromFile(fixedPath, "rb");
|
|
|
|
}
|
|
|
|
return _rw != 0;
|
|
|
|
}
|
2017-11-03 17:00:00 +01:00
|
|
|
bool open(const char *path, const char *mode) {
|
|
|
|
_ioErr = false;
|
|
|
|
return prefixedOpen("", path) || prefixedOpen("/sdcard/flashback/", path);
|
|
|
|
}
|
2017-06-07 18:00:00 +02:00
|
|
|
void close() {
|
|
|
|
if (_rw) {
|
|
|
|
SDL_RWclose(_rw);
|
|
|
|
_rw = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
uint32_t size() {
|
|
|
|
if (_rw) {
|
|
|
|
return SDL_RWsize(_rw);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
void seek(int32_t off) {
|
|
|
|
if (_rw) {
|
|
|
|
SDL_RWseek(_rw, off, RW_SEEK_SET);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
uint32_t read(void *ptr, uint32_t len) {
|
|
|
|
if (_rw) {
|
|
|
|
const int count = SDL_RWread(_rw, ptr, 1, len);
|
|
|
|
if (count != len) {
|
|
|
|
_ioErr = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2017-11-03 17:00:00 +01:00
|
|
|
uint32_t write(const void *ptr, uint32_t len) {
|
2017-06-07 18:00:00 +02:00
|
|
|
_ioErr = true;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
#endif
|
2015-08-02 18:00:00 +02:00
|
|
|
|
2019-12-28 17:00:00 +01:00
|
|
|
struct MemoryBufferFile: File_impl {
|
|
|
|
uint8_t *_ptr;
|
|
|
|
uint32_t _capacity, _offset, _len;
|
|
|
|
MemoryBufferFile(int initialCapacity) {
|
|
|
|
_capacity = initialCapacity;
|
|
|
|
_ptr = (uint8_t *)malloc(_capacity);
|
|
|
|
_offset = _len = 0;
|
|
|
|
}
|
|
|
|
~MemoryBufferFile() {
|
|
|
|
free(_ptr);
|
|
|
|
}
|
|
|
|
bool open(const char *path, const char *mode) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
void close() {
|
|
|
|
}
|
|
|
|
uint32_t size() {
|
|
|
|
return _len;
|
|
|
|
}
|
|
|
|
uint32_t tell() {
|
|
|
|
return _offset;
|
|
|
|
}
|
|
|
|
void seek(int offs) {
|
|
|
|
_offset = offs;
|
|
|
|
}
|
|
|
|
uint32_t read(void *ptr, uint32_t len) {
|
|
|
|
int count = len;
|
|
|
|
if (_offset + count > _len) {
|
|
|
|
count = _len - _offset;
|
|
|
|
_ioErr = true;
|
|
|
|
}
|
|
|
|
if (count != 0) {
|
|
|
|
memcpy(ptr, _ptr + _offset, count);
|
|
|
|
_offset += count;
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
uint32_t write(const void *ptr, uint32_t len) {
|
|
|
|
int count = len;
|
|
|
|
while (_offset + count > _capacity) {
|
|
|
|
_capacity *= 2;
|
|
|
|
_ptr = (uint8_t *)realloc(_ptr, _capacity);
|
|
|
|
}
|
|
|
|
if (count != 0) {
|
|
|
|
memcpy(_ptr + _offset, ptr, count);
|
|
|
|
_offset += count;
|
|
|
|
}
|
|
|
|
_len = _offset;
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-08-02 18:00:00 +02:00
|
|
|
File::File()
|
|
|
|
: _impl(0) {
|
|
|
|
}
|
|
|
|
|
|
|
|
File::~File() {
|
|
|
|
if (_impl) {
|
|
|
|
_impl->close();
|
|
|
|
delete _impl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool File::open(const char *filename, const char *mode, FileSystem *fs) {
|
|
|
|
if (_impl) {
|
|
|
|
_impl->close();
|
|
|
|
delete _impl;
|
|
|
|
_impl = 0;
|
|
|
|
}
|
|
|
|
assert(mode[0] != 'z');
|
2016-05-09 18:00:00 +02:00
|
|
|
_impl = new StdioFile;
|
2015-08-02 18:00:00 +02:00
|
|
|
char *path = fs->findPath(filename);
|
|
|
|
if (path) {
|
|
|
|
debug(DBG_FILE, "Open file name '%s' mode '%s' path '%s'", filename, mode, path);
|
|
|
|
bool ret = _impl->open(path, mode);
|
|
|
|
free(path);
|
|
|
|
return ret;
|
|
|
|
}
|
2017-06-07 18:00:00 +02:00
|
|
|
#ifdef USE_RWOPS
|
|
|
|
if (mode[0] == 'r') {
|
|
|
|
_impl = new AssetFile;
|
|
|
|
return _impl->open(filename, mode);
|
|
|
|
} else if (mode[0] == 'w') {
|
|
|
|
bool ret = false;
|
|
|
|
char *prefPath = SDL_GetPrefPath("org.cyxdown", "fb");
|
|
|
|
if (prefPath) {
|
|
|
|
char path[MAXPATHLEN];
|
|
|
|
snprintf(path, sizeof(path), "%s/%s", prefPath, filename);
|
|
|
|
_impl = new StdioFile;
|
|
|
|
ret = _impl->open(path, mode);
|
|
|
|
SDL_free(prefPath);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
2015-08-02 18:00:00 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool File::open(const char *filename, const char *mode, const char *directory) {
|
|
|
|
if (_impl) {
|
|
|
|
_impl->close();
|
|
|
|
delete _impl;
|
|
|
|
_impl = 0;
|
|
|
|
}
|
|
|
|
#ifdef USE_ZLIB
|
|
|
|
if (mode[0] == 'z') {
|
2016-05-09 18:00:00 +02:00
|
|
|
_impl = new GzipFile;
|
2015-08-02 18:00:00 +02:00
|
|
|
++mode;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (!_impl) {
|
2016-05-09 18:00:00 +02:00
|
|
|
_impl = new StdioFile;
|
2015-08-02 18:00:00 +02:00
|
|
|
}
|
2016-03-20 17:00:00 +01:00
|
|
|
char path[MAXPATHLEN];
|
2015-08-02 18:00:00 +02:00
|
|
|
snprintf(path, sizeof(path), "%s/%s", directory, filename);
|
|
|
|
debug(DBG_FILE, "Open file name '%s' mode '%s' path '%s'", filename, mode, path);
|
|
|
|
return _impl->open(path, mode);
|
|
|
|
}
|
|
|
|
|
2019-12-28 17:00:00 +01:00
|
|
|
void File::openMemoryBuffer(int initialCapacity) {
|
|
|
|
if (_impl) {
|
|
|
|
_impl->close();
|
|
|
|
delete _impl;
|
|
|
|
_impl = 0;
|
|
|
|
}
|
|
|
|
_impl = new MemoryBufferFile(initialCapacity);
|
|
|
|
}
|
|
|
|
|
2015-08-02 18:00:00 +02:00
|
|
|
void File::close() {
|
|
|
|
if (_impl) {
|
|
|
|
_impl->close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool File::ioErr() const {
|
|
|
|
return _impl->_ioErr;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t File::size() {
|
|
|
|
return _impl->size();
|
|
|
|
}
|
|
|
|
|
|
|
|
void File::seek(int32_t off) {
|
|
|
|
_impl->seek(off);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t File::read(void *ptr, uint32_t len) {
|
|
|
|
return _impl->read(ptr, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t File::readByte() {
|
|
|
|
uint8_t b;
|
|
|
|
read(&b, 1);
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t File::readUint16LE() {
|
|
|
|
uint8_t lo = readByte();
|
|
|
|
uint8_t hi = readByte();
|
|
|
|
return (hi << 8) | lo;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t File::readUint32LE() {
|
|
|
|
uint16_t lo = readUint16LE();
|
|
|
|
uint16_t hi = readUint16LE();
|
|
|
|
return (hi << 16) | lo;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t File::readUint16BE() {
|
|
|
|
uint8_t hi = readByte();
|
|
|
|
uint8_t lo = readByte();
|
|
|
|
return (hi << 8) | lo;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t File::readUint32BE() {
|
|
|
|
uint16_t hi = readUint16BE();
|
|
|
|
uint16_t lo = readUint16BE();
|
|
|
|
return (hi << 16) | lo;
|
|
|
|
}
|
|
|
|
|
2017-11-03 17:00:00 +01:00
|
|
|
uint32_t File::write(const void *ptr, uint32_t len) {
|
2015-08-02 18:00:00 +02:00
|
|
|
return _impl->write(ptr, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
void File::writeByte(uint8_t b) {
|
|
|
|
write(&b, 1);
|
|
|
|
}
|
|
|
|
|
2019-10-27 17:00:00 +01:00
|
|
|
void File::writeUint16LE(uint16_t n) {
|
|
|
|
writeByte(n & 0xFF);
|
|
|
|
writeByte(n >> 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
void File::writeUint32LE(uint32_t n) {
|
|
|
|
writeUint16LE(n & 0xFFFF);
|
|
|
|
writeUint16LE(n >> 16);
|
|
|
|
}
|
|
|
|
|
2015-08-02 18:00:00 +02:00
|
|
|
void File::writeUint16BE(uint16_t n) {
|
|
|
|
writeByte(n >> 8);
|
|
|
|
writeByte(n & 0xFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
void File::writeUint32BE(uint32_t n) {
|
|
|
|
writeUint16BE(n >> 16);
|
|
|
|
writeUint16BE(n & 0xFFFF);
|
|
|
|
}
|
2019-10-27 17:00:00 +01:00
|
|
|
|
|
|
|
void dumpFile(const char *filename, const uint8_t *p, int size) {
|
|
|
|
char path[MAXPATHLEN];
|
|
|
|
snprintf(path, sizeof(path), "DUMP/%s", filename);
|
|
|
|
FILE *fp = fopen(filename, "wb");
|
|
|
|
if (fp) {
|
|
|
|
const int count = fwrite(p, 1, size, fp);
|
|
|
|
if (count != size) {
|
|
|
|
warning("Failed to write %d bytes (expected %d)", count, size);
|
|
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
}
|