Skip to content

Latest commit

 

History

History
258 lines (166 loc) · 14.3 KB

moviResources.md

File metadata and controls

258 lines (166 loc) · 14.3 KB

MOVI Resources

Resources with content type 0x11 are a media container themselves, similar to audio/video container formats. These resources are called "MOVI Resources" since the initial tag is the four letter code MOVI.

Format

Such a resource contains the following components:

  • a header, describing meta information about the media,
  • a palette, with initial colours,
  • an index, describing the following data entries, and
  • the data entries.

MOVI Header (MovieHeader struct) (1024 bytes)

0000  uint32      magicId          Container tag 0x49564F4D ("MOVI")
0004  int32       numChunks        Number of chunk entries in file
0008  int32       sizeChunks       Size of index table
000C  int32       sizeData         Size of content (all data entries)
0010  fix         totalTime        Length of media (in seconds)
0014  fix         frameRate        Frame rate (informational only)
0018  int16       frameWidht       Width of video
001A  int16       frameHeight      Height of video
001C  int16       gfxNumBits       For videos may be 0x0008, 0x000F or 0x0018, 0x0000 otherwise
001E  int16       isPalette        Palette presence in file. For videos 0x0001, 0x0000 otherwise
0020  int16       audioNumChans    Numbers of audiochannels 0x0000 - no audio, 0x0001 - mono, 0x0002 stereo
0022  int16       audioSampleSize  Audio sample size, 0x0001 - 8-bit, 0x0002 - 16-bit
0026  fix         audioSampleRate  Sample rate of audio, in Hz (0x2B770000 - 11127 Hz, 0x56EE0000 - 22254 Hz)
0030  uint8[216]  reserved         Reserved
0108  uint8[768]  palette          Palette

If the index table size is greater than the total of index entry number times one entry size, the remaining bytes are zero.

The index table sizes come in multiples of 0x0400. The rough rule for recreating the existing index sizes is: Required size less than 0x0400 -> Use 0x0400. Otherwise: Round up to next 0x0400 border and add another 0x0400.

Palette (256 * 3 bytes)

The palette is always present, even if the MOVI contains only audio. In such cases all the colour channels are 0x00.

Movie chunk Entry (MovieChunk struct) (8 bytes)

0000  uint32              Chunk info (bitfield)
0004  uint32   offset     Offset to data (from beginning)

First 4 bytes are uint32 bit field which follow structure:

bits  00-23  24  time       Fixed point timestamp from movie start (in seconds)
bits  24-24   1  played     Has this chunk been clocked out? (Used by engine internally)
bits  25-28   4  flags      Chunk type specific flags
bits  39-31   3  chunkType  Chunk type (see below)

Chunk type specific flags may be:

For Video chunk type (0x01 MOVIE_CHUNK_VIDEO)

0x0F  MOVIE_FVIDEO_BMTMASK    4 bits of flags is bmtype
0x0F  MOVIE_FVIDEO_BMF_4X4    4x4 movie format

Not clear, why those flags uses same code, this may be bug or leftovers from reusing library component

For Palette chunk type (0x04 MOVIE_CHUNK_PALETTE)

0x00  MOVIE_FPAL_SET          Set palette from data 
0x01  MOVIE_FPAL_BLACK        Set palette to black
0x07  MOVIE_FPAL_EFFECTMASK   4 bits of flags is effect
0x08  MOVIE_FPAL_CLEAR        If bit set, clear screen

For Table chunk type (0x05 MOVIE_CHUNK_TABLE)

0x01  MOVIE_FTABLE_COLORSET    Table is color set
0x02  MOVIE_FTABLE_HUFFTAB     Huffman table (compressed)

Chunk Types

0 End of chunk (MOVIE_CHUNK_END)

This type is always present as the last entry in the index. It is used to calculate the length of the previous entry as the table does not contain a length field for entries.

The timestamp of this entry should match the media length from the header.

1 Video (MOVIE_CHUNK_VIDEO)

Video frames are compressed in two variants, for low and high resolution.

Frames are either "full" frames, which set all, or nearly all, pixels in the buffer, typically at a scene change. Other frames are "delta" frames, which re-use (most of) the previous pixel in the buffer. This re-use is done by writing/using the palete index 0x00, which is meant to be transparent.

Low-resolution Video

