diff --git a/Dockerfile b/Dockerfile index b4990fa..83f1534 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,16 +3,23 @@ FROM devkitpro/devkitarm:20260610 LABEL author="Striaton Lab Team" LABEL name="Poke Transporter GB" -USER root - -ARG USER_ID -ARG GROUP_ID +# uid and gid are overwritten by build command +ARG USER=docker +ARG UID +ARG GID +RUN groupadd -g $GID $USER +RUN useradd -m -u ${UID} -g ${GID} ${USER} ENV DEBIAN_FRONTEND="noninteractive" ENV IS_PTGB=true -RUN apt-get update && apt-get install -y build-essential cmake bison flex pkg-config libpng-dev git python3-pip jq && pip install --root-user-action=ignore pandas requests openpyxl debugpy pypng --break-system-packages +RUN apt-get update && apt-get install -y sudo build-essential cmake bison flex pkg-config libpng-dev git python3-pip jq && pip install --root-user-action=ignore pandas requests openpyxl debugpy pypng --break-system-packages + +RUN echo "${USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USER} && chmod 0440 /etc/sudoers.d/${USER} RUN mkdir -p rgbds && curl -fsSL https://github.com/gbdev/rgbds/releases/download/v1.0.2+hotfix/rgbds-linux-x86_64.tar.xz | tar -xJ -C rgbds && cd rgbds && ./install.sh -RUN git config --global --add safe.directory /ptgb \ No newline at end of file +RUN git config --global --add safe.directory /ptgb + +USER ${UID}:${GID} +WORKDIR /home/${USER} \ No newline at end of file diff --git a/Makefile b/Makefile index 83633f6..a11c8a3 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,11 @@ BUILD_LANGS := japanese english french german italian spanishEU spanishLA korean BUILD_TYPES := release debug BUILD_XLSXS := remote local +UID := $(shell id -u) +GID := $(shell id -g) +export UID +export GID + .ONESHELL: default: help @@ -85,7 +90,7 @@ setup: # Setup all required dependencies fi echo "Creating Docker image..." mkdir -p $(SRCDIR)/docker-build && cd $(SRCDIR)/docker-build - DOCKER_BUILDKIT=1 docker build --platform linux/amd64 -t ptgb-builder:latest -f $(SRCDIR)/Dockerfile $(SRCDIR)/docker-build + DOCKER_BUILDKIT=1 docker build --platform linux/amd64 -t ptgb-builder:latest --build-arg UID=$$UID --build-arg GID=$$GID -f $(SRCDIR)/Dockerfile $(SRCDIR)/docker-build rm -rf $(SRCDIR)/docker-build echo echo "Creating Docker container..." diff --git a/include/dbg/debug_mode.h b/include/dbg/debug_mode.h index 025d492..5c2fcc8 100644 --- a/include/dbg/debug_mode.h +++ b/include/dbg/debug_mode.h @@ -149,6 +149,6 @@ extern debug_options g_debug_options; // Compiling with this option enabled in release builds will cause a static_assert failure. // But the && DEBUG_MODE check will already prevent this from being enabled in release builds accidentally, // so as long as no-one removes that, it should be fine. -#define DEBUG_USE_MGBA_PRINT (false && DEBUG_MODE) +#define DEBUG_USE_MGBA_PRINT (true && DEBUG_MODE) #endif \ No newline at end of file diff --git a/include/link_handler.h b/include/link_handler.h index d390c3c..490ae31 100644 --- a/include/link_handler.h +++ b/include/link_handler.h @@ -230,6 +230,7 @@ enum LinkState PACKET_EXCHANGE = 0x10, STANDARD_PACKET_EXCHANGE, + RUNNING_CODE_DELAY, SECONDARY_PACKET_EXCHANGE, PROCESS_PACKET, LOAD_NEXT_PACKET, @@ -250,8 +251,8 @@ enum LinkConnectionError enum PayloadCommand { - CMD_NONE = -1, - CMD_ReloadCurrentBox, // no arguments used. Reloads the current box from SRAM. Use this as the first command byte before performing other commands. + CMD_NONE = 0x7F, // Using 0x7F instead of -1, since that ends up being 0xFE. + CMD_ReloadCurrentBox = 0, // 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_ModifySRAMAccess, // 1st argument = SRAM dis/enable. set to 0x0A to open access, set to 0x00 to close access. Second argument = SRAM bank. When closing SRAM access, set to 0x00. SRAM remains open until closed by this command or until closed by CMD_TransferPokemon/CMD_ReloadCurrentBox. @@ -299,7 +300,7 @@ struct LinkPacket LinkPacket() {}; LinkPacket(PayloadCommand cmd, byte arg1, byte arg2, u16 addr); - void loadSecondaryPayload(byte *payload, int payloadSize); + void loadSecondaryPayload(byte *payload, int paylHeoadSize); }; class LinkConnection @@ -340,7 +341,7 @@ public: LinkPacket *currOutgoingPacket; LinkPacket *currIncomingPacket; LinkPacket dummyPacket = LinkPacket(); - byte linkPacketArrIndex = 0; + byte linkPacketArrIndex = LINK_PACKET_ARRAY_SIZE; // We want to avoid starting at zero, since that is a common value sent back in error. u16 linkPacketDataAddr = 0; u16 linkPacketDataStart = 0; int linkPacketDataSize = 0; diff --git a/source/dbg/debug_mode.cpp b/source/dbg/debug_mode.cpp index 364e75f..ee7a5bf 100644 --- a/source/dbg/debug_mode.cpp +++ b/source/dbg/debug_mode.cpp @@ -14,7 +14,7 @@ debug_options g_debug_options = .dont_hide_invalid_pkmn = (false && DEBUG_MODE), .ignore_dex_completion = (false && DEBUG_MODE), .force_all_caught = (false && DEBUG_MODE), - .write_cable_data_to_save = (u8)WRITE_CABLE_DATA_MODE_CART, + .write_cable_data_to_save = (u8)WRITE_CABLE_DATA_MODE_OFF, .load_cable_data_from_save = (u8)WRITE_CABLE_DATA_MODE_OFF, .display_control_char = (false && DEBUG_MODE) }; \ No newline at end of file diff --git a/source/link_handler.cpp b/source/link_handler.cpp index 0efc334..69cb5ed 100644 --- a/source/link_handler.cpp +++ b/source/link_handler.cpp @@ -6,6 +6,7 @@ #include "link_handler.h" #include "script_array.h" #include "dbg/debug_mode.h" +#include "dbg/ptgb_mgba_print.h" #include "interrupt.h" #include "text_engine.h" #include "global_frame_controller.h" @@ -88,6 +89,11 @@ void LinkPacket::loadSecondaryPayload(byte *payload, int payloadSize) secondaryPayloadSizeChecksum += payload[i]; } secondaryPayloadSizeChecksum = 0x100 - secondaryPayloadSizeChecksum; + + PTGB_MGBA_INFO("Secondary payload loaded: dataSize=%d packetSizeArg=%d checksum=%X", + secondaryPayloadDataSize, + secondaryPayloadDataSize + SECONDARY_PAYLOAD_HEADER_SIZE, + secondaryPayloadSizeChecksum); } void LinkConnection::setup(const u16 *debug_charset) @@ -198,6 +204,7 @@ void LinkConnection::exchangeBytes() case WRITE_CABLE_DATA_MODE_OFF: // Normal transfer :-) inData = linkSPI->transfer(outData); + PTGB_MGBA_INFO("in: %X, out: %X", inData, outData); break; case WRITE_CABLE_DATA_MODE_SRAM: // Pretend transfer, by loading the bytes from SRAM (where we stored them with writeData() in a previous transfer) @@ -238,7 +245,7 @@ void LinkConnection::startConnection(LinkState startState) break; case PACKET_EXCHANGE: REG_TM3D = -0x0040; - // REG_TM3D = -0x4000 / 2; + // REG_TM3D = -0x4000 / 60; REG_TM3CNT = TM_FREQ_1024 | TM_ENABLE; break; default: @@ -459,7 +466,7 @@ void LinkConnection::handleStateLogic() exitState = WAIT_FOR_CHECKSUM_PAYLOAD; } } - nextOutData = payloadBuffer[subStateCounter]; + nextOutData = payloadBuffer[subStateCounter]; break; case MAIL: @@ -564,6 +571,41 @@ void LinkConnection::handleStateLogic() nextOutData = currOutgoingPacket->pointer >> 8; break; case TOTAL_PACKET_LENGTH: + PTGB_MGBA_INFO("Sent packet: cmd=[%X], args=[%X, %X], ptr=[%X], ID=[%X], Data=[%X, %X, %X, %X, %X, %X, %X, %X]", + currOutgoingPacket->command, + currOutgoingPacket->argument[0], + currOutgoingPacket->argument[1], + currOutgoingPacket->pointer, + currOutgoingPacket->packetID, + currOutgoingPacket->recievedData[0], + currOutgoingPacket->recievedData[1], + currOutgoingPacket->recievedData[2], + currOutgoingPacket->recievedData[3], + currOutgoingPacket->recievedData[4], + currOutgoingPacket->recievedData[5], + currOutgoingPacket->recievedData[6], + currOutgoingPacket->recievedData[7] + ); + exitState = RUNNING_CODE_DELAY; + nextOutData = 0xFF; + break; + default: + nextOutData = 0xFF; + break; + } + } + dataOutBuffer[subStateCounter] = inData; + break; + + case RUNNING_CODE_DELAY: + { + // We are currently processing something, we need to wait for that to finish. + if (inData == 0xFE) + { + nextOutData = 0xFF; + } + else + { if (currOutgoingPacket->secondaryPayloadData != nullptr) { // We get our response after the GB gets the secondary payload, send that first. @@ -573,13 +615,9 @@ void LinkConnection::handleStateLogic() { exitState = PROCESS_PACKET; } - default: - nextOutData = 0xFF; - break; } } - dataOutBuffer[subStateCounter] = inData; - break; + break; case SECONDARY_PACKET_EXCHANGE: { @@ -587,28 +625,63 @@ void LinkConnection::handleStateLogic() { case 0: nextOutData = 0xFD; + PTGB_MGBA_INFO("Secondary byte: sub=%d phase=preamble out=%X in=%X packet=%X cmd=%d", + subStateCounter, + nextOutData, + inData, + currOutgoingPacket->packetID, + currOutgoingPacket->command); break; case 1: nextOutData = linkPacketArrIndex; currOutgoingPacket->packetID = linkPacketArrIndex; + PTGB_MGBA_INFO("Secondary byte: sub=%d phase=counter out=%X in=%X packet=%X cmd=%d", + subStateCounter, + nextOutData, + inData, + currOutgoingPacket->packetID, + currOutgoingPacket->command); break; case 2: // The command ID matches the saftey byte (0xFF) nextOutData = 0xFF; + PTGB_MGBA_INFO("Secondary byte: sub=%d phase=safety out=%X in=%X packet=%X cmd=%d", + subStateCounter, + nextOutData, + inData, + currOutgoingPacket->packetID, + currOutgoingPacket->command); break; case 3: nextOutData = currOutgoingPacket->secondaryPayloadDataSize + 1; // We need to include the checksum value here as well + PTGB_MGBA_INFO("Secondary byte: sub=%d phase=dataSize out=%X in=%X dataSize=%d packetSizeArg=%d", + subStateCounter, + nextOutData, + inData, + currOutgoingPacket->secondaryPayloadDataSize, + currOutgoingPacket->secondaryPayloadDataSize + SECONDARY_PAYLOAD_HEADER_SIZE); break; default: // We're through all the constant values, let's do the rest. if ((subStateCounter - SECONDARY_PAYLOAD_HEADER_SIZE) < currOutgoingPacket->secondaryPayloadDataSize) { nextOutData = currOutgoingPacket->secondaryPayloadData[subStateCounter - SECONDARY_PAYLOAD_HEADER_SIZE]; + PTGB_MGBA_INFO("Secondary byte: sub=%d phase=data index=%d out=%X in=%X packet=%X", + subStateCounter, + subStateCounter - SECONDARY_PAYLOAD_HEADER_SIZE, + nextOutData, + inData, + currOutgoingPacket->packetID); } else if ((subStateCounter - SECONDARY_PAYLOAD_HEADER_SIZE) == currOutgoingPacket->secondaryPayloadDataSize) { // Add the checksum and we're done with the packet. nextOutData = currOutgoingPacket->secondaryPayloadSizeChecksum; + PTGB_MGBA_INFO("Secondary byte: sub=%d phase=checksum out=%X in=%X packet=%X", + subStateCounter, + nextOutData, + inData, + currOutgoingPacket->packetID); } else if (inData != currOutgoingPacket->packetID) { @@ -617,9 +690,17 @@ void LinkConnection::handleStateLogic() } else { - // Now it's time to process the packet. Use a dummy packet to flush it out + PTGB_MGBA_INFO("Secondary response counter seen: sub=%d packet=%X cmd=%d in=%X", + subStateCounter, + currOutgoingPacket->packetID, + currOutgoingPacket->command, + inData); + // Great, the secondary payload has been processed and we are currently receiving the response. + // Let's log the first response byte and head back to the normal packet talk + exitState = STANDARD_PACKET_EXCHANGE; + + // Use a dummy packet to flush it out currOutgoingPacket = &dummyPacket; - exitState = PROCESS_PACKET; nextOutData = 0xFF; } } @@ -798,10 +879,34 @@ bool LinkConnection::earlyExit() bool LinkConnection::processPacket() { + + PTGB_MGBA_INFO("Recieved raw data: Data=[%X, %X, %X, %X, %X, %X, %X, %X, %X, %X, %X, %X, %X, %X, %X, %X]", + dataOutBuffer[0], + dataOutBuffer[1], + dataOutBuffer[2], + dataOutBuffer[3], + dataOutBuffer[4], + dataOutBuffer[5], + dataOutBuffer[6], + dataOutBuffer[7], + dataOutBuffer[8], + dataOutBuffer[9], + dataOutBuffer[10], + dataOutBuffer[11], + dataOutBuffer[12], + dataOutBuffer[13], + dataOutBuffer[14], + dataOutBuffer[15] + ); + int checksum = 0; if (currIncomingPacket->packetID != dataOutBuffer[INP_COUNTER_INDEX]) { // This packet is not the correct ID for the response, ignore it + PTGB_MGBA_INFO("Packet counter mismatch: expected=%u got=%u cmd=%d", + currIncomingPacket->packetID, + dataOutBuffer[INP_COUNTER_INDEX], + currIncomingPacket->command); return false; } @@ -825,13 +930,43 @@ bool LinkConnection::processPacket() (dataOutBuffer[INP_DATA_INDEX + i] << 1) | ((lsbByte >> (7 - i)) & 0b1); } + PTGB_MGBA_INFO("Packet decoded: packet=%u cmd=%d lsb=%X/%X checksumExpected=%X checksumGot=%X recv=%X %X %X %X %X %X %X %X", + currIncomingPacket->packetID, + currIncomingPacket->command, + dataOutBuffer[INP_LSB_INDEX], + dataOutBuffer[INP_LSB_INDEX + 1], + checksum, + dataOutBuffer[INP_CHECKSUM_INDEX], + currIncomingPacket->recievedData[0], + currIncomingPacket->recievedData[1], + currIncomingPacket->recievedData[2], + currIncomingPacket->recievedData[3], + currIncomingPacket->recievedData[4], + currIncomingPacket->recievedData[5], + currIncomingPacket->recievedData[6], + currIncomingPacket->recievedData[7]); + 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; + PTGB_MGBA_INFO("Packet checksum mismatch: packet=%u cmd=%d expected=%X got=%X", + currIncomingPacket->packetID, + currIncomingPacket->command, + checksum, + dataOutBuffer[INP_CHECKSUM_INDEX]); return false; } + if ((currIncomingPacket->command != CMD_ReadDataRequest) && (dataOutBuffer[INP_DATA_INDEX] & 0x80)) + { + PTGB_MGBA_INFO("Packet reports GB command failure: packet=%u cmd=%d encodedFirstData=%X decodedFirstData=%X", + currIncomingPacket->packetID, + currIncomingPacket->command, + dataOutBuffer[INP_DATA_INDEX], + currIncomingPacket->recievedData[0]); + } + if (currIncomingPacket->command != CMD_ReadDataRequest) { // These command types will echo the first 8 bytes of the packet back to verify they were recieved correctly. Make sure that's the case. @@ -847,11 +982,33 @@ bool LinkConnection::processPacket() currIncomingPacket->recievedData[7] != 0xFF) { currIncomingPacket->latestError = ECHO_MISMATCH; + PTGB_MGBA_INFO("Packet echo mismatch: packet=%u cmd=%d recv=%X %X %X %X %X %X %X %X", + currIncomingPacket->packetID, + currIncomingPacket->command, + currIncomingPacket->recievedData[0], + currIncomingPacket->recievedData[1], + currIncomingPacket->recievedData[2], + currIncomingPacket->recievedData[3], + currIncomingPacket->recievedData[4], + currIncomingPacket->recievedData[5], + currIncomingPacket->recievedData[6], + currIncomingPacket->recievedData[7]); return false; } } currIncomingPacket->latestError = PACKET_SUCCESS; + PTGB_MGBA_INFO("Packet success: packet=%u cmd=%d recv=%X %X %X %X %X %X %X %X", + currIncomingPacket->packetID, + currIncomingPacket->command, + currIncomingPacket->recievedData[0], + currIncomingPacket->recievedData[1], + currIncomingPacket->recievedData[2], + currIncomingPacket->recievedData[3], + currIncomingPacket->recievedData[4], + currIncomingPacket->recievedData[5], + currIncomingPacket->recievedData[6], + currIncomingPacket->recievedData[7]); return true; } @@ -974,7 +1131,9 @@ bool LinkConnection::LinkCommand_InitalizeConnection(bool waitForCompletion) { return true; } - + + PTGB_MGBA_INFO("Running command: InitalizeConnection"); + globalLinkCable.startConnection(INITIAL_CONNECTION); if (waitForCompletion) @@ -992,6 +1151,8 @@ bool LinkConnection::LinkCommand_ReloadCurrentBox(bool waitForCompletion) return true; } + PTGB_MGBA_INFO("Running command: ReloadCurrentBox"); + resetLinkPackets(); linkPacketArr[0] = LinkPacket(CMD_ReloadCurrentBox, 0x00, 0x00, 0xC6DC); @@ -1040,11 +1201,15 @@ bool LinkConnection::LinkCommand_TransferPokemon(int boxNumber, byte removalArra return false; } + PTGB_MGBA_INFO("Running command: TransferPokemon"); + resetLinkPackets(); linkPacketArr[0] = LinkPacket(CMD_TransferPokemon, removalArrayLength + SECONDARY_PAYLOAD_HEADER_SIZE, boxNumber, 0xC6DC); linkPacketArr[0].loadSecondaryPayload(removalArray, removalArrayLength); + // REG_TM3D = -0x4000 / 60; + globalLinkCable.startConnection(PACKET_EXCHANGE); if (waitForCompletion) { @@ -1060,6 +1225,8 @@ bool LinkConnection::LinkCommand_SoftReset(bool waitForCompletion) return true; } + PTGB_MGBA_INFO("Running command: SoftReset"); + resetLinkPackets(); linkPacketArr[0] = LinkPacket(CMD_SoftReset, 0x00, 0x00, 0xC6DC); @@ -1083,6 +1250,9 @@ bool LinkConnection::LinkCommand_ModifySRAMAccess(bool enableSRAM, byte SRAMbank { return false; } + + PTGB_MGBA_INFO("Running command: ModifySRAMAccess"); + resetLinkPackets(); if (enableSRAM) @@ -1108,7 +1278,9 @@ bool LinkConnection::LinkCommand_RunSecondaryPayload(byte payload[], int payload { return true; } - + + PTGB_MGBA_INFO("Running command: RunSecondaryPayload"); + resetLinkPackets(); linkPacketArr[0] = LinkPacket(CMD_RunSecondaryPayload, payloadLength + SECONDARY_PAYLOAD_HEADER_SIZE, 0x00, 0xC6DC); @@ -1128,7 +1300,9 @@ bool LinkConnection::LinkCommand_ReadMemorySection(u16 dataPointer, byte outArra { return true; } - + + PTGB_MGBA_INFO("Running command: ReadMemorySection"); + resetLinkPackets(); linkPacketDataStart = dataPointer; diff --git a/source/script_array.cpp b/source/script_array.cpp index 35ef9c0..4ea23b9 100644 --- a/source/script_array.cpp +++ b/source/script_array.cpp @@ -804,9 +804,7 @@ bool run_conditional(int index) } boxRemovalPayload[arrayIndex] = 0xFF; arrayIndex++; - - globalLinkCable.LinkCommand_TransferPokemon(0, - boxRemovalPayload, arrayIndex); + globalLinkCable.LinkCommand_TransferPokemon(2, boxRemovalPayload, arrayIndex); } return true;