Description of MOO2 graphic formats

Some time has passed since I’ve written something here. Some people have downloaded my viewer, however I know Python is not obvious to everybody so I’ve decided to publish information regarding graphic formats of Master Of Orion 2. I don’t have time to work on this viewer and it was always just a proof of concept that graphic modding of MOO2 can be done. I’m not saying no to further development of the viewer for MOO2, however my priorities have shifted once again and development of the viewer/converter halted for now.

First we’ll start with palette files. There are three types of palettes in MOO2. External, internal and mixed. Palette format in MOO2 is in DAC RGB which means that color is represented as four bytes, where each byte consists of a value from range of 0 to 63 in decimal. Each of these values must be then multiplied by four. The first byte represents alpha channel (which is really a pseudo alpha, it seems that it only has a value of either 00 or 01), the second is RED, third is GREEN and fourth is BLUE. Let’s take a look at one of the so called External palette files in this case FONTS.LBX_001 which is the second file where external palettes reside. Take a look at this hexadecimal representation of the beginning of the file:

0000:0000 01 00 00 00 01 00 03 00 01 04 04 06 01 06 06 08
0000:0010 01 09 09 0a 01 0b 0b 0d 01 0d 0d 0f 01 0f 0f 11
0000:0020 01 12 12 14 01 14 14 16 01 16 16 18 01 18 18 1a
0000:0030 01 1b 1b 1d 01 1d 1d 1f 01 1f 1f 21 01 21 21 23
0000:0040 01 22 22 24 01 24 24 26 01 26 26 28 01 27 27 2
0000:0050 01 29 29 2b 01 2b 2b 2d 01 2d 2d 2f 01 2f 2f 31
0000:0060 01 31 31 33 01 33 33 35 01 35 35 37 01 37 37 39
0000:0070 01 38 38 3a 01 39 39 3b 01 3b 3b 3d 01 3c 3c 3e
0000:0080 01 00 03 04 01 00 05 07 01 03 0a 0d 01 0a 0f 13
0000:0090 01 0e 12 16 01 13 16 1a 01 16 19 1e 01 1a 1d 21
0000:00a0 01 1e 21 25 01 22 25 29 01 27 29 2d 01 2c 2e 31

The first column is a hex offset and the second is the hex representation of byte values. I have set to bold every odd numbered color bytes and to normal an even numbered color bytes. Each four bytes represents one color in the format mentioned above. Within the external palette files (FONTS.LBX_001 to FONTS.LBX_013 and IFONTS.LBX_001 to IFONTS.LBX_004) palettes start from zero byte to 1023. Each external palette has 256 colors counted from 0 to 255. If you’d like to read one color you have to read four bytes. For example we have color represented by 01 0a 0f 13. Let’s assume we have read those four bytes and we want to convert them to RGB. The first byte with value 01 is alpha channel. However it only has 00 or 01 values. It probably acts as a boolean: either we have this color (value 01) or not (value 00). But I’m not sure. Perhaps it acts as a special modifier in MOO2 (transparency or rewritable color perhaps?). We can safely ignore it. The next three bytes represents DAC RGB values. It means that each intensity value must be multiplied by four. Take a look:

A R G B = 01 0a 0f 13 = 01 10 15 19

The range of values for R G B in DAC RGB is from 0 to 63 (hex 0x3f). If you want to get 24bit color representation you have to multiply each DAC RGB value by four:

red = dacred * 4; green = dacgreen * 4; blue = dacblue * 4;

If you want to convert 24bit RGB back to DAC RGB you have to do the reverse operation including rounding to integer. Let’s assume we have 24bit RGB color with values (35,127,193) which in hex is (0x23,0x7f,0xc1). You do it like this:

r = 35; dac_r = int(r / 4); g=127; dac_g = int(g / 4); b=193; dac_b = int(b / 4);

Remember! You have to round it to integer, otherwise sometimes you’ll get a floating point value. The external palette files reside within LBX archives FONTS.LBX and IFONTS.LBX. Those archives must be unpacked before they can be used as palette files. After unpacking FONTS.LBX you have to remember that FONTS.LBX_000 IS NOT PALETTE FILE! It’s only a font file. Palette files start from FONTS.LBX_001 (the second file in archive). Palettes reside within first 1024 bytes (256 * 4 bytes). However as you’ll see those files are not 1024 bytes long, but few times longer. That’s because there are also mouse cursors stored behind the palette. They’re visible in some files in a good hexeditor if you properly adjust the viewing columns. However I wasn’t able to figure out how they can be retrieved. I think that’s all regarding external palettes.

The internal palettes are stored within the graphic files. They have the same DAC RGB format as external palettes, however they are stored differently. But before I’ll give you description on how to read internal palettes I have to give you information regarding the graphic format itself.

