/* fv is a binary file editor written by Chris Hooper (cdh@mtu.edu)
 *	on 6-2-91 and has gone through many revisions since then.
 *      Much inspiration was given by Bill Moore, the first real user!
 *
 *   Author:  CHRISTOPHER D HOOPER
 *
 *   fv source Copyright (c) 1992 - 1998  Chris Hooper
 *
 *   Modification and redistribution is strictly prohibited.
 *   Sale of this software above media cost is prohibited.
 *
 *   Except for above two restrictions, this software may be used for
 *       any purpose, commercial or private.
 *
 *   Disclaimer:  This product is fit for no use, foreign or domestic.
 *                Use implies knowledge you intend to destroy something.
 */

#include <stdio.h>
#include <curses.h>
#include "main.h"
#include "screen.h"
#include "io.h"

#include "pcurses.h"

#ifdef LINT
#	include "lint.h"
#endif

int (*xaddstr)();
int (*xmove)();
int (*xdelwin)();
int (*xrefresh)();
int (*xtouchwin)();
int (*xstandout)();
int (*xstandend)();
int (*xclrtoeol)();
int (*xclrtobot)();
int (*xclear)();
int (*xaddch)();
int (*xgetch)();
WINDOW *(*xnewwin)();

WINDOW  *main_window;		/* main display window */
WINDOW  *status_window;		/* lower single row status window */
int	status_line;
extern	int zmode;
extern	int Zmode;
extern	ulong cursor_address;
ulong	inbuf_size;
uchar	ascii[256];		/* ascii translation map */
uchar	zmodes[] = ". _~!@#$%^&*";
char	*hex = "0123456789abcdef";	/* hexadecimal translation map */
char	*divmsg[] = {
 "    ", "   2", "   4", "   8", "  16", "  32", "  64", " 128", " 256", " 512",
 "  1K", "  2K", "  4K", "  8K", " 16K", " 32K", " 64K", "128K", "256K", "512K",
 "  1M", "  2M", "  4M", "  8M", " 16M", " 32M", " 64M", "128M", "256M", "512M"
};

int curses_up = 0;
static void strhex8();
static void strhex32();

