- Extension: hnm
- Company: CRYO Interactive Entertainment
- Samples: http://samples.mplayerhq.hu/game-formats/hnm/losteden-hnm4/
HNM4 is the third generation of the Cryo HNM video format. At least two decoder-incompatible variation of this format is known. The main version operates in 320x200x256 resolution. A simplified version operates in 640x480x256; this is used in "Aliens: A Comic Book Adventure". Both formats share the same container structure.
dword signature - file signature, "HNM4" dword unknown1 - probably format version word width - width of the frame word height - height of the frame dword filesize - size of the entrie hnm file dword frames - number of frames dword taboffset - offset of the TAB chunk word bits - sound sample resolution word channels - number of sound channels dword framesize - frame allocation size (width * height) byte unknown2 - seems to be unused byte copyright - "-Copyright CRYO-"
The rest of the file organized on manner similar to HNM (1) format. All single-frame related chunks combined to superchunks, where each superchunk starts with 32-bit value, lower 24 bits of it indicates superchunk size and highest 8 bits - flags (unknown). Each chunk starts with another 24+8 bits length header followed by 4 bytes of chunk ID (where only 2 bytes can be actually used), and chunk data. Near the end of file usually TAB chunk located, it doesn't have an upper-level superchunk.
Palette. Stored in same format as used in HNM (1).
- ALIENS: However, non-zero bit 23 of ChunkId indicates that palette stored in complete 8-bits-per-component format, unlike regular 6-bits otherwise.
Keyframe. See video decompression below.
Interframe. See video decompression below.
Sound data. DPCM-encoded sound fragment.
Intraframe decompression is simialar to HNM (1) 0xFE frames. But with a few changes. Chunk data starts with small 4-bytes header:
word width - width of the frame byte height - height of the frame (only lower byte of it) byte mode - rendering mode (always 0xFE ?)
Then immediately followed by LZ-packed raster (without 6-byte compression header as was used before). LZ decompression algorithm remains the same, but bitreader use 32-bits queue and processing bits from highest to lowest.
- You can safely ignore width/height/mode fields of the frame header as original decoder always renders whole frame regardless specified values.
Packed interframe consist of one or more compression codes. Depending on the code's count field following cases possible:
bits 0..4 : count = 0 5..7 : tag: 0: copy next two bytes of input to the output 1: skip (next byte of input) * 2 bytes of output 2: skip (next word of input) * 2 bytes of output 3: fill (next byte of input) * 2 of output with (next byte of input) else: finish the unpacking - or - 0..4 : count <> 0 5 : previous 6 : backline 7 : backward 8 : swap 9..23 : offset
In this case for (count) pixel pairs, copy them from the (output + offset * 2 - 32768) of the current frame to the output. Flag bits modify various parameters of this scheme (multiple flags can be used):
- previous - copy pairs from the previous frame
- backline - first pixel located 2 lines above + (swap)
- backward - advance source pixel pairs in backward order
- swap - swap pair's pixels
Since the original decoder uses double buffering, "previous frame" refers to the other buffer. Buffers are flipped after each frame. Intraframes are copied to both buffers. HNM4 (but not HNM4A): All Interframe operations are performed on non-postprocessed data.
HNM4A interframe is a simplified version of the HNM4 interframe. Code format:
bits 0..5 : count = 0 6..7 : tag: 0: skip (next byte of input) bytes of output 1: draw 1x2 column of the output with next 2 bytes of input 2: advance to the next line of output (but keep current x position) 3: finish the unpacking - or - 0..5 : count <> 0 6 : previous 7 : delta 8..23 : offset
For (count) 1x2 columns copy them from the (output + offset) to the output.
- previous - copy column from the previous frame
- delta - copy column from (output + offset - 65536)
For HNM4 (but not HNM4A movies) you need to perform extra frame postprocessing by swapping pixels using following self-explaining example (assuming 4xN image):
Just unpacked frame Final frame p0 p1 p2 p3 ==> p0 p2 p4 p6 p4 p5 p6 p7 p1 p3 p5 p7 ... ...
This applies to both intraframes and interframes.