/*
 *	Copyright (C) 1989 Andrew Kemmis
 *
 *	All Rights Reserved
 *
 *	Date:	Mar 1989
 */
#define  AK___CRYPT

#include <ak/stdak.h>

char	Program_name[]	= "BlockEd";
int	Version 	= CRYPTNUM(1);
int	MinorVer	= CRYPTNUM(4);
char	Date[]		= "Oct 1993";
char	CRDate[]	= "1989-93";

#include <ak/err.h>
#include <ak/err_num.h>
#include <ak/pool.h>
#include <ak/dev.h>
#include <ak/dos.h>
#include <ak/menu.h>
#include <ak/gad.h>
#include <ak/filesystem.h>
#include <ak/rawkey.h>
#include <intuition/intuition.h>
#include <graphics/gfxbase.h>
#include <ctype.h>

#include "blocked.qi"

#define STRINGJUST	(Hex ? STRINGCENTER : (Left ? 0L : STRINGRIGHT))

#define INTUITION_MESSAGE	(1L<<MyWin->UserPort->mp_SigBit)

extern	char	*strchr();
extern	char	*strrchr();
extern	struct	Window	*OpenWindow();
extern	struct	Screen	*OpenScreen();
extern	struct	IntuiMessage	*GetMsg();
extern	struct	MenuItem	*ItemAddress();
extern	struct	TextFont	*OpenDiskFont();
extern	LONG	Wait();
extern	void	*AllocMem();

int	cli;

struct	IBASE	*IBASE = (struct IBASE *)NULL;
struct	GfxBase *GfxBase = (struct GfxBase *)NULL;
struct	Library *DiskfontBase = (struct Library *)NULL;

Static	struct	AkMenu	AkMenu = AKMENUINIT;
Static	struct	TextFont	*Opal = (struct TextFont *)NULL;
Static	struct	TextAttr	OpalAttr = { (STRPTR)NULL, 0, 0, 0 };   /* Setup in main */
Static	UWORD	FXSize;
Static	UWORD	FYSize;
Static	UWORD	FYBase;
Static	int	Inhibited = 0;
Static	struct	AkGad	AkGad = AKGADINIT;
Static	struct	Gadget	*BlkGad;
Static	struct	Gadget	*DirGad;
Static	struct	Gadget	*NumGad;

Static	char	*Device;

Static	struct	AkDevStuff	Stuff = DEVSTUFFINIT;
Static	ulong	TheBlock;
Static	char	TheBlockBuf[10+1];
Static	ulong	CurBlock = 0;
Static	ulong	*Buffer;
Static	ulong	*Buffer2;
Static	uchar	*BufPtr;
Static	long	Dev = -1;
Static	int	LongPos = 0;
Static	int	BytePos = 0;
Static	struct	Menu	*ProjectMenu;
Static	struct	MenuItem	*RestoreItem;
Static	struct	RastPort	*rp;
Static	char	DirBuf[FILENAMELENGTH+1];
Static	char	NumBuf[10+1];

#define EDIT_NO 	0
#define EDIT_YES	1

Static	int	Edit = EDIT_NO;

#define MENUFLAGS	(USHORT)MENUENABLED

#define ITEMFLAGS	(USHORT)(ITEMTEXT | COMMSEQ | HIGHBOX)
#define MOREITEMFLAGS	(USHORT)(ITEMENABLED | ITEMFLAGS)
#define EXTRAITEMFLAGS	(USHORT)(CHECKIT | MENUTOGGLE | MOREITEMFLAGS)

Static	struct	PoolKey GenPoolKey;

Static	struct	Screen	*MyScreen = (struct Screen *)NULL;
Static	struct	NewScreen	MyNewScreen =
{
	0,	0,	640,	330,	2,
	0,	1,
	LACE | SPRITES | HIRES,
	CUSTOMSCREEN | SCREENBEHIND | BEEPING,
	&OpalAttr,
	(UBYTE *)Program_name,
	(struct Gadget *)NULL,
	(struct BitMap *)NULL
};

Static	struct	Window	*MyWin = (struct Window *)NULL;
Static	struct	NewWindow	MyNewWin =
{
	0, 7,
	640, 323,
	-1, -1,
	MENUVERIFY | MOUSEBUTTONS | GADGETDOWN | GADGETUP | MENUPICK | CLOSEWINDOW | RAWKEY,
	WINDOWCLOSE | SMART_REFRESH | REPORTMOUSE | NOCAREREFRESH | ACTIVATE,
	(struct Gadget *)NULL,
	(struct Image *)NULL,
	(UBYTE *)Program_name,
	(struct Screen *)NULL,
	(struct BitMap *)NULL,
	0, 0, 0, 0,
	CUSTOMSCREEN
};

#define MoveTo(x, y)    Move(rp, (long)(x), (long)(y))
#define TextTo(s, n)    Text(rp, (s), (long)(n))

Static send(str, x, y, len)
REG	char	*str;
REG	int	x;
REG	int	y;
REG	int	len;
{
	MoveTo(x, y);
	TextTo(str, len);
}

#define DSP_BIG 	1
#define DSP_MEDIUM	2
#define DSP_TINY	4

