/*******************************************************************************
 *
 * (c) Copyright 1993 Commodore-Amiga, Inc.  All rights reserved.
 *
 * This software is provided as-is and is subject to change; no warranties
 * are made.  All use is at your own risk.  No liability or responsibility
 * is assumed.
 *
 * PlayRaw - the main code of the Animation demo. Uses lots of neat AA features
 * and V39 functions.
 *
 ******************************************************************************/

#define LIBVERSION 39

/*******************************************************************/
/*             TO DISABLE CTRL-C HANDLING, SET NO_CTRL_C TO 1      */
/*******************************************************************/

#define NO_CTRL_C 1
#if NO_CTRL_C == 1
int CXBRK (void) { return (0) ; }	/* disable ctrl-c */
#endif

#include <exec/types.h>
#include <exec/exec.h>
#include <exec/execbase.h>
#include <proto/exec.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <proto/dos.h>
#include <intuition/screens.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <proto/intuition.h>
#include <graphics/gfx.h>
#include <graphics/gfxbase.h>
#include <graphics/displayinfo.h>
#include <graphics/copper.h>
#include <graphics/gfxmacros.h>
#include <graphics/videocontrol.h>
#include <graphics/sprite.h>
#include <proto/graphics.h>
#include <libraries/iffparse.h>
#include <proto/iffparse.h>
#include <hardware/custom.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <devices/audio.h>

#include <stdio.h>
#include <stdlib.h>

#include "playanim.h"
#include "asyncio.h"
#include "sprites.h"
#include "stick.h"

/*********************************************************************/
/*                            GLOBAL VARIABLES                       */
/*********************************************************************/

struct IntuitionBase *IntuitionBase = NULL ;
struct GfxBase *GfxBase = NULL ;
struct Library *IFFParseBase = NULL;
extern struct ExecBase *SysBase;

/* This code uses a modified version of Martin Taillefer's AsyncIO routines.
 * Here, we define the DOS functions as macros, so that we can use either normal
 * DOS or AsyncIO routines at the flick of one compiler option.
 *
 * See asyncio.c and AmigaMail Vol. 2 Section 2 P 77 for more details about
 * AsyncIO.
 */

#define ASYNCIO
#ifdef ASYNCIO
#define OPEN(f, m, b) OpenAsync((f), (((m) == MODE_READ) ? MODE_READ : MODE_WRITE), (b))
#define CLOSEFILE(f) CloseAsync((f))
#define READ(f, b, s) ReadAsync((f), (b), (s))
#define WRITE(f, b, s) WriteAsync((f), (b), (s))
#define SEEK(f, s) SeekAsync((f), (b), MODE_START)
typedef struct AsyncFile *FILETYPE;
#else
#define OPEN(f, m, b) Open(f, (((m) == MODE_READ) ? MODE_OLDFILE : MODE_NEWFILE))
#define CLOSEFILE(f) Close((f))
#define READ(f, b, s) Read((f), rbuff, (s))
#define WRITE(f, b, s) Write((f), (b), (s))
#define SEEK(f, s) Seek((f), (s), OFFSET_BEGINNING)
typedef BPTR FILETYPE;
#endif

FILETYPE InFile = NULL;

struct RDArgs *rdargs = NULL;
struct Screen *screen = NULL;
struct Screen *cockpitscreen = NULL;
struct BitMap *bmbuff[] = {NULL, NULL, NULL, NULL};	/* double buffers */
UWORD *table = NULL;
UBYTE *inbuff = NULL;
UBYTE *rbuff;
APTR XHair = NULL;
struct UCopList *ucl = NULL;
struct Window *window =  NULL;
struct Custom *custom = (struct Custom *)0xdff000;
struct ExtSprite *Sprites[SPRITE_COUNT] = {NULL};
struct Missile Missiles[MISSILE_COUNT] = {NULL};

/* These are for the inputevent from the joystick */
struct IOStdReq *InputIO = NULL;
struct MsgPort *InputMP = NULL;
struct InputEvent *FakeEvent = NULL;
struct IEPointerPixel *NewPixel = NULL;
BOOL InputOpened = FALSE;
struct JoyStick stick;

