|
|
(2 intermediate revisions by one other user not shown) |
Line 1: |
Line 1: |
| {{Riven}} | | {{Riven}} |
− | This page shows the structure of Riven tBMP resources, which store game bitmaps.
| + | Riven tBMP resources contain a [[Mohawk Bitmaps|Mohawk Bitmap]]. All images in Riven are 8bpp, except for tBMP 24 in b2_Data.mhk (which is used in the developer's slideshow, accessible from the [[Riven debug shell]]). |
− | | + | |
− | ==General tBMP structure==
| + | |
− | tBMP resources store 8-bit and 24-bit bitmaps, which can be plain or compressed. Everything is in big-endian order. Each resource begins with this structure:
| + | |
− | {| class="structure"
| + | |
− | |unsigned short||bitmap width (pixels)
| + | |
− | |-
| + | |
− | |unsigned short||bitmap height (pixels)
| + | |
− | |-
| + | |
− | |unsigned short||bytes per row
| + | |
− | |-
| + | |
− | |unsigned char||compression flag
| + | |
− | |-
| + | |
− | |unsigned char||truecolor flag
| + | |
− | |}
| + | |
− | | + | |
− | If the truecolor flag is 4 then the bitmap is a 24-bit image, and the structure goes on with height rows, each row following this structure:
| + | |
− | {| class="structure"
| + | |
− | |''width''*3 unsigned chars||pixel values (BGR, BGR, BGR...)
| + | |
− | |-
| + | |
− | |''width'' unsigned chars||unknown (zero)
| + | |
− | |}
| + | |
− | | + | |
− | Note that there is only one 24-bit bitmap in the whole game! It's tBMP 24 in b2_data.mhk, and it's a secret image.
| + | |
− | | + | |
− | If the truecolor flag is not 4 the tBMP is a normal 8-bit bitmap and after the initial fields we find:
| + | |
− | {| class="structure"
| + | |
− | |unsigned long||unknown (seems always 0x030418ff)
| + | |
− | |-
| + | |
− | |256*3 unsigned chars||color table (BGR, BGR, BGR...)
| + | |
− | |}
| + | |
− | | + | |
− | If the compression flag is 0, the bitmap is uncompressed: pixel data follows immediately as a simple block of pixels. Each pixel is a byte representing an index in the color table. Rows may be padded with filling bytes to reach the specified number of bytes per row; just discard those extra bytes. Otherwise, if the compression flag is 4 then the bitmap is compressed with a proprietary format (explained below); the compressed data block follows immediately.
| + | |
− | | + | |
− | ==Compression details==
| + | |
− | In compressed tBMP bitmaps, pixels are encoded as a data stream made of variable length commands. Pixels are always decoded in duplets: each command generates at least 2 pixels. The encoding is heavily based on what comes before each command, so even a little decoding bug can cripple the whole image. The commands can appear in any order inside the data stream. The first 4 bytes of the data stream are unknown and can be ignored. Many thanks to Arthur Muller for his precious help in decoding this format.
| + | |
− | Main commands
| + | |
− | | + | |
− | They are all 1-byte commands, followed by a variable number of arguments.
| + | |
− | | + | |
− | {| border=1 cellpadding=4 cellspacing=0 style="border:1px #000 solid;border-collapse:collapse;"
| + | |
− | |- style="background:#CCC"
| + | |
− | ! Command !! Action
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x00||End of stream: when reaching it, the decoding is complete. No additional bytes follow. I think some bitmaps don't have this, so just stop when you have decoded enough pixels to fill the image.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x01 -0x3f||Output ''n'' pixel duplets, where ''n'' is the command value itself. Pixel data comes immediately after the command as 2*''n'' bytes representing direct indices in the 8-bit color table.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x40-0x7f||Repeat last 2 pixels ''n'' times, where ''n'' = ''command_value'' & 0x3F. No additional bytes follow.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x80-0xbf||Repeat last 4 pixels ''n'' times, where ''n'' = ''command_value'' & 0x3F. No additional bytes follow.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xc0-0xff||Begin of a subcommand stream. This is like the main command stream, but contains another set of commands which are somewhat more specific and a bit more complex. This command says that ''command_value'' & 0x3F subcommands will follow. It doesn't generate pixels itself.
| + | |
− | |}
| + | |
− | | + | |
− | ==Subcommands, part 1: arithmetic operations==
| + | |
− | Subcommands are not simply 1-byte values, but are somewhat mixed with their arguments, so the full byte pattern is reported.
| + | |
− | {| border=1 cellpadding=4 cellspacing=0 style="border:1px #000 solid;border-collapse:collapse;"
| + | |
− | |- style="background:#CCC"
| + | |
− | ! Command !! Byte pattern !! Action
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x01-0x0f
| + | |
− | |style="font-family:monospace"|0000mmmm
| + | |
− | |Repeat duplet at relative position -''m'', where ''m'' is given in duplets. So if ''m''=1, repeat the last duplet.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x10
| + | |
− | |style="font-family:monospace"|0x10 p
| + | |
− | |Repeat last duplet, but change second pixel to ''p''.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x11-0x1f
| + | |
− | |style="font-family:monospace"|0001mmmm
| + | |
− | |Output the first pixel of last duplet, then pixel at relative position -''m''. ''m'' is given in pixels.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x20-0x2f
| + | |
− | |style="font-family:monospace"|0010xxxx
| + | |
− | |Repeat last duplet, but add ''x'' to second pixel.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x30-0x3f
| + | |
− | |style="font-family:monospace"|0011xxxx
| + | |
− | |Repeat last duplet, but subtract ''x'' to second pixel.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x40
| + | |
− | |style="font-family:monospace"|0x40 p
| + | |
− | |Repeat last duplet, but change first pixel to ''p''.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x41-0x4f
| + | |
− | |style="font-family:monospace"|0100mmmm
| + | |
− | |Output pixel at relative position -''m'', then second pixel of last duplet.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x50
| + | |
− | |style="font-family:monospace"|0x50 p1 p2
| + | |
− | |Output two absolute pixel values, ''p1'' and ''p2''.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x51-0x57
| + | |
− | |style="font-family:monospace"|01010mmm p
| + | |
− | |Output pixel at relative position -''m'', then absolute pixel value ''p''.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x59-0x5f
| + | |
− | |style="font-family:monospace"|01011mmm p
| + | |
− | |Output absolute pixel value ''p'', then pixel at relative position -''m''.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x60-0x6f
| + | |
− | |style="font-family:monospace"|0110xxxx p
| + | |
− | |Output absolute pixel value ''p'', then (second pixel of last duplet) + ''x''.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x70-0x7f
| + | |
− | |style="font-family:monospace"|0111xxxx p
| + | |
− | |Output absolute pixel value ''p'', then (second pixel of last duplet) - ''x''.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x80-0x8f
| + | |
− | |style="font-family:monospace"|1000xxxx
| + | |
− | |Repeat last duplet adding ''x'' to the first pixel.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0x90-0x9f
| + | |
− | |style="font-family:monospace"|1001xxxx p
| + | |
− | |Output (first pixel of last duplet) + ''x'', then absolute pixel value ''p''.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xa0
| + | |
− | |style="font-family:monospace"|0xa0 xxxxyyyy
| + | |
− | |Repeat last duplet, adding ''x'' to the first pixel and ''y'' to the second.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xb0
| + | |
− | |style="font-family:monospace"|0xb0 xxxxyyyy
| + | |
− | |Repeat last duplet, adding ''x'' to the first pixel and subtracting ''y'' from the second.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xc0-0xcf
| + | |
− | |style="font-family:monospace"|1100xxxx
| + | |
− | |Repeat last duplet subtracting ''x'' from first pixel.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xd0-0xdf
| + | |
− | |style="font-family:monospace"|1101xxxx p
| + | |
− | |Output (first pixel of last duplet) - ''x'', then absolute pixel value ''p''.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xe0
| + | |
− | |style="font-family:monospace"|0xe0 xxxxyyyy
| + | |
− | |Repeat last duplet, subtracting ''x'' from first pixel and adding ''y'' to second.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xf0 and 0xff
| + | |
− | |style="font-family:monospace"|0xfx xxxxyyyy
| + | |
− | |Repeat last duplet, subtracting ''x'' from first pixel and ''y'' from second.
| + | |
− | |}
| + | |
− | | + | |
− | ==Subcommands, part 2: repeat operations==
| + | |
− | {| border=1 cellpadding=4 cellspacing=0 style="border:1px #000 solid;border-collapse:collapse;"
| + | |
− | |- style="background:#CCC"
| + | |
− | ! Command !! Byte pattern !! Action
| + | |
− | |-
| + | |
− | |various
| + | |
− | |style="font-family:monospace"|1x1xxxmm mmmmmmmm
| + | |
− | |
| + | |
− | {| border=1 cellspacing=0 style="float:right;border:none;border-collapse:collapse;padding:0px 3px;"
| + | |
− | |- style="background:#CCC"
| + | |
− | ! Command !! n !! r
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xa4 - 0xa7||2||0
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xa8 - 0xab||2||1
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xac - 0xaf||3||0
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xb4 - 0xb7||3||1
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xb8 - 0xbb||4||0
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xbc - 0xbf||4||1
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xe4 - 0xe7||5||0
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xe8 - 0xeb||5||1
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xec - 0xef||6||0
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xf4 - 0xf7||6||1
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xf8 - 0xfb||7||0
| + | |
− | |}
| + | |
− | Repeat n duplets from relative position -''m'' (given in pixels, not duplets). If ''r'' is 0, another byte follows and the last pixel is set to that value. ''n'' and ''r'' come from the table on the right.
| + | |
− | |-
| + | |
− | |style="font-family:monospace"|0xfc
| + | |
− | |style="font-family:monospace"|0xfc nnnnnrmm mmmmmmmm (p)
| + | |
− | |Repeat n+2 duplets from relative position -''m'' (given in pixels, not duplets). If ''r'' is 0, another byte ''p'' follows and the last pixel is set to absolute value ''p''.
| + | |
− | |}
| + | |