REMEMBER! All multibyte values in MOO2 formats are stored as LITTLE ENDIAN! That means we have integer (2 bytes) and long (4 bytes) bytes representing the value stored in reverse fashion. Each atomic element size is 8bit which means it’s one byte long. For clarification let’s assume we have an integer with value of 52304 (remember that unsigned integers allow values in range of 0-65535 in that case). In hexadecimal this value is represented as 0xCC50. That means when we want to store this value we have to divide it to two bytes, in this case 0xCC and 0x05. So the LEAST SIGNIFICANT BYTE (in this case 0x05) is saved FIRST! It means that we save this value as “05 CC” not CC 05! The same applies to long. Let’s say we have long integer with value of 492062186 (hex 0x1D5445EA). In this case we have four 8 bit (one byte) atomic elements: “1D 54 45 EA”. The least significant byte is 0xEA. If we want to save it in little endian we have to do this in reverse order. The result would be: “EA 45 54 1D” and that’s what we save. The same applies except in reverse order when we want to read little-endian multibyte value. In this case if we have “7F 03 B9 0C” we reassemble this value reading the last byte first. The result must be 0x0CB9037F which is 213451647 in decimal. Hope that’s clear enough.

So now I can give you the information on how graphics are saved in MOO2. As our example we take the first unpacked file from BLDG0.LBX archive the BLDG0.LBX_000. This LBX archive stores graphics for the buildings visible on colony screen. If you want to properly view the buildings you have to use FONTS.LBX_002 external palette file. This graphic file we have taken shows the Alien Management Center building at center bottom of the colony screen. I will show you how the beginning of the file is seen in hexeditor.

0000:0000 80 02 e0 01 00 00 01 00 00 00 00 08 14 00 00 00
0000:0010 1c 21 00 00 01 00 55 01 01 00 0e 01 55 00 00 00
0000:0020 01 00 01 00 0e 01 55 00 00 00 01 00 01 00 0e 01
0000:0030 55 00 00 00 01 00 01 00 0e 01 55 00 00 00 01 00
0000:0040 01 00 0e 01 55 00 00 00 01 00 01 00 0e 01 55 00
0000:0050 00 00 01 00 01 00 0e 01 55 00 01 00 0c 00 55 00
0000:0060 00 00 01 00 01 00 0e 01 56 00 01 00 0c 00 55 00
0000:0070 00 00 01 00 01 00 0e 01 56 00 01 00 0c 00 55 00
0000:0080 00 00 01 00 01 00 0e 01 56 00 01 00 0c 00 55 00
0000:0090 00 00 01 00 01 00 0e 01 57 00 01 00 0c 00 55 00
0000:00a0 00 00 01 00 01 00 0e 01 57 00 01 00 0c 00 55 00
0000:00b0 00 00 01 00 01 00 0e 01 58 00 01 00 0c 00 55 00
0000:00c0 00 00 01 00 01 00 0e 01 58 00 01 00 02 00 55 00
0000:00d0 01 00 09 00 56 00 00 00 01 00 02 00 0d 01 55 d5
0000:00e0 02 00 02 00 55 55 01 00 08 00 56 00 00 00 01 00
0000:00f0 02 00 0d 01 d3 b8 01 00 02 00 55 00 01 00 09 00

This dump from hexeditor similar to the previous one. Left italic – offsets, right – byte values. I have colored some bytes and backgrounded sections of the file for clarity. The brownish background under the bytes shows the header of the graphic file. I’ll explain the header of the file now.

First two bytes marked as red are “80 02”. This is the width of the image. As you remember what I have told you about the multibyte values in MOO2 (little-endian) this value is in fact 0x0280. I wouldn’t remind you about this again so keep in mind that all multibyte values are LITTLE ENDIAN. In this case we have 640. The value represents width in pixels.

The next two bytes (green) is the height of the image. In this case it’s 480 so we have size of the image 640×480 pixels. The next two bytes are always zero. Two bytes marked in blue is number of frames (in this case one). Next two bytes marked in yellow is the frame delay i.e. delay between each frame is shown. It’s used in animations. In this case we have only one frame so the delay is 0, but if we had multi-frame graphic (animation) the delay would be set. The last two bytes in the header marked as cyan represents image flags. We have to stop here for a moment and explain those flags.

The flags is a two byte value also stored as little endian. Contrary to previous multibyte values this doesn’t represent decimal value, but a binary value. Each flag is represented as one bit within the value. Take a look:

0000 0000 0000 0000
..JI FB.N .... ....

