Skip to content

Commit 20ae6b4

Browse files
committed
Add tool to extract hardcoded data from swep1rcr.exe
1 parent 7dc3239 commit 20ae6b4

File tree

4 files changed

+268
-1
lines changed

4 files changed

+268
-1
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
@@ -11,6 +11,7 @@ This is a collection of small tools to work with files from the 1999 Game "Star
1111
- parse-racer-tab.py: Validates racer.tab translation files
1212
- parse-savedata.py: Parses (tgfd.dat, 4056 bytes) and profile (*.sav, 84 bytes) files
1313
- decompress.c: Decompress "Comp" modelblock chunks (found in N64 version)
14+
- extract-data.c: Extracts hardcoded data from swep1rcr.exe
1415

1516
## Installation
1617

decompress.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#define MAGIC(a1, a2, a3, a4) (((a1) << 24) | ((a2) << 16) | ((a3) << 8) | (a4))
88

99
static inline uint32_t swap32(uint32_t v) {
10-
return ((v & 0xFF0000 | (v >> 16)) >> 8) | (((v << 16) | v & 0xFF00) << 8);
10+
return (((v & 0xFF0000) | (v >> 16)) >> 8) | (((v << 16) | (v & 0xFF00)) << 8);
1111
}
1212

1313
//----- (0042D520) --------------------------------------------------------

extract-data.c

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

0 commit comments

Comments
 (0)