Packet 0xB2

From Sylverant
Jump to navigationJump to search

Packet 0xB2 is used to send bits of code to be run on the client for various purposes, such as doing run-time patches of the game's code. This packet will also perform a CRC32 over a specified range of the memory of the console it is running on, regardless of whether or not code is run. Clients should respond with a Packet 0xB3

This packet is only fully supported by Version 2 clients on Dreamcast and all non "Plus" versions on the Gamecube. PSO for PC only supports the CRC calculation done by this packet, and not the code patching. PSO Episodes I & II Plus (both US and Japanese) will disconnect if they receive this packet -- even if it only indicates that there is a CRC to be done with no code.

Dreamcast Packet Format (Client->Server)

Bytes Meaning
0x00 Packet type (0xB2)
0x01 Flags
0x02-0x03 Packet size (Variable)
0x04-0x07 Offset to footer, or 0 if no code to be run (see below)
0x08-0x0B CRC32 range start
0x0C-0x0F CRC32 range length
0x10-0x13 Pointer to the start of the code, relative to the start of this value (usually 4)
0x14 ... Code (if applicable), followed by the footer below

The pointer to the start of the code is stored in the same byte order as the platform that will be receiving the code (little endian on Dreamcast, big endian on Gamecube).

Footer format

If there is any code specified in the packet (and the offset to footer is non-zero), then the following footer is tacked on (usually) immediately after the end of the code, accounting for proper alignment. Offsets below are relative to the start of the footer. All footer fields are stored in the same byte order as the platform at hand (so little endian on Dreamcast, big endian on Gamecube).

Offset Meaning
0x00-0x03 Pointer to the number of pointers field, relative to the start of the packet
0x04-0x07 Number of pointers to relocate
0x08-0x0F Unused? (set to 0)
0x10-0x13 Offset to find the pointer to the start of the code, relative to the pointer to the start of the code (this should be 0)
0x14 ... Pointers, relative to the pointer to the start of code, to relocate (see below)

Code Relocation

Code within the patch packets should generally be position-independent code for simplicity, however you can specify code that is to be relocated by the game before running. To do this, you must compile a list of pointers within the code that need to be relocated and append them at the end of the packet (making sure to increment the counter of pointers appropriately). The game will then automatically adjust the pointers specified for their position in RAM before running the code.

You must always have at least one pointer in this table, which should usually be zero. This will point to the start of code pointer in the header of the packet, which must be relocated in order to make the game run your code (otherwise, it will crash the game and potentially corrupt saved character data). The game uses the following algorithm to relocate the code (reversed a bit from the US release of PSOv2 on the Dreamcast):

/*
   Relocate code and find start point.
   entry = &(pkt->code_begin) = pkt + 0x10 bytes
   footer = pkt->entry_offset = end of code + 0x10
*/
uint32_t f_8c1058cc(uint32_t entry, uint32_t footer) {
    uint32_t *r6;
    uint32_t r7, r1, r0, r3;

    /* r6 = pktbase + end of code + 0x20 - 0x20 = right after code */
    r6 = (uint32_t *)(entry + footer - 0x20);
    r7 = *r6;                   /* r7 = pkt_footer->offset_count */
    r1 = entry;
    r3 = r6[1];                 /* r3 = pkt_footer->num_ptrs */
    r7 += entry                 /* r7 += entry = &pkt_footer->offset_entry */
    r5 = 0;

    while(r3 > r5) {
        r0 = *((uint16_t *)r7);
        r5 += 1;
        r0 <<= 2;               /* offset * 4 */
        r1 += r0;               /* entry + (offset * 4) */
        r0 = *((uint32_t *)r1); /* r0 = ((uint32 *)entry)[offset] */
        r7 += 2;                /* r7 = next offset */
        r0 += entry;            /* r0 = relocated value */
        *((uint32_t *)r1) = r0; /* save relocated pointer */
    }

    r0 = r6[4] + entry;         /* r0 = pkt_footer->offset_start + entry */
    return r0;
}