<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.multimedia.cx/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Andrew+Dream</id>
	<title>MultimediaWiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.multimedia.cx/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Andrew+Dream"/>
	<link rel="alternate" type="text/html" href="https://wiki.multimedia.cx/index.php/Special:Contributions/Andrew_Dream"/>
	<updated>2026-05-04T16:53:37Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.5</generator>
	<entry>
		<id>https://wiki.multimedia.cx/index.php?title=VQA&amp;diff=16098</id>
		<title>VQA</title>
		<link rel="alternate" type="text/html" href="https://wiki.multimedia.cx/index.php?title=VQA&amp;diff=16098"/>
		<updated>2025-11-26T07:21:41Z</updated>

		<summary type="html">&lt;p&gt;Andrew Dream: /* VQHD chunk (&amp;quot;VQa HeaDer&amp;quot; ???) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Extension: vqa&lt;br /&gt;
* Company: [[Westwood Studios]]&lt;br /&gt;
* Samples: [http://samples.mplayerhq.hu/game-formats/vqa/ http://samples.mplayerhq.hu/game-formats/vqa/]&lt;br /&gt;
* Samples: {{DexvertSamples|video/westwoodStudiosVQA}}&lt;br /&gt;
&lt;br /&gt;
VQA stands for Vector Quantized Animation and is a FMV format used in a number of computer games by Westwood Studios.&lt;br /&gt;
&lt;br /&gt;
== Technical Description ==&lt;br /&gt;
&lt;br /&gt;
'''TODO: import and combine Gordan Ugarkovic's documents from [http://multimedia.cx/VQA_INFO.TXT http://multimedia.cx/VQA_INFO.TXT] and [http://multimedia.cx/HC-VQA.TXT http://multimedia.cx/HC-VQA.TXT].&lt;br /&gt;
==VQA_INFO.txt==&lt;br /&gt;
&lt;br /&gt;
===NOTE #1=== &lt;br /&gt;
This document only applies to VQA files in the original C&amp;amp;C and C&amp;amp;C: Red Alert (version 2) as well as Legend of Kyrandia III : Malcolm's Revenge (version 1).   It DOES NOT apply to the HiColor VQAs found in Westwood's newer games. They use a somewhat different approach to compressing video data (I will provide some facts about their sound stream, though). Look at my other document (HC-VQA.TXT) for a description of the HiColor VQA movies.&lt;br /&gt;
&lt;br /&gt;
===NOTE #2=== &lt;br /&gt;
Throughout the document, I will assume that:&lt;br /&gt;
* CHAR is 1 byte in size, unsigned,&lt;br /&gt;
* SHORT is 2 bytes in size, unsigned,&lt;br /&gt;
* LONG is 4 bytes in size.&lt;br /&gt;
&lt;br /&gt;
Each VQA file is comprised of a series of chunks. A chunk can contain&lt;br /&gt;
other sub-chunks nested in it. Every chunk has a 4 letter ID (all uppercase&lt;br /&gt;
letters) and a LONG written using Motorola byte ordering system (first&lt;br /&gt;
comes the Most Significant Byte), unlike the usual Intel system (Least&lt;br /&gt;
Significant Byte first).&lt;br /&gt;
&lt;br /&gt;
For example, if you had a value 0x12345678 in hexadecimal, using the Intel&lt;br /&gt;
notation it would be written as 78 56 34 12, while using Motorola's &lt;br /&gt;
12 34 56 78.&lt;br /&gt;
&lt;br /&gt;
NOTE: Some chunk IDs start with a NULL byte (0x00) because of reasons&lt;br /&gt;
that will become apparent later. You should just skip this byte&lt;br /&gt;
and assume the next 4 letters hold the chunk ID.&lt;br /&gt;
&lt;br /&gt;
Following the chunk header is the chunk data.&lt;br /&gt;
&lt;br /&gt;
===Typical VQA File===&lt;br /&gt;
Here is a scheme of a typical VQA file (nested chunks are indented):&lt;br /&gt;
&lt;br /&gt;
 FORM&lt;br /&gt;
   VQHD&lt;br /&gt;
   FINF       &amp;lt;-  Frame data positions&lt;br /&gt;
   SND?    \  &amp;lt;-  First sound chunk, contains 1/2 second of sound&lt;br /&gt;
   SND?     |     &amp;lt;- Contains 1 frame's worth of sound&lt;br /&gt;
   VQFR     |     &amp;lt;- Contains various video data chunks&lt;br /&gt;
     CBF?   | 1st frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     CPL?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
   SND?    \&lt;br /&gt;
   VQFR     | 2nd frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
   SND?    \&lt;br /&gt;
   VQFR     | 3rd frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
 . . .&lt;br /&gt;
&lt;br /&gt;
NOTE: There can also be some other chunks (i.e. PINF, PINH, SN2J) included,&lt;br /&gt;
but they are not relevant (?!) for viewing the movie, so they can&lt;br /&gt;
easily be skipped.&lt;br /&gt;
&lt;br /&gt;
===FORM chunk===&lt;br /&gt;
&lt;br /&gt;
This chunk is the main chunk, containing all other chunks.&lt;br /&gt;
In case of version 2 and 3 movies, its size is actually the size of the&lt;br /&gt;
entire file minus the size of the chunk header (8 bytes). Version 1 movies&lt;br /&gt;
seem to have this set to the length of the header VQHD + FINF chunk.&lt;br /&gt;
&lt;br /&gt;
Immediately after the chunk's header, a 4-character signature,&lt;br /&gt;
&amp;quot;WVQA&amp;quot; is located. Then come all the other chunks.&lt;br /&gt;
&lt;br /&gt;
===VQHD chunk (&amp;quot;VQa HeaDer&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This is the header chunk, containing vital information about the movie.&lt;br /&gt;
Its size is always 42 bytes.&lt;br /&gt;
The information is structured like this:&lt;br /&gt;
&lt;br /&gt;
 struct VQAHeader&lt;br /&gt;
 {&lt;br /&gt;
  short  Version;       /* VQA version number                         */&lt;br /&gt;
  short  Flags;         /* VQA flags                                  */&lt;br /&gt;
  short  NumFrames;     /* Number of frames                           */&lt;br /&gt;
  short  Width;         /* Movie width (pixels)                       */&lt;br /&gt;
  short  Height;        /* Movie height (pixels)                      */&lt;br /&gt;
  char   BlockW;        /* Width of each image block (pixels)         */&lt;br /&gt;
  char   BlockH;        /* Height of each image block (pixels)        */&lt;br /&gt;
  char   FrameRate;     /* Frame rate of the VQA                      */&lt;br /&gt;
  char   CBParts;       /* How many images use the same lookup table  */&lt;br /&gt;
  short  Colors;        /* Maximum number of colors used in VQA       */&lt;br /&gt;
  short  MaxBlocks;     /* Maximum number of image blocks             */&lt;br /&gt;
  short  OffsetX;       /* Offset X position in background movie      */&lt;br /&gt;
  short  OffsetY;       /* Offset Y position in background movie      */&lt;br /&gt;
  short  MaxVPT?Size;   /* Max. VPT? (VPTZ, VPRZ) size                */&lt;br /&gt;
  short  Freq;          /* Sound sampling frequency                   */&lt;br /&gt;
  char   Channels;      /* Number of sound channels                   */&lt;br /&gt;
  char   Bits;          /* Sound resolution                           */&lt;br /&gt;
  long   Unknown1;      /* Always 0 ???                               */&lt;br /&gt;
  short  Unknown2;      /* 0 in old VQAs, 4 in HiColor ones ???       */&lt;br /&gt;
  long   MaxCBFZSize;   /* Max. CBFZ size (in old VQAs usually is 0)  */&lt;br /&gt;
  long   Unknown3;      /* Always 0 ???                               */&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Version denotes the VQA version. Valid values are:&lt;br /&gt;
* 1 - First VQAs, used only in Legend of Kyrandia III.&lt;br /&gt;
* 2 - Used in C&amp;amp;C, Red Alert, Lands of Lore II, Dune 2000.&lt;br /&gt;
* 3 - Lands of Lore III (?), Blade Runner (?), Nox and Tiberian Sun. These VQAs are HiColor (15 bit).&lt;br /&gt;
&lt;br /&gt;
Flags most probably contain some flags. I know that bit 0 (LSB) and 2 (Overlay)&lt;br /&gt;
* bit 0 denotes whether the VQA has a soundtrack or not.&lt;br /&gt;
* (1 = Has sound, 0 = No sound)&lt;br /&gt;
* bit 2 denotes whether this movie is an overlay on the background movie.&lt;br /&gt;
* (1 = Yes, 0 = No)&lt;br /&gt;
Movie with bit 2 may play separately, but need apply VPTR\VPRZ chunks&lt;br /&gt;
to &amp;quot;Null&amp;quot; Vector Pointer Table (filled by zeros) for each frame.&lt;br /&gt;
&lt;br /&gt;
If NumFrames of background movie less than NumFrames of overlay movie&lt;br /&gt;
then background movie will be repeat, while will not finish overlay movie.&lt;br /&gt;
&lt;br /&gt;
Width is usually 320, Height is usually 156 or 200 although one movie in&lt;br /&gt;
* Red Alert is 640x400 in size (the start movie for the Win95 version).&lt;br /&gt;
&lt;br /&gt;
Each frame of a VQA movie is comprised of a series of blocks that are&lt;br /&gt;
BlockW pixels in width and BlockH pixels in height. Imagine the frame&lt;br /&gt;
is a mosaic with the blocks being 'pieces' that make up the frame.&lt;br /&gt;
&lt;br /&gt;
BlockW is the width and BlockH is the height of each screen block.&lt;br /&gt;
In VQAs version 2 (and perhaps 1) the blocks are usually 4x2.&lt;br /&gt;
&lt;br /&gt;
FrameRate is always 15 in C&amp;amp;C and RA and seems to be 10 in LoK III.&lt;br /&gt;
&lt;br /&gt;
CBParts denotes how many frames use the same lookup table. It also&lt;br /&gt;
implies how many parts the new block lookup table is split into.&lt;br /&gt;
In C&amp;amp;C and RA it is always 8. When CBParts is 0, and movie have&lt;br /&gt;
CBP? chunks, then need see FINF or CIND chunk.&lt;br /&gt;
&lt;br /&gt;
Colors indicates the maximum number of colors used by the VQA.&lt;br /&gt;
The HiColor VQAs have this set to 0, while the old movies have&lt;br /&gt;
256 or less in here.&lt;br /&gt;
&lt;br /&gt;
Offset X and Offset Y uses only when there is bit 2 in the flags.&lt;br /&gt;
Usually uses chunk CLIP too, where the width and height of the&lt;br /&gt;
background movie are specified.&lt;br /&gt;
&lt;br /&gt;
MaxVPT?Size tells you the size of the largest VPTZ or VPRZ chunk, in bytes.&lt;br /&gt;
&lt;br /&gt;
Freq is usually 22050 Hz. Note that version 1 movies can have this set &lt;br /&gt;
to 0 Hz. In that case, you should use 22050 Hz.&lt;br /&gt;
&lt;br /&gt;
Channels specifies the number of sound channels, i.e. is the sound&lt;br /&gt;
mono or stereo. Channels=1 -&amp;gt; sound is mono, Channels=2 -&amp;gt; stereo.&lt;br /&gt;
C&amp;amp;C and RA almost always use mono sound. &lt;br /&gt;
Version 1 can have this set to 0, but you should use 1 (mono sound).&lt;br /&gt;
The majority of Tiberian Sun movies use stereo sound instead.&lt;br /&gt;
&lt;br /&gt;
* Bits indicates whether the sound is 8 bit or 16 bit.&lt;br /&gt;
Bits=8 -&amp;gt; 8 bit sound, Bits=16 -&amp;gt; 16 bit sound (surprise! :). &lt;br /&gt;
The Legend of Kyrandia III: Malcolm's Revenge uses 8 bits where&lt;br /&gt;
C&amp;amp;C, RA, TS, Dune 2000, Lands of Lore III use 16 bits.&lt;br /&gt;
Note, again, that version 1 of the VQAs can have this set to 0 in&lt;br /&gt;
which case 8 bits are assumed.&lt;br /&gt;
&lt;br /&gt;
MaxCBFZSize tells you the size of the largest CBFZ chunk, in bytes.&lt;br /&gt;
In old VQAs this value may be 0.&lt;br /&gt;
&lt;br /&gt;
Following the chunk data are the sub-chunks.&lt;br /&gt;
&lt;br /&gt;
===FINF chunk (&amp;quot;Frame INFormation&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This chunk contains the positions (absolute from the start of the VQA)&lt;br /&gt;
of data for every frame.&lt;br /&gt;
That means that it points to the SND? chunk associated with that frame,&lt;br /&gt;
which is followed by a VQFR chunk containing frame's video data.&lt;br /&gt;
&lt;br /&gt;
The positions are given as LONGs which are in normal Intel byte order.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' Some frame positions are 0x40000000 too large. This is a flag&lt;br /&gt;
indicating those frames contain a new color palette. To get the&lt;br /&gt;
actual positions, you should subtract 0x40000000 from the values.&lt;br /&gt;
&lt;br /&gt;
'''NOTE #2:''' To get the actual position of the frame data you have to multiply&lt;br /&gt;
the value by 2. This is why some chunk IDs start with 0x00. Since&lt;br /&gt;
you multiply by 2, you can't get an odd chunk position so if the&lt;br /&gt;
chunk position would normally be odd, a 0x00 is inserted to make&lt;br /&gt;
it even.&lt;br /&gt;
&lt;br /&gt;
===SND? chunk (&amp;quot;SouND&amp;quot; ???)===&lt;br /&gt;
These chunks contain the sound data for the movie. The last byte of the ID&lt;br /&gt;
can be either '0', '1' or '2' so the actual IDs would be &amp;quot;SND0&amp;quot;, &amp;quot;SND1&amp;quot;&lt;br /&gt;
and &amp;quot;SND2&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
In VQAs version 1 (Legend of Kyrandia 3) there are ''NumFrames'' sound chunks.&lt;br /&gt;
Old 8 bit VQA movies of version 2 have ''NumFrames+1'' sound chunks.&lt;br /&gt;
Note, however, that Dune2000 movies, which are also version 2, but HiColor&lt;br /&gt;
have ''NumFrames'' sound chunks, instead.&lt;br /&gt;
Version 3 movies also have ''NumFrames'' sound chunks.&lt;br /&gt;
&lt;br /&gt;
In case of the old, 8 bit VQAs, the first chunk contains half a second&lt;br /&gt;
(ver. 2) or more (ver. 1) of the wave data, in all (?) the HiColor movies&lt;br /&gt;
the first chunk contains exactly the amount of sound required for one&lt;br /&gt;
frame of the movie. The downside is this requires a somewhat more advanced &lt;br /&gt;
buffering technique on the side of the player in order to allow smooth &lt;br /&gt;
playback.&lt;br /&gt;
&lt;br /&gt;
===SND0 chunk===&lt;br /&gt;
&lt;br /&gt;
This one contains the raw 8 or 16 bit PCM wave data. If the data is&lt;br /&gt;
8 bit, the sound is unsigned and if it is 16 bit, the samples are&lt;br /&gt;
signed.&lt;br /&gt;
&lt;br /&gt;
===SND1 chunk===&lt;br /&gt;
&lt;br /&gt;
It contains 8 bit sound compressed using Westwood's own&lt;br /&gt;
proprietary ADPCM algorithm. The chunk has a 4 byte header:&lt;br /&gt;
&lt;br /&gt;
  struct SND1Header&lt;br /&gt;
  {&lt;br /&gt;
   short OutSize;&lt;br /&gt;
   short Size;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
These values are needed for the decoding algoritm (see APPENDIX C).&lt;br /&gt;
The encoded samples follow immediately after the header.&lt;br /&gt;
It's important to know this algorithm produces UNSIGNED sound, unlike&lt;br /&gt;
the IMA ADPCM algorithm supplied here (see below). It is, however very&lt;br /&gt;
simple to adapt both algorithms to produce either signed or unsigned&lt;br /&gt;
sample output...&lt;br /&gt;
&lt;br /&gt;
===SND2 chunk===&lt;br /&gt;
&lt;br /&gt;
It contains the 16 bit sound data compressed using the IMA ADPCM&lt;br /&gt;
algorithm which compresses 16 bits into 4. That's why the SND2 chunks&lt;br /&gt;
are 4 times smaller than SND0 chunks and they are used almost all&lt;br /&gt;
the time. For the description of the algorithm, see later in the document.&lt;br /&gt;
  &lt;br /&gt;
Different VQA versions have different stereo sample layout. In case&lt;br /&gt;
of Tiberian Sun stereo sound is encoded the same way as mono except the&lt;br /&gt;
SND2 chunk is split into two halfs. The first half contains the left channel&lt;br /&gt;
sound and the second half contains the right channel. The layout of&lt;br /&gt;
nibbles is as follows: LL LL LL LL LL ... RR RR RR RR RR.&lt;br /&gt;
Old movies (C&amp;amp;C, RA) use a different layout. Here, the nibbles are packed&lt;br /&gt;
together like this: LL RR LL RR LL RR ... This means that two left channel&lt;br /&gt;
samples are packed into one byte and then two right channel samples are&lt;br /&gt;
packed into another.&lt;br /&gt;
&lt;br /&gt;
Naturally, the size of a stereo SND2 chunk is exactly twice as big as&lt;br /&gt;
a mono SND2.&lt;br /&gt;
&lt;br /&gt;
It is important to note that, when decoding, you have to keep separate &lt;br /&gt;
values of ''Index'' and ''Cur_Sample'' for each channel (see APPENDIX B).&lt;br /&gt;
&lt;br /&gt;
===VQFR chunk (&amp;quot;Vector Quantized FRame&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
A chunk that includes many nested sub-chunks which contain video data.&lt;br /&gt;
It doesn't contain any data itself so the sub-chunks follow immediately&lt;br /&gt;
after the VQFR chunk header.&lt;br /&gt;
All following sub-chunks are nested inside a VQFR chunk. They can all&lt;br /&gt;
contain '0' or 'Z' as the last byte of their ID.&lt;br /&gt;
&lt;br /&gt;
* If the last byte is '0' it means that the chunk data is uncompressed.&lt;br /&gt;
* If the last byte is 'Z' it means that the data is compressed using&lt;br /&gt;
Format80 compression. You can find its description in APPENDIX A.&lt;br /&gt;
&lt;br /&gt;
===CBF? chunk (&amp;quot;CodeBook, Full&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
Lookup table containing the screen block data as an array&lt;br /&gt;
of elements that each are BlockW*BlockH bytes long. It is always located&lt;br /&gt;
in the data for the first frame. In Vector Quantization terminology&lt;br /&gt;
these tables are called CODEBOOKS.&lt;br /&gt;
&lt;br /&gt;
There can be max. 0x0f00 of these elements (blocks) at any one time in &lt;br /&gt;
normal VQAs and 0xff00 in the hi-res VQAs (Red Alert 95 start movie) although&lt;br /&gt;
I seriously doubt that so many blocks (0xff00 = 65280 blocks) would&lt;br /&gt;
ever be used.&lt;br /&gt;
&lt;br /&gt;
The uncompressed version of these chunks (&amp;quot;CBF0&amp;quot;) is used mainly in&lt;br /&gt;
the original Command &amp;amp; Conquer, while the compressed version (&amp;quot;CBFZ&amp;quot;)&lt;br /&gt;
is used in C&amp;amp;C: Red Alert.&lt;br /&gt;
&lt;br /&gt;
===CBP? chunk (&amp;quot;CodeBook Part&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
Like CBF?, but it contains a part of the lookup table, so to get the new&lt;br /&gt;
complete table you need to append CBParts of these in frame order.&lt;br /&gt;
Once you get the complete table and display the current frame, replace&lt;br /&gt;
the old table with the new one.&lt;br /&gt;
As in CBF? chunk, the uncompressed chunks are used in C&amp;amp;C and the compressed&lt;br /&gt;
chunks are used in Red Alert.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' If the chunks are CBPZ, first you need to append CBParts of them and&lt;br /&gt;
then decompress the data, NOT decompress each chunk individually.&lt;br /&gt;
&lt;br /&gt;
===CPL? chunk (&amp;quot;Color PaLette&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
The simplest one of all... Contains a palette for the VQA. It is an&lt;br /&gt;
array of red, green and blue values (in that order, all have a size of&lt;br /&gt;
1 byte). Seems that the values range from 0-255, but you should mask out&lt;br /&gt;
the bits 6 and 7 to get the correct palette (VGA hardware uses only&lt;br /&gt;
bits 0..5 anyway).&lt;br /&gt;
&lt;br /&gt;
===VPT? chunk: (&amp;quot;Vector Pointer Table&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This chunk contains the indexes into the block lookup table which contains&lt;br /&gt;
the data to display the frame.&lt;br /&gt;
The image blocks are called VECTORS in Vector Quantization terminology.&lt;br /&gt;
&lt;br /&gt;
These chunks are always compressed, but I guess the uncompressed ones&lt;br /&gt;
can also be used (although this would lower the overall compression achieved).&lt;br /&gt;
&lt;br /&gt;
The size of this index table is ''(Width/BlockW)*(Height/BlockH)*2 bytes''.&lt;br /&gt;
&lt;br /&gt;
Now, there is a catch: version 2 VQAs use a different layout of the&lt;br /&gt;
index table than version 1 VQAs. I will first describe the version&lt;br /&gt;
2 table format, as it's more common.&lt;br /&gt;
&lt;br /&gt;
====VERSION 2 INDEX TABLE LAYOUT====&lt;br /&gt;
The index table is an array of CHARs and is split into 2 parts - the top&lt;br /&gt;
half and the bottom half.&lt;br /&gt;
&lt;br /&gt;
Now, if you want to diplay the block at coordinates (in block units),&lt;br /&gt;
say (bx,by) you should read two bytes from the table, one from the top&lt;br /&gt;
and one from the bottom half:&lt;br /&gt;
&lt;br /&gt;
  LoVal=Table[by*(Width/BlockW)+bx]&lt;br /&gt;
  HiVal=Table[(Width/BlockW)*(Height/BlockH)+by*(Width/BlockW)+bx]&lt;br /&gt;
&lt;br /&gt;
If HiVal=0x0f (0xff for the start movie of Red Alert 95) you should&lt;br /&gt;
simply fill the block with color LoVal, otherwise you should copy&lt;br /&gt;
the block with index number HiVal*256+LoVal from the lookup table.&lt;br /&gt;
&lt;br /&gt;
Do that for every block on the screen (remember, there are Width/BlockW&lt;br /&gt;
blocks in the horizontal direction and Height/BlockH blocks in the vertical&lt;br /&gt;
direction) and you've decoded your first frame!&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' I was unable to find an entry in the VQA header which determines&lt;br /&gt;
whether 0x0f or 0xff is used in HiVal to signal that the block&lt;br /&gt;
is of uniform color. I assume that the Wy entry of the header&lt;br /&gt;
implies this: If BlockW=2 -&amp;gt; 0x0f is used, if BlockH=4 -&amp;gt; 0xff is used.&lt;br /&gt;
&lt;br /&gt;
====VERSION 1 INDEX TABLE LAYOUT====&lt;br /&gt;
Here, the index table is simply an array of SHORTs written in normal, Intel&lt;br /&gt;
byte order.&lt;br /&gt;
&lt;br /&gt;
The LoVal and HiVal are given as:&lt;br /&gt;
&lt;br /&gt;
  LoVal=Table[(by*(Width/BlockW)+bx)*2]&lt;br /&gt;
  HiVal=Table[(by*(Width/BlockW)+bx)*2+1]&lt;br /&gt;
&lt;br /&gt;
If HiVal=0xff, the block is of uniform color which is (255-LoVal).&lt;br /&gt;
Otherwise, write the block with index number (HiVal*256+LoVal)/8.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Appendix A===&lt;br /&gt;
&lt;br /&gt;
FORMAT80 COMPRESSION METHOD&lt;br /&gt;
by Vladan Bato (bat22@geocities.com)&lt;br /&gt;
&lt;br /&gt;
There are several different commands, with different sizes : from 1 to 5 bytes.&lt;br /&gt;
The positions mentioned below always refer to the destination buffer (i.e.&lt;br /&gt;
the uncompressed image). The relative positions are relative to the current&lt;br /&gt;
position in the destination buffer, which is one byte beyond the last written&lt;br /&gt;
byte.&lt;br /&gt;
&lt;br /&gt;
I will give some sample code at the end.&lt;br /&gt;
&lt;br /&gt;
(1) 1 byte&lt;br /&gt;
      +---+---+---+---+---+---+---+---+&lt;br /&gt;
      | 1 | 0 |   |   |   |   |   |   |&lt;br /&gt;
      +---+---+---+---+---+---+---+---+&lt;br /&gt;
              \_______________________/&lt;br /&gt;
                         |&lt;br /&gt;
                       Count&lt;br /&gt;
&lt;br /&gt;
      This one means : copy next Count bytes as is from Source to Dest.&lt;br /&gt;
&lt;br /&gt;
(2) 2 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
  | 0 |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
      \___________/\__________________________________________________/&lt;br /&gt;
            |                             |&lt;br /&gt;
         Count-3                    Relative Pos.&lt;br /&gt;
&lt;br /&gt;
  This means copy Count bytes from Dest at Current Pos.-Rel. Pos. to&lt;br /&gt;
  Current position.&lt;br /&gt;
  Note that you have to add 3 to the number you find in the bits 4-6 of the&lt;br /&gt;
  first byte to obtain the Count.&lt;br /&gt;
  Note that if the Rel. Pos. is 1, that means repeat Count times the previous&lt;br /&gt;
  byte.&lt;br /&gt;
&lt;br /&gt;
(3) 3 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---------------+---------------+&lt;br /&gt;
  | 1 | 1 |   |   |   |   |   |   |   |               |               |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---------------+---------------+&lt;br /&gt;
          \_______________________/                  Pos&lt;br /&gt;
                     |&lt;br /&gt;
                 Count-3&lt;br /&gt;
&lt;br /&gt;
  Copy Count bytes from Pos, where Pos is absolute from the start of the&lt;br /&gt;
  destination buffer. (Pos is a word, that means that the images can't be&lt;br /&gt;
  larger than 64K)&lt;br /&gt;
&lt;br /&gt;
(4) 4 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+&lt;br /&gt;
  | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |   |       |       |  |       |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+&lt;br /&gt;
                                            Count          Color&lt;br /&gt;
&lt;br /&gt;
  Write Color Count times.&lt;br /&gt;
  (Count is a word, color is a byte)&lt;br /&gt;
&lt;br /&gt;
(5) 5 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+&lt;br /&gt;
  | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |   |       |       |  |       |       |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+&lt;br /&gt;
                                            Count               Pos&lt;br /&gt;
&lt;br /&gt;
  Copy Count bytes from Dest. starting at Pos. Pos is absolute from the start&lt;br /&gt;
  of the Destination buffer.&lt;br /&gt;
  Both Count and Pos are words.&lt;br /&gt;
&lt;br /&gt;
These are all the commands I found out. Maybe there are other ones, but I&lt;br /&gt;
haven't seen them yet.&lt;br /&gt;
&lt;br /&gt;
All the images end with a 80h command.&lt;br /&gt;
&lt;br /&gt;
To make things more clearer here's a piece of code that will uncompress the&lt;br /&gt;
image.&lt;br /&gt;
&lt;br /&gt;
  DP = destination pointer&lt;br /&gt;
  SP = source pointer&lt;br /&gt;
  Source and Dest are the two buffers&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  SP:=0;&lt;br /&gt;
  DP:=0;&lt;br /&gt;
  repeat&lt;br /&gt;
    Com:=Source[SP];&lt;br /&gt;
    inc(SP);&lt;br /&gt;
    b7:=Com shr 7;  {b7 is bit 7 of Com}&lt;br /&gt;
    case b7 of&lt;br /&gt;
      0 : begin  {copy command (2)}&lt;br /&gt;
            {Count is bits 4-6 + 3}&lt;br /&gt;
            Count:=(Com and $7F) shr 4 + 3;&lt;br /&gt;
            {Position is bits 0-3, with bits 0-7 of next byte}&lt;br /&gt;
            Posit:=(Com and $0F) shl 8+Source[SP];&lt;br /&gt;
            Inc(SP);&lt;br /&gt;
            {Starting pos=Cur pos. - calculated value}&lt;br /&gt;
            Posit:=DP-Posit;&lt;br /&gt;
            for i:=Posit to Posit+Count-1 do&lt;br /&gt;
            begin&lt;br /&gt;
              Dest[DP]:=Dest[i];&lt;br /&gt;
              Inc(DP);&lt;br /&gt;
            end;&lt;br /&gt;
          end;&lt;br /&gt;
      1 : begin&lt;br /&gt;
            {Check bit 6 of Com}&lt;br /&gt;
            b6:=(Com and $40) shr 6;&lt;br /&gt;
            case b6 of&lt;br /&gt;
              0 : begin  {Copy as is command (1)}&lt;br /&gt;
                    Count:=Com and $3F;  {mask 2 topmost bits}&lt;br /&gt;
                    if Count=0 then break; {EOF marker}&lt;br /&gt;
                    for i:=1 to Count do&lt;br /&gt;
                    begin&lt;br /&gt;
                      Dest[DP]:=Source[SP];&lt;br /&gt;
                      Inc(DP);&lt;br /&gt;
                      Inc(SP);&lt;br /&gt;
                    end;&lt;br /&gt;
                  end;&lt;br /&gt;
              1 : begin  {large copy, very large copy and fill commands}&lt;br /&gt;
                    {Count = (bits 0-5 of Com) +3}&lt;br /&gt;
                    {if Com=FEh then fill, if Com=FFh then very large copy}&lt;br /&gt;
                    Count:=Com and $3F;&lt;br /&gt;
                    if Count&amp;lt;$3E then {large copy (3)}&lt;br /&gt;
                    begin&lt;br /&gt;
                      Inc(Count,3);&lt;br /&gt;
                      {Next word = pos. from start of image}&lt;br /&gt;
                      Posit:=Word(Source[SP]);&lt;br /&gt;
                      Inc(SP,2);&lt;br /&gt;
                      for i:=Posit to Posit+Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=Dest[i];&lt;br /&gt;
                        Inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end&lt;br /&gt;
                    else if Count=$3F then   {very large copy (5)}&lt;br /&gt;
                    begin&lt;br /&gt;
                      {next 2 words are Count and Pos}&lt;br /&gt;
                      Count:=Word(Source[SP]);&lt;br /&gt;
                      Posit:=Word(Source[SP+2]);&lt;br /&gt;
                      Inc(SP,4);&lt;br /&gt;
                      for i:=Posit to Posit+Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=Dest[i];&lt;br /&gt;
                        Inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end else&lt;br /&gt;
                    begin   {Count=$3E, fill (4)}&lt;br /&gt;
                      {Next word is count, the byte after is color}&lt;br /&gt;
                      Count:=Word(Source[SP]);&lt;br /&gt;
                      Inc(SP,2);&lt;br /&gt;
                      b:=Source[SP];&lt;br /&gt;
                      Inc(SP);&lt;br /&gt;
                      for i:=0 to Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=b;&lt;br /&gt;
                        inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end;&lt;br /&gt;
                  end;&lt;br /&gt;
            end;&lt;br /&gt;
          end;&lt;br /&gt;
    end;&lt;br /&gt;
  until false;&lt;br /&gt;
&lt;br /&gt;
Note that you won't be able to compile this code, because the typecasting&lt;br /&gt;
won't work. (But I'm sure you'll be able to fix it).&lt;br /&gt;
&lt;br /&gt;
===Appendix B===&lt;br /&gt;
IMA ADPCM DECOMPRESSION&lt;br /&gt;
by Vladan Bato  (bat22@geocities.com)&lt;br /&gt;
http://www.geocities.com/SiliconValley/8682&lt;br /&gt;
&lt;br /&gt;
Note that the current sample value and index into the Step Table should&lt;br /&gt;
be initialized to 0 at the start and are maintained across the chunks&lt;br /&gt;
(see below).&lt;br /&gt;
&lt;br /&gt;
====IMA-ADPCM DECOMPRESSION ====&lt;br /&gt;
&lt;br /&gt;
It is the exact opposite of the above. It receives 4-bit codes in input&lt;br /&gt;
and produce 16-bit samples in output.&lt;br /&gt;
&lt;br /&gt;
Again you have to mantain an Index into the Step Table an the current&lt;br /&gt;
sample value.&lt;br /&gt;
&lt;br /&gt;
The tables used are the same as for compression.&lt;br /&gt;
&lt;br /&gt;
Here's the code :&lt;br /&gt;
&lt;br /&gt;
  Index:=0;&lt;br /&gt;
  Cur_Sample:=0;&lt;br /&gt;
&lt;br /&gt;
  while there_is_more_data do&lt;br /&gt;
  begin&lt;br /&gt;
    Code:=Get_Next_Code;&lt;br /&gt;
&lt;br /&gt;
    if (Code and $8) &amp;lt;&amp;gt; 0 then Sb:=1 else Sb:=0;&lt;br /&gt;
    Code:=Code and $7;&lt;br /&gt;
    {Separate the sign bit from the rest}&lt;br /&gt;
&lt;br /&gt;
    Delta:=(Step_Table[Index]*Code) div 4 + Step_Table[Index] div 8;&lt;br /&gt;
    {The last one is to minimize errors}&lt;br /&gt;
&lt;br /&gt;
    if Sb=1 then Delta:=-Delta;&lt;br /&gt;
&lt;br /&gt;
    Cur_Sample:=Cur_Sample+Delta;&lt;br /&gt;
    if Cur_Sample&amp;gt;32767 then Cur_Sample:=32767&lt;br /&gt;
    else if Cur_Sample&amp;lt;-32768 then Cur_Sample:=-32768;&lt;br /&gt;
&lt;br /&gt;
    Output_Sample(Cur_Sample);&lt;br /&gt;
&lt;br /&gt;
    Index:=Index+Index_Adjust[Code];&lt;br /&gt;
    if Index&amp;lt;0 then Index:=0;&lt;br /&gt;
    if Index&amp;gt;88 the Index:=88;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
Again, this can be done more efficiently (no need for multiplication).&lt;br /&gt;
&lt;br /&gt;
The ''Get_Next_Code'' function should return the next 4-bit code. It must&lt;br /&gt;
extract it from the input buffer (note that two 4-bit codes are stored&lt;br /&gt;
in the same byte, the first one in the lower bits).&lt;br /&gt;
&lt;br /&gt;
The Output_Sample function should write the signed 16-bit sample to the&lt;br /&gt;
output buffer.&lt;br /&gt;
&lt;br /&gt;
=== Appendix A : THE INDEX ADJUSTMENT TABLE ===&lt;br /&gt;
&lt;br /&gt;
  Index_Adjust : array [0..7] of integer = (-1,-1,-1,-1,2,4,6,8);&lt;br /&gt;
&lt;br /&gt;
=== Appendix B : THE STEP TABLE ===&lt;br /&gt;
&lt;br /&gt;
  Steps_Table : array [0..88] of integer =(&lt;br /&gt;
        7,     8,     9,     10,    11,    12,     13,    14,    16,&lt;br /&gt;
        17,    19,    21,    23,    25,    28,     31,    34,    37,&lt;br /&gt;
        41,    45,    50,    55,    60,    66,     73,    80,    88,&lt;br /&gt;
        97,    107,   118,   130,   143,   157,    173,   190,   209,&lt;br /&gt;
        230,   253,   279,   307,   337,   371,    408,   449,   494,&lt;br /&gt;
        544,   598,   658,   724,   796,   876,    963,   1060,  1166,&lt;br /&gt;
        1282,  1411,  1552,  1707,  1878,  2066,   2272,  2499,  2749,&lt;br /&gt;
        3024,  3327,  3660,  4026,  4428,  4871,   5358,  5894,  6484,&lt;br /&gt;
        7132,  7845,  8630,  9493,  10442, 11487,  12635, 13899, 15289,&lt;br /&gt;
        16818, 18500, 20350, 22385, 24623, 27086,  29794, 32767 );&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Appendix C===&lt;br /&gt;
WESTWOOD STUDIOS' ADPCM DECOMPRESSION&lt;br /&gt;
by Asatur V. Nazarian (samael@avn.mccme.ru)&lt;br /&gt;
&lt;br /&gt;
==== WS ADPCM Decompression Algorithm ====&lt;br /&gt;
&lt;br /&gt;
Each SND1 chunk may be decompressed independently of others. This lets you&lt;br /&gt;
implement seeking/skipping for WS ADPCM sounds (unlike IMA ADPCM ones).&lt;br /&gt;
But during the decompression of the given chunk a variable (''CurSample'') should&lt;br /&gt;
be maintained for this whole chunk:&lt;br /&gt;
&lt;br /&gt;
 SHORT CurSample;&lt;br /&gt;
 BYTE  InputBuffer[InputBufferSize]; // input buffer containing the whole chunk&lt;br /&gt;
 WORD  wSize, wOutSize; // Size and OutSize values from this chunk's header&lt;br /&gt;
 BYTE  code;&lt;br /&gt;
 CHAR  count; // this is a signed char!&lt;br /&gt;
 WORD  i; // index into InputBuffer&lt;br /&gt;
 WORD  input; // shifted input&lt;br /&gt;
 &lt;br /&gt;
 if (wSize==wOutSize) // such chunks are NOT compressed&lt;br /&gt;
 {&lt;br /&gt;
  for (i=0;i&amp;lt;wOutSize;i++)&lt;br /&gt;
      Output(InputBuffer[i]); // send to output stream&lt;br /&gt;
  return; // chunk is done!&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // otherwise we need to decompress chunk&lt;br /&gt;
 &lt;br /&gt;
 CurSample=0x80; // unsigned 8-bit&lt;br /&gt;
 i=0;&lt;br /&gt;
 &lt;br /&gt;
 // note that wOutSize value is crucial for decompression!&lt;br /&gt;
 &lt;br /&gt;
 while (wOutSize&amp;gt;0) // until wOutSize is exhausted!&lt;br /&gt;
 {&lt;br /&gt;
  input=InputBuffer[i++];&lt;br /&gt;
  input&amp;lt;&amp;lt;=2;&lt;br /&gt;
  code=HIBYTE(input);&lt;br /&gt;
  count=LOBYTE(input)&amp;gt;&amp;gt;2;&lt;br /&gt;
  switch (code) // parse code&lt;br /&gt;
  {&lt;br /&gt;
    case 2: // no compression...&lt;br /&gt;
	 if (count &amp;amp; 0x20)&lt;br /&gt;
	 {&lt;br /&gt;
	   count&amp;lt;&amp;lt;=3;		// here it's significant that (count) is signed:&lt;br /&gt;
	   CurSample+=count&amp;gt;&amp;gt;3; // the sign bit will be copied by these shifts!&lt;br /&gt;
&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize--; // one byte added to output&lt;br /&gt;
	 }&lt;br /&gt;
	 else // copy (count+1) bytes from input to output&lt;br /&gt;
	 {&lt;br /&gt;
	   for (count++;count&amp;gt;0;count--,wOutSize--,i++)&lt;br /&gt;
	       Output(InputBuffer[i]);&lt;br /&gt;
	   CurSample=InputBuffer[i-1]; // set (CurSample) to the last byte sent to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    case 1: // ADPCM 8-bit -&amp;gt; 4-bit&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--) // decode (count+1) bytes&lt;br /&gt;
	 {&lt;br /&gt;
	   code=InputBuffer[i++];&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable4bit[(code &amp;amp; 0x0F)]; // lower nibble&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable4bit[(code &amp;gt;&amp;gt; 4)]; // higher nibble&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize-=2; // two bytes added to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    case 0: // ADPCM 8-bit -&amp;gt; 2-bit&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--) // decode (count+1) bytes&lt;br /&gt;
	 {&lt;br /&gt;
	   code=InputBuffer[i++];&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[(code &amp;amp; 0x03)]; // lower 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;2) &amp;amp; 0x03)]; // lower middle 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;4) &amp;amp; 0x03)]; // higher middle 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;6) &amp;amp; 0x03)]; // higher 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize-=4; // 4 bytes sent to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    default: // just copy (CurSample) (count+1) times to output&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--,wOutSize--)&lt;br /&gt;
	     Output((BYTE)CurSample);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
HIBYTE and LOBYTE are just higher and lower bytes of WORD:&lt;br /&gt;
 #define HIBYTE(word) ((word) &amp;gt;&amp;gt; 8)&lt;br /&gt;
 #define LOBYTE(word) ((word) &amp;amp; 0xFF)&lt;br /&gt;
Note that depending on your compiler you may need to use additional byte&lt;br /&gt;
separation in these defines, e.g. (((byte) &amp;gt;&amp;gt; 8) &amp;amp; 0xFF). The same holds for&lt;br /&gt;
4-bit and 2-bit nibble separation in the code above.&lt;br /&gt;
&lt;br /&gt;
''WSTable4bit'' and ''WSTable2bit'' are the delta tables given in the next section.&lt;br /&gt;
&lt;br /&gt;
Output() is just a placeholder for any action you would like to perform for&lt;br /&gt;
decompressed sample value.&lt;br /&gt;
&lt;br /&gt;
Clip8BitSample is quite evident:&lt;br /&gt;
&lt;br /&gt;
 SHORT Clip8BitSample(SHORT sample)&lt;br /&gt;
 {&lt;br /&gt;
  if (sample&amp;gt;255)&lt;br /&gt;
     return 255;&lt;br /&gt;
  else if (sample&amp;lt;0)&lt;br /&gt;
     return 0;&lt;br /&gt;
  else&lt;br /&gt;
     return sample;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This algorithm is ONLY for mono 8-bit unsigned sound, as I've never seen any&lt;br /&gt;
other sound format used with WS ADPCM compression.&lt;br /&gt;
&lt;br /&gt;
Of course, the decompression routine described above may be greatly&lt;br /&gt;
optimized.&lt;br /&gt;
&lt;br /&gt;
==== WS ADPCM Tables ====&lt;br /&gt;
&lt;br /&gt;
 CHAR WSTable2bit[]=&lt;br /&gt;
 {&lt;br /&gt;
    -2,&lt;br /&gt;
    -1,&lt;br /&gt;
     0,&lt;br /&gt;
     1&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 CHAR WSTable4bit[]=&lt;br /&gt;
 {&lt;br /&gt;
    -9, -8, -6, -5, -4, -3, -2, -1,&lt;br /&gt;
     0,  1,  2,  3,  4,  5,  6,  8&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
==HC_VQA.txt==&lt;br /&gt;
by Gordan Ugarkovic (ugordan@yahoo.com)&lt;br /&gt;
http://members.xoom.com/ugordan&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This document describes how to view Westwood's HiColor VQA movies. These&lt;br /&gt;
are version 3 movies, but there are at least some version 2 VQAs that are&lt;br /&gt;
in this format. I will not describe the whole VQA layout here, I will&lt;br /&gt;
just explain how to display the VIDEO stream of the VQA, that is, how&lt;br /&gt;
to decompress the CBFZ and VPTR/VPRZ chunks.&lt;br /&gt;
&lt;br /&gt;
First a little warning: I'm not sure which flag denotes the VQA is 8 bit&lt;br /&gt;
or 15 bit. I'm pretty convinced it's either bit 4 (0x10) or bit 2 (0x04)&lt;br /&gt;
of the Flags entry (see my VQA_INFO.TXT) in the header. Another way would &lt;br /&gt;
be to check the Colors entry in the header, if it is 0 it could imply &lt;br /&gt;
a HiColor movie.&lt;br /&gt;
&lt;br /&gt;
There's a major difference between the old (8 bit, 256 color) and the new,&lt;br /&gt;
HiColor VQAs. Lookup tables are no longer split up into several CBP?&lt;br /&gt;
chunks, instead they come in one piece (a CBFZ chunk). Two lookup tables&lt;br /&gt;
can now be many frames apart, not just 8 (as usual). This is indicated&lt;br /&gt;
by the CBParts entry of the header (see VQA_INFO.TXT), which is set to 0.&lt;br /&gt;
Subsequent frames use the last lookup table loaded, of course.&lt;br /&gt;
&lt;br /&gt;
Another thing: It appears the first CBFZ chunk comes inside the VQFR chunk&lt;br /&gt;
but the other ones seem to be located inside their own chunks,&lt;br /&gt;
called VQFL, which are followed by the usual VQFR chunks (containing&lt;br /&gt;
VPTR/VPRZ chunks).&lt;br /&gt;
&lt;br /&gt;
Also, the movies are 15 bit, NOT 16 bit. There is a difference because&lt;br /&gt;
in 16 bit color depth there are 6 bits for the green channel, but&lt;br /&gt;
the VQAs use 5.&lt;br /&gt;
&lt;br /&gt;
=== The CBFZ chunks===&lt;br /&gt;
&lt;br /&gt;
These are a bit modified since the 8 bit VQAs. If the first byte of the&lt;br /&gt;
chunk is not NULL (0x00), it means the chunk is compressed using the&lt;br /&gt;
standard Format80 algorithm (see Vladan Bato's text on C&amp;amp;C file formats),&lt;br /&gt;
starting from that byte. If the first byte is NULL, the chunk is compressed&lt;br /&gt;
using a modified version of Format80 (see below), starting from the next&lt;br /&gt;
byte of the chunk. The original Format80 algorithm is used when the&lt;br /&gt;
amount of data to be compressed is less than 64 KB, otherwise the 'new'&lt;br /&gt;
algorithm is used.&lt;br /&gt;
&lt;br /&gt;
When decompressed properly, a CBFZ chunk expands into 15 bit pixels packed&lt;br /&gt;
as shorts in normal Intel byte order. The red, green and blue values are&lt;br /&gt;
packed like this:&lt;br /&gt;
&lt;br /&gt;
  15      bit      0&lt;br /&gt;
   arrrrrgg gggbbbbb&lt;br /&gt;
   HI byte  LO byte&lt;br /&gt;
&lt;br /&gt;
The r,g,b values make up a pixel and they can range from 0-31.&lt;br /&gt;
As in the old CBFZ chunks, these pixels make up the block lookup table&lt;br /&gt;
(also called a codebook). The a (alpha) value is used in Blade Runner in&lt;br /&gt;
overlay videos to indicate a transparent pixel.&lt;br /&gt;
&lt;br /&gt;
=== The VPTR chunks===&lt;br /&gt;
&lt;br /&gt;
These chunks use some sort of differential, run-length algorithm that&lt;br /&gt;
only records changes from the previous frame. Therefore, the previous&lt;br /&gt;
frame bitmap must be maintained throughout all the frames (you could&lt;br /&gt;
just draw the blocks that changed, though). This makes dropping frames&lt;br /&gt;
(in case of bad performance) impossible.&lt;br /&gt;
&lt;br /&gt;
When decoding, you take a short int (Intel) from the chunk and examine &lt;br /&gt;
its 3 most significant bits (bits 15,14,13). These bits make up a &lt;br /&gt;
code prefix that determines which action is to be done.&lt;br /&gt;
&lt;br /&gt;
Here's a list of the prefixes I encountered and their description&lt;br /&gt;
(Val is the short int value):&lt;br /&gt;
&lt;br /&gt;
  BITS - MEANING&lt;br /&gt;
&lt;br /&gt;
   000 - Skip Count blocks. Count is (Val &amp;amp; 0x1fff).&lt;br /&gt;
&lt;br /&gt;
   001 - Write block number (Val &amp;amp; 0xff) Count times.&lt;br /&gt;
         Count is (((Val/256) &amp;amp; 0x1f)+1)*2. Note that this can only&lt;br /&gt;
         index the first 256 blocks.&lt;br /&gt;
&lt;br /&gt;
   010 - Write block number (Val &amp;amp; 0xff) and then write Count blocks&lt;br /&gt;
         getting their indexes by reading next Count bytes from&lt;br /&gt;
         the VPTR chunk. Count is (((Val/256) &amp;amp; 0x1f)+1)*2.&lt;br /&gt;
         Again, the block numbers range from 0-255.&lt;br /&gt;
&lt;br /&gt;
   011 - Write block (Val &amp;amp; 0x1fff).&lt;br /&gt;
&lt;br /&gt;
   100 - Same as 011 but skip pixels with alpha bit set.&lt;br /&gt;
&lt;br /&gt;
   101 - Write block (Val &amp;amp; 0x1fff) Count times. Count is the next&lt;br /&gt;
         byte from the VPTR chunk.&lt;br /&gt;
&lt;br /&gt;
   110 - Same as 101 but skip pixels with alpha bit set.&lt;br /&gt;
&lt;br /&gt;
After this, you take the next short int and repeat the above process.&lt;br /&gt;
&lt;br /&gt;
Prefixes 100 and 110 are used in Blade Runner for overlay videos.&lt;br /&gt;
&lt;br /&gt;
Every row of blocks is processed individually of others.&lt;br /&gt;
When you encounter the end of a row, proceed to the next row&lt;br /&gt;
(blocks are processed left to right, top to down).&lt;br /&gt;
Repeat this process until all blocks in the frame are covered and&lt;br /&gt;
that's it!&lt;br /&gt;
&lt;br /&gt;
Note that the above implies an absolute maximum of 8192 blocks (0x1fff+1). &lt;br /&gt;
&lt;br /&gt;
As for the VPRZ chunks, these are just VPTR chunks compressed with&lt;br /&gt;
the standard Format80 algorithm.&lt;br /&gt;
&lt;br /&gt;
=== The modified Format80 scheme ===&lt;br /&gt;
&lt;br /&gt;
This is really only a small modification of the basic algorithm.&lt;br /&gt;
The only commands that are modified are commands (3) and (5)&lt;br /&gt;
See Vladan's text). Instead of using offsets ABSOLUTE from&lt;br /&gt;
the start of the destination buffer, offsets RELATIVE to the&lt;br /&gt;
current destination pointer are used. If you ask me, I don't see&lt;br /&gt;
why this approach wasn't used in the first place as it would&lt;br /&gt;
suffer no disadvantage with the old files and it would be much&lt;br /&gt;
easier to compress even larger amounts of data. The guys at WW&lt;br /&gt;
were just careless, I guess... :-)&lt;br /&gt;
&lt;br /&gt;
Anyway, in Vladan's algorithm, there is a line in&lt;br /&gt;
command (3) that says:&lt;br /&gt;
    Posit:=Word(Source[SP]);&lt;br /&gt;
it should say:&lt;br /&gt;
    Posit:=DP-Word(Source[SP]);&lt;br /&gt;
&lt;br /&gt;
Likewise, for command (5):&lt;br /&gt;
    Posit:=Word(Source[SP+2]);&lt;br /&gt;
it should be:&lt;br /&gt;
    Posit:=DP-Word(Source[SP+2]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Games Using VQA ==&lt;br /&gt;
&lt;br /&gt;
=== Version 1 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/win3x/legend-of-kyrandia-malcolms-revenge The Legend of Kyrandia: Malcolm's Revenge]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/monopoly Monopoly] (Version 1???)&lt;br /&gt;
&lt;br /&gt;
=== Versions 2 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/dos/command-conquer Command &amp;amp; Conquer]&lt;br /&gt;
* [http://www.mobygames.com/game/dos/command-conquer-red-alert Command &amp;amp; Conquer: Red Alert]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/command-conquer-sole-survivor Command &amp;amp; Conquer: Sole Survivor]&lt;br /&gt;
* [http://www.mobygames.com/game/lands-of-lore-guardians-of-destiny Lands of Lore: Guardians of Destiny]&lt;br /&gt;
&lt;br /&gt;
=== Versions 3 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/windows/blade-runner Blade Runner]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/command-conquer-tiberian-sun Command &amp;amp; Conquer: Tiberian Sun]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/dune-2000 Dune 2000]&lt;br /&gt;
* [http://www.mobygames.com/game/lands-of-lore-iii Lands of Lore III]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/nox Nox]&lt;br /&gt;
&lt;br /&gt;
==Other Documentation==&lt;br /&gt;
http://www.gamers.org/pub/idgames2/planetquake/planetcnc/cncdz/ has lots of data about this format. See vqa_overview.zip and vqafilesguild.zip. Also there is a decoder (vqatoavi) that decodes the dune2000 sample. &lt;br /&gt;
&lt;br /&gt;
[[Category:Game Formats]]&lt;br /&gt;
[[Category:Video Codecs]]&lt;br /&gt;
[[Category:Incomplete Video Codecs]]&lt;/div&gt;</summary>
		<author><name>Andrew Dream</name></author>
	</entry>
	<entry>
		<id>https://wiki.multimedia.cx/index.php?title=VQA&amp;diff=16097</id>
		<title>VQA</title>
		<link rel="alternate" type="text/html" href="https://wiki.multimedia.cx/index.php?title=VQA&amp;diff=16097"/>
		<updated>2025-11-25T12:27:24Z</updated>

		<summary type="html">&lt;p&gt;Andrew Dream: /* CBP? chunk (&amp;quot;CodeBook Part&amp;quot; ???) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Extension: vqa&lt;br /&gt;
* Company: [[Westwood Studios]]&lt;br /&gt;
* Samples: [http://samples.mplayerhq.hu/game-formats/vqa/ http://samples.mplayerhq.hu/game-formats/vqa/]&lt;br /&gt;
* Samples: {{DexvertSamples|video/westwoodStudiosVQA}}&lt;br /&gt;
&lt;br /&gt;
VQA stands for Vector Quantized Animation and is a FMV format used in a number of computer games by Westwood Studios.&lt;br /&gt;
&lt;br /&gt;
== Technical Description ==&lt;br /&gt;
&lt;br /&gt;
'''TODO: import and combine Gordan Ugarkovic's documents from [http://multimedia.cx/VQA_INFO.TXT http://multimedia.cx/VQA_INFO.TXT] and [http://multimedia.cx/HC-VQA.TXT http://multimedia.cx/HC-VQA.TXT].&lt;br /&gt;
==VQA_INFO.txt==&lt;br /&gt;
&lt;br /&gt;
===NOTE #1=== &lt;br /&gt;
This document only applies to VQA files in the original C&amp;amp;C and C&amp;amp;C: Red Alert (version 2) as well as Legend of Kyrandia III : Malcolm's Revenge (version 1).   It DOES NOT apply to the HiColor VQAs found in Westwood's newer games. They use a somewhat different approach to compressing video data (I will provide some facts about their sound stream, though). Look at my other document (HC-VQA.TXT) for a description of the HiColor VQA movies.&lt;br /&gt;
&lt;br /&gt;
===NOTE #2=== &lt;br /&gt;
Throughout the document, I will assume that:&lt;br /&gt;
* CHAR is 1 byte in size, unsigned,&lt;br /&gt;
* SHORT is 2 bytes in size, unsigned,&lt;br /&gt;
* LONG is 4 bytes in size.&lt;br /&gt;
&lt;br /&gt;
Each VQA file is comprised of a series of chunks. A chunk can contain&lt;br /&gt;
other sub-chunks nested in it. Every chunk has a 4 letter ID (all uppercase&lt;br /&gt;
letters) and a LONG written using Motorola byte ordering system (first&lt;br /&gt;
comes the Most Significant Byte), unlike the usual Intel system (Least&lt;br /&gt;
Significant Byte first).&lt;br /&gt;
&lt;br /&gt;
For example, if you had a value 0x12345678 in hexadecimal, using the Intel&lt;br /&gt;
notation it would be written as 78 56 34 12, while using Motorola's &lt;br /&gt;
12 34 56 78.&lt;br /&gt;
&lt;br /&gt;
NOTE: Some chunk IDs start with a NULL byte (0x00) because of reasons&lt;br /&gt;
that will become apparent later. You should just skip this byte&lt;br /&gt;
and assume the next 4 letters hold the chunk ID.&lt;br /&gt;
&lt;br /&gt;
Following the chunk header is the chunk data.&lt;br /&gt;
&lt;br /&gt;
===Typical VQA File===&lt;br /&gt;
Here is a scheme of a typical VQA file (nested chunks are indented):&lt;br /&gt;
&lt;br /&gt;
 FORM&lt;br /&gt;
   VQHD&lt;br /&gt;
   FINF       &amp;lt;-  Frame data positions&lt;br /&gt;
   SND?    \  &amp;lt;-  First sound chunk, contains 1/2 second of sound&lt;br /&gt;
   SND?     |     &amp;lt;- Contains 1 frame's worth of sound&lt;br /&gt;
   VQFR     |     &amp;lt;- Contains various video data chunks&lt;br /&gt;
     CBF?   | 1st frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     CPL?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
   SND?    \&lt;br /&gt;
   VQFR     | 2nd frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
   SND?    \&lt;br /&gt;
   VQFR     | 3rd frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
 . . .&lt;br /&gt;
&lt;br /&gt;
NOTE: There can also be some other chunks (i.e. PINF, PINH, SN2J) included,&lt;br /&gt;
but they are not relevant (?!) for viewing the movie, so they can&lt;br /&gt;
easily be skipped.&lt;br /&gt;
&lt;br /&gt;
===FORM chunk===&lt;br /&gt;
&lt;br /&gt;
This chunk is the main chunk, containing all other chunks.&lt;br /&gt;
In case of version 2 and 3 movies, its size is actually the size of the&lt;br /&gt;
entire file minus the size of the chunk header (8 bytes). Version 1 movies&lt;br /&gt;
seem to have this set to the length of the header VQHD + FINF chunk.&lt;br /&gt;
&lt;br /&gt;
Immediately after the chunk's header, a 4-character signature,&lt;br /&gt;
&amp;quot;WVQA&amp;quot; is located. Then come all the other chunks.&lt;br /&gt;
&lt;br /&gt;
===VQHD chunk (&amp;quot;VQa HeaDer&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This is the header chunk, containing vital information about the movie.&lt;br /&gt;
Its size is always 42 bytes.&lt;br /&gt;
The information is structured like this:&lt;br /&gt;
&lt;br /&gt;
 struct VQAHeader&lt;br /&gt;
 {&lt;br /&gt;
  short  Version;       /* VQA version number                         */&lt;br /&gt;
  short  Flags;         /* VQA flags                                  */&lt;br /&gt;
  short  NumFrames;     /* Number of frames                           */&lt;br /&gt;
  short  Width;         /* Movie width (pixels)                       */&lt;br /&gt;
  short  Height;        /* Movie height (pixels)                      */&lt;br /&gt;
  char   BlockW;        /* Width of each image block (pixels)         */&lt;br /&gt;
  char   BlockH;        /* Height of each image block (pixels)        */&lt;br /&gt;
  char   FrameRate;     /* Frame rate of the VQA                      */&lt;br /&gt;
  char   CBParts;       /* How many images use the same lookup table  */&lt;br /&gt;
  short  Colors;        /* Maximum number of colors used in VQA       */&lt;br /&gt;
  short  MaxBlocks;     /* Maximum number of image blocks             */&lt;br /&gt;
  short  OffsetX;       /* Offset X position in background movie      */&lt;br /&gt;
  short  OffsetY;       /* Offset Y position in background movie      */&lt;br /&gt;
  short  MaxVPT?Size;   /* Max. VPT? (VPTZ, VPRZ) size                */&lt;br /&gt;
  short  Freq;          /* Sound sampling frequency                   */&lt;br /&gt;
  char   Channels;      /* Number of sound channels                   */&lt;br /&gt;
  char   Bits;          /* Sound resolution                           */&lt;br /&gt;
  long   Unknown1;      /* Always 0 ???                               */&lt;br /&gt;
  short  Unknown2;      /* 0 in old VQAs, 4 in HiColor ones ???       */&lt;br /&gt;
  long   MaxCBFZSize;   /* Max. CBFZ size (in old VQAs usually is 0)  */&lt;br /&gt;
  long   Unknown3;      /* Always 0 ???                               */&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Version denotes the VQA version. Valid values are:&lt;br /&gt;
* 1 - First VQAs, used only in Legend of Kyrandia III.&lt;br /&gt;
* 2 - Used in C&amp;amp;C, Red Alert, Lands of Lore II, Dune 2000.&lt;br /&gt;
* 3 - Lands of Lore III (?), Blade Runner (?), Nox and Tiberian Sun. These VQAs are HiColor (15 bit).&lt;br /&gt;
&lt;br /&gt;
Flags most probably contain some flags. I know that bit 0 (LSB) and 2 (Overlay)&lt;br /&gt;
* bit 0 denotes whether the VQA has a soundtrack or not.&lt;br /&gt;
* (1 = Has sound, 0 = No sound)&lt;br /&gt;
* bit 2 denotes whether this movie is an overlay on the background video.&lt;br /&gt;
* (1 = Yes, 0 = No)&lt;br /&gt;
Movie with bit 2 may play separately, but need apply VPTR\VPRZ chunks&lt;br /&gt;
to &amp;quot;Null&amp;quot; Vector Pointer Table (filled by zeros) for each frame.&lt;br /&gt;
&lt;br /&gt;
If NumFrames of background movie less than NumFrames of overlay movie&lt;br /&gt;
then background movie will be repeat, while will not finish overlay movie.&lt;br /&gt;
&lt;br /&gt;
Width is usually 320, Height is usually 156 or 200 although one movie in&lt;br /&gt;
* Red Alert is 640x400 in size (the start movie for the Win95 version).&lt;br /&gt;
&lt;br /&gt;
Each frame of a VQA movie is comprised of a series of blocks that are&lt;br /&gt;
BlockW pixels in width and BlockH pixels in height. Imagine the frame&lt;br /&gt;
is a mosaic with the blocks being 'pieces' that make up the frame.&lt;br /&gt;
&lt;br /&gt;
BlockW is the width and BlockH is the height of each screen block.&lt;br /&gt;
In VQAs version 2 (and perhaps 1) the blocks are usually 4x2.&lt;br /&gt;
&lt;br /&gt;
FrameRate is always 15 in C&amp;amp;C and RA and seems to be 10 in LoK III.&lt;br /&gt;
&lt;br /&gt;
CBParts denotes how many frames use the same lookup table. It also&lt;br /&gt;
implies how many parts the new block lookup table is split into.&lt;br /&gt;
In C&amp;amp;C and RA it is always 8. When CBParts is 0, and movie have&lt;br /&gt;
CBP? chunks, then need see FINF or CIND chunk.&lt;br /&gt;
&lt;br /&gt;
Colors indicates the maximum number of colors used by the VQA.&lt;br /&gt;
The HiColor VQAs have this set to 0, while the old movies have&lt;br /&gt;
256 or less in here.&lt;br /&gt;
&lt;br /&gt;
Offset X and Offset Y uses only when there is bit 2 in the flags.&lt;br /&gt;
Usually uses chunk CLIP too, where the width and height of the&lt;br /&gt;
background movie are specified.&lt;br /&gt;
&lt;br /&gt;
MaxVPT?Size tells you the size of the largest VPTZ or VPRZ chunk, in bytes.&lt;br /&gt;
&lt;br /&gt;
Freq is usually 22050 Hz. Note that version 1 movies can have this set &lt;br /&gt;
to 0 Hz. In that case, you should use 22050 Hz.&lt;br /&gt;
&lt;br /&gt;
Channels specifies the number of sound channels, i.e. is the sound&lt;br /&gt;
mono or stereo. Channels=1 -&amp;gt; sound is mono, Channels=2 -&amp;gt; stereo.&lt;br /&gt;
C&amp;amp;C and RA almost always use mono sound. &lt;br /&gt;
Version 1 can have this set to 0, but you should use 1 (mono sound).&lt;br /&gt;
The majority of Tiberian Sun movies use stereo sound instead.&lt;br /&gt;
&lt;br /&gt;
* Bits indicates whether the sound is 8 bit or 16 bit.&lt;br /&gt;
Bits=8 -&amp;gt; 8 bit sound, Bits=16 -&amp;gt; 16 bit sound (surprise! :). &lt;br /&gt;
The Legend of Kyrandia III: Malcolm's Revenge uses 8 bits where&lt;br /&gt;
C&amp;amp;C, RA, TS, Dune 2000, Lands of Lore III use 16 bits.&lt;br /&gt;
Note, again, that version 1 of the VQAs can have this set to 0 in&lt;br /&gt;
which case 8 bits are assumed.&lt;br /&gt;
&lt;br /&gt;
MaxCBFZSize tells you the size of the largest CBFZ chunk, in bytes.&lt;br /&gt;
In old VQAs this value may be 0.&lt;br /&gt;
&lt;br /&gt;
Following the chunk data are the sub-chunks.&lt;br /&gt;
&lt;br /&gt;
===FINF chunk (&amp;quot;Frame INFormation&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This chunk contains the positions (absolute from the start of the VQA)&lt;br /&gt;
of data for every frame.&lt;br /&gt;
That means that it points to the SND? chunk associated with that frame,&lt;br /&gt;
which is followed by a VQFR chunk containing frame's video data.&lt;br /&gt;
&lt;br /&gt;
The positions are given as LONGs which are in normal Intel byte order.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' Some frame positions are 0x40000000 too large. This is a flag&lt;br /&gt;
indicating those frames contain a new color palette. To get the&lt;br /&gt;
actual positions, you should subtract 0x40000000 from the values.&lt;br /&gt;
&lt;br /&gt;
'''NOTE #2:''' To get the actual position of the frame data you have to multiply&lt;br /&gt;
the value by 2. This is why some chunk IDs start with 0x00. Since&lt;br /&gt;
you multiply by 2, you can't get an odd chunk position so if the&lt;br /&gt;
chunk position would normally be odd, a 0x00 is inserted to make&lt;br /&gt;
it even.&lt;br /&gt;
&lt;br /&gt;
===SND? chunk (&amp;quot;SouND&amp;quot; ???)===&lt;br /&gt;
These chunks contain the sound data for the movie. The last byte of the ID&lt;br /&gt;
can be either '0', '1' or '2' so the actual IDs would be &amp;quot;SND0&amp;quot;, &amp;quot;SND1&amp;quot;&lt;br /&gt;
and &amp;quot;SND2&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
In VQAs version 1 (Legend of Kyrandia 3) there are ''NumFrames'' sound chunks.&lt;br /&gt;
Old 8 bit VQA movies of version 2 have ''NumFrames+1'' sound chunks.&lt;br /&gt;
Note, however, that Dune2000 movies, which are also version 2, but HiColor&lt;br /&gt;
have ''NumFrames'' sound chunks, instead.&lt;br /&gt;
Version 3 movies also have ''NumFrames'' sound chunks.&lt;br /&gt;
&lt;br /&gt;
In case of the old, 8 bit VQAs, the first chunk contains half a second&lt;br /&gt;
(ver. 2) or more (ver. 1) of the wave data, in all (?) the HiColor movies&lt;br /&gt;
the first chunk contains exactly the amount of sound required for one&lt;br /&gt;
frame of the movie. The downside is this requires a somewhat more advanced &lt;br /&gt;
buffering technique on the side of the player in order to allow smooth &lt;br /&gt;
playback.&lt;br /&gt;
&lt;br /&gt;
===SND0 chunk===&lt;br /&gt;
&lt;br /&gt;
This one contains the raw 8 or 16 bit PCM wave data. If the data is&lt;br /&gt;
8 bit, the sound is unsigned and if it is 16 bit, the samples are&lt;br /&gt;
signed.&lt;br /&gt;
&lt;br /&gt;
===SND1 chunk===&lt;br /&gt;
&lt;br /&gt;
It contains 8 bit sound compressed using Westwood's own&lt;br /&gt;
proprietary ADPCM algorithm. The chunk has a 4 byte header:&lt;br /&gt;
&lt;br /&gt;
  struct SND1Header&lt;br /&gt;
  {&lt;br /&gt;
   short OutSize;&lt;br /&gt;
   short Size;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
These values are needed for the decoding algoritm (see APPENDIX C).&lt;br /&gt;
The encoded samples follow immediately after the header.&lt;br /&gt;
It's important to know this algorithm produces UNSIGNED sound, unlike&lt;br /&gt;
the IMA ADPCM algorithm supplied here (see below). It is, however very&lt;br /&gt;
simple to adapt both algorithms to produce either signed or unsigned&lt;br /&gt;
sample output...&lt;br /&gt;
&lt;br /&gt;
===SND2 chunk===&lt;br /&gt;
&lt;br /&gt;
It contains the 16 bit sound data compressed using the IMA ADPCM&lt;br /&gt;
algorithm which compresses 16 bits into 4. That's why the SND2 chunks&lt;br /&gt;
are 4 times smaller than SND0 chunks and they are used almost all&lt;br /&gt;
the time. For the description of the algorithm, see later in the document.&lt;br /&gt;
  &lt;br /&gt;
Different VQA versions have different stereo sample layout. In case&lt;br /&gt;
of Tiberian Sun stereo sound is encoded the same way as mono except the&lt;br /&gt;
SND2 chunk is split into two halfs. The first half contains the left channel&lt;br /&gt;
sound and the second half contains the right channel. The layout of&lt;br /&gt;
nibbles is as follows: LL LL LL LL LL ... RR RR RR RR RR.&lt;br /&gt;
Old movies (C&amp;amp;C, RA) use a different layout. Here, the nibbles are packed&lt;br /&gt;
together like this: LL RR LL RR LL RR ... This means that two left channel&lt;br /&gt;
samples are packed into one byte and then two right channel samples are&lt;br /&gt;
packed into another.&lt;br /&gt;
&lt;br /&gt;
Naturally, the size of a stereo SND2 chunk is exactly twice as big as&lt;br /&gt;
a mono SND2.&lt;br /&gt;
&lt;br /&gt;
It is important to note that, when decoding, you have to keep separate &lt;br /&gt;
values of ''Index'' and ''Cur_Sample'' for each channel (see APPENDIX B).&lt;br /&gt;
&lt;br /&gt;
===VQFR chunk (&amp;quot;Vector Quantized FRame&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
A chunk that includes many nested sub-chunks which contain video data.&lt;br /&gt;
It doesn't contain any data itself so the sub-chunks follow immediately&lt;br /&gt;
after the VQFR chunk header.&lt;br /&gt;
All following sub-chunks are nested inside a VQFR chunk. They can all&lt;br /&gt;
contain '0' or 'Z' as the last byte of their ID.&lt;br /&gt;
&lt;br /&gt;
* If the last byte is '0' it means that the chunk data is uncompressed.&lt;br /&gt;
* If the last byte is 'Z' it means that the data is compressed using&lt;br /&gt;
Format80 compression. You can find its description in APPENDIX A.&lt;br /&gt;
&lt;br /&gt;
===CBF? chunk (&amp;quot;CodeBook, Full&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
Lookup table containing the screen block data as an array&lt;br /&gt;
of elements that each are BlockW*BlockH bytes long. It is always located&lt;br /&gt;
in the data for the first frame. In Vector Quantization terminology&lt;br /&gt;
these tables are called CODEBOOKS.&lt;br /&gt;
&lt;br /&gt;
There can be max. 0x0f00 of these elements (blocks) at any one time in &lt;br /&gt;
normal VQAs and 0xff00 in the hi-res VQAs (Red Alert 95 start movie) although&lt;br /&gt;
I seriously doubt that so many blocks (0xff00 = 65280 blocks) would&lt;br /&gt;
ever be used.&lt;br /&gt;
&lt;br /&gt;
The uncompressed version of these chunks (&amp;quot;CBF0&amp;quot;) is used mainly in&lt;br /&gt;
the original Command &amp;amp; Conquer, while the compressed version (&amp;quot;CBFZ&amp;quot;)&lt;br /&gt;
is used in C&amp;amp;C: Red Alert.&lt;br /&gt;
&lt;br /&gt;
===CBP? chunk (&amp;quot;CodeBook Part&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
Like CBF?, but it contains a part of the lookup table, so to get the new&lt;br /&gt;
complete table you need to append CBParts of these in frame order.&lt;br /&gt;
Once you get the complete table and display the current frame, replace&lt;br /&gt;
the old table with the new one.&lt;br /&gt;
As in CBF? chunk, the uncompressed chunks are used in C&amp;amp;C and the compressed&lt;br /&gt;
chunks are used in Red Alert.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' If the chunks are CBPZ, first you need to append CBParts of them and&lt;br /&gt;
then decompress the data, NOT decompress each chunk individually.&lt;br /&gt;
&lt;br /&gt;
===CPL? chunk (&amp;quot;Color PaLette&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
The simplest one of all... Contains a palette for the VQA. It is an&lt;br /&gt;
array of red, green and blue values (in that order, all have a size of&lt;br /&gt;
1 byte). Seems that the values range from 0-255, but you should mask out&lt;br /&gt;
the bits 6 and 7 to get the correct palette (VGA hardware uses only&lt;br /&gt;
bits 0..5 anyway).&lt;br /&gt;
&lt;br /&gt;
===VPT? chunk: (&amp;quot;Vector Pointer Table&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This chunk contains the indexes into the block lookup table which contains&lt;br /&gt;
the data to display the frame.&lt;br /&gt;
The image blocks are called VECTORS in Vector Quantization terminology.&lt;br /&gt;
&lt;br /&gt;
These chunks are always compressed, but I guess the uncompressed ones&lt;br /&gt;
can also be used (although this would lower the overall compression achieved).&lt;br /&gt;
&lt;br /&gt;
The size of this index table is ''(Width/BlockW)*(Height/BlockH)*2 bytes''.&lt;br /&gt;
&lt;br /&gt;
Now, there is a catch: version 2 VQAs use a different layout of the&lt;br /&gt;
index table than version 1 VQAs. I will first describe the version&lt;br /&gt;
2 table format, as it's more common.&lt;br /&gt;
&lt;br /&gt;
====VERSION 2 INDEX TABLE LAYOUT====&lt;br /&gt;
The index table is an array of CHARs and is split into 2 parts - the top&lt;br /&gt;
half and the bottom half.&lt;br /&gt;
&lt;br /&gt;
Now, if you want to diplay the block at coordinates (in block units),&lt;br /&gt;
say (bx,by) you should read two bytes from the table, one from the top&lt;br /&gt;
and one from the bottom half:&lt;br /&gt;
&lt;br /&gt;
  LoVal=Table[by*(Width/BlockW)+bx]&lt;br /&gt;
  HiVal=Table[(Width/BlockW)*(Height/BlockH)+by*(Width/BlockW)+bx]&lt;br /&gt;
&lt;br /&gt;
If HiVal=0x0f (0xff for the start movie of Red Alert 95) you should&lt;br /&gt;
simply fill the block with color LoVal, otherwise you should copy&lt;br /&gt;
the block with index number HiVal*256+LoVal from the lookup table.&lt;br /&gt;
&lt;br /&gt;
Do that for every block on the screen (remember, there are Width/BlockW&lt;br /&gt;
blocks in the horizontal direction and Height/BlockH blocks in the vertical&lt;br /&gt;
direction) and you've decoded your first frame!&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' I was unable to find an entry in the VQA header which determines&lt;br /&gt;
whether 0x0f or 0xff is used in HiVal to signal that the block&lt;br /&gt;
is of uniform color. I assume that the Wy entry of the header&lt;br /&gt;
implies this: If BlockW=2 -&amp;gt; 0x0f is used, if BlockH=4 -&amp;gt; 0xff is used.&lt;br /&gt;
&lt;br /&gt;
====VERSION 1 INDEX TABLE LAYOUT====&lt;br /&gt;
Here, the index table is simply an array of SHORTs written in normal, Intel&lt;br /&gt;
byte order.&lt;br /&gt;
&lt;br /&gt;
The LoVal and HiVal are given as:&lt;br /&gt;
&lt;br /&gt;
  LoVal=Table[(by*(Width/BlockW)+bx)*2]&lt;br /&gt;
  HiVal=Table[(by*(Width/BlockW)+bx)*2+1]&lt;br /&gt;
&lt;br /&gt;
If HiVal=0xff, the block is of uniform color which is (255-LoVal).&lt;br /&gt;
Otherwise, write the block with index number (HiVal*256+LoVal)/8.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Appendix A===&lt;br /&gt;
&lt;br /&gt;
FORMAT80 COMPRESSION METHOD&lt;br /&gt;
by Vladan Bato (bat22@geocities.com)&lt;br /&gt;
&lt;br /&gt;
There are several different commands, with different sizes : from 1 to 5 bytes.&lt;br /&gt;
The positions mentioned below always refer to the destination buffer (i.e.&lt;br /&gt;
the uncompressed image). The relative positions are relative to the current&lt;br /&gt;
position in the destination buffer, which is one byte beyond the last written&lt;br /&gt;
byte.&lt;br /&gt;
&lt;br /&gt;
I will give some sample code at the end.&lt;br /&gt;
&lt;br /&gt;
(1) 1 byte&lt;br /&gt;
      +---+---+---+---+---+---+---+---+&lt;br /&gt;
      | 1 | 0 |   |   |   |   |   |   |&lt;br /&gt;
      +---+---+---+---+---+---+---+---+&lt;br /&gt;
              \_______________________/&lt;br /&gt;
                         |&lt;br /&gt;
                       Count&lt;br /&gt;
&lt;br /&gt;
      This one means : copy next Count bytes as is from Source to Dest.&lt;br /&gt;
&lt;br /&gt;
(2) 2 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
  | 0 |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
      \___________/\__________________________________________________/&lt;br /&gt;
            |                             |&lt;br /&gt;
         Count-3                    Relative Pos.&lt;br /&gt;
&lt;br /&gt;
  This means copy Count bytes from Dest at Current Pos.-Rel. Pos. to&lt;br /&gt;
  Current position.&lt;br /&gt;
  Note that you have to add 3 to the number you find in the bits 4-6 of the&lt;br /&gt;
  first byte to obtain the Count.&lt;br /&gt;
  Note that if the Rel. Pos. is 1, that means repeat Count times the previous&lt;br /&gt;
  byte.&lt;br /&gt;
&lt;br /&gt;
(3) 3 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---------------+---------------+&lt;br /&gt;
  | 1 | 1 |   |   |   |   |   |   |   |               |               |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---------------+---------------+&lt;br /&gt;
          \_______________________/                  Pos&lt;br /&gt;
                     |&lt;br /&gt;
                 Count-3&lt;br /&gt;
&lt;br /&gt;
  Copy Count bytes from Pos, where Pos is absolute from the start of the&lt;br /&gt;
  destination buffer. (Pos is a word, that means that the images can't be&lt;br /&gt;
  larger than 64K)&lt;br /&gt;
&lt;br /&gt;
(4) 4 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+&lt;br /&gt;
  | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |   |       |       |  |       |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+&lt;br /&gt;
                                            Count          Color&lt;br /&gt;
&lt;br /&gt;
  Write Color Count times.&lt;br /&gt;
  (Count is a word, color is a byte)&lt;br /&gt;
&lt;br /&gt;
(5) 5 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+&lt;br /&gt;
  | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |   |       |       |  |       |       |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+&lt;br /&gt;
                                            Count               Pos&lt;br /&gt;
&lt;br /&gt;
  Copy Count bytes from Dest. starting at Pos. Pos is absolute from the start&lt;br /&gt;
  of the Destination buffer.&lt;br /&gt;
  Both Count and Pos are words.&lt;br /&gt;
&lt;br /&gt;
These are all the commands I found out. Maybe there are other ones, but I&lt;br /&gt;
haven't seen them yet.&lt;br /&gt;
&lt;br /&gt;
All the images end with a 80h command.&lt;br /&gt;
&lt;br /&gt;
To make things more clearer here's a piece of code that will uncompress the&lt;br /&gt;
image.&lt;br /&gt;
&lt;br /&gt;
  DP = destination pointer&lt;br /&gt;
  SP = source pointer&lt;br /&gt;
  Source and Dest are the two buffers&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  SP:=0;&lt;br /&gt;
  DP:=0;&lt;br /&gt;
  repeat&lt;br /&gt;
    Com:=Source[SP];&lt;br /&gt;
    inc(SP);&lt;br /&gt;
    b7:=Com shr 7;  {b7 is bit 7 of Com}&lt;br /&gt;
    case b7 of&lt;br /&gt;
      0 : begin  {copy command (2)}&lt;br /&gt;
            {Count is bits 4-6 + 3}&lt;br /&gt;
            Count:=(Com and $7F) shr 4 + 3;&lt;br /&gt;
            {Position is bits 0-3, with bits 0-7 of next byte}&lt;br /&gt;
            Posit:=(Com and $0F) shl 8+Source[SP];&lt;br /&gt;
            Inc(SP);&lt;br /&gt;
            {Starting pos=Cur pos. - calculated value}&lt;br /&gt;
            Posit:=DP-Posit;&lt;br /&gt;
            for i:=Posit to Posit+Count-1 do&lt;br /&gt;
            begin&lt;br /&gt;
              Dest[DP]:=Dest[i];&lt;br /&gt;
              Inc(DP);&lt;br /&gt;
            end;&lt;br /&gt;
          end;&lt;br /&gt;
      1 : begin&lt;br /&gt;
            {Check bit 6 of Com}&lt;br /&gt;
            b6:=(Com and $40) shr 6;&lt;br /&gt;
            case b6 of&lt;br /&gt;
              0 : begin  {Copy as is command (1)}&lt;br /&gt;
                    Count:=Com and $3F;  {mask 2 topmost bits}&lt;br /&gt;
                    if Count=0 then break; {EOF marker}&lt;br /&gt;
                    for i:=1 to Count do&lt;br /&gt;
                    begin&lt;br /&gt;
                      Dest[DP]:=Source[SP];&lt;br /&gt;
                      Inc(DP);&lt;br /&gt;
                      Inc(SP);&lt;br /&gt;
                    end;&lt;br /&gt;
                  end;&lt;br /&gt;
              1 : begin  {large copy, very large copy and fill commands}&lt;br /&gt;
                    {Count = (bits 0-5 of Com) +3}&lt;br /&gt;
                    {if Com=FEh then fill, if Com=FFh then very large copy}&lt;br /&gt;
                    Count:=Com and $3F;&lt;br /&gt;
                    if Count&amp;lt;$3E then {large copy (3)}&lt;br /&gt;
                    begin&lt;br /&gt;
                      Inc(Count,3);&lt;br /&gt;
                      {Next word = pos. from start of image}&lt;br /&gt;
                      Posit:=Word(Source[SP]);&lt;br /&gt;
                      Inc(SP,2);&lt;br /&gt;
                      for i:=Posit to Posit+Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=Dest[i];&lt;br /&gt;
                        Inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end&lt;br /&gt;
                    else if Count=$3F then   {very large copy (5)}&lt;br /&gt;
                    begin&lt;br /&gt;
                      {next 2 words are Count and Pos}&lt;br /&gt;
                      Count:=Word(Source[SP]);&lt;br /&gt;
                      Posit:=Word(Source[SP+2]);&lt;br /&gt;
                      Inc(SP,4);&lt;br /&gt;
                      for i:=Posit to Posit+Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=Dest[i];&lt;br /&gt;
                        Inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end else&lt;br /&gt;
                    begin   {Count=$3E, fill (4)}&lt;br /&gt;
                      {Next word is count, the byte after is color}&lt;br /&gt;
                      Count:=Word(Source[SP]);&lt;br /&gt;
                      Inc(SP,2);&lt;br /&gt;
                      b:=Source[SP];&lt;br /&gt;
                      Inc(SP);&lt;br /&gt;
                      for i:=0 to Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=b;&lt;br /&gt;
                        inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end;&lt;br /&gt;
                  end;&lt;br /&gt;
            end;&lt;br /&gt;
          end;&lt;br /&gt;
    end;&lt;br /&gt;
  until false;&lt;br /&gt;
&lt;br /&gt;
Note that you won't be able to compile this code, because the typecasting&lt;br /&gt;
won't work. (But I'm sure you'll be able to fix it).&lt;br /&gt;
&lt;br /&gt;
===Appendix B===&lt;br /&gt;
IMA ADPCM DECOMPRESSION&lt;br /&gt;
by Vladan Bato  (bat22@geocities.com)&lt;br /&gt;
http://www.geocities.com/SiliconValley/8682&lt;br /&gt;
&lt;br /&gt;
Note that the current sample value and index into the Step Table should&lt;br /&gt;
be initialized to 0 at the start and are maintained across the chunks&lt;br /&gt;
(see below).&lt;br /&gt;
&lt;br /&gt;
====IMA-ADPCM DECOMPRESSION ====&lt;br /&gt;
&lt;br /&gt;
It is the exact opposite of the above. It receives 4-bit codes in input&lt;br /&gt;
and produce 16-bit samples in output.&lt;br /&gt;
&lt;br /&gt;
Again you have to mantain an Index into the Step Table an the current&lt;br /&gt;
sample value.&lt;br /&gt;
&lt;br /&gt;
The tables used are the same as for compression.&lt;br /&gt;
&lt;br /&gt;
Here's the code :&lt;br /&gt;
&lt;br /&gt;
  Index:=0;&lt;br /&gt;
  Cur_Sample:=0;&lt;br /&gt;
&lt;br /&gt;
  while there_is_more_data do&lt;br /&gt;
  begin&lt;br /&gt;
    Code:=Get_Next_Code;&lt;br /&gt;
&lt;br /&gt;
    if (Code and $8) &amp;lt;&amp;gt; 0 then Sb:=1 else Sb:=0;&lt;br /&gt;
    Code:=Code and $7;&lt;br /&gt;
    {Separate the sign bit from the rest}&lt;br /&gt;
&lt;br /&gt;
    Delta:=(Step_Table[Index]*Code) div 4 + Step_Table[Index] div 8;&lt;br /&gt;
    {The last one is to minimize errors}&lt;br /&gt;
&lt;br /&gt;
    if Sb=1 then Delta:=-Delta;&lt;br /&gt;
&lt;br /&gt;
    Cur_Sample:=Cur_Sample+Delta;&lt;br /&gt;
    if Cur_Sample&amp;gt;32767 then Cur_Sample:=32767&lt;br /&gt;
    else if Cur_Sample&amp;lt;-32768 then Cur_Sample:=-32768;&lt;br /&gt;
&lt;br /&gt;
    Output_Sample(Cur_Sample);&lt;br /&gt;
&lt;br /&gt;
    Index:=Index+Index_Adjust[Code];&lt;br /&gt;
    if Index&amp;lt;0 then Index:=0;&lt;br /&gt;
    if Index&amp;gt;88 the Index:=88;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
Again, this can be done more efficiently (no need for multiplication).&lt;br /&gt;
&lt;br /&gt;
The ''Get_Next_Code'' function should return the next 4-bit code. It must&lt;br /&gt;
extract it from the input buffer (note that two 4-bit codes are stored&lt;br /&gt;
in the same byte, the first one in the lower bits).&lt;br /&gt;
&lt;br /&gt;
The Output_Sample function should write the signed 16-bit sample to the&lt;br /&gt;
output buffer.&lt;br /&gt;
&lt;br /&gt;
=== Appendix A : THE INDEX ADJUSTMENT TABLE ===&lt;br /&gt;
&lt;br /&gt;
  Index_Adjust : array [0..7] of integer = (-1,-1,-1,-1,2,4,6,8);&lt;br /&gt;
&lt;br /&gt;
=== Appendix B : THE STEP TABLE ===&lt;br /&gt;
&lt;br /&gt;
  Steps_Table : array [0..88] of integer =(&lt;br /&gt;
        7,     8,     9,     10,    11,    12,     13,    14,    16,&lt;br /&gt;
        17,    19,    21,    23,    25,    28,     31,    34,    37,&lt;br /&gt;
        41,    45,    50,    55,    60,    66,     73,    80,    88,&lt;br /&gt;
        97,    107,   118,   130,   143,   157,    173,   190,   209,&lt;br /&gt;
        230,   253,   279,   307,   337,   371,    408,   449,   494,&lt;br /&gt;
        544,   598,   658,   724,   796,   876,    963,   1060,  1166,&lt;br /&gt;
        1282,  1411,  1552,  1707,  1878,  2066,   2272,  2499,  2749,&lt;br /&gt;
        3024,  3327,  3660,  4026,  4428,  4871,   5358,  5894,  6484,&lt;br /&gt;
        7132,  7845,  8630,  9493,  10442, 11487,  12635, 13899, 15289,&lt;br /&gt;
        16818, 18500, 20350, 22385, 24623, 27086,  29794, 32767 );&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Appendix C===&lt;br /&gt;
WESTWOOD STUDIOS' ADPCM DECOMPRESSION&lt;br /&gt;
by Asatur V. Nazarian (samael@avn.mccme.ru)&lt;br /&gt;
&lt;br /&gt;
==== WS ADPCM Decompression Algorithm ====&lt;br /&gt;
&lt;br /&gt;
Each SND1 chunk may be decompressed independently of others. This lets you&lt;br /&gt;
implement seeking/skipping for WS ADPCM sounds (unlike IMA ADPCM ones).&lt;br /&gt;
But during the decompression of the given chunk a variable (''CurSample'') should&lt;br /&gt;
be maintained for this whole chunk:&lt;br /&gt;
&lt;br /&gt;
 SHORT CurSample;&lt;br /&gt;
 BYTE  InputBuffer[InputBufferSize]; // input buffer containing the whole chunk&lt;br /&gt;
 WORD  wSize, wOutSize; // Size and OutSize values from this chunk's header&lt;br /&gt;
 BYTE  code;&lt;br /&gt;
 CHAR  count; // this is a signed char!&lt;br /&gt;
 WORD  i; // index into InputBuffer&lt;br /&gt;
 WORD  input; // shifted input&lt;br /&gt;
 &lt;br /&gt;
 if (wSize==wOutSize) // such chunks are NOT compressed&lt;br /&gt;
 {&lt;br /&gt;
  for (i=0;i&amp;lt;wOutSize;i++)&lt;br /&gt;
      Output(InputBuffer[i]); // send to output stream&lt;br /&gt;
  return; // chunk is done!&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // otherwise we need to decompress chunk&lt;br /&gt;
 &lt;br /&gt;
 CurSample=0x80; // unsigned 8-bit&lt;br /&gt;
 i=0;&lt;br /&gt;
 &lt;br /&gt;
 // note that wOutSize value is crucial for decompression!&lt;br /&gt;
 &lt;br /&gt;
 while (wOutSize&amp;gt;0) // until wOutSize is exhausted!&lt;br /&gt;
 {&lt;br /&gt;
  input=InputBuffer[i++];&lt;br /&gt;
  input&amp;lt;&amp;lt;=2;&lt;br /&gt;
  code=HIBYTE(input);&lt;br /&gt;
  count=LOBYTE(input)&amp;gt;&amp;gt;2;&lt;br /&gt;
  switch (code) // parse code&lt;br /&gt;
  {&lt;br /&gt;
    case 2: // no compression...&lt;br /&gt;
	 if (count &amp;amp; 0x20)&lt;br /&gt;
	 {&lt;br /&gt;
	   count&amp;lt;&amp;lt;=3;		// here it's significant that (count) is signed:&lt;br /&gt;
	   CurSample+=count&amp;gt;&amp;gt;3; // the sign bit will be copied by these shifts!&lt;br /&gt;
&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize--; // one byte added to output&lt;br /&gt;
	 }&lt;br /&gt;
	 else // copy (count+1) bytes from input to output&lt;br /&gt;
	 {&lt;br /&gt;
	   for (count++;count&amp;gt;0;count--,wOutSize--,i++)&lt;br /&gt;
	       Output(InputBuffer[i]);&lt;br /&gt;
	   CurSample=InputBuffer[i-1]; // set (CurSample) to the last byte sent to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    case 1: // ADPCM 8-bit -&amp;gt; 4-bit&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--) // decode (count+1) bytes&lt;br /&gt;
	 {&lt;br /&gt;
	   code=InputBuffer[i++];&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable4bit[(code &amp;amp; 0x0F)]; // lower nibble&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable4bit[(code &amp;gt;&amp;gt; 4)]; // higher nibble&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize-=2; // two bytes added to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    case 0: // ADPCM 8-bit -&amp;gt; 2-bit&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--) // decode (count+1) bytes&lt;br /&gt;
	 {&lt;br /&gt;
	   code=InputBuffer[i++];&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[(code &amp;amp; 0x03)]; // lower 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;2) &amp;amp; 0x03)]; // lower middle 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;4) &amp;amp; 0x03)]; // higher middle 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;6) &amp;amp; 0x03)]; // higher 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize-=4; // 4 bytes sent to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    default: // just copy (CurSample) (count+1) times to output&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--,wOutSize--)&lt;br /&gt;
	     Output((BYTE)CurSample);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
HIBYTE and LOBYTE are just higher and lower bytes of WORD:&lt;br /&gt;
 #define HIBYTE(word) ((word) &amp;gt;&amp;gt; 8)&lt;br /&gt;
 #define LOBYTE(word) ((word) &amp;amp; 0xFF)&lt;br /&gt;
Note that depending on your compiler you may need to use additional byte&lt;br /&gt;
separation in these defines, e.g. (((byte) &amp;gt;&amp;gt; 8) &amp;amp; 0xFF). The same holds for&lt;br /&gt;
4-bit and 2-bit nibble separation in the code above.&lt;br /&gt;
&lt;br /&gt;
''WSTable4bit'' and ''WSTable2bit'' are the delta tables given in the next section.&lt;br /&gt;
&lt;br /&gt;
Output() is just a placeholder for any action you would like to perform for&lt;br /&gt;
decompressed sample value.&lt;br /&gt;
&lt;br /&gt;
Clip8BitSample is quite evident:&lt;br /&gt;
&lt;br /&gt;
 SHORT Clip8BitSample(SHORT sample)&lt;br /&gt;
 {&lt;br /&gt;
  if (sample&amp;gt;255)&lt;br /&gt;
     return 255;&lt;br /&gt;
  else if (sample&amp;lt;0)&lt;br /&gt;
     return 0;&lt;br /&gt;
  else&lt;br /&gt;
     return sample;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This algorithm is ONLY for mono 8-bit unsigned sound, as I've never seen any&lt;br /&gt;
other sound format used with WS ADPCM compression.&lt;br /&gt;
&lt;br /&gt;
Of course, the decompression routine described above may be greatly&lt;br /&gt;
optimized.&lt;br /&gt;
&lt;br /&gt;
==== WS ADPCM Tables ====&lt;br /&gt;
&lt;br /&gt;
 CHAR WSTable2bit[]=&lt;br /&gt;
 {&lt;br /&gt;
    -2,&lt;br /&gt;
    -1,&lt;br /&gt;
     0,&lt;br /&gt;
     1&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 CHAR WSTable4bit[]=&lt;br /&gt;
 {&lt;br /&gt;
    -9, -8, -6, -5, -4, -3, -2, -1,&lt;br /&gt;
     0,  1,  2,  3,  4,  5,  6,  8&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
==HC_VQA.txt==&lt;br /&gt;
by Gordan Ugarkovic (ugordan@yahoo.com)&lt;br /&gt;
http://members.xoom.com/ugordan&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This document describes how to view Westwood's HiColor VQA movies. These&lt;br /&gt;
are version 3 movies, but there are at least some version 2 VQAs that are&lt;br /&gt;
in this format. I will not describe the whole VQA layout here, I will&lt;br /&gt;
just explain how to display the VIDEO stream of the VQA, that is, how&lt;br /&gt;
to decompress the CBFZ and VPTR/VPRZ chunks.&lt;br /&gt;
&lt;br /&gt;
First a little warning: I'm not sure which flag denotes the VQA is 8 bit&lt;br /&gt;
or 15 bit. I'm pretty convinced it's either bit 4 (0x10) or bit 2 (0x04)&lt;br /&gt;
of the Flags entry (see my VQA_INFO.TXT) in the header. Another way would &lt;br /&gt;
be to check the Colors entry in the header, if it is 0 it could imply &lt;br /&gt;
a HiColor movie.&lt;br /&gt;
&lt;br /&gt;
There's a major difference between the old (8 bit, 256 color) and the new,&lt;br /&gt;
HiColor VQAs. Lookup tables are no longer split up into several CBP?&lt;br /&gt;
chunks, instead they come in one piece (a CBFZ chunk). Two lookup tables&lt;br /&gt;
can now be many frames apart, not just 8 (as usual). This is indicated&lt;br /&gt;
by the CBParts entry of the header (see VQA_INFO.TXT), which is set to 0.&lt;br /&gt;
Subsequent frames use the last lookup table loaded, of course.&lt;br /&gt;
&lt;br /&gt;
Another thing: It appears the first CBFZ chunk comes inside the VQFR chunk&lt;br /&gt;
but the other ones seem to be located inside their own chunks,&lt;br /&gt;
called VQFL, which are followed by the usual VQFR chunks (containing&lt;br /&gt;
VPTR/VPRZ chunks).&lt;br /&gt;
&lt;br /&gt;
Also, the movies are 15 bit, NOT 16 bit. There is a difference because&lt;br /&gt;
in 16 bit color depth there are 6 bits for the green channel, but&lt;br /&gt;
the VQAs use 5.&lt;br /&gt;
&lt;br /&gt;
=== The CBFZ chunks===&lt;br /&gt;
&lt;br /&gt;
These are a bit modified since the 8 bit VQAs. If the first byte of the&lt;br /&gt;
chunk is not NULL (0x00), it means the chunk is compressed using the&lt;br /&gt;
standard Format80 algorithm (see Vladan Bato's text on C&amp;amp;C file formats),&lt;br /&gt;
starting from that byte. If the first byte is NULL, the chunk is compressed&lt;br /&gt;
using a modified version of Format80 (see below), starting from the next&lt;br /&gt;
byte of the chunk. The original Format80 algorithm is used when the&lt;br /&gt;
amount of data to be compressed is less than 64 KB, otherwise the 'new'&lt;br /&gt;
algorithm is used.&lt;br /&gt;
&lt;br /&gt;
When decompressed properly, a CBFZ chunk expands into 15 bit pixels packed&lt;br /&gt;
as shorts in normal Intel byte order. The red, green and blue values are&lt;br /&gt;
packed like this:&lt;br /&gt;
&lt;br /&gt;
  15      bit      0&lt;br /&gt;
   arrrrrgg gggbbbbb&lt;br /&gt;
   HI byte  LO byte&lt;br /&gt;
&lt;br /&gt;
The r,g,b values make up a pixel and they can range from 0-31.&lt;br /&gt;
As in the old CBFZ chunks, these pixels make up the block lookup table&lt;br /&gt;
(also called a codebook). The a (alpha) value is used in Blade Runner in&lt;br /&gt;
overlay videos to indicate a transparent pixel.&lt;br /&gt;
&lt;br /&gt;
=== The VPTR chunks===&lt;br /&gt;
&lt;br /&gt;
These chunks use some sort of differential, run-length algorithm that&lt;br /&gt;
only records changes from the previous frame. Therefore, the previous&lt;br /&gt;
frame bitmap must be maintained throughout all the frames (you could&lt;br /&gt;
just draw the blocks that changed, though). This makes dropping frames&lt;br /&gt;
(in case of bad performance) impossible.&lt;br /&gt;
&lt;br /&gt;
When decoding, you take a short int (Intel) from the chunk and examine &lt;br /&gt;
its 3 most significant bits (bits 15,14,13). These bits make up a &lt;br /&gt;
code prefix that determines which action is to be done.&lt;br /&gt;
&lt;br /&gt;
Here's a list of the prefixes I encountered and their description&lt;br /&gt;
(Val is the short int value):&lt;br /&gt;
&lt;br /&gt;
  BITS - MEANING&lt;br /&gt;
&lt;br /&gt;
   000 - Skip Count blocks. Count is (Val &amp;amp; 0x1fff).&lt;br /&gt;
&lt;br /&gt;
   001 - Write block number (Val &amp;amp; 0xff) Count times.&lt;br /&gt;
         Count is (((Val/256) &amp;amp; 0x1f)+1)*2. Note that this can only&lt;br /&gt;
         index the first 256 blocks.&lt;br /&gt;
&lt;br /&gt;
   010 - Write block number (Val &amp;amp; 0xff) and then write Count blocks&lt;br /&gt;
         getting their indexes by reading next Count bytes from&lt;br /&gt;
         the VPTR chunk. Count is (((Val/256) &amp;amp; 0x1f)+1)*2.&lt;br /&gt;
         Again, the block numbers range from 0-255.&lt;br /&gt;
&lt;br /&gt;
   011 - Write block (Val &amp;amp; 0x1fff).&lt;br /&gt;
&lt;br /&gt;
   100 - Same as 011 but skip pixels with alpha bit set.&lt;br /&gt;
&lt;br /&gt;
   101 - Write block (Val &amp;amp; 0x1fff) Count times. Count is the next&lt;br /&gt;
         byte from the VPTR chunk.&lt;br /&gt;
&lt;br /&gt;
   110 - Same as 101 but skip pixels with alpha bit set.&lt;br /&gt;
&lt;br /&gt;
After this, you take the next short int and repeat the above process.&lt;br /&gt;
&lt;br /&gt;
Prefixes 100 and 110 are used in Blade Runner for overlay videos.&lt;br /&gt;
&lt;br /&gt;
Every row of blocks is processed individually of others.&lt;br /&gt;
When you encounter the end of a row, proceed to the next row&lt;br /&gt;
(blocks are processed left to right, top to down).&lt;br /&gt;
Repeat this process until all blocks in the frame are covered and&lt;br /&gt;
that's it!&lt;br /&gt;
&lt;br /&gt;
Note that the above implies an absolute maximum of 8192 blocks (0x1fff+1). &lt;br /&gt;
&lt;br /&gt;
As for the VPRZ chunks, these are just VPTR chunks compressed with&lt;br /&gt;
the standard Format80 algorithm.&lt;br /&gt;
&lt;br /&gt;
=== The modified Format80 scheme ===&lt;br /&gt;
&lt;br /&gt;
This is really only a small modification of the basic algorithm.&lt;br /&gt;
The only commands that are modified are commands (3) and (5)&lt;br /&gt;
See Vladan's text). Instead of using offsets ABSOLUTE from&lt;br /&gt;
the start of the destination buffer, offsets RELATIVE to the&lt;br /&gt;
current destination pointer are used. If you ask me, I don't see&lt;br /&gt;
why this approach wasn't used in the first place as it would&lt;br /&gt;
suffer no disadvantage with the old files and it would be much&lt;br /&gt;
easier to compress even larger amounts of data. The guys at WW&lt;br /&gt;
were just careless, I guess... :-)&lt;br /&gt;
&lt;br /&gt;
Anyway, in Vladan's algorithm, there is a line in&lt;br /&gt;
command (3) that says:&lt;br /&gt;
    Posit:=Word(Source[SP]);&lt;br /&gt;
it should say:&lt;br /&gt;
    Posit:=DP-Word(Source[SP]);&lt;br /&gt;
&lt;br /&gt;
Likewise, for command (5):&lt;br /&gt;
    Posit:=Word(Source[SP+2]);&lt;br /&gt;
it should be:&lt;br /&gt;
    Posit:=DP-Word(Source[SP+2]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Games Using VQA ==&lt;br /&gt;
&lt;br /&gt;
=== Version 1 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/win3x/legend-of-kyrandia-malcolms-revenge The Legend of Kyrandia: Malcolm's Revenge]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/monopoly Monopoly] (Version 1???)&lt;br /&gt;
&lt;br /&gt;
=== Versions 2 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/dos/command-conquer Command &amp;amp; Conquer]&lt;br /&gt;
* [http://www.mobygames.com/game/dos/command-conquer-red-alert Command &amp;amp; Conquer: Red Alert]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/command-conquer-sole-survivor Command &amp;amp; Conquer: Sole Survivor]&lt;br /&gt;
* [http://www.mobygames.com/game/lands-of-lore-guardians-of-destiny Lands of Lore: Guardians of Destiny]&lt;br /&gt;
&lt;br /&gt;
=== Versions 3 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/windows/blade-runner Blade Runner]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/command-conquer-tiberian-sun Command &amp;amp; Conquer: Tiberian Sun]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/dune-2000 Dune 2000]&lt;br /&gt;
* [http://www.mobygames.com/game/lands-of-lore-iii Lands of Lore III]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/nox Nox]&lt;br /&gt;
&lt;br /&gt;
==Other Documentation==&lt;br /&gt;
http://www.gamers.org/pub/idgames2/planetquake/planetcnc/cncdz/ has lots of data about this format. See vqa_overview.zip and vqafilesguild.zip. Also there is a decoder (vqatoavi) that decodes the dune2000 sample. &lt;br /&gt;
&lt;br /&gt;
[[Category:Game Formats]]&lt;br /&gt;
[[Category:Video Codecs]]&lt;br /&gt;
[[Category:Incomplete Video Codecs]]&lt;/div&gt;</summary>
		<author><name>Andrew Dream</name></author>
	</entry>
	<entry>
		<id>https://wiki.multimedia.cx/index.php?title=VQA&amp;diff=16096</id>
		<title>VQA</title>
		<link rel="alternate" type="text/html" href="https://wiki.multimedia.cx/index.php?title=VQA&amp;diff=16096"/>
		<updated>2025-11-25T12:19:31Z</updated>

		<summary type="html">&lt;p&gt;Andrew Dream: /* VQHD chunk (&amp;quot;VQa HeaDer&amp;quot; ???) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Extension: vqa&lt;br /&gt;
* Company: [[Westwood Studios]]&lt;br /&gt;
* Samples: [http://samples.mplayerhq.hu/game-formats/vqa/ http://samples.mplayerhq.hu/game-formats/vqa/]&lt;br /&gt;
* Samples: {{DexvertSamples|video/westwoodStudiosVQA}}&lt;br /&gt;
&lt;br /&gt;
VQA stands for Vector Quantized Animation and is a FMV format used in a number of computer games by Westwood Studios.&lt;br /&gt;
&lt;br /&gt;
== Technical Description ==&lt;br /&gt;
&lt;br /&gt;
'''TODO: import and combine Gordan Ugarkovic's documents from [http://multimedia.cx/VQA_INFO.TXT http://multimedia.cx/VQA_INFO.TXT] and [http://multimedia.cx/HC-VQA.TXT http://multimedia.cx/HC-VQA.TXT].&lt;br /&gt;
==VQA_INFO.txt==&lt;br /&gt;
&lt;br /&gt;
===NOTE #1=== &lt;br /&gt;
This document only applies to VQA files in the original C&amp;amp;C and C&amp;amp;C: Red Alert (version 2) as well as Legend of Kyrandia III : Malcolm's Revenge (version 1).   It DOES NOT apply to the HiColor VQAs found in Westwood's newer games. They use a somewhat different approach to compressing video data (I will provide some facts about their sound stream, though). Look at my other document (HC-VQA.TXT) for a description of the HiColor VQA movies.&lt;br /&gt;
&lt;br /&gt;
===NOTE #2=== &lt;br /&gt;
Throughout the document, I will assume that:&lt;br /&gt;
* CHAR is 1 byte in size, unsigned,&lt;br /&gt;
* SHORT is 2 bytes in size, unsigned,&lt;br /&gt;
* LONG is 4 bytes in size.&lt;br /&gt;
&lt;br /&gt;
Each VQA file is comprised of a series of chunks. A chunk can contain&lt;br /&gt;
other sub-chunks nested in it. Every chunk has a 4 letter ID (all uppercase&lt;br /&gt;
letters) and a LONG written using Motorola byte ordering system (first&lt;br /&gt;
comes the Most Significant Byte), unlike the usual Intel system (Least&lt;br /&gt;
Significant Byte first).&lt;br /&gt;
&lt;br /&gt;
For example, if you had a value 0x12345678 in hexadecimal, using the Intel&lt;br /&gt;
notation it would be written as 78 56 34 12, while using Motorola's &lt;br /&gt;
12 34 56 78.&lt;br /&gt;
&lt;br /&gt;
NOTE: Some chunk IDs start with a NULL byte (0x00) because of reasons&lt;br /&gt;
that will become apparent later. You should just skip this byte&lt;br /&gt;
and assume the next 4 letters hold the chunk ID.&lt;br /&gt;
&lt;br /&gt;
Following the chunk header is the chunk data.&lt;br /&gt;
&lt;br /&gt;
===Typical VQA File===&lt;br /&gt;
Here is a scheme of a typical VQA file (nested chunks are indented):&lt;br /&gt;
&lt;br /&gt;
 FORM&lt;br /&gt;
   VQHD&lt;br /&gt;
   FINF       &amp;lt;-  Frame data positions&lt;br /&gt;
   SND?    \  &amp;lt;-  First sound chunk, contains 1/2 second of sound&lt;br /&gt;
   SND?     |     &amp;lt;- Contains 1 frame's worth of sound&lt;br /&gt;
   VQFR     |     &amp;lt;- Contains various video data chunks&lt;br /&gt;
     CBF?   | 1st frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     CPL?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
   SND?    \&lt;br /&gt;
   VQFR     | 2nd frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
   SND?    \&lt;br /&gt;
   VQFR     | 3rd frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
 . . .&lt;br /&gt;
&lt;br /&gt;
NOTE: There can also be some other chunks (i.e. PINF, PINH, SN2J) included,&lt;br /&gt;
but they are not relevant (?!) for viewing the movie, so they can&lt;br /&gt;
easily be skipped.&lt;br /&gt;
&lt;br /&gt;
===FORM chunk===&lt;br /&gt;
&lt;br /&gt;
This chunk is the main chunk, containing all other chunks.&lt;br /&gt;
In case of version 2 and 3 movies, its size is actually the size of the&lt;br /&gt;
entire file minus the size of the chunk header (8 bytes). Version 1 movies&lt;br /&gt;
seem to have this set to the length of the header VQHD + FINF chunk.&lt;br /&gt;
&lt;br /&gt;
Immediately after the chunk's header, a 4-character signature,&lt;br /&gt;
&amp;quot;WVQA&amp;quot; is located. Then come all the other chunks.&lt;br /&gt;
&lt;br /&gt;
===VQHD chunk (&amp;quot;VQa HeaDer&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This is the header chunk, containing vital information about the movie.&lt;br /&gt;
Its size is always 42 bytes.&lt;br /&gt;
The information is structured like this:&lt;br /&gt;
&lt;br /&gt;
 struct VQAHeader&lt;br /&gt;
 {&lt;br /&gt;
  short  Version;       /* VQA version number                         */&lt;br /&gt;
  short  Flags;         /* VQA flags                                  */&lt;br /&gt;
  short  NumFrames;     /* Number of frames                           */&lt;br /&gt;
  short  Width;         /* Movie width (pixels)                       */&lt;br /&gt;
  short  Height;        /* Movie height (pixels)                      */&lt;br /&gt;
  char   BlockW;        /* Width of each image block (pixels)         */&lt;br /&gt;
  char   BlockH;        /* Height of each image block (pixels)        */&lt;br /&gt;
  char   FrameRate;     /* Frame rate of the VQA                      */&lt;br /&gt;
  char   CBParts;       /* How many images use the same lookup table  */&lt;br /&gt;
  short  Colors;        /* Maximum number of colors used in VQA       */&lt;br /&gt;
  short  MaxBlocks;     /* Maximum number of image blocks             */&lt;br /&gt;
  short  OffsetX;       /* Offset X position in background movie      */&lt;br /&gt;
  short  OffsetY;       /* Offset Y position in background movie      */&lt;br /&gt;
  short  MaxVPT?Size;   /* Max. VPT? (VPTZ, VPRZ) size                */&lt;br /&gt;
  short  Freq;          /* Sound sampling frequency                   */&lt;br /&gt;
  char   Channels;      /* Number of sound channels                   */&lt;br /&gt;
  char   Bits;          /* Sound resolution                           */&lt;br /&gt;
  long   Unknown1;      /* Always 0 ???                               */&lt;br /&gt;
  short  Unknown2;      /* 0 in old VQAs, 4 in HiColor ones ???       */&lt;br /&gt;
  long   MaxCBFZSize;   /* Max. CBFZ size (in old VQAs usually is 0)  */&lt;br /&gt;
  long   Unknown3;      /* Always 0 ???                               */&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Version denotes the VQA version. Valid values are:&lt;br /&gt;
* 1 - First VQAs, used only in Legend of Kyrandia III.&lt;br /&gt;
* 2 - Used in C&amp;amp;C, Red Alert, Lands of Lore II, Dune 2000.&lt;br /&gt;
* 3 - Lands of Lore III (?), Blade Runner (?), Nox and Tiberian Sun. These VQAs are HiColor (15 bit).&lt;br /&gt;
&lt;br /&gt;
Flags most probably contain some flags. I know that bit 0 (LSB) and 2 (Overlay)&lt;br /&gt;
* bit 0 denotes whether the VQA has a soundtrack or not.&lt;br /&gt;
* (1 = Has sound, 0 = No sound)&lt;br /&gt;
* bit 2 denotes whether this movie is an overlay on the background video.&lt;br /&gt;
* (1 = Yes, 0 = No)&lt;br /&gt;
Movie with bit 2 may play separately, but need apply VPTR\VPRZ chunks&lt;br /&gt;
to &amp;quot;Null&amp;quot; Vector Pointer Table (filled by zeros) for each frame.&lt;br /&gt;
&lt;br /&gt;
If NumFrames of background movie less than NumFrames of overlay movie&lt;br /&gt;
then background movie will be repeat, while will not finish overlay movie.&lt;br /&gt;
&lt;br /&gt;
Width is usually 320, Height is usually 156 or 200 although one movie in&lt;br /&gt;
* Red Alert is 640x400 in size (the start movie for the Win95 version).&lt;br /&gt;
&lt;br /&gt;
Each frame of a VQA movie is comprised of a series of blocks that are&lt;br /&gt;
BlockW pixels in width and BlockH pixels in height. Imagine the frame&lt;br /&gt;
is a mosaic with the blocks being 'pieces' that make up the frame.&lt;br /&gt;
&lt;br /&gt;
BlockW is the width and BlockH is the height of each screen block.&lt;br /&gt;
In VQAs version 2 (and perhaps 1) the blocks are usually 4x2.&lt;br /&gt;
&lt;br /&gt;
FrameRate is always 15 in C&amp;amp;C and RA and seems to be 10 in LoK III.&lt;br /&gt;
&lt;br /&gt;
CBParts denotes how many frames use the same lookup table. It also&lt;br /&gt;
implies how many parts the new block lookup table is split into.&lt;br /&gt;
In C&amp;amp;C and RA it is always 8. When CBParts is 0, and movie have&lt;br /&gt;
CBP? chunks, then need see FINF or CIND chunk.&lt;br /&gt;
&lt;br /&gt;
Colors indicates the maximum number of colors used by the VQA.&lt;br /&gt;
The HiColor VQAs have this set to 0, while the old movies have&lt;br /&gt;
256 or less in here.&lt;br /&gt;
&lt;br /&gt;
Offset X and Offset Y uses only when there is bit 2 in the flags.&lt;br /&gt;
Usually uses chunk CLIP too, where the width and height of the&lt;br /&gt;
background movie are specified.&lt;br /&gt;
&lt;br /&gt;
MaxVPT?Size tells you the size of the largest VPTZ or VPRZ chunk, in bytes.&lt;br /&gt;
&lt;br /&gt;
Freq is usually 22050 Hz. Note that version 1 movies can have this set &lt;br /&gt;
to 0 Hz. In that case, you should use 22050 Hz.&lt;br /&gt;
&lt;br /&gt;
Channels specifies the number of sound channels, i.e. is the sound&lt;br /&gt;
mono or stereo. Channels=1 -&amp;gt; sound is mono, Channels=2 -&amp;gt; stereo.&lt;br /&gt;
C&amp;amp;C and RA almost always use mono sound. &lt;br /&gt;
Version 1 can have this set to 0, but you should use 1 (mono sound).&lt;br /&gt;
The majority of Tiberian Sun movies use stereo sound instead.&lt;br /&gt;
&lt;br /&gt;
* Bits indicates whether the sound is 8 bit or 16 bit.&lt;br /&gt;
Bits=8 -&amp;gt; 8 bit sound, Bits=16 -&amp;gt; 16 bit sound (surprise! :). &lt;br /&gt;
The Legend of Kyrandia III: Malcolm's Revenge uses 8 bits where&lt;br /&gt;
C&amp;amp;C, RA, TS, Dune 2000, Lands of Lore III use 16 bits.&lt;br /&gt;
Note, again, that version 1 of the VQAs can have this set to 0 in&lt;br /&gt;
which case 8 bits are assumed.&lt;br /&gt;
&lt;br /&gt;
MaxCBFZSize tells you the size of the largest CBFZ chunk, in bytes.&lt;br /&gt;
In old VQAs this value may be 0.&lt;br /&gt;
&lt;br /&gt;
Following the chunk data are the sub-chunks.&lt;br /&gt;
&lt;br /&gt;
===FINF chunk (&amp;quot;Frame INFormation&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This chunk contains the positions (absolute from the start of the VQA)&lt;br /&gt;
of data for every frame.&lt;br /&gt;
That means that it points to the SND? chunk associated with that frame,&lt;br /&gt;
which is followed by a VQFR chunk containing frame's video data.&lt;br /&gt;
&lt;br /&gt;
The positions are given as LONGs which are in normal Intel byte order.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' Some frame positions are 0x40000000 too large. This is a flag&lt;br /&gt;
indicating those frames contain a new color palette. To get the&lt;br /&gt;
actual positions, you should subtract 0x40000000 from the values.&lt;br /&gt;
&lt;br /&gt;
'''NOTE #2:''' To get the actual position of the frame data you have to multiply&lt;br /&gt;
the value by 2. This is why some chunk IDs start with 0x00. Since&lt;br /&gt;
you multiply by 2, you can't get an odd chunk position so if the&lt;br /&gt;
chunk position would normally be odd, a 0x00 is inserted to make&lt;br /&gt;
it even.&lt;br /&gt;
&lt;br /&gt;
===SND? chunk (&amp;quot;SouND&amp;quot; ???)===&lt;br /&gt;
These chunks contain the sound data for the movie. The last byte of the ID&lt;br /&gt;
can be either '0', '1' or '2' so the actual IDs would be &amp;quot;SND0&amp;quot;, &amp;quot;SND1&amp;quot;&lt;br /&gt;
and &amp;quot;SND2&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
In VQAs version 1 (Legend of Kyrandia 3) there are ''NumFrames'' sound chunks.&lt;br /&gt;
Old 8 bit VQA movies of version 2 have ''NumFrames+1'' sound chunks.&lt;br /&gt;
Note, however, that Dune2000 movies, which are also version 2, but HiColor&lt;br /&gt;
have ''NumFrames'' sound chunks, instead.&lt;br /&gt;
Version 3 movies also have ''NumFrames'' sound chunks.&lt;br /&gt;
&lt;br /&gt;
In case of the old, 8 bit VQAs, the first chunk contains half a second&lt;br /&gt;
(ver. 2) or more (ver. 1) of the wave data, in all (?) the HiColor movies&lt;br /&gt;
the first chunk contains exactly the amount of sound required for one&lt;br /&gt;
frame of the movie. The downside is this requires a somewhat more advanced &lt;br /&gt;
buffering technique on the side of the player in order to allow smooth &lt;br /&gt;
playback.&lt;br /&gt;
&lt;br /&gt;
===SND0 chunk===&lt;br /&gt;
&lt;br /&gt;
This one contains the raw 8 or 16 bit PCM wave data. If the data is&lt;br /&gt;
8 bit, the sound is unsigned and if it is 16 bit, the samples are&lt;br /&gt;
signed.&lt;br /&gt;
&lt;br /&gt;
===SND1 chunk===&lt;br /&gt;
&lt;br /&gt;
It contains 8 bit sound compressed using Westwood's own&lt;br /&gt;
proprietary ADPCM algorithm. The chunk has a 4 byte header:&lt;br /&gt;
&lt;br /&gt;
  struct SND1Header&lt;br /&gt;
  {&lt;br /&gt;
   short OutSize;&lt;br /&gt;
   short Size;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
These values are needed for the decoding algoritm (see APPENDIX C).&lt;br /&gt;
The encoded samples follow immediately after the header.&lt;br /&gt;
It's important to know this algorithm produces UNSIGNED sound, unlike&lt;br /&gt;
the IMA ADPCM algorithm supplied here (see below). It is, however very&lt;br /&gt;
simple to adapt both algorithms to produce either signed or unsigned&lt;br /&gt;
sample output...&lt;br /&gt;
&lt;br /&gt;
===SND2 chunk===&lt;br /&gt;
&lt;br /&gt;
It contains the 16 bit sound data compressed using the IMA ADPCM&lt;br /&gt;
algorithm which compresses 16 bits into 4. That's why the SND2 chunks&lt;br /&gt;
are 4 times smaller than SND0 chunks and they are used almost all&lt;br /&gt;
the time. For the description of the algorithm, see later in the document.&lt;br /&gt;
  &lt;br /&gt;
Different VQA versions have different stereo sample layout. In case&lt;br /&gt;
of Tiberian Sun stereo sound is encoded the same way as mono except the&lt;br /&gt;
SND2 chunk is split into two halfs. The first half contains the left channel&lt;br /&gt;
sound and the second half contains the right channel. The layout of&lt;br /&gt;
nibbles is as follows: LL LL LL LL LL ... RR RR RR RR RR.&lt;br /&gt;
Old movies (C&amp;amp;C, RA) use a different layout. Here, the nibbles are packed&lt;br /&gt;
together like this: LL RR LL RR LL RR ... This means that two left channel&lt;br /&gt;
samples are packed into one byte and then two right channel samples are&lt;br /&gt;
packed into another.&lt;br /&gt;
&lt;br /&gt;
Naturally, the size of a stereo SND2 chunk is exactly twice as big as&lt;br /&gt;
a mono SND2.&lt;br /&gt;
&lt;br /&gt;
It is important to note that, when decoding, you have to keep separate &lt;br /&gt;
values of ''Index'' and ''Cur_Sample'' for each channel (see APPENDIX B).&lt;br /&gt;
&lt;br /&gt;
===VQFR chunk (&amp;quot;Vector Quantized FRame&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
A chunk that includes many nested sub-chunks which contain video data.&lt;br /&gt;
It doesn't contain any data itself so the sub-chunks follow immediately&lt;br /&gt;
after the VQFR chunk header.&lt;br /&gt;
All following sub-chunks are nested inside a VQFR chunk. They can all&lt;br /&gt;
contain '0' or 'Z' as the last byte of their ID.&lt;br /&gt;
&lt;br /&gt;
* If the last byte is '0' it means that the chunk data is uncompressed.&lt;br /&gt;
* If the last byte is 'Z' it means that the data is compressed using&lt;br /&gt;
Format80 compression. You can find its description in APPENDIX A.&lt;br /&gt;
&lt;br /&gt;
===CBF? chunk (&amp;quot;CodeBook, Full&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
Lookup table containing the screen block data as an array&lt;br /&gt;
of elements that each are BlockW*BlockH bytes long. It is always located&lt;br /&gt;
in the data for the first frame. In Vector Quantization terminology&lt;br /&gt;
these tables are called CODEBOOKS.&lt;br /&gt;
&lt;br /&gt;
There can be max. 0x0f00 of these elements (blocks) at any one time in &lt;br /&gt;
normal VQAs and 0xff00 in the hi-res VQAs (Red Alert 95 start movie) although&lt;br /&gt;
I seriously doubt that so many blocks (0xff00 = 65280 blocks) would&lt;br /&gt;
ever be used.&lt;br /&gt;
&lt;br /&gt;
The uncompressed version of these chunks (&amp;quot;CBF0&amp;quot;) is used mainly in&lt;br /&gt;
the original Command &amp;amp; Conquer, while the compressed version (&amp;quot;CBFZ&amp;quot;)&lt;br /&gt;
is used in C&amp;amp;C: Red Alert.&lt;br /&gt;
&lt;br /&gt;
===CBP? chunk (&amp;quot;CodeBook Part&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
Like CBF?, but it contains a part of the lookup table, so to get the new&lt;br /&gt;
complete table you need to append CBParts of these in frame order.&lt;br /&gt;
Once you get the complete table and display the current frame, replace&lt;br /&gt;
the old table with the new one.&lt;br /&gt;
As in CBF? chunk, the uncompressed chunks are used in C&amp;amp;C and the compressed&lt;br /&gt;
chunks are used in Red Alert.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' If the chunks are CBFZ, first you need to append CBParts of them and&lt;br /&gt;
then decompress the data, NOT decompress each chunk individually.&lt;br /&gt;
&lt;br /&gt;
===CPL? chunk (&amp;quot;Color PaLette&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
The simplest one of all... Contains a palette for the VQA. It is an&lt;br /&gt;
array of red, green and blue values (in that order, all have a size of&lt;br /&gt;
1 byte). Seems that the values range from 0-255, but you should mask out&lt;br /&gt;
the bits 6 and 7 to get the correct palette (VGA hardware uses only&lt;br /&gt;
bits 0..5 anyway).&lt;br /&gt;
&lt;br /&gt;
===VPT? chunk: (&amp;quot;Vector Pointer Table&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This chunk contains the indexes into the block lookup table which contains&lt;br /&gt;
the data to display the frame.&lt;br /&gt;
The image blocks are called VECTORS in Vector Quantization terminology.&lt;br /&gt;
&lt;br /&gt;
These chunks are always compressed, but I guess the uncompressed ones&lt;br /&gt;
can also be used (although this would lower the overall compression achieved).&lt;br /&gt;
&lt;br /&gt;
The size of this index table is ''(Width/BlockW)*(Height/BlockH)*2 bytes''.&lt;br /&gt;
&lt;br /&gt;
Now, there is a catch: version 2 VQAs use a different layout of the&lt;br /&gt;
index table than version 1 VQAs. I will first describe the version&lt;br /&gt;
2 table format, as it's more common.&lt;br /&gt;
&lt;br /&gt;
====VERSION 2 INDEX TABLE LAYOUT====&lt;br /&gt;
The index table is an array of CHARs and is split into 2 parts - the top&lt;br /&gt;
half and the bottom half.&lt;br /&gt;
&lt;br /&gt;
Now, if you want to diplay the block at coordinates (in block units),&lt;br /&gt;
say (bx,by) you should read two bytes from the table, one from the top&lt;br /&gt;
and one from the bottom half:&lt;br /&gt;
&lt;br /&gt;
  LoVal=Table[by*(Width/BlockW)+bx]&lt;br /&gt;
  HiVal=Table[(Width/BlockW)*(Height/BlockH)+by*(Width/BlockW)+bx]&lt;br /&gt;
&lt;br /&gt;
If HiVal=0x0f (0xff for the start movie of Red Alert 95) you should&lt;br /&gt;
simply fill the block with color LoVal, otherwise you should copy&lt;br /&gt;
the block with index number HiVal*256+LoVal from the lookup table.&lt;br /&gt;
&lt;br /&gt;
Do that for every block on the screen (remember, there are Width/BlockW&lt;br /&gt;
blocks in the horizontal direction and Height/BlockH blocks in the vertical&lt;br /&gt;
direction) and you've decoded your first frame!&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' I was unable to find an entry in the VQA header which determines&lt;br /&gt;
whether 0x0f or 0xff is used in HiVal to signal that the block&lt;br /&gt;
is of uniform color. I assume that the Wy entry of the header&lt;br /&gt;
implies this: If BlockW=2 -&amp;gt; 0x0f is used, if BlockH=4 -&amp;gt; 0xff is used.&lt;br /&gt;
&lt;br /&gt;
====VERSION 1 INDEX TABLE LAYOUT====&lt;br /&gt;
Here, the index table is simply an array of SHORTs written in normal, Intel&lt;br /&gt;
byte order.&lt;br /&gt;
&lt;br /&gt;
The LoVal and HiVal are given as:&lt;br /&gt;
&lt;br /&gt;
  LoVal=Table[(by*(Width/BlockW)+bx)*2]&lt;br /&gt;
  HiVal=Table[(by*(Width/BlockW)+bx)*2+1]&lt;br /&gt;
&lt;br /&gt;
If HiVal=0xff, the block is of uniform color which is (255-LoVal).&lt;br /&gt;
Otherwise, write the block with index number (HiVal*256+LoVal)/8.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Appendix A===&lt;br /&gt;
&lt;br /&gt;
FORMAT80 COMPRESSION METHOD&lt;br /&gt;
by Vladan Bato (bat22@geocities.com)&lt;br /&gt;
&lt;br /&gt;
There are several different commands, with different sizes : from 1 to 5 bytes.&lt;br /&gt;
The positions mentioned below always refer to the destination buffer (i.e.&lt;br /&gt;
the uncompressed image). The relative positions are relative to the current&lt;br /&gt;
position in the destination buffer, which is one byte beyond the last written&lt;br /&gt;
byte.&lt;br /&gt;
&lt;br /&gt;
I will give some sample code at the end.&lt;br /&gt;
&lt;br /&gt;
(1) 1 byte&lt;br /&gt;
      +---+---+---+---+---+---+---+---+&lt;br /&gt;
      | 1 | 0 |   |   |   |   |   |   |&lt;br /&gt;
      +---+---+---+---+---+---+---+---+&lt;br /&gt;
              \_______________________/&lt;br /&gt;
                         |&lt;br /&gt;
                       Count&lt;br /&gt;
&lt;br /&gt;
      This one means : copy next Count bytes as is from Source to Dest.&lt;br /&gt;
&lt;br /&gt;
(2) 2 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
  | 0 |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
      \___________/\__________________________________________________/&lt;br /&gt;
            |                             |&lt;br /&gt;
         Count-3                    Relative Pos.&lt;br /&gt;
&lt;br /&gt;
  This means copy Count bytes from Dest at Current Pos.-Rel. Pos. to&lt;br /&gt;
  Current position.&lt;br /&gt;
  Note that you have to add 3 to the number you find in the bits 4-6 of the&lt;br /&gt;
  first byte to obtain the Count.&lt;br /&gt;
  Note that if the Rel. Pos. is 1, that means repeat Count times the previous&lt;br /&gt;
  byte.&lt;br /&gt;
&lt;br /&gt;
(3) 3 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---------------+---------------+&lt;br /&gt;
  | 1 | 1 |   |   |   |   |   |   |   |               |               |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---------------+---------------+&lt;br /&gt;
          \_______________________/                  Pos&lt;br /&gt;
                     |&lt;br /&gt;
                 Count-3&lt;br /&gt;
&lt;br /&gt;
  Copy Count bytes from Pos, where Pos is absolute from the start of the&lt;br /&gt;
  destination buffer. (Pos is a word, that means that the images can't be&lt;br /&gt;
  larger than 64K)&lt;br /&gt;
&lt;br /&gt;
(4) 4 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+&lt;br /&gt;
  | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |   |       |       |  |       |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+&lt;br /&gt;
                                            Count          Color&lt;br /&gt;
&lt;br /&gt;
  Write Color Count times.&lt;br /&gt;
  (Count is a word, color is a byte)&lt;br /&gt;
&lt;br /&gt;
(5) 5 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+&lt;br /&gt;
  | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |   |       |       |  |       |       |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+&lt;br /&gt;
                                            Count               Pos&lt;br /&gt;
&lt;br /&gt;
  Copy Count bytes from Dest. starting at Pos. Pos is absolute from the start&lt;br /&gt;
  of the Destination buffer.&lt;br /&gt;
  Both Count and Pos are words.&lt;br /&gt;
&lt;br /&gt;
These are all the commands I found out. Maybe there are other ones, but I&lt;br /&gt;
haven't seen them yet.&lt;br /&gt;
&lt;br /&gt;
All the images end with a 80h command.&lt;br /&gt;
&lt;br /&gt;
To make things more clearer here's a piece of code that will uncompress the&lt;br /&gt;
image.&lt;br /&gt;
&lt;br /&gt;
  DP = destination pointer&lt;br /&gt;
  SP = source pointer&lt;br /&gt;
  Source and Dest are the two buffers&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  SP:=0;&lt;br /&gt;
  DP:=0;&lt;br /&gt;
  repeat&lt;br /&gt;
    Com:=Source[SP];&lt;br /&gt;
    inc(SP);&lt;br /&gt;
    b7:=Com shr 7;  {b7 is bit 7 of Com}&lt;br /&gt;
    case b7 of&lt;br /&gt;
      0 : begin  {copy command (2)}&lt;br /&gt;
            {Count is bits 4-6 + 3}&lt;br /&gt;
            Count:=(Com and $7F) shr 4 + 3;&lt;br /&gt;
            {Position is bits 0-3, with bits 0-7 of next byte}&lt;br /&gt;
            Posit:=(Com and $0F) shl 8+Source[SP];&lt;br /&gt;
            Inc(SP);&lt;br /&gt;
            {Starting pos=Cur pos. - calculated value}&lt;br /&gt;
            Posit:=DP-Posit;&lt;br /&gt;
            for i:=Posit to Posit+Count-1 do&lt;br /&gt;
            begin&lt;br /&gt;
              Dest[DP]:=Dest[i];&lt;br /&gt;
              Inc(DP);&lt;br /&gt;
            end;&lt;br /&gt;
          end;&lt;br /&gt;
      1 : begin&lt;br /&gt;
            {Check bit 6 of Com}&lt;br /&gt;
            b6:=(Com and $40) shr 6;&lt;br /&gt;
            case b6 of&lt;br /&gt;
              0 : begin  {Copy as is command (1)}&lt;br /&gt;
                    Count:=Com and $3F;  {mask 2 topmost bits}&lt;br /&gt;
                    if Count=0 then break; {EOF marker}&lt;br /&gt;
                    for i:=1 to Count do&lt;br /&gt;
                    begin&lt;br /&gt;
                      Dest[DP]:=Source[SP];&lt;br /&gt;
                      Inc(DP);&lt;br /&gt;
                      Inc(SP);&lt;br /&gt;
                    end;&lt;br /&gt;
                  end;&lt;br /&gt;
              1 : begin  {large copy, very large copy and fill commands}&lt;br /&gt;
                    {Count = (bits 0-5 of Com) +3}&lt;br /&gt;
                    {if Com=FEh then fill, if Com=FFh then very large copy}&lt;br /&gt;
                    Count:=Com and $3F;&lt;br /&gt;
                    if Count&amp;lt;$3E then {large copy (3)}&lt;br /&gt;
                    begin&lt;br /&gt;
                      Inc(Count,3);&lt;br /&gt;
                      {Next word = pos. from start of image}&lt;br /&gt;
                      Posit:=Word(Source[SP]);&lt;br /&gt;
                      Inc(SP,2);&lt;br /&gt;
                      for i:=Posit to Posit+Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=Dest[i];&lt;br /&gt;
                        Inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end&lt;br /&gt;
                    else if Count=$3F then   {very large copy (5)}&lt;br /&gt;
                    begin&lt;br /&gt;
                      {next 2 words are Count and Pos}&lt;br /&gt;
                      Count:=Word(Source[SP]);&lt;br /&gt;
                      Posit:=Word(Source[SP+2]);&lt;br /&gt;
                      Inc(SP,4);&lt;br /&gt;
                      for i:=Posit to Posit+Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=Dest[i];&lt;br /&gt;
                        Inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end else&lt;br /&gt;
                    begin   {Count=$3E, fill (4)}&lt;br /&gt;
                      {Next word is count, the byte after is color}&lt;br /&gt;
                      Count:=Word(Source[SP]);&lt;br /&gt;
                      Inc(SP,2);&lt;br /&gt;
                      b:=Source[SP];&lt;br /&gt;
                      Inc(SP);&lt;br /&gt;
                      for i:=0 to Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=b;&lt;br /&gt;
                        inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end;&lt;br /&gt;
                  end;&lt;br /&gt;
            end;&lt;br /&gt;
          end;&lt;br /&gt;
    end;&lt;br /&gt;
  until false;&lt;br /&gt;
&lt;br /&gt;
Note that you won't be able to compile this code, because the typecasting&lt;br /&gt;
won't work. (But I'm sure you'll be able to fix it).&lt;br /&gt;
&lt;br /&gt;
===Appendix B===&lt;br /&gt;
IMA ADPCM DECOMPRESSION&lt;br /&gt;
by Vladan Bato  (bat22@geocities.com)&lt;br /&gt;
http://www.geocities.com/SiliconValley/8682&lt;br /&gt;
&lt;br /&gt;
Note that the current sample value and index into the Step Table should&lt;br /&gt;
be initialized to 0 at the start and are maintained across the chunks&lt;br /&gt;
(see below).&lt;br /&gt;
&lt;br /&gt;
====IMA-ADPCM DECOMPRESSION ====&lt;br /&gt;
&lt;br /&gt;
It is the exact opposite of the above. It receives 4-bit codes in input&lt;br /&gt;
and produce 16-bit samples in output.&lt;br /&gt;
&lt;br /&gt;
Again you have to mantain an Index into the Step Table an the current&lt;br /&gt;
sample value.&lt;br /&gt;
&lt;br /&gt;
The tables used are the same as for compression.&lt;br /&gt;
&lt;br /&gt;
Here's the code :&lt;br /&gt;
&lt;br /&gt;
  Index:=0;&lt;br /&gt;
  Cur_Sample:=0;&lt;br /&gt;
&lt;br /&gt;
  while there_is_more_data do&lt;br /&gt;
  begin&lt;br /&gt;
    Code:=Get_Next_Code;&lt;br /&gt;
&lt;br /&gt;
    if (Code and $8) &amp;lt;&amp;gt; 0 then Sb:=1 else Sb:=0;&lt;br /&gt;
    Code:=Code and $7;&lt;br /&gt;
    {Separate the sign bit from the rest}&lt;br /&gt;
&lt;br /&gt;
    Delta:=(Step_Table[Index]*Code) div 4 + Step_Table[Index] div 8;&lt;br /&gt;
    {The last one is to minimize errors}&lt;br /&gt;
&lt;br /&gt;
    if Sb=1 then Delta:=-Delta;&lt;br /&gt;
&lt;br /&gt;
    Cur_Sample:=Cur_Sample+Delta;&lt;br /&gt;
    if Cur_Sample&amp;gt;32767 then Cur_Sample:=32767&lt;br /&gt;
    else if Cur_Sample&amp;lt;-32768 then Cur_Sample:=-32768;&lt;br /&gt;
&lt;br /&gt;
    Output_Sample(Cur_Sample);&lt;br /&gt;
&lt;br /&gt;
    Index:=Index+Index_Adjust[Code];&lt;br /&gt;
    if Index&amp;lt;0 then Index:=0;&lt;br /&gt;
    if Index&amp;gt;88 the Index:=88;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
Again, this can be done more efficiently (no need for multiplication).&lt;br /&gt;
&lt;br /&gt;
The ''Get_Next_Code'' function should return the next 4-bit code. It must&lt;br /&gt;
extract it from the input buffer (note that two 4-bit codes are stored&lt;br /&gt;
in the same byte, the first one in the lower bits).&lt;br /&gt;
&lt;br /&gt;
The Output_Sample function should write the signed 16-bit sample to the&lt;br /&gt;
output buffer.&lt;br /&gt;
&lt;br /&gt;
=== Appendix A : THE INDEX ADJUSTMENT TABLE ===&lt;br /&gt;
&lt;br /&gt;
  Index_Adjust : array [0..7] of integer = (-1,-1,-1,-1,2,4,6,8);&lt;br /&gt;
&lt;br /&gt;
=== Appendix B : THE STEP TABLE ===&lt;br /&gt;
&lt;br /&gt;
  Steps_Table : array [0..88] of integer =(&lt;br /&gt;
        7,     8,     9,     10,    11,    12,     13,    14,    16,&lt;br /&gt;
        17,    19,    21,    23,    25,    28,     31,    34,    37,&lt;br /&gt;
        41,    45,    50,    55,    60,    66,     73,    80,    88,&lt;br /&gt;
        97,    107,   118,   130,   143,   157,    173,   190,   209,&lt;br /&gt;
        230,   253,   279,   307,   337,   371,    408,   449,   494,&lt;br /&gt;
        544,   598,   658,   724,   796,   876,    963,   1060,  1166,&lt;br /&gt;
        1282,  1411,  1552,  1707,  1878,  2066,   2272,  2499,  2749,&lt;br /&gt;
        3024,  3327,  3660,  4026,  4428,  4871,   5358,  5894,  6484,&lt;br /&gt;
        7132,  7845,  8630,  9493,  10442, 11487,  12635, 13899, 15289,&lt;br /&gt;
        16818, 18500, 20350, 22385, 24623, 27086,  29794, 32767 );&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Appendix C===&lt;br /&gt;
WESTWOOD STUDIOS' ADPCM DECOMPRESSION&lt;br /&gt;
by Asatur V. Nazarian (samael@avn.mccme.ru)&lt;br /&gt;
&lt;br /&gt;
==== WS ADPCM Decompression Algorithm ====&lt;br /&gt;
&lt;br /&gt;
Each SND1 chunk may be decompressed independently of others. This lets you&lt;br /&gt;
implement seeking/skipping for WS ADPCM sounds (unlike IMA ADPCM ones).&lt;br /&gt;
But during the decompression of the given chunk a variable (''CurSample'') should&lt;br /&gt;
be maintained for this whole chunk:&lt;br /&gt;
&lt;br /&gt;
 SHORT CurSample;&lt;br /&gt;
 BYTE  InputBuffer[InputBufferSize]; // input buffer containing the whole chunk&lt;br /&gt;
 WORD  wSize, wOutSize; // Size and OutSize values from this chunk's header&lt;br /&gt;
 BYTE  code;&lt;br /&gt;
 CHAR  count; // this is a signed char!&lt;br /&gt;
 WORD  i; // index into InputBuffer&lt;br /&gt;
 WORD  input; // shifted input&lt;br /&gt;
 &lt;br /&gt;
 if (wSize==wOutSize) // such chunks are NOT compressed&lt;br /&gt;
 {&lt;br /&gt;
  for (i=0;i&amp;lt;wOutSize;i++)&lt;br /&gt;
      Output(InputBuffer[i]); // send to output stream&lt;br /&gt;
  return; // chunk is done!&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // otherwise we need to decompress chunk&lt;br /&gt;
 &lt;br /&gt;
 CurSample=0x80; // unsigned 8-bit&lt;br /&gt;
 i=0;&lt;br /&gt;
 &lt;br /&gt;
 // note that wOutSize value is crucial for decompression!&lt;br /&gt;
 &lt;br /&gt;
 while (wOutSize&amp;gt;0) // until wOutSize is exhausted!&lt;br /&gt;
 {&lt;br /&gt;
  input=InputBuffer[i++];&lt;br /&gt;
  input&amp;lt;&amp;lt;=2;&lt;br /&gt;
  code=HIBYTE(input);&lt;br /&gt;
  count=LOBYTE(input)&amp;gt;&amp;gt;2;&lt;br /&gt;
  switch (code) // parse code&lt;br /&gt;
  {&lt;br /&gt;
    case 2: // no compression...&lt;br /&gt;
	 if (count &amp;amp; 0x20)&lt;br /&gt;
	 {&lt;br /&gt;
	   count&amp;lt;&amp;lt;=3;		// here it's significant that (count) is signed:&lt;br /&gt;
	   CurSample+=count&amp;gt;&amp;gt;3; // the sign bit will be copied by these shifts!&lt;br /&gt;
&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize--; // one byte added to output&lt;br /&gt;
	 }&lt;br /&gt;
	 else // copy (count+1) bytes from input to output&lt;br /&gt;
	 {&lt;br /&gt;
	   for (count++;count&amp;gt;0;count--,wOutSize--,i++)&lt;br /&gt;
	       Output(InputBuffer[i]);&lt;br /&gt;
	   CurSample=InputBuffer[i-1]; // set (CurSample) to the last byte sent to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    case 1: // ADPCM 8-bit -&amp;gt; 4-bit&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--) // decode (count+1) bytes&lt;br /&gt;
	 {&lt;br /&gt;
	   code=InputBuffer[i++];&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable4bit[(code &amp;amp; 0x0F)]; // lower nibble&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable4bit[(code &amp;gt;&amp;gt; 4)]; // higher nibble&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize-=2; // two bytes added to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    case 0: // ADPCM 8-bit -&amp;gt; 2-bit&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--) // decode (count+1) bytes&lt;br /&gt;
	 {&lt;br /&gt;
	   code=InputBuffer[i++];&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[(code &amp;amp; 0x03)]; // lower 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;2) &amp;amp; 0x03)]; // lower middle 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;4) &amp;amp; 0x03)]; // higher middle 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;6) &amp;amp; 0x03)]; // higher 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize-=4; // 4 bytes sent to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    default: // just copy (CurSample) (count+1) times to output&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--,wOutSize--)&lt;br /&gt;
	     Output((BYTE)CurSample);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
HIBYTE and LOBYTE are just higher and lower bytes of WORD:&lt;br /&gt;
 #define HIBYTE(word) ((word) &amp;gt;&amp;gt; 8)&lt;br /&gt;
 #define LOBYTE(word) ((word) &amp;amp; 0xFF)&lt;br /&gt;
Note that depending on your compiler you may need to use additional byte&lt;br /&gt;
separation in these defines, e.g. (((byte) &amp;gt;&amp;gt; 8) &amp;amp; 0xFF). The same holds for&lt;br /&gt;
4-bit and 2-bit nibble separation in the code above.&lt;br /&gt;
&lt;br /&gt;
''WSTable4bit'' and ''WSTable2bit'' are the delta tables given in the next section.&lt;br /&gt;
&lt;br /&gt;
Output() is just a placeholder for any action you would like to perform for&lt;br /&gt;
decompressed sample value.&lt;br /&gt;
&lt;br /&gt;
Clip8BitSample is quite evident:&lt;br /&gt;
&lt;br /&gt;
 SHORT Clip8BitSample(SHORT sample)&lt;br /&gt;
 {&lt;br /&gt;
  if (sample&amp;gt;255)&lt;br /&gt;
     return 255;&lt;br /&gt;
  else if (sample&amp;lt;0)&lt;br /&gt;
     return 0;&lt;br /&gt;
  else&lt;br /&gt;
     return sample;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This algorithm is ONLY for mono 8-bit unsigned sound, as I've never seen any&lt;br /&gt;
other sound format used with WS ADPCM compression.&lt;br /&gt;
&lt;br /&gt;
Of course, the decompression routine described above may be greatly&lt;br /&gt;
optimized.&lt;br /&gt;
&lt;br /&gt;
==== WS ADPCM Tables ====&lt;br /&gt;
&lt;br /&gt;
 CHAR WSTable2bit[]=&lt;br /&gt;
 {&lt;br /&gt;
    -2,&lt;br /&gt;
    -1,&lt;br /&gt;
     0,&lt;br /&gt;
     1&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 CHAR WSTable4bit[]=&lt;br /&gt;
 {&lt;br /&gt;
    -9, -8, -6, -5, -4, -3, -2, -1,&lt;br /&gt;
     0,  1,  2,  3,  4,  5,  6,  8&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
==HC_VQA.txt==&lt;br /&gt;
by Gordan Ugarkovic (ugordan@yahoo.com)&lt;br /&gt;
http://members.xoom.com/ugordan&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This document describes how to view Westwood's HiColor VQA movies. These&lt;br /&gt;
are version 3 movies, but there are at least some version 2 VQAs that are&lt;br /&gt;
in this format. I will not describe the whole VQA layout here, I will&lt;br /&gt;
just explain how to display the VIDEO stream of the VQA, that is, how&lt;br /&gt;
to decompress the CBFZ and VPTR/VPRZ chunks.&lt;br /&gt;
&lt;br /&gt;
First a little warning: I'm not sure which flag denotes the VQA is 8 bit&lt;br /&gt;
or 15 bit. I'm pretty convinced it's either bit 4 (0x10) or bit 2 (0x04)&lt;br /&gt;
of the Flags entry (see my VQA_INFO.TXT) in the header. Another way would &lt;br /&gt;
be to check the Colors entry in the header, if it is 0 it could imply &lt;br /&gt;
a HiColor movie.&lt;br /&gt;
&lt;br /&gt;
There's a major difference between the old (8 bit, 256 color) and the new,&lt;br /&gt;
HiColor VQAs. Lookup tables are no longer split up into several CBP?&lt;br /&gt;
chunks, instead they come in one piece (a CBFZ chunk). Two lookup tables&lt;br /&gt;
can now be many frames apart, not just 8 (as usual). This is indicated&lt;br /&gt;
by the CBParts entry of the header (see VQA_INFO.TXT), which is set to 0.&lt;br /&gt;
Subsequent frames use the last lookup table loaded, of course.&lt;br /&gt;
&lt;br /&gt;
Another thing: It appears the first CBFZ chunk comes inside the VQFR chunk&lt;br /&gt;
but the other ones seem to be located inside their own chunks,&lt;br /&gt;
called VQFL, which are followed by the usual VQFR chunks (containing&lt;br /&gt;
VPTR/VPRZ chunks).&lt;br /&gt;
&lt;br /&gt;
Also, the movies are 15 bit, NOT 16 bit. There is a difference because&lt;br /&gt;
in 16 bit color depth there are 6 bits for the green channel, but&lt;br /&gt;
the VQAs use 5.&lt;br /&gt;
&lt;br /&gt;
=== The CBFZ chunks===&lt;br /&gt;
&lt;br /&gt;
These are a bit modified since the 8 bit VQAs. If the first byte of the&lt;br /&gt;
chunk is not NULL (0x00), it means the chunk is compressed using the&lt;br /&gt;
standard Format80 algorithm (see Vladan Bato's text on C&amp;amp;C file formats),&lt;br /&gt;
starting from that byte. If the first byte is NULL, the chunk is compressed&lt;br /&gt;
using a modified version of Format80 (see below), starting from the next&lt;br /&gt;
byte of the chunk. The original Format80 algorithm is used when the&lt;br /&gt;
amount of data to be compressed is less than 64 KB, otherwise the 'new'&lt;br /&gt;
algorithm is used.&lt;br /&gt;
&lt;br /&gt;
When decompressed properly, a CBFZ chunk expands into 15 bit pixels packed&lt;br /&gt;
as shorts in normal Intel byte order. The red, green and blue values are&lt;br /&gt;
packed like this:&lt;br /&gt;
&lt;br /&gt;
  15      bit      0&lt;br /&gt;
   arrrrrgg gggbbbbb&lt;br /&gt;
   HI byte  LO byte&lt;br /&gt;
&lt;br /&gt;
The r,g,b values make up a pixel and they can range from 0-31.&lt;br /&gt;
As in the old CBFZ chunks, these pixels make up the block lookup table&lt;br /&gt;
(also called a codebook). The a (alpha) value is used in Blade Runner in&lt;br /&gt;
overlay videos to indicate a transparent pixel.&lt;br /&gt;
&lt;br /&gt;
=== The VPTR chunks===&lt;br /&gt;
&lt;br /&gt;
These chunks use some sort of differential, run-length algorithm that&lt;br /&gt;
only records changes from the previous frame. Therefore, the previous&lt;br /&gt;
frame bitmap must be maintained throughout all the frames (you could&lt;br /&gt;
just draw the blocks that changed, though). This makes dropping frames&lt;br /&gt;
(in case of bad performance) impossible.&lt;br /&gt;
&lt;br /&gt;
When decoding, you take a short int (Intel) from the chunk and examine &lt;br /&gt;
its 3 most significant bits (bits 15,14,13). These bits make up a &lt;br /&gt;
code prefix that determines which action is to be done.&lt;br /&gt;
&lt;br /&gt;
Here's a list of the prefixes I encountered and their description&lt;br /&gt;
(Val is the short int value):&lt;br /&gt;
&lt;br /&gt;
  BITS - MEANING&lt;br /&gt;
&lt;br /&gt;
   000 - Skip Count blocks. Count is (Val &amp;amp; 0x1fff).&lt;br /&gt;
&lt;br /&gt;
   001 - Write block number (Val &amp;amp; 0xff) Count times.&lt;br /&gt;
         Count is (((Val/256) &amp;amp; 0x1f)+1)*2. Note that this can only&lt;br /&gt;
         index the first 256 blocks.&lt;br /&gt;
&lt;br /&gt;
   010 - Write block number (Val &amp;amp; 0xff) and then write Count blocks&lt;br /&gt;
         getting their indexes by reading next Count bytes from&lt;br /&gt;
         the VPTR chunk. Count is (((Val/256) &amp;amp; 0x1f)+1)*2.&lt;br /&gt;
         Again, the block numbers range from 0-255.&lt;br /&gt;
&lt;br /&gt;
   011 - Write block (Val &amp;amp; 0x1fff).&lt;br /&gt;
&lt;br /&gt;
   100 - Same as 011 but skip pixels with alpha bit set.&lt;br /&gt;
&lt;br /&gt;
   101 - Write block (Val &amp;amp; 0x1fff) Count times. Count is the next&lt;br /&gt;
         byte from the VPTR chunk.&lt;br /&gt;
&lt;br /&gt;
   110 - Same as 101 but skip pixels with alpha bit set.&lt;br /&gt;
&lt;br /&gt;
After this, you take the next short int and repeat the above process.&lt;br /&gt;
&lt;br /&gt;
Prefixes 100 and 110 are used in Blade Runner for overlay videos.&lt;br /&gt;
&lt;br /&gt;
Every row of blocks is processed individually of others.&lt;br /&gt;
When you encounter the end of a row, proceed to the next row&lt;br /&gt;
(blocks are processed left to right, top to down).&lt;br /&gt;
Repeat this process until all blocks in the frame are covered and&lt;br /&gt;
that's it!&lt;br /&gt;
&lt;br /&gt;
Note that the above implies an absolute maximum of 8192 blocks (0x1fff+1). &lt;br /&gt;
&lt;br /&gt;
As for the VPRZ chunks, these are just VPTR chunks compressed with&lt;br /&gt;
the standard Format80 algorithm.&lt;br /&gt;
&lt;br /&gt;
=== The modified Format80 scheme ===&lt;br /&gt;
&lt;br /&gt;
This is really only a small modification of the basic algorithm.&lt;br /&gt;
The only commands that are modified are commands (3) and (5)&lt;br /&gt;
See Vladan's text). Instead of using offsets ABSOLUTE from&lt;br /&gt;
the start of the destination buffer, offsets RELATIVE to the&lt;br /&gt;
current destination pointer are used. If you ask me, I don't see&lt;br /&gt;
why this approach wasn't used in the first place as it would&lt;br /&gt;
suffer no disadvantage with the old files and it would be much&lt;br /&gt;
easier to compress even larger amounts of data. The guys at WW&lt;br /&gt;
were just careless, I guess... :-)&lt;br /&gt;
&lt;br /&gt;
Anyway, in Vladan's algorithm, there is a line in&lt;br /&gt;
command (3) that says:&lt;br /&gt;
    Posit:=Word(Source[SP]);&lt;br /&gt;
it should say:&lt;br /&gt;
    Posit:=DP-Word(Source[SP]);&lt;br /&gt;
&lt;br /&gt;
Likewise, for command (5):&lt;br /&gt;
    Posit:=Word(Source[SP+2]);&lt;br /&gt;
it should be:&lt;br /&gt;
    Posit:=DP-Word(Source[SP+2]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Games Using VQA ==&lt;br /&gt;
&lt;br /&gt;
=== Version 1 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/win3x/legend-of-kyrandia-malcolms-revenge The Legend of Kyrandia: Malcolm's Revenge]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/monopoly Monopoly] (Version 1???)&lt;br /&gt;
&lt;br /&gt;
=== Versions 2 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/dos/command-conquer Command &amp;amp; Conquer]&lt;br /&gt;
* [http://www.mobygames.com/game/dos/command-conquer-red-alert Command &amp;amp; Conquer: Red Alert]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/command-conquer-sole-survivor Command &amp;amp; Conquer: Sole Survivor]&lt;br /&gt;
* [http://www.mobygames.com/game/lands-of-lore-guardians-of-destiny Lands of Lore: Guardians of Destiny]&lt;br /&gt;
&lt;br /&gt;
=== Versions 3 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/windows/blade-runner Blade Runner]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/command-conquer-tiberian-sun Command &amp;amp; Conquer: Tiberian Sun]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/dune-2000 Dune 2000]&lt;br /&gt;
* [http://www.mobygames.com/game/lands-of-lore-iii Lands of Lore III]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/nox Nox]&lt;br /&gt;
&lt;br /&gt;
==Other Documentation==&lt;br /&gt;
http://www.gamers.org/pub/idgames2/planetquake/planetcnc/cncdz/ has lots of data about this format. See vqa_overview.zip and vqafilesguild.zip. Also there is a decoder (vqatoavi) that decodes the dune2000 sample. &lt;br /&gt;
&lt;br /&gt;
[[Category:Game Formats]]&lt;br /&gt;
[[Category:Video Codecs]]&lt;br /&gt;
[[Category:Incomplete Video Codecs]]&lt;/div&gt;</summary>
		<author><name>Andrew Dream</name></author>
	</entry>
	<entry>
		<id>https://wiki.multimedia.cx/index.php?title=VQA&amp;diff=16095</id>
		<title>VQA</title>
		<link rel="alternate" type="text/html" href="https://wiki.multimedia.cx/index.php?title=VQA&amp;diff=16095"/>
		<updated>2025-11-25T11:44:27Z</updated>

		<summary type="html">&lt;p&gt;Andrew Dream: /* VQHD chunk (&amp;quot;VQa HeaDer&amp;quot; ???) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Extension: vqa&lt;br /&gt;
* Company: [[Westwood Studios]]&lt;br /&gt;
* Samples: [http://samples.mplayerhq.hu/game-formats/vqa/ http://samples.mplayerhq.hu/game-formats/vqa/]&lt;br /&gt;
* Samples: {{DexvertSamples|video/westwoodStudiosVQA}}&lt;br /&gt;
&lt;br /&gt;
VQA stands for Vector Quantized Animation and is a FMV format used in a number of computer games by Westwood Studios.&lt;br /&gt;
&lt;br /&gt;
== Technical Description ==&lt;br /&gt;
&lt;br /&gt;
'''TODO: import and combine Gordan Ugarkovic's documents from [http://multimedia.cx/VQA_INFO.TXT http://multimedia.cx/VQA_INFO.TXT] and [http://multimedia.cx/HC-VQA.TXT http://multimedia.cx/HC-VQA.TXT].&lt;br /&gt;
==VQA_INFO.txt==&lt;br /&gt;
&lt;br /&gt;
===NOTE #1=== &lt;br /&gt;
This document only applies to VQA files in the original C&amp;amp;C and C&amp;amp;C: Red Alert (version 2) as well as Legend of Kyrandia III : Malcolm's Revenge (version 1).   It DOES NOT apply to the HiColor VQAs found in Westwood's newer games. They use a somewhat different approach to compressing video data (I will provide some facts about their sound stream, though). Look at my other document (HC-VQA.TXT) for a description of the HiColor VQA movies.&lt;br /&gt;
&lt;br /&gt;
===NOTE #2=== &lt;br /&gt;
Throughout the document, I will assume that:&lt;br /&gt;
* CHAR is 1 byte in size, unsigned,&lt;br /&gt;
* SHORT is 2 bytes in size, unsigned,&lt;br /&gt;
* LONG is 4 bytes in size.&lt;br /&gt;
&lt;br /&gt;
Each VQA file is comprised of a series of chunks. A chunk can contain&lt;br /&gt;
other sub-chunks nested in it. Every chunk has a 4 letter ID (all uppercase&lt;br /&gt;
letters) and a LONG written using Motorola byte ordering system (first&lt;br /&gt;
comes the Most Significant Byte), unlike the usual Intel system (Least&lt;br /&gt;
Significant Byte first).&lt;br /&gt;
&lt;br /&gt;
For example, if you had a value 0x12345678 in hexadecimal, using the Intel&lt;br /&gt;
notation it would be written as 78 56 34 12, while using Motorola's &lt;br /&gt;
12 34 56 78.&lt;br /&gt;
&lt;br /&gt;
NOTE: Some chunk IDs start with a NULL byte (0x00) because of reasons&lt;br /&gt;
that will become apparent later. You should just skip this byte&lt;br /&gt;
and assume the next 4 letters hold the chunk ID.&lt;br /&gt;
&lt;br /&gt;
Following the chunk header is the chunk data.&lt;br /&gt;
&lt;br /&gt;
===Typical VQA File===&lt;br /&gt;
Here is a scheme of a typical VQA file (nested chunks are indented):&lt;br /&gt;
&lt;br /&gt;
 FORM&lt;br /&gt;
   VQHD&lt;br /&gt;
   FINF       &amp;lt;-  Frame data positions&lt;br /&gt;
   SND?    \  &amp;lt;-  First sound chunk, contains 1/2 second of sound&lt;br /&gt;
   SND?     |     &amp;lt;- Contains 1 frame's worth of sound&lt;br /&gt;
   VQFR     |     &amp;lt;- Contains various video data chunks&lt;br /&gt;
     CBF?   | 1st frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     CPL?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
   SND?    \&lt;br /&gt;
   VQFR     | 2nd frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
   SND?    \&lt;br /&gt;
   VQFR     | 3rd frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
 . . .&lt;br /&gt;
&lt;br /&gt;
NOTE: There can also be some other chunks (i.e. PINF, PINH, SN2J) included,&lt;br /&gt;
but they are not relevant (?!) for viewing the movie, so they can&lt;br /&gt;
easily be skipped.&lt;br /&gt;
&lt;br /&gt;
===FORM chunk===&lt;br /&gt;
&lt;br /&gt;
This chunk is the main chunk, containing all other chunks.&lt;br /&gt;
In case of version 2 and 3 movies, its size is actually the size of the&lt;br /&gt;
entire file minus the size of the chunk header (8 bytes). Version 1 movies&lt;br /&gt;
seem to have this set to the length of the header VQHD + FINF chunk.&lt;br /&gt;
&lt;br /&gt;
Immediately after the chunk's header, a 4-character signature,&lt;br /&gt;
&amp;quot;WVQA&amp;quot; is located. Then come all the other chunks.&lt;br /&gt;
&lt;br /&gt;
===VQHD chunk (&amp;quot;VQa HeaDer&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This is the header chunk, containing vital information about the movie.&lt;br /&gt;
Its size is always 42 bytes.&lt;br /&gt;
The information is structured like this:&lt;br /&gt;
&lt;br /&gt;
 struct VQAHeader&lt;br /&gt;
 {&lt;br /&gt;
  short  Version;       /* VQA version number                         */&lt;br /&gt;
  short  Flags;         /* VQA flags                                  */&lt;br /&gt;
  short  NumFrames;     /* Number of frames                           */&lt;br /&gt;
  short  Width;         /* Movie width (pixels)                       */&lt;br /&gt;
  short  Height;        /* Movie height (pixels)                      */&lt;br /&gt;
  char   BlockW;        /* Width of each image block (pixels)         */&lt;br /&gt;
  char   BlockH;        /* Height of each image block (pixels)        */&lt;br /&gt;
  char   FrameRate;     /* Frame rate of the VQA                      */&lt;br /&gt;
  char   CBParts;       /* How many images use the same lookup table  */&lt;br /&gt;
  short  Colors;        /* Maximum number of colors used in VQA       */&lt;br /&gt;
  short  MaxBlocks;     /* Maximum number of image blocks             */&lt;br /&gt;
  short  OffsetX;       /* Offset X position in background movie      */&lt;br /&gt;
  short  OffsetY;       /* Offset Y position in background movie      */&lt;br /&gt;
  short  MaxVPT?Size;   /* Max. VPT? (VPTZ, VPRZ) size                */&lt;br /&gt;
  short  Freq;          /* Sound sampling frequency                   */&lt;br /&gt;
  char   Channels;      /* Number of sound channels                   */&lt;br /&gt;
  char   Bits;          /* Sound resolution                           */&lt;br /&gt;
  long   Unknown1;      /* Always 0 ???                               */&lt;br /&gt;
  short  Unknown2;      /* 0 in old VQAs, 4 in HiColor ones ???       */&lt;br /&gt;
  long   MaxCBFZSize;   /* Max. CBFZ size (in old VQAs usually is 0)  */&lt;br /&gt;
  long   Unknown3;      /* Always 0 ???                               */&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Version denotes the VQA version. Valid values are:&lt;br /&gt;
* 1 - First VQAs, used only in Legend of Kyrandia III.&lt;br /&gt;
* 2 - Used in C&amp;amp;C, Red Alert, Lands of Lore II, Dune 2000.&lt;br /&gt;
* 3 - Lands of Lore III (?), Blade Runner (?), Nox and Tiberian Sun. These VQAs are HiColor (15 bit).&lt;br /&gt;
&lt;br /&gt;
Flags most probably contain some flags. I know that bit 0 (LSB) and 2 (Overlay)&lt;br /&gt;
* bit 0 denotes whether the VQA has a soundtrack or not.&lt;br /&gt;
* (1 = Has sound, 0 = No sound)&lt;br /&gt;
* bit 2 denotes whether this movie is an overlay on the background video.&lt;br /&gt;
* (1 = Yes, 0 = No)&lt;br /&gt;
Movie with bit 2 may play separately, but need apply VPTR\VPRZ chunks&lt;br /&gt;
to &amp;quot;Null&amp;quot; Vector Pointer Table (filled by zeros) for each frame.&lt;br /&gt;
&lt;br /&gt;
If NumFrames of background movie less than NumFrames of overlay movie&lt;br /&gt;
then background movie will be repeat, while will not finish overlay movie.&lt;br /&gt;
&lt;br /&gt;
Width is usually 320, Height is usually 156 or 200 although one movie in&lt;br /&gt;
* Red Alert is 640x400 in size (the start movie for the Win95 version).&lt;br /&gt;
&lt;br /&gt;
Each frame of a VQA movie is comprised of a series of blocks that are&lt;br /&gt;
BlockW pixels in width and BlockH pixels in height. Imagine the frame&lt;br /&gt;
is a mosaic with the blocks being 'pieces' that make up the frame.&lt;br /&gt;
&lt;br /&gt;
BlockW is the width and BlockH is the height of each screen block.&lt;br /&gt;
In VQAs version 2 (and perhaps 1) the blocks are usually 4x2.&lt;br /&gt;
&lt;br /&gt;
FrameRate is always 15 in C&amp;amp;C and RA and seems to be 10 in LoK III.&lt;br /&gt;
&lt;br /&gt;
CBParts denotes how many frames use the same lookup table. It also&lt;br /&gt;
implies how many parts the new block lookup table is split into.&lt;br /&gt;
In C&amp;amp;C and RA it is always 8. When CBParts is 0, and movie have&lt;br /&gt;
CBP? chunks, then need see CIND chunk.&lt;br /&gt;
&lt;br /&gt;
Colors indicates the maximum number of colors used by the VQA.&lt;br /&gt;
The HiColor VQAs have this set to 0, while the old movies have&lt;br /&gt;
256 or less in here.&lt;br /&gt;
&lt;br /&gt;
Offset X and Offset Y uses only when there is bit 2 in the flags.&lt;br /&gt;
Usually uses chunk CLIP too, where the width and height of the&lt;br /&gt;
background movie are specified.&lt;br /&gt;
&lt;br /&gt;
MaxVPT?Size tells you the size of the largest VPTZ or VPRZ chunk, in bytes.&lt;br /&gt;
&lt;br /&gt;
Freq is usually 22050 Hz. Note that version 1 movies can have this set &lt;br /&gt;
to 0 Hz. In that case, you should use 22050 Hz.&lt;br /&gt;
&lt;br /&gt;
Channels specifies the number of sound channels, i.e. is the sound&lt;br /&gt;
mono or stereo. Channels=1 -&amp;gt; sound is mono, Channels=2 -&amp;gt; stereo.&lt;br /&gt;
C&amp;amp;C and RA almost always use mono sound. &lt;br /&gt;
Version 1 can have this set to 0, but you should use 1 (mono sound).&lt;br /&gt;
The majority of Tiberian Sun movies use stereo sound instead.&lt;br /&gt;
&lt;br /&gt;
* Bits indicates whether the sound is 8 bit or 16 bit.&lt;br /&gt;
Bits=8 -&amp;gt; 8 bit sound, Bits=16 -&amp;gt; 16 bit sound (surprise! :). &lt;br /&gt;
The Legend of Kyrandia III: Malcolm's Revenge uses 8 bits where&lt;br /&gt;
C&amp;amp;C, RA, TS, Dune 2000, Lands of Lore III use 16 bits.&lt;br /&gt;
Note, again, that version 1 of the VQAs can have this set to 0 in&lt;br /&gt;
which case 8 bits are assumed.&lt;br /&gt;
&lt;br /&gt;
MaxCBFZSize tells you the size of the largest CBFZ chunk, in bytes.&lt;br /&gt;
In old VQAs this value may be 0.&lt;br /&gt;
&lt;br /&gt;
Following the chunk data are the sub-chunks.&lt;br /&gt;
&lt;br /&gt;
===FINF chunk (&amp;quot;Frame INFormation&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This chunk contains the positions (absolute from the start of the VQA)&lt;br /&gt;
of data for every frame.&lt;br /&gt;
That means that it points to the SND? chunk associated with that frame,&lt;br /&gt;
which is followed by a VQFR chunk containing frame's video data.&lt;br /&gt;
&lt;br /&gt;
The positions are given as LONGs which are in normal Intel byte order.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' Some frame positions are 0x40000000 too large. This is a flag&lt;br /&gt;
indicating those frames contain a new color palette. To get the&lt;br /&gt;
actual positions, you should subtract 0x40000000 from the values.&lt;br /&gt;
&lt;br /&gt;
'''NOTE #2:''' To get the actual position of the frame data you have to multiply&lt;br /&gt;
the value by 2. This is why some chunk IDs start with 0x00. Since&lt;br /&gt;
you multiply by 2, you can't get an odd chunk position so if the&lt;br /&gt;
chunk position would normally be odd, a 0x00 is inserted to make&lt;br /&gt;
it even.&lt;br /&gt;
&lt;br /&gt;
===SND? chunk (&amp;quot;SouND&amp;quot; ???)===&lt;br /&gt;
These chunks contain the sound data for the movie. The last byte of the ID&lt;br /&gt;
can be either '0', '1' or '2' so the actual IDs would be &amp;quot;SND0&amp;quot;, &amp;quot;SND1&amp;quot;&lt;br /&gt;
and &amp;quot;SND2&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
In VQAs version 1 (Legend of Kyrandia 3) there are ''NumFrames'' sound chunks.&lt;br /&gt;
Old 8 bit VQA movies of version 2 have ''NumFrames+1'' sound chunks.&lt;br /&gt;
Note, however, that Dune2000 movies, which are also version 2, but HiColor&lt;br /&gt;
have ''NumFrames'' sound chunks, instead.&lt;br /&gt;
Version 3 movies also have ''NumFrames'' sound chunks.&lt;br /&gt;
&lt;br /&gt;
In case of the old, 8 bit VQAs, the first chunk contains half a second&lt;br /&gt;
(ver. 2) or more (ver. 1) of the wave data, in all (?) the HiColor movies&lt;br /&gt;
the first chunk contains exactly the amount of sound required for one&lt;br /&gt;
frame of the movie. The downside is this requires a somewhat more advanced &lt;br /&gt;
buffering technique on the side of the player in order to allow smooth &lt;br /&gt;
playback.&lt;br /&gt;
&lt;br /&gt;
===SND0 chunk===&lt;br /&gt;
&lt;br /&gt;
This one contains the raw 8 or 16 bit PCM wave data. If the data is&lt;br /&gt;
8 bit, the sound is unsigned and if it is 16 bit, the samples are&lt;br /&gt;
signed.&lt;br /&gt;
&lt;br /&gt;
===SND1 chunk===&lt;br /&gt;
&lt;br /&gt;
It contains 8 bit sound compressed using Westwood's own&lt;br /&gt;
proprietary ADPCM algorithm. The chunk has a 4 byte header:&lt;br /&gt;
&lt;br /&gt;
  struct SND1Header&lt;br /&gt;
  {&lt;br /&gt;
   short OutSize;&lt;br /&gt;
   short Size;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
These values are needed for the decoding algoritm (see APPENDIX C).&lt;br /&gt;
The encoded samples follow immediately after the header.&lt;br /&gt;
It's important to know this algorithm produces UNSIGNED sound, unlike&lt;br /&gt;
the IMA ADPCM algorithm supplied here (see below). It is, however very&lt;br /&gt;
simple to adapt both algorithms to produce either signed or unsigned&lt;br /&gt;
sample output...&lt;br /&gt;
&lt;br /&gt;
===SND2 chunk===&lt;br /&gt;
&lt;br /&gt;
It contains the 16 bit sound data compressed using the IMA ADPCM&lt;br /&gt;
algorithm which compresses 16 bits into 4. That's why the SND2 chunks&lt;br /&gt;
are 4 times smaller than SND0 chunks and they are used almost all&lt;br /&gt;
the time. For the description of the algorithm, see later in the document.&lt;br /&gt;
  &lt;br /&gt;
Different VQA versions have different stereo sample layout. In case&lt;br /&gt;
of Tiberian Sun stereo sound is encoded the same way as mono except the&lt;br /&gt;
SND2 chunk is split into two halfs. The first half contains the left channel&lt;br /&gt;
sound and the second half contains the right channel. The layout of&lt;br /&gt;
nibbles is as follows: LL LL LL LL LL ... RR RR RR RR RR.&lt;br /&gt;
Old movies (C&amp;amp;C, RA) use a different layout. Here, the nibbles are packed&lt;br /&gt;
together like this: LL RR LL RR LL RR ... This means that two left channel&lt;br /&gt;
samples are packed into one byte and then two right channel samples are&lt;br /&gt;
packed into another.&lt;br /&gt;
&lt;br /&gt;
Naturally, the size of a stereo SND2 chunk is exactly twice as big as&lt;br /&gt;
a mono SND2.&lt;br /&gt;
&lt;br /&gt;
It is important to note that, when decoding, you have to keep separate &lt;br /&gt;
values of ''Index'' and ''Cur_Sample'' for each channel (see APPENDIX B).&lt;br /&gt;
&lt;br /&gt;
===VQFR chunk (&amp;quot;Vector Quantized FRame&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
A chunk that includes many nested sub-chunks which contain video data.&lt;br /&gt;
It doesn't contain any data itself so the sub-chunks follow immediately&lt;br /&gt;
after the VQFR chunk header.&lt;br /&gt;
All following sub-chunks are nested inside a VQFR chunk. They can all&lt;br /&gt;
contain '0' or 'Z' as the last byte of their ID.&lt;br /&gt;
&lt;br /&gt;
* If the last byte is '0' it means that the chunk data is uncompressed.&lt;br /&gt;
* If the last byte is 'Z' it means that the data is compressed using&lt;br /&gt;
Format80 compression. You can find its description in APPENDIX A.&lt;br /&gt;
&lt;br /&gt;
===CBF? chunk (&amp;quot;CodeBook, Full&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
Lookup table containing the screen block data as an array&lt;br /&gt;
of elements that each are BlockW*BlockH bytes long. It is always located&lt;br /&gt;
in the data for the first frame. In Vector Quantization terminology&lt;br /&gt;
these tables are called CODEBOOKS.&lt;br /&gt;
&lt;br /&gt;
There can be max. 0x0f00 of these elements (blocks) at any one time in &lt;br /&gt;
normal VQAs and 0xff00 in the hi-res VQAs (Red Alert 95 start movie) although&lt;br /&gt;
I seriously doubt that so many blocks (0xff00 = 65280 blocks) would&lt;br /&gt;
ever be used.&lt;br /&gt;
&lt;br /&gt;
The uncompressed version of these chunks (&amp;quot;CBF0&amp;quot;) is used mainly in&lt;br /&gt;
the original Command &amp;amp; Conquer, while the compressed version (&amp;quot;CBFZ&amp;quot;)&lt;br /&gt;
is used in C&amp;amp;C: Red Alert.&lt;br /&gt;
&lt;br /&gt;
===CBP? chunk (&amp;quot;CodeBook Part&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
Like CBF?, but it contains a part of the lookup table, so to get the new&lt;br /&gt;
complete table you need to append CBParts of these in frame order.&lt;br /&gt;
Once you get the complete table and display the current frame, replace&lt;br /&gt;
the old table with the new one.&lt;br /&gt;
As in CBF? chunk, the uncompressed chunks are used in C&amp;amp;C and the compressed&lt;br /&gt;
chunks are used in Red Alert.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' If the chunks are CBFZ, first you need to append CBParts of them and&lt;br /&gt;
then decompress the data, NOT decompress each chunk individually.&lt;br /&gt;
&lt;br /&gt;
===CPL? chunk (&amp;quot;Color PaLette&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
The simplest one of all... Contains a palette for the VQA. It is an&lt;br /&gt;
array of red, green and blue values (in that order, all have a size of&lt;br /&gt;
1 byte). Seems that the values range from 0-255, but you should mask out&lt;br /&gt;
the bits 6 and 7 to get the correct palette (VGA hardware uses only&lt;br /&gt;
bits 0..5 anyway).&lt;br /&gt;
&lt;br /&gt;
===VPT? chunk: (&amp;quot;Vector Pointer Table&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This chunk contains the indexes into the block lookup table which contains&lt;br /&gt;
the data to display the frame.&lt;br /&gt;
The image blocks are called VECTORS in Vector Quantization terminology.&lt;br /&gt;
&lt;br /&gt;
These chunks are always compressed, but I guess the uncompressed ones&lt;br /&gt;
can also be used (although this would lower the overall compression achieved).&lt;br /&gt;
&lt;br /&gt;
The size of this index table is ''(Width/BlockW)*(Height/BlockH)*2 bytes''.&lt;br /&gt;
&lt;br /&gt;
Now, there is a catch: version 2 VQAs use a different layout of the&lt;br /&gt;
index table than version 1 VQAs. I will first describe the version&lt;br /&gt;
2 table format, as it's more common.&lt;br /&gt;
&lt;br /&gt;
====VERSION 2 INDEX TABLE LAYOUT====&lt;br /&gt;
The index table is an array of CHARs and is split into 2 parts - the top&lt;br /&gt;
half and the bottom half.&lt;br /&gt;
&lt;br /&gt;
Now, if you want to diplay the block at coordinates (in block units),&lt;br /&gt;
say (bx,by) you should read two bytes from the table, one from the top&lt;br /&gt;
and one from the bottom half:&lt;br /&gt;
&lt;br /&gt;
  LoVal=Table[by*(Width/BlockW)+bx]&lt;br /&gt;
  HiVal=Table[(Width/BlockW)*(Height/BlockH)+by*(Width/BlockW)+bx]&lt;br /&gt;
&lt;br /&gt;
If HiVal=0x0f (0xff for the start movie of Red Alert 95) you should&lt;br /&gt;
simply fill the block with color LoVal, otherwise you should copy&lt;br /&gt;
the block with index number HiVal*256+LoVal from the lookup table.&lt;br /&gt;
&lt;br /&gt;
Do that for every block on the screen (remember, there are Width/BlockW&lt;br /&gt;
blocks in the horizontal direction and Height/BlockH blocks in the vertical&lt;br /&gt;
direction) and you've decoded your first frame!&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' I was unable to find an entry in the VQA header which determines&lt;br /&gt;
whether 0x0f or 0xff is used in HiVal to signal that the block&lt;br /&gt;
is of uniform color. I assume that the Wy entry of the header&lt;br /&gt;
implies this: If BlockW=2 -&amp;gt; 0x0f is used, if BlockH=4 -&amp;gt; 0xff is used.&lt;br /&gt;
&lt;br /&gt;
====VERSION 1 INDEX TABLE LAYOUT====&lt;br /&gt;
Here, the index table is simply an array of SHORTs written in normal, Intel&lt;br /&gt;
byte order.&lt;br /&gt;
&lt;br /&gt;
The LoVal and HiVal are given as:&lt;br /&gt;
&lt;br /&gt;
  LoVal=Table[(by*(Width/BlockW)+bx)*2]&lt;br /&gt;
  HiVal=Table[(by*(Width/BlockW)+bx)*2+1]&lt;br /&gt;
&lt;br /&gt;
If HiVal=0xff, the block is of uniform color which is (255-LoVal).&lt;br /&gt;
Otherwise, write the block with index number (HiVal*256+LoVal)/8.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Appendix A===&lt;br /&gt;
&lt;br /&gt;
FORMAT80 COMPRESSION METHOD&lt;br /&gt;
by Vladan Bato (bat22@geocities.com)&lt;br /&gt;
&lt;br /&gt;
There are several different commands, with different sizes : from 1 to 5 bytes.&lt;br /&gt;
The positions mentioned below always refer to the destination buffer (i.e.&lt;br /&gt;
the uncompressed image). The relative positions are relative to the current&lt;br /&gt;
position in the destination buffer, which is one byte beyond the last written&lt;br /&gt;
byte.&lt;br /&gt;
&lt;br /&gt;
I will give some sample code at the end.&lt;br /&gt;
&lt;br /&gt;
(1) 1 byte&lt;br /&gt;
      +---+---+---+---+---+---+---+---+&lt;br /&gt;
      | 1 | 0 |   |   |   |   |   |   |&lt;br /&gt;
      +---+---+---+---+---+---+---+---+&lt;br /&gt;
              \_______________________/&lt;br /&gt;
                         |&lt;br /&gt;
                       Count&lt;br /&gt;
&lt;br /&gt;
      This one means : copy next Count bytes as is from Source to Dest.&lt;br /&gt;
&lt;br /&gt;
(2) 2 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
  | 0 |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
      \___________/\__________________________________________________/&lt;br /&gt;
            |                             |&lt;br /&gt;
         Count-3                    Relative Pos.&lt;br /&gt;
&lt;br /&gt;
  This means copy Count bytes from Dest at Current Pos.-Rel. Pos. to&lt;br /&gt;
  Current position.&lt;br /&gt;
  Note that you have to add 3 to the number you find in the bits 4-6 of the&lt;br /&gt;
  first byte to obtain the Count.&lt;br /&gt;
  Note that if the Rel. Pos. is 1, that means repeat Count times the previous&lt;br /&gt;
  byte.&lt;br /&gt;
&lt;br /&gt;
(3) 3 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---------------+---------------+&lt;br /&gt;
  | 1 | 1 |   |   |   |   |   |   |   |               |               |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---------------+---------------+&lt;br /&gt;
          \_______________________/                  Pos&lt;br /&gt;
                     |&lt;br /&gt;
                 Count-3&lt;br /&gt;
&lt;br /&gt;
  Copy Count bytes from Pos, where Pos is absolute from the start of the&lt;br /&gt;
  destination buffer. (Pos is a word, that means that the images can't be&lt;br /&gt;
  larger than 64K)&lt;br /&gt;
&lt;br /&gt;
(4) 4 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+&lt;br /&gt;
  | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |   |       |       |  |       |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+&lt;br /&gt;
                                            Count          Color&lt;br /&gt;
&lt;br /&gt;
  Write Color Count times.&lt;br /&gt;
  (Count is a word, color is a byte)&lt;br /&gt;
&lt;br /&gt;
(5) 5 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+&lt;br /&gt;
  | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |   |       |       |  |       |       |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+&lt;br /&gt;
                                            Count               Pos&lt;br /&gt;
&lt;br /&gt;
  Copy Count bytes from Dest. starting at Pos. Pos is absolute from the start&lt;br /&gt;
  of the Destination buffer.&lt;br /&gt;
  Both Count and Pos are words.&lt;br /&gt;
&lt;br /&gt;
These are all the commands I found out. Maybe there are other ones, but I&lt;br /&gt;
haven't seen them yet.&lt;br /&gt;
&lt;br /&gt;
All the images end with a 80h command.&lt;br /&gt;
&lt;br /&gt;
To make things more clearer here's a piece of code that will uncompress the&lt;br /&gt;
image.&lt;br /&gt;
&lt;br /&gt;
  DP = destination pointer&lt;br /&gt;
  SP = source pointer&lt;br /&gt;
  Source and Dest are the two buffers&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  SP:=0;&lt;br /&gt;
  DP:=0;&lt;br /&gt;
  repeat&lt;br /&gt;
    Com:=Source[SP];&lt;br /&gt;
    inc(SP);&lt;br /&gt;
    b7:=Com shr 7;  {b7 is bit 7 of Com}&lt;br /&gt;
    case b7 of&lt;br /&gt;
      0 : begin  {copy command (2)}&lt;br /&gt;
            {Count is bits 4-6 + 3}&lt;br /&gt;
            Count:=(Com and $7F) shr 4 + 3;&lt;br /&gt;
            {Position is bits 0-3, with bits 0-7 of next byte}&lt;br /&gt;
            Posit:=(Com and $0F) shl 8+Source[SP];&lt;br /&gt;
            Inc(SP);&lt;br /&gt;
            {Starting pos=Cur pos. - calculated value}&lt;br /&gt;
            Posit:=DP-Posit;&lt;br /&gt;
            for i:=Posit to Posit+Count-1 do&lt;br /&gt;
            begin&lt;br /&gt;
              Dest[DP]:=Dest[i];&lt;br /&gt;
              Inc(DP);&lt;br /&gt;
            end;&lt;br /&gt;
          end;&lt;br /&gt;
      1 : begin&lt;br /&gt;
            {Check bit 6 of Com}&lt;br /&gt;
            b6:=(Com and $40) shr 6;&lt;br /&gt;
            case b6 of&lt;br /&gt;
              0 : begin  {Copy as is command (1)}&lt;br /&gt;
                    Count:=Com and $3F;  {mask 2 topmost bits}&lt;br /&gt;
                    if Count=0 then break; {EOF marker}&lt;br /&gt;
                    for i:=1 to Count do&lt;br /&gt;
                    begin&lt;br /&gt;
                      Dest[DP]:=Source[SP];&lt;br /&gt;
                      Inc(DP);&lt;br /&gt;
                      Inc(SP);&lt;br /&gt;
                    end;&lt;br /&gt;
                  end;&lt;br /&gt;
              1 : begin  {large copy, very large copy and fill commands}&lt;br /&gt;
                    {Count = (bits 0-5 of Com) +3}&lt;br /&gt;
                    {if Com=FEh then fill, if Com=FFh then very large copy}&lt;br /&gt;
                    Count:=Com and $3F;&lt;br /&gt;
                    if Count&amp;lt;$3E then {large copy (3)}&lt;br /&gt;
                    begin&lt;br /&gt;
                      Inc(Count,3);&lt;br /&gt;
                      {Next word = pos. from start of image}&lt;br /&gt;
                      Posit:=Word(Source[SP]);&lt;br /&gt;
                      Inc(SP,2);&lt;br /&gt;
                      for i:=Posit to Posit+Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=Dest[i];&lt;br /&gt;
                        Inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end&lt;br /&gt;
                    else if Count=$3F then   {very large copy (5)}&lt;br /&gt;
                    begin&lt;br /&gt;
                      {next 2 words are Count and Pos}&lt;br /&gt;
                      Count:=Word(Source[SP]);&lt;br /&gt;
                      Posit:=Word(Source[SP+2]);&lt;br /&gt;
                      Inc(SP,4);&lt;br /&gt;
                      for i:=Posit to Posit+Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=Dest[i];&lt;br /&gt;
                        Inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end else&lt;br /&gt;
                    begin   {Count=$3E, fill (4)}&lt;br /&gt;
                      {Next word is count, the byte after is color}&lt;br /&gt;
                      Count:=Word(Source[SP]);&lt;br /&gt;
                      Inc(SP,2);&lt;br /&gt;
                      b:=Source[SP];&lt;br /&gt;
                      Inc(SP);&lt;br /&gt;
                      for i:=0 to Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=b;&lt;br /&gt;
                        inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end;&lt;br /&gt;
                  end;&lt;br /&gt;
            end;&lt;br /&gt;
          end;&lt;br /&gt;
    end;&lt;br /&gt;
  until false;&lt;br /&gt;
&lt;br /&gt;
Note that you won't be able to compile this code, because the typecasting&lt;br /&gt;
won't work. (But I'm sure you'll be able to fix it).&lt;br /&gt;
&lt;br /&gt;
===Appendix B===&lt;br /&gt;
IMA ADPCM DECOMPRESSION&lt;br /&gt;
by Vladan Bato  (bat22@geocities.com)&lt;br /&gt;
http://www.geocities.com/SiliconValley/8682&lt;br /&gt;
&lt;br /&gt;
Note that the current sample value and index into the Step Table should&lt;br /&gt;
be initialized to 0 at the start and are maintained across the chunks&lt;br /&gt;
(see below).&lt;br /&gt;
&lt;br /&gt;
====IMA-ADPCM DECOMPRESSION ====&lt;br /&gt;
&lt;br /&gt;
It is the exact opposite of the above. It receives 4-bit codes in input&lt;br /&gt;
and produce 16-bit samples in output.&lt;br /&gt;
&lt;br /&gt;
Again you have to mantain an Index into the Step Table an the current&lt;br /&gt;
sample value.&lt;br /&gt;
&lt;br /&gt;
The tables used are the same as for compression.&lt;br /&gt;
&lt;br /&gt;
Here's the code :&lt;br /&gt;
&lt;br /&gt;
  Index:=0;&lt;br /&gt;
  Cur_Sample:=0;&lt;br /&gt;
&lt;br /&gt;
  while there_is_more_data do&lt;br /&gt;
  begin&lt;br /&gt;
    Code:=Get_Next_Code;&lt;br /&gt;
&lt;br /&gt;
    if (Code and $8) &amp;lt;&amp;gt; 0 then Sb:=1 else Sb:=0;&lt;br /&gt;
    Code:=Code and $7;&lt;br /&gt;
    {Separate the sign bit from the rest}&lt;br /&gt;
&lt;br /&gt;
    Delta:=(Step_Table[Index]*Code) div 4 + Step_Table[Index] div 8;&lt;br /&gt;
    {The last one is to minimize errors}&lt;br /&gt;
&lt;br /&gt;
    if Sb=1 then Delta:=-Delta;&lt;br /&gt;
&lt;br /&gt;
    Cur_Sample:=Cur_Sample+Delta;&lt;br /&gt;
    if Cur_Sample&amp;gt;32767 then Cur_Sample:=32767&lt;br /&gt;
    else if Cur_Sample&amp;lt;-32768 then Cur_Sample:=-32768;&lt;br /&gt;
&lt;br /&gt;
    Output_Sample(Cur_Sample);&lt;br /&gt;
&lt;br /&gt;
    Index:=Index+Index_Adjust[Code];&lt;br /&gt;
    if Index&amp;lt;0 then Index:=0;&lt;br /&gt;
    if Index&amp;gt;88 the Index:=88;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
Again, this can be done more efficiently (no need for multiplication).&lt;br /&gt;
&lt;br /&gt;
The ''Get_Next_Code'' function should return the next 4-bit code. It must&lt;br /&gt;
extract it from the input buffer (note that two 4-bit codes are stored&lt;br /&gt;
in the same byte, the first one in the lower bits).&lt;br /&gt;
&lt;br /&gt;
The Output_Sample function should write the signed 16-bit sample to the&lt;br /&gt;
output buffer.&lt;br /&gt;
&lt;br /&gt;
=== Appendix A : THE INDEX ADJUSTMENT TABLE ===&lt;br /&gt;
&lt;br /&gt;
  Index_Adjust : array [0..7] of integer = (-1,-1,-1,-1,2,4,6,8);&lt;br /&gt;
&lt;br /&gt;
=== Appendix B : THE STEP TABLE ===&lt;br /&gt;
&lt;br /&gt;
  Steps_Table : array [0..88] of integer =(&lt;br /&gt;
        7,     8,     9,     10,    11,    12,     13,    14,    16,&lt;br /&gt;
        17,    19,    21,    23,    25,    28,     31,    34,    37,&lt;br /&gt;
        41,    45,    50,    55,    60,    66,     73,    80,    88,&lt;br /&gt;
        97,    107,   118,   130,   143,   157,    173,   190,   209,&lt;br /&gt;
        230,   253,   279,   307,   337,   371,    408,   449,   494,&lt;br /&gt;
        544,   598,   658,   724,   796,   876,    963,   1060,  1166,&lt;br /&gt;
        1282,  1411,  1552,  1707,  1878,  2066,   2272,  2499,  2749,&lt;br /&gt;
        3024,  3327,  3660,  4026,  4428,  4871,   5358,  5894,  6484,&lt;br /&gt;
        7132,  7845,  8630,  9493,  10442, 11487,  12635, 13899, 15289,&lt;br /&gt;
        16818, 18500, 20350, 22385, 24623, 27086,  29794, 32767 );&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Appendix C===&lt;br /&gt;
WESTWOOD STUDIOS' ADPCM DECOMPRESSION&lt;br /&gt;
by Asatur V. Nazarian (samael@avn.mccme.ru)&lt;br /&gt;
&lt;br /&gt;
==== WS ADPCM Decompression Algorithm ====&lt;br /&gt;
&lt;br /&gt;
Each SND1 chunk may be decompressed independently of others. This lets you&lt;br /&gt;
implement seeking/skipping for WS ADPCM sounds (unlike IMA ADPCM ones).&lt;br /&gt;
But during the decompression of the given chunk a variable (''CurSample'') should&lt;br /&gt;
be maintained for this whole chunk:&lt;br /&gt;
&lt;br /&gt;
 SHORT CurSample;&lt;br /&gt;
 BYTE  InputBuffer[InputBufferSize]; // input buffer containing the whole chunk&lt;br /&gt;
 WORD  wSize, wOutSize; // Size and OutSize values from this chunk's header&lt;br /&gt;
 BYTE  code;&lt;br /&gt;
 CHAR  count; // this is a signed char!&lt;br /&gt;
 WORD  i; // index into InputBuffer&lt;br /&gt;
 WORD  input; // shifted input&lt;br /&gt;
 &lt;br /&gt;
 if (wSize==wOutSize) // such chunks are NOT compressed&lt;br /&gt;
 {&lt;br /&gt;
  for (i=0;i&amp;lt;wOutSize;i++)&lt;br /&gt;
      Output(InputBuffer[i]); // send to output stream&lt;br /&gt;
  return; // chunk is done!&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // otherwise we need to decompress chunk&lt;br /&gt;
 &lt;br /&gt;
 CurSample=0x80; // unsigned 8-bit&lt;br /&gt;
 i=0;&lt;br /&gt;
 &lt;br /&gt;
 // note that wOutSize value is crucial for decompression!&lt;br /&gt;
 &lt;br /&gt;
 while (wOutSize&amp;gt;0) // until wOutSize is exhausted!&lt;br /&gt;
 {&lt;br /&gt;
  input=InputBuffer[i++];&lt;br /&gt;
  input&amp;lt;&amp;lt;=2;&lt;br /&gt;
  code=HIBYTE(input);&lt;br /&gt;
  count=LOBYTE(input)&amp;gt;&amp;gt;2;&lt;br /&gt;
  switch (code) // parse code&lt;br /&gt;
  {&lt;br /&gt;
    case 2: // no compression...&lt;br /&gt;
	 if (count &amp;amp; 0x20)&lt;br /&gt;
	 {&lt;br /&gt;
	   count&amp;lt;&amp;lt;=3;		// here it's significant that (count) is signed:&lt;br /&gt;
	   CurSample+=count&amp;gt;&amp;gt;3; // the sign bit will be copied by these shifts!&lt;br /&gt;
&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize--; // one byte added to output&lt;br /&gt;
	 }&lt;br /&gt;
	 else // copy (count+1) bytes from input to output&lt;br /&gt;
	 {&lt;br /&gt;
	   for (count++;count&amp;gt;0;count--,wOutSize--,i++)&lt;br /&gt;
	       Output(InputBuffer[i]);&lt;br /&gt;
	   CurSample=InputBuffer[i-1]; // set (CurSample) to the last byte sent to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    case 1: // ADPCM 8-bit -&amp;gt; 4-bit&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--) // decode (count+1) bytes&lt;br /&gt;
	 {&lt;br /&gt;
	   code=InputBuffer[i++];&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable4bit[(code &amp;amp; 0x0F)]; // lower nibble&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable4bit[(code &amp;gt;&amp;gt; 4)]; // higher nibble&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize-=2; // two bytes added to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    case 0: // ADPCM 8-bit -&amp;gt; 2-bit&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--) // decode (count+1) bytes&lt;br /&gt;
	 {&lt;br /&gt;
	   code=InputBuffer[i++];&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[(code &amp;amp; 0x03)]; // lower 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;2) &amp;amp; 0x03)]; // lower middle 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;4) &amp;amp; 0x03)]; // higher middle 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;6) &amp;amp; 0x03)]; // higher 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize-=4; // 4 bytes sent to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    default: // just copy (CurSample) (count+1) times to output&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--,wOutSize--)&lt;br /&gt;
	     Output((BYTE)CurSample);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
HIBYTE and LOBYTE are just higher and lower bytes of WORD:&lt;br /&gt;
 #define HIBYTE(word) ((word) &amp;gt;&amp;gt; 8)&lt;br /&gt;
 #define LOBYTE(word) ((word) &amp;amp; 0xFF)&lt;br /&gt;
Note that depending on your compiler you may need to use additional byte&lt;br /&gt;
separation in these defines, e.g. (((byte) &amp;gt;&amp;gt; 8) &amp;amp; 0xFF). The same holds for&lt;br /&gt;
4-bit and 2-bit nibble separation in the code above.&lt;br /&gt;
&lt;br /&gt;
''WSTable4bit'' and ''WSTable2bit'' are the delta tables given in the next section.&lt;br /&gt;
&lt;br /&gt;
Output() is just a placeholder for any action you would like to perform for&lt;br /&gt;
decompressed sample value.&lt;br /&gt;
&lt;br /&gt;
Clip8BitSample is quite evident:&lt;br /&gt;
&lt;br /&gt;
 SHORT Clip8BitSample(SHORT sample)&lt;br /&gt;
 {&lt;br /&gt;
  if (sample&amp;gt;255)&lt;br /&gt;
     return 255;&lt;br /&gt;
  else if (sample&amp;lt;0)&lt;br /&gt;
     return 0;&lt;br /&gt;
  else&lt;br /&gt;
     return sample;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This algorithm is ONLY for mono 8-bit unsigned sound, as I've never seen any&lt;br /&gt;
other sound format used with WS ADPCM compression.&lt;br /&gt;
&lt;br /&gt;
Of course, the decompression routine described above may be greatly&lt;br /&gt;
optimized.&lt;br /&gt;
&lt;br /&gt;
==== WS ADPCM Tables ====&lt;br /&gt;
&lt;br /&gt;
 CHAR WSTable2bit[]=&lt;br /&gt;
 {&lt;br /&gt;
    -2,&lt;br /&gt;
    -1,&lt;br /&gt;
     0,&lt;br /&gt;
     1&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 CHAR WSTable4bit[]=&lt;br /&gt;
 {&lt;br /&gt;
    -9, -8, -6, -5, -4, -3, -2, -1,&lt;br /&gt;
     0,  1,  2,  3,  4,  5,  6,  8&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
==HC_VQA.txt==&lt;br /&gt;
by Gordan Ugarkovic (ugordan@yahoo.com)&lt;br /&gt;
http://members.xoom.com/ugordan&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This document describes how to view Westwood's HiColor VQA movies. These&lt;br /&gt;
are version 3 movies, but there are at least some version 2 VQAs that are&lt;br /&gt;
in this format. I will not describe the whole VQA layout here, I will&lt;br /&gt;
just explain how to display the VIDEO stream of the VQA, that is, how&lt;br /&gt;
to decompress the CBFZ and VPTR/VPRZ chunks.&lt;br /&gt;
&lt;br /&gt;
First a little warning: I'm not sure which flag denotes the VQA is 8 bit&lt;br /&gt;
or 15 bit. I'm pretty convinced it's either bit 4 (0x10) or bit 2 (0x04)&lt;br /&gt;
of the Flags entry (see my VQA_INFO.TXT) in the header. Another way would &lt;br /&gt;
be to check the Colors entry in the header, if it is 0 it could imply &lt;br /&gt;
a HiColor movie.&lt;br /&gt;
&lt;br /&gt;
There's a major difference between the old (8 bit, 256 color) and the new,&lt;br /&gt;
HiColor VQAs. Lookup tables are no longer split up into several CBP?&lt;br /&gt;
chunks, instead they come in one piece (a CBFZ chunk). Two lookup tables&lt;br /&gt;
can now be many frames apart, not just 8 (as usual). This is indicated&lt;br /&gt;
by the CBParts entry of the header (see VQA_INFO.TXT), which is set to 0.&lt;br /&gt;
Subsequent frames use the last lookup table loaded, of course.&lt;br /&gt;
&lt;br /&gt;
Another thing: It appears the first CBFZ chunk comes inside the VQFR chunk&lt;br /&gt;
but the other ones seem to be located inside their own chunks,&lt;br /&gt;
called VQFL, which are followed by the usual VQFR chunks (containing&lt;br /&gt;
VPTR/VPRZ chunks).&lt;br /&gt;
&lt;br /&gt;
Also, the movies are 15 bit, NOT 16 bit. There is a difference because&lt;br /&gt;
in 16 bit color depth there are 6 bits for the green channel, but&lt;br /&gt;
the VQAs use 5.&lt;br /&gt;
&lt;br /&gt;
=== The CBFZ chunks===&lt;br /&gt;
&lt;br /&gt;
These are a bit modified since the 8 bit VQAs. If the first byte of the&lt;br /&gt;
chunk is not NULL (0x00), it means the chunk is compressed using the&lt;br /&gt;
standard Format80 algorithm (see Vladan Bato's text on C&amp;amp;C file formats),&lt;br /&gt;
starting from that byte. If the first byte is NULL, the chunk is compressed&lt;br /&gt;
using a modified version of Format80 (see below), starting from the next&lt;br /&gt;
byte of the chunk. The original Format80 algorithm is used when the&lt;br /&gt;
amount of data to be compressed is less than 64 KB, otherwise the 'new'&lt;br /&gt;
algorithm is used.&lt;br /&gt;
&lt;br /&gt;
When decompressed properly, a CBFZ chunk expands into 15 bit pixels packed&lt;br /&gt;
as shorts in normal Intel byte order. The red, green and blue values are&lt;br /&gt;
packed like this:&lt;br /&gt;
&lt;br /&gt;
  15      bit      0&lt;br /&gt;
   arrrrrgg gggbbbbb&lt;br /&gt;
   HI byte  LO byte&lt;br /&gt;
&lt;br /&gt;
The r,g,b values make up a pixel and they can range from 0-31.&lt;br /&gt;
As in the old CBFZ chunks, these pixels make up the block lookup table&lt;br /&gt;
(also called a codebook). The a (alpha) value is used in Blade Runner in&lt;br /&gt;
overlay videos to indicate a transparent pixel.&lt;br /&gt;
&lt;br /&gt;
=== The VPTR chunks===&lt;br /&gt;
&lt;br /&gt;
These chunks use some sort of differential, run-length algorithm that&lt;br /&gt;
only records changes from the previous frame. Therefore, the previous&lt;br /&gt;
frame bitmap must be maintained throughout all the frames (you could&lt;br /&gt;
just draw the blocks that changed, though). This makes dropping frames&lt;br /&gt;
(in case of bad performance) impossible.&lt;br /&gt;
&lt;br /&gt;
When decoding, you take a short int (Intel) from the chunk and examine &lt;br /&gt;
its 3 most significant bits (bits 15,14,13). These bits make up a &lt;br /&gt;
code prefix that determines which action is to be done.&lt;br /&gt;
&lt;br /&gt;
Here's a list of the prefixes I encountered and their description&lt;br /&gt;
(Val is the short int value):&lt;br /&gt;
&lt;br /&gt;
  BITS - MEANING&lt;br /&gt;
&lt;br /&gt;
   000 - Skip Count blocks. Count is (Val &amp;amp; 0x1fff).&lt;br /&gt;
&lt;br /&gt;
   001 - Write block number (Val &amp;amp; 0xff) Count times.&lt;br /&gt;
         Count is (((Val/256) &amp;amp; 0x1f)+1)*2. Note that this can only&lt;br /&gt;
         index the first 256 blocks.&lt;br /&gt;
&lt;br /&gt;
   010 - Write block number (Val &amp;amp; 0xff) and then write Count blocks&lt;br /&gt;
         getting their indexes by reading next Count bytes from&lt;br /&gt;
         the VPTR chunk. Count is (((Val/256) &amp;amp; 0x1f)+1)*2.&lt;br /&gt;
         Again, the block numbers range from 0-255.&lt;br /&gt;
&lt;br /&gt;
   011 - Write block (Val &amp;amp; 0x1fff).&lt;br /&gt;
&lt;br /&gt;
   100 - Same as 011 but skip pixels with alpha bit set.&lt;br /&gt;
&lt;br /&gt;
   101 - Write block (Val &amp;amp; 0x1fff) Count times. Count is the next&lt;br /&gt;
         byte from the VPTR chunk.&lt;br /&gt;
&lt;br /&gt;
   110 - Same as 101 but skip pixels with alpha bit set.&lt;br /&gt;
&lt;br /&gt;
After this, you take the next short int and repeat the above process.&lt;br /&gt;
&lt;br /&gt;
Prefixes 100 and 110 are used in Blade Runner for overlay videos.&lt;br /&gt;
&lt;br /&gt;
Every row of blocks is processed individually of others.&lt;br /&gt;
When you encounter the end of a row, proceed to the next row&lt;br /&gt;
(blocks are processed left to right, top to down).&lt;br /&gt;
Repeat this process until all blocks in the frame are covered and&lt;br /&gt;
that's it!&lt;br /&gt;
&lt;br /&gt;
Note that the above implies an absolute maximum of 8192 blocks (0x1fff+1). &lt;br /&gt;
&lt;br /&gt;
As for the VPRZ chunks, these are just VPTR chunks compressed with&lt;br /&gt;
the standard Format80 algorithm.&lt;br /&gt;
&lt;br /&gt;
=== The modified Format80 scheme ===&lt;br /&gt;
&lt;br /&gt;
This is really only a small modification of the basic algorithm.&lt;br /&gt;
The only commands that are modified are commands (3) and (5)&lt;br /&gt;
See Vladan's text). Instead of using offsets ABSOLUTE from&lt;br /&gt;
the start of the destination buffer, offsets RELATIVE to the&lt;br /&gt;
current destination pointer are used. If you ask me, I don't see&lt;br /&gt;
why this approach wasn't used in the first place as it would&lt;br /&gt;
suffer no disadvantage with the old files and it would be much&lt;br /&gt;
easier to compress even larger amounts of data. The guys at WW&lt;br /&gt;
were just careless, I guess... :-)&lt;br /&gt;
&lt;br /&gt;
Anyway, in Vladan's algorithm, there is a line in&lt;br /&gt;
command (3) that says:&lt;br /&gt;
    Posit:=Word(Source[SP]);&lt;br /&gt;
it should say:&lt;br /&gt;
    Posit:=DP-Word(Source[SP]);&lt;br /&gt;
&lt;br /&gt;
Likewise, for command (5):&lt;br /&gt;
    Posit:=Word(Source[SP+2]);&lt;br /&gt;
it should be:&lt;br /&gt;
    Posit:=DP-Word(Source[SP+2]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Games Using VQA ==&lt;br /&gt;
&lt;br /&gt;
=== Version 1 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/win3x/legend-of-kyrandia-malcolms-revenge The Legend of Kyrandia: Malcolm's Revenge]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/monopoly Monopoly] (Version 1???)&lt;br /&gt;
&lt;br /&gt;
=== Versions 2 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/dos/command-conquer Command &amp;amp; Conquer]&lt;br /&gt;
* [http://www.mobygames.com/game/dos/command-conquer-red-alert Command &amp;amp; Conquer: Red Alert]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/command-conquer-sole-survivor Command &amp;amp; Conquer: Sole Survivor]&lt;br /&gt;
* [http://www.mobygames.com/game/lands-of-lore-guardians-of-destiny Lands of Lore: Guardians of Destiny]&lt;br /&gt;
&lt;br /&gt;
=== Versions 3 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/windows/blade-runner Blade Runner]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/command-conquer-tiberian-sun Command &amp;amp; Conquer: Tiberian Sun]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/dune-2000 Dune 2000]&lt;br /&gt;
* [http://www.mobygames.com/game/lands-of-lore-iii Lands of Lore III]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/nox Nox]&lt;br /&gt;
&lt;br /&gt;
==Other Documentation==&lt;br /&gt;
http://www.gamers.org/pub/idgames2/planetquake/planetcnc/cncdz/ has lots of data about this format. See vqa_overview.zip and vqafilesguild.zip. Also there is a decoder (vqatoavi) that decodes the dune2000 sample. &lt;br /&gt;
&lt;br /&gt;
[[Category:Game Formats]]&lt;br /&gt;
[[Category:Video Codecs]]&lt;br /&gt;
[[Category:Incomplete Video Codecs]]&lt;/div&gt;</summary>
		<author><name>Andrew Dream</name></author>
	</entry>
	<entry>
		<id>https://wiki.multimedia.cx/index.php?title=VQA&amp;diff=16094</id>
		<title>VQA</title>
		<link rel="alternate" type="text/html" href="https://wiki.multimedia.cx/index.php?title=VQA&amp;diff=16094"/>
		<updated>2025-11-25T11:38:26Z</updated>

		<summary type="html">&lt;p&gt;Andrew Dream: /* VQHD chunk (&amp;quot;VQa HeaDer&amp;quot; ???) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Extension: vqa&lt;br /&gt;
* Company: [[Westwood Studios]]&lt;br /&gt;
* Samples: [http://samples.mplayerhq.hu/game-formats/vqa/ http://samples.mplayerhq.hu/game-formats/vqa/]&lt;br /&gt;
* Samples: {{DexvertSamples|video/westwoodStudiosVQA}}&lt;br /&gt;
&lt;br /&gt;
VQA stands for Vector Quantized Animation and is a FMV format used in a number of computer games by Westwood Studios.&lt;br /&gt;
&lt;br /&gt;
== Technical Description ==&lt;br /&gt;
&lt;br /&gt;
'''TODO: import and combine Gordan Ugarkovic's documents from [http://multimedia.cx/VQA_INFO.TXT http://multimedia.cx/VQA_INFO.TXT] and [http://multimedia.cx/HC-VQA.TXT http://multimedia.cx/HC-VQA.TXT].&lt;br /&gt;
==VQA_INFO.txt==&lt;br /&gt;
&lt;br /&gt;
===NOTE #1=== &lt;br /&gt;
This document only applies to VQA files in the original C&amp;amp;C and C&amp;amp;C: Red Alert (version 2) as well as Legend of Kyrandia III : Malcolm's Revenge (version 1).   It DOES NOT apply to the HiColor VQAs found in Westwood's newer games. They use a somewhat different approach to compressing video data (I will provide some facts about their sound stream, though). Look at my other document (HC-VQA.TXT) for a description of the HiColor VQA movies.&lt;br /&gt;
&lt;br /&gt;
===NOTE #2=== &lt;br /&gt;
Throughout the document, I will assume that:&lt;br /&gt;
* CHAR is 1 byte in size, unsigned,&lt;br /&gt;
* SHORT is 2 bytes in size, unsigned,&lt;br /&gt;
* LONG is 4 bytes in size.&lt;br /&gt;
&lt;br /&gt;
Each VQA file is comprised of a series of chunks. A chunk can contain&lt;br /&gt;
other sub-chunks nested in it. Every chunk has a 4 letter ID (all uppercase&lt;br /&gt;
letters) and a LONG written using Motorola byte ordering system (first&lt;br /&gt;
comes the Most Significant Byte), unlike the usual Intel system (Least&lt;br /&gt;
Significant Byte first).&lt;br /&gt;
&lt;br /&gt;
For example, if you had a value 0x12345678 in hexadecimal, using the Intel&lt;br /&gt;
notation it would be written as 78 56 34 12, while using Motorola's &lt;br /&gt;
12 34 56 78.&lt;br /&gt;
&lt;br /&gt;
NOTE: Some chunk IDs start with a NULL byte (0x00) because of reasons&lt;br /&gt;
that will become apparent later. You should just skip this byte&lt;br /&gt;
and assume the next 4 letters hold the chunk ID.&lt;br /&gt;
&lt;br /&gt;
Following the chunk header is the chunk data.&lt;br /&gt;
&lt;br /&gt;
===Typical VQA File===&lt;br /&gt;
Here is a scheme of a typical VQA file (nested chunks are indented):&lt;br /&gt;
&lt;br /&gt;
 FORM&lt;br /&gt;
   VQHD&lt;br /&gt;
   FINF       &amp;lt;-  Frame data positions&lt;br /&gt;
   SND?    \  &amp;lt;-  First sound chunk, contains 1/2 second of sound&lt;br /&gt;
   SND?     |     &amp;lt;- Contains 1 frame's worth of sound&lt;br /&gt;
   VQFR     |     &amp;lt;- Contains various video data chunks&lt;br /&gt;
     CBF?   | 1st frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     CPL?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
   SND?    \&lt;br /&gt;
   VQFR     | 2nd frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
   SND?    \&lt;br /&gt;
   VQFR     | 3rd frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
 . . .&lt;br /&gt;
&lt;br /&gt;
NOTE: There can also be some other chunks (i.e. PINF, PINH, SN2J) included,&lt;br /&gt;
but they are not relevant (?!) for viewing the movie, so they can&lt;br /&gt;
easily be skipped.&lt;br /&gt;
&lt;br /&gt;
===FORM chunk===&lt;br /&gt;
&lt;br /&gt;
This chunk is the main chunk, containing all other chunks.&lt;br /&gt;
In case of version 2 and 3 movies, its size is actually the size of the&lt;br /&gt;
entire file minus the size of the chunk header (8 bytes). Version 1 movies&lt;br /&gt;
seem to have this set to the length of the header VQHD + FINF chunk.&lt;br /&gt;
&lt;br /&gt;
Immediately after the chunk's header, a 4-character signature,&lt;br /&gt;
&amp;quot;WVQA&amp;quot; is located. Then come all the other chunks.&lt;br /&gt;
&lt;br /&gt;
===VQHD chunk (&amp;quot;VQa HeaDer&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This is the header chunk, containing vital information about the movie.&lt;br /&gt;
Its size is always 42 bytes.&lt;br /&gt;
The information is structured like this:&lt;br /&gt;
&lt;br /&gt;
 struct VQAHeader&lt;br /&gt;
 {&lt;br /&gt;
  short  Version;       /* VQA version number                         */&lt;br /&gt;
  short  Flags;         /* VQA flags                                  */&lt;br /&gt;
  short  NumFrames;     /* Number of frames                           */&lt;br /&gt;
  short  Width;         /* Movie width (pixels)                       */&lt;br /&gt;
  short  Height;        /* Movie height (pixels)                      */&lt;br /&gt;
  char   BlockW;        /* Width of each image block (pixels)         */&lt;br /&gt;
  char   BlockH;        /* Height of each image block (pixels)        */&lt;br /&gt;
  char   FrameRate;     /* Frame rate of the VQA                      */&lt;br /&gt;
  char   CBParts;       /* How many images use the same lookup table  */&lt;br /&gt;
  short  Colors;        /* Maximum number of colors used in VQA       */&lt;br /&gt;
  short  MaxBlocks;     /* Maximum number of image blocks             */&lt;br /&gt;
  short  OffsetX;       /* Offset X position in background movie      */&lt;br /&gt;
  short  OffsetY;       /* Offset Y position in background movie      */&lt;br /&gt;
  short  MaxVPT?Size;   /* Max. VPT? (VPTZ, VPRZ) size                */&lt;br /&gt;
  short  Freq;          /* Sound sampling frequency                   */&lt;br /&gt;
  char   Channels;      /* Number of sound channels                   */&lt;br /&gt;
  char   Bits;          /* Sound resolution                           */&lt;br /&gt;
  long   Unknown1;      /* Always 0 ???                               */&lt;br /&gt;
  short  Unknown2;      /* 0 in old VQAs, 4 in HiColor ones ???       */&lt;br /&gt;
  long   MaxCBFZSize;   /* Max. CBFZ size (in old VQAs usually is 0)  */&lt;br /&gt;
  long   Unknown3;      /* Always 0 ???                               */&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Version denotes the VQA version. Valid values are:&lt;br /&gt;
* 1 - First VQAs, used only in Legend of Kyrandia III.&lt;br /&gt;
* 2 - Used in C&amp;amp;C, Red Alert, Lands of Lore II, Dune 2000.&lt;br /&gt;
* 3 - Lands of Lore III (?), Blade Runner (?), Nox and Tiberian Sun. These VQAs are HiColor (15 bit).&lt;br /&gt;
&lt;br /&gt;
Flags most probably contain some flags. I know that bit 0 (LSB) and 2 (Overlay)&lt;br /&gt;
* bit 0 denotes whether the VQA has a soundtrack or not.&lt;br /&gt;
* (1 = Has sound, 0 = No sound)&lt;br /&gt;
* bit 2 denotes whether this movie is an overlay on the background video.&lt;br /&gt;
* (1 = Yes, 0 = No)&lt;br /&gt;
Movie with bit 2 may play separately, but need apply VPTR\VPRZ chunks&lt;br /&gt;
to &amp;quot;Null&amp;quot; Vector Pointer Table (filled by zeros) for each frame.&lt;br /&gt;
&lt;br /&gt;
If NumFrames of background movie less than NumFrames of overlay movie&lt;br /&gt;
then background movie will be repeat, while will not finish overlay movie.&lt;br /&gt;
&lt;br /&gt;
Width is usually 320, Height is usually 156 or 200 although one movie in&lt;br /&gt;
* Red Alert is 640x400 in size (the start movie for the Win95 version).&lt;br /&gt;
&lt;br /&gt;
Each frame of a VQA movie is comprised of a series of blocks that are&lt;br /&gt;
BlockW pixels in width and BlockH pixels in height. Imagine the frame&lt;br /&gt;
is a mosaic with the blocks being 'pieces' that make up the frame.&lt;br /&gt;
&lt;br /&gt;
BlockW is the width and BlockH is the height of each screen block.&lt;br /&gt;
In VQAs version 2 (and perhaps 1) the blocks are usually 4x2.&lt;br /&gt;
&lt;br /&gt;
FrameRate is always 15 in C&amp;amp;C and RA and seems to be 10 in LoK III.&lt;br /&gt;
&lt;br /&gt;
CBParts denotes how many frames use the same lookup table. It also&lt;br /&gt;
implies how many parts the new block lookup table is split into.&lt;br /&gt;
In C&amp;amp;C and RA it is always 8. When CBParts is 0, and movie have&lt;br /&gt;
CBP? chunks, then need see CIND chunk.&lt;br /&gt;
&lt;br /&gt;
Colors indicates the maximum number of colors used by the VQA.&lt;br /&gt;
The HiColor VQAs have this set to 0, while the old movies have&lt;br /&gt;
256 or less in here.&lt;br /&gt;
&lt;br /&gt;
Offset X and Offset Y uses only when there is bit 2 in the flags.&lt;br /&gt;
Usually uses chunk CLIP too, where the width and height of the&lt;br /&gt;
background movie are specified.&lt;br /&gt;
&lt;br /&gt;
MaxVPT?Size tells you the size of the largest VPTZ or VPRZ chunk, in bytes.&lt;br /&gt;
&lt;br /&gt;
Freq is usually 22050 Hz. Note that version 1 movies can have this set &lt;br /&gt;
to 0 Hz. In that case, you should use 22050 Hz.&lt;br /&gt;
&lt;br /&gt;
Channels specifies the number of sound channels, i.e. is the sound&lt;br /&gt;
mono or stereo. Channels=1 -&amp;gt; sound is mono, Channels=2 -&amp;gt; stereo.&lt;br /&gt;
C&amp;amp;C and RA almost always use mono sound. &lt;br /&gt;
Version 1 can have this set to 0, but you should use 1 (mono sound).&lt;br /&gt;
The majority of Tiberian Sun movies use stereo sound instead.&lt;br /&gt;
&lt;br /&gt;
* Bits indicates whether the sound is 8 bit or 16 bit.&lt;br /&gt;
Bits=8 -&amp;gt; 8 bit sound, Bits=16 -&amp;gt; 16 bit sound (surprise! :). &lt;br /&gt;
The Legend of Kyrandia III: Malcolm's Revenge uses 8 bits where&lt;br /&gt;
C&amp;amp;C, RA, TS, Dune 2000, Lands of Lore III use 16 bits.&lt;br /&gt;
Note, again, that version 1 of the VQAs can have this set to 0 in&lt;br /&gt;
which case 8 bits are assumed.&lt;br /&gt;
&lt;br /&gt;
MaxCBFZSize tells you the size of the largest CBFZ chunk, in bytes.&lt;br /&gt;
&lt;br /&gt;
Following the chunk data are the sub-chunks.&lt;br /&gt;
&lt;br /&gt;
===FINF chunk (&amp;quot;Frame INFormation&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This chunk contains the positions (absolute from the start of the VQA)&lt;br /&gt;
of data for every frame.&lt;br /&gt;
That means that it points to the SND? chunk associated with that frame,&lt;br /&gt;
which is followed by a VQFR chunk containing frame's video data.&lt;br /&gt;
&lt;br /&gt;
The positions are given as LONGs which are in normal Intel byte order.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' Some frame positions are 0x40000000 too large. This is a flag&lt;br /&gt;
indicating those frames contain a new color palette. To get the&lt;br /&gt;
actual positions, you should subtract 0x40000000 from the values.&lt;br /&gt;
&lt;br /&gt;
'''NOTE #2:''' To get the actual position of the frame data you have to multiply&lt;br /&gt;
the value by 2. This is why some chunk IDs start with 0x00. Since&lt;br /&gt;
you multiply by 2, you can't get an odd chunk position so if the&lt;br /&gt;
chunk position would normally be odd, a 0x00 is inserted to make&lt;br /&gt;
it even.&lt;br /&gt;
&lt;br /&gt;
===SND? chunk (&amp;quot;SouND&amp;quot; ???)===&lt;br /&gt;
These chunks contain the sound data for the movie. The last byte of the ID&lt;br /&gt;
can be either '0', '1' or '2' so the actual IDs would be &amp;quot;SND0&amp;quot;, &amp;quot;SND1&amp;quot;&lt;br /&gt;
and &amp;quot;SND2&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
In VQAs version 1 (Legend of Kyrandia 3) there are ''NumFrames'' sound chunks.&lt;br /&gt;
Old 8 bit VQA movies of version 2 have ''NumFrames+1'' sound chunks.&lt;br /&gt;
Note, however, that Dune2000 movies, which are also version 2, but HiColor&lt;br /&gt;
have ''NumFrames'' sound chunks, instead.&lt;br /&gt;
Version 3 movies also have ''NumFrames'' sound chunks.&lt;br /&gt;
&lt;br /&gt;
In case of the old, 8 bit VQAs, the first chunk contains half a second&lt;br /&gt;
(ver. 2) or more (ver. 1) of the wave data, in all (?) the HiColor movies&lt;br /&gt;
the first chunk contains exactly the amount of sound required for one&lt;br /&gt;
frame of the movie. The downside is this requires a somewhat more advanced &lt;br /&gt;
buffering technique on the side of the player in order to allow smooth &lt;br /&gt;
playback.&lt;br /&gt;
&lt;br /&gt;
===SND0 chunk===&lt;br /&gt;
&lt;br /&gt;
This one contains the raw 8 or 16 bit PCM wave data. If the data is&lt;br /&gt;
8 bit, the sound is unsigned and if it is 16 bit, the samples are&lt;br /&gt;
signed.&lt;br /&gt;
&lt;br /&gt;
===SND1 chunk===&lt;br /&gt;
&lt;br /&gt;
It contains 8 bit sound compressed using Westwood's own&lt;br /&gt;
proprietary ADPCM algorithm. The chunk has a 4 byte header:&lt;br /&gt;
&lt;br /&gt;
  struct SND1Header&lt;br /&gt;
  {&lt;br /&gt;
   short OutSize;&lt;br /&gt;
   short Size;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
These values are needed for the decoding algoritm (see APPENDIX C).&lt;br /&gt;
The encoded samples follow immediately after the header.&lt;br /&gt;
It's important to know this algorithm produces UNSIGNED sound, unlike&lt;br /&gt;
the IMA ADPCM algorithm supplied here (see below). It is, however very&lt;br /&gt;
simple to adapt both algorithms to produce either signed or unsigned&lt;br /&gt;
sample output...&lt;br /&gt;
&lt;br /&gt;
===SND2 chunk===&lt;br /&gt;
&lt;br /&gt;
It contains the 16 bit sound data compressed using the IMA ADPCM&lt;br /&gt;
algorithm which compresses 16 bits into 4. That's why the SND2 chunks&lt;br /&gt;
are 4 times smaller than SND0 chunks and they are used almost all&lt;br /&gt;
the time. For the description of the algorithm, see later in the document.&lt;br /&gt;
  &lt;br /&gt;
Different VQA versions have different stereo sample layout. In case&lt;br /&gt;
of Tiberian Sun stereo sound is encoded the same way as mono except the&lt;br /&gt;
SND2 chunk is split into two halfs. The first half contains the left channel&lt;br /&gt;
sound and the second half contains the right channel. The layout of&lt;br /&gt;
nibbles is as follows: LL LL LL LL LL ... RR RR RR RR RR.&lt;br /&gt;
Old movies (C&amp;amp;C, RA) use a different layout. Here, the nibbles are packed&lt;br /&gt;
together like this: LL RR LL RR LL RR ... This means that two left channel&lt;br /&gt;
samples are packed into one byte and then two right channel samples are&lt;br /&gt;
packed into another.&lt;br /&gt;
&lt;br /&gt;
Naturally, the size of a stereo SND2 chunk is exactly twice as big as&lt;br /&gt;
a mono SND2.&lt;br /&gt;
&lt;br /&gt;
It is important to note that, when decoding, you have to keep separate &lt;br /&gt;
values of ''Index'' and ''Cur_Sample'' for each channel (see APPENDIX B).&lt;br /&gt;
&lt;br /&gt;
===VQFR chunk (&amp;quot;Vector Quantized FRame&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
A chunk that includes many nested sub-chunks which contain video data.&lt;br /&gt;
It doesn't contain any data itself so the sub-chunks follow immediately&lt;br /&gt;
after the VQFR chunk header.&lt;br /&gt;
All following sub-chunks are nested inside a VQFR chunk. They can all&lt;br /&gt;
contain '0' or 'Z' as the last byte of their ID.&lt;br /&gt;
&lt;br /&gt;
* If the last byte is '0' it means that the chunk data is uncompressed.&lt;br /&gt;
* If the last byte is 'Z' it means that the data is compressed using&lt;br /&gt;
Format80 compression. You can find its description in APPENDIX A.&lt;br /&gt;
&lt;br /&gt;
===CBF? chunk (&amp;quot;CodeBook, Full&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
Lookup table containing the screen block data as an array&lt;br /&gt;
of elements that each are BlockW*BlockH bytes long. It is always located&lt;br /&gt;
in the data for the first frame. In Vector Quantization terminology&lt;br /&gt;
these tables are called CODEBOOKS.&lt;br /&gt;
&lt;br /&gt;
There can be max. 0x0f00 of these elements (blocks) at any one time in &lt;br /&gt;
normal VQAs and 0xff00 in the hi-res VQAs (Red Alert 95 start movie) although&lt;br /&gt;
I seriously doubt that so many blocks (0xff00 = 65280 blocks) would&lt;br /&gt;
ever be used.&lt;br /&gt;
&lt;br /&gt;
The uncompressed version of these chunks (&amp;quot;CBF0&amp;quot;) is used mainly in&lt;br /&gt;
the original Command &amp;amp; Conquer, while the compressed version (&amp;quot;CBFZ&amp;quot;)&lt;br /&gt;
is used in C&amp;amp;C: Red Alert.&lt;br /&gt;
&lt;br /&gt;
===CBP? chunk (&amp;quot;CodeBook Part&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
Like CBF?, but it contains a part of the lookup table, so to get the new&lt;br /&gt;
complete table you need to append CBParts of these in frame order.&lt;br /&gt;
Once you get the complete table and display the current frame, replace&lt;br /&gt;
the old table with the new one.&lt;br /&gt;
As in CBF? chunk, the uncompressed chunks are used in C&amp;amp;C and the compressed&lt;br /&gt;
chunks are used in Red Alert.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' If the chunks are CBFZ, first you need to append CBParts of them and&lt;br /&gt;
then decompress the data, NOT decompress each chunk individually.&lt;br /&gt;
&lt;br /&gt;
===CPL? chunk (&amp;quot;Color PaLette&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
The simplest one of all... Contains a palette for the VQA. It is an&lt;br /&gt;
array of red, green and blue values (in that order, all have a size of&lt;br /&gt;
1 byte). Seems that the values range from 0-255, but you should mask out&lt;br /&gt;
the bits 6 and 7 to get the correct palette (VGA hardware uses only&lt;br /&gt;
bits 0..5 anyway).&lt;br /&gt;
&lt;br /&gt;
===VPT? chunk: (&amp;quot;Vector Pointer Table&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This chunk contains the indexes into the block lookup table which contains&lt;br /&gt;
the data to display the frame.&lt;br /&gt;
The image blocks are called VECTORS in Vector Quantization terminology.&lt;br /&gt;
&lt;br /&gt;
These chunks are always compressed, but I guess the uncompressed ones&lt;br /&gt;
can also be used (although this would lower the overall compression achieved).&lt;br /&gt;
&lt;br /&gt;
The size of this index table is ''(Width/BlockW)*(Height/BlockH)*2 bytes''.&lt;br /&gt;
&lt;br /&gt;
Now, there is a catch: version 2 VQAs use a different layout of the&lt;br /&gt;
index table than version 1 VQAs. I will first describe the version&lt;br /&gt;
2 table format, as it's more common.&lt;br /&gt;
&lt;br /&gt;
====VERSION 2 INDEX TABLE LAYOUT====&lt;br /&gt;
The index table is an array of CHARs and is split into 2 parts - the top&lt;br /&gt;
half and the bottom half.&lt;br /&gt;
&lt;br /&gt;
Now, if you want to diplay the block at coordinates (in block units),&lt;br /&gt;
say (bx,by) you should read two bytes from the table, one from the top&lt;br /&gt;
and one from the bottom half:&lt;br /&gt;
&lt;br /&gt;
  LoVal=Table[by*(Width/BlockW)+bx]&lt;br /&gt;
  HiVal=Table[(Width/BlockW)*(Height/BlockH)+by*(Width/BlockW)+bx]&lt;br /&gt;
&lt;br /&gt;
If HiVal=0x0f (0xff for the start movie of Red Alert 95) you should&lt;br /&gt;
simply fill the block with color LoVal, otherwise you should copy&lt;br /&gt;
the block with index number HiVal*256+LoVal from the lookup table.&lt;br /&gt;
&lt;br /&gt;
Do that for every block on the screen (remember, there are Width/BlockW&lt;br /&gt;
blocks in the horizontal direction and Height/BlockH blocks in the vertical&lt;br /&gt;
direction) and you've decoded your first frame!&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' I was unable to find an entry in the VQA header which determines&lt;br /&gt;
whether 0x0f or 0xff is used in HiVal to signal that the block&lt;br /&gt;
is of uniform color. I assume that the Wy entry of the header&lt;br /&gt;
implies this: If BlockW=2 -&amp;gt; 0x0f is used, if BlockH=4 -&amp;gt; 0xff is used.&lt;br /&gt;
&lt;br /&gt;
====VERSION 1 INDEX TABLE LAYOUT====&lt;br /&gt;
Here, the index table is simply an array of SHORTs written in normal, Intel&lt;br /&gt;
byte order.&lt;br /&gt;
&lt;br /&gt;
The LoVal and HiVal are given as:&lt;br /&gt;
&lt;br /&gt;
  LoVal=Table[(by*(Width/BlockW)+bx)*2]&lt;br /&gt;
  HiVal=Table[(by*(Width/BlockW)+bx)*2+1]&lt;br /&gt;
&lt;br /&gt;
If HiVal=0xff, the block is of uniform color which is (255-LoVal).&lt;br /&gt;
Otherwise, write the block with index number (HiVal*256+LoVal)/8.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Appendix A===&lt;br /&gt;
&lt;br /&gt;
FORMAT80 COMPRESSION METHOD&lt;br /&gt;
by Vladan Bato (bat22@geocities.com)&lt;br /&gt;
&lt;br /&gt;
There are several different commands, with different sizes : from 1 to 5 bytes.&lt;br /&gt;
The positions mentioned below always refer to the destination buffer (i.e.&lt;br /&gt;
the uncompressed image). The relative positions are relative to the current&lt;br /&gt;
position in the destination buffer, which is one byte beyond the last written&lt;br /&gt;
byte.&lt;br /&gt;
&lt;br /&gt;
I will give some sample code at the end.&lt;br /&gt;
&lt;br /&gt;
(1) 1 byte&lt;br /&gt;
      +---+---+---+---+---+---+---+---+&lt;br /&gt;
      | 1 | 0 |   |   |   |   |   |   |&lt;br /&gt;
      +---+---+---+---+---+---+---+---+&lt;br /&gt;
              \_______________________/&lt;br /&gt;
                         |&lt;br /&gt;
                       Count&lt;br /&gt;
&lt;br /&gt;
      This one means : copy next Count bytes as is from Source to Dest.&lt;br /&gt;
&lt;br /&gt;
(2) 2 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
  | 0 |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
      \___________/\__________________________________________________/&lt;br /&gt;
            |                             |&lt;br /&gt;
         Count-3                    Relative Pos.&lt;br /&gt;
&lt;br /&gt;
  This means copy Count bytes from Dest at Current Pos.-Rel. Pos. to&lt;br /&gt;
  Current position.&lt;br /&gt;
  Note that you have to add 3 to the number you find in the bits 4-6 of the&lt;br /&gt;
  first byte to obtain the Count.&lt;br /&gt;
  Note that if the Rel. Pos. is 1, that means repeat Count times the previous&lt;br /&gt;
  byte.&lt;br /&gt;
&lt;br /&gt;
(3) 3 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---------------+---------------+&lt;br /&gt;
  | 1 | 1 |   |   |   |   |   |   |   |               |               |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---------------+---------------+&lt;br /&gt;
          \_______________________/                  Pos&lt;br /&gt;
                     |&lt;br /&gt;
                 Count-3&lt;br /&gt;
&lt;br /&gt;
  Copy Count bytes from Pos, where Pos is absolute from the start of the&lt;br /&gt;
  destination buffer. (Pos is a word, that means that the images can't be&lt;br /&gt;
  larger than 64K)&lt;br /&gt;
&lt;br /&gt;
(4) 4 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+&lt;br /&gt;
  | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |   |       |       |  |       |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+&lt;br /&gt;
                                            Count          Color&lt;br /&gt;
&lt;br /&gt;
  Write Color Count times.&lt;br /&gt;
  (Count is a word, color is a byte)&lt;br /&gt;
&lt;br /&gt;
(5) 5 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+&lt;br /&gt;
  | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |   |       |       |  |       |       |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+&lt;br /&gt;
                                            Count               Pos&lt;br /&gt;
&lt;br /&gt;
  Copy Count bytes from Dest. starting at Pos. Pos is absolute from the start&lt;br /&gt;
  of the Destination buffer.&lt;br /&gt;
  Both Count and Pos are words.&lt;br /&gt;
&lt;br /&gt;
These are all the commands I found out. Maybe there are other ones, but I&lt;br /&gt;
haven't seen them yet.&lt;br /&gt;
&lt;br /&gt;
All the images end with a 80h command.&lt;br /&gt;
&lt;br /&gt;
To make things more clearer here's a piece of code that will uncompress the&lt;br /&gt;
image.&lt;br /&gt;
&lt;br /&gt;
  DP = destination pointer&lt;br /&gt;
  SP = source pointer&lt;br /&gt;
  Source and Dest are the two buffers&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  SP:=0;&lt;br /&gt;
  DP:=0;&lt;br /&gt;
  repeat&lt;br /&gt;
    Com:=Source[SP];&lt;br /&gt;
    inc(SP);&lt;br /&gt;
    b7:=Com shr 7;  {b7 is bit 7 of Com}&lt;br /&gt;
    case b7 of&lt;br /&gt;
      0 : begin  {copy command (2)}&lt;br /&gt;
            {Count is bits 4-6 + 3}&lt;br /&gt;
            Count:=(Com and $7F) shr 4 + 3;&lt;br /&gt;
            {Position is bits 0-3, with bits 0-7 of next byte}&lt;br /&gt;
            Posit:=(Com and $0F) shl 8+Source[SP];&lt;br /&gt;
            Inc(SP);&lt;br /&gt;
            {Starting pos=Cur pos. - calculated value}&lt;br /&gt;
            Posit:=DP-Posit;&lt;br /&gt;
            for i:=Posit to Posit+Count-1 do&lt;br /&gt;
            begin&lt;br /&gt;
              Dest[DP]:=Dest[i];&lt;br /&gt;
              Inc(DP);&lt;br /&gt;
            end;&lt;br /&gt;
          end;&lt;br /&gt;
      1 : begin&lt;br /&gt;
            {Check bit 6 of Com}&lt;br /&gt;
            b6:=(Com and $40) shr 6;&lt;br /&gt;
            case b6 of&lt;br /&gt;
              0 : begin  {Copy as is command (1)}&lt;br /&gt;
                    Count:=Com and $3F;  {mask 2 topmost bits}&lt;br /&gt;
                    if Count=0 then break; {EOF marker}&lt;br /&gt;
                    for i:=1 to Count do&lt;br /&gt;
                    begin&lt;br /&gt;
                      Dest[DP]:=Source[SP];&lt;br /&gt;
                      Inc(DP);&lt;br /&gt;
                      Inc(SP);&lt;br /&gt;
                    end;&lt;br /&gt;
                  end;&lt;br /&gt;
              1 : begin  {large copy, very large copy and fill commands}&lt;br /&gt;
                    {Count = (bits 0-5 of Com) +3}&lt;br /&gt;
                    {if Com=FEh then fill, if Com=FFh then very large copy}&lt;br /&gt;
                    Count:=Com and $3F;&lt;br /&gt;
                    if Count&amp;lt;$3E then {large copy (3)}&lt;br /&gt;
                    begin&lt;br /&gt;
                      Inc(Count,3);&lt;br /&gt;
                      {Next word = pos. from start of image}&lt;br /&gt;
                      Posit:=Word(Source[SP]);&lt;br /&gt;
                      Inc(SP,2);&lt;br /&gt;
                      for i:=Posit to Posit+Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=Dest[i];&lt;br /&gt;
                        Inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end&lt;br /&gt;
                    else if Count=$3F then   {very large copy (5)}&lt;br /&gt;
                    begin&lt;br /&gt;
                      {next 2 words are Count and Pos}&lt;br /&gt;
                      Count:=Word(Source[SP]);&lt;br /&gt;
                      Posit:=Word(Source[SP+2]);&lt;br /&gt;
                      Inc(SP,4);&lt;br /&gt;
                      for i:=Posit to Posit+Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=Dest[i];&lt;br /&gt;
                        Inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end else&lt;br /&gt;
                    begin   {Count=$3E, fill (4)}&lt;br /&gt;
                      {Next word is count, the byte after is color}&lt;br /&gt;
                      Count:=Word(Source[SP]);&lt;br /&gt;
                      Inc(SP,2);&lt;br /&gt;
                      b:=Source[SP];&lt;br /&gt;
                      Inc(SP);&lt;br /&gt;
                      for i:=0 to Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=b;&lt;br /&gt;
                        inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end;&lt;br /&gt;
                  end;&lt;br /&gt;
            end;&lt;br /&gt;
          end;&lt;br /&gt;
    end;&lt;br /&gt;
  until false;&lt;br /&gt;
&lt;br /&gt;
Note that you won't be able to compile this code, because the typecasting&lt;br /&gt;
won't work. (But I'm sure you'll be able to fix it).&lt;br /&gt;
&lt;br /&gt;
===Appendix B===&lt;br /&gt;
IMA ADPCM DECOMPRESSION&lt;br /&gt;
by Vladan Bato  (bat22@geocities.com)&lt;br /&gt;
http://www.geocities.com/SiliconValley/8682&lt;br /&gt;
&lt;br /&gt;
Note that the current sample value and index into the Step Table should&lt;br /&gt;
be initialized to 0 at the start and are maintained across the chunks&lt;br /&gt;
(see below).&lt;br /&gt;
&lt;br /&gt;
====IMA-ADPCM DECOMPRESSION ====&lt;br /&gt;
&lt;br /&gt;
It is the exact opposite of the above. It receives 4-bit codes in input&lt;br /&gt;
and produce 16-bit samples in output.&lt;br /&gt;
&lt;br /&gt;
Again you have to mantain an Index into the Step Table an the current&lt;br /&gt;
sample value.&lt;br /&gt;
&lt;br /&gt;
The tables used are the same as for compression.&lt;br /&gt;
&lt;br /&gt;
Here's the code :&lt;br /&gt;
&lt;br /&gt;
  Index:=0;&lt;br /&gt;
  Cur_Sample:=0;&lt;br /&gt;
&lt;br /&gt;
  while there_is_more_data do&lt;br /&gt;
  begin&lt;br /&gt;
    Code:=Get_Next_Code;&lt;br /&gt;
&lt;br /&gt;
    if (Code and $8) &amp;lt;&amp;gt; 0 then Sb:=1 else Sb:=0;&lt;br /&gt;
    Code:=Code and $7;&lt;br /&gt;
    {Separate the sign bit from the rest}&lt;br /&gt;
&lt;br /&gt;
    Delta:=(Step_Table[Index]*Code) div 4 + Step_Table[Index] div 8;&lt;br /&gt;
    {The last one is to minimize errors}&lt;br /&gt;
&lt;br /&gt;
    if Sb=1 then Delta:=-Delta;&lt;br /&gt;
&lt;br /&gt;
    Cur_Sample:=Cur_Sample+Delta;&lt;br /&gt;
    if Cur_Sample&amp;gt;32767 then Cur_Sample:=32767&lt;br /&gt;
    else if Cur_Sample&amp;lt;-32768 then Cur_Sample:=-32768;&lt;br /&gt;
&lt;br /&gt;
    Output_Sample(Cur_Sample);&lt;br /&gt;
&lt;br /&gt;
    Index:=Index+Index_Adjust[Code];&lt;br /&gt;
    if Index&amp;lt;0 then Index:=0;&lt;br /&gt;
    if Index&amp;gt;88 the Index:=88;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
Again, this can be done more efficiently (no need for multiplication).&lt;br /&gt;
&lt;br /&gt;
The ''Get_Next_Code'' function should return the next 4-bit code. It must&lt;br /&gt;
extract it from the input buffer (note that two 4-bit codes are stored&lt;br /&gt;
in the same byte, the first one in the lower bits).&lt;br /&gt;
&lt;br /&gt;
The Output_Sample function should write the signed 16-bit sample to the&lt;br /&gt;
output buffer.&lt;br /&gt;
&lt;br /&gt;
=== Appendix A : THE INDEX ADJUSTMENT TABLE ===&lt;br /&gt;
&lt;br /&gt;
  Index_Adjust : array [0..7] of integer = (-1,-1,-1,-1,2,4,6,8);&lt;br /&gt;
&lt;br /&gt;
=== Appendix B : THE STEP TABLE ===&lt;br /&gt;
&lt;br /&gt;
  Steps_Table : array [0..88] of integer =(&lt;br /&gt;
        7,     8,     9,     10,    11,    12,     13,    14,    16,&lt;br /&gt;
        17,    19,    21,    23,    25,    28,     31,    34,    37,&lt;br /&gt;
        41,    45,    50,    55,    60,    66,     73,    80,    88,&lt;br /&gt;
        97,    107,   118,   130,   143,   157,    173,   190,   209,&lt;br /&gt;
        230,   253,   279,   307,   337,   371,    408,   449,   494,&lt;br /&gt;
        544,   598,   658,   724,   796,   876,    963,   1060,  1166,&lt;br /&gt;
        1282,  1411,  1552,  1707,  1878,  2066,   2272,  2499,  2749,&lt;br /&gt;
        3024,  3327,  3660,  4026,  4428,  4871,   5358,  5894,  6484,&lt;br /&gt;
        7132,  7845,  8630,  9493,  10442, 11487,  12635, 13899, 15289,&lt;br /&gt;
        16818, 18500, 20350, 22385, 24623, 27086,  29794, 32767 );&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Appendix C===&lt;br /&gt;
WESTWOOD STUDIOS' ADPCM DECOMPRESSION&lt;br /&gt;
by Asatur V. Nazarian (samael@avn.mccme.ru)&lt;br /&gt;
&lt;br /&gt;
==== WS ADPCM Decompression Algorithm ====&lt;br /&gt;
&lt;br /&gt;
Each SND1 chunk may be decompressed independently of others. This lets you&lt;br /&gt;
implement seeking/skipping for WS ADPCM sounds (unlike IMA ADPCM ones).&lt;br /&gt;
But during the decompression of the given chunk a variable (''CurSample'') should&lt;br /&gt;
be maintained for this whole chunk:&lt;br /&gt;
&lt;br /&gt;
 SHORT CurSample;&lt;br /&gt;
 BYTE  InputBuffer[InputBufferSize]; // input buffer containing the whole chunk&lt;br /&gt;
 WORD  wSize, wOutSize; // Size and OutSize values from this chunk's header&lt;br /&gt;
 BYTE  code;&lt;br /&gt;
 CHAR  count; // this is a signed char!&lt;br /&gt;
 WORD  i; // index into InputBuffer&lt;br /&gt;
 WORD  input; // shifted input&lt;br /&gt;
 &lt;br /&gt;
 if (wSize==wOutSize) // such chunks are NOT compressed&lt;br /&gt;
 {&lt;br /&gt;
  for (i=0;i&amp;lt;wOutSize;i++)&lt;br /&gt;
      Output(InputBuffer[i]); // send to output stream&lt;br /&gt;
  return; // chunk is done!&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // otherwise we need to decompress chunk&lt;br /&gt;
 &lt;br /&gt;
 CurSample=0x80; // unsigned 8-bit&lt;br /&gt;
 i=0;&lt;br /&gt;
 &lt;br /&gt;
 // note that wOutSize value is crucial for decompression!&lt;br /&gt;
 &lt;br /&gt;
 while (wOutSize&amp;gt;0) // until wOutSize is exhausted!&lt;br /&gt;
 {&lt;br /&gt;
  input=InputBuffer[i++];&lt;br /&gt;
  input&amp;lt;&amp;lt;=2;&lt;br /&gt;
  code=HIBYTE(input);&lt;br /&gt;
  count=LOBYTE(input)&amp;gt;&amp;gt;2;&lt;br /&gt;
  switch (code) // parse code&lt;br /&gt;
  {&lt;br /&gt;
    case 2: // no compression...&lt;br /&gt;
	 if (count &amp;amp; 0x20)&lt;br /&gt;
	 {&lt;br /&gt;
	   count&amp;lt;&amp;lt;=3;		// here it's significant that (count) is signed:&lt;br /&gt;
	   CurSample+=count&amp;gt;&amp;gt;3; // the sign bit will be copied by these shifts!&lt;br /&gt;
&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize--; // one byte added to output&lt;br /&gt;
	 }&lt;br /&gt;
	 else // copy (count+1) bytes from input to output&lt;br /&gt;
	 {&lt;br /&gt;
	   for (count++;count&amp;gt;0;count--,wOutSize--,i++)&lt;br /&gt;
	       Output(InputBuffer[i]);&lt;br /&gt;
	   CurSample=InputBuffer[i-1]; // set (CurSample) to the last byte sent to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    case 1: // ADPCM 8-bit -&amp;gt; 4-bit&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--) // decode (count+1) bytes&lt;br /&gt;
	 {&lt;br /&gt;
	   code=InputBuffer[i++];&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable4bit[(code &amp;amp; 0x0F)]; // lower nibble&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable4bit[(code &amp;gt;&amp;gt; 4)]; // higher nibble&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize-=2; // two bytes added to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    case 0: // ADPCM 8-bit -&amp;gt; 2-bit&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--) // decode (count+1) bytes&lt;br /&gt;
	 {&lt;br /&gt;
	   code=InputBuffer[i++];&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[(code &amp;amp; 0x03)]; // lower 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;2) &amp;amp; 0x03)]; // lower middle 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;4) &amp;amp; 0x03)]; // higher middle 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;6) &amp;amp; 0x03)]; // higher 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize-=4; // 4 bytes sent to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    default: // just copy (CurSample) (count+1) times to output&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--,wOutSize--)&lt;br /&gt;
	     Output((BYTE)CurSample);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
HIBYTE and LOBYTE are just higher and lower bytes of WORD:&lt;br /&gt;
 #define HIBYTE(word) ((word) &amp;gt;&amp;gt; 8)&lt;br /&gt;
 #define LOBYTE(word) ((word) &amp;amp; 0xFF)&lt;br /&gt;
Note that depending on your compiler you may need to use additional byte&lt;br /&gt;
separation in these defines, e.g. (((byte) &amp;gt;&amp;gt; 8) &amp;amp; 0xFF). The same holds for&lt;br /&gt;
4-bit and 2-bit nibble separation in the code above.&lt;br /&gt;
&lt;br /&gt;
''WSTable4bit'' and ''WSTable2bit'' are the delta tables given in the next section.&lt;br /&gt;
&lt;br /&gt;
Output() is just a placeholder for any action you would like to perform for&lt;br /&gt;
decompressed sample value.&lt;br /&gt;
&lt;br /&gt;
Clip8BitSample is quite evident:&lt;br /&gt;
&lt;br /&gt;
 SHORT Clip8BitSample(SHORT sample)&lt;br /&gt;
 {&lt;br /&gt;
  if (sample&amp;gt;255)&lt;br /&gt;
     return 255;&lt;br /&gt;
  else if (sample&amp;lt;0)&lt;br /&gt;
     return 0;&lt;br /&gt;
  else&lt;br /&gt;
     return sample;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This algorithm is ONLY for mono 8-bit unsigned sound, as I've never seen any&lt;br /&gt;
other sound format used with WS ADPCM compression.&lt;br /&gt;
&lt;br /&gt;
Of course, the decompression routine described above may be greatly&lt;br /&gt;
optimized.&lt;br /&gt;
&lt;br /&gt;
==== WS ADPCM Tables ====&lt;br /&gt;
&lt;br /&gt;
 CHAR WSTable2bit[]=&lt;br /&gt;
 {&lt;br /&gt;
    -2,&lt;br /&gt;
    -1,&lt;br /&gt;
     0,&lt;br /&gt;
     1&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 CHAR WSTable4bit[]=&lt;br /&gt;
 {&lt;br /&gt;
    -9, -8, -6, -5, -4, -3, -2, -1,&lt;br /&gt;
     0,  1,  2,  3,  4,  5,  6,  8&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
==HC_VQA.txt==&lt;br /&gt;
by Gordan Ugarkovic (ugordan@yahoo.com)&lt;br /&gt;
http://members.xoom.com/ugordan&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This document describes how to view Westwood's HiColor VQA movies. These&lt;br /&gt;
are version 3 movies, but there are at least some version 2 VQAs that are&lt;br /&gt;
in this format. I will not describe the whole VQA layout here, I will&lt;br /&gt;
just explain how to display the VIDEO stream of the VQA, that is, how&lt;br /&gt;
to decompress the CBFZ and VPTR/VPRZ chunks.&lt;br /&gt;
&lt;br /&gt;
First a little warning: I'm not sure which flag denotes the VQA is 8 bit&lt;br /&gt;
or 15 bit. I'm pretty convinced it's either bit 4 (0x10) or bit 2 (0x04)&lt;br /&gt;
of the Flags entry (see my VQA_INFO.TXT) in the header. Another way would &lt;br /&gt;
be to check the Colors entry in the header, if it is 0 it could imply &lt;br /&gt;
a HiColor movie.&lt;br /&gt;
&lt;br /&gt;
There's a major difference between the old (8 bit, 256 color) and the new,&lt;br /&gt;
HiColor VQAs. Lookup tables are no longer split up into several CBP?&lt;br /&gt;
chunks, instead they come in one piece (a CBFZ chunk). Two lookup tables&lt;br /&gt;
can now be many frames apart, not just 8 (as usual). This is indicated&lt;br /&gt;
by the CBParts entry of the header (see VQA_INFO.TXT), which is set to 0.&lt;br /&gt;
Subsequent frames use the last lookup table loaded, of course.&lt;br /&gt;
&lt;br /&gt;
Another thing: It appears the first CBFZ chunk comes inside the VQFR chunk&lt;br /&gt;
but the other ones seem to be located inside their own chunks,&lt;br /&gt;
called VQFL, which are followed by the usual VQFR chunks (containing&lt;br /&gt;
VPTR/VPRZ chunks).&lt;br /&gt;
&lt;br /&gt;
Also, the movies are 15 bit, NOT 16 bit. There is a difference because&lt;br /&gt;
in 16 bit color depth there are 6 bits for the green channel, but&lt;br /&gt;
the VQAs use 5.&lt;br /&gt;
&lt;br /&gt;
=== The CBFZ chunks===&lt;br /&gt;
&lt;br /&gt;
These are a bit modified since the 8 bit VQAs. If the first byte of the&lt;br /&gt;
chunk is not NULL (0x00), it means the chunk is compressed using the&lt;br /&gt;
standard Format80 algorithm (see Vladan Bato's text on C&amp;amp;C file formats),&lt;br /&gt;
starting from that byte. If the first byte is NULL, the chunk is compressed&lt;br /&gt;
using a modified version of Format80 (see below), starting from the next&lt;br /&gt;
byte of the chunk. The original Format80 algorithm is used when the&lt;br /&gt;
amount of data to be compressed is less than 64 KB, otherwise the 'new'&lt;br /&gt;
algorithm is used.&lt;br /&gt;
&lt;br /&gt;
When decompressed properly, a CBFZ chunk expands into 15 bit pixels packed&lt;br /&gt;
as shorts in normal Intel byte order. The red, green and blue values are&lt;br /&gt;
packed like this:&lt;br /&gt;
&lt;br /&gt;
  15      bit      0&lt;br /&gt;
   arrrrrgg gggbbbbb&lt;br /&gt;
   HI byte  LO byte&lt;br /&gt;
&lt;br /&gt;
The r,g,b values make up a pixel and they can range from 0-31.&lt;br /&gt;
As in the old CBFZ chunks, these pixels make up the block lookup table&lt;br /&gt;
(also called a codebook). The a (alpha) value is used in Blade Runner in&lt;br /&gt;
overlay videos to indicate a transparent pixel.&lt;br /&gt;
&lt;br /&gt;
=== The VPTR chunks===&lt;br /&gt;
&lt;br /&gt;
These chunks use some sort of differential, run-length algorithm that&lt;br /&gt;
only records changes from the previous frame. Therefore, the previous&lt;br /&gt;
frame bitmap must be maintained throughout all the frames (you could&lt;br /&gt;
just draw the blocks that changed, though). This makes dropping frames&lt;br /&gt;
(in case of bad performance) impossible.&lt;br /&gt;
&lt;br /&gt;
When decoding, you take a short int (Intel) from the chunk and examine &lt;br /&gt;
its 3 most significant bits (bits 15,14,13). These bits make up a &lt;br /&gt;
code prefix that determines which action is to be done.&lt;br /&gt;
&lt;br /&gt;
Here's a list of the prefixes I encountered and their description&lt;br /&gt;
(Val is the short int value):&lt;br /&gt;
&lt;br /&gt;
  BITS - MEANING&lt;br /&gt;
&lt;br /&gt;
   000 - Skip Count blocks. Count is (Val &amp;amp; 0x1fff).&lt;br /&gt;
&lt;br /&gt;
   001 - Write block number (Val &amp;amp; 0xff) Count times.&lt;br /&gt;
         Count is (((Val/256) &amp;amp; 0x1f)+1)*2. Note that this can only&lt;br /&gt;
         index the first 256 blocks.&lt;br /&gt;
&lt;br /&gt;
   010 - Write block number (Val &amp;amp; 0xff) and then write Count blocks&lt;br /&gt;
         getting their indexes by reading next Count bytes from&lt;br /&gt;
         the VPTR chunk. Count is (((Val/256) &amp;amp; 0x1f)+1)*2.&lt;br /&gt;
         Again, the block numbers range from 0-255.&lt;br /&gt;
&lt;br /&gt;
   011 - Write block (Val &amp;amp; 0x1fff).&lt;br /&gt;
&lt;br /&gt;
   100 - Same as 011 but skip pixels with alpha bit set.&lt;br /&gt;
&lt;br /&gt;
   101 - Write block (Val &amp;amp; 0x1fff) Count times. Count is the next&lt;br /&gt;
         byte from the VPTR chunk.&lt;br /&gt;
&lt;br /&gt;
   110 - Same as 101 but skip pixels with alpha bit set.&lt;br /&gt;
&lt;br /&gt;
After this, you take the next short int and repeat the above process.&lt;br /&gt;
&lt;br /&gt;
Prefixes 100 and 110 are used in Blade Runner for overlay videos.&lt;br /&gt;
&lt;br /&gt;
Every row of blocks is processed individually of others.&lt;br /&gt;
When you encounter the end of a row, proceed to the next row&lt;br /&gt;
(blocks are processed left to right, top to down).&lt;br /&gt;
Repeat this process until all blocks in the frame are covered and&lt;br /&gt;
that's it!&lt;br /&gt;
&lt;br /&gt;
Note that the above implies an absolute maximum of 8192 blocks (0x1fff+1). &lt;br /&gt;
&lt;br /&gt;
As for the VPRZ chunks, these are just VPTR chunks compressed with&lt;br /&gt;
the standard Format80 algorithm.&lt;br /&gt;
&lt;br /&gt;
=== The modified Format80 scheme ===&lt;br /&gt;
&lt;br /&gt;
This is really only a small modification of the basic algorithm.&lt;br /&gt;
The only commands that are modified are commands (3) and (5)&lt;br /&gt;
See Vladan's text). Instead of using offsets ABSOLUTE from&lt;br /&gt;
the start of the destination buffer, offsets RELATIVE to the&lt;br /&gt;
current destination pointer are used. If you ask me, I don't see&lt;br /&gt;
why this approach wasn't used in the first place as it would&lt;br /&gt;
suffer no disadvantage with the old files and it would be much&lt;br /&gt;
easier to compress even larger amounts of data. The guys at WW&lt;br /&gt;
were just careless, I guess... :-)&lt;br /&gt;
&lt;br /&gt;
Anyway, in Vladan's algorithm, there is a line in&lt;br /&gt;
command (3) that says:&lt;br /&gt;
    Posit:=Word(Source[SP]);&lt;br /&gt;
it should say:&lt;br /&gt;
    Posit:=DP-Word(Source[SP]);&lt;br /&gt;
&lt;br /&gt;
Likewise, for command (5):&lt;br /&gt;
    Posit:=Word(Source[SP+2]);&lt;br /&gt;
it should be:&lt;br /&gt;
    Posit:=DP-Word(Source[SP+2]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Games Using VQA ==&lt;br /&gt;
&lt;br /&gt;
=== Version 1 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/win3x/legend-of-kyrandia-malcolms-revenge The Legend of Kyrandia: Malcolm's Revenge]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/monopoly Monopoly] (Version 1???)&lt;br /&gt;
&lt;br /&gt;
=== Versions 2 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/dos/command-conquer Command &amp;amp; Conquer]&lt;br /&gt;
* [http://www.mobygames.com/game/dos/command-conquer-red-alert Command &amp;amp; Conquer: Red Alert]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/command-conquer-sole-survivor Command &amp;amp; Conquer: Sole Survivor]&lt;br /&gt;
* [http://www.mobygames.com/game/lands-of-lore-guardians-of-destiny Lands of Lore: Guardians of Destiny]&lt;br /&gt;
&lt;br /&gt;
=== Versions 3 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/windows/blade-runner Blade Runner]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/command-conquer-tiberian-sun Command &amp;amp; Conquer: Tiberian Sun]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/dune-2000 Dune 2000]&lt;br /&gt;
* [http://www.mobygames.com/game/lands-of-lore-iii Lands of Lore III]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/nox Nox]&lt;br /&gt;
&lt;br /&gt;
==Other Documentation==&lt;br /&gt;
http://www.gamers.org/pub/idgames2/planetquake/planetcnc/cncdz/ has lots of data about this format. See vqa_overview.zip and vqafilesguild.zip. Also there is a decoder (vqatoavi) that decodes the dune2000 sample. &lt;br /&gt;
&lt;br /&gt;
[[Category:Game Formats]]&lt;br /&gt;
[[Category:Video Codecs]]&lt;br /&gt;
[[Category:Incomplete Video Codecs]]&lt;/div&gt;</summary>
		<author><name>Andrew Dream</name></author>
	</entry>
	<entry>
		<id>https://wiki.multimedia.cx/index.php?title=VQA&amp;diff=16093</id>
		<title>VQA</title>
		<link rel="alternate" type="text/html" href="https://wiki.multimedia.cx/index.php?title=VQA&amp;diff=16093"/>
		<updated>2025-11-25T11:37:14Z</updated>

		<summary type="html">&lt;p&gt;Andrew Dream: /* VQHD chunk (&amp;quot;VQa HeaDer&amp;quot; ???) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Extension: vqa&lt;br /&gt;
* Company: [[Westwood Studios]]&lt;br /&gt;
* Samples: [http://samples.mplayerhq.hu/game-formats/vqa/ http://samples.mplayerhq.hu/game-formats/vqa/]&lt;br /&gt;
* Samples: {{DexvertSamples|video/westwoodStudiosVQA}}&lt;br /&gt;
&lt;br /&gt;
VQA stands for Vector Quantized Animation and is a FMV format used in a number of computer games by Westwood Studios.&lt;br /&gt;
&lt;br /&gt;
== Technical Description ==&lt;br /&gt;
&lt;br /&gt;
'''TODO: import and combine Gordan Ugarkovic's documents from [http://multimedia.cx/VQA_INFO.TXT http://multimedia.cx/VQA_INFO.TXT] and [http://multimedia.cx/HC-VQA.TXT http://multimedia.cx/HC-VQA.TXT].&lt;br /&gt;
==VQA_INFO.txt==&lt;br /&gt;
&lt;br /&gt;
===NOTE #1=== &lt;br /&gt;
This document only applies to VQA files in the original C&amp;amp;C and C&amp;amp;C: Red Alert (version 2) as well as Legend of Kyrandia III : Malcolm's Revenge (version 1).   It DOES NOT apply to the HiColor VQAs found in Westwood's newer games. They use a somewhat different approach to compressing video data (I will provide some facts about their sound stream, though). Look at my other document (HC-VQA.TXT) for a description of the HiColor VQA movies.&lt;br /&gt;
&lt;br /&gt;
===NOTE #2=== &lt;br /&gt;
Throughout the document, I will assume that:&lt;br /&gt;
* CHAR is 1 byte in size, unsigned,&lt;br /&gt;
* SHORT is 2 bytes in size, unsigned,&lt;br /&gt;
* LONG is 4 bytes in size.&lt;br /&gt;
&lt;br /&gt;
Each VQA file is comprised of a series of chunks. A chunk can contain&lt;br /&gt;
other sub-chunks nested in it. Every chunk has a 4 letter ID (all uppercase&lt;br /&gt;
letters) and a LONG written using Motorola byte ordering system (first&lt;br /&gt;
comes the Most Significant Byte), unlike the usual Intel system (Least&lt;br /&gt;
Significant Byte first).&lt;br /&gt;
&lt;br /&gt;
For example, if you had a value 0x12345678 in hexadecimal, using the Intel&lt;br /&gt;
notation it would be written as 78 56 34 12, while using Motorola's &lt;br /&gt;
12 34 56 78.&lt;br /&gt;
&lt;br /&gt;
NOTE: Some chunk IDs start with a NULL byte (0x00) because of reasons&lt;br /&gt;
that will become apparent later. You should just skip this byte&lt;br /&gt;
and assume the next 4 letters hold the chunk ID.&lt;br /&gt;
&lt;br /&gt;
Following the chunk header is the chunk data.&lt;br /&gt;
&lt;br /&gt;
===Typical VQA File===&lt;br /&gt;
Here is a scheme of a typical VQA file (nested chunks are indented):&lt;br /&gt;
&lt;br /&gt;
 FORM&lt;br /&gt;
   VQHD&lt;br /&gt;
   FINF       &amp;lt;-  Frame data positions&lt;br /&gt;
   SND?    \  &amp;lt;-  First sound chunk, contains 1/2 second of sound&lt;br /&gt;
   SND?     |     &amp;lt;- Contains 1 frame's worth of sound&lt;br /&gt;
   VQFR     |     &amp;lt;- Contains various video data chunks&lt;br /&gt;
     CBF?   | 1st frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     CPL?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
   SND?    \&lt;br /&gt;
   VQFR     | 2nd frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
   SND?    \&lt;br /&gt;
   VQFR     | 3rd frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
 . . .&lt;br /&gt;
&lt;br /&gt;
NOTE: There can also be some other chunks (i.e. PINF, PINH, SN2J) included,&lt;br /&gt;
but they are not relevant (?!) for viewing the movie, so they can&lt;br /&gt;
easily be skipped.&lt;br /&gt;
&lt;br /&gt;
===FORM chunk===&lt;br /&gt;
&lt;br /&gt;
This chunk is the main chunk, containing all other chunks.&lt;br /&gt;
In case of version 2 and 3 movies, its size is actually the size of the&lt;br /&gt;
entire file minus the size of the chunk header (8 bytes). Version 1 movies&lt;br /&gt;
seem to have this set to the length of the header VQHD + FINF chunk.&lt;br /&gt;
&lt;br /&gt;
Immediately after the chunk's header, a 4-character signature,&lt;br /&gt;
&amp;quot;WVQA&amp;quot; is located. Then come all the other chunks.&lt;br /&gt;
&lt;br /&gt;
===VQHD chunk (&amp;quot;VQa HeaDer&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This is the header chunk, containing vital information about the movie.&lt;br /&gt;
Its size is always 42 bytes.&lt;br /&gt;
The information is structured like this:&lt;br /&gt;
&lt;br /&gt;
 struct VQAHeader&lt;br /&gt;
 {&lt;br /&gt;
  short  Version;       /* VQA version number                         */&lt;br /&gt;
  short  Flags;         /* VQA flags                                  */&lt;br /&gt;
  short  NumFrames;     /* Number of frames                           */&lt;br /&gt;
  short  Width;         /* Movie width (pixels)                       */&lt;br /&gt;
  short  Height;        /* Movie height (pixels)                      */&lt;br /&gt;
  char   BlockW;        /* Width of each image block (pixels)         */&lt;br /&gt;
  char   BlockH;        /* Height of each image block (pixels)        */&lt;br /&gt;
  char   FrameRate;     /* Frame rate of the VQA                      */&lt;br /&gt;
  char   CBParts;       /* How many images use the same lookup table  */&lt;br /&gt;
  short  Colors;        /* Maximum number of colors used in VQA       */&lt;br /&gt;
  short  MaxBlocks;     /* Maximum number of image blocks             */&lt;br /&gt;
  short  OffsetX;       /* Offset X position in background movie      */&lt;br /&gt;
  short  OffsetY;       /* Offset Y position in background movie      */&lt;br /&gt;
  short  MaxVPT?Size;   /* Max. VPT? (VPTZ, VPRZ) size                */&lt;br /&gt;
  short  Freq;          /* Sound sampling frequency                   */&lt;br /&gt;
  char   Channels;      /* Number of sound channels                   */&lt;br /&gt;
  char   Bits;          /* Sound resolution                           */&lt;br /&gt;
  long   Unknown1;      /* Always 0 ???                               */&lt;br /&gt;
  short  Unknown2;      /* 0 in old VQAs, 4 in HiColor ones ???       */&lt;br /&gt;
  long   MaxCBFZSize;   /* Max. CBFZ size (in old VQAs usually is 0)  */&lt;br /&gt;
  long   Unknown3;      /* Always 0 ???                               */&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Version denotes the VQA version. Valid values are:&lt;br /&gt;
* 1 - First VQAs, used only in Legend of Kyrandia III.&lt;br /&gt;
* 2 - Used in C&amp;amp;C, Red Alert, Lands of Lore II, Dune 2000.&lt;br /&gt;
* 3 - Lands of Lore III (?), Blade Runner (?), Nox and Tiberian Sun. These VQAs are HiColor (15 bit).&lt;br /&gt;
&lt;br /&gt;
Flags most probably contain some flags. I know that bit 0 (LSB) and 2 (Overlay)&lt;br /&gt;
* bit 0 denotes whether the VQA has a soundtrack or not.&lt;br /&gt;
* (1 = Has sound, 0 = No sound)&lt;br /&gt;
* bit 2 denotes whether this movie is an overlay on the background video.&lt;br /&gt;
* (1 = Yes, 0 = No)&lt;br /&gt;
* Movie with bit 2 may play separately, but need apply VPTR\VPRZ chunks&lt;br /&gt;
* to &amp;quot;Null&amp;quot; Vector Pointer Table (filled by zeros) for each frame.&lt;br /&gt;
&lt;br /&gt;
If NumFrames of background movie less than NumFrames of overlay movie&lt;br /&gt;
* then background movie will be repeat, while will not finish overlay movie.&lt;br /&gt;
&lt;br /&gt;
Width is usually 320, Height is usually 156 or 200 although one movie in&lt;br /&gt;
* Red Alert is 640x400 in size (the start movie for the Win95 version).&lt;br /&gt;
&lt;br /&gt;
Each frame of a VQA movie is comprised of a series of blocks that are&lt;br /&gt;
BlockW pixels in width and BlockH pixels in height. Imagine the frame&lt;br /&gt;
is a mosaic with the blocks being 'pieces' that make up the frame.&lt;br /&gt;
&lt;br /&gt;
BlockW is the width and BlockH is the height of each screen block.&lt;br /&gt;
In VQAs version 2 (and perhaps 1) the blocks are usually 4x2.&lt;br /&gt;
&lt;br /&gt;
FrameRate is always 15 in C&amp;amp;C and RA and seems to be 10 in LoK III.&lt;br /&gt;
&lt;br /&gt;
CBParts denotes how many frames use the same lookup table. It also&lt;br /&gt;
implies how many parts the new block lookup table is split into.&lt;br /&gt;
In C&amp;amp;C and RA it is always 8. When CBParts is 0, and movie have&lt;br /&gt;
CBP? chunks, then need see CIND chunk.&lt;br /&gt;
&lt;br /&gt;
Colors indicates the maximum number of colors used by the VQA.&lt;br /&gt;
The HiColor VQAs have this set to 0, while the old movies have&lt;br /&gt;
256 or less in here.&lt;br /&gt;
&lt;br /&gt;
Offset X and Offset Y uses only when there is bit 2 in the flags.&lt;br /&gt;
Usually uses chunk CLIP too, where the width and height of the&lt;br /&gt;
background movie are specified.&lt;br /&gt;
&lt;br /&gt;
MaxVPT?Size tells you the size of the largest VPTZ or VPRZ chunk, in bytes.&lt;br /&gt;
&lt;br /&gt;
Freq is usually 22050 Hz. Note that version 1 movies can have this set &lt;br /&gt;
to 0 Hz. In that case, you should use 22050 Hz.&lt;br /&gt;
&lt;br /&gt;
Channels specifies the number of sound channels, i.e. is the sound&lt;br /&gt;
mono or stereo. Channels=1 -&amp;gt; sound is mono, Channels=2 -&amp;gt; stereo.&lt;br /&gt;
C&amp;amp;C and RA almost always use mono sound. &lt;br /&gt;
Version 1 can have this set to 0, but you should use 1 (mono sound).&lt;br /&gt;
The majority of Tiberian Sun movies use stereo sound instead.&lt;br /&gt;
&lt;br /&gt;
* Bits indicates whether the sound is 8 bit or 16 bit.&lt;br /&gt;
Bits=8 -&amp;gt; 8 bit sound, Bits=16 -&amp;gt; 16 bit sound (surprise! :). &lt;br /&gt;
The Legend of Kyrandia III: Malcolm's Revenge uses 8 bits where&lt;br /&gt;
C&amp;amp;C, RA, TS, Dune 2000, Lands of Lore III use 16 bits.&lt;br /&gt;
Note, again, that version 1 of the VQAs can have this set to 0 in&lt;br /&gt;
which case 8 bits are assumed.&lt;br /&gt;
&lt;br /&gt;
MaxCBFZSize tells you the size of the largest CBFZ chunk, in bytes.&lt;br /&gt;
&lt;br /&gt;
Following the chunk data are the sub-chunks.&lt;br /&gt;
&lt;br /&gt;
===FINF chunk (&amp;quot;Frame INFormation&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This chunk contains the positions (absolute from the start of the VQA)&lt;br /&gt;
of data for every frame.&lt;br /&gt;
That means that it points to the SND? chunk associated with that frame,&lt;br /&gt;
which is followed by a VQFR chunk containing frame's video data.&lt;br /&gt;
&lt;br /&gt;
The positions are given as LONGs which are in normal Intel byte order.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' Some frame positions are 0x40000000 too large. This is a flag&lt;br /&gt;
indicating those frames contain a new color palette. To get the&lt;br /&gt;
actual positions, you should subtract 0x40000000 from the values.&lt;br /&gt;
&lt;br /&gt;
'''NOTE #2:''' To get the actual position of the frame data you have to multiply&lt;br /&gt;
the value by 2. This is why some chunk IDs start with 0x00. Since&lt;br /&gt;
you multiply by 2, you can't get an odd chunk position so if the&lt;br /&gt;
chunk position would normally be odd, a 0x00 is inserted to make&lt;br /&gt;
it even.&lt;br /&gt;
&lt;br /&gt;
===SND? chunk (&amp;quot;SouND&amp;quot; ???)===&lt;br /&gt;
These chunks contain the sound data for the movie. The last byte of the ID&lt;br /&gt;
can be either '0', '1' or '2' so the actual IDs would be &amp;quot;SND0&amp;quot;, &amp;quot;SND1&amp;quot;&lt;br /&gt;
and &amp;quot;SND2&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
In VQAs version 1 (Legend of Kyrandia 3) there are ''NumFrames'' sound chunks.&lt;br /&gt;
Old 8 bit VQA movies of version 2 have ''NumFrames+1'' sound chunks.&lt;br /&gt;
Note, however, that Dune2000 movies, which are also version 2, but HiColor&lt;br /&gt;
have ''NumFrames'' sound chunks, instead.&lt;br /&gt;
Version 3 movies also have ''NumFrames'' sound chunks.&lt;br /&gt;
&lt;br /&gt;
In case of the old, 8 bit VQAs, the first chunk contains half a second&lt;br /&gt;
(ver. 2) or more (ver. 1) of the wave data, in all (?) the HiColor movies&lt;br /&gt;
the first chunk contains exactly the amount of sound required for one&lt;br /&gt;
frame of the movie. The downside is this requires a somewhat more advanced &lt;br /&gt;
buffering technique on the side of the player in order to allow smooth &lt;br /&gt;
playback.&lt;br /&gt;
&lt;br /&gt;
===SND0 chunk===&lt;br /&gt;
&lt;br /&gt;
This one contains the raw 8 or 16 bit PCM wave data. If the data is&lt;br /&gt;
8 bit, the sound is unsigned and if it is 16 bit, the samples are&lt;br /&gt;
signed.&lt;br /&gt;
&lt;br /&gt;
===SND1 chunk===&lt;br /&gt;
&lt;br /&gt;
It contains 8 bit sound compressed using Westwood's own&lt;br /&gt;
proprietary ADPCM algorithm. The chunk has a 4 byte header:&lt;br /&gt;
&lt;br /&gt;
  struct SND1Header&lt;br /&gt;
  {&lt;br /&gt;
   short OutSize;&lt;br /&gt;
   short Size;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
These values are needed for the decoding algoritm (see APPENDIX C).&lt;br /&gt;
The encoded samples follow immediately after the header.&lt;br /&gt;
It's important to know this algorithm produces UNSIGNED sound, unlike&lt;br /&gt;
the IMA ADPCM algorithm supplied here (see below). It is, however very&lt;br /&gt;
simple to adapt both algorithms to produce either signed or unsigned&lt;br /&gt;
sample output...&lt;br /&gt;
&lt;br /&gt;
===SND2 chunk===&lt;br /&gt;
&lt;br /&gt;
It contains the 16 bit sound data compressed using the IMA ADPCM&lt;br /&gt;
algorithm which compresses 16 bits into 4. That's why the SND2 chunks&lt;br /&gt;
are 4 times smaller than SND0 chunks and they are used almost all&lt;br /&gt;
the time. For the description of the algorithm, see later in the document.&lt;br /&gt;
  &lt;br /&gt;
Different VQA versions have different stereo sample layout. In case&lt;br /&gt;
of Tiberian Sun stereo sound is encoded the same way as mono except the&lt;br /&gt;
SND2 chunk is split into two halfs. The first half contains the left channel&lt;br /&gt;
sound and the second half contains the right channel. The layout of&lt;br /&gt;
nibbles is as follows: LL LL LL LL LL ... RR RR RR RR RR.&lt;br /&gt;
Old movies (C&amp;amp;C, RA) use a different layout. Here, the nibbles are packed&lt;br /&gt;
together like this: LL RR LL RR LL RR ... This means that two left channel&lt;br /&gt;
samples are packed into one byte and then two right channel samples are&lt;br /&gt;
packed into another.&lt;br /&gt;
&lt;br /&gt;
Naturally, the size of a stereo SND2 chunk is exactly twice as big as&lt;br /&gt;
a mono SND2.&lt;br /&gt;
&lt;br /&gt;
It is important to note that, when decoding, you have to keep separate &lt;br /&gt;
values of ''Index'' and ''Cur_Sample'' for each channel (see APPENDIX B).&lt;br /&gt;
&lt;br /&gt;
===VQFR chunk (&amp;quot;Vector Quantized FRame&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
A chunk that includes many nested sub-chunks which contain video data.&lt;br /&gt;
It doesn't contain any data itself so the sub-chunks follow immediately&lt;br /&gt;
after the VQFR chunk header.&lt;br /&gt;
All following sub-chunks are nested inside a VQFR chunk. They can all&lt;br /&gt;
contain '0' or 'Z' as the last byte of their ID.&lt;br /&gt;
&lt;br /&gt;
* If the last byte is '0' it means that the chunk data is uncompressed.&lt;br /&gt;
* If the last byte is 'Z' it means that the data is compressed using&lt;br /&gt;
Format80 compression. You can find its description in APPENDIX A.&lt;br /&gt;
&lt;br /&gt;
===CBF? chunk (&amp;quot;CodeBook, Full&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
Lookup table containing the screen block data as an array&lt;br /&gt;
of elements that each are BlockW*BlockH bytes long. It is always located&lt;br /&gt;
in the data for the first frame. In Vector Quantization terminology&lt;br /&gt;
these tables are called CODEBOOKS.&lt;br /&gt;
&lt;br /&gt;
There can be max. 0x0f00 of these elements (blocks) at any one time in &lt;br /&gt;
normal VQAs and 0xff00 in the hi-res VQAs (Red Alert 95 start movie) although&lt;br /&gt;
I seriously doubt that so many blocks (0xff00 = 65280 blocks) would&lt;br /&gt;
ever be used.&lt;br /&gt;
&lt;br /&gt;
The uncompressed version of these chunks (&amp;quot;CBF0&amp;quot;) is used mainly in&lt;br /&gt;
the original Command &amp;amp; Conquer, while the compressed version (&amp;quot;CBFZ&amp;quot;)&lt;br /&gt;
is used in C&amp;amp;C: Red Alert.&lt;br /&gt;
&lt;br /&gt;
===CBP? chunk (&amp;quot;CodeBook Part&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
Like CBF?, but it contains a part of the lookup table, so to get the new&lt;br /&gt;
complete table you need to append CBParts of these in frame order.&lt;br /&gt;
Once you get the complete table and display the current frame, replace&lt;br /&gt;
the old table with the new one.&lt;br /&gt;
As in CBF? chunk, the uncompressed chunks are used in C&amp;amp;C and the compressed&lt;br /&gt;
chunks are used in Red Alert.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' If the chunks are CBFZ, first you need to append CBParts of them and&lt;br /&gt;
then decompress the data, NOT decompress each chunk individually.&lt;br /&gt;
&lt;br /&gt;
===CPL? chunk (&amp;quot;Color PaLette&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
The simplest one of all... Contains a palette for the VQA. It is an&lt;br /&gt;
array of red, green and blue values (in that order, all have a size of&lt;br /&gt;
1 byte). Seems that the values range from 0-255, but you should mask out&lt;br /&gt;
the bits 6 and 7 to get the correct palette (VGA hardware uses only&lt;br /&gt;
bits 0..5 anyway).&lt;br /&gt;
&lt;br /&gt;
===VPT? chunk: (&amp;quot;Vector Pointer Table&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This chunk contains the indexes into the block lookup table which contains&lt;br /&gt;
the data to display the frame.&lt;br /&gt;
The image blocks are called VECTORS in Vector Quantization terminology.&lt;br /&gt;
&lt;br /&gt;
These chunks are always compressed, but I guess the uncompressed ones&lt;br /&gt;
can also be used (although this would lower the overall compression achieved).&lt;br /&gt;
&lt;br /&gt;
The size of this index table is ''(Width/BlockW)*(Height/BlockH)*2 bytes''.&lt;br /&gt;
&lt;br /&gt;
Now, there is a catch: version 2 VQAs use a different layout of the&lt;br /&gt;
index table than version 1 VQAs. I will first describe the version&lt;br /&gt;
2 table format, as it's more common.&lt;br /&gt;
&lt;br /&gt;
====VERSION 2 INDEX TABLE LAYOUT====&lt;br /&gt;
The index table is an array of CHARs and is split into 2 parts - the top&lt;br /&gt;
half and the bottom half.&lt;br /&gt;
&lt;br /&gt;
Now, if you want to diplay the block at coordinates (in block units),&lt;br /&gt;
say (bx,by) you should read two bytes from the table, one from the top&lt;br /&gt;
and one from the bottom half:&lt;br /&gt;
&lt;br /&gt;
  LoVal=Table[by*(Width/BlockW)+bx]&lt;br /&gt;
  HiVal=Table[(Width/BlockW)*(Height/BlockH)+by*(Width/BlockW)+bx]&lt;br /&gt;
&lt;br /&gt;
If HiVal=0x0f (0xff for the start movie of Red Alert 95) you should&lt;br /&gt;
simply fill the block with color LoVal, otherwise you should copy&lt;br /&gt;
the block with index number HiVal*256+LoVal from the lookup table.&lt;br /&gt;
&lt;br /&gt;
Do that for every block on the screen (remember, there are Width/BlockW&lt;br /&gt;
blocks in the horizontal direction and Height/BlockH blocks in the vertical&lt;br /&gt;
direction) and you've decoded your first frame!&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' I was unable to find an entry in the VQA header which determines&lt;br /&gt;
whether 0x0f or 0xff is used in HiVal to signal that the block&lt;br /&gt;
is of uniform color. I assume that the Wy entry of the header&lt;br /&gt;
implies this: If BlockW=2 -&amp;gt; 0x0f is used, if BlockH=4 -&amp;gt; 0xff is used.&lt;br /&gt;
&lt;br /&gt;
====VERSION 1 INDEX TABLE LAYOUT====&lt;br /&gt;
Here, the index table is simply an array of SHORTs written in normal, Intel&lt;br /&gt;
byte order.&lt;br /&gt;
&lt;br /&gt;
The LoVal and HiVal are given as:&lt;br /&gt;
&lt;br /&gt;
  LoVal=Table[(by*(Width/BlockW)+bx)*2]&lt;br /&gt;
  HiVal=Table[(by*(Width/BlockW)+bx)*2+1]&lt;br /&gt;
&lt;br /&gt;
If HiVal=0xff, the block is of uniform color which is (255-LoVal).&lt;br /&gt;
Otherwise, write the block with index number (HiVal*256+LoVal)/8.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Appendix A===&lt;br /&gt;
&lt;br /&gt;
FORMAT80 COMPRESSION METHOD&lt;br /&gt;
by Vladan Bato (bat22@geocities.com)&lt;br /&gt;
&lt;br /&gt;
There are several different commands, with different sizes : from 1 to 5 bytes.&lt;br /&gt;
The positions mentioned below always refer to the destination buffer (i.e.&lt;br /&gt;
the uncompressed image). The relative positions are relative to the current&lt;br /&gt;
position in the destination buffer, which is one byte beyond the last written&lt;br /&gt;
byte.&lt;br /&gt;
&lt;br /&gt;
I will give some sample code at the end.&lt;br /&gt;
&lt;br /&gt;
(1) 1 byte&lt;br /&gt;
      +---+---+---+---+---+---+---+---+&lt;br /&gt;
      | 1 | 0 |   |   |   |   |   |   |&lt;br /&gt;
      +---+---+---+---+---+---+---+---+&lt;br /&gt;
              \_______________________/&lt;br /&gt;
                         |&lt;br /&gt;
                       Count&lt;br /&gt;
&lt;br /&gt;
      This one means : copy next Count bytes as is from Source to Dest.&lt;br /&gt;
&lt;br /&gt;
(2) 2 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
  | 0 |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
      \___________/\__________________________________________________/&lt;br /&gt;
            |                             |&lt;br /&gt;
         Count-3                    Relative Pos.&lt;br /&gt;
&lt;br /&gt;
  This means copy Count bytes from Dest at Current Pos.-Rel. Pos. to&lt;br /&gt;
  Current position.&lt;br /&gt;
  Note that you have to add 3 to the number you find in the bits 4-6 of the&lt;br /&gt;
  first byte to obtain the Count.&lt;br /&gt;
  Note that if the Rel. Pos. is 1, that means repeat Count times the previous&lt;br /&gt;
  byte.&lt;br /&gt;
&lt;br /&gt;
(3) 3 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---------------+---------------+&lt;br /&gt;
  | 1 | 1 |   |   |   |   |   |   |   |               |               |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---------------+---------------+&lt;br /&gt;
          \_______________________/                  Pos&lt;br /&gt;
                     |&lt;br /&gt;
                 Count-3&lt;br /&gt;
&lt;br /&gt;
  Copy Count bytes from Pos, where Pos is absolute from the start of the&lt;br /&gt;
  destination buffer. (Pos is a word, that means that the images can't be&lt;br /&gt;
  larger than 64K)&lt;br /&gt;
&lt;br /&gt;
(4) 4 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+&lt;br /&gt;
  | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |   |       |       |  |       |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+&lt;br /&gt;
                                            Count          Color&lt;br /&gt;
&lt;br /&gt;
  Write Color Count times.&lt;br /&gt;
  (Count is a word, color is a byte)&lt;br /&gt;
&lt;br /&gt;
(5) 5 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+&lt;br /&gt;
  | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |   |       |       |  |       |       |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+&lt;br /&gt;
                                            Count               Pos&lt;br /&gt;
&lt;br /&gt;
  Copy Count bytes from Dest. starting at Pos. Pos is absolute from the start&lt;br /&gt;
  of the Destination buffer.&lt;br /&gt;
  Both Count and Pos are words.&lt;br /&gt;
&lt;br /&gt;
These are all the commands I found out. Maybe there are other ones, but I&lt;br /&gt;
haven't seen them yet.&lt;br /&gt;
&lt;br /&gt;
All the images end with a 80h command.&lt;br /&gt;
&lt;br /&gt;
To make things more clearer here's a piece of code that will uncompress the&lt;br /&gt;
image.&lt;br /&gt;
&lt;br /&gt;
  DP = destination pointer&lt;br /&gt;
  SP = source pointer&lt;br /&gt;
  Source and Dest are the two buffers&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  SP:=0;&lt;br /&gt;
  DP:=0;&lt;br /&gt;
  repeat&lt;br /&gt;
    Com:=Source[SP];&lt;br /&gt;
    inc(SP);&lt;br /&gt;
    b7:=Com shr 7;  {b7 is bit 7 of Com}&lt;br /&gt;
    case b7 of&lt;br /&gt;
      0 : begin  {copy command (2)}&lt;br /&gt;
            {Count is bits 4-6 + 3}&lt;br /&gt;
            Count:=(Com and $7F) shr 4 + 3;&lt;br /&gt;
            {Position is bits 0-3, with bits 0-7 of next byte}&lt;br /&gt;
            Posit:=(Com and $0F) shl 8+Source[SP];&lt;br /&gt;
            Inc(SP);&lt;br /&gt;
            {Starting pos=Cur pos. - calculated value}&lt;br /&gt;
            Posit:=DP-Posit;&lt;br /&gt;
            for i:=Posit to Posit+Count-1 do&lt;br /&gt;
            begin&lt;br /&gt;
              Dest[DP]:=Dest[i];&lt;br /&gt;
              Inc(DP);&lt;br /&gt;
            end;&lt;br /&gt;
          end;&lt;br /&gt;
      1 : begin&lt;br /&gt;
            {Check bit 6 of Com}&lt;br /&gt;
            b6:=(Com and $40) shr 6;&lt;br /&gt;
            case b6 of&lt;br /&gt;
              0 : begin  {Copy as is command (1)}&lt;br /&gt;
                    Count:=Com and $3F;  {mask 2 topmost bits}&lt;br /&gt;
                    if Count=0 then break; {EOF marker}&lt;br /&gt;
                    for i:=1 to Count do&lt;br /&gt;
                    begin&lt;br /&gt;
                      Dest[DP]:=Source[SP];&lt;br /&gt;
                      Inc(DP);&lt;br /&gt;
                      Inc(SP);&lt;br /&gt;
                    end;&lt;br /&gt;
                  end;&lt;br /&gt;
              1 : begin  {large copy, very large copy and fill commands}&lt;br /&gt;
                    {Count = (bits 0-5 of Com) +3}&lt;br /&gt;
                    {if Com=FEh then fill, if Com=FFh then very large copy}&lt;br /&gt;
                    Count:=Com and $3F;&lt;br /&gt;
                    if Count&amp;lt;$3E then {large copy (3)}&lt;br /&gt;
                    begin&lt;br /&gt;
                      Inc(Count,3);&lt;br /&gt;
                      {Next word = pos. from start of image}&lt;br /&gt;
                      Posit:=Word(Source[SP]);&lt;br /&gt;
                      Inc(SP,2);&lt;br /&gt;
                      for i:=Posit to Posit+Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=Dest[i];&lt;br /&gt;
                        Inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end&lt;br /&gt;
                    else if Count=$3F then   {very large copy (5)}&lt;br /&gt;
                    begin&lt;br /&gt;
                      {next 2 words are Count and Pos}&lt;br /&gt;
                      Count:=Word(Source[SP]);&lt;br /&gt;
                      Posit:=Word(Source[SP+2]);&lt;br /&gt;
                      Inc(SP,4);&lt;br /&gt;
                      for i:=Posit to Posit+Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=Dest[i];&lt;br /&gt;
                        Inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end else&lt;br /&gt;
                    begin   {Count=$3E, fill (4)}&lt;br /&gt;
                      {Next word is count, the byte after is color}&lt;br /&gt;
                      Count:=Word(Source[SP]);&lt;br /&gt;
                      Inc(SP,2);&lt;br /&gt;
                      b:=Source[SP];&lt;br /&gt;
                      Inc(SP);&lt;br /&gt;
                      for i:=0 to Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=b;&lt;br /&gt;
                        inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end;&lt;br /&gt;
                  end;&lt;br /&gt;
            end;&lt;br /&gt;
          end;&lt;br /&gt;
    end;&lt;br /&gt;
  until false;&lt;br /&gt;
&lt;br /&gt;
Note that you won't be able to compile this code, because the typecasting&lt;br /&gt;
won't work. (But I'm sure you'll be able to fix it).&lt;br /&gt;
&lt;br /&gt;
===Appendix B===&lt;br /&gt;
IMA ADPCM DECOMPRESSION&lt;br /&gt;
by Vladan Bato  (bat22@geocities.com)&lt;br /&gt;
http://www.geocities.com/SiliconValley/8682&lt;br /&gt;
&lt;br /&gt;
Note that the current sample value and index into the Step Table should&lt;br /&gt;
be initialized to 0 at the start and are maintained across the chunks&lt;br /&gt;
(see below).&lt;br /&gt;
&lt;br /&gt;
====IMA-ADPCM DECOMPRESSION ====&lt;br /&gt;
&lt;br /&gt;
It is the exact opposite of the above. It receives 4-bit codes in input&lt;br /&gt;
and produce 16-bit samples in output.&lt;br /&gt;
&lt;br /&gt;
Again you have to mantain an Index into the Step Table an the current&lt;br /&gt;
sample value.&lt;br /&gt;
&lt;br /&gt;
The tables used are the same as for compression.&lt;br /&gt;
&lt;br /&gt;
Here's the code :&lt;br /&gt;
&lt;br /&gt;
  Index:=0;&lt;br /&gt;
  Cur_Sample:=0;&lt;br /&gt;
&lt;br /&gt;
  while there_is_more_data do&lt;br /&gt;
  begin&lt;br /&gt;
    Code:=Get_Next_Code;&lt;br /&gt;
&lt;br /&gt;
    if (Code and $8) &amp;lt;&amp;gt; 0 then Sb:=1 else Sb:=0;&lt;br /&gt;
    Code:=Code and $7;&lt;br /&gt;
    {Separate the sign bit from the rest}&lt;br /&gt;
&lt;br /&gt;
    Delta:=(Step_Table[Index]*Code) div 4 + Step_Table[Index] div 8;&lt;br /&gt;
    {The last one is to minimize errors}&lt;br /&gt;
&lt;br /&gt;
    if Sb=1 then Delta:=-Delta;&lt;br /&gt;
&lt;br /&gt;
    Cur_Sample:=Cur_Sample+Delta;&lt;br /&gt;
    if Cur_Sample&amp;gt;32767 then Cur_Sample:=32767&lt;br /&gt;
    else if Cur_Sample&amp;lt;-32768 then Cur_Sample:=-32768;&lt;br /&gt;
&lt;br /&gt;
    Output_Sample(Cur_Sample);&lt;br /&gt;
&lt;br /&gt;
    Index:=Index+Index_Adjust[Code];&lt;br /&gt;
    if Index&amp;lt;0 then Index:=0;&lt;br /&gt;
    if Index&amp;gt;88 the Index:=88;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
Again, this can be done more efficiently (no need for multiplication).&lt;br /&gt;
&lt;br /&gt;
The ''Get_Next_Code'' function should return the next 4-bit code. It must&lt;br /&gt;
extract it from the input buffer (note that two 4-bit codes are stored&lt;br /&gt;
in the same byte, the first one in the lower bits).&lt;br /&gt;
&lt;br /&gt;
The Output_Sample function should write the signed 16-bit sample to the&lt;br /&gt;
output buffer.&lt;br /&gt;
&lt;br /&gt;
=== Appendix A : THE INDEX ADJUSTMENT TABLE ===&lt;br /&gt;
&lt;br /&gt;
  Index_Adjust : array [0..7] of integer = (-1,-1,-1,-1,2,4,6,8);&lt;br /&gt;
&lt;br /&gt;
=== Appendix B : THE STEP TABLE ===&lt;br /&gt;
&lt;br /&gt;
  Steps_Table : array [0..88] of integer =(&lt;br /&gt;
        7,     8,     9,     10,    11,    12,     13,    14,    16,&lt;br /&gt;
        17,    19,    21,    23,    25,    28,     31,    34,    37,&lt;br /&gt;
        41,    45,    50,    55,    60,    66,     73,    80,    88,&lt;br /&gt;
        97,    107,   118,   130,   143,   157,    173,   190,   209,&lt;br /&gt;
        230,   253,   279,   307,   337,   371,    408,   449,   494,&lt;br /&gt;
        544,   598,   658,   724,   796,   876,    963,   1060,  1166,&lt;br /&gt;
        1282,  1411,  1552,  1707,  1878,  2066,   2272,  2499,  2749,&lt;br /&gt;
        3024,  3327,  3660,  4026,  4428,  4871,   5358,  5894,  6484,&lt;br /&gt;
        7132,  7845,  8630,  9493,  10442, 11487,  12635, 13899, 15289,&lt;br /&gt;
        16818, 18500, 20350, 22385, 24623, 27086,  29794, 32767 );&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Appendix C===&lt;br /&gt;
WESTWOOD STUDIOS' ADPCM DECOMPRESSION&lt;br /&gt;
by Asatur V. Nazarian (samael@avn.mccme.ru)&lt;br /&gt;
&lt;br /&gt;
==== WS ADPCM Decompression Algorithm ====&lt;br /&gt;
&lt;br /&gt;
Each SND1 chunk may be decompressed independently of others. This lets you&lt;br /&gt;
implement seeking/skipping for WS ADPCM sounds (unlike IMA ADPCM ones).&lt;br /&gt;
But during the decompression of the given chunk a variable (''CurSample'') should&lt;br /&gt;
be maintained for this whole chunk:&lt;br /&gt;
&lt;br /&gt;
 SHORT CurSample;&lt;br /&gt;
 BYTE  InputBuffer[InputBufferSize]; // input buffer containing the whole chunk&lt;br /&gt;
 WORD  wSize, wOutSize; // Size and OutSize values from this chunk's header&lt;br /&gt;
 BYTE  code;&lt;br /&gt;
 CHAR  count; // this is a signed char!&lt;br /&gt;
 WORD  i; // index into InputBuffer&lt;br /&gt;
 WORD  input; // shifted input&lt;br /&gt;
 &lt;br /&gt;
 if (wSize==wOutSize) // such chunks are NOT compressed&lt;br /&gt;
 {&lt;br /&gt;
  for (i=0;i&amp;lt;wOutSize;i++)&lt;br /&gt;
      Output(InputBuffer[i]); // send to output stream&lt;br /&gt;
  return; // chunk is done!&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // otherwise we need to decompress chunk&lt;br /&gt;
 &lt;br /&gt;
 CurSample=0x80; // unsigned 8-bit&lt;br /&gt;
 i=0;&lt;br /&gt;
 &lt;br /&gt;
 // note that wOutSize value is crucial for decompression!&lt;br /&gt;
 &lt;br /&gt;
 while (wOutSize&amp;gt;0) // until wOutSize is exhausted!&lt;br /&gt;
 {&lt;br /&gt;
  input=InputBuffer[i++];&lt;br /&gt;
  input&amp;lt;&amp;lt;=2;&lt;br /&gt;
  code=HIBYTE(input);&lt;br /&gt;
  count=LOBYTE(input)&amp;gt;&amp;gt;2;&lt;br /&gt;
  switch (code) // parse code&lt;br /&gt;
  {&lt;br /&gt;
    case 2: // no compression...&lt;br /&gt;
	 if (count &amp;amp; 0x20)&lt;br /&gt;
	 {&lt;br /&gt;
	   count&amp;lt;&amp;lt;=3;		// here it's significant that (count) is signed:&lt;br /&gt;
	   CurSample+=count&amp;gt;&amp;gt;3; // the sign bit will be copied by these shifts!&lt;br /&gt;
&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize--; // one byte added to output&lt;br /&gt;
	 }&lt;br /&gt;
	 else // copy (count+1) bytes from input to output&lt;br /&gt;
	 {&lt;br /&gt;
	   for (count++;count&amp;gt;0;count--,wOutSize--,i++)&lt;br /&gt;
	       Output(InputBuffer[i]);&lt;br /&gt;
	   CurSample=InputBuffer[i-1]; // set (CurSample) to the last byte sent to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    case 1: // ADPCM 8-bit -&amp;gt; 4-bit&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--) // decode (count+1) bytes&lt;br /&gt;
	 {&lt;br /&gt;
	   code=InputBuffer[i++];&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable4bit[(code &amp;amp; 0x0F)]; // lower nibble&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable4bit[(code &amp;gt;&amp;gt; 4)]; // higher nibble&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize-=2; // two bytes added to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    case 0: // ADPCM 8-bit -&amp;gt; 2-bit&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--) // decode (count+1) bytes&lt;br /&gt;
	 {&lt;br /&gt;
	   code=InputBuffer[i++];&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[(code &amp;amp; 0x03)]; // lower 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;2) &amp;amp; 0x03)]; // lower middle 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;4) &amp;amp; 0x03)]; // higher middle 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;6) &amp;amp; 0x03)]; // higher 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize-=4; // 4 bytes sent to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    default: // just copy (CurSample) (count+1) times to output&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--,wOutSize--)&lt;br /&gt;
	     Output((BYTE)CurSample);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
HIBYTE and LOBYTE are just higher and lower bytes of WORD:&lt;br /&gt;
 #define HIBYTE(word) ((word) &amp;gt;&amp;gt; 8)&lt;br /&gt;
 #define LOBYTE(word) ((word) &amp;amp; 0xFF)&lt;br /&gt;
Note that depending on your compiler you may need to use additional byte&lt;br /&gt;
separation in these defines, e.g. (((byte) &amp;gt;&amp;gt; 8) &amp;amp; 0xFF). The same holds for&lt;br /&gt;
4-bit and 2-bit nibble separation in the code above.&lt;br /&gt;
&lt;br /&gt;
''WSTable4bit'' and ''WSTable2bit'' are the delta tables given in the next section.&lt;br /&gt;
&lt;br /&gt;
Output() is just a placeholder for any action you would like to perform for&lt;br /&gt;
decompressed sample value.&lt;br /&gt;
&lt;br /&gt;
Clip8BitSample is quite evident:&lt;br /&gt;
&lt;br /&gt;
 SHORT Clip8BitSample(SHORT sample)&lt;br /&gt;
 {&lt;br /&gt;
  if (sample&amp;gt;255)&lt;br /&gt;
     return 255;&lt;br /&gt;
  else if (sample&amp;lt;0)&lt;br /&gt;
     return 0;&lt;br /&gt;
  else&lt;br /&gt;
     return sample;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This algorithm is ONLY for mono 8-bit unsigned sound, as I've never seen any&lt;br /&gt;
other sound format used with WS ADPCM compression.&lt;br /&gt;
&lt;br /&gt;
Of course, the decompression routine described above may be greatly&lt;br /&gt;
optimized.&lt;br /&gt;
&lt;br /&gt;
==== WS ADPCM Tables ====&lt;br /&gt;
&lt;br /&gt;
 CHAR WSTable2bit[]=&lt;br /&gt;
 {&lt;br /&gt;
    -2,&lt;br /&gt;
    -1,&lt;br /&gt;
     0,&lt;br /&gt;
     1&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 CHAR WSTable4bit[]=&lt;br /&gt;
 {&lt;br /&gt;
    -9, -8, -6, -5, -4, -3, -2, -1,&lt;br /&gt;
     0,  1,  2,  3,  4,  5,  6,  8&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
==HC_VQA.txt==&lt;br /&gt;
by Gordan Ugarkovic (ugordan@yahoo.com)&lt;br /&gt;
http://members.xoom.com/ugordan&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This document describes how to view Westwood's HiColor VQA movies. These&lt;br /&gt;
are version 3 movies, but there are at least some version 2 VQAs that are&lt;br /&gt;
in this format. I will not describe the whole VQA layout here, I will&lt;br /&gt;
just explain how to display the VIDEO stream of the VQA, that is, how&lt;br /&gt;
to decompress the CBFZ and VPTR/VPRZ chunks.&lt;br /&gt;
&lt;br /&gt;
First a little warning: I'm not sure which flag denotes the VQA is 8 bit&lt;br /&gt;
or 15 bit. I'm pretty convinced it's either bit 4 (0x10) or bit 2 (0x04)&lt;br /&gt;
of the Flags entry (see my VQA_INFO.TXT) in the header. Another way would &lt;br /&gt;
be to check the Colors entry in the header, if it is 0 it could imply &lt;br /&gt;
a HiColor movie.&lt;br /&gt;
&lt;br /&gt;
There's a major difference between the old (8 bit, 256 color) and the new,&lt;br /&gt;
HiColor VQAs. Lookup tables are no longer split up into several CBP?&lt;br /&gt;
chunks, instead they come in one piece (a CBFZ chunk). Two lookup tables&lt;br /&gt;
can now be many frames apart, not just 8 (as usual). This is indicated&lt;br /&gt;
by the CBParts entry of the header (see VQA_INFO.TXT), which is set to 0.&lt;br /&gt;
Subsequent frames use the last lookup table loaded, of course.&lt;br /&gt;
&lt;br /&gt;
Another thing: It appears the first CBFZ chunk comes inside the VQFR chunk&lt;br /&gt;
but the other ones seem to be located inside their own chunks,&lt;br /&gt;
called VQFL, which are followed by the usual VQFR chunks (containing&lt;br /&gt;
VPTR/VPRZ chunks).&lt;br /&gt;
&lt;br /&gt;
Also, the movies are 15 bit, NOT 16 bit. There is a difference because&lt;br /&gt;
in 16 bit color depth there are 6 bits for the green channel, but&lt;br /&gt;
the VQAs use 5.&lt;br /&gt;
&lt;br /&gt;
=== The CBFZ chunks===&lt;br /&gt;
&lt;br /&gt;
These are a bit modified since the 8 bit VQAs. If the first byte of the&lt;br /&gt;
chunk is not NULL (0x00), it means the chunk is compressed using the&lt;br /&gt;
standard Format80 algorithm (see Vladan Bato's text on C&amp;amp;C file formats),&lt;br /&gt;
starting from that byte. If the first byte is NULL, the chunk is compressed&lt;br /&gt;
using a modified version of Format80 (see below), starting from the next&lt;br /&gt;
byte of the chunk. The original Format80 algorithm is used when the&lt;br /&gt;
amount of data to be compressed is less than 64 KB, otherwise the 'new'&lt;br /&gt;
algorithm is used.&lt;br /&gt;
&lt;br /&gt;
When decompressed properly, a CBFZ chunk expands into 15 bit pixels packed&lt;br /&gt;
as shorts in normal Intel byte order. The red, green and blue values are&lt;br /&gt;
packed like this:&lt;br /&gt;
&lt;br /&gt;
  15      bit      0&lt;br /&gt;
   arrrrrgg gggbbbbb&lt;br /&gt;
   HI byte  LO byte&lt;br /&gt;
&lt;br /&gt;
The r,g,b values make up a pixel and they can range from 0-31.&lt;br /&gt;
As in the old CBFZ chunks, these pixels make up the block lookup table&lt;br /&gt;
(also called a codebook). The a (alpha) value is used in Blade Runner in&lt;br /&gt;
overlay videos to indicate a transparent pixel.&lt;br /&gt;
&lt;br /&gt;
=== The VPTR chunks===&lt;br /&gt;
&lt;br /&gt;
These chunks use some sort of differential, run-length algorithm that&lt;br /&gt;
only records changes from the previous frame. Therefore, the previous&lt;br /&gt;
frame bitmap must be maintained throughout all the frames (you could&lt;br /&gt;
just draw the blocks that changed, though). This makes dropping frames&lt;br /&gt;
(in case of bad performance) impossible.&lt;br /&gt;
&lt;br /&gt;
When decoding, you take a short int (Intel) from the chunk and examine &lt;br /&gt;
its 3 most significant bits (bits 15,14,13). These bits make up a &lt;br /&gt;
code prefix that determines which action is to be done.&lt;br /&gt;
&lt;br /&gt;
Here's a list of the prefixes I encountered and their description&lt;br /&gt;
(Val is the short int value):&lt;br /&gt;
&lt;br /&gt;
  BITS - MEANING&lt;br /&gt;
&lt;br /&gt;
   000 - Skip Count blocks. Count is (Val &amp;amp; 0x1fff).&lt;br /&gt;
&lt;br /&gt;
   001 - Write block number (Val &amp;amp; 0xff) Count times.&lt;br /&gt;
         Count is (((Val/256) &amp;amp; 0x1f)+1)*2. Note that this can only&lt;br /&gt;
         index the first 256 blocks.&lt;br /&gt;
&lt;br /&gt;
   010 - Write block number (Val &amp;amp; 0xff) and then write Count blocks&lt;br /&gt;
         getting their indexes by reading next Count bytes from&lt;br /&gt;
         the VPTR chunk. Count is (((Val/256) &amp;amp; 0x1f)+1)*2.&lt;br /&gt;
         Again, the block numbers range from 0-255.&lt;br /&gt;
&lt;br /&gt;
   011 - Write block (Val &amp;amp; 0x1fff).&lt;br /&gt;
&lt;br /&gt;
   100 - Same as 011 but skip pixels with alpha bit set.&lt;br /&gt;
&lt;br /&gt;
   101 - Write block (Val &amp;amp; 0x1fff) Count times. Count is the next&lt;br /&gt;
         byte from the VPTR chunk.&lt;br /&gt;
&lt;br /&gt;
   110 - Same as 101 but skip pixels with alpha bit set.&lt;br /&gt;
&lt;br /&gt;
After this, you take the next short int and repeat the above process.&lt;br /&gt;
&lt;br /&gt;
Prefixes 100 and 110 are used in Blade Runner for overlay videos.&lt;br /&gt;
&lt;br /&gt;
Every row of blocks is processed individually of others.&lt;br /&gt;
When you encounter the end of a row, proceed to the next row&lt;br /&gt;
(blocks are processed left to right, top to down).&lt;br /&gt;
Repeat this process until all blocks in the frame are covered and&lt;br /&gt;
that's it!&lt;br /&gt;
&lt;br /&gt;
Note that the above implies an absolute maximum of 8192 blocks (0x1fff+1). &lt;br /&gt;
&lt;br /&gt;
As for the VPRZ chunks, these are just VPTR chunks compressed with&lt;br /&gt;
the standard Format80 algorithm.&lt;br /&gt;
&lt;br /&gt;
=== The modified Format80 scheme ===&lt;br /&gt;
&lt;br /&gt;
This is really only a small modification of the basic algorithm.&lt;br /&gt;
The only commands that are modified are commands (3) and (5)&lt;br /&gt;
See Vladan's text). Instead of using offsets ABSOLUTE from&lt;br /&gt;
the start of the destination buffer, offsets RELATIVE to the&lt;br /&gt;
current destination pointer are used. If you ask me, I don't see&lt;br /&gt;
why this approach wasn't used in the first place as it would&lt;br /&gt;
suffer no disadvantage with the old files and it would be much&lt;br /&gt;
easier to compress even larger amounts of data. The guys at WW&lt;br /&gt;
were just careless, I guess... :-)&lt;br /&gt;
&lt;br /&gt;
Anyway, in Vladan's algorithm, there is a line in&lt;br /&gt;
command (3) that says:&lt;br /&gt;
    Posit:=Word(Source[SP]);&lt;br /&gt;
it should say:&lt;br /&gt;
    Posit:=DP-Word(Source[SP]);&lt;br /&gt;
&lt;br /&gt;
Likewise, for command (5):&lt;br /&gt;
    Posit:=Word(Source[SP+2]);&lt;br /&gt;
it should be:&lt;br /&gt;
    Posit:=DP-Word(Source[SP+2]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Games Using VQA ==&lt;br /&gt;
&lt;br /&gt;
=== Version 1 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/win3x/legend-of-kyrandia-malcolms-revenge The Legend of Kyrandia: Malcolm's Revenge]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/monopoly Monopoly] (Version 1???)&lt;br /&gt;
&lt;br /&gt;
=== Versions 2 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/dos/command-conquer Command &amp;amp; Conquer]&lt;br /&gt;
* [http://www.mobygames.com/game/dos/command-conquer-red-alert Command &amp;amp; Conquer: Red Alert]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/command-conquer-sole-survivor Command &amp;amp; Conquer: Sole Survivor]&lt;br /&gt;
* [http://www.mobygames.com/game/lands-of-lore-guardians-of-destiny Lands of Lore: Guardians of Destiny]&lt;br /&gt;
&lt;br /&gt;
=== Versions 3 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/windows/blade-runner Blade Runner]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/command-conquer-tiberian-sun Command &amp;amp; Conquer: Tiberian Sun]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/dune-2000 Dune 2000]&lt;br /&gt;
* [http://www.mobygames.com/game/lands-of-lore-iii Lands of Lore III]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/nox Nox]&lt;br /&gt;
&lt;br /&gt;
==Other Documentation==&lt;br /&gt;
http://www.gamers.org/pub/idgames2/planetquake/planetcnc/cncdz/ has lots of data about this format. See vqa_overview.zip and vqafilesguild.zip. Also there is a decoder (vqatoavi) that decodes the dune2000 sample. &lt;br /&gt;
&lt;br /&gt;
[[Category:Game Formats]]&lt;br /&gt;
[[Category:Video Codecs]]&lt;br /&gt;
[[Category:Incomplete Video Codecs]]&lt;/div&gt;</summary>
		<author><name>Andrew Dream</name></author>
	</entry>
	<entry>
		<id>https://wiki.multimedia.cx/index.php?title=VQA&amp;diff=16092</id>
		<title>VQA</title>
		<link rel="alternate" type="text/html" href="https://wiki.multimedia.cx/index.php?title=VQA&amp;diff=16092"/>
		<updated>2025-11-25T11:36:39Z</updated>

		<summary type="html">&lt;p&gt;Andrew Dream: /* VQHD chunk (&amp;quot;VQa HeaDer&amp;quot; ???) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Extension: vqa&lt;br /&gt;
* Company: [[Westwood Studios]]&lt;br /&gt;
* Samples: [http://samples.mplayerhq.hu/game-formats/vqa/ http://samples.mplayerhq.hu/game-formats/vqa/]&lt;br /&gt;
* Samples: {{DexvertSamples|video/westwoodStudiosVQA}}&lt;br /&gt;
&lt;br /&gt;
VQA stands for Vector Quantized Animation and is a FMV format used in a number of computer games by Westwood Studios.&lt;br /&gt;
&lt;br /&gt;
== Technical Description ==&lt;br /&gt;
&lt;br /&gt;
'''TODO: import and combine Gordan Ugarkovic's documents from [http://multimedia.cx/VQA_INFO.TXT http://multimedia.cx/VQA_INFO.TXT] and [http://multimedia.cx/HC-VQA.TXT http://multimedia.cx/HC-VQA.TXT].&lt;br /&gt;
==VQA_INFO.txt==&lt;br /&gt;
&lt;br /&gt;
===NOTE #1=== &lt;br /&gt;
This document only applies to VQA files in the original C&amp;amp;C and C&amp;amp;C: Red Alert (version 2) as well as Legend of Kyrandia III : Malcolm's Revenge (version 1).   It DOES NOT apply to the HiColor VQAs found in Westwood's newer games. They use a somewhat different approach to compressing video data (I will provide some facts about their sound stream, though). Look at my other document (HC-VQA.TXT) for a description of the HiColor VQA movies.&lt;br /&gt;
&lt;br /&gt;
===NOTE #2=== &lt;br /&gt;
Throughout the document, I will assume that:&lt;br /&gt;
* CHAR is 1 byte in size, unsigned,&lt;br /&gt;
* SHORT is 2 bytes in size, unsigned,&lt;br /&gt;
* LONG is 4 bytes in size.&lt;br /&gt;
&lt;br /&gt;
Each VQA file is comprised of a series of chunks. A chunk can contain&lt;br /&gt;
other sub-chunks nested in it. Every chunk has a 4 letter ID (all uppercase&lt;br /&gt;
letters) and a LONG written using Motorola byte ordering system (first&lt;br /&gt;
comes the Most Significant Byte), unlike the usual Intel system (Least&lt;br /&gt;
Significant Byte first).&lt;br /&gt;
&lt;br /&gt;
For example, if you had a value 0x12345678 in hexadecimal, using the Intel&lt;br /&gt;
notation it would be written as 78 56 34 12, while using Motorola's &lt;br /&gt;
12 34 56 78.&lt;br /&gt;
&lt;br /&gt;
NOTE: Some chunk IDs start with a NULL byte (0x00) because of reasons&lt;br /&gt;
that will become apparent later. You should just skip this byte&lt;br /&gt;
and assume the next 4 letters hold the chunk ID.&lt;br /&gt;
&lt;br /&gt;
Following the chunk header is the chunk data.&lt;br /&gt;
&lt;br /&gt;
===Typical VQA File===&lt;br /&gt;
Here is a scheme of a typical VQA file (nested chunks are indented):&lt;br /&gt;
&lt;br /&gt;
 FORM&lt;br /&gt;
   VQHD&lt;br /&gt;
   FINF       &amp;lt;-  Frame data positions&lt;br /&gt;
   SND?    \  &amp;lt;-  First sound chunk, contains 1/2 second of sound&lt;br /&gt;
   SND?     |     &amp;lt;- Contains 1 frame's worth of sound&lt;br /&gt;
   VQFR     |     &amp;lt;- Contains various video data chunks&lt;br /&gt;
     CBF?   | 1st frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     CPL?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
   SND?    \&lt;br /&gt;
   VQFR     | 2nd frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
   SND?    \&lt;br /&gt;
   VQFR     | 3rd frame data&lt;br /&gt;
     CBP?   |&lt;br /&gt;
     VPT?  /&lt;br /&gt;
 . . .&lt;br /&gt;
&lt;br /&gt;
NOTE: There can also be some other chunks (i.e. PINF, PINH, SN2J) included,&lt;br /&gt;
but they are not relevant (?!) for viewing the movie, so they can&lt;br /&gt;
easily be skipped.&lt;br /&gt;
&lt;br /&gt;
===FORM chunk===&lt;br /&gt;
&lt;br /&gt;
This chunk is the main chunk, containing all other chunks.&lt;br /&gt;
In case of version 2 and 3 movies, its size is actually the size of the&lt;br /&gt;
entire file minus the size of the chunk header (8 bytes). Version 1 movies&lt;br /&gt;
seem to have this set to the length of the header VQHD + FINF chunk.&lt;br /&gt;
&lt;br /&gt;
Immediately after the chunk's header, a 4-character signature,&lt;br /&gt;
&amp;quot;WVQA&amp;quot; is located. Then come all the other chunks.&lt;br /&gt;
&lt;br /&gt;
===VQHD chunk (&amp;quot;VQa HeaDer&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This is the header chunk, containing vital information about the movie.&lt;br /&gt;
Its size is always 42 bytes.&lt;br /&gt;
The information is structured like this:&lt;br /&gt;
&lt;br /&gt;
 struct VQAHeader&lt;br /&gt;
 {&lt;br /&gt;
  short  Version;       /* VQA version number                         */&lt;br /&gt;
  short  Flags;         /* VQA flags                                  */&lt;br /&gt;
  short  NumFrames;     /* Number of frames                           */&lt;br /&gt;
  short  Width;         /* Movie width (pixels)                       */&lt;br /&gt;
  short  Height;        /* Movie height (pixels)                      */&lt;br /&gt;
  char   BlockW;        /* Width of each image block (pixels)         */&lt;br /&gt;
  char   BlockH;        /* Height of each image block (pixels)        */&lt;br /&gt;
  char   FrameRate;     /* Frame rate of the VQA                      */&lt;br /&gt;
  char   CBParts;       /* How many images use the same lookup table  */&lt;br /&gt;
  short  Colors;        /* Maximum number of colors used in VQA       */&lt;br /&gt;
  short  MaxBlocks;     /* Maximum number of image blocks             */&lt;br /&gt;
  short  OffsetX;       /* Offset X position in background video      */&lt;br /&gt;
  short  OffsetY;       /* Offset Y position in background video      */&lt;br /&gt;
  short  MaxVPT?Size;   /* Max. VPT? (VPTZ, VPRZ) size                */&lt;br /&gt;
  short  Freq;          /* Sound sampling frequency                   */&lt;br /&gt;
  char   Channels;      /* Number of sound channels                   */&lt;br /&gt;
  char   Bits;          /* Sound resolution                           */&lt;br /&gt;
  long   Unknown1;      /* Always 0 ???                               */&lt;br /&gt;
  short  Unknown2;      /* 0 in old VQAs, 4 in HiColor ones ???       */&lt;br /&gt;
  long   MaxCBFZSize;   /* Max. CBFZ size (in old VQAs usually is 0)  */&lt;br /&gt;
  long   Unknown3;      /* Always 0 ???                               */&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Version denotes the VQA version. Valid values are:&lt;br /&gt;
* 1 - First VQAs, used only in Legend of Kyrandia III.&lt;br /&gt;
* 2 - Used in C&amp;amp;C, Red Alert, Lands of Lore II, Dune 2000.&lt;br /&gt;
* 3 - Lands of Lore III (?), Blade Runner (?), Nox and Tiberian Sun. These VQAs are HiColor (15 bit).&lt;br /&gt;
&lt;br /&gt;
Flags most probably contain some flags. I know that bit 0 (LSB) and 2 (Overlay)&lt;br /&gt;
* bit 0 denotes whether the VQA has a soundtrack or not.&lt;br /&gt;
* (1 = Has sound, 0 = No sound)&lt;br /&gt;
* bit 2 denotes whether this movie is an overlay on the background video.&lt;br /&gt;
* (1 = Yes, 0 = No)&lt;br /&gt;
* Movie with bit 2 may play separately, but need apply VPTR\VPRZ chunks&lt;br /&gt;
* to &amp;quot;Null&amp;quot; Vector Pointer Table (filled by zeros) for each frame.&lt;br /&gt;
&lt;br /&gt;
If NumFrames of background movie less than NumFrames of overlay movie&lt;br /&gt;
* then background movie will be repeat, while will not finish overlay movie.&lt;br /&gt;
&lt;br /&gt;
Width is usually 320, Height is usually 156 or 200 although one movie in&lt;br /&gt;
* Red Alert is 640x400 in size (the start movie for the Win95 version).&lt;br /&gt;
&lt;br /&gt;
Each frame of a VQA movie is comprised of a series of blocks that are&lt;br /&gt;
BlockW pixels in width and BlockH pixels in height. Imagine the frame&lt;br /&gt;
is a mosaic with the blocks being 'pieces' that make up the frame.&lt;br /&gt;
&lt;br /&gt;
BlockW is the width and BlockH is the height of each screen block.&lt;br /&gt;
In VQAs version 2 (and perhaps 1) the blocks are usually 4x2.&lt;br /&gt;
&lt;br /&gt;
FrameRate is always 15 in C&amp;amp;C and RA and seems to be 10 in LoK III.&lt;br /&gt;
&lt;br /&gt;
CBParts denotes how many frames use the same lookup table. It also&lt;br /&gt;
implies how many parts the new block lookup table is split into.&lt;br /&gt;
In C&amp;amp;C and RA it is always 8. When CBParts is 0, and movie have&lt;br /&gt;
CBP? chunks, then need see CIND chunk.&lt;br /&gt;
&lt;br /&gt;
Colors indicates the maximum number of colors used by the VQA.&lt;br /&gt;
The HiColor VQAs have this set to 0, while the old movies have&lt;br /&gt;
256 or less in here.&lt;br /&gt;
&lt;br /&gt;
Offset X and Offset Y uses only when there is bit 2 in the flags.&lt;br /&gt;
Usually uses chunk CLIP too, where the width and height of the&lt;br /&gt;
background movie are specified.&lt;br /&gt;
&lt;br /&gt;
MaxVPT?Size tells you the size of the largest VPTZ or VPRZ chunk, in bytes.&lt;br /&gt;
&lt;br /&gt;
Freq is usually 22050 Hz. Note that version 1 movies can have this set &lt;br /&gt;
to 0 Hz. In that case, you should use 22050 Hz.&lt;br /&gt;
&lt;br /&gt;
Channels specifies the number of sound channels, i.e. is the sound&lt;br /&gt;
mono or stereo. Channels=1 -&amp;gt; sound is mono, Channels=2 -&amp;gt; stereo.&lt;br /&gt;
C&amp;amp;C and RA almost always use mono sound. &lt;br /&gt;
Version 1 can have this set to 0, but you should use 1 (mono sound).&lt;br /&gt;
The majority of Tiberian Sun movies use stereo sound instead.&lt;br /&gt;
&lt;br /&gt;
* Bits indicates whether the sound is 8 bit or 16 bit.&lt;br /&gt;
Bits=8 -&amp;gt; 8 bit sound, Bits=16 -&amp;gt; 16 bit sound (surprise! :). &lt;br /&gt;
The Legend of Kyrandia III: Malcolm's Revenge uses 8 bits where&lt;br /&gt;
C&amp;amp;C, RA, TS, Dune 2000, Lands of Lore III use 16 bits.&lt;br /&gt;
Note, again, that version 1 of the VQAs can have this set to 0 in&lt;br /&gt;
which case 8 bits are assumed.&lt;br /&gt;
&lt;br /&gt;
MaxCBFZSize tells you the size of the largest CBFZ chunk, in bytes.&lt;br /&gt;
&lt;br /&gt;
Following the chunk data are the sub-chunks.&lt;br /&gt;
&lt;br /&gt;
===FINF chunk (&amp;quot;Frame INFormation&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This chunk contains the positions (absolute from the start of the VQA)&lt;br /&gt;
of data for every frame.&lt;br /&gt;
That means that it points to the SND? chunk associated with that frame,&lt;br /&gt;
which is followed by a VQFR chunk containing frame's video data.&lt;br /&gt;
&lt;br /&gt;
The positions are given as LONGs which are in normal Intel byte order.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' Some frame positions are 0x40000000 too large. This is a flag&lt;br /&gt;
indicating those frames contain a new color palette. To get the&lt;br /&gt;
actual positions, you should subtract 0x40000000 from the values.&lt;br /&gt;
&lt;br /&gt;
'''NOTE #2:''' To get the actual position of the frame data you have to multiply&lt;br /&gt;
the value by 2. This is why some chunk IDs start with 0x00. Since&lt;br /&gt;
you multiply by 2, you can't get an odd chunk position so if the&lt;br /&gt;
chunk position would normally be odd, a 0x00 is inserted to make&lt;br /&gt;
it even.&lt;br /&gt;
&lt;br /&gt;
===SND? chunk (&amp;quot;SouND&amp;quot; ???)===&lt;br /&gt;
These chunks contain the sound data for the movie. The last byte of the ID&lt;br /&gt;
can be either '0', '1' or '2' so the actual IDs would be &amp;quot;SND0&amp;quot;, &amp;quot;SND1&amp;quot;&lt;br /&gt;
and &amp;quot;SND2&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
In VQAs version 1 (Legend of Kyrandia 3) there are ''NumFrames'' sound chunks.&lt;br /&gt;
Old 8 bit VQA movies of version 2 have ''NumFrames+1'' sound chunks.&lt;br /&gt;
Note, however, that Dune2000 movies, which are also version 2, but HiColor&lt;br /&gt;
have ''NumFrames'' sound chunks, instead.&lt;br /&gt;
Version 3 movies also have ''NumFrames'' sound chunks.&lt;br /&gt;
&lt;br /&gt;
In case of the old, 8 bit VQAs, the first chunk contains half a second&lt;br /&gt;
(ver. 2) or more (ver. 1) of the wave data, in all (?) the HiColor movies&lt;br /&gt;
the first chunk contains exactly the amount of sound required for one&lt;br /&gt;
frame of the movie. The downside is this requires a somewhat more advanced &lt;br /&gt;
buffering technique on the side of the player in order to allow smooth &lt;br /&gt;
playback.&lt;br /&gt;
&lt;br /&gt;
===SND0 chunk===&lt;br /&gt;
&lt;br /&gt;
This one contains the raw 8 or 16 bit PCM wave data. If the data is&lt;br /&gt;
8 bit, the sound is unsigned and if it is 16 bit, the samples are&lt;br /&gt;
signed.&lt;br /&gt;
&lt;br /&gt;
===SND1 chunk===&lt;br /&gt;
&lt;br /&gt;
It contains 8 bit sound compressed using Westwood's own&lt;br /&gt;
proprietary ADPCM algorithm. The chunk has a 4 byte header:&lt;br /&gt;
&lt;br /&gt;
  struct SND1Header&lt;br /&gt;
  {&lt;br /&gt;
   short OutSize;&lt;br /&gt;
   short Size;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
These values are needed for the decoding algoritm (see APPENDIX C).&lt;br /&gt;
The encoded samples follow immediately after the header.&lt;br /&gt;
It's important to know this algorithm produces UNSIGNED sound, unlike&lt;br /&gt;
the IMA ADPCM algorithm supplied here (see below). It is, however very&lt;br /&gt;
simple to adapt both algorithms to produce either signed or unsigned&lt;br /&gt;
sample output...&lt;br /&gt;
&lt;br /&gt;
===SND2 chunk===&lt;br /&gt;
&lt;br /&gt;
It contains the 16 bit sound data compressed using the IMA ADPCM&lt;br /&gt;
algorithm which compresses 16 bits into 4. That's why the SND2 chunks&lt;br /&gt;
are 4 times smaller than SND0 chunks and they are used almost all&lt;br /&gt;
the time. For the description of the algorithm, see later in the document.&lt;br /&gt;
  &lt;br /&gt;
Different VQA versions have different stereo sample layout. In case&lt;br /&gt;
of Tiberian Sun stereo sound is encoded the same way as mono except the&lt;br /&gt;
SND2 chunk is split into two halfs. The first half contains the left channel&lt;br /&gt;
sound and the second half contains the right channel. The layout of&lt;br /&gt;
nibbles is as follows: LL LL LL LL LL ... RR RR RR RR RR.&lt;br /&gt;
Old movies (C&amp;amp;C, RA) use a different layout. Here, the nibbles are packed&lt;br /&gt;
together like this: LL RR LL RR LL RR ... This means that two left channel&lt;br /&gt;
samples are packed into one byte and then two right channel samples are&lt;br /&gt;
packed into another.&lt;br /&gt;
&lt;br /&gt;
Naturally, the size of a stereo SND2 chunk is exactly twice as big as&lt;br /&gt;
a mono SND2.&lt;br /&gt;
&lt;br /&gt;
It is important to note that, when decoding, you have to keep separate &lt;br /&gt;
values of ''Index'' and ''Cur_Sample'' for each channel (see APPENDIX B).&lt;br /&gt;
&lt;br /&gt;
===VQFR chunk (&amp;quot;Vector Quantized FRame&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
A chunk that includes many nested sub-chunks which contain video data.&lt;br /&gt;
It doesn't contain any data itself so the sub-chunks follow immediately&lt;br /&gt;
after the VQFR chunk header.&lt;br /&gt;
All following sub-chunks are nested inside a VQFR chunk. They can all&lt;br /&gt;
contain '0' or 'Z' as the last byte of their ID.&lt;br /&gt;
&lt;br /&gt;
* If the last byte is '0' it means that the chunk data is uncompressed.&lt;br /&gt;
* If the last byte is 'Z' it means that the data is compressed using&lt;br /&gt;
Format80 compression. You can find its description in APPENDIX A.&lt;br /&gt;
&lt;br /&gt;
===CBF? chunk (&amp;quot;CodeBook, Full&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
Lookup table containing the screen block data as an array&lt;br /&gt;
of elements that each are BlockW*BlockH bytes long. It is always located&lt;br /&gt;
in the data for the first frame. In Vector Quantization terminology&lt;br /&gt;
these tables are called CODEBOOKS.&lt;br /&gt;
&lt;br /&gt;
There can be max. 0x0f00 of these elements (blocks) at any one time in &lt;br /&gt;
normal VQAs and 0xff00 in the hi-res VQAs (Red Alert 95 start movie) although&lt;br /&gt;
I seriously doubt that so many blocks (0xff00 = 65280 blocks) would&lt;br /&gt;
ever be used.&lt;br /&gt;
&lt;br /&gt;
The uncompressed version of these chunks (&amp;quot;CBF0&amp;quot;) is used mainly in&lt;br /&gt;
the original Command &amp;amp; Conquer, while the compressed version (&amp;quot;CBFZ&amp;quot;)&lt;br /&gt;
is used in C&amp;amp;C: Red Alert.&lt;br /&gt;
&lt;br /&gt;
===CBP? chunk (&amp;quot;CodeBook Part&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
Like CBF?, but it contains a part of the lookup table, so to get the new&lt;br /&gt;
complete table you need to append CBParts of these in frame order.&lt;br /&gt;
Once you get the complete table and display the current frame, replace&lt;br /&gt;
the old table with the new one.&lt;br /&gt;
As in CBF? chunk, the uncompressed chunks are used in C&amp;amp;C and the compressed&lt;br /&gt;
chunks are used in Red Alert.&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' If the chunks are CBFZ, first you need to append CBParts of them and&lt;br /&gt;
then decompress the data, NOT decompress each chunk individually.&lt;br /&gt;
&lt;br /&gt;
===CPL? chunk (&amp;quot;Color PaLette&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
The simplest one of all... Contains a palette for the VQA. It is an&lt;br /&gt;
array of red, green and blue values (in that order, all have a size of&lt;br /&gt;
1 byte). Seems that the values range from 0-255, but you should mask out&lt;br /&gt;
the bits 6 and 7 to get the correct palette (VGA hardware uses only&lt;br /&gt;
bits 0..5 anyway).&lt;br /&gt;
&lt;br /&gt;
===VPT? chunk: (&amp;quot;Vector Pointer Table&amp;quot; ???)===&lt;br /&gt;
&lt;br /&gt;
This chunk contains the indexes into the block lookup table which contains&lt;br /&gt;
the data to display the frame.&lt;br /&gt;
The image blocks are called VECTORS in Vector Quantization terminology.&lt;br /&gt;
&lt;br /&gt;
These chunks are always compressed, but I guess the uncompressed ones&lt;br /&gt;
can also be used (although this would lower the overall compression achieved).&lt;br /&gt;
&lt;br /&gt;
The size of this index table is ''(Width/BlockW)*(Height/BlockH)*2 bytes''.&lt;br /&gt;
&lt;br /&gt;
Now, there is a catch: version 2 VQAs use a different layout of the&lt;br /&gt;
index table than version 1 VQAs. I will first describe the version&lt;br /&gt;
2 table format, as it's more common.&lt;br /&gt;
&lt;br /&gt;
====VERSION 2 INDEX TABLE LAYOUT====&lt;br /&gt;
The index table is an array of CHARs and is split into 2 parts - the top&lt;br /&gt;
half and the bottom half.&lt;br /&gt;
&lt;br /&gt;
Now, if you want to diplay the block at coordinates (in block units),&lt;br /&gt;
say (bx,by) you should read two bytes from the table, one from the top&lt;br /&gt;
and one from the bottom half:&lt;br /&gt;
&lt;br /&gt;
  LoVal=Table[by*(Width/BlockW)+bx]&lt;br /&gt;
  HiVal=Table[(Width/BlockW)*(Height/BlockH)+by*(Width/BlockW)+bx]&lt;br /&gt;
&lt;br /&gt;
If HiVal=0x0f (0xff for the start movie of Red Alert 95) you should&lt;br /&gt;
simply fill the block with color LoVal, otherwise you should copy&lt;br /&gt;
the block with index number HiVal*256+LoVal from the lookup table.&lt;br /&gt;
&lt;br /&gt;
Do that for every block on the screen (remember, there are Width/BlockW&lt;br /&gt;
blocks in the horizontal direction and Height/BlockH blocks in the vertical&lt;br /&gt;
direction) and you've decoded your first frame!&lt;br /&gt;
&lt;br /&gt;
'''NOTE:''' I was unable to find an entry in the VQA header which determines&lt;br /&gt;
whether 0x0f or 0xff is used in HiVal to signal that the block&lt;br /&gt;
is of uniform color. I assume that the Wy entry of the header&lt;br /&gt;
implies this: If BlockW=2 -&amp;gt; 0x0f is used, if BlockH=4 -&amp;gt; 0xff is used.&lt;br /&gt;
&lt;br /&gt;
====VERSION 1 INDEX TABLE LAYOUT====&lt;br /&gt;
Here, the index table is simply an array of SHORTs written in normal, Intel&lt;br /&gt;
byte order.&lt;br /&gt;
&lt;br /&gt;
The LoVal and HiVal are given as:&lt;br /&gt;
&lt;br /&gt;
  LoVal=Table[(by*(Width/BlockW)+bx)*2]&lt;br /&gt;
  HiVal=Table[(by*(Width/BlockW)+bx)*2+1]&lt;br /&gt;
&lt;br /&gt;
If HiVal=0xff, the block is of uniform color which is (255-LoVal).&lt;br /&gt;
Otherwise, write the block with index number (HiVal*256+LoVal)/8.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Appendix A===&lt;br /&gt;
&lt;br /&gt;
FORMAT80 COMPRESSION METHOD&lt;br /&gt;
by Vladan Bato (bat22@geocities.com)&lt;br /&gt;
&lt;br /&gt;
There are several different commands, with different sizes : from 1 to 5 bytes.&lt;br /&gt;
The positions mentioned below always refer to the destination buffer (i.e.&lt;br /&gt;
the uncompressed image). The relative positions are relative to the current&lt;br /&gt;
position in the destination buffer, which is one byte beyond the last written&lt;br /&gt;
byte.&lt;br /&gt;
&lt;br /&gt;
I will give some sample code at the end.&lt;br /&gt;
&lt;br /&gt;
(1) 1 byte&lt;br /&gt;
      +---+---+---+---+---+---+---+---+&lt;br /&gt;
      | 1 | 0 |   |   |   |   |   |   |&lt;br /&gt;
      +---+---+---+---+---+---+---+---+&lt;br /&gt;
              \_______________________/&lt;br /&gt;
                         |&lt;br /&gt;
                       Count&lt;br /&gt;
&lt;br /&gt;
      This one means : copy next Count bytes as is from Source to Dest.&lt;br /&gt;
&lt;br /&gt;
(2) 2 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
  | 0 |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+&lt;br /&gt;
      \___________/\__________________________________________________/&lt;br /&gt;
            |                             |&lt;br /&gt;
         Count-3                    Relative Pos.&lt;br /&gt;
&lt;br /&gt;
  This means copy Count bytes from Dest at Current Pos.-Rel. Pos. to&lt;br /&gt;
  Current position.&lt;br /&gt;
  Note that you have to add 3 to the number you find in the bits 4-6 of the&lt;br /&gt;
  first byte to obtain the Count.&lt;br /&gt;
  Note that if the Rel. Pos. is 1, that means repeat Count times the previous&lt;br /&gt;
  byte.&lt;br /&gt;
&lt;br /&gt;
(3) 3 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---------------+---------------+&lt;br /&gt;
  | 1 | 1 |   |   |   |   |   |   |   |               |               |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +---------------+---------------+&lt;br /&gt;
          \_______________________/                  Pos&lt;br /&gt;
                     |&lt;br /&gt;
                 Count-3&lt;br /&gt;
&lt;br /&gt;
  Copy Count bytes from Pos, where Pos is absolute from the start of the&lt;br /&gt;
  destination buffer. (Pos is a word, that means that the images can't be&lt;br /&gt;
  larger than 64K)&lt;br /&gt;
&lt;br /&gt;
(4) 4 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+&lt;br /&gt;
  | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |   |       |       |  |       |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+&lt;br /&gt;
                                            Count          Color&lt;br /&gt;
&lt;br /&gt;
  Write Color Count times.&lt;br /&gt;
  (Count is a word, color is a byte)&lt;br /&gt;
&lt;br /&gt;
(5) 5 bytes&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+&lt;br /&gt;
  | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |   |       |       |  |       |       |&lt;br /&gt;
  +---+---+---+---+---+---+---+---+   +-------+-------+  +-------+-------+&lt;br /&gt;
                                            Count               Pos&lt;br /&gt;
&lt;br /&gt;
  Copy Count bytes from Dest. starting at Pos. Pos is absolute from the start&lt;br /&gt;
  of the Destination buffer.&lt;br /&gt;
  Both Count and Pos are words.&lt;br /&gt;
&lt;br /&gt;
These are all the commands I found out. Maybe there are other ones, but I&lt;br /&gt;
haven't seen them yet.&lt;br /&gt;
&lt;br /&gt;
All the images end with a 80h command.&lt;br /&gt;
&lt;br /&gt;
To make things more clearer here's a piece of code that will uncompress the&lt;br /&gt;
image.&lt;br /&gt;
&lt;br /&gt;
  DP = destination pointer&lt;br /&gt;
  SP = source pointer&lt;br /&gt;
  Source and Dest are the two buffers&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  SP:=0;&lt;br /&gt;
  DP:=0;&lt;br /&gt;
  repeat&lt;br /&gt;
    Com:=Source[SP];&lt;br /&gt;
    inc(SP);&lt;br /&gt;
    b7:=Com shr 7;  {b7 is bit 7 of Com}&lt;br /&gt;
    case b7 of&lt;br /&gt;
      0 : begin  {copy command (2)}&lt;br /&gt;
            {Count is bits 4-6 + 3}&lt;br /&gt;
            Count:=(Com and $7F) shr 4 + 3;&lt;br /&gt;
            {Position is bits 0-3, with bits 0-7 of next byte}&lt;br /&gt;
            Posit:=(Com and $0F) shl 8+Source[SP];&lt;br /&gt;
            Inc(SP);&lt;br /&gt;
            {Starting pos=Cur pos. - calculated value}&lt;br /&gt;
            Posit:=DP-Posit;&lt;br /&gt;
            for i:=Posit to Posit+Count-1 do&lt;br /&gt;
            begin&lt;br /&gt;
              Dest[DP]:=Dest[i];&lt;br /&gt;
              Inc(DP);&lt;br /&gt;
            end;&lt;br /&gt;
          end;&lt;br /&gt;
      1 : begin&lt;br /&gt;
            {Check bit 6 of Com}&lt;br /&gt;
            b6:=(Com and $40) shr 6;&lt;br /&gt;
            case b6 of&lt;br /&gt;
              0 : begin  {Copy as is command (1)}&lt;br /&gt;
                    Count:=Com and $3F;  {mask 2 topmost bits}&lt;br /&gt;
                    if Count=0 then break; {EOF marker}&lt;br /&gt;
                    for i:=1 to Count do&lt;br /&gt;
                    begin&lt;br /&gt;
                      Dest[DP]:=Source[SP];&lt;br /&gt;
                      Inc(DP);&lt;br /&gt;
                      Inc(SP);&lt;br /&gt;
                    end;&lt;br /&gt;
                  end;&lt;br /&gt;
              1 : begin  {large copy, very large copy and fill commands}&lt;br /&gt;
                    {Count = (bits 0-5 of Com) +3}&lt;br /&gt;
                    {if Com=FEh then fill, if Com=FFh then very large copy}&lt;br /&gt;
                    Count:=Com and $3F;&lt;br /&gt;
                    if Count&amp;lt;$3E then {large copy (3)}&lt;br /&gt;
                    begin&lt;br /&gt;
                      Inc(Count,3);&lt;br /&gt;
                      {Next word = pos. from start of image}&lt;br /&gt;
                      Posit:=Word(Source[SP]);&lt;br /&gt;
                      Inc(SP,2);&lt;br /&gt;
                      for i:=Posit to Posit+Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=Dest[i];&lt;br /&gt;
                        Inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end&lt;br /&gt;
                    else if Count=$3F then   {very large copy (5)}&lt;br /&gt;
                    begin&lt;br /&gt;
                      {next 2 words are Count and Pos}&lt;br /&gt;
                      Count:=Word(Source[SP]);&lt;br /&gt;
                      Posit:=Word(Source[SP+2]);&lt;br /&gt;
                      Inc(SP,4);&lt;br /&gt;
                      for i:=Posit to Posit+Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=Dest[i];&lt;br /&gt;
                        Inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end else&lt;br /&gt;
                    begin   {Count=$3E, fill (4)}&lt;br /&gt;
                      {Next word is count, the byte after is color}&lt;br /&gt;
                      Count:=Word(Source[SP]);&lt;br /&gt;
                      Inc(SP,2);&lt;br /&gt;
                      b:=Source[SP];&lt;br /&gt;
                      Inc(SP);&lt;br /&gt;
                      for i:=0 to Count-1 do&lt;br /&gt;
                      begin&lt;br /&gt;
                        Dest[DP]:=b;&lt;br /&gt;
                        inc(DP);&lt;br /&gt;
                      end;&lt;br /&gt;
                    end;&lt;br /&gt;
                  end;&lt;br /&gt;
            end;&lt;br /&gt;
          end;&lt;br /&gt;
    end;&lt;br /&gt;
  until false;&lt;br /&gt;
&lt;br /&gt;
Note that you won't be able to compile this code, because the typecasting&lt;br /&gt;
won't work. (But I'm sure you'll be able to fix it).&lt;br /&gt;
&lt;br /&gt;
===Appendix B===&lt;br /&gt;
IMA ADPCM DECOMPRESSION&lt;br /&gt;
by Vladan Bato  (bat22@geocities.com)&lt;br /&gt;
http://www.geocities.com/SiliconValley/8682&lt;br /&gt;
&lt;br /&gt;
Note that the current sample value and index into the Step Table should&lt;br /&gt;
be initialized to 0 at the start and are maintained across the chunks&lt;br /&gt;
(see below).&lt;br /&gt;
&lt;br /&gt;
====IMA-ADPCM DECOMPRESSION ====&lt;br /&gt;
&lt;br /&gt;
It is the exact opposite of the above. It receives 4-bit codes in input&lt;br /&gt;
and produce 16-bit samples in output.&lt;br /&gt;
&lt;br /&gt;
Again you have to mantain an Index into the Step Table an the current&lt;br /&gt;
sample value.&lt;br /&gt;
&lt;br /&gt;
The tables used are the same as for compression.&lt;br /&gt;
&lt;br /&gt;
Here's the code :&lt;br /&gt;
&lt;br /&gt;
  Index:=0;&lt;br /&gt;
  Cur_Sample:=0;&lt;br /&gt;
&lt;br /&gt;
  while there_is_more_data do&lt;br /&gt;
  begin&lt;br /&gt;
    Code:=Get_Next_Code;&lt;br /&gt;
&lt;br /&gt;
    if (Code and $8) &amp;lt;&amp;gt; 0 then Sb:=1 else Sb:=0;&lt;br /&gt;
    Code:=Code and $7;&lt;br /&gt;
    {Separate the sign bit from the rest}&lt;br /&gt;
&lt;br /&gt;
    Delta:=(Step_Table[Index]*Code) div 4 + Step_Table[Index] div 8;&lt;br /&gt;
    {The last one is to minimize errors}&lt;br /&gt;
&lt;br /&gt;
    if Sb=1 then Delta:=-Delta;&lt;br /&gt;
&lt;br /&gt;
    Cur_Sample:=Cur_Sample+Delta;&lt;br /&gt;
    if Cur_Sample&amp;gt;32767 then Cur_Sample:=32767&lt;br /&gt;
    else if Cur_Sample&amp;lt;-32768 then Cur_Sample:=-32768;&lt;br /&gt;
&lt;br /&gt;
    Output_Sample(Cur_Sample);&lt;br /&gt;
&lt;br /&gt;
    Index:=Index+Index_Adjust[Code];&lt;br /&gt;
    if Index&amp;lt;0 then Index:=0;&lt;br /&gt;
    if Index&amp;gt;88 the Index:=88;&lt;br /&gt;
  end;&lt;br /&gt;
&lt;br /&gt;
Again, this can be done more efficiently (no need for multiplication).&lt;br /&gt;
&lt;br /&gt;
The ''Get_Next_Code'' function should return the next 4-bit code. It must&lt;br /&gt;
extract it from the input buffer (note that two 4-bit codes are stored&lt;br /&gt;
in the same byte, the first one in the lower bits).&lt;br /&gt;
&lt;br /&gt;
The Output_Sample function should write the signed 16-bit sample to the&lt;br /&gt;
output buffer.&lt;br /&gt;
&lt;br /&gt;
=== Appendix A : THE INDEX ADJUSTMENT TABLE ===&lt;br /&gt;
&lt;br /&gt;
  Index_Adjust : array [0..7] of integer = (-1,-1,-1,-1,2,4,6,8);&lt;br /&gt;
&lt;br /&gt;
=== Appendix B : THE STEP TABLE ===&lt;br /&gt;
&lt;br /&gt;
  Steps_Table : array [0..88] of integer =(&lt;br /&gt;
        7,     8,     9,     10,    11,    12,     13,    14,    16,&lt;br /&gt;
        17,    19,    21,    23,    25,    28,     31,    34,    37,&lt;br /&gt;
        41,    45,    50,    55,    60,    66,     73,    80,    88,&lt;br /&gt;
        97,    107,   118,   130,   143,   157,    173,   190,   209,&lt;br /&gt;
        230,   253,   279,   307,   337,   371,    408,   449,   494,&lt;br /&gt;
        544,   598,   658,   724,   796,   876,    963,   1060,  1166,&lt;br /&gt;
        1282,  1411,  1552,  1707,  1878,  2066,   2272,  2499,  2749,&lt;br /&gt;
        3024,  3327,  3660,  4026,  4428,  4871,   5358,  5894,  6484,&lt;br /&gt;
        7132,  7845,  8630,  9493,  10442, 11487,  12635, 13899, 15289,&lt;br /&gt;
        16818, 18500, 20350, 22385, 24623, 27086,  29794, 32767 );&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Appendix C===&lt;br /&gt;
WESTWOOD STUDIOS' ADPCM DECOMPRESSION&lt;br /&gt;
by Asatur V. Nazarian (samael@avn.mccme.ru)&lt;br /&gt;
&lt;br /&gt;
==== WS ADPCM Decompression Algorithm ====&lt;br /&gt;
&lt;br /&gt;
Each SND1 chunk may be decompressed independently of others. This lets you&lt;br /&gt;
implement seeking/skipping for WS ADPCM sounds (unlike IMA ADPCM ones).&lt;br /&gt;
But during the decompression of the given chunk a variable (''CurSample'') should&lt;br /&gt;
be maintained for this whole chunk:&lt;br /&gt;
&lt;br /&gt;
 SHORT CurSample;&lt;br /&gt;
 BYTE  InputBuffer[InputBufferSize]; // input buffer containing the whole chunk&lt;br /&gt;
 WORD  wSize, wOutSize; // Size and OutSize values from this chunk's header&lt;br /&gt;
 BYTE  code;&lt;br /&gt;
 CHAR  count; // this is a signed char!&lt;br /&gt;
 WORD  i; // index into InputBuffer&lt;br /&gt;
 WORD  input; // shifted input&lt;br /&gt;
 &lt;br /&gt;
 if (wSize==wOutSize) // such chunks are NOT compressed&lt;br /&gt;
 {&lt;br /&gt;
  for (i=0;i&amp;lt;wOutSize;i++)&lt;br /&gt;
      Output(InputBuffer[i]); // send to output stream&lt;br /&gt;
  return; // chunk is done!&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // otherwise we need to decompress chunk&lt;br /&gt;
 &lt;br /&gt;
 CurSample=0x80; // unsigned 8-bit&lt;br /&gt;
 i=0;&lt;br /&gt;
 &lt;br /&gt;
 // note that wOutSize value is crucial for decompression!&lt;br /&gt;
 &lt;br /&gt;
 while (wOutSize&amp;gt;0) // until wOutSize is exhausted!&lt;br /&gt;
 {&lt;br /&gt;
  input=InputBuffer[i++];&lt;br /&gt;
  input&amp;lt;&amp;lt;=2;&lt;br /&gt;
  code=HIBYTE(input);&lt;br /&gt;
  count=LOBYTE(input)&amp;gt;&amp;gt;2;&lt;br /&gt;
  switch (code) // parse code&lt;br /&gt;
  {&lt;br /&gt;
    case 2: // no compression...&lt;br /&gt;
	 if (count &amp;amp; 0x20)&lt;br /&gt;
	 {&lt;br /&gt;
	   count&amp;lt;&amp;lt;=3;		// here it's significant that (count) is signed:&lt;br /&gt;
	   CurSample+=count&amp;gt;&amp;gt;3; // the sign bit will be copied by these shifts!&lt;br /&gt;
&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize--; // one byte added to output&lt;br /&gt;
	 }&lt;br /&gt;
	 else // copy (count+1) bytes from input to output&lt;br /&gt;
	 {&lt;br /&gt;
	   for (count++;count&amp;gt;0;count--,wOutSize--,i++)&lt;br /&gt;
	       Output(InputBuffer[i]);&lt;br /&gt;
	   CurSample=InputBuffer[i-1]; // set (CurSample) to the last byte sent to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    case 1: // ADPCM 8-bit -&amp;gt; 4-bit&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--) // decode (count+1) bytes&lt;br /&gt;
	 {&lt;br /&gt;
	   code=InputBuffer[i++];&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable4bit[(code &amp;amp; 0x0F)]; // lower nibble&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable4bit[(code &amp;gt;&amp;gt; 4)]; // higher nibble&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize-=2; // two bytes added to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    case 0: // ADPCM 8-bit -&amp;gt; 2-bit&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--) // decode (count+1) bytes&lt;br /&gt;
	 {&lt;br /&gt;
	   code=InputBuffer[i++];&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[(code &amp;amp; 0x03)]; // lower 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;2) &amp;amp; 0x03)]; // lower middle 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;4) &amp;amp; 0x03)]; // higher middle 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   CurSample+=WSTable2bit[((code&amp;gt;&amp;gt;6) &amp;amp; 0x03)]; // higher 2 bits&lt;br /&gt;
 &lt;br /&gt;
	   CurSample=Clip8BitSample(CurSample);&lt;br /&gt;
	   Output((BYTE)CurSample);&lt;br /&gt;
 &lt;br /&gt;
	   wOutSize-=4; // 4 bytes sent to output&lt;br /&gt;
	 }&lt;br /&gt;
	 break;&lt;br /&gt;
    default: // just copy (CurSample) (count+1) times to output&lt;br /&gt;
	 for (count++;count&amp;gt;0;count--,wOutSize--)&lt;br /&gt;
	     Output((BYTE)CurSample);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
HIBYTE and LOBYTE are just higher and lower bytes of WORD:&lt;br /&gt;
 #define HIBYTE(word) ((word) &amp;gt;&amp;gt; 8)&lt;br /&gt;
 #define LOBYTE(word) ((word) &amp;amp; 0xFF)&lt;br /&gt;
Note that depending on your compiler you may need to use additional byte&lt;br /&gt;
separation in these defines, e.g. (((byte) &amp;gt;&amp;gt; 8) &amp;amp; 0xFF). The same holds for&lt;br /&gt;
4-bit and 2-bit nibble separation in the code above.&lt;br /&gt;
&lt;br /&gt;
''WSTable4bit'' and ''WSTable2bit'' are the delta tables given in the next section.&lt;br /&gt;
&lt;br /&gt;
Output() is just a placeholder for any action you would like to perform for&lt;br /&gt;
decompressed sample value.&lt;br /&gt;
&lt;br /&gt;
Clip8BitSample is quite evident:&lt;br /&gt;
&lt;br /&gt;
 SHORT Clip8BitSample(SHORT sample)&lt;br /&gt;
 {&lt;br /&gt;
  if (sample&amp;gt;255)&lt;br /&gt;
     return 255;&lt;br /&gt;
  else if (sample&amp;lt;0)&lt;br /&gt;
     return 0;&lt;br /&gt;
  else&lt;br /&gt;
     return sample;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This algorithm is ONLY for mono 8-bit unsigned sound, as I've never seen any&lt;br /&gt;
other sound format used with WS ADPCM compression.&lt;br /&gt;
&lt;br /&gt;
Of course, the decompression routine described above may be greatly&lt;br /&gt;
optimized.&lt;br /&gt;
&lt;br /&gt;
==== WS ADPCM Tables ====&lt;br /&gt;
&lt;br /&gt;
 CHAR WSTable2bit[]=&lt;br /&gt;
 {&lt;br /&gt;
    -2,&lt;br /&gt;
    -1,&lt;br /&gt;
     0,&lt;br /&gt;
     1&lt;br /&gt;
 };&lt;br /&gt;
 &lt;br /&gt;
 CHAR WSTable4bit[]=&lt;br /&gt;
 {&lt;br /&gt;
    -9, -8, -6, -5, -4, -3, -2, -1,&lt;br /&gt;
     0,  1,  2,  3,  4,  5,  6,  8&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
==HC_VQA.txt==&lt;br /&gt;
by Gordan Ugarkovic (ugordan@yahoo.com)&lt;br /&gt;
http://members.xoom.com/ugordan&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This document describes how to view Westwood's HiColor VQA movies. These&lt;br /&gt;
are version 3 movies, but there are at least some version 2 VQAs that are&lt;br /&gt;
in this format. I will not describe the whole VQA layout here, I will&lt;br /&gt;
just explain how to display the VIDEO stream of the VQA, that is, how&lt;br /&gt;
to decompress the CBFZ and VPTR/VPRZ chunks.&lt;br /&gt;
&lt;br /&gt;
First a little warning: I'm not sure which flag denotes the VQA is 8 bit&lt;br /&gt;
or 15 bit. I'm pretty convinced it's either bit 4 (0x10) or bit 2 (0x04)&lt;br /&gt;
of the Flags entry (see my VQA_INFO.TXT) in the header. Another way would &lt;br /&gt;
be to check the Colors entry in the header, if it is 0 it could imply &lt;br /&gt;
a HiColor movie.&lt;br /&gt;
&lt;br /&gt;
There's a major difference between the old (8 bit, 256 color) and the new,&lt;br /&gt;
HiColor VQAs. Lookup tables are no longer split up into several CBP?&lt;br /&gt;
chunks, instead they come in one piece (a CBFZ chunk). Two lookup tables&lt;br /&gt;
can now be many frames apart, not just 8 (as usual). This is indicated&lt;br /&gt;
by the CBParts entry of the header (see VQA_INFO.TXT), which is set to 0.&lt;br /&gt;
Subsequent frames use the last lookup table loaded, of course.&lt;br /&gt;
&lt;br /&gt;
Another thing: It appears the first CBFZ chunk comes inside the VQFR chunk&lt;br /&gt;
but the other ones seem to be located inside their own chunks,&lt;br /&gt;
called VQFL, which are followed by the usual VQFR chunks (containing&lt;br /&gt;
VPTR/VPRZ chunks).&lt;br /&gt;
&lt;br /&gt;
Also, the movies are 15 bit, NOT 16 bit. There is a difference because&lt;br /&gt;
in 16 bit color depth there are 6 bits for the green channel, but&lt;br /&gt;
the VQAs use 5.&lt;br /&gt;
&lt;br /&gt;
=== The CBFZ chunks===&lt;br /&gt;
&lt;br /&gt;
These are a bit modified since the 8 bit VQAs. If the first byte of the&lt;br /&gt;
chunk is not NULL (0x00), it means the chunk is compressed using the&lt;br /&gt;
standard Format80 algorithm (see Vladan Bato's text on C&amp;amp;C file formats),&lt;br /&gt;
starting from that byte. If the first byte is NULL, the chunk is compressed&lt;br /&gt;
using a modified version of Format80 (see below), starting from the next&lt;br /&gt;
byte of the chunk. The original Format80 algorithm is used when the&lt;br /&gt;
amount of data to be compressed is less than 64 KB, otherwise the 'new'&lt;br /&gt;
algorithm is used.&lt;br /&gt;
&lt;br /&gt;
When decompressed properly, a CBFZ chunk expands into 15 bit pixels packed&lt;br /&gt;
as shorts in normal Intel byte order. The red, green and blue values are&lt;br /&gt;
packed like this:&lt;br /&gt;
&lt;br /&gt;
  15      bit      0&lt;br /&gt;
   arrrrrgg gggbbbbb&lt;br /&gt;
   HI byte  LO byte&lt;br /&gt;
&lt;br /&gt;
The r,g,b values make up a pixel and they can range from 0-31.&lt;br /&gt;
As in the old CBFZ chunks, these pixels make up the block lookup table&lt;br /&gt;
(also called a codebook). The a (alpha) value is used in Blade Runner in&lt;br /&gt;
overlay videos to indicate a transparent pixel.&lt;br /&gt;
&lt;br /&gt;
=== The VPTR chunks===&lt;br /&gt;
&lt;br /&gt;
These chunks use some sort of differential, run-length algorithm that&lt;br /&gt;
only records changes from the previous frame. Therefore, the previous&lt;br /&gt;
frame bitmap must be maintained throughout all the frames (you could&lt;br /&gt;
just draw the blocks that changed, though). This makes dropping frames&lt;br /&gt;
(in case of bad performance) impossible.&lt;br /&gt;
&lt;br /&gt;
When decoding, you take a short int (Intel) from the chunk and examine &lt;br /&gt;
its 3 most significant bits (bits 15,14,13). These bits make up a &lt;br /&gt;
code prefix that determines which action is to be done.&lt;br /&gt;
&lt;br /&gt;
Here's a list of the prefixes I encountered and their description&lt;br /&gt;
(Val is the short int value):&lt;br /&gt;
&lt;br /&gt;
  BITS - MEANING&lt;br /&gt;
&lt;br /&gt;
   000 - Skip Count blocks. Count is (Val &amp;amp; 0x1fff).&lt;br /&gt;
&lt;br /&gt;
   001 - Write block number (Val &amp;amp; 0xff) Count times.&lt;br /&gt;
         Count is (((Val/256) &amp;amp; 0x1f)+1)*2. Note that this can only&lt;br /&gt;
         index the first 256 blocks.&lt;br /&gt;
&lt;br /&gt;
   010 - Write block number (Val &amp;amp; 0xff) and then write Count blocks&lt;br /&gt;
         getting their indexes by reading next Count bytes from&lt;br /&gt;
         the VPTR chunk. Count is (((Val/256) &amp;amp; 0x1f)+1)*2.&lt;br /&gt;
         Again, the block numbers range from 0-255.&lt;br /&gt;
&lt;br /&gt;
   011 - Write block (Val &amp;amp; 0x1fff).&lt;br /&gt;
&lt;br /&gt;
   100 - Same as 011 but skip pixels with alpha bit set.&lt;br /&gt;
&lt;br /&gt;
   101 - Write block (Val &amp;amp; 0x1fff) Count times. Count is the next&lt;br /&gt;
         byte from the VPTR chunk.&lt;br /&gt;
&lt;br /&gt;
   110 - Same as 101 but skip pixels with alpha bit set.&lt;br /&gt;
&lt;br /&gt;
After this, you take the next short int and repeat the above process.&lt;br /&gt;
&lt;br /&gt;
Prefixes 100 and 110 are used in Blade Runner for overlay videos.&lt;br /&gt;
&lt;br /&gt;
Every row of blocks is processed individually of others.&lt;br /&gt;
When you encounter the end of a row, proceed to the next row&lt;br /&gt;
(blocks are processed left to right, top to down).&lt;br /&gt;
Repeat this process until all blocks in the frame are covered and&lt;br /&gt;
that's it!&lt;br /&gt;
&lt;br /&gt;
Note that the above implies an absolute maximum of 8192 blocks (0x1fff+1). &lt;br /&gt;
&lt;br /&gt;
As for the VPRZ chunks, these are just VPTR chunks compressed with&lt;br /&gt;
the standard Format80 algorithm.&lt;br /&gt;
&lt;br /&gt;
=== The modified Format80 scheme ===&lt;br /&gt;
&lt;br /&gt;
This is really only a small modification of the basic algorithm.&lt;br /&gt;
The only commands that are modified are commands (3) and (5)&lt;br /&gt;
See Vladan's text). Instead of using offsets ABSOLUTE from&lt;br /&gt;
the start of the destination buffer, offsets RELATIVE to the&lt;br /&gt;
current destination pointer are used. If you ask me, I don't see&lt;br /&gt;
why this approach wasn't used in the first place as it would&lt;br /&gt;
suffer no disadvantage with the old files and it would be much&lt;br /&gt;
easier to compress even larger amounts of data. The guys at WW&lt;br /&gt;
were just careless, I guess... :-)&lt;br /&gt;
&lt;br /&gt;
Anyway, in Vladan's algorithm, there is a line in&lt;br /&gt;
command (3) that says:&lt;br /&gt;
    Posit:=Word(Source[SP]);&lt;br /&gt;
it should say:&lt;br /&gt;
    Posit:=DP-Word(Source[SP]);&lt;br /&gt;
&lt;br /&gt;
Likewise, for command (5):&lt;br /&gt;
    Posit:=Word(Source[SP+2]);&lt;br /&gt;
it should be:&lt;br /&gt;
    Posit:=DP-Word(Source[SP+2]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Games Using VQA ==&lt;br /&gt;
&lt;br /&gt;
=== Version 1 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/win3x/legend-of-kyrandia-malcolms-revenge The Legend of Kyrandia: Malcolm's Revenge]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/monopoly Monopoly] (Version 1???)&lt;br /&gt;
&lt;br /&gt;
=== Versions 2 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/dos/command-conquer Command &amp;amp; Conquer]&lt;br /&gt;
* [http://www.mobygames.com/game/dos/command-conquer-red-alert Command &amp;amp; Conquer: Red Alert]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/command-conquer-sole-survivor Command &amp;amp; Conquer: Sole Survivor]&lt;br /&gt;
* [http://www.mobygames.com/game/lands-of-lore-guardians-of-destiny Lands of Lore: Guardians of Destiny]&lt;br /&gt;
&lt;br /&gt;
=== Versions 3 ===&lt;br /&gt;
&lt;br /&gt;
* [http://www.mobygames.com/game/windows/blade-runner Blade Runner]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/command-conquer-tiberian-sun Command &amp;amp; Conquer: Tiberian Sun]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/dune-2000 Dune 2000]&lt;br /&gt;
* [http://www.mobygames.com/game/lands-of-lore-iii Lands of Lore III]&lt;br /&gt;
* [http://www.mobygames.com/game/windows/nox Nox]&lt;br /&gt;
&lt;br /&gt;
==Other Documentation==&lt;br /&gt;
http://www.gamers.org/pub/idgames2/planetquake/planetcnc/cncdz/ has lots of data about this format. See vqa_overview.zip and vqafilesguild.zip. Also there is a decoder (vqatoavi) that decodes the dune2000 sample. &lt;br /&gt;
&lt;br /&gt;
[[Category:Game Formats]]&lt;br /&gt;
[[Category:Video Codecs]]&lt;br /&gt;
[[Category:Incomplete Video Codecs]]&lt;/div&gt;</summary>
		<author><name>Andrew Dream</name></author>
	</entry>
</feed>