game/bin/
game/data/
/*
 * Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
 * 
 * $Id: udb_ochunk.c,v 1.2 1995/11/22 23:33:17 root Exp $
 */

/*
 * configure all options BEFORE including system stuff. 
 */
#include	"autoconf.h"
#include	"udb_defs.h"


#ifdef VMS
#include	<malloc.h>
#include        <types.h>
#include        <file.h>
#include        <unixio.h>
#include        "vms_dbm.h"
#else
#ifndef NEXT
#include	<malloc.h>
#endif /*
        * NEXT 
        */
#include	<sys/param.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<sys/file.h>

#include	"gdbm.h"
#endif /*
        * VMS 
        */

#include	"udb.h"
#include	"config.h"
#include	"alloc.h"

/*
 * #define              DBMCHUNK_DEBUG
 */

/*
 * default block size to use for bitmapped chunks 
 */
#define	DDDB_BLOCK	256

/*
 * bitmap growth increment in BLOCKS not bytes (512 is 64 BYTES) 
 */
#define	DDDB_BITBLOCK	512

#define	LOGICAL_BLOCK(off)	(off/bsiz)
#define	BLOCK_OFFSET(block)	(block*bsiz)
#define	BLOCKS_NEEDED(siz)	(siz % bsiz ? (siz / bsiz) + 1 : (siz / bsiz))
#define BLOCKS_TO_ALLOC(siz)	BLOCKS_NEEDED(siz) + (BLOCKS_NEEDED(siz) >> 3)

/*
 * dbm-based object storage routines. Somewhat trickier than the default
 * directory hash. a free space list is maintained as a bitmap of free
 * blocks, and an object-pointer list is maintained in a dbm database.
 */
struct hrec {
	off_t off;		/*
				 * Where it lives in the chunk file 
				 */
	int siz;		/*
				 * How long it really is, in bytes 
				 */
	unsigned int blox;	/*
				 * How many blocks it owns 
				 */
};

static int dddb_mark();

#define DEFAULT_DBMCHUNKFILE "mudDB"

static char *dbfile = DEFAULT_DBMCHUNKFILE;
static int bsiz = DDDB_BLOCK;
static int db_initted = 0;
static int last_free = 0;	/*

				 * last known or suspected free block 
				 */

static GDBM_FILE dbp = (GDBM_FILE) 0;

static FILE *dbf = (FILE *) 0;
static struct hrec hbuf;

static char *bitm = (char *)0;
static int bm_top = 0;
static datum dat;
static datum key;

static void growbit();

/*
 * gdbm_reorganize compresses unused space in the db 
 */

int dddb_optimize()
{
	return (gdbm_reorganize(dbp));
}

static void gdbm_panic(mesg)
char *mesg;
{
	log_text(tprintf("GDBM panic: %s\n", mesg));
}

int dddb_init()
{
	static char *copen = "db_init cannot open ";
	char fnam[MAXPATHLEN];
	struct stat sbuf;
	int fxp, block_size;

	/*
	 * now open chunk file 
	 */
	sprintf(fnam, "%s.db", dbfile);

	/*
	 * Bah.  Posix-y systems break "a+", so use this gore instead 
	 */
	if (((dbf = fopen(fnam, "r+")) == (FILE *) 0)
	    && ((dbf = fopen(fnam, "w+")) == (FILE *) 0)) {
		logf(copen, fnam, " ", (char *)-1, "\n", (char *)0);
		return (1);
	}
	/*
	 * open hash table 
	 */
	for (block_size = 1; block_size < LBUF_SIZE; block_size = block_size << 1) ;

	if ((dbp = gdbm_open(dbfile, block_size, GDBM_WRCREAT, 0600, gdbm_panic)) == (GDBM_FILE) 0) {
		logf(copen, dbfile, " ", (char *)-1, "\n", (char *)0);
		return (1);
	}
	/*
	 * determine size of chunk file for allocation bitmap 
	 */
	sprintf(fnam, "%s.db", dbfile);
	if (stat(fnam, &sbuf)) {
		logf("db_init cannot stat ", fnam, " ", (char *)-1, "\n", (char *)0);
		return (1);
	}
	/*
	 * allocate bitmap 
	 */
	growbit(LOGICAL_BLOCK(sbuf.st_size) + DDDB_BITBLOCK);

	key = gdbm_firstkey(dbp);
	while (key.dptr != (char *)0) {
		dat = gdbm_fetch(dbp, key);

		if (dat.dptr == (char *)0) {
			logf("db_init index inconsistent\n", (char *)0);
			return (1);
		}
		bcopy(dat.dptr, (char *)&hbuf, sizeof(hbuf));	/*
								 * alignment 
								 */


		/*
		 * mark it as busy in the bitmap 
		 */
		dddb_mark(LOGICAL_BLOCK(hbuf.off), hbuf.blox, 1);

		key = gdbm_nextkey(dbp, key);
	}

	db_initted = 1;
	return (0);
}

dddb_initted()
{
	return (db_initted);
}

dddb_setbsiz(nbsiz)
int nbsiz;
{
	return (bsiz = nbsiz);
}

dddb_setfile(fil)
char *fil;
{
	char *xp;

	if (db_initted)
		return (1);

	/*
	 * KNOWN memory leak. can't help it. it's small 
	 */
	xp = (char *)malloc((unsigned)strlen(fil) + 1);
	if (xp == (char *)0)
		return (1);
	(void)StringCopy(xp, fil);
	dbfile = xp;
	return (0);
}

int dddb_close()
{
	if (dbf != (FILE *) 0) {
		fclose(dbf);
		dbf = (FILE *) 0;
	}
	if (dbp != (GDBM_FILE) 0) {
		gdbm_close(dbp);
		dbp = (GDBM_FILE) 0;
	}
	if (bitm != (char *)0) {
		free((mall_t) bitm);
		bitm = (char *)0;
		bm_top = 0;
	}
	db_initted = 0;
	return (0);
}


/*
 * grow the bitmap to given size 
 */
static void growbit(maxblok)
int maxblok;
{
	int newtop, newbytes, topbytes;
	char *nbit;

	/*
	 * round up to eight and then some 
	 */
	newtop = (maxblok + 15) & 0xfffffff8;

	if (newtop <= bm_top)
		return;

	newbytes = newtop / 8;
	topbytes = bm_top / 8;
	nbit = (char *)malloc(newbytes);
	if (bitm != (char *)0) {
		bcopy(bitm, (char *)nbit, topbytes);
		free((mall_t) bitm);
	}
	bitm = nbit;

	if (bitm == (char *)0)
		fatal("db_init cannot grow bitmap ", (char *)-1, "\n", (char *)0);

	bzero(bitm + topbytes, (newbytes - topbytes));
	bm_top = newtop;
}



static int dddb_mark(lbn, siz, taken)
off_t lbn;
int siz;
int taken;
{
	int bcnt;

	bcnt = siz;

	/*
	 * remember a free block was here 
	 */
	if (!taken)
		last_free = lbn;

	while (bcnt--) {
		if (lbn >= bm_top - 32)
			growbit(lbn + DDDB_BITBLOCK);

		if (taken)
			bitm[lbn >> 3] |= (1 << (lbn & 7));
		else
			bitm[lbn >> 3] &= ~(1 << (lbn & 7));
		lbn++;
	}
}

static int dddb_alloc(siz)
int siz;
{
	int bcnt;		/*

				 * # of blocks to operate on 
				 */
	int lbn;		/*

				 * logical block offset 
				 */
	int tbcnt;
	int slbn;
	int overthetop = 0;
	int offend = 0;

	lbn = last_free;
	bcnt = BLOCKS_TO_ALLOC(siz);

	while (1) {
		if (lbn >= bm_top - 32) {
			/*
			 * only check here. can't break around the top 
			 */
			if (!overthetop) {
				lbn = 0;
				overthetop++;
			} else {
				growbit(lbn + DDDB_BITBLOCK);
			}
		}
		slbn = lbn;
		tbcnt = bcnt;

		while (1) {
			if ((bitm[lbn >> 3] & (1 << (lbn & 7))) != 0)
				break;

			/*
			 * enough free blocks - mark and done 
			 */
			if (--tbcnt == 0) {
				for (tbcnt = slbn; bcnt > 0; tbcnt++, bcnt--)
					bitm[tbcnt >> 3] |= (1 << (tbcnt & 7));

				if (offend) {
					last_free = 0;
				} else {
					last_free = lbn;
				}
				return (slbn);
			}
			lbn++;
			if (lbn >= bm_top - 32) {
				offend++;
				growbit(lbn + DDDB_BITBLOCK);
			}
		}
		lbn++;
	}
}

Obj *
 dddb_get(nam)
Objname *nam;
{
	Obj *ret;

	if (!db_initted)
		return ((Obj *) 0);

	key.dptr = (char *)nam;
	key.dsize = sizeof(Objname);
	dat = gdbm_fetch(dbp, key);

	if (dat.dptr == (char *)0)
		return ((Obj *) 0);
	bcopy(dat.dptr, (char *)&hbuf, sizeof(hbuf));

	/*
	 * seek to location 
	 */
	if (fseek(dbf, (long)hbuf.off, 0))
		return ((Obj *) 0);

	/*
	 * if the file is badly formatted, ret == Obj * 0 
	 */
	if ((ret = objfromFILE(dbf)) == (Obj *) 0)
		logf("db_get: cannot decode ", nam, "\n", (char *)0);
	return (ret);
}

