shithub: dporg

ref: b079d708225b49908129e3ea54d1bb80c004b25f
dir: /fs.c/

View raw version
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include "dat.h"
#include "fns.h"

s32int sintab[256];
char **basestr;
int nfontmap;
uchar *fontmap;
int nglyph;
Pic *dfont;
int nwalls;
Wall *walls;
int nsprites;
Sprite *sprites;

static Biobuf *
eopen(char *s, int mode)
{
	Biobuf *bf;

	if((bf = Bopen(s, mode)) == nil)
		sysfatal("Bopen: %r");
	Blethal(bf, nil);
	return bf;
}

static long
eread(Biobuf *bf, void *buf, long n)
{
	if(Bread(bf, buf, n) != n)
		sysfatal("ebread: short read: %r");
	return n;
}

static vlong
bsize(Biobuf *bf)
{
	vlong n;
	Dir *d;

	d = dirfstat(Bfildes(bf));
	if(d == nil)
		sysfatal("bstat: %r");
	n = d->length;
	free(d);
	return n;
}

static u8int
get8(Biobuf *bf)
{
	u8int v;

	eread(bf, &v, 1);
	return v;
}

static u16int
get16(Biobuf *bf)
{
	u16int v;

	v = get8(bf);
	return v | get8(bf) << 8;
}

static u32int
get24(Biobuf *bf)
{
	u32int v;

	v = get16(bf);
	return v | get8(bf) << 16;
}

static u32int
get32(Biobuf *bf)
{
	u32int v;

	v = get16(bf);
	return v | get16(bf) << 16;
}

static void
loadpic(char *name, Pic *pic)
{
	int fd, n, m, dx, dy;
	uchar *b, *s;
	u32int *p;
	Image *i;

	if((fd = open(name, OREAD)) < 0)
		sysfatal("loadpic: %r");
	if((i = readimage(display, fd, 0)) == nil)
		sysfatal("readimage: %r");
	close(fd);
	if(i->chan != RGBA32)
		sysfatal("loadpic %s: inappropriate image format", name);
	dx = Dx(i->r);
	dy = Dy(i->r);
	n = dx * dy;
	if(dx * dy > Vw * Vfullh)
		sysfatal("loadpic %s: inappropriate image size", name);
	p = emalloc(n * sizeof *p);
	pic->p = p;
	pic->w = dx;
	pic->h = dy;
	m = i->depth / 8;
	b = emalloc(n * m);
	unloadimage(i, i->r, b, n * m);
	freeimage(i);
	s = b;
	while(n-- > 0){
		*p++ = s[0] << 24 | s[3] << 16 | s[2] << 8 | s[1];
		s += m;
	}
	free(b);
}

static void
loadfont(void)
{
	int i, n, nx, ny;
	u32int *d, *s;
	Pic pic, *pp;

	loadpic("a.bit", &pic);
	nx = pic.w / Vfntpicw;
	ny = pic.h / Vfnth;
	if(pic.w % nx != 0 || pic.h % ny != 0)
		sysfatal("loadfont: invalid font pic");
	nglyph = nx * ny;
	dfont = emalloc(nglyph * sizeof *dfont);
	for(i=0, pp=dfont; pp<dfont+nglyph; pp++, i++){
		pp->w = Vfntw;
		pp->h = Vfnth;
		pp->p = emalloc(pp->w * pp->h * sizeof *pp->p);
		d = pp->p;
		s = pic.p + i / nx * pic.w * pp->h + i % nx * Vfntpicw + Vfntspc;
		for(n=0; n<pp->h; n++){
			memcpy(d, s, pp->w * sizeof *d);
			d += pp->w;
			s += pic.w;
		}
	}
	free(pic.p);
}

void
loadpics(void)
{
	loadpic("b.bit", pics + PCarrow);
	loadpic("c.bit", pics + PCspace);
	loadpic("d.bit", pics + PCgrid);
	loadpic("e.bit", pics + PCplanets);
	loadpic("f.bit", pics + PCship);
	loadpic("k.bit", pics + PChud);
	loadpic("l.bit", pics + PCface);
	loadpic("m.bit", pics + PCammo);
	loadpic("n.bit", pics + PChit);
	loadpic("o.bit", pics + PCdir);
	loadpic("p.bit", pics + PCcur);
	loadpic("q.bit", pics + PCscroll);
	loadpic("r.bit", pics + PCgibs);
	loadfont();
	canvas.p = emalloc(Vw * Vfullh * sizeof *canvas.p);
	canvas.w = Vw;
	canvas.h = Vfullh;
}