/* These are all for the Audio device */
struct IOAudio *FireAudioIO = NULL;
struct MsgPort *FireAudioMP = NULL;
UBYTE *FireAudioBuff = NULL;
BOOL FireAudioOpened = FALSE;
struct RawAudio FireRawAudio;
struct IOAudio *ThrustAudioIO[] = {NULL, NULL};
struct MsgPort *ThrustAudioMP[] = {NULL, NULL};
UBYTE *ThrustAudioBuff = NULL;
BOOL ThrustAudioOpened[] = {FALSE, FALSE};
struct RawAudio ThrustRawAudio;

/**********************************************************************/
/*                                                                    */
/* void Error (char *String)                                          */
/* Print string and exit                                              */
/*                                                                    */
/**********************************************************************/

void Error (char *String)
{
    void CloseAll(void);
	
    printf(String);

    CloseAll();
    exit(0);
}


/**********************************************************************/
/*                                                                    */
/* void Init ()                                                       */
/*                                                                    */
/* Opens all the required libraries                                   */
/* allocates all memory, etc.                                         */
/*                                                                    */
/**********************************************************************/

APTR InitXHair(void);
BOOL InitMissiles(struct Missile *Missile, struct ExtSprite *Sprites[]);
ULONG InitAudio(struct IOAudio *FireAudioIO, struct IOAudio *ThrustAudioIO[]);

void Init (void)
{
    /* Open the intuition library.... */
    if ((IntuitionBase = (struct IntuitionBase *)OpenLibrary ("intuition.library", LIBVERSION)) == NULL)
    {
	Error ("Could not open the Intuition.library") ;
    }

    /* Open the graphics library.... */
    if ((GfxBase = (struct GfxBase *)OpenLibrary ("graphics.library", LIBVERSION)) == NULL)
    {
	Error ("Could not open the Graphics.library") ;
    }

    /* open IFFParse library. V37 of IFFParse is sufficient for our needs. */
    if ((IFFParseBase = OpenLibrary("iffparse.library", 37L)) == NULL)
    {
	Error("Could not open the iffparse.library");
    }

    if (InitMissiles(&Missiles[0], &Sprites[0]) == NULL)
    {
    	Error("Missile allocation error\n");
    }

    /* get everything we need for our input event */
    if (!((InputMP = CreateMsgPort()) &&
          (FakeEvent = AllocMem(sizeof(struct InputEvent), MEMF_PUBLIC)) &&
          (NewPixel = AllocMem(sizeof(struct IEPointerPixel), MEMF_PUBLIC)) &&
          (InputIO = CreateIORequest(InputMP, sizeof(struct IOStdReq))) &&
          (InputOpened = ((OpenDevice("input.device", NULL, (struct IORequest *)InputIO, NULL)) == NULL))))
    {
	Error("Error with the input device\n");
    }

    /* Get everything we need for the Fire audio */
    if (!((FireAudioMP = CreateMsgPort()) &&
          (FireAudioIO = CreateIORequest(FireAudioMP, sizeof(struct IOAudio))) &&
          (FireAudioOpened = ((OpenDevice("audio.device", NULL, (struct IORequest *)FireAudioIO, NULL)) == NULL))))
    {
	Error("Could not get fire audio\n");
    }
    /* Get everything we need for the Thrust audio */
    if (!((ThrustAudioMP[0] = CreateMsgPort()) &&
          (ThrustAudioIO[0] = CreateIORequest(ThrustAudioMP[0], sizeof(struct IOAudio))) &&
          (ThrustAudioOpened[0] = ((OpenDevice("audio.device", NULL, (struct IORequest *)ThrustAudioIO[0], NULL)) == NULL))))
    {
	Error("Could not get thrust audio 0\n");
    }
    if (!((ThrustAudioMP[1] = CreateMsgPort()) &&
          (ThrustAudioIO[1] = CreateIORequest(ThrustAudioMP[1], sizeof(struct IOAudio))) &&
          (ThrustAudioOpened[1] = ((OpenDevice("audio.device", NULL, (struct IORequest *)ThrustAudioIO[1], NULL)) == NULL))))
    {
	Error("Could not get thrust audio 1\n");
    }
    if (InitAudio(FireAudioIO, ThrustAudioIO))
    {
	Error("Could not allocate the Audio channels\n");
    }

    return ;
}

/**********************************************************************/
/*                                                                    */
/* void CloseAll ()                                                   */
/*                                                                    */
/* Closes and tidies up everything that was used.                     */
/*                                                                    */
/**********************************************************************/

