/*****************************************************************************\
 * $VER: TetrisQ_levelscore.c 1.4                                            *
 *                               DICE/LATTICE C/SAS C/AZTEC C + AmigaOS 2.04 *
 *                 _                                                         *
 * 5.10.92    _   // (c)1992 by "Quarky" Dieter Temme                        *
 *            \\ //                                                          *
 * :ts=4       \X/ --- Freeware --- ONLY AMIGA MAKES IT POSSIBLE             *
 *                                                                           *
\*****************************************************************************/

#include "amigacompq.h"
#include "definitions.h"

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <clib/asl_protos.h>
#include <clib/commodities_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/icon_protos.h>
#include <clib/intuition_protos.h>
#include <clib/keymap_protos.h>
#include <clib/utility_protos.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <libraries/gadtools.h>
#include <workbench/workbench.h>

#ifdef PRAGMAS_
 #include <pragmas/asl_lib.h>
 #include <pragmas/commodities_lib.h>
 #include <pragmas/dos_lib.h>
 #include <pragmas/exec_lib.h>
 #include <pragmas/gadtools_lib.h>
 #include <pragmas/graphics_lib.h>
 #include <pragmas/icon_lib.h>
 #include <pragmas/intuition_lib.h>
 #include <pragmas/keymap_lib.h>
 #include <pragmas/utility_lib.h>
#endif

/*>> various definitions <<*/
#define PROGID_ '\xffTET'	/* id for tetris highscore files */
#define PLACES_		10		/* number of places in highscore table */
#define NSIZE_		12		/* length of names in highscore table */
#define NMAX_		4		/* number of names to guess */
#define TXTHGT_		7		/* baseline! */
#define HSWIDTH_	(20*8)	/* highscore table width in pixels */
#define HSHEIGHT_	(2*9+1)	/* highscore table height in pixels */
#define HSCLFT_	(win_WDT+INTERWIDTH)
#define HSCTOP_	((win_HGT-PLACES_*HSHEIGHT_-2*INTERHEIGHT-2*BBWIDTH)>>1)
#define TABLFT_	(HSCLFT_+INTERWIDTH+2*BBWIDTH)
#define TABTOP_	(HSCTOP_+BBHEIGHT+INTERHEIGHT)

/*>> local variables <<*/
ULONG id;					/* id for tetris highscore files */
struct champ				/* highscore table with actual players */
{	ULONG score;
	UBYTE level;
	UBYTE pad;
	TEXT name[NSIZE_+1];
	TEXT date[8];
} champions[PLACES_+2], *ch[2];
TEXT date[8];				/* actual date as a text string */
UBYTE place[2]=				/* actual place-1 in Highscore */
{	PLACES_, PLACES_+1
};
UBYTE lines[2];				/* number of full lines */
TEXT names[2][NMAX_][NSIZE_+1]; /* memory for former input names */
UBYTE startlevel;			/* level to start game with (for both players) */
BOOL tableopen;				/* TRUE if highscore table is visible */
TEXT *hsdir, *hsname;		/* path and name of highscores file */
BPTR hslock;				/* lock to path of highscores file */
BOOL hsread;				/* TRUE if highscores file read already */
BOOL autoact;				/* TRUE if auto-activation of windows enabled */
struct FileRequester *freq;	/* file requester structure */
struct Gadget *lcongad;		/* GadTools' anchor to level window gadgets */
struct Window *lwin;		/* pointer to level window structure */
struct Gadget *hcongad;		/* GadTools' anchor to input window gadgets */
struct Window *hwin;		/* pointer to input window structure */


/*==== set to defaults ====*/
void LS_ResetLevel_(void)
{	startlevel= 5;
}

/*==== change number into a string ====*/
void ultoa_(const TEXT fill, TEXT *s, BYTE len, ULONG num)
{	do
	{	s[--len]= '0'+num%10;
		num/= 10;
	} while (num && len);

	while (len > 0) s[--len]= fill;
}

/*==== print number with Move(), Text() ====*/
void OutNumber_(const UBYTE pen, const WORD x, const WORD y,
	ULONG num, BYTE len)
{	extern struct RastPort *rport;					/* from "playerctrl.c" */
	extern UWORD winoffs[2], wbleft, wbtop, winwidth; /* from "playerctrl.c" */
	TEXT str[6];

	ultoa_(' ', str, len, num);

	SetAPen(rport, pen);
	if (x >= 0) Move(rport, x+wbleft, y+wbtop);
	Text(rport, str, len);
}

/*==== print place into highscore table (result is Y coordinate) ====*/
#define ls_OUTNAM_	0x01
#define ls_OUTLEV_	0x02
#define ls_OUTSCO_	0x04
#define ls_OUTALL_	0x0F
#define ls_GONEUP_	0x1F
#define ls_ABORT_	0x2F
#define ls_RESET_	0x80

