Poke_Transporter_GB/include/link_handler.h
2026-06-03 11:36:20 -04:00

271 lines
8.5 KiB
C++

#ifndef LINK_HANDLER_H_
#define LINK_HANDLER_H_
#include <tonc.h>
#include "libraries/gba-link-connection/LinkSPI.hpp"
#include "pokemon_party.h"
#include "GB_Payloads.h"
#define DATA_PER_PACKET 8
#define PACKET_DATA_START 2
#define PACKET_DATA_AT(i) (PACKET_DATA_START + (i * 2))
#define PACKET_FLAG_AT(i) (PACKET_DATA_START + (i * 2) + 1)
#define PACKET_CHECKSUM (PACKET_DATA_START + (2 * DATA_PER_PACKET))
#define PACKET_LOCATION_UPPER (PACKET_CHECKSUM + 1)
#define PACKET_LOCATION_LOWER (PACKET_CHECKSUM + 2)
// 0xFD, 0x00, data bytes per packet, flag bytes per packet, the checksum, and two location bytes
#define PACKET_SIZE (1 + 1 + (2 * DATA_PER_PACKET) + 1 + 2) // Originally 13
#define TIMEOUT 2
#define TIMEOUT_ONE_LENGTH 1000000 // Maybe keep a 10:1 ratio between ONE and TWO?
#define TIMEOUT_TWO_LENGTH 100000
#define SPI_TEXT_OUT_ARRAY_ELEMENT_SIZE 64
enum GameBoyROM
{
RED_JP_v0,
RED_JP_v1,
RED_EN,
RED_FR,
RED_IT,
RED_DE,
RED_SP,
GREEN_JP_v0,
GREEN_JP_v1,
BLUE_JP,
BLUE_EN,
BLUE_FR,
BLUE_IT,
BLUE_DE,
BLUE_SP,
YELLOW_JP_v0,
YELLOW_JP_v1,
YELLOW_JP_v2,
YELLOW_JP_v3,
YELLOW_EN,
YELLOW_FR,
YELLOW_IT,
YELLOW_DE,
YELLOW_SP,
GOLD_JP_v0,
GOLD_JP_v1,
GOLD_EN,
GOLD_FR,
GOLD_IT,
GOLD_DE,
GOLD_SP,
GOLD_KOR,
SILVER_JP_v0,
SILVER_JP_v1,
SILVER_EN,
SILVER_FR,
SILVER_IT,
SILVER_DE,
SILVER_SP,
SILVER_KOR,
CRYSTAL_JP,
CRYSTAL_EN,
CRYSTAL_FR,
CRYSTAL_IT,
CRYSTAL_DE,
CRYSTAL_SP,
NO_GB_ROM,
GB_ROM_ERROR,
};
// This table has the 3 checksums, followed by the enum value
const u16 GameBoyROMChecksumTable[][4]{
{0x32, 0xA2, 0xC1, RED_JP_v0},
{0x31, 0xB8, 0x66, RED_JP_v1},
{0x20, 0x91, 0xE6, RED_EN},
{0x18, 0x7A, 0xFC, RED_FR},
{0x18, 0x89, 0xD2, RED_IT},
{0x18, 0x5C, 0xDC, RED_DE},
{0x18, 0x38, 0x4A, RED_SP},
{0x9C, 0xDD, 0xD5, GREEN_JP_v0},
{0x9B, 0xF5, 0x47, GREEN_JP_v1},
{0xE5, 0xDC, 0x36, BLUE_JP},
{0xD3, 0x9D, 0x0A, BLUE_EN},
{0xCB, 0x56, 0xA4, BLUE_FR},
{0xCB, 0x5E, 0x9C, BLUE_IT},
{0xCB, 0x2E, 0xBC, BLUE_DE},
{0xCB, 0x14, 0xD7, BLUE_SP},
{0x20, 0x9C, 0x29, YELLOW_JP_v0},
{0x1F, 0x88, 0x58, YELLOW_JP_v1},
{0x1E, 0xED, 0xD9, YELLOW_JP_v2},
{0x1D, 0xD9, 0x84, YELLOW_JP_v3},
{0x97, 0x04, 0x7C, YELLOW_EN},
{0x61, 0x66, 0xFB, YELLOW_FR},
{0x5C, 0x4E, 0x8F, YELLOW_IT},
{0x5F, 0xB7, 0xC1, YELLOW_DE},
{0x52, 0x56, 0x37, YELLOW_SP},
{0x48, 0x8A, 0x70, GOLD_JP_v0},
{0x47, 0x84, 0x60, GOLD_JP_v1},
{0x4B, 0x68, 0x2D, GOLD_EN},
{0x4A, 0x6F, 0xC6, GOLD_FR},
{0x47, 0xCE, 0x0C, GOLD_IT},
{0x4C, 0xDC, 0x97, GOLD_DE},
{0x3D, 0x93, 0x53, GOLD_SP},
{0x08, 0x77, 0x8A, GOLD_KOR},
{0x27, 0x76, 0x91, SILVER_JP_v0},
{0x26, 0x1D, 0x34, SILVER_JP_v1},
{0x2A, 0x0D, 0xAE, SILVER_EN},
{0x29, 0xFB, 0x8C, SILVER_FR},
{0x26, 0x73, 0x50, SILVER_IT},
{0x2B, 0xCD, 0x6E, SILVER_DE},
{0x1C, 0x06, 0x4B, SILVER_SP},
{0xE7, 0x98, 0x5B, SILVER_KOR},
{0x22, 0x9A, 0x40, CRYSTAL_JP},
{0x26, 0x18, 0xD2, CRYSTAL_EN},
{0x26, 0xF2, 0xE2, CRYSTAL_FR},
{0x23, 0xDB, 0xBA, CRYSTAL_IT},
{0x28, 0x49, 0x82, CRYSTAL_DE},
{0x19, 0x42, 0xF4, CRYSTAL_SP},
};
enum CompositeState
{
NO_COMPOSITE_STATE,
INITIAL_CONNECTION,
PACKET_EXCHANGE,
};
enum SubstateState
{
NO_SUBSTATE,
// INITIAL_CONNECTION
CLOCK,
SAVE_SUCCESS,
MENU_OPEN,
MENU_SUCCESS,
WAIT_FOR_TRADE,
TRADE_PREAMBLE,
TRADE,
MAIL,
WAIT_FOR_CHECKSUM_PAYLOAD,
GET_CHECKSUM,
WAIT_FOR_SECOND_PAYLOAD,
SEND_SPECIFIC_PAYLOAD,
SOFT_RESET,
// PACKET_EXCHANGE
BYTE_EXCHANGE,
END,
};
enum LinkConnectionError
{
NO_ERROR,
PACKET_TIMED_OUT,
};
enum PayloadCommand
{
CMD_ReloadCurrentBox, // no arguments used. Reloads the current box from SRAM. Use this as the first command byte before performing other commands.
CMD_TransferPokemon, // 1st argument = secondary payload size, 2nd argument = box that should be transferred from. This command uses a secondary payload. The size of this secondary payload needs to be declared beforehand. Prior to requesting a secondary payload, the program will use the second argument to load a specific box from SRAM. Box numbers are 0-indexed and range from 0x00 (box 1) to 0x0B(box 12). This load procedure currently cannot be skipped. Once the secondary payload arrives, the program will verify its integrity and align the payload. Afterwards, it will use the information within this payload to remove pokémon from the current box. Once all transferred pokémon have been removed, the program will save the current box.
CMD_SoftReset, // no arguments used. Instantly soft resets the game.
CMD_OpenSRAM, // 1st argument = SRAM bank to be opened. Opens access to SRAM and switches to the SRAM bank determined by the 1st argument. SRAM will remain open afterwards.
CMD_CloseSRAM, // no arguments used. Instantly closes access to SRAM.
CMD_RunSecondaryPayload, // 1st argument = secondary payload size. This command uses a secondary payload. The size of this secondary payload needs to be declared beforehand. Once the secondary payload arrives, the program will verify its integrity and align the payload. Afterwards, it will jump to the secondary payload and execute it. IMPORTANT: if you want to report an error when executing this secondary payload, return with carry flag set. IMPORTANT: if you want to report that the payload was executed safely, return with carry flag reset.*/
CMD_ReadDataRequest, // a request to read data starting from the address defined by the 16-bit pointer.
};
#define OUT_PACKET_LENGTH 16
#define IN_PACKET_LENGTH 16
struct LinkPacket
{
// Out packet parameters
PayloadCommand command;
byte argument[2] = {0x00, 0x00};
u16 pointer;
// Incoming data
byte recievedData[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
LinkConnectionError latestError = NO_ERROR;
};
class LinkConnection
{
public:
LinkConnection *globalPtr;
CompositeState compState = NO_COMPOSITE_STATE;
CompositeState nextCompState = NO_COMPOSITE_STATE;
bool compStateChanged = false;
SubstateState subState = NO_SUBSTATE;
SubstateState nextSubState = NO_SUBSTATE;
bool subStateChanged = false;
LinkConnectionError lastError = NO_ERROR;
uint8_t inData;
uint8_t outData;
int compStateCounter = 0; // the counter for the total number of bytes sent compstate
int subStateCounter = 0; // The counter for the total number of bytes sent in this substate
int gen = 0; // The generation we are trading with
GameBoyROM currROM = NO_GB_ROM; // The GameBoy ROM we're communicating with
int FF_count = 0; // The number of 0xFF bytes that have been in a row
int zero_count = 0; // The number of 0x00 bytes that have been in a row
int mosi_delay = 4; // inital delay, speeds up once sending
int received_offset = 0; // The offset contained in the last packet
int next_offset = 0; // The offset we are sending in the next packet
int packet_index = 0; // The index of the current packet
bool failed_packet = false; // Flags if a packet failed
bool init_packet = true; // Flags if a packet is the inital one
bool end_of_data = false; // Flags if we are at the end of the data
bool test_packet_fail = false; // ???
byte data_packet[PACKET_SIZE];
byte payloadBuffer[0x2A0];
int curr_payload_size = 0;
byte dataOutBuffer[16];
int dataOutBufferCurrIndex = 0;
LinkPacket *currLinkPacketArr;
int currLinkPacketArrNum = 0;
int currLinkPacketArrIndex = 0;
bool paused = false; // Used for pausing and sending one byte at a time
void setup(const u16 *debug_charset);
void startConnection(CompositeState startState);
bool earlyExit();
void exchangeBytes();
void printData();
void writeData();
void handleStateLogic();
private:
void load_payload(GB_PayloadsFiles payload);
void LoadCurrGameFromChecksum();
void logicState_initConnection();
void logicState_packetExchange();
// Used for debug features
#define LINE_WIDTH 24
#define NUM_LINES 8
char stuff[NUM_LINES][LINE_WIDTH];
char line[LINE_WIDTH] = "OUT";
int link_cable_array_index = 0;
int link_cable_memory_section_index = 0;
const u16 *debug_charset;
};
extern LinkConnection globalLinkCable;
void linkCableIRQ();
#endif /* LINK_HANDLER_H_ */