int dddb_put(obj, nam)
Obj *obj;
Objname *nam;
{
	int nsiz;

	if (!db_initted)
		return (1);

	nsiz = obj_siz(obj);

	key.dptr = (char *)nam;
	key.dsize = sizeof(Objname);

	dat = gdbm_fetch(dbp, key);

	if (dat.dptr != (char *)0) {

		bcopy(dat.dptr, (char *)&hbuf, sizeof(hbuf));	/*
								 * align 
								 */

		if (BLOCKS_NEEDED(nsiz) > hbuf.blox) {

#ifdef	DBMCHUNK_DEBUG
			printf("put: moving obj %d, owned %d blox, required %d, siz %d\n",
			       *(unsigned int *)nam, hbuf.blox, BLOCKS_NEEDED(nsiz), nsiz);
#endif
			/*
			 * mark free in bitmap 
			 */
			dddb_mark(LOGICAL_BLOCK(hbuf.off), hbuf.blox, 0);

			hbuf.off = BLOCK_OFFSET(dddb_alloc(nsiz));
			hbuf.siz = nsiz;
			hbuf.blox = BLOCKS_TO_ALLOC(nsiz);
#ifdef	DBMCHUNK_DEBUG
			printf("put: %s moved to offset %d, size %d\n",
			       *(unsigned int *)nam, hbuf.off, hbuf.siz);
#endif
		} else {
			hbuf.siz = nsiz;
#ifdef	DBMCHUNK_DEBUG
			printf("put: %s replaced within offset %d, size %d owns %d blox\n",
			*(unsigned int *)nam, hbuf.off, hbuf.siz, hbuf.blox);
#endif
		}
	} else {
		hbuf.off = BLOCK_OFFSET(dddb_alloc(nsiz));
		hbuf.siz = nsiz;
		hbuf.blox = BLOCKS_TO_ALLOC(nsiz);
#ifdef	DBMCHUNK_DEBUG
		printf("put: %s (new) at offset %d, size %d owns %d blox\n",
		       *(unsigned int *)nam, hbuf.off, hbuf.siz, hbuf.blox);
#endif
	}


	/*
	 * make table entry 
	 */
	dat.dptr = (char *)&hbuf;
	dat.dsize = sizeof(hbuf);

	if (gdbm_store(dbp, key, dat, GDBM_REPLACE)) {
		logf("db_put: can't gdbm_store ", nam, " ", (char *)-1, "\n", (char *)0);
		return (1);
	}
#ifdef	DBMCHUNK_DEBUG
	printf("%s offset %d size %d owns %d blox\n",
	       *(unsigned int *)nam, hbuf.off, hbuf.siz, hbuf.blox);
#endif
	if (fseek(dbf, (long)hbuf.off, 0)) {
		logf("db_put: can't seek ", nam, " ", (char *)-1, "\n", (char *)0);
		return (1);
	}
	if (objtoFILE(obj, dbf) != 0 || fflush(dbf) != 0) {
		logf("db_put: can't save ", nam, " ", (char *)-1, "\n", (char *)0);
		return (1);
	}
	return (0);
}

int dddb_check(nam)
char *nam;
{
	if (!db_initted)
		return (0);

	key.dptr = nam;
	key.dsize = strlen(nam) + 1;

	dat = gdbm_fetch(dbp, key);

	if (dat.dptr == (char *)0)
		return (0);
	return (1);
}

int dddb_del(nam)
Objname *nam;
{

	if (!db_initted)
		return (-1);

	key.dptr = (char *)nam;
	key.dsize = sizeof(Objname);
	dat = gdbm_fetch(dbp, key);

	/*
	 * not there? 
	 */
	if (dat.dptr == (char *)0)
		return (0);
	bcopy(dat.dptr, (char *)&hbuf, sizeof(hbuf));

	/*
	 * drop key from db 
	 */
	if (gdbm_delete(dbp, key)) {
		logf("db_del: can't delete key ", nam, "\n", (char *)0);
		return (1);
	}
	/*
	 * mark free space in bitmap 
	 */
	dddb_mark(LOGICAL_BLOCK(hbuf.off), hbuf.blox, 0);
#ifdef	DBMCHUNK_DEBUG
	printf("del: %s free offset %d, size %d owned %d blox\n",
	       *(unsigned int *)nam, hbuf.off, hbuf.siz, hbuf.blox);
#endif

	/*
	 * mark object dead in file 
	 */
	if (fseek(dbf, (long)hbuf.off, 0))
		logf("db_del: can't seek ", nam, " ", (char *)-1, "\n", (char *)0);
	else {
		fprintf(dbf, "delobj");
		fflush(dbf);
	}
	return (0);
}