Mario Kart 64 Hacking General Discussion « 1 ... 10 11 12 13 14 ... 17 »
Users browsing this thread: 2 Guest(s)

I have taken shygoo's and abney317's EEPROM notes and filled in the remaining holes.  The MK64 EEPROM Map wiki page contains the complete list, but here are my contributions:

| 0x186 | 0x2 | Checksum of 0x180-0x184
| 0x188 | 0xC | Copies of Mushroom Cup 1st Record Times
| 0x194 | 0xC | Copies of Flower Cup 1st Record Times
| 0x1A0 | 0xC | Copies of Mushroom Cup Lap Record Times
| 0x1AC | 0xC | Copies of Flower Cup Lap Record Times
| 0x1B8 | 0x6 | Unknown (Padding?)
| 0x1BE | 0x2 | Checksum of 0x188-0x1BD
| 0x1C0 | 0xC | Copies of Star Cup 1st Record Times
| 0x1CC | 0xC | Copies of Special Cup 1st Record Times
| 0x1D8 | 0xC | Copies of Star Cup Lap Record Times
| 0x1E4 | 0xC | Copies of Special Cup Lap Record Times
| 0x1F0 | 0x6 | Unknown (Padding?)
| 0x1F6 | 0x2 | Checksum of 0x1C0-0x1F5
| 0x1FC | 0x1 | Copy of audio setting (0x184)
| 0x1FD | 0x1 | Copy of Unused, padding? (0x185)
| 0x1FE | 0x2 | Copy of Checksum of 0x180-0x184


Copies of record times
The copies of record times are broken into two sets: Mushroom/Flower 1st/Lap and Star/Special 1st/Lap. You can see this mapping from the following highlighted hex display:
[Image: Jtv7x9l.png]

Checksums

Record Checksum
Each record contains a 1-byte checksum at offset 0x17 and is computed as follows:
Code:
​unsigned char RecordChecksum(unsigned char data[], int offset)
{
  int checksum = 0;
  for (int rec = 0; rec < 7; rec++) {
     for (int i = 0; i < 3; i++) {
        unsigned char val = data[offset + rec * 3 + i];
        checksum += (val * (i + 1)) + rec;
     }
  }
  return (unsigned char)(checksum & 0xFF);
}

Checksum at 0x186-0x187
0x186 is a checksum of offsets 0x180-0x184 and is computed by procedure at 800B492C and 0x187 is computed by procedure at 800B49E4

Code:
​eeprom[0x186] = (eeprom[0x180] + 1 + ((eeprom[0x181] + 1) * 2) + 1 + ((eeprom[0x182] + 1) * 3) + 2 + ((eeprom[0x183] + 1) * 4) + 3 + ((eeprom[0x184] + 1) * 5) + 4) & 0xFF
eeprom[0x187] = (eeprom[0x186] + 0x5A) & 0xFF

Checksums at 0x1BE and 0x1F6

Code:
​// eeprom: base of EEPROM data
// offset: offset in eeprom to checksum (either 0x188 or 0x1C0)
void Checksum1BEand1F6(unsigned char eeprom[], int offset)
{
  int checksum = 0;
  int blockOffset = offset;
  for (int block = 0; block < 3; block++) {
     int loopOffset = blockOffset + 1;
     checksum += (eeprom[blockOffset] + 1) * (block + 1);
     for (int byteOff = 1; byteOff < 0x11; byteOff += 0x4) {
        int mult2 = (eeprom[loopOffset] + 1) * (block + 1);
        checksum += mult2 + byteOff;
        int mult3 = (eeprom[loopOffset + 1] + 1) * (block + 1);
        checksum += mult3 + byteOff + 1;
        int mult4 = (eeprom[loopOffset + 2] + 1) * (block + 1);
        checksum += mult4 + byteOff + 2;
        int mult5 = (eeprom[loopOffset + 3] + 1) * (block + 1);
        checksum += mult5 + byteOff + 3;
        loopOffset += 4;
     }
     blockOffset += 0x11;
  }
  eeprom[offset + 0x36] = (unsigned char)(checksum & 0xFF);
  eeprom[offset + 0x37] = (unsigned char)((data[offset + 0x36] + 0x5A) & 0xFF);
}