UWORD OutEntry_(const UBYTE spec, ...)
{	extern struct RastPort *rport; /* from "playerctrl.c" */
	extern UWORD winoffs[2], wbleft, wbtop, winwidth; /* from "playerctrl.c" */
	extern BYTE players, pl; /* from "playerctrl.c" */
	extern struct Window *win; /* from "playerctrl.c" */

	UWORD x= wbleft+TABLFT_, tx= x+32, rx= x+HSWIDTH_+1;
	UWORD y, ty;
	UBYTE num, dest;
	BOOL p;
	va_list ap;

	#define ls_OUTNEW_	0x08

	/*- calculate place number to output -*/
	switch (spec)
	{	case ls_GONEUP_:
		case ls_OUTALL_:
			va_start(ap, spec);
			dest= va_arg(ap, ULONG);
			va_end(ap);
			if (spec != ls_GONEUP_) break;
			num= dest;
		default:
			dest= place[pl];
	}

	y= wbtop+TABTOP_+dest*HSHEIGHT_;

	if (spec&ls_RESET_) place[pl]= PLACES_+pl;

	if (!win) return;

	if (tableopen)
	{	/* eventually scroll highscore list */
		switch (spec)
		{	case ls_ABORT_:
				num= PLACES_-1-dest;
				dest= PLACES_-1;
				goto scroll;
			case ls_GONEUP_:
				if (num >= PLACES_) num= PLACES_-1;
				num-= place[pl];
				dest= place[pl];
scroll:			if (dest < PLACES_)
					ScrollRaster(rport, 0, (spec == ls_ABORT_)? HSHEIGHT_ :
						-HSHEIGHT_, tx-2, y, rx, y+(num+1)*HSHEIGHT_-1);
				y= wbtop+TABTOP_+dest*HSHEIGHT_;
		}

		p= (dest == place[0]) || (dest == place[1]);
		ty= y+TXTHGT_;

		/* put entry into highscore list in window */
		if (dest < PLACES_)
		{	if ((spec&ls_OUTNEW_) || !champions[dest].level)
			{	SetAPen(rport, p? 3 : 0);
				RectFill(rport, tx-2, y, rx, y+HSHEIGHT_-1);
			}

			if (p) SetBPen(rport, 3);

			if (champions[dest].level)
			{	if (spec&ls_OUTNAM_)
				{	SetAPen(rport, 1);
					Move(rport, tx, ty);
					Text(rport, champions[dest].name, NSIZE_);
					SetAPen(rport, p? 0 : 3);
					Move(rport, tx, ty+9);
					Text(rport, champions[dest].date, 8);
				}
				if (spec&ls_OUTLEV_)
				{	Move(rport, rx-8, ty);
					OutNumber_(1, -1, -1, champions[dest].level, 1);
				}
				if (spec&ls_OUTSCO_)
				{	Move(rport, rx-6*8, ty+9);
					OutNumber_(2, -1, -1, champions[dest].score, 6);
				}
			}
		}
	}
	SetBPen(rport, 0);
	return ty-wbtop;
}

/*==== set highscores file's path and name ====*/
void LS_SetTable_(TEXT *path, TEXT *name)
{	extern BPTR cdlock; /* from "main.c" */

	TEXT c, *f, *s;
	UWORD l;
	BPTR templock;

	/*- rearrange path and name, reserve memory, get lock -*/
	s= MAllocQ_(l= strlen(path)+strlen(name)+3);
	strcpy(s, path);
	AddPart(s, name, l);
	f= FilePart(s);
	name= MAllocQ_(strlen(f)+1);
	strcpy(name, f);
	f= PathPart(s);
	c= *f; *f= '\0';
	templock= Lock(s, ACCESS_READ);
	if (SameLock(templock, cdlock) != LOCK_SAME)
	{	UnLock(templock);
		path= MAllocQ_(strlen(s)+1);
		templock= Lock(s, ACCESS_READ);
	} else
		path= NULL;

	/*- clear read flag and set lock -*/
	hsread= FALSE;
	if (hslock) UnLock(hslock);
	hslock= templock;

	/*- set highscores file's path and name -*/
	if (*hsdir) free(hsdir);
	hsdir= path? strcpy(path, s) : "";
	*f= c;
	if (hsname) free(hsname);
	hsname= name;
	free(s);
}

/*==== choose and load new highscore table ====*/
/* ATTENTION: if called from LoadSettings_(): CurrentDir() != cdlock */
void LS_LoadTable_(const UBYTE choose)
{	extern struct Window *win; /* from "playerctrl.c" */
	extern struct RastPort *rport; /* from "playerctrl.c" */
	extern BPTR cdlock; /* from "main.c" */

	BPTR hsfile= (BPTR)0;

	switch (choose)
	{	case LT_CHOOSE_: /* request filename thru file requester */
		{	BOOL error= TRUE;

			if (AslRequestTags((APTR)freq,
				ASLFR_TitleText, (ULONG)"Open highscores",
				ASLFR_InitialDrawer, (ULONG)hsdir,
				ASLFR_InitialFile, (ULONG)hsname,
				ASLFR_Flags1, FRF_DOPATTERNS,
				TAG_DONE))
			{	BPTR dirlock, lock;

				/* if not a TetrisQ highscores file: error */
				if (dirlock= Lock(freq->fr_Drawer, ACCESS_READ))
				{	lock= CurrentDir(dirlock);
					hsfile= Open(freq->fr_File, MODE_OLDFILE);
					UnLock(CurrentDir(lock));
					if (hsfile) /* if file is ok, set filename */
					{	if ((Read(hsfile, &id, sizeof(id)) == sizeof(id))
							&& (id == PROGID_))
						{	LS_SetTable_(freq->fr_Drawer, freq->fr_File);
							error= FALSE;
						} else
							Close(hsfile);
					}
				}
			}

			if (error) return;

			/* save new icon if necessary */
			{	BPTR lock= CurrentDir(hslock);
				struct DiskObject *obj= GetDiskObject(hsname);
				CurrentDir(lock);
				if (!obj)	LS_SaveSettings_();
				else		FreeDiskObject(obj);
			}
		}

		case LT_LOAD_: /* load highscore file */
			if (hsread) return;
			PC_SetWindowTitle_();
			if (!hsfile)
			{	BPTR lock= CurrentDir(hslock);
				hsfile= Open(hsname, MODE_OLDFILE);
				CurrentDir(lock);
				if (!hsfile || (Read(hsfile, &id, sizeof(id)) < sizeof(id))
					|| (id != PROGID_)) goto closeit;
			}
			Read(hsfile, &champions[0], sizeof(struct champ)*PLACES_);
			{	ULONG *ptr;
				for (ptr= (ULONG *)&champions[0];
					ptr < (ULONG *)&champions[PLACES_]; ptr++)
					*ptr= (*ptr)^0x12345678;
			}
closeit:	if (!hsfile) break;
			Close(hsfile);
			hsread= TRUE;

		case LT_SHOW_: /* show highscore table in window */
			if (tableopen && win)
			{	UBYTE i;

				for (i= 0; i < PLACES_; i++)
				{	OutNumber_(3, (i >= 9)? TABLFT_ : TABLFT_+4,
						OutEntry_(ls_OUTALL_, (ULONG)i),
						i+1, (i >= 9)? 2 : 1);
					Text(rport, ".", 1);
				}
			}
	}
}

/*==== handle icon dropped on window ====*/
void LS_AppLoadTable_(void)
{	extern struct MsgPort *appport; /* from "playerctrl.c" */
	extern struct Window *win; /* from "playerctrl.c" */

	struct AppMessage *amsg;
	struct WBArg *argptr;

	while (amsg= (struct AppMessage *)GetMsg(appport))
	{	if (tableopen && (amsg->am_MouseX >= HSCLFT_)
			&& (amsg->am_MouseX < HSCLFT_+2*INTERWIDTH+4*BBWIDTH+HSWIDTH_))
		{	UWORD l= 0;
			TEXT *str;

			argptr= &amsg->am_ArgList[amsg->am_NumArgs-1];
			do
			{	if (l) free(str);
				l+= 256;
				str= (TEXT *)MAllocQ_(l);
			} while (!NameFromLock(argptr->wa_Lock, str, l));

			LS_SetTable_(str, argptr->wa_Name);
			free(str);
			LS_LoadTable_(LT_LOAD_);
		} else
			DisplayBeep(win->WScreen);

		ReplyMsg((struct Message *)amsg);
	}
}

/*==== save highscore table ====*/
void LS_SaveTable_(const BOOL quiet)
{	extern struct Window *win; /* from "playerctrl.c" */
	extern BPTR cdlock; /* from "main.c" */

	BPTR hsfile= (BPTR)0;

	if (!win) return;

	/*- request filename thru file requester -*/
	if (!quiet)
	{	BOOL error= TRUE;

		if (AslRequestTags((APTR)freq,
			ASLFR_TitleText, (ULONG)"Save highscores as",
			ASLFR_InitialDrawer, (ULONG)hsdir,
			ASLFR_InitialFile, (ULONG)hsname,
			ASLFR_Flags1, FRF_DOSAVEMODE|FRF_DOPATTERNS,
			TAG_DONE))
		{	CurrentDir(hslock);
			hsfile= Open(freq->fr_File, MODE_OLDFILE);
			if (!hsfile) hsfile= Open(freq->fr_File, MODE_NEWFILE);
			CurrentDir(cdlock);
			if (hsfile) /* file is ok, set filename */
			{	LS_SetTable_(freq->fr_Drawer, freq->fr_File);
				LS_SaveSettings_();
				error= FALSE;
			} else
				Close(hsfile);
		}

		if (error) return;
	}

	/*- save highscore file -*/
	if (!hsfile)
	{	CurrentDir(hslock);
		if (!(hsfile= Open(hsname, MODE_OLDFILE)))
			hsfile= Open(hsname, MODE_NEWFILE);
		CurrentDir(cdlock);
	}

	if (hsfile)
	{	ULONG *ptr;

		id= PROGID_;
		Write(hsfile, &id, sizeof(id));
		for (ptr= (ULONG *)&champions[0]; /* encode list */
			ptr < (ULONG *)(&champions[PLACES_]); ptr++)
			*ptr= (*ptr)^0x12345678;
		Write(hsfile, &champions[0], sizeof(struct champ)*PLACES_);
		while (ptr-- > (ULONG *)&champions[0]) /* decode list */
			*ptr= (*ptr)^0x12345678;
		Close(hsfile);
	}
}

/*==== get actual date, allocate file requester structure ====*/
void LS_InitTable_(void)
{	extern struct Window *win; /* from "playerctrl.c" */
	extern struct Library *AslBase; /* from "main.c" */

	ULONG secs, micros;
	struct ClockData cdata;

	/*- get actual date -*/
	CurrentTime(&secs, &micros);
	Amiga2Date(secs, &cdata);
	ultoa_('0', date, 2, cdata.year%100);
	ultoa_('0', &date[3], 2, cdata.month);
	ultoa_('0', &date[6], 2, cdata.mday);
	date[2]= date[5]= '-';

	/*- allocate file requester structure -*/
	freq= AllocAslRequestTags(ASL_FileRequest,
		ASLFR_Window, (ULONG)win,
		ASLFR_InitialPattern, (ULONG) ((AslBase->lib_Version < 38)?
			"TQ~(#?.info)" : "TQ#?"),
		ASLFR_RejectIcons, TRUE,
		TAG_DONE);
}

/*==== free file requester structure ====*/
void LS_CloseTable_(void)
{	UnLock(hslock);
	FreeAslRequest(freq);
}

/*==== clear highscore table ====*/
void LS_ClearTable_(void)
{	UBYTE i;
	for (i= 0; i < PLACES_; i++)
	{	champions[i].score= 0;
		champions[i].level= 0;
	}
	LS_LoadTable_(LT_SHOW_);
}

