[Back to EGAVGA SWAG index] [Back to Main SWAG index] [Original]

    {
    > Where can I get TP7 or TP6 source code for playing back FLI animations? If
    > there aren't any, is there an efficient, quick, and unnoticeable way to do
    > a shell to DOS and run a FLI player program? Any and all help is greatly
    > appreciated. Please, if possible, email to the address in my .sig below.

    Here is my the unit I use to play FLI files.  Hope it Helps.
    From: JOHARROW@homeloan.demon.co.uk (John O'Harrow)
    }

    {----------Written by John O'Harrow 1994----------}

    {$G+}
    UNIT FliPlay;

    INTERFACE

      PROCEDURE AAPlay(Filename : String); {Play FLI at default speed}

    TYPE
      PFliPlayer = ^TFliPlayer;
      TFliPlayer = OBJECT
        CONSTRUCTOR Init;
        DESTRUCTOR  Done;                      VIRTUAL;
        PROCEDURE   SetSpeed(Speed : Integer); VIRTUAL; {0=Fastest}
        PROCEDURE   ClearSpeed;                VIRTUAL;
        PROCEDURE   Play(Filename : String);   VIRTUAL;
      PRIVATE
        Buffer   : Pointer;
        Interval : Integer;
        FliFile  : File;
      END; {TFliPlayer}

    IMPLEMENTATION

    USES
      Crt;

    CONST
      Clock_Hz     = 4608;                   {Frequency of clock}
      Monitor_Hz   = 70;                     {Frequency of monitor}
      Clock_Scale  = Clock_Hz DIV Monitor_Hz;
      CData        = $40;                    {Port number of timer 0}
      CMode        = $43;                    {Port number of timer control word}
      BufSize      = 65528;                  {Frame buffer size - Must be even}
      MCGA         = $13;                    {Number for MCGA mode}

    TYPE
      MainHeaderRec = RECORD
        Padding1 : LongInt;
        ID       : Word;
        Frames   : Word;
        Padding2 : LongInt;
        Padding3 : LongInt;
        Speed    : Word;
        Padding4 : ARRAY[1..110] OF Char; {Pad to 128 Bytes}
      END; {MainHeaderRec}

      FrameHeaderRec = RECORD
        Size     : LongInt;
        Padding1 : Word;
        Chunks   : Word;
        Padding2 : ARRAY[1..8] OF Char; {Pad to 16 Bytes}
      END; {FrameHeaderRec}

    {---------------------------------------------------------------------------}

      PROCEDURE VideoMode(Mode : Word);
        INLINE ($58/$CD/$10); {POP AX/INT 10}

      PROCEDURE InitClock; ASSEMBLER; {Taken from the FLILIB source}
      ASM
        mov  al,00110100b
        out  CMode,al
        xor  al,al
        out  CData,al
        out  CData,al
      END; {InitClock}

      FUNCTION GetClock : LongInt; ASSEMBLER; {Taken from the FLILIB source}
      {this routine returns a clock with occassional spikes where time
        will look like its running backwards 1/18th of a second.  The resolution
        of the clock is 1/(18*256) = 1/4608 second.  66 ticks of this clock
        are supposed to be equal to a monitor 1/70 second tick.}
      ASM
        mov  ah,0                  {get tick count from Dos and use For hi 3 Bytes}
        int  01ah                  {lo order count in DX, hi order in CX}
        mov  ah,dl
        mov  dl,dh
        mov  dh,cl
        mov  al,0                  {read lo Byte straight from timer chip}
        out  CMode,al              {latch count}
        mov  al,1
        out  CMode,al              {set up to read count}
        in   al,CData              {read in lo Byte (and discard)}
        in   al,CData              {hi Byte into al}
        neg  al                    {make it so counting up instead of down}
      END; {GetClock}

      PROCEDURE DrawFrame(Buffer : Pointer; Chunks : Word); ASSEMBLER;
      {this is the routine that takes a frame and put it on the screen}
      ASM
        cli                        {disable interrupts}
        push ds
        push es
        lds  si,Buffer             {let DS:SI point at the frame to be drawn}
      @Fli_Loop:                   {main loop that goes through all the chunks in a
    frame}
        cmp  Chunks,0              {are there any more chunks to draw?}
        je   @Exit
        dec  Chunks                {decrement Chunks For the chunk to process now}
        mov  ax,[Word ptr ds:si+4] {let AX have the ChunkType}
        add  si,6                  {skip the ChunkHeader}
        cmp  ax,0Bh                {is it a FLI_COLor chunk?}
        je   @Fli_Color
        cmp  ax,0Ch                {is it a FLI_LC chunk?}
        je   @Fli_Lc
        cmp  ax,0Dh                {is it a FLI_BLACK chunk?}
        je   @Fli_Black
        cmp  ax,0Fh                {is it a FLI_BRUN chunk?}
        je   @Fli_Brun
        cmp  ax,10h                {is it a FLI_COPY chunk?}
        je   @Fli_Copy
        jmp  @Fli_Loop             {This command should not be necessary }
      @Fli_Color:
        mov  bx,[Word ptr ds:si]   {number of packets in this chunk (always 1?)}
        add  si,2                  {skip the NumberofPackets}
        mov  al,0                  {start at color 0}
        xor  cx,cx                 {reset CX}
      @Color_Loop:
        or   bx,bx                 {set flags}
        jz   @Fli_Loop             {Exit if no more packages}
        dec  bx                    {decrement NumberofPackages For the package to
    process now}
        mov  cl,[Byte ptr ds:si+0] {first Byte in packet tells how many colors to
    skip}
        add  al,cl                 {add the skiped colors to the start to get the
    new start}
        mov  dx,$3C8               {PEL Address Write Mode Register}
        out  dx,al                 {tell the VGA card what color we start changing}
        inc  dx                    {at the port abow the PEL_A_W_M_R is the PEL
    Data Register}
        mov  cl,[Byte ptr ds:si+1] {next Byte in packet tells how many colors to
    change}
        or   cl,cl                 {set the flags}
        jnz  @Jump_Over            {if NumberstoChange=0 then NumberstoChange=256}
        inc  ch                    {CH=1 and CL=0 => CX=256}
      @Jump_Over:
        add  al,cl                 {update the color to start at}
        mov  di,cx                 {since each color is made of 3 Bytes (Red, Green
    & Blue) we have to -}
        shl  cx,1                  {- multiply CX (the data counter) With 3}
        add  cx,di                 {- CX = old_CX shl 1 + old_CX   (the fastest way
    to multiply With 3)}
        add  si,2                  {skip the NumberstoSkip and NumberstoChange
    Bytes}
        rep  outsb                 {put the color data to the VGA card FAST!}
        jmp  @Color_Loop           {finish With this packet - jump back}
      @Fli_Lc:
        mov  ax,0A000h
        mov  es,ax                 {let ES point at the screen segment}
        mov  di,[Word ptr ds:si+0] {put LinestoSkip into DI -}
        mov  ax,di                 {- to get the offset address to this line we
    have to multiply With 320 -}
        shl  ax,8                  {- DI = old_DI shl 8 + old_DI shl 6 -}
        shl  di,6                  {- it is the same as DI = old_DI*256 + old_DI*64
    = old_DI*320 -}
        add  di,ax                 {- but this way is faster than a plain mul}
        mov  bx,[Word ptr ds:si+2] {put LinestoChange into BX}
        add  si,4                  {skip the LinestoSkip and LinestoChange Words}
        xor  cx,cx                 {reset cx}
      @Line_Loop:
        or   bx,bx                 {set flags}
        jz   @Fli_Loop             {Exit if no more lines to change}
        dec  bx
        mov  dl,[Byte ptr ds:si]   {put PacketsInLine into DL}
        inc  si                    {skip the PacketsInLine Byte}
        push di                    {save the offset address of this line}
      @Pack_Loop:
        or   dl,dl                 {set flags}
        jz   @Next_Line            {Exit if no more packets in this line}
        dec  dl
        mov  cl,[Byte ptr ds:si+0] {put BytestoSkip into CL}
        add  di,cx                 {update the offset address}
        mov  cl,[Byte ptr ds:si+1] {put BytesofDatatoCome into CL}
        or   cl,cl                 {set flags}
        jns  @Copy_Bytes           {no SIGN means that CL number of data is to come
    -}
                                   {- else the next data should be put -CL number
    of times}
        mov  al,[Byte ptr ds:si+2] {put the Byte to be Repeated into AL}
        add  si,3                  {skip the packet}
        neg  cl                    {Repeat -CL times}
        rep  stosb
        jmp  @Pack_Loop            {finish With this packet}
      @Copy_Bytes:
        add  si,2                  {skip the two count Bytes at the start of the
    packet}
        rep  movsb
        jmp  @Pack_Loop            {finish With this packet}
      @Next_Line:
        pop  di                    {restore the old offset address of the current
    line}
        add  di,320                {offset address to the next line}
        jmp  @Line_Loop
      @Fli_Black:
        mov  ax,0A000h
        mov  es,ax                 {let ES:DI point to the start of the screen}
        xor  di,di
        mov  cx,32000              {number of Words in a screen}
        xor  ax,ax                 {color 0 is to be put on the screen}
        rep  stosw
        jmp  @Fli_Loop             {jump back to main loop}
      @Fli_Brun:
        mov  ax,0A000h
        mov  es,ax                 {let ES:DI point at the start of the screen}
        xor  di,di
        mov  bx,200                {numbers of lines in a screen}
        xor  cx,cx
      @Line_Loop2:
        mov  dl,[Byte ptr ds:si]   {put PacketsInLine into DL}
        inc  si                    {skip the PacketsInLine Byte}
        push di                    {save the offset address of this line}
      @Pack_Loop2:
        or   dl,dl                 {set flags}
        jz   @Next_Line2           {Exit if no more packets in this line}
        dec  dl
        mov  cl,[Byte ptr ds:si]   {put BytesofDatatoCome into CL}
        or   cl,cl                 {set flags}
        js   @Copy_Bytes2          {SIGN meens that CL number of data is to come -}
                                   {- else the next data should be put -CL number
    of times}
        mov  al,[Byte ptr ds:si+1] {put the Byte to be Repeated into AL}
        add  si,2                  {skip the packet}
        rep  stosb
        jmp  @Pack_Loop2           {finish With this packet}
      @Copy_Bytes2:
        inc  si                    {skip the count Byte at the start of the packet}
        neg  cl                    {Repeat -CL times}
        rep  movsb
        jmp  @Pack_Loop2           {finish With this packet}
      @Next_Line2:
        pop  di                    {restore the old offset address of the current
    line}
        add  di,320                {offset address to the next line}
        dec  bx                    {any more lines to draw?}
        jnz  @Line_Loop2
        jmp  @Fli_Loop             {jump back to main loop}
      @Fli_Copy:
        mov  ax,0A000h
        mov  es,ax                 {let ES:DI point to the start of the screen}
        xor  di,di
        mov  cx,32000              {number of Words in a screen}
        rep  movsw
        jmp  @Fli_Loop             {jump back to main loop}
      @Exit:
        sti                        {enable interrupts}
        pop  es
        pop  ds
      END; {DrawFrame}

      CONSTRUCTOR TFliPlayer.Init;
      BEGIN
        IF MemAvail < BufSize THEN Fail;
        GetMem(Buffer,BufSize);
        ClearSpeed;
      END; {Init}

      DESTRUCTOR TFliPlayer.Done;
      BEGIN
        FreeMem(Buffer,BufSize);
      END; {Done}

      PROCEDURE TFliPlayer.SetSpeed(Speed : Integer);
      BEGIN
        Interval := Speed * Clock_Scale;
      END; {SetSpeed}

      PROCEDURE TFliPlayer.ClearSpeed;
      BEGIN
        Interval := -1;
      END; {ClearSpeed}

      PROCEDURE TFliPlayer.Play(Filename : String);
      VAR
        MainHeader  : MainHeaderRec;
        FrameHeader : FrameHeaderRec;
        FrameSize   : LongInt;
        RestartPos  : LongInt;
        Frame       : Word;
        Timeout     : LongInt;

        FUNCTION ReadHeader : Boolean;
        BEGIN
          BlockRead(FliFile,MainHeader,SizeOf(MainHeader)); {Read header record}
          WITH MainHeader DO
            IF ID <> $AF11 THEN
              ReadHeader := FALSE {Not a .FLI File}
            ELSE
              BEGIN
                IF Interval = -1 THEN {Read speed from header}
                  Interval := Speed * Clock_Scale;
                ReadHeader := TRUE;
              END;
        END; {ReadHeader}

        PROCEDURE ReadFrame;
        BEGIN
          BlockRead(FliFile,FrameHeader,SizeOf(FrameHeader));
          FrameSize := FrameHeader.Size - SizeOf(FrameHeader);
        END; {ReadFrame}

        PROCEDURE ProcessFrame;
        BEGIN
          BlockRead(FliFile,Buffer^,FrameSize);
          DrawFrame(Buffer,FrameHeader.Chunks);
        END; {ProcessFrame}

      BEGIN {Play}
        {$I-}
        Assign(FLiFile,Filename);
        Reset(FliFile,1);
        IF (IOResult = 0) THEN
          BEGIN
            IF ReadHeader THEN
              BEGIN
                VideoMode(MCGA);
                InitClock;
                ReadFrame;
                RestartPos := SizeOf(MainHeader) + SizeOf(FrameHeader) + FrameSize;
                ProcessFrame;
                REPEAT
                  Frame := 1;
                  REPEAT
                    Timeout := GetClock + Interval;
                    ReadFrame;
                    IF FrameSize <> 0 THEN
                      ProcessFrame;
                    REPEAT UNTIL GetClock > Timeout;
                    Inc(Frame);
                  UNTIL (Frame > MainHeader.Frames) OR Keypressed;
                  Seek(FliFile,RestartPos);
                UNTIL Keypressed;
                VideoMode(CO80);
              END;
            Close(FliFile);
          END;
        {$I+}
      END; {Play}

    {---------------------------------------------------------------------------}

      FUNCTION Is286Able: Boolean; ASSEMBLER;
      ASM
        PUSHF
        POP     BX
        AND     BX,0FFFH
        PUSH    BX
        POPF
        PUSHF
        POP     BX
        AND     BX,0F000H
        CMP     BX,0F000H
        MOV     AX,0
        JZ      @@1
        MOV     AX,1
      @@1:
      END; {Is286Able}

      FUNCTION IsVGA : Boolean; ASSEMBLER;
      ASM
        MOV  AX,1A00h
        MOV  BL,10h
        INT  10h
        CMP  BL,8
        MOV  AX,1
        JZ   @@1
        MOV  AX,0
      @@1:
      END; {IsVGA}

      PROCEDURE AAPlay(Filename : String);
      VAR
        Player : TFliPlayer;
      BEGIN
        IF Is286Able AND IsVga THEN
          WITH Player DO
            IF Init THEN
              BEGIN
                Play(Filename);
                Done;
              END;
      END; {AAPlay}

    {===========================================================================}

    END.

[Back to EGAVGA SWAG index] [Back to Main SWAG index] [Original]