#ifndef lint
static char *RCSid = "$Header: file.c,v 1.19 90/03/19 11:23:14 mr-frog Exp $";
#endif /* not lint */

/*
 * file.c
 *
 * operations on files
 *
 * Dave Pare, 1989
 */

#include <fcntl.h>
#include <signal.h>
#include "misc.h"
#include "xy.h"
#include "nsc.h"
#include "file.h"
#include "match.h"
#include "struct.h"

static	int fillcache();

int
ef_open(type, mode, how)
	int	type;
	int	mode;
	int	how;
{
	register struct empfile *ep;
	static	int block;
	int	size;

	if (type < 0 || type >= EF_MAX) {
		logerror("ef_open: bad EF_type %d\n", type);
		return 0;
	}
	ep = &empfile[type];
	if ((ep->fd = open(ep->file, mode, 0660)) < 0) {
		logerror("%s: open failed", ep->file);
		return 0;
	}
	if (block == 0)
		block = blksize(ep->fd);
	ep->baseid = 0;
	ep->cids = 0;
	ep->mode = mode;
	ep->flags |= how;
	ep->fids = fsize(ep->fd) / ep->size;
	if (ep->flags & EFF_MEM)
		ep->csize = ep->fids;
	else
		ep->csize = block / ep->size;
	size = ep->csize * ep->size;
	ep->cache = malloc(size);
	if (ep->cache == 0) {
		logerror("ef_open: %s malloc(%d) failed\n", ep->file, size);
		return 0;
	}
	if (ep->flags & EFF_MEM) {
		if (read(ep->fd, ep->cache, size) != size) {
			logerror("ef_open: read(%s) failed\n", ep->file);
			return 0;
		}
	}
	return 1;
}

int
ef_close(type)
	int	type;
{
	register struct empfile *ep;
	int	size;

	if (type < 0 || type >= EF_MAX) {
		logerror("ef_close: bad EF_type %d\n", type);
		return 0;
	}
	ep = &empfile[type];
	if (ep->cache == 0) {
		/* no cache implies never opened */
		return 0;
	}
	size = ep->csize * ep->size;
	if (ep->mode > 0 && ep->flags & EFF_MEM) {
		if (lseek(ep->fd, 0L, 0) < 0) {
			logerror("ef_close; lseek (%s) failed", ep->file);
			return 0;
		}
		if (write(ep->fd, ep->cache, size) != size) {
			logerror("ef_close; write: (%s, %d bytes) failed",
				ep->file, size);
			return 0;
		}
	}
	ep->flags &= ~EFF_MEM;
	free(ep->cache);
	ep->cache = 0;
	if (close(ep->fd) < 0)
		logerror("ef_close; close (%s) failed", ep->file);
	return 1;
}

char *
ef_ptr(type, id)
	int	type;
	int	id;
{
	register struct empfile *ep;

	if (type < 0 || type >= EF_MAX) {
		logerror("ef_ptr: bad EF_type %d\n", type);
		return 0;
	}
	ep = &empfile[type];
	if (id < 0 || id >= ep->fids)
		return 0;
	if ((ep->flags & EFF_MEM) == 0) {
		logerror("ef_ptr: (%s) only valid for EFF_MEM entries",
			ep->file);
		return 0;
	}
	return (char *) (ep->cache + ep->size * id);
}

/*
 * buffered read.  Tries to read a large number of items.
 * This system won't work is item size is > sizeof buffer area.
 */
int
ef_read(type, id, ptr)
	int	type;
	int	id;
	caddr_t	ptr;
{
	register struct empfile *ep;
	caddr_t	from;

	if (type < 0 || type >= EF_MAX) {
		logerror("ef_read: bad EF_type %d\n", type);
		return 0;
	}
	ep = &empfile[type];
	if (id < 0)
		return 0;
	if (ep->flags & EFF_MEM) {
		if (id >= ep->fids)
			return 0;
		from = ep->cache + (id * ep->size);
	} else {
		if (id >= ep->fids) {
			ep->fids = fsize(ep->fd) / ep->size;
			if (id >= ep->fids)
				return 0;
		}
		if (ep->baseid + ep->cids <= id || ep->baseid > id)
			fillcache(ep, id);
		from = ep->cache + (id - ep->baseid) * ep->size;
	}
	bcopy(from, ptr, ep->size);
	if (ep->postread)
		ep->postread(id, ptr);
	return 1;
}

