shithub: wl3d

Download patch

ref: a6fe5b8c177c72c40efd76c6bf255897854bb76f
parent: eb9bc6ef4f8fd2595ec79e6469a1efd7b317fb28
author: qwx <>
date: Sat Jul 22 21:11:04 EDT 2017

add save/load game

- hub: fix blinking prompts and cursor initial state
- gm: use WPnone constant to indicate hiding weapon
- gm: remove crmmap
- map: reset boss die states' dt whenever loading map
- wl3d(6): misc fixes and document save format
- savegame format is incompatible; in particular, at the expense of
  a 16-32kB file size, it contains the current state of the map,
  including both map planes; this allows us to just load everything
  once instead of loading map from disk, spawning everything, then
  nuking most of the state and retrieving that from the save

--- a/dat.h
+++ b/dat.h
@@ -1199,6 +1199,7 @@
 enum{
 	Mapdxy = 64,
 	Mapa = Mapdxy * Mapdxy,
+
 	MTgoup = 21,
 	MTarrows = 90,
 	MTpush = 98,
@@ -1206,6 +1207,11 @@
 	MTambush = 106,
 	MTsetec = 107,
 	MTfloor = MTsetec,
+
+	Nobj = 150,
+	Ndoor = 64,
+	Nstc = 400,
+	Narea = 37
 };
 struct Tile{
 	u16int p0;
@@ -1231,6 +1237,7 @@
 	WPpistol,
 	WPmg,
 	WPgatling,
+	WPnone,
 
 	GMup = 0,
 	GMsetec,
@@ -1280,10 +1287,10 @@
 	int com;
 	int demo;
 	int record;
-	int load;
 };
 extern Game gm;
 extern int allrecv, god, noclip, slomo;
+extern int loaded;
 
 struct Score{
 	char name[58];
--- a/drw.c
+++ b/drw.c
@@ -422,6 +422,16 @@
 }
 
 void
+disking(void)
+{
+	static int n;
+
+	pic(104, 85, pict[Pread1] + n);
+	out();
+	n ^= 1;
+}
+
+void
 viewbox(void)
 {
 	int x, y;
--- a/fns.h
+++ b/fns.h
@@ -2,6 +2,8 @@
 void	grab(int);
 void	toss(void);
 void	flush(void);
+int	wrsav(int);
+int	ldsav(int);
 char*	demof(char*);
 u16int*	readmap(int);
 void	dat(char*);
@@ -30,6 +32,7 @@
 void	sdstripe(int);
 void	outbox(int, int, int, int, int, int);
 void	box(int, int, int, int, int, int, int);
+void	disking(void);
 void	viewbox(void);
 void	hudf(void);
 void	hudh(void);
@@ -59,6 +62,8 @@
 Obj*	ospawn(Tile*, State*);
 void	uworld(void);
 void	mapmus(void);
+uchar*	wrmap(uchar*);
+int	ldmap(uchar*, uchar**);
 void	initmap(void);
 void	sodmap(void);
 void	dieturn(void);
@@ -74,6 +79,8 @@
 void	nextmap(void);
 void	game(void);
 void	spshunt(void);
+uchar*	wrgm(uchar*);
+uchar*	ldgm(uchar*);
 void	greset(void);
 void	ginit(uchar*, int, int);
 uchar*	opl2out(uchar*, int);
@@ -87,3 +94,10 @@
 void	stopmus(void);
 void	mus(int);
 void	initsnd(void);
+
+#define	GET8(p)	((p)[0]);(p)++
+#define	GET16(p)	((p)[0]|((p)[1]<<8));(p)+=2
+#define	GET32(p)	((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24));(p)+=4
+#define	PUT8(p,v)	(p)[0]=(v);(p)++
+#define	PUT16(p,v)	(p)[0]=(v);(p)[1]=(v)>>8;(p)+=2
+#define	PUT32(p,v)	(p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24;(p)+=4
--- a/fs.c
+++ b/fs.c
@@ -444,7 +444,15 @@
 enum{
 	Nplane = 2,
 	Planesz = Mapa * Nplane,
-	Mapsz = Planesz * Nplane
+	Mapsz = Planesz * Nplane,
+	Svgmsz = 2+2+4+4+4+17*2+4+4+4+2+2+2+4+4+1+1,
+	Svmapsz = Mapa * (2+2+1+1) + Narea + Narea * Narea,
+	Svobjsz = 2+2+2+2+1+4+4+4+2+2+1+2+2+4+2+2+4+2+2,
+	Svstsz = 2+2+1+1,
+	Svdrsz = 2+1+1+2+2+2,
+	Svpshsz = 2+2+2+2,
+	Svsz = sizeof(savs[0]) + Svgmsz + Svmapsz + Svobjsz + 2 + 2 + 2 + Svpshsz,
+	Svmax = Svsz + Nobj * Svobjsz + (Nstc-1) * Svstsz + (Ndoor-1) * Svdrsz
 };
 static Dat *pcms;
 static int alofs, npcm;
@@ -462,11 +470,22 @@
 	snprint(s, sizeof s, "%s%s", f, ext);
 	bf = Bopen(s, m);
 	if(bf == nil)
-		sysfatal("bopen: %r");
+		return nil;
 	Blethal(bf, nil);
 	return bf;
 }
 
+static Biobuf *
+eopen(char *f, int m)
+{
+	Biobuf *bf;
+
+	bf = bopen(f, m);
+	if(bf == nil)
+		sysfatal("bopen: %r");
+	return bf;
+}
+
 static long
 eread(Biobuf *bf, void *u, long n)
 {
@@ -737,7 +756,7 @@
 	u16int *szs, *szp;
 	Biobuf *bf;
 
-	bf = bopen("vswap.", OREAD);
+	bf = eopen("vswap.", OREAD);
 	n = get16(bf);
 	so = get16(bf);
 	po = get16(bf);
@@ -764,7 +783,7 @@
 	u32int v, *d;
 	Biobuf *hed;
 
-	hed = bopen("maphead.", OREAD);
+	hed = eopen("maphead.", OREAD);
 	n = ver==WL6 ? 60 : ver==WL1 ? 10 : ver==SDM ? 2 : 21;
 	rlewtag = get16(hed);
 	d = mapofs = emalloc(n * sizeof *mapofs);
@@ -779,6 +798,24 @@
 }
 
 static void
+savnames(void)
+{
+	int n;
+	char u[sizeof savs[0]], (*t)[sizeof savs[0]], s[10] = "savegam?.";
+	Biobuf *bf;
+
+	for(n='0', t=savs; n<='9'; n++, t++){
+		s[7] = n;
+		bf = bopen(s, OREAD);
+		if(bf == nil)
+			continue;
+		if(Bread(bf, u, sizeof(u)-1) == sizeof(u)-1)
+			memcpy(t, u, sizeof(u)-1);
+		Bterm(bf);
+	}
+}
+
+static void
 swap(Sfx *a, Sfx *b)
 {
 	Sfx c;
@@ -864,8 +901,8 @@
 	int n;
 	Biobuf *hed, *dat;
 
-	hed = bopen("audiohed.", OREAD);
-	dat = bopen("audiot.", OREAD);
+	hed = eopen("audiohed.", OREAD);
+	dat = eopen("audiot.", OREAD);
 	n = ver < SDM ? Send : Ssend;
 	Bseek(hed, n*4, 0);
 	al(dat, hed, n);
@@ -982,13 +1019,13 @@
 	u16int hf[512], *h;
 	Biobuf *dat, *aux;
 
-	aux = bopen("vgadict.", OREAD);
+	aux = eopen("vgadict.", OREAD);
 	for(h=hf; h<hf+nelem(hf); h++)
 		*h = get16(aux);
 	Bterm(aux);
 
-	aux = bopen("vgahead.", OREAD);
-	dat = bopen("vgagraph.", OREAD);
+	aux = eopen("vgahead.", OREAD);
+	dat = eopen("vgagraph.", OREAD);
 	n = piched(dat, aux, hf);
 	getfnts(dat, aux, hf);
 	getpics(dat, aux, hf, n);
@@ -1004,7 +1041,7 @@
 	Biobuf *bf;
 
 	ext = ver < SDM ? "wl6" : "sod";
-	bf = bopen("intro.", OREAD);
+	bf = eopen("intro.", OREAD);
 	eread(bf, pxb, Va);
 	out();
 	Bterm(bf);
@@ -1034,6 +1071,55 @@
 	}
 }
 
