Format docs
This commit is contained in:
350
doc/vqa_frmt.txt
Normal file
350
doc/vqa_frmt.txt
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
.VQA files
|
||||||
|
by Aaron Glover (arn@ibm.net)
|
||||||
|
|
||||||
|
Each VQA has a 62-byte header, as follows:
|
||||||
|
|
||||||
|
VQAHeader: record
|
||||||
|
TextFORM: array[0..3] of Char; {Always 'FORM'}
|
||||||
|
FileBTF: LongInt; {Reverse - Bytes to follow}
|
||||||
|
TextWVQAVQHD: array[0..7] of Char; {Always 'WVQAVQHD'}
|
||||||
|
RStartPos: LongInt; {Reverse - Relative start position}
|
||||||
|
Unknown1: Word;
|
||||||
|
Unknown2: Word;
|
||||||
|
NumFrames: Word;
|
||||||
|
Width: Word;
|
||||||
|
Height: Word;
|
||||||
|
Unknown3: Word;
|
||||||
|
Unknown4: Word;
|
||||||
|
Unknown5: Word;
|
||||||
|
Unknown6: Word;
|
||||||
|
Unknown7: LongInt;
|
||||||
|
Unknown8: Word; {This changes}
|
||||||
|
Unknown9: Word;
|
||||||
|
Unknown10: Word;
|
||||||
|
Unknown11: array[0..13] of Char;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Following the header, there are a number of `sub-files' that each
|
||||||
|
have a header of 8 bytes. The first four are the name (or type) of
|
||||||
|
the sub-file, the next four are a reverse LongInt that equals the
|
||||||
|
number of sub-file data bytes to follow (sub-file size minus sub-file
|
||||||
|
header size).
|
||||||
|
|
||||||
|
By `reverse LongInt', I mean a 4-byte Integer value stored backwards.
|
||||||
|
For example, take the the decimal value 77236. In hexadecimal it's
|
||||||
|
12DB4h, and would be stored in a binary file as the bytes B4 2D 01
|
||||||
|
00. As a reverse LongInt, it would be stored as 00 01 2D B4. More
|
||||||
|
human readable, but not how computers work.
|
||||||
|
|
||||||
|
Some sub-file names seem to start with a null byte (00h). There is a
|
||||||
|
reason for this, which will become apparent later. Just ignore the
|
||||||
|
null byte and assume the next one is the start of the sub-file
|
||||||
|
header.
|
||||||
|
|
||||||
|
So, after the header, you should find the something like the
|
||||||
|
following sub-files:
|
||||||
|
FINF
|
||||||
|
SND2
|
||||||
|
SND2
|
||||||
|
VQFR
|
||||||
|
SND2
|
||||||
|
VQFR
|
||||||
|
SND2
|
||||||
|
VQFR
|
||||||
|
SND2
|
||||||
|
VQFR
|
||||||
|
...
|
||||||
|
|
||||||
|
Each VQFR sub-file itself has sub-files. If you treat each VQFR sub-
|
||||||
|
file as if the `data bytes to follow' value was zero, you should get
|
||||||
|
something like:
|
||||||
|
FINF
|
||||||
|
SND2
|
||||||
|
SND2
|
||||||
|
VQFR
|
||||||
|
CBF0
|
||||||
|
CBP0
|
||||||
|
CPL0
|
||||||
|
VPTZ
|
||||||
|
SND2
|
||||||
|
VQFR
|
||||||
|
CBP0
|
||||||
|
VPTZ
|
||||||
|
SND2
|
||||||
|
VQFR
|
||||||
|
CBP0
|
||||||
|
VPTZ
|
||||||
|
SND2
|
||||||
|
VQFR
|
||||||
|
CBP0
|
||||||
|
VPTZ
|
||||||
|
...
|
||||||
|
|
||||||
|
FINF sub-file type:
|
||||||
|
|
||||||
|
First is a Word value that, if you multiply by two, gives the
|
||||||
|
position of the first data sub-file (relative to the start of the
|
||||||
|
VQA), then another Word value that seems to be always 4000h.
|
||||||
|
|
||||||
|
Following that is an array of LongInt values that, when multiplied by
|
||||||
|
two, give the position of each of the frame data sub-files (relative
|
||||||
|
to the start of the VQA). Each frame comprises of a SND2 sub-file
|
||||||
|
and a VQFR sub-file that follows immediately after.
|
||||||
|
|
||||||
|
This is why some of the sub-file names start with a null byte. Since
|
||||||
|
you have to multiply by two, each offset value must be even. So if
|
||||||
|
it would normally be odd, a null is inserted as the first byte to
|
||||||
|
make the sub-file's name offset even. Whew! Try saying that five
|
||||||
|
times fast!
|
||||||
|
|
||||||
|
The number of elements in the array is FrameNum (from the VQA header)
|
||||||
|
minus one.
|
||||||
|
|
||||||
|
I've noticed some of the LongInt values in this array are 40000000h
|
||||||
|
too large. I don't know why this is, at the moment I subtract
|
||||||
|
40000000h from values over 40000000h, it seems to work OK.
|
||||||
|
|
||||||
|
SND2 sub-file type:
|
||||||
|
|
||||||
|
I bet you've guessed this one. Well, so did I. Audio, right? I've
|
||||||
|
had a go at decoding them, and they seem to be in the same format as
|
||||||
|
the .AUD files, but I can't work them out (yet).
|
||||||
|
|
||||||
|
CBF0 sub-file type:
|
||||||
|
|
||||||
|
An array of eight-byte (4x2 pixel) uncompressed screen graphics.
|
||||||
|
I'll explain what they're used for when we get to the VPTZ sub-file
|
||||||
|
type. Just remember that it's an array of graphics that measure 4x2
|
||||||
|
screen pixels.
|
||||||
|
|
||||||
|
CBP0 sub-file type:
|
||||||
|
|
||||||
|
Eight of these (in frame order) appended together make up a complete
|
||||||
|
CBF0 sub-file that replaces the previous CBF0 sub-file information.
|
||||||
|
After you've displayed each eighth frame, you need to replace the
|
||||||
|
current CBF0 information with the new one you've made up from eight
|
||||||
|
CBP0 sub-files. Just do it, OK? This will make more sense when we
|
||||||
|
get to the VPTZ sub-file type.
|
||||||
|
|
||||||
|
CPL0 sub-file type:
|
||||||
|
|
||||||
|
The palette for the VQA file. An array of Red, Green and Blue byte
|
||||||
|
values (in that order). Note that there are sometimes less than 256
|
||||||
|
colours in the palette.
|
||||||
|
|
||||||
|
VPTZ sub-file type:
|
||||||
|
|
||||||
|
Well, here it is. This is the heart of the VQA file, the graphics.
|
||||||
|
Each VPTZ sub-file is compressed with the Format 80 method as
|
||||||
|
described later in this document.
|
||||||
|
|
||||||
|
When you decompress a VPTZ sub-file, you get an 80x156 graphic. The
|
||||||
|
top half (the first 78 lines) is the basis of the finished frame,
|
||||||
|
while the bottom half is a modifier for the pixels in the top half.
|
||||||
|
|
||||||
|
The final size of each VQA frame is 320x156. With the top half
|
||||||
|
(basis of the finished frame, remember) being 80x78, you can see that
|
||||||
|
we need to multiply by four in the X (horizontal) direction, and by
|
||||||
|
two in the Y (vertical) direction. Imagine that each pixel in the
|
||||||
|
top half in fact represents eight screen pixels, arranged in a 4x2
|
||||||
|
format.
|
||||||
|
|
||||||
|
I must distinguish between pixels and screen pixels. By pixel, I
|
||||||
|
mean one byte read from the decompressed VPTZ graphic, which, when
|
||||||
|
displayed on screen, measures 4x2 screen pixels.
|
||||||
|
|
||||||
|
Now, if you view a VQA, you can see that there is a higher resolution
|
||||||
|
used than each pixel being 4x2 screen pixels. This is where the
|
||||||
|
bottom half and the CBF0 sub-file type comes in.
|
||||||
|
|
||||||
|
The bottom half is an overlay of modifiers for the top half. That
|
||||||
|
is, the top-left pixel in the bottom half is a modifier for the top-
|
||||||
|
left pixel in the top half. The bottom half pixel values range from
|
||||||
|
00h to 0Fh.
|
||||||
|
|
||||||
|
0Fh means `no modifcation'. The corresponding pixel value in the top
|
||||||
|
half is copied eight times to produce the 4x2 screen pixel format.
|
||||||
|
|
||||||
|
00-0Eh are modifiers. If you treat these pixel values as the high
|
||||||
|
byte in a Word value, and treat the corresponding pixel value in the
|
||||||
|
top half as the low byte, you get the CBF0 array element number of
|
||||||
|
the 4x2 screen graphic you should display for that pixel. Make
|
||||||
|
sense? That is how the higher resolution is achieved.
|
||||||
|
|
||||||
|
Perhaps I should clarify. If TopByte is the top half pixel byte
|
||||||
|
value, and BottomByte is the bottom half pixel byte value, then the
|
||||||
|
4x2 screen pixel graphic is element number (BottomByte * 256 +
|
||||||
|
TopByte) in the CBF0 array.
|
||||||
|
|
||||||
|
Just display the frames in order, and presto! You have a VQA movie.
|
||||||
|
|
||||||
|
|
||||||
|
Format 80 compression method
|
||||||
|
by Vladan Bato (bat22@geocities.com)
|
||||||
|
|
||||||
|
----------
|
||||||
|
Format80
|
||||||
|
----------
|
||||||
|
|
||||||
|
There are several different commands, with different sizes : form 1 to 5
|
||||||
|
bytes.
|
||||||
|
The positions mentioned below always refer to the destination buffer (i.e.
|
||||||
|
the uncompressed image). The relative positions are relative to the current
|
||||||
|
position in the destination buffer, which is one byte beyond the last written
|
||||||
|
byte.
|
||||||
|
|
||||||
|
I will give some sample code at the end.
|
||||||
|
|
||||||
|
(1) 1 byte
|
||||||
|
+---+---+---+---+---+---+---+---+
|
||||||
|
| 1 | 0 | | | | | | |
|
||||||
|
+---+---+---+---+---+---+---+---+
|
||||||
|
\_______________________/
|
||||||
|
|
|
||||||
|
Count
|
||||||
|
|
||||||
|
This one means : copy next Count bytes as is from Source to Dest.
|
||||||
|
|
||||||
|
(2) 2 bytes
|
||||||
|
+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+
|
||||||
|
| 0 | | | | | | | | | | | | | | | | |
|
||||||
|
+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+
|
||||||
|
\___________/\__________________________________________________/
|
||||||
|
| |
|
||||||
|
Count-3 Relative Pos.
|
||||||
|
|
||||||
|
This means copy Count bytes from Dest at Current Pos.-Rel. Pos. to
|
||||||
|
Current position.
|
||||||
|
Note that you have to add 3 to the number you find in the bits 4-6 of the
|
||||||
|
first byte to obtain the Count.
|
||||||
|
Note that if the Rel. Pos. is 1, that means repeat Count times the previous
|
||||||
|
byte.
|
||||||
|
|
||||||
|
(3) 3 bytes
|
||||||
|
+---+---+---+---+---+---+---+---+ +---------------+---------------+
|
||||||
|
| 1 | 1 | | | | | | | | | |
|
||||||
|
+---+---+---+---+---+---+---+---+ +---------------+---------------+
|
||||||
|
\_______________________/ Pos
|
||||||
|
|
|
||||||
|
Count-3
|
||||||
|
|
||||||
|
Copy Count bytes from Pos, where Pos is absolute from the start of the
|
||||||
|
destination buffer. (Pos is a word, that means that the images can't be
|
||||||
|
larger than 64K)
|
||||||
|
|
||||||
|
(4) 4 bytes
|
||||||
|
+---+---+---+---+---+---+---+---+ +-------+-------+ +-------+
|
||||||
|
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | | | | | |
|
||||||
|
+---+---+---+---+---+---+---+---+ +-------+-------+ +-------+
|
||||||
|
Count Color
|
||||||
|
|
||||||
|
Write Color Count times.
|
||||||
|
(Count is a word, color is a byte)
|
||||||
|
|
||||||
|
(5) 5 bytes
|
||||||
|
+---+---+---+---+---+---+---+---+ +-------+-------+ +-------+-------+
|
||||||
|
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | | | | | | |
|
||||||
|
+---+---+---+---+---+---+---+---+ +-------+-------+ +-------+-------+
|
||||||
|
Count Pos
|
||||||
|
|
||||||
|
Copy Count bytes from Dest. starting at Pos. Pos is absolute from the start
|
||||||
|
of the Destination buffer.
|
||||||
|
Both Count and Pos are words.
|
||||||
|
|
||||||
|
These are all the commands I found out. Maybe there are other ones, but I
|
||||||
|
haven't seen them yet.
|
||||||
|
|
||||||
|
All the images end with a 80h command.
|
||||||
|
|
||||||
|
To make things more clearer here's a piece of code that will uncompress the
|
||||||
|
image.
|
||||||
|
|
||||||
|
DP = destination pointer
|
||||||
|
SP = source pointer
|
||||||
|
Source and Dest are the two buffers
|
||||||
|
|
||||||
|
|
||||||
|
SP:=0;
|
||||||
|
DP:=0;
|
||||||
|
repeat
|
||||||
|
Com:=Source[SP];
|
||||||
|
inc(SP);
|
||||||
|
b7:=Com shr 7; {b7 is bit 7 of Com}
|
||||||
|
case b7 of
|
||||||
|
0 : begin {copy command (2)}
|
||||||
|
{Count is bits 4-6 + 3}
|
||||||
|
Count:=(Com and $7F) shr 4 + 3;
|
||||||
|
{Position is bits 0-3, with bits 0-7 of next byte}
|
||||||
|
Posit:=(Com and $0F) shl 8+Source[SP];
|
||||||
|
Inc(SP);
|
||||||
|
{Starting pos=Cur pos. - calculated value}
|
||||||
|
Posit:=DP-Posit;
|
||||||
|
for i:=Posit to Posit+Count-1 do
|
||||||
|
begin
|
||||||
|
Dest[DP]:=Dest[i];
|
||||||
|
Inc(DP);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
1 : begin
|
||||||
|
{Check bit 6 of Com}
|
||||||
|
b6:=(Com and $40) shr 6;
|
||||||
|
case b6 of
|
||||||
|
0 : begin {Copy as is command (1)}
|
||||||
|
Count:=Com and $3F; {mask 2 topmost bits}
|
||||||
|
if Count=0 then break; {EOF marker}
|
||||||
|
for i:=1 to Count do
|
||||||
|
begin
|
||||||
|
Dest[DP]:=Source[SP];
|
||||||
|
Inc(DP);
|
||||||
|
Inc(SP);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
1 : begin {large copy, very large copy and fill commands}
|
||||||
|
{Count = (bits 0-5 of Com) +3}
|
||||||
|
{if Com=FEh then fill, if Com=FFh then very large copy}
|
||||||
|
Count:=Com and $3F;
|
||||||
|
if Count<$3E then {large copy (3)}
|
||||||
|
begin
|
||||||
|
Inc(Count,3);
|
||||||
|
{Next word = pos. from start of image}
|
||||||
|
Posit:=Word(Source[SP]);
|
||||||
|
Inc(SP,2);
|
||||||
|
for i:=Posit to Posit+Count-1 do
|
||||||
|
begin
|
||||||
|
Dest[DP]:=Dest[i];
|
||||||
|
Inc(DP);
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else if Count=$3F then {very large copy (5)}
|
||||||
|
begin
|
||||||
|
{next 2 words are Count and Pos}
|
||||||
|
Count:=Word(Source[SP]);
|
||||||
|
Posit:=Word(Source[SP+2]);
|
||||||
|
Inc(SP,4);
|
||||||
|
for i:=Posit to Posit+Count-1 do
|
||||||
|
begin
|
||||||
|
Dest[DP]:=Dest[i];
|
||||||
|
Inc(DP);
|
||||||
|
end;
|
||||||
|
end else
|
||||||
|
begin {Count=$3E, fill (4)}
|
||||||
|
{Next word is count, the byte after is color}
|
||||||
|
Count:=Word(Source[SP]);
|
||||||
|
Inc(SP,2);
|
||||||
|
b:=Source[SP];
|
||||||
|
Inc(SP);
|
||||||
|
for i:=0 to Count-1 do
|
||||||
|
begin
|
||||||
|
Dest[DP]:=b;
|
||||||
|
inc(DP);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
until false;
|
||||||
|
|
||||||
|
Note that you won't be able to compile this code, because the typecasting
|
||||||
|
won't work. (But I'm sure you'll be able to fix it).
|
||||||
|
|
||||||
Reference in New Issue
Block a user