Channels ▼
RSS

.NET

The Flic File Format

Source Code Accompanies This Article. Download It Now.


MAR93: THE FLIC FILE FORMAT

A fast and simple file format for graphics and animation

This article contains the following executables: FLIC.ARC

Jim Kent

Jim is the author of Autodesk Animator and can be contacted at Dancing Flame, 305 Dolan Ave., Mill Valley, CA 94941.


Flic files are a sequence of still frames which can be flipped through rapidly to achieve the illusion of movement--the software equivalent of movies. I developed the flic file format for the Autodesk Animator. A number of other multimedia applications, including IBM's Ultimedia Tool Series and Microsoft's Video for Windows, have since supported it. Although flics provide only 256 colors and don't contain sound, the speed and simplicity of playing them back has made them the format of choice for many artists and game developers. This article explains the flic file format and presents C programs that let you play flic files.

There are two types of flic files: .FLI and .FLC. The .FLI files are older and are limited to 32Ox2OO resolution. The newer .FLC files use a faster compression scheme, have a slightly different header, and can have any resolution. The program presented with this article is capable of playing both .FLI and .FLC files.

Overview of File Structure

The key idea behind flic delta compression is simple: You only store the parts of the picture that change from frame to frame. This helps make the file much smaller than if each frame had to be stored completely. Delta compression also makes it possible to quickly display a flic, even on a relatively slow display device, since only the parts of the screen that are changing need to be updated. Flic files use a simple-minded, run-length compression scheme on the bits of the picture that do change from frame to frame. The entire first frame is stored run-length compressed as well. Figure 1(a) shows an overview of the file structure.

Figure 1:(a) Overview of the flic-file stucture; (b)flic-file hierarchy of chunks.

  (a)

  file header
  optional prefix chunk
  first frame (run-length compressed)
  second frame (delta compressed)
          ...
  nth frame (delta compressed)
  ring frame (delta between nth frame and first)

(b)

  Flic chunk (includes header and rest of file)
     prefix chunk (in .FLC files only)
        settings chunk (Animator Pro private area)
          position chunk (Offset of cel into screen)
     frame 1 chunk
          postage stamp (icon first frame, .FLC only)
          color data (256 colors)
          pixel data (normally run length encoded)
     frame 2 chunk
          color data (only colors that change)
          pixel data (delta encoded)
...
     ring frame chunk
          color data (only colors that change)
          pixel data (delta encoded)

The flic includes the ring frame so the flic can be played repeatedly without a perceptible pause between the last frame and the first. This is necessary since unpacking the first frame (which is run-length compressed) is generally much slower than updating the screen from a delta.

Flic files are structured in a hierarchy of chunks. A chunk contains the size of itself, a type, and some data peculiar to whatever type of chunk it is. The hierarchy in a flic is three levels deep, as shown in Figure 1(b). The advantage of the chunk structure is that new types of chunks can be added to the file format without breaking existing flic readers. A flic player simply skips over chunks it does not understand or is not interested in.

Reading a Flic

To read a flic, first open the file, read in the 128-byte flic header (see Table 1), and check that the type field has an appropriate number. If the type field indicates it is an .FLI file, convert the speed field from 1/70th to 1/1000th of a second. If it's an .FLC file, seek to the first frame position as defined by the oframe1 field of the header. You can then read in a 16-byte frame header (see Tables 2 and 3). It's good to check the type field here, as well, and report an error if it doesn't match hexadecimal F1FA. The size of the data portion of the frame will vary. You can allocate buffer space according to the size field of the header minus 16 (since the size field includes the size of the header) and read the rest of the frame into the buffer. To decode the data in the buffer, loop through each chunk, dispatching the ones you recognize to routines specific to that chunk type. Table 4 lists the chunk types that occur inside a frame.

