/*	QWE -- Quick Waveform Editor for Mirage   psl 8/87 */

#include	<suntool/sunview.h>
#include	<suntool/canvas.h>
#include	<suntool/panel.h>
#include	<suntool/scrollbar.h>
#include	<midi.h>
#include	<stdio.h>
#include	<sys/types.h>
#include	<sys/stat.h>

#define	DATAWIDTH	1024
#define	DATAHEIGHT	256
#define	MARGIN		1
#define	VIEWWIDTH	(DATAWIDTH+2*MARGIN)
#define	VIEWHEIGHT	(DATAHEIGHT+2*MARGIN)
#define	SCROLLHEIGHT	20
#define	PANELHEIGHT	64
#define	BORDER		16
#define	MISC		16

#define	BPP		256

#define	DSCMAGIC	05504

#define	OUTFILE	"qwe.data"
#define	TMPFILE	"/tmp/qwe.tmp"
#define	WDRFILE	"/tmp/qwe.wdr"
#define	SEQFILE	"/tmp/qwe.seq"

#define	PIX_INV	PIX_NOT(PIX_DST)
#define	PIX_XOR	(PIX_DST^PIX_SRC)

#define	ROP(OX,OY,CX,CY,OP)	pw_rop(Dpw,OX,OY,CX-(OX),CY-(OY),OP,0,0,0);
#define	RECTOP(R,OP)		ROP((R).o.x,(R).o.y,(R).c.x,(R).c.y,OP)
#define	RINV(OX,OY,CX,CY)	pw_rop(Dpw,OX,OY,CX-(OX),CY-(OY),PIX_INV,0,0,0);
#define	RECTINV(R)		RINV((R).o.x,(R).o.y,(R).c.x,(R).c.y)

#define	B_TO_PIX(B)	(MARGIN+((B)*Ppp)/BPP)
#define	D_TO_PIX(D)	(BORDER+MARGIN+0xFF-(D))
#define	S_TO_PIX(S)	(BORDER+MARGIN+0x7F-(((S)+2)>>2))
#define	PIX_TO_B(P)	((((P)-MARGIN)*BPP)/Ppp)
#define	PIX_TO_D(P)	(BORDER+MARGIN+0xFF-(P))
#define	PIX_TO_S(P)	((BORDER+MARGIN+0x7F-(P))<<2)

#define	D_TO_NUM(D)	((D)-0x80)
#define	NUM_TO_D(N)	((N)+0x80)

typedef	unsigned char	uchar;

#define	NUMSCALE	256

struct	scstr	{
	int	bpos;		/* byte position */
	short	scale;		/* scale * 256 */
} Scale[NUMSCALE]	= {
	{	0,	256,	},
	{	65536,	256,	},
};
int	Nscale	= 2;		/* number of points in the scaling fcn */

char	Oldfile[128];		/* where the data comes from for "read" */
char	Flbuf[128];		/* frame label buffer */
uchar	*Data;			/* pointer to wavesample (sbrk) */
int	Chan	= 2;		/* Where the Mirage listens */
int	Pcnt	= 0;		/* data page count */
int	Ppp	= 16;		/* pixels/page (magnification) */
int	Tticks	= BPP;		/* bytes per tickmark (time ticks) */
int	Aticks	= 128;		/* values per tickmark (amplitude ticks) */
int	Ramt	= 1;		/* rotation amount */
int	Cbusy	= 0;		/* semaphore, to hide cursor while busy */
Pixrect	*Grey;			/* grey texture */
Frame	Dframe;			/* whole display's frame */
Canvas	Dlborder;		/* left border */
Canvas	Dcanvas;		/* waveform area */
Scrollbar Dcsb;			/* waveform area's scrollbar */
int	Dcwidth, Dcheight;	/* width & height of Dcanvas */
Pixwin	*Bpw	= 0;		/* display pixwin for border area (Dborder) */
Pixwin	*Dpw	= 0;		/* display pixwin for waveform area (Dcanvas) */
Panel	Controls;		/* control panel */
Panel_item Mslide;		/* id for magnification slider */
Panel_item Tslide;		/* id for time ticks slider */
Panel_item Aslide;		/* id for amplitude ticks slider */
Panel_item Rslide;		/* id for rotation amount slider */

