/*-------------------------------------------------------------------------*/
/* Snake 'N Snack (c) Copyright 1989    by Mark E. Whitehead  02 July 89   */
/*                                                                         */
/* This is an original arcade game for the Commodore Amiga (TM) computer.  */
/* It features 8 color graphics, digitized sound, fade i/o, intuition i/o  */
/* multiple levels of skill, multiple levels of play, scorekeeping, etc.   */
/*                      						   */
/* This program may be freely distributed so long as it is not for profit! */
/* Thanks go to Electronic Arts for DPaint II and to those PD hackers who  */
/* gave me something to start with.  Long live Amiga!  MEW                 */


#include "exec/types.h"
#include "exec/memory.h"
#include "intuition/intuition.h"
#include "graphics/gfx.h"
#include "exec/devices.h"
#include "devices/gameport.h"
#include "devices/inputevent.h"
#include "hardware/custom.h"
#include "hardware/dmabits.h"
#include "libraries/dos.h"
#include "devices/audio.h"
#include "stdio.h"

#define	SPACE		0
#define	BODY		1	
#define	HEAD0		2
#define	HEAD1		3
#define	HEAD2		4
#define	HEAD3		5
#define	WALL		6
#define	APPLE		7
#define	ORANGE		8
#define	BANANA		9
#define	STRAWB		10
#define	LEMON		11
#define	LIME		12
#define	CHERRY		13
#define	BLANK		14

#define	UP		0
#define	RIGHT		1
#define	DOWN		2
#define	LEFT		3

#define	TOP		0
#define	RSIDE		39
#define	BOTTOM		23
#define	LSIDE		0

#define	PEN_BLACK	0
#define	PEN_WHITE	1	
#define	PEN_ORANGE	2
#define	PEN_RED		3
#define	PEN_YELLOW	4
#define	PEN_LGRN	5
#define	PEN_MGRN	6
#define	PEN_DGRN	7

#define	DURATION	100	
#define	BLINK1		9
#define	BLINK2		6
#define	BLINK3		3
#define	BLINKOFF	0

#define	ever		(;;)
#define	REV		0L
#define ABS(x)		((x) < 0 ? -(x) : (x))
#define	HLIM(x)		(x) < LSIDE ? RSIDE : (x) > RSIDE ? LSIDE : (x)
#define	VLIM(y)		(y) < TOP  ? BOTTOM : (y) > BOTTOM ? TOP  : (y)
#define SCRNWIDTH	640
#define SCRNHEIGHT	200
#define SCRNDEPTH	3
#define	N_HBLK		40
#define N_VBLK		24
#define BLK_W		16
#define	BLK_H		8
#define	BLK_D		3

#define OUT		0
#define IN		1

#define	REV 		0L

extern	void	*OpenLibrary(), *GetMsg(), *OpenWindow(), *AllocMem(),
		*OpenScreen(),  *OpenDevice(), *CreatePort();

void	*GfxBase, *IntuitionBase;

/****** Image definitions for the blocks *******/

extern	UWORD head0[], head1[], head2[], head3[], body[],
	      wall[], apple[], orange[], banana[], strawberry[],
	      lemon[], lime[], cherry[], blank[];

struct	Window	 *win;
struct	ViewPort *vp;
struct	RastPort *rp;
struct	Screen	 *scrn;

struct NewWindow windef = {
	0, 2, SCRNWIDTH, 10,
	0, 1,
	CLOSEWINDOW | VANILLAKEY,
	WINDOWCLOSE | BORDERLESS | ACTIVATE,
	NULL, NULL,
	NULL,
	NULL, NULL,
	0, 0, 0, 0,
	CUSTOMSCREEN
};

struct	NewScreen scrndef = {
	0, 0, SCRNWIDTH, SCRNHEIGHT,
	SCRNDEPTH,
	1, 0,
	HIRES, 
	CUSTOMSCREEN,
	NULL, 
	NULL,
        NULL, NULL
};


#define	NUMCOLORS	8

UWORD	cm[] = {
	0x0000, 0x0eca, 0x0e50, 0x0c00,	/* Black, White, Orange, Red   */
	0x0ea0, 0x00c0, 0x0080, 0x0040 	/* Yellow, L.Grn, M.Grn, D.Grn */
};

