/*
** MPEG Hardware Player
**
** by Sigbjørn (CISC) Skjæret
**
**
** Do whatever you want with this code, but I'd appreciate it if you contacted
** me and told me about it first and/or you credited me for it in your project.
**
*/

#define __USE_SYSBASE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/peggympeg.h>

#include "vpeg.h"

#define MAXTOC 100
#define MAXBUF 16384
#define MAXPATH 1024
#define CheckFlag(mask,flag,name) (mask & flag) ? name : ""

static const char Version[] = "$VER: VPeg 2.0 (10.06.01)";

struct IntuitionBase	*IntuitionBase = NULL;
struct Library			*PeggyMPEGBase;

const ULONG  layer[]  = { 0, 3, 2, 1 };
const ULONG  freq[]   = { 44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000 };
const STRPTR fps[]    = { "0", "23.976", "24", "25", "29.97", "30", "50", "59.94", "60" };
const STRPTR aspect[] = { "0:0", "1:1", "1:0.6735", "1:0.7031", "1:0.7615", "1:0.8055", "1:0.8437", "1:0.8935", "1:0.9157 (PAL)", "1:0.9815", "1:1.0255", "1:1.0695", "1:1.0950 (NTSC)", "1:1.1575", "1:1.2015" };
const STRPTR mode[]   = { "Stereo", "J-Stereo", "Dual", "Mono" };
const STRPTR track[]  = { "Root", "Data", "Audio", "MPEG", "Whitebook", "Unknown" };


ULONG IssueRequest(struct IORequest *Request, ULONG WaitSignals)
{
	ULONG signal;

	SendIO(Request);
	signal = Wait(WaitSignals);

	if (signal & ~(SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F))	/* Check if any of the signals are non-break ones */
	{
		while (GetMsg(Request->io_Message.mn_ReplyPort));	/* Empty message-queue */

		if (Request->io_Error)	/* Check for error */
		{
			Printf("An error occured while performing command 0x%04lx: %ld.\n", (ULONG)Request->io_Command, (LONG)Request->io_Error);
			return NULL;
		}
	}

	return signal;
}

ULONG PeggyCtrl(ULONG signal, ULONG usersig, ULONG ctrlsig, ULONG pegdsig, LONG step, struct IOMPEGReq *PeggyIO, struct IOMPEGReq *Peggy2IO)
{
	LONG pause = 0, search = 0;

	while (!(signal & usersig))
	{
		if (signal & SIGBREAKF_CTRL_E)
		{
			Peggy2IO->iomr_Req.io_Command = MPEGCMD_PAUSE;
			Peggy2IO->iomr_StreamType     = PeggyIO->iomr_StreamType;
			Peggy2IO->iomr_PauseMode      = (pause ^= 1);
			IssueRequest((struct IORequest *)Peggy2IO, pegdsig);
			search = 0;
		}

		if (signal & SIGBREAKF_CTRL_F)
		{
			Peggy2IO->iomr_Req.io_Command = MPEGCMD_SEARCH;
			Peggy2IO->iomr_StreamType     = PeggyIO->iomr_StreamType;
			Peggy2IO->iomr_SearchSpeed    = (search ^= step);
			IssueRequest((struct IORequest *)Peggy2IO, pegdsig);
			pause = 0;
		}

		signal = Wait(usersig | ctrlsig);
		while (GetMsg(PeggyIO->iomr_Req.io_Message.mn_ReplyPort));
	}

	return signal;
}

void PrintStreamInfo(struct MPEGStreamInfo *StreamInfo)
{
	switch (StreamInfo->StreamType)
	{
	case MPEGSTREAM_SYSTEM:
		Printf("System Stream -> %lux%lu %lukbps %sfps %s\n", (ULONG)StreamInfo->Width, (ULONG)StreamInfo->Height, (ULONG)StreamInfo->BitRate/1000, fps[StreamInfo->PictureRate], aspect[StreamInfo->AspectRatio]);
		break;
	case MPEGSTREAM_VIDEO:
		Printf("Video Stream  -> %lux%lu %lukbps %sfps %s\n", (ULONG)StreamInfo->Width, (ULONG)StreamInfo->Height, (ULONG)StreamInfo->BitRate/1000, fps[StreamInfo->PictureRate], aspect[StreamInfo->AspectRatio]);
		break;
	case MPEGSTREAM_AUDIO:
		Printf("Audio Stream  -> Layer%lu %s %luHz %lukbps\n", layer[StreamInfo->AudioLayer], mode[(StreamInfo->AudioMode & MPEGAUD_MODE)>>2], freq[StreamInfo->AudioFreq], (ULONG)StreamInfo->AudioBitRate);
		break;
	default:
		Printf("Unknown streamtype: %lu\n", (ULONG)StreamInfo->StreamType);
		break;
	}
}

void ClearMPEGReq(struct IOMPEGReq *PeggyIO)
{
	PeggyIO->iomr_Req.io_Error = 0;
	PeggyIO->iomr_Req.io_Actual = 0;
	PeggyIO->iomr_Req.io_Length = 0;
	PeggyIO->iomr_Req.io_Data = 0;
	PeggyIO->iomr_Req.io_Offset = 0;

	PeggyIO->iomr_MPEGError = 0;
	PeggyIO->iomr_Version = 0;
	PeggyIO->iomr_StreamType = 0;
	PeggyIO->iomr_MPEGFlags = 0;
	PeggyIO->iomr_Arg1 = 0;
	PeggyIO->iomr_Arg2 = 0;
	PeggyIO->iomr_PTSHigh = 0;
	PeggyIO->iomr_PTSMid = 0;
	PeggyIO->iomr_PTSLow = 0;
}


void main(void)
{
	APTR mempool = NULL;
	BYTE PeggyDevice = -1;
	struct RDArgs *rdargs = NULL;
	struct Screen *screen = NULL;
	struct IOMPEGReq *PeggyIO = NULL;
	struct IOMPEGReq *Peggy2IO = NULL;
	struct MsgPort *PeggyMP = NULL;
	struct MsgPort *Peggy2MP = NULL;
	struct PeggyData *Peggy;

	STRPTR Template = "FILE/A/M,FINDSYNC/S,NEWSCREEN/S,KEEPSCREEN/S,DISPLAY/K,KEEPAUDIO/S,KEEPVIDEO/S,NOPLAY/S,YCBCRMODE/S,PVC=CONVERTER/S,VOL=VOLUME/N,OP=OPACITY/N,L=LOOPS/N,JL=JUSTIFYLEFT/N,JT=JUSTIFYTOP/N,VCD/S,DEV=DEVICE/K,U=UNIT/N,MPDEV=MPEGDEVICE/K,VERBOSE/S";
	enum { TEM_FILE, TEM_FINDSYNC, TEM_NEWSCREEN, TEM_KEEPSCREEN, TEM_DISPLAY, TEM_KEEPAUDIO, TEM_KEEPVIDEO, TEM_NOPLAY, TEM_YCBCR, TEM_CONVERTER, TEM_VOLUME, TEM_OPACITY, TEM_LOOPS, TEM_JUSTIFYLEFT, TEM_JUSTIFYTOP, TEM_VCD, TEM_DEVICE, TEM_UNIT, TEM_MPEGDEV, TEM_VERBOSE, TEM_NUMARGS };
	LONG ArgArray[TEM_NUMARGS];
	ULONG signal, pegpsig, pegdsig, usersig = SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D, ctrlsig = SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F;

	STRPTR	 tempfile	= "T:vpeg.tmp";
	STRPTR	 mpegdev	= "peggympeg.device";
	BPTR	 fp			= NULL;
	UWORD	 loops		= 1;
	UWORD	 converter	= 0;
	UWORD	 volume		= 65535;
	UWORD	 opacity	= 65535;
	UWORD	 justleft	= 0;
	UWORD	 justtop	= 0;
	UWORD	 picrate	= MPEG_RATE_AUTO;
	ULONG	*filearray;
	STRPTR	 filename;
	size_t	 bufsize;
	STRPTR	 buf;
	unsigned int pos, i, f, x, offset, cdi = 0;

	for (i=0;i<TEM_NUMARGS;ArgArray[i++]=NULL);
	if (!(rdargs = ReadArgs(Template,ArgArray,NULL))) { Printf("Invalid arguments.\n"); goto exit; }
	if (ArgArray[TEM_VCD] && (!ArgArray[TEM_DEVICE] || !ArgArray[TEM_UNIT])) { Printf("Missing arguments.\n"); goto exit; }

	if (!(mempool = CreatePool(MEMF_PUBLIC | MEMF_CLEAR, sizeof(struct MPEGCDInfo)*MAXTOC*2, sizeof(struct MPEGCDInfo)*MAXTOC))) { Printf("Unable to allocate needed memory.\n"); goto exit; }
	if (!(Peggy = AllocPooled(mempool, sizeof(struct PeggyData)))) { Printf("Unable to allocate needed memory.\n"); goto exit; }
	if (!(Peggy->DevInfo = AllocPooled(mempool, sizeof(struct MPEGDevInfo)))) { Printf("Unable to allocate needed memory.\n"); goto exit; }
	if (!(Peggy->StreamInfo = AllocPooled(mempool, sizeof(struct MPEGStreamInfo)))) { Printf("Unable to allocate needed memory.\n"); goto exit; }
	if (!(Peggy->Environment = AllocPooled(mempool, sizeof(struct MPEGEnvironment)))) { Printf("Unable to allocate needed memory.\n"); goto exit; }
	if (!(Peggy->AudioParams = AllocPooled(mempool, sizeof(struct MPEGAudioParams)))) { Printf("Unable to allocate needed memory.\n"); goto exit; }
	if (!(Peggy->BorderParams = AllocPooled(mempool, sizeof(struct MPEGBorderParams)))) { Printf("Unable to allocate needed memory.\n"); goto exit; }
	if (!(Peggy->WindowParams = AllocPooled(mempool, sizeof(struct MPEGWindowParams)))) { Printf("Unable to allocate needed memory.\n"); goto exit; }
	if (!(Peggy->VideoParams = AllocPooled(mempool, sizeof(struct MPEGVideoParamsSet)))) { Printf("Unable to allocate needed memory.\n"); goto exit; }

	if (ArgArray[TEM_LOOPS]) loops = *((ULONG *)ArgArray[TEM_LOOPS]);
	if (ArgArray[TEM_VOLUME]) volume = *((ULONG *)ArgArray[TEM_VOLUME]);
	if (ArgArray[TEM_OPACITY]) opacity = *((ULONG *)ArgArray[TEM_OPACITY]);
	if (ArgArray[TEM_JUSTIFYLEFT]) justleft = *((ULONG *)ArgArray[TEM_JUSTIFYLEFT]);
	if (ArgArray[TEM_JUSTIFYTOP]) justtop = *((ULONG *)ArgArray[TEM_JUSTIFYTOP]);
	if (ArgArray[TEM_MPEGDEV]) mpegdev = (STRPTR)ArgArray[TEM_MPEGDEV];
	if (ArgArray[TEM_CONVERTER]) converter = 1;
	filearray = (ULONG *)ArgArray[TEM_FILE];

	if (ArgArray[TEM_DISPLAY])
	{
		if ((x = strlen((STRPTR)ArgArray[TEM_DISPLAY])) > 0)
		{
			if (strnicmp((STRPTR)ArgArray[TEM_DISPLAY], "PAL", x) == 0) picrate = MPEG_RATE_PAL;
			else if (strnicmp((STRPTR)ArgArray[TEM_DISPLAY], "NTSC", x) == 0) picrate = MPEG_RATE_NTSC;
		}
	}

	if (!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",37))) { Printf("Unable to open intuition.library v37+.\n"); goto exit; }

	if (!(PeggyMP = CreateMsgPort())) { Printf("Unable to create MsgPort.\n"); goto exit; }
	if (!(Peggy2MP = CreateMsgPort())) { Printf("Unable to create MsgPort.\n"); goto exit; }

	if (!(PeggyIO = (struct IOMPEGReq *)CreateIORequest(PeggyMP, sizeof(struct IOMPEGReq)))) { Printf("Unable to create IORequest.\n"); goto exit; }
	if (!(Peggy2IO = (struct IOMPEGReq *)AllocMem(sizeof(struct IOMPEGReq), MEMF_PUBLIC))) { Printf("Unable to create IORequest.\n"); goto exit; }

	if ((PeggyDevice = OpenDevice(mpegdev, 0, (struct IORequest *)PeggyIO, NULL))) { Printf("Unable to open %s.\n", mpegdev); goto exit; }
	PeggyMPEGBase = (struct Library *)PeggyIO->iomr_Req.io_Device;

	CopyMem(PeggyIO, Peggy2IO, sizeof(struct IOMPEGReq));	/* Duplicate PeggyIO */
	Peggy2IO->iomr_Req.io_Message.mn_ReplyPort = Peggy2MP;	/* Set up duplicate PeggyIO with alternate MsgPort */

	pegpsig = 1L << PeggyMP->mp_SigBit;
	pegdsig = 1L << Peggy2MP->mp_SigBit;

	if (ArgArray[TEM_VERBOSE])
	{
		PeggyIO->iomr_Req.io_Command = MPEGCMD_GETDEVINFO;
		PeggyIO->iomr_Req.io_Data    = Peggy->DevInfo;
		PeggyIO->iomr_Req.io_Length  = sizeof(struct MPEGDevInfo);
		if (!IssueRequest((struct IORequest *)PeggyIO, pegpsig)) goto exit;

		Printf("Board:         %s\n",Peggy->DevInfo->mdi_BoardDesc);
		Printf("Version:       %lu\n",(ULONG)Peggy->DevInfo->mdi_Version);
		Printf("Capabilities: %s%s%s%s%s%s%s%s%s\n\n",
			CheckFlag(Peggy->DevInfo->mdi_BoardCapabilities,MPEGCF_PLAYRAWVIDEO," RawVideo"),
			CheckFlag(Peggy->DevInfo->mdi_BoardCapabilities,MPEGCF_PLAYRAWAUDIO," RawAudio"),
			CheckFlag(Peggy->DevInfo->mdi_BoardCapabilities,MPEGCF_PLAYSYSTEM," System"),
			CheckFlag(Peggy->DevInfo->mdi_BoardCapabilities,MPEGCF_WINDOWVIDEO," Window"),
			CheckFlag(Peggy->DevInfo->mdi_BoardCapabilities,MPEGCF_SCALEVIDEO," Scale"),
			CheckFlag(Peggy->DevInfo->mdi_BoardCapabilities,MPEGCF_STEPPLAY," Step"),
			CheckFlag(Peggy->DevInfo->mdi_BoardCapabilities,MPEGCF_SCANPLAY," Scan"),
			CheckFlag(Peggy->DevInfo->mdi_BoardCapabilities,MPEGCF_SLOWPLAY," Slow"),
			CheckFlag(Peggy->DevInfo->mdi_BoardCapabilities,MPEGCF_READFRAME," Read"));
	}

	if (ArgArray[TEM_NEWSCREEN])
	{
		ULONG display;

		switch (picrate)
		{
		case MPEG_RATE_NTSC:
			display = NTSC_MONITOR_ID;
			break;
		case MPEG_RATE_PAL:
			display = PAL_MONITOR_ID;
			break;
		default:
			if (SysBase->ex_EClockFrequency==715909) display = NTSC_MONITOR_ID;
			else display = PAL_MONITOR_ID;
			break;
		}
		screen = OpenScreenTags(NULL, SA_DisplayID, display, SA_ShowTitle, FALSE, SA_Draggable, FALSE, TAG_END);

		if (!ArgArray[TEM_KEEPSCREEN])
		{
			CloseScreen(screen);
			screen = NULL;

			CloseLibrary((struct Library *)IntuitionBase);
			IntuitionBase = NULL;
		}
	}

	if (ArgArray[TEM_FINDSYNC])
	{
		if (!(buf = (STRPTR)AllocPooled(mempool, MAXBUF))) { Printf("Unable to allocate needed memory.\n"); goto exit; }
	}


//	/* Init the Peggy+ board */
//	PeggyIO->iomr_Req.io_Message.mn_Node.ln_Pri = 0;
//	PeggyIO->iomr_Req.io_Command = CMD_RESET;
//	if (!IssueRequest((struct IORequest *)PeggyIO, pegpsig)) goto exit;

	/* Video init */
	Peggy->VideoParams->mvp_Fade        = opacity;		/* No effect on Peggy+ (except no video when 0) */
	Peggy->VideoParams->mvp_VideoOutput = converter;
	Peggy->VideoParams->mvp_DisplayType	= picrate;
	if (ArgArray[TEM_YCBCR]) Peggy->VideoParams->mvp_ColorMode   = YCbCr_MODE;
	else                     Peggy->VideoParams->mvp_ColorMode   = RGB_MODE;

	PeggyIO->iomr_Req.io_Command = MPEGCMD_SETVIDEOPARAMS;
	PeggyIO->iomr_Req.io_Data    = Peggy->VideoParams;
	PeggyIO->iomr_Req.io_Length  = sizeof(struct MPEGVideoParamsSet);
	if (!IssueRequest((struct IORequest *)PeggyIO, pegpsig)) goto exit;

	/* Audio init */
	Peggy->AudioParams->map_VolumeLeft  = volume;		/* No effect on Peggy+ (except no audio when 0) */
	Peggy->AudioParams->map_VolumeRight = volume;
	Peggy->AudioParams->map_AudioMode   = MPEG_AUDIO;
//	Peggy->AudioParams->map_StreamID    = ~0;			/* WTF is this for? */

	PeggyIO->iomr_Req.io_Command = MPEGCMD_SETAUDIOPARAMS;
	PeggyIO->iomr_Req.io_Data    = Peggy->AudioParams;
	PeggyIO->iomr_Req.io_Length  = sizeof(struct MPEGAudioParams);
	if (!IssueRequest((struct IORequest *)PeggyIO, pegpsig)) goto exit;

	/* Border init */
	Peggy->BorderParams->mbp_BorderLeft = justleft;
	Peggy->BorderParams->mbp_BorderTop  = justtop;

	PeggyIO->iomr_Req.io_Command = MPEGCMD_SETBORDER;
	PeggyIO->iomr_Req.io_Data    = Peggy->BorderParams;
	PeggyIO->iomr_Req.io_Length  = sizeof(struct MPEGBorderParams);
	if (!IssueRequest((struct IORequest *)PeggyIO, pegpsig)) goto exit;

//	if (ArgArray[TEM_CROP])
//	{
//		Peggy->WindowParams->mwp_XOffset = 0;
//		Peggy->WindowParams->mwp_YOffset = 0;
//		Peggy->WindowParams->mwp_Width   = 704;	/* 352 low-res pixels */
//		Peggy->WindowParams->mwp_Height  = 288;	/* This is Peggy+ max */

//		PeggyIO->iomr_Req.io_Command = MPEGCMD_SETWINDOW;
//		PeggyIO->iomr_Req.io_Data    = Peggy->WindowParams;
//		PeggyIO->iomr_Req.io_Length  = sizeof(struct MPEGWindowParams);
//		if (!IssueRequest((struct IORequest *)PeggyIO, pegpsig)) goto exit;
//	}


	if (ArgArray[TEM_VCD])
	{
		if (!(Peggy->CDInfo = AllocPooled(mempool, sizeof(struct MPEGCDInfo)*MAXTOC))) { Printf("Unable to allocate needed memory.\n"); goto end; }

		Peggy->Environment->met_Length  = sizeof(struct MPEGEnvironment);
		Peggy->Environment->met_Device  = (STRPTR)ArgArray[TEM_DEVICE];
		Peggy->Environment->met_DevUnit = *((ULONG *)ArgArray[TEM_UNIT]);
		Peggy->Environment->met_DevType = MPEGDEV_CDXA;

		PeggyIO->iomr_Req.io_Command = MPEGCMD_GETCDCONTENTS;
		PeggyIO->iomr_Req.io_Data    = Peggy->CDInfo;
		PeggyIO->iomr_Req.io_Length  = sizeof(struct MPEGCDInfo)*MAXTOC;
		PeggyIO->iomr_Arg1           = (LONG)Peggy->Environment;
		if (!IssueRequest((struct IORequest *)PeggyIO, pegpsig)) goto exit;

		if (ArgArray[TEM_VERBOSE])
		{
			Printf("Title:   %s\n", Peggy->CDInfo[0].mcd_Name);
			Printf("Blocks:  %lu\n\n", Peggy->CDInfo[0].mcd_Length);
		}

		if (Peggy->CDInfo[0].mcd_Length)
		for (i=1;Peggy->CDInfo[i].mcd_Length;i++)
		{
			if (ArgArray[TEM_VERBOSE])
			{
				Printf("Track %lu: %s\n", (ULONG)Peggy->CDInfo[i].mcd_TrackNumber, Peggy->CDInfo[i].mcd_Name);
				Printf("Blocks:  %lu-%lu\n", Peggy->CDInfo[i].mcd_StartBlock, Peggy->CDInfo[i].mcd_StartBlock + Peggy->CDInfo[i].mcd_Length);
			}

			if (Peggy->CDInfo[i].mcd_Length == Peggy->CDInfo[0].mcd_Length)
			{
				if (!GetCDStreamInfo(Peggy->Environment, Peggy->StreamInfo, 4250, sizeof(struct MPEGStreamInfo)))
				{
					UWORD Width = Peggy->StreamInfo->Width, Height = Peggy->StreamInfo->Height;
					ULONG BitRate = Peggy->StreamInfo->BitRate;

					cdi = 4250;
					if (ArgArray[TEM_VERBOSE]) Printf("Possible CDi detected, checking for bumper-sequence...\n");

					for (f=5300;f<6000;f++)
					{
						GetCDStreamInfo(Peggy->Environment, Peggy->StreamInfo, f, sizeof(struct MPEGStreamInfo));

						if (Peggy->StreamInfo->StreamType == MPEGSTREAM_SYSTEM && Peggy->StreamInfo->Height != 0 && (Peggy->StreamInfo->Height != Height || Peggy->StreamInfo->Width != Width || Peggy->StreamInfo->BitRate != BitRate))
						{
							cdi = f;
							if (ArgArray[TEM_VERBOSE]) Printf("Bumper-sequence detected, skipping to sector %lu!\n", cdi);
							break;
						}
					}

					Peggy->CDInfo[i].mcd_TrackType = TRACKTYPE_WHITEMPEG;
					Peggy->CDInfo[i].mcd_StartBlock = cdi;
					Peggy->CDInfo[i].mcd_Length -= cdi;
				}
			}

			if (Peggy->CDInfo[i].mcd_TrackType == TRACKTYPE_MPEG || Peggy->CDInfo[i].mcd_TrackType == TRACKTYPE_WHITEMPEG)
			{
				x = GetCDStreamInfo(Peggy->Environment, Peggy->StreamInfo, Peggy->CDInfo[i].mcd_StartBlock, sizeof(struct MPEGStreamInfo));

				if (!x)
				{
					if (ArgArray[TEM_VERBOSE]) PrintStreamInfo(Peggy->StreamInfo);
				}
				else { if (!cdi) Printf("Couldn't figure out filetype of track %lu.\n", i); continue; }

				if (!ArgArray[TEM_NOPLAY])
				{
					ClearMPEGReq(PeggyIO);

					PeggyIO->iomr_Req.io_Command = MPEGCMD_PLAYLSN;
					PeggyIO->iomr_Req.io_Data    = Peggy->Environment;
					PeggyIO->iomr_Req.io_Offset  = Peggy->CDInfo[i].mcd_StartBlock;
					PeggyIO->iomr_Req.io_Length  = Peggy->CDInfo[i].mcd_Length;
					PeggyIO->iomr_StreamType     = Peggy->StreamInfo->StreamType;
					PeggyIO->iomr_SectorSize     = 2328;	/* The data is really just 2324 */
					PeggyIO->iomr_StreamStart    = Peggy->CDInfo[i].mcd_StartBlock;

					if (loops != 1)
					{
						PeggyIO->iomr_MPEGFlags  = MPEGF_REPEAT;
						PeggyIO->iomr_Loops      = loops;
					}

					if (ArgArray[TEM_VERBOSE]) Printf("Playing %s track (CTRL-D to skip / CTRL-C to exit).\n\n", track[Peggy->CDInfo[i].mcd_TrackType]);
					if (!(signal = IssueRequest((struct IORequest *)PeggyIO, pegpsig | usersig | ctrlsig))) goto exit;

					if (signal & ctrlsig)
					{
						signal = PeggyCtrl(signal, pegpsig | usersig, ctrlsig, pegdsig, 100, PeggyIO, Peggy2IO);
					}

					if (signal & usersig)
					{
						AbortIO((struct IORequest *)PeggyIO);
						WaitIO((struct IORequest *)PeggyIO);
						SetSignal(0L, pegpsig | usersig);	/* Clear signalbits since WaitIO most likely preserves them */

						if (signal & SIGBREAKF_CTRL_C) break;
						if (signal & SIGBREAKF_CTRL_D) continue;
					}
				}
			}
		}
	}
	else
	{
		if (!(Peggy->WildAnchor = AllocPooled(mempool, sizeof(struct AnchorPath) + MAXPATH))) { Printf("Unable to allocate needed memory.\n"); goto end; }
		Peggy->WildAnchor->ap_Strlen = MAXPATH;

		for (f=0,x=0,offset=0;filearray[f];f++)
		{
			if (!MatchFirst((STRPTR)filearray[f], Peggy->WildAnchor))
			{
				do
				{
					filename = Peggy->WildAnchor->ap_Buf;

					if (ArgArray[TEM_FINDSYNC])
					{
						if (!(fp = Open(filename, MODE_OLDFILE))) { Printf("Couldn't open \"%s\".\n", filename); continue; }

						for (pos=0;(bufsize=Read(fp, buf, MAXBUF))>0;pos+=bufsize)
						{
							for (i=0;i<bufsize-4;i++)	/* We're lazy, don't overlap buffers */
							{
								if (buf[i]==0x00 && buf[i+1]==0x00 && buf[i+2]==0x01 && (buf[i+3]==0xBA || buf[i+3]==0xB3))
								{
									offset = pos+i;
									x = 1;
									break;
								}
							}
							if (x) break;

							if (SetSignal(0,0) & SIGBREAKF_CTRL_C) { MatchEnd(Peggy->WildAnchor); goto end; }
						}

						Close(fp);
						fp = NULL;
					}

					if (ArgArray[TEM_FINDSYNC] && x)
					{
						if (ArgArray[TEM_VERBOSE]) Printf("Found sync at %lu\n", offset);

						if ((fp = Open(tempfile, MODE_NEWFILE)))
						{
							Write(fp, &buf[i], bufsize-i);
							Close(fp);
							fp = NULL;

							x = GetStreamInfo(tempfile, Peggy->StreamInfo, sizeof(struct MPEGStreamInfo));
							DeleteFile(tempfile);
						}
					}
					else x = GetStreamInfo(filename, Peggy->StreamInfo, sizeof(struct MPEGStreamInfo));

					if (!x)
					{
						if (ArgArray[TEM_VERBOSE]) PrintStreamInfo(Peggy->StreamInfo);
					}
					else { Printf("Couldn't figure out filetype of, or unable to open \"%s\".\n", filename); continue; }

					if (!ArgArray[TEM_NOPLAY])
					{
						ClearMPEGReq(PeggyIO);

						PeggyIO->iomr_Req.io_Command = MPEGCMD_PLAYFILE;
						PeggyIO->iomr_Req.io_Data    = filename;
						PeggyIO->iomr_Req.io_Offset  = offset;
						PeggyIO->iomr_Req.io_Length  = strlen(filename);
						PeggyIO->iomr_StreamType     = Peggy->StreamInfo->StreamType;

						if (loops != 1)
						{
							PeggyIO->iomr_MPEGFlags  = MPEGF_REPEAT;
							PeggyIO->iomr_Loops      = loops;
						}

						if (ArgArray[TEM_VERBOSE]) Printf("Playing \"%s\" (CTRL-D to skip / CTRL-C to exit).\n\n", filename);
						if (!(signal = IssueRequest((struct IORequest *)PeggyIO, pegpsig | usersig | ctrlsig))) { MatchEnd(Peggy->WildAnchor); goto exit; }

						if (signal & ctrlsig)
						{
							signal = PeggyCtrl(signal, pegpsig | usersig, ctrlsig, pegdsig, 1, PeggyIO, Peggy2IO);
						}

						if (signal & usersig)
						{
							AbortIO((struct IORequest *)PeggyIO);
							WaitIO((struct IORequest *)PeggyIO);
							SetSignal(0L, pegpsig | usersig);	/* Clear signalbits since WaitIO most likely preserves them */

							if (signal & SIGBREAKF_CTRL_C) { MatchEnd(Peggy->WildAnchor); goto end; }
							if (signal & SIGBREAKF_CTRL_D) continue;
						}
					}
				} while (!MatchNext(Peggy->WildAnchor));

				MatchEnd(Peggy->WildAnchor);
			}
		}
	}

end:
	ClearMPEGReq(PeggyIO);

	if (!ArgArray[TEM_KEEPAUDIO])
	{
		Peggy->AudioParams->map_AudioMode = AMIGA_AUDIO;

		PeggyIO->iomr_Req.io_Command = MPEGCMD_SETAUDIOPARAMS;
		PeggyIO->iomr_Req.io_Data    = Peggy->AudioParams;
		PeggyIO->iomr_Req.io_Length  = sizeof(struct MPEGAudioParams);
		if (!IssueRequest((struct IORequest *)PeggyIO, pegpsig)) goto exit;
	}

	if (!ArgArray[TEM_KEEPVIDEO])
	{
		Peggy->VideoParams->mvp_Fade = 0;

		PeggyIO->iomr_Req.io_Command = MPEGCMD_SETVIDEOPARAMS;
		PeggyIO->iomr_Req.io_Data    = Peggy->VideoParams;
		PeggyIO->iomr_Req.io_Length  = sizeof(struct MPEGVideoParamsSet);
		if (!IssueRequest((struct IORequest *)PeggyIO, pegpsig)) goto exit;
	}

exit:
	if (fp)				Close(fp);
	if (rdargs)			FreeArgs(rdargs);
	if (screen)			CloseScreen(screen);

	if (!PeggyDevice)	CloseDevice((struct IORequest *)PeggyIO);
	if (PeggyIO)		DeleteIORequest((struct IORequest *)PeggyIO);
	if (Peggy2IO)		DeleteIORequest((struct IORequest *)Peggy2IO);
	if (PeggyMP)		DeleteMsgPort(PeggyMP);
	if (Peggy2MP)		DeleteMsgPort(Peggy2MP);

	if (mempool)		DeletePool(mempool);
	if (IntuitionBase)	CloseLibrary((struct Library *)IntuitionBase);
}
