rott/rott/fli_main.c

599 lines
13 KiB
C
Executable File

/*
Copyright (C) 1994-1995 Apogee Software, Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* readflic.c - This module contains the routines to read and decompress
* a flic. They assume Intel byte ordering, but otherwise should be
* fairly portable. They call machine specific stuff in pcclone.c.
* This file starts with the low level decompression routines - first
* for colors and then for pixels. Then it goes to the higher level
* exported flic_xxxx routines as prototyped in readflic.h.
*
* Copyright (c) 1992 Jim Kent. This file may be freely used, modified,
* copied and distributed. This file was first published as part of
* an article for Dr. Dobb's Journal March 1993 issue.
*/
#include "cin_glob.h"
#include <errno.h>
#include <string.h>
#include <io.h>
#include "fli_type.h"
#include "fli_util.h"
#include "fli_def.h"
#include "fli_main.h"
//MED
#include "memcheck.h"
typedef void ColorOut(Screen *s, int start, Color *colors, int count);
/* This is the type of output parameter to our decode_color below.
* Not coincedently screen_put_color is of this type. */
static void decode_color(Uchar *data
, Flic *flic, Screen *s, ColorOut *output)
/* Decode color map. Put results into output. The two color
* compressions are identical except for whether the RGB values
* are 0-63 or 0-255. Passing in an output that does the appropriate
* shifting on the way to the real pallete lets us use the same
* code for both COLOR_64 and COLOR_256 compression.
*/
{
int start = 0;
Uchar *cbuf = (Uchar *)data;
Short *wp = (Short *)cbuf;
Short ops;
int count;
ops = *wp;
cbuf += sizeof(*wp);
while (--ops >= 0)
{
start += *cbuf++;
if ((count = *cbuf++) == 0)
count = 256;
(*output)(s, start, (Color *)cbuf, count);
cbuf += 3*count;
start += count;
}
}
static void decode_color_256(Uchar *data, Flic *flic, Screen *s)
/* Decode COLOR_256 chunk. */
{
decode_color(data, flic, s, screen_put_colors);
}
static void decode_color_64(Uchar *data, Flic *flic, Screen *s)
/* Decode COLOR_64 chunk. */
{
decode_color(data, flic, s, screen_put_colors_64);
}
static void decode_byte_run(Uchar *data, Flic *flic, Screen *s)
/* Byte-run-length decompression. */
{
int x,y;
int width = flic->head.width;
int height = flic->head.height;
Char psize;
Char *cpt = data;
int end;
y = flic->yoff;
end = flic->xoff + width;
while (--height >= 0)
{
x = flic->xoff;
cpt += 1; /* skip over obsolete opcount byte */
psize = 0;
while ((x+=psize) < end)
{
psize = *cpt++;
if (psize >= 0)
{
screen_repeat_one(s, x, y, *cpt++, psize);
}
else
{
psize = -psize;
screen_copy_seg(s, x, y, (Pixel *)cpt, psize);
cpt += psize;
}
}
y++;
}
}
static void decode_delta_fli(Uchar *data, Flic *flic, Screen *s)
/* Fli style delta decompression. */
{
int xorg = flic->xoff;
int yorg = flic->yoff;
Short *wpt = (Short *)data;
Uchar *cpt = (Uchar *)(wpt + 2);
int x,y;
Short lines;
Uchar opcount;
Char psize;
y = yorg + *wpt++;
lines = *wpt;
while (--lines >= 0)
{
x = xorg;
opcount = *cpt++;
while (opcount > 0)
{
x += *cpt++;
psize = *cpt++;
if (psize < 0)
{
psize = -psize;
screen_repeat_one(s, x, y, *cpt++, psize);
x += psize;
opcount-=1;
}
else
{
screen_copy_seg(s, x, y, (Pixel *)cpt, psize);
cpt += psize;
x += psize;
opcount -= 1;
}
}
y++;
}
}
static void decode_delta_flc(Uchar *data, Flic *flic, Screen *s)
/* Flc-style delta decompression. The data is word oriented though
* a lot of the control info (how to skip, how many words to
* copy) are byte oriented still to save space. */
{
int xorg = flic->xoff;
int yorg = flic->yoff;
int width = flic->head.width;
int x,y;
Short lp_count;
Short opcount;
int psize;
union {Short *w; Uchar *ub; Char *b; Pixels2 *p2;} wpt;
int lastx;
lastx = xorg + width - 1;
wpt.ub = data;
lp_count = *wpt.w++;
y = yorg;
goto LPACK;
SKIPLINES: /* Advance over some lines. */
y -= opcount;
LPACK: /* do next line */
if ((opcount = *wpt.w++) >= 0)
goto DO_SS2OPS;
if( ((Ushort)opcount) & 0x4000) /* skip lines */
goto SKIPLINES;
screen_put_dot(s,(Uchar)opcount,lastx,y); /* put dot at eol with low byte */
if((opcount = *wpt.w++) == 0)
{
++y;
if (--lp_count > 0)
goto LPACK;
goto OUT;
}
DO_SS2OPS:
x = xorg;
PPACK: /* do next packet */
x += *wpt.ub++;
psize = *wpt.b++;
if ((psize += psize) >= 0)
{
screen_copy_seg(s, x, y, (Pixel *)wpt.ub, psize);
x += psize;
wpt.ub += psize;
if (--opcount != 0)
goto PPACK;
++y;
if (--lp_count > 0)
goto LPACK;
}
else
{
psize = -psize;
screen_repeat_two(s, x, y, *wpt.p2++, psize>>1);
x += psize;
if (--opcount != 0)
goto PPACK;
++y;
if (--lp_count > 0)
goto LPACK;
}
OUT:
return;
}
static void decode_black(Uchar *data, Flic *flic, Screen *s)
/* Decode a BLACK chunk. Set frame to solid color 0 one
* line at a time. */
{
Pixels2 black;
int i;
int height = flic->head.height;
int width = flic->head.width;
int x = flic->xoff;
int y = flic->yoff;
black.pixels[0] = black.pixels[1] = 0;
for (i=0; i<height; ++i)
{
screen_repeat_two(s, x, y+i, black, width/2);
if (width & 1) /* if odd set last pixel */
screen_put_dot(s, x+width-1, y+i, 0);
}
}
static void decode_literal(Uchar *data, Flic *flic, Screen *s)
/* Decode a LITERAL chunk. Just copy data to screen one line at
* a time. */
{
int i;
int height = flic->head.height;
int width = flic->head.width;
int x = flic->xoff;
int y = flic->yoff;
for (i=0; i<height; ++i)
{
screen_copy_seg(s, x, y+i, (Pixel *)data, width);
data += width;
}
}
ErrCode SetupFlicAccess (Flic * flic)
{
if (flic->usefile==TRUE)
{
return file_open_to_read(&flic->handle, flic->name);
}
else
{
flic->flicoffset=0;
return Success;
}
}
ErrCode CopyNextFlicBlock (Flic * flic, MemPtr buf, Ulong size)
{
ErrCode err;
if (flic->usefile==TRUE)
{
err = file_read_big_block(flic->handle, buf, size);
return err;
}
else
{
memcpy(buf, flic->flicbuffer+flic->flicoffset, size);
flic->flicoffset+=size;
return Success;
}
}
void SetFlicOffset (Flic * flic, Ulong offset )
{
if (flic->usefile==TRUE)
{
lseek(flic->handle,offset,SEEK_SET);
}
else
{
flic->flicoffset = offset;
}
}
ErrCode flic_open(Flic *flic, char *name, MemPtr buf, Boolean usefile)
/* Open flic file. Read header and verify it's a flic.
* Seek to first frame. */
{
ErrCode err;
ClearStruct(flic); /* Start at a known state. */
flic->usefile=usefile; /* use file or buffer */
flic->name = name; /* Save name for future use. */
flic->flicbuffer=buf; /* save address of flicbuffer */
if ((err = SetupFlicAccess (flic)) >= Success)
{
if ((err = CopyNextFlicBlock (flic, (Uchar *)&flic->head, sizeof(flic->head)))
>= Success)
{
if (flic->head.type == FLC_TYPE)
{
/* Seek frame 1. */
SetFlicOffset (flic, flic->head.oframe1 );
return Success;
}
if (flic->head.type == FLI_TYPE)
{
/* Do some conversion work here. */
flic->head.oframe1 = sizeof(flic->head);
flic->head.speed = flic->head.speed * 1000L / 70L;
return Success;
}
else
{
err = ErrBadFlic;
}
}
}
flic_close(flic); /* Close down and scrub partially opened flic. */
return err;
}
void flic_close(Flic *flic)
/* Close flic file and scrub flic. */
{
if (flic->usefile==TRUE)
{
close(flic->handle);
}
ClearStruct(flic); /* Discourage use after close. */
}
static ErrCode decode_frame(Flic *flic
, FrameHead *frame, Uchar *data, Screen *s)
/* Decode a frame that is in memory already into screen.
* Here we just loop through each chunk calling appropriate
* chunk decoder.
*/
{
int i;
ChunkHead *chunk;
for (i=0; i<frame->chunks; ++i)
{
chunk = (ChunkHead *)data;
data += chunk->size;
switch (chunk->type)
{
case COLOR_256:
decode_color_256((Uchar *)(chunk+1), flic, s);
break;
case DELTA_FLC:
decode_delta_flc((Uchar *)(chunk+1), flic, s);
break;
case COLOR_64:
decode_color_64((Uchar *)(chunk+1), flic, s);
break;
case DELTA_FLI:
decode_delta_fli((Uchar *)(chunk+1), flic, s);
break;
case BLACK:
decode_black((Uchar *)(chunk+1), flic, s);
break;
case BYTE_RUN:
decode_byte_run((Uchar *)(chunk+1), flic, s);
break;
case LITERAL:
decode_literal((Uchar *)(chunk+1), flic, s);
break;
default:
break;
}
}
return Success;
}
ErrCode flic_next_frame(Flic *flic, Screen *screen)
/* Advance to next frame of flic. */
{
FrameHead head;
ErrCode err;
MemPtr bb;
long size;
if ((err = CopyNextFlicBlock (flic, (Uchar *)&head, sizeof(head))) >= Success)
{
if (head.type == FRAME_TYPE)
{
size = head.size - sizeof(head); /* Don't include head. */
if (size > 0)
{
if ((err = big_alloc(&bb, size)) >= Success)
{
if ((err = CopyNextFlicBlock (flic, bb, size)) >= Success)
{
err = decode_frame(flic, &head, bb, screen);
}
big_free(&bb);
}
}
}
else
{
err = ErrBadFrame;
}
}
return err;
}
static Ulong calc_end_time(Ulong millis)
/* Little helper subroutine to find out when to start on next
* frame. */
{
return (GetCinematicTime() + ( (millis * CLOCKSPEED) / 4000l) );
}
static ErrCode wait_til(Ulong end_time, Machine *machine)
/* This waits until key is hit or end_time arrives.
* Return Success if timed out, ErrCancel if key hit.
* Insures keyboard will be polled at least once.
*/
{
do
{
if (CinematicAbort()!=0)
return ErrCancel;
}
while (GetCinematicTime() < end_time);
return Success;
}
ErrCode flic_play_once(Flic *flic, Machine *machine)
/* Play a flic through once. */
{
ErrCode err;
int i;
Ulong end_time;
for (i=0; i<flic->head.frames; ++i)
{
end_time = calc_end_time(flic->head.speed);
if ((err = flic_next_frame(flic, &machine->screen)) < Success)
break;
if ((err = wait_til(end_time, machine)) < Success)
break;
}
return err;
}
static ErrCode fill_in_frame2(Flic *flic)
/* This figures out where the second frame of the flic is
* (useful for playing in a loop). */
{
FrameHead head;
ErrCode err;
SetFlicOffset (flic, flic->head.oframe1 );
if ((err = CopyNextFlicBlock (flic, (MemPtr)&head, sizeof(head))) < Success)
return err;
flic->head.oframe2 = flic->head.oframe1 + head.size;
return Success;
}
ErrCode flic_play_loop(Flic *flic, Machine *machine)
/* Play a flic until key is pressed. */
{
int i;
Ulong end_time;
ErrCode err;
if (flic->head.oframe2 == 0)
{
fill_in_frame2(flic);
}
/* Seek to first frame. */
SetFlicOffset (flic, flic->head.oframe1 );
/* Save time to move on. */
end_time = calc_end_time(flic->head.speed);
/* Display first frame. */
if ((err = flic_next_frame(flic, &machine->screen)) < Success)
return err;
for (;;)
{
/* Seek to second frame */
SetFlicOffset (flic, flic->head.oframe2 );
/* Loop from 2nd frame thru ring frame*/
for (i=0; i<flic->head.frames; ++i)
{
if (wait_til(end_time, machine) < Success)
return Success; /* Time out is a success here. */
if ((err = flic_next_frame(flic, &machine->screen)) < Success)
return err;
end_time = calc_end_time(flic->head.speed);
}
}
}
static char *err_strings[] =
{
"Unspecified error",
"Not enough memory",
"Not a flic file",
"Bad frame in flic",
NULL,
NULL,
"Couldn't open display",
"Couldn't open keyboard",
"User canceled action",
};
char *flic_err_string(ErrCode err)
/* Return a string that describes an error. */
{
if (err >= Success)
return "Success"; /* Shouldn't happen really... */
if (err == ErrOpen || err == ErrRead)
return strerror(errno); /* Get Disk IO error from DOS. */
err = -err;
err -= 1;
if (err > ArrayEls(err_strings))
return "Unknown error";
return err_strings[err];
}
static void center_flic(Flic *flic, Screen *s)
/* Set flic.xoff and flic.yoff so flic plays centered rather
* than in upper left corner of display. */
{
flic->xoff = (screen_width(s) - (signed)flic->head.width)/2;
flic->yoff = (screen_height(s) - (signed)flic->head.height)/2;
}
void PlayFlic ( char * name, unsigned char * buffer, int usefile, int loop)
{
ErrCode err;
Flic flic;
Machine machine;
if ((err = machine_open(&machine)) >= Success)
{
if ((err = flic_open(&flic, name, buffer, usefile)) >= Success)
{
center_flic(&flic, &machine.screen);
if (loop==0)
{
err = flic_play_once(&flic, &machine);
}
else
{
err = flic_play_loop(&flic, &machine);
}
flic_close(&flic);
}
machine_close(&machine);
}
if (err < Success && err != ErrCancel)
{
Error("Play Flic had troubles with %s.\n%s.\n",name, flic_err_string(err));
}
}