#include <proto/intuition.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/dos.h>
#include <devices/audio.h>
#include <exec/memory.h>
#include <string.h>

#define SCREENW 320
#define SCREENH 256
#define SCREEND 4
#define SIDE 10
#define PLAYFIELDW (SIDE*10)
#define PLAYFIELDH (SIDE*20)
#define PLAYFIELDY 10
#define PLAYFIELDX ((SCREENW-PLAYFIELDW)/2)

extern struct GfxBase *GfxBase;
extern struct IntuitionBase *IntuitionBase;
extern struct WBStartup *WBenchMsg;

struct Piece {
   struct Piece *RPiece;
   char x, y; /* in quadrati ! */
	char deltax, deltay;
	char type, points;
	unsigned short structure; /* sei bit più bassi: struttura; i quattro più alti: colore */
	struct RastPort *RPort;
   };

struct AllPieces {
	struct Piece Cube;
	struct Piece BarVert;
	struct Piece BarHor;
	struct Piece SVert;
	struct Piece SHor;
	struct Piece ZVert;
	struct Piece ZHor;
	struct Piece THorU;
	struct Piece TVertR;
	struct Piece THorD;
	struct Piece TVertL;
	struct Piece LHorU;
	struct Piece LVertR;
	struct Piece LHorD;
	struct Piece LVertL;
	struct Piece KHorU;
	struct Piece KVertR;
	struct Piece KHorD;
	struct Piece KVertL;
};

union Janus {
	struct AllPieces S;
	struct Piece Piece[19];
};

extern union Janus J;

union Janus J = {
	{
		{
			&J.S.Cube,
			2, 2,
			0, 0, 0, 1,
			0x300F
		},
		{
			&J.S.BarHor,
			1, 4,
			2, -1, 1, 1,
			0x500F
		},
		{
			&J.S.BarVert,
			4, 1,
			-2, 1, 1, 1,
			0x500F
		},
		{
			&J.S.SHor,
			2, 3,
			1, -1, 2, 1,
			0x702D
		},
		{
			&J.S.SVert,
			3, 2,
			-1, 1, 2, 1,
			0x701E
		},
		{
			&J.S.ZHor,
			2, 3,
			1, -1, 3, 1,
			0x901E
		},
		{
			&J.S.ZVert,
			3, 2,
			-1, 1, 3, 1,
			0x9033
		},
		{
			&J.S.TVertR,
			3, 2,
			0, 0, 6, 1,
			0xB03A
		},
		{
			&J.S.THorD,
			2, 3,
			1, -1, 6, 1,
			0xB01D
		},
		{
			&J.S.TVertL,
			3, 2,
			-1, 1, 6, 1,
			0xB017
		},
		{
			&J.S.THorU,
			2, 3,
			0, 0, 6, 1,
			0xB02E
		},
		{
			&J.S.LVertR,
			3, 2,
			0, 0, 4, 1,
			0xD03C
		},
		{
			&J.S.LHorD,
			2, 3,
			1, -1, 4, 1,
			0xD035
		},
		{
			&J.S.LVertL,
			3, 2,
			-1, 1, 4, 1,
			0xD00F
		},
		{
			&J.S.LHorU,
			2, 3,
			0, 0, 4, 1,
			0xD02B
		},
		{
			&J.S.KVertR,
			3, 2,
			0, 0, 5, 1,
			0xF039
		},
		{
			&J.S.KHorD,
			2, 3,
			1, -1, 5, 1,
			0xF017
		},
		{
			&J.S.KVertL,
			3, 2,
			-1, 1, 5, 1,
			0xF027
		},
		{
			&J.S.KHorU,
			2, 3,
			0, 0, 5, 1,
			0xF03A
		}
	}
};

struct Piece *SelectRandom[7] = {
	&J.S.Cube,
	&J.S.BarHor,
	&J.S.LHorD,
	&J.S.KHorD,
	&J.S.THorD,
	&J.S.SHor,
	&J.S.ZHor
};