Table 1: Flic header structure (128 bytes). Fields marked .FLC only are set to 0 in an .FLI.

  Offset   Size   Name     Description

    0      4      size     Size of entire file.
    4      2      type     File-format identifier.  Always hex
                           AF12 for .FLC; AF11 for .FLI files.
    6      2      frames   Number of frames in flic.
    8      2      width    Screen width in pixels.
   10      2      height   Screen height in pixels.
   12      2      depth    Bits per pixel (always 8).
   14      2      flags    Set to hex 0003.
   16      4      speed    Time delay between frames.  For .FLI
                           files, in units of 1/70 second; for .FLC,
                           in milliseconds.
   22      4      created  MS-DOS-formatted date and time of
                           file's creation (.FLC only).
   26      4      creator  Animator Pro sets this to serial number
                           of copy of program that created flic.
                           Safe to set to 0 and ignore (.FLC only).
   30      4      updated  MS-DOS-formatted date and time of
                           file's most recent update (.FLC only).
   34      4      updater  Serial number of program that last
                           up-dated file.  See creator (.FLC only).
   38      2      aspectx  X-axis aspect ratio of display where file
                           was created.  A rectangle of dimensions
                           aspectx by aspecty will appear square.
                           For 320x200 display, aspect ratio is
                           6:5.  For most other displays, 1:1.
                           (.FLC only).
   40      2      aspecty  Y-axis aspect ratio of flic (.FLC only).
   42     38      reserved Set to 0.
   80      4      oframe 1 Offset from beginning of file to first frame
                           (.FLC only).
   84      4      oframe 2 Offset from beginning of file to second
                           frame (.FLC only).
   88     40      reserved Set to 0.

Table 2: Prefix header structure (16 bytes). Most programs just seek over the prefix chunk if it is present.

Offset  Size  Name     Description

   0     4   size      Size of prefix chunk, including subchunks.
   4     2   type      Prefix chunk identifier.  Always hex F100.
   6     2   chunks    Number of subchunks.
   8     8   reserved  Set to 0.

Table 3: Frame header structure (16 bytes).

Offset  Size  Name      Description

   0     4    size      Size of frame chunk, including subchunks
                        and this header.
   4     2    type      Prefix chunk identifier.  Always hex F1FA.
   6     2    chunks    Number of subchunks.
   8     8    reserved  Set to 0.

Table 4: Frame subchunk types.

Value  Name       Description

   4   COLOR_256  256-level color palette information (.FLC
                  only).
   7   DELTA_FLC  Delta compression (.FLC only).
  11   COLOR_64   64-level color pallete information (.FLI only).
  12   DELTA_FLI  Delta compression (.FLI only).
  13   BLACK      Entire frame set to color 0.
  15   BYTE_RUN   Byte run-length compression (first frame).
  16   LITERAL    Uncompressed pixels.
  18   PSTAMP     Miniature image of flic for visual file
                  requestor.  First frame only, safe to ignore.

Decoding subsequent frames is just like decoding the first. Some chunks appear only in the first frame and others appear only in subsequent frames, but it is convenient to decode all fr mes with a single routine that includes all chunk types in its central switch. A flic reader reads and decodes one frame after the other until it comes to the last (ring) frame. If the flic reader only wants to play the flic once, it stops here. If it wishes to play the flic again, it reads and decodes the ring frame, then seeks back to the second frame of the flic to start over.

Decoding Chunk Types

There are a variety of different chunk types, and understanding how to decode them is central to writing flic-supported programs. The file READFLIC.C (Listing One , page 92) presents routines that read and decompress a flic. The routines assume Intel byte ordering, but are otherwise fairly portable. The file starts with the low-level decompression routines: first for colors, then for pixels. It then goes to the higher-level exported flic_xxxx routines as prototyped in READFLIC.H (Listing Two, page 96). The program requires other files, such as those that attend to machine-specific details. Because of their length, these files and an executable version of the program are available only electronically; see "Availability," page 5. In this section, I'll describe in detail the available chunk types.

Chunk Type 16 (FLI_COPY). No compression. This chunk contains an uncompressed image of the frame. The number of pixels following the chunk header is exactly the width of the animation times its height. The data starts in the upper-left corner with pixels copied from left to right and then top to bottom. Chunk 16 is created when the preferred compression method generates more data than the uncompressed frame image--a relatively rare situation.

Chunk Type 13 (BLACK). No data. This chunk has no data following the header. All pixels in the frame are set to color-index 0.

Chunk Type 15 (BYTE_RUN). Byte run-length compression. This chunk contains the entire image in a compressed format. Usually this chunk is used in the first frame of an animation, or within a postage-stamp image chunk.

The data is organized in lines. Each line contains packets of compressed pixels. The first line is at the top of the animation, followed by subsequent lines moving downward. The number of lines in this chunk is given by the height of the animation. The first byte of each line is a count of packets in the line. This value is ignored--it is a holdover from the original Animator. It is possible to generate more than 255 packets on a line. The width of the animation is now used to drive the decoding of packets on a line; continue reading and processing packets until width pixels have been processed, then proceed to the next line.

