|
|
Line 1: |
Line 1: |
| This page attempts to document the [[LucasArts]] Smush codec, FOURCC "SANM". Note that at this stage the document is quite incomplete as the codec is still being reverse engineered. Most information regarding ''codecs'' here is speculative, at best. Structural information is quite correct unless otherwise noted. | | This page attempts to document the [[LucasArts]] Smush codec. Note that at this stage the document is quite incomplete as the codec is still being reverse engineered. Most information regarding ''codecs'' here is speculative, at best. Structural information is quite correct unless otherwise noted. |
|
| |
|
| == Samples == | | == Samples == |
Line 19: |
Line 19: |
| * Shadows of the Empire: http://www.mplayerhq.hu/MPlayer/samples/game-formats/la-san/sote-demo/ | | * Shadows of the Empire: http://www.mplayerhq.hu/MPlayer/samples/game-formats/la-san/sote-demo/ |
|
| |
|
| == Use in Grim Fandango == | | == Variants == |
| SANM is used in Grim Fandango for cut-scenes and in-game animations. The actual SNM movie files are gzipped and stored inside LAB archive files (which are quite easy to extract, there are many tools). You must use a tool like *nix's "gunzip" to decompress the SNM files after extracting the SNM files out of the LAB files. A decompressed Smush file has the "SANM" FOURCC as the first four bytes.
| | Smush comes in two variants. Version 1, FOURCC "ANIM" was used up until Grim Fandango. Version 2, FOURCC "SANM" was used from Grim Fandango up until its replacement by [[Bink]] possibly in Escape From Monkey Island. |
|
| |
|
| == Organization ==
| | See individual variant documentation in [[SAN]] and [[SNM]]. |
| This section deals with the structural properties of Smush movies. In other words, we describe the various headers used.
| |
| | |
| === Preamble ===
| |
| The movie begins with a basic 8-byte section that looks like this:
| |
| | |
| 0x00|"SANM" FOURCC |4 bytes big endian
| |
| 0x04|Movie size (in bytes)|4 bytes big endian
| |
| | |
| === Video Header ===
| |
| This header immediately follows the preamble. It describes the movie's video properties.
| |
| | |
| 0x000|"SHDR" FOURCC |4 bytes big endian
| |
| 0x004|Header size (in bytes) |4 bytes big endian
| |
| 0x008|Version |2 bytes little endian
| |
| 0x00A|# of frames |4 bytes little endian
| |
| 0x00E|Padding? |2 bytes
| |
| 0x010|Width |2 bytes little endian
| |
| 0x012|Height |2 bytes little endian
| |
| 0x014|Padding? |2 bytes
| |
| 0x016|Frame delay (microseconds)|4 bytes little endian
| |
| 0x01A|Color palette? |1024 bytes
| |
| 0x41A|Padding? |16 bytes
| |
| | |
| === Audio/Keyframe Header ===
| |
| Smush supports variable-size keyframes. An example of this usage can be seen in the Full Throttle highway chase scenes, where different images are composited into the streaming video depending on the player's actions.
| |
| | |
| Curiously enough, this header includes both audio and keyframe information.
| |
| | |
| 0x00|"FLHD" FOURCC |4 bytes big endian
| |
| 0x04|Header size (in bytes)|4 bytes big endian
| |
| | |
| Followed by any number of keyframe dimension chunks, which should match the number of keyframes in the movie. Dimension chunks in this header specify the dimensions of corresponding keyframes in the stream, in the order they're encountered. This information has not been rigorously verified, though.
| |
| 0x00|"Bl16" FOURCC |4 bytes big endian
| |
| 0x04|Header size (in bytes)|4 bytes big endian
| |
| 0x08|Padding? |2 bytes
| |
| 0x0A|Width |2 bytes little endian
| |
| 0x0C|Height |2 bytes little endian
| |
| 0x0E|Padding? |2 bytes
| |
| | |
| Followed by exactly one audio info chunk.
| |
| 0x00|"Wave" FOURCC |4 bytes big endian
| |
| 0x04|Header size (in bytes)|4 bytes big endian
| |
| 0x08|Frequency (Hz) |4 bytes little endian
| |
| 0x0C|# of channels |4 bytes little endian
| |
| 0x10|See notes |4 bytes
| |
| | |
| ==== Notes ====
| |
| * For some movies, the "Wave" chunk contains an extra 4-byte field at its end, the purpose of which is unknown.
| |
| * Movies without audio do not contain an audio info chunk.
| |
| * The order in which Wave/Bl16 chunks are organized in the FLHD header is unspecified and is known to vary between movies.
| |
| | |
| === Annotation ===
| |
| Movies may contain an optional plaintext annotation. In Grim Fandango, the only such movies are in-game animations. Keep in mind that the string itself may not always be as large as the advertised annotation size. In that case, the remaining space is padded with zeros until the advertised length is reached.
| |
| | |
| 0x00|"ANNO" FOURCC |4 bytes big endian
| |
| 0x04|Annotation size (in bytes)|4 bytes big endian
| |
| 0x08|Null-terminated string |(Annotation size) bytes
| |
| | |
| === Frame ===
| |
| This header is used as a container for a video frame and/or an audio frame, stored in an arbitrary order. In itself, it's just a FOURCC and a size.
| |
| 0x00|"FRME" FOURCC |4 bytes big endian
| |
| 0x04|Chunk size (in bytes)|4 bytes big endian
| |
| | |
| ==== Audio ====
| |
| Please see the appropriate section in [[VIMA]] for an audio frame's header/codec details. Note that as far as we know right now, this codec is specific to Grim Fandango Smush files.
| |
| | |
| ==== Video ====
| |
| This chunk stores a potentially encoded video frame, as well as various opcodes and other stuff that's used to decode it. More details downstairs.
| |
| 0x000|"Bl16" FOURCC |4 bytes big endian
| |
| 0x004|Chunk size (in bytes) |4 bytes big endian
| |
| 0x008|Unknown |8 bytes
| |
| 0x010|Width |4 bytes little endian
| |
| 0x014|Height |4 bytes little endian
| |
| 0x018|Sequence # |2 bytes little endian
| |
| 0x01A|Subcodec ID |1 byte
| |
| 0x01B|Diff buffer rotate code |1 byte
| |
| 0x020|See notes |8 bytes
| |
| 0x028|Background colour |2 bytes little endian
| |
| 0x02C|Unknown |4 bytes little endian
| |
| 0x030|Codebook? |Each entry is 2 bytes little endian?
| |
| 0x238|Video stream |...
| |
| | |
| ===== Notes =====
| |
| * The field at 0x020 is only mentioned because there's evidence of the original decoder reading it.
| |
| | |
| == Codec ==
| |
| The codec is actually a combination of several subcodecs. The subcodec that's used for a particular frame is indicated by the appropriate field in the "Bl16" chunk of the frame.
| |
| | |
| === Triple Diff Buffering ===
| |
| Smush uses a triple diff buffer mechanism to decode image data. A decoder's state includes three buffers, which are occasionally referenced by various subcodecs to decode individual frames. We will hereafter refer to said buffers as "db0", "db1", and "db2", where "db0" is the logical "current" diff buffer. It is crucial to note that "dbX" is only an ''alias'' to a particular diff buffer and does not stand for the ''contents'' of the buffer itself. In other words, it's a pointer.
| |
| | |
| Each frame contains an opcode that specifies how said buffers are rotated. The algorithm itself is actually quite straightforward.
| |
| | |
| Opcode 1:
| |
| swap db0 and db2
| |
| Opcode 2:
| |
| rotate buffer aliases one step left.
| |
| e.g. before: 0 1 2
| |
| after: 1 2 0
| |
| | |
| === Main Algorithm ===
| |
| This section is possibly incomplete.
| |
| | |
| if 0 == sequence number:
| |
| {
| |
| // this is a keyframe
| |
| fill db1 and db2 with background color.
| |
| }
| |
| handle subcodec according to ID.
| |
| copy contents of db0 into output image.
| |
| rotate buffers according to opcode.
| |
| | |
| === Subcodecs ===
| |
| This section explains what the individual subcodecs mean, and how to suck out image data in each case. Note that ImageSize, in bytes, is defined as (Width * Height * 2)
| |
| | |
| This section is particularly incomplete.
| |
| | |
| ID|What
| |
| 0|Keyframe. Copy ImageSize bytes from video stream into db0.
| |
| 1|Never encountered so far.
| |
| 2|TODO
| |
| 3|Copy ImageSize bytes from db2 into db0.
| |
| 4|Copy ImageSize bytes from db1 into db0.
| |
| 5|TODO
| |
| 6|Simple lookup/write. See below.
| |
| 7|Never encountered so far.
| |
| 8|TODO
| |
| | |
| ==== Subcodec 6 ====
| |
| This is a straightforward palette (codebook?) lookup/write routine.
| |
| for each pixel in db0:
| |
| {
| |
| index = value of next byte in video stream;
| |
| pixel = (2 bytes little endian) codebook[index];
| |
| }
| |
| | |
| (This is where my notes end.)
| |
| | |
| [[Category:Game Formats]] | |