Improving reliability of card transfers

This commit is contained in:
Rodrigo Alfonso 2025-04-21 03:46:51 -03:00
parent 3d4b5f934d
commit a8ab944b58
6 changed files with 132 additions and 31 deletions

View File

@ -16,7 +16,8 @@ const data = {
"MSG_TRANSFERRING": "",
"MSG_CARD_SENT": " ",
"MSG_ERROR": "",
"MSG_PRESS_B_CANCEL": "   "
"MSG_PRESS_B_CANCEL": "   ",
"MSG_NUMBERS": ""
};
*/

View File

@ -84,7 +84,14 @@ bool send(u16 data, CancelCallback cancel) {
bool sendAndExpect(u16 data, u16 expect, CancelCallback cancel) {
u16 received;
do {
if (!send(data, cancel))
bool sent = false;
for (u32 attempt = 0; attempt < 3; attempt++) {
if (send(data, cancel)) {
sent = true;
break;
}
}
if (!sent)
return false;
received = getDataFromPlayer0();

View File

@ -3,8 +3,21 @@
#include "link.h"
#include "protocol.h"
#ifdef REGION_JAP
// Enable this to simulate failed scans with ⮜ and successful scans with ➤
#define DEBUG_MODE 0
// Enable this to display error codes
#define DISPLAY_ERROR_CODES 1
// Japanese strings are encoded as Shift-JIS byte arrays.
#ifdef REGION_JAP
#if DISPLAY_ERROR_CODES == 1
/* "" */
const u8 MSG_NUMBERS[] = {0x82, 0x4f, 0x82, 0x50, 0x82, 0x51, 0x82,
0x52, 0x82, 0x53, 0x82, 0x54, 0x82, 0x55,
0x82, 0x56, 0x82, 0x57, 0x82, 0x58, 0x00};
#endif
#ifdef LANGUAGE_ENG
const u8 MSG_WAITING_GAME[] = {
0x82, 0x76, 0x82, 0x60, 0x82, 0x68, 0x82, 0x73, 0x82, 0x68, 0x82,
@ -62,8 +75,42 @@ extern int __end[];
ERAPI_HANDLE_REGION region;
u8 card[CARD_BUFFER_SIZE];
const u16 palette[] = {0x0000, 0xFFFF};
u32 previousKeys = 0;
void print(const char* text);
#if DISPLAY_ERROR_CODES == 1
void codeToString(char* buf, int num) {
#ifdef REGION_JAP
const u8* digits = MSG_NUMBERS;
int temp[5];
int len = 0;
do {
temp[len++] = num % 10;
num /= 10;
} while (num && len < 5);
u8* p = (u8*)buf;
for (int i = len - 1; i >= 0; --i) {
int idx = temp[i] * 2;
*p++ = digits[idx];
*p++ = digits[idx + 1];
}
*p = 0;
#else
char temp[6];
int pos = 0;
do {
temp[pos++] = '0' + (num % 10);
num /= 10;
} while (num && pos < 5);
int j = 0;
while (pos)
buf[j++] = temp[--pos];
buf[j] = '\0';
#endif
}
#endif
void print(const char* text, bool canCancel);
bool cancel();
void reset();
@ -85,14 +132,13 @@ int main() {
// loop
while (1) {
if (cancel())
break;
u32 errorCode = 0;
// init loader
reset();
// "Waiting for game..."
print(MSG_WAITING_GAME);
print(MSG_WAITING_GAME, false);
// handshake with game
if (!sendAndExpect(HANDSHAKE_1, HANDSHAKE_1, cancel))
@ -104,8 +150,10 @@ int main() {
// wait for card request
u16 cardRequest = sendAndReceiveExcept(HANDSHAKE_3, HANDSHAKE_3, cancel);
if (cardRequest != GAME_REQUEST)
if (cardRequest != GAME_REQUEST) {
errorCode = 1;
goto error;
}
// confirm card request
if (!sendAndExpect(GAME_ANIMATING, EREADER_ANIMATING, cancel))
@ -113,45 +161,80 @@ int main() {
if (!send(EREADER_ANIMATING, cancel))
continue;
// "Scan a card!"
print(MSG_SCAN_CARD);
// scan card
if (cancel())
goto abort;
if (!sendAndExpect(EREADER_READY, GAME_READY, cancel))
goto abort;
u32 resultCode = ERAPI_ScanDotCode((u32)card);
if (resultCode != SCAN_SUCCESS)
if (!sendAndExpect(EREADER_READY, GAME_READY, cancel)) {
errorCode = 2;
goto error;
}
// "Scan a card!"
print(MSG_SCAN_CARD, false);
#if DEBUG_MODE == 1
u32 resultCode = 0;
while (true) {
u32 debugKeys = ERAPI_GetKeyStateRaw();
if ((debugKeys & ERAPI_KEY_LEFT) != 0) {
resultCode = SCAN_SUCCESS - 1;
break;
}
if ((debugKeys & ERAPI_KEY_RIGHT) != 0) {
resultCode = SCAN_SUCCESS;
const char msg[] = "HelloWorld";
const u32 msgLen = sizeof(msg) - 1;
const u32 byteCount = CARD_BUFFER_SIZE - CARD_OFFSET;
for (u32 i = 0; i < byteCount; i++)
card[CARD_OFFSET + i] = i == byteCount - 1 ? '!' : msg[i % msgLen];
break;
}
}
#else
u32 resultCode = ERAPI_ScanDotCode((u32)card);
#endif
if (resultCode != SCAN_SUCCESS) {
errorCode = 3;
goto error;
}
// "Transferring..."
print(MSG_TRANSFERRING);
print(MSG_TRANSFERRING, true);
// transfer start
if (!sendAndExpect(EREADER_SEND_READY, GAME_RECEIVE_READY, cancel))
if (!sendAndExpect(EREADER_SEND_READY, GAME_RECEIVE_READY, cancel)) {
errorCode = 4;
goto error;
if (!send(EREADER_SEND_START, cancel))
}
if (!send(EREADER_SEND_START, cancel)) {
errorCode = 5;
goto error;
}
// transfer
u32 checksum = 0;
for (u32 o = CARD_OFFSET; o < CARD_SIZE; o += 2) {
u16 block = *(u16*)(card + o);
if (!send(block, cancel))
if (!send(block, cancel)) {
errorCode = 6;
goto error;
}
checksum += block;
}
if (!send(checksum & 0xffff, cancel))
if (!send(checksum & 0xffff, cancel)) {
errorCode = 7;
goto error;
if (!send(checksum >> 16, cancel))
}
if (!send(checksum >> 16, cancel)) {
errorCode = 8;
goto error;
if (!send(EREADER_SEND_END, cancel))
}
if (!send(EREADER_SEND_END, cancel)) {
errorCode = 9;
goto error;
}
// "Card sent!"
print(MSG_CARD_SENT);
print(MSG_CARD_SENT, false);
for (u32 i = 0; i < POST_TRANSFER_WAIT; i++)
ERAPI_RenderFrame(1);
@ -168,7 +251,13 @@ int main() {
// "Error!"
ERAPI_ClearRegion(region);
ERAPI_DrawText(region, 0, 0, MSG_ERROR);
#if DISPLAY_ERROR_CODES == 1
char errorCodeStr[11];
codeToString(errorCodeStr, errorCode);
ERAPI_DrawText(region, 0, 16, errorCodeStr);
#else
ERAPI_DrawText(region, 0, 16, MSG_WAITING_GAME);
#endif
ERAPI_RenderFrame(1);
send(EREADER_CANCEL, cancel);
@ -183,16 +272,20 @@ int main() {
return ERAPI_EXIT_TO_MENU;
}
void print(const char* text) {
void print(const char* text, bool canCancel) {
ERAPI_ClearRegion(region);
ERAPI_DrawText(region, 0, 0, text);
ERAPI_DrawText(region, 0, 16, MSG_PRESS_B_CANCEL);
if (canCancel)
ERAPI_DrawText(region, 0, 16, MSG_PRESS_B_CANCEL);
ERAPI_RenderFrame(1);
}
bool cancel() {
u32 keys = ERAPI_GetKeyStateRaw();
return (keys & ERAPI_KEY_B) != 0;
bool isPressed =
(previousKeys & ERAPI_KEY_B) == 0 && (keys & ERAPI_KEY_B) != 0;
previousKeys = keys;
return isPressed;
}
void reset() {

View File

@ -51,7 +51,7 @@ int main() {
u32 initialVCount = REG_VCOUNT;
auto device = linkCard->getConnectedDevice([&initialVCount]() {
u32 elapsed = (REG_VCOUNT - initialVCount + 228) % 228;
return elapsed > 30;
return elapsed > 150;
});
switch (device) {

View File

@ -81,7 +81,7 @@ class LinkCard {
static constexpr int CMD_LINKCARD_RESET = 0;
static constexpr int MODE_SWITCH_WAIT = 228;
static constexpr int DEACTIVATION_WAIT = 50;
static constexpr int PRE_TRANSFER_WAIT = 2 + 1;
static constexpr int PRE_TRANSFER_WAIT = 4;
public:
enum class ConnectedDevice {