EGG
- Extension: egg (Sega Saturn port), yqs (PC port)
- Samples: http://samples.mplayerhq.hu/game-formats/egg/
- Game: Independence Day
The format employs YUV frames coded per-plane and IMA ADPCM-encoded audio. Data is aligned to CD sector size (2048 bytes)
Egg does not have audio stream and has slightly different coding but the differences are minimal.
EGG header:
4 bytes - "EGG\0" 4 bytes - always 0x350? 4 bytes - number of frames 2036 bytes - padding
EGG frame header:
4x2x3 bytes - offsets to each plane pixel and mode data
EGG frames are always 20kB long.
YQS header:
2 bytes - number of frames 4 bytes - audio sample rate 2042 bytes - padding
YQS frame header:
4 bytes - size 4x2x3 bytes - offsets to each plane pixel and mode data 4 bytes - audio data offset 4 bytes - audio data size
Video coding employs quadtree coding and vector quantisation: before actual image data there's an array of possible 2x2 block values which may be used during luma decoding.
Luma plane is coded as 8x8 blocks with mode flags telling how to decode them (fill/skip/split). Chroma plane is coded in similar manner using 4x4 blocks.
Luma coding:
if (get_bit() == 0) { raw block - read 64 pixels from pixel data to fill it } else { //split block for (blk_no = 0; blk_no < 4; blk_no++) { if (get_bit() == 1) decode_luma_4x4_block(); // otherwise leave it unchanged } }
4x4 luma blocks are coded in the same manner, 2x2 blocks depend on the format.
EGG uses bit pattern (highest bit is the one read first) to determine coding mode:
- 00 - skip
- 01 - fill
- 10 - raw
- 11xx - VQ with possible transformation (00 - as is, 01 - flip horizontally, 10 - flip vertically, 11 - flip in both directions)
YQS simply uses 2-bit mode for that:
- 0 - fill
- 1 - raw
- 2 - VQ (followed by index in the header table of 2x2 blocks)
- 3 - VQ flipped vertically
Chroma coding:
if (get_bit() == 1) { // otherwise skip if (get_bit() == 0) { fill 4x4 block with single colour } else { for (blk_no = 0; blk_no < 4; blk_no++) { if (get_bit() == 1) { // decode 2x2 chroma block if (get_bit() == 0) fill 2x2 block else raw 2x2 block } // otherwise leave it unchanged } } }