/*==== let user enter a new startlevel ====*/
void LS_NewLevel_(void)
{	extern struct TextFont *font; /* from "playerctrl.c" */
	extern void *vi; /* from "playerctrl.c" */
	extern struct Window *win; /* from "playerctrl.c" */
	extern ULONG winsig; /* from "playerctrl.c" */
	extern struct TextAttr textattr; /* from "playerctrl.c" */
	extern UWORD winoffs[2], wbleft, wbtop, winwidth; /* from "playerctrl.c" */

	BOOL end= FALSE;
	BYTE i, j, templevel;
	UWORD x, y;
	struct Gadget *lgad, *lgads[9];
	struct IntuiMessage *limsg= NULL;

	#define ESC		0x1B

	#define lwin_WDT	(4*INTERWIDTH+3*30)
	#define lwin_HGT	(5*INTERHEIGHT+4*14)

	static struct NewWindow levelwindow=
	{	0, 0, 0, 0, 0, 1,
		BUTTONIDCMP|IDCMP_CLOSEWINDOW|IDCMP_VANILLAKEY,
		WFLG_DRAGBAR|WFLG_CLOSEGADGET|WFLG_DEPTHGADGET|WFLG_ACTIVATE
			|WFLG_SMART_REFRESH|WFLG_GIMMEZEROZERO,
		NULL, NULL, "Startlevel", NULL, NULL, 0, 0, 0, 0, PUBLICSCREEN
	};
	static struct NewGadget levelgadget=
	{	0, 0, 30, 14, NULL, &textattr, 1, PLACETEXT_IN, NULL, NULL
	};
	static struct NewGadget okgadget=
	{	INTERWIDTH, 4*INTERHEIGHT+3*14,
		30, 14, "OK", &textattr, 10, PLACETEXT_IN, NULL, NULL
	};
	static struct NewGadget cancelgadget=
	{	2*INTERWIDTH+30, 4*INTERHEIGHT+3*14,
		2*30+INTERWIDTH, 14, "Cancel", &textattr, 11, PLACETEXT_IN, NULL, NULL
	};

	static TEXT levelnums[9][2]=
	{	"1", "2", "3", "4", "5", "6", "7", "8", "9"
	};

	if (!win) return;

	PC_CtrlWindow_(CW_SLEEP_);

	/*- create level number gadgets -*/
	levelwindow.LeftEdge= win->LeftEdge+((winwidth-lwin_WDT)>>1);
	levelwindow.TopEdge= win->TopEdge+((win_HGT-lwin_HGT)>>1);

	levelgadget.ng_VisualInfo= okgadget.ng_VisualInfo=
		cancelgadget.ng_VisualInfo= vi;
retry_g:
	lgad= CreateContext(&lcongad); /* create gadgets */
	lgad= CreateGadget(BUTTON_KIND, lgad, &okgadget,
		TAG_DONE);
	lgad= CreateGadget(BUTTON_KIND, lgad, &cancelgadget,
		TAG_DONE);
	levelgadget.ng_TopEdge= INTERHEIGHT;
	for (i= 6; i >= 0; i-= 3)
	{	levelgadget.ng_LeftEdge= INTERWIDTH;
		for (j= 1; j <= 3; j++)
		{	levelgadget.ng_GadgetID= i+j;
			levelgadget.ng_GadgetText= levelnums[i+j-1];
			lgads[i+j-1]= lgad= CreateGadget(BUTTON_KIND, lgad, &levelgadget,
				TAG_DONE);
			levelgadget.ng_LeftEdge+= 30+INTERWIDTH;
		}
		levelgadget.ng_TopEdge+= 14+INTERHEIGHT;
	}

	/*- open startlevel window -*/
	if (!lgad || !(lwin= OpenWindowTags(&levelwindow, /* open window */
		WA_ScreenTitle, PRGNAME " - (c)1992 by Dieter Temme",
		WA_InnerWidth, lwin_WDT,
		WA_InnerHeight, lwin_HGT,
		TAG_DONE)))
		PrintNoMem_();

	/*- add gadget list to window -*/
	AddGList(lwin, lcongad, -1, -1, NULL);
	RefreshGList(lcongad, lwin, NULL, -1);
	GT_RefreshWindow(lwin, NULL);

	SetFont(lwin->RPort, font);
	SetDrMd(lwin->RPort, JAM1);
	templevel= startlevel;
	goto colorlevel;

	/*- process messages -*/
	do
	{	while (!(limsg= GT_GetIMsg(lwin->UserPort)))
		{	if (winsig&Wait(winsig|(1<<lwin->UserPort->mp_SigBit)))
			{	while (limsg= GT_GetIMsg(win->UserPort)) GT_ReplyIMsg(limsg);
				if (autoact) ActivateWindow(lwin);
			}
		}

		switch (limsg->Class)
		{	case IDCMP_GADGETUP:
				switch (i= ((struct Gadget *)limsg->IAddress)->GadgetID)
				{	case 10: /* ok gadget */
ok:						startlevel= templevel;
					case 11: /* cancel gadget */
cancel:					end= TRUE;
						break;
					default: /* number gadget */
setlevel:				if (templevel != i)
						{

	#define CALCXPOS  (INTERWIDTH+BBWIDTH+((templevel-1)%3)*(INTERWIDTH+30))
	#define CALCYPOS  (INTERHEIGHT+BBHEIGHT+((9-templevel)/3)*(INTERHEIGHT+14))

							x= CALCXPOS; y= CALCYPOS;
							SetAPen(lwin->RPort, 0);
							RectFill(lwin->RPort, x, y,
								x+30-2*BBWIDTH-1, y+14-2*BBHEIGHT-1);
							SetAPen(lwin->RPort, 1);
							Move(lwin->RPort, x+BBWIDTH+7, y+BBHEIGHT+7);
							Text(lwin->RPort, levelnums[templevel-1], 1);
							templevel= i;
						}
colorlevel:				x= CALCXPOS; y= CALCYPOS;
						SetAPen(lwin->RPort, 3);
						RectFill(lwin->RPort, x, y,
							x+30-2*BBWIDTH-1, y+14-2*BBHEIGHT-1);
						SetAPen(lwin->RPort, 1);
						Move(lwin->RPort, x+BBWIDTH+7, y+BBHEIGHT+7);
						Text(lwin->RPort, levelnums[templevel-1], 1);

	#undef CALCXPOS
	#undef CALCYPOS

				}
				break;
			case IDCMP_VANILLAKEY:
				if ((i= limsg->Code) == '\r')			goto ok;
				else if (i == ESC)						goto cancel;
				else if (((i-= '0') >= 1) && (i <= 9))	goto setlevel;
				break;
			case IDCMP_CLOSEWINDOW:
				end= TRUE;
		}

		GT_ReplyIMsg(limsg);
	} while (!end);

	LS_CloseNewLevel_();
	PC_CtrlWindow_(CW_ACTIV_);

	#undef ESC
}

/*==== close level window ====*/
void LS_CloseNewLevel_(void)
{	if (lwin)
	{	CloseWindow(lwin);
		lwin= NULL;
	}
	FreeGadgets(lcongad); lcongad= NULL;
}