void CloseAll ()
{
    int i;

    /* Try to close everything in the reverse order in which they were opened */

    if (FireAudioBuff)
    {
	FreeVec(FireAudioBuff);
    }

    /* Free the sprite numbers */

    for (i = 0; i < MISSILE_COUNT; i++)
    {
	if (Missiles[i].mis_Sprite->es_SimpleSprite.num != -1)
	{
		FreeSprite(Missiles[i].mis_Sprite->es_SimpleSprite.num);
	}
    }
    for (i = 0; i < SPRITE_COUNT; i++)
    {
	if (Sprites[i])
	{
		FreeSpriteData(Sprites[i]);
	}
    }

    if (window)
    {
	CloseWindow(window);
    }

    if (screen)
    {
	ScreenToBack(screen);
    }

    if (bmbuff[1])
    {
	/* This is the copy of the original bitmap from AllocBitMap(). */
	FreeBitMap(bmbuff[1]);
    }

    if (table)
    {
	FreeVec(table);
    }

    if (inbuff)
    {
    	FreeVec(inbuff);
    }

    if (cockpitscreen)
    {
	CloseScreen(cockpitscreen);
    }

    if (screen)
    {
    	CloseScreen(screen);
    }

    if (rdargs)
    {
	FreeArgs(rdargs);
    }

    if (InFile)
    {
    	CLOSEFILE(InFile);
    }

    if (XHair)
    {
	DisposeObject(XHair);
    }

    if (ThrustAudioOpened[1])
    {
	GetMsg(ThrustAudioIO[1]->ioa_Request.io_Message.mn_ReplyPort);
    	CloseDevice((struct IORequest *)ThrustAudioIO[1]);
    }

    if (ThrustAudioIO[1])
    {
	DeleteIORequest(ThrustAudioIO[1]);
    }

    if (ThrustAudioMP[1])
    {
	DeleteMsgPort(ThrustAudioMP[1]);
    }

    if (ThrustAudioOpened[0])
    {
	GetMsg(ThrustAudioIO[0]->ioa_Request.io_Message.mn_ReplyPort);
    	CloseDevice((struct IORequest *)ThrustAudioIO[0]);
    }

    if (ThrustAudioIO[0])
    {
	DeleteIORequest(ThrustAudioIO[0]);
    }

    if (ThrustAudioMP[0])
    {
	DeleteMsgPort(ThrustAudioMP[0]);
    }

    if (FireAudioOpened)
    {
	GetMsg(FireAudioIO->ioa_Request.io_Message.mn_ReplyPort);
    	CloseDevice((struct IORequest *)FireAudioIO);
    }

    if (FireAudioIO)
    {
	DeleteIORequest(FireAudioIO);
    }

    if (FireAudioMP)
    {
	DeleteMsgPort(FireAudioMP);
    }

    if (InputOpened)
    {
	CloseDevice((struct IORequest *)InputIO);
    }

    if (InputIO)
    {
	DeleteIORequest(InputIO);
    }

    if (NewPixel)
    {
	FreeMem(NewPixel, sizeof(struct IEPointerPixel));
    }

    if (FakeEvent)
    {
	FreeMem(FakeEvent, sizeof(struct InputEvent));
    }

    if (InputMP)
    {
	DeleteMsgPort(InputMP);
    }

    if (IFFParseBase)
    {
	CloseLibrary(IFFParseBase);
    }

    /* Close the Graphics Library */
    if (GfxBase)
    {
	CloseLibrary ((struct Library *) GfxBase) ;
    }

    /* Close the Intuition Library */
    if (IntuitionBase)
    {
	CloseLibrary ((struct Library *) IntuitionBase) ;
    }

    return ;
}

/***************************************************************************/

UBYTE *ReadAudio(FILETYPE InFile, struct RawAudio *RawAudio)
{
    UBYTE *Buff = NULL;
    if (READ(InFile, RawAudio, sizeof(struct RawAudio)) == sizeof(struct RawAudio))
    {
	ULONG padsize;
	UBYTE *pad;

	/* The READ() macro always reads into rbuff, so we need to copy
	 * the contents into RawAudio.
	 */
	CopyMem((APTR)rbuff, (APTR)RawAudio, sizeof(struct RawAudio));
	padsize = ((WHOLE_SECTORS(RawAudio->ra_SizeOfSample + sizeof(struct RawAudio)) * SECTORSIZE) - (RawAudio->ra_SizeOfSample + sizeof(struct RawAudio)));
	if (Buff = AllocVec(RawAudio->ra_SizeOfSample, MEMF_CHIP | MEMF_PUBLIC))
	{
		READ(InFile, Buff, RawAudio->ra_SizeOfSample);
		CopyMem((APTR)rbuff, (APTR)Buff, RawAudio->ra_SizeOfSample);
	}
	if (pad = AllocVec(padsize, 0))
	{
		READ(InFile, pad, padsize);
		FreeVec(pad);
	}
    }
    return(Buff);
}

