/**
* =========================================================================
* File : vfs_redirector.cpp
* Project : 0 A.D.
* Description :
* =========================================================================
*/
// license: GPL; see lib/license.txt
#include "precompiled.h"
#include "vfs_redirector.h"
#include "lib/byte_order.h" // FOURCC
#include "file_internal.h"
static const u32 vtbl_magic = FOURCC('F','P','V','T');
// HACK: these thunks and the vtbls are implemented here,
// although they belong in their respective provider's source file.
// this is currently necessary because vfs_mount doesn't yet
// abstract away the file provider (it's hardcoded for files+archives).
LibError afile_open_vfs(const char* fn, uint flags, TFile* tf,
File* f) // out
{
const uintptr_t memento = tfile_get_memento(tf);
const Mount* m = tfile_get_mount(tf);
const Handle ha = mount_get_archive(m);
return afile_open(ha, fn, memento, flags, f);
}
LibError file_open_vfs(const char* V_path, uint flags, TFile* tf,
File* f) // out
{
char N_path[PATH_MAX];
const Mount* m = tfile_get_mount(tf);
RETURN_ERR(mount_realpath(V_path, m, N_path));
RETURN_ERR(file_open(N_path, flags|FILE_DONT_SET_FN, f));
// file_open didn't set fc.atom_fn due to FILE_DONT_SET_FN.
f->atom_fn = file_make_unique_fn_copy(V_path);
return INFO::OK;
}
static const FileProvider_VTbl archive_vtbl =
{
vtbl_magic,
0,0,0, // not supported for archives ATM
afile_open_vfs, afile_close, afile_validate,
afile_io_issue, afile_io_has_completed, afile_io_wait, afile_io_discard, afile_io_validate,
afile_read,
afile_map, afile_unmap
};
static const FileProvider_VTbl file_vtbl =
{
vtbl_magic,
dir_open, dir_next_ent, dir_close,
file_open_vfs, file_close, file_validate,
file_io_issue, file_io_has_completed, file_io_wait, file_io_discard, file_io_validate,
file_io,
file_map, file_unmap
};
// see FileProvider_VTbl decl for details on why this is so empty.
static const FileProvider_VTbl tree_vtbl =
{
vtbl_magic,
tree_dir_open, tree_dir_next_ent, tree_dir_close,
0, 0, 0,
0, 0, 0, 0, 0,
0,
0, 0
};
// rationale for not using virtual functions for file_open vs afile_open:
// it would spread out the implementation of each function and makes
// keeping them in sync harder. we will very rarely add new sources and
// all these functions are in one spot anyway.
static LibError vtbl_validate(const FileProvider_VTbl* vtbl)
{
if(!vtbl)
WARN_RETURN(ERR::INVALID_PARAM);
if(vtbl->magic != vtbl_magic)
WARN_RETURN(ERR::CORRUPTED);
return INFO::OK;
}
#define CHECK_VTBL(type) RETURN_ERR(vtbl_validate(type))
//
// directory entry enumeration
//
LibError xdir_open(const char* dir, DirIterator* di)
{
// HACK: it is unclear ATM how to set this properly. assume tree_dir_* is
// the only user ATM.
di->type = &tree_vtbl;
CHECK_VTBL(di->type);
return di->type->dir_open(dir, di);
}
LibError xdir_next_ent(DirIterator* di, DirEnt* ent)
{
CHECK_VTBL(di->type);
return di->type->dir_next_ent(di, ent);
}
LibError xdir_close(DirIterator* di)
{
CHECK_VTBL(di->type);
return di->type->dir_close(di);
}
//
// file object
//
bool xfile_is_open(const File* f)
{
// not currently in use
if(f->type == 0)
return false;
WARN_ERR(vtbl_validate(f->type));
return true;
}
LibError xfile_open(const char* V_path, uint flags, TFile* tf, File* f)
{
// find out who is providing this file
const Mount* m = tfile_get_mount(tf);
debug_assert(m != 0);
// HACK: see decl of vtbls. ideally vtbl would already be stored in
// Mount, but that's not implemented yet.
char c = mount_get_type(m);
const FileProvider_VTbl* vtbl = (c == 'F')? &file_vtbl : &archive_vtbl;
CHECK_VTBL(vtbl);
RETURN_ERR(vtbl->file_open(V_path, flags, tf, f));
// success
// note: don't assign these unless we succeed to avoid the
// false impression that all is well.
f->type = vtbl;
return INFO::OK;
}
LibError xfile_close(File* f)
{
// we must not complain if the file is not open. this happens if
// attempting to open a nonexistent file: h_mgr automatically calls
// the dtor after reload fails.
// note: this takes care of checking the vtbl.
if(!xfile_is_open(f))
return INFO::OK;
LibError ret = f->type->file_close(f);
f->type = 0;
return ret;
}
LibError xfile_validate(const File* f)
{
CHECK_VTBL(f->type);
return f->type->file_validate(f);
}
//
// IO
//
LibError xfile_io_issue(File* f, off_t ofs, size_t size, void* buf, FileIo* io)
{
io->type = f->type;
CHECK_VTBL(io->type);
return io->type->io_issue(f, ofs, size, buf, io);
}
int xfile_io_has_completed(FileIo* io)
{
CHECK_VTBL(io->type);
return io->type->io_has_completed(io);
}
LibError xfile_io_wait(FileIo* io, void*& p, size_t& size)
{
CHECK_VTBL(io->type);
return io->type->io_wait(io, p, size);
}
LibError xfile_io_discard(FileIo* io)
{
CHECK_VTBL(io->type);
return io->type->io_discard(io);
}
LibError xfile_io_validate(const FileIo* io)
{
CHECK_VTBL(io->type);
return io->type->io_validate(io);
}
ssize_t xfile_io(File* f, off_t ofs, size_t size, FileIOBuf* pbuf, FileIOCB cb, uintptr_t ctx)
{
CHECK_VTBL(f->type);
// notes:
// - for archive file: vfs_open makes sure it wasn't opened for writing
// - normal file: let file_io alloc the buffer if the caller didn't
// (i.e. p = 0), because it knows about alignment / padding requirements
return f->type->io(f, ofs, size, pbuf, cb, ctx);
}
//
// file mapping
//
LibError xfile_map(File* f, void*& p, size_t& size)
{
CHECK_VTBL(f->type);
return f->type->map(f, p, size);
}
LibError xfile_unmap(File* f)
{
CHECK_VTBL(f->type);
return f->type->unmap(f);
}