From 5cb96b5b69baf1ffdd034ecd0dc8e0915d6770c0 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Mon, 17 Feb 2025 00:08:41 -0300 Subject: [PATCH] Adding cancellation callbacks to LinkCard and sending the right loader based on region --- examples/LinkCard_demo/src/main.cpp | 22 +++-- lib/LinkCard.hpp | 145 ++++++++++++++++++---------- 2 files changed, 112 insertions(+), 55 deletions(-) diff --git a/examples/LinkCard_demo/src/main.cpp b/examples/LinkCard_demo/src/main.cpp index 6120911..b2d40c7 100644 --- a/examples/LinkCard_demo/src/main.cpp +++ b/examples/LinkCard_demo/src/main.cpp @@ -41,13 +41,13 @@ int main() { ; } - bool a = true; + bool a = true, b = true; while (true) { std::string output = "LinkCard_demo (v8.0.0)\n\n"; output += "Device: "; - auto device = linkCard->getConnectedDevice(); + auto device = linkCard->getConnectedDevice([]() { return false; }); switch (device) { case LinkCard::ConnectedDevice::E_READER_JAP: case LinkCard::ConnectedDevice::E_READER_USA: { @@ -55,11 +55,21 @@ int main() { if (Common::didPress(KEY_A, a)) { Common::log("Sending...\n\nPress B to cancel"); + auto fileName = device == LinkCard::ConnectedDevice::E_READER_JAP + ? JAP_LOADER + : USA_LOADER; + u32 loaderSize; - const u8* loader = - (const u8*)gbfs_get_nth_obj(fs, 0, NULL, &loaderSize); - auto res = linkCard->sendLoader(loader, loaderSize); - Common::log(std::to_string((int)res)); + const u8* loader = (const u8*)gbfs_get_obj(fs, fileName, &loaderSize); + auto res = linkCard->sendLoader(loader, loaderSize, [&b]() { + return Common::didPress(KEY_B, b); + }); + + if (res == LinkCard::SendResult::SUCCESS) + Common::log("Success! Press DOWN"); + else + Common::log("Error " + std::to_string((int)res) + "! Press DOWN"); + Common::waitForKey(KEY_DOWN); } diff --git a/lib/LinkCard.hpp b/lib/LinkCard.hpp index 462d6e8..9c53534 100644 --- a/lib/LinkCard.hpp +++ b/lib/LinkCard.hpp @@ -10,13 +10,21 @@ // - 2) Probe the connected device: // auto device = getConnectedDevice(); // - 2) Send the DLC loader program: -// if (device == LinkCard::ConnectedDevice::E_READER_LOADER_NEEDED) -// LinkCard::SendResult result = linkCard->sendLoader(loader); +// if (device == LinkCard::ConnectedDevice::E_READER_USA) +// LinkCard::SendResult result = linkCard->sendLoader(loader, []() { +// u16 keys = ~REG_KEYS & KEY_ANY; +// return keys & KEY_START; +// })); // - 3) Receive scanned cards: // if (device == LinkCard::ConnectedDevice::CARD_SCANNER) { -// u8 card[2076] -// if (linkCard->receiveCard(card)) { -// // use card as DLC +// u8 card[1998]; +// bool received = linkCard->receiveCard(card, []() { +// u16 keys = ~REG_KEYS & KEY_ANY; +// return keys & KEY_START; +// })); +// +// if (received) { +// // use `card` as DLC // } // } // -------------------------------------------------------------------------- @@ -59,6 +67,7 @@ class LinkCard { SUCCESS, UNALIGNED, INVALID_SIZE, + CANCELED, WRONG_DEVICE, FAILURE_DURING_TRANSFER }; @@ -76,11 +85,24 @@ class LinkCard { * with `sendLoader(...)`. * If it returns `DLC_LOADER`, you should receive scanned cards with * `receiveCard(...)`. - * If it returns `WRONG_CONNECTION`, the console is connected with the wrong - * end (it has to use the 1P end, aka playerId=0). If it returns - * `UNKNOWN_DEVICE`, the connected console uses another protocol. + * \warning Blocks the system until completion. */ ConnectedDevice getConnectedDevice() { + return getConnectedDevice([]() { return false; }); + } + + /** + * Returns the connected device. + * If it returns `E_READER_USA` or `E_READER_JAP`, you should send the loader + * with `sendLoader(...)`. + * If it returns `DLC_LOADER`, you should receive scanned cards with + * `receiveCard(...)`. + * @param cancel A function that will be continuously invoked. If it returns + * `true`, the transfer will be aborted. + * \warning Blocks the system until completion or cancellation. + */ + template + ConnectedDevice getConnectedDevice(F cancel) { linkRawCable.activate(); auto guard = Link::ScopeGuard([&]() { linkRawCable.deactivate(); }); @@ -88,7 +110,7 @@ class LinkCard { return ConnectedDevice::WRONG_CONNECTION; u16 remoteValues[3]; for (u32 i = 0; i < 3; i++) { - remoteValues[i] = transferMulti(0); + remoteValues[i] = transferMulti(0, []() { return false; }); if (i > 0 && remoteValues[i] != remoteValues[i - 1]) return ConnectedDevice::UNKNOWN_DEVICE; } @@ -107,15 +129,19 @@ class LinkCard { * the scanned cards to the game. Must be 4-byte aligned. * @param loaderSize Size of the loader program in bytes. Must be a multiple * of 32. + * @param cancel A function that will be continuously invoked. If it returns + * `true`, the transfer will be aborted. + * \warning Blocks the system until completion or cancellation. */ - SendResult sendLoader(const u8* loader, u32 loaderSize) { + template + SendResult sendLoader(const u8* loader, u32 loaderSize, F cancel) { if ((u32)loader % 4 != 0) return SendResult::UNALIGNED; if (loaderSize < MIN_LOADER_SIZE || loaderSize > MAX_LOADER_SIZE || (loaderSize % 0x20) != 0) return SendResult::INVALID_SIZE; - auto device = getConnectedDevice(); + auto device = getConnectedDevice(cancel); if (device != ConnectedDevice::E_READER_USA && device != ConnectedDevice::E_READER_JAP) return SendResult::WRONG_DEVICE; @@ -124,52 +150,71 @@ class LinkCard { ? DEVICE_E_READER_USA : DEVICE_E_READER_JAP; - linkRawCable.activate(); - Link::wait(MODE_SWITCH_WAIT); + { + linkRawCable.activate(); + auto guard = Link::ScopeGuard([&]() { linkRawCable.deactivate(); }); - retry: - transferMulti(HANDSHAKE_SEND); - if (transferMulti(HANDSHAKE_SEND) != deviceId) - goto retry; - if (transferMulti(deviceId) != deviceId) - goto retry; + Link::wait(MODE_SWITCH_WAIT); + if (cancel()) + return SendResult::CANCELED; - linkRawCable.deactivate(); - linkSPI.activate(LinkSPI::Mode::MASTER_256KBPS); - Link::wait(MODE_SWITCH_WAIT); - - transferNormal(loaderSize); // cancel? - - u32 checksum = 0; - - u32* dataOut = (u32*)loader; - for (u32 i = 0; i < loaderSize / 4; i++) { - u32 data = dataOut[i]; - checksum += data; - transferNormal(data); - // cancel? + retry: + transferMulti(HANDSHAKE_SEND, cancel); + if (transferMulti(HANDSHAKE_SEND, cancel) != deviceId) + goto retry; + if (transferMulti(deviceId, cancel) != deviceId) + goto retry; } - transferNormal(0); - transferNormal(checksum); - transferNormal(checksum); + { + linkSPI.activate(LinkSPI::Mode::MASTER_256KBPS); + auto guard = Link::ScopeGuard([&]() { linkSPI.deactivate(); }); - linkSPI.deactivate(); - linkRawCable.activate(); - Link::wait(MODE_SWITCH_WAIT); + Link::wait(MODE_SWITCH_WAIT); + if (cancel()) + return SendResult::CANCELED; - if (transferMulti(deviceId) != deviceId || - transferMulti(deviceId) != TRANSFER_SUCCESS) { - linkRawCable.deactivate(); - return SendResult::FAILURE_DURING_TRANSFER; + transferNormal(loaderSize, cancel); + + u32 checksum = 0; + u32* dataOut = (u32*)loader; + for (u32 i = 0; i < loaderSize / 4; i++) { + u32 data = dataOut[i]; + checksum += data; + transferNormal(data, cancel); + } + + transferNormal(0, cancel); + transferNormal(checksum, cancel); + transferNormal(checksum, cancel); } - linkRawCable.deactivate(); + { + linkRawCable.activate(); + auto guard = Link::ScopeGuard([&]() { linkRawCable.deactivate(); }); + + Link::wait(MODE_SWITCH_WAIT); + if (cancel()) + return SendResult::CANCELED; + + if (transferMulti(deviceId, cancel) != deviceId || + transferMulti(deviceId, cancel) != TRANSFER_SUCCESS) + return SendResult::FAILURE_DURING_TRANSFER; + } return SendResult::SUCCESS; } - bool receiveCard() { + /** + * Receives a 1998-byte `card` from the DLC Loader and returns `true` if it + * worked correctly. + * @param card A pointer to fill the card bytes. + * @param cancel A function that will be continuously invoked. If it returns + * `true`, the transfer will be aborted. + * \warning Blocks the system until completion or cancellation. + */ + template + bool receiveCard(u8* card, F cancel) { LINK_READ_TAG(LINK_CARD_VERSION); // TODO: IMPLEMENT return true; @@ -179,14 +224,16 @@ class LinkCard { LinkRawCable linkRawCable; LinkSPI linkSPI; - u16 transferMulti(u16 value) { + template + u16 transferMulti(u16 value, F cancel) { Link::wait(PRE_TRANSFER_WAIT); - return linkRawCable.transfer(value).data[1]; + return linkRawCable.transfer(value, cancel).data[1]; } - void transferNormal(u32 value) { + template + void transferNormal(u32 value, F cancel) { Link::wait(PRE_TRANSFER_WAIT); - linkSPI.transfer(value); + linkSPI.transfer(value, cancel); } };