Skip to content

Commit

Permalink
Add tool to extract hardcoded data from swep1rcr.exe
Browse files Browse the repository at this point in the history
  • Loading branch information
JayFoxRox committed May 7, 2018
1 parent 6756098 commit d2d8abb
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ set(CMAKE_C_STANDARD 11)
add_definitions(-D__cdecl=)

add_executable(decompress decompress.c)
add_executable(extract-data extract-data.c)
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ This is a collection of small tools to work with files from the 1999 Game "Star
- extract-racer-tab.py: Extracts strings from swep1rcr.exe which are typcially translated
- parse-racer-tab.py: Validates racer.tab translation files
- decompress.c: Decompress "Comp" modelblock chunks (found in N64 version)
- extract-data.c: Extracts hardcoded data from swep1rcr.exe

## Installation

Expand Down
209 changes: 209 additions & 0 deletions extract-data.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>

#define PTR(x) uint32_t

#define PACKED __attribute__((packed))

typedef struct {
uint8_t index; // absolute index to identify part?
uint8_t level; // level / quality?
uint8_t unk1; // ?
uint8_t category; // what kind of part it is?
int32_t price;
int32_t model; // model index
PTR(char*) title;
} PACKED PartData;

typedef struct {
uint32_t index;
uint32_t pod_model; // "Podd"
uint32_t pod_model_lod1; // "Malt": Good quality
uint32_t pod_model_lod2; // "Malt": Medium quality
uint32_t pod_model_lod3; // "Malt": Lowest quality
PTR(char*) first_name;
PTR(char*) last_name;
float unkf1; // Olganix: Scale for pod to fit into Cantina hologram ?
float unkf2; // Olganix: Scale for character to fit into Cantina hologram ?
uint32_t unk1;
uint32_t photo_sprite;
uint32_t flag_sprite;
uint32_t character_model;
} PACKED PodracerData;

typedef struct {
uint32_t track_model;
uint32_t unk_spline; // Spline for this track - unknown usage
uint8_t raceIndexOnPlanet;
uint8_t planet;
uint8_t trackFavorite; // This is only byte-accessed by the game
uint8_t pad; // padding?
} PACKED TrackData;

static void* readExe(FILE* f, off_t offset, size_t size) {

/*
From `objdump -x swep1rcr.exe` for the patched US version:
0 .text 000aa750 00401000 00401000 00000400 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .rdata 000054a2 004ac000 004ac000 000aac00 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .data 00023600 004b2000 004b2000 000b0200 2**2
CONTENTS, ALLOC, LOAD, DATA
3 .rsrc 000017b8 00ece000 00ece000 000d3800 2**2
*/

if ((offset >= 0x00401000) && (offset < (0x00401000 + 0x000aa750))) {
// .text
offset -= 0x00401000;
offset += 0x00000400;
} else if ((offset >= 0x004ac000) && (offset < (0x004ac000 + 0x000054a2))) {
// .rdata
offset -= 0x004ac000;
offset += 0x000aac00;
} else if ((offset >= 0x004b2000) && (offset < (0x004b2000 + 0x00023600))) {
offset -= 0x004b2000;
offset += 0x000b0200;
} else {
// .rsrc or unsupported
printf("Unknown offset: 0x%08X\n", offset);
//assert(false);
return strdup("");
}

// Allocate space for the read
void* data = malloc(size);

// Read the data from the exe
off_t previous_offset = ftell(f);
fseek(f, offset, SEEK_SET);
fread(data, size, 1, f);
fseek(f, previous_offset, SEEK_SET);

return data;
}

static char* escapeString(const char* string) {
char* escaped_string = malloc(strlen(string) * 2 + 1);
const char* s = string;
char* d = escaped_string;
while(*s != '\0') {
if ((*s == '\"') || (*s == '\'') || (*s == '\\')) {
*d++ = '\\';
}
*d++ = *s++;
}
*d = '\0';
return escaped_string;
}