/*==== increase or reset score and print it ====*/
void LS_IncScore_(const UBYTE row)
{	extern UWORD winoffs[2], wbleft, wbtop, winwidth; /* from "playerctrl.c" */
	extern BYTE players, pl; /* from "playerctrl.c" */

	struct champ tempchamp;
	UBYTE oldplace, i;

	/*- increase or reset score -*/
	if (row == ls_RESET_)
		ch[pl]->score= 0;
	else
		ch[pl]->score+= 26-row+ch[pl]->level+(ch[pl]->level>>1);
	OutNumber_(2, winoffs[pl]+130, 18, ch[pl]->score, 6);

	/*- find new place number in highscore list -*/
	oldplace= place[pl];
	while (place[pl] && (ch[pl]->score >= champions[place[pl]-1].score))
	{	place[pl]--;
		ch[pl]--;
	}

	/*- mix entries according to new place number -*/
	if (oldplace != place[pl])
	{	if ((oldplace > place[pl^1]) && (place[pl] <= place[pl^1]))
		{	place[pl^1]++;
			ch[pl^1]++;
		}

		CopyMem(&champions[oldplace], &tempchamp, sizeof(struct champ));
		for (i= oldplace; i > place[pl]; i--)
			CopyMem(&champions[i-1], &champions[i], sizeof(struct champ));
		CopyMem(&tempchamp, &champions[i], sizeof(struct champ));

		OutEntry_(ls_GONEUP_, (ULONG)oldplace);
	} else
		OutEntry_(ls_OUTSCO_);
}

/*==== open/close highscore table ====*/
void LS_CtrlTable_(UBYTE openit)
{	extern void *vi; /* from "playerctrl.c" */
	extern struct RastPort *rport; /* from "playerctrl.c" */
	extern UWORD winoffs[2], wbleft, wbtop, winwidth; /* from "playerctrl.c" */

	if (openit == CT_TOGGLE_)
		openit= tableopen^1;
	else if ((tableopen == openit)
		|| ((openit == CT_SHOW_) && !tableopen))
		return;

	if (openit != CT_SHOW_)
	{	PC_CtrlMenu_(CM_UNCHK_|openit, MEN_HIGHSCORE_);
		PC_ResizeWindow_(openit);

		tableopen= openit;
	}

	if (openit && win)
	{	DrawBevelBox(rport, wbleft+HSCLFT_, wbtop+HSCTOP_,
			2*INTERWIDTH+4*BBWIDTH+HSWIDTH_,
			2*INTERHEIGHT+PLACES_*HSHEIGHT_+4*BBHEIGHT,
			GT_VisualInfo, vi,
			TAG_DONE);

		DrawBevelBox(rport, wbleft+HSCLFT_+BBWIDTH+2, wbtop+HSCTOP_+BBHEIGHT+1,
			2*INTERWIDTH+2*BBWIDTH+HSWIDTH_-4,
			2*INTERHEIGHT+PLACES_*HSHEIGHT_+2*BBHEIGHT-2,
			GT_VisualInfo, vi,
			GTBB_Recessed, TRUE,
			TAG_DONE);

		LS_LoadTable_(LT_SHOW_);
	}
}

/*==== get level (for timerctrl to set timer correctly) ====*/
UBYTE LS_GetLevel_(void)
{	extern BYTE players, pl; /* from "playerctrl.c" */

	return ch[pl]->level;
}

/*==== increase or reset level and print it ====*/
void LS_IncLevel_(const BOOL inc)
{	extern UWORD winoffs[2], wbleft, wbtop, winwidth; /* from "playerctrl.c" */
	extern BYTE players, pl; /* from "playerctrl.c" */

	if (inc)
	{	if (ch[pl]->level < 9)
		{	ch[pl]->level++;
			OutEntry_(ls_OUTLEV_);
		}
	} else
		ch[pl]->level= startlevel;

	OutNumber_(1, winoffs[pl]+170, 10, ch[pl]->level, 1);
}

/*==== increase or reset number of full lines and print it ====*/
void LS_IncLines_(const BOOL inc)
{	extern UWORD winoffs[2], wbleft, wbtop, winwidth; /* from "playerctrl.c" */
	extern BYTE players, pl; /* from "playerctrl.c" */

	if (inc)
	{	if (lines[pl]/10 > ch[pl]->level) LS_IncLevel_(IL_INC_);
		lines[pl]++;
	} else
		lines[pl]= 0;
	OutNumber_(3, winoffs[pl]+78, 13, (LONG)lines[pl], 3);
}

/*==== set entry to default for display while game is running ====*/
void LS_StartGame_(void)
{	extern BYTE players, pl; /* from "playerctrl.c" */

	ch[pl]= &champions[place[pl]= PLACES_+pl];
	CopyMem("Player 0    ", ch[pl]->name, NSIZE_);
	ch[pl]->name[7]= '1'+pl;
	CopyMem(date, ch[pl]->date, sizeof(date));
	LS_IncLines_(IL_RESET_);
	LS_IncLevel_(IL_RESET_);
	LS_IncScore_(ls_RESET_);
}

/*==== revert highscore table to old status ====*/
void LS_AbortGame_(void)
{	extern BYTE players, pl; /* from "playerctrl.c" */

	UBYTE i= place[pl];

	while (i++ <= PLACES_)
		CopyMem(&champions[i], &champions[i-1], sizeof(struct champ));

	OutEntry_(ls_ABORT_|ls_RESET_);
}

