I thus spent a few days working on this in the past two weeks.
We have been asked not to share the original source code (and anyway you would have to be a bit of a masochist if you want to see C code from over 20 years ago). But I will show a small extract to give you an idea of the work involved. The original source code is in C and for DOS.
The source code for the mod conversion tool is very compact and starts with these two functions:
For anyone familiar with supporting both big endian and little endian platforms, what they do is obvious. They swap bytes to convert between big endian and little endian representations.
If you are wondering what big and little endians are, go read my first post in this blog on adding support for the mac version of Broken Sword (you can also read the third post in that series about fixing speech for some mac versions of Broken Sword).
Conversion between big endian and little endian should come as no surprise. The MOD format was originally developed for Amiga, which are (or at least were at the time) big endians computers. Looking at the specifications of the MOD format shows that it is indeed using the big endian convention. On the other hand the DOS operating system was working on little endian computers. Using a little endian format for the music in Mission Supernova thus made sense to avoid having to swap bytes during runtime. Every little helped at the time to get good performances...
Another thing visible in the code above and that should come as no surprise (at least for developers dealing with old platforms) is that an unsigned int is coded on two bytes (and not on 4 bytes as you would expect nowadays) and a long int uses 4 bytes.
The last point we can note is that functions and variables have German names. Fortunately for me I did study German at school and could understand most of the code straightaway without having to ask Google translate (or my German sister in law) for help.
The first step of my work was to rewrite the code of that tools so that it works on modern computers, whether they are using big endian or little endian conventions, and can be understandable by others.
- I replaced the byte swap function from the original source code with code we already have in ScummVM that handles byte swapping depending on the platform on which the code is run (so that for example reading a MOD file would only swap bytes when the code is run on a little endian computer).
- I replaced data types such as unsigned and long with types provided by ScummVM such as uint16 and int32.
- I rewrote the code to use ScummVM Common::File API instead of the low level DOS file access code.
- I translated variable and function names to English.
- I objectified the code a bit adding a ModReader class.
At this point, without the original MOD file, I had no way to know if the code I wrote was correct. Writing this code however helped me understand the differences between the MOD format and the format used by the Mission Supernova game.
The two formats are very similar, but besides the different endianness, there are a few other differences. Actually the format for the two parts of Mission Supernova is slightly different.
Here is a description of the MOD file header:
And one of the Mission Supernova part 1 data file header:
For the Mission Supernova part 2, there are only 15 instruments stored and not 22.
Note how some information is missing in the Mission Supernova data file. That means that we have to guess what that information should be when converting that data file back to a MOD file. Fortunately none of that missing information is really important. For example for the song name I just decided to use the name of the MOD file that was hardcoded in the original source code.
Some other information is just formatted in a different way, such as the Mission Supernova instruments data having a loop start and loop end instead of a loop start and loop length.
Also the Mission Supernova data file stores explicitly the number of patterns and the offsets of the samples data. Those have to be computed from other informations in the MOD format.
Note how some information is missing in the Mission Supernova data file. That means that we have to guess what that information should be when converting that data file back to a MOD file. Fortunately none of that missing information is really important. For example for the song name I just decided to use the name of the MOD file that was hardcoded in the original source code.
Some other information is just formatted in a different way, such as the Mission Supernova instruments data having a loop start and loop end instead of a loop start and loop length.
Also the Mission Supernova data file stores explicitly the number of patterns and the offsets of the samples data. Those have to be computed from other informations in the MOD format.
The other difference not seen above between the two formats is in the pattern data. Both are using 32 bit values, but they are not coded in exactly the same way. For details on the differences just look at the source code and comments in the rewritten tool.
This knowledge of the MSN music data file might be useful when we have to work on supporting the music in the game engine reimplementation. For now I used it to write some code to do the conversion the other way around: from the game data file to a MOD file.
This allowed me to check that the code is correct:
- By checking that the converted MOD file I am getting is played correctly in a player supporting that format.
- By doing a round trip conversion: converting from MSN data file to MOD and then back to MSN data and checking that I get back the original file.
My first round trip test actually resulted in the original and converted MSN data file having a one byte difference (every bytes were identical except one). The offset of that bytes indicated it was the second byte of the order list length value, coded on two bytes in the Mission Supernova format. And then I realised that I was using a char variable (that uses one byte) since in the MOD format the order list length is coded on one byte. Writing that variable on two bytes meant the second byte was garbage.
The final source code is available at https://github.com/criezy/scummvm-tools/tree/supernova/engines/supernova. At some point I might merge it in the main ScummVM repository.
Implementing this reverse conversion also allowed me to listen to the music without waiting for the games to be supported in ScummVM. And to let you enjoyed that music as well, here are recordings for the music of the first and second parts of Mission Supernova converted to MOD and played back in an OpenSource ProTracker clone.
Implementing this reverse conversion also allowed me to listen to the music without waiting for the games to be supported in ScummVM. And to let you enjoyed that music as well, here are recordings for the music of the first and second parts of Mission Supernova converted to MOD and played back in an OpenSource ProTracker clone.
Mission Supernova part 1 music
Mission Supernova part 2 music