USHORT chip Sigma[] = {
	0xFE00,
	0xC300,
	0x6000,
	0x3000,
	0x6000,
	0xC300,
	0xFE00
};

BYTE chip sound[]={ 0, 64, 127, 64, 0, -64, -127, -64 };
BYTE AllocTries[]={ 1, 2, 4, 8 };

USHORT *UndoBuf=NULL;
USHORT (*PieceBuf)[256]=NULL;

struct BitMap *PieceBitMap=NULL;
struct RastPort *PieceRPort=NULL;

struct BitMap UndoBitMap;
struct RastPort UndoRPort;
struct Piece UndoPiece, *CurrentPiece;

struct Screen *Screen=NULL;
struct Window *Window=NULL;
struct IntuiMessage Message;
struct Message *TmpMsg;
struct IOAudio IOAudio;

char CLI_Help[]="\n[1m[33mATetris[m © 1989 Sebastiano Vigna\n\n[3mThe first Exec-compatible game!!![m\n\nESC or P to pause/restart\n4 or 7 to move left\n6 or 9 to shift right\n5, 8 or TAB to rotate\nSPACE or RETURN to drop\n";

char Places[20][10];
char FullLine[10] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };

int Ticks, Level, GameLevel, CurrentX, CurrentY, GameOver=1, Pause, Score, Lines, SoundOpen, SoundOn;
short TicksNum[6] = { 6, 5, 4, 3, 2, 1 };

short YStats[7] = { 61, 79, 99, 123, 147, 171, 195 };
short Stats[7], SumStats;

struct TextAttr TOPAZ80 = {
	(STRPTR)"topaz.font",
	TOPAZ_EIGHTY, 0, 0
};

struct NewScreen NewScreen = {
	0, 0,
	SCREENW, SCREENH,
	SCREEND,
	0, 1,
	NULL,
	CUSTOMSCREEN,
	&TOPAZ80,
	"ATetris"
};


USHORT Palette[] = {
	0x0000,
	0x0FFF,
	0x0888,
	0x000F,
	0x000F,
	0x0F00,
	0x0F0F,
	0x00F0,
	0x0FF0,
	0x00FF,
	0x0F00,
	0x0B72,
	0x00FF,
	0x0FFF,
	0x0FF0,
	0x0F0F
};


struct IntuiText IText00 = {
	2, 1, JAM1,
	19, 0,
	&TOPAZ80,
	"Quit",
	NULL
};

struct MenuItem MenuItem5 = {
	NULL,
	2, 38,
	114, 9,
	ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP,
	0,
	(APTR)&IText00,
	NULL,
	'Q',
	NULL,
	MENUNULL
};

struct IntuiText IText0 = {
	2, 1, JAM1,
	19, 0,
	&TOPAZ80,
	"HighPri",
	NULL
};

struct MenuItem MenuItem4 = {
	&MenuItem5,
	2, 29,
	114, 9,
	CHECKIT | MENUTOGGLE | ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP,
	0,
	(APTR)&IText0,
	NULL,
	'P',
	NULL,
	MENUNULL
};

struct IntuiText IText1 = {
	2, 1, JAM1,
	19, 0,
	&TOPAZ80,
	"Sound",
	NULL
};

struct MenuItem MenuItem3 = {
	&MenuItem4,
	2, 20,
	114, 9,
	CHECKIT | CHECKED | MENUTOGGLE | ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP,
	0,
	(APTR)&IText1,
	NULL,
	'S',
	NULL,
	MENUNULL
};

struct IntuiText IText6 = {
	2, 1, JAM1,
	19, 0,
	&TOPAZ80,
	"5",
	NULL
};

struct MenuItem SubItem6 = {
	NULL,
	89, 45,
	67, 9,
	ITEMTEXT | CHECKIT | COMMSEQ | ITEMENABLED | HIGHCOMP,
	0x3DF,
	(APTR)&IText6,
	NULL,
	'5',
	NULL,
	MENUNULL
};