UWORD	cvm[] = {
	0x0000, 0x0ec0, 0x0e50, 0x0c00,
	0x0ea0, 0x00c0, 0x0080, 0x0040 
};

struct	Image block = {0, 0, BLK_W, BLK_H, BLK_D, NULL, 0x0007, 0x0000, NULL};

struct	IntuiMessage *msg = NULL;
short	pause = FALSE; sound = TRUE, refresh;

UWORD	score = 0, highscore = 0, totalscore = 0, level;
short	dir, x, y, crash, xold, yold, xend, yend, endgame=FALSE;
short	blink = FALSE, timer = DURATION, flag = FALSE, speed = 5;
short	map[N_VBLK][N_HBLK];
short	gotonext = FALSE, menuselect = FALSE, levelselect = FALSE;

/*--------------------- A U D I O  S T U F F -----------------------------*/

#define	MOVESND		0
#define	JUMPSND		1
#define	EXITSND		2
#define	WONKSND		3
#define	SPINSND		4
#define	REVSND		5
#define	ORIENTSND	6

struct	MsgPort	*port = 0;
struct	Device	*device = 0;
struct	IOAudio	audio;

#define	NMAXSAMP 7
short	n_samples = 0;

struct	Sample { UBYTE	*data;
		 ULONG	length;
		 UWORD 	period,
			volume;
		} samp[NMAXSAMP];

struct	IOAudio	snd;

/*--------------------- I N S T R  S T U F F -----------------------------*/

char	*instr1[] = {
"This is a game of skill and strategy in which you control the direction of",
"the head of an ever-growing snake.  You must avoid running into walls and",
"your own body. Many magical fruits are scattered around which can make you",
"jump, turn, hype, reverse, and temporarily make you invincible!  The object",
"of the game is to get AT LEAST 500 points per level and then exit by eating",
"a cherry.  (Cherries make good desserts - so save at least one for last!)"
};

char	*instr2[] = {
"You control the DIRECTION of movement with the numeric keypad:",
"        LEFT - '4'    RIGHT - '6'   UP - '8'  DOWN - '5'",
"SPEED is controlled with the numbers '0' - '3'.  These keys as well as the",
"pause on/off, sound on/off, and quit keys are always active during play."
};
  
char	*instr3[] = {
"   YOU!", "0 Points", " 1 Point", "5 Points", "10 Points", 
"25 Points", "50 Points", "75 Points", "100 Points", "125 Points"
};

/*------------------- M A I N ---- R O U T I N E -------------------------*/

main()
{
	char	*itos();

	openstuff();
	fade(OUT);
	audiostuff();
	initgame();	

	for ever {
		if (msg = GetMsg(win->UserPort) ) handlemsg();
		if (endgame) break;
		if (!pause) {
			WaitTOF();
			movehead();
			sound_fx(MOVESND);
			collcheck();
			countdown();
			print_rp( 418, 192, itos(score), PEN_WHITE);
			if (crash) {
				spinhead();
				Delay(50L);
				initgame();
			}
			Delay( (long) speed);
			if (gotonext) nextlevel();
		}
	}
	fade(OUT);
	closestuff();
}


/*-------------------------------------------------------------------------*/
/* Handle keyboard commands */

handlemsg()
{
	UBYTE	key;

	switch (msg->Class) {
		case CLOSEWINDOW: ReplyMsg(msg);
				  closestuff();
	  			  exit();
		case VANILLAKEY:  key = msg->Code;
				  ReplyMsg(msg);
				  if (key > '9') sound_fx(WONKSND);
				  switch(key) {
				  case '8': dir = UP;
					    break;
				  case '6': dir = RIGHT;
					    break;
				  case '5': dir = DOWN;
					    break;
				  case '4': dir = LEFT;
					    break;
				  case ' ':
				  case 'P': case 'p':
					pause = !pause;
					break;
				  case 'S': case 's':
					sound = !sound;
					break;
				  case 'Q': case 'q':
					closestuff();
					exit();
				  case 'B': case 'b':
					menuselect = FALSE;
					break;
				  case 'H': case 'h':
					if (menuselect)
						howtoplay();
					break;
				  case '0':
					speed = 15;
					break;
				  case '1':
					speed = 10;
					break;
				  case '2':
					speed = 5;
					break;
				  case '3':
					speed = 2;
					break;
				  }
				  break;
		default:	  ReplyMsg(msg);
	}
}


/*-------------------------------------------------------------------------*/
/* Initialize the game  */

initgame()
{
	if (totalscore > highscore) highscore = totalscore; 
	showtitle();
	totalscore = 0;
	score = 0;
	level = 0;	
	nextlevel();
}


/*-------------------------------------------------------------------------*/
/* Read in some samples and store the vitals in the samp structure. Make   */
/* sure that n_samples is correct 'cause it is used in closestuff() for    */
/* memory deallocation.							   */

audiostuff()
{
	init_audio();		/* Setup the audio device and structure */
	snd = audio;		/* Copy audio struct for each sampled sound */

	read_sample( "move.ss",    &samp[0]);
	read_sample( "jump.ss",    &samp[1]);
	read_sample( "exit.ss",    &samp[2]);
	read_sample( "wonk.ss",    &samp[3]);
	read_sample( "spin.ss",    &samp[4]);
	read_sample( "reverse.ss", &samp[5]);
	read_sample( "orient.ss",  &samp[6]);

	n_samples = 7;

	samp[MOVESND].volume = 16;	/* Doctor up sound a bit */
	samp[SPINSND].volume = 50;
}


/*-------------------------------------------------------------------------*/
/* Let's hear some sound effects! */

sound_fx( which )
short	which;
{
	if (!samp[which].data) return;	/* No sounds loaded in! */
	
	AbortIO(&snd);		/* Bag any active sound */

	if (!sound) return;	/* User requests no sound */
	
	if (which >= n_samples) which = 0;

	setup_sample(&snd, &samp[which]);

	BeginIO(&snd);		/* This actually requests sound */
}


/*-------------------------------------------------------------------------*/
/* Set up the screen to start off the next level */

nextlevel()
{
	char	*itos();

	x = 20; y = 10;
	xold = x; yold = y;
	xend = x; yend = y;
	
	dir = 1; crash = FALSE; timer = DURATION; flag = FALSE; blink = FALSE;
 	gotonext = FALSE;
	
	clearmap();
	fade(OUT);

	SetRast( rp, 0L);
	scatter( WALL,  5 + 5*level);
	scatter( APPLE,  8);
	scatter( ORANGE, 5);
	scatter( BANANA, 5);
	scatter( STRAWB, 4);
	scatter( LEMON,  4);
	scatter( LIME,   3);
	scatter( CHERRY, 2);

	print_rp( 0, 192, "SNAKE 'N SNACK", PEN_LGRN);
	print_rp( 144, 192, "By Mark E. Whitehead 1989", PEN_ORANGE);
	print_rp( 362, 192,"Score:        Total Score:", PEN_YELLOW);

	totalscore += score;
	score = 0;
	level++;

	print_rp( 418, 192, itos(score), PEN_WHITE);
	print_rp( 578, 192, itos(totalscore), PEN_WHITE);
	
	fade(IN);
	getoriented();
}


/*-------------------------------------------------------------------------*/
/* Clear out the map */

clearmap()
{
	short i, j;

	for (j=0; j<N_VBLK; j++)
		for (i=0; i<N_HBLK; i++)
			map[j][i] = SPACE;
}


/*-------------------------------------------------------------------------*/
/* Move head around */

movehead()
{
	xold = x; yold = y;

	switch (dir) {
		case UP:   y--; break;
		case RIGHT:x++; break;
		case DOWN: y++; break;
		case LEFT: x--; break;
	}
	x = HLIM(x);
	y = VLIM(y);
}
	

/*-------------------------------------------------------------------------*/
/* Check for collision - followed by various collision handlers */

collcheck()
{
	UWORD	*image;
	UWORD	points = 0;

	drawblock( &body, xold*BLK_W, yold*BLK_H);

	switch ( map[y][x] ) {
		case SPACE:  points = 1; break;
		case BODY:   if (!flag) { crash = TRUE; return;} else break;
		case WALL:   crash = TRUE; return;
		case APPLE:  points = 5;   twist(); break;
		case ORANGE: points = 10;  reverse(); break;
		case BANANA: points = 25;  hype(); break;
		case STRAWB: points = 50;  jump(); break;
		case LEMON : points = 75;  mystery(); break;
		case LIME:   points = 100; flag = TRUE; break;
		case CHERRY: points = 125; exitcheck(); break;
	}	

	if (!crash) {
		switch (dir) {
			case UP:   image = head0; break;
			case RIGHT:image = head1; break;
			case DOWN: image = head2; break;
			case LEFT: image = head3; break;
		}
		if (blink) {
			block.PlanePick = 0x0000;
			block.PlaneOnOff = 0x0004;
			drawblock( image, x*BLK_W, y*BLK_H);
			block.PlanePick = 0x0007;
			block.PlaneOnOff = 0x0000;
		} else
			drawblock( image, x*BLK_W, y*BLK_H);

	}
	score += points;
	map[y][x] = BODY;
}