/* The actual animation is only 6 bitplanes deep, so it uses 64 pens */
#define MAXPEN 64

void InitCopLists(struct ViewPort *vp)
{
    /* The frames were rendered with a purple sky, so use FindColor() to find
     * the purple pen. We will then build a UserCopperList to change the purple
     * to a gradient skyblue.
     *
     * For now, we have to use 12 bits for the gradient. We hope to have a new
     * LVO in the 3.01 graphics.library that will do full 24bit gradual colour
     * changes.
     */

    ULONG sky = FindColor(screen->ViewPort.ColorMap, (0xff << 24), 0, (0xff << 24), MAXPEN);
    struct UCopList *ucl;
    ULONG RGB[3] = {FIRSTCOLOUR_RED, FIRSTCOLOUR_GREEN, FIRSTCOLOUR_BLUE};
    LONG Grad[3];
    int i;

    if ((ucl = AllocMem(sizeof(struct UCopList), MEMF_PUBLIC | MEMF_CLEAR)) == NULL)
    {
	Error("No RAM for UCopList1\n");
    }

    CINIT(ucl, COPINSCNT);

    /* calculate the colour gradient. */
    Grad[0] = ((LASTCOLOUR_RED - FIRSTCOLOUR_RED) / COLOURRANGE);
    Grad[1] = ((LASTCOLOUR_GREEN - FIRSTCOLOUR_GREEN) / COLOURRANGE);
    Grad[2] = ((LASTCOLOUR_BLUE - FIRSTCOLOUR_BLUE) / COLOURRANGE);

    for (i = 0; i < COLOURRANGE; i++)
    {
	CWAIT(ucl, i, 0);
	/* sky */
	CMOVE(ucl, custom->color[sky & 0x1f], (((RGB[0] & 0xf0000000) >> 20) | ((RGB[1] & 0xf0000000) >> 24) | ((RGB[2] & 0xf0000000) >> 28)));
	RGB[0] += Grad[0];
	RGB[1] += Grad[1];
	RGB[2] += Grad[2];
    }
    CEND(ucl);
    screen->ViewPort.UCopIns = ucl;

    /* We can't put the gradient sky in the reflected colour range, because we
     * don't know how to set colours > 32. So in the meanwhilst, just set the
     * reflection to a sky blue.
     * All should be better for 3.01.
     */
    SetRGB32(vp, (sky + MAXPEN), FIRSTCOLOUR_RED, FIRSTCOLOUR_GREEN, FIRSTCOLOUR_BLUE);

    RethinkDisplay();
}

ULONG ScanRate(struct ViewPort *vp)
{
    /* Calculate the scan rate of the Monitor mode used. */

    ULONG ModeID = GetVPModeID(vp);
    ULONG ScanRate = 60;
    struct MonitorInfo minfo;

    if (GetDisplayInfoData(NULL, (APTR)&minfo, sizeof(minfo), DTAG_MNTR, ModeID))
    {
	/* The scan rate is the number of nanoseconds in one second (10e9) /
	 * the total time for one line in nanoseconds (== colourclocks * ~280ns per
	 * colourclock) * the total number of lines.
	 */
	ScanRate = (1000000000 / ((ULONG)minfo.TotalColorClocks * 280 * (ULONG)minfo.TotalRows));
    }

    return(ScanRate);
}

/******************************************************************************/

void __asm ShowILBM(register __a0 struct BitMapHeader *bmhd, register __a1 struct BitMap *bm, register __a2 UBYTE *buff);
void __asm PlayDLTA(register __a2 struct BitMap *bm, register __a1 UBYTE *buff, register __a3 WORD *table, register __d4 UWORD ImageWidth, register __d5 UWORD ImageDepth);
void HandleInput(struct Input *MyInput);
void MoveMissile(struct Input *input);
void FireMissile(struct Input *input);
void ThrustAudio(struct IOAudio *ThrustAudioIO[], struct RawAudio *RawAudio, UBYTE *ThrustBuffer);

