Packed Animation File
- Extension: paf
- Company: Amazing Studio
- Samples: http://samples.mplayerhq.hu/game-formats/hod-paf/ (hod1-partial.paf)
There is also a PlayStation version of this game; which employs the PlayStation Motion Decoder format for its FMV cutscenes, rather than this proprietary PAF format.
The Windows version of Heart Of Darkness contains a large data file called hod.paf. This file contains 25 PAF files concatenated together and prepended with a header. The header is 100 bytes long and consists of 25 little endian, 32-bit numbers which are absolute offsets into the archive file. For example, bytes 0-3 contain the number 0x000000C8 = 100 which indicates that the first file occurs immediately following the 100-byte header.
A PAF consists of a header and a series of interleaved audio and video data. The video data is stored with a custom paletted video encoding method. The audio is stored as uncompressed, stereo, 16-bit, little endian PCM data.
All multi-byte numbers are stored in little endian format.
bytes 0-55 signature bytes 56-127 unknown; might be unused bytes for the signature block bytes 132-135 frame count bytes 140-143 video width bytes 144-147 video height bytes 152-155 read buffer size bytes 156-159 number of frame blocks to preload bytes 160-163 frame blocks count bytes 164-167 starting offset of multimedia data bytes 168-171 max video frames block count bytes 172-175 max audio frames block count
The 55-byte signature field should contain the string "Packed Animation File V1.0\n(c) 1992-96 Amazing Studio\x0A\x1A".
Several tables follow
The custom video codec is a paletted video codec that maintains an array of 256 3-byte values. Each value has a range of 6 bits (due to VGA-era graphics) which will probably need to be scaled to support the more common 8-bit RGB component model.
The video resolution is usually 256x192. In fact, the reverse engineered decoder hardcodes these values as constants although they appear to be encoded in the file header.
The video codec maintains a ring buffer of 4 frames that are all allocated and initialized to 0 at the beginning of the decode process. The codec may refer back to any of the other 3 frames while decoding the current video frame.
allocate array of 4 video_frames and initialize to 0 current_frame = 0 loop: decode data to video_frames[current_frame] current_frame = current_frame + 1
Decoding a video frame operates by processing the video frame as a sequence of opcodes and data:
consume next byte as opcode if bit 5 of opcode is set (opcode & 0x20): frame is a keyframe clear all 4 video_frames to 0 reset current_frame to 0 if bit 6 of opcode is set (opcode * 0x40): frame begins with palette update consume next byte as index consume next byte as count starting at the palette entry denoted by index, copy (count * 3) bytes from bytestream to palette process the lower 4 bits of the opcode
For the last step of the opcode decoding process, there are 4 valid encoding modes: 0, 1, 2, and 4.
Block-based motion compensation using 4x4 blocks with either horizontal or vertical vectors; might incorporate VQ as well.
Uncompressed data. This mode specifies that (width * height) bytes should be copied directly from the encoded buffer into the output. Note that there are 2 unknown bytes before the raw data (possibly chunk length data; this needs to be verified). Thus, a mode 1 chunk would be laid out as:
0x01 [2 unknown bytes] [width * height bytes of video data]
Copy reference frame: Consume the next byte in the stream as the reference frame (which should be 0, 1, 2, or 3, and should not be the same as the current frame number). Copy the specified reference frame to the current frame.
Run length encoding:
skip the next 2 bytes in the bytestream (chunk length?) while there is still data in the bytestream, consume next byte as code if code < 0: consume next byte as run code count = -code + 1 output count copies of run code else: count = code + 1 copy count bytes from bytestream to output