static void
loadpal(void)
{
	int n;
	u8int r, g, b;
	u32int *p;
	Biobuf *bf;

	bf = eopen("palettes.bin", OREAD);
	npal = get32(bf) / 2;
	if(npal % Npalcol != 0)
		sysfatal("loadpal: invalid palette size %d\n", npal);
	pal = emalloc(npal * sizeof *pal);
	for(p=pal; p<pal+npal; p++){
		n = get16(bf);
		r = n & 0x1f;
		r = r << 3 | r >> 2;
		g = n >> 5 & 0x3f;
		g = g << 2 | g >> 4;
		b = n >> 11 & 0x1f;
		b = b << 3 | b >> 2;
		*p = 0xff << 24 | r << 16 | g << 8 | b;
	}
	npal /= Npalcol;
	Bterm(bf);
}

static char **
loadstr(char *name, int *nel)
{
	int n;
	char **s, **p, **e, *q;
	Biobuf *bf;

	bf = eopen(name, OREAD);
	n = get16(bf);
	s = emalloc(n * sizeof *p);
	e = s + n;
	*nel = n;
	for(p=s; p<e; p++){
		n = get16(bf);
		*p = emalloc(n + 1);
		eread(bf, *p, n);
		/* FIXME: not always? */
		q = *p;
		while((q = strchr(q, '|')) != nil)
			*q = '\n';
	}
	Bterm(bf);
	return s;
}

static void
unpackspr(Sprite *s, Biobuf *btex, Biobuf *bshp)
{
	int n, B, b, i, v;
	u32int *p;

	v = -1;
	for(i=0; i<s->w; i++){
		p = s->p + i;
		n = s->h;
		while(n > 0){
			B = n > 8 ? 8 : n;
			n -= B;
			b = get8(bshp);
			while(B > 0){
				if(b & 1){
					if(v >= 0){
						*p = v & 15;
						v = -1;
					}else{
						v = get8(btex);
						*p = v & 15;
						v >>= 4;
					}
				}else
					*p = -1;
				p += s->w;
				b >>= 1;
				B--;
			}
		}
	}
}

static void
dumpbwwalls(Wall *wbuf, int nbuf)
{
	int n, fd;
	char name[32], c[9];
	Wall *w;

	for(w=wbuf; w<wbuf+nbuf; w++){
		snprint(name, sizeof name, "wall%02zd.bit", w-walls);
		if((fd = create(name, OWRITE, 0644)) < 0)
			sysfatal("dumpbwwalls: %r");
		fprint(fd, "%11s %11d %11d %11d %11d ",
			chantostr(c, GREY8), 0, 0, Wallsz, Wallsz);
		for(n=0; n<nelem(w->p); n++){
			c[0] = w->p[n] & 0xff;
			write(fd, c, 1);
		}
		close(fd);
	}
}

static void
dumpbwspr(Sprite *sbuf, int nbuf)
{
	int n, fd;
	char name[32], c[9];
	Sprite *s;

	for(s=sbuf; s<sbuf+nbuf; s++){
		snprint(name, sizeof name, "spr%02zd.bit", s-sbuf);
		if((fd = create(name, OWRITE, 0644)) < 0)
			sysfatal("dumpbwspr: %r");
		fprint(fd, "%11s %11d %11d %11d %11d ",
			chantostr(c, GREY8), 0, 0, s->w, s->h);
		for(n=0; n<s->w*s->h; n++){
			c[0] = 96 * (s->p[n] & 0xff) / 16;
			write(fd, c, 1);
		}
		close(fd);
	}
}

static void
dumpspr(Sprite *sbuf, int nbuf)
{
	int fd;
	char name[32], c[9];
	Sprite *s;

	for(s=sbuf; s<sbuf+nbuf; s++){
		snprint(name, sizeof name, "spr%02zd.bit", s-sbuf);
		if((fd = create(name, OWRITE, 0644)) < 0)
			sysfatal("dumpspr: %r");
		fprint(fd, "%11s %11d %11d %11d %11d ",
			chantostr(c, ARGB32), 0, 0, s->w, s->h);
		write(fd, s->p, s->w * s->h * sizeof *s->p);
		close(fd);
	}
}