void show_addr(address)
ulong address;
{
	register int index;
	ulong temp;

	status_range(address, address + buf_size);
	
#if defined(CURSES_UPDATE_BUG)
	/* evil hack because curses for Linux is broken */
	xclear(stdscr);
	xmove(stdscr, 0, 0);
	xclrtobot(stdscr);
	xtouchwin(main_window);
	xtouchwin(status_window);
#endif

	if (swapbytes | Swapbytes | SWAPBYTES) {
	    if (chdis) {
#ifdef CHAR_COLOR
		wattron(main_window, CHAR_COLOR);
#endif
		temp = addis * 10 + wpline * 9 * wdis;
	        for (index = 0; index < buf_size; index++) {
		    if (index % cpline == 0)
	 	        xmove(main_window, index / cpline, temp);
		    if (index < inbuf_size)
			xaddch_filt(buffer[index ^ Swapbytes ^ SWAPBYTES]);
		    else
			xaddch(main_window, ' ');
		}
	    }

	    for (index = 0; index < (buf_size + 3) / 4; index++) {
		if (addis && ((index % wpline) == 0)) {
#ifdef ADDR_COLOR
			wattron(main_window, ADDR_COLOR);
#endif
			xmove(main_window, index / wpline, 0);
			if (index >= (inbuf_size + 3) / 4)
			    xaddstr(main_window, "          ");
			else {
			    if (divmod2)
				temp = (index * 4 + address) &
				       ((1 << divisor2) - 1);
			    else
				temp = (index * 4 + address) >> divisor2;

			    if (wdismode)
				sprintf(out_string, "%8d: ", (int) temp);
			    else
				sprintf(out_string, "%08x: ", (int) temp);
			    xaddstr(main_window, out_string);
			}
		}
		if (wdis) {
#ifdef WORD_COLOR
		    wattron(main_window, WORD_COLOR);
#endif
		    xmove(main_window, index / wpline, addis * 10 +
		                        (index % wpline) * 9 * wdis);
		    if (index < (inbuf_size + 3) / 4)
			switch (wdismode) {
			    case 0:	/* hex */
				strhex32(out_string, swap32(ibuffer[index ^ (SWAPBYTES | swapbytes) >> 2]));
				out_string[8] = ' ';
				out_string[9] = '\0';
				xaddstr(main_window, out_string);
				break;
			    case 1:	/* decimal */
				sprintf(out_string, "%8d ", (int)
					swap32(ibuffer[index ^ (swapbytes | SWAPBYTES) >> 2]));
				out_string[9] = '\0';
				xaddstr(main_window, out_string);
				break;
			    case 2:	/* float */
				(void) sprintf(out_string, "%-9f ",
					    *((float *) &ibuffer[index]));
				out_string[9] = '\0';
				xaddstr(main_window, out_string);
				break;
			    case 3:	/* double */
				if ((index & 1) == 0) {
				    (void) sprintf(out_string, "%-17f ",
					        *((double *) &ibuffer[index]));
				    out_string[17] = '\0';
				    xaddstr(main_window, out_string);
				}
				break;
			}
		    else
			xaddstr(main_window, "         ");
		}
	    }
	} else {
	    if (chdis) {
#ifdef CHAR_COLOR
		wattron(main_window, CHAR_COLOR);
#endif
		temp = addis * 10 + wpline * 9 * wdis;
	        for (index = 0; index < buf_size; index++) {
		    if (index % cpline == 0)
	 	        xmove(main_window, index / cpline, temp);
		    if (index < inbuf_size)
			xaddch_filt(buffer[index]);
		    else
			xaddch(main_window, ' ');
		}
	    }

	    for (index = 0; index < (buf_size + 3) / 4; index++) {
		if (addis && ((index % wpline) == 0)) {
#ifdef ADDR_COLOR
			wattron(main_window, ADDR_COLOR);
#endif
			xmove(main_window, index / wpline, 0);
			if (index >= (inbuf_size + 3) / 4)
			    xaddstr(main_window, "          ");
			else {
			    if (divmod2)
				temp = (index * 4 + address) &
				       ((1 << divisor2) - 1);
			    else
				temp = (index * 4 + address) >> divisor2;

			    if (wdismode)
				sprintf(out_string, "%8d: ", (int) temp);
			    else
				sprintf(out_string, "%08x: ", (int) temp);
			    xaddstr(main_window, out_string);
			}
		}
		if (wdis) {
#ifdef WORD_COLOR
		    wattron(main_window, WORD_COLOR);
#endif
		    xmove(main_window, index / wpline, addis * 10 + (index % wpline) * 9 * wdis);
		    if (index < (inbuf_size + 3) / 4)
			switch (wdismode) {
			    case 0:
				strhex32(out_string, ibuffer[index]);
				out_string[8] = ' ';
				out_string[9] = '\0';
				xaddstr(main_window, out_string);
				break;
			    case 1:
				(void) sprintf(out_string, "%8d ", (uint) ibuffer[index]);
				out_string[8] = '\0';
				xaddstr(main_window, out_string);
				break;
			    case 2:
				(void) sprintf(out_string, "%-9f ",
					    *((float *) &ibuffer[index]));
				out_string[8] = '\0';
				xaddstr(main_window, out_string);
				break;
			    case 3:
				if ((index & 1) == 0) {
				    (void) sprintf(out_string, "%-17f ",
					        *((double *) &ibuffer[index]));
				    out_string[17] = '\0';
				    xaddstr(main_window, out_string);
				}
				break;
			}
		    else
			xaddstr(main_window, "         ");
		}
	    }
	}
	if (!kbuffer_size)
		xrefresh(main_window);

#ifdef NO_COLOR
	wattrset(main_window, 0);
#endif
}

