Difference between revisions of "HNM6"

From MultimediaWiki
Jump to navigation Jump to search
Line 69: Line 69:


== Video Format ==
== Video Format ==
The video compression relies on a concept of Key-blocks, encoded with [[JPEG]]-like algorithm and motion blocks, derived from previously drawn blocks. The frame is encoded in 8x8 or smaller block. The Key-blocks are always 8x8 and directly encoded, while motion blocks heavily rely on [[D&C]] approach and various block transformation techniques. Generally, this codec is quite similar to [[4XM]] codec and [[Mobiclip]] codecs, because it's written by the same people.
The video compression relies on a concept of Key-blocks, encoded with [[JPEG]]-like algorithm and motion blocks, derived from previously drawn blocks. The frame is encoded in 8x8 or smaller blocks. The Key-blocks are always 8x8 and directly encoded, while motion blocks heavily rely on [[D&C]] approach and various block transformation techniques. Generally, this codec is quite similar to [[4XM]] codec and [[Mobiclip]] codecs, because it's written by the same people.


There are two variants of HNM6 video codec.
There are two variants of HNM6 video codec.
* WARP codec. Prototype codec, simplified version. Used in a very little number of games.
* WARP codec. Prototype codec, simplified version. Used in a very little number of games circa 1997.
* Normal (yes, that's how it's called internally) codec. Fully-featured version, widely used.
* Normal (yes, that's how it's called internally) codec. Fully-featured version, widely used.


Line 84: Line 84:
  u32  jpegend            -- offset of jpeg data buffer end
  u32  jpegend            -- offset of jpeg data buffer end


All offsets a relative to this header.
All offsets are little-endian and relative to this header.


* quality is a standard [[JPEG]] quality value (0..100%). Negative value is used to specify a keyframe (for Normal codec only.)
* quality is a standard [[JPEG]] quality value (0..100%). Negative value is used to specify a keyframe (for Normal codec only.)
Line 93: Line 93:


=== Key-blocks decoding ===
=== Key-blocks decoding ===
Key-blocks are JPEG-encoded blocks in YUV 4:4:4 format. The requantization, [[IDCT]] and colorspace conversion steps are identical to standard [[JPEG]]. Standard zigzag (0, 1, 8, 16, 9, ...) and quantization tables (16, 11, 10, 16, 24, ... ; 17, 18, 24, 47, 99, ...) used as well.  
Key-blocks are JPEG-encoded blocks in YUV 4:4:4 format. The requantization, [[IDCT]] and colorspace conversion steps are identical to standard [[JPEG]]. Standard zigzag (0,1,8,16,9,...) and quantization tables (16,11,10,16, 24,...; 17,18,24,47,99,...) used as well.  


The only custom feature used is the coefficients encoding. Instead of Huffman coding they are stored using RLE scheme.
The only custom feature used is the coefficients encoding. Instead of Huffman coding they are stored using RLE scheme.
Line 99: Line 99:
Macroblock's jpeg data is accessed by 4-bit nibbles, lower nibble of each byte first. Also, the following primitives are used:
Macroblock's jpeg data is accessed by 4-bit nibbles, lower nibble of each byte first. Also, the following primitives are used:
* half - half of a nibble, upper part first, then lower (i.e. read a nibble, process upper part of it upon first use, lower part upon second)
* half - half of a nibble, upper part first, then lower (i.e. read a nibble, process upper part of it upon first use, lower part upon second)
* s2 - signed two-bit 2's compliment value of a half, no zero point (i.e. 0b00 -> 1, 0b01 -> 2, 0b10 -> -2, 0b11 -> -1)
* s2 - signed two-bit 2's complement value of a half, no zero point (i.e. 0b00 -> 1, 0b01 -> 2, 0b10 -> -2, 0b11 -> -1)
* s4 - signed four-bit 2's compliment value of a nibble, no zero point
* s4 - signed four-bit 2's complement value of a nibble, no zero point
* s44 - signed eight-bit value of two nibbles, with zero point (this is basically (signextend(nibble1) << 4) | nibble2)
* s44 - signed eight-bit value of two nibbles, with zero point (this is basically (signextend(nibble1) << 4) | nibble2)