This is the binary representation of the two byte flags. The first line shows bits and the line below corresponding flags. The dot in the second line means that setting this bit either doesn’t do anything (is not used) or the behaviour of the flag is unknown. I have given those flags unique letters and they are as follows:

  • J – Junction – I don’t know really what it’s for. Grig de Griz has pointed out this flag in his format description so I’m including it here. (TAlchemist pointed out that: “Junction flag means that you add each frame to the frame before it making a composted image. and start from the begining on the first frame. That is how only the first frame of the big animations is large and all other frames just include the pixels that changed.”)
  • I – Internal palette – Tells if the image has internal palette
  • F – Functional color – Tells if the image has functional color. This color is used for effects like transparency or shading.
  • B – Fill background – Tells the game if it should clear the area on which the image is going to be drawn.
  • N – No compression – Tells if the image uses compression. For me it’s behaviour is unknown. It’s probably a left-over from MOO1 graphic formats which are almost identical to MOO2.

Back to our header. We have the value “00 08” in our header for flags. This means the value is 0x0800 in hex. Let’s see what flags reside here. After converting this value to binary we get 0000 1000 0000 0000. Let’s compare this new value to our line describing the flags:

0000 1000 0000 0000
..JI FB.N .... ....
Voila! We now know that the image has functional color flag. If we had for example:

0011 1001 0000 0000
..JI FB.N .... ....

We would have junction, internal palette, functional color and no compress flags set. There are really only two flags that are significant for us. The more significant is INTERNAL PALETTE and the less significant is FUNCTIONAL COLOR. The first one would be covered later in greater detail. The second is really needed for properly plotting graphics and I wouldn’t give it too much attention, because I haven’t completely figured out how it works. All I can say for now regarding FUNCTIONAL COLOR is that it’s used in our BLDG0.LBX_000 for drawing shadows. If you manage to draw this image you’ll see that the shadow is represented by dark violet color, but within the game it’s giving a proper shadow on the colony background screen.

Let’s get back to our header. I’ll refresh your memory here:

0000:0000 80 02 e0 01 00 00 01 00 00 00 00 08 14 00 00 00
0000:0010 1c 21 00 00 01 00 55 01 01 00 0e 01 55 00 00 00

We have already covered all the bytes marked in colors up to cyan marked flags. What are those last eight bytes that are in the header? Those are four byte frame offsets. This needs greater explanation. Frame is the proper graphic that is displayed on the screen. It’s an animation frame. Since we have only one frame image (still image, not animation) here there are only two frame offsets, namely: “14 00 00 00” and “1c 21 00 00”. Those offsets say where within the file each frame starts. However I have said we have only one frame image here so why the hell we have two frame offsets you might ask. Well. It’s because we always have frame_number + 1 frames. The last frame isn’t de facto a frame. It’s a special frame-like identifier that tells that the end of file (EOF) has been reached. It’s probably connected to format reading specific used in MOO2. How to get frame offsets then? Here’s a little pseudo-code to show you how:

offset_array = array(); // Create result array of offsets
for (i=0; i<frame_number+1; i++) // Go through each frame offset
{
bytes = fp.read(4); // Read four bytes representing the offset
bytes = reverse_bytes(bytes); // Convert little endian to proper byte representation
offset = str_to_long(bytes); // Convert string to long number
offset_array.add(offset); // Add offset to offsets array
}

If you now want to read frames you just set internal file pointer (fp in the pseudo-code) to the offset of each frame. In C++ you can do this with: fp.seek(offset); and that should do.

That is all for now. I’ll explain frame reading and internal and mixed palettes in the next post. For now you can still download my viewer and browse the code or you can read somewhat outdated previous posts regarding the formats. For now the most complete source for getting information regarding the formats if still source code of my viewer. You can download it from the previous post and I encourage you to do so. Python is easy language, I have learned it in a day or so if you’re a programmer you’ll be able to decipher my code quite quickly. Greets to all and I’ll try to write the second part of the tutorial ASAP.

About Wolverine

If you are looking for IT consultant, let me know! karol at karoltomala dot REMOVE com Just remove the REMOVE word from the e-mail above!
This entry was posted in Coding, Formats, Hacking, Master Of Orion 2. Bookmark the permalink.

4 Responses to Description of MOO2 graphic formats

  1. TAlchemist says:

    I have found a few files that are not compressed. Try to remember the ones and from where. Junction flag means that you add each frame to the frame before it making a composted image. and start from the begining on the first frame. That is how only the first frame of the big animations is large and all other frames just include the pixels that changed.

  2. TAlchemist says:

    I would like to talk with you sometime about the formatting. I have an executable that allows me to start up the game and navigate all the main menus from inside my exe using the original palettes images and even all the animations work perfectly I have colonies drawing correctly and learning new technologies, i am working on rendering the star system now.

  3. Wolverine says:

    Sure. If you want to reach me you can do this writing email to me at karol at karoltomala dot com. Then I’ll give you my instant messenger contacts or we can arrange meeting on irc. However you must be patient because I’m not checking this email very often.

    Also thanks for more information, I’ll update this post when I’ll have some time.

  4. Pingback: Wolverine’s Blog » Blog Archive » Description of MOO2 graphic formats part 2

Leave a Reply

Your email address will not be published. Required fields are marked *