Low-resolution video is using type 0x21. Data contains a bounding box and then the compressed frame using the compression algorithm for standard bitmaps.

Low Resolution Video Data (8+N bytes)

0000  [4]int16  Bounding Box
0008  []byte    Compressed pixel data

In the existing videos, the bounding box is always 0, 0, 320, 150, specifying the same size as the video itself.

High-resolution Video

High-resolution video is using type 0x79 with a more complex compression algorithm. This algorithm works in conjunction of the Table types. See below for details on the algorithm.

High Resolution Video Data (2+N bytes)

0000  int16   Offset to Mask-stream in bytes, from beginning (XXXX)
0002  []byte  Bitstream
XXXX  []byte  Mask-stream

The Bitstream is a stream of serialized big-endian integers. The Mask-stream is a stream of serialized little-endian integers of different lengths (2, 4, 6, and 8 bytes).

The order and layout of the values in the streams is determined by the algorithm. The decoder will want to read past the end of the streams in a few cases. For these cases the missing bits/bytes are to be set to zero. The encoder removed any trailing 0x00 bytes from these streams in order to save further space.

2 Audio (MOVIE_CHUNK_AUDIO)

The data entry contains unsigned 8-bit PCM samples.

Audio is chunked into entries of 8192 bytes, even if the MOVI contains only audio.

3 Text (MOVIE_CHUNK_TEXT)

Subtitle Entry (MovieTextItem struct) (16+N bytes)

0000  uint32   tag     Subtitle Control
0004  uint32   offset  Offset to text string, always 0x10
0008  [8]byte          Unused -- always 0x00, as they are skipped
0010  []byte           Null terminated string

Subtitle text will be shown with the color from palette index 255 (last entry).

Subtitle Controls

'AREA' (uint32 0x41455241) is for setting up the subtitle space. It defines the rectangle, in pixel (left, top, right, bottom), where the text is to be displayed. Example:

20 365 620 395 CLR

'STD ' (uint32 0x20445453), 'FRN ' (uint32 0x204E5246) and 'GER ' (uint32 0x20524547) specify the actual subtitle text in the respective language, with 'STD ' being the default language (English).

4 Palette (MOVIE_CHUNK_PALETTE)

There are two palette entry types: 0x4C and 0x04. For any change in the palette, two entries are in the container. Both entries always come in sequence (first 0x4C, then 0x04) and both have identical timestamp values.

The entry with type 0x4C will have a length of 0 and the same data offset as the next entry, 0x04. The entry with type 0x04 points to data of 0x300 bytes (= 256*3) which describes the new palette for upcoming frames.

At least for high-resolution videos, the next frame after a palette change does not always entirely set the complete pixel buffer for the next frame. In the intro video for instance, SHODAN's gaining of conscience scene has a few pixel left over from the previous scene.

As a recommendation, a decoder should wipe the current frame buffer with 0x00 before applying a new palette/drawing further frames. An alternative could be to remap all pixel values with palette indices as close as possible to the new palette, though this might alter the resulting frame too much.

5 Table (MOVIE_CHUNK_TABLE)

Table entries are used in combination with video type 0x79 entries for high-resolution videos. There are two table types:

  • 0x05: Palette lookup list
  • 0x0D: Control table

See below for details on the algorithm.

Such entries are common to several frames. For any change, two entries are in the container. Both entries always come in sequence (first 0x05, then 0x0D).

In the existing files, a change of tables is always followed by a change of palette. The exception is the first scene, which uses the initial palette from the MOVI header.

Palette lookup list

This entry, with type 0x05 is a simple lookup list. Every entry of type byte is a palette index. This list is truncated as well. The decoder will want to read past the end of this stream, in which case zero bytes must be returned.

Packed Control Words

Table type 0x0D contains a runlength encoded list of control words.

Packed Control Words (4+(N*4) bytes)

0000  int32     Unpacked size
0004  []Packed  Packed entries

The Unpacked size field equals the number of control words, times three (specifying the size in bytes). The remainder of the entry is then the list of packed control words, from which the actual list of control words is unpacked.

Packed Entry (4 bytes)

0000  byte    Word count
0001  uint24  Control Word

The field Word count specifies how often the Control Word shall be extracted. A value of 0 is never encountered.

High-resolution Video Compression