Static display_num(x, y, num, size, buf)
REG	int	x;
REG	int	y;
REG	ulong	num;
int	size;
char	*buf;
{
	static	char	Str[11];
	REG	int	hex;
	REG	int	dec;

	switch (size)
	{
	case DSP_BIG:
		hex = 8;
		dec = 10;
		break;
	case DSP_MEDIUM:
		hex = 4;
		dec = 6;
		break;
	case DSP_TINY:
		hex = 2;
		dec = 4;
		break;
	}

	if (!buf)
	{	if (Hex)
			sprintf(Str, " %0*lx ", hex, num);
		else
			if (Left)
				sprintf(Str, "%-*lu", dec, num);
			else
				sprintf(Str, "%*lu", dec, num);

		send(Str, x, y, dec);
	}
	else
	{	if (Hex)
			sprintf(buf, "%0*lx", hex, num);
		else
			sprintf(buf, "%lu", num);
	}
}

#define NXTIMES ((FXSize * 10) + 4)
#define NYTIMES FYSize
#define AXTIMES FXSize
#define AYTIMES FYSize
#define NXPLUS	7
#define NYPLUS	(FYSize * 4)
#define AXPLUS	((FXSize * 42) + 6)
#define AYPLUS	(FYSize * 4)

#define ACTIVE	1
#define BACKGR	0

Static display_pos(pos, active)
REG	int	pos;
REG	int	active;
{
	REG	int	xn;
	REG	int	xs;
	REG	int	y;
	REG	uchar	ptr[4];
	int	i;

	for (i = (ByteMove ? 0 : 3); i >= 0; i--)
		if (isprint(BufPtr[i+pos]))
			ptr[i] = BufPtr[i+pos];
		else
			ptr[i] = NullChar;

	xn = (pos / 4) % 4;
	xs = pos % 16;
	y = pos / 16;

	if (active == ACTIVE)
	{	SetAPen(rp, SelectPen);
		SetBPen(rp, OtherPen);
	}
	else
	{	SetAPen(rp, DataPen);
		SetBPen(rp, BackPen);
	}

	display_num(xn * NXTIMES + NXPLUS, y * NYTIMES + NYPLUS, Buffer[pos/4], DSP_BIG, NULL);

	send(ptr, xs * AXTIMES + AXPLUS, y * AYTIMES + AYPLUS, ByteMove ? 1 : 4);
}

#define PT_STR	0
#define PT_NUM	1
#define PT_BUF	2
#define PT_PTR	3
#define PT_NPT	4
#define PT_DEV	5
#define PT_LON	6
#define PT_BYT	7
#define PT_SAV	8
#define PT_END	9

#define DRAW_ONCE_ONLY		1
#define DRAW_EACH_BLOCK 	2
#define DRAW_EACH_CHECKSUM	4
#define DRAW_EACH_MOVE		8
#define DRAW_MARK_EDIT		16
#define DRAW_UNMARK_EDIT	32

#define BLKGAD_SAV	0L
#define DIRGAD_SAV	1L
#define MAX_SAV 	1L

Static	struct
{	int	x;
	int	y;
} saves[MAX_SAV+1];