void init()
{
	init_ascii();

	if (noscreen) {
	    wpline   = 8;
	    cpline   = 32;
	    buf_size = 8192;
#ifdef HUGE_BUFFER
	    wpline   *= 8;
	    cpline   *= 8;
	    buf_size *= 8;
#endif
	    return;
	}

#if defined(COLOR_CURSES) && defined(Amiga)
	start_color();
#endif

#ifdef OS9
	if (initscr() == 0)
		LINES = 0;
#else
	initscr();
#endif

#if defined(COLOR_CURSES) && !defined(Amiga)
	start_color();
#endif

	if (LINES == 0)
		error_exit("Curses initialization failed.");

	curses_up = 1;
#if defined(COLOR_CURSES)
			/* COLOR 1-7   RED  GRN  BLUE  1-1000 */
	init_color(WORD_COLOR, 900, 900, 200);	/* word disp is yellow */
	init_color(CHAR_COLOR, 900, 900, 900);	/* char disp is white */
	init_color(ADDR_COLOR, 420, 920, 840);	/* address column is cyan */
	init_color(STAT_COLOR, 1000, 240, 80);	/* status area is red */
	init_color(CRSR_COLOR, 688, 859, 984);	/* cursor color is blue */
#endif

#ifdef Dynix
	raw();
#else
	cbreak();
#endif
	noecho();
	nonl();
	wclear(stdscr);
	wrefresh(stdscr);

	wpline   = (COLS - addis * 10 - 1) / (9 * wdis + chdis * 4);
	cpline   = wpline << 2;
	buf_size = (LINES - 1) * cpline;

	status_line = LINES - 1;

	main_window = xnewwin(LINES - 1, COLS, status_line ? 0 : 1, 0);
	status_window = xnewwin(1, COLS, status_line, 0);

#if defined(Solaris2) || defined(Amiga) || defined(SYSV)
	keypad(stdscr, TRUE);
	keypad(main_window, TRUE);
#endif

#ifdef STAT_COLOR
	wattrset(status_window, STAT_COLOR);
#endif

#if defined(Solaris2) || defined(Amiga) || defined(SYSV)
	leaveok(main_window, TRUE);
#endif
	leaveok(status_window, TRUE);

	status_mode();
}

void de_init()
{
	if (!curses_up)
		return;

	wclear(stdscr);
	wrefresh(stdscr);
	echo();
	nl();
	endwin();
	curses_up = 0;
}

void update_cursor(mode)
int mode;
{
	int index;
	int row;
	int offset;
	int ioffset;
	int column;
	int icoffset;

	if (noscreen)
		return;

	if (mode == ON) {
#ifdef CRSR_COLOR
	    wattrset(main_window, CRSR_COLOR);
#endif
	    xstandout(main_window);
	} else
	    xstandend(main_window);

	offset = ALIGNED(cursor_address - view_address);

	if (chdis) {
#ifdef CHAR_COLOR
	    if (mode == OFF)
		wattron(main_window, CHAR_COLOR);
#endif
	    for (index = 0; index < alignment; index++) {
		ioffset = (offset + index) ^ Swapbytes;
		xmove(main_window, ioffset / cpline, addis * 10 +
		      wpline * 9 * wdis + (ioffset % cpline));
		xaddch_filt(buffer[(offset + index) ^ SWAPBYTES]);
	    }
	}


	if (wdis) {
#ifdef WORD_COLOR
	    if (mode == OFF)
		wattron(main_window, WORD_COLOR);
#endif
	    ioffset = offset ^ swapbytes;

	    if (alignment == 8)
	        icoffset = (ioffset % cpline) & ~3;
	    else
	        icoffset = ALIGNED(ioffset % cpline);

	    column = (icoffset >> 2) + (icoffset << 1) + addis * 10;
	    row    = ioffset / cpline;

	    xmove(main_window, row, column);
	    switch (wdismode) {
		case 0:			/* hex */
		    if (alignment == 1)
			strhex8(out_string, (ulong) buffer[offset ^ SWAPBYTES]);
		    else if (alignment == 2)
			if (swapbytes & 1) {
#ifdef LITTLE_ENDIAN
			  strhex8(out_string + 2, (ulong) buffer[offset ^ SWAPBYTES]);
#endif
			  strhex8(out_string, (ulong) buffer[(offset + 1) ^ SWAPBYTES]);
#ifndef LITTLE_ENDIAN
			  strhex8(out_string + 2, (ulong) buffer[offset ^ SWAPBYTES]);
#endif
			} else {
#ifdef LITTLE_ENDIAN
			  strhex8(out_string + 2, (ulong) buffer[(offset + 1) ^ SWAPBYTES]);
#endif
			  strhex8(out_string, (ulong) buffer[offset ^ SWAPBYTES]);
#ifndef LITTLE_ENDIAN
			  strhex8(out_string + 2, (ulong) buffer[(offset + 1) ^ SWAPBYTES]);
#endif
			}
		    else if (alignment == 4)
			strhex32(out_string, 
			         swap32(ibuffer[(offset ^ SWAPBYTES) >> 2]));
		    else {	/* alignment == 8 */
			strhex32(out_string, 
			         swap32(ibuffer[(offset ^ SWAPBYTES) >> 2]));
			offset  += 4;
			ioffset  = offset ^ swapbytes;
			if (ioffset < buf_size) {
			    xaddstr(main_window, out_string);
			    icoffset = (ioffset % cpline) & ~3;
			    column  = (icoffset >> 2) + (icoffset << 1) +
			               addis * 10;
			    row      = ioffset / cpline;
			    xmove(main_window, row, column);
			    strhex32(out_string, 
			             swap32(ibuffer[(offset ^ SWAPBYTES) >> 2]));
			}
		    }
		    break;
		case 1:			/* integer */
		    (void) sprintf(out_string, "%8d", (int)
				   swap32(ibuffer[(ioffset ^ SWAPBYTES) >> 2]));
		    out_string[8] = '\0';
		    break;
		case 2:			/* float */
		    (void) sprintf(out_string, "%-8f",
				   *((float *) &ibuffer[offset >> 2]));
		    if (strlen(out_string) > 8) {
			if (mode == ON)
			    out_string[7] = '*';
			out_string[8] = '\0';
		    }
		    break;
		case 3:			/* double */
		    (void) sprintf(out_string, "%-17f",
				   *((double *) &ibuffer[offset >> 2]));
		    if (strlen(out_string) > 17) {
			if (mode == ON)
			    out_string[16] = '*';
		        out_string[17] = '\0';
		    }
		    break;
	    }

	    xaddstr(main_window, out_string);
	}

	if (mode == ON) {
#ifdef NO_COLOR
	    wattrset(main_window, 0);
#endif
	    xstandend(main_window);
	}
}


