Cog/Frameworks/HighlyExperimental/HighlyExperimental/Core/vfs.c
2013-09-30 03:36:30 -07:00

158 lines
4.5 KiB
C

/////////////////////////////////////////////////////////////////////////////
//
// vfs - Virtual filesystem management
//
/////////////////////////////////////////////////////////////////////////////
#ifndef EMU_COMPILE
#error "Hi I forgot to set EMU_COMPILE"
#endif
#include "vfs.h"
/////////////////////////////////////////////////////////////////////////////
//
// Static information
//
sint32 EMU_CALL vfs_init(void) { return 0; }
/////////////////////////////////////////////////////////////////////////////
//
// State information
//
#define VFS_MAXPATH (250)
#define VFS_MAXOPENFILES (32)
struct VFS_STATE {
psx_readfile_t readfile_cb;
void *readfile_context;
sint32 ofs[VFS_MAXOPENFILES];
sint32 length[VFS_MAXOPENFILES];
char path[VFS_MAXOPENFILES][VFS_MAXPATH];
};
#define VFSSTATE ((struct VFS_STATE*)(state))
uint32 EMU_CALL vfs_get_state_size(void) {
return sizeof(struct VFS_STATE);
}
void EMU_CALL vfs_clear_state(void *state) {
memset(VFSSTATE, 0, sizeof(struct VFS_STATE));
}
void EMU_CALL vfs_set_readfile(void *state, psx_readfile_t readfile, void *context) {
VFSSTATE->readfile_cb = readfile;
VFSSTATE->readfile_context = context;
}
/////////////////////////////////////////////////////////////////////////////
//
// check if the given fd is valid
//
static sint32 EMU_CALL isvalidfd(struct VFS_STATE *state, sint32 fd) {
if(fd < 0 || fd >= VFS_MAXOPENFILES) return 0;
if(state->path[fd][0] == 0) return 0;
return 1;
}
/////////////////////////////////////////////////////////////////////////////
//
// open
//
sint32 EMU_CALL vfs_open(void *state, const char *path) {
sint32 l;
sint32 fd;
char tempbuf[4];
if(!(VFSSTATE->readfile_cb)) return -5; // EIO if no callback was set
if(!path) return -22; // EINVAL if this was NULL for some reason
if(!path[0]) return -2; // ENOENT if the path is empty
l = (VFSSTATE->readfile_cb)(
VFSSTATE->readfile_context,
path,
0,
tempbuf,
0
);
if(l < -1) return -5; // EIO for fatal errors
if(l == -1) return -2; // ENOENT for not found
// Otherwise, find a free fd and keep it
for(fd = 0; fd < VFS_MAXOPENFILES; fd++) {
if(VFSSTATE->path[fd][0] == 0) break;
}
if(fd >= VFS_MAXOPENFILES) return -24; // EMFILE too many open files
VFSSTATE->ofs[fd] = 0;
VFSSTATE->length[fd] = l;
strncpy(VFSSTATE->path[fd], path, VFS_MAXPATH);
VFSSTATE->path[fd][VFS_MAXPATH-1] = 0;
return fd;
}
/////////////////////////////////////////////////////////////////////////////
//
// close
//
sint32 EMU_CALL vfs_close(void *state, sint32 fd) {
if(!(VFSSTATE->readfile_cb)) return -5; // EIO if no callback was set
if(!isvalidfd(VFSSTATE, fd)) return -9; // EBADF
VFSSTATE->path[fd][0] = 0;
return 0;
}
/////////////////////////////////////////////////////////////////////////////
//
// read
//
sint32 EMU_CALL vfs_read(void *state, sint32 fd, char *buffer, sint32 length) {
sint32 r;
if(!(VFSSTATE->readfile_cb)) return -5; // EIO if no callback was set
if(!isvalidfd(VFSSTATE, fd)) return -9; // EBADF
// if length is 0, just return 0
if(!length) return 0;
// if we're past end-of-file, return 0
if(VFSSTATE->ofs[fd] >= VFSSTATE->length[fd]) return 0;
// make sure we don't read past the end
{ sint32 remain = VFSSTATE->length[fd] - VFSSTATE->ofs[fd];
if(length > remain) length = remain;
}
// attempt actual read
r = (VFSSTATE->readfile_cb)(
VFSSTATE->readfile_context,
VFSSTATE->path[fd],
VFSSTATE->ofs[fd],
buffer,
length
);
if(r < -1) return -5; // EIO for fatal errors
if(r == -1) return -2; // ENOENT for not found - weird, but should work
VFSSTATE->ofs[fd] += r;
return r;
}
/////////////////////////////////////////////////////////////////////////////
//
// lseek
//
sint32 EMU_CALL vfs_lseek(void *state, sint32 fd, sint32 offset, sint32 whence) {
if(!(VFSSTATE->readfile_cb)) return -5; // EIO if no callback was set
if(!isvalidfd(VFSSTATE, fd)) return -9; // EBADF
switch(whence) {
case 0: // SEEK_SET
break;
case 1: // SEEK_CUR
offset += VFSSTATE->ofs[fd];
break;
case 2: // SEEK_END
offset += VFSSTATE->length[fd];
break;
default:
return -22; // EINVAL if whence isn't right
}
if(offset < 0) return -22; // EINVAL if offset ends up negative
VFSSTATE->ofs[fd] = offset;
return offset;
}
/////////////////////////////////////////////////////////////////////////////