MK64 EEPROM Editor
I created a small program to modify MK64 EEPROM save files. I've posted the details here:
​Mario Kart 64 EEPROM Save Editor
[Image: hxDp9rn.png]
(This post was last modified: 23-06-2017, 08:00 PM by queueRAM. Edit Reason: wiki links )

Fantastic!

Does anyone know what setting game audio to "headphones" actually does?

Holy cow, that's awesome! Nicely done!

(11-07-2016, 08:01 PM)Drew Weatherton Wrote: Does anyone know what setting game audio to "headphones"  actually does?


The only thing I was able to find about it was here: http://n64devkit.square7.ch/qa/audio/audio.htm#20
Quote:
Q20 Nintendo titles have a headphone mode. How do you do that? How is listening different?

A20 Sorry, that's a trade secret.

Nintendo has a proprietary driver that makes sounds very clean-sounding with headphones.


Many first-party titles have a "Headphone" or "Headset" options: Super Mario 64, Mario Kart 64, 1080.
I also asked over on #n64dev and the response I got was that it was just EQ to normalize the audio output in a set range.

OK. I was wondering it it maybe tried to simulate surround sound or something. I may record with audacity and see if I can see the difference.
(This post was last modified: 13-07-2016, 12:46 AM by Drew Weatherton.)

I have some information about the format and storage of ghost data and staff ghosts. Both the ghost data that can be saved on the controller memory pack and the staff ghost data takes the same format as described below, with exception that the memory pack one is MIO0 compressed. The storage format is 4-bytes per entry, defined by:

[BB] [FF] [YY] [XX]

FieldDescription
BBButton state:
0x80 = A
0x40 = B
0x20 = Z
0x10 = R
Bits 0-3 are unknown
FF# Frames controller in this state - 1
YYAnalog stick Y (signed byte)
XXAnalog stick X (signed byte)

There doesn't seem to be any termination sequence in the memory pack saved data, but the staff ghost data always ends with 00 00 00 00

​SeanSullivan86's MK64 ghost data project was a great help in decoding these values.

The staff ghost data for Luigi, Mario, and Royal Raceways are stored in ROM at the locations in the table below. These data DMA'd to RAM at 0x802D2B80, although it always copies 0x4000 bytes even though they are each less than 0x2000 bytes long.