/*==== if necessary, let user input name(s) for highscore table ====*/
void LS_InputEntry_(void)
{	extern struct TagItem disable[]; /* from "playerctrl.c" */
	extern void *vi; /* from "playerctrl.c" */
	extern struct Window *win; /* from "playerctrl.c" */
	extern ULONG winsig; /* from "playerctrl.c" */
	extern struct TextAttr textattr; /* from "playerctrl.c" */
	extern UWORD winoffs[2], wbleft, wbtop, winwidth; /* from "playerctrl.c" */
	extern BYTE players, pl; /* from "playerctrl.c" */

	UBYTE savedpl= pl;	/* preserve variable 'pl' */
	BOOL wasopen= TRUE;	/* temporary variable to close table properly */
	BOOL end, changed= FALSE;
	BYTE i, rem;
	struct Gadget *hgad;
	struct IntuiMessage *himsg;
	BPTR hfile;			/* file handle for highscore file */

	#define hwin_WDT	(3*INTERWIDTH+64+116)
	#define hwin_HGT	(3*INTERHEIGHT+2*14)

	static struct NewWindow inputwindow=
	{	0, 0, 0, 0, 0, 1,
		BUTTONIDCMP|STRINGIDCMP|IDCMP_CLOSEWINDOW|IDCMP_ACTIVEWINDOW,
		WFLG_DRAGBAR|WFLG_CLOSEGADGET|WFLG_DEPTHGADGET|WFLG_ACTIVATE
			|WFLG_SMART_REFRESH|WFLG_GIMMEZEROZERO,
		NULL, NULL, "Enter your name", NULL, NULL, 0, 0, 0, 0, PUBLICSCREEN
	};

	static struct NewGadget namegadget=
	{	2*INTERWIDTH+64, INTERHEIGHT,
		116, 14, "Player 1", &textattr, 0, PLACETEXT_LEFT, NULL, NULL
	};
	static struct NewGadget okgadget=
	{	INTERWIDTH, 2*INTERHEIGHT+14,
		38, 14, "OK", &textattr, 1, PLACETEXT_IN, NULL, NULL
	};
	static struct NewGadget guessgadget=
	{	2*INTERWIDTH+38+1, 2*INTERHEIGHT+14,
		62, 14, "Guess", &textattr, 2, PLACETEXT_IN, NULL, NULL
	};
	static struct NewGadget cancelgadget=
	{	3*INTERWIDTH+38+62+2, 2*INTERHEIGHT+14,
		70, 14, "Cancel", &textattr, 3, PLACETEXT_IN, NULL, NULL
	};

	if (!win) return;

	for (pl= 0; pl <= players; pl++)
	{	if (place[pl] > 9) continue;

		PC_CtrlWindow_(CW_SLEEP_);

		if (!tableopen)
		{	wasopen= FALSE;
			LS_CtrlTable_(CT_OPEN_);
		}

		namegadget.ng_GadgetText[7]= '1'+pl;

		inputwindow.LeftEdge= win->LeftEdge+winoffs[pl]-
			(pl? hwin_WDT-win_WDT : 0);
		inputwindow.TopEdge= win->TopEdge+((win_HGT-hwin_HGT)>>1);

		namegadget.ng_VisualInfo= okgadget.ng_VisualInfo=
			guessgadget.ng_VisualInfo= cancelgadget.ng_VisualInfo= vi;

		/* create name input gadgets */
		hgad= CreateContext(&hcongad); /* create gadgets */
		hgad= CreateGadget(STRING_KIND, hgad, &namegadget,
			GTST_MaxChars, NSIZE_,
			TAG_DONE);
		for (i= 0; i <= 2; i++)
			hgad= CreateGadget(BUTTON_KIND, hgad, (i == 2)? &cancelgadget :
				(i? &guessgadget : &okgadget), 
				TAG_DONE);

		/* open name input window */
		if (!hgad || !(hwin= OpenWindowTags(&inputwindow, /* open window */
			WA_ScreenTitle, PRGNAME " - (c)1992 by Dieter Temme",
			WA_InnerWidth, hwin_WDT,
			WA_InnerHeight, hwin_HGT,
			TAG_DONE)))
			PrintNoMem_();


		/* add gadget list to window */
		AddGList(hwin, hcongad, -1, -1, NULL);
		RefreshGList(hcongad, hwin, NULL, -1);
		GT_RefreshWindow(hwin, NULL);
		hgad= hcongad->NextGadget;

		end= FALSE;
		rem= NMAX_-1;

		/* eventually disable guess gadget */
		if (!names[pl][NMAX_-1][0])
			GT_SetGadgetAttrsA(hgad->NextGadget->NextGadget, hwin, NULL, disable);

		/* process messages */
		do
		{	while (!(himsg= GT_GetIMsg(hwin->UserPort)))
			{	if (winsig&Wait(winsig|(1<<hwin->UserPort->mp_SigBit)))
				{	while (himsg= GT_GetIMsg(win->UserPort)) GT_ReplyIMsg(himsg);
					if (autoact) ActivateWindow(hwin);
					himsg= NULL;
					goto activ;					
				}
			}

			switch (himsg->Class)
			{	case IDCMP_GADGETUP:
					switch (((struct Gadget *)himsg->IAddress)->GadgetID)
					{	case 3: /* abort gadget */
	cancel:					LS_AbortGame_();
							end= TRUE;
							break;
						case 2: /* guess gadget */
							GT_SetGadgetAttrs(hgad, hwin, NULL,
								GTST_String, names[pl][rem],
								TAG_DONE);
							if (!names[pl][rem][0] || !rem--) rem= NMAX_-1;
							goto activ;
						default: /* ok gadget */
	ok:						if (!(*((struct StringInfo *)hgad->SpecialInfo)
								->Buffer)) break;
							CopyMem(((struct StringInfo *)hgad
								->SpecialInfo)->Buffer, ch[pl]->name, NSIZE_);

							i= 0;
							while (strcmp(ch[pl]->name, names[pl][i])
								&& (i < NMAX_-1)) i++;
							while (i < NMAX_-1)
							{	CopyMem(names[pl][i+1], names[pl][i], NSIZE_);
								i++;
							}
							CopyMem(ch[pl]->name, names[pl][NMAX_-1], NSIZE_);

							for (i= 0; ch[pl]->name[i]; i++);
							while (i < NSIZE_) ch[pl]->name[i++]= ' ';

							OutEntry_(ls_OUTALL_|ls_RESET_);
							changed= end= TRUE;
					}
					break;
				case IDCMP_CLOSEWINDOW:
					goto cancel;
				case IDCMP_ACTIVEWINDOW:
	activ:			ActivateGadget(hgad, hwin, NULL);
			}

			GT_ReplyIMsg(himsg);
		} while (!end);

		CloseWindow(hwin); hwin= NULL;
	}

	/*- save highscore table if necessary -*/
	if (changed) LS_SaveTable_(ST_QUIET_);

	LS_CloseInputEntry_();
	if (!wasopen) LS_CtrlTable_(CT_CLOSE_);
	PC_CtrlWindow_(CW_ACTIV_);
	pl= savedpl;
}