twist()
{
	sound_fx(REVSND);
	switch( RangeRand(3L) ) {
		case 0: break;
		case 1: dir++; break;
		case 2: dir++; break;
		default: break;
	}
	if (dir>3) dir = 0; else if (dir<0) dir = 3;
	map[y][x] = BODY;
}

reverse()
{
	short	tx, ty;

	sound_fx(REVSND);
	map[y][x] = BODY;
	tx = x; ty = y;
	x = xend; y = yend;
	xend = tx; yend = ty;
	drawblock( &body, xend*BLK_W, yend*BLK_H);
	getoriented();
}
	
hype()
{
	sound_fx(JUMPSND);
	drawblock( &body, x*BLK_W, y*BLK_H);
	map[y][x] = BODY;

	x = RangeRand(39L);
	y = RangeRand(22L);
	
	while ( map[y][x] != SPACE) {
		x = RangeRand(39L);
		y = RangeRand(22L);
	}

	xend = x; yend = y;
	getoriented();
}

jump()
{
	sound_fx(JUMPSND);
	drawblock( &body, x*BLK_W, y*BLK_H);
	map[y][x] = BODY;
	
	xold = x;
	yold = y;

	switch (dir) {
		case UP:   y-=2; break;
		case RIGHT:x+=2; break;
		case DOWN: y+=2; break;
		case LEFT: x-=2; break;
	}
	x = HLIM(x);
	y = VLIM(y);

	collcheck();			/* May have jumped to another fruit */
	if (crash) {			/* May have jumped into a wall or a */
		x = xold;		/* body segment.  If so, move the   */
		y = yold;		/* head toward the block and limit  */
		switch (dir) {
			case UP:   y--; break;
			case RIGHT:x++; break;
			case DOWN: y++; break;
			case LEFT: x--; break;
		}
		x = HLIM(x); 
		y = VLIM(y);
		if (map[y][x] != BODY && map[y][x] != WALL) {
			xold = x;	/* Block occurred at jump dest.  */
			yold = y;	/* and directly in front of head */
		}
	}
}		

mystery()
{
	sound_fx(WONKSND);
	map[y][x] = BODY;
	switch (RangeRand(8L)) {
		case 0: jump(); break;
		case 1: hype(); break;
		case 2: reverse(); break;
		case 3: twist(); break;
		default: break;
	}
}
		

exitcheck()
{
	if (score > 500) {
		sound_fx(EXITSND);
		gotonext = TRUE;
	} else
		sound_fx(REVSND);
}


/*-------------------------------------------------------------------------*/
/* Count down routine */

countdown()
{
	if (flag) {
		timer--;
		blink = FALSE;
		switch (timer) {
			case BLINK1:
			case BLINK2:
			case BLINK3: sound_fx(ORIENTSND);blink = TRUE; break;
			case BLINKOFF: flag = FALSE; timer = DURATION; break;
		}
	}
}


/*-------------------------------------------------------------------------*/
/* Let the user get re-oriented after a reversal, hype or new screen       */

getoriented()
{
	short	i;
	UWORD	*image;
	UBYTE	key;	

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

		if (msg = GetMsg(win->UserPort) ) handlemsg();

		switch (dir) {
			case UP:    image = head0; break;
			case RIGHT: image = head1; break;
			case DOWN:  image = head2; break;
			case LEFT:  image = head3; break;
		}
		drawblock( image, x*BLK_W, y*BLK_H);
		Delay(7L);
		sound_fx(ORIENTSND);
		drawblock( &blank, x*BLK_W, y*BLK_H);
		Delay(7L);
	}
}
	
/*-------------------------------------------------------------------------*/
/* Spin head around after crash */

