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 [email protected].

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:             [email protected]
Subject:          f1gp
To:               [email protected] (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:             [email protected]
Subject:          Re: Karusell
To:               [email protected]
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 <[email protected]>
To:               [email protected]
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;
} table[NPATCHES] = {
/*      packed   packed   packed   unpacked unpacked unpacked
	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"
};

unsigned long ver105off[] = {
	0x33841, 0x3380d, 0x33803, 0x6bc79, 0x6bc39, 0x6bc37
};

char *version[] = { "European", "Italian", "US" };

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 <[email protected]>
Subject:          Re: Tire Cheats for WC.
To:               [email protected]
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.
[...]