Unlocking Rayman 1 (PC Version)
Posted: Sun Nov 14, 2010 2:51 pm
As the "Project: UnLockRay ( ULR / REL )" thread is getting more and more bloated and as it was only made for the announcement, I'm making a new thread to talk about all the work done so far.
Also I hope this will help people to start hacking on their on and, hopefully, make the project go on faster.
This thread will ONLY cover Rayman 1 for PC. Please stop talking about Rayman 2/3/etc. or about the PSX/Jaguar versions of Rayman 1.
A few month ago, I began to look for help from some people already working on reverse engineering of old PC games such as ScummVM and CloneKeen. I got a bit of a help from Caitlin Shaw (Kate) one of the persons behind CloneKeen.
Kate started in a very brutal way by installing Rayman in DOSbox, looking at the files in the installation folder and renaming the VIGNET.DAT file to something else to see how was the game acting.
Luckily the game crashed AND sayed something interesting.
Bingo ! That clearly means one of the textures inside VIGNET.DAT (if not all) is a PCX image.
Kate then used her knowledge about the PCX file format to make a research of a valid PCX file header in the VIGNET.DAT file using an hexadecimal editor.
By removing all the data before the (only) header found she ended up with black 384x288 PCX image meaning that the palette of the image is corrupted or missing.
While being opened with a image editor (here GIMP), the file looks like this :

