412 lines
8.9 KiB
C
412 lines
8.9 KiB
C
/*
|
|
* This file is subject to the terms of the GFX License. If a copy of
|
|
* the license was not distributed with this file, you can obtain one at:
|
|
*
|
|
* http://ugfx.org/license.html
|
|
*/
|
|
|
|
#include "../../gfx.h"
|
|
|
|
#if GFX_USE_GFILE
|
|
|
|
#include "gfile_fs.h"
|
|
|
|
/**
|
|
* Define the VMT's for the file-systems we want to search for files.
|
|
* Virtual file-systems that have special open() calls do not need to
|
|
* be in this list.
|
|
*/
|
|
#if GFILE_NEED_USERFS
|
|
extern const GFILEVMT FsUSERVMT;
|
|
#endif
|
|
#if GFILE_NEED_ROMFS
|
|
extern const GFILEVMT FsROMVMT;
|
|
#endif
|
|
#if GFILE_NEED_NATIVEFS
|
|
extern const GFILEVMT FsNativeVMT;
|
|
#endif
|
|
#if GFILE_NEED_FATFS
|
|
extern const GFILEVMT FsFatFSVMT;
|
|
#endif
|
|
#if GFILE_NEED_RAMFS
|
|
extern const GFILEVMT FsRAMVMT;
|
|
#endif
|
|
|
|
|
|
/**
|
|
* The order of the file-systems below determines the order
|
|
* that they are searched to find a file.
|
|
*/
|
|
static const GFILEVMT const * FsArray[] = {
|
|
#if GFILE_NEED_USERFS
|
|
&FsUSERVMT,
|
|
#endif
|
|
#if GFILE_NEED_ROMFS
|
|
&FsROMVMT,
|
|
#endif
|
|
#if GFILE_NEED_NATIVEFS
|
|
&FsNativeVMT,
|
|
#endif
|
|
#if GFILE_NEED_FATFS
|
|
&FsFatFSVMT,
|
|
#endif
|
|
#if GFILE_NEED_RAMFS
|
|
&FsRAMVMT,
|
|
#endif
|
|
};
|
|
|
|
/*
|
|
* The table of GFILE's
|
|
*/
|
|
static GFILE gfileArr[GFILE_MAX_GFILES];
|
|
GFILE *gfileStdIn;
|
|
GFILE *gfileStdOut;
|
|
GFILE *gfileStdErr;
|
|
|
|
/**
|
|
* The init routine
|
|
*/
|
|
void _gfileInit(void) {
|
|
#if GFILE_NEED_NATIVEFS
|
|
extern void _gfileNativeAssignStdio(void);
|
|
_gfileNativeAssignStdio();
|
|
#endif
|
|
}
|
|
|
|
void _gfileDeinit(void)
|
|
{
|
|
GFILE * f;
|
|
for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) {
|
|
if (f->flags & GFILEFLG_OPEN)
|
|
gfileClose(f);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Internal routine to find an empty GFILE slot and interpret flags.
|
|
*/
|
|
GFILE *_gfileFindSlot(const char *mode) {
|
|
GFILE * f;
|
|
|
|
// First find an available GFILE slot.
|
|
for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) {
|
|
if (!(f->flags & GFILEFLG_OPEN)) {
|
|
// Get the flags
|
|
switch(mode[0]) {
|
|
case 'r':
|
|
f->flags = GFILEFLG_READ|GFILEFLG_MUSTEXIST;
|
|
while (*++mode) {
|
|
switch(mode[0]) {
|
|
case '+': f->flags |= GFILEFLG_WRITE; break;
|
|
case 'b': f->flags |= GFILEFLG_BINARY; break;
|
|
}
|
|
}
|
|
break;
|
|
case 'w':
|
|
f->flags = GFILEFLG_WRITE|GFILEFLG_TRUNC;
|
|
while (*++mode) {
|
|
switch(mode[0]) {
|
|
case '+': f->flags |= GFILEFLG_READ; break;
|
|
case 'b': f->flags |= GFILEFLG_BINARY; break;
|
|
case 'x': f->flags |= GFILEFLG_MUSTNOTEXIST; break;
|
|
}
|
|
}
|
|
break;
|
|
case 'a':
|
|
f->flags = GFILEFLG_WRITE|GFILEFLG_APPEND;
|
|
while (*++mode) {
|
|
switch(mode[0]) {
|
|
case '+': f->flags |= GFILEFLG_READ; break;
|
|
case 'b': f->flags |= GFILEFLG_BINARY; break;
|
|
case 'x': f->flags |= GFILEFLG_MUSTNOTEXIST; break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
return f;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/********************************************************
|
|
* IO routines
|
|
********************************************************/
|
|
|
|
bool_t gfileExists(const char *fname) {
|
|
const GFILEVMT * const *p;
|
|
|
|
#if GFILE_ALLOW_DEVICESPECIFIC
|
|
if (fname[0] && fname[1] == '|') {
|
|
for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
|
|
if (p[0]->prefix == fname[0])
|
|
return p[0]->exists && p[0]->exists(fname+2);
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
|
|
if (p[0]->exists && p[0]->exists(fname))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool_t gfileDelete(const char *fname) {
|
|
const GFILEVMT **p;
|
|
|
|
#if GFILE_ALLOW_DEVICESPECIFIC
|
|
if (fname[0] && fname[1] == '|') {
|
|
for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
|
|
if (p[0]->prefix == fname[0])
|
|
return p[0]->del && p[0]->del(fname+2);
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
|
|
if (p[0]->del && p[0]->del(fname))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
long int gfileGetFilesize(const char *fname) {
|
|
const GFILEVMT * const *p;
|
|
long int res;
|
|
|
|
#if GFILE_ALLOW_DEVICESPECIFIC
|
|
if (fname[0] && fname[1] == '|') {
|
|
for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
|
|
if (p[0]->prefix == fname[0])
|
|
return p[0]->filesize ? p[0]->filesize(fname+2) : -1;
|
|
}
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
|
|
if (p[0]->filesize && (res = p[0]->filesize(fname)) != -1)
|
|
return res;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool_t gfileRename(const char *oldname, const char *newname) {
|
|
const GFILEVMT * const *p;
|
|
|
|
#if GFILE_ALLOW_DEVICESPECIFIC
|
|
if ((oldname[0] && oldname[1] == '|') || (newname[0] && newname[1] == '|')) {
|
|
char ch;
|
|
|
|
if (oldname[0] && oldname[1] == '|') {
|
|
ch = oldname[0];
|
|
oldname += 2;
|
|
if (newname[0] && newname[1] == '|') {
|
|
if (newname[0] != ch)
|
|
// Both oldname and newname are fs specific but different ones.
|
|
return FALSE;
|
|
newname += 2;
|
|
}
|
|
} else {
|
|
ch = newname[0];
|
|
newname += 2;
|
|
}
|
|
for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
|
|
if (p[0]->prefix == ch)
|
|
return p[0]->ren && p[0]->ren(oldname, newname);
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
|
|
if (p[0]->ren && p[0]->ren(oldname,newname))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static bool_t testopen(const GFILEVMT *p, GFILE *f, const char *fname) {
|
|
// If we want write but the fs doesn't allow it then return
|
|
if ((f->flags & GFILEFLG_WRITE) && !(p->flags & GFSFLG_WRITEABLE))
|
|
return FALSE;
|
|
|
|
// Try to open
|
|
if (!p->open || !p->open(f, fname))
|
|
return FALSE;
|
|
|
|
// File is open - fill in all the details
|
|
f->vmt = p;
|
|
f->pos = 0;
|
|
f->flags |= GFILEFLG_OPEN;
|
|
if (p->flags & GFSFLG_SEEKABLE)
|
|
f->flags |= GFILEFLG_CANSEEK;
|
|
return TRUE;
|
|
}
|
|
|
|
GFILE *gfileOpen(const char *fname, const char *mode) {
|
|
GFILE * f;
|
|
const GFILEVMT * const *p;
|
|
|
|
// Get an empty file and set the flags
|
|
if (!(f = _gfileFindSlot(mode)))
|
|
return 0;
|
|
|
|
#if GFILE_ALLOW_DEVICESPECIFIC
|
|
if (fname[0] && fname[1] == '|') {
|
|
for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
|
|
if (p[0]->prefix == fname[0])
|
|
return testopen(p[0], f, fname+2) ? f : 0;
|
|
}
|
|
|
|
// File not found
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
|
|
if (testopen(p[0], f, fname))
|
|
return f;
|
|
}
|
|
|
|
// File not found
|
|
return 0;
|
|
}
|
|
|
|
void gfileClose(GFILE *f) {
|
|
if (!f || !(f->flags & GFILEFLG_OPEN))
|
|
return;
|
|
if (f->vmt->close)
|
|
f->vmt->close(f);
|
|
f->flags = 0;
|
|
}
|
|
|
|
size_t gfileRead(GFILE *f, void *buf, size_t len) {
|
|
size_t res;
|
|
|
|
if (!f || (f->flags & (GFILEFLG_OPEN|GFILEFLG_READ)) != (GFILEFLG_OPEN|GFILEFLG_READ))
|
|
return 0;
|
|
if (!f->vmt->read)
|
|
return 0;
|
|
if ((res = f->vmt->read(f, buf, len)) <= 0)
|
|
return 0;
|
|
f->pos += res;
|
|
return res;
|
|
}
|
|
|
|
size_t gfileWrite(GFILE *f, const void *buf, size_t len) {
|
|
size_t res;
|
|
|
|
if (!f || (f->flags & (GFILEFLG_OPEN|GFILEFLG_WRITE)) != (GFILEFLG_OPEN|GFILEFLG_WRITE))
|
|
return 0;
|
|
if (!f->vmt->write)
|
|
return 0;
|
|
if ((res = f->vmt->write(f, buf, len)) <= 0)
|
|
return 0;
|
|
f->pos += res;
|
|
return res;
|
|
}
|
|
|
|
long int gfileGetPos(GFILE *f) {
|
|
if (!f || !(f->flags & GFILEFLG_OPEN))
|
|
return 0;
|
|
return f->pos;
|
|
}
|
|
|
|
bool_t gfileSetPos(GFILE *f, long int pos) {
|
|
if (!f || !(f->flags & GFILEFLG_OPEN))
|
|
return FALSE;
|
|
if (!f->vmt->setpos || !f->vmt->setpos(f, pos))
|
|
return FALSE;
|
|
f->pos = pos;
|
|
return TRUE;
|
|
}
|
|
|
|
long int gfileGetSize(GFILE *f) {
|
|
if (!f || !(f->flags & GFILEFLG_OPEN))
|
|
return 0;
|
|
if (!f->vmt->getsize)
|
|
return 0;
|
|
return f->vmt->getsize(f);
|
|
}
|
|
|
|
bool_t gfileEOF(GFILE *f) {
|
|
if (!f || !(f->flags & GFILEFLG_OPEN))
|
|
return TRUE;
|
|
if (!f->vmt->eof)
|
|
return FALSE;
|
|
return f->vmt->eof(f);
|
|
}
|
|
|
|
bool_t gfileMount(char fs, const char* drive) {
|
|
const GFILEVMT * const *p;
|
|
|
|
// Find the correct VMT
|
|
for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
|
|
if (p[0]->prefix == fs) {
|
|
if (!p[0]->mount)
|
|
return FALSE;
|
|
return p[0]->mount(drive);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool_t gfileUnmount(char fs, const char* drive) {
|
|
const GFILEVMT * const *p;
|
|
|
|
// Find the correct VMT
|
|
for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
|
|
if (p[0]->prefix == fs) {
|
|
if (!p[0]->mount)
|
|
return FALSE;
|
|
return p[0]->unmount(drive);
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool_t gfileSync(GFILE *f) {
|
|
if (!f->vmt->sync)
|
|
return FALSE;
|
|
return f->vmt->sync(f);
|
|
}
|
|
|
|
#if GFILE_NEED_FILELISTS
|
|
gfileList *gfileOpenFileList(char fs, const char *path, bool_t dirs) {
|
|
const GFILEVMT * const *p;
|
|
gfileList * pfl;
|
|
|
|
// Find the correct VMT
|
|
for(p = FsArray; p < &FsArray[sizeof(FsArray)/sizeof(FsArray[0])]; p++) {
|
|
if (p[0]->prefix == fs) {
|
|
if (!p[0]->flopen)
|
|
return 0;
|
|
pfl = p[0]->flopen(path, dirs);
|
|
if (pfl) {
|
|
pfl->vmt = p[0];
|
|
pfl->dirs = dirs;
|
|
}
|
|
return pfl;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char *gfileReadFileList(gfileList *pfl) {
|
|
return pfl->vmt->flread ? pfl->vmt->flread(pfl) : 0;
|
|
}
|
|
|
|
void gfileCloseFileList(gfileList *pfl) {
|
|
if (pfl->vmt->flclose)
|
|
pfl->vmt->flclose(pfl);
|
|
}
|
|
#endif
|
|
|
|
#endif /* GFX_USE_GFILE */
|