/* Button 3 menu */
Menu	b3mp;
#define	PLAY		1
#define	DUMP		2
#define	LOAD		3
#define	ROTL		4
#define	ROTR		5
#define	TRIM		6
#define	SCALE		7
#define	NORMALIZE	8
#define	READ		9
#define	WRITE		10
#define	READDSC		11
#define	WRITEDSC	12
#define	QUIT		13
#define	B3MINIT() b3mp = menu_create( \
MENU_INITIAL_SELECTION, MENU_SELECTED, \
MENU_INITIAL_SELECTION_SELECTED, FALSE, \
MENU_STRINGS, \
	"PLAY SEQ", \
	"MIRAGE->QWE", \
	"QWE->MIRAGE", \
	"ROT LEFT", \
	"ROT RIGHT", \
	"TRIM", \
	"SCALE", \
	"NORMALIZE", \
	"READ", \
	"WRITE", \
	"READ DSC", \
	"WRITE DSC", \
	"QUIT", \
0, 0)

short	Grey_bits[]	= {
	0xAAAA, 0x5555, 0xAAAA, 0x5555,
	0xAAAA, 0x5555, 0xAAAA, 0x5555,
	0xAAAA, 0x5555, 0xAAAA, 0x5555,
	0xAAAA, 0x5555, 0xAAAA, 0x5555,
};

main(argc, argv)
char	*argv[];
{
	extern FILE *sopen();

/****/setbuf(stderr, 0);	/* THOSE ASSHOLES AT SUN! */
	while (--argc > 0) {
	    if (argv[argc][0] == '-') {
		switch (argv[argc][1]) {
		case 'm':		/* magnify */
		    Ppp = atoi(&argv[argc][2]);
		    if (Ppp < 1 || Ppp > BPP)
			goto syntax;
		    break;
		default:
		    goto syntax;
		}
	    } else if (*Oldfile == 0)
		sprintf(Oldfile, "%s", argv[argc]);
	    else {
syntax:
		fprintf(stderr, "Usage: %s [-m#] [file]\n", argv[0]);
		exit(2);
	    }
	}
	if (readdata(Oldfile) == 0)	/* no file read */
	    exit(1);
	miscinits();
	redraw();
	window_main_loop(Dframe);
	exit(0);
}

redraw()
{
	busy_cursor();
	drawdata();
	drawticks();
	flipscale();
	ready_cursor();
}

drawdata()
{
	register int i, x, y, ox, oy, nbytes;

	pw_writebackground(Dpw, 0, BORDER, Dcwidth, VIEWHEIGHT, PIX_CLR);
	if (Dpw)
	    pw_batch_on(Dpw);
	nbytes = BPP * Pcnt;
	ox = oy = -1;
	for (i = 0; i < nbytes; i++) {
	    x = B_TO_PIX(i);
	    y = D_TO_PIX(Data[i] & 0xFF);
	    if (ox >= 0)
		pw_vector(Dpw, ox, oy, x, y, PIX_SET, 1);
	    ox = x;
	    oy = y;
	}
	if (Dpw)
	    pw_batch_off(Dpw);
}

drawticks()
{
	register int i, x, nbytes, h2;

	if (Dpw)
	    pw_batch_on(Dpw);
	nbytes = BPP * Pcnt;
	pw_writebackground(Dpw, 0, 0, Dcwidth, BORDER, PIX_CLR);
	if (Tticks) {
	    for (i = 0; i < nbytes; i += Tticks) {
		x = (i * Ppp) / BPP;
		pw_vector(Dpw, x, 0, x, BORDER, PIX_SRC, 1);
	    }
	}
	pw_writebackground(Bpw, 0, 0, BORDER, DATAHEIGHT, PIX_CLR);
	h2 = 0x80;
	pw_vector(Bpw, 0, h2, BORDER, h2, PIX_INV, 1);
	if (Aticks) {
	    for (i = Aticks; i < h2; i += Aticks) {
		pw_vector(Bpw, 0, h2 + i, BORDER, h2 + i, PIX_INV, 1);
		pw_vector(Bpw, 0, h2 - i, BORDER, h2 - i, PIX_INV, 1);
	    }
	}
	if (Dpw)
	    pw_batch_off(Dpw);
	return(1);
}

