#define LINK_RAW_WIRELESS_ENABLE_LOGGING #include "../../../../lib/LinkRawWireless.hpp" #include "DebugScene.h" #include #include #include #include "../../../_lib/common.h" #include "../../../_lib/libgba-sprite-engine/scene.h" DebugScene::DebugScene(std::shared_ptr engine) : Scene(engine) {} static std::unique_ptr aHandler = std::unique_ptr(new InputHandler()); static std::unique_ptr bHandler = std::unique_ptr(new InputHandler()); static std::unique_ptr upHandler = std::unique_ptr(new InputHandler()); static std::unique_ptr downHandler = std::unique_ptr(new InputHandler()); static std::unique_ptr lHandler = std::unique_ptr(new InputHandler()); static std::unique_ptr rHandler = std::unique_ptr(new InputHandler()); static std::unique_ptr selectHandler = std::unique_ptr(new InputHandler()); static std::unique_ptr startHandler = std::unique_ptr(new InputHandler()); static std::unique_ptr rightHandler = std::unique_ptr(new InputHandler()); static std::vector logLines; static u32 currentLogLine = 0; static bool useVerboseLog = true; static const std::string CHARACTERS[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}; static const u32 CHARACTERS_LEN = 62; #define MAX_LINES 18 #define DRAW_LINE 2 void printScrollableText(u32 currentLine, std::vector lines, bool withCursor = false) { for (u32 i = 0; i < MAX_LINES; i++) { u32 lastLineIndex = MAX_LINES - 1; u32 index = max(currentLine, lastLineIndex) - lastLineIndex + i; if (index < lines.size()) { std::string cursor = currentLine == index ? "> " : " "; TextStream::instance().setText((withCursor ? cursor : "") + lines[index], DRAW_LINE + i, -3); } else { TextStream::instance().setText(" ", DRAW_LINE + i, -3); } } } void print() { printScrollableText(currentLogLine, logLines); } void scrollBack() { if (currentLogLine <= 0) return; currentLogLine--; print(); } void scrollForward() { if (currentLogLine < MAX_LINES - 1) currentLogLine = min(MAX_LINES - 1, logLines.size() - 1); if (currentLogLine == logLines.size() - 1) return; currentLogLine++; print(); } void scrollPageUp() { currentLogLine = max(currentLogLine - MAX_LINES, 0); print(); } void scrollPageDown() { currentLogLine = min(currentLogLine + MAX_LINES, logLines.size() - 1); print(); } void scrollToTop() { currentLogLine = 0; print(); } void scrollToBottom() { currentLogLine = logLines.size() - 1; print(); } void clear() { logLines.clear(); currentLogLine = 0; print(); } void log(std::string string) { logLines.push_back(string); scrollPageDown(); } const u32* toArray(std::vector vector) { return vector.data(); } std::vector DebugScene::backgrounds() { return {}; } std::vector DebugScene::sprites() { std::vector sprites; return sprites; } void DebugScene::load() { SCENE_init(); BACKGROUND_enable(true, false, false, false); linkRawWireless->logger = [](std::string string) { if (useVerboseLog) log(string); }; log("---"); log("LinkRawWireless_demo"); log(" (v8.0.3)"); log(""); log("START: reset wireless adapter"); log("RIGHT: restore from multiboot"); log("A: send command"); log("B: toggle log level"); log("UP/DOWN: scroll up/down"); log("L/R: scroll page up/down"); log("UP+L/DOWN+R: scroll to top/bottom"); log("SELECT: clear"); log("---"); log(""); toggleLogLevel(); addCommandMenuOptions(); for (u32 i = 0; i < 4; i++) serverIds[i] = 0; } void DebugScene::tick(u16 keys) { if (engine->isTransitioning()) return; std::string state = linkRawWireless->getState() == LinkRawWireless::State::NEEDS_RESET ? "NEEDS_RESET " : linkRawWireless->getState() == LinkRawWireless::State::AUTHENTICATED ? "AUTHENTICATED " : linkRawWireless->getState() == LinkRawWireless::State::SEARCHING ? "SEARCHING " : linkRawWireless->getState() == LinkRawWireless::State::SERVING ? "SERVING " : linkRawWireless->getState() == LinkRawWireless::State::CONNECTING ? "CONNECTING " : linkRawWireless->getState() == LinkRawWireless::State::CONNECTED ? "CONNECTED " : "? "; TextStream::instance().setText( "state = " + state + "p" + std::to_string(linkRawWireless->sessionState.currentPlayerId) + (linkRawWireless->getState() == LinkRawWireless::State::SERVING ? "/" + std::to_string(linkRawWireless->sessionState.playerCount) : ""), 0, -3); processKeys(keys); processButtons(); __qran_seed += keys; __qran_seed += REG_RCNT; __qran_seed += REG_SIOCNT; } void DebugScene::addCommandMenuOptions() { commandMenuOptions.push_back( CommandMenuOption{.name = "0x10 (Hello)", .command = 0x10}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x11 (SignalLevel)", .command = 0x11}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x12 (VersionStatus)", .command = 0x12}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x13 (SystemStatus)", .command = 0x13}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x14 (SlotStatus)", .command = 0x14}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x15 (ConfigStatus)", .command = 0x15}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x16 (Broadcast)", .command = 0x16}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x17 (Setup)", .command = 0x17}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x18 (?)", .command = 0x18}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x19 (StartHost)", .command = 0x19}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x1A (PollConnections)", .command = 0x1A}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x1B (EndHost)", .command = 0x1B}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x1C (BroadcastReadStart)", .command = 0x1C}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x1D (BroadcastReadPoll)", .command = 0x1D}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x1E (BroadcastReadEnd)", .command = 0x1E}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x1F (Connect)", .command = 0x1F}); commandMenuOptions.push_back(CommandMenuOption{ .name = "0x20 (IsConnectionComplete)", .command = 0x20}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x21 (FinishConnection)", .command = 0x21}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x24 (SendData)", .command = 0x24}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x25 (SendDataAndWait)", .command = 0x25}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x26 (ReceiveData)", .command = 0x26}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x27 (Wait)", .command = 0x27}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x30 (DisconnectClient)", .command = 0x30}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x32 (?)", .command = 0x32}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x33 (?)", .command = 0x33}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x34 (?)", .command = 0x34}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x35 (?!)", .command = 0x35}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x37 (RetransmitAndWait)", .command = 0x37}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x38 (?)", .command = 0x38}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x39 (?)", .command = 0x39}); commandMenuOptions.push_back( CommandMenuOption{.name = "0x3D (Bye)", .command = 0x3D}); } void DebugScene::processKeys(u16 keys) { aHandler->setIsPressed(keys & KEY_A); bHandler->setIsPressed(keys & KEY_B); upHandler->setIsPressed(keys & KEY_UP); downHandler->setIsPressed(keys & KEY_DOWN); lHandler->setIsPressed(keys & KEY_L); rHandler->setIsPressed(keys & KEY_R); selectHandler->setIsPressed(keys & KEY_SELECT); startHandler->setIsPressed(keys & KEY_START); rightHandler->setIsPressed(keys & KEY_RIGHT); } void DebugScene::processButtons() { if (bHandler->hasBeenPressedNow()) toggleLogLevel(); if (aHandler->hasBeenPressedNow()) { std::vector commandNames; commandNames.resize(commandMenuOptions.size()); std::transform(commandMenuOptions.begin(), commandMenuOptions.end(), commandNames.begin(), [](CommandMenuOption x) { return x.name; }); int selectedCommandIndex = selectOption("Which command?", commandNames, lastSelectedCommandIndex); if (selectedCommandIndex > -1) { lastSelectedCommandIndex = selectedCommandIndex; processCommand((u32)selectedCommandIndex); } print(); } if (lHandler->hasBeenPressedNow()) { if (upHandler->getIsPressed()) scrollToTop(); else scrollPageUp(); } if (rHandler->hasBeenPressedNow()) { if (downHandler->getIsPressed()) scrollToBottom(); else scrollPageDown(); } if (upHandler->getIsPressed()) scrollBack(); if (downHandler->getIsPressed()) scrollForward(); if (selectHandler->hasBeenPressedNow()) clear(); if (startHandler->hasBeenPressedNow()) resetAdapter(); if (rightHandler->hasBeenPressedNow()) restoreExistingConnection(); } void DebugScene::toggleLogLevel() { if (useVerboseLog) { useVerboseLog = false; log("! setting log level to NORMAL"); } else { useVerboseLog = true; log("! setting log level to VERBOSE"); } log(""); } int DebugScene::selectOption(std::string title, std::vector options, u32 cursor = 0) { u32 selectedOption = cursor; bool firstTime = true; while (true) { u16 keys = ~REG_KEYS & KEY_ANY; processKeys(keys); u32 oldOption = selectedOption; if (lHandler->hasBeenPressedNow()) { if (upHandler->getIsPressed()) selectedOption = 0; else selectedOption = max(selectedOption - MAX_LINES, 0); } if (rHandler->hasBeenPressedNow()) { if (downHandler->getIsPressed()) selectedOption = options.size() - 1; else selectedOption = min(selectedOption + MAX_LINES, options.size() - 1); } if (downHandler->hasBeenPressedNow() && selectedOption < options.size() - 1) selectedOption++; if (upHandler->hasBeenPressedNow() && selectedOption > 0) selectedOption--; if (firstTime || selectedOption != oldOption) { TextStream::instance().setText(title, 0, -3); printScrollableText(selectedOption, options, true); } if (bHandler->hasBeenPressedNow()) return -1; if (aHandler->hasBeenPressedNow()) return selectedOption; VBlankIntrWait(); firstTime = false; } } std::string DebugScene::selectString(u32 maxCharacters) { std::vector options = {""}; for (u32 i = 0; i < CHARACTERS_LEN; i++) options.push_back(CHARACTERS[i]); again: std::string str; for (u32 i = 0; i < maxCharacters; i++) { int characterIndex = -1; if ((characterIndex = selectOption("Next character? (" + str + ")", options)) == -1) { if (i == 0) return ""; else goto again; } if (characterIndex == 0) break; str += CHARACTERS[characterIndex - 1]; } if (str == "") goto again; if (selectOption(">> " + str + "?", std::vector{"yes", "no"}) == 1) goto again; return str; } int DebugScene::selectU32(std::string title) { again0: int byte0 = selectU8(title + " - Byte 0 (0x000000XX)"); if (byte0 == -1) return -1; again1: int byte1 = selectU8(title + " - Byte 1 (0x0000XX" + linkRawWireless->toHex(byte0, 2) + ")"); if (byte1 == -1) goto again0; again2: int byte2 = selectU8(title + " - Byte 2 (0x00XX" + linkRawWireless->toHex(byte1, 2) + linkRawWireless->toHex(byte0, 2) + ")"); if (byte2 == -1) goto again1; int byte3 = selectU8(title + " - Byte 3 (0xXX" + linkRawWireless->toHex(byte2, 2) + linkRawWireless->toHex(byte1, 2) + linkRawWireless->toHex(byte0, 2) + ")"); if (byte3 == -1) goto again2; u16 numberLow = Link::buildU16((u8)byte1, (u8)byte0); u16 numberHigh = Link::buildU16((u8)byte3, (u8)byte2); u32 number = Link::buildU32(numberHigh, numberLow); if (selectOption(">> 0x" + linkRawWireless->toHex(number, 8) + "?", std::vector{"yes", "no"}) == 1) goto again0; return number; } int DebugScene::selectU16() { again: int lsB = selectU8("Choose lsB (0x00XX)"); if (lsB == -1) return -1; int msB = selectU8("Choose msB (0xXX" + linkRawWireless->toHex(lsB, 2) + ")"); if (msB == -1) goto again; u16 number = Link::buildU16((u8)msB, (u8)lsB); if (selectOption(">> 0x" + linkRawWireless->toHex(number, 4) + "?", std::vector{"yes", "no"}) == 1) goto again; return number; } int DebugScene::selectU8(std::string title) { std::vector options; for (u32 i = 0; i < 1 << 8; i++) options.push_back(linkRawWireless->toHex(i, 2)); return selectOption(title, options); } void DebugScene::processCommand(u32 selectedCommandIndex) { CommandMenuOption selectedOption = commandMenuOptions[selectedCommandIndex]; auto name = selectedOption.name; auto command = selectedOption.command; if (selectHandler->getIsPressed()) goto generic; switch (command) { case 0x10: case 0x11: return logOperation("sending " + name, []() { LinkRawWireless::SignalLevelResponse response; bool success = linkRawWireless->getSignalLevel(response); if (success) { log("< [levelH] " + std::to_string(response.signalLevels[0])); log("< [levelC0] " + std::to_string(response.signalLevels[1])); log("< [levelC1] " + std::to_string(response.signalLevels[2])); log("< [levelC2] " + std::to_string(response.signalLevels[3])); log("< [levelC3] " + std::to_string(response.signalLevels[4])); } return success; }); case 0x12: goto simple; case 0x13: { return logOperation("sending " + name, []() { LinkRawWireless::SystemStatusResponse response; bool success = linkRawWireless->getSystemStatus(response); if (success) { log("< [device id] " + linkRawWireless->toHex(response.deviceId, 4)); log("< [player id] " + std::to_string(response.currentPlayerId)); log("< [state] " + std::to_string((int)response.adapterState)); log("< [closed] " + std::to_string(response.isServerClosed)); } return success; }); } case 0x14: { return logOperation("sending " + name, []() { LinkRawWireless::SlotStatusResponse response; bool success = linkRawWireless->getSlotStatus(response); if (success) { log("< [next slot] " + linkRawWireless->toHex(response.nextClientNumber, 2)); for (u32 i = 0; i < response.connectedClientsSize; i++) { log("< [client" + std::to_string(response.connectedClients[i].clientNumber) + "] " + linkRawWireless->toHex(response.connectedClients[i].deviceId, 4)); } } return success; }); } case 0x15: goto simple; case 0x16: { int gameId = selectGameId(); if (gameId == -1) return; std::string gameName = selectGameName(); if (gameName == "") return; std::string userName = selectUserName(); if (userName == "") return; return logOperation("sending " + name, [gameName, userName, gameId]() { log("setting broadcast:"); log("[room.gameId] " + linkRawWireless->toHex(gameId, 4)); log("[room.game] " + gameName); log("[room.user] " + userName); bool success = linkRawWireless->broadcast( gameName.c_str(), userName.c_str(), (u16)gameId); if (success) log("NOW CALL 0x19!"); return success; }); } case 0x17: { int maxPlayers = -1; while ((maxPlayers = selectOption( "Max players?", std::vector{"5", "4", "3", "2"})) == -1) ; int maxTransmissions = -1; while ((maxTransmissions = selectU8("Max transmissions?")) == -1) ; int waitTimeout = -1; while ((waitTimeout = selectU8("Wait timeout?")) == -1) ; return logOperation( "sending " + name, [maxPlayers, maxTransmissions, waitTimeout]() { log("maxPlayers = " + std::to_string(maxPlayers)); log("maxTransmissions = " + std::to_string(maxTransmissions)); log("waitTimeout = " + std::to_string(waitTimeout)); return linkRawWireless->setup(5 - maxPlayers, maxTransmissions, waitTimeout); }); } case 0x18: goto generic; case 0x19: { return logOperation("sending " + name, []() { return linkRawWireless->startHost(); }); } case 0x1A: { return logOperation("sending " + name, []() { LinkRawWireless::PollConnectionsResponse response; bool success = linkRawWireless->pollConnections(response); if (success) { for (u32 i = 0; i < response.connectedClientsSize; i++) { log("< [client" + std::to_string(response.connectedClients[i].clientNumber) + "] " + linkRawWireless->toHex(response.connectedClients[i].deviceId, 4)); } } return success; }); } case 0x1B: { return logOperation("sending " + name, []() { LinkRawWireless::PollConnectionsResponse response; bool success = linkRawWireless->endHost(response); if (success) { for (u32 i = 0; i < response.connectedClientsSize; i++) { log("< [client" + std::to_string(response.connectedClients[i].clientNumber) + "] " + linkRawWireless->toHex(response.connectedClients[i].deviceId, 4)); } } return success; }); } case 0x1C: { return logOperation("sending " + name, []() { bool success = linkRawWireless->broadcastReadStart(); if (success) log("NOW CALL 0x1D!"); return success; }); } case 0x1D: { return logOperation("sending " + name, [this]() { LinkRawWireless::BroadcastReadPollResponse response; bool success = linkRawWireless->broadcastReadPoll(response); if (success) { auto servers = response.servers; for (u32 i = 0; i < response.serversSize; i++) { serverIds[i] = servers[i].id; log("< [room" + std::to_string(i) + ".id] " + linkRawWireless->toHex(servers[i].id, 4)); log("< [room" + std::to_string(i) + ".gameId] " + linkRawWireless->toHex(servers[i].gameId, 4)); log("< [room" + std::to_string(i) + ".game] " + servers[i].gameName); log("< [room" + std::to_string(i) + ".user] " + servers[i].userName); log("< [room" + std::to_string(i) + ".nextSlot] " + linkRawWireless->toHex(servers[i].nextClientNumber, 2)); } if (response.serversSize > 0) log("NOW CALL 0x1E!"); else log("No rooms? NOW CALL 0x1E!"); } return success; }); } case 0x1E: { return logOperation("sending " + name, []() { bool success = linkRawWireless->broadcastReadEnd(); if (success) log("NOW CALL 0x1F!"); return success; }); } case 0x1F: { u16 serverId = selectServerId(); if (serverId == -1) return; return logOperation("sending " + name, [serverId]() { log("connecting to " + linkRawWireless->toHex(serverId, 4)); bool success = linkRawWireless->connect(serverId); if (success) log("NOW CALL 0x20!"); return success; }); } case 0x20: { return logOperation("sending " + name, []() { LinkRawWireless::ConnectionStatus response; bool success = linkRawWireless->keepConnecting(response); if (success) { log(std::string("< [phase] ") + (response.phase == LinkRawWireless::ConnectionPhase::STILL_CONNECTING ? "CONNECTING" : response.phase == LinkRawWireless::ConnectionPhase::ERROR ? "ERROR" : "SUCCESS")); if (response.phase == LinkRawWireless::ConnectionPhase::SUCCESS) log("< [slot] " + std::to_string(response.assignedClientNumber)); log("NOW CALL 0x21!"); } return success; }); } case 0x21: { return logOperation("sending " + name, []() { return linkRawWireless->finishConnection(); }); } case 0x24: { auto data = selectDataToSend(); if (data.empty()) return; u32 bytes = data[0]; data.erase(data.begin()); return logOperation("sending " + name, [&data, bytes]() { return linkRawWireless->sendData(toArray(data), data.size(), bytes); }); } case 0x25: { auto data = selectDataToSend(); if (data.empty()) return; if (selectOption("What mode?", std::vector{"sync", "async"}) == 1) { return logOperation("sending " + name, [&data]() { u32 bytes = data[0]; data[0] = linkRawWireless->getSendDataHeaderFor(bytes); bool success = linkRawWireless->sendCommandAsync(0x25, toArray(data), data.size(), true); if (!success) { log("! not now"); return false; } while (linkRawWireless->getAsyncState() == LinkRawWireless::AsyncState::WORKING) ; auto result = linkRawWireless->getAsyncCommandResult(); if (result.success) { log("< [notif] " + linkRawWireless->toHex(result.commandId)); for (u32 i = 0; i < result.dataSize; i++) { log("< [param" + std::to_string(i) + "] " + linkRawWireless->toHex(result.data[i])); } } return success; }); } else { u32 bytes = data[0]; data.erase(data.begin()); return logOperation("sending " + name, [&data, bytes]() { LinkRawWireless::CommandResult remoteCommand; bool success = linkRawWireless->sendDataAndWait( toArray(data), data.size(), remoteCommand, bytes); if (success) { log("< [notif] " + linkRawWireless->toHex(remoteCommand.commandId)); for (u32 i = 0; i < remoteCommand.dataSize; i++) { log("< [param" + std::to_string(i) + "] " + linkRawWireless->toHex(remoteCommand.data[i])); } } return success; }); } } case 0x26: { return logOperation("sending " + name, []() { LinkRawWireless::ReceiveDataResponse response; bool success = linkRawWireless->receiveData(response); if (success) { log("< [bytesH] " + std::to_string(response.sentBytes[0])); log("< [bytesC0] " + std::to_string(response.sentBytes[1])); log("< [bytesC1] " + std::to_string(response.sentBytes[2])); log("< [bytesC2] " + std::to_string(response.sentBytes[3])); log("< [bytesC3] " + std::to_string(response.sentBytes[4])); for (u32 i = 0; i < response.dataSize; i++) log("< [data" + std::to_string(i) + "] " + linkRawWireless->toHex(response.data[i])); } return success; }); } case 0x27: { return logOperation("sending " + name, []() { LinkRawWireless::CommandResult remoteCommand; bool success = linkRawWireless->wait(remoteCommand); if (success) { log("< [notif] " + linkRawWireless->toHex(remoteCommand.commandId)); for (u32 i = 0; i < remoteCommand.dataSize; i++) { log("< [param" + std::to_string(i) + "] " + linkRawWireless->toHex(remoteCommand.data[i])); } } return success; }); } case 0x30: { bool client0 = selectOption("Disconnect client 0?", std::vector{"yes", "no"}) == 0; bool client1 = selectOption("Disconnect client 1?", std::vector{"yes", "no"}) == 0; bool client2 = selectOption("Disconnect client 2?", std::vector{"yes", "no"}) == 0; bool client3 = selectOption("Disconnect client 3?", std::vector{"yes", "no"}) == 0; return logOperation("sending " + name, [client0, client1, client2, client3]() { bool success = linkRawWireless->disconnectClient( client0, client1, client2, client3); if (success) log("DISCONNECTED!"); return success; }); } case 0x32: case 0x33: case 0x34: goto generic; case 0x35: case 0x37: goto genericWait; case 0x38: case 0x39: goto generic; case 0x3D: goto simple; default: return; } simple: return logSimpleCommand(name, command); generic: return logGenericCommand(name, command); genericWait: return logGenericWaitCommand(name, command); } int DebugScene::selectServerId() { switch (selectOption("Which server ID?", std::vector{ "", "", "", "", ""})) { case 0: { if (serverIds[0] == 0) return -1; return serverIds[0]; } case 1: { if (serverIds[1] == 0) return -1; return serverIds[1]; } case 2: { if (serverIds[2] == 0) return -1; return serverIds[2]; } case 3: { if (serverIds[3] == 0) return -1; return serverIds[3]; } default: { return selectU16(); } } } int DebugScene::selectGameId() { switch (selectOption( "GameID?", std::vector{"0x7FFF", "0x1234", "", ""})) { case 0: { return 0x7FFF; } case 1: { return 0x1234; } case 2: { return Link::buildU16(qran_range(0, 256), qran_range(0, 256)); } default: { return selectU16(); } } } std::string DebugScene::selectGameName() { switch (selectOption("Game name?", std::vector{"LinkConnection", ""})) { case 0: { return "LinkConnection"; } default: { return selectString(LINK_RAW_WIRELESS_MAX_GAME_NAME_LENGTH); } } } std::vector DebugScene::selectDataToSend() { std::vector data; std::vector options; for (u32 i = 0; i < 87; i++) options.push_back(std::to_string(i)); int bytes = selectOption("How many BYTES?", options); if (bytes == -1) return data; data.push_back(bytes); if (selectOption(">> Include data?", std::vector{"yes", "no"}) == 1) return data; u32 words = (bytes + 3) / 4; for (u32 i = 0; i < (u32)words; i++) { int word = selectU32("Word " + std::to_string(i + 1) + "/" + std::to_string(words)); if (word == -1) return data; data.push_back(word); } return data; } std::vector DebugScene::selectData() { std::vector data; std::vector options; for (u32 i = 0; i < LINK_RAW_WIRELESS_MAX_COMMAND_TRANSFER_LENGTH; i++) options.push_back(std::to_string(i)); int words = selectOption("How many words?", options); if (words == -1) return data; for (u32 i = 0; i < (u32)words; i++) { int word = selectU32("Word " + std::to_string(i + 1) + "/" + std::to_string(words)); if (word == -1) return data; data.push_back(word); } return data; } std::string DebugScene::selectUserName() { switch ( selectOption("User name?", std::vector{"Demo", ""})) { case 0: { return "Demo"; } default: { return selectString(LINK_RAW_WIRELESS_MAX_USER_NAME_LENGTH); } } } void DebugScene::logGenericWaitCommand(std::string name, u32 id) { auto data = selectData(); return logOperation("sending " + name, [id, &data]() { auto result = linkRawWireless->sendCommand(id, toArray(data), data.size(), true); for (u32 i = 0; i < result.dataSize; i++) { log("< [response" + std::to_string(i) + "] " + linkRawWireless->toHex(result.data[i])); } if (!result.success) return false; log("Now WAITING..."); LinkRawWireless::CommandResult remoteCommand = linkRawWireless->receiveCommandFromAdapter(); if (remoteCommand.success) { log("< [notif] " + linkRawWireless->toHex(remoteCommand.commandId)); for (u32 i = 0; i < remoteCommand.dataSize; i++) { log("< [param" + std::to_string(i) + "] " + linkRawWireless->toHex(remoteCommand.data[i])); } } return remoteCommand.success; }); } void DebugScene::logGenericCommand(std::string name, u32 id) { auto data = selectData(); return logSimpleCommand(name, id, data); } void DebugScene::logSimpleCommand(std::string name, u32 id, std::vector params) { logOperation("sending " + name, [id, ¶ms]() { auto result = linkRawWireless->sendCommand(id, toArray(params), params.size()); for (u32 i = 0; i < result.dataSize; i++) { log("< [response" + std::to_string(i) + "] " + linkRawWireless->toHex(result.data[i])); } return result.success; }); } void DebugScene::logOperation(std::string name, std::function operation) { log("> " + name + "..."); bool success = operation(); log(success ? "< success :)" : "< failure :("); log(""); } void DebugScene::resetAdapter() { logOperation("resetting adapter", []() { return linkRawWireless->activate(); }); } void DebugScene::restoreExistingConnection() { logOperation("restoring from multiboot", []() { return linkRawWireless->restoreExistingConnection(); }); }