+int
+wrsav(int i)
+{
+	int r;
+	vlong n;
+	uchar *u, *p;
+	char s[10] = "savegam?.";
+	Biobuf *bf;
+
+	s[7] = '0' + i;
+	bf = eopen(s, OWRITE);
+	u = emalloc(Svmax);
+	memcpy(u, savs[i], sizeof savs[0]);
+	p = wrgm(u + sizeof savs[0]);
+	p = wrmap(p);
+	assert(p <= u + Svmax);
+	n = p - u;
+	r = Bwrite(bf, u, n) != n ? -1 : 0;
+	Bterm(bf);
+	free(u);
+	return r;
+}
+
+int
+ldsav(int i)
+{
+	int r;
+	vlong n;
+	uchar *u, *p;
+	char s[10] = "savegam?.";
+	Biobuf *bf;
+
+	s[7] = '0' + i;
+	bf = eopen(s, OREAD);
+	n = bsize(bf);
+	if(n < Svsz){
+		werrstr("ldsav: short map");
+		return -1;
+	}
+	u = emalloc(n);
+	eread(bf, u, n);
+	Bterm(bf);
+	p = ldgm(u + sizeof savs[0]);
+	r = ldmap(p, &p);
+	assert(p <= u + n);
+	free(u);
+	return r;
+}
+
 u16int *
 readmap(int n)
 {
@@ -1044,7 +1130,7 @@
 	m = mapofs + n;
 	if(m >= mape)
 		sysfatal("readmap: invalid map number %d", n);
-	dat = bopen("gamemaps.", OREAD);
+	dat = eopen("gamemaps.", OREAD);
 	Bseek(dat, *m, 0);
 	u = emalloc(Mapsz);
 	p0 = get32(dat);
@@ -1061,18 +1147,18 @@
 char *
 demof(char *f)
 {
-	char *p;
+	char *p, *e;
 	vlong n;
 	Biobuf *bf;
 
-	bf = Bopen(f, OREAD);
-	if(bf == nil)
-		sysfatal("demof: %r");
-	Blethal(bf, nil);
+	e = ext;
+	ext = "";
+	bf = eopen(f, OREAD);
 	n = bsize(bf);
 	p = emalloc(n);
 	eread(bf, p, n);
 	Bterm(bf);
+	ext = e;
 	return p;
 }
 
@@ -1089,6 +1175,7 @@
 	ext = e;
 	vswap();
 	gamemaps();
+	savnames();
 	if(ver == SOD)
 		ext = "sod";
 	audiot();
--- a/gm.c
+++ b/gm.c
@@ -28,6 +28,7 @@
 Game gm;
 int msense;
 int allrecv, god, noclip, slomo;
+int loaded;
 
 typedef struct Crm Crm;
 enum{
@@ -2259,10 +2260,6 @@
 	crmchop();
 }
 static void
-crmmap(void)
-{
-}
-static void
 crmgod(void)
 {
 	god ^= 1;
@@ -2331,7 +2328,7 @@
 	int θ, lrot, rrot;
 	double fθ;
 
-	gm.w = -1;
+	gm.w = WPnone;
 	gm.lives--;
 	stopmus();
 	sfx(Sdie);
@@ -2521,7 +2518,6 @@
 		{"opeopn", crmkey},
 		{"opephz", crmwep},
 		{"opemli", crmmli},
-		{"opepda", crmmap},
 		{"opedqd", crmgod},
 		{"opeclp", crmclp},
 		{"opeslo", crmslo},
@@ -2641,10 +2637,12 @@
 void
 game(void)
 {
-	initmap();
-	killx = oplr->x;
-	killy = oplr->y;
-	mapmus();
+	if(!loaded){
+		initmap();
+		killx = oplr->x;
+		killy = oplr->y;
+		mapmus();
+	}
 	pal = pals[C0];
 	dofizz++;
 	step = gstep;
@@ -2666,10 +2664,86 @@
 	oplr->areaid = oplr->tl->p0 - MTfloor;
 }
 
+uchar *
+wrgm(uchar *p)
+{
+	disking();
+	PUT16(p, gm.difc);
+	PUT16(p, gm.map);
+	PUT32(p, gm.oldpt);
+	PUT32(p, gm.pt);
+	PUT32(p, gm.to1up);
+	PUT16(p, gm.lives);
+	PUT16(p, gm.hp);
+	PUT16(p, gm.ammo);
+	PUT16(p, gm.keys);
+	PUT16(p, gm.bestw);
+	PUT16(p, gm.w);
+	PUT16(p, gm.lastw);
+	PUT16(p, gm.facefrm);
+	PUT16(p, atkfrm);
+	PUT16(p, atktc);
+	PUT16(p, gm.wfrm);
+	PUT16(p, gm.sp);
+	PUT16(p, gm.tp);
+	PUT16(p, gm.kp);
+	PUT16(p, gm.stot);
+	PUT16(p, gm.ttot);
+	PUT16(p, gm.ktot);
+	PUT32(p, gm.tc);
+	PUT32(p, killx);
+	PUT32(p, killy);
+	PUT16(p, gm.epk);
+	PUT16(p, gm.eps);
+	PUT16(p, gm.ept);
+	PUT32(p, gm.eptm);
+	PUT8(p, dirty);
+	PUT8(p, firing);
+	return p;
+}
+
+uchar *
+ldgm(uchar *p)
+{
+	disking();
+	gm.difc = GET16(p);
+	gm.map = GET16(p);
+	gm.oldpt = GET32(p);
+	gm.pt = GET32(p);
+	gm.to1up = GET32(p);
+	gm.lives = GET16(p);
+	gm.hp = GET16(p);
+	gm.ammo = GET16(p);
+	gm.keys = GET16(p);
+	gm.bestw = GET16(p);
+	gm.w = GET16(p);
+	gm.lastw = GET16(p);
+	gm.facefrm = GET16(p);
+	atkfrm = GET16(p);
+	atktc = (s16int)GET16(p);
+	gm.wfrm = GET16(p);
+	gm.sp = GET16(p);
+	gm.tp = GET16(p);
+	gm.kp = GET16(p);
+	gm.stot = GET16(p);
+	gm.ttot = GET16(p);
+	gm.ktot = GET16(p);
+	gm.tc = GET32(p);
+	killx = GET32(p);
+	killy = GET32(p);
+	gm.epk = GET16(p);
+	gm.eps = GET16(p);
+	gm.ept = GET16(p);
+	gm.eptm = GET32(p);
+	dirty = GET8(p);
+	firing = GET8(p);
+	return p;
+}
+
 void
 greset(void)
 {
-	if(gm.w == -1){
+	if(gm.w == WPnone){
 		gm.hp = 100;
 		gm.ammo = 8;
 		gm.w = gm.lastw = gm.bestw = WPpistol;
@@ -2693,6 +2767,7 @@
 	slomo = noclip = god = 0;
 	if(ver == SOD && gm.map == 20)
 		givek(0);
+	loaded = 0;
 }
 
 void
--- a/hub.c
+++ b/hub.c
@@ -1,5 +1,6 @@
 #include <u.h>
 #include <libc.h>
+#include <ctype.h>
 #include <thread.h>
 #include <keyboard.h>
 #include "dat.h"
@@ -17,7 +18,7 @@
 	{"John Romero", 10000, 1},
 	{"Jay Wilbur", 10000, 1},
 };
-char savs[10][32];
+char savs[11][32];
 
 typedef struct Fade Fade;
 typedef struct Sp Sp;
@@ -107,14 +108,22 @@
 	Lfsav,
 	Lmsav,
 	Lsvctl,
+	Lsvname,
+	Lwrsav,
 	Lflod,
 	Lmlod,
 	Lldctl,
+	Lldsav,
+	Lldsav2,
+	Lldsav3,
+	Lldsav4,
+	Lldsav5,
 	Lfvw,
 	Lmvw,
 	Lvwctl,
 	Lfmscore,
 	Lmscore,
+	Lovrsav,
 	Lend,
 	Lcurgame,
 	Lquit,
@@ -305,12 +314,21 @@
 static void
 pblink(void)
 {
-	if(mp->n == 0)
+	if(mp->n ^= 1)
+		put(mp->x, mp->y, fnt->w['_'], fnt->h, DIreg);
+	else
 		txt(mp->x, mp->y, "_", 0);
+	out();
+}
+
+static void
+iblink(void)
+{
+	if(mp->n ^= 1)
+		txt(111 + irb - 1, mp->y + 3, "\x80", DIrhi);
 	else
-		put(mp->x, mp->y, fnt->w['_'], fnt->h, DIreg);
+		put(111 + irb - 1, mp->y + 3, fnt->w[0x80]-1, fnt->h, mcol[DMbg]);
 	out();
-	mp->n ^= 1;
 }
 
 static void
@@ -318,8 +336,8 @@
 {
 	put(mp->x, mp->y, 24, 16, mcol[DMbg]);
 	pic(mp->x, mp->y, pict[Pcur1] + mp->n);
-	out();
 	mp->n ^= 1;
+	out();
 }
 
 static void
@@ -368,6 +386,7 @@
 
 	m = ml+LMctl;
 	i = m->s;
+	i[3].c = DIreg;
 	i[4].c = mcol[DMoff];
 	i[6].s = "View Scores";
 	i[6].q = ql+Lfmscore;
@@ -467,6 +486,15 @@
 }
 
 static void
+ovrsav(void)
+{
+	msg("There's already a game\n"
+		"saved at this position.\n      Overwrite?", 0);
+	mp->p->q = ql+Lsvname;
+	qesc = ql+Lsvctl;
+}
+
+static void
 denied(void)
 {
 	msg("Please select \"Read This!\"\nfrom the Options menu to\n"
@@ -624,15 +652,61 @@
 }
 
 static void
-savitem(int i, int c)
+disk(void)
 {
-	outbox(109, 55 + i * 13, 136, 11, c, c);
-	fnt = fnts;
-	txt(111, 56 + i * 13, savs[i][0] == 0 ? "      - empty -" : savs[i], c);
+	int n;
+
+	box(96, 80, 130, 42, DIreg, 0, DIrhi);
+	pic(104, 85, pict[Pread1]);
 	fnt = fnts+1;
+	txt(142, 93, qsp == ql+Lwrsav ? "Saving..." : "Loading...", 0);
+	out();
+	n = mp->p - mp->s;
+	if(qsp == ql+Lwrsav){
+		qsp->q = ql+Lftoctl;
+		if(wrsav(n) < 0)
+			goto err;
+	}else{
+		qsp->q = ql+Lldsav2;
+		ginit(nil, -1, 0);
+		greset();
+		if(ldsav(n) < 0)
+			goto err;
+		ingctl();
+		loaded++;
+	}
+	sfx(Sshoot);
+	return;
+err:
+	memset(savs[n], 0, sizeof savs[0]);
+	qsp->q = ql+Lftoctl;
+	sfx(Snoway);
 }
 
 static void
+savtxt(Item *i, char *s)
+{
+	int n;
+
+	n = 56 + (i - mp->s) * 13;
+	put(110, n, 135, fnt->h, mcol[DMbg]);
+	txt(111, n, s, i->c);
+}
+
+static void
+savitem(Item *i, int n)
+{
+	outbox(109, 55 + n * 13, 136, 11, i->c, i->c);
+	if(savs[n][0] != 0){
+		savtxt(i, savs[n]);
+		i->q = qsp->q == ql+Lsvctl ? ql+Lovrsav : ql+Lldsav;
+	}else{
+		savtxt(i, "      - empty -");
+		i->q = qsp->q == ql+Lsvctl ? ql+Lsvname : nil;
+	}
+}
+
+static void
 sav(void)
 {
 	Menu *m;
@@ -642,13 +716,14 @@
 	pic(112, 184, pict[Pmouselback]);
 	mbox(75, 50, 175, 140);
 	stripe(10);
-	pic(56, 0, pict[qsp->q == ql+Lsvctl ? Psave : Pload]);
+	pic(56, 0, pict[qsp->q == ql+Lldctl ? Pload : Psave]);
 	m = ml+LMsav;
 	mp = m;
 	i = m->s;
 	e = m->e;
+	fnt = fnts;
 	do
-		savitem(i - m->s, i->c);
+		savitem(i, i - m->s);
 	while(++i < e);
 	qesc = ql+Lftoctl;
 	m->n = 0;
@@ -656,6 +731,17 @@
 }
 
 static void
+savname(void)
+{
+	sav();
+	memcpy(savs[10], savs[mp->p - mp->s], sizeof savs[10]);
+	savtxt(mp->p, savs[10]);
+	iri = strlen(savs[10]);
+	irb = txtw(savs[10]);
+	mp->n = 0;
+}
+
+static void
 mvw(void)
 {
 	int dx, dy;
@@ -825,6 +911,69 @@
 }
 
 static void
+prompt(void)
+{
+	int n, m;
+	char *s;
+	Rune r;
+
+	if(nbrecv(csc, &r) <= 0)
+		return;
+	s = savs[10];
+	n = strlen(s);
+	switch(r){
+	redraw:
+		savtxt(mp->p, savs[10]);
+		mp->n = 0;
+		iblink();
+		qtc = 0;
+		break;
+	default:
+		if(runelen(r) > 1 || !isprint(r))
+			break;
+		m = fnt->w[r];
+		if(txtw(s) + m > 134 || n == sizeof(savs[10]) - 1)
+			break;
+		while(n-- > iri)
+			s[n+1] = s[n];
+		s[iri++] = r;
+		irb += m;
+		goto redraw;
+	case Kleft:
+		if(iri == 0)
+			break;
+		irb -= fnt->w[s[--iri]];
+		goto redraw;
+	case Kright:
+		if(iri == n)
+			break;
+		irb += fnt->w[s[iri++]];
+		goto redraw;
+	case Kdel:
+	case Kbs:
+		if(iri == 0)
+			break;
+		irb -= fnt->w[s[--iri]];	
+		for(m=iri+1; m<=n; m++)
+			s[m-1] = s[m];
+		s[m] = 0;
+		goto redraw;
+	abort:
+	case Kesc:
+		sfx(Sesc);
+		reset(ql+Lsvctl);
+		break;
+	case '\n':
+		if(n == 0)
+			goto abort;
+		strcpy(savs[mp->p - mp->s], savs[10]);
+		sfx(Sshoot);
+		reset(ql+Lwrsav);
+		break;
+	}
+}
+
+static void
 ask(void)
 {
 	Rune r;
@@ -948,7 +1097,6 @@
 		a[3] = 0;
 		txt(241, 72, a, DIshi);
 	}
-	fnt = fnts+1;
 	mus(ver < SDM ? Mwon : Msdwon);
 	grab(0);
 }
@@ -1246,7 +1394,8 @@
 static void
 ingam(void)
 {
-	greset();
+	if(qsp != ql+Lldsav4)
+		greset();
 	view();
 }
 
@@ -1434,6 +1583,8 @@
 	slq[] = {{1, slider}},
 	curq[] = {{8, nil}, {0, cursfx}},
 	togq[] = {{1, toggle}},
+	promptq[] = {{0, iblink}, {Tb / 2, prompt}},
+	diskq[] = {{1, disk}, {1, nil}},	/* buffer extra tics */
 	mscoreq[] = {{10, fadein}, {1, bwait}, {10, fadeout}},
 	quitq[] = {{0, pblink}, {10, ask}},
 	ackq[] = {{1, bwait}},
@@ -1509,9 +1660,15 @@
 	[Lfsav] {nil, escq, escq+nelem(escq), ql+Lmsav, &fctl},
 	[Lmsav] {sav, toctlq, toctlq+nelem(toctlq), ql+Lsvctl, &fctl},
 	[Lsvctl] {sav, ctlq, ctlq+nelem(ctlq), ql+Lsvctl},
+	[Lsvname] {savname, promptq, promptq+nelem(promptq), ql+Lsvname},
+	[Lwrsav] {nil, diskq, diskq+nelem(diskq), nil},
 	[Lflod] {nil, escq, escq+nelem(escq), ql+Lmlod, &fctl},
 	[Lmlod] {sav, toctlq, toctlq+nelem(toctlq), ql+Lldctl, &fctl},
 	[Lldctl] {sav, ctlq, ctlq+nelem(ctlq), ql+Lldctl},
+	[Lldsav] {nil, diskq, diskq+nelem(diskq), nil},
+	[Lldsav2] {nil, escq, escq+nelem(escq), ql+Lldsav3, &fctl},
+	[Lldsav3] {nil, loadq, loadq+nelem(loadq), ql+Lldsav4, &fblk},
+	[Lldsav4] {psych, psychq, psychq+nelem(psychq), ql+Lgame, &fblk},
 	[Lfsens] {nil, escq, escq+nelem(escq), ql+Lmsens, &fctl},
 	[Lmsens] {sens, toctlq, toctlq+nelem(toctlq), ql+Lsectl, &fctl},
 	[Lsectl] {sens, slq, slq+nelem(slq), ql+Lsectl},
@@ -1520,6 +1677,7 @@
 	[Lvwctl] {mvw, slq, slq+nelem(slq), ql+Lvwctl},
 	[Lfmscore] {nil, escq, escq+nelem(escq), ql+Lmscore, &fctl},
 	[Lmscore] {score, mscoreq, mscoreq+nelem(mscoreq), ql+Lmtoctl, &fctl},
+	[Lovrsav] {ovrsav, quitq, quitq+nelem(quitq), ql+Lovrsav},
 	[Lend] {mend, quitq, quitq+nelem(quitq), ql+Lend},
 	[Lcurgame] {curgame, quitq, quitq+nelem(quitq), ql+Lcurgame},
 	[Lquit] {quit, quitq, quitq+nelem(quitq), ql+Lquit},
--- a/main.c
+++ b/main.c
@@ -122,237 +122,6 @@
 	}
 }
 