flipscale()			/* XOR the scale line in */
{
	register int i, x, y, ox, oy;

	if (Nscale < 1)
	    return;
	x = B_TO_PIX(Scale[0].bpos);
	y = S_TO_PIX(Scale[0].scale);
	if (Nscale == 1)
	    pw_vector(Dpw, B_TO_PIX(0), y, B_TO_PIX(Pcnt*BPP), y, PIX_INV, 1);
	else {
	    for (i = 1; i < Nscale; i++) {
		ox = x;
		oy = y;
		x = B_TO_PIX(Scale[i].bpos);
		y = S_TO_PIX(Scale[i].scale);
		pw_vector(Dpw, ox, oy, x, y, PIX_INV, 1);
	    }
	}
}

upmisc()		/* update misc - canvas size, frame label, etc. */
{
	sprintf(Flbuf, "-=[ QWE ]=-   %s, %d page%s",
	 Oldfile, Pcnt, Pcnt==1? "" : "s");
	window_set(Dframe, FRAME_LABEL,	Flbuf, 0);
	Dcwidth = Ppp * Pcnt;
	window_set(Dcanvas, CANVAS_WIDTH, Dcwidth, 0);
	scrollbar_scroll_to(Dcsb, 0);
	panel_set(Mslide, PANEL_VALUE, Ppp, 0);
}

miscinits()		/* do one-time inits */
{
	Rect tmp;
	Cursor cursor;
	void input();

	tmp.r_left = 8;
	tmp.r_top = 8;
/****
	tmp.r_width = BORDER + MARGIN + VIEWWIDTH + MISC;
/****/
	tmp.r_width = BORDER + MARGIN + VIEWWIDTH;
	tmp.r_height = BORDER + MARGIN + VIEWHEIGHT
/****
	 + SCROLLHEIGHT + MARGIN + PANELHEIGHT + MISC;
/****/
	 + MARGIN + PANELHEIGHT + MISC;
	Dframe = window_create(NULL, FRAME,
	    FRAME_OPEN_RECT, &tmp,
	    0);
	Dlborder = window_create(Dframe, CANVAS,
	    CANVAS_FIXED_IMAGE,		TRUE,
	    WIN_X,			0,
	    WIN_Y,			BORDER,
	    WIN_WIDTH,			BORDER,
	    WIN_HEIGHT,			VIEWHEIGHT,
	    0);
	Dcsb = scrollbar_create(
	    SCROLL_DIRECTION,		SCROLL_HORIZONTAL,
	    SCROLL_PLACEMENT,		SCROLL_SOUTH,
	    SCROLL_THICKNESS,		SCROLLHEIGHT,
	    0);
	Dcwidth = Ppp * Pcnt;
	Dcanvas = window_create(Dframe, CANVAS,
	    CANVAS_FIXED_IMAGE,		TRUE,
	    CANVAS_AUTO_SHRINK,		FALSE,
	    WIN_X,			BORDER + MARGIN,
	    WIN_Y,			0,
	    WIN_WIDTH,			VIEWWIDTH,
	    CANVAS_WIDTH,		Dcwidth,
	    WIN_HEIGHT,			BORDER + VIEWHEIGHT + SCROLLHEIGHT,
	    CANVAS_HEIGHT,		BORDER + VIEWHEIGHT,
	    WIN_HORIZONTAL_SCROLLBAR,	Dcsb,
	    WIN_CURSOR,			cursor_create(
					    CURSOR_SHOW_CROSSHAIRS, TRUE,
					    0),
	    WIN_CONSUME_PICK_EVENTS,	WIN_NO_EVENTS,
					LOC_WINENTER,
					LOC_WINEXIT,
					LOC_MOVE,
					WIN_MOUSE_BUTTONS,
					0,
	    WIN_EVENT_PROC,		input,
	    0);
	Bpw = canvas_pixwin(Dlborder);
	Dpw = canvas_pixwin(Dcanvas);
	Grey = mem_point(16, 16, 1, Grey_bits);
	B3MINIT();
	gencntl();
	upmisc();
}