For each plane of 8x8 coefficients, first entry is s44, then read nibbles and fill the rest of coefficients as follows until the whole plane is complete. Repeat for each Y, U and V plane.
For each plane of 8x8 coefficients, the first coefficient is s44, then read nibbles and fill the rest of coefficients as follows until the whole plane is complete. Repeat for each Y, U and V plane.


   0 - fill remainder of plane with zeros
   0 - fill remainder of a plane with zeros
   1 - 0, 0, 0, 0
   1 - 0, 0, 0, 0
   2 - 0, 1
   2 - 0, 1
Line 141: Line 141:




* Keyblock is a JPEG-encoded macroblock
* Keyblock is a JPEG-encoded macroblock.
* CrossCut means that the current block should be cut on to 4 smaller subblocks and each one processed independently, using the same scheme recursively.
* CrossCut means that the current block should be cut on to 4 smaller subblocks and each one processed independently, using the same scheme recursively.
* Motion means that the block is motion-coded and should be copied from some other part of the frame. To do so, read the next entry of the motion buffer and process it as follows:
* Motion means that the block is motion-coded and should be copied from some other part of the frame. To do so, read the next entry of the motion buffer and process it as follows:
** transformation mode = next 2 bits from bitbuffer (upper part) combined with bit 15 of motion (lower bit.)
** transformation mode = next 2 bits from bitbuffer (upper part) combined with bit 15 of motion (lower bit)
** xmotion = 128 - (bits 7..14 of motion)
** xmotion = 128 - (bits 7..14 of motion)
** ymotion = (if block is not 8x8, then 4) - (bits 0..6 of motion)
** ymotion = (if block is not 8x8, then 4) - (bits 0..6 of motion)
* 2x2 blocks are always short motion coded, using no extra VLC
* 2x2 blocks are always short motion coded, using no extra VLC:
** transformation mode = bits 1..3 of short motion
** transformation mode = bits 1..3 of short motion
** motion index = bit 0 || bits 4..7 || bits 8..11 (total 9 bits)
** motion index = bit 0 || bits 4..7 || bits 8..11 (total 9 bits)
Line 161: Line 161:
     ymotion = ymotion - 1
     ymotion = ymotion - 1