-void DiskFlopAnim(s16int x,s16int y)
-{
- static char which=0;
- if (!x && !y)
-   return;
- VWB_DrawPic(x,y,Pread1+which);
- VW_UpdateScreen();
- which^=1;
-}
-
-
-s32int DoChecksum(u8int far *source,u16int size,s32int checksum)
-{
- u16int i;
-
- for (i=0;i<size-1;i++)
-   checksum += source[i]^source[i+1];
-
- return checksum;
-}
-
-int SaveTheGame(s16int file,s16int x,s16int y)
-{
-	struct diskfree_t dfree;
-	s32int avail,size,checksum;
-	objtype *ob,nullobj;
-
-
-	if (_dos_getdiskfree(0,&dfree))
-	  Quit("Error in _dos_getdiskfree call");
-
-	avail = (s32int)dfree.avail_clusters *
-			dfree.bytes_per_sector *
-			dfree.sectors_per_cluster;
-
-	size = 0;
-	for (ob = player; ob ; ob=ob->next)
-	  size += sizeof(*ob);
-	size += sizeof(nullobj);
-
-	size += sizeof(gamestate) +
-			sizeof(LRstruct)*8 +
-			sizeof(tilemap) +
-			sizeof(actorat) +
-			sizeof(laststatobj) +
-			sizeof(statobjlist) +
-			sizeof(doorposition) +
-			sizeof(pwallstate) +
-			sizeof(pwallx) +
-			sizeof(pwally) +
-			sizeof(pwalldir) +
-			sizeof(pwallpos);
-
-	if (avail < size)
-	{
-	 Message("There is not enough space\n"
-		"on your disk to Save Game!");
-	 return false;
-	}
-
-	checksum = 0;
-
-
-	DiskFlopAnim(x,y);
-	CA_FarWrite (file,(void far *)&gamestate,sizeof(gamestate));
-	checksum = DoChecksum((u8int far *)&gamestate,sizeof(gamestate),checksum);
-
-	DiskFlopAnim(x,y);
-#ifdef SPEAR
-	CA_FarWrite (file,(void far *)&LevelRatios[0],sizeof(LRstruct)*20);
-	checksum = DoChecksum((u8int far *)&LevelRatios[0],sizeof(LRstruct)*20,checksum);
-#else
-	CA_FarWrite (file,(void far *)&LevelRatios[0],sizeof(LRstruct)*8);
-	checksum = DoChecksum((u8int far *)&LevelRatios[0],sizeof(LRstruct)*8,checksum);
-#endif
-
-	DiskFlopAnim(x,y);
-	CA_FarWrite (file,(void far *)tilemap,sizeof(tilemap));
-	checksum = DoChecksum((u8int far *)tilemap,sizeof(tilemap),checksum);
-	DiskFlopAnim(x,y);
-	CA_FarWrite (file,(void far *)actorat,sizeof(actorat));
-	checksum = DoChecksum((u8int far *)actorat,sizeof(actorat),checksum);
-
-	CA_FarWrite (file,(void far *)conarea,sizeof(conarea));
-	CA_FarWrite (file,(void far *)plrarea,sizeof(plrarea));
-
-	for (ob = player ; ob ; ob=ob->next)
-	{
-	 DiskFlopAnim(x,y);
-	 CA_FarWrite (file,(void far *)ob,sizeof(*ob));
-	}
-	nullobj.active = ac_badobject;          // end of file marker
-	DiskFlopAnim(x,y);
-	CA_FarWrite (file,(void far *)&nullobj,sizeof(nullobj));
-
-
-
-	DiskFlopAnim(x,y);
-	CA_FarWrite (file,(void far *)&laststatobj,sizeof(laststatobj));
-	checksum = DoChecksum((u8int far *)&laststatobj,sizeof(laststatobj),checksum);
-	DiskFlopAnim(x,y);
-	CA_FarWrite (file,(void far *)statobjlist,sizeof(statobjlist));
-	checksum = DoChecksum((u8int far *)statobjlist,sizeof(statobjlist),checksum);
-
-	DiskFlopAnim(x,y);
-	CA_FarWrite (file,(void far *)doorposition,sizeof(doorposition));
-	checksum = DoChecksum((u8int far *)doorposition,sizeof(doorposition),checksum);
-	DiskFlopAnim(x,y);
-	CA_FarWrite (file,(void far *)doorobjlist,sizeof(doorobjlist));
-	checksum = DoChecksum((u8int far *)doorobjlist,sizeof(doorobjlist),checksum);
-
-	DiskFlopAnim(x,y);
-	CA_FarWrite (file,(void far *)&pwallstate,sizeof(pwallstate));
-	checksum = DoChecksum((u8int far *)&pwallstate,sizeof(pwallstate),checksum);
-	CA_FarWrite (file,(void far *)&pwallx,sizeof(pwallx));
-	checksum = DoChecksum((u8int far *)&pwallx,sizeof(pwallx),checksum);
-	CA_FarWrite (file,(void far *)&pwally,sizeof(pwally));
-	checksum = DoChecksum((u8int far *)&pwally,sizeof(pwally),checksum);
-	CA_FarWrite (file,(void far *)&pwalldir,sizeof(pwalldir));
-	checksum = DoChecksum((u8int far *)&pwalldir,sizeof(pwalldir),checksum);
-	CA_FarWrite (file,(void far *)&pwallpos,sizeof(pwallpos));
-	checksum = DoChecksum((u8int far *)&pwallpos,sizeof(pwallpos),checksum);
-
-	//
-	// WRITE OUT CHECKSUM
-	//
-	CA_FarWrite (file,(void far *)&checksum,sizeof(checksum));
-
-	return(true);
-}
-
-int LoadTheGame(s16int file,s16int x,s16int y)
-{
-	s32int checksum,oldchecksum;
-	objtype *ob,nullobj;
-
-
-	checksum = 0;
-
-	DiskFlopAnim(x,y);
-	CA_FarRead (file,(void far *)&gamestate,sizeof(gamestate));
-	checksum = DoChecksum((u8int far *)&gamestate,sizeof(gamestate),checksum);
-
-	DiskFlopAnim(x,y);
-#ifdef SPEAR
-	CA_FarRead (file,(void far *)&LevelRatios[0],sizeof(LRstruct)*20);
-	checksum = DoChecksum((u8int far *)&LevelRatios[0],sizeof(LRstruct)*20,checksum);
-#else
-	CA_FarRead (file,(void far *)&LevelRatios[0],sizeof(LRstruct)*8);
-	checksum = DoChecksum((u8int far *)&LevelRatios[0],sizeof(LRstruct)*8,checksum);
-#endif
-
-	DiskFlopAnim(x,y);
-	initmap ();
-
-	DiskFlopAnim(x,y);
-	CA_FarRead (file,(void far *)tilemap,sizeof(tilemap));
-	checksum = DoChecksum((u8int far *)tilemap,sizeof(tilemap),checksum);
-	DiskFlopAnim(x,y);
-	CA_FarRead (file,(void far *)actorat,sizeof(actorat));
-	checksum = DoChecksum((u8int far *)actorat,sizeof(actorat),checksum);
-
-	CA_FarRead (file,(void far *)conarea,sizeof(conarea));
-	CA_FarRead (file,(void far *)plrarea,sizeof(plrarea));
-
-
-
-	oinit ();
-	DiskFlopAnim(x,y);
-	CA_FarRead (file,(void far *)player,sizeof(*player));
-
-	while (1)
-	{
-	 DiskFlopAnim(x,y);
-		CA_FarRead (file,(void far *)&nullobj,sizeof(nullobj));
-		if (nullobj.active == ac_badobject)
-			break;
-		onew ();
-	 // don't copy over the links
-		memcpy (new,&nullobj,sizeof(nullobj)-4);
-	}
-
-
-
-	DiskFlopAnim(x,y);
-	CA_FarRead (file,(void far *)&laststatobj,sizeof(laststatobj));
-	checksum = DoChecksum((u8int far *)&laststatobj,sizeof(laststatobj),checksum);
-	DiskFlopAnim(x,y);
-	CA_FarRead (file,(void far *)statobjlist,sizeof(statobjlist));
-	checksum = DoChecksum((u8int far *)statobjlist,sizeof(statobjlist),checksum);
-
-	DiskFlopAnim(x,y);
-	CA_FarRead (file,(void far *)doorposition,sizeof(doorposition));
-	checksum = DoChecksum((u8int far *)doorposition,sizeof(doorposition),checksum);
-	DiskFlopAnim(x,y);
-	CA_FarRead (file,(void far *)doorobjlist,sizeof(doorobjlist));
-	checksum = DoChecksum((u8int far *)doorobjlist,sizeof(doorobjlist),checksum);
-
-	DiskFlopAnim(x,y);
-	CA_FarRead (file,(void far *)&pwallstate,sizeof(pwallstate));
-	checksum = DoChecksum((u8int far *)&pwallstate,sizeof(pwallstate),checksum);
-	CA_FarRead (file,(void far *)&pwallx,sizeof(pwallx));
-	checksum = DoChecksum((u8int far *)&pwallx,sizeof(pwallx),checksum);
-	CA_FarRead (file,(void far *)&pwally,sizeof(pwally));
-	checksum = DoChecksum((u8int far *)&pwally,sizeof(pwally),checksum);
-	CA_FarRead (file,(void far *)&pwalldir,sizeof(pwalldir));
-	checksum = DoChecksum((u8int far *)&pwalldir,sizeof(pwalldir),checksum);
-	CA_FarRead (file,(void far *)&pwallpos,sizeof(pwallpos));
-	checksum = DoChecksum((u8int far *)&pwallpos,sizeof(pwallpos),checksum);
-
-	CA_FarRead (file,(void far *)&oldchecksum,sizeof(oldchecksum));
-
-	if (oldchecksum != checksum)
-	{
-		Message("Your Save Game file is,\n"
-		"shall we say, \"corrupted\".\n"
-		"But I'll let you go on and\n"
-		"play anyway....");
-
-		IN_ClearKeysDown();
-		IN_Ack();
-
-		gm.score = 0;
-		gm.lives = 1;
-		gm.w = gm.bestw = gm.lastw = WPpistol;
-		gm.ammo = 8;
-	}
-
-	return true;
-}
-
 void    DemoLoop (void)
 {
 	while (1)
--- a/man/1/wl3d
+++ b/man/1/wl3d
@@ -31,7 +31,8 @@
 .RE
 .PP
 The command line options are:
-.TP \w'\fLw\ \ \fI"map\ 0-3"'u
+.TF "-w map 0-3"
+.TP
 .B -2
 Set game version to Spear of Destiny Mission 2: Return to Danger.
 .TP
@@ -84,9 +85,9 @@
 flags (see
 .IR bind (2)).
 Data files can thus be contained in a system directory while the config and save files' location, which are user-specific, can be left at the user's discretion.
-If these user-specific files exist and
+If the configuration file exists and
 .I wl3d
-fails to parse them, they are not overwritten.
+fails to parse it, it is not overwritten.
 The
 .B -m
 parameter is used to change
--- a/man/6/wl3d
+++ b/man/6/wl3d
@@ -48,7 +48,9 @@
 In the notation for file and lump formats below, the number of bytes in a field is given in brackets after the field name.
 The notation
 .IR a [ s ]
-denotes an unterminated array of
+denotes an unterminated array
+.I a
+of
 .I s
 .SM ASCII
 characters.
@@ -244,7 +246,7 @@
 pair with a
 .IR delay ,
 expressed in multiples of 1/700 seconds.
-A zero delay means to execute the next command immediately.
+A zero delay means to write the next command immediately.
 .I Tag
 is a variable length field suffixed by Muse, and is ignored.
 .SH MAPS
@@ -283,10 +285,12 @@
 .PD
 .PP
 Missing maps have an offset of zero.
-As zero offsets are not handled specially, references of these maps will point to the first map.
+As zero offsets are not handled specially,
+the references to these maps will point to the first map.
 .PP
 An offset of 0xffffffff marks the map lump as sparse.
-In this case, the engine will not initialize the map's reference, resulting in a crash if it should be accessed.
+In this case, the engine will not initialize the map's reference,
+resulting in a crash should it be accessed.
 .IR wl3d (1)
 exits if it reads such an offset.
 .SS Gamemaps
@@ -320,8 +324,17 @@
 .I Planes
 stores contiguously each plane's data.
 .PP
+Each map plane is first compressed using
+.SM RLEW,
+then further using what is eponymously referred to as
+.SM Carmack compression.
+.SS RLEW Compression
+[words]
+.SS Carmack Compression
+[words]
+.SS Plane 0
 The first plane is an array of [words].
-.PP
+.SS Plane 1
 The second plane is an array of [other words].
 .PP
 There are static limits for objects on the map:
@@ -335,15 +348,6 @@
 .TP
 .B static objects
 399
-.SS RLEW Compression
-Each map plane is first compressed using
-.SM RLEW,
-then further using what is eponymously refered to as
-.SM Carmack compression.
-.PP
-[words]
-.SS Carmack Compression
-[words]
 .SH GRAPHICS
 Graphics are either static data loaded in the executable, or huffman-compressed lumps contained in
 .BR vgagraph .
@@ -372,6 +376,8 @@
 .PP
 This file contains a dictionary used for decompression of Huffman-encoded lumps in
 .IR vgagraph .
+.SS Huffman Compression
+[words]
 .SS Vgagraph
 .RS
 .IR pt [ np ][]
@@ -694,7 +700,241 @@
 .SH "CONFIGURATION FILE AND HIGHSCORES"
 [words]
 .SH "SAVED GAMES"
-[words]
+.RS
+.IR name [32]
+.IR game []
+.IR map []
+.IR chksum [4]
+.br
+.PP
+.BR game :
+.IR gm1 []
+.IR score []
+.IR plr []
+.IR gm2 []
+.IR stat []
+.IR misc []
+.IR epstat []
+.br
+.BR gm1 :
+.IR difc [2]
+.IR map [2]
+.br
+.BR score :
+.IR oldpt [4]
+.IR pt [4]
+.IR to1up [4]
+.br
+.BR plr :
+.IR lives [2]
+.IR hp [2]
+.IR ammo [2]
+.IR keys [2]
+.IR bestw [2]
+.IR wep [2]
+.IR lastw [2]
+.br
+.BR gm2 :
+.IR facefrm [2]
+.IR atkfrm [2]
+.IR wfrm [2]
+.IR ep [2]
+.br
+.BR stat :
+.IR sp [2]
+.IR tp [2]
+.IR kp [2]
+.IR stot [2]
+.IR ttot [2]
+.IR ktot [2]
+.IR tc [4]
+.br
+.BR misc :
+.IR killx [4]
+.IR killy [4]
+.IR won [2]
+.br
+.BR epstat :
+{
+.IR k [2]
+.IR s [2]
+.IR t [2]
+.IR tm [4]
+.RI }[ nmap ]
+.br
+.PP
+.BR map :
+.IR tilemap []
+.IR obj []
+.IR stats []
+.IR doors []
+.IR pwall []
+.br
+.BR tilemap :
+.IR tl [64*64]
+.IR tlo [64*64][2]
+.br
+.BR obj :
+{
+.IR on [2]
+.IR tc [2]
+.IR type [2]
+.IR stt [2]
+.IR f [1]
+.IR Δr [4]
+.IR dir [2]
+.IR x [4]
+.IR y [4]
+.IR tx [2]
+.IR ty [2]
+.IR aid [1]
+.IR vwx [2]
+.IR vwh [2]
+.IR trx [4]
+.IR try [4]
+.IR θ [2]
+.IR hp [2]
+.IR v [4]
+.IR tmp [3][2]
+.IR node [2][2]
+.RI }[ nobj+1 ]
+.br
+.BR stats :
+.IR statse [2]
+{
+.IR tx [1]
+.IR ty [1]
+.IR vis [2]
+.IR spr [2]
+.IR f [1]
+.IR itm [1]
+}[400]
+.br
+.BR doors :
+.IR dopen [64][2]
+{
+.IR tx [1]
+.IR ty [1]
+.IR vert [2]
+.IR lck [1]
+.IR φ [2]
+.IR tc [2]
+}[64]
+.br
+.BR pwall :
+.IR φ [2]
+.IR x [2]
+.IR y [2]
+.IR dir [2]
+.IR dopen [2]
+.RE
+.PP
+Savegame files contain game and map state used to regenerate a live game.
+It can be split into lumps containing variable-size fields.
+.PP
+A checksum of each written byte is calculated and appended.
+It is tested upon subsequent loading to guard against tampering.
+Its formula is:
+.RS
+.BR chksum \ = \ ∑\ n[i]\ XOR\ n[i+1]
+.RE
+.PP
+A save file does not contain the entire state necessary to restore a game.
+The map must first be loaded and initialized,
+then the information from the save file is used
+to overwrite some of the resulting state.
+.IR Wl3d (1)
+uses a different save game format, containing a complete state.
+Converting between the two formats is possible.
+.PP
+.I Name
+contains a nul-terminated ascii string, which may be empty.
+It is the savegame's name displayed ingame using the second game font,
+and may be truncated depending on its total on screen width.
+.SS Game state
+.PP
+The first two fields are the game difficulty (0-3, from easiest to hardest),
+and a zero-indexed map number (0-59).
+.PP
+Then follow three scoring fields:
+the points at the start of the level,
+current points, and the points needed for another 1-up reward.
+.PP
+Next are seven player state fields:
+number of lives (0-9),
+hit points (1-100), ammo points (0-99),
+collected keys bitfield,
+best available weapon (1-3),
+currently equipped weapon,
+and last switched from weapon.
+.PP
+The subsequent four fields store frame counters for face and firing animations,
+and an episode index (0-5 or 0 for
+.IR sod/sdm ),
+derived from the map index.
+.PP
+Seven map statistics fields follow:
+current secrets, treasure and kills counts,
+then their respective maximums,
+and a level time in 70 Hz tics.
+.PP
+The ensuing three fields are used when a victory condition is triggered,
+and contain the coordinates of a reference object and a victory flag.
+.PP
+The last lump is an array of scoring information for each of the current
+episode's maps:
+percentage of kills, secrets found and treasure collected,
+and level time in 70 Hz tics.
+.I Nmap
+is 8 for
+.I wl1/wl6
+and 20 for
+.IR sdm/sod .
+.SS Map state
+The
+.I tilemap
+is an array of byte-sized tile numbers,
+followed by an array of object pointers.
+Values lower than 256 in
+.I tlo
+denote item numbers.
+.PP
+The subsequent lump is a variable-size array of objects,
+defined by the following fields:
+active flag, state timer, object type, state pointer, object flags,
+displacement counter, direction, global and tile coordinates, area number,
+rendering dimensions,
+angle (used for player and projectiles),
+hit points, speed,
+three temporary variables used for timing and drawing,
+and next and previous node pointers.
+The
+.I obj
+lump is terminated by a trailing object definition with
+.LR 0xffff
+in its
+.I on
+field.
+.PP
+Afterwards is stored a fixed-size array of static objects,
+preceded by an end pointer, referencing past the last element.
+Each element is defined by tile coordinates,
+a pointer to an array of visible elements,
+a sprite number,
+a flags field,
+and an item number.
+.PP
+Next is stored a fixed-size array of doors,
+prepended by an array of door positions (0-0xffff, from closed to open).
+Each is ascribed tile coordinates,
+a vertical map position flag,
+a lock bitfield,
+an open/close phase,
+and a timer.
+.PP
+The last lump describes an active pushwall:
+opening phase, global map coordinates, direction,
+and fine position.
 .SH "SEE ALSO"
 .IR opl2 (1) ,
 .IR pcmconv (1) ,
--- a/map.c
+++ b/map.c
@@ -3,12 +3,6 @@
 #include "dat.h"
 #include "fns.h"
 
-enum{
-	Nobj = 150,
-	Ndoor = 64,
-	Nstc = 400,
-	Narea = 37
-};
 Tile tiles[Mapa];
 Obj *objs, *ofree, *oplr;
 Door doors[Ndoor], *doore, pusher;
@@ -49,8 +43,7 @@
 	case Rbible:
 	case Rcrown:
 	case R1up:
-		if(!gm.load)
-			gm.ttot++;
+		gm.ttot++;
 		/* wet floor */
 	default:
 		stce->f = OFbonus;
@@ -62,7 +55,7 @@
 	stce->spr = sprs + n;
 	if(stce->spr == nil)
 		sysfatal("spawnstc: missing static sprite %d\n", n);
-	if(++stce == stcs+Nstc)
+	if(++stce == stcs + nelem(stcs))
 		sysfatal("static object overflow");
 }
 