struct IntuiText IText7 = {
	2, 1, JAM1,
	19, 0,
	&TOPAZ80,
	"4",
	NULL
};

struct MenuItem SubItem5 = {
	&SubItem6,
	89, 36,
	67, 9,
	ITEMTEXT | CHECKIT | COMMSEQ | ITEMENABLED | HIGHCOMP,
	0x3EF,
	(APTR)&IText7,
	NULL,
	'4',
	NULL,
	MENUNULL
};

struct IntuiText IText8 = {
	2, 1, JAM1,
	19, 0,
	&TOPAZ80,
	"3",
	NULL
};

struct MenuItem SubItem4 = {
	&SubItem5,
	89, 27,
	67, 9,
	ITEMTEXT | CHECKIT | COMMSEQ | ITEMENABLED | HIGHCOMP,
	0x3F7,
	(APTR)&IText8,
	NULL,
	'3',
	NULL,
	MENUNULL
};

struct IntuiText IText9 = {
	2, 1, JAM1,
	19, 0,
	&TOPAZ80,
	"2",
	NULL
};

struct MenuItem SubItem3 = {
	&SubItem4,
	89, 18,
	67, 9,
	ITEMTEXT | CHECKIT | COMMSEQ | ITEMENABLED | HIGHCOMP,
	0x3FB,
	(APTR)&IText9,
	NULL,
	'2',
	NULL,
	MENUNULL
};

struct IntuiText IText10 = {
	2, 1, JAM1, 
	19, 0,
	&TOPAZ80,
	"1",
	NULL
};

struct MenuItem SubItem2 = {
	&SubItem3,
	89, 9,
	67, 9,
	ITEMTEXT | CHECKIT | COMMSEQ | ITEMENABLED | HIGHCOMP,
	0x3FD,
	(APTR)&IText10,
	NULL,
	'1',
	NULL,
	MENUNULL
};

struct IntuiText IText11 = {
	2, 1, JAM1,
	19, 0,
	&TOPAZ80,
	"0",
	NULL
};

struct MenuItem SubItem1 = {
	&SubItem2,
	89, 0,
	67, 9,
	CHECKIT | CHECKED | ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP,
	0x3FE,
	(APTR)&IText11,
	NULL,
	'0',
	NULL,
	MENUNULL
};

struct IntuiText IText12 = {
	2, 1, JAM1,
	19, 0,
	&TOPAZ80,
	"Level",
	NULL
};

struct MenuItem MenuItem2 = {
	&MenuItem3,
	2, 11,
	114, 9,
	ITEMTEXT | ITEMENABLED | HIGHCOMP,
	0,
	(APTR)&IText12,
	NULL,
	NULL,
	&SubItem1,
	MENUNULL
};

struct IntuiText IText13 = {
	2, 1, JAM1,
	19, 0,
	&TOPAZ80,
	"New Game",
	NULL
};

struct MenuItem MenuItem1 = {
	&MenuItem2,
	2, 2,
	114, 9,
	ITEMTEXT | COMMSEQ | ITEMENABLED | HIGHCOMP,
	0,
	(APTR)&IText13,
	NULL,
	'N',
	NULL,
	MENUNULL
};

struct Menu Menu = {
	NULL,
	0, 0,
	63, 0,
	MENUENABLED,
	"Project",
	&MenuItem1
};

struct NewWindow NewWindow = {
	0, 0,
	SCREENW, SCREENH,
	0, 1,
	MENUPICK | VANILLAKEY | INTUITICKS,
	SIMPLE_REFRESH | BACKDROP | BORDERLESS | ACTIVATE | NOCAREREFRESH,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	320, 256,
	-1, -1,
	CUSTOMSCREEN
};

struct IntuiText StatsText = {
	1, 0, JAM2,
	280, 0,
	&TOPAZ80
};