void PlayAnim(FILETYPE InFile, struct Frame1Head *f1h, struct BitMap *bmbuff[], struct ViewPort *vp, UWORD *table, UBYTE *inbuff, ULONG FirstRS, ULONG FirstDS)
{
    /* This is the main loop that plays the animation.
     *
     * It uses the V39 ChangeVPBitMap() functions, and whilst waiting for the
     * signal to double-buffer, it handles all the input and audio events.
     */

    #define WAIT_TO_WRITE while(!GetMsg(ports)) Wait(1l << (ports->mp_SigBit))

    struct DBufInfo *dbuf;
    struct MsgPort *ports = NULL;
    struct VistaScript *vista;
    ULONG *secptr, *bytesptr;
    struct Input MyInput;
    ULONG ReadSectors;
    ULONG DLTASize;
    BOOL SafeToWrite = TRUE;
    WORD sbuffno = 1;
    ULONG vbcounter = 0, vbc1, fcount = 0;
    UWORD imagewidth = (((f1h->f1_ImageWidth + 15) >> 4) << 1);

    /* Set up the DBuff stuff */
    if ((dbuf = AllocDBufInfo(vp)) == NULL)
    {
	return;
    }

    /* Set up the Input structure to pass to HandleInput() */
    MyInput.Stick = &stick;
    MyInput.NewPixel = NewPixel;
    MyInput.InputIO = InputIO;
    MyInput.FakeEvent = FakeEvent;
    MyInput.Screen = screen;
    MyInput.BugOut = &ReadSectors;
    MyInput.Missiles = &Missiles[0];
    MyInput.Sprites = Sprites[0];
    MyInput.FireAudio.RawAudio = &FireRawAudio;
    MyInput.FireAudio.AudioIO = FireAudioIO;
    MyInput.FireAudio.AudioMP = FireAudioMP;
    MyInput.FireAudio.AudioBuffer = FireAudioBuff;
    MyInput.FireAudio.Playing = FALSE;
    MyInput.FireAudio.Period = PERIOD(FireRawAudio.ra_SamplesPerSecond);
    MyInput.FireAudio.Unit = (ULONG)FireAudioIO->ioa_Request.io_Unit;
    MyInput.FireAudio.AllocKey = FireAudioIO->ioa_AllocKey;

    if (ports = CreateMsgPort())
    {
	/* A message port for ChangeVPBitMap() */
	dbuf->dbi_SafeMessage.mn_ReplyPort = ports;

	ReadSectors = FirstRS;
	DLTASize = FirstDS;

	/* GfxBase->VBCounter was added after the 3.0 (V39.106) release.
	 * It is just a counter that is incremented every vertical blank.
	 */
	vbc1 = GfxBase->VBCounter;

	/* Start up the Thrusters */
	ThrustAudio(ThrustAudioIO, &ThrustRawAudio, ThrustAudioBuff);
	WaitBlit();	/* from the BltBitMap() call in main() */

	/**************************************/
	/*                                    */
	/* Here is the main animation loop... */                       
	/*                                    */
	/**************************************/

	while (ReadSectors)
	{
		READ(InFile, inbuff, (SECTORSIZE * ReadSectors));

		bytesptr = (ULONG *)(rbuff + DLTASize);
		secptr = (bytesptr + 1);
		ReadSectors = *secptr;
		DLTASize = *bytesptr;
		vista = (struct VistaScript *)(secptr + 1);
		MyInput.Vista = vista;

		if (!SafeToWrite)
		{
			WAIT_TO_WRITE;
		}
		SafeToWrite = TRUE;

		PlayDLTA(bmbuff[sbuffno + 2], rbuff, table, imagewidth, f1h->f1_ImageDepth);

		vbcounter += (GfxBase->VBCounter - vbc1);
		ChangeVPBitMap(vp, bmbuff[sbuffno], dbuf);
		vbc1 = GfxBase->VBCounter;
		fcount++;
		SafeToWrite = FALSE;
		sbuffno ^= 1;

		HandleInput(&MyInput);
		if (Missiles[0].mis_Active)
		{
			MoveMissile(&MyInput);
		}
	}

	/**************************************/
	/*                                    */
	/*           That was it!!            */
	/*                                    */
	/**************************************/

	if (!SafeToWrite)
	{
		WAIT_TO_WRITE;
	}
	DeleteMsgPort(ports);
    }

    FreeDBufInfo(dbuf);

    if (vbcounter)
    {
	    printf("Played %ld frames in %ld frames, or %ld fps\n", fcount, vbcounter, ((ScanRate(vp) * fcount) / vbcounter));
    }
}