void init_ascii()
{
	uchar dot;
	int index;

	dot = zmodes[zmode];
	
	ascii[0] = zmodes[Zmode];

	for (index = 1; index < 32; index++)
		ascii[index] = dot;

	ascii['\011'] = ' ';
	ascii['\012'] = ' ';
	ascii['\015'] = ' ';

	for (; index < 128; index++)
		ascii[index] = index;

#ifdef SHOW_EXTASCII
	for (; index < 161; index++)
		ascii[index] = dot;

	do {
		index++;
		ascii[index] = index;
	} while (index < 255);
#else
	for (index = 127; index <= 255; index++)
		ascii[index] = dot;
#endif
}

void status_range(range1, range2)
ulong range1;
ulong range2;
{
	char out_string[32];

	if (noscreen)
		return;

	if (COLS < 28)
		return;

        leaveok(status_window, FALSE);

	if (divmod) {
	    range1 &= ((1 << divisor) - 1);
	    range2 &= ((1 << divisor) - 1);
	    xstandout(status_window);
	} else {
	    range1 >>= divisor;
	    range2 >>= divisor;
	}
	xmove(status_window, 0, COLS - 21);
	xaddstr(status_window, divmsg[divisor]);
	if (divmod && !divmod2)
	    xstandend(status_window);
	else if (!divmod && divmod2)
	    xstandout(status_window);

	xaddch(status_window, ' ');
	xaddstr(status_window, divmsg[divisor2]);
	if (divmod2)
	    xstandend(status_window);

	xmove(status_window, 0, 19);
	xaddch(status_window, ' ');
	xmove(status_window, 0, 0);
	if (wdismode)
	    sprintf(out_string, "[%8d-%8d]", (uint) range1, (uint) range2);
	else 
	    sprintf(out_string, "[%08x-%08x]", (uint) range1, (uint) range2);
	xaddstr(status_window, out_string);

	xrefresh(status_window);

        leaveok(status_window, TRUE);
}

