Created
July 28, 2017 21:32
-
-
Save samunders-core/1acaadc064f203e4f2ab769c7dfabeda to your computer and use it in GitHub Desktop.
Fatal Racing / Whiplash game files (BM,DRH,KC,RAW,TRK) decoder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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