gencntl()
{
	int swidth;
	void cntlchng();

	swidth = 258;
	Controls = window_create(Dframe, PANEL,
	    WIN_X,		0,
	    WIN_Y,		BORDER + VIEWHEIGHT + SCROLLHEIGHT,
	    WIN_WIDTH,		WIN_EXTEND_TO_EDGE,
	    WIN_HEIGHT,		PANELHEIGHT,
	    PANEL_SHOW_MENU,	FALSE,
	    PANEL_LABEL_BOLD,	TRUE,
	    PANEL_ITEM_X_GAP,	25,
	    0);
	Mslide = panel_create_item(Controls, PANEL_SLIDER,
	    PANEL_LABEL_STRING,	"PIXELS/PAGE",
	    PANEL_SLIDER_WIDTH,	swidth,
	    PANEL_VALUE,	Ppp,
	    PANEL_MIN_VALUE,	4,
	    PANEL_MAX_VALUE,	BPP,
	    PANEL_NOTIFY_PROC,	cntlchng,
	    0);
	Tslide = panel_create_item(Controls, PANEL_SLIDER,
	    PANEL_LABEL_STRING,	" BYTES/TICK",
	    PANEL_SLIDER_WIDTH,	swidth,
	    PANEL_VALUE,	Tticks,
	    PANEL_MIN_VALUE,	2,
	    PANEL_MAX_VALUE,	BPP,
	    PANEL_NOTIFY_PROC,	cntlchng,
	    0);
	Aslide = panel_create_item(Controls, PANEL_SLIDER,
	    PANEL_LABEL_STRING,	"VALUES/TICK",
	    PANEL_SLIDER_WIDTH,	swidth,
	    PANEL_VALUE,	Aticks,
	    PANEL_MIN_VALUE,	2,
	    PANEL_MAX_VALUE,	128,
	    PANEL_NOTIFY_PROC,	cntlchng,
	    0);
	Rslide = panel_create_item(Controls, PANEL_SLIDER,
	    PANEL_LABEL_STRING,	" ROTATE AMT",
	    PANEL_SLIDER_WIDTH,	swidth,
	    PANEL_VALUE,	Ramt,
	    PANEL_MIN_VALUE,	1,
	    PANEL_MAX_VALUE,	BPP,
	    PANEL_NOTIFY_PROC,	cntlchng,
	    0);
	window_fit_height(Controls);
}

void
cntlchng(item, value, event)
Panel_item item;
Event	*event;
{
	if (item == Mslide) {
	    if (Ppp != value) {
		pw_writebackground(Dpw, 0,BORDER, Dcwidth,VIEWHEIGHT, PIX_CLR);
		Ppp = value;
		upmisc();
		redraw();
	    }
	} else if (item == Tslide) {
	    if (Tticks != value) {
		Tticks = value;
		drawticks();
	    }
	} else if (item == Aslide) {
	    if (Aticks != value) {
		Aticks = value;
		drawticks();
	    }
	} else if (item == Rslide)
	    Ramt = value;
}

busy_cursor()
{
	Cursor cursor;

	if (Cbusy++ == 0) {
/****/fprintf(stderr, "busy... ");
#ifdef	NOTHING_WORKS
	    cursor = window_get(Dframe, WIN_CURSOR);
	    cursor_set(cursor, CURSOR_SHOW_CROSSHAIRS, FALSE, 0);
	    window_set(Dframe, WIN_CURSOR, cursor, 0);
/****/window_release_event_lock(Dframe);
#endif
	}
	return(Cbusy);
}