If like me you spent all your childhood playing Rayman 1, you can clearly recognize one of the backgrounds of the game.
Her work stopped here as we simply stopped to send emails to each other because I was getting busy.
A few hours ago I started to work again on the other files of Rayman 1 and I quickly understood some interesting things.
The Rayman installation folder (or the CD whatever) contains the following files (I list them with the original gussing of what it contains) :
First thing I've done was to import the SND*.DAT files as RAW sound files in Audacity. It turned out I was right, except for SNDH8B.DAT which remains an unknown-purpose file.
Both SNDD8B.DAT and SNDVIG.DAT contains PCM-like sound in Mono, 8bit, 11025Hz.
The sound is super fuzzy, but you can clearly heard that it's all the sounds effects of Rayman 1.
(You can download the already decoded files here : SNDH8B.DAT, SNDD8B.DAT, SNDVIG.DAT)
This is where I'm stuck, my knowledge is too low to understand what is the correct algorithms to decode the file.
It reminds me when I tried to decode the musics of Rayman 2 and faced the same problem.
If you can recall, Synthesis (also known as Palorifgrodbierzrt) pointed out that the files were actually encoded using IMA-ADPCM and wrote the following piece of code to convert them as standard WAV :
If we are lucky, the Rayman 1 sound effects are also encoded using IMA-ADPCM. One thing for sure, it's some kind of PCM thinggy.
Now for the video files INTRO.DAT and CONCLU.DAT. One super easy way to see they really are the cutscenes it to swap their names and create a new player : You'll see the outro being played instead of the intro.
This one was also an easy thing. I randomly tried to play the INTRO.DAT file with VLC and... It worked !... almost...
VLC started to play a black image for some seconds. Nothing interesting, but VLC can give you some informations about the file it's playing and the codec it's using. It gaves me enough information to continue forward :
Finding information about FLIC... Comparing the INTRO.DAT and CONCLU.DAT files with some other FLIC videofiles with an hex editor... Yep ! Thoses files are FLIC files, but with a smaller header.
Tried to play it simple by typing "ffplay INTRO.DAT" into the Terminal (yeah I'm using GNU/Linux only) and BANG ! The intro is playing !
Still there's a problem, surely about the shorter header, the palette is messed up... so it looks like "Rayman on LSD".
I uploaded the result on YouTube so you can watch it.
So we also got the cutscences to be played but again there's a palette problem we have to fix.
That's all for now folks, I'll finish the thread by posting all the email we exchanged between Kate and me.

Happy hacking !
Also I hope this will help people to start hacking on their on and, hopefully, make the project go on faster.
This thread will ONLY cover Rayman 1 for PC. Please stop talking about Rayman 2/3/etc. or about the PSX/Jaguar versions of Rayman 1.
A few month ago, I began to look for help from some people already working on reverse engineering of old PC games such as ScummVM and CloneKeen. I got a bit of a help from Caitlin Shaw (Kate) one of the persons behind CloneKeen.
Kate started in a very brutal way by installing Rayman in DOSbox, looking at the files in the installation folder and renaming the VIGNET.DAT file to something else to see how was the game acting.
Luckily the game crashed AND sayed something interesting.
Code: Select all
Rayman says fatal error :
VIGNET.DAT : Can not open (pcx).Kate then used her knowledge about the PCX file format to make a research of a valid PCX file header in the VIGNET.DAT file using an hexadecimal editor.
By removing all the data before the (only) header found she ended up with black 384x288 PCX image meaning that the palette of the image is corrupted or missing.
While being opened with a image editor (here GIMP), the file looks like this :

If like me you spent all your childhood playing Rayman 1, you can clearly recognize one of the backgrounds of the game.
Her work stopped here as we simply stopped to send emails to each other because I was getting busy.
A few hours ago I started to work again on the other files of Rayman 1 and I quickly understood some interesting things.
The Rayman installation folder (or the CD whatever) contains the following files (I list them with the original gussing of what it contains) :
Code: Select all
VIGNET.DAT # VIGNET can stand for Vignettes which obviously means it contains textures
INTRO.DAT # INTRO stands for Introduction -> intro cutscene
CONCLU.DAT # CONCLU stands for Conclusion -> outro cutscene
RAYMAN.EXE # Obivous...
SNDD8B.DAT # SND stands for sound -> contains sound effects
SNDH8B.DAT ?? Same guess... but the doesn't contain any sound and doesn't speak much to me when openned with an hex editor
SNDVIG.DAT # Same guess. This file actually contains voices and misc. sounds I've never heard before
HMIDET.386 # .386 files are often drivers, DET surely stands for Detection. This file is absolutely useless for us.
HMIDRV.386 # Same guess -> DRV stands for Driver. Useless file.
RAY.LNG ?? LNG maybe stands for Languages, so maybe the file actually contains all of the text of the came but I can't tell
RAYMAN.CFG ?? CFG obvious stands for Configuration... but what ?
Both SNDD8B.DAT and SNDVIG.DAT contains PCM-like sound in Mono, 8bit, 11025Hz.
The sound is super fuzzy, but you can clearly heard that it's all the sounds effects of Rayman 1.
(You can download the already decoded files here : SNDH8B.DAT, SNDD8B.DAT, SNDVIG.DAT)
This is where I'm stuck, my knowledge is too low to understand what is the correct algorithms to decode the file.
It reminds me when I tried to decode the musics of Rayman 2 and faced the same problem.
If you can recall, Synthesis (also known as Palorifgrodbierzrt) pointed out that the files were actually encoded using IMA-ADPCM and wrote the following piece of code to convert them as standard WAV :
Code: Select all
/*
* Ray2Get
* Source Code
* by Palorifgrodbierzrt 2008
*
* Ray2Get is a little command-line tool that allows you
* to convert Rayman 2 musics (.apm files) to regular
* .wav files.
*
* The .apm files are encoded in ADPCM format. But they
* can't be converted or even played properly with any
* standard ADPCM decoder/reader because of their format
* specification.
*
* I used the example code from MultimediaWiki to decode
* from IMA-ADPCM to 16-bit PCM, and adapted it for a
* stereo output.
* I also tried to study the files' headers to get the
* correct initial values.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// The IMA step table
const unsigned short step_table[89] = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};
// The IMA index table
const signed char index_table[8] = {
-1, -1, -1, -1, 2, 4, 6, 8
};
signed int decode(unsigned char nibble, signed short *stepindex, signed int *step, signed int predictor)
{
/*
The following algorithm has been taken from the Multimedia Wiki,
and optimized for stereo output.
-> http://wiki.multimedia.cx/index.php?title=IMA_ADPCM
Don't ask me how it works...
*/
signed short diff = 0;
*stepindex += index_table[(nibble & 7)];
diff = *step >> 3;
if (nibble & 1) diff += (*step >> 2);
if (nibble & 2) diff += (*step >> 1);
if (nibble & 4) diff += *step;
if (nibble & 8) diff = -diff;
predictor += diff;
if (predictor > 32767) predictor = 32767;
else if (predictor < -32768) predictor = -32768;
if (*stepindex > 88) *stepindex = 88;
else if (*stepindex < 0) *stepindex = 0;
*step = step_table[(*stepindex)];
return predictor;
}
int main(int argc, char* argv[])
{
// Checking the command syntax
if (argc != 2)
{
printf("Error: too");
if (argc < 2) printf(" few ");
else printf(" many ");
printf("arguments.\nUsage: ray2get.exe file.apm file.wav\n");
exit(EXIT_FAILURE);
}
// Variables
unsigned char nibblel = 0; // The data to decode
unsigned char nibbler = 0;
signed int predictorl = 0; // The PCM16 data
signed int predictorr = 0;
signed short step_indexl = 0;
signed short step_indexr = 0;
signed int stepl = 0;
signed int stepr = 0;
unsigned int file_length = 0;
unsigned int tmp32;
char pcm_filename[strlen(argv[1]) + 5];
char* dot_in_filename = NULL;
// File buffers
FILE *adp = NULL;
FILE *pcm = NULL;
adp = fopen(argv[1], "rb");
if (adp == NULL)
{
printf("Error: unable to open file %s.\n", argv[1]);
exit(EXIT_FAILURE);
}
// Give the apm file's name to the wave file, changing the
// extension to .wav
strcpy(pcm_filename, argv[1]);
dot_in_filename = strrchr(pcm_filename, '.');
if (dot_in_filename != NULL) *dot_in_filename = 0;
strcat(pcm_filename, ".wav");
pcm = fopen(pcm_filename, "wb");
if (pcm == NULL)
{
printf("Error: unable to create output file.\n");
fclose(adp);
exit(EXIT_FAILURE);
}
// Wave file rendering...
// RIFF chunk
fprintf(pcm, "RIFF");
fseek(adp, 0, SEEK_END);
file_length = ftell(adp);
file_length -= 0x62; file_length *= 4; file_length += 36;
fwrite(&file_length, 4, 1, pcm);
// Format subchunk
fprintf(pcm, "WAVEfmt ");
tmp32 = 16; fwrite(&tmp32, 4, 1, pcm); // Subchunk size
tmp32 = 1; fwrite(&tmp32, 2, 1, pcm); // PCM
tmp32 = 2; fwrite(&tmp32, 2, 1, pcm); // Stereo
tmp32 = 22050; fwrite(&tmp32, 4, 1, pcm); // Frequency
tmp32 = 88200; fwrite(&tmp32, 4, 1, pcm); // Byte rate
tmp32 = 4; fwrite(&tmp32, 2, 1, pcm); // Block align
tmp32 = 16; fwrite(&tmp32, 2, 1, pcm); // Bits per sample
// Data subchunk
fprintf(pcm, "data");
file_length -= 36; fwrite(&file_length, 4, 1, pcm);
// Reading the initial values
// (predictor and step index for both channels)
fseek(adp, 0x2c, SEEK_SET);
fread(&predictorr, 4, 1, adp);
fread(&step_indexr, 2, 1, adp);
fseek(adp, 6, SEEK_CUR);
fread(&predictorl, 4, 1, adp);
fread(&step_indexl, 2, 1, adp);
// Initializing steps for predictors
stepl = step_table[step_indexl];
stepr = step_table[step_indexr];
// Seek for DATA chunk in apm file
fseek(adp, 0x64, SEEK_SET);
while (!feof(adp))
{
// First nibble decoding, left channel
fread(&nibblel, 1, 1, adp);
predictorl = decode(nibblel >> 4, &step_indexl, &stepl, predictorl);
// Writing output...
fwrite(&predictorl, 2, 1, pcm); // Writing output...
// First nibble decoding, right channel
fread(&nibbler, 1, 1, adp);
predictorr = decode(nibbler >> 4, &step_indexr, &stepr, predictorr);
// Writing output...
fwrite(&predictorr, 2, 1, pcm); // Writing output...
// Second nibble decoding, left channel
predictorl = decode(nibblel, &step_indexl, &stepl, predictorl);
// Writing output...
fwrite(&predictorl, 2, 1, pcm); // Writing output...
// Second nibble decoding, right channel
predictorr = decode(nibbler, &step_indexr, &stepr, predictorr);
fwrite(&predictorr, 2, 1, pcm); // Writing output...
}
fclose(pcm);
fclose(adp);
return 0;
}Now for the video files INTRO.DAT and CONCLU.DAT. One super easy way to see they really are the cutscenes it to swap their names and create a new player : You'll see the outro being played instead of the intro.
This one was also an easy thing. I randomly tried to play the INTRO.DAT file with VLC and... It worked !... almost...
VLC started to play a black image for some seconds. Nothing interesting, but VLC can give you some informations about the file it's playing and the codec it's using. It gaves me enough information to continue forward :
Code: Select all
Flux 0
Type : Video
Codec : Flic Video (FLIC)
Resolution : 240x150
Framerate : 14
Tried to play it simple by typing "ffplay INTRO.DAT" into the Terminal (yeah I'm using GNU/Linux only) and BANG ! The intro is playing !
Still there's a problem, surely about the shorter header, the palette is messed up... so it looks like "Rayman on LSD".
I uploaded the result on YouTube so you can watch it.
So we also got the cutscences to be played but again there's a palette problem we have to fix.
That's all for now folks, I'll finish the thread by posting all the email we exchanged between Kate and me.

Happy hacking !