Skip to content

Commit 96582ee

Browse files
committed
Add tool to extract hardcoded data from swep1rcr.exe
1 parent 6756098 commit 96582ee

File tree

3 files changed

+211
-0
lines changed

3 files changed

+211
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ set(CMAKE_C_STANDARD 11)
66
add_definitions(-D__cdecl=)
77

88
add_executable(decompress decompress.c)
9+
add_executable(extract-data extract-data.c)

README.md

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

1415
## Installation
1516

extract-data.c

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <stdint.h>
4+
#include <stdbool.h>
5+
#include <assert.h>
6+
#include <string.h>
7+
8+
#define PTR(x) uint32_t
9+
10+
#define PACKED __attribute__((packed))
11+
12+
typedef struct {
13+
uint8_t index; // absolute index to identify part?
14+
uint8_t level; // level / quality?
15+
uint8_t unk1; // ?
16+
uint8_t category; // what kind of part it is?
17+
int32_t price;
18+
int32_t model; // model index
19+
PTR(char*) title;
20+
} PACKED PartData;
21+
22+
typedef struct {
23+
uint32_t index;
24+
uint32_t pod_model; // "Podd"
25+
uint32_t pod_model_lod1; // "Malt": Good quality
26+
uint32_t pod_model_lod2; // "Malt": Medium quality
27+
uint32_t pod_model_lod3; // "Malt": Lowest quality
28+
PTR(char*) first_name;
29+
PTR(char*) last_name;
30+
float unkf1; // Olganix: Scale for pod to fit into Cantina hologram ?
31+
float unkf2; // Olganix: Scale for character to fit into Cantina hologram ?
32+
uint32_t unk1;
33+
uint32_t unk2;
34+
uint32_t unk3;
35+
uint32_t character_model;
36+
} PACKED PodracerData;
37+
38+
typedef struct {
39+
uint32_t track_model;
40+
uint32_t unk; // An index / object reference of some sort probably
41+
uint8_t raceIndexOnPlanet;
42+
uint8_t planet;
43+
uint8_t trackFavorite; // This is only byte-accessed by the game
44+
uint8_t pad; // padding?
45+
} PACKED TrackData;
46+
47+
static void* readExe(FILE* f, off_t offset, size_t size) {
48+
49+
/*
50+
From `objdump -x swep1rcr.exe` for the patched US version:
51+
52+
0 .text 000aa750 00401000 00401000 00000400 2**2
53+
CONTENTS, ALLOC, LOAD, READONLY, CODE
54+
1 .rdata 000054a2 004ac000 004ac000 000aac00 2**2
55+
CONTENTS, ALLOC, LOAD, READONLY, DATA
56+
2 .data 00023600 004b2000 004b2000 000b0200 2**2
57+
CONTENTS, ALLOC, LOAD, DATA
58+
3 .rsrc 000017b8 00ece000 00ece000 000d3800 2**2
59+
*/
60+
61+
if ((offset >= 0x00401000) && (offset < (0x00401000 + 0x000aa750))) {
62+
// .text
63+
offset -= 0x00401000;
64+
offset += 0x00000400;
65+
} else if ((offset >= 0x004ac000) && (offset < (0x004ac000 + 0x000054a2))) {
66+
// .rdata
67+
offset -= 0x004ac000;
68+
offset += 0x000aac00;
69+
} else if ((offset >= 0x004b2000) && (offset < (0x004b2000 + 0x00023600))) {
70+
offset -= 0x004b2000;
71+
offset += 0x000b0200;
72+
} else {
73+
// .rsrc or unsupported
74+
printf("Unknown offset: 0x%08X\n", offset);
75+
//assert(false);
76+
return strdup("");
77+
}
78+
79+
// Allocate space for the read
80+
void* data = malloc(size);
81+
82+
// Read the data from the exe
83+
off_t previous_offset = ftell(f);
84+
fseek(f, offset, SEEK_SET);
85+
fread(data, size, 1, f);
86+
fseek(f, previous_offset, SEEK_SET);
87+
88+
return data;
89+
}
90+
91+
static char* escapeString(const char* string) {
92+
char* escaped_string = malloc(strlen(string) * 2 + 1);
93+
const char* s = string;
94+
char* d = escaped_string;
95+
while(*s != '\0') {
96+
if ((*s == '\"') || (*s == '\'') || (*s == '\\')) {
97+
*d++ = '\\';
98+
}
99+
*d++ = *s++;
100+
}
101+
*d = '\0';
102+
return escaped_string;
103+
}
104+
105+
static char* reallocEscapedString(char* string) {
106+
char* escaped_string = escapeString(string);
107+
free(string);
108+
return escaped_string;
109+
}
110+
111+
int main(int argc, char* argv[]) {
112+
113+
FILE* f = fopen(argv[1], "rb");
114+
assert(f != NULL);
115+
116+
// Read timestamp of binary to see which base version this is
117+
uint32_t timestamp;
118+
fseek(f, 216, SEEK_SET); //FIXME: Parse headers properly
119+
fread(&timestamp, 4, 1, f);
120+
121+
// Now set the correct pointers for this binary
122+
off_t replacementPartOffset;
123+
off_t podracerOffset;
124+
off_t trackOffset;
125+
switch(timestamp) {
126+
case 0x3C60692C:
127+
replacementPartOffset = 0x4C1CB8;
128+
podracerOffset = 0x4C2700;
129+
trackOffset = 0x4BFEE8;
130+
break;
131+
default:
132+
printf("Unsupported version of the game, timestamp 0x%08X\n", timestamp);
133+
return 1;
134+
}
135+
136+
// Dump the list of replacement parts
137+
unsigned int replacementPartCount = 7 * 6; // 7 categories x 6 levels - FIXME: Read from file?
138+
PartData* replacementParts = readExe(f, replacementPartOffset, replacementPartCount * sizeof(PartData));
139+
size_t max_title_len = 0;
140+
for(unsigned int i = 0; i < replacementPartCount; i++) {
141+
PartData* d = &replacementParts[i];
142+
char* title = reallocEscapedString(readExe(f, d->title, 4096)); // FIXME: What length would be good here?
143+
if (strlen(title) > max_title_len) {
144+
max_title_len = strlen(title);
145+
}
146+
free(title);
147+
}
148+
printf("PartData replacementParts[] = {\n");
149+
for(unsigned int i = 0; i < replacementPartCount; i++) {
150+
PartData* d = &replacementParts[i];
151+
char* title = reallocEscapedString(readExe(f, d->title, 4096)); // FIXME: What length would be good here?
152+
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);
153+
free(title);
154+
}
155+
printf("};\n");
156+
free(replacementParts);
157+
158+
printf("\n");
159+
160+
// Dump podracer information
161+
unsigned int podracerCount = 23;
162+
PodracerData* podracers = readExe(f, podracerOffset, podracerCount * sizeof(PodracerData));
163+
printf("PodracerData podracers[] = {\n");
164+
for(unsigned int i = 0; i < podracerCount; i++) {
165+
PodracerData* d = &podracers[i];
166+
char* first_name = reallocEscapedString(readExe(f, d->first_name, 4096)); // FIXME: What length would be good here?
167+
char* last_name = reallocEscapedString(readExe(f, d->last_name, 4096)); // FIXME: What length would be good here?
168+
printf(" { PODRACER_%d, MODEL_%d, MODEL_%d, MODEL_%d, MODEL_%d, \"%s\", \"%s\", %f, %f, 0x%08X, 0x%08X, %d, MODEL_%d },\n",
169+
d->index,
170+
d->pod_model,
171+
d->pod_model_lod1,
172+
d->pod_model_lod2,
173+
d->pod_model_lod3,
174+
first_name, last_name,
175+
d->unkf1,
176+
d->unkf2,
177+
d->unk1,
178+
d->unk2,
179+
d->unk3,
180+
d->character_model);
181+
free(first_name);
182+
free(last_name);
183+
}
184+
printf("};\n");
185+
free(podracers);
186+
187+
//FIXME: Dump podracer handling data
188+
189+
printf("\n");
190+
191+
// Dump list of tracks
192+
unsigned int trackCount = 25;
193+
TrackData* tracks = readExe(f, trackOffset, trackCount * sizeof(TrackData));
194+
printf("TrackData tracks[] = {\n");
195+
for(unsigned int i = 0; i < trackCount; i++) {
196+
TrackData* d = &tracks[i];
197+
printf(" { MODEL_%d, 0x%08X, %d, PLANET_%d, PODRACER_%d, 0x%02X },\n",
198+
d->track_model, d->unk, d->raceIndexOnPlanet, d->planet, d->trackFavorite, d->pad);
199+
}
200+
printf("};\n");
201+
free(tracks);
202+
203+
204+
205+
206+
fclose(f);
207+
208+
return 0;
209+
}

0 commit comments

Comments
 (0)