@@ -73,7 +66,7 @@
 
 	a = conarea + id * Narea;
 	p = plrarea;
-	while(p < plrarea+nelem(plrarea)){
+	while(p < plrarea + nelem(plrarea)){
 		if(*a && !*p){
 			(*p)++;
 			rconair(p-plrarea);
@@ -293,7 +286,7 @@
 }
 
 static void
-oinit(int all)
+oinit(void)
 {
 	Obj *o, *p;
 
@@ -313,15 +306,6 @@
 	}
 	ofree->p = p;
 	p->n = ofree;
-
-	if(all){
-		memset(plrarea, 0, sizeof plrarea);
-		memset(conarea, 0, sizeof conarea);
-		memset(doors, 0, sizeof doors);
-		memset(stcs, 0, sizeof stcs);
-		doore = doors;
-		stce = stcs;
-	}
 }
 
 static void
@@ -426,8 +410,7 @@
 	o->v = 1500;
 	o->θ = θE;
 	o->f |= OFambush;
-	if(!gm.load)
-		gm.ktot++;
+	gm.ktot++;
 }
 
 static void
@@ -461,7 +444,6 @@
 		θ = θS;
 		goto wlonly;
 	case Oschb:
-		stt[GSschbdie2].dt = pcmon ? 140 : 5;
 		s = stt+GSschb;
 		hp = gm.difc<GDeasy ? 850 : gm.difc<GDmed ? 950
 			: gm.difc<GDhard ? 1550 : 2400;