/*==== close name input window ====*/
void LS_CloseInputEntry_(void)
{	if (hwin)
	{	CloseWindow(hwin);
		hwin= NULL;
	}
	FreeGadgets(hcongad); hcongad= NULL;
}

/*==== change guess table of former players' names ====*/
void LS_CtrlGuess_(const UBYTE num)
{	BYTE i, j, k;
	TEXT *new[NMAX_]= { NULL };

	if (num)
		CopyMem(names[0], names[1], NSIZE_);
	else
	{	i= NMAX_-1;
		for (j= 2*NMAX_-1; j >= 0; j--)
		{	if (new[i]= names[j&1][j>>1])
			{	for (k= NMAX_-1; k > i; k--)
					if (!strcmp(new[i], new[k])) break;
				if ((k == i) && (--i < 0)) break;
			}
		}
		for (i= 0; i < NMAX_; i++) CopyMem(new[i], names[0][i], NSIZE_);
	}
}

/*==== set autoact variable ====*/
void LS_CtrlAutoAct_(const UBYTE new)
{	autoact= (new == CA_TOGGLE_)? 1-autoact : new;
	PC_CtrlMenu_(CM_UNCHK_+autoact, MEN_AUTOACT_);
}

/*==== read key definition string ====*/
void ReadKeyDef_(TEXT *source, UBYTE *dest, UBYTE dlen)
{	TEXT *index;
	IX ix;

	while (source && *source && dlen)
	{	if ((*source != (UBYTE)'«') || !(index= strchr(source, (UBYTE)'»')))
			return;
		*index= '\0';
		if (!ParseIX(source+1, &ix) && (ix.ix_Class == IECLASS_RAWKEY))
			*dest= ix.ix_Code;
		source= index+1;
		dest++;
		dlen--;
	}
}

/*==== read parameters from the highscores file icon's ToolTypes ====*/
void LS_LoadSettings_(void)
{	extern struct Library *AslBase;					/* from "main.c" */
	extern BPTR cdlock;								/* from "main.c" */
	extern UBYTE player1keys[], player2keys[], pausekey[];
													/* from "figurectrl.c" */

	struct DiskObject *obj;
	struct TEXT **tt;
	LONG num;
	TEXT *s;
	
	CurrentDir(hslock);
	if (obj= GetDiskObject(hsname))
	{	tt= obj->do_ToolTypes;
		s= FindToolType(tt, "MODE");
		if (s) PC_CtrlPlayers_(!Stricmp(s, "2PLAYERS")? 2 :
			!Stricmp(s, "1PLAYER")? 1 : 0);
		s= FindToolType(tt, "STARTLEVEL");
		startlevel= (s && (num= atol(s)) && (num <= 9))? num : 5;
		s= FindToolType(tt, "TABLE");
		LS_CtrlTable_((s && !Stricmp(s, "OPEN"))? CT_OPEN_ : CT_CLOSE_);
		s= FindToolType(tt, "AUTOACTIVATE");
		LS_CtrlAutoAct_(s? !Stricmp(s, "ON") : AslBase->lib_Version < 38);
		memcpy(player1keys, DEFPLAYER1KEYS, NUMPLAYERKEYS);
		memcpy(player2keys, DEFPLAYER2KEYS, NUMPLAYERKEYS);
		pausekey[0]= DEFPAUSEKEY;
		ReadKeyDef_(FindToolType(tt, "PLAYER1KEYS"), player1keys,
			NUMPLAYERKEYS);
		ReadKeyDef_(FindToolType(tt, "PLAYER2KEYS"), player2keys,
			NUMPLAYERKEYS);
		ReadKeyDef_(FindToolType(tt, "PAUSEKEY"), pausekey, 1);
		FreeDiskObject(obj);
		CurrentDir(cdlock);
	}
}

/*==== uncode unqualified rawkey into commodities like string ====*/
TEXT *LS_UncodeRawKey_(const UBYTE code)
{	struct InputEvent ie;			/* structure for MapRawKey() */
	TEXT *index;					/* temporary index for highkeys */
	static TEXT string[MAXKEYLEN];	/* result string */

	static UBYTE highkeycodes[]=
	{	0x40,			0x48,			0x50,	0x58,
		0x41,			0x49,			0x51,	0x59,
		0x42,							0x52,	0x5f,
		0x43,			0x4b,			0x53,
		0x44,			0x4c,			0x54,	0x6e,
		0x45,			0x4d,			0x55,	0x6f,
		0x46,			0x4e,			0x56,	0x70,
		0x47,			0x4f,			0x57,	0x71,		0
	};
	static TEXT *highkeytexts[]=
	{	"space",		"page_up",		"f1",	"f9",
		"backspace",	"page_down",	"f2",	"f10",
		"tab",							"f3",	"help",
		"enter",		"f11",			"f4",
		"return",		"up",			"f5",	"pause",
		"esc",			"down",			"f6",	"f12",
		"del",			"right",		"f7",	"home",
		"insert",		"left",			"f8",	"end"
	};
	static TEXT numkeycodes[]=
	{	0x0f, 0x1d, 0x1e, 0x1f, 0x2d, 0x2e, 0x2f, 0x3c,
		0x3d, 0x3e, 0x3f, 0x4a, 0x5a, 0x5b, 0x5c, 0x5d,
		0x5e,                                           0
	};

	ie.ie_Class= IECLASS_RAWKEY;
	ie.ie_Qualifier= 0;
	ie.ie_EventAddress= 0;

	if (code && (index= strchr(highkeycodes, code)))
	{	strcpy(string, highkeytexts[index-highkeycodes]);
	} else
	{	if (code && strchr(numkeycodes, code))	strcpy(string, "numericpad  ");
		else									strcpy(string, " ");
		ie.ie_Code= code;
		MapRawKey(&ie, strrchr(string, ' '), 1, NULL);
	}

	return string;
}