struct IntuiText ScreenText5 = {
	1, 0, JAM1,
	226, 32,
	&TOPAZ80,
	"Statistics:",
	NULL
};

struct IntuiText ScreenText4 = {
	1, 0, JAM1,
	30, 100,
	&TOPAZ80,
	"Next:",
	&ScreenText5
};

struct IntuiText LevelText = {
	1, 0, JAM2,
	56, 52,
	&TOPAZ80
};

struct IntuiText ScreenText3 = {
	1, 0, JAM1,
	8, 52,
	&TOPAZ80,
	"Level:0",
	&ScreenText4
};

struct IntuiText LinesText = {
	1, 0, JAM2,
	56, 42,
	&TOPAZ80
};

struct IntuiText ScreenText2 = {
	1, 0, JAM1,
	8, 42,
	&TOPAZ80,
	"Lines:0",
	&ScreenText3
};

struct IntuiText ScoreText = {
	1, 0, JAM2,
	56, 32,
	&TOPAZ80
};

struct IntuiText ScreenText1 = {
	1, 0, JAM1,
	8, 32,
	&TOPAZ80,
	"Score:0",
	&ScreenText2
};

struct IntuiText GameOverText3 = {
	8, 10, JAM2,
	PLAYFIELDX-2, PLAYFIELDY+(PLAYFIELDH-8)/2-8,
	&TOPAZ80,
	"             ",
	NULL
};

struct IntuiText GameOverText2 = {
	8, 10, JAM2,
	PLAYFIELDX-2, PLAYFIELDY+(PLAYFIELDH-8)/2+8,
	&TOPAZ80,
	"             ",
	&GameOverText3
};

struct IntuiText GameOverText = {
	8, 10, JAM2,
	PLAYFIELDX-2, PLAYFIELDY+(PLAYFIELDH-8)/2,
	&TOPAZ80,
	"  Game Over  ",
	&GameOverText2
};


struct Image ScreenImage3 = {
	PLAYFIELDX+PLAYFIELDW+1, PLAYFIELDY,
	9, 201, SCREEND,
 	NULL,
	0, 2,
   NULL
};

struct Image ScreenImage2 = {
	PLAYFIELDX-SIDE, PLAYFIELDY+PLAYFIELDH+1,
	120, 9, SCREEND,
	NULL,
	0, 2,
	&ScreenImage3
};

struct Image ScreenImage1 = {
	PLAYFIELDX-SIDE, PLAYFIELDY,
	9, 201, SCREEND,
	NULL,
	0, 2,
	&ScreenImage2
};

struct Image SigmaImage = {
	244, 220,
	8, 7, SCREEND,
	Sigma,
	1, 0,
	&ScreenImage1
};

struct Image APoint = {
	0, 0,
	2, 2, SCREEND,
	NULL,
	0, 1,
	NULL
};

struct Image Square = {
	0, 0,
	SIDE, SIDE, SCREEND,
	NULL,
	0, 1,
	NULL
};


void Beep(int Period) {
	if (SoundOn) {
		IOAudio.ioa_Period = Period;
		WaitIO((struct IORequest *)&IOAudio);
		BeginIO((struct IORequest *)&IOAudio);
	}
}


void RefreshNumber(struct IntuiText *IntuiText, int n, int l) {

	static char buf[16];
	int t;

	t = stci_d(buf, n);
	if (t<l) strncat(buf, "             ", l-t);
	IntuiText->IText = buf;
	PrintIText(Window->RPort, IntuiText, 0, 0);
}

void RefreshStats(int type) {

	StatsText.TopEdge = YStats[type];
	RefreshNumber(&StatsText, Stats[type], 3);
	StatsText.TopEdge = 220;
	RefreshNumber(&StatsText, SumStats, 4);
}

void IncrementStats(int type) {

	++SumStats, ++(Stats[type]);
	RefreshStats(type);
}

void DrawPiece(struct Piece *Piece, int x, int y) {

	ClipBlit(Piece->RPort, 0, 0, Window->RPort, x, y, Piece->x*SIDE, Piece->y*SIDE, 0xE0);
}