@@ -473,26 +455,21 @@
 		θ = θN;
 		goto wlonly;
 	case Ootto:
-		stt[GSottodie2].dt = pcmon ? 140 : 5;
 		s = stt+GSotto;
 		hp = gm.difc<GDhard ? 850 + gm.difc * 100 : 1200;
 		θ = θN;
 		goto wlonly;
 	case Ofett:
-		stt[GSfettdie2].dt = pcmon ? 140 : 5;
 		s = stt+GSfett;
 		hp = gm.difc<GDhard ? 850 + gm.difc * 100 : 1200;
 		θ = θS;
 		goto wlonly;
 	case Ofake:
-		/* bug? */
-		stt[GShitlerdie2].dt = pcmon ? 140 : 5;
 		s = stt+GSfake;
 		hp = 200 + 100 * gm.difc;
 		θ = θN;
 		goto wlonly;
 	case Omech:
-		stt[GShitlerdie2].dt = pcmon ? 140 : 5;
 		s = stt+GSmech;
 		hp = gm.difc<GDeasy ? 800 : gm.difc<GDmed ? 950
 			: gm.difc<GDhard ? 1050 : 1200;
@@ -499,26 +476,18 @@
 		θ = θS;
 		goto wlonly;
 	case Otrans:
-		if(pcmon)
-			stt[GStransdie2].dt = 105;
 		s = stt+GStrans;
 		hp = gm.difc<GDhard ? 850 + gm.difc * 100 : 1200;
 		goto sdonly;
 	case Owilh:
-		if(pcmon)
-			stt[GSwilhdie2].dt = 70;
 		s = stt+GSwilh;
 		hp = gm.difc<GDhard ? 950 + gm.difc * 100 : 1300;
 		goto sdonly;
 	case Ouber:
-		if(pcmon)
-			stt[GSuberdie2].dt = 70;
 		s = stt+GSuber;
 		hp = gm.difc<GDhard ? 1050 + gm.difc * 100 : 1400;
 		goto sdonly;
 	case Oknight:
-		if(pcmon)
-			stt[GSknightdie2].dt = 105;
 		s = stt+GSknight;
 		hp = gm.difc<GDhard ? 1250 + 100 * gm.difc : 1600;
 		goto sdonly;
@@ -527,8 +496,6 @@
 		hp = gm.difc<GDhard ? 5 * (1 + gm.difc) : 25;
 		goto sdonly;
 	case Oangel:
-		if(pcmon)
-			stt[GSangeldie2].dt = 105;
 		s = stt+GSangel;
 		hp = gm.difc<GDhard ? 1450 + 100 * gm.difc : 2000;
 		goto sdonly;
@@ -538,8 +505,7 @@
 	o->hp = hp;
 	o->θ = θ;
 	o->f |= OFshootable | OFambush;
-	if(!gm.load)
-		gm.ktot++;
+	gm.ktot++;
 }
 
 static void
@@ -605,8 +571,7 @@
 	o->v = type == Odog ? 1500 : 512;
 	o->θ = dir * 90;
 
-	if(!gm.load)
-		gm.ktot++;
+	gm.ktot++;
 	if(patrol){
 		o->Δr = Dtlglobal;
 		o->on++;
@@ -638,8 +603,7 @@
 		spawnstc(tl, n-23);
 		break;
 	case 98:
-		if(!gm.load)
-			gm.stot++;
+		gm.stot++;
 		break;
 	case 180: case 181: case 182: case 183: difc++;	n-=36; /* wet floor */
 	case 144: case 145: case 146: case 147: difc+=2; n-=36; /* wet floor */
@@ -730,7 +694,7 @@
 	for(sti=stctype; sti<stctype+nelem(stctype); sti++)
 		if(*sti == n)
 			break;
-	if(sti >= stctype+nelem(stctype))
+	if(sti >= stctype + nelem(stctype))
 		sysfatal("drop: unknown item type");
 	for(s=stcs; s<stcs+nelem(stcs); s++)
 		if(s->tl == nil){
@@ -738,7 +702,7 @@
 				stce++;
 			break;
 		}
-	if(s >= stcs+nelem(stcs))
+	if(s >= stcs + nelem(stcs))
 		return;
 	s->tl = tl;
 	sn = n == Rclip2 ? 28 : 2+(sti-stctype);
@@ -855,6 +819,201 @@
 	mus(ver < SDM ? wlmus[gm.map] : sdmus[gm.map]);
 }
 