Static draw_page(often)
REG	int	often;
{
	static struct ScrDat
	{	char	*str;
		int	often;
		int	reset;
		int	xt;
		int	xa;
		int	yt;
		int	ya;
		int	type;
		long	*pen;
	} Page[] =
	{
	"Offset",               1,      1,      1,      2,      2,      4,      PT_STR, &DataPen,
	"O",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	(char *)BLKGAD_SAV,     1,      0,      7,      0,      0,      0,      PT_SAV, NULL,

	"Long",                 1,      0,      16,     0,      0,      0,      PT_STR, &DataPen,
	(char *)&LongPos,       10,     0,      5,      0,      0,      0,      PT_LON, &SelectPen,

	"Byte",                 1,      0,      11,     0,      0,      0,      PT_STR, &DataPen,
	(char *)&BytePos,       10,     0,      5,      0,      0,      0,      PT_BYT, &SelectPen,

	"Root",                 1,      0,      14,     4,      0,      0,      PT_STR, &OtherPen,
	"R",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	(char *)&Stuff.Info.Root, 3,    0,      10,     -4,     0,      0,      PT_NUM, &DataPen,

	"Parent",               1,      0,      -10,    4,      1,      4,      PT_STR, &OtherPen,
	"P",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	(char *)POS_PAR,        3,      0,      10,     -4,     0,      0,      PT_BUF, &DataPen,
	"Key",                  1,      0,      -10,    4,      1,      0,      PT_STR, &OtherPen,
	"K",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	(char *)POS_KEY,        3,      0,      10,     -4,     0,      0,      PT_BUF, &DataPen,
	"HashChain",            1,      0,      -10,    4,      1,      0,      PT_STR, &OtherPen,
	"H",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	(char *)POS_HASH_CH,    3,      0,      10,     -4,     0,      0,      PT_BUF, &DataPen,
	"Extension",            1,      0,      -10,    4,      1,      0,      PT_STR, &OtherPen,
	"E",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	(char *)POS_EXT,        3,      0,      10,     -4,     0,      0,      PT_BUF, &DataPen,
	"FirstData",            1,      0,      -10,    4,      1,      0,      PT_STR, &OtherPen,
	"F",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	(char *)POS_FST_DAT,    3,      0,      10,     -4,     0,      0,      PT_BUF, &DataPen,
	"NextData",             1,      0,      -10,    4,      1,      0,      PT_STR, &OtherPen,
	"N",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	(char *)POS_D_NXT_DAT,  3,      0,      10,     -4,     0,      0,      PT_BUF, &DataPen,
	"CheckSum",             1,      0,      -10,    4,      1,      0,      PT_STR, &OtherPen,
	"C",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	(char *)POS_CHKSUM,     3,      0,      10,     -4,     0,      0,      PT_BUF, &DataPen,

	"+/= Advance One",      1,      0,      -10,    4,      1,      4,      PT_STR, &OtherPen,
	"+/=",                  1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	"+/",                   1,      0,      0,      0,      0,      0,      PT_STR, &OtherPen,
	"+",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	"-/_ Retreat One",      1,      0,      0,      0,      1,      0,      PT_STR, &OtherPen,
	"-/_",                  1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	"-/",                   1,      0,      0,      0,      0,      0,      PT_STR, &OtherPen,
	"-",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	"Goto Current",         1,      0,      0,      0,      1,      0,      PT_STR, &OtherPen,
	"G",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	"Zero Goto",            1,      0,      0,      0,      1,      0,      PT_STR, &OtherPen,
	"Z",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	"ALT-ReRead",           1,      0,      0,      0,      1,      4,      PT_STR, &OtherPen,
	"ALT-R",                1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	"ALT-Update",           1,      0,      0,      0,      1,      0,      PT_STR, &OtherPen,
	"ALT-U",                1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	"*",                    16,     0,      -1,     0,      1,      4,      PT_STR, &SelectPen,
	"*",                    32,     0,      0,      0,      0,      0,      PT_STR, &BackPen,
	"Ascii/Number Edit",    1,      0,      1,      0,      0,      0,      PT_STR, &OtherPen,
	"A",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	"Spread Current",       1,      0,      0,      0,      1,      0,      PT_STR, &OtherPen,
	"S",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	"Byte/Long Move",       1,      0,      0,      0,      1,      4,      PT_STR, &OtherPen,
	"B",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	"Mode Hex/Dec",         1,      0,      0,      0,      1,      0,      PT_STR, &OtherPen,
	"M",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	"Justify Left/Right",   1,      0,      0,      0,      1,      0,      PT_STR, &OtherPen,
	"J",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,

	"Driver",               1,      0,      0,      0,      1,      4,      PT_STR, &OtherPen,
	(char *)&Stuff.Info.dname, 1,   0,      7,      0,      0,      0,      PT_DEV, &DataPen,
	"Unit",                 1,      0,      -7,     0,      1,      0,      PT_STR, &OtherPen,
	(char *)&Stuff.Info.unit, 1,    0,      11,     0,      0,      0,      PT_NPT, &DataPen,
	"Device",               1,      0,      -11,    0,      1,      0,      PT_STR, &OtherPen,
	(char *)&Stuff.Info.name, 1,    0,      11,     0,      0,      0,      PT_PTR, &DataPen,
	"LowCyl",               1,      0,      -11,    0,      1,      0,      PT_STR, &OtherPen,
	(char *)&Stuff.Info.lowcyl, 1,  0,      11,     0,      0,      0,      PT_NPT, &DataPen,
	"HiCyl",                1,      0,      -11,    0,      1,      0,      PT_STR, &OtherPen,
	(char *)&Stuff.Info.hicyl, 1,   0,      11,     0,      0,      0,      PT_NPT, &DataPen,
	"Heads",                1,      0,      -11,    0,      1,      0,      PT_STR, &OtherPen,
	(char *)&Stuff.Info.heads, 1,   0,      11,     0,      0,      0,      PT_NPT, &DataPen,
	"BlksPerTrk",           1,      0,      -11,    0,      1,      0,      PT_STR, &OtherPen,
	(char *)&Stuff.Info.bpt, 1,     0,      11,     0,      0,      0,      PT_NPT, &DataPen,
	"Reserved",             1,      0,      -11,    0,      1,      0,      PT_STR, &OtherPen,
	(char *)&Stuff.Info.res, 1,     0,      11,     0,      0,      0,      PT_NPT, &DataPen,

	"Directory/Filename",   1,      0,      -11,    0,      1,      4,      PT_STR, &OtherPen,
	"D",                    1,      0,      0,      0,      0,      0,      PT_STR, &SelectPen,
	(char *)DIRGAD_SAV,     1,      0,      0,      0,      1,      4,      PT_SAV, NULL,

	NULL,			0,	0,	0,	0,	0,	0,	PT_END
	};
	REG	struct	ScrDat	*ScrPtr = Page;
	REG	int	x;
	REG	int	y;
	REG	long	pen = -1;
	char	NBuf[9];
	char	*ptr;
	char	*str;

	SetBPen(rp, BackPen);

	while (ScrPtr->type != PT_END)
	{	if (ScrPtr->reset)
			(x = 0), (y = 0);

		x += (ScrPtr->xt * FXSize + ScrPtr->xa);
		y += (ScrPtr->yt * FYSize + ScrPtr->ya);

		if (ScrPtr->often & often)
		{	if (pen != *(ScrPtr->pen))
			{	pen = *(ScrPtr->pen);
				SetAPen(rp, pen);
			}
			switch(ScrPtr->type)
			{
			case PT_STR:
				send(ScrPtr->str, x, y, strlen(ScrPtr->str));
				break;
			case PT_NUM:
				display_num(x, y, *(ulong *)(ScrPtr->str), DSP_BIG, NULL);
				break;
			case PT_BUF:
				display_num(x, y, Buffer[(long)(ScrPtr->str)], DSP_BIG, NULL);
				break;
			case PT_PTR:
				send(*(char **)(ScrPtr->str), x, y, strlen(*(char **)(ScrPtr->str)));
				break;
			case PT_NPT:
				sprintf(NBuf, "%ld", *(ulong *)(ScrPtr->str));
				send(NBuf, x, y, strlen(NBuf));
				break;
			case PT_DEV:
				str = *(char **)(ScrPtr->str);
				ptr = strrchr(str, '.');
				if (!ptr)
					ptr = strchr(str, '\0');

				send(str, x, y, ptr - str);
				break;
			case PT_BYT:
				display_num(x, y, (ulong)(*(int *)(ScrPtr->str)), DSP_MEDIUM, NULL);
				break;
			case PT_LON:
				display_num(x, y, (ulong)(*(int *)(ScrPtr->str)), DSP_TINY, NULL);
				break;
			case PT_SAV:
				saves[(long)(ScrPtr->str)].x = x;
				saves[(long)(ScrPtr->str)].y = y;
			}
		}
		ScrPtr++;
	}

	if (often & DRAW_EACH_BLOCK)
		BlkGadRefresh();
}