int ThereIsPlace(struct Piece *Piece, int x, int y) {

	int i, j;

	for(i=0; i<Piece->y; i++)
		for(j=0; j<Piece->x; j++)
			if ( ((Piece->structure >> (i*Piece->x+j) ) & 1) && Places[y+i][x+j])
				return(0);
	return(1);
}


void DrawNext(struct Piece *Piece, int b) {

	RectFill(Window->RPort, 20, 108, 70, 160);
	if (b) DrawPiece(Piece, 30+(36-Piece->x*SIDE)/2, 120);
}


struct Piece *GenerateNext(int b) {

	static struct Piece *NextNext, *t;

	t = NextNext;
	NextNext = SelectRandom[rand()%7];
	DrawNext(NextNext, b);
	if (b) IncrementStats(t->type);
	return(t);
}


void SaveBackGround(void) {

	ClipBlit(Window->RPort, PLAYFIELDX+CurrentX*SIDE, PLAYFIELDY+CurrentY*SIDE, &UndoRPort, 0, 0, CurrentPiece->x*SIDE, CurrentPiece->y*SIDE, 0xC0);
	UndoPiece = *CurrentPiece;
	UndoPiece.deltax = CurrentX;
	UndoPiece.deltay = CurrentY;

}


void RestoreBackGround(void) {

	ClipBlit(&UndoRPort, 0, 0, Window->RPort, PLAYFIELDX+UndoPiece.deltax*SIDE, PLAYFIELDY+UndoPiece.deltay*SIDE, UndoPiece.x*SIDE, UndoPiece.y*SIDE, 0xC0);
}

void SafelyDrawPiece(int b) {

	if (b) RestoreBackGround();
	SaveBackGround();
	DrawPiece(CurrentPiece, PLAYFIELDX+CurrentX*SIDE, PLAYFIELDY+CurrentY*SIDE);
}

void DrawGrid(void) {
	int x, y;

	RectFill(Window->RPort,
		(SCREENW-PLAYFIELDW)/2-1, PLAYFIELDY,
		(SCREENW-PLAYFIELDW)/2+PLAYFIELDW, PLAYFIELDY+PLAYFIELDH-1);
	for(x = 0; x<5; x++)
		for(y = 0; y<20; y++)
			DrawImage(Window->RPort, &APoint,
				(SCREENW-PLAYFIELDW)/2+SIDE*(1+x*2)+4, SIDE*(y+1)+PLAYFIELDY-6);
}

void PlacePiece() {

	CurrentPiece = GenerateNext(TRUE);
	CurrentX = (10-CurrentPiece->x)/2;
	CurrentY = 0;
	SafelyDrawPiece(FALSE);
}


void InitGame(void) {

	int i;

	OffMenu(Window, SHIFTMENU(0) | SHIFTITEM(1) | SHIFTSUB(NOSUB)); /* Non si può cambiare livello mentre si gioca */
	RefreshNumber(&ScoreText, 0, 5);
	RefreshNumber(&LinesText, 0, 5);
	RefreshNumber(&LevelText, GameLevel = Level, 2);
	DrawImage(Window->RPort, &ScreenImage1, 0, 0);
	DrawGrid();
	setmem((void *)Places, 200, 0);
	setmem((void *)Stats, 14, 0);
	SumStats = Lines = Score = GameOver = 0;
	for(i = 0; i<7; i++) RefreshStats(i);
	GenerateNext(FALSE);
	PlacePiece();
}