Block's source coordinates are the sum of current 8x8 macroblock's coordinates (not it's subblock's!) and the xmotion and ymotion respectively. If x-coordinate happens to go out of frame, it should be wrapped around the line. Copy block at the following coordinates to the current, applying the transformation as specified.
Block's source coordinates are the sum of current 8x8 macroblock's coordinates (not its subblock's!) and the xmotion and ymotion respectively. If x-coordinate happens to go out of frame, it should be wrapped around the line. Copy block at the following coordinates to the current, applying the transformation as specified.


=== Normal decoding ===
=== Normal decoding ===
Line 203: Line 203:
* HorizontalCut splits the block horizontally, producing two subblocks of smaller height. Each one then processed recursively.
* HorizontalCut splits the block horizontally, producing two subblocks of smaller height. Each one then processed recursively.
* VerticalCut is identical to HorizontalCut, but splits the block vertically.
* VerticalCut is identical to HorizontalCut, but splits the block vertically.
* Skip simply copies the block from the previous frame
* Skip simply copies the block from the previous frame.
* ShortMotion copies block from previous frame, using the spiral-encoded motion. Unlike regular motion, short motion is relative to subblock's coordinates, not the whole macroblock. Read ShortMotion index, then obtain block's relative coordinates as per illustration:
* ShortMotion copies block from previous frame, using the spiral-encoded motion. Unlike regular motion, short motion is relative to subblock's coordinates, not the whole macroblock. Read ShortMotion index, then obtain block's relative coordinates as per illustration:
[[Image:HNM6Spiral.png]]
[[Image:HNM6Spiral.png]]
* Motion blocks are similar to WARP motion block, with minor enhancements
* Motion blocks are similar to WARP motion block, with minor enhancements:
** For keyframe motion is the same as for WRAP, with the exception for blocks smaller than 4x4. For such blocks:
** For keyframe motion is the same as for WRAP, with the exception for blocks smaller than 4x4. For such blocks:
*** transformation mode = bits 12.14 of motion
*** transformation mode = bits 12.14 of motion
Line 224: Line 224:
*** xmotion = 31 - (bits 0..5 of motion)
*** xmotion = 31 - (bits 0..5 of motion)
*** ymotion = 31 - (bits 6..11 of motion)
*** ymotion = 31 - (bits 6..11 of motion)
** Don't forget to wrap x motion around the line if it goes out of frame
** Don't forget to wrap x motion around the line if it goes out of frame.


=== Block transformation ===
=== Block transformation ===
Line 231: Line 231:
Copy block as is.
Copy block as is.
==== 1 - Horizontal flip ====
==== 1 - Horizontal flip ====
Swap left and right
Swap left and right:
  abc -> cba
  abc -> cba
==== 2 -  Vertical flip ====
==== 2 -  Vertical flip ====
Swap up and down
Swap up and down:
  a    c
  a    c
  b -> b
  b -> b
  c    a
  c    a
==== 3 - Cross flip ====
==== 3 - Cross flip ====
Swap both up-down and left-right
Swap both up-down and left-right:
  ab    fe
  ab    fe
  cd -> dc
  cd -> dc
  ef    ba
  ef    ba
==== 4 - Forward flip ====
==== 4 - Forward flip ====
Swap block around /
Swap block around / :
  ab -> db  
  ab -> db  
  cd    ca
  cd    ca
==== 5 - Forward rotate ====
==== 5 - Forward rotate ====
Rotate block 90 degrees clockwise
Rotate block 90 degrees clockwise:
  ab    eca    fe
  ab    eca    fe
  cd -> fdb -> dc
  cd -> fdb -> dc
  ef          ba
  ef          ba
==== 6 - Backward rotate ====
==== 6 - Backward rotate ====
Rotate block 90 degrees counterclockwise
Rotate block 90 degrees counterclockwise:
  ab    bdf    fe
  ab    bdf    fe
  cd -> ace -> dc
  cd -> ace -> dc
  ef          ba
  ef          ba
==== 7 - Backward flip ====
==== 7 - Backward flip ====
Swap block around \
Swap block around \ :
  ab -> ac
  ab -> ac
  cd    bd
  cd    bd

Revision as of 11:15, 8 September 2012

  • Extension: hnm
  • Company: CRYO Interactive Entertainment
  • Samples:

HNM6 is the latest variant of HNM video format by Cryo. Unlike its previous versions, it has Hi-color video support.

File Format

File consist of a main header, followed by frame chunks. Each frame chunk consist of individual audio and video chunks. All numbers are little-endian.

u8   sig[4]           -- file signature, "HNM6"
u8   reserved[2]      -- usually 0
u8   audioflags       -- nonzero value indicates file has APC sound (not authoritative)
u8   bpp
u16  width
u16  height
u32  filesize
u16  frames           -- number of frames
u16  reserved2
u32  reserved3        -- usually 0, but sometimes "V107", "V108" - version?
u16  speed            -- playback speed in fps, may be zero (?assume 15 fps then?)
u16  maxbuffer        -- number of frame buffers used
u32  maxchunk         -- max frame chunk size
u8   note[16]
u8   copyright[16]    -- "-Copyright CRYO-"

Each frame chunk begins with u32 chunk size (including this size field), then followed by frame's individual chunks:

 u32  chunksize       -- chunk size including this field, excluding padding
 u16  chunkid         -- TWOCC chunk id
 u16  reserved
 u8   data[]
 u8   padding[]       -- pads chunk to 4-byte boundary

AA Chunk

APC Audio. See CRYO APC

BB Chunk

Audio continuation.

IW Chunk

Video frame, WARP format.

IX Chunk

Video frame, normal format.

Format modifications

At least one known game (Riverworld) uses slightly different HNM6 container format, most likely due to different sound encoding. This format is just a small enhancement of the original, but it's not backward compatible.

Standard HNM6 header continues with additional audio description header:

u32  freq
u32  bits
u32  channels
u32  ?
u32  ?
u8   copyright[28]  -- "HNMS 1.1 by SARRET Hubert\0\0\0"
u32  ?              -- some initial audio samples?
u32  ?
u32  ?
u32  ?

Each frame chunk followed by an audio chunk, however, this audio chunk size is not counted by frame's chunk size field. Frame chunk format:

u32  sig[4]          -- "SOUN"
u32  chunksize       -- including this preamble
u8   data[]

Video Format

The video compression relies on a concept of Key-blocks, encoded with JPEG-like algorithm and motion blocks, derived from previously drawn blocks. The frame is encoded in 8x8 or smaller blocks. The Key-blocks are always 8x8 and directly encoded, while motion blocks heavily rely on D&C approach and various block transformation techniques. Generally, this codec is quite similar to 4XM codec and Mobiclip codecs, because it's written by the same people.

There are two variants of HNM6 video codec.

  • WARP codec. Prototype codec, simplified version. Used in a very little number of games circa 1997.
  • Normal (yes, that's how it's called internally) codec. Fully-featured version, widely used.

Both version has identical bitstream layout. Frame data begins this the following header:

s32  quality            -- JPEG quality index, negative value indicates that the frame is a keyframe
u32  bitbuffer          -- offset of bitbuffer
u32  motionbuffer       -- offset of motion vector buffer
u32  shortmotionbuffer  -- offset of short motion vector buffer
u32  jpegbuffer         -- offset of jpeg data buffer
u32  jpegend            -- offset of jpeg data buffer end

All offsets are little-endian and relative to this header.

  • quality is a standard JPEG quality value (0..100%). Negative value is used to specify a keyframe (for Normal codec only.)
  • bitbuffer points to frame's VLC-encoded macroblocks. This kind of data is accessed by a bit-reader with 32-bit internal queue, MSB bit comes out first.
  • motion buffer points to array of u16 motion vectors, used to copy blocks from various areas of the frame
  • short motion buffer is used for accessing blocks near the current macroblock. Each entry of buffer is 12 bits long, accessed in LSB order.
  • jpeg buffer points to array of encoded JPEG macroblocks

Key-blocks decoding

Key-blocks are JPEG-encoded blocks in YUV 4:4:4 format. The requantization, IDCT and colorspace conversion steps are identical to standard JPEG. Standard zigzag (0,1,8,16,9,...) and quantization tables (16,11,10,16, 24,...; 17,18,24,47,99,...) used as well.

The only custom feature used is the coefficients encoding. Instead of Huffman coding they are stored using RLE scheme.

Macroblock's jpeg data is accessed by 4-bit nibbles, lower nibble of each byte first. Also, the following primitives are used:

  • half - half of a nibble, upper part first, then lower (i.e. read a nibble, process upper part of it upon first use, lower part upon second)
  • s2 - signed two-bit 2's complement value of a half, no zero point (i.e. 0b00 -> 1, 0b01 -> 2, 0b10 -> -2, 0b11 -> -1)
  • s4 - signed four-bit 2's complement value of a nibble, no zero point
  • s44 - signed eight-bit value of two nibbles, with zero point (this is basically (signextend(nibble1) << 4) | nibble2)

For each plane of 8x8 coefficients, the first coefficient is s44, then read nibbles and fill the rest of coefficients as follows until the whole plane is complete. Repeat for each Y, U and V plane.

 0 - fill remainder of a plane with zeros
 1 - 0, 0, 0, 0
 2 - 0, 1
 3 - 0, -1
 4 - 0, 0, 1
 5 - 0, 0, -1
 6 - 0, 0, 0, 1
 7 - 0, 0, 0, -1
 8 - zeros = half, tail = half
     fill (zeros+1) coefficients with 0
     fill (tail+2) coefficients with s2
 9 - zeros = half, tail = half
     fill (zeros+1) coefficients with 0
     fill (tail+1) coefficients with s4
10 - s2, s2
11 - s4
12 - s4, s4
13 - s4, s4, s4
14 - 0
15 - s44

WARP decoding

Macroblocks are encoded using the following VLC codes:

Block type 8x8 4x4
Keyblock 11 n/a
Motion 10 0
CrossCut 0 1


  • Keyblock is a JPEG-encoded macroblock.
  • CrossCut means that the current block should be cut on to 4 smaller subblocks and each one processed independently, using the same scheme recursively.
  • Motion means that the block is motion-coded and should be copied from some other part of the frame. To do so, read the next entry of the motion buffer and process it as follows:
    • transformation mode = next 2 bits from bitbuffer (upper part) combined with bit 15 of motion (lower bit)
    • xmotion = 128 - (bits 7..14 of motion)
    • ymotion = (if block is not 8x8, then 4) - (bits 0..6 of motion)
  • 2x2 blocks are always short motion coded, using no extra VLC:
    • transformation mode = bits 1..3 of short motion
    • motion index = bit 0 || bits 4..7 || bits 8..11 (total 9 bits)
    • decode the index as follows:
if index < 12 * 8
  xmotion = -2 - index % 12
  ymotion =  6 - index / 12
else
  index -= 12 * 8
  xmotion = 19 - index % 32
  ymotion = -2 - index / 32
  if ymotion <= -8
   ymotion = ymotion - 1

Block's source coordinates are the sum of current 8x8 macroblock's coordinates (not its subblock's!) and the xmotion and ymotion respectively. If x-coordinate happens to go out of frame, it should be wrapped around the line. Copy block at the following coordinates to the current, applying the transformation as specified.

Normal decoding

Normal compression introduces interframes, more split modes and different short motion encoding.

Macroblocks are encoded using the following VLC codes:

Block type 8x8 4x8 8x4 4x4 2x4 4x2 2x2
Keyframe
Keyblock 110 n/a n/a n/a n/a n/a n/a
HorizontalCut 00 0 n/a 01 1 n/a n/a
VerticalCut 01 n/a 0 10 n/a 1 n/a
CrossCut 10 n/a n/a 11 n/a n/a n/a
Motion 111 1 1 00 0 0 n/a
Interframe
Keyblock 011 n/a n/a n/a n/a n/a n/a
HorizontalCut 100 10 n/a 101 111 n/a n/a
VerticalCut 101 n/a 10 110 n/a 111 n/a
CrossCut 1110 n/a n/a 111 n/a n/a n/a
Skip 110 11 11 100 110 110 11
ShortMotion 00 00 00 00 0 0 0
Motion 010 01 01 01 10 10 10
  • HorizontalCut splits the block horizontally, producing two subblocks of smaller height. Each one then processed recursively.
  • VerticalCut is identical to HorizontalCut, but splits the block vertically.
  • Skip simply copies the block from the previous frame.
  • ShortMotion copies block from previous frame, using the spiral-encoded motion. Unlike regular motion, short motion is relative to subblock's coordinates, not the whole macroblock. Read ShortMotion index, then obtain block's relative coordinates as per illustration:

HNM6Spiral.png

  • Motion blocks are similar to WARP motion block, with minor enhancements:
    • For keyframe motion is the same as for WRAP, with the exception for blocks smaller than 4x4. For such blocks:
      • transformation mode = bits 12.14 of motion
      • xmotion = 63 - (bits 0..6 of motion)
      • ymotion = 6 - (bits 7..11 of motion)
    • Keyframe's 2x2 blocks are always motion-coded as above.
    • For interframe motion is different. For blocks 4x4 and larger:
      • transformation mode = next 3 bits from bitbuffer
      • if bit 15 of motion is zero then source is the previous frame, current frame otherwise
      • xmotion = 128 - (bits 7..14 of motion)
      • ymotion = (if from previous frame then 64, otherwise if block is not 8x8, then 4) - (bits 0..6 of motion)
    • For blocks smaller than 4x4:
      • if bit 15 of motion is zero then source is the previous frame, current frame otherwise
      • if from current frame - same as for keyframe, see above. for previous frame:
      • transformation mode = next 3 bits from bitbuffer
      • xmotion = 31 - (bits 0..5 of motion)
      • ymotion = 31 - (bits 6..11 of motion)
    • Don't forget to wrap x motion around the line if it goes out of frame.

Block transformation

During block copying, it can be additionally transformed. Transformation modes:

0 - Normal copy

Copy block as is.

1 - Horizontal flip

Swap left and right:

abc -> cba

2 - Vertical flip

Swap up and down:

a    c
b -> b
c    a

3 - Cross flip

Swap both up-down and left-right:

ab    fe
cd -> dc
ef    ba

4 - Forward flip

Swap block around / :

ab -> db 
cd    ca

5 - Forward rotate

Rotate block 90 degrees clockwise:

ab    eca    fe
cd -> fdb -> dc
ef           ba

6 - Backward rotate

Rotate block 90 degrees counterclockwise:

ab    bdf    fe
cd -> ace -> dc
ef           ba

7 - Backward flip

Swap block around \ :

ab -> ac
cd    bd

Bitexact decoding

While you can use stock JPEG routines to decode Key-blocks, if you want to produce bitexact output you need to use very specific code to do so.