Static	int	NotBeenHere = !0;

Static display_block()
{
	REG	int	x;
	REG	int	y;
	REG	ulong	*Buf = Buffer;
	REG	uchar	*Ptr = BufPtr;
	char	Str[20];

	if (NotBeenHere)
	{	NotBeenHere = 0;
		draw_page(DRAW_ONCE_ONLY);      /* Fills in saves[] */

		add_gadgets();
	}

	draw_page(DRAW_EACH_BLOCK);

	SetAPen(rp, DataPen);
	SetBPen(rp, BackPen);

	for (y = 0; y < 32; y++)
	{	for (x = 0; x < 4; x++)
		{	display_num(x * NXTIMES + NXPLUS, y * NYTIMES + NYPLUS, *Buf, DSP_BIG, NULL);
			Buf++;
		}
		for (x = 0; x < 16; x++)
		{	send(isprint(*Ptr) ? Ptr : &((uchar)NullChar),
				x * AXTIMES + AXPLUS, y * AYTIMES + AYPLUS, 1);
			Ptr++;
		}
	}
	display_pos(BytePos, ACTIVE);
}

Static read(ignore)
REG	int	ignore;
{
	REG	ulong	l = TheBlock;
	REG	LONG	ret;

	if (l != CurBlock || ignore)
	{	if (l < 0 || l > (Stuff.Info.End - Stuff.Info.Begin))
		{	sprintf(err_buf,
				"Invalid block Min = 0 Max = %lu (0x%08lx)",
				Stuff.Info.End - Stuff.Info.Begin,
				Stuff.Info.End - Stuff.Info.Begin);
			WinMistake(MyWin, err_buf);
			EnableRestore();
			TheBlock = CurBlock;
		}
		else
		{	AkMotorOn(&Stuff);
			ret = AkDevRead(l + Stuff.Info.Begin, Buffer, &Stuff, BLOCK_SIZE);
			AkMotorOff(&Stuff);

			if (ret != 0)
			{	sprintf(err_buf, "Read Failed Status = 0x%lx (IoErr - 0x%lx)", ret, IoErr());
				WinMistake(MyWin, err_buf);
				EnableRestore();
				TheBlock = CurBlock;
				if (NotBeenHere)
					display_block();
			}
			else
			{	CurBlock = l;
				display_block();
			}
		}
	}
}

Static Update()
{
	REG	ulong	lb = TheBlock + Stuff.Info.Begin;
	REG	LONG	ret;

	AkMotorOn(&Stuff);
	ret = AkDevRead(lb, Buffer2, &Stuff, BLOCK_SIZE);

	if (ret != 0)
	{	sprintf(err_buf, "Read for Update Failed Status = 0x%lx (IoErr - 0x%lx)", ret, IoErr());
		WinMistake(MyWin, err_buf);
		EnableRestore();
	}
	else
	{	for (ret = 0; ret < LBlockSize; ret++)
			if (Buffer[ret] != Buffer2[ret])
			{	ret = AkDevWrite(lb, Buffer, &Stuff, BLOCK_SIZE);
				if (ret != 0)
				{	sprintf(err_buf,
						"Write Failed Status = 0x%lx (IoErr - 0x%lx)",
						ret, IoErr());
					WinMistake(MyWin, err_buf);
					EnableRestore();
				}
				else
				{	ret = AkDevUpdate(&Stuff);
					if (ret != 0)
					{	sprintf(err_buf,
							"Update Failed Status = 0x%lx (IoErr - 0x%lx)",
							ret, IoErr());
						WinMistake(MyWin, err_buf);
						EnableRestore();
					}
				}
				break;
			}
	}
	AkMotorOff(&Stuff);
}