ready_cursor()
{
	Cursor cursor;

	if (--Cbusy <= 0) {
/****/fprintf(stderr, "\n");
#ifdef	NOTHING_WORKS
	    cursor = window_get(Dframe, WIN_CURSOR);
	    cursor_set(cursor, CURSOR_SHOW_CROSSHAIRS, TRUE, 0);
	    window_set(Dframe, WIN_CURSOR, cursor, 0);
#endif
	    Cbusy = 0;
	}
	return(Cbusy);
}

void
input(window, event, arg)
Window	window;
Event	*event;
{
	register int i, change;

	i = event_id(event);
	if (i != MS_LEFT && i != MS_MIDDLE && i != MS_RIGHT)
	    return;
	busy_cursor();
	change = 0;
	if (i == MS_LEFT)
	    change = mouseleft(window, event);
	else if (i == MS_MIDDLE)
	    change = mousemiddle(window, event);
	else if (i == MS_RIGHT)
	    change = mouseright(window, event);
	if (change)
	    redraw();
	ready_cursor();
}

mouseleft(window, event)
Window	window;
Event	*event;
{
	char tbuf[64], vbuf[64], abuf[64];
	int x, y, byte, val, p, b;
	Menu m;

	if (!event_is_down(event))
	    return(0);
	x = event_x(event);
	y = event_y(event);
	byte = PIX_TO_B(x);
	val = PIX_TO_D(y);
	p = byte / BPP;
	b = byte % BPP;
	sprintf(tbuf, "%d page%s, %d byte%s",
	 p, p==1? "" : "s", b, b==1? "" : "s");
	sprintf(vbuf, "Crosshair: %d", val);
	sprintf(abuf, "Amplitude: %d", Data[byte]);
	m = menu_create(MENU_STRINGS, tbuf, vbuf, abuf, 0, 0);
	menu_show(m, window, event, 0);
	menu_destroy(m);
	return(0);
}

mousemiddle(window, event)
Window	window;
Event	*event;
{
	char tbuf[64], vbuf[64], abuf[64], dbuf[64];
	int i, byte, p, b, val, j, di;
	Menu m;

	if (!event_is_down(event))
	    return(0);
	byte = PIX_TO_B(event_x(event));
	val = PIX_TO_S(event_y(event));
	p = byte / BPP;
	b = byte % BPP;
	sprintf(tbuf, "%d page%s, %d byte%s",
	 p, p==1? "" : "s", b, b==1? "" : "s");
	sprintf(vbuf, "Data:%d, Scale:%g", Data[byte], val / 256.);
	sprintf(abuf, "ADD %g @ %d?", val / 256., byte);
	if (Nscale > 0) {
	    for (di = 0; di < Nscale-1 && byte > Scale[di].bpos; di++);
	    if (di > 0 && Scale[di].bpos - byte > byte - Scale[di-1].bpos)
		--di;
	    sprintf(dbuf,
	     "DEL %g @ %d?", Scale[di].scale / 256., Scale[di].bpos);
	    m = menu_create(MENU_STRINGS,
	     "SCALING", tbuf, vbuf, abuf, dbuf, 0, 0);
	} else
	    m = menu_create(MENU_STRINGS, "SCALING", tbuf, vbuf, abuf, 0, 0);
	i = (int) menu_show(m, window, event, 0);
	menu_destroy(m);
	flipscale();
	if (i == 4) {					/* add a new point */
	    for (i = 0; i < Nscale && byte > Scale[i].bpos; i++);
	    if (i < Nscale && byte == Scale[i].bpos) {
		if (val != Scale[i].scale)
		    Scale[i].scale = val;
	    } else if (Nscale >= NUMSCALE) {
		printf("Too many scale points.\n");
	    } else {
		if (i < Nscale)
		    for (j = Nscale; --j >= i; Scale[j+1] = Scale[j]);
		Nscale++;
		Scale[i].bpos = byte;
		Scale[i].scale = val;
	    }
	} else if (i == 5) {				/* delete old point */
	    for (--Nscale; di < Nscale; di++)
		Scale[di] = Scale[di+1];
	}
	flipscale();
	return(0);
}