static void
dumppal(void)
{
	int fd;
	char name[32], c[9];

	snprint(name, sizeof name, "pal.bit");
	if((fd = create(name, OWRITE, 0644)) < 0)
		sysfatal("dumppal: %r");
	fprint(fd, "%11s %11d %11d %11d %11d ", chantostr(c, ARGB32), 0, 0, 16, npal);
	write(fd, pal, npal * Npalcol * sizeof *pal);
	close(fd);
}

static int
loadblankspr(Sprite **sbuf, int *nsbuf, int **shpofs)
{
	int nbuf, nspan, spansz, bshpsz, bsize, ofs, *ofsbuf, *o;
	Biobuf *btex, *bshp;
	Sprite *s, *buf;

	btex = eopen("stexels.bin", OREAD);
	get32(btex);
	bshp = eopen("bitshapes.bin", OREAD);
	bshpsz = get32(bshp);
	nbuf = 0;
	buf = nil;
	s = nil;
	ofsbuf = nil;
	o = nil;
	ofs = 0;
	while(bshpsz > 0){
		if(s >= buf + nbuf){
			buf = erealloc(buf, (nbuf+32) * sizeof *buf,
				nbuf * sizeof *buf);
			ofsbuf = erealloc(ofsbuf, (nbuf+32) * sizeof *ofsbuf,
				nbuf * sizeof *ofsbuf);
			s = buf + nbuf;
			o = ofsbuf + nbuf;
			nbuf += 32;
		}
		get16(bshp);
		get16(bshp);
		bsize = get16(bshp) + 8;
		get16(bshp);
		s->r.min.x = get8(bshp);
		s->r.max.x = get8(bshp);
		s->r.min.y = get8(bshp);
		s->r.max.y = get8(bshp);
		nspan = Dx(s->r) + 1;
		spansz = Dy(s->r) + 1;
		s->w = nspan;
		s->h = spansz;
		s->p = emalloc(s->w * s->h * sizeof *s->p);
		unpackspr(s, btex, bshp);
		s++;
		*o++ = ofs;
		ofs += bsize;
		bshpsz -= bsize;
	}
	if(bshpsz != 0){
		werrstr("sprite data and shape mismatch by %d bytes", bshpsz);
		return -1;
	}
	*sbuf = buf;
	*nsbuf = s - buf;
	*shpofs = ofsbuf;
	Bterm(btex);
	Bterm(bshp);
	return 0;
}

static int
gencolorspr(Sprite *sbuf, int nbuf, int *shpofs)
{
	int *shp;
	u32int n, ofs, palofs, *pp, *bp, *p;
	Biobuf *bf;
	Sprite *s, *bw;

	bf = eopen("mappings.bin", OREAD);
	ofs = get32(bf) * 2 * sizeof(u32int);
	n = get32(bf);
	get32(bf);
	get32(bf);
	Bseek(bf, ofs, 1);
	sprites = emalloc(n * sizeof *sprites);
	nsprites = n;
	for(s=sprites; s<sprites+nsprites; s++){
		ofs = get32(bf);
		palofs = get32(bf);
		if(palofs % Npalcol != 0 || palofs / Npalcol >= npal){
			werrstr("gencolorspr: invalid palette index %ud", palofs);
			return -1;
		}
		for(shp=shpofs; shp<shpofs+nbuf; shp++)
			if(*shp == ofs)
				break;
		if(shp >= shpofs+nbuf){
			werrstr("gencolorspr: invalid bitshapes offset %ud", ofs);
			return -1;
		}
		bw = sbuf + (shp - shpofs);
		pp = pal + palofs;
		memcpy(s, bw, sizeof *s);
		s->p = emalloc(s->w * s->h * sizeof *s->p);
		for(p=s->p, bp=bw->p; p<s->p+s->w*s->h; p++, bp++){
			n = *bp;
			if(n == -1UL)
				*p = 0;
			else if(n > Npalcol){
				werrstr("gencolor: invalid palette color index %ud", n);
				return -1;
			}else
				*p = pp[n];
		}
	}
	Bterm(bf);
	return 0;
}

static void
loadsprites(void)
{
	int nbuf, *shpofs;
	Sprite *sbuf, *s;

	sbuf = nil;
	nbuf = 0;
	shpofs = nil;
	if(loadblankspr(&sbuf, &nbuf, &shpofs) < 0
	|| gencolorspr(sbuf, nbuf, shpofs) < 0)
		sysfatal("loadsprites: %r");
	for(s=sbuf; s<sbuf+nbuf; s++)
		free(s->p);
	free(sbuf);
	free(shpofs);
}