Static Checksum()
{
	REG	ulong	*Chk = &Buffer[POS_CHKSUM];

	*Chk -= DosChecksum(Buffer);

	display_pos((int)(POS_CHKSUM)*4, BACKGR);

	display_pos(BytePos, ACTIVE);

	draw_page(DRAW_EACH_BLOCK);
}

Static BlkGadRefresh()
{
	RemoveGad(&AkGad, BlkGad, 0);
	BlkGad->Activation &= (~(STRINGRIGHT | STRINGCENTER));
	BlkGad->Activation |= STRINGJUST;
	display_num(0, 0, TheBlock, DSP_BIG, TheBlockBuf);
	ActivateGad(&AkGad, BlkGad);
}

Static DoBlk(Class, Gad)
ULONG	Class;
struct	Gadget	*Gad;
{
	int	ret;

	switch(Class)
	{
	case GADGETUP:
		if (Hex)
			ret = sscanf(TheBlockBuf, "%lx", &TheBlock);
		else
			ret = sscanf(TheBlockBuf, "%ld", &TheBlock);

		if (ret)
			read(0);
		else
		{	TheBlock = CurBlock;
			BlkGadRefresh();
		}
		break;
	case GADGETDOWN:
		break;
	default:	/* Ignore */
		break;
	}
}

Static DoDir(Class, Gad)
ULONG	Class;
struct	Gadget	*Gad;
{
	int	NewLongPos;

	switch(Class)
	{
	case GADGETUP:
		NewLongPos = DosHash(DirBuf);
		if (LongPos != NewLongPos)
		{	display_pos(BytePos, BACKGR);
			LongPos = NewLongPos;
			BytePos = LongPos * 4;
			display_pos(BytePos, ACTIVE);
			draw_page(DRAW_EACH_MOVE);
		}
		break;
	case GADGETDOWN:
		break;
	default:	/* Ignore */
		break;
	}
}

Static DoNum(Class, Gad)
ULONG	Class;
struct	Gadget	*Gad;
{
	int	ret;
	ulong	NewNum;

	switch(Class)
	{
	case GADGETUP:
		if (Hex)
			ret = sscanf(NumBuf, "%lx", &NewNum);
		else
			ret = sscanf(NumBuf, "%ld", &NewNum);

		RemoveGad(&AkGad, NumGad, 1);

		if (ret)
		{	Buffer[LongPos] = NewNum;
			display_pos(BytePos, BACKGR);
			if (++LongPos > 127)
				LongPos -= 128;
			BytePos = LongPos * 4;
		}

		display_pos(BytePos, ACTIVE);

		Edit = EDIT_NO;
		ActivateGad(&AkGad, BlkGad);
		ActivateGad(&AkGad, DirGad);
		draw_page(DRAW_EACH_MOVE|DRAW_EACH_BLOCK);

		break;
	case GADGETDOWN:
		break;
	default:	/* Ignore */
		break;
	}
}

#define BIT_DOREAD	1
#define BIT_TESTVAL	2
#define BIT_DOBYTE	4