ULONG GetModeID(struct Frame1Head *f1h)
{
    /* Check that the ID in the raw file can handle sprites and doublebuffering.
     * If not, then find the best ModeID that can.
     */
    ULONG ModeID = f1h->f1_ScreenModeID;
    struct DisplayInfo disp;

    if (GetDisplayInfoData(NULL, (UBYTE *)&disp, sizeof(struct DisplayInfo), DTAG_DISP, ModeID))
    {
	if ((disp.PropertyFlags & (DIPF_IS_SPRITES | DIPF_IS_DBUFFER)) != (DIPF_IS_SPRITES | DIPF_IS_DBUFFER))
	{
		ModeID = BestModeID(BIDTAG_NominalWidth, f1h->f1_ScreenWidth,
		                    BIDTAG_NominalHeight, f1h->f1_ScreenHeight,
		                    BIDTAG_Depth, f1h->f1_ScreenDepth,
		                    /* Keep the DIPF_ properties of the original
		                     * ModeID, but ensure we have SPRITES and DBUFFER
		                     * properties too.
		                     */
		                    BIDTAG_SourceID, ModeID,
		                    BIDTAG_DIPFMustHave, (DIPF_IS_SPRITES | DIPF_IS_DBUFFER),
		                    TAG_END);
	}
    }
    return(ModeID);
}


/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

void ReadILBM(STRPTR Mask, struct Screen *Screen, BOOL CMap);
struct Screen *OpenCockpit(STRPTR Cockpit, struct Screen *Parent);