mouseright(window, event)
Window	window;
Event	*event;
{
	register int i, nbytes, change, fh, beg, end;
	char *fp, *bp, fbuf[128];
	uchar buf[BPP];

	change = 0;
	i = (int) menu_show(b3mp, window, event, 0);
	switch(i) {
	case PLAY:
	    if (close(open(SEQFILE, 0)) < 0) {
		if ((fh = creat(SEQFILE, 0644)) < 0) {
		    perror(SEQFILE);
		    break;
		} else {
		    buf[0] = 0;
		    buf[1] = 0x90 | Chan;
		    buf[2] = i;
		    buf[3] = 0x40;
		    buf[4] = 24;
		    buf[5] = i;
		    buf[6] = 0;
		    for (i = 36; i < 97; i++) {
			buf[2] = buf[5] = i;
			write(fh, buf, 7);
		    }
		    close(fh);
		}
	    }
	    sprintf(buf, "play %s", SEQFILE);
	    system(buf);
	    break;
	case DUMP:
	    if ((i = creat(WDRFILE, 0644)) < 0) {
		perror(WDRFILE);
		break;
	    }
	    buf[0] = SX_CMD;
	    buf[1] = ID_ENSONIQ;
	    buf[2] = ID_MIRAGE;
	    buf[3] = MIR_SXF_REQ_WAVE;
	    buf[4] = SX_EOB;
	    write(i, buf, 5);
	    close(i);
	    strcpy(Oldfile, "/tmp/qwe.dmp");
	    sprintf(buf, "sxmpu <%s -r >%s", WDRFILE, Oldfile);
	    system(buf);
	    readdata(Oldfile);
	    upmisc();
	    change++;
	    break;
	case LOAD:
	    if ((i = creat(TMPFILE, 0644)) < 0) {
		perror(TMPFILE);
		break;
	    }
	    writedata(i);
	    close(i);
	    sprintf(buf, "sxmpu <%s", TMPFILE);
	    system(buf);
	    fprintf(stderr, "\"%s\" written & sent\n", TMPFILE);
	    break;
	case ROTL:
	    nbytes = BPP * Pcnt;
	    for (i = 0; i < Ramt; i++)
		buf[i] = Data[i];
	    for (i = Ramt; i < nbytes; i++)
		Data[i - Ramt] = Data[i];
	    for (i = 0; i < Ramt; i++)
		Data[nbytes - Ramt + i] = buf[i];
	    change++;
	    break;
	case ROTR:
	    nbytes = BPP * Pcnt;
	    for (i = 0; i < Ramt; i++)
		buf[i] = Data[nbytes - Ramt + i];
	    for (i = nbytes; --i >= Ramt; )
		Data[i] = Data[i - Ramt];
	    for (i = 0; i < Ramt; i++)
		Data[i] = buf[i];
	    change++;
	    break;
	case TRIM:
	    nbytes = BPP * Pcnt;
	    for (beg = 0; beg < nbytes && Data[beg] == 0x80; beg++);
	    for (end = nbytes; --end > beg && Data[end] == 0x80; );
	    if (beg > 0)
		for (i = beg; i < nbytes; i++)
		    Data[i - beg] = Data[i];
	    Pcnt = (end - beg + BPP - 1) / BPP;
	    upmisc();
	    change++;
	    break;
	case SCALE:
	    nbytes = BPP * Pcnt;
	    if (Nscale < 1)
		break;
	    if (Nscale == 1) {
		for (i = nbytes; --i >= 0; ) {
		    if (Data[i] != 0)
			Data[i] = (Data[i] * Scale[0].scale + 127) >> 8;
		}
	    } else {	int lb, hb, ls, hs, b, q;
		for (i = 1; i < Nscale; i++) {
		    lb = Scale[i-1].bpos;
		    if (lb >= nbytes)
			break;
		    ls = Scale[i-1].scale;
		    hb = Scale[i].bpos;
		    if (hb > nbytes)
			hb = nbytes;
		    hs = Scale[i].scale;
		    for (b = lb; b++ < hb; ) {
			if (Data[b] != 0) {
			    q = D_TO_NUM(Data[b]);
			    q = (q * (ls * (hb - b) + hs * (b - lb)));
			    q = q / (hb - lb);
			    q = (q + 128) >> 8;
			    q = NUM_TO_D(q);
			    Data[b] = q < 0? 1 : (q > 0xFF? 0xFF : q);
			}
		    }
		}
	    }
	    change++;
	    break;
	case NORMALIZE:
	    nbytes = BPP * Pcnt;
	    {		int minv, maxv, den, q;
		minv = maxv = 0x80;
		for (i = nbytes; --i >= 0; ) {
		    if (Data[i]) {
			if (Data[i] < minv)
			    minv = Data[i];
			else if (Data[i] > maxv)
			    maxv = Data[i];
		    }
		}
		minv = 0x80 - minv;
		maxv = maxv - 0x80;
		den = maxv > minv? maxv : minv;
/****/fprintf(stderr, "scaling=%g\n", (float) 0x7F / den);
		if (den < 0x7F) {
		    for (i = nbytes; --i >= 0; ) {
			if (Data[i] != 0) {
			    q = D_TO_NUM(Data[i]);
			    Data[i] = NUM_TO_D((q * 0x7F) / den);
			}
		    }
		    change++;
		}
	    }
	    break;
	case READ:
	    printf("Filename? [%s] ", Oldfile);
	    if (fgets(fbuf, sizeof fbuf, stdin)) {
		if (*fbuf && *fbuf != '\n') {
		    for (fp = Oldfile, bp = fbuf; (*fp = *bp++) > ' '; fp++);
		    *fp = '\0';
		}
		readdata(Oldfile);
		upmisc();
		change++;
	    }
	    break;
	case WRITE:
	    if ((i = creat(OUTFILE, 0644)) < 0)
		perror(OUTFILE);
	    else {
		writedata(i);
		close(i);
		fprintf(stderr, "\"%s\" written\n", OUTFILE);
	    }
	    break;
	case READDSC:
	    printf("Filename? [%s] ", Oldfile);
	    if (fgets(fbuf, sizeof fbuf, stdin)) {
		if (*fbuf && *fbuf != '\n') {
		    for (fp = Oldfile, bp = fbuf; (*fp = *bp++) > ' '; fp++);
		    *fp = '\0';
		}
		readdsc(Oldfile);
		upmisc();
		change++;
	    }
	    break;
	case WRITEDSC:
	    if ((i = creat(OUTFILE, 0644)) < 0)
		perror(OUTFILE);
	    else {
		writedsc(i);
		close(i);
		fprintf(stderr, "\"%s\" written in dsc format\n", OUTFILE);
	    }
	    break;
	case QUIT:
	    quit();
	    break;
	}
	return(change);
}