spinhead()
{
	short	i, spin;
	UWORD	*image;

	if ( (spin = ++dir) > 3 ) spin = 3;

	for (i=0; i<16; i++) {
		switch (spin) {
			case UP:    image = head0; break;
			case RIGHT: image = head1; break;
			case DOWN:  image = head2; break;
			case LEFT:  image = head3; break;
		}
		sound_fx(SPINSND);
		drawblock( image, xold*BLK_W, yold*BLK_H);
		spin++;
		if (spin > 3) spin = 0;
		Delay(5L);
	}
}


/*-----------------------------------------------------------------------*/
/* Convert UWORD into a fixed length string of chars */

#define	NDIGITS		5
#define	TEN_EXP_NDIGITS	10000
char  	 scorebuf[NDIGITS+1];
	
char	*itos(number)
UWORD	number;
{
	short	i;
	UWORD 	f;
	register char	*loc;

	loc = scorebuf;

	if (number > 65000) number = 65000;
	
	for (i=0, f=TEN_EXP_NDIGITS; i<NDIGITS; i++, f=f/10)
		*loc++ = (number/f) % 10 + '0';

	scorebuf[NDIGITS] = NULL;
	return(scorebuf);
}


/*-------------------------------------------------------------------------*/
/* Scatter images throughout screen at Random */

scatter( shape, count)
short	shape, count;
{
	short	x, y, i;
	UWORD	*image;

	for (i=0; i<count; i++) {
		x = RangeRand(39L);
		y = RangeRand(22L);
		map[y][x] = shape;
		switch (shape) {
			case SPACE: return;
			case BODY:  image = body; break;
			case HEAD0: image = head0; break;
			case HEAD1: image = head1; break;
			case HEAD2: image = head2; break;
			case HEAD3: image = head3; break;
			case WALL:  image = wall; break;
			case APPLE: image = apple; break;
			case ORANGE:image = orange; break;
			case BANANA:image = banana; break;
			case STRAWB:image = strawberry; break;
			case LEMON :image = lemon; break;
			case LIME:  image = lime; break;
			case CHERRY:image = cherry; break;
			default: image = APPLE;
		}
		drawblock( image, x*BLK_W, y*BLK_H);
	}
}
	
		
/*------------------------------------------------------------------------*/
/* Use a generic Image structure called block for all graphics - easy!    */

drawblock( p, x, y)
UWORD	*p;
short	x, y;
{
	block.LeftEdge = x;
	block.TopEdge  = y;
	block.ImageData  = p;
	DrawImage(rp, &block, 0L, 0L);
}


/*-----------------------------------------------------------------------*/
/* Show the title and get user game options */

showtitle()
{
	UBYTE	key;
	short	bx, by, i;

	menuselect = TRUE; refresh = TRUE;

	while (menuselect) {
		if (refresh) {
		fade(OUT);
		SetRast(rp, 0L);

		for (i=0, bx=0; i<N_HBLK; i++, bx+=BLK_W) {
			drawblock( &body, bx, 0);
			drawblock( &body, bx, BLK_H*N_VBLK);
		}

		for (i=0, by=0; i<N_VBLK; i++, by+=BLK_H) {
			drawblock( &body, 0, by);
			drawblock( &body, BLK_W*(N_HBLK-1), by);
		}

		print_rp( 264, 30, "SNAKE 'N SNACK", PEN_LGRN);		
		print_rp( 268, 55,  "Game Options:", PEN_WHITE);
		print_rp( 250, 70,  "H)ow To Play", PEN_ORANGE);
		print_rp( 250, 80,  "B)egin Game", PEN_ORANGE);
		print_rp( 250, 90,  "S)ound On/Off", PEN_ORANGE);
		print_rp( 250, 100, "P)ause Game (or space bar)", PEN_ORANGE);
		print_rp( 250, 110, "Q)uit Game", PEN_ORANGE);
		print_rp( 340, 150, itos(highscore), PEN_MGRN);
		print_rp( 250, 150, "High Score:", PEN_MGRN);
		print_rp( 264, 170, "CopyFree 1989", PEN_YELLOW);
		print_rp( 140, 180, "By Mark E. Whitehead - Public Domain GiftWare", PEN_YELLOW);
		}
		refresh = FALSE;
		fade(IN);
		if (msg = GetMsg(win->UserPort) ) handlemsg();
	}
}


