mirror of
https://github.com/GearsProgress/Poke_Transporter_GB.git
synced 2026-03-21 17:34:42 -05:00
There was a crash happening with ptgb::vector when you'd press A on the CONFIRM button of the box screen. It only occurred on actual gba hardware and was a real heisenbug: as soon as you'd add code to display logs on screen, the problem would disappear. So it was very difficult to figure this one out. We're not even entirely sure why, but it looks like the malloc/realloc/free use in ptgb::vector would cause issues. Maybe it was alignment, but after messing with the code we also saw a warning appear in the terminal telling us that realloc wouldn't properly deal with non-POD types. It complained about this very thing while referring to the add_track() function, which stores ptgb::vectors inside another ptgb::vector. We also didn't have a custom copy constructor yet to actually copy the buffer instead of its pointer. All of these could potentially have led to the crash. But debugging during the link cable flow was difficult, so we were never able to confirm it in a debugger, log or dump. Because I suspected the high IWRAM consumption (especially now with ZX0 decompression) for a while, I also did an optimization in mystery_gift_builder to pass global_memory_buffer as its section_30_data buffer instead. This reduces IWRAM consumption by 4 KB. There was another problem I discovered during my crash hunt: the out_array (now payload_buffer) was allocated as a 672 byte array, but the payloads were actually 707 bytes. Therefore writing this to the buffer caused a buffer overflow, thereby corrupting the global variables appearing after it in IWRAM. It turned out eventually that none of these variables were really critical, but it could explain some minor bugs GearsProgress has seen. I also did a few performance optimizations: - At various stages in the code, for loops were used to copy data from one buffer into another byte-by-byte. This was far from optimal because the gba cpu can load/copy 4 bytes at a time if you ask it to. So I replaced those with memcpy(), which is a hand-optimized assembly function to copy data using this principle. - generate_payload was being called twice: once at start_link and once at continue_link, giving the exact same result, even though it was already being stored in a global buffer allocated in IWRAM. This was also a fairly heavy function. So I optimized the code to only initialize it once in the script chain and then just retrieve the buffer. - generate_payload was constructing the eventual payload twice even within the same call. That's because it first merged z80_rng_seed, z80_payload and z80_patchlist into a full_data ptgb::vector, after which it then copied the data again to out_array (now called payload_buffer). I eliminated the full_data vector now.
990 lines
42 KiB
C++
990 lines
42 KiB
C++
#include "payload_builder.h"
|
|
#include "gb_rom_values/base_gb_rom_struct.h"
|
|
#include "gb_rom_values/gb_rom_wrapper.h"
|
|
#include "debug_mode.h"
|
|
#include "z80_asm.h"
|
|
|
|
#define DATA_LOC (SHOW_DATA_PACKETS ? curr_rom.transferStringLocation : curr_rom.wEnemyMonSpecies)
|
|
|
|
static byte payload_buffer[PAYLOAD_SIZE];
|
|
|
|
void init_payload(GB_ROM curr_rom, int type, bool debug)
|
|
{
|
|
/* 10 RNG bytes
|
|
8 Preamble bytes
|
|
418 / 441 Party bytes
|
|
7 Preamble bytes
|
|
194 Patch list bytes (last 2 are unused)
|
|
|
|
637 / 660 total bytes
|
|
*/
|
|
if ((curr_rom.generation == 1 && curr_rom.version != YELLOW_ID))
|
|
{
|
|
ptgb::vector<z80_jump *> jump_vector;
|
|
ptgb::vector<z80_variable *> var_vector;
|
|
|
|
z80_asm_handler z80_rng_seed(0x0A, curr_rom.wSerialOtherGameboyRandomNumberListBlock + 8);
|
|
z80_asm_handler z80_payload(0x1AA, curr_rom.wSerialEnemyDataBlock);
|
|
z80_asm_handler z80_patchlist(0xC9, curr_rom.wSerialEnemyMonsPatchList);
|
|
|
|
z80_jump asm_start(&jump_vector);
|
|
z80_jump save_box(&jump_vector);
|
|
z80_jump remove_array_loop(&jump_vector);
|
|
z80_jump packet_loop(&jump_vector);
|
|
z80_jump fe_bypass(&jump_vector);
|
|
z80_jump send_packet_loop(&jump_vector);
|
|
|
|
z80_variable array_counter(&var_vector, 1, 0x00); // 1 byte to store the current array counter
|
|
z80_variable removal_array(&var_vector); // 40 byte storage for list of Pokemon to remove, plus a permanent array terminator
|
|
|
|
byte removal_array_data[41];
|
|
for (int i = 0; i < 41; i++)
|
|
{
|
|
if (debug)
|
|
{
|
|
removal_array_data[i] = (i < 30 ? (29 - i) : 0xFF);
|
|
}
|
|
else
|
|
{
|
|
removal_array_data[i] = (i < 30 ? 0xFD : 0xFF);
|
|
}
|
|
}
|
|
removal_array.load_data(41, removal_array_data);
|
|
z80_variable transfer_wait_string(&var_vector, 13, // SENDING DATA
|
|
0x92, 0x84, 0x8D, 0x83, 0x88, 0x8D, 0x86, 0x7F, 0x83, 0x80, 0x93, 0x80, 0x50);
|
|
z80_variable custom_name(&var_vector, 11, // FENNEL
|
|
0x85, 0x84, 0x8D, 0x8D, 0x84, 0x8B, 0x50, 0x50, 0x50, 0x50, 0x50);
|
|
|
|
// RNG Seed
|
|
// Location of the entrance vector
|
|
z80_rng_seed.index = 5; // Set the entrance vector
|
|
z80_rng_seed.JP(asm_start.place_direct_jump(&z80_rng_seed) | T_U16);
|
|
|
|
// Preamble
|
|
// At 0x00, 0x07 in length
|
|
// Must be filled with 0xFD
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
z80_payload.add_byte(0xFD);
|
|
}
|
|
|
|
// Rival name
|
|
// At 0x07, 0x0B in length
|
|
// Set to stored name
|
|
z80_payload.index = 0x07;
|
|
custom_name.insert_variable(&z80_payload);
|
|
|
|
// Number of Pokemon
|
|
// At 0x12, 0x01 in length
|
|
// Does not need to be set
|
|
z80_payload.index = 0x12;
|
|
z80_payload.add_byte(0x06);
|
|
|
|
// Pokemon list
|
|
// At 0x13, can be up to 0x1A2 / 0x1B9 bytes in length.
|
|
// Calculate the number of Pokemon names that need to be printed,
|
|
// and add them to the list. Then terminate the list.
|
|
z80_payload.index = 0x13;
|
|
int distance = curr_rom.stack_overwrite_location - curr_rom.print_string_start;
|
|
distance /= 20; // Automatically truncated, so it won't overshoot
|
|
|
|
for (int i = 0; i < distance; i++)
|
|
{
|
|
z80_payload.add_byte(curr_rom.short_pkmn_name);
|
|
}
|
|
z80_payload.add_byte(curr_rom.pointer_pkmn_name);
|
|
z80_payload.add_byte(0xFF);
|
|
|
|
// Patchlist preamble
|
|
// At 0x1B4 / 0x1D7, 0x07 in length
|
|
// Set as five 0xFD and two 0xFF
|
|
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
z80_patchlist.add_byte(i < 5 ? 0xFD : 0xFF);
|
|
}
|
|
|
|
// Patchlist
|
|
// At 0x1BB / 0x1DE, 0xC2 in length (0xC4, but the last 2 are unused)
|
|
// Fill with custom code
|
|
asm_start.set_start(&z80_patchlist);
|
|
|
|
/* Write transferring message to screen: */
|
|
// call ClearScreen
|
|
z80_patchlist.CALL(curr_rom.clearScreen | T_U16);
|
|
z80_patchlist.LD(HL, curr_rom.textBorderUppLeft | T_U16);
|
|
z80_patchlist.LD(C, curr_rom.textBorderWidth | T_U8);
|
|
z80_patchlist.LD(B, curr_rom.textBorderHeight | T_U8);
|
|
z80_patchlist.CALL(curr_rom.CableClub_TextBoxBorder | T_U16);
|
|
z80_patchlist.LD(HL, curr_rom.transferStringLocation | T_U16);
|
|
z80_patchlist.LD(DE, transfer_wait_string.place_ptr(&z80_patchlist) | T_U16);
|
|
z80_patchlist.CALL(curr_rom.placeString | T_U16);
|
|
|
|
/* Build the packet */
|
|
// HL is the current data pointer
|
|
// DE is the destination pointer
|
|
// A is the checksum
|
|
// B is the 0xFE flag byte
|
|
// C is the counter
|
|
send_packet_loop.set_start(&z80_patchlist);
|
|
|
|
z80_patchlist.LD(HL, (DATA_LOC + PACKET_SIZE + 3) | T_U16); // Load the next data location into HL
|
|
z80_patchlist.LD(A, HLI_PTR);
|
|
z80_patchlist.LD(H, HL_PTR);
|
|
z80_patchlist.LD(L, A);
|
|
z80_patchlist.LD(DE, (DATA_LOC + 2) | T_U16); // Enemy Pokemon data, should be unused
|
|
z80_patchlist.XOR(A, A); // Clear the register
|
|
z80_patchlist.LD(B, A); // Clear B as well
|
|
z80_patchlist.LD(C, A); // Clear C as well
|
|
z80_patchlist.PUSH(AF);
|
|
packet_loop.set_start(&z80_patchlist);
|
|
z80_patchlist.LD(B, 0x00 | T_U8); // Reset the flag byte
|
|
z80_patchlist.POP(AF);
|
|
z80_patchlist.ADD(A, HL_PTR); // Add the current data to the checksum
|
|
z80_patchlist.PUSH(AF);
|
|
z80_patchlist.LD(A, 0xFE);
|
|
z80_patchlist.CP(A, HL_PTR); // Compare the current data to 0xFE
|
|
z80_patchlist.LD(A, HLI_PTR); // Load HL's data into A for modification (if need be)
|
|
|
|
// If HL's data is 0xFE
|
|
z80_patchlist.JR(NZ_F, fe_bypass.place_relative_jump(&z80_patchlist) | T_I8);
|
|
z80_patchlist.DEC(A);
|
|
z80_patchlist.INC(B); // Set flag
|
|
|
|
fe_bypass.set_start(&z80_patchlist);
|
|
z80_patchlist.LD(DE_PTR, A); // Place the data in
|
|
z80_patchlist.INC(DE);
|
|
z80_patchlist.PUSH(AF);
|
|
z80_patchlist.LD(A, B);
|
|
z80_patchlist.LD(DE_PTR, A); // Place the flag in as well
|
|
z80_patchlist.POP(AF);
|
|
z80_patchlist.INC(DE);
|
|
z80_patchlist.INC(C);
|
|
z80_patchlist.LD(A, DATA_PER_PACKET - 1);
|
|
z80_patchlist.CP(A, C);
|
|
z80_patchlist.JR(NC_F, packet_loop.place_relative_jump(&z80_patchlist) | T_I8); // If all the data has been set, send the rest of the data
|
|
z80_patchlist.POP(AF);
|
|
z80_patchlist.RES(7 | T_BIT, A); // Reset bit 7 of the checksum, guaranteeing that it will never be 0xFE
|
|
z80_patchlist.LD(DE_PTR, A);
|
|
z80_patchlist.INC(DE);
|
|
z80_patchlist.LD(A, H);
|
|
z80_patchlist.LD(DE_PTR, A);
|
|
z80_patchlist.INC(DE);
|
|
z80_patchlist.LD(A, L);
|
|
z80_patchlist.LD(DE_PTR, A);
|
|
|
|
// z80_patchlist.LD(HL, curr_rom.garbageDataLocation | T_U16);
|
|
|
|
/* Transfer box data packet: */
|
|
z80_patchlist.LD(A, (debug ? 0x02 : 0x01) | T_U8); // Make sure GB is the slave, master if debug
|
|
z80_patchlist.LDH((curr_rom.hSerialConnectionStatus & 0xFF) | T_U8, A); // Since hSerialConnectionStatus is at 0xFFxx we can use this method instead
|
|
z80_patchlist.LD(HL, DATA_LOC | T_U16);
|
|
z80_patchlist.LD(HL_PTR, 0xFD | T_U8); // set the start of the data to 0xFD so Serial_ExchangeBytes is happy
|
|
z80_patchlist.INC(HL);
|
|
z80_patchlist.LD(HL_PTR, 0x00 | T_U8); // add a 0x00 after the 0xFD to prevent further 0xFDs from being interpreted as part of the preamble
|
|
z80_patchlist.DEC(HL); // Reset HL back so it points to 0xFD
|
|
z80_patchlist.LD(DE, (DATA_LOC + PACKET_SIZE) | T_U16); // location to put stored data
|
|
z80_patchlist.LD(BC, PACKET_SIZE | T_U16);
|
|
if (debug) // Don't call serialExchangeBytes if debug is enabled
|
|
{
|
|
z80_patchlist.index += 3;
|
|
}
|
|
else
|
|
{
|
|
z80_patchlist.CALL(curr_rom.Serial_ExchangeBytes | T_U16);
|
|
}
|
|
|
|
z80_patchlist.LD(A, (DATA_LOC + PACKET_SIZE + 3 + 1) | T_U16);
|
|
z80_patchlist.CP(A, 0xFF);
|
|
z80_patchlist.JR(NZ_F, send_packet_loop.place_relative_jump(&z80_patchlist) | T_I8);
|
|
|
|
/* Recieve the Pokemon to remove */
|
|
z80_patchlist.LD(HL, curr_rom.hSerialConnectionStatus | T_U16); // This can also be shortened
|
|
z80_patchlist.LD(HL_PTR, (debug ? 0x02 : 0x01) | T_U8); // Make sure GB is the slave, master if debug
|
|
z80_patchlist.LD(HL, curr_rom.garbageDataLocation | T_U16);
|
|
z80_patchlist.LD(DE, removal_array.place_ptr(&z80_patchlist) | T_U16);
|
|
z80_patchlist.LD(BC, 0x001E | T_U16); // Preamble does *not* count
|
|
if (debug) // Don't add in the Serial_ExchangeBytes call if in debug
|
|
{
|
|
z80_patchlist.index += 3;
|
|
}
|
|
else
|
|
{
|
|
z80_patchlist.CALL(curr_rom.Serial_ExchangeBytes | T_U16);
|
|
}
|
|
|
|
/* Remove the transfered Pokemon */
|
|
z80_patchlist.LD(HL, curr_rom.wRemoveMonFromBox | T_U16);
|
|
z80_patchlist.LD(HL_PTR, 0x01 | T_U8); // != 0x00 specifies the current box
|
|
remove_array_loop.set_start(&z80_patchlist);
|
|
z80_patchlist.LD(A, array_counter.place_ptr(&z80_patchlist) | T_U16);
|
|
z80_patchlist.LD(E, A);
|
|
z80_patchlist.LD(D, 0x00 | T_U8);
|
|
z80_patchlist.LD(HL, removal_array.place_ptr(&z80_patchlist) | T_U16);
|
|
z80_patchlist.ADD(HL, DE);
|
|
z80_patchlist.INC(A);
|
|
z80_patchlist.LD(array_counter.place_ptr(&z80_patchlist) | T_U16, A);
|
|
z80_patchlist.LD(A, curr_rom.wBoxCount | T_U16);
|
|
z80_patchlist.LD(B, A);
|
|
z80_patchlist.LD(A, HL_PTR);
|
|
z80_patchlist.CP(A, 0xFF | T_U8);
|
|
z80_patchlist.JR(Z_F, save_box.place_relative_jump(&z80_patchlist) | T_I8);
|
|
z80_patchlist.CP(A, B);
|
|
z80_patchlist.JR(NC_F, remove_array_loop.place_relative_jump(&z80_patchlist) | T_I8);
|
|
z80_patchlist.LD(HL, curr_rom.wWhichPokemon | T_U16);
|
|
z80_patchlist.LD(HL_PTR, A);
|
|
if (DONT_REMOVE_PKMN)
|
|
{
|
|
z80_patchlist.index += 3;
|
|
}
|
|
else
|
|
{
|
|
z80_patchlist.CALL(curr_rom._RemovePokemon | T_U16);
|
|
}
|
|
z80_patchlist.JR((remove_array_loop.place_relative_jump(&z80_patchlist) & 0xFF) | T_I8);
|
|
|
|
/* Save the current box */
|
|
save_box.set_start(&z80_patchlist);
|
|
z80_patchlist.LD(B, (curr_rom.SaveSAVtoSRAM1 >> 16) | T_U8); // Load ROM Bank
|
|
z80_patchlist.LD(HL, curr_rom.SaveSAVtoSRAM1 | T_U16);
|
|
z80_patchlist.CALL(curr_rom.Bankswitch | T_U16);
|
|
z80_patchlist.LD(B, (curr_rom.SaveSAVtoSRAM1 >> 16) | T_U8); // Load ROM Bank
|
|
z80_patchlist.LD(HL, curr_rom.SaveSAVtoSRAM2 | T_U16);
|
|
z80_patchlist.CALL(curr_rom.Bankswitch | T_U16);
|
|
z80_patchlist.JP(curr_rom.SoftReset | T_U16);
|
|
|
|
// z80_patchlist.index += 5;
|
|
|
|
array_counter.insert_variable(&z80_patchlist);
|
|
removal_array.insert_variable(&z80_payload);
|
|
transfer_wait_string.insert_variable(&z80_patchlist);
|
|
|
|
// This payload works by placing Pokemon ID 0xFC's name in the stack, and causing a return to CD8E,
|
|
// which is part of the RNG seed. From there we can jump anywhere- and we choose to jump to D887,
|
|
// which is the rival's name. This code fixes the stack and jumps to the patchlist, which is where
|
|
// our final code is.
|
|
|
|
// Update all the pointers
|
|
for (unsigned int i = 0; i < var_vector.size(); i++)
|
|
{
|
|
var_vector.at(i)->update_ptrs();
|
|
}
|
|
for (unsigned int i = 0; i < jump_vector.size(); i++)
|
|
{
|
|
jump_vector.at(i)->update_jumps();
|
|
}
|
|
|
|
// Combine the vectors into the full payload
|
|
u8 *cur_out = payload_buffer;
|
|
memcpy(cur_out, z80_rng_seed.data_vector.data(), z80_rng_seed.data_vector.size());
|
|
cur_out += z80_rng_seed.data_vector.size();
|
|
memcpy(cur_out, z80_payload.data_vector.data(), z80_payload.data_vector.size());
|
|
cur_out += z80_payload.data_vector.size();
|
|
memcpy(cur_out, z80_patchlist.data_vector.data(), z80_patchlist.data_vector.size());
|
|
cur_out += z80_patchlist.data_vector.size();
|
|
|
|
return;
|
|
}
|
|
|
|
else if ((curr_rom.generation == 1 && curr_rom.version == YELLOW_ID))
|
|
{
|
|
ptgb::vector<z80_jump *> jump_vector;
|
|
ptgb::vector<z80_variable *> var_vector;
|
|
|
|
z80_asm_handler z80_rng_seed(0x0A, curr_rom.wSerialOtherGameboyRandomNumberListBlock + 8);
|
|
z80_asm_handler z80_payload(0x1AA, curr_rom.wSerialEnemyDataBlock - 8); // Subtracting 8 is because the data is shifted after patching, removing part of the enemy name. May change depending on language
|
|
z80_asm_handler z80_patchlist(0xC9, curr_rom.wSerialEnemyMonsPatchList);
|
|
|
|
z80_jump asm_start(&jump_vector);
|
|
z80_jump save_box(&jump_vector);
|
|
z80_jump remove_array_loop(&jump_vector);
|
|
z80_jump packet_loop(&jump_vector);
|
|
z80_jump fe_bypass(&jump_vector);
|
|
z80_jump send_packet_loop(&jump_vector);
|
|
z80_jump skip_enemy_write(&jump_vector);
|
|
|
|
z80_jump test_loop(&jump_vector);
|
|
|
|
z80_variable array_counter(&var_vector, 1, 0x00); // 1 byte to store the current array counter
|
|
z80_variable removal_array(&var_vector); // 40 byte storage for list of Pokemon to remove, plus a permanent array terminator
|
|
|
|
byte removal_array_data[41];
|
|
for (int i = 0; i < 41; i++)
|
|
{
|
|
if (debug)
|
|
{
|
|
removal_array_data[i] = (i < 30 ? (29 - i) : 0xFF);
|
|
}
|
|
else
|
|
{
|
|
removal_array_data[i] = (i < 30 ? 0xFD : 0xFF);
|
|
}
|
|
}
|
|
removal_array.load_data(41, removal_array_data);
|
|
z80_variable transfer_wait_string(&var_vector, 13, // SENDING DATA
|
|
0x92, 0x84, 0x8D, 0x83, 0x88, 0x8D, 0x86, 0x7F, 0x83, 0x80, 0x93, 0x80, 0x50);
|
|
z80_variable custom_name(&var_vector, 11, // FENNEL
|
|
0x85, 0x84, 0x8D, 0x8D, 0x84, 0x8B, 0x50, 0x50, 0x50, 0x50, 0x50);
|
|
|
|
// RNG Seed:
|
|
// at 0x00, 0x0A in length
|
|
// Can be empty
|
|
|
|
// Preamble
|
|
// At 0x00, 0x07 in length
|
|
// Must be filled with 0xFD
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
z80_payload.add_byte(0xFD);
|
|
}
|
|
|
|
// Rival name
|
|
// At 0x07, 0x0B in length
|
|
// Set to stored name
|
|
z80_payload.index = 0x07;
|
|
custom_name.insert_variable(&z80_payload);
|
|
|
|
// Number of Pokemon
|
|
// At 0x12, 0x01 in length
|
|
// 2 or higher, 0xE8 for JPN
|
|
z80_payload.index = 0x12;
|
|
z80_payload.add_byte(0x02);
|
|
|
|
// Pokemon list
|
|
// At 0x13, is usually 6 bytes and 1 terminator
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
z80_payload.add_byte(i < 6 ? i + 1 : 0xFF);
|
|
}
|
|
|
|
// Pokemon data
|
|
// Specific for each language
|
|
|
|
// Mon #1 HP
|
|
z80_payload.add_bytes(2,
|
|
0x00, 0x00);
|
|
|
|
// Mon #2 data
|
|
z80_payload.index = 0x46;
|
|
z80_payload.add_bytes(9,
|
|
0x4E, 0x4E, 0x4E, 0x00, 0x01, 0x80, 0x80, 0xD4, 0xCF);
|
|
|
|
z80_payload.add_bytes(2, asm_start.place_pointer(&z80_payload), 0x00); // These values must not have any control characters in them.
|
|
|
|
z80_payload.add_bytes(33,
|
|
0x4E, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x4E, 0x50, 0x00, 0x00,
|
|
0x00, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x59);
|
|
|
|
// Mon #2 name
|
|
z80_payload.index = 0x16F;
|
|
for (int i = 0; i < 11; i++)
|
|
{
|
|
z80_payload.add_byte(0x4E);
|
|
}
|
|
|
|
// Now that the ACE has been set up, we can use the space between Mon #2's data and Mon #2's name to put our code.
|
|
// Since this space is larger than the patchlist, we will be able to fit everything in it
|
|
|
|
// Patchlist preamble
|
|
// At 0x1B4 0x07 in length
|
|
// Set as five 0xFD, the 0xFF will be added as part of the patch list.
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
z80_patchlist.add_byte(0xFD);
|
|
}
|
|
|
|
z80_payload.index = 0x72;
|
|
|
|
array_counter.insert_variable(&z80_payload);
|
|
removal_array.insert_variable(&z80_payload);
|
|
transfer_wait_string.insert_variable(&z80_payload);
|
|
|
|
asm_start.set_start(&z80_payload);
|
|
|
|
z80_payload.CALL(0x2233 | T_U16); // Stop the battle music
|
|
|
|
/* Write transferring message to screen: */
|
|
// call ClearScreen
|
|
z80_payload.CALL(curr_rom.clearScreen | T_U16);
|
|
z80_payload.LD(HL, curr_rom.textBorderUppLeft | T_U16);
|
|
z80_payload.LD(C, curr_rom.textBorderWidth | T_U8);
|
|
z80_payload.LD(B, curr_rom.textBorderHeight | T_U8);
|
|
z80_payload.CALL(curr_rom.CableClub_TextBoxBorder | T_U16);
|
|
z80_payload.LD(HL, curr_rom.transferStringLocation | T_U16);
|
|
z80_payload.LD(DE, transfer_wait_string.place_ptr(&z80_payload) | T_U16);
|
|
z80_payload.CALL(curr_rom.placeString | T_U16);
|
|
z80_payload.JR(skip_enemy_write.place_relative_jump(&z80_payload) | T_I8); // This is a simple way to avoid the enemy name placement. Might have to modify for other languages
|
|
|
|
z80_payload.index = 0xCA;
|
|
|
|
skip_enemy_write.set_start(&z80_payload);
|
|
|
|
// Reload the save data to remove any data that was overwritten during exploit entry
|
|
z80_payload.LD(A, (curr_rom.SaveSAVtoSRAM1 >> 16) | T_U8);
|
|
z80_payload.CALL(curr_rom.Bankswitch | T_U16);
|
|
z80_payload.CALL(0x79FC | T_U16); // Call LoadSAV1 TODO FIX THIS BAD BAD NO BAD
|
|
|
|
/* Build the packet */
|
|
// HL is the current data pointer
|
|
// DE is the destination pointer
|
|
// A is the checksum
|
|
// B is the 0xFE flag byte
|
|
// C is the counter
|
|
send_packet_loop.set_start(&z80_payload);
|
|
|
|
z80_payload.LD(HL, (DATA_LOC + PACKET_SIZE + 3) | T_U16); // Load the next data location into HL
|
|
z80_payload.LD(A, HLI_PTR);
|
|
z80_payload.LD(H, HL_PTR);
|
|
z80_payload.LD(L, A);
|
|
z80_payload.LD(DE, (DATA_LOC + 2) | T_U16); // Enemy Pokemon data, should be unused
|
|
z80_payload.XOR(A, A); // Clear the register
|
|
z80_payload.LD(B, A); // Clear B as well
|
|
z80_payload.LD(C, A); // Clear C as well
|
|
z80_payload.PUSH(AF);
|
|
packet_loop.set_start(&z80_payload);
|
|
z80_payload.LD(B, 0x00 | T_U8); // Reset the flag byte
|
|
z80_payload.POP(AF);
|
|
z80_payload.ADD(A, HL_PTR); // Add the current data to the checksum
|
|
z80_payload.PUSH(AF);
|
|
z80_payload.LD(A, 0xFE);
|
|
z80_payload.CP(A, HL_PTR); // Compare the current data to 0xFE
|
|
z80_payload.LD(A, HLI_PTR); // Load HL's data into A for modification (if need be)
|
|
|
|
// If HL's data is 0xFE
|
|
z80_payload.JR(NZ_F, fe_bypass.place_relative_jump(&z80_payload) | T_I8);
|
|
z80_payload.DEC(A);
|
|
z80_payload.INC(B); // Set flag
|
|
|
|
fe_bypass.set_start(&z80_payload);
|
|
z80_payload.LD(DE_PTR, A); // Place the data in
|
|
z80_payload.INC(DE);
|
|
z80_payload.PUSH(AF);
|
|
z80_payload.LD(A, B);
|
|
z80_payload.LD(DE_PTR, A); // Place the flag in as well
|
|
z80_payload.POP(AF);
|
|
z80_payload.INC(DE);
|
|
z80_payload.INC(C);
|
|
z80_payload.LD(A, DATA_PER_PACKET - 1);
|
|
z80_payload.CP(A, C);
|
|
z80_payload.JR(NC_F, packet_loop.place_relative_jump(&z80_payload) | T_I8); // If all the data has been set, send the rest of the data
|
|
z80_payload.POP(AF);
|
|
z80_payload.RES(7 | T_BIT, A); // Reset bit 7 of the checksum, guaranteeing that it will never be 0xFE
|
|
z80_payload.LD(DE_PTR, A);
|
|
z80_payload.INC(DE);
|
|
z80_payload.LD(A, H);
|
|
|
|
z80_payload.LD(DE_PTR, A);
|
|
z80_payload.INC(DE);
|
|
z80_payload.LD(A, L);
|
|
z80_payload.LD(DE_PTR, A);
|
|
|
|
/* Transfer box data packet: */
|
|
z80_payload.LD(A, (debug ? 0x02 : 0x01) | T_U8); // Make sure GB is the slave, master if debug
|
|
z80_payload.LDH((curr_rom.hSerialConnectionStatus & 0xFF) | T_U8, A); // Since hSerialConnectionStatus is at 0xFFxx we can use this method instead
|
|
z80_payload.LD(HL, DATA_LOC | T_U16);
|
|
z80_payload.LD(HL_PTR, 0xFD | T_U8); // set the start of the data to 0xFD so Serial_ExchangeBytes is happy
|
|
z80_payload.INC(HL);
|
|
z80_payload.LD(HL_PTR, 0x00 | T_U8); // add a 0x00 after the 0xFD to prevent further 0xFDs from being interpreted as part of the preamble
|
|
z80_payload.DEC(HL); // Reset HL back so it points to 0xFD
|
|
z80_payload.LD(DE, (DATA_LOC + PACKET_SIZE) | T_U16); // location to put stored data
|
|
z80_payload.LD(BC, PACKET_SIZE | T_U16);
|
|
if (debug) // Don't call serialExchangeBytes if debug is enabled
|
|
{
|
|
z80_payload.index += 3;
|
|
}
|
|
else
|
|
{
|
|
z80_payload.CALL(curr_rom.Serial_ExchangeBytes | T_U16);
|
|
}
|
|
|
|
z80_payload.LD(A, (DATA_LOC + PACKET_SIZE + 3 + 1) | T_U16);
|
|
z80_payload.CP(A, 0xFF);
|
|
if (debug && false) // Don't compare the "recieved" data if in debug
|
|
{
|
|
z80_payload.index += 2;
|
|
}
|
|
else
|
|
{
|
|
z80_payload.JR(NZ_F, send_packet_loop.place_relative_jump(&z80_payload) | T_I8);
|
|
}
|
|
|
|
/* Recieve the Pokemon to remove */
|
|
z80_payload.LD(HL, curr_rom.hSerialConnectionStatus | T_U16); // This can also be shortened
|
|
z80_payload.LD(HL_PTR, (debug ? 0x02 : 0x01) | T_U8); // Make sure GB is the slave, master if debug
|
|
z80_payload.LD(HL, curr_rom.garbageDataLocation | T_U16);
|
|
z80_payload.LD(DE, removal_array.place_ptr(&z80_payload) | T_U16);
|
|
z80_payload.LD(BC, 0x001E | T_U16); // Preamble does *not* count
|
|
if (debug) // Don't add in the Serial_ExchangeBytes call if in debug
|
|
{
|
|
z80_payload.index += 3;
|
|
}
|
|
else
|
|
{
|
|
z80_payload.CALL(curr_rom.Serial_ExchangeBytes | T_U16);
|
|
}
|
|
|
|
/* Remove the transfered Pokemon */
|
|
z80_payload.LD(HL, curr_rom.wRemoveMonFromBox | T_U16);
|
|
z80_payload.LD(HL_PTR, 0x01 | T_U8); // != 0x00 specifies the current box
|
|
remove_array_loop.set_start(&z80_payload);
|
|
z80_payload.LD(A, array_counter.place_ptr(&z80_payload) | T_U16);
|
|
z80_payload.LD(E, A);
|
|
z80_payload.LD(D, 0x00 | T_U8);
|
|
z80_payload.LD(HL, removal_array.place_ptr(&z80_payload) | T_U16);
|
|
z80_payload.ADD(HL, DE);
|
|
z80_payload.INC(A);
|
|
z80_payload.LD(array_counter.place_ptr(&z80_payload) | T_U16, A);
|
|
z80_payload.LD(A, curr_rom.wBoxCount | T_U16);
|
|
z80_payload.LD(B, A);
|
|
z80_payload.LD(A, HL_PTR);
|
|
z80_payload.CP(A, 0xFF | T_U8);
|
|
z80_payload.JR(Z_F, save_box.place_relative_jump(&z80_payload) | T_I8);
|
|
z80_payload.CP(A, B);
|
|
z80_payload.JR(NC_F, remove_array_loop.place_relative_jump(&z80_payload) | T_I8);
|
|
z80_payload.LD(HL, curr_rom.wWhichPokemon | T_U16);
|
|
z80_payload.LD(HL_PTR, A);
|
|
if (DONT_REMOVE_PKMN)
|
|
{
|
|
z80_payload.index += 3;
|
|
}
|
|
else
|
|
{
|
|
z80_payload.CALL(curr_rom._RemovePokemon | T_U16);
|
|
}
|
|
z80_payload.JR((remove_array_loop.place_relative_jump(&z80_payload) & 0xFF) | T_I8);
|
|
|
|
/* Save the current box */
|
|
save_box.set_start(&z80_payload);
|
|
z80_payload.LD(A, (curr_rom.SaveSAVtoSRAM1 >> 16) | T_U8); // Load ROM Bank
|
|
z80_payload.LD(HL, curr_rom.SaveSAVtoSRAM1 | T_U16);
|
|
z80_payload.CALL(curr_rom.Bankswitch | T_U16);
|
|
z80_payload.LD(A, (curr_rom.SaveSAVtoSRAM1 >> 16) | T_U8); // Load ROM Bank
|
|
z80_payload.LD(HL, curr_rom.SaveSAVtoSRAM2 | T_U16);
|
|
z80_payload.CALL(curr_rom.Bankswitch | T_U16);
|
|
z80_payload.JP(curr_rom.SoftReset | T_U16);
|
|
|
|
// z80_payload.index += 5;
|
|
|
|
// Patchlist
|
|
// At 0x1BB / 0x1DE, 0xC2 in length (0xC4, but the last 2 are unused)
|
|
z80_patchlist.generate_patchlist(&z80_payload);
|
|
|
|
// This payload works by placing Pokemon ID 0xFC's name in the stack, and causing a return to CD8E,
|
|
// which is part of the RNG seed. From there we can jump anywhere- and we choose to jump to D887,
|
|
// which is the rival's name. This code fixes the stack and jumps to the patchlist, which is where
|
|
// our final code is.
|
|
|
|
// Update all the pointers
|
|
for (unsigned int i = 0; i < var_vector.size(); i++)
|
|
{
|
|
var_vector.at(i)->update_ptrs();
|
|
}
|
|
for (unsigned int i = 0; i < jump_vector.size(); i++)
|
|
{
|
|
jump_vector.at(i)->update_jumps();
|
|
}
|
|
|
|
// Combine the vectors into the full payload
|
|
u8 *cur_out = payload_buffer;
|
|
memcpy(cur_out, z80_rng_seed.data_vector.data(), z80_rng_seed.data_vector.size());
|
|
cur_out += z80_rng_seed.data_vector.size();
|
|
memcpy(cur_out, z80_payload.data_vector.data(), z80_payload.data_vector.size());
|
|
cur_out += z80_payload.data_vector.size();
|
|
memcpy(cur_out, z80_patchlist.data_vector.data(), z80_patchlist.data_vector.size());
|
|
cur_out += z80_patchlist.data_vector.size();
|
|
|
|
return;
|
|
|
|
/*
|
|
else if (type == EVENT)
|
|
{
|
|
// ld a, $03
|
|
payload_storage[offset++] = 0x3A;
|
|
payload_storage[offset++] = 0x03;
|
|
|
|
// call OpenSRAM
|
|
payload_storage[offset++] = 0xC3;
|
|
payload_storage[offset++] = (curr_rom.OpenSRAM >> 0) & 0xFF;
|
|
payload_storage[offset++] = (curr_rom.OpenSRAM >> 8) & 0xFF;
|
|
|
|
// ld de, $BEB0
|
|
payload_storage[offset++] = 0x11;
|
|
payload_storage[offset++] = 0xB0;
|
|
payload_storage[offset++] = 0xBE;
|
|
|
|
// ld bc, $150
|
|
payload_storage[offset++] = 0x01;
|
|
payload_storage[offset++] = 0x50;
|
|
payload_storage[offset++] = 0x01;
|
|
|
|
// ld hl, ???
|
|
payload_storage[offset++] = 0x21;
|
|
payload_storage[offset++] = 0x00;
|
|
payload_storage[offset++] = 0x00;
|
|
|
|
// CUSTOM EVENT SCRIPT:
|
|
// Start with the table data
|
|
int init_offset = offset;
|
|
insert_ext_copy_cmd(&offset, 0x11410E, 0xD930, 18); // Copy text pointers
|
|
insert_ext_copy_cmd(&offset, 0x00C120, 0xC140, 10); //
|
|
insert_ext_copy_cmd(&offset, 0x00C220, 0xC240, 16); //
|
|
byte temp[] = {0xD0, 0x0A};
|
|
insert_int_copy_cmd(&offset, 0xD4E9, 2, temp); //
|
|
byte temp1[] = {0x15, 0x05};
|
|
insert_int_copy_cmd(&offset, 0xD517, 2, temp1); //
|
|
byte temp2[] = {0x30, 0xD9};
|
|
insert_int_copy_cmd(&offset, 0xD36B, 2, temp2); //
|
|
byte temp3[] = {0x0B, 0xF0, 0xFF};
|
|
insert_int_copy_cmd(&offset, 0xD5D3, 3, temp3); //
|
|
byte temp4[] = {0x05};
|
|
insert_int_copy_cmd(&offset, 0xD4E0, 1, temp4); //
|
|
byte temp5[] = {0x44, 0xD9, 0x08, 0x21, 0x54, 0xD9, 0xCD, 0x68, 0x31, 0x21, 0xB0, 0xC1, 0x00, 0x00, 0xC3, 0xD2, 0x23, 0x01, 0x00, 0x5E, 0xD8, 0xD8, 0xC9, 0xD8, 0xC9, 0xD8, 0xC9, 0xD8, 0xC9, 0x00, 0x8C, 0xB8, 0xB4, 0xB4, 0xE7, 0x50, 0x08, 0x3E, 0x83, 0xCD, 0x8B, 0x11, 0xCD, 0x3E, 0x37, 0xC3, 0xD2, 0x23};
|
|
insert_int_copy_cmd(&offset, 0xD942, 48, temp5); //
|
|
payload_storage[offset++] = 0xFF;
|
|
|
|
|
|
// call CloseSRAM
|
|
payload_storage[offset++] = 0xC3;
|
|
payload_storage[offset++] = (curr_rom.CloseSRAM >> 0) & 0xFF;
|
|
payload_storage[offset++] = (curr_rom.CloseSRAM >> 8) & 0xFF;
|
|
|
|
// call SoftReset
|
|
payload_storage[offset++] = 0xC3;
|
|
payload_storage[offset++] = (curr_rom.SoftReset >> 0) & 0xFF;
|
|
payload_storage[offset++] = (curr_rom.SoftReset >> 8) & 0xFF;
|
|
|
|
}*/
|
|
}
|
|
|
|
else if (curr_rom.generation == 2)
|
|
{
|
|
ptgb::vector<z80_jump *> jump_vector;
|
|
ptgb::vector<z80_variable *> var_vector;
|
|
|
|
z80_asm_handler z80_rng_seed(0x0A, curr_rom.wSerialOtherGameboyRandomNumberListBlock);
|
|
z80_asm_handler z80_payload(0x1CD, curr_rom.wSerialEnemyDataBlock); // wOTPartyData
|
|
z80_asm_handler z80_patchlist(0xC9, curr_rom.wSerialEnemyMonsPatchList); // wOTPatchLists
|
|
|
|
/*
|
|
Initally the entire wLinkData is copied into the data section at D26B.
|
|
then, CopyBytes is called four times within link.skip_mail:
|
|
1. It copies 0xB bytes from wLinkData to wOTPlayerName (D26B)
|
|
2. It copies 0x8 bytes from where the previous one ended (D280) to wOTPartyCount
|
|
3. It copies 0x2 bytes from where the previous one ended (D276) to wOTPlayerID
|
|
4. It copies 0x1A4 bytes from where the previous one ended (D288) to wOTPartyMons
|
|
|
|
8 bytes aren't overwritten when copied over (D278-D28F), which is where the weird offset comes from. These bytes are index 0xE through 0x15 in the original data
|
|
By having 9 bytes of 0xFD, the offset ends up working out correctly.
|
|
*/
|
|
|
|
z80_jump asm_start(&jump_vector);
|
|
z80_jump save_box(&jump_vector);
|
|
z80_jump remove_array_loop(&jump_vector);
|
|
z80_jump jump_to_party(&jump_vector);
|
|
z80_jump jump_to_payload(&jump_vector);
|
|
z80_jump packet_loop(&jump_vector);
|
|
z80_jump fe_bypass(&jump_vector);
|
|
z80_jump send_packet_loop(&jump_vector);
|
|
|
|
z80_variable array_counter(&var_vector, 1, 0x00); // 1 byte to store the current array counter
|
|
z80_variable removal_array(&var_vector); // 40 byte storage for list of Pokemon to remove, plus a permanent array terminator
|
|
byte removal_array_data[41];
|
|
for (int i = 0; i < 41; i++)
|
|
{
|
|
if (debug)
|
|
{
|
|
removal_array_data[i] = (i < 30 ? (29 - i) : 0xFF);
|
|
}
|
|
else
|
|
{
|
|
removal_array_data[i] = (i < 30 ? 0xFD : 0xFF);
|
|
}
|
|
}
|
|
removal_array.load_data(41, removal_array_data);
|
|
z80_variable transfer_wait_string(&var_vector, 13, // SENDING DATA
|
|
0x92, 0x84, 0x8D, 0x83, 0x88, 0x8D, 0x86, 0x7F, 0x83, 0x80, 0x93, 0x80, 0x50);
|
|
z80_variable custom_name(&var_vector, 11, // FENNEL
|
|
0x85, 0x84, 0x8D, 0x8D, 0x84, 0x8B, 0x50, 0x50, 0x50, 0x50, 0x50);
|
|
|
|
// RNG Seed:
|
|
// at 0x00, 0x0A in length
|
|
// Does not need to be set
|
|
|
|
// Preamble
|
|
// At 0x00, 0x09 in length
|
|
// Must be filled with 0xFD
|
|
for (int i = 0; i < 9; i++)
|
|
{
|
|
z80_payload.add_byte(0xFD);
|
|
}
|
|
|
|
// Preamble/Rival name
|
|
// At 0x00, 0x1C5 in length
|
|
// Set to stored name
|
|
int distance = curr_rom.stack_overwrite_location - curr_rom.print_string_start;
|
|
int remainder = distance % 40;
|
|
distance /= 40; // Automatically truncated, so it won't overshoot
|
|
distance -= 8; // There will be 8 extra bytes due to how the copy functions work.
|
|
|
|
for (int i = 0; i < distance; i++)
|
|
{
|
|
z80_payload.add_byte(curr_rom.short_pkmn_name);
|
|
}
|
|
for (int i = 0; i < remainder; i++)
|
|
{
|
|
z80_payload.add_byte(0x80);
|
|
}
|
|
|
|
z80_payload.index -= 1; // Prep for the direct jump, since it usually has to jump forward one for the ASM call
|
|
jump_to_payload.place_direct_jump(&z80_payload);
|
|
z80_payload.index += 3;
|
|
z80_payload.add_byte(0x50); // String terminator
|
|
|
|
// Saving the box overwrites our code, so we need to move it here.
|
|
jump_to_party.set_start(&z80_payload);
|
|
z80_payload.LD(A, curr_rom.SaveBox >> 16 | T_U8);
|
|
z80_payload.RST(0x10); // Bank switch
|
|
z80_payload.CALL(curr_rom.SaveBox | T_U16);
|
|
|
|
z80_payload.JP(curr_rom.SoftReset | T_U16);
|
|
|
|
// Patchlist preamble
|
|
// At 0x1B4 / 0x1D7, 0x07 in length
|
|
// Set as five 0xFD and two 0xFF
|
|
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
z80_patchlist.add_byte(i < 5 ? 0xFD : 0xFF);
|
|
}
|
|
|
|
// Patchlist
|
|
// At 0x1DE, 194 in length
|
|
// Fill with custom code
|
|
|
|
// Write transferring message to screen:
|
|
// call ClearScreen
|
|
jump_to_payload.set_start(&z80_patchlist);
|
|
z80_patchlist.CALL(curr_rom.clearScreen | T_U16);
|
|
|
|
z80_patchlist.LD(HL, curr_rom.textBorderUppLeft | T_U16);
|
|
z80_patchlist.LD(C, curr_rom.textBorderWidth | T_U8);
|
|
z80_patchlist.LD(B, curr_rom.textBorderHeight | T_U8);
|
|
z80_patchlist.LD(A, curr_rom.CableClub_TextBoxBorder >> 16 | T_U8);
|
|
z80_patchlist.RST(0x10); // Bank switch
|
|
z80_patchlist.CALL(curr_rom.CableClub_TextBoxBorder | T_U16);
|
|
z80_patchlist.LD(HL, curr_rom.transferStringLocation | T_U16);
|
|
z80_patchlist.LD(DE, transfer_wait_string.place_ptr(&z80_patchlist) | T_U16);
|
|
|
|
// call PlaceString
|
|
z80_patchlist.CALL(curr_rom.placeString | T_U16);
|
|
|
|
// call OpenSRAM
|
|
z80_patchlist.LD(A, 0x01 | T_U8);
|
|
z80_patchlist.CALL(curr_rom.OpenSRAM | T_U16);
|
|
|
|
/* Build the packet */
|
|
// HL is the current data pointer
|
|
// DE is the destination pointer
|
|
// A is the checksum
|
|
// B is the 0xFE flag byte
|
|
// C is the counter
|
|
send_packet_loop.set_start(&z80_patchlist);
|
|
|
|
z80_patchlist.LD(HL, (DATA_LOC + PACKET_SIZE + 3) | T_U16); // Load the next data location into HL
|
|
z80_patchlist.LD(A, HLI_PTR);
|
|
z80_patchlist.LD(H, HL_PTR);
|
|
z80_patchlist.LD(L, A);
|
|
|
|
z80_patchlist.LD(DE, (DATA_LOC + 2) | T_U16); // Enemy Pokemon data, should be unused
|
|
z80_patchlist.XOR(A, A); // Clear the register
|
|
z80_patchlist.LD(B, A); // Clear B as well
|
|
z80_patchlist.LD(C, A); // Clear C as well
|
|
z80_patchlist.PUSH(AF);
|
|
packet_loop.set_start(&z80_patchlist);
|
|
z80_patchlist.LD(B, 0x00 | T_U8); // Reset the flag byte
|
|
z80_patchlist.POP(AF);
|
|
z80_patchlist.ADD(A, HL_PTR); // Add the current data to the checksum
|
|
z80_patchlist.PUSH(AF);
|
|
z80_patchlist.LD(A, 0xFE);
|
|
z80_patchlist.CP(A, HL_PTR); // Compare the current data to 0xFE
|
|
z80_patchlist.LD(A, HLI_PTR); // Load HL's data into A for modification (if need be)
|
|
|
|
// If HL's data is 0xFE
|
|
z80_patchlist.JR(NZ_F, fe_bypass.place_relative_jump(&z80_patchlist) | T_I8);
|
|
z80_patchlist.DEC(A);
|
|
z80_patchlist.INC(B); // Set flag
|
|
|
|
fe_bypass.set_start(&z80_patchlist);
|
|
z80_patchlist.LD(DE_PTR, A); // Place the data in
|
|
z80_patchlist.INC(DE);
|
|
z80_patchlist.PUSH(AF);
|
|
z80_patchlist.LD(A, B);
|
|
z80_patchlist.LD(DE_PTR, A); // Place the flag in as well
|
|
z80_patchlist.POP(AF);
|
|
z80_patchlist.INC(DE);
|
|
z80_patchlist.INC(C);
|
|
z80_patchlist.LD(A, DATA_PER_PACKET - 1);
|
|
z80_patchlist.CP(A, C);
|
|
z80_patchlist.JR(NC_F, packet_loop.place_relative_jump(&z80_patchlist) | T_I8); // If all the data has been set, send the rest of the data
|
|
z80_patchlist.POP(AF);
|
|
z80_patchlist.RES(7 | T_BIT, A); // Reset bit 7 of the checksum, guaranteeing that it will never be 0xFE
|
|
z80_patchlist.LD(DE_PTR, A);
|
|
z80_patchlist.INC(DE);
|
|
z80_patchlist.LD(A, H);
|
|
z80_patchlist.LD(DE_PTR, A);
|
|
z80_patchlist.INC(DE);
|
|
z80_patchlist.LD(A, L);
|
|
z80_patchlist.LD(DE_PTR, A);
|
|
|
|
/* Transfer box data packet: */
|
|
z80_patchlist.LD(A, (debug ? 0x02 : 0x01) | T_U8); // Make sure GB is the slave, master if debug
|
|
z80_patchlist.LDH((curr_rom.hSerialConnectionStatus & 0xFF) | T_U8, A); // Since hSerialConnectionStatus is at 0xFFxx we can use this method instead
|
|
z80_patchlist.LD(HL, DATA_LOC | T_U16);
|
|
z80_patchlist.LD(HL_PTR, 0xFD | T_U8); // set the start of the data to 0xFD so Serial_ExchangeBytes is happy
|
|
z80_patchlist.INC(HL);
|
|
z80_patchlist.LD(HL_PTR, 0x00 | T_U8); // add a 0x00 after the 0xFD to prevent further 0xFDs from being interpreted as part of the preamble
|
|
z80_patchlist.DEC(HL); // Reset HL back so it points to 0xFD
|
|
z80_patchlist.LD(DE, (DATA_LOC + PACKET_SIZE) | T_U16); // location to put stored data
|
|
z80_patchlist.LD(BC, PACKET_SIZE | T_U16);
|
|
if (debug) // Don't call serialExchangeBytes if debug is enabled
|
|
{
|
|
z80_patchlist.index += 3;
|
|
}
|
|
else
|
|
{
|
|
z80_patchlist.CALL(curr_rom.Serial_ExchangeBytes | T_U16);
|
|
}
|
|
|
|
z80_patchlist.LD(A, (DATA_LOC + PACKET_SIZE + 3 + 1) | T_U16);
|
|
z80_patchlist.CP(A, 0xFF);
|
|
z80_patchlist.JR(NZ_F, send_packet_loop.place_relative_jump(&z80_patchlist) | T_I8);
|
|
|
|
// Recieve the Pokemon to remove
|
|
z80_patchlist.LD(HL, curr_rom.hSerialConnectionStatus | T_U16); // This can also be shortened
|
|
z80_patchlist.LD(HL_PTR, (debug ? 0x02 : 0x01) | T_U8); // Make sure GB is the slave, master if debug
|
|
z80_patchlist.LD(HL, curr_rom.garbageDataLocation | T_U16);
|
|
z80_patchlist.LD(DE, removal_array.place_ptr(&z80_patchlist) | T_U16);
|
|
z80_patchlist.LD(BC, 0x001E | T_U16); // Preamble does *not* count
|
|
if (debug) // Don't add in the Serial_ExchangeBytes call if in debug
|
|
{
|
|
z80_patchlist.index += 3;
|
|
}
|
|
else
|
|
{
|
|
z80_patchlist.CALL(curr_rom.Serial_ExchangeBytes | T_U16);
|
|
}
|
|
|
|
// Remove the transfered Pokemon
|
|
z80_patchlist.LD(HL, curr_rom.wRemoveMonFromBox | T_U16);
|
|
z80_patchlist.LD(HL_PTR, 0x01 | T_U8); // != 0x00 specifies the current box
|
|
remove_array_loop.set_start(&z80_patchlist);
|
|
z80_patchlist.LD(A, array_counter.place_ptr(&z80_patchlist) | T_U16);
|
|
z80_patchlist.LD(E, A);
|
|
z80_patchlist.LD(D, 0x00 | T_U8);
|
|
z80_patchlist.LD(HL, removal_array.place_ptr(&z80_patchlist) | T_U16);
|
|
z80_patchlist.ADD(HL, DE);
|
|
z80_patchlist.INC(A);
|
|
z80_patchlist.LD(array_counter.place_ptr(&z80_patchlist) | T_U16, A);
|
|
z80_patchlist.LD(A, curr_rom.wBoxCount | T_U16);
|
|
z80_patchlist.LD(B, A);
|
|
z80_patchlist.LD(A, HL_PTR);
|
|
z80_patchlist.CP(A, 0xFF | T_U8);
|
|
z80_patchlist.JR(Z_F, save_box.place_relative_jump(&z80_patchlist) | T_I8);
|
|
z80_patchlist.CP(A, B);
|
|
z80_patchlist.JR(NC_F, remove_array_loop.place_relative_jump(&z80_patchlist) | T_I8);
|
|
z80_patchlist.LD(HL, curr_rom.wWhichPokemon | T_U16);
|
|
z80_patchlist.LD(HL_PTR, A);
|
|
if (DONT_REMOVE_PKMN)
|
|
{
|
|
z80_patchlist.index += 6;
|
|
}
|
|
else
|
|
{
|
|
z80_patchlist.LD(A, curr_rom._RemovePokemon >> 16 | T_U8);
|
|
z80_patchlist.RST(0x10); // Bank switch
|
|
z80_patchlist.CALL(curr_rom._RemovePokemon | T_U16);
|
|
}
|
|
z80_patchlist.JR(remove_array_loop.place_relative_jump(&z80_patchlist) | T_I8);
|
|
|
|
save_box.set_start(&z80_patchlist);
|
|
z80_patchlist.JP(jump_to_party.place_direct_jump(&z80_patchlist) | T_U16); // jp pkmn list (because saving the box overwrites the data)
|
|
|
|
array_counter.insert_variable(&z80_patchlist);
|
|
removal_array.insert_variable(&z80_payload);
|
|
transfer_wait_string.insert_variable(&z80_patchlist);
|
|
|
|
// This payload works by placing Pokemon ID 0xFC's name in the stack, and causing a return to CD8E,
|
|
// which is part of the RNG seed. From there we can jump anywhere- and we choose to jump to D887,
|
|
// which is the rival's name. This code fixes the stack and jumps to the patchlist, which is where
|
|
// our final code is.
|
|
|
|
// Update all the pointers
|
|
for (unsigned int i = 0; i < var_vector.size(); i++)
|
|
{
|
|
var_vector.at(i)->update_ptrs();
|
|
}
|
|
for (unsigned int i = 0; i < jump_vector.size(); i++)
|
|
{
|
|
jump_vector.at(i)->update_jumps();
|
|
}
|
|
|
|
// Combine the vectors into the full payload
|
|
u8 *cur_out = payload_buffer;
|
|
memcpy(cur_out, z80_rng_seed.data_vector.data(), z80_rng_seed.data_vector.size());
|
|
cur_out += z80_rng_seed.data_vector.size();
|
|
memcpy(cur_out, z80_payload.data_vector.data(), z80_payload.data_vector.size());
|
|
cur_out += z80_payload.data_vector.size();
|
|
memcpy(cur_out, z80_patchlist.data_vector.data(), z80_patchlist.data_vector.size());
|
|
cur_out += z80_patchlist.data_vector.size();
|
|
|
|
return;
|
|
|
|
// This payload works by placing Pokemon ID 0xFC's name in the stack, and causing a return to CD8E,
|
|
// which is part of the RNG seed. From there we can jump anywhere- and we choose to jump to D887,
|
|
// which is the rival's name. This code fixes the stack and jumps to the patchlist, which is where
|
|
// our final code is.
|
|
}
|
|
memset(payload_buffer, 0x00, PAYLOAD_SIZE);
|
|
};
|
|
|
|
byte* get_payload()
|
|
{
|
|
return payload_buffer;
|
|
}
|
|
|
|
// Uncomment to send the payload to test_payload.txt
|
|
#if 0
|
|
#include <cstdio>
|
|
int main()
|
|
{
|
|
freopen("test_payload.txt", "w", stdout);
|
|
printf("\n");
|
|
init_payload(ENG_RED, TRANSFER, true);
|
|
byte *payload = get_payload();
|
|
if (true)
|
|
{
|
|
for (int i = 0; i < 0x2A0; i++)
|
|
{
|
|
printf("0x%02X, ", (unsigned int)payload[i]);
|
|
if (i % 0x10 == 0xF)
|
|
{
|
|
printf("\n# 0x%X\n", i + 1);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < 0x150; i++)
|
|
{
|
|
printf("%02X ", (unsigned int)payload[i + ((0x10 * 28) + 9)]);
|
|
if (i % 0x10 == 0xF)
|
|
{
|
|
printf("\n");
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|