5.4.08

MOO2 Graphics

I had in mind to post earlier about it. A while back, I got an e-mail from Patrick Owens:

Hello siron,

I stumbled across your site and just wanted to drop a note and say hello. It’s great to see that there are some people out there still playing MOO2. I see that Russ and Rob have both checked in with you, and it was interesting to hear news of Ken Burd, (D&D Online, huh? Well what do you know…)

I was one of the artists on the project, responsible for most of the ship designs and art, and most of the alien designs as well. Fortunately for my fragile ego, there doesn’t seem to be much criticism of the graphics, as dated as they are by today’s standards. While I’m sure most if not all of your questions are about the design and programming aspects of the game, let me know if any questions have cropped up about the graphics.

Best of luck with the site,

Patrick Owens
Of course I had some questions. Here are some further e-mail excerpts:
If I understand correctly you mean the appearance of the different races? Do you remember them?

Yes, I designed the "look" of the aliens. Of course, I had the first MOO aliens as a foundation, but after many discussions, Steve let us take them all in a slightly new direction for MOO2. I believe I designed all of them but the Mrrshan. I then passed off the designs to the very talented Judith Peterson, who created the color versions you see in the game.

Two little anecdotes: The bird guys became pterodactyl guys in the game, but there was also a bat guy version of the avian race. We went back and forth on it for awhile, and the pterodactyl guys finally won out. I still have the bat guy sketch around here somewhere. I'll see if I can find it for you.

The Antarans got redesigned about two dozen times. I think the design was never really finished or approved, we just basically ran out of time to fiddle with it anymore.

Also, I've seen the Bulrathi described as being pug-dog like; in truth, they were meant to be minotaur-like. So it goes.

I also designed the emblems for the various races, but I look at most of those today and think "what the hell was I thinking there?" Very few of them appeal to me nowadays.

Do you have a favorite color? Or what color do you expect as the most popular one?

At the time, my favorite was always the one I was currently working on. Looking at them today, the blue, red, silver, and purple ships are probably my favorites. Also, the orange ships were mostly designed by someone else, with slight revisions from me at Steve's request. I would be interested to know which color is the favorite among the fans. [The poll results are here.]

Another anecdote: At the eleventh hour, Steve came in and announced that we were going to be redoing all the ships in 3D Studio Max. Understand that almost all the ships were done at this point, and that we were shipping the product in about 6 weeks. To their credit, the 3D guys humored him for about two weeks, gamely trying to come up with a system that could produce that many ships in that short a time-span. It did not take long for them to realize that there was no way in hell it was going to happen though.

And of course I agree that the graphics are awesome... (have you ever seen MOO3 combat?) :(

I've never seen MOO3, but I only like about half of the stuff I did for MOO2. Some of it makes me cringe just thinking about it. The ships are the thing I like the most, so I'm glad we didn't have the time to redo them.

Nevertheless there is a guy who works on a tool to implement new graphics...and there are several fans who work on graphic mods...I hope thats OK for your ego!

To me, the best thing about the gaming community is the mod community. I love that people create their own stuff for their favorite games. I didn't notice new graphics on your site, but I'll go back and take a look now. I'm sure people are creating great stuff, and I'd love to take a look at it.

A small misunderstanding, there are no new graphics on this blog, but other MOO2 fans worked on projects. I meant AR81's pictures and Wolverine's attempt to create a tool. See Wolverine's description of the MOO2 graphic format at bottom.

Do you have kept some of your MOO2 work (f.e. even rejected artwork would be very interesting)?

I have a whole file of artwork for MOO2. I think most of it made it into the game, though. I'll take a look through it and see if there's anything that might be of interest. Most of the electronic art was created in the very outdated Animator Pro and DPaint. I'm not even sure I still have any apps that can open some of the files, but I'll look into that too.

Are you still in contact with other MOO2 developers?

None of them, unfortunately. I've only kept track of one of the artists I used to work with as well, (the insanely talented Michael Washburn.)

I do wish Ken would get in touch with you. He's a great guy and I'm sure he'd have some good insights into the game.

If you have any other questions, or if you'd like to forward some questions from the community, feel free to contact me.

Regards,
Patrick Owens

The above-mentioned tool to implement graphics was a project by Wolverine. Unfortunately, this tool is unfinished. But he wrote an interesting description about the MOO2 graphic formats. Since his blog was temporarily down, I was afraid we could lose his knowledge. I mailed him and he kindly allowed me to repost his descriptions:

Description of MOO2 graphic formats part 1

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 0×3f). 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 (0×23,0×7f,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 0×05. So the LEAST SIGNIFICANT BYTE (in this case 0×05) 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 0×1D5445EA). 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 0×0CB9037F 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 0×0280. 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 0×0800 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.

