Working on transfer Pokemon commands

This commit is contained in:
GearsProgress
2026-06-29 21:02:59 -04:00
parent 84a29e38cc
commit 150a87c59c
4 changed files with 158 additions and 70 deletions

View File

@@ -285,7 +285,7 @@ struct LinkPacket
PayloadCommand command = CMD_NONE;
byte argument[2] = {0x00, 0x00};
u16 pointer = 0xC6DC;
byte packetID = 0;
byte packetID = 0x7F;
// Incoming data
LinkConnectionError latestError = NO_ERROR;
@@ -299,7 +299,7 @@ struct LinkPacket
LinkPacket() {};
LinkPacket(PayloadCommand cmd, byte arg1, byte arg2, u16 addr);
LinkPacket(byte *payload, int payloadSize);
void loadSecondaryPayload(byte *payload, int payloadSize);
};
class LinkConnection
@@ -332,9 +332,14 @@ public:
// This MUST be a power of 2!
#define LINK_PACKET_ARRAY_SIZE 4
// This is lower than the max 0x7F so we can save that value for dummy packets
#define LINK_PACKET_INDEX_MASK 0x3F
LinkPacket linkPacketArr[LINK_PACKET_ARRAY_SIZE];
LinkPacket *currOutgoingPacket;
LinkPacket *currIncomingPacket;
LinkPacket dummyPacket = LinkPacket();
byte linkPacketArrIndex = 0;
u16 linkPacketDataAddr = 0;
u16 linkPacketDataStart = 0;
@@ -358,10 +363,10 @@ public:
// These are all the Link Commands
bool LinkCommand_InitalizeConnection(bool waitForCompletion = true);
bool LinkCommand_ReloadCurrentBox(bool waitForCompletion = true);
bool LinkCommand_TransferPokemon(bool waitForCompletion = true);
bool LinkCommand_TransferPokemon(byte payload[], int payloadLength, bool waitForCompletion = true);
bool LinkCommand_SoftReset(bool waitForCompletion = true);
bool LinkCommand_ModifySRAMAccess(bool enableSRAM, byte SRAMbank, bool waitForCompletion = true);
bool LinkCommand_RunSecondaryPayload(bool waitForCompletion = true);
bool LinkCommand_RunSecondaryPayload(byte payload[], int payloadLength, bool waitForCompletion = true);
bool LinkCommand_ReadMemorySection(u16 dataPointer, byte outArray[], int outArraySize, bool waitForCompletion = true);
// Some operations are too long to be done within the IRQ.
@@ -374,7 +379,8 @@ private:
void loadCurrGameFromChecksum();
bool allPacketsProcessed();
bool processPacket();
void loadNextPacket();
void consumePacket();
bool prepareNextPacket();
void waitForEnd();
// Used for debug features

View File