High-resolution video compression uses scene-specific tables shared amongst several frames and frame-specific bit- and mask-streams. Frames in this compression are made up of tiles with a size of 4x4 pixel each. For the high-resolution videos of 600x300 pixel this means 150x75 tiles. Tiles are coloured according to different types of control words, from left to right, top to bottom.

Control Word (3 bytes, masked)

0xF00000  Count value
0x0E0000  Type
0x01FFFF  Parameter

As a rough guideline, the frame-specific bit-stream provides index values into the control word table. According to the type of the control word, the current tile is coloured either directly, or with the additional help of the mask-stream and palette lookup list.

A note on the bit-stream: Reading from this stream does not automatically advance the current position. Advancing the bit-stream is a separate operation, which may advance with a different number of bits than were previously read. This may cause re-use of some bits to increase compression.

As noted above, the bit- and mask-stream, as well as the palette lookup list, have possible trailing zero values removed in the files. When the decoder wants to read beyond their end, they must provide values set to 0.

Decoder sequence

A possible decoder is implemented using the following sequence:

  • Iterate over the tiles, outer loop is the rows, inner loop the columns. Finish the loops if either all tiles are covered, or the bit-stream has been exhausted. The stream is exhausted if the current position is at the end of or beyond the available range.
  • Read a 12-bit value from the bit-stream. Use this value as an index into the control word table.
  • If the Count value of the control word is zero
    • A Count value of zero indicates a Long Offset. In this case, the Type and Parameter fields are taken together to form a 20-bit Long Offset value.
    • Advance the bit-stream by 8 bits
    • Enter a loop as long as the current control word indicates a Long Offset.
      • Advance the bit-stream by 4 bits
      • Read a 4-bit value from the bit-stream. Add this value to the Long Offset of the current control word and use the result as another index into the control word table. The retrieved entry becomes the new current control word.
  • Advance the bit-stream by the Count value of the current control word. This may indicate a different amount of bits than previously read, even less.
  • If the type of the current control word is 6, use the previously processed control word as the current one for further processing.
  • If the type of the current control word is 5, tiles shall be skipped
    • Read a 5-bit value from the bit-stream and advance it by 5 bits. This is the Skip Count value.
    • A Skip Count of 0x1F skips the remainder of the current tile row
    • Any other value skips the given amount of columns, additionally to the current one. A value of 0 skips only the current tile.
  • Any type in the range of 0 to 4 colours the current tile. See below.
  • Next loop iteration

Type 7 is never encountered. According to the documentation of TSSHP, this has the same meaning as 6: use previous control word.

Encoder hints
  • Using the previous control word (type 6) is not allowed for the first tile of a row. It can't repeat the last operation from the previous row.
  • Usage of "transparency" byte 0x00 is only processed if at the first index of a palette lookup. See below.
Colouring a tile

To colour a tile, the Parameter value of the current control word is used, as well as the mask-stream and the palette lookup list. The 16 pixel of a tile are coloured from left to right, top to bottom.

The palette value for a pixel is determined by an index into a small lookup array of palette indices. Depending on the type of the control word, this lookup array is either a made up one, or a segment of the palette lookup list. The length of a lookup array is always a power of 2 and has possible lengths of 2, 4, 8 or 16.

The corresponding index values into such lookup arrays have bit sizes of 1, 2, 3 or 4 bits. The 16 index values for a tile are packed into a mask integer of 2, 4, 6 or 8 bytes. Depending on the type of the control word, this mask integer is made up or read from the mask-stream.

For example, a lookup array of 4 palette values requires an index size of 2 bits, resulting in a mask integer of 4 byte.

For pixel coordinates given in x,y: Mask integer bits 0-1 colour pixel 0,0; mask integer bits 2-3 colour pixel 1,0; ... mask integer bits 28-29 colour pixel 2,3 and mask integer bits 30-31 colour pixel 3,3.

Type Lookup Array Mask Integer
0 [ Parameter >> 0 & 0xFF, Parameter >> 8 & 0xFF ] Static 0xAAAA
1 [ Parameter >> 0 & 0xFF, Parameter >> 8 & 0xFF ] 2 byte from mask-stream
2 Parameter is start index into list, 4 entries 4 byte from mask-stream
3 Parameter is start index into list, 8 entries 6 byte from mask-stream
4 Parameter is start index into list, 16 entries 8 byte from mask-stream

If the lookup array index is 0, and the entry ix 0x00, use the color from the previous frame.