/*==== fill key definition string ====*/
void WriteKeyDef_(UBYTE *source, TEXT *dest, TEXT *defstr, UBYTE slen)
{	strcpy(dest, defstr);
	while (slen)
	{	strcat(dest, "«");
		strcat(dest, LS_UncodeRawKey_(*source));
		strcat(dest, "»");
		source++;
		slen--;
	}
}

/*==== save actual settings into ToolTypes of the icon ====*/
void LS_SaveSettings_(void)
{	extern BYTE players, pl;						/* from "playerctrl.c" */
	extern BPTR cdlock;								/* from "main.c" */
	extern UBYTE player1keys[], player2keys[], pausekey[];
													/* from "figurectrl.c" */
	
	BPTR lock;
	BOOL new= FALSE;
	struct DiskObject *obj;
	static UWORD projectidata[]=
	{	0xFFFF,0xCFFF,0xFCFF,0xFFC0,0xE618,0xCC61,0x8CC6,0x18C0,
		0xFF3F,0xCCFF,0xFCFF,0x3FC0,0x03F0,0x0FC0,0x0003,0xF000,
		0x0330,0x0CC0,0x0003,0x3000,0x0330,0x0CC0,0x0003,0x3000,
		0x03F0,0x0FFF,0x0003,0xF000,0x03F0,0x0FFF,0x0003,0xF000,
		0x03F0,0x0FFF,0x0003,0xF000,0x03F0,0x0FC0,0x0003,0xF000,
		0x03F0,0x0FC0,0x0003,0xF000,0x03F0,0x0FC0,0x0003,0xF000,
		0x03F0,0x0FFF,0xFC03,0xF000,0x03F0,0x0FFF,0xFC03,0xF000,
		0x03F0,0x0FFF,0xFC03,0xF000,0x0000,0x0000,0x0000,0x0000,
		0x00FF,0xF07E,0x0FFF,0x0000,0x00C6,0x3066,0x0C63,0x0000,
		0x00CF,0xF066,0x0FFF,0x0000,0x00FC,0x7E7E,0x7E00,0x0000,
		0x00CC,0x6666,0x6600,0x0000,0x00CC,0x7E66,0x7E00,0x0000,
		0x00FF,0xF07E,0x0FFF,0x0000,0x00FF,0xF07E,0x0FFF,0x0000,
		0x00FF,0xF07E,0x0FFF,0x0000,0x00FC,0x7E7E,0x0007,0xE000,
		0x00FC,0x7E7E,0x0007,0xE000,0x00FC,0x7E7E,0x0007,0xE000,
		0x00FC,0x7E7E,0x0FFF,0x0000,0x00FC,0x7E7E,0x0FFF,0x0000,
		0x00FC,0x7E7E,0x0FFF,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x19E7,0x039E,0x7039,0xE700,0x00C0,0x0300,0x0000,0xC000,
		0x0000,0x0000,0x0000,0x0000,0x00C0,0x0300,0x0000,0xC000,
		0x00C0,0x0300,0x0000,0xC000,0x0000,0x0000,0x0000,0x0000,
		0x00C0,0x039C,0x0000,0xC000,0x0000,0x0000,0x0000,0x0000,
		0x00C0,0x0300,0x0000,0xC000,0x00C0,0x0300,0x0000,0xC000,
		0x0000,0x0000,0x0000,0x0000,0x00C0,0x0300,0x0000,0xC000,
		0x00C0,0x039E,0x7000,0xC000,0x0000,0x0000,0x0000,0x0000,
		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0039,0xC018,0x039C,0x0000,0x0030,0x0018,0x0000,0x0000,
		0x0000,0x0000,0x0000,0x0000,0x0030,0x1818,0x1800,0x0000,
		0x0030,0x0018,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0039,0xC018,0x039C,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0030,0x0018,0x0000,0x0000,0x0030,0x1818,0x0001,0x8000,
		0x0000,0x0000,0x0000,0x0000,0x0030,0x1818,0x0000,0x0000,
		0x0030,0x1818,0x039C,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x00C0,0x0300,0x0000,0xC000,0x00C0,0x039E,0x7000,0xC000,
		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
		0x0030,0x1818,0x0000,0x0000,0x0030,0x1818,0x039C,0x0000,
		0x0000,0x0000,0x0000,0x0000
	};
	static struct Image projectimage=
	{	0, 0, 58, 31, 3, projectidata, 0x07, 0x00, NULL
	};
	static TEXT keys[3][MAXKEYLEN*NUMPLAYERKEYS];
	static TEXT *st[]=
	{	"MODE=2PLAYERS",
		"STARTLEVEL=x",
		"TABLE=CLOSED",
		keys[0], keys[1], keys[2],
		NULL
	};
	static struct DiskObject projectobj=
	{	WB_DISKMAGIC, 1,
		{	NULL, 0, 0, 58, 32, GADGBACKFILL|GFLG_GADGIMAGE,
			GACT_RELVERIFY, GTYP_BOOLGADGET, &projectimage,
			NULL, NULL, 0, NULL, 0, (APTR)1
		},
		WBPROJECT, NULL, st, NO_ICON_POSITION, NO_ICON_POSITION
	};

	{	CurrentDir(hslock);

		if (obj= GetDiskObject(hsname))
			obj->do_ToolTypes= st;
		else
		{	new= TRUE;
			projectobj.do_DefaultTool= PRGNAME;
		}

		strcpy(&st[0][5], players? "2PLAYERS" : "1PLAYER");
		st[1][11]= '0'+startlevel;
		strcpy(&st[2][6], tableopen? "OPEN" : "CLOSED");
		WriteKeyDef_(player1keys, keys[0], "PLAYER1KEYS=", NUMPLAYERKEYS);
		WriteKeyDef_(player2keys, keys[1], "PLAYER2KEYS=", NUMPLAYERKEYS);
		WriteKeyDef_(pausekey, keys[2], "PAUSEKEY=", 1);
		PutDiskObject(hsname, new? &projectobj : obj);
		CurrentDir(cdlock);
	}

	if (!new) FreeDiskObject(obj);
}