void status_filename()
{
	int len;
	int namelen;
	int tildes = 0;
	char *ptr = filename[filenum];

	if (COLS < 28)
		return;

	xmove(status_window, 0, 20);
	switch (wdismode) {
	    case 0:     /* hex */
		sprintf(out_string, "(%x of %x) ",
			(uint) cursor_address, (uint) fend);
		break;
	    case 1:     /* decimal */
	    default:    /* all other types */
		sprintf(out_string, "(%d of %d) ",
			(uint) cursor_address, (uint) fend);
		break;
	}
	len = strlen(out_string);
	namelen = strlen(ptr);
	while ((len + namelen + tildes > COLS - 42) && namelen) {
	    tildes = 2;
	    ptr++;
	    namelen--;
	}
	xaddstr(status_window, out_string);
	if (tildes)
	    xaddstr(status_window, "~~");
	xaddstr(status_window, ptr);
	xrefresh(status_window);
}


void status_mode()
{
	if (noscreen)
		return;

	if (COLS < 18)
		return;

	xmove(status_window, 0, COLS - 11);

	if (Swapbytes | SWAPBYTES)
		xstandout(status_window);
	xaddch(status_window, " w?l???q w?l???q W?L???Q????????"[
	       swapbytes | Swapbytes | SWAPBYTES | (swapmode << 3)]);
	if (Swapbytes | SWAPBYTES)
		xstandend(status_window);

	xaddch(status_window, '0' + alignment);
	if ((ronly | force_ronly) && !force_write)
		xaddstr(status_window, "R");
	else
		xaddstr(status_window, "W");

	xaddstr(status_window, " ");

	if (mode == STRING)
		xaddstr(status_window, "Str");
	else
		xaddstr(status_window, "Num");
	switch (wdismode) {
	    case 0:
		xaddstr(status_window, "Hex");
		break;
	    case 1:
		xaddstr(status_window, "Dec");
		break;
	    case 2:
		xaddstr(status_window, "Flt");
		break;
	    case 3:
		xaddstr(status_window, "Dbl");
		break;
	    case 4:
		xaddstr(status_window, "Oct");
		break;
	}

	xtouchwin(status_window);
	xrefresh(status_window);
}


ulong swap32(x)
ulong x;
{
	if ((swapbytes | SWAPBYTES) == 0)
		return(x);

	if ((swapbytes | SWAPBYTES) > 1)   /* long and quad */
		return((x >> 24) | (x << 24) |
		       ((x << 8) & (255 << 16)) |
		       ((x >> 8) & (255 << 8)));

	/* must be swap words */
	return(((x >> 8) & 0x00ff00ff) |
	       ((x << 8) & 0xff00ff00));
}

int select_file()
{
	char ch;
	int index;
	int count;
	int tlen;
	int current = filenum;
	static int lowest = 0;

	if (noscreen)
	    return(filenum);

	leaveok(status_window, FALSE);

	do {
	    xmove(status_window, 0, 0);
	    xclrtoeol(status_window);
	    xmove(status_window, 0, 0);
	    xaddstr(status_window, "File: ");
	    if (current < lowest)
		lowest = current;
	    count = strlen(filename[current]) + 1;
	    for (index = current - 1; index >= lowest; index--) {
		tlen = strlen(filename[index]) + 1;
		if (tlen + count > COLS - 6) {
		    lowest = index + 1;
		    break;
		} else
		    count += tlen;
	    }

	    for (index = lowest; index < current; index++) {
		xaddstr(status_window, filename[index]);
		xaddch(status_window, ' ');
	    }
	    xstandout(status_window);
	    xaddstr(status_window, filename[current]);
	    xstandend(status_window);

	    for (index = current + 1; index < filenames; index++) {
		tlen = strlen(filename[index]) + 1;
		if (tlen + count > COLS - 6)
			break;
		else {
		    count += tlen;
		    xaddch(status_window, ' ');
		    xaddstr(status_window, filename[index]);
		}
	    }
	    xmove(status_window, 0, COLS - 1);
	    xrefresh(status_window);

	    ch = get_key();
	    switch (ch) {
		case 'h':	/* left one file */
		case 'k':
		case 'b':
		case 2:		/* ^B */
		case 8:		/* ^H - backspace */
		    if (current > 0)
			current--;
		    break;
		case 'l':	/* right one file */
		case 'j':
		case 'f':
		case ' ':	/* space */
		case 6:		/* ^F */
		    if (current < filenames - 1)
			current++;
		    break;
		case 'q':	/* quit without selecting */
		case 24:	/* ^X */
		case 27:	/* ESC */
		    leaveok(status_window, TRUE);
		    return(filenum);
		case 10:	/* ^J */
		    ch = 13;	/* ^M */
		    break;
		case '0':
		case '^':
		    current = 0;
		    break;
		case '$':
		    current = filenames - 1;
		    break;
		case '%':
		    current = filenum;
		    break;
		case 9:		/* ^I - Tab */
		    current = (current + 1) % filenames;
		    break;
		case '?':
		    help_me(4);
		    xtouchwin(status_window);
		    xrefresh(status_window);
		    xtouchwin(main_window);
		    xrefresh(main_window);
		    break;
		case 12:	/* ^L */
		    xclear(stdscr);
		    xmove(stdscr, 0, 0);
		    xclrtobot(stdscr);
		    xtouchwin(main_window);
		    xtouchwin(status_window);
		    xrefresh(stdscr);
		    xrefresh(main_window);
		    xrefresh(status_window);
		    break;
	    }
	} while (ch != 13);	/* ^M selects */

	leaveok(status_window, TRUE);

	return(current);
}