readdata(file)
char	*file;
{
	register int i, j, nbytes;
	FILE *ifp;

	if (file && *file) {
	    if ((ifp = sopen(file, "r")) == NULL) {
		perror(file);
		return(0);
	    }
	} else {
	    ifp = 0;
	    sprintf(Oldfile, "BLANK");
	}
	while (ifp) {				/* find wavedata */
	    while ((i = fgetc(ifp)) != EOF && i != SX_CMD);
	    if (i != SX_CMD) {
		fprintf(stderr, "no wavedata found in %s\n", file);
		exit(1);
	    }
	    if (fgetc(ifp) == ID_ENSONIQ
	     && fgetc(ifp) == ID_MIRAGE
	     && fgetc(ifp) == MIR_SXF_DAT_WAVE)
		break;
	}
	if (ifp) {
	    Pcnt = fgetc(ifp);
	    Pcnt += fgetc(ifp) << 4;
	} else
	    Pcnt = 16;
	if (Pcnt <= 0 || Pcnt > 256) {
	    fprintf(stderr, "Weird page count; %d\n", Pcnt);
	    exit(1);
	}
	nbytes = BPP * Pcnt;
	Data = (uchar *) sbrk(nbytes);
	if ((int) Data == -1) {
	    fprintf(stderr, "sbrk(%d) failed\n", nbytes);
	    exit(3);
	}
	for (i = 0; i < nbytes; i++) {
	    if (ifp) {
		j = fgetc(ifp);
		j += fgetc(ifp) << 4;
	    } else
		j = 0x80;
	    Data[i] = j;
	}
	if (ifp)
	    fclose(ifp);
	Ppp = VIEWWIDTH / Pcnt;
	return(1);
}