int ParseMenu(unsigned short MenuNumber) { /* Controlla le chiamate del menu */

	while(MenuNumber != MENUNULL) {

		switch(ITEMNUM(MenuNumber)) {

			case 0:	InitGame();
						break;

			case 1:  Level = SUBNUM(MenuNumber);
						RefreshNumber(&LevelText, Level, 1);
						break;

			case 2:	if (SoundOpen) SoundOn = !SoundOn;
						break;

			case 3:	if (SetTaskPri(FindTask(0), 1))
							SetTaskPri(FindTask(0), 0);
						break;

			case 4:	return(1);
						break;

			default:	break;
		}
		MenuNumber = (ItemAddress(&Menu, MenuNumber))->NextSelect;

	}

	return(0);
}

void IncrementTicks(int b) { /* Routine principale di gestione del gioco, chiamata ad ogni INTUITICK */
	int i, j, StartY;

	if ((++Ticks<TicksNum[GameLevel]) && (!b)) return;

	Ticks = 0;
	StartY = CurrentY;

	do {
		if ((CurrentY+CurrentPiece->y<20) && ThereIsPlace(CurrentPiece, CurrentX, CurrentY+1)) {
			CurrentY++;
			if ((CurrentX+1+CurrentPiece->x<=10) && ThereIsPlace(CurrentPiece, CurrentX+1, CurrentY) && (GameLevel == 6) && (!(CurrentY%5)) && (!b)) CurrentX++;
			if (!b) Beep(3200);
			SafelyDrawPiece(TRUE);
		}
		else {	/* Se il pezzo non può più scendere... */

			for(i = 0; i<CurrentPiece->y; i++)
				for(j = 0; j<CurrentPiece->x; j++)
					if ((CurrentPiece->structure >> (i*CurrentPiece->x+j) ) & 1) Places[CurrentY+i][CurrentX+j] = 1;

			Score += 4+20-CurrentY+( (b) ? CurrentY-StartY : 0)+(GameLevel+3)*3;

			for(i = 0; i<CurrentPiece->y; i++)
				if (!memcmp(Places[CurrentY+i], FullLine, 10)) {
					for(j = 0; j<10; j++) Beep(2400);
					ClipBlit(Window->RPort, PLAYFIELDX, PLAYFIELDY-SIDE, Window->RPort, PLAYFIELDX, PLAYFIELDY, SIDE*10, SIDE*(CurrentY+i+1), 0xC0);
					movmem(Places[0], Places[1], 10*(CurrentY+i));
					setmem(Places[0], 10, 0);
					RefreshNumber(&LinesText, ++Lines, 5);
					if ((Lines%10 == 1) && (Lines>1) && (GameLevel<6)) RefreshNumber(&LevelText, ++GameLevel, 2);
					Score--;
				}
			RefreshNumber(&ScoreText, Score, 5);

			PlacePiece();
			if (!ThereIsPlace(CurrentPiece, CurrentX, CurrentY)) {
				GameOver = 1;
				OnMenu(Window, SHIFTMENU(0) | SHIFTITEM(1) | SHIFTSUB(NOSUB));
				PrintIText(Window->RPort, &GameOverText, 0, 0);
			}
		}
	} while(b && (CurrentY != 0));
}


void IfHePressedAKey(void) {
	if (	((CurrentY+CurrentPiece->y == 20) || (!ThereIsPlace(CurrentPiece, CurrentX, CurrentY+1))) &&
			(Ticks>TicksNum[GameLevel]-2)	) Ticks -= 2;
}