Static no_edit(key)
ushort	key;
{
	REG	int	tests = 0;
	REG	int	NewLongPos;
	REG	int	NewBytePos;

	switch(key)
	{
	case RAW_R | RAWB_SHIFT:
	case RAW_R:
		TheBlock = Stuff.Info.Root;
		tests = BIT_DOREAD;
		break;
	case RAW_P | RAWB_SHIFT:
	case RAW_P:
		TheBlock = Buffer[POS_PAR];
		tests = BIT_DOREAD | BIT_TESTVAL;
		break;
	case RAW_K | RAWB_SHIFT:
	case RAW_K:
		TheBlock = Buffer[POS_KEY];
		tests = BIT_DOREAD | BIT_TESTVAL;
		break;
	case RAW_H | RAWB_SHIFT:
	case RAW_H:
		TheBlock = Buffer[POS_HASH_CH];
		tests = BIT_DOREAD | BIT_TESTVAL;
		break;
	case RAW_E | RAWB_SHIFT:
	case RAW_E:
		TheBlock = Buffer[POS_EXT];
		tests = BIT_DOREAD | BIT_TESTVAL;
		break;
	case RAW_F | RAWB_SHIFT:
	case RAW_F:
		TheBlock = Buffer[POS_FST_DAT];
		tests = BIT_DOREAD | BIT_TESTVAL;
		break;
	case RAW_N | RAWB_SHIFT:
	case RAW_N:
		TheBlock = Buffer[POS_D_NXT_DAT];
		tests = BIT_DOREAD | BIT_TESTVAL;
		break;
	case RAW_G | RAWB_SHIFT:
	case RAW_G:
		TheBlock = Buffer[LongPos];
		tests = BIT_DOREAD | BIT_TESTVAL;
		break;
	case RAW_UP:
		if (ByteMove)
			NewBytePos = BytePos - 16;
		else
			NewLongPos = LongPos - 4;
		tests = BIT_DOBYTE;
		break;
	case RAW_UP | RAWB_SHIFT:
		if (ByteMove)
			NewBytePos = BytePos % 16;
		else
			NewLongPos = LongPos % 4;
		tests = BIT_DOBYTE;
		break;
	case RAW_LEFT:
		if (ByteMove)
			NewBytePos = BytePos - 1;
		else
			NewLongPos = LongPos - 1;
		tests = BIT_DOBYTE;
		break;
	case RAW_LEFT | RAWB_SHIFT:
		if (ByteMove)
			NewBytePos = BytePos - (BytePos % 16);
		else
			NewLongPos = LongPos - (LongPos % 4);
		tests = BIT_DOBYTE;
		break;
	case RAW_LEFT | RAWB_LALT:
	case RAW_LEFT | RAWB_RALT:
		if (ByteMove)
		{	NewBytePos = BytePos - 1;
			if (NewBytePos < 0)
				NewBytePos = 511;
			while (NewBytePos != BytePos && !BufPtr[NewBytePos])
			{	NewBytePos--;
				if (NewBytePos < 0)
					NewBytePos = 511;
			}
		}
		else
		{	NewLongPos = LongPos - 1;
			if (NewLongPos < 0)
				NewLongPos = 127;
			while (NewLongPos != LongPos && !Buffer[NewLongPos])
			{	NewLongPos--;
				if (NewLongPos < 0)
					NewLongPos = 127;
			}
		}
		tests = BIT_DOBYTE;
		break;
	case RAW_DOWN:
		if (ByteMove)
			NewBytePos = BytePos + 16;
		else
			NewLongPos = LongPos + 4;
		tests = BIT_DOBYTE;
		break;
	case RAW_DOWN | RAWB_SHIFT:
		if (ByteMove)
			NewBytePos = (512-16) + (BytePos % 16);
		else
			NewLongPos = (128-4) + (LongPos % 4);
		tests = BIT_DOBYTE;
		break;
	case RAW_RIGHT:
		if (ByteMove)
			NewBytePos = BytePos + 1;
		else
			NewLongPos = LongPos + 1;
		tests = BIT_DOBYTE;
		break;
	case RAW_RIGHT | RAWB_SHIFT:
		if (ByteMove)
			NewBytePos = BytePos - (BytePos % 16) + 15;
		else
			NewLongPos = LongPos - (LongPos % 4) + 3;
		tests = BIT_DOBYTE;
		break;
	case RAW_RIGHT | RAWB_LALT:
	case RAW_RIGHT | RAWB_RALT:
		if (ByteMove)
		{	NewBytePos = BytePos + 1;
			if (NewBytePos > 511)
				NewBytePos = 0;
			while (NewBytePos != BytePos && !BufPtr[NewBytePos])
			{	NewBytePos++;
				if (NewBytePos > 511)
					NewBytePos = 0;
			}
		}
		else
		{	NewLongPos = LongPos + 1;
			if (NewLongPos > 127)
				NewLongPos = 0;
			while (NewLongPos != LongPos && !Buffer[NewLongPos])
			{	NewLongPos++;
				if (NewLongPos > 127)
					NewLongPos = 0;
			}
		}
		tests = BIT_DOBYTE;
		break;
	case RAW_EQ | RAWB_SHIFT:
	case RAW_EQ:
		TheBlock++;
		tests = BIT_DOREAD;
		break;
	case RAW_MINUS | RAWB_SHIFT:
	case RAW_MINUS:
		TheBlock--;
		tests = BIT_DOREAD;
		break;
	case RAW_J | RAWB_SHIFT:
	case RAW_J:
		Left = !Left;
		if (!Hex)
			display_block();
		break;
	case RAW_M | RAWB_SHIFT:
	case RAW_M:
		Hex = !Hex;
		display_block();
		break;
	case RAW_U | RAWB_LALT:
	case RAW_U | RAWB_RALT:
		Update();
		break;
	case RAW_C | RAWB_SHIFT:
	case RAW_C:
		Checksum();
		break;
	case RAW_O | RAWB_SHIFT:
	case RAW_O:
		ActivateGadget(BlkGad, MyWin, NULL);
		break;
	case RAW_D | RAWB_SHIFT:
	case RAW_D:
		ActivateGadget(DirGad, MyWin, NULL);
		break;
	case RAW_S | RAWB_SHIFT:
	case RAW_S:
		if (ByteMove)
			for (NewBytePos = 0; NewBytePos < 512; NewBytePos++)
				BufPtr[NewBytePos] = BufPtr[BytePos];
		else
			for (NewLongPos = 0; NewLongPos < 128; NewLongPos++)
				Buffer[NewLongPos] = Buffer[LongPos];
		display_block();
		break;
	case RAW_B | RAWB_SHIFT:
	case RAW_B:
		display_pos(BytePos, BACKGR);
		if (ByteMove)
			BytePos = LongPos * 4;
		ByteMove = !ByteMove;
		display_pos(BytePos, ACTIVE);
		break;
	case RAW_R | RAWB_LALT:
	case RAW_R | RAWB_RALT:
		read(!0);
		break;
	case RAW_Z | RAWB_SHIFT:
	case RAW_Z:
		TheBlock = 0;
		tests = BIT_DOREAD;
		break;
	case RAW_A | RAWB_SHIFT:
	case RAW_A:
		Edit = EDIT_YES;
		RemoveGad(&AkGad, BlkGad, 0);
		RemoveGad(&AkGad, DirGad, 0);
		if (ByteMove)
			draw_page(DRAW_MARK_EDIT);
		else
		{	display_pos(BytePos, BACKGR);
			NumGad->LeftEdge = (LongPos % 4) * NXTIMES + NXPLUS;
			NumGad->TopEdge = (LongPos / 4) * NYTIMES + NYPLUS - FYBase;
			NumGad->Activation &= (~(STRINGRIGHT | STRINGCENTER));
			NumGad->Activation |= STRINGJUST;
			display_num(0, 0, Buffer[LongPos], DSP_BIG, NumBuf);
			ActivateGad(&AkGad, NumGad);
			ActivateGadget(NumGad, MyWin, NULL);
		}
		break;
	default:
		break;
	}

	if (tests & BIT_DOBYTE)
	{	if (ByteMove)
		{	if (NewBytePos < 0)
				NewBytePos += 512;
			if (NewBytePos > 511)
				NewBytePos -= 512;
			if (BytePos != NewBytePos)
			{	display_pos(BytePos, BACKGR);
				BytePos = NewBytePos;
				LongPos = BytePos / 4;
				display_pos(BytePos, ACTIVE);
				draw_page(DRAW_EACH_MOVE);
			}
		}
		else
		{	if (NewLongPos < 0)
				NewLongPos += 128;
			if (NewLongPos > 127)
				NewLongPos -= 128;
			if (LongPos != NewLongPos)
			{	display_pos(BytePos, BACKGR);
				LongPos = NewLongPos;
				BytePos = LongPos * 4;
				display_pos(BytePos, ACTIVE);
				draw_page(DRAW_EACH_MOVE);
			}
		}
	}

	if (tests & BIT_TESTVAL)
		if (TheBlock == 0)
			TheBlock = CurBlock;

	if (tests & BIT_DOREAD)
		read(0);
}