@@ -78,9 +78,8 @@ LinkPacket::LinkPacket(PayloadCommand cmd, byte arg1, byte arg2, u16 addr)
inUse = true;
};
LinkPacket::LinkPacket(byte *payload, int payloadSize)
void LinkPacket::loadSecondaryPayload(byte *payload, int payloadSize)
{
command = CMD_SecondaryPayload;
secondaryPayloadData = payload;
secondaryPayloadDataSize = payloadSize;
for (int i = 0; i < payloadSize; i++)
@@ -278,7 +277,7 @@ void LinkConnection::printData()
}
// TODO: This is pretty rough, but it's the best we can do until the text engine rewrite.
if (g_debug_options.print_link_packets)
if (g_debug_options.print_link_packets && currIncomingPacket != nullptr)
{
tte_erase_rect(160, 16, H_MAX, V_MAX);
for (int i = 0; i < 4; i++)
@@ -448,7 +447,7 @@ void LinkConnection::handleStateLogic()
break;
case TRADE:
if (subStateCounter > curr_payload_size)
if (subStateCounter >= curr_payload_size)
{
if (this->gen == 2)
{
@@ -463,7 +462,7 @@ void LinkConnection::handleStateLogic()
break;
case MAIL:
if (subStateCounter > 0x186)
if (subStateCounter >= 0x186)
{
exitState = WAIT_FOR_CHECKSUM_PAYLOAD;
}
@@ -506,7 +505,7 @@ void LinkConnection::handleStateLogic()
break;
case SEND_SPECIFIC_PAYLOAD:
if (subStateCounter > 255) // The 255 comes from the Universal Payload
if (subStateCounter >= 255) // The 255 comes from the Universal Payload
{
exitState = SEND_FIRST_PACKET;
}
@@ -521,31 +520,15 @@ void LinkConnection::handleStateLogic()
break;
case SEND_FIRST_PACKET:
// In order to make sure everything is set to go, we need to "send and recieve" one packet
if (subStateCounter % TOTAL_PACKET_LENGTH == 0)
{
nextOutData = 0xFD;
}
else
{
nextOutData = 0x00;
}
if (subStateCounter >= TOTAL_PACKET_LENGTH * 2)
{
exitState = END;
}
// In order to make sure everything is set to go, we need to send a dummy packet.
currOutgoingPacket = &dummyPacket;
nextOutData = 0xFF;
exitState = STANDARD_PACKET_EXCHANGE;
break;
case PACKET_EXCHANGE:
nextOutData = 0xFF;
if (currOutgoingPacket->command == CMD_SecondaryPayload)
{
exitState = SECONDARY_PACKET_EXCHANGE;
}
else
{
exitState = STANDARD_PACKET_EXCHANGE;
}
exitState = STANDARD_PACKET_EXCHANGE;
break;
case STANDARD_PACKET_EXCHANGE:
@@ -580,7 +563,15 @@ void LinkConnection::handleStateLogic()
nextOutData = currOutgoingPacket->pointer >> 8;
break;
case TOTAL_PACKET_LENGTH:
exitState = PROCESS_PACKET;
if (currOutgoingPacket->secondaryPayloadData != nullptr)
{
// We get our response after the GB gets the secondary payload, send that first.
exitState = SECONDARY_PACKET_EXCHANGE;
}
else
{
exitState = PROCESS_PACKET;
}
default:
nextOutData = 0xFF;
break;
@@ -602,10 +593,10 @@ void LinkConnection::handleStateLogic()
break;
case 2:
// The command ID matches the saftey byte (0xFF)
nextOutData = currOutgoingPacket->command;
nextOutData = 0xFF;
break;
case 3:
nextOutData = currOutgoingPacket->secondaryPayloadDataSize;
nextOutData = currOutgoingPacket->secondaryPayloadDataSize + 1; // We need to include the checksum value here as well
break;
default:
// We're through all the constant values, let's do the rest.
@@ -617,31 +608,50 @@ void LinkConnection::handleStateLogic()
{
// Add the checksum and we're done with the packet.
nextOutData = currOutgoingPacket->secondaryPayloadSizeChecksum;
exitState = PROCESS_PACKET;
}
break;
else
{
// Now it's time to process the packet. Use a dummy packet to flush it out
currOutgoingPacket = &dummyPacket;
exitState = PROCESS_PACKET;
nextOutData = 0xFF;
}
}
}
dataOutBuffer[subStateCounter] = inData;
break;
}
break;
case PROCESS_PACKET:
currIncomingPacket = &linkPacketArr[dataOutBuffer[INP_COUNTER_INDEX] % LINK_PACKET_ARRAY_SIZE];
processPacket();
if (allPacketsProcessed())
{
// We have no valid responses that we are looking for, go to the end.
exitState = END;
}
else if (processPacket())
{
// Processing succeeded, let's load the next one.
exitState = LOAD_NEXT_PACKET;
}
else
{
// The processing failed. Let's move on and try again later
prepareNextPacket();
exitState = PACKET_EXCHANGE;
}
nextOutData = 0xFF;
exitState = LOAD_NEXT_PACKET;
break;
case LOAD_NEXT_PACKET:
loadNextPacket();
consumePacket();
if (allPacketsProcessed())
{
exitState = END;
}
else
{
linkPacketArrIndex = (linkPacketArrIndex + 1) & 0x7F;
currOutgoingPacket = &linkPacketArr[linkPacketArrIndex % LINK_PACKET_ARRAY_SIZE];
prepareNextPacket();
exitState = PACKET_EXCHANGE;
}
nextOutData = 0xFF;
@@ -768,7 +778,7 @@ bool LinkConnection::earlyExit()
{
if (key_hit(KEY_A))
{
return false; // Even if paused, run once
return false; // Even if paused, run once5
}
}
@@ -804,12 +814,7 @@ bool LinkConnection::processPacket()
(dataOutBuffer[INP_DATA_INDEX + i] << 1) | ((lsbByte >> (7 - i)) & 0b1);
}
// The soft reset command has no response, don't expect one.
if (currIncomingPacket->command == CMD_SoftReset)
{
// If this is a soft reset packet, that means that we don't care what we recieve from the final packet.
}
else if (checksum != dataOutBuffer[INP_CHECKSUM_INDEX])
if (checksum != dataOutBuffer[INP_CHECKSUM_INDEX])
{
// The checksum has to match in order for it to be valid, if we've made it this far down the line.
currIncomingPacket->latestError = CHECKSUM_MISMATCH;
@@ -838,7 +843,7 @@ bool LinkConnection::processPacket()
return true;
}
void LinkConnection::loadNextPacket()
void LinkConnection::consumePacket()
{
/*
----------------
@@ -850,14 +855,12 @@ void LinkConnection::loadNextPacket()
has all been read and we are finished.
----------------
*/
//currIncomingPacket = &linkPacketArr[dataOutBuffer[INP_COUNTER_INDEX] & (LINK_PACKET_ARRAY_SIZE - 1)];
if (currIncomingPacket->latestError == PACKET_SUCCESS)
{
// Packet was successful, Find which packet sent it and mark it.
// If this was data, then save the data and replace it with the next one in line
currIncomingPacket->latestError = PACKET_READ;
if (((currIncomingPacket->command == CMD_ReadDataRequest) && (currIncomingPacket->pointer - linkPacketDataStart) < linkPacketDataSize))
if (currIncomingPacket->command == CMD_ReadDataRequest)
{
for (int i = 0; i < 8; i++)
{
@@ -866,13 +869,21 @@ void LinkConnection::loadNextPacket()
outDataArrayPtr[(currIncomingPacket->pointer - linkPacketDataStart) + i] = currIncomingPacket->recievedData[i];
}
}
*currIncomingPacket = LinkPacket(CMD_ReadDataRequest, 0x00, 0x00, linkPacketDataAddr);
linkPacketDataAddr += 8;
// Do we have more data to read?
if ((linkPacketDataAddr - linkPacketDataStart) < linkPacketDataSize)
{
*currIncomingPacket = LinkPacket(CMD_ReadDataRequest, 0x00, 0x00, linkPacketDataAddr);
linkPacketDataAddr += 8;
}
else
{
// Mark the packet as out of use, we've gotten all the data we need.
currIncomingPacket->inUse = false;
}
}
else
{
// Mark the packet as out of use, we've gotten all the data we need.
currIncomingPacket->inUse = false;
// Command was not a data request, don't do anything.
}
}
else
@@ -881,6 +892,25 @@ void LinkConnection::loadNextPacket()
}
}
bool LinkConnection::prepareNextPacket()
{
for (int i = 0; i < LINK_PACKET_ARRAY_SIZE - 1; i++)
{
linkPacketArrIndex = (linkPacketArrIndex + 1) & LINK_PACKET_INDEX_MASK;
LinkPacket *candidate = &linkPacketArr[linkPacketArrIndex % LINK_PACKET_ARRAY_SIZE];
if (candidate->inUse && candidate->latestError != PACKET_READ)
{
currOutgoingPacket = candidate;
return true;
}
}
// We're back where we started. Send the dummy packet to get a response.
currOutgoingPacket = &dummyPacket;
return false;
}
bool LinkConnection::allPacketsProcessed()
{
for (int i = 0; i < LINK_PACKET_ARRAY_SIZE; i++)
@@ -903,9 +933,14 @@ void LinkConnection::resetLinkPackets()
// Loop the packet index around back to 0
while (linkPacketArrIndex % LINK_PACKET_ARRAY_SIZE != 0)
{
linkPacketArrIndex = (linkPacketArrIndex + 1) & 0x7F;
linkPacketArrIndex = (linkPacketArrIndex + 1) & LINK_PACKET_INDEX_MASK;
}
// Set the current outgoing packet to be the first in order.
currOutgoingPacket = &linkPacketArr[0];
// And set the incoming packet to nullptr since we don't know what that is yet.
currIncomingPacket = nullptr;
for (int i = 0; i < 16; i++)
{
dataOutBuffer[i] = 0;
@@ -947,12 +982,15 @@ bool LinkConnection::LinkCommand_ReloadCurrentBox(bool waitForCompletion)
return true;
};
bool LinkConnection::LinkCommand_TransferPokemon(bool waitForCompletion)
bool LinkConnection::LinkCommand_TransferPokemon(byte payload[], int payloadLength, bool waitForCompletion)
{
// Check that box number is correct
resetLinkPackets();
linkPacketArr[0] = LinkPacket(CMD_ReloadCurrentBox, 0x00, 0x00, 0xC6DC);
// TODO: THIS SHOULD NOT BE HARDCODED TO A BOX
linkPacketArr[0] = LinkPacket(CMD_RunSecondaryPayload, payloadLength + SECONDARY_PAYLOAD_HEADER_SIZE, 0x03, 0xC6DC);
linkPacketArr[0].loadSecondaryPayload(payload, payloadLength);
globalLinkCable.startConnection(PACKET_EXCHANGE);
if (waitForCompletion)
@@ -1001,15 +1039,15 @@ bool LinkConnection::LinkCommand_ModifySRAMAccess(bool enableSRAM, byte SRAMbank
return true;
};
bool LinkConnection::LinkCommand_RunSecondaryPayload(bool waitForCompletion)
bool LinkConnection::LinkCommand_RunSecondaryPayload(byte payload[], int payloadLength, bool waitForCompletion)
{
resetLinkPackets();
byte tempPayload[] = {0x21, 0xA0, 0xC3, 0x75, 0xC9};
byte size = 5;
linkPacketArr[0] = LinkPacket(CMD_RunSecondaryPayload, size, 0x00, 0xC6DC);
linkPacketArr[1] = LinkPacket(tempPayload, size);
linkPacketArr[0] = LinkPacket(CMD_RunSecondaryPayload, size + SECONDARY_PAYLOAD_HEADER_SIZE, 0x00, 0xC6DC);
linkPacketArr[0].loadSecondaryPayload(tempPayload, size);
globalLinkCable.startConnection(PACKET_EXCHANGE);
if (waitForCompletion)

View File

@@ -727,7 +727,8 @@ bool run_conditional(int index)
link_animation_state(STATE_CONNECTION);
if (g_debug_options.ignore_link_cable)
{
// TODO - Fix debug data being added here
// TODO - Make this less hardcoded
//party_data.box.loadData(1, ENGLISH, gen1_rb_debug_box_data);
}
else
{
@@ -795,10 +796,7 @@ bool run_conditional(int index)
globalLinkCable.skipPrint = false;
globalLinkCable.pauseOnPacket = true;
//globalLinkCable.LinkCommand_ReadMemorySection(0xDA80, party_data.box_data_array, 1122);
//globalLinkCable.LinkCommand_ModifySRAMAccess(true, 3);
globalLinkCable.LinkCommand_RunSecondaryPayload();
//globalLinkCable.LinkCommand_SoftReset();
globalLinkCable.LinkCommand_ReadMemorySection(0xDA80, party_data.box_data_array, 1122);
party_data.box.loadData(globalLinkCable.gen, globalLinkCable.lang, party_data.box_data_array);
}
@@ -884,6 +882,26 @@ bool run_conditional(int index)
return true;
case CMD_CONTINUE_LINK:
{
byte boxRemovalPayload[31];
int arrayIndex = 0;
for (int i = 29; i >= 0; i--)
{
if (party_data.box.getGen3Pokemon(i)->isValid)
{
party_data.box.removePokemon(i);
if (!(DONT_TRANSFER_POKEMON_AT_INDEX_X && i == POKEMON_INDEX_TO_SKIP))
{
boxRemovalPayload[arrayIndex] = i;
arrayIndex++;
}
}
}
boxRemovalPayload[arrayIndex] = 0xFF;
arrayIndex++;
globalLinkCable.LinkCommand_TransferPokemon(boxRemovalPayload, arrayIndex);
}
return true;
case CMD_BOX_MENU:
@@ -900,6 +918,7 @@ bool run_conditional(int index)
return party_data.box.getNumValid() > 0 || g_debug_options.dont_hide_invalid_pkmn;
case CMD_CANCEL_LINK:
globalLinkCable.LinkCommand_SoftReset();
return true;
case CMD_END_MISSINGNO:

View File

@@ -2,14 +2,20 @@
#include "sound.h"
#include "soundbank_bin.h"
//#define INCLUDE_SOUND
static void sound_irq_handler(void)
{
#ifdef INCLUDE_SOUND
mmVBlank();
mmFrame();
#endif
}
bool sound_init(void)
{
#ifdef INCLUDE_SOUND
irq_add(II_VBLANK, sound_irq_handler);
mm_addr soundbank = (mm_addr)soundbank_bin;
@@ -20,49 +26,68 @@ bool sound_init(void)
mmInitDefault(soundbank, 16);
return true;
#endif
}
void play_song(u32 song_index, bool loop)
{
#ifdef INCLUDE_SOUND
mmStart(song_index, loop ? MM_PLAY_LOOP : MM_PLAY_ONCE);
#endif
}
bool is_song_playing(void)
{
#ifdef INCLUDE_SOUND
return mmActive();
#endif
}
void stop_song(void)
{
#ifdef INCLUDE_SOUND
mmStop();
#endif
}
PTGBSFXHandle play_sound_effect(u32 sound_effect_index)
{
#ifdef INCLUDE_SOUND
return (PTGBSFXHandle)mmEffect(sound_effect_index);
#endif
}
void stop_sound_effect(PTGBSFXHandle handle)
{
#ifdef INCLUDE_SOUND
mmEffectCancel((mm_sfxhand)handle);
#endif
}
void stop_all_sound_effects(void)
{
#ifdef INCLUDE_SOUND
mmEffectCancelAll();
#endif
}
void release_sound_effect(PTGBSFXHandle handle)
{
#ifdef INCLUDE_SOUND
mmEffectRelease((mm_sfxhand)handle);
#endif
}
void play_jingle(u32 jingle_index)
{
#ifdef INCLUDE_SOUND
mmJingle(jingle_index);
#endif
}
bool is_jingle_playing(void)
{
#ifdef INCLUDE_SOUND
return mmActiveSub();
#endif
}