/*------------------------------------------------------------------------*/
/* Tell ya how ta play the game... */

howtoplay()
{
	short	i, x, y, frame;

	fade(OUT);
	SetRast( rp, 0L);	/* First clear screen */

	print_rp( 200, 0, "* How to play SNAKE 'N SNACK *", PEN_WHITE);
	
	for (i=0, x=10, y=20; i<6; i++, y+=10)
		print_rp( x, y, instr1[i], PEN_YELLOW); 

	x = 0; y = 90;
	drawblock( &head3, x+=100, y);
	drawblock( &wall, x+=100, y);
	drawblock( &body, x+=100, y);
	drawblock( &apple, x+=100, y);
	drawblock( &orange, x+=100, y);

	y+=BLK_H+2;
	for (i=0, x=70; i<5; i++, x+=100)
		print_rp( x, y, instr3[i], PEN_LGRN);

	x = 0; y = 110;
	drawblock( &banana, x+=100, y);
	drawblock( &strawberry, x+=100, y);
	drawblock( &lemon, x+=100, y);
	drawblock( &lime, x+=100, y);
	drawblock( &cherry, x+=100, y);

	y+=BLK_H+2;
	for (i=5, x=70; i<10; i++, x+=100)
		print_rp( x, y, instr3[i], PEN_LGRN);

	for (i=0, x=10, y=140; i<4; i++, y+=10)
		print_rp( x, y, instr2[i], PEN_YELLOW); 

	print_rp( 228, 190, "<HIT ANY KEY TO RETURN>", PEN_WHITE);
	
	fade(IN);
	for ever 
		if (msg = GetMsg(win->UserPort) )
			switch (msg->Class) {
				case VANILLAKEY: ReplyMsg(msg);
						 refresh = TRUE;
						 return();
				default:	 break;
			}
}	


/*------------------------------------------------------------------------*/
/* Create and print to rastport a text string */

print_rp( left, top, mes, color)
short	left, top, color;
UBYTE	*mes;
{
	struct	IntuiText x;

	x.FrontPen = color;
	x.BackPen = 0;
	x.DrawMode = JAM2;
	x.LeftEdge = left;
	x.TopEdge = top;
	x.ITextFont = NULL;
	x.IText = mes;
	x.NextText = NULL;

	PrintIText( rp, &x, 0L, 0L);
}


/*------------------------------------------------------------------------*/
/* Fade OUT by passing flag = 0 and Fade IN by passing flag = 1           */
/* A color map 'cm' must be defined as the reference.  a color view map   */
/* 'cvm' is actually what is seen by the user.  A delay of 1L or 2L is    */
/* just about right for smooth transitions between graphics setups.       */

fade( flag)
short	flag;
{
	short	i, j, r, g, b, rv, gv, bv;

	for (j=0; j<16; j++) {
		for (i=0; i<NUMCOLORS; i++) {
			rv = (cvm[i] & 0x0f00) >>8;
			gv = (cvm[i] & 0x00f0) >>4;
			bv = (cvm[i] & 0x000f); 
			
			if (flag) {	/* Fade in */

				r  = (cm[i] & 0x0f00) >>8;
				g  = (cm[i] & 0x00f0) >>4;
				b  = (cm[i] & 0x000f); 

				if (++rv > r) rv = r;
				if (++gv > g) gv = g;
				if (++bv > b) bv = b;
				
			} else {	/* Fade out */

				if (--rv < 0) rv = 0;
				if (--gv < 0) gv = 0;
				if (--bv < 0) bv = 0;

			}
			cvm[i] = rv<<8 | gv<<4 | bv;
		}
		LoadRGB4( vp, &cvm[0], (long) NUMCOLORS);
		Delay( 1L);
	}
}


/*------------------------------------------------------------------------*/
/* Initialize the audio device and audio structure.  Note that the global */
/* variable 'port' and 'audio' are assigned - these will be necessary     */
/* later for cleanup.  This function opens up a reply port for the audio  */
/* device, sets up priority and channels requested, then opens the audio  */
/* device itself.  If anything goes bad we exit via the 'die' routine.    */