+uchar *
+wrmap(uchar *p)
+{
+	Tile *tl;
+	Obj *o;
+	Static *s;
+	Door *d;
+
+	disking();
+	for(tl=tiles; tl<tiles+nelem(tiles); tl++){
+		PUT16(p, tl->p0);
+		PUT16(p, tl->p1);
+		PUT8(p, tl->tl);
+		PUT8(p, tl->to);
+	}
+	memcpy(p, conarea, sizeof conarea); p+=sizeof conarea;
+	memcpy(p, plrarea, sizeof plrarea); p+=sizeof plrarea;
+	for(o=oplr; o!=objs; o=o->n){
+		disking();
+		PUT16(p, o->on);
+		PUT16(p, o->tc);
+		PUT16(p, o->type);
+		PUT16(p, o->s - stt);
+		PUT8(p, o->f);
+		PUT32(p, o->Δr);
+		PUT32(p, o->x);
+		PUT32(p, o->y);
+		PUT16(p, o->tx);
+		PUT16(p, o->ty);
+		PUT8(p, o->areaid);
+		PUT16(p, o->vwx);
+		PUT16(p, o->vwdy);
+		PUT32(p, o->vwdx);
+		PUT16(p, o->θ);
+		PUT16(p, o->hp);
+		PUT32(p, o->v);
+		PUT16(p, o->atkdt);
+		PUT16(p, o->sdt);
+	}
+	PUT16(p, 0xffff);
+	disking();
+	PUT16(p, stce - stcs);
+	for(s=stcs; s<stce; s++){
+		PUT16(p, s->tl - tiles);
+		PUT16(p, s->spr - sprs);
+		PUT8(p, s->f);
+		PUT8(p, s->item);
+	}
+	disking();
+	PUT16(p, doore - doors);
+	for(d=doors; d<doore; d++){
+		PUT16(p, d->tl - tiles);
+		PUT8(p, d->isvert);
+		PUT8(p, d->lock);
+		PUT16(p, d->φ);
+		PUT16(p, d->tc);
+		PUT16(p, d->dopen);
+	}
+	disking();
+	PUT16(p, pusher.φ);
+	PUT16(p, pusher.tl - tiles);
+	PUT16(p, pusher.isvert);
+	PUT16(p, pusher.dopen);
+	return p;
+}
+
+static void
+sttdtinit(void)
+{
+	/* bug: die state durations are set on spawn and persist regardless of
+	 * changes in sound settings, until map load; sod: durations persist
+	 * across maps */
+	if(pcmon){
+		stt[GSschbdie2].dt = 140;
+		stt[GSottodie2].dt = 140;
+		stt[GSfettdie2].dt = 140;
+		/* bug?: set for Ofake as well */
+		stt[GShitlerdie2].dt = 140;
+		stt[GStransdie2].dt = 105;
+		stt[GSuberdie2].dt = 70;
+		stt[GSknightdie2].dt = 105;
+		stt[GSangeldie2].dt = 105;
+	}else{
+		stt[GSschbdie2].dt = 5;
+		stt[GSottodie2].dt = 5;
+		stt[GSfettdie2].dt = 5;
+		stt[GShitlerdie2].dt = 5;
+		stt[GStransdie2].dt = 1;
+		stt[GSuberdie2].dt = 1;
+		stt[GSknightdie2].dt = 10;
+		stt[GSangeldie2].dt = 1;
+	}
+}
+
+static void
+nukemap(void)
+{
+	memset(tiles, 0, sizeof tiles);
+	memset(plrarea, 0, sizeof plrarea);
+	memset(conarea, 0, sizeof conarea);
+	memset(doors, 0, sizeof doors);
+	memset(stcs, 0, sizeof stcs);
+	doore = doors;
+	stce = stcs;
+	oinit();
+	sttdtinit();
+}
+
+int
+ldmap(uchar *p, uchar **ep)
+{
+	int n;
+	Tile *tl;
+	Obj *o;
+	Static *s;
+	Door *d;
+
+	disking();
+	nukemap();
+	disking();
+	for(tl=tiles; tl<tiles+nelem(tiles); tl++){
+		tl->p0 = GET16(p);
+		tl->p1 = GET16(p);
+		tl->tl = GET8(p);
+		tl->to = GET8(p);
+	}
+	disking();
+	memcpy(conarea, p, sizeof conarea); p+=sizeof conarea;
+	memcpy(plrarea, p, sizeof plrarea); p+=sizeof plrarea;
+	for(o=nil;;){
+		n = GET16(p);
+		if(n == 0xffff)
+			break;
+		o = o == nil ? oplr : onew();
+		o->on = n;
+		o->tc = (s16int)GET16(p);
+		o->type = GET16(p);
+		o->s = stt + GET16(p);
+		o->f = GET8(p);
+		o->Δr = (s32int)GET32(p);
+		o->x = GET32(p);
+		o->y = GET32(p);
+		o->tx = GET16(p);
+		o->ty = GET16(p);
+		o->tl = tiles + o->ty * Mapdxy + o->tx;
+		o->areaid = GET8(p);
+		o->vwx = GET16(p);
+		o->vwdy = GET16(p);
+		o->vwdx = GET32(p);
+		o->θ = (s16int)GET16(p);
+		o->hp = GET16(p);
+		o->v = GET32(p);
+		o->atkdt = (s16int)GET16(p);
+		o->sdt = GET16(p);
+		if(o != oplr && ((o->f & OFnevermark) == 0
+		|| (o->f & OFnomark) == 0 || o->tl->o == nil)){
+			o->tl->o = o;
+			o->tl->to = 0;
+		}
+	}
+	disking();
+	stce = stcs + GET16(p);
+	if(stce > stcs + nelem(stcs)){
+		werrstr("ldmap: static object overflow");
+		return -1;
+	}
+	for(s=stcs; s<stce; s++){
+		s->tl = tiles + GET16(p);
+		s->spr = sprs + GET16(p);
+		s->f = GET8(p);
+		s->item = GET8(p);
+	}
+	disking();
+	doore = doors + GET16(p);
+	if(doore > doors + nelem(doors)){
+		werrstr("ldmap: door overflow");
+		return -1;
+	}
+	for(d=doors; d<doore; d++){
+		d->tl = tiles + GET16(p);
+		d->isvert = GET8(p);
+		d->lock = GET8(p);
+		d->φ = GET16(p);
+		d->tc = GET16(p);
+		d->dopen = GET16(p);
+	}
+	disking();
+	pusher.φ = GET16(p);
+	pusher.tl = tiles + GET16(p);
+	pusher.isvert = GET16(p);
+	pusher.dopen = GET16(p);
+	*ep = p;
+	return 0;
+}
+
 void
 initmap(void)
 {
@@ -861,8 +1020,7 @@
 	u16int *p0, *p1, *s;
 	Tile *tl;
 
-	oinit(1);
-	memset(tiles, 0, sizeof tiles);
+	nukemap();
 	p0 = s = readmap(gm.map);
 	p1 = p0 + Mapa;
 	for(tl=tiles; tl<tiles+nelem(tiles); tl++){
--- a/menu.c
+++ b/menu.c
@@ -1,44 +1,3 @@
-s16int SaveGamesAvail[10],StartGame,SoundStatus=1,pickquick;
-char SaveGameNames[10][32],SaveName[13]="SAVEGAM?.";
-
-void US_ControlPanel(u8int scancode)
-{
-	if (ingame)
-		if (CP_CheckQuick(scancode))
-			return;
-	switch(scancode)
-	{
-		case sc_F1:
-			#ifndef SPEAR
-			HelpScreens();
-			#endif
-			goto finishup;
-
-		case sc_F2:
-			CP_SaveGame(0);
-			goto finishup;
-
-		case sc_F3:
-			CP_LoadGame(0);
-			goto finishup;
-
-		case sc_F4:
-			CP_Sound();
-			goto finishup;
-
-		case sc_F5:
-			CP_ChangeView();
-			goto finishup;
-
-		case sc_F6:
-			CP_Control();
-			goto finishup;
-
-		finishup:
-			return;
-	}
-}
-
 void CP_ReadThis(void)
 {
 	StartCPMusic(0);
@@ -160,402 +119,4 @@
 		}
 
 	return 0;
-}
-
-void CP_NewGame(void)
-{
-	s16int which,episode;
-
-#ifndef SPEAR
-firstpart:
-
-	DrawNewEpisode();
-	do
-	{
-		which=HandleMenu(&NewEitems,&NewEmenu[0],NULL);
-		switch(which)
-		{
-			case -1:
-				MenuFadeOut();
-				return;
-
-			default:
-				if (!EpisodeSelect[which/2])
-				{
-					sfx (Snoway);
-					Message("Please select \"Read This!\"\n"
-							"from the Options menu to\n"
-							"find out how to order this\n"
-							"episode from Apogee.");
-					IN_ClearKeysDown();
-					IN_Ack();
-					DrawNewEpisode();
-					which = 0;
-				}
-				else
-				{
-					episode = which/2;
-					which = 1;
-				}
-				break;
-		}
-
-	} while (!which);
-
-	ShootSnd();
-
-	//
-	// ALREADY IN A GAME?
-	//
-	if (ingame)
-		if (!Confirm(CURGAME))
-		{
-			MenuFadeOut();
-			return;
-		}
-
-	MenuFadeOut();
-
-#else
-	episode = 0;
-
-	//
-	// ALREADY IN A GAME?
-	//
-	DrawNewGame();
-	if (ingame)
-		if (!Confirm(CURGAME))
-		{
-			MenuFadeOut();
-			return;
-		}
-
-#endif
-
-	DrawNewGame();
-	which=HandleMenu(&NewItems,&NewMenu[0],DrawNewGameDiff);
-	if (which<0)
-	{
-		MenuFadeOut();
-		#ifndef SPEAR
-		goto firstpart;
-		#else
-		return;
-		#endif
-	}
-
-	ShootSnd();
-	NewGame(which,episode);
-	StartGame=1;
-	MenuFadeOut();
-
-	//
-	// CHANGE "READ THIS!" TO NORMAL COLOR
-	//
-	#ifndef SPEAR
-	MainMenu[readthis].active=1;
-	#endif
-
-	pickquick = 0;
-}
-
-void DrawLSAction(s16int which)
-{
-	#define LSA_X	96
-	#define LSA_Y	80
-	#define LSA_W	130
-	#define LSA_H	42
-
-	DrawWindow(LSA_X,LSA_Y,LSA_W,LSA_H,TEXTCOLOR);
-	DrawOutline(LSA_X,LSA_Y,LSA_W,LSA_H,0,HIGHLIGHT);
-	VWB_DrawPic(LSA_X+8,LSA_Y+5,Pread1);
-
-	fontnumber=1;
-	SETFONTCOLOR(0,TEXTCOLOR);
-	PrintX=LSA_X+46;
-	PrintY=LSA_Y+13;
-
-	if (!which)
-		US_Print("Loading...");
-	else
-		US_Print("Saving...");
-
-	VW_UpdateScreen();
-}
-
-s16int CP_LoadGame(s16int quick)
-{
-	s16int handle,which,exit=0;
-	char name[13];
-
-
-	strcpy(name,SaveName);
-
-	//
-	// QUICKLOAD?
-	//
-	if (quick)
-	{
-		which=LSItems.curpos;
-
-		if (SaveGamesAvail[which])
-		{
-			name[7]=which+'0';
-			handle=open(name,O_BINARY);
-			lseek(handle,32,SEEK_SET);
-			gm.load=true;
-			LoadTheGame(handle,0,0);
-			gm.load=false;
-			close(handle);
-
-			hudf ();
-			hudh ();
-			hudl ();
-			hudm ();
-			huda ();
-			hudk ();
-			hudw ();
-			hudp ();
-			return 1;
-		}
-	}
-
-	DrawLoadSaveScreen(0);
-
-	do
-	{
-		which=HandleMenu(&LSItems,&LSMenu[0],TrackWhichGame);
-		if (which>=0 && SaveGamesAvail[which])
-		{
-			ShootSnd();
-			name[7]=which+'0';
-
-			handle=open(name,O_BINARY);
-			lseek(handle,32,SEEK_SET);
-
-			DrawLSAction(0);
-			gm.load=true;
-
-			LoadTheGame(handle,LSA_X+8,LSA_Y+5);
-			close(handle);
-
-			StartGame=1;
-			ShootSnd();
-			//
-			// CHANGE "READ THIS!" TO NORMAL COLOR
-			//
-			#ifndef SPEAR
-			MainMenu[readthis].active=1;
-			#endif
-
-			exit=1;
-			break;
-		}
-
-	} while(which>=0);
-
-	MenuFadeOut();
-
-	return exit;
-}
-
-void TrackWhichGame(s16int w)
-{
-	static s16int lastgameon=0;
-
-	PrintLSEntry(lastgameon,TEXTCOLOR);
-	PrintLSEntry(w,HIGHLIGHT);
-
-	lastgameon=w;
-}
-
-void DrawLoadSaveScreen(s16int loadsave)
-{
-	s16int i;
-
-	ClearMScreen();
-	fontnumber=1;
-	VWB_DrawPic(112,184,Pmouselback);
-	DrawWindow(LSM_X-10,LSM_Y-5,LSM_W,LSM_H,BKGDCOLOR);
-	DrawStripes(10);
-
-	if (!loadsave)
-		VWB_DrawPic(60,0,Pload);
-	else
-		VWB_DrawPic(60,0,Psave);
-
-	for (i=0;i<10;i++)
-		PrintLSEntry(i,TEXTCOLOR);
-
-	DrawMenu(&LSItems,&LSMenu[0]);
-	VW_UpdateScreen();
-	MenuFadeIn();
-	WaitKeyUp();
-}
-
-void PrintLSEntry(s16int w,s16int color)
-{
-	SETFONTCOLOR(color,BKGDCOLOR);
-	DrawOutline(LSM_X+LSItems.indent,LSM_Y+w*13,LSM_W-LSItems.indent-15,11,color,color);
-	PrintX=LSM_X+LSItems.indent+2;
-	PrintY=LSM_Y+w*13+1;
-	fontnumber=0;
-
-	if (SaveGamesAvail[w])
-		US_Print(SaveGameNames[w]);
-	else
-		US_Print("      - empty -");
-
-	fontnumber=1;
-}
-
-s16int CP_SaveGame(s16int quick)
-{
-	s16int handle,which,exit=0;
-	u16int nwritten;
-	char name[13],input[32];
-
-
-	strcpy(name,SaveName);
-
-	//
-	// QUICKSAVE?
-	//
-	if (quick)
-	{
-		which=LSItems.curpos;
-
-		if (SaveGamesAvail[which])
-		{
-			name[7]=which+'0';
-			unlink(name);
-			handle=creat(name,S_IREAD|S_IWRITE);
-
-			strcpy(input,&SaveGameNames[which][0]);
-
-			_dos_write(handle,(void far *)input,32,&nwritten);
-			lseek(handle,32,SEEK_SET);
-			SaveTheGame(handle,0,0);
-			close(handle);
-
-			return 1;
-		}
-	}
-
-	DrawLoadSaveScreen(1);
-
-	do
-	{
-		which=HandleMenu(&LSItems,&LSMenu[0],TrackWhichGame);
-		if (which>=0)
-		{
-			//
-			// OVERWRITE EXISTING SAVEGAME?
-			//
-			if (SaveGamesAvail[which])
-				if (!Confirm("There's already a game\nsaved at this position.\n      Overwrite?"))
-				{
-					DrawLoadSaveScreen(1);
-					continue;
-				}
-				else
-				{
-					DrawLoadSaveScreen(1);
-					PrintLSEntry(which,HIGHLIGHT);
-					VW_UpdateScreen();
-				}
-
-			ShootSnd();
-
-			strcpy(input,&SaveGameNames[which][0]);
-			name[7]=which+'0';
-
-			fontnumber=0;
-			if (!SaveGamesAvail[which])
-				VWB_Bar(LSM_X+LSItems.indent+1,LSM_Y+which*13+1,LSM_W-LSItems.indent-16,10,BKGDCOLOR);
-			VW_UpdateScreen();
-
-			if (US_LineInput(LSM_X+LSItems.indent+2,LSM_Y+which*13+1,input,input,true,31,LSM_W-LSItems.indent-30))
-			{
-				SaveGamesAvail[which]=1;
-				strcpy(&SaveGameNames[which][0],input);
-
-				unlink(name);
-				handle=creat(name,S_IREAD|S_IWRITE);
-				_dos_write(handle,(void far *)input,32,&nwritten);
-				lseek(handle,32,SEEK_SET);
-
-				DrawLSAction(1);
-				SaveTheGame(handle,LSA_X+8,LSA_Y+5);
-
-				close(handle);
-
-				ShootSnd();
-				exit=1;
-			}
-			else
-			{
-				VWB_Bar(LSM_X+LSItems.indent+1,LSM_Y+which*13+1,LSM_W-LSItems.indent-16,10,BKGDCOLOR);
-				PrintLSEntry(which,HIGHLIGHT);
-				VW_UpdateScreen();
-				sfx(Sesc);
-				continue;
-			}
-
-			fontnumber=1;
-			break;
-		}
-
-	} while(which>=0);
-
-	MenuFadeOut();
-
-	return exit;
-}
-
-void SetupControlPanel(void)
-{
-	//
-	// SEE WHICH SAVE GAME FILES ARE AVAILABLE & READ STRING IN
-	//
-	strcpy(name,SaveName);
-	if (!findfirst(name,&f,0))
-		do
-		{
-			which=f.ff_name[7]-'0';
-			if (which<10)
-			{
-				s16int handle;
-				char temp[32];
-
-				SaveGamesAvail[which]=1;
-				handle=open(f.ff_name,O_BINARY);
-				read(handle,temp,32);
-				close(handle);
-				strcpy(&SaveGameNames[which][0],temp);
-			}
-		} while(!findnext(&f));
-}
-
-void StartCPMusic(s16int song)
-{
-	SD_MusicOff();
-	SD_mapmus((MusicGroup far *)audiosegs[STARTMUSIC + song]);
-}
-
-void CheckPause(void)
-{
-	if (Paused)
-	{
-		switch(SoundStatus)
-		{
-			case 0: SD_MusicOn(); break;
-			case 1: SD_MusicOff(); break;
-		}
-
-		SoundStatus^=1;
-		VW_WaitVBL(3);
-		IN_ClearKeysDown();
-		Paused=false;
-	}
 }
--- a/rend.c
+++ b/rend.c
@@ -182,7 +182,7 @@
 			scalspr(SPcam, vw.dx/2, vw.dy+1);
 		return;
 	}