Description of MOO2 graphic formats part 2

It’s been a really long time. Many things changed, some usual and some not so usual twists and turns in my life caused abstinence from writing here. I had time to think about everything and I’ve finally decided to passionately dive into information technology as I used to and screw everything else. These are two things I’m really good at. But enough of this.

I’m not really good at keeping promises, but this time I’ve decided that I have to do this. It’s been a long time since I’ve written part one describing MOO2 graphic formats. If you want this initial description of the formats check “Description of MOO2 graphic formats”. In that post I have described how to retrieve graphic header information and external palettes. This time I’m going to show you how to read internal and mixed palettes, and how to retrieve each animation frame. Some of you skilled hackers out there probably figured it out, but still there could be some of you who want to know. Here we go.

Just as a quick reminder. The header of the graphic file consists of: two bytes width, two bytes height, two always null bytes, two bytes with frame count, two bytes of delay between frames, and two bytes of bit-flags. Then we have (frame_count + 1) * 4 bytes for each frame offset. For example if we had byte indication of three frames we would need to read sixteen bytes. Each four bytes within those sixteen would be a frame offset. Mind you that the last offset is pointing to a special frame-like identifier designating end of file (EOF). Let’s stop here for a moment. After you have read the header you must identify if Internal Palette flag has been set. If so the next offset immediately after frame position offsets is the beginning of internal palette. Internal palette has it’s own pseudo header. It consists of four bytes. Two bytes representing color shift of the internal palette in relation to standard palette. And two bytes representing number of colors. REMEMBER that all bytes should be read as LOW-ENDIAN! For more info check part 1 of this tutorial. After reading four bytes of internal palette header, there are actual color definitions.

Those internal palette color definitions are sequences of DAC RGB. Just to remind you what it means: each 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. For more information refer to Part 1 describing MOO2 graphic formats.

To read the whole internal palette you must read four bytes multiplied by number of colors from internal palette header. Then if you’d probably want to convert those colors to RGB, so remember to multiply color values by four. This will give you internal palette in RGB. Let’s stop for a moment.

It’s really rare in MOO2 to find images with internal palette that spans entire 256 color space. Mostly internal palletes will have color count of 64 or less. What it really means? If internal palette is less than entire color space it means that the internal palette is really a mixed one. That’s where the color shift from internal palette header comes in. Mixed palette is really internal pallete “mixed” with external palette. It means that internal palette substitutes number of colors within external palette. The color shift is basically the starting color of this substitution.

Let’s suppose we have graphic file with internal palette. We read from internal palette header that color shift is 24 and color number is 64. Then we read color values from internal palette, convert them to RGB and voila. We have read our internal palette. What’s next? To create mixed type palette we have to choose one of the external palettes. Which one? That’s a tough question really. There’s no indication whatsoever which one should we choose. They are probably hardcoded within MOO2 executable.You’ll have to experiment by yourself. Suppose we chose the first external palette available. What’s next? Well… It’s really simple. The external palette always has 256 colors beginning with 0 and ending at 255. If we have color shift at 24 we start substituting color values of external palette at 24 (not 23 as some of you may think, because palette starts at 0!) and all the way to 88, because we have 64 colors within internal palette. It’s really that simple.

There’s also another type of palettes. I have found them to be within 1×1 pixel size images. These are not really images, but a placeholders for internal palettes. Some of the SHIPS.LBX_XXX files have them. These special files are used exactly as mixed palette, but they apply not only to one image, but to many graphic files following this special “image”. If you used my viewer you could see that after encountering such special image, the following files are images of the ships for each “nation”. It’s not really nation, but if you look closely in MOO2, ships are chosen not by race, but by the color banner. And that’s it. Palette of this special image applies to each graphic representing ship until the next special image with internal palette is encountered.