init_audio()
{
	UBYTE	sunit=0x0f;	/* All four channels requested - why not? */

	if (!(port = CreatePort("p1",0L)))
		die("Can't create sound port.");

	audio.ioa_Request.io_Message.mn_ReplyPort = port;
	audio.ioa_Request.io_Message.mn_Node.ln_Pri = 10;
	audio.ioa_Data = &sunit;
	audio.ioa_Length = (ULONG) sizeof(sunit);

	if( (OpenDevice(AUDIONAME,0L,&audio,0L)) )
		die("Can't open audio device.");

	device = audio.ioa_Request.io_Device;
}


/*-------------------------------------------------------------------------*/
/* Setup a sampled sound specifying: volume, number of cycles, period, etc.*/

setup_sample(chnl, sample)
struct	IOAudio	*chnl;
struct	Sample	*sample;
{
	chnl -> ioa_Request.io_Command = CMD_WRITE;
	chnl -> ioa_Request.io_Flags   = ADIOF_PERVOL | IOF_QUICK;
	chnl -> ioa_Data   = sample -> data;
	chnl -> ioa_Cycles = 1;
	chnl -> ioa_Length = sample -> length;
	chnl -> ioa_Period = sample -> period;
	chnl -> ioa_Volume = sample -> volume;
}


/*------------------------------------------------------------------------*/
/* This function reads in a particular kind of 8SVX sampled sound.  Just  */
/* pass it file name and a Sample structure and it'll fill in the fields. */

read_sample( name, sample)
UBYTE	*name;
struct	Sample	*sample;
{
	FILE	*fp, *fopen();
	UBYTE	c, c0, c1, c2, c3, *count, *samp_data;
	void	*AllocMem();
	UWORD	i, length, rate, period;

	if ( (fp = fopen( name, "r")) == NULL ) {
		sample -> data = NULL;
		return();
	}	

	for (i=0; i<136; i++) {
		c = getc(fp);
		switch (i) {
			case 32 : c0 = c; break;
			case 33 : c1 = c; break;
			case 134: c2 = c; break;
			case 135: c3 = c; break;
			default : break;
		}
	}

	rate   = (UWORD) c0 << 8 | (UWORD) c1;
	length = (UWORD) c2 << 8 | (UWORD) c3;

	if (!(samp_data = AllocMem( (long) length, MEMF_CHIP)))
		die("Memory allocation error.");

	count = samp_data;
	for ( i = 0; i < length; i++)		/* Read sound data */ 
		*count++ = getc(fp);		/* into buffer     */

	fclose(fp);

	/* Convert samples per second to amiga hardware period value */

	period = (35795465L/rate)/10;

	/* Now assign parameters to the appropriate sample fields */

	sample -> data   = samp_data;
	sample -> length = length;
	sample -> period = period;
	sample -> volume = 64;

}


/*------------------------------------------------------------------------*/
/* Open devices, libraries, ports, you name it we do it! */

openstuff()
{
	if (!(IntuitionBase = OpenLibrary ("intuition.library", REV)))
		die ("Intuition doesn't 'open up' to me.");

	if (!(GfxBase = OpenLibrary ("graphics.library", REV)))
		die ("Art shop closed.");

	if (!(scrn = OpenScreen (&scrndef)))
		die ("Screen painted shut.");

	windef.Screen = scrn;

	if (!(win = OpenWindow( &windef)))
		die ("Window stuck tight.");

	rp = &(scrn -> RastPort);
	vp = &(scrn -> ViewPort);

	LoadRGB4(vp, &cm[0], 8L);
}


/*------------------------------------------------------------------------*/
/* Close devices, libraries, ports, you name it we dump it! */

closestuff()
{
	short	i;

	AbortIO(&snd);

	if (win)
		CloseWindow(win);
	if (scrn)
		CloseScreen(scrn);
	if (GfxBase)
		CloseLibrary (GfxBase);
	if (IntuitionBase)
		CloseLibrary (IntuitionBase);
	if (device)
		CloseDevice(&audio);
	if (port)
		DeletePort(port);
	for (i=0; i<n_samples; i++)
		if (samp[i].data != NULL)
			FreeMem( samp[i].data, (long) samp[i].length);

}


/*------------------------------------------------------------------------*/
/* Put up an error message on your way out.  Come again soon. */

die(str)
char *str;
{
	if (win) {
		SetWindowTitles(win, str, -1L);
		WaitPort(win->UserPort);
	} else
		puts(str);
	closestuff();
	exit (100);
}