void main (int argc, char *argv[])
{
    #define TEMPLATE "FROM/A,C=COCKPIT,M=MASK"
    #define OPT_FROM 0
    #define OPT_COCKPIT 1
    #define OPT_MASK 2
    #define OPT_COUNT 3

    #define SPRITE_BASE 128

    LONG result[OPT_COUNT];
    struct TagItem VCTags[] =
    {
	VTAG_SPEVEN_BASE_SET, SPRITE_BASE,
	VTAG_SPODD_BASE_SET, SPRITE_BASE,
	VTAG_USERCLIP_SET, TRUE,
	TAG_END,
    };
    STRPTR from, cockpit = NULL, mask = NULL;
    ULONG *secptr, *bytesptr;
    ULONG ReadSectors;
    ULONG DLTASize;
    struct Frame1Head f1h;
    struct BitMapHeader bmhd;
    struct BitMap bm1, bm2, bm3;
    UWORD XOffs, YOffs;
    int j, r;
    UWORD ncolours;

    Init () ;

    /* init the result[] */
    result[OPT_FROM] = (LONG)&from;
    result[OPT_COCKPIT] = (LONG)&cockpit;
    result[OPT_MASK] = (LONG)&mask;

    if (rdargs = ReadArgs(TEMPLATE, result, NULL))
    {
	from = (STRPTR)result[OPT_FROM];
	cockpit = (STRPTR)result[OPT_COCKPIT];
	mask = (STRPTR)result[OPT_MASK];
    }
    else
    {
    	Error("Command line error\n");
    }

    if (inbuff = AllocVec(SECTORSIZE, 0))
    {
	if ((InFile = OPEN(from, MODE_READ, SECTORSIZE)) == NULL)
	{
		Error("Could not open the file\n");
	}

	rbuff = inbuff;
    	if (READ(InFile, inbuff, SECTORSIZE) == SECTORSIZE)
    	{
    		/* have all the info we need to open the screen and set up
    		 * the colours.
    		 */

		ULONG ModeID;
		UBYTE *cmap = (rbuff + sizeof(f1h));
		int i;
		
    		f1h = (*(struct Frame1Head *)rbuff);
		ncolours = f1h.f1_Colours;

		ModeID = GetModeID(&f1h);
		printf("Animation ModeID = 0x%lx\n", ModeID);

		/* Open the screen. Notice that we are using SA_FullPalette which
		 * informs intuition that we want a palette with the maximum
		 * possible number of colours (256 for AA). This lets us use the
		 * upper colours that are not part of the animation for the sprite
		 * colours. This is a feature of the AA chips, and we handle this
		 * on a ViewPort (or Screen) basis with the VideoControl() tags
		 * defined in VCTags[]. Under V39, we can pass OpenScreenTags()
		 * a taglist of VideoControl() tags to be used when the screen is
		 * opened.
		 */
    		if ((screen = OpenScreenTags(NULL,
    			SA_Width, f1h.f1_ScreenWidth,
    			SA_Height, f1h.f1_ScreenHeight,
    			SA_Depth, f1h.f1_ScreenDepth,
    			SA_DisplayID, ModeID,
    			SA_FullPalette, TRUE,
    			SA_Behind, TRUE,
    			SA_VideoControl, VCTags,
    			TAG_DONE)) == NULL)
    		{
    			Error("Could not open screen\n");
    		}

		/* Display the cockpit as a child screen */
		
		if (cockpit)
		{
			cockpitscreen = OpenCockpit(cockpit, screen);
		}

		/* Set up the colours for the sprites */
		SetRGB32(&screen->ViewPort, SPRITE_BASE, 0, 0, 0);	/* transparent */
		SetRGB32(&screen->ViewPort, (SPRITE_BASE + 1), -1, -1, -1);	/* white */
		SetRGB32(&screen->ViewPort, (SPRITE_BASE + 2), -1, 0, 0);	/* red */
		SetRGB32(&screen->ViewPort, (SPRITE_BASE + 3), 0, -1, 0);	/* green (unused) */
		
		/* Open a borderless backdrop window in the screen. We use this
		 * to change the pointer to our crosshair, and treat any keypress
		 * to quit the animation.
		 */
		if ((window = OpenWindowTags(NULL,
			WA_CustomScreen, screen,
			WA_Backdrop, TRUE,
			WA_Borderless, TRUE,
			WA_Activate, TRUE,
			WA_RMBTrap, TRUE,
			WA_IDCMP, IDCMP_RAWKEY,
			TAG_DONE)) == NULL)
		{
			Error("Could not open the window\n");
		}

		/* If there is a Mask for the animation area, load it in here. */
		ReadILBM(mask, screen, FALSE);

		for (i = 0; i < ncolours; i++)
		{
			ULONG red = (*cmap++ << 24);
			ULONG green = (*cmap++ << 24);
			ULONG blue = (*cmap++ << 24);
			ULONG rred;
			ULONG rgreen;
			ULONG rblue;
			/* before creating the reflected colours, check for
			 * overflow.
			 */
			if ((rred = (red + (red / 5))) < red)
			{
				rred = 0xffffffff;
			}
			if ((rgreen = (green + (green / 5))) < green)
			{
				rgreen = 0xffffffff;
			}
			if ((rblue = (blue + (blue / 5))) < blue)
			{
				rblue = 0xffffffff;
			}
			SetRGB32(&screen->ViewPort, i, rred, rgreen, rblue);
			SetRGB32(&screen->ViewPort, (i + ncolours), red, green, blue);
		}
		CLOSEFILE(InFile);
		InFile = NULL;
	}
	else
	{
		Error("File read error\n");
	}
	FreeVec(inbuff);
	inbuff = NULL;
    }
    else
    {
    	Error("not enough memory\n");
    }

    /* Now, we have a screen and we know the maximum buffer size we need.
     * So, read in the first frame, and treat it as a raw ILBM, once we skip over
     * all the Frame1Header info.
     */

    /* Set up our two buffers */
    bm1 = *(screen->RastPort.BitMap);
    bmbuff[0] = &bm1;
    if ((bmbuff[1] = AllocBitMap(f1h.f1_ScreenWidth, f1h.f1_ScreenHeight, f1h.f1_ScreenDepth, BMF_DISPLAYABLE, bmbuff[0])) == NULL)
    {
	Error("Not enough memory for 2nd bitmap\n");
    }
    /* Because the image may be smaller than the whole screen,
     * we will munge the bitmaps for the Anim code
     * so that the planes point to the correct offset in
     * the bitplanes to center the image. This means
     * we need to keep separate copies of the true bitmaps.
     */
    bm2 = *(bmbuff[0]);
    bm3 = *(bmbuff[1]);
    bmbuff[2] = &bm2;
    bmbuff[3] = &bm3;
								
    /* Center this image in the screen */
    XOffs = ((f1h.f1_ScreenWidth - f1h.f1_ImageWidth) >> 4);	/* byte offset */
    YOffs = (((f1h.f1_ScreenHeight - f1h.f1_ImageHeight) >> 1) * bmbuff[0]->BytesPerRow);	/* row offset in bytes */

    /* Munge the planepointers to the correct offsets to center */
    for (j = 2; j < 4; j++)
    {
	for (r = 0; r < f1h.f1_ImageDepth; r++)
	{
		bmbuff[j]->Planes[r] += (YOffs + XOffs);
	}
    }

    /* create the offset table for the DLTA code */
    if (table = AllocVec((f1h.f1_ImageHeight << 1), 0))
    {
	UWORD c = 0;
	UWORD *tmp = table;
	for (r = 0; r < f1h.f1_ImageHeight; r++)
	{
		*tmp++ = c;
		c += screen->RastPort.BitMap->BytesPerRow;
	}
    }
    else
    {
	Error("Not memory for table\n");
    }



    if (inbuff = AllocVec((SECTORSIZE * f1h.f1_MaxSectorSize), MEMF_CLEAR))
    {
	if (InFile = OPEN(from, MODE_READ, (SECTORSIZE * f1h.f1_MaxSectorSize * 2)))
	{
		rbuff = inbuff;
		if (READ(InFile, inbuff, (f1h.f1_Sectors * SECTORSIZE)) == (f1h.f1_Sectors * SECTORSIZE))
		{
			UBYTE *image = (rbuff + DATASIZE);

			/* set up the bmhd with the values ShowILBM() needs */
			bmhd.nplanes = f1h.f1_ImageDepth;
			bmhd.w = f1h.f1_ImageWidth;
			bmhd.h = f1h.f1_ImageHeight;

			ShowILBM(&bmhd, bmbuff[2], image);

			/* Copy the image into the other buffer */
			BltBitMap(bmbuff[0], 0, 0, bmbuff[1], 0, 0, f1h.f1_ScreenWidth, f1h.f1_ScreenHeight, 0xc0, -1, NULL);
		}
		else
		{
			Error("1st frame error\n");
		}
	}
	else
	{
		Error("1st frame did not open\n");
	}
    }
    else
    {
    	Error("RAM all gone?\n");
    }

    /* get the ReadSectors and DLTASize here, because reading the Audio data trashes the rbuff buffer */
    bytesptr = (ULONG *)(rbuff + DATASIZE + f1h.f1_ByteSize);
    secptr = (bytesptr + 1);
    ReadSectors = *secptr;
    DLTASize = *bytesptr;

    /* Read the Audio sectors */
    FireAudioBuff = ReadAudio(InFile, &FireRawAudio);
    ThrustAudioBuff = ReadAudio(InFile, &ThrustRawAudio);
	
    /* final initialisations.... */
    InitCopLists(&screen->ViewPort);
    ScreenToFront(screen);
    if ((XHair = InitXHair()) == NULL)
    {
	Error("No XHair sprite\n");
    }
    SetWindowPointer(window, WA_Pointer, XHair, TAG_DONE);
    /* Put the cursor (intuition's pointer) in the center of the screen */
    FakeEvent->ie_NextEvent = NULL;
    FakeEvent->ie_Class = IECLASS_NEWPOINTERPOS;
    FakeEvent->ie_SubClass = IESUBCLASS_PIXEL;
    FakeEvent->ie_Code = IECODE_NOBUTTON;
    FakeEvent->ie_Qualifier = NULL;
    FakeEvent->ie_EventAddress = (APTR)NewPixel;
    InputIO->io_Data = (APTR)FakeEvent;
    InputIO->io_Length = sizeof(struct InputEvent);
    InputIO->io_Command = IND_WRITEEVENT;
    NewPixel->iepp_Position.X = (screen->Width >> 1);
    NewPixel->iepp_Position.Y = (screen->Height >> 1);
    NewPixel->iepp_Screen = screen;
    DoIO((struct IORequest *)InputIO);

    /* Now play the anim until the end! */
    PlayAnim(InFile, &f1h, bmbuff, &screen->ViewPort, table, inbuff, ReadSectors, DLTASize);

    CloseAll () ;
}
