home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 18 / CD_ASCQ_18_111294_W.iso / dos / prg / pas / pasgraph / fli.pas < prev    next >
Pascal/Delphi Source File  |  1994-07-31  |  33KB  |  713 lines

  1. {$G+}
  2.  
  3. PROGRAM FliPlayer;
  4.  
  5. {  v1.1 made by Thaco   }
  6. { (c) EPOS, August 1992 }
  7.  
  8.  
  9. CONST
  10.   CLOCK_HZ              =4608;                   { Frequency of clock }
  11.   MONITOR_HZ            =70;                     { Frequency of monitor }
  12.   CLOCK_SCALE           =CLOCK_HZ DIV MONITOR_HZ;
  13.  
  14.   BUFFERSIZE            =$FFFE;                  { Size of the framebuffer, must be an even number }
  15.   CDATA                 =$040;                   { Port number of timer 0 }
  16.   CMODE                 =$043;                   { Port number of timers control word }
  17.   CO80                  =$3;                     { Number for standard text mode }
  18.   KEYBOARD              =28;                     { Numbers returned by PORT[$64] indicating what hardware caused INT 09/the - }
  19.   MOUSE                 =60;                     { - number on PORT[$60] }
  20.   MCGA                  =$13;                    { Number for MCGA mode }
  21.   MCGACheck:BOOLEAN     =TRUE;                   { Variable for MCGA checking }
  22.   UseXMS:BOOLEAN        =TRUE;                   { Variable for XMS usage }
  23.   XMSError:BYTE         =0;                      { Variable indicating the errornumber returned from the last XMS operation }
  24.  
  25. TYPE
  26.   EMMStructure          =RECORD
  27.                            BytesToMoveLo,              { Low word of bytes to move. NB: Must be even! }
  28.                            BytesToMoveHi,              { High word of bytes to move }
  29.                            SourceHandle,               { Handle number of source (SH=0 => conventional memory) }
  30.                            SourceOffsetLo,             { Low word of source offset, or OFS if SH=0 }
  31.                            SourceOffsetHi,             { High word of source offset, or SEG if SH=0 }
  32.                            DestinationHandle,          { Handle number of destination (DH=0 => conventional memory) }
  33.                            DestinationOffsetLo,        { Low word of destination offset, or OFS if DH=0 }
  34.                            DestinationOffsetHi  :WORD; { High word of destination offset, or SEG if DH=0 }
  35.                          END;
  36.   HeaderType            =ARRAY[0..128] OF BYTE;  { A buffertype used to read all kinds of headers }
  37.  
  38.  
  39. VAR
  40.   Key,                                           { Variable used to check if a key has been pressed }
  41.   OldKey                :BYTE;                   { Variable used to check if a key has been pressed }
  42.   XMSRecord             :EMMStructure;           { Variable for passing values to the XMS routine }
  43.   InputFile             :FILE;                   { Variable for the incomming .FLI file }
  44.   Header                :HeaderType;             { Buffer used to read all kinds of headers }
  45.   Counter,                                       { General purpose counter }
  46.   Speed                 :INTEGER;                { Timedifference in video tics from one frame to the next }
  47.   FileCounter,                                   { Variable telling the point to read from in the file stored in XMS }
  48.   FileSize,                                      { Size of the .FLI-file }
  49.   FrameSize,                                     { Variable indicating the datasize of current frame }
  50.   NextTime,                                      { Variable saying when it is time to move on to the next frame }
  51.   TimeCounter,                                   { Holding the current time in video tics }
  52.   SecondPos             :LONGINT;                { Number of bytes to skip from the start of the .FLI file when starting - }
  53.                                                  { - from the beginning again }
  54.   Buffer,                                        { Pointer to the Framebuffer }
  55.   XMSEntryPoint         :POINTER;                { Entry point of the XMS routine in memory }
  56.   SpeedString           :STRING[2];              { String used to parse the -sNN command }
  57.   FileName              :STRING;                 { String holding the name of the .FLI-file }
  58.   BufferHandle,                                  { Handle number returned from the XMS routine }
  59.   BytesRead,                                     { Variable telling the numbers of bytes read from the .FLI file }
  60.   FrameNumber,                                   { Number of the current frame }
  61.   Frames,                                        { Total number of frames }
  62.   Chunks                :WORD;                   { Total number of chunks in a frame }
  63.  
  64.  
  65. FUNCTION UpCaseString(Streng:String):String;
  66. { takes a string and convert all letters to uppercase }
  67. VAR
  68.   DummyString           :String;
  69.   Counter               :Integer;
  70. BEGIN
  71.   DummyString:='';
  72.   FOR Counter:=1 TO Length(Streng) DO
  73.     DummyString:=DummyString+UpCase(Streng[Counter]);
  74.   UpCaseString:=DummyString;
  75. END;
  76.  
  77.  
  78. PROCEDURE InitMode(Mode:WORD); ASSEMBLER;
  79. { uses BIOS interrupts to set a videomode }
  80. ASM
  81.   mov  ax,Mode
  82.   int  10h
  83. END;
  84.  
  85.  
  86. FUNCTION ModeSupport(Mode:WORD):BOOLEAN; ASSEMBLER;
  87. { uses BIOS interrupts to check if a videomode is supported }
  88. LABEL Exit, Last_Modes, No_Support, Supported;
  89. VAR
  90.   DisplayInfo           :ARRAY[1..64] OF BYTE;   { Array for storing functionality/state information }
  91. ASM
  92.   push es
  93.  
  94.   mov  ah,1Bh                                    { the functionality/state information request at int 10h }
  95.   mov  bx,0                                      { 0 = return functionality/state information }
  96.   push ds                                        { push DS on the stack and pop it into ES so ES:DI could be used to - }
  97.   pop  es                                        { - address DisplayInfo, as demanded of the interrupt function }
  98.   mov  di,offset DisplayInfo
  99.   int  10h
  100.  
  101.   les  di,[dword ptr es:di]                      { The first dword in the buffer for state information is the address - }
  102.                                                  { - of static funtionality table }
  103.   mov  cx,Mode                                   { Can only check for the 0h-13h modes }
  104.   cmp  cx,13h
  105.   ja   No_Support                                { Return 'no support' for modes > 13h }
  106.  
  107.   mov  ax,1                                      { Shift the right byte the right - }
  108.                                                  { - times and test for the right - }
  109.   cmp  cx,10h                                    { - bit for knowing if the - }
  110.   jae  Last_Modes                                { - videomode is supported - }
  111.                                                  { - }
  112.   shl  ax,cl                                     { - }
  113.   test ax,[word ptr es:di+0]                     { - }
  114.   jz   No_Support                                { - }
  115.   jmp  Supported                                 { - }
  116.                                                  { - }
  117. Last_Modes:                                      { - }
  118.   sub  cx,10h                                    { - }
  119.   shl  ax,cl                                     { - }
  120.   test al,[byte ptr es:di+2]                     { - }
  121.   jz   No_Support                                { - }
  122.  
  123. Supported:
  124.   mov  al,1                                      { AL=1 makes the function return TRUE }
  125.   jmp  Exit
  126.  
  127. No_Support:
  128.   mov  al,0                                      { AL=0 makes the function return TRUE }
  129.  
  130. Exit:
  131.   pop  es
  132. END;
  133.  
  134.  
  135. FUNCTION NoXMS:BOOLEAN; ASSEMBLER;
  136. { checks out if there is a XMS driver installed, and in case it initialize the
  137.   XMSEntryPoint Variable }
  138. LABEL JumpOver;
  139. ASM
  140.   push es
  141.  
  142.   mov  ax,4300h                                  { AX = 4300h => INSTALLATION CHECK }
  143.   int  2Fh                                       { use int 2Fh EXTENDED MEMORY SPECIFICATION (XMS) }
  144.   mov  bl,1                                      { use BL as a flag to indicate success }
  145.   cmp  al,80h                                    { is a XMS driver installed? }
  146.   jne  JumpOver
  147.   mov  ax,4310h                                  { AX = 4310h => GET DRIVER ADDRESS }
  148.   int  2Fh
  149.   mov  [word ptr XMSEntryPoint+0],BX             { initialize low word of XMSEntryPoint }
  150.   mov  [word ptr XMSEntryPoint+2],ES             { initialize high word of XMSEntryPoint }
  151.   mov  bl,0                                      { indicate success }
  152. JumpOver:
  153.   mov  al,bl                                     { make the function return TRUE (AH=1) or FALSE (AH=0) }
  154.  
  155.   pop  es
  156. END;
  157.  
  158.  
  159. FUNCTION XMSMaxAvail:WORD; ASSEMBLER;
  160. { returns size of largest contiguous block of XMS in kilo (1024) bytes }
  161. LABEL JumpOver;
  162. ASM
  163.   mov  ah,08h                                    { 'Query free extended memory' function }
  164.   mov  XMSError,0                                { clear error variable }
  165.   call [dword ptr XMSEntryPoint]
  166.   or   ax,ax                                     { check for error }
  167.   jnz  JumpOver
  168.   mov  XMSError,bl                               { errornumber stored in BL }
  169. JumpOver:                                        { AX=largest contiguous block of XMS }
  170. END;
  171.  
  172.  
  173. FUNCTION XMSGetMem(SizeInKB:WORD):WORD; ASSEMBLER;
  174. { allocates specified numbers of kilo (1024) bytes of XMS and return a handle
  175.   to this XMS block }
  176. LABEL JumpOver;
  177. ASM
  178.   mov  ah,09h                                    { 'Allocate extended memory block' function }
  179.   mov  dx,SizeInKB                               { number of KB requested }
  180.   mov  XMSError,0                                { clear error variable }
  181.   call [dword ptr XMSEntryPoint]
  182.   or   ax,ax                                     { check for error }
  183.   jnz  JumpOver
  184.   mov  XMSError,bl                               { errornumber stored in BL }
  185. JumpOver:
  186.   mov  ax,dx                                     { return handle number to XMS block }
  187. END;
  188.  
  189.  
  190. PROCEDURE XMSFreeMem(Handle:WORD); ASSEMBLER;
  191. LABEL JumpOver;
  192. ASM
  193.   mov  ah,0Ah                                    { 'Free extended memory block' function }
  194.   mov  dx,Handle                                 { XMS's handle number to free }
  195.   mov  XMSError,0                                { clear error variable }
  196.   call [dword ptr XMSEntryPoint]
  197.   or   ax,ax                                     { check for error }
  198.   jnz  JumpOver
  199.   mov  XMSError,bl                               { errornumber stored in BL }
  200. JumpOver:
  201. END;
  202.  
  203.  
  204. PROCEDURE XMSMove(VAR EMMParamBlock:EMMStructure); ASSEMBLER;
  205. LABEL JumpOver;
  206. ASM
  207.   push ds
  208.   push es
  209.   push ds
  210.   pop  es
  211.   mov  ah,0Bh                                    { 'Move extended memory block' function }
  212.   mov  XMSError,0                                { clear error variable }
  213.   lds  si,EMMParamBlock                          { DS:SI -> data to pass to the XMS routine }
  214.   call [dword ptr es:XMSEntryPoint]
  215.   or   ax,ax                                     { check for error }
  216.   jnz  JumpOver
  217.   mov  XMSError,bl                               { errornumber stored in BL }
  218. JumpOver:
  219.   pop  es
  220.   pop  ds
  221. END;
  222.  
  223.  
  224. PROCEDURE ExitDueToXMSError;
  225. BEGIN
  226.   InitMode(CO80);
  227.   WriteLn('ERROR! XMS routine has reported error ',XMSError);
  228.   XMSFreeMem(BufferHandle);
  229.   Halt(0);
  230. END;
  231.  
  232. PROCEDURE GetBlock(VAR Buffer; Size:WORD);
  233. { reads a specified numbers of data from a diskfile or XMS into a buffer }
  234. VAR
  235.   XMSRecord             :EMMStructure;
  236.   NumberOfBytes         :WORD;
  237. BEGIN
  238.   IF UseXMS THEN
  239.   BEGIN
  240.     NumberOfBytes:=Size;
  241.     IF Size MOD 2=1 THEN
  242.       Inc(NumberOfBytes);  { one must allways ask for a EQUAL number of bytes }
  243.     WITH XMSRecord DO
  244.     BEGIN
  245.       BytesToMoveLo      :=NumberOfBytes;
  246.       BytesToMoveHi      :=0;
  247.       SourceHandle       :=BufferHandle;
  248.       SourceOffsetLo     :=FileCounter MOD 65536;
  249.       SourceOffsetHi     :=FileCounter DIV 65536;
  250.       DestinationHandle  :=0;
  251.       DestinationOffsetLo:=Ofs(Buffer);
  252.       DestinationOffsetHi:=Seg(Buffer);
  253.     END;
  254.     XMSMove(XMSRecord);
  255.     IF XMSError<>0 THEN
  256.       ExitDueToXMSError;
  257.     Inc(FileCounter,Size);
  258.   END
  259.   ELSE
  260.     BlockRead(InputFile,Buffer,Size);
  261. END;
  262.  
  263.  
  264. PROCEDURE InitClock; ASSEMBLER; {Taken from the FLILIB source}
  265. ASM
  266.   mov  al,00110100b                          { put it into linear count instead
  267. of divide by 2 }
  268.   out  CMODE,al
  269.   xor  al,al
  270.   out  CDATA,al
  271.   out  CDATA,al
  272. END;
  273.  
  274.  
  275. FUNCTION GetClock:LONGINT; ASSEMBLER; {Taken from the FLILIB source}
  276. { this routine returns a clock with occassional spikes where time
  277.   will look like its running backwards 1/18th of a second.  The resolution
  278.   of the clock is 1/(18*256) = 1/4608 second.  66 ticks of this clock
  279.   are supposed to be equal to a monitor 1/70 second tick.}
  280. ASM
  281.   mov  ah,0                                  { get tick count from dos and use for hi 3 bytes }
  282.   int  01ah                                  { lo order count in DX, hi order in CX }
  283.   mov  ah,dl
  284.   mov  dl,dh
  285.   mov  dh,cl
  286.  
  287.   mov  al,0                           { read lo byte straight from timer chip }
  288.   out  CMODE,al                                  { latch count }
  289.   mov  al,1
  290.   out  CMODE,al                                  { set up to read count }
  291.   in   al,CDATA                                  { read in lo byte (and discard) }
  292.   in   al,CDATA                                  { hi byte into al }
  293.   neg  al                                  { make it so counting up instead of down }
  294. END;
  295.  
  296.  
  297. PROCEDURE TreatFrame(Buffer:POINTER;Chunks:WORD); ASSEMBLER;
  298. { this is the 'workhorse' routine that takes a frame and put it on the screen }
  299. { chunk by chunk }
  300. LABEL
  301.   Color_Loop, Copy_Bytes, Copy_Bytes2, Exit, Fli_Black, Fli_Brun, Fli_Color,
  302.   Fli_Copy, Fli_Lc, Fli_Loop, Jump_Over, Line_Loop, Line_Loop2, Next_Line,
  303.   Next_Line2, Pack_Loop, Pack_Loop2;
  304. ASM
  305.   cli                                            { disable interrupts }
  306.   push ds
  307.   push es
  308.   lds  si,Buffer                                 { let DS:SI point at the frame to be drawn }
  309.  
  310. Fli_Loop:                                        { main loop that goes through all the chunks in a frame }
  311.   cmp  Chunks,0                                  { are there any more chunks to draw? }
  312.   je   Exit
  313.   dec  Chunks                                    { decrement Chunks for the chunk to process now }
  314.  
  315.   mov  ax,[word ptr ds:si+4]                     { let AX have the ChunkType }
  316.   add  si,6                                      { skip the ChunkHeader }
  317.  
  318.   cmp  ax,0Bh                                    { is it a FLI_COLOR chunk? }
  319.   je   Fli_Color
  320.   cmp  ax,0Ch                                    { is it a FLI_LC chunk? }
  321.   je   Fli_Lc
  322.   cmp  ax,0Dh                                    { is it a FLI_BLACK chunk? }
  323.   je   Fli_Black
  324.   cmp  ax,0Fh                                    { is it a FLI_BRUN chunk? }
  325.   je   Fli_Brun
  326.   cmp  ax,10h                                    { is it a FLI_COPY chunk? }
  327.   je   Fli_Copy
  328.   jmp  Fli_Loop                                  { This command should not be
  329. necessary since the program should make one - }
  330.                                                  { - of the other jumps }
  331.  
  332. Fli_Color:
  333.   mov  bx,[word ptr ds:si]                       { number of packets in this
  334. chunk (allways 1?) }
  335.   add  si,2                                      { skip the NumberOfPackets }
  336.   mov  al,0                                      { start at color 0 }
  337.   xor  cx,cx                                     { reset CX }
  338.  
  339. Color_Loop:
  340.   or   bx,bx                                     { set flags }
  341.   jz   Fli_Loop                                  { exit if no more packages }
  342.   dec  bx                                        { decrement NumberOfPackages for the package to process now }
  343.  
  344.   mov  cl,[byte ptr ds:si+0]                     { first byte in packet tells how many colors to skip }
  345.   add  al,cl                                     { add the skiped colors to the start to get the new start }
  346.   mov  dx,$3C8                                   { PEL Address Write Mode Register }
  347.   out  dx,al                                     { tell the VGA card what color we start changing }
  348.  
  349.   inc  dx                                        { at the port abow the PEL_A_W_M_R is the PEL Data Register }
  350.   mov  cl,[byte ptr ds:si+1]                     { next byte in packet tells how many colors to change }
  351.   or   cl,cl                                     { set the flags }
  352.   jnz  Jump_Over                                 { if NumbersToChange=0 then NumbersToChange=256 }
  353.   inc  ch                                        { CH=1 and CL=0 => CX=256 }
  354. Jump_Over:
  355.   add  al,cl                                     { update the color to start at }
  356.   mov  di,cx                                     { since each color is made of 3 bytes (Red, Green & Blue) we have to - }
  357.   shl  cx,1                                      { - multiply CX (the data counter) with 3 }
  358.   add  cx,di                                     { - CX = old_CX shl 1 + old_CX (the fastest way to multiply with 3) }
  359.   add  si,2                                      { skip the NumbersToSkip and NumbersToChange bytes }
  360.   rep  outsb                                     { put the color data to the VGA card FAST! }
  361.  
  362.   jmp  Color_Loop                                { finish with this packet - jump back }
  363.  
  364.  
  365. Fli_Lc:
  366.   mov  ax,0A000h
  367.   mov  es,ax                                     { let ES point at the screen segment }
  368.   mov  di,[word ptr ds:si+0]                     { put LinesToSkip into DI - }
  369.   mov  ax,di                                     { - to get the offset address to this line we have to multiply with 320 - }
  370.   shl  ax,8                                      { - DI = old_DI shl 8 + old_DI shl 6 - }
  371.   shl  di,6                                      { - it is the same as DI = old_DI*256 + old_DI*64 = old_DI*320 - }
  372.   add  di,ax                                     { - but this way is faster than a plain mul }
  373.   mov  bx,[word ptr ds:si+2]                     { put LinesToChange into BX }
  374.   add  si,4                                      { skip the LinesToSkip and LinesToChange words }
  375.   xor  cx,cx                                     { reset cx }
  376.  
  377. Line_Loop:
  378.   or   bx,bx                                     { set flags }
  379.   jz  Fli_Loop                                   { exit if no more lines to change }
  380.   dec  bx
  381.  
  382.   mov  dl,[byte ptr ds:si]                       { put PacketsInLine into DL }
  383.   inc  si                                        { skip the PacketsInLine byte
  384. }
  385.   push di                                        { save the offset address of this line }
  386.  
  387. Pack_Loop:
  388.   or   dl,dl                                     { set flags }
  389.   jz   Next_Line                                 { exit if no more packets in this line }
  390.   dec  dl
  391.   mov  cl,[byte ptr ds:si+0]                     { put BytesToSkip into CL }
  392.   add  di,cx                                     { update the offset address }
  393.   mov  cl,[byte ptr ds:si+1]                     { put BytesOfDataToCome into CL }
  394.   or   cl,cl                                     { set flags }
  395.   jns  Copy_Bytes                                { no SIGN means that CL number of data is to come - }
  396.                                                  { - else the next data should be put -CL number of times }
  397.   mov  al,[byte ptr ds:si+2]                     { put the byte to be repeated into AL }
  398.   add  si,3                                      { skip the packet }
  399.   neg  cl                                        { repeat -CL times }
  400.   rep  stosb
  401.   jmp  Pack_Loop                                 { finish with this packet }
  402.  
  403. Copy_Bytes:
  404.   add  si,2                                      { skip the two count bytes at the start of the packet }
  405.   rep  movsb
  406.   jmp  Pack_Loop                                 { finish with this packet }
  407.  
  408. Next_Line:
  409.   pop  di                                        { restore the old offset address of the current line }
  410.   add  di,320                                    { offset address to the next line }
  411.   jmp  Line_Loop
  412.  
  413.  
  414. Fli_Black:
  415.   mov  ax,0A000h
  416.   mov  es,ax                                     { let ES:DI point to the start of the screen }
  417.   xor  di,di
  418.   mov  cx,32000                                  { number of words in a screen }
  419.   xor  ax,ax                                     { color 0 is to be put on the screen }
  420.   rep  stosw
  421.   jmp  Fli_Loop                                  { jump back to main loop }
  422.  
  423.  
  424. Fli_Brun:
  425.   mov  ax,0A000h
  426.   mov  es,ax                                     { let ES:DI point at the start of the screen }
  427.   xor  di,di
  428.   mov  bx,200                                    { numbers of lines in a screen }
  429.   xor  cx,cx
  430.  
  431. Line_Loop2:
  432.   mov  dl,[byte ptr ds:si]                       { put PacketsInLine into DL }
  433.   inc  si                                        { skip the PacketsInLine byte }
  434.   push di                                        { save the offset address of this line }
  435.  
  436. Pack_Loop2:
  437.   or   dl,dl                                     { set flags }
  438.   jz   Next_Line2                                { exit if no more packets in this line }
  439.   dec  dl
  440.   mov  cl,[byte ptr ds:si]                       { put BytesOfDataToCome into CL }
  441.   or   cl,cl                                     { set flags }
  442.   js   Copy_Bytes2                               { SIGN meens that CL number of data is to come - }
  443.                                                  { - else the next data should be put -CL number of times }
  444.   mov  al,[byte ptr ds:si+1]                     { put the byte to be repeated into AL }
  445.   add  si,2                                      { skip the packet }
  446.   rep  stosb
  447.   jmp  Pack_Loop2                                { finish with this packet }
  448.  
  449. Copy_Bytes2:
  450.   inc  si                                        { skip the count byte at the start of the packet }
  451.   neg  cl                                        { repeat -CL times }
  452.   rep  movsb
  453.   jmp  Pack_Loop2                                { finish with this packet }
  454.  
  455. Next_Line2:
  456.   pop  di                                        { restore the old offset address of the current line }
  457.   add  di,320                                    { offset address to the next line }
  458.   dec  bx                                        { any more lines to draw? }
  459.   jnz  Line_Loop2
  460.   jmp  Fli_Loop                                  { jump back to main loop }
  461.  
  462. Fli_Copy:
  463.   mov  ax,0A000h
  464.   mov  es,ax                                     { let ES:DI point to the start of the screen }
  465.   xor  di,di
  466.   mov  cx,32000                                  { number of words in a screen }
  467.   rep  movsw
  468.   jmp  Fli_Loop                                  { jump back to main loop }
  469.  
  470.  
  471. Exit:
  472.   sti                                            { enable interrupts }
  473.   pop  es
  474.   pop  ds
  475. END;
  476.  
  477.  
  478.  
  479. BEGIN
  480.   WriteLn;
  481.   WriteLn('.FLI-Player v1.1 by Thaco');
  482.   WriteLn('  (c) EPOS, August 1992');
  483.   WriteLn;
  484.   IF ParamCount=0 THEN                           { if no input parameters then write the 'usage text' }
  485.   BEGIN
  486.     WriteLn('USAGE: FLIPLAY <options> <filename>');
  487.     WriteLn('                   '+#24+'         '+#24);
  488.     WriteLn('                   │         └──  Filename of .FLI file');
  489.     WriteLn('                   └────────────  -d   = Do not use XMS');
  490.     WriteLn('                                  -i   = Information about the program');
  491.     WriteLn('                                  -n   = No checking of MCGA mode support');
  492.     WriteLn('                                  -sNN = Set playspeed to NN video ticks (0-99)');
  493.     WriteLn('                                         ( NN=70 ≈ frame delay of 1 second )');
  494.     Halt(0);
  495.   END;
  496.  
  497.   FOR Counter:=1 TO ParamCount DO                { search through the input parameters for a -Info option }
  498.     IF Pos('-I',UpCaseString(ParamStr(Counter)))<>0 THEN
  499.     BEGIN
  500.       WriteLn('Program information:');
  501.       WriteLn('This program plays animations (sequences of pictures) made by programs like',#10#13,
  502.               'Autodesk Animator (so called .FLI-files). The program decodes the .FLI file,',#10#13,
  503.               'frame by frame, and uses the systemclock for mesuring the time-delay between',#10#13,
  504.               'each frame.');
  505.       WriteLn('Basis for the program was the FliLib package made by Jim Kent, but since the',#10#13,
  506.               'original source was written in C, and I am not a good C-writer, I decided',#10#13,
  507.               'to write my own .FLI-player in Turbo Pascal v6.0.');
  508.       WriteLn('This program was made by Eirik Milch Pedersen (thaco@solan.unit.no).');
  509.       WriteLn('Copyright Eirik Pedersens Own Softwarecompany (EPOS), August 1992');
  510.       WriteLn;
  511.       WriteLn('Autodesk Animator is (c) Autodesk Inc');
  512.       WriteLn('FliLib is (c) Dancing Flame');
  513.       WriteLn('Turbo Pascal is (c) Borland International Inc');
  514.       Halt(0);
  515.     END;
  516.  
  517.   Speed:=-1;
  518.   Counter:=1;
  519.   WHILE (Copy(ParamStr(Counter),1,1)='-') AND (ParamCount>=Counter) DO { search
  520. through the input parameters to assemble them }
  521.   BEGIN
  522.    IF Pos('-D',UpCaseString(ParamStr(Counter)))<>0 THEN  { do not use XMS for
  523. storing the file into memory }
  524.      UseXMS:=FALSE
  525.    ELSE
  526.      IF Pos('-N',UpCaseString(ParamStr(Counter)))<>0 THEN  { do not check for a
  527. vga card present }
  528.        MCGACheck:=FALSE
  529.      ELSE
  530.        IF Pos('-S',UpCaseString(ParamStr(Counter)))<>0 THEN { speed override
  531. has been specified }
  532.        BEGIN
  533.          SpeedString:=Copy(ParamStr(Counter),3,2);  { cut out the NN parameter
  534. }
  535.          IF NOT(SpeedString[1] IN ['0'..'9']) OR    { check if the NN parameter
  536. is legal }
  537.             (NOT(SpeedString[2] IN ['0'..'9',' ']) AND (Length(SpeedString)=2))
  538. THEN
  539.          BEGIN
  540.            WriteLn('ERROR! Can not parse speed ''',SpeedString,'''.');
  541.            Halt(0);
  542.          END;
  543.          Speed:=Byte(SpeedString[1])-48;  { take the first number, in ASCII,
  544. and convert it to a standard number }
  545.          IF Length(SpeedString)=2 THEN    { if there is two numbers then
  546. multiply the first with 10 and add the next }
  547.            Speed:=Speed*10+Byte(SpeedString[2])-48;
  548.          Speed:=Speed*CLOCK_SCALE;        { convert the speed to number of
  549. clock tics }
  550.        END;
  551.    Inc(Counter);
  552.   END;
  553.  
  554.   IF ParamCount<Counter THEN
  555.   BEGIN
  556.     WriteLn('ERROR! No filename specified.');
  557.     Halt(0);
  558.   END;
  559.  
  560.   FileName:=UpCaseString(ParamStr(Counter));
  561.   IF Pos('.',FileName)=0 THEN  { find out if there exist a . in the filename }
  562.     FileName:=FileName+'.FLI'; { if not then add the .FLI extension on the
  563. filename }
  564.  
  565.   IF MaxAvail<BUFFERSIZE THEN   { check if there is enough memory to the frame buffer }
  566.   BEGIN
  567.     WriteLn('ERROR! Can not allocate enough memory to a frame buffer.');
  568.     Halt(0);
  569.   END;
  570.  
  571.   GetMem(Buffer,BUFFERSIZE);
  572.   Assign(InputFile,FileName);
  573.   Reset(InputFile,1);
  574.   IF IOResult<>0 THEN  { has an error occured during opening the file? }
  575.   BEGIN
  576.     WriteLn('ERROR! Can not open file ''',FileName,'''.');
  577.     Halt(0);
  578.   END;
  579.  
  580.   IF NOT(MCGACheck) OR ModeSupport(MCGA) THEN
  581.     InitMode(MCGA)
  582.   ELSE
  583.   BEGIN
  584.     WriteLn('ERROR! Video mode 013h - 320x200x256 colors - is not supported.');
  585.     Halt(0);
  586.   END;
  587.  
  588.   BlockRead(InputFile,Header,128);  { read the .FLI main header }
  589.  
  590.   IF NOT((Header[4]=$11) AND (Header[5]=$AF)) THEN  { check if the file has got the magic number }
  591.   BEGIN
  592.     InitMode(CO80);
  593.     WriteLn('ERROR! File ''',FileName,''' is of a wrong file type.');
  594.     Halt(0);
  595.   END;
  596.  
  597.   IF NoXMS THEN  { if no XMS driver present then do not use XMS }
  598.     UseXMS:=FALSE;
  599.  
  600.   IF UseXMS THEN
  601.   BEGIN
  602. FileSize:=Header[0]+256*(LongInt(Header[1])+256*(LongInt(Header[2])+256*LongInt
  603. (Header[3])));
  604.     IF XMSMaxAvail<=(FileSize+1023) SHR 10 THEN  { is there enough XMS (rounded up to nearest KB) availible? }
  605.     BEGIN
  606.       WriteLn('ERROR! Not enough XMS for the file');
  607.       Halt(0);
  608.     END
  609.     ELSE
  610.     BEGIN
  611.       Seek(InputFile,0);  { skip back to start of .FLI-file to put it all into XMS }
  612.       BufferHandle:=XMSGetMem((FileSize+1023) SHR 10);  { allocate XMS for the whole .FLI file }
  613.       FileCounter:=0;
  614.       REPEAT
  615.         BlockRead(InputFile,Buffer^,BUFFERSIZE,BytesRead);  { read a part from the .FLI file }
  616.         IF BytesRead MOD 2=1 THEN  { since BUFFERSIZE shoud be an even number, the only time this triggers is the last part }
  617.           Inc(BytesRead);          { must be done because the XMS routine demands an even number of bytes to be moved }
  618.         IF BytesRead<>0 THEN
  619.         BEGIN
  620.           WITH XMSRecord DO  { put data into the XMSRecord }
  621.           BEGIN
  622.             BytesToMoveLo      :=BytesRead;
  623.             BytesToMoveHi      :=0;
  624.             SourceHandle       :=0;
  625.             SourceOffsetLo     :=Ofs(Buffer^);
  626.             SourceOffsetHi     :=Seg(Buffer^);
  627.             DestinationHandle  :=BufferHandle;
  628.             DestinationOffsetLo:=FileCounter MOD 65536;
  629.             DestinationOffsetHi:=FileCounter DIV 65536;
  630.           END;
  631.           XMSMove(XMSRecord);   { move bytes to XMS }
  632.           IF XMSError<>0 THEN   { have any XMS errors occured? }
  633.             ExitDueToXMSError;
  634.           Inc(FileCounter,BytesRead);  { update the offset into XMS where to put the next bytes }
  635.         END;
  636.       UNTIL BytesRead<>BUFFERSIZE;  { repeat until bytes read <> bytes tried to read => end of file }
  637.     END;
  638.     FileCounter:=128;  { we continue (after reading the .FLI file into XMS)
  639. right after the .FLI main header }
  640.   END;
  641.  
  642.   Frames:=Header[6]+Header[7]*256;  { get the number of frames from the .FLI-header }
  643.   IF Speed=-1 THEN                  { if speed is not set by a speed override then get it from the .FLI-header }
  644.     Speed:=(Header[16]+Integer(Header[17])*256)*CLOCK_SCALE;
  645.   InitClock;  { initialize the System Clock }
  646.   OldKey:=PORT[$60];  { get the current value from the keyboard }
  647.   Key:=OldKey;        { and set the 'current key' Variable to the same value }
  648.  
  649.   GetBlock(Header,16);  { read the first frame-header }
  650. FrameSize:=Header[0]+256*(LongInt(Header[1])+256*(LongInt(Header[2])+256*LongInt(Header[3])))-16;  { calculate framesize }
  651.   SecondPos:=128+16+FrameSize;  { calculate what position to skip to when the
  652. .FLI is finished and is going to start again - }
  653.                                 { the position = .FLI-header +
  654. first_frame-header + first_framesize }
  655.   Chunks:=Header[6]+Header[7]*256;  { calculate number of chunks in frame }
  656.   GetBlock(Buffer^,FrameSize);  { read the frame into the framebuffer }
  657.   TreatFrame(Buffer,Chunks);  { treat the first frame }
  658.  
  659.   TimeCounter:=GetClock;  { get the current time }
  660.  
  661.   {
  662.     The first frame must be handeled separatly from the rest. This is because
  663. the rest of the frames are updates/changes of the
  664.     first frame.
  665.     At the end of the .FLI-file there is one extra frame who handles the
  666. changes from the last frame to the first frame.
  667.   }
  668.  
  669.   REPEAT
  670.     FrameNumber:=1;  { we start at the first frame (after the initial frame) }
  671.     REPEAT
  672.       GetBlock(Header,16);  { read frame-header }
  673. FrameSize:=Header[0]+256*(LongInt(Header[1])+256*(LongInt(Header[2])+256*LongInt(Header[3])))-16;  { size of frame }
  674.       IF FrameSize<>0 THEN  { sometimes there are no changes from one frame to
  675. the next (used for extra delays). In such - }
  676.                             { - cases the size of the frame is 0 and we don't
  677. have to process them }
  678.       BEGIN
  679.         Chunks:=Header[6]+Header[7]*256;  { calculate number of chunks in the
  680. frame }
  681.         GetBlock(Buffer^,FrameSize);  { read the frame into the framebuffer }
  682.         TreatFrame(Buffer,Chunks);  { treat the frame }
  683.       END;
  684.  
  685.       NextTime:=TimeCounter+Speed;   { calculate the delay to the next frame }
  686.       WHILE TimeCounter<NextTime DO  { wait for this long }
  687.         TimeCounter:=GetClock;
  688.  
  689.       IF PORT[$64]=KEYBOARD THEN   { check if the value at the keyboard port is
  690. caused by a key pressed }
  691.         Key:=PORT[$60];            { get the current value from the keyboard }
  692.       Inc(FrameNumber);  { one frame finished, over to the next one }
  693.     UNTIL (FrameNumber>Frames) OR (Key<>OldKey);  { repeated until we come to
  694. the last frame or a key is pressed }
  695.  
  696.     IF UseXMS THEN
  697.       FileCounter:=SecondPos
  698.     ELSE
  699.       Seek(InputFile,SecondPos);  { set current position in the file to the
  700. second frame }
  701.  
  702.   UNTIL Key<>OldKey;  { exit the loop if a key has been pressed }
  703.  
  704.   InitMode(CO80);  { get back to text mode }
  705.  
  706.   Close(InputFile);            { be a kind boy and close the file before we end
  707. the program }
  708.   FreeMem(Buffer,BUFFERSIZE);  { and free the framebuffer }
  709.  
  710.   IF UseXMS THEN
  711.     XMSFreeMem(BufferHandle);
  712. END.
  713.