int dummy()
{
	return(0);
}


WINDOW *wdummy()
{
	return(0);
}

int getdummy()
{
	static last = 0;

	last = !last;

	if (last)
	    return(27);     /* ESC */
	else
	    return(24);     /* ^X */
}

void assign_dummy()
{
	xaddstr = dummy;
	xmove = dummy;
	xdelwin = dummy;
	xrefresh = dummy;
	xtouchwin = dummy;
	xstandout = dummy;
	xstandend = dummy;
	xclrtoeol = dummy;
	xclrtobot = dummy;
	xclear = dummy;
	xaddch = dummy;
	xgetch = getdummy;
	xnewwin = wdummy;
}

void assign_curses()
{
	xaddstr = lwaddstr;
	xmove = wmove;
	xdelwin = delwin;
	xrefresh = wrefresh;
	xtouchwin = touchwin;
	xstandout = lwstandout;
	xstandend = lwstandend;
	xclrtoeol = wclrtoeol;
	xclrtobot = wclrtobot;
	xclear = wclear;
	xaddch = lwaddch;
	xgetch = wgetch;
	xnewwin = newwin;
}

static void strhex8(optr, value)
char *optr;
ulong value;
{
	optr[2] = '\0';
	optr[1] = hex[value & 15];
	value >>= 4;
	optr[0] = hex[value & 15];
}

static void strhex32(optr, value)
char *optr;
ulong value;
{
#ifdef LITTLE_ENDIAN
	value = (value >> 24) | (value << 24) |
	        ((value << 8) & (255 << 16)) |
	        ((value >> 8) & (255 << 8));
#endif
	optr[8] = '\0';
	optr[7] = hex[value & 15];
	value >>= 4;
	optr[6] = hex[value & 15];
	value >>= 4;
	optr[5] = hex[value & 15];
	value >>= 4;
	optr[4] = hex[value & 15];
	value >>= 4;
	optr[3] = hex[value & 15];
	value >>= 4;
	optr[2] = hex[value & 15];
	value >>= 4;
	optr[1] = hex[value & 15];
	value >>= 4;
	optr[0] = hex[value & 15];
}

#ifdef OS9
int wstandout(WINDOW *win)
{
	return(wattron(win, W_STANDOUT));
}
 
int wstandend(WINDOW *win)
{
	return(wattrset(win, 0));
}
#endif

#if defined(Linux) || defined(FreeBSD)
int lwaddstr(WINDOW *win, char *str)
{
	return(waddstr(win, str));
}
#endif	/* Linux || FreeBSD */

#if defined(Win32)
int lwclear(WINDOW *win)
{
	return(wclear(win));
}

int lwaddch(WINDOW *win, chtype ch)
{
	return(waddch(win, ch));
}

int ltouchwin(WINDOW *win)
{
	return(touchwin(win));
}

int lwstandout(WINDOW *win)
{
	return(wstandout(win));
}

int lwstandend(WINDOW *win)
{
	return(wstandend(win));
}
#endif	/* Win32 */