static char* reallocEscapedString(char* string) {
char* escaped_string = escapeString(string);
free(string);
return escaped_string;
}

int main(int argc, char* argv[]) {

FILE* f = fopen(argv[1], "rb");
assert(f != NULL);

// Read timestamp of binary to see which base version this is
uint32_t timestamp;
fseek(f, 216, SEEK_SET); //FIXME: Parse headers properly
fread(&timestamp, 4, 1, f);

// Now set the correct pointers for this binary
off_t replacementPartOffset;
off_t podracerOffset;
off_t trackOffset;
switch(timestamp) {
case 0x3C60692C:
replacementPartOffset = 0x4C1CB8;
podracerOffset = 0x4C2700;
trackOffset = 0x4BFEE8;
break;
default:
printf("Unsupported version of the game, timestamp 0x%08X\n", timestamp);
return 1;
}

// Dump the list of replacement parts
unsigned int replacementPartCount = 7 * 6; // 7 categories x 6 levels - FIXME: Read from file?
PartData* replacementParts = readExe(f, replacementPartOffset, replacementPartCount * sizeof(PartData));
size_t max_title_len = 0;
for(unsigned int i = 0; i < replacementPartCount; i++) {
PartData* d = &replacementParts[i];
char* title = reallocEscapedString(readExe(f, d->title, 4096)); // FIXME: What length would be good here?
if (strlen(title) > max_title_len) {
max_title_len = strlen(title);
}
free(title);
}
printf("PartData replacementParts[] = {\n");
for(unsigned int i = 0; i < replacementPartCount; i++) {
PartData* d = &replacementParts[i];
char* title = reallocEscapedString(readExe(f, d->title, 4096)); // FIXME: What length would be good here?
printf(" { PART_%d, %d, 0x%02X, PART_CATEGORY_%d, %5d, \"%s\"%*s, MODEL_%d },\n", d->index, d->level, d->unk1, d->category, d->price, title, max_title_len - strlen(title), "", d->model);
free(title);
}
printf("};\n");
free(replacementParts);

printf("\n");

// Dump podracer information
unsigned int podracerCount = 23;
PodracerData* podracers = readExe(f, podracerOffset, podracerCount * sizeof(PodracerData));
printf("PodracerData podracers[] = {\n");
for(unsigned int i = 0; i < podracerCount; i++) {
PodracerData* d = &podracers[i];
char* first_name = reallocEscapedString(readExe(f, d->first_name, 4096)); // FIXME: What length would be good here?
char* last_name = reallocEscapedString(readExe(f, d->last_name, 4096)); // FIXME: What length would be good here?
printf(" { PODRACER_%d, MODEL_%d, MODEL_%d, MODEL_%d, MODEL_%d, \"%s\", \"%s\", %ff, %ff, %d, SPRITE_%d, SPRITE_%d, MODEL_%d },\n",
d->index,
d->pod_model,
d->pod_model_lod1,
d->pod_model_lod2,
d->pod_model_lod3,
first_name, last_name,
d->unkf1,
d->unkf2,
d->unk1,
d->photo_sprite,
d->flag_sprite,
d->character_model);
free(first_name);
free(last_name);
}
printf("};\n");
free(podracers);

//FIXME: Dump podracer handling data

printf("\n");

// Dump list of tracks
unsigned int trackCount = 25;
TrackData* tracks = readExe(f, trackOffset, trackCount * sizeof(TrackData));
printf("TrackData tracks[] = {\n");
for(unsigned int i = 0; i < trackCount; i++) {
TrackData* d = &tracks[i];
printf(" { MODEL_%d, SPLINE_%d, %d, PLANET_%d, PODRACER_%d, 0x%02X },\n",
d->track_model, d->unk_spline, d->raceIndexOnPlanet, d->planet, d->trackFavorite, d->pad);
}
printf("};\n");
free(tracks);




fclose(f);

return 0;
}

0 comments on commit d2d8abb

Please # to comment.