Each packet consist of a type/size byte, followed by one or more pixels. If the packet type is negative, it is a count of pixels to be copied from the packet to the animation image. If the packet type is positive, it contains a single pixel that is to be replicated; the absolute value of the packet type is the number of times the pixel is to be replicated.

Chunk Type 12 (DELTA_FLI). Byte-oriented delta compression. This chunk contains the differences between the previous frame and this frame. This compression method was used by the original Animator, but is not created by Animator Pro. This type of chunk can appear in an Animator Pro file, however, if the file was originally created by Animator and some (but not all) frames were modified using Animator Pro.

The first 16-bit word following the chunk header contains the position of the first line in the chunk. This is a count of lines (down from the top of the image) unchanged from the prior frame. The second 16-bit word contains the number of lines in the chunk. The data for the lines follows these two words. Each line begins with two bytes. The first byte of each line contains the number of packets for the line. Unlike BRUN compression, the packet count is significant (because this compression method is only used on 32Ox2OO flics).

Each packet consists of a single-byte column skip, followed by a packet type/size byte. If the packet type is positive, it is a count of pixels to be copied from the packet to the animation image. If the packet type is negative, it contains a single pixel which is to be replicated; the absolute value of the packet type gives the number of times the pixel is to be replicated. (The negative/positive meaning of the packet-type bytes in DELTA_FLI compression is reversed from that used in BYTE_RUN compression. This gives better performance during playback.)

Chunk Type 7 (DELTA_FLC). Word-oriented delta compression. This format contains the differences between consecutive frames. This is the format most often used by Animator Pro for frames other than the first frame of an animation. It is similar to the byte-oriented delta (DELTA_FLI) compression, but is word oriented instead of byte oriented. The data is organized into lines, and each line is organized into packets.

The first word in the data following the chunk header contains the number of lines in the chunk. Each line can begin with some optional words that are used to skip lines and set the last byte in the line for animations with odd widths. These optional words are followed by a count of the packets in the line. The line count does not include skipped lines.

The high-order two bits of the word are used to determine the contents of the word, as shown in Table 5. The packets in each line are similar to the packets for the line-coded chunk. The first byte of each packet is a column-skip count. The second byte is a packet type. If the packet type is positive, the packet type is a count of words to be copied from the packet to the animation image. If the packet type is negative, the packet contains one more word which is to be replicated. The absolute value of the packet type gives the number of times the word is to be replicated. The high-and low-order byte in the replicated word do not necessarily have the same value.

Table 5: High-order two bits of the word determine the contents of the word.

Bit 15  Bit 14  Meaning

   0       0    Word contains packet count.  Packets follow this
                word.  Packet count can be 0; occurs when only
                last pixel on a line changes.

   1       0    Low-order byte is to be stored in the last byte of
                current line.  Packet count always follows this
                word.

   1       1    Word contains a line skip count.  Number of lines
                skipped is given by absolute value of word.
                Word can be followed by more skip counts, a
                last byte word, or packet count.

Chunk Type 4 (COLOR_256). 256-level color map. The data in this chunk is organized in packets. The first word following the chunk header is a count of the number of packets in the chunk. Each packet consists of a 1-byte color-index skip count, a 1-byte color count, and three bytes of color in formation for each color defined.

At the start of the chunk, the color index is assumed to be 0. Before processing any colors in a packet, the color-index skip count is added to the current color index. The number of colors defined in the packet is retrieved. A 0 in this byte indicates that 256 colors follow. The three bytes for each color define the red, green, and blue components of the color in that order. Each component can range from 0 (off) to 255 (full on). The data to change colors 2, 7, 8, and 9 would appear as in Example 1.

Example 1: Data to change colors 2, 7, 8, and 9.

   2                                ; two packets
   2, 1, r, g, b                    ; skip 2, change 1
   4, 3, r, g, b, r, g, b, r, g, b  ; skip 4, change 3

Chunk Type 11 (COLOR_64). 64-level color map. This chunk is identical to FLI_COLOR256 except that the values for the red, green, and blue components are in the range of 0-63 instead of 0-255.

Limitations and Conclusions

