Skip to content

Instantly share code, notes, and snippets.

@samunders-core
Created July 28, 2017 21:32
Show Gist options
  • Save samunders-core/1acaadc064f203e4f2ab769c7dfabeda to your computer and use it in GitHub Desktop.
Save samunders-core/1acaadc064f203e4f2ab769c7dfabeda to your computer and use it in GitHub Desktop.
Fatal Racing / Whiplash game files (BM,DRH,KC,RAW,TRK) decoder
#ifndef UNMANGLE_H
#define UNMANGLE_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef debug
#define debug(...)
//#define debug(...) do { fprintf(stdout, __VA_ARGS__); } while(0)
#define UNMANGLE_H_remove_debug
#endif
struct kBuffer {
unsigned char values[1024]; // cannot go below 64
int offset;
};
void fill_kBuffer(FILE * f, struct kBuffer * buffer) {
fread(buffer->values, 1, sizeof(buffer->values), f);
buffer->offset = 0;
}
unsigned char * next(FILE * f, struct kBuffer * buffer, int lookahead) {
if (sizeof(buffer->values) < buffer->offset + lookahead) {
fseek(f, ftell(f) - (sizeof(buffer->values) - buffer->offset), SEEK_SET);
fill_kBuffer(f, buffer);
}
debug("file_byte[%ld]=%02X -> ", ftell(f) - (sizeof(buffer->values) - buffer->offset), buffer->values[buffer->offset]);
return &buffer->values[buffer->offset++];
}
int no_mangling_copy(FILE * source, char * base, char * destination, struct kBuffer * buffer, int count) {
memcpy(destination, next(source, buffer, count), count);
debug("memcpy(dest[%d], file_byte[%ld], %d)\n", destination - base, ftell(source) - (sizeof(buffer->values) - buffer->offset) - 1, count);
buffer->offset += count - 1; // next already incremented it
return count;
}
void generateAscendingBytes(char * base, char * destination, int count) {
int value = *(destination - 1) & 0x0FF;
int delta = value - (*(destination - 2) & 0x0FF);
debug("%dx dest[%d] = (%d += %d)\n", count, destination - base, value, delta);
while (count-- != 0) {
value += delta;
*destination++ = (char) (value & 0x0FF);
}
}
void generateAscendingWords(char * base, signed short * destination, int count) {
signed int value = *(destination - 1); // movsx ecx,w,[ebp][-2]
signed int delta = value - *(destination - 2); // movsx ecx,w,[ebp][-4]
debug("%dx dest[%d] = (short) (%d += %d)\n", count, ((char *) destination) - base, value, delta);
while (count-- != 0) {
value += delta;
*destination++ = (signed short) (value & 0x0FFFF);
}
}
void cloneByte(char * base, char * destination, int count) {
unsigned char value = *(destination - 1);
debug("memset(dest[%d], %c, %d)\n", destination - base, value, count);
while (count-- != 0) {
*destination++ = value;
}
}
void cloneWord(char * base, signed short * destination, int count) {
signed int value = *(destination - 1); // movsx eax,w,[ebp][-2]
debug("memset(dest[%d], (short) %d, %d)\n", ((char *) destination) - base, (signed short) (value & 0x0FFFF), count);
while (count-- != 0) {
*destination++ = (signed short) (value & 0x0FFFF);
}
}
int shuffle3Bytes(char * base, char * destination, int offset) {
debug("shuffle: dest[%d]=dest[%d]; dest[%d]=dest[%d]; dest[%d]=dest[%d];\n", destination - base, destination - offset - 3 - base, destination - base + 1, destination - offset - 2 - base, destination - base + 2, destination - offset - 1 - base);
char * src = destination - offset;
*(destination + 0) = *(src - 3);
*(destination + 1) = *(src - 2);
*(destination + 2) = *(src - 1);
return 3;
}
void copy(char * base, char * destination, char * source, unsigned int count) {
debug("memcpy(dest[%d], dest[%d], %d)\n", destination - base, source - base, count);
while (count--) { // memcpy cannot be used since source and destination overlap
*destination++ = *source++;
}
}
int unmangle_switch(FILE * source, char * base, char * destination, struct kBuffer * buffer, unsigned int value) {
if (value < 64) { // 0x40
return no_mangling_copy(source, base, destination, buffer, value);
} else if (value < 80) { // 0x50
generateAscendingBytes(base, destination, value = ((value & 0x0F) + 3));
} else if (value < 96) { // 0x60
generateAscendingWords(base, (signed short *) destination, (value & 0x0F) + 2);
value = 2 * ((value & 0x0F) + 2);
} else if (value < 112) { // 0x70
cloneByte(base, destination, value = ((value & 0x0F) + 3));
} else if (value < 128) { // 0x80
cloneWord(base, (signed short *) destination, (value & 0x0F) + 2);
value = 2 * ((value & 0x0F) + 2);
} else if (value < 192) { // 0xB0
return shuffle3Bytes(base, destination, value & 0x03F);
} else if (value < 224) { // 0xE0
char * ecx = destination - ((value & 0x03) << 8);
char * src = (ecx - *next(source, buffer, 1)) - 3;
copy(base, destination, src, value = (((value >> 2) & 0x07) + 4)); // sar ebx, 2; implies signed byte_value but it's never out of & 0x000000FF mask
} else { // 0xF0
char * ecx = destination - ((value & 0x01F) << 8);
ecx = (ecx - *next(source, buffer, 1)) - 3;
copy(base, destination, ecx, value = (*next(source, buffer, 1) + 5));
}
return value;
}
int unmangled_length(FILE * f) {
int length;
fseek(f, 0, SEEK_SET);
fread(&length, 1, 4, f);
return length;
}
void unmangle(FILE * source, char * destination, int length) {
if (4 != ftell(source)) {
fprintf(stderr, "Programming error: call unmangled_length(FILE *) first\n");
exit(1);
}
struct kBuffer buffer;
fill_kBuffer(source, &buffer);
char * dest = destination;
while (dest < destination + length) {
unsigned char value = *next(source, &buffer, 1) & 0x0FF;
dest += value != 0 ? unmangle_switch(source, destination, dest, &buffer, value) : length;
}
}
#ifdef UNMANGLE_H_remove_debug
#undef debug
#undef UNMANGLE_H_remove_debug
#endif
#ifdef __cplusplus
}
#endif
#endif /* UNMANGLE_H */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment