Blast Corps uses a 512 byte EEPROM in the cartridge to save game state of levels unlocked, record times, and tutorials given. A copy of the EEPROM data is stored at 80364A40 in RAM.
Offset | Length | Description |
---|---|---|
0x000 | 0x8 | Name (ASCII name entered during game start) |
0x008 | 0x1 | Selected Level |
0x009 | 0x1 | Unknown? |
0x00A | 0x2 | Points |
0x00C | 0x1 | Rank |
0x00D | 0x3 | Unknown? |
0x010 | 0x4 | Vehicles |
0x014 | 0x4 | Total Accumulated Money |
0x018 | 0x3C | Level Medals |
0x054 | 0x3C | Level Paths |
0x090 | 0x1 | Scientists |
0x091 | 0x1 | Event |
0x092 | 0x3C | Level Vehicles |
0x0CE | 0x12 | Tutorials |
0x0E0 | 0xE | Unknown? |
0x0EE | 0x1 | Cut Scenes |
0x0EF | 0x4 | Unknown? |
0x0F3 | 0x1 | Control Mode |
0x0F4 | 0x8 | Unknown? |
0x0FC | 0x4 | Checksum |
0x100 | 0xF0 | Level Times |
0x1F0 | 0x8 | Language 0x1982198219821982 = English 0x1945194519451945 = German |
0x1F8 | 0x8 | Footer 0x2704197125121981 = Before saving rest of EEPROM data 0x87569AB6CD076AEC = After saving rest of EEPROM data |
The record time is stored for each level as a 2-byte integer in big endian in units of hundredths of a second followed by two bytes of checksum. The checksum is computed as follows (where ^ is XOR):
record[2] = 0x55 ^ record[0]; record[3] = 0xFF ^ record[0] ^ record[1] ^ record[2];
For example, if the record value is 0x00E4554E, the time is 0x00E4 = 228 = 22.8 seconds.
Checksum verifies because 0x00 ^ 0x55 = 0x55, and 0xFF ^ 0x00 ^ 0xE4 ^ 0x55 = 0x4E
The 4-byte checksum at 0xFC is computed using functions that are stored in the hd_front_end_text.raw gzip file. The UpdateChecksum/801F2C84 function takes a pointer to the start of the EEPROM data in A0 and an offset to compute the checksum over and store result in A1. It uses helper function ComputeChecksumByte/80205760 to compute each round.
// helper function for checksum update routine below static u8 ComputeChecksumByte(u8 *data) // 80205760 { u8 cksum = 0; u8 key; for (int i = 0; i < 33; i++) { for (int j = 7; j >= 0; j--) { if (cksum & 0x80) { key = 0x85; } else { key = 0x00; } cksum = cksum << 1; if (i == 0x20) { cksum = cksum & 0xFF; } else { u8 mask = data[i] & (1 << j); u8 bit = mask ? 1 : 0; cksum |= bit; } cksum = cksum ^ key; } } return cksum; } // usually called with base address of EEPROM data and length = 0x100 // this will update checksum at eeprom[0x100 - 4] u8 UpdateChecksum(u8 *eeprom, int length) // 801F2C84 { u8 cksum[4]; int blocks = (length + 0x7F) >> 7; // zero out existing EEPROM checksum and local buffer for (int i = 0; i < 4; i++) { cksum[i] = 0; eeprom[length + i - 4] = 0; } if (blocks > 0) { for (int i = 0; i < blocks; i++) { for (int j = 0; j < 4; j++) { int offset = (i << 7) + (j << 5); if (offset < length) { u8 ckbyte = ComputeChecksumByte(eeprom + offset); cksum[j] += (ckbyte & 0xFF); } } } } // copy the checksum into the EEPROM data for (int i = 0; i < 4; i++) { // 801F2C84 eeprom[length + i - 4] = cksum[i]; } return 0; }
The EEPROM functions are all stored in the hd_front_end_text.raw gzip file. In the (U) (1.0) ROM, these are inflated to 0x801E7000.
RAM/offset | Function |
---|---|
801F1944/00A944 | int ManageEeprom(u8 block, u8 mode) |
801F2C84/00BC84 | u8 UpdateChecksum(u8 *eeprom, int length) |
80205760/01E760 | u8 ComputeChecksumByte(u8 *data) |
80202850/01B850 | s32 osEepromProbe(OSMesgQueue *mq) |
802042A0/01D2A0 | s32 osEepromLongWrite(OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes) |
802043E0/01D3E0 | s32 osEepromLongRead(OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes) |
80204BE0/01DBE0 | s32 osEepromWrite(OSMesgQueue *mq, u8 address, u8 *buffer) |
802050C0/01E0C0 | s32 osEepromRead(OSMesgQueue *mq, u8 address, u8 *buffer) |