
/* main.c - the central module for playriff. 
	This module includes the global variables, the main() routine, and
	various routines to manage the memory etc. for our screen.  */
	
#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <functions.h>
#include <ctype.h>
#include "jiff.h"
#include "vcomp.h"
#include "playriff.h"

/* Height and width of a character */
#define CH_WIDTH 8
#define CH_HEIGHT 10

extern long get60hz();	/* a 60Hz clock */

/* Yer standard libraries to do anything graphical with a user interface */
struct Library *IntuitionBase, *GfxBase;

struct BitMap demo_BitMap;	/* Our custom bitmap */
struct BitMap back_BitMap;	/* where to decompress etc */
struct Screen *demo_screen;	/* our own custom screen */
struct Window *demo_window;	/* borderless backdrop window on demo_screen*/
struct NewScreen ns;	/* to help open demo_screen */
struct NewWindow nw;	/* to help open demo_window */
struct Window *launch_window; /* Where AmigaDOS puts our errors initially */
struct Process *demo_process; /* our process */

long plane_size;
WORD colors;
int skipit;  /* is beam skipping enabled */

/* The number of frames loaded, and array of pointers to beginning of
   each frame */
short riff_count;	
struct vcomp_iff *riff_list[MAX_RIFFS];
WORD frame_ix;		/* the current frame on the screen */

/* the riff head contains our ViewModes and resolution info */
struct riff_head demo_rh;	/* head of last riff loaded */
struct riff_head first_rh;	/* head of first riff if loading more than one
  							into memory at once */

/* Little multiplication table ... BytesPerRow*0, BytesPerRow*1 ...
	BytesPerRow*Rows */
WORD *ytable;
long ytsize;	/* size of above table in bytes */

/* some globals to handle file i/o */
struct FileHandle *infile; /* handle for the riff file we're loading */
char *name;		/* name of file we're loading or saving */
char first_name[256];	/* name of first riff file in this batch loaded */
WORD fatal_load;	/* flag set if load failed badly */


/* Yer basic handle on the 80 character ROM font */
UBYTE topaz_name[] =  "topaz.font";
struct TextAttr topaz80 =
	{
    topaz_name,
    TOPAZ_EIGHTY,
    FS_NORMAL,
    FPF_ROMFONT,
	};    


/* SafeAllocMem() - like AllocMem, but makes sure Intuition still has
   enough memory to do it's thing with menus and windows. */
void *
SafeAllocMem(size, type)
long size, type;
{
char *pt;
char *safept;

if (size <= 0)
	return(NULL);
pt = AllocMem(size, type);
if (pt == NULL)
	return(NULL);
if ((safept = AllocMem(8000L, type) ) == NULL)
	{
	FreeMem(pt, size);
	return(NULL);
	}
FreeMem(safept, 8000L);
return(pt);
}

/* Print error message of up to two lines */
error(message1, message2)
char *message1, *message2;
{
long width, height;
long nwidth;

puts(message1);
if (message2 != NULL)
	puts(message2);
return;
}

outta_memory()
{
error("Out of memory!", NULL);
}

cant_open()
{
char sbuf[80];

sprintf(sbuf, "can't open %s", name);
error(sbuf, NULL);
}

truncated()
{
char *sbuf[80];

sprintf(sbuf, "%s truncated!", name);
error(sbuf, NULL);
}


/* match_res() - checks that the current riff is the same resolution as
   the first riff in the chain.  Squawks if its not. */
match_res()
{
char buf[60];

if (first_rh.width != demo_rh.width ||
	first_rh.height != demo_rh.height ||
	first_rh.depth != demo_rh.depth)
	{
	sprintf(buf, "%dx%dx%d and %dx%dx%d",
		first_rh.width, first_rh.height, first_rh.depth,
		demo_rh.width, demo_rh.height, demo_rh.depth);
	error("Resolution mismatch:", buf);
	return(0);
	}
return(1);
}

/* make_ytable() - makes up the multiplication table for the decompression
   routines */
make_ytable(width, height)
WORD width;
register WORD height;
{
register WORD linebytes;
register WORD *pt, acc;

free_ytable();	/* free previous y table */
linebytes = (((width+15)>>4)<<1);
ytsize = height * sizeof(WORD);
if ((ytable = SafeAllocMem(ytsize, 0L)) == NULL)
	return(0);
pt = ytable;
acc = 0;
while (--height >= 0)
	{
	*pt++ = acc;
	acc += linebytes;
	}
return(1);
}

/* free_ytable() - free up memory used by ytable */
free_ytable()
{
if (ytable != NULL)
	FreeMem(ytable, ytsize);
ytable = NULL;
}

/* close_demo_screen() - closes down our screen and window, and puts
   the process error handling window pointer back to where it was when
   we were launched */
close_demo_screen()
{
if (demo_window != NULL)
	{
	CloseWindow(demo_window);
	demo_window = NULL;
	}
if (demo_screen != NULL)
	{
	CloseScreen(demo_screen);
	demo_screen = NULL;
	}
free_BitMap(&demo_BitMap);
free_BitMap(&back_BitMap);
demo_process->pr_WindowPtr = (APTR)launch_window;
}

/* free_demo_riff() - free up the memory used by the riff frames in memory */
free_demo_riff()
{
struct vcomp_iff **riffs;
struct vcomp_iff *riff;
WORD i;

riffs = riff_list;
i = riff_count;
while (--i >= 0)
	{
	riff = *riffs;
	if (riff != NULL)
		FreeMem(riff, riff->iff_size + 8); /* plus 8 is sizeof type +
												sizeof lenght field */
	*riffs++ = NULL;
	}
riff_count = 0;
frame_ix = 0;
}

/* free_BitMap() - frees the bitplanes associated with a BitMap and set
   them to NULL */
free_BitMap(bm)
register struct BitMap *bm;
{
if (bm->Planes[0] != NULL)
	{
	FreeMem(bm->Planes[0], (long)bm->BytesPerRow * bm->Rows * bm->Depth); 
	bm->Planes[0] = NULL;
	}
}

/* alloc_BitMap() - allocate the bit-planes for a BitMap.  Puts 'em in
   CHIP ram of course! */
alloc_BitMap(bm, w, h, d)
register struct BitMap *bm;
short w, h, d;
{
register short i;
register long plane_size;
register PLANEPTR bits;

plane_size = PlaneSize(w, h);
bm->Rows = h;
bm->BytesPerRow = LineBytes(w);
bm->Depth = d;
bm->Planes[0] = NULL;
if ((bits = SafeAllocMem(plane_size * d, (long)MEMF_CHIP)) == NULL)
	return(0);
for (i=0; i<d; i++)	
	{
	bm->Planes[i] = bits;
	bits += plane_size;
	}
return(1);
}

/* some variable to keep track of what screen-resolution and mode we're
   in to avoid having to close and re-open screens when possible */
static WORD ow, oh, od, omode;


/* get_draw_space_man() - man went out years ago.  Should be
   get_draw_space_dude().  Actually considering it's getting it
   from Intuition perhaps it should be get_draw_space_idiot().  Anyways
   this baby opens a screen and a backdrop window of the indicated dimensions
   and ViewMode.  Also initializes the ytable to the screen dimensions, and
   allocates the demo_BitMap. */
get_draw_space_man(w, h, d, mode)
WORD w, h, d, mode;
{
ow = w;
oh = h;
od = d;
omode = mode;
if (!make_ytable(w, h))
	{
	outta_memory();
	return(0);
	}
if (!alloc_BitMap(&demo_BitMap, w, h, d) )
	{
	outta_memory();
	return(0);
	}
if (!alloc_BitMap(&back_BitMap, w, h, d) )
	{
	outta_memory();
	return(0);
	}
plane_size = h*demo_BitMap.BytesPerRow;
if (ns.ViewModes & HAM)
	colors = 16;
else
	colors = (1<<d);

skipit = (w < 400 && h < 300);	/* beam skip if lo-res non-interlace... */
nw.Width = nw.MinWidth = nw.MaxWidth = ns.Width = w;
nw.Height = nw.MinHeight = nw.MaxHeight = ns.Height = h;
ns.Depth = d;
ns.ViewModes = mode;
ns.Type = CUSTOMSCREEN | CUSTOMBITMAP;
ns.CustomBitMap = &demo_BitMap;
ns.Font = &topaz80;
if ((demo_screen = OpenScreen(&ns)) == NULL)
	return(0);
ShowTitle(demo_screen, 0L);
nw.LeftEdge = 0;
nw.TopEdge = 0;
nw.DetailPen = 0;
nw.BlockPen= 1;
nw.Title = NULL;
nw.Flags = BACKDROP| ACTIVATE
             |WINDOWDRAG|BORDERLESS|NOCAREREFRESH;
nw.IDCMPFlags = 
	MOUSEBUTTONS | MENUPICK; /* IDCMP flags */
nw.Type = CUSTOMSCREEN;
nw.FirstGadget = NULL;
nw.CheckMark = NULL;
nw.Screen = demo_screen;
nw.BitMap = NULL;
if ((demo_window = (struct Window *)OpenWindow(&nw)) == NULL)
	{
	CloseScreen(demo_screen);
	demo_screen = NULL;
	}
demo_process->pr_WindowPtr = (APTR)demo_window;
return(1);
}

/* open a screen with the dimensions/modes from the riff-header we just
   loaded */
screen_for_rh()
{
return(get_draw_space_man(demo_rh.width, demo_rh.height, demo_rh.depth,
	demo_rh.ViewModes) );
}

#define BPOS 47
#define BWIN 40

skip_beam()
{
register WORD pos;

for (;;)
	{
	pos = VBeamPos();
	if (pos > BPOS && pos < BPOS+BWIN)
		break;
	}
}


/* play_demo_riff()
	loop around blasting riffs to the screen until someone hits a mouse
	button */
play_demo_riff(loops)
int loops;
{
int i;
register long dest_time;
register long wait_time;

wait_time = demo_rh.jiffies_frame;
for (i=0; i<loops; i++)
	{
	for ( ; frame_ix<riff_count; frame_ix++)
		{
		dest_time = get60hz() + wait_time;
		unpack_it(riff_list[frame_ix]);
		if (poll_demo_window())
			{
			return;
			}
		while (get60hz() < dest_time)
			{
			if (poll_demo_window())
				return;
			wait_milli();
			}
		}
	frame_ix = 0;
	}
}


/* poll_demo_window() - see if anythings happening intui-wise.  Return
   1 if we've got a user event, 0 if nothing going on.  */
poll_demo_window()
{
struct IntuiMessage *message;   /* Gets input event   */

message = (struct IntuiMessage *)GetMsg(demo_window->UserPort);
if (message != NULL)
	{
	ReplyMsg( message );    /* Tell intuition we're through with event   */
	return(1);
	}
return(0);
}

USHORT *nopointer;

pointeroff()
{
if (demo_window != NULL)
	SetPointer(demo_window,nopointer,2,16,0,0);/* turn it back to off*/
}/* end of pointeroff*/

pointeron()
{
if (demo_window != NULL)
	ClearPointer(demo_window);
}


/* unpack_it() - unpack a single frame of a riff onto the screen */
unpack_it(head_buf)
register struct vcomp_iff *head_buf;
{
WORD w, h, d;
register char *in;
register char *out;
WORD i;
WORD ctype;
register WORD size;
register WORD linebytes;


in = (char *)(head_buf+1);

d = back_BitMap.Depth;
linebytes = back_BitMap.BytesPerRow;

for (i=0; i<d; i++)
	{
	ctype = head_buf->comps[i].comp;
	size = head_buf->comps[i].size;
	out = (char *)back_BitMap.Planes[i];
	switch (ctype)
		{
		case VCOMP_NONE:
			copy_structure(in, out, size);
			break;
		case VCOMP_VRUN:
			decode_vplane(in, out, linebytes);
			break;
		case VCOMP_SKIP:
			decode_vkplane(in, out, linebytes);
			break;
		}
	in += size;
	}
if (skipit)
	skip_beam();
qput_cmap(head_buf->cmap);
copy_lines_blit(back_BitMap.Planes[0], demo_BitMap.Planes[0],
	plane_size, back_BitMap.BytesPerRow, 
	back_BitMap.Rows, back_BitMap.Depth);
}


main(count, names)
int count;
char *names[];
{
demo_process = (struct Process *)FindTask(0L);	/* find our process */
/* see if under CLI.  Under 1.1 AmigaDOS the arc count would be like
   100000 or so from WB launched app's, 1.2 it's zero already */
if (demo_process->pr_CLI == NULL)
	return(0);	/* CLI only! */

if ((GfxBase = OpenLibrary("graphics.library", 0L)) == NULL)
	{
	return;
	}
if ((IntuitionBase = OpenLibrary("intuition.library", 0L)) == NULL)
	{
	return;
	}
if (!init_time())	/* try to get our little real-time 60Hz clock */
	{
	puts("can't init timer");
	return;
	}
launch_window = (struct Window *)demo_process->pr_WindowPtr;
if (nopointer = AllocMem(32L, MEMF_CHIP | MEMF_CLEAR) )
	cli_playriff(count, names);
if (nopointer)
	FreeMem(nopointer, 32L);
free_demo_riff();
free_ytable();
close_demo_screen();
uninit_time();
CloseLibrary(IntuitionBase);
CloseLibrary(GfxBase);
}