void ParseKey(unsigned short KeyCode) {	/* Controlla il tasto premuto e agisce di conseguenza */


	if ((Pause) && (KeyCode != 'p')) return;

	switch(KeyCode) {
		case '4':
		case '7':	if ((CurrentX>0) && ThereIsPlace(CurrentPiece, CurrentX-1, CurrentY)) {
							CurrentX--;
							SafelyDrawPiece(TRUE);
							IfHePressedAKey();
						}
						break;

		case '6':
		case '9':	if ((CurrentX+CurrentPiece->x<10) && ThereIsPlace(CurrentPiece, CurrentX+1, CurrentY)) {
							CurrentX++;
							SafelyDrawPiece(TRUE);
							IfHePressedAKey();
						}
						break;

		case '\t':
		case '5':
		case '8':	if (	(CurrentX+CurrentPiece->RPiece->deltax+CurrentPiece->RPiece->x <= 10) &&
								(CurrentY+CurrentPiece->RPiece->deltay+CurrentPiece->RPiece->y <= 20) &&
								(CurrentX+CurrentPiece->RPiece->deltax >= 0) &&
								(CurrentY+CurrentPiece->RPiece->deltay >= 0) &&
								ThereIsPlace(CurrentPiece->RPiece, CurrentX+CurrentPiece->RPiece->deltax, CurrentY+CurrentPiece->RPiece->deltay) ) {
							CurrentPiece = CurrentPiece->RPiece;
							CurrentX += CurrentPiece->deltax;
							CurrentY += CurrentPiece->deltay;
							SafelyDrawPiece(TRUE);
							IfHePressedAKey();
						}
						break;

		case 0xD:
		case ' ':	IncrementTicks(TRUE);
						break;

		case 0x1B:
		case 'p':	Pause = !Pause;
						break;

		default:		break;
	}
}

