13 June 2015
The making of a Banania clone
The level data
The level files were encoded exactly as I predicted, a 50x21x13 16-bit integer array containing 50 levels
that are 21 blocks wide and 13 blocks high. Firstly, I had to find out if the array was in row-major or
column-major order. I used notepad++ to highlight certain blocks which helped me to visualise the level data.
The original level data in a human-readable format with highlights on '03' bytes.
The actual positions of the blocks if they are encoded in column-major order.
The corresponding level to the map data. As it turned out, the array was in column-major order. Like this, it's easy to see that the block '00' is air (empty) and the block '03' is grey with a red pin. And so on.
Extracting the sound files
The sound files were WAVE. All I had to do was read the offsets and lengths from the NE dumper file and copy that block of the binaries into a new file. Rename it to .wav and it's done. I later converted the sounds to mp3 to avoid compatibility issues. Internet Explorer is incapable of playing back WAVE. Yes, you heard right.
Reverse-engineering the image format
A tougher nut to crack was the image format. I knew the offsets and lengths, but the format was entirely
unknown (I wouldn't be surprised if someone knows the format and makes me look like a fool now). I didn't
even know the header length. Trying to match a magic number was unsuccessful. What can you do in such a
situation? Visualize! By adjusting the width of the notepad++ window, it is possible to find out the width of
Hmm... That doesn't look recognisable...
Hey, that looks like a pattern! Let's go on.
A rectangle. It cannot be a coincidence. Also note the wraparound.
We found the header length. Now it's a perfect rectangle. A button?
It's a pressed OK button, in row-major order, upside down! I was lucky that there was no image compression. Note the trailing zeros. This confused me at first but they may be some padding. Also note that in all those images, we highlighted two colours at once. One colour is half a byte (4 bit) which means there are only 16 different colours in this game.
Of course, it took me quite a bit longer to figure things out, especially the header length was something that I got wrong many times. Some pictures are too large such that this trick becomes much harder. Other pictures don't reveal their shape quite as quickly.
Analysing the header was also something that took longer than necessary. I highlighted the differences in the
headers and tried to figure out width and height.
There were three numbers that stood out. The second one, I figured out quickly, was the height of the image. However, the first number didn't make sense, and neither did the third. 0x7801 = 30'721. There is no way this image has a width or size so high. I was stumped. But then I suddenly got it: The numbers were little-endian! That's right, I have to invert the bytes. Stupid me, how could I not see that! 0x0178 = 376. 376px is the width of the image! The lesson I learned from that has been very valuable and saved me a lot of time later in a project I did for university where a sensor would supply little-endian values. It's one thing to know the theory about endianness, another to realize that the values you get are completely wrong because you forgot to invert the byte order. The last number is the size of the body of the image, in bytes. I didn't find out the meaning of the other numbers.
Now I had everything to convert the image to something modern like PNG - hold that thought! We don't have the
colour palette. There are 16 colours in Windows 3.x, remember?
Colour palette of Windows 3.0
Colour palette of Windows 3.1
I picked the brighter palette (Win 3.0) because bright colours are prettier. Also, changing palette is
trivial - I considered adding a menu option to switch between the palettes but that didn't make it into the
game (so far). I wrote a little PHP script (PHP because it is so easy and productive in handling images) that
parses this obscure format and spits out PNG images. This is how the output looked like:
I sliced the image by hardcoding the offsets in PHP since I didn't find any metadata about sub-images. Also, the separating lines had different colours for different images and the lines only separated the image into vertical stripes. Some images didn't have separating lines at all despite having multiple sub-images. One image also had an issue with the colour palette, two colours were exactly swapped. Very obscure indeed. I'm still curious about this format.
Despite making massive mistakes, I went on and produced a monolithic block of roughly 3000 lines of code. I'm not proud of the quality of my code. But I learned a lot and it seems to work. I didn't read the assembly code of Banania to figure out the game mechanics, I largely drew conclusions from videos of gameplay and playing it myself. Ironically, the original game doesn't work on Windows 7 anymore but it does with Wine on Linux. It was also my first complete playthrough of the game; I was never able to beat Banania as a kid.
Very interesting. Can you say more about how you recreated the monsters behaviour? Did you find and reproduce the original one? How do they move, cause it seems a mix of algorithm and random? Or did you create your own rules?
Can you put the game the lines of code for download