In the worst case, the Animator will currently produce first frames that are 7102+width * height bytes and subsequent frames that are 802+width * height bytes. Fortunately, in most cases, significant image compression is possible for hand-drawn and computer-synthesized imagery. Digitized video imagery does not compress well with the simple lossless run-length delta-compression schemes used in a flic. Frames of objects moving against a solid dark background may compress significantly even if taken from video; but lighter-colored backgrounds often contain enough noise on the video signal to foil the flic compression scheme even if the camera isn't moving. Still, for those of us lacking MPEG hardware, flics are one of the best ways of storing and displaying moving digital imagery on our computers.


_THE FLIC FILE FORMAT_
by Jim Kent


[LISTING ONE]
<a name="00af_0010">

/* readflic.c -- Routines to read and decompress a flic. Assumes Intel byte
 * ordering, but otherwise should be fairly portable. Calls machine-specific
 * stuff in pcclone.c. This file starts with the low-level decompression
 * routines: first for colors, then for pixels. It then goes to 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 <errno.h>
#include <string.h>
#include <io.h>
#include "types.h"
#include "pcclone.h"
#include "flic.h"
#include "readflic.h"

typedef void ColorOut(Screen *s, int start, Color far *colors, int count);
    /* This is the type of output parameter to our decode_color below.
     * Not coincidently screen_put_color is of this type. */