void FreeAndExit(void) {	/* Esce liberando quanto eventualmente allocato */

	if (Window) CloseWindow(Window);
	if (Screen) CloseScreen(Screen);
	if (UndoBuf) FreeMem(UndoBuf, 960);
	if (PieceBuf) FreeMem(PieceBuf, 382*19);
	if (PieceBitMap) FreeMem(PieceBitMap, sizeof(struct BitMap)*19);
	if (PieceRPort) FreeMem(PieceRPort, sizeof(struct RastPort)*19);
	if (SoundOpen) {
		WaitIO((struct IORequest *)&IOAudio);
		CloseDevice((struct IORequest *)&IOAudio);
   }
	if (IOAudio.ioa_Request.io_Message.mn_ReplyPort)
		DeletePort(IOAudio.ioa_Request.io_Message.mn_ReplyPort);
	if (GfxBase) CloseLibrary((struct Library *)GfxBase);
	if (IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
   _exit(FALSE);
}


void _main(void)
{
	int end = 0, i;

  	if (!WBenchMsg) Write(Output(), CLI_Help, strlen(CLI_Help));

	if ( (GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0)) == NULL) _exit();
	if ( (IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 0)) == NULL) FreeAndExit();


	if ( (UndoBuf = AllocMem(1280, MEMF_CLEAR | MEMF_CHIP)) == NULL ) FreeAndExit();
	if ( (PieceBuf = AllocMem(512*19, MEMF_CLEAR | MEMF_CHIP)) == NULL ) FreeAndExit();
	if ( (PieceBitMap = AllocMem(sizeof(struct BitMap)*19, MEMF_CLEAR | MEMF_PUBLIC)) == NULL ) FreeAndExit();
	if ( (PieceRPort = AllocMem(sizeof(struct RastPort)*19, MEMF_CLEAR | MEMF_PUBLIC)) == NULL ) FreeAndExit();

	if ( (Screen = OpenScreen(&NewScreen)) == NULL ) FreeAndExit();

	NewWindow.Screen = Screen;
	if ( (Window = OpenWindow(&NewWindow)) == NULL ) FreeAndExit();

	LoadRGB4(&Screen->ViewPort, Palette, 16);

	InitBitMap(&UndoBitMap, SCREEND, 64, 40);
	UndoBitMap.Planes[0] = (PLANEPTR)UndoBuf;
	UndoBitMap.Planes[1] = (PLANEPTR)&UndoBuf[160];
	UndoBitMap.Planes[2] = (PLANEPTR)&UndoBuf[320];
	UndoBitMap.Planes[3] = (PLANEPTR)&UndoBuf[480];

	InitRastPort(&UndoRPort);
	UndoRPort.BitMap = &UndoBitMap;

	for(i = 0; i<19; i++) {

		int j, k, xaligned;

		xaligned = ((J.Piece[i].x*SIDE)/16+1)*16;

		InitBitMap(&PieceBitMap[i], SCREEND, J.Piece[i].x*SIDE, J.Piece[i].y*SIDE);
		PieceBitMap[i].Planes[0] = (PLANEPTR)PieceBuf[i];
		PieceBitMap[i].Planes[1] = (PLANEPTR)&PieceBuf[i][(xaligned/16)*J.Piece[i].y*SIDE];
		PieceBitMap[i].Planes[2] = (PLANEPTR)&PieceBuf[i][(xaligned/16)*J.Piece[i].y*2*SIDE];
		PieceBitMap[i].Planes[3] = (PLANEPTR)&PieceBuf[i][(xaligned/16)*J.Piece[i].y*3*SIDE];

		InitRastPort(&PieceRPort[i]);
		PieceRPort[i].BitMap = &PieceBitMap[i];
		J.Piece[i].RPort = &PieceRPort[i];

		Square.PlaneOnOff = J.Piece[i].structure>>12;

		for(j = 0; j<J.Piece[i].y; j++)
			for(k = 0; k<J.Piece[i].x; k++)
				if ( (J.Piece[i].structure >> (j*J.Piece[i].x+k) ) & 1)
					DrawImage(&PieceRPort[i], &Square, k*SIDE, j*SIDE);
	}

	srand((unsigned int)&Menu); /* resetta il generatore di numeri casuali */
	SetMenuStrip(Window, &Menu);
	SetAPen(Window->RPort, 0);
	PrintIText(Window->RPort, &ScreenText1, 0, 0);
	DrawImage(Window->RPort, &SigmaImage, 0, 0);
	DrawGrid();
	ClipBlit(Window->RPort, PLAYFIELDX, PLAYFIELDY, Window->RPort, PLAYFIELDX, PLAYFIELDY-SIDE, SIDE*10, SIDE, 0xC0);

	DrawPiece(&J.S.Cube, 238, 54);
	DrawPiece(&J.S.BarHor, 228, 78);
	DrawPiece(&J.S.SHor, 233, 92);
	DrawPiece(&J.S.ZHor, 233, 116);
	DrawPiece(&J.S.LHorU, 233, 140);
	DrawPiece(&J.S.KHorU, 233, 164);
	DrawPiece(&J.S.THorU, 233, 188);

	IOAudio.ioa_Request.io_Message.mn_Node.ln_Pri = 10;
	if ((IOAudio.ioa_Request.io_Message.mn_ReplyPort = CreatePort("ATetris sound", 0)) == NULL ) FreeAndExit();
	IOAudio.ioa_Data = AllocTries;
	IOAudio.ioa_Length = sizeof(AllocTries);
	if (!OpenDevice(AUDIONAME, 0, (struct IORequest *)&IOAudio, 0)) {
		SoundOpen = SoundOn = 1;
  		IOAudio.ioa_Request.io_Command = CMD_WRITE;
		IOAudio.ioa_Request.io_Flags = ADIOF_PERVOL;
		IOAudio.ioa_Data = sound;
		IOAudio.ioa_Length = sizeof(sound);
		IOAudio.ioa_Period = 400;
		IOAudio.ioa_Cycles = 3;
		BeginIO((struct IORequest *)&IOAudio);
		IOAudio.ioa_Volume = 16;
	}

	do {
		Wait(1 << Window->UserPort->mp_SigBit);

		while(TmpMsg = GetMsg(Window->UserPort)) {

			Message = *((struct IntuiMessage *)TmpMsg);
			ReplyMsg(TmpMsg);

			switch(Message.Class) {
				case MENUPICK:		end = ParseMenu(Message.Code);
 										break;
				case VANILLAKEY:	if (!GameOver) ParseKey(Message.Code);
										break;
				case INTUITICKS:	if ((!GameOver) && (!Pause)) IncrementTicks(FALSE);
										break;
				default:				break;
			}
		}
	} while(!end);

	FreeAndExit();
}