writedata(fh)
{
	register uchar *dp, *bp;
	register int csum;
	uchar buf[2 * BPP];
	int p, i;

	csum = 0;
	buf[0] = RT_RESET;
	buf[1] = SX_CMD;
	buf[2] = ID_ENSONIQ;
	buf[3] = ID_MIRAGE;
	buf[4] = MIR_SXF_DAT_WAVE;
	buf[5] = (Pcnt & 0x0F);
	buf[6] = (Pcnt >> 4);
	csum = buf[5] + buf[6];
	write(fh, buf, 7);
	dp = Data;
	for (p = 0; p < Pcnt; p++) {
	    bp = buf;
	    for (i = 0; i < BPP; i++) {
		*bp = (*dp & 0x0F);
		csum += *bp++;
		*bp = (*dp++ & 0xF0) >> 4;
		csum += *bp++;
	    }
	    write(fh, buf, 512);
	    csum &= 0x7F;
	}
	buf[0] = (csum & 0x7F);
	buf[1] = 0xF7;
	write(fh, buf, 2);
}

readdsc(file)
char	*file;
{
	register int i, j, k, nbytes;
	char c;
	long rnbytes;
	struct stat statb;
	FILE *ifp;

	if (file && *file) {
	    if ((ifp = sopen(file, "r")) == NULL) {
		perror(file);
		return(0);
	    }
	} else
	    return(readdata(0));
	j = getc(ifp);
	k = getc(ifp);
	if (((k << 8) | j) != DSCMAGIC) {
	    fprintf(stderr, "%s is not a DSC file\n", file);
	    return(0);
	}
	if (fstat(fileno(ifp), &statb) < 0) {
	    perror(file);
	    return(0);
	}
	rnbytes = (statb.st_size - 2) / 2;	/* ignore half of them 16->8 */
	Pcnt = (rnbytes + BPP - 1) / BPP;
	if (Pcnt <= 0 || Pcnt > 256) {
	    fprintf(stderr, "weird size; %d (pages)\n", Pcnt);
	    exit(1);
	}
	nbytes = BPP * Pcnt;
	Data = (uchar *) sbrk(nbytes);
	if ((int) Data == -1) {
	    fprintf(stderr, "sbrk(%d) failed\n", nbytes);
	    exit(3);
	}
	for (i = 0; i < nbytes; i++) {
	    if (ifp) {
		j = fgetc(ifp);
		k = fgetc(ifp);
		if (j == EOF || k == EOF) {
		    fclose(ifp);
		    ifp = 0;
		    j = 0x80;
		} else {
		    c = k;				/* extend sign */
		    j = c + ((j & 0x80)? 1 : 0);	/* half adjust */
		    j += 0x80;				/* move 0 to 0x80 */
		}
	    } else
		j = 0x80;
	    Data[i] = j;
	}
	if (ifp)
	    fclose(ifp);
	Ppp = VIEWWIDTH / Pcnt;
	return(1);
}

writedsc(fh)
{
	register uchar *dp, *bp;
	register int csum;
	uchar buf[2 * BPP];
	int p, i;

	buf[0] = (DSCMAGIC & 0xFF);
	buf[1] = (DSCMAGIC >> 8);
	write(fh, buf, 2);
	dp = Data;
	for (p = 0; p < Pcnt; p++) {
	    bp = buf;
	    for (i = 0; i < BPP; i++) {
		*bp++ = 0;
		*bp++ = *dp++ - 0x80;		/* move 0x80 to 0 */
	    }
	    write(fh, buf, bp - buf);
	}
}

quit()
{
	while (ready_cursor());
	exit(0);
	/*NOTREACHED*/
}