static void
loadwalls(void)
{
	int i, j, n, v;
	Biobuf *bf;
	Wall *w, *we;
	u32int *p;

	bf = eopen("wtexels.bin", OREAD);
	n = get32(bf) / (Wallsz * Wtexelsz);
	walls = emalloc(n * sizeof *walls);
	nwalls = n;
	for(w=walls, we=w+n; w<we; w++){
		for(i=0; i<Wallsz; i++){
			p = w->p + i;
			for(j=0; j<Wtexelsz; j++){
				v = get8(bf);
				*p = v & 15;
				p += Wallsz;
				*p = v >> 4 & 15;
				p += Wallsz;
			}
		}
	}
	Bterm(bf);
}

static void
loadfontmap(void)
{
	int n;
	Biobuf *bf;

	bf = eopen("a.map", OREAD);
	n = bsize(bf);
	fontmap = emalloc(n * sizeof *fontmap);
	eread(bf, fontmap, n);
	nfontmap = n;
	Bterm(bf);
}

static void
loadbasestr(void)
{
	int nel;

	basestr = loadstr("base.str", &nel);
	if(nel != BSend)
		sysfatal("loadbasestr: inconsistent base.str file: entries %d not %d", nel, BSend);
}

static void
dumpbspnodes(Map *m)
{
	int ni, fd;
	char path[64], s[32];
	Point p;
	Font *f;
	Image *i, *b, *c, *o;
	Line *l, *le;
	Node *n;

	assert(display != nil);
	b = eallocimage(Rect(0,0,256<<2,256<<2), 0, DBlack);
	i = eallocimage(b->r, 0, DNofill);
	c = eallocimage(Rect(0,0,1,1), 1, DRed);
	o = eallocimage(Rect(0,0,1,1), 1, 0x777777FF);
	if((f = openfont(display, "/lib/font/bit/fixed/unicode.6x10.font")) == nil)
		sysfatal("openfont: %r");
	for(l=m->lines; l<m->lines+m->nlines; l++)
		line(b, divpt(l->min,2), divpt(l->max,2), 0, Endarrow, 0, display->white, ZP);
	for(n=m->nodes, ni=0; n<m->nodes+m->nnodes; n++){
		draw(i, i->r, b, nil, ZP);
		draw(i, Rpt(divpt(n->min,2), divpt(n->max,2)), o, nil, ZP);
		switch(n->type){
		case 0:
			l = m->lines + n->right;
			le = l + n->left;
			for(; l<le; l++){
				line(i, divpt(l->min,2), divpt(l->max,2), 0, Endarrow, 0, c, ZP);
				p.x = (l->min.x + Dx(l->Rectangle) / 2) / 2;
				p.y = (l->min.y + Dy(l->Rectangle) / 2) / 2;
				snprint(s, sizeof s, "%zd", l - (m->lines+n->right));
				string(i, p, c, ZP, f, s);
			}
			break;
		case 1:
			line(i, Pt(0,n->split/2), Pt(i->r.max.x,n->split/2), 0, 0, 0, c, ZP);
			line(i, divpt(n->min,2), divpt(n->max,2), 0, Endarrow, 0, c, ZP);
			p.x = (n->min.x + Dx(n->Rectangle) / 2) / 2;
			p.y = (n->min.y + Dy(n->Rectangle) / 2) / 2;
			snprint(s, sizeof s, "%zd → %d,%d", n-m->nodes, n->left, n->right);
			string(i, p, c, ZP, f, s);
			break;
		case 2:
			line(i, Pt(n->split/2,0), Pt(n->split/2,i->r.max.y), 0, 0, 0, c, ZP);
			line(i, divpt(n->min,2), divpt(n->max,2), 0, Endarrow, 0, c, ZP);
			p.x = (n->min.x + Dx(n->Rectangle) / 2) / 2;
			p.y = (n->min.y + Dy(n->Rectangle) / 2) / 2;
			snprint(s, sizeof s, "%zd → %d,%d", n-m->nodes, n->left, n->right);
			string(i, p, c, ZP, f, s);
			break;
		default:
			sysfatal("unknown node type %d\n", n->type);
		}
		snprint(path, sizeof path, "node%04d.bit", ni++);
		if((fd = create(path, OWRITE, 0664)) < 0)
			sysfatal("create: %r");
		if(writeimage(fd, i, 0) < 0)
			sysfatal("writeimage: %r");
		close(fd);
	}
	freeimage(b);
	freeimage(i);
	freeimage(c);
	freeimage(o);
	freefont(f);
}

static void
dumplines(Map *m)
{
	int fd;
	Image *i;
	Line *l;

	assert(display != nil);
	i = eallocimage(Rect(0,0,256<<3,256<<3), 0, DBlack);
	for(l=m->lines; l<m->lines+m->nlines; l++)
		line(i, l->min, l->max, 0, Endarrow, 0, display->white, ZP);
	if((fd = create("lines.bit", OWRITE, 0664)) < 0)
		sysfatal("create: %r");
	if(writeimage(fd, i, 0) < 0)
		sysfatal("writeimage: %r");
	freeimage(i);
	close(fd);
}

static void
loadbsp(Biobuf *bf, Map *m)
{
	Node *n;

	m->nnodes = get16(bf);
	m->nodes = emalloc(m->nnodes * sizeof *m->nodes);
	for(n=m->nodes; n<m->nodes+m->nnodes; n++){
		n->min.x = get8(bf) << Fineshift;
		n->min.y = get8(bf) << Fineshift;
		n->max.x = get8(bf) << Fineshift;
		n->max.y = get8(bf) << Fineshift;
		n->type = get8(bf);
		n->split = get8(bf) << Fineshift;
		n->left = get16(bf);
		n->right = get16(bf);
	}
}

static void
loadlines(Biobuf *bf, Map *m)
{
	Line *l;

	m->nlines = get16(bf);
	m->lines = emalloc(m->nlines * sizeof *m->lines);
	for(l=m->lines; l<m->lines+m->nlines; l++){
		l->min.x = get8(bf) << Fineshift;
		l->min.y = get8(bf) << Fineshift;
		l->max.x = get8(bf) << Fineshift;
		l->max.y = get8(bf) << Fineshift;
		l->tex = get16(bf);	/* FIXME: actual Wall* */
		l->flags = get16(bf);
		if(l->flags & LFmirrored)
			l->minpshift = max(Dx(l->Rectangle), Dy(l->Rectangle));
		else
			l->maxpshift = max(Dx(l->Rectangle), Dy(l->Rectangle));
		if(l->flags & (LFxshift | LFshiftS⋁E))
			l->Rectangle = rectaddpt(l->Rectangle, Pt(3,0));
		else if(l->flags & (LFxshift | LFshiftN∨W))
			l->Rectangle = rectsubpt(l->Rectangle, Pt(3,0));
		else if(l->flags & (LFyshift | LFshiftS⋁E))
			l->Rectangle = rectaddpt(l->Rectangle, Pt(0,3));
		else if(l->flags & (LFyshift | LFshiftN∨W))
			l->Rectangle = rectsubpt(l->Rectangle, Pt(0,3));
		/* FIXME: no way to know why until we properly process walls to dump them */
		if(l->tex == 7){ }	/* FIXME */
	}
}

static void
loadmap(char *name)
{
	Biobuf *bf;
	Map *m;

	bf = eopen(name, OREAD);
	m = &map;
	m->ceilc = 0xff << 24 | get24(bf);
	m->floorc = 0xff << 24 | get24(bf);
	m->backc = 0xff << 24 | get24(bf);
	m->id = get8(bf);
	m->unkn1 = get16(bf);
	m->unkn2 = get8(bf);
	loadbsp(bf, m);
	loadlines(bf, m);

	/* FIXME: things, events, commands */

	Bterm(bf);
}

static void
loadsintab(void)
{
	Biobuf *bf;
	s32int *s;

	bf = eopen("sintable.bin", OREAD);
	for(s=sintab; s<sintab+nelem(sintab); s++)
		*s = get32(bf);
	Bterm(bf);
}

void
initfs(void)
{
	rfork(RFNAMEG);
	if(bind(".", prefix, MBEFORE|MCREATE) == -1 || chdir(prefix) < 0)
		fprint(2, "initfs: %r\n");
	loadsintab();
	loadpal();
	loadbasestr();
	loadfontmap();
	// FIXME: walls: same processing as sprites
	loadwalls();
	loadsprites();
	loadmap("intro.bsp");
}