F1GP/WC TECHNICAL FAQ
_This document is just a collection of to stuff sent in by readers. I
don't currently have the time or inclination to edit it into a "real"
document; if anyone wants to volunteer I will happily delegate this
task. Mail me at dgy...@gdcarc.co.uk._
Much of this material is in the form of C source code. Needless to
say, familiarity with the C language is a definite plus. I haven't
edited the source in any way, so beware portability and compiler
problems, and note also that it's all for the PC version. You Amiga
owners seem a pretty tight-lipped bunch!
You can also look for my GPList program and GPTools source archive. I
never really finished GPList and will probably re-write it from
scratch as part of GPTools, but it does contain details of how the
setups, track records, and names files are stored. Both are available
from http://www.mal.com/~dgymer/f1gp/util/.
_________________________________________________________________
From: u...@rz.uni-karlsruhe.de
Subject: f1gp
To: dgy...@gdcarc.co.uk (gizmo)
Date sent: Mon, 1 May 1995 03:07:08 +0200 (CES)
[...]
In this case some details concerning your upcomming tech. FAQ:
Date of files on disk: 04/19/93
Length of gp.exe : 321878 Bytes
Version : 1.05
Offsets:
Driving Aids: $1E4FF
BHP : $1C212
Colors:
Cars : $26B24
Helmets : $26C4B
Team clothes: $26EBD (I haven't seen it used in any Editor ??)
.
.
.
More details are available if required (e.g.the exepack-alghorithm,
which
seems to be different to other versions, see above mentioned problems
with
F1ED).
_________________________________________________________________
From: u...@rz.uni-karlsruhe.de
Subject: Re: Karusell
To: dgy...@gdcarc.co.uk
Date sent: Wed, 14 Jun 1995 19:39:50 +0200 (CES)
[gp.exe] Compression (only verified with European 1.05 packed version)
may be you find this sequence of bytes (in hex):
.... ll hh B2 nn ll hh B0 .....
ll hh - Low and high byte of the count of bytes since last appearance of
a B0. This indicates that the following B2 is a compression
indicator
B2 - start of compressed area
nn - byte to repeat
ll hh - low and high byte of the count of repeats
B0 - end of compressed area
Example:
compressed: ... B2 4F 09 00 B0 00 01 4F 4F 4F 4F 06 07 00 B2 ....
9 x 4F | 7 bytes not compr. | next compressed
area
is expanded to: ... 4F 4F 4F 4F 4F 4F 4F 4F 4F 00 01 4F 4F 4F 06 ....
You need at least 8 equal bytes to spare some bytes.
_________________________________________________________________
From: Craig Heath <cr...@sco.COM>
To: dgy...@gdcarc.co.uk
Subject: F1GP Save File Checksums
Date sent: Wed, 19 Apr 95 19:16:19 BST
Here is a little C program which recalculates the checksums on
F1GP save files - enjoy!
- Craig @ SCO near London.
#include <stdio.h>
main(argc, argv)
char *argv[];
{
FILE *savefile;
long datasize;
unsigned short sum = 0, cycle = 0;
unsigned char c;
if (argc != 2) {
fprintf(stderr, "usage: %s save-file\n", argv[0]);
exit(1);
}
if ((savefile=fopen(argv[1], "rb+"))==NULL) {
fprintf(stderr, "%s: cannot open %s for update\n", argv[0],
argv[1]);
exit(1);
}
if (fseek(savefile, -4L, SEEK_END) < 0) {
fprintf(stderr, "%s: seek failed in %s\n", argv[0],
argv[1]);
exit(1);
}
datasize = ftell(savefile);
rewind(savefile);
while (datasize--) {
sum += (c = fgetc(savefile));
cycle = (cycle << 3) + (cycle >> 13);
cycle += c;
}
if (fseek(savefile, -4L, SEEK_END) < 0) {
fprintf(stderr, "%s: seek failed in %s\n", argv[0],
argv[1]);
exit(1);
}
if (fwrite(&sum, 2, 1, savefile) < 1 ||
fwrite(&cycle, 2, 1, savefile) < 1) {
fprintf(stderr, "%s: error writing %s checksum\n",
argv[0], argv[1]);
exit(1);
}
(void) fclose(savefile);
exit(0);
I believe the following code is also from Craig Heath.
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
typedef unsigned char uchar;
#define NPATCHES 5
uchar qtso1[] = { 0xf6,0x86,0x4a,0x12,0x40,0x74,0x23 };
uchar qtsn1[] = { 0xf6,0x86,0x4a,0x12,0x80,0x75,0x23 };
uchar mano1[] = { 0xb8,0x00,0x00,0x9a,0x0a,0x08 };
uchar mann1[] = { 0xb8,0x0b,0x00,0x9a,0x25,0x08 };
uchar mann2[] = { 0x8b,0xbb,0xe4,0x02 };
uchar aido1[] = { 0x3f,0x3e,0x3e,0x32,0x02 };
uchar aidn1[] = { 0x3f,0x3f,0x3f,0x3f,0x3f };
uchar ffdo1[] = { 0xec,0xa8,0x08,0x75,0xfb,0xec,0xa8,0x08,0x74,0xfb };
uchar ffdn1[] = { 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90 };
struct {
unsigned long offset[6];
char *oldpart1, *newpart1;
int length1, gap;
char *newpart2;
int length2;
char *name;
European Italian American European Italian American */
0x0ac14, 0x0ac14, 0x0ac14, 0x0c014, 0x0c014, 0x0c014,
qtso1, qtsn1, sizeof qtso1, 0, 0, 0, "practice tyres",
0x1aa61, 0x1aa2d, 0x1aa35, 0x1c104, 0x1c0d0, 0x1c0d8,
mano1, mann1, sizeof mano1, 1, mann2, sizeof mann2, "manual
crack",
0x1e4ff, 0x1e4cb, 0x1e4d3, 0x1fd36, 0x1fcf6, 0x1fd06,
aido1, aidn1, sizeof aido1, 0, 0, 0, "allow all aids",
0x33d80, 0, 0, 0x6c1b8, 0, 0,
"Siete:", "Seite:", 6, 0, 0, 0, "German spelling",
0x4a606, 0x4a57b, 0x4a560, 0x8d53a, 0x8d4aa, 0x8d49a,
ffdo1, ffdn1, sizeof ffdo1, 0, 0, 0, "fast fades"
0x33841, 0x3380d, 0x33803, 0x6bc79, 0x6bc39, 0x6bc37
void
main(int argc, char *argv[])
{
FILE *exe;
register i;
int ver;
uchar buf[64];
if (argc != 2) {
fprintf(stderr, "usage: %s exe-file\n", argv[0]);
exit(1);
}
if ((exe=fopen(argv[1], "rb+"))==NULL) {
fprintf(stderr, "%s: cannot open %s for update\n", argv[0],
argv[1]);
exit(1);
}
for (i=0; i<6; i++) {
if (fseek(exe, ver105off[i], SEEK_SET) == 0 &&
fread(buf, 12, 1, exe) == 1 &&
memcmp(buf, "Version 1.05", 12) == 0) {
printf("%s version (%spacked)\n", version[i % 3],
(i > 2) ? "un" :
"");
goto gotver;
}
}
fprintf(stderr, "%s: %s unrecognised version (v1.05 required)\n",
argv[0], argv[1]);
exit(1);
gotver:
ver = i;
for (i=0; i<NPATCHES; i++) {
if (table[i].offset[ver] == 0)
continue;
if (fseek(exe, table[i].offset[ver], SEEK_SET) != 0 ||
fread(buf, table[i].length1, 1, exe) < 1) {
fprintf(stderr, "%s: error reading %s\n", argv[0],
argv[1]);
exit(1);
}
if (memcmp(buf, table[i].oldpart1, table[i].length1) == 0) {
if (fseek(exe, table[i].offset[ver], SEEK_SET) != 0 ||
fwrite(table[i].newpart1, table[i].length1, 1, exe) < 1 ||
(table[i].gap &&
(fseek(exe, (long)table[i].gap, SEEK_CUR) != 0 ||
fwrite(table[i].newpart2, table[i].length2, 1, exe) < 1
))) {
fprintf(stderr, "%s: error writing %s\n", argv[0],
argv[1])
;
exit(1);
}
printf("%s patch: applied\n", table[i].name);
} else if (memcmp(buf, table[i].newpart1, table[i].length1) == 0) {
printf("%s patch: already applied\n", table[i].name);
} else {
printf("%s patch: not found!\n", table[i].name);
}
}
(void) fclose(exe);
exit(0);
From: John Robert Cole <s9506...@arcadia.cs.rmit.edu.au>
Subject: Re: Tire Cheats for WC.
To: dgy...@gdcarc.co.uk
Date sent: Sun, 18 Jun 1995 13:49:30 +0000 (australasia)
[...]
Here is some information that could be used in the technical FAQ or
passed onto someone who is writing an editor for word circuit, it
involves the points scoring system and modifying it as I have figured
out it's location.
(This is the ofsets for the US v1.05 version of the game.)
ofset length contains
Normal 026a59 6 bytes 0a 06 04 03 02 01
UnCompressed 0acfcf 26 bytes 0a 06 04 03 02 01 00 00 ... 00
Effectively every driver can obtain a point if the uncompressed
EXE was set up in that way, I have set up an EXE which is based around
the Indycar points scoring system and it works fine.
[...]
Chris "Dreams" Peper, maintainer of the official F1GP/WC FAQ
j.c.a.pe...@research.kpn.com