Ok. This should be enough for you in getting familiar with palettes. So now we’ll get into reading actual graphics (frames).

If you want to read each frame you have to position your reading cursor (fp.seek() in C) to offset stored in memory. (Frame offsets reading was described in Part 1 of this tutorial). At this seek position we read two bytes for frame beginning indicator. It’s always 1. If it’s not, you should not continue to read the frame and skip to next offset. If you had frame beginning indicator the next thing is frame y indent (relative vertical position). This is the number of vertical pixels you should render frame relative to screen (or rendering context) top position. After reading those bytes we must stop here again for a while.

Each frame has multiple data sequences. I call them lines or sequences for short. In MOO2 graphics not needed bytes are not stored. This way of saving graphics also allowed transparency effects. If there’s no pixel it means it’s transparent. Transparency is achieved simply by storing relative indents (x and y positions). Each sequence (or color line) has it’s own pseudo header.

Sequence begins with two bytes representing number of pixels in sequence. It’s also a special indicator, but I’ll explain that later. For now you have to remember that the following applies only if this pixel number is greater than 0! Next we read two bytes for x indent (x relative position). This is the amount of pixels that should be skipped relative to previous sequence x position when rendering. If we had x indent of value 12 and our last sequence ended at 24th pixel on screen we should start painting this new sequence from 36th pixel. I hope this is clear enough.

Then we have to read pixel data. Pixel data is simply one byte multiplied by the number of pixels. So you have to read number of pixels. Pixel data is 8bit value (1 byte) which represents pixel color number within the palette. If you had value of 46 you use 46th color from current palette in memory when rendering. Simple isn’t it? BUT! You have to remember one thing. If number of pixels was odd value, YOU HAVE TO READ ADDITIONAL BYTE! This byte is then discarded, but you should always remember doing it. It’s probably linked to internal image loading mechanism used in MOO2.

So, let’s get back to our pixel number in sequence. I told you that the above applies only if pixel number in sequence is greater than zero. But what if pixel number IS ZERO? Well. It’s a special indication of something fancy going on (not really ;) ). If you encountered 0 in pixel number of sequence, you have to read additional two bytes. Those two bytes are y indent of the sequence. It means that you have to start painting next sequence in new relative y position, but resetting x indent to ZERO! It’s like a number of newlines + carriage return, except for graphics. Sequence that has y indent DOESN’T HAVE ANY PIXEL DATA. But that should be obvious.

However. There’s also one small glitch. If y indent equals 1000 it means we have encountered END OF FRAME. When encountering this we should save the frame data in memory and proceed to the next frame offset (if any).

That’s all. I hope you found this post useful. If you have any comments or questions please post them. Also please forgive me for not showing any pseudo code or hex data, but I think this should be pretty obvious. If it’s not download: moo2gfxview-008tar.gz unpack it and refer to file graphics.py, function __getframes. I know it’s in Python, but you should be able to understand it, because file reading in Python is almost the same as in C or C++.

Hope you liked it. If you have any comments or questions feel free to post them.

6 comments:

Wolverine said...

I think my input is somewhat exageratted here. ;) Ok I have cracked the format by myself at first, but it seems that Grig de Griz did it also at the same time. He also helped me to understand some parts of the format which I didn't, and pointed me to right direction.

So if it was not for Grig de Griz, probably I could do it, but it would take much more time. So big kudos to him.

Fredrik said...

Hmm... sorry, but how do I run your python program? I'm on Linux and I have python installed but I've never ever used it. Could you please give me a hint??

Cheers!

Android app developers said...

Hello..This is one of the Knowledgeable post.I like your blog talent.Supper.

Anonymous said...

wow man this file data structure makes the Doom WAD look so easy. And now days, data storage is so inefficiently arranged that it's basically as open as your own file system.

Anonymous said...

not bad, thanks sharing

The Eye of Antares said...

Very informative post. I always like hearing about interesting things that went on during a game's development.

Loading...