Add general-purpose JoyBoot utility

This commit is contained in:
Jack Andersen 2017-01-06 18:57:28 -10:00
parent ab163e856a
commit 52eae7421b
6 changed files with 195 additions and 38 deletions

View File

@ -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<void(EndpointLocal& endpoint, EJoyReturn status)>;
using FGBACallback = std::function<void(ThreadLocalEndpoint& endpoint, EJoyReturn status)>;
u64 GetGCTicks();
void WaitGCTicks(u64 ticks);

View File

@ -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);

View File

@ -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<sockaddr*>(&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;
}

View File

@ -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<std::mutex> 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;

View File

@ -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<std::mutex> 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()

View File

@ -1,4 +1,122 @@
#include <stdio.h>
#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 <client_pad.bin>\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<jbus::u8[]> 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<jbus::Endpoint> 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;
}