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.