-	if(gm.w != -1)
+	if(gm.w != WPnone)
 		scalspr(wspr[gm.w] + gm.wfrm, vw.dx/2, vw.dy+1);
 	if(gm.record || gm.demo)
 		scalspr(SPdemo, vw.dx/2, vw.dy+1);
--- a/us.h
+++ /dev/null
@@ -1,12 +1,0 @@
-#define	MaxHighName	57
-#define	MaxScores	7
-typedef	struct
-		{
-			char	name[MaxHighName + 1];
-			s32int	score;
-			u16int	completed,episode;
-		} HighScore;
-
-#define	MaxString	128	// Maximum input string size
-
-extern	HighScore	Scores[];
--- a/us1.c
+++ /dev/null
@@ -1,224 +1,0 @@
-///////////////////////////////////////////////////////////////////////////
-//
-//	USL_XORICursor() - XORs the I-bar text cursor. Used by US_LineInput()
-//
-///////////////////////////////////////////////////////////////////////////
-static void
-USL_XORICursor(s16int x,s16int y,char *s,u16int cursor)
-{
-	static	int	status;		// VGA doesn't XOR...
-	char	buf[MaxString];
-	s16int		temp;
-	u16int	w,h;
-
-	strcpy(buf,s);
-	buf[cursor] = '\0';
-	USL_MeasureString(buf,&w,&h);
-
-	px = x + w - 1;
-	py = y;
-	if (status^=1)
-		USL_DrawString("\x80");
-	else
-	{
-		temp = fontcolor;
-		fontcolor = backcolor;
-		USL_DrawString("\x80");
-		fontcolor = temp;
-	}
-
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-//	US_LineInput() - Gets a line of user input at (x,y), the string defaults
-//		to whatever is pointed at by def. Input is restricted to maxchars
-//		chars or maxwidth pixels wide. If the user hits escape (and escok is
-//		true), nothing is copied into buf, and false is returned. If the
-//		user hits return, the current string is copied into buf, and true is
-//		returned
-//
-///////////////////////////////////////////////////////////////////////////
-int
-US_LineInput(s16int x,s16int y,char *buf,char *def,int escok,
-				s16int maxchars,s16int maxwidth)
-{
-	int		redraw,
-				cursorvis,cursormoved,
-				done,result;
-	u8int	sc;
-	char		c,
-				s[MaxString],olds[MaxString];
-	u16int		i,
-				cursor,
-				w,h,
-				len,temp;
-	u32int	lasttime;
-
-	if (def)
-		strcpy(s,def);
-	else
-		*s = '\0';
-	*olds = '\0';
-	cursor = strlen(s);
-	cursormoved = redraw = true;
-
-	cursorvis = done = false;
-	lasttime = TimeCount;
-	LastASCII = key_None;
-	LastScan = sc_None;
-
-	while (!done)
-	{
-		if (cursorvis)
-			USL_XORICursor(x,y,s,cursor);
-
-	asm	pushf
-	asm	cli
-
-		sc = LastScan;
-		LastScan = sc_None;
-		c = LastASCII;
-		LastASCII = key_None;
-
-	asm	popf
-
-		switch (sc)
-		{
-		case sc_LeftArrow:
-			if (cursor)
-				cursor--;
-			c = key_None;
-			cursormoved = true;
-			break;
-		case sc_RightArrow:
-			if (s[cursor])
-				cursor++;
-			c = key_None;
-			cursormoved = true;
-			break;
-		case sc_Home:
-			cursor = 0;
-			c = key_None;
-			cursormoved = true;
-			break;
-		case sc_End:
-			cursor = strlen(s);
-			c = key_None;
-			cursormoved = true;
-			break;
-
-		case sc_Return:
-			strcpy(buf,s);
-			done = true;
-			result = true;
-			c = key_None;
-			break;
-		case sc_Escape:
-			if (escok)
-			{
-				done = true;
-				result = false;
-			}
-			c = key_None;
-			break;
-
-		case sc_BackSpace:
-			if (cursor)
-			{
-				strcpy(s + cursor - 1,s + cursor);
-				cursor--;
-				redraw = true;
-			}
-			c = key_None;
-			cursormoved = true;
-			break;
-		case sc_Delete:
-			if (s[cursor])
-			{
-				strcpy(s + cursor,s + cursor + 1);
-				redraw = true;
-			}
-			c = key_None;
-			cursormoved = true;
-			break;
-
-		case 0x4c:	// Keypad 5
-		case sc_UpArrow:
-		case sc_DownArrow:
-		case sc_PgUp:
-		case sc_PgDn:
-		case sc_Insert:
-			c = key_None;
-			break;
-		}
-
-		if (c)
-		{
-			len = strlen(s);
-			USL_MeasureString(s,&w,&h);
-
-			if
-			(
-				isprint(c)
-			&&	(len < MaxString - 1)
-			&&	((!maxchars) || (len < maxchars))
-			&&	((!maxwidth) || (w < maxwidth))
-			)
-			{
-				for (i = len + 1;i > cursor;i--)
-					s[i] = s[i - 1];
-				s[cursor++] = c;
-				redraw = true;
-			}
-		}
-
-		if (redraw)
-		{
-			px = x;
-			py = y;
-			temp = fontcolor;
-			fontcolor = backcolor;
-			USL_DrawString(olds);
-			fontcolor = temp;
-			strcpy(olds,s);
-
-			px = x;
-			py = y;
-			USL_DrawString(s);
-
-			redraw = false;
-		}
-
-		if (cursormoved)
-		{
-			cursorvis = false;
-			lasttime = TimeCount - TickBase;
-
-			cursormoved = false;
-		}
-		if (TimeCount - lasttime > TickBase / 2)
-		{
-			lasttime = TimeCount;
-
-			cursorvis ^= true;
-		}
-		if (cursorvis)
-			USL_XORICursor(x,y,s,cursor);
-
-		VW_UpdateScreen();
-	}
-
-	if (cursorvis)
-		USL_XORICursor(x,y,s,cursor);
-	if (!result)
-	{
-		px = x;
-		py = y;
-		USL_DrawString(olds);
-	}
-	VW_UpdateScreen();
-
-	IN_ClearKeysDown();
-	return(result);
-}