MR image: Difference between revisions
No edit summary |
m (Reverted edits by Darc (talk) to last revision by Unknown user) Tag: Rollback |
||
(11 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
==About== | ==About== | ||
A MR image is an image format solely used inside an [[IP.BIN]] file to show off a logo during the boot screen process of the Sega Dreamcast. [[File:Windows CE boot screen.png|thumb|Games powered by Windows CE boot screen]]This image format was used in commercialized Dreamcast games utilizing Windows CE SDK where we see a "Powered by Microsoft Windows CE" logo. It is now commonly used in homebrew to distinguish the creator(s) of the application or to disassociate the application being produced by or under license from Sega. Since [[IP.BIN]] is restricted to a 32k file size | A MR image is an image format solely used inside an [[IP.BIN]] file to show off a logo during the boot screen process of the Sega Dreamcast. [[File:Windows CE boot screen.png|thumb|Games powered by Windows CE boot screen]]This image format was used in commercialized Dreamcast games utilizing Windows CE SDK where we see a "Powered by Microsoft Windows CE" logo. It is now commonly used in homebrew to distinguish the creator(s) of the application or to disassociate the application being produced by or under license from Sega. The MR image must be inserted into a [[IP.BIN]] file at offset '''0x3820''' in order to be viewed during boot sequence. Since [[IP.BIN]] is restricted to a 32k file size, there are a couple of guidelines the MR image must meet to in order to fit inside the [[IP.BIN]]. | ||
<br/> | <br/> | ||
'''MR Image must be:''' | '''MR Image must be:''' | ||
* 320x90 or less | * '''320x90''' or less | ||
* Max 128 colors | * Max '''128''' colors | ||
* Less than 8192 bytes to fit in a IP.BIN | * Less than '''8192''' bytes to fit in a IP.BIN | ||
* The transparent color is #c0c0c0, or 192, 192, 192 in RGB | * The transparent color is '''#c0c0c0''', or 192, 192, 192 in RGB | ||
<br/> | <br/> | ||
==Tools== | ==Tools== | ||
'''Selfboot-Inducer''' | '''Selfboot-Inducer''' | ||
SiZious's [https://sizious.com/download/dreamcast/#selfboot-inducer Tool] to create homebrew compilations. Using this application you can view and create MR images from other various image formats. | SiZious's Windows [https://sizious.com/download/dreamcast/#selfboot-inducer Tool] to create homebrew compilations. Using this application you can view and create MR images from other various other image formats. | ||
<br/> | <br/> | ||
Line 20: | Line 19: | ||
[https://www.gimp.org/ GIMP] is a cross-platform image editor available for GNU/Linux, OS X, Windows. | [https://www.gimp.org/ GIMP] is a cross-platform image editor available for GNU/Linux, OS X, Windows. | ||
Using this GIMP plugin([https://gitlab.com/BBHoodsta/makeip "file-mr.py"]), you can create view, edit, and create a MR image from any other image format GIMP supports. | Using this GIMP plugin([https://gitlab.com/BBHoodsta/makeip "file-mr.py"]), you can create view, edit, and create a MR image from any other image format that GIMP supports. | ||
==File Format== | ==File Format== | ||
The file format is composed of three sections: '''Header''', '''Palette''', and the '''Image Data'''. All of the data written in the file is in '''little-endian''' format. | The file format is composed of three sections: '''Header''', '''Palette''', and the '''Image Data'''. All of the data written in the file is in '''little-endian''' format. | ||
A MR file starts off with a 30 byte header. | A MR file starts off with a 30 byte header. | ||
===Header=== | ===File Header=== | ||
{| class="wikitable" | {| class="wikitable" style="text-align: center" | ||
|- | |- | ||
! Size !! Contents | ! Size (bytes) !! Contents | ||
|- | |- | ||
| 2 | | 2 | ||
|style="text-align: left;"| "MR" | |||
|- | |- | ||
| 4 | | 4 | ||
|style="text-align: left;"| Total file size | |||
|- | |- | ||
| 4 | | 4 | ||
|style="text-align: left;"| Crap (fill with 0's) | |||
|- | |- | ||
| 4 | | 4 | ||
|style="text-align: left;"| Image data offset in bytes (Header size + Palette size) | |||
|- | |- | ||
| 4 | | 4 | ||
|style="text-align: left;"| Image width | |||
|- | |- | ||
| 4 | | 4 | ||
|style="text-align: left;"| Image height | |||
|- | |- | ||
| 4 | | 4 | ||
|style="text-align: left;"| Crap (fill with 0's) | |||
|- | |- | ||
| 4 | | 4 | ||
|style="text-align: left;"| Amount of colors in palette | |||
|} | |} | ||
===Palette=== | ===Palette=== | ||
The header is followed by the image's palette. The palette is composed of palette entries where each palette entry is 4 bytes long and is stored as BGRA. The alpha byte goes unused. | The header is followed by the image's palette. The palette is composed of palette entries where each palette entry is 4 bytes long and is stored as BGRA. The alpha byte goes unused. The maximum number of palette entries is '''128'''. | ||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
Line 58: | Line 66: | ||
In total, the byte size of your palette should be number of colors * 4. | In total, the byte size of your palette should be number of colors * 4. | ||
===Image Data=== | ===Image Data=== | ||
Lastly, the image data. | Lastly, the image data. The image data is basically an array of indices, each a byte long, that refers to a palette entry in the palette. The image data is compressed using a form of Run-Length Encoding (RLE). The python algorithms to encode/decode the image data are shown below. | ||
==Encoding Algorithm== | ==Encoding Algorithm== | ||
<syntaxhighlight lang="c" line="line"> | |||
# input: Byte array | |||
# output: Byte array | |||
# input_size: Size of input in bytes (image width * image height) | |||
# returns: Size of the compressed data | |||
def mr_encode(input, output, input_size): | |||
run = 0 | |||
length = 0 | |||
position = 0 | |||
while(position < input_size): | |||
run = 1 | |||
while((run < 0x17f) and (position+run < input_size) and (input[position] == input[position+run])): | |||
run += 1 | |||
if(run > 0xff): | |||
output[length] = 0x82 | |||
length += 1 | |||
output[length] = 0x80 | (run - 0x100) | |||
length += 1 | |||
output[length] = input[position] | |||
length += 1 | |||
elif(run > 0x7f): | |||
output[length] = 0x81 | |||
length += 1 | |||
output[length] = run | |||
length += 1 | |||
output[length] = input[position] | |||
length += 1 | |||
elif(run > 1): | |||
output[length] = 0x80 | run | |||
length += 1 | |||
output[length] = input[position] | |||
length += 1 | |||
else: | |||
output[length] = input[position] | |||
length += 1 | |||
position += run | |||
return length | |||
</syntaxhighlight> | |||
==Decoding Algorithm== | ==Decoding Algorithm== | ||
<syntaxhighlight lang="c" line="line"> | |||
# input: Byte array | |||
# input_size: Size of input in bytes | |||
# uncompressed_size: Size of uncompressed data in bytes (image width * image height) | |||
# returns: Decoded image data | |||
def mr_decode(input, input_size, uncompressed_size): | |||
run = 0 | |||
position = 0 | |||
idx_position = 0 | |||
indexed_data = array("B", "\x00" * uncompressed_size) | |||
while(position < input_size): | |||
first_byte = input[position] | |||
if((position+1) < input_size): | |||
second_byte = input[position+1] | |||
# The bytes lower than 0x80 are recopied just as they are in the Bitmap | |||
if(first_byte < 0x80): | |||
run = 1 | |||
position += 1 | |||
# The tag 0x81 is followed by a byte giving directly the count of points | |||
elif(first_byte == 0x81): | |||
run = second_byte | |||
first_byte = input[position+2] | |||
position += 3 | |||
# The tag 0x82 is followed by the number of the points decoded in Run | |||
# By retaining only the first byte for each point | |||
elif(first_byte == 0x82 and second_byte >= 0x80): | |||
run = second_byte - 0x80 + 0x100 | |||
first_byte = input[position+2] | |||
position += 3 | |||
else: | |||
run = first_byte - 0x80 | |||
first_byte = second_byte | |||
position += 2 | |||
# Writing decompressed bytes | |||
for i in range(run): | |||
if(idx_position+i < uncompressed_size): | |||
indexed_data[idx_position+i] = first_byte | |||
idx_position += run | |||
return indexed_data | |||
</syntaxhighlight> |
Latest revision as of 00:23, 11 October 2023
About
A MR image is an image format solely used inside an IP.BIN file to show off a logo during the boot screen process of the Sega Dreamcast.
This image format was used in commercialized Dreamcast games utilizing Windows CE SDK where we see a "Powered by Microsoft Windows CE" logo. It is now commonly used in homebrew to distinguish the creator(s) of the application or to disassociate the application being produced by or under license from Sega. The MR image must be inserted into a IP.BIN file at offset 0x3820 in order to be viewed during boot sequence. Since IP.BIN is restricted to a 32k file size, there are a couple of guidelines the MR image must meet to in order to fit inside the IP.BIN.
MR Image must be:
- 320x90 or less
- Max 128 colors
- Less than 8192 bytes to fit in a IP.BIN
- The transparent color is #c0c0c0, or 192, 192, 192 in RGB
Tools
Selfboot-Inducer
SiZious's Windows Tool to create homebrew compilations. Using this application you can view and create MR images from other various other image formats.
MR GIMP Plugin
GIMP is a cross-platform image editor available for GNU/Linux, OS X, Windows.
Using this GIMP plugin("file-mr.py"), you can create view, edit, and create a MR image from any other image format that GIMP supports.
File Format
The file format is composed of three sections: Header, Palette, and the Image Data. All of the data written in the file is in little-endian format.
A MR file starts off with a 30 byte header.
File Header
Size (bytes) | Contents |
---|---|
2 | "MR" |
4 | Total file size |
4 | Crap (fill with 0's) |
4 | Image data offset in bytes (Header size + Palette size) |
4 | Image width |
4 | Image height |
4 | Crap (fill with 0's) |
4 | Amount of colors in palette |
Palette
The header is followed by the image's palette. The palette is composed of palette entries where each palette entry is 4 bytes long and is stored as BGRA. The alpha byte goes unused. The maximum number of palette entries is 128.
Palette Entry | |||
---|---|---|---|
B | G | R | A |
In total, the byte size of your palette should be number of colors * 4.
Image Data
Lastly, the image data. The image data is basically an array of indices, each a byte long, that refers to a palette entry in the palette. The image data is compressed using a form of Run-Length Encoding (RLE). The python algorithms to encode/decode the image data are shown below.
Encoding Algorithm
# input: Byte array
# output: Byte array
# input_size: Size of input in bytes (image width * image height)
# returns: Size of the compressed data
def mr_encode(input, output, input_size):
run = 0
length = 0
position = 0
while(position < input_size):
run = 1
while((run < 0x17f) and (position+run < input_size) and (input[position] == input[position+run])):
run += 1
if(run > 0xff):
output[length] = 0x82
length += 1
output[length] = 0x80 | (run - 0x100)
length += 1
output[length] = input[position]
length += 1
elif(run > 0x7f):
output[length] = 0x81
length += 1
output[length] = run
length += 1
output[length] = input[position]
length += 1
elif(run > 1):
output[length] = 0x80 | run
length += 1
output[length] = input[position]
length += 1
else:
output[length] = input[position]
length += 1
position += run
return length
Decoding Algorithm
# input: Byte array
# input_size: Size of input in bytes
# uncompressed_size: Size of uncompressed data in bytes (image width * image height)
# returns: Decoded image data
def mr_decode(input, input_size, uncompressed_size):
run = 0
position = 0
idx_position = 0
indexed_data = array("B", "\x00" * uncompressed_size)
while(position < input_size):
first_byte = input[position]
if((position+1) < input_size):
second_byte = input[position+1]
# The bytes lower than 0x80 are recopied just as they are in the Bitmap
if(first_byte < 0x80):
run = 1
position += 1
# The tag 0x81 is followed by a byte giving directly the count of points
elif(first_byte == 0x81):
run = second_byte
first_byte = input[position+2]
position += 3
# The tag 0x82 is followed by the number of the points decoded in Run
# By retaining only the first byte for each point
elif(first_byte == 0x82 and second_byte >= 0x80):
run = second_byte - 0x80 + 0x100
first_byte = input[position+2]
position += 3
else:
run = first_byte - 0x80
first_byte = second_byte
position += 2
# Writing decompressed bytes
for i in range(run):
if(idx_position+i < uncompressed_size):
indexed_data[idx_position+i] = first_byte
idx_position += run
return indexed_data