Static edit(key)
ushort	key;
{
	REG	int	NewBytePos;
	REG	char	ch;
	REG	int	modify = FALSE;

	NewBytePos = BytePos;
	switch (key)
	{
	case RAW_RIGHT:
		NewBytePos++;
		break;
	case RAW_LEFT:
		NewBytePos--;
		break;
	case RAW_DOWN:
		NewBytePos += 16;
		break;
	case RAW_UP:
		NewBytePos -= 16;
		break;
	default:
		ch = AKCOOK(key);

		switch(ch)
		{
		case '\033':
			Edit = EDIT_NO;
			ActivateGad(&AkGad, BlkGad);
			ActivateGad(&AkGad, DirGad);
			draw_page(DRAW_UNMARK_EDIT);
			break;
		case '\0':
			break;
		default:
			BufPtr[BytePos] = ch;
			NewBytePos++;
			modify = TRUE;
			break;
		}
		break;
	}
	if (NewBytePos < 0)
		NewBytePos += 512;
	if (NewBytePos > 511)
		NewBytePos -= 512;
	if (BytePos != NewBytePos)
	{	display_pos(BytePos, BACKGR);
		BytePos = NewBytePos;
		LongPos = BytePos / 4;
		display_pos(BytePos, ACTIVE);

		if (modify)
			draw_page(DRAW_EACH_MOVE|DRAW_EACH_BLOCK);
		else
			draw_page(DRAW_EACH_MOVE);
	}
}

Static gotkey(key)
ushort	key;
{
	switch(Edit)
	{
	case EDIT_NO:
		no_edit(key);
		break;
	case EDIT_YES:
		if (ByteMove)
			edit(key);
		break;
	}
}

#define KEYS_WANTED	(RAWB_CHARS | RAWB_SHIFT | RAWB_CTRL | RAWB_LALT | RAWB_RALT)

Static BlockEd()
{
	REG	struct	IntuiMessage	*Msg;
	REG	ULONG	WaitMask;

	TheBlock = Stuff.Info.Root;

	read(!0);

	WaitMask = INTUITION_MESSAGE;

	while (1)
	{	Wait(WaitMask);

		while ((Msg = GetMsg(MyWin->UserPort)))
		{	switch(Msg->Class)
			{
			case RAWKEY:
				AkCookKey(Msg->Code, gotkey, KEYS_WANTED);
				ReplyMsg(Msg);
				break;
			case CLOSEWINDOW:
				cleanup();
				break;
			case MENUPICK:
				MnuMsg(&AkMenu, Msg);
				break;
			case GADGETUP:
			case GADGETDOWN:
				GadMsg(&AkGad, Msg);
				break;
			default:
				ReplyMsg(Msg);
				break;
			}
		}
	}
}

Static EnableRestore()
{
	(RestoreItem->Flags) |= ITEMENABLED;
}

Static Restore(Menu, MenuItem, Item)
struct	Menu	*Menu;
struct	MenuItem	*MenuItem;
USHORT	Item;
{
	(RestoreItem->Flags) &= (~ITEMENABLED);
	WinMistake(MyWin, NULL);
}

