diff --git a/include/jbus/Common.hpp b/include/jbus/Common.hpp index e19cf15..d20ff67 100644 --- a/include/jbus/Common.hpp +++ b/include/jbus/Common.hpp @@ -137,7 +137,7 @@ static inline double SBig(double val) {return val;} #endif class Endpoint; -class EndpointLocal; +class ThreadLocalEndpoint; enum EJStatFlags { @@ -159,7 +159,7 @@ enum EJoyReturn GBA_JOYBOOT_ERR_INVALID = 4 }; -using FGBACallback = std::function; +using FGBACallback = std::function; u64 GetGCTicks(); void WaitGCTicks(u64 ticks); diff --git a/include/jbus/Endpoint.hpp b/include/jbus/Endpoint.hpp index 6289e5a..fb9bf55 100644 --- a/include/jbus/Endpoint.hpp +++ b/include/jbus/Endpoint.hpp @@ -89,19 +89,19 @@ class KawasedoChallenge u32 x64_totalBytes; bool m_started = true; - void F23(EndpointLocal& endpoint, EJoyReturn status); - void F25(EndpointLocal& endpoint, EJoyReturn status); - void F27(EndpointLocal& endpoint, EJoyReturn status); - void F29(EndpointLocal& endpoint, EJoyReturn status); + void F23(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void F25(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void F27(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void F29(ThreadLocalEndpoint& endpoint, EJoyReturn status); void GBAX02(); - void GBAX01(EndpointLocal& endpoint); - void F31(EndpointLocal& endpoint, EJoyReturn status); - void F33(EndpointLocal& endpoint, EJoyReturn status); - void F35(EndpointLocal& endpoint, EJoyReturn status); - void F37(EndpointLocal& endpoint, EJoyReturn status); - void F39(EndpointLocal& endpoint, EJoyReturn status); + void GBAX01(ThreadLocalEndpoint& endpoint); + void F31(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void F33(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void F35(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void F37(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void F39(ThreadLocalEndpoint& endpoint, EJoyReturn status); - auto bindThis(void(KawasedoChallenge::*ptmf)(EndpointLocal&, EJoyReturn)) + auto bindThis(void(KawasedoChallenge::*ptmf)(ThreadLocalEndpoint&, EJoyReturn)) { return std::bind(ptmf, this, std::placeholders::_1, std::placeholders::_2); } @@ -121,7 +121,7 @@ public: class Endpoint { - friend class EndpointLocal; + friend class ThreadLocalEndpoint; enum EJoybusCmds { @@ -169,7 +169,7 @@ class Endpoint size_t runBuffer(u8* buffer, u64& remTicks, EWaitResp resp); bool idleGetStatus(u64& remTicks); void transferProc(); - void transferWakeup(EndpointLocal& endpoint, u8 status); + void transferWakeup(ThreadLocalEndpoint& endpoint, u8 status); auto bindSync() { @@ -196,11 +196,11 @@ public: ~Endpoint(); }; -class EndpointLocal +class ThreadLocalEndpoint { friend class Endpoint; Endpoint& m_ep; - EndpointLocal(Endpoint& ep) : m_ep(ep) {} + ThreadLocalEndpoint(Endpoint& ep) : m_ep(ep) {} public: EJoyReturn GBAGetStatusAsync(u8* status, FGBACallback&& callback); EJoyReturn GBAResetAsync(u8* status, FGBACallback&& callback); diff --git a/include/jbus/Socket.hpp b/include/jbus/Socket.hpp index 4f47184..7c12557 100644 --- a/include/jbus/Socket.hpp +++ b/include/jbus/Socket.hpp @@ -122,7 +122,7 @@ class Socket m_socket = socket(PF_INET, SOCK_STREAM, 0); if (m_socket == -1) { - fprintf(stderr, "Can't allocate socket"); + fprintf(stderr, "Can't allocate socket\n"); return false; } @@ -192,14 +192,14 @@ public: if (bind(m_socket, reinterpret_cast(&addr), sizeof(addr)) == -1) { /* Not likely to happen, but... */ - fprintf(stderr, "Failed to bind listener socket to port %d", port); + fprintf(stderr, "Failed to bind listener socket to port %d\n", port); return false; } if (::listen(m_socket, 0) == -1) { /* Oops, socket is deaf */ - fprintf(stderr, "Failed to listen to port %d", port); + fprintf(stderr, "Failed to listen to port %d\n", port); return false; } @@ -220,7 +220,7 @@ public: { EResult res = (errno == EAGAIN) ? EResult::Busy : EResult::Error; if (res == EResult::Error) - fprintf(stderr, "Failed to accept incoming connection: %s", strerror(errno)); + fprintf(stderr, "Failed to accept incoming connection: %s\n", strerror(errno)); return res; } diff --git a/src/Endpoint.cpp b/src/Endpoint.cpp index a2fe304..63b4694 100644 --- a/src/Endpoint.cpp +++ b/src/Endpoint.cpp @@ -1,9 +1,11 @@ #include "jbus/Endpoint.hpp" +#define LOG_TRANSFER 0 + namespace jbus { -void KawasedoChallenge::F23(EndpointLocal& endpoint, EJoyReturn status) +void KawasedoChallenge::F23(ThreadLocalEndpoint& endpoint, EJoyReturn status) { if (status != GBA_READY || (status = endpoint.GBAResetAsync(x10_statusPtr, @@ -18,7 +20,7 @@ void KawasedoChallenge::F23(EndpointLocal& endpoint, EJoyReturn status) } } -void KawasedoChallenge::F25(EndpointLocal& endpoint, EJoyReturn status) +void KawasedoChallenge::F25(ThreadLocalEndpoint& endpoint, EJoyReturn status) { if (status == GBA_READY) if (*x10_statusPtr != GBA_JSTAT_SEND) @@ -37,7 +39,7 @@ void KawasedoChallenge::F25(EndpointLocal& endpoint, EJoyReturn status) } } -void KawasedoChallenge::F27(EndpointLocal& endpoint, EJoyReturn status) +void KawasedoChallenge::F27(ThreadLocalEndpoint& endpoint, EJoyReturn status) { if (status == GBA_READY) if (*x10_statusPtr != (GBA_JSTAT_PSF0 | GBA_JSTAT_SEND)) @@ -56,7 +58,7 @@ void KawasedoChallenge::F27(EndpointLocal& endpoint, EJoyReturn status) } } -void KawasedoChallenge::F29(EndpointLocal& endpoint, EJoyReturn status) +void KawasedoChallenge::F29(ThreadLocalEndpoint& endpoint, EJoyReturn status) { if (status != GBA_READY) { @@ -83,7 +85,7 @@ void KawasedoChallenge::GBAX02() xf8_dspHmac.ProcessGBACrypto(); } -void KawasedoChallenge::GBAX01(EndpointLocal& endpoint) +void KawasedoChallenge::GBAX01(ThreadLocalEndpoint& endpoint) { x58_currentKey = xf8_dspHmac.x20_publicKey; x5c_initMessage = xf8_dspHmac.x24_authInitCode; @@ -116,7 +118,7 @@ void KawasedoChallenge::GBAX01(EndpointLocal& endpoint) } } -void KawasedoChallenge::F31(EndpointLocal& endpoint, EJoyReturn status) +void KawasedoChallenge::F31(ThreadLocalEndpoint& endpoint, EJoyReturn status) { if (status != GBA_READY) { @@ -129,7 +131,9 @@ void KawasedoChallenge::F31(EndpointLocal& endpoint, EJoyReturn status) return; } +#if LOG_TRANSFER printf("PROG [%d/%d]\n", x34_bytesSent, x64_totalBytes); +#endif if (x30_justStarted) { x30_justStarted = 0; @@ -261,7 +265,7 @@ void KawasedoChallenge::F31(EndpointLocal& endpoint, EJoyReturn status) } } -void KawasedoChallenge::F33(EndpointLocal& endpoint, EJoyReturn status) +void KawasedoChallenge::F33(ThreadLocalEndpoint& endpoint, EJoyReturn status) { if (status != GBA_READY || (status = endpoint.GBAGetStatusAsync(x10_statusPtr, @@ -276,7 +280,7 @@ void KawasedoChallenge::F33(EndpointLocal& endpoint, EJoyReturn status) } } -void KawasedoChallenge::F35(EndpointLocal& endpoint, EJoyReturn status) +void KawasedoChallenge::F35(ThreadLocalEndpoint& endpoint, EJoyReturn status) { if (status == GBA_READY) if (*x10_statusPtr & (GBA_JSTAT_FLAGS_MASK | GBA_JSTAT_RECV)) @@ -320,7 +324,7 @@ void KawasedoChallenge::F35(EndpointLocal& endpoint, EJoyReturn status) } } -void KawasedoChallenge::F37(EndpointLocal& endpoint, EJoyReturn status) +void KawasedoChallenge::F37(ThreadLocalEndpoint& endpoint, EJoyReturn status) { if (status != GBA_READY || (status = endpoint.GBAWriteAsync(x18_readBuf, x10_statusPtr, @@ -335,10 +339,10 @@ void KawasedoChallenge::F37(EndpointLocal& endpoint, EJoyReturn status) } } -void KawasedoChallenge::F39(EndpointLocal& endpoint, EJoyReturn status) +void KawasedoChallenge::F39(ThreadLocalEndpoint& endpoint, EJoyReturn status) { if (status == GBA_READY) - *x10_statusPtr = GBA_READY; + *x10_statusPtr = 0; x28_ticksAfterXf = 0; @@ -436,11 +440,13 @@ void Endpoint::send(const u8* buffer) { m_running = false; } +#if LOG_TRANSFER else { printf("Send %02x [> %02x%02x%02x%02x] (%lu)\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], sentBytes); } +#endif m_timeCmdSent = GetGCTicks(); } @@ -483,6 +489,7 @@ size_t Endpoint::seceive(u8* buffer) if (recvBytes > 5) recvBytes = 5; +#if LOG_TRANSFER if (recvBytes > 0) { if (m_lastCmd == CMD_STATUS || m_lastCmd == CMD_RESET) @@ -498,6 +505,7 @@ size_t Endpoint::seceive(u8* buffer) (u8)buffer[3], (u8)buffer[4], recvBytes); } } +#endif return recvBytes; } @@ -542,7 +550,9 @@ bool Endpoint::idleGetStatus(u64& remTicks) void Endpoint::transferProc() { +#if LOG_TRANSFER printf("Starting JoyBus transfer thread for channel %d\n", m_chan); +#endif std::unique_lock lk(m_syncLock); while (m_running) @@ -596,7 +606,7 @@ void Endpoint::transferProc() { FGBACallback cb = std::move(m_callback); m_callback = {}; - EndpointLocal ep(*this); + ThreadLocalEndpoint ep(*this); cb(ep, xferStatus); } @@ -628,10 +638,13 @@ void Endpoint::transferProc() m_syncCv.notify_all(); m_dataSocket.close(); m_clockSocket.close(); + +#if LOG_TRANSFER printf("Stopping JoyBus transfer thread for channel %d\n", m_chan); +#endif } -void Endpoint::transferWakeup(EndpointLocal& endpoint, u8 status) +void Endpoint::transferWakeup(ThreadLocalEndpoint& endpoint, u8 status) { m_syncCv.notify_all(); } @@ -819,7 +832,7 @@ Endpoint::Endpoint(u8 chan, net::Socket&& data, net::Socket&& clock) Endpoint::~Endpoint() { stop(); } -EJoyReturn EndpointLocal::GBAGetStatusAsync(u8* status, FGBACallback&& callback) +EJoyReturn ThreadLocalEndpoint::GBAGetStatusAsync(u8* status, FGBACallback&& callback) { if (m_ep.m_cmdIssued) return GBA_NOT_READY; @@ -832,7 +845,7 @@ EJoyReturn EndpointLocal::GBAGetStatusAsync(u8* status, FGBACallback&& callback) return GBA_READY; } -EJoyReturn EndpointLocal::GBAResetAsync(u8* status, FGBACallback&& callback) +EJoyReturn ThreadLocalEndpoint::GBAResetAsync(u8* status, FGBACallback&& callback) { if (m_ep.m_cmdIssued) return GBA_NOT_READY; @@ -845,7 +858,7 @@ EJoyReturn EndpointLocal::GBAResetAsync(u8* status, FGBACallback&& callback) return GBA_READY; } -EJoyReturn EndpointLocal::GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback) +EJoyReturn ThreadLocalEndpoint::GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback) { if (m_ep.m_cmdIssued) return GBA_NOT_READY; @@ -859,7 +872,7 @@ EJoyReturn EndpointLocal::GBAReadAsync(u8* dst, u8* status, FGBACallback&& callb return GBA_READY; } -EJoyReturn EndpointLocal::GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback) +EJoyReturn ThreadLocalEndpoint::GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback) { if (m_ep.m_cmdIssued) return GBA_NOT_READY; diff --git a/src/Listener.cpp b/src/Listener.cpp index 974790f..0e20a61 100644 --- a/src/Listener.cpp +++ b/src/Listener.cpp @@ -1,12 +1,16 @@ #include "jbus/Listener.hpp" #include "jbus/Endpoint.hpp" +#define LOG_LISTENER 0 + namespace jbus { void Listener::listenerProc() { +#if LOG_LISTENER printf("JoyBus listener started\n"); +#endif net::IPAddress localhost("0.0.0.0"); bool dataBound = false; @@ -18,22 +22,34 @@ void Listener::listenerProc() if (!(dataBound = m_dataServer.openAndListen(localhost, DataPort))) { m_dataServer = net::Socket(false); +#if LOG_LISTENER printf("data open failed %s; will retry\n", strerror(errno)); +#endif sleep(1); } else + { +#if LOG_LISTENER printf("data listening on port %d\n", DataPort); +#endif + } } if (!clockBound) { if (!(clockBound = m_clockServer.openAndListen(localhost, ClockPort))) { m_clockServer = net::Socket(false); +#if LOG_LISTENER printf("clock open failed %s; will retry\n", strerror(errno)); +#endif sleep(1); } else + { +#if LOG_LISTENER printf("clock listening on port %d\n", ClockPort); +#endif + } } } @@ -44,9 +60,17 @@ void Listener::listenerProc() while (m_running && chan < 4) { if (m_dataServer.accept(acceptData, hostname) == net::Socket::EResult::OK) + { +#if LOG_LISTENER printf("accepted data connection from %s\n", hostname.c_str()); +#endif + } if (m_clockServer.accept(acceptClock, hostname) == net::Socket::EResult::OK) + { +#if LOG_LISTENER printf("accepted clock connection from %s\n", hostname.c_str()); +#endif + } if (acceptData && acceptClock) { std::unique_lock lk(m_queueLock); @@ -58,7 +82,9 @@ void Listener::listenerProc() m_dataServer.close(); m_clockServer.close(); +#if LOG_LISTENER printf("JoyBus listener stopped\n"); +#endif } void Listener::start() diff --git a/tools/joyboot.cpp b/tools/joyboot.cpp index d32091f..2f31641 100644 --- a/tools/joyboot.cpp +++ b/tools/joyboot.cpp @@ -1,4 +1,122 @@ +#include +#include "jbus/Listener.hpp" +#include "jbus/Endpoint.hpp" + +static void clientPadComplimentCheck(jbus::u8* buffer) +{ + jbus::u8 check = 0x19; + for (int i=0xa0 ; i<0xbd ; ++i) + check += buffer[i]; + buffer[0xbd] = -check; +} + +static jbus::EJoyReturn BootStatus = jbus::GBA_JOYBOOT_ERR_INVALID; +static void JoyBootDone(jbus::ThreadLocalEndpoint& endpoint, jbus::EJoyReturn status) +{ + BootStatus = status; +} + +static bool DonePoll(jbus::Endpoint& endpoint) +{ + jbus::u8 status; + if (endpoint.GBAReset(&status) == jbus::GBA_NOT_READY) + if (endpoint.GBAReset(&status) == jbus::GBA_NOT_READY) + return false; + + if (endpoint.GBAGetStatus(&status) == jbus::GBA_NOT_READY) + return false; + if (status != (jbus::GBA_JSTAT_PSF1 | jbus::GBA_JSTAT_SEND)) + return false; + + return true; +} + int main(int argc, char** argv) { + if (argc < 2) + { + printf("Usage: joyboot \n"); + return 1; + } + FILE* fp = fopen(argv[1], "rb"); + if (!fp) + { + fprintf(stderr, "Unable to open %s\n", argv[1]); + return 1; + } + + fseek(fp, 0, SEEK_END); + int fsize = ftell(fp); + fseek(fp, 0, SEEK_SET); + std::unique_ptr data(new jbus::u8[fsize]); + fread(data.get(), 1, fsize, fp); + fclose(fp); + if (fsize < 512) + { + fprintf(stderr, "%s must be at least 512 bytes\n", argv[1]); + return 1; + } + + clientPadComplimentCheck(data.get()); + + jbus::Initialize(); + printf("Listening for client\n"); + jbus::Listener listener; + listener.start(); + std::unique_ptr endpoint; + while (true) + { + jbus::s64 frameStart = jbus::GetGCTicks(); + endpoint = listener.accept(); + if (endpoint) + break; + jbus::s64 frameEnd = jbus::GetGCTicks(); + jbus::s64 passedTicks = frameEnd - frameStart; + jbus::s64 waitTicks = jbus::GetGCTicksPerSec() / 60 - passedTicks; + if (waitTicks > 0) + jbus::WaitGCTicks(waitTicks); + } + + printf("Waiting 4 sec\n"); + jbus::WaitGCTicks(jbus::GetGCTicksPerSec() * 4); + + jbus::u8 status; + if (endpoint->GBAJoyBootAsync(2, 2, data.get(), fsize, + &status, JoyBootDone) != jbus::GBA_READY) + { + fprintf(stderr, "Unable to start JoyBoot\n"); + return 1; + } + + jbus::s64 start = jbus::GetGCTicks(); + jbus::u8 percent = 0; + while (endpoint->GBAGetProcessStatus(&percent) == jbus::GBA_BUSY) + { + printf("\rUpload %d%%", percent); + fflush(stdout); + jbus::s64 curTime = jbus::GetGCTicks(); + jbus::s64 passedTicks = curTime - start; + if (passedTicks > jbus::GetGCTicksPerSec() * 5) + { + fprintf(stderr, "JoyBoot timeout\n"); + return 1; + } + jbus::WaitGCTicks(jbus::GetGCTicksPerSec() / 60); + } + printf("\nJoy Boot finished with %d status\n", status); + + while (!DonePoll(*endpoint)) + { + jbus::s64 curTime = jbus::GetGCTicks(); + jbus::s64 passedTicks = curTime - start; + if (passedTicks > jbus::GetGCTicksPerSec() * 10) + { + fprintf(stderr, "JoyBoot timeout\n"); + return 1; + } + jbus::WaitGCTicks(jbus::GetGCTicksPerSec() / 60); + } + + return 0; }