static void decode_color(Uchar huge *data
, Flic *flic, Screen *s, ColorOut *output)
    /* Decode color map. Put results into output. Two color compressions
         * are identical except that RGB values are 0-63 or 0-255. Passing in
         * an output that does appropriate shifting on way to real pallete lets
         * us use the same code for both COLOR_64 and COLOR_256 compression. */
{
int start = 0;
Uchar far *cbuf = (Uchar far *)data;
Short far *wp = (Short far *)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 far *)cbuf, count);
    cbuf += 3*count;
    start += count;
    }
}
static void decode_color_256(Uchar huge *data, Flic *flic, Screen *s)
    /* Decode COLOR_256 chunk. */
{
decode_color(data, flic, s, screen_put_colors);
}
static void decode_color_64(Uchar huge *data, Flic *flic, Screen *s)
    /* Decode COLOR_64 chunk. */
{
decode_color(data, flic, s, screen_put_colors_64);
}
static void decode_byte_run(Uchar huge *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 huge *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 far *)cpt, psize);
            cpt += psize;
            }
        }
    y++;
    }
}
static void decode_delta_fli(Uchar huge *data, Flic *flic, Screen *s)
    /* Fli style delta decompression. */
{
int xorg = flic->xoff;
int yorg = flic->yoff;
Short huge *wpt = (Short huge *)data;
Uchar huge *cpt = (Uchar huge *)(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 far *)cpt, psize);
            cpt += psize;
            x += psize;
            opcount -= 1;
            }
        }
    y++;
    }
}
static void decode_delta_flc(Uchar huge *data, Flic *flic, Screen *s)
    /* Flc-style delta decompression. Data is word-oriented. Much control info
     * (how far to skip, how many words to copy) are still byte-oriented. */
{
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 huge *w; Uchar huge *ub; Char huge *b; Pixels2 huge *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); /* eol dot 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 far *)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 huge *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 huge *data, Flic *flic, Screen *s)
    /* Decode a LITERAL chunk. 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 far *)data, width);
    data += width;
    }
}
ErrCode flic_open(Flic *flic, char *name)
    /* Open flic file. Read header, verify it's a flic. Seek to first frame. */
{
ErrCode err;
ClearStruct(flic);      /* Start at a known state. */
if ((err = file_open_to_read(&flic->handle, name)) >= Success)
     {
     if ((err = file_read_block(flic->handle, &flic->head, sizeof(flic->head)))
     >= Success)
        {
        flic->name = name;      /* Save name for future use. */
        if (flic->head.type == FLC_TYPE)
            {
            /* Seek frame 1. */
            lseek(flic->handle,flic->head.oframe1,SEEK_SET);
            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. */
{
close(flic->handle);
ClearStruct(flic);      /* Discourage use after close. */
}
static ErrCode decode_frame(Flic *flic
, FrameHead *frame, Uchar huge *data, Screen *s)
    /* Decode a frame that is in memory already into screen. Here we
     * loop through each chunk calling appropriate chunk decoder. */
{
int i;
ChunkHead huge *chunk;
for (i=0; i<frame->chunks; ++i)
    {
    chunk = (ChunkHead huge *)data;
    data += chunk->size;
    switch (chunk->type)
        {
        case COLOR_256:
            decode_color_256((Uchar huge *)(chunk+1), flic, s);
            break;
        case DELTA_FLC:
            decode_delta_flc((Uchar huge *)(chunk+1), flic, s);
            break;
        case COLOR_64:
            decode_color_64((Uchar huge *)(chunk+1), flic, s);
            break;
        case DELTA_FLI:
            decode_delta_fli((Uchar huge *)(chunk+1), flic, s);
            break;
        case BLACK:
            decode_black((Uchar huge *)(chunk+1), flic, s);
            break;
        case BYTE_RUN:
            decode_byte_run((Uchar huge *)(chunk+1), flic, s);
            break;
        case LITERAL:
            decode_literal((Uchar huge *)(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;
BigBlock bb;
long size;
if ((err = file_read_block(flic->handle, &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 = file_read_big_block(flic->handle, &bb, size))
            >= Success)
            {
                  err = decode_frame(flic, &head, bb.hpt, screen);
                    }
                big_free(&bb);
                }
            }
        }
    else
        {
        err = ErrBadFrame;
        }
    }
return err;
}
static Ulong calc_end_time(Ulong millis, Clock *clock)
    /* Little helper subroutine to find out when to start on next frame. */
{
return clock_ticks(clock) + millis * clock->speed / 1000l;
}
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 poll at least once. */
{
    do
        {
        if (key_ready(&machine->key))
            {
            key_read(&machine->key);
            return ErrCancel;
            }
        }
    while (clock_ticks(&machine->clock) < 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, &machine->clock);
    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 determines where second frame of flic is (useful for a loop). */
{
FrameHead head;
ErrCode err;
lseek(flic->handle, flic->head.oframe1, SEEK_SET);
if ((err = file_read_block(flic->handle, &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. */
lseek(flic->handle, flic->head.oframe1, SEEK_SET);
    /* Save time to move on. */
end_time = calc_end_time(flic->head.speed, &machine->clock);
    /* Display first frame. */
if ((err = flic_next_frame(flic, &machine->screen)) < Success)
    return err;
for (;;)
    {
        /* Seek to second frame */
    lseek(flic->handle, flic->head.oframe2, SEEK_SET);
        /* 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, &machine->clock);
        }
    }
}
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];
}




<a name="00af_0011">
<a name="00af_0012">
[LISTING TWO]
<a name="00af_0012">

/* Readflic.h -- Prototypes and other structural info for readflic program.
 * 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.  */

/* Some handy macros I use in lots of programs: */
#define ArrayEls(a) (sizeof(a)/sizeof((a)[0]))
    /* Count up number of elements in an array */
#define ClearMem(buf,size)  memset(buf, 0, size)
    /* Clear a block of memory. */
#define ClearStruct(pt) ClearMem(pt, sizeof(*(pt)))
    /* Clear a structure (pass in pointer) */
/* Data structures peculiar to readflic program: */
typedef struct
    {
    FlicHead head;  /* Flic file header. */
    int handle;     /* File handle. */
    int frame;      /* Current frame in flic. */
    char *name;     /* Name from flic_open. */
    int xoff,yoff;  /* Offset to display flic at. */
    } Flic;
/* Prototypes peculiar to readflic program: */
ErrCode flic_open(Flic *flic, char *name);
    /* Open flic file.  Read header and verify it's a flic. */
void flic_close(Flic *flic);
    /* Close flic file and scrub flic. */
ErrCode flic_play_once(Flic *flic, Machine *machine);
    /* Play a flic through once. */
ErrCode flic_play_loop(Flic *flic, Machine *machine);
    /* Play a flic until key is pressed. */
ErrCode flic_next_frame(Flic *flic, Screen *screen);
    /* Advance to next frame of flic. */
/* Various error codes flic reader can get. */
#define ErrNoMemory -2      /* Not enough memory. */
#define ErrBadFlic  -3      /* File isn't a flic. */
#define ErrBadFrame -4      /* Bad frame in flic. */
#define ErrOpen     -5      /* Couldn't open file.  Check errno. */
#define ErrRead     -6      /* Couldn't read file.  Check errno. */
#define ErrDisplay  -7      /* Couldn't open display. */
#define ErrClock    -8      /* Couldn't open clock. */
#define ErrKey      -9      /* Couldn't open keyboard. */
#define ErrCancel   -10     /* User cancelled. */






Copyright © 1993, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.