TrackROM OffsetSeg. Addr.LengthTime
Luigi Raceway0x63F11C0x0F4F9CAC0x10580x2BC1 (1'52"00)
Mario Raceway0x63E2800x0F4F8E100x0E9C0x2329 (1'30"00)
Royal Raceway0x6401740x0F4FAD040x1DCC0x3E81 (2'40"00)

As an example, I took a silly run on Luigi's Raceway with Luigi and saved it to memory pack, MIO0 decompressed it and copied it over the entries at 0x63F11C. Luigi course ghost is pretty good at spin hopping:
[Image: iW7t3Ei.gif]

This means that we could probably collect a few memory pack ghost saves from the Luigi, Mario, and Royal Raceways in krom's 1500cc and replace the staff ghost data with them. Although, we still need to figure out how to scale back the boosts before the jump on Royal Raceway first.

​Edit: The offsets for these tables and the minimum times are hard-coded in the assembly. The function at 80005040 looks at the current course and if 0, 7, or 8 (Mario, Royal, Luigi) it compares the course record time and if less than max, assigns values to RAM. The function at 80004EF0 performs the actual DMA copy of the staff course ghost data then.

Wiki article: https://wiki.origami64.net/mario_kart_64/ghost_data
(This post was last modified: 23-06-2017, 07:32 PM by queueRAM. Edit Reason: wiki links )

Thanks for documenting this here. It could definitely be interesting to add more challenging course ghosts to the game Cool One other interesting thing about the course ghosts is that if you force the lap count so that the race is more than three laps, the ghost keeps racing a bit after the race finishes. It does really weird spin hopping stuff though much like what you showed in that gif. I had wondered whether that was garbage data or something else, it seemed as though A and B were held at all times while the control stick moved around. On Royal Raceway, Peach eventually drives into the water and an invisible Lakitu comes to get her.

As for the ramp on RRy, the boost pads on RRy and DK both have unique speed values applied to them. It seems we could just modify those values.

(13-07-2016, 01:46 PM)Drew Weatherton Wrote: One other interesting thing about the course ghosts is that if you force the lap count so that the race is more than three laps, the ghost keeps racing a bit after the race finishes. It does really weird spin hopping stuff though much like what you showed in that gif. I had wondered whether that was garbage data or something else, it seemed as though A and B were held at all times while the control stick moved around. On Royal Raceway, Peach eventually drives into the water and an invisible Lakitu comes to get her.


Just a hypothesis: this could be because 0x4000 bytes of data are always copied from ROM for the course ghosts, even though there clearly isn't that much data used. Since all the ghost data are stored back-to-back in the ROM, this means that Mario Raceway's ghost also copies both Luigi's and Royal's ghost data into RAM and the code probably just keeps going through it. Eventually, if the ghost kart doesn't complete all the laps, it'll spill out into whatever data follows the ghosts.

By the way, I also figured out where in the code the ghost data offsets are stored and updated my post above with this information. If one wanted to change the courses that have built-in staff ghosts or the record times when they appear, this can now be done.

(13-07-2016, 01:46 PM)Drew Weatherton Wrote:
As for the ramp on RRy, the boost pads on RRy and DK both have unique speed values applied to them. It seems we could just modify those values.


That is very useful information. I haven't dug into the boost pads yet - do you know where this information is stored for each course or how I can start figuring out the unique speed values?

queueRAM, here is the information about the ramps:

Minimum Speed On DK Jungle Boost Ramp (Default 4396) 8108F076 ????
Minimum Speed On Royal Raceway Boost Ramp (Default 43C8) 8108EDFA ????

Note: these codes are from Rena Kunisaki's (aka HyperHacker) old site.

(25-07-2016, 03:53 AM)Drew Weatherton Wrote: information about the ramps:

Minimum Speed On DK Jungle Boost Ramp (Default 4396) 8108F076 ????
Minimum Speed On Royal Raceway Boost Ramp (Default 43C8) 8108EDFA ????


Thanks Drew. I dug into this code a little bit and it is a bit more complex than I was expecting. The two values you indicated are actually floating point numbers used in the ASM code (0x43960000 = 300.0, 0x43c80000 = 400.0). The code around their usage looks something like:

Code:
​// DK's: 8008F008-8008F080
if ((a3->u16_F8 != 0xFC) && ((a3->u32_BC & 0x8) != 0x8)) {
  proc_800225CC(&a3->80, 0, 1.0); // 0x00000000, 0x3f800000
} else {
  proc_800225CC(&a3->80, 300.0, 0.1); // 0x43960000, 0x3dcccccd
}

// Royal Raceway: 8008EDC0-8008EE3C
if ((a3->u16_F8 != 0xFE) && ((a3->u32_BC & 0x8) != 0x8)) {
  proc_800225CC(&a3->80, 0, 1.0); // 0x00000000, 0x3f800000
} else {
  proc_800225CC(&a3->80, 400.0, 0.01); // 0x43c80000, 0x3c23d70a
}

Where the proc_800225CC() is some filtering function that performs the following operation. It looks like some filtering operation and it has the characteristic that if the parameter I've named "alpha" (A2) = 1, then speed = new_speed
Code:
​void proc_800225CC(float *speed, float new_speed, float alpha)
{
  *speed = *speed - ((*speed - new_speed) * alpha);
 
  // clamping thresholds stored as doubles at 0x800ED680 & 0x800ED688
  // 800ED680: 3F50624DD2F1A9FC =  0.001
  // 800ED688: BF50624DD2F1A9FC = -0.001
  if (*speed < 0.001 && *speed > -0.001) {
     *speed = 0;
  }
}

I tried adjusting those 300.0 and 400.0 values, but decreasing them lower than 150.0 or so just causes the kart to get stuck on the boost ramps. Increasing them does seem to make the kart travel farther. I don't have a solution yet, but this helps immensely clue me in to the right code.
(This post was last modified: 25-07-2016, 06:00 PM by queueRAM.)

Mario Kart 64 Hacking General Discussion « 1 ... 10 11 12 13 14 ... 17 »
Users browsing this thread: 2 Guest(s)