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