Static add_gadgets()
{
	GadInit(&AkGad, MyWin);

	BlkGad = AddStrGad(&AkGad, 0L, saves[BLKGAD_SAV].x, saves[BLKGAD_SAV].y-FYBase, FXSize*10, FYSize, STRINGJUST, TheBlockBuf, sizeof(TheBlockBuf), DoBlk);
	DirGad = AddStrGad(&AkGad, 0L, saves[DIRGAD_SAV].x, saves[DIRGAD_SAV].y-FYBase, FXSize*18, FYSize, 0L, DirBuf, sizeof(DirBuf), DoDir);

	ActivateGads(&AkGad);

	NumGad = AddStrGad(&AkGad, 0L, 0, 0, FXSize*10, FYBase, 0L, NumBuf, sizeof(NumBuf), DoNum);
}

main(argc, argv)
int	argc;
char	*argv[];
{
	cli = argc;

	DECRYPT();

	DO_QUAL();

	Device = argv[1];

	if (!Quiet && cli)
		STARTUP_MSG;

	OPEN_LIB(IBASE, "intuition.library", INTUITION_REV);
	OPEN_LIB(GfxBase, "graphics.library", GRAPHICS_REV);
	OPEN_LIB(DiskfontBase, "diskfont.library", 0L);

	OpalAttr.ta_Name = (STRPTR)FontName;
	OpalAttr.ta_YSize = FontSize;

	Opal = OpenDiskFont(&OpalAttr);
	if (!Opal)
		error("Can't open font", ERR_NOVAL);

	FXSize = Opal->tf_XSize;
	FYSize = Opal->tf_YSize + 1;
	FYBase = Opal->tf_Baseline;

	PoolInit(&GenPoolKey, 0L, MEMF_CHIP|MEMF_CLEAR);
	PoolAlloc(Buffer, (long)BLOCK_SIZE, &GenPoolKey);
	BufPtr = (uchar *)Buffer;
	PoolAlloc(Buffer2, (long)BLOCK_SIZE, &GenPoolKey);

	if (Dev = AkDevOpen(Device, &Stuff))
		error(Device, Dev);

	if (Inhibit)
		Inhibited = AkDevInhibit(&Stuff, -1L);

	if (!(MyScreen = OpenScreen(&MyNewScreen)))
		error(MyNewScreen.DefaultTitle, ERR_NOSCR);

	SetRGB4(&(MyScreen->ViewPort), 0L, (long)((ColTbl0 & 0xf00) >> 8),
					   (long)((ColTbl0 & 0x0f0) >> 4),
					   (long)((ColTbl0 & 0x00f) >> 0));
	SetRGB4(&(MyScreen->ViewPort), 1L, (long)((ColTbl1 & 0xf00) >> 8),
					   (long)((ColTbl1 & 0x0f0) >> 4),
					   (long)((ColTbl1 & 0x00f) >> 0));
	SetRGB4(&(MyScreen->ViewPort), 2L, (long)((ColTbl2 & 0xf00) >> 8),
					   (long)((ColTbl2 & 0x0f0) >> 4),
					   (long)((ColTbl2 & 0x00f) >> 0));
	SetRGB4(&(MyScreen->ViewPort), 3L, (long)((ColTbl3 & 0xf00) >> 8),
					   (long)((ColTbl3 & 0x0f0) >> 4),
					   (long)((ColTbl3 & 0x00f) >> 0));

	ScreenToFront(MyScreen);

	MyNewWin.Screen = MyScreen;

	if (!(MyWin = OpenWindow(&MyNewWin)))
		error(MyNewWin.Title, ERR_NOWIN);

	rp = MyWin->RPort;

	WinMistake(MyWin, NULL);

	MenuInit(&AkMenu, MyWin, (struct TextAttr *)NULL);

	ProjectMenu = AddMenu(&AkMenu," Project ", MENUFLAGS, NULL, 0);
	AddItem(&AkMenu, ProjectMenu, " Quit ", MOREITEMFLAGS, NULL, 0, 'Q', cleanup);
	RestoreItem = AddItem(&AkMenu, ProjectMenu, " Restore ", ITEMFLAGS, NULL, 0, 'R', Restore);

	AlignMenus(&AkMenu, NULL);

	BlockEd();

	cleanup();
}

error(why, num)
char	*why;
long	num;
{
	ERROR_INIT

	FreeGads(&AkGad);

	ClearMenu(&AkMenu);

	if (MyWin)
		CloseWindow(MyWin);

	if (MyScreen)
		CloseScreen(MyScreen);

	if (Inhibited)
		AkDevInhibit(&Stuff, 0L);

	if (!Dev)
		AkDevClose(&Stuff);

	if (Opal)
		CloseFont(Opal);

	ERROR_TEST
		CASE_ERR_NOGADDEV
		CASE_ERR_AKDEVALL
		CASE_ERR_NOVAL
		CASE_ERR_NOMEM
		CASE_ERR_NOWIN
		CASE_ERR_NOSCR
		CASE_ERR_NOLIB
	ERROR_END(FACILITY_BLOCKED)

	PoolFree(&GenPoolKey);

	if (DiskfontBase)
		CloseLibrary(DiskfontBase);

	if (GfxBase)
		CloseLibrary(GfxBase);

	if (IBASE)
		CloseLibrary(IBASE);

	CLEANUP_QUAL();

	ERROR_EXIT
}