static
fillcache(ep, start)
	struct	empfile *ep;
	int	start;
{
	int	n;

	ep->baseid = start;
	lseek(ep->fd, start * ep->size, 0);
	n = read(ep->fd, ep->cache, ep->csize * ep->size);
	ep->cids = n / ep->size;
}

/*
 * no-buffered read
 * zaps read cache
 */
int
ef_nbread(type, id, ptr)
	int	type;
	int	id;
	caddr_t	ptr;
{
	register struct empfile *ep;

	if (type < 0 || type >= EF_MAX) {
		logerror("ef_nbread: bad EF_type %d\n", type);
		return 0;
	}
	ep = &empfile[type];
	if (id < 0)
		return 0;
	if (id >= ep->fids) {
		ep->fids = fsize(ep->fd) / ep->size;
		if (id >= ep->fids)
			return 0;
	}
	if (lseek(ep->fd, id * ep->size, 0) < 0) {
		logerror("ef_nbread: lseek id %d failed\n", id);
		return 0;
	}
	if (read(ep->fd, ptr, ep->size) != ep->size) {
		logerror("ef_nbread: read id %d failed\n", id);
		return 0;
	}
	ef_zapcache(type);
	if (ep->postread)
		ep->postread(id, ptr);
	return 1;
}

/*
 * no-buffered write
 * zaps read cache
 */
int
ef_nbwrite(type, id, ptr)
	int	type;
	int	id;
	caddr_t	ptr;
{
	register int r;
	register struct empfile *ep;

	if (type < 0 || type >= EF_MAX || id < 0)
		return 0;
	ep = &empfile[type];
	if (id > 65536) {
		/* largest unit id; this may bite us in large games */
		logerror("ef_nbwrite: type %d id %d is too large!\n", type, id);
		return 0;
	}
	if (lseek(ep->fd, id * ep->size, 0) < 0) {
		logerror("ef_nbwrite: (lseek) id %d, type %d", id, type);
		return 0;
	}
	if (ep->prewrite)
		ep->prewrite(id, ptr);
	if ((r=write(ep->fd, ptr, ep->size)) != ep->size) {
		logerror("ef_nbwrite: (write) id %d, type %d %d!=%d", id, type,r,ep->size);
		logerror("	ef_nbwrite: (write) fd %d, ptr %d", ep->fd, ptr);
		return 0;
	}
	if ((ep->flags & EFF_MEM) == 0)
		ef_zapcache(type);
	if (id >= ep->fids) {
		/* write expanded file; ep->fids = last id + 1 */
		ep->fids = id + 1;
	}
	return 1;
}

ef_zapcache(type)
	int	type;
{
	empfile[type].cids = 0;
	empfile[type].baseid = -1;
}

struct castr *
ef_cadef(type)
	int	type;
{
	return empfile[type].cadef;
}

int
ef_nelem(type)
	int	type;
{
	return empfile[type].fids;
}

int
ef_flags(type)
	int	type;
{
	return empfile[type].flags;
}

int
ef_lock(type)
	int	type;
{
	return file_lock(empfile[type].fd);
}

int
ef_unlock(type)
	int	type;
{
	return file_unlock(empfile[type].fd);
}

time_t
ef_mtime(type)
	int	type;
{
	extern	time_t fdate();

	if (empfile[type].fd <= 0)
		return 0;
	return fdate(empfile[type].fd);
}

int
ef_vars(type, sp, nvp, vp, ap)
	int	type;
	register char *sp;
	u_char	**nvp;
	u_char	**vp;
	u_short	**ap;
{
	register struct empfile *ef;

	if (type < 0 || type >= EF_MAX) {
		logerror("ef_vars: bad EF_type %d\n", type);
		return -1;
	}
	ef = &empfile[type];
	if ((ef->flags & EFF_COM) == 0)
		return -1;
	*nvp = (u_char *) (sp + ef->varoffs[0]);
	*vp = (u_char *) (sp + ef->varoffs[1]);
	*ap = (u_short *) (sp + ef->varoffs[2]);
	return ef->maxvars;
}

int
ef_byname(name)
	char	*name;
{
	register struct empfile *ef;
	register int i;
	int	len;

	len = strlen(name);
	for (i=0; i<EF_MAX; i++) {
		ef = &empfile[i];
		if (strncmp(ef->name, name, len) == 0)
			return i;
	}
	return -1;
}

char *
ef_nameof(type)
	int	type;
{
	if (type < 0 || type >= EF_MAX)
		return "bad item type";
	return empfile[type].name;
}
