diff --git a/include/jbus/Common.hpp b/include/jbus/Common.hpp index 9c3de8d..6a40c3f 100644 --- a/include/jbus/Common.hpp +++ b/include/jbus/Common.hpp @@ -4,8 +4,7 @@ #include #include -namespace jbus -{ +namespace jbus { using s8 = int8_t; using u8 = uint8_t; @@ -24,114 +23,100 @@ using u64 = uint64_t; /* Type-sensitive byte swappers */ template -static inline T bswap16(T val) -{ +static inline T bswap16(T val) { #if __GNUC__ - return __builtin_bswap16(val); + return __builtin_bswap16(val); #elif _WIN32 - return _byteswap_ushort(val); + return _byteswap_ushort(val); #else - return (val = (val << 8) | ((val >> 8) & 0xFF)); + return (val = (val << 8) | ((val >> 8) & 0xFF)); #endif } template -static inline T bswap32(T val) -{ +static inline T bswap32(T val) { #if __GNUC__ - return __builtin_bswap32(val); + return __builtin_bswap32(val); #elif _WIN32 - return _byteswap_ulong(val); + return _byteswap_ulong(val); #else - val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16; - val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8; - return val; + val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16; + val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8; + return val; #endif } template -static inline T bswap64(T val) -{ +static inline T bswap64(T val) { #if __GNUC__ - return __builtin_bswap64(val); + return __builtin_bswap64(val); #elif _WIN32 - return _byteswap_uint64(val); + return _byteswap_uint64(val); #else - return ((val & 0xFF00000000000000ULL) >> 56) | - ((val & 0x00FF000000000000ULL) >> 40) | - ((val & 0x0000FF0000000000ULL) >> 24) | - ((val & 0x000000FF00000000ULL) >> 8) | - ((val & 0x00000000FF000000ULL) << 8) | - ((val & 0x0000000000FF0000ULL) << 24) | - ((val & 0x000000000000FF00ULL) << 40) | - ((val & 0x00000000000000FFULL) << 56); + return ((val & 0xFF00000000000000ULL) >> 56) | ((val & 0x00FF000000000000ULL) >> 40) | + ((val & 0x0000FF0000000000ULL) >> 24) | ((val & 0x000000FF00000000ULL) >> 8) | + ((val & 0x00000000FF000000ULL) << 8) | ((val & 0x0000000000FF0000ULL) << 24) | + ((val & 0x000000000000FF00ULL) << 40) | ((val & 0x00000000000000FFULL) << 56); #endif } - #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -static inline int16_t SBig(int16_t val) {return bswap16(val);} -static inline uint16_t SBig(uint16_t val) {return bswap16(val);} -static inline int32_t SBig(int32_t val) {return bswap32(val);} -static inline uint32_t SBig(uint32_t val) {return bswap32(val);} -static inline int64_t SBig(int64_t val) {return bswap64(val);} -static inline uint64_t SBig(uint64_t val) {return bswap64(val);} -static inline float SBig(float val) -{ - int32_t ival = bswap32(*((int32_t*)(&val))); - return *((float*)(&ival)); +static inline int16_t SBig(int16_t val) { return bswap16(val); } +static inline uint16_t SBig(uint16_t val) { return bswap16(val); } +static inline int32_t SBig(int32_t val) { return bswap32(val); } +static inline uint32_t SBig(uint32_t val) { return bswap32(val); } +static inline int64_t SBig(int64_t val) { return bswap64(val); } +static inline uint64_t SBig(uint64_t val) { return bswap64(val); } +static inline float SBig(float val) { + int32_t ival = bswap32(*((int32_t*)(&val))); + return *((float*)(&ival)); } -static inline double SBig(double val) -{ - int64_t ival = bswap64(*((int64_t*)(&val))); - return *((double*)(&ival)); +static inline double SBig(double val) { + int64_t ival = bswap64(*((int64_t*)(&val))); + return *((double*)(&ival)); } #ifndef SBIG -#define SBIG(q) ( ( (q) & 0x000000FF ) << 24 | ( (q) & 0x0000FF00 ) << 8 \ - | ( (q) & 0x00FF0000 ) >> 8 | ( (q) & 0xFF000000 ) >> 24 ) +#define SBIG(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24) #endif -static inline int16_t SLittle(int16_t val) {return val;} -static inline uint16_t SLittle(uint16_t val) {return val;} -static inline int32_t SLittle(int32_t val) {return val;} -static inline uint32_t SLittle(uint32_t val) {return val;} -static inline int64_t SLittle(int64_t val) {return val;} -static inline uint64_t SLittle(uint64_t val) {return val;} -static inline float SLittle(float val) {return val;} -static inline double SLittle(double val) {return val;} +static inline int16_t SLittle(int16_t val) { return val; } +static inline uint16_t SLittle(uint16_t val) { return val; } +static inline int32_t SLittle(int32_t val) { return val; } +static inline uint32_t SLittle(uint32_t val) { return val; } +static inline int64_t SLittle(int64_t val) { return val; } +static inline uint64_t SLittle(uint64_t val) { return val; } +static inline float SLittle(float val) { return val; } +static inline double SLittle(double val) { return val; } #ifndef SLITTLE #define SLITTLE(q) (q) #endif #else -static inline int16_t SLittle(int16_t val) {return bswap16(val);} -static inline uint16_t SLittle(uint16_t val) {return bswap16(val);} -static inline int32_t SLittle(int32_t val) {return bswap32(val);} -static inline uint32_t SLittle(uint32_t val) {return bswap32(val);} -static inline int64_t SLittle(int64_t val) {return bswap64(val);} -static inline uint64_t SLittle(uint64_t val) {return bswap64(val);} -static inline float SLittle(float val) -{ - int32_t ival = bswap32(*((int32_t*)(&val))); - return *((float*)(&ival)); +static inline int16_t SLittle(int16_t val) { return bswap16(val); } +static inline uint16_t SLittle(uint16_t val) { return bswap16(val); } +static inline int32_t SLittle(int32_t val) { return bswap32(val); } +static inline uint32_t SLittle(uint32_t val) { return bswap32(val); } +static inline int64_t SLittle(int64_t val) { return bswap64(val); } +static inline uint64_t SLittle(uint64_t val) { return bswap64(val); } +static inline float SLittle(float val) { + int32_t ival = bswap32(*((int32_t*)(&val))); + return *((float*)(&ival)); } -static inline double SLittle(double val) -{ - int64_t ival = bswap64(*((int64_t*)(&val))); - return *((double*)(&ival)); +static inline double SLittle(double val) { + int64_t ival = bswap64(*((int64_t*)(&val))); + return *((double*)(&ival)); } #ifndef SLITTLE -#define SLITTLE(q) ( ( (q) & 0x000000FF ) << 24 | ( (q) & 0x0000FF00 ) << 8 \ - | ( (q) & 0x00FF0000 ) >> 8 | ( (q) & 0xFF000000 ) >> 24 ) +#define SLITTLE(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24) #endif -static inline int16_t SBig(int16_t val) {return val;} -static inline uint16_t SBig(uint16_t val) {return val;} -static inline int32_t SBig(int32_t val) {return val;} -static inline uint32_t SBig(uint32_t val) {return val;} -static inline int64_t SBig(int64_t val) {return val;} -static inline uint64_t SBig(uint64_t val) {return val;} -static inline float SBig(float val) {return val;} -static inline double SBig(double val) {return val;} +static inline int16_t SBig(int16_t val) { return val; } +static inline uint16_t SBig(uint16_t val) { return val; } +static inline int32_t SBig(int32_t val) { return val; } +static inline uint32_t SBig(uint32_t val) { return val; } +static inline int64_t SBig(int64_t val) { return val; } +static inline uint64_t SBig(uint64_t val) { return val; } +static inline float SBig(float val) { return val; } +static inline double SBig(double val) { return val; } #ifndef SBIG #define SBIG(q) (q) #endif @@ -142,24 +127,22 @@ class ThreadLocalEndpoint; #endif -enum EJStatFlags -{ - GBA_JSTAT_MASK = 0x3a, - GBA_JSTAT_FLAGS_SHIFT = 4, - GBA_JSTAT_FLAGS_MASK = 0x30, - GBA_JSTAT_PSF1 = 0x20, - GBA_JSTAT_PSF0 = 0x10, - GBA_JSTAT_SEND = 0x08, - GBA_JSTAT_RECV = 0x02 +enum EJStatFlags { + GBA_JSTAT_MASK = 0x3a, + GBA_JSTAT_FLAGS_SHIFT = 4, + GBA_JSTAT_FLAGS_MASK = 0x30, + GBA_JSTAT_PSF1 = 0x20, + GBA_JSTAT_PSF0 = 0x10, + GBA_JSTAT_SEND = 0x08, + GBA_JSTAT_RECV = 0x02 }; -enum EJoyReturn -{ - GBA_READY = 0, - GBA_NOT_READY = 1, - GBA_BUSY = 2, - GBA_JOYBOOT_UNKNOWN_STATE = 3, - GBA_JOYBOOT_ERR_INVALID = 4 +enum EJoyReturn { + GBA_READY = 0, + GBA_NOT_READY = 1, + GBA_BUSY = 2, + GBA_JOYBOOT_UNKNOWN_STATE = 3, + GBA_JOYBOOT_ERR_INVALID = 4 }; /** @brief Standard callback for asynchronous jbus::Endpoint APIs. @@ -182,5 +165,4 @@ static constexpr u64 GetGCTicksPerSec() { return 486000000ull; } /** @brief Initialize platform specifics of JBus library */ void Initialize(); -} - +} // namespace jbus diff --git a/include/jbus/Endpoint.hpp b/include/jbus/Endpoint.hpp index 43c7d62..bda3d96 100644 --- a/include/jbus/Endpoint.hpp +++ b/include/jbus/Endpoint.hpp @@ -6,277 +6,257 @@ #include #include -namespace jbus -{ +namespace jbus { /** Main class for performing JoyBoot and subsequent JoyBus I/O operations. * Instances should be obtained though the jbus::Listener::accept method. */ -class Endpoint -{ - /** Self-contained class for solving Kawasedo's GBA BootROM challenge. - * GBA will boot client_pad.bin code on completion. - * - * This class shouldn't be used directly. JoyBoot operations are started - * via jbus::Endpoint::GBAJoyBootAsync. The JoyBoot status may be obtained - * via jbus::Endpoint::GBAGetProcessStatus. */ - class KawasedoChallenge - { - /** DSP-hosted public-key unwrap and initial message crypt - * Reference: https://github.com/dolphin-emu/dolphin/blob/master/Source/Core/Core/HW/DSPHLE/UCodes/GBA.cpp */ - struct DSPSecParms - { - /* Nonce challenge (first read from GBA, hence already little-endian) */ - u32 x0_gbaChallenge; +class Endpoint { + /** Self-contained class for solving Kawasedo's GBA BootROM challenge. + * GBA will boot client_pad.bin code on completion. + * + * This class shouldn't be used directly. JoyBoot operations are started + * via jbus::Endpoint::GBAJoyBootAsync. The JoyBoot status may be obtained + * via jbus::Endpoint::GBAGetProcessStatus. */ + class KawasedoChallenge { + /** DSP-hosted public-key unwrap and initial message crypt + * Reference: https://github.com/dolphin-emu/dolphin/blob/master/Source/Core/Core/HW/DSPHLE/UCodes/GBA.cpp */ + struct DSPSecParms { + /* Nonce challenge (first read from GBA, hence already little-endian) */ + u32 x0_gbaChallenge; - /* Palette of pulsing logo on GBA during transmission [0,6] */ - u32 x4_logoPalette; + /* Palette of pulsing logo on GBA during transmission [0,6] */ + u32 x4_logoPalette; - /* Speed and direction of palette interpolation [-4,4] */ - u32 x8_logoSpeed; + /* Speed and direction of palette interpolation [-4,4] */ + u32 x8_logoSpeed; - /* Length of JoyBoot program to upload */ - u32 xc_progLength; + /* Length of JoyBoot program to upload */ + u32 xc_progLength; - /* Unwrapped public key */ - u32 x20_key; + /* Unwrapped public key */ + u32 x20_key; - /* Message authentication code */ - u32 x24_authInitCode; + /* Message authentication code */ + u32 x24_authInitCode; - void ProcessGBACrypto(); - } xf8_dspHmac; + void ProcessGBACrypto(); + } xf8_dspHmac; - s32 x0_pColor; - s32 x4_pSpeed; - const u8* x8_progPtr; - u32 xc_progLen; - u8* x10_statusPtr; - FGBACallback x14_callback; - u8 x18_readBuf[4]; - u8 x1c_writeBuf[4]; - s32 x20_byteInWindow; - u64 x28_ticksAfterXf; - u32 x30_justStarted; - u32 x34_bytesSent; - u32 x38_crc; - u32 x3c_checkStore[7]; - s32 x58_currentKey; - s32 x5c_initMessage; - s32 x60_gameId; - u32 x64_totalBytes; - bool m_started = true; - bool m_initialized = false; + s32 x0_pColor; + s32 x4_pSpeed; + const u8* x8_progPtr; + u32 xc_progLen; + u8* x10_statusPtr; + FGBACallback x14_callback; + u8 x18_readBuf[4]; + u8 x1c_writeBuf[4]; + s32 x20_byteInWindow; + u64 x28_ticksAfterXf; + u32 x30_justStarted; + u32 x34_bytesSent; + u32 x38_crc; + u32 x3c_checkStore[7]; + s32 x58_currentKey; + s32 x5c_initMessage; + s32 x60_gameId; + u32 x64_totalBytes; + bool m_started = true; + bool m_initialized = false; - void _0Reset(ThreadLocalEndpoint& endpoint, EJoyReturn status); - void _1GetStatus(ThreadLocalEndpoint& endpoint, EJoyReturn status); - void _2ReadChallenge(ThreadLocalEndpoint& endpoint, EJoyReturn status); - void _3DSPCrypto(ThreadLocalEndpoint& endpoint, EJoyReturn status); - void _DSPCryptoInit(); - void _DSPCryptoDone(ThreadLocalEndpoint& endpoint); - void _4TransmitProgram(ThreadLocalEndpoint& endpoint, EJoyReturn status); - void _5StartBootPoll(ThreadLocalEndpoint& endpoint, EJoyReturn status); - void _6BootPoll(ThreadLocalEndpoint& endpoint, EJoyReturn status); - void _7BootAcknowledge(ThreadLocalEndpoint& endpoint, EJoyReturn status); - void _8BootDone(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void _0Reset(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void _1GetStatus(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void _2ReadChallenge(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void _3DSPCrypto(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void _DSPCryptoInit(); + void _DSPCryptoDone(ThreadLocalEndpoint& endpoint); + void _4TransmitProgram(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void _5StartBootPoll(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void _6BootPoll(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void _7BootAcknowledge(ThreadLocalEndpoint& endpoint, EJoyReturn status); + void _8BootDone(ThreadLocalEndpoint& endpoint, EJoyReturn status); - auto bindThis(void(KawasedoChallenge::*ptmf)(ThreadLocalEndpoint&, EJoyReturn)) - { - return std::bind(ptmf, this, std::placeholders::_1, std::placeholders::_2); - } - - public: - KawasedoChallenge() = default; - KawasedoChallenge(s32 paletteColor, s32 paletteSpeed, - const u8* programp, s32 length, u8* status, FGBACallback&& callback); - void start(Endpoint& endpoint); - bool started() const { return m_started; } - u8 percentComplete() const - { - if (!x64_totalBytes) - return 0; - return x34_bytesSent * 100 / x64_totalBytes; - } - bool isDone() const { return !x14_callback; } - operator bool() const { return m_initialized; } - }; - - friend class ThreadLocalEndpoint; - - enum EJoybusCmds - { - CMD_RESET = 0xff, - CMD_STATUS = 0x00, - CMD_READ = 0x14, - CMD_WRITE = 0x15 - }; - - static const u64 BITS_PER_SECOND = 115200; - static const u64 BYTES_PER_SECOND = BITS_PER_SECOND / 8; - - net::Socket m_dataSocket; - net::Socket m_clockSocket; - std::thread m_transferThread; - std::mutex m_syncLock; - std::condition_variable m_syncCv; - std::condition_variable m_issueCv; - KawasedoChallenge m_joyBoot; - FGBACallback m_callback; - u8 m_buffer[5]; - u8* m_readDstPtr = nullptr; - u8* m_statusPtr = nullptr; - u64 m_lastGCTick = 0; - u8 m_lastCmd = 0; - u8 m_chan; - bool m_booted = false; - bool m_cmdIssued = false; - bool m_running = true; - - void clockSync(); - void send(const u8* buffer); - size_t receive(u8* buffer); - size_t runBuffer(u8* buffer, std::unique_lock& lk); - bool idleGetStatus(std::unique_lock& lk); - void transferProc(); - void transferWakeup(ThreadLocalEndpoint& endpoint, u8 status); - - auto bindSync() - { - return std::bind(&Endpoint::transferWakeup, this, - std::placeholders::_1, std::placeholders::_2); + auto bindThis(void (KawasedoChallenge::*ptmf)(ThreadLocalEndpoint&, EJoyReturn)) { + return std::bind(ptmf, this, std::placeholders::_1, std::placeholders::_2); } + public: + KawasedoChallenge() = default; + KawasedoChallenge(s32 paletteColor, s32 paletteSpeed, const u8* programp, s32 length, u8* status, + FGBACallback&& callback); + void start(Endpoint& endpoint); + bool started() const { return m_started; } + u8 percentComplete() const { + if (!x64_totalBytes) + return 0; + return x34_bytesSent * 100 / x64_totalBytes; + } + bool isDone() const { return !x14_callback; } + operator bool() const { return m_initialized; } + }; + + friend class ThreadLocalEndpoint; + + enum EJoybusCmds { CMD_RESET = 0xff, CMD_STATUS = 0x00, CMD_READ = 0x14, CMD_WRITE = 0x15 }; + + static const u64 BITS_PER_SECOND = 115200; + static const u64 BYTES_PER_SECOND = BITS_PER_SECOND / 8; + + net::Socket m_dataSocket; + net::Socket m_clockSocket; + std::thread m_transferThread; + std::mutex m_syncLock; + std::condition_variable m_syncCv; + std::condition_variable m_issueCv; + KawasedoChallenge m_joyBoot; + FGBACallback m_callback; + u8 m_buffer[5]; + u8* m_readDstPtr = nullptr; + u8* m_statusPtr = nullptr; + u64 m_lastGCTick = 0; + u8 m_lastCmd = 0; + u8 m_chan; + bool m_booted = false; + bool m_cmdIssued = false; + bool m_running = true; + + void clockSync(); + void send(const u8* buffer); + size_t receive(u8* buffer); + size_t runBuffer(u8* buffer, std::unique_lock& lk); + bool idleGetStatus(std::unique_lock& lk); + void transferProc(); + void transferWakeup(ThreadLocalEndpoint& endpoint, u8 status); + + auto bindSync() { return std::bind(&Endpoint::transferWakeup, this, std::placeholders::_1, std::placeholders::_2); } + public: - /** @brief Request stop of I/O thread and block until joined. - * Further use of this Endpoint will return GBA_NOT_READY. - * The destructor calls this implicitly. */ - void stop(); + /** @brief Request stop of I/O thread and block until joined. + * Further use of this Endpoint will return GBA_NOT_READY. + * The destructor calls this implicitly. */ + void stop(); - /** @brief Get status of last asynchronous operation. - * @param percentOut Reference to output transfer percent of GBAJoyBootAsync. - * @return GBA_READY when idle, or GBA_BUSY when operation in progress. */ - EJoyReturn GBAGetProcessStatus(u8& percentOut); + /** @brief Get status of last asynchronous operation. + * @param percentOut Reference to output transfer percent of GBAJoyBootAsync. + * @return GBA_READY when idle, or GBA_BUSY when operation in progress. */ + EJoyReturn GBAGetProcessStatus(u8& percentOut); - /** @brief Get JOYSTAT register from GBA asynchronously. - * @param status Destination pointer for EJStatFlags. - * @param callback Functor to execute when operation completes. - * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ - EJoyReturn GBAGetStatusAsync(u8* status, FGBACallback&& callback); + /** @brief Get JOYSTAT register from GBA asynchronously. + * @param status Destination pointer for EJStatFlags. + * @param callback Functor to execute when operation completes. + * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ + EJoyReturn GBAGetStatusAsync(u8* status, FGBACallback&& callback); - /** @brief Get JOYSTAT register from GBA synchronously. - * @param status Destination pointer for EJStatFlags. - * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ - EJoyReturn GBAGetStatus(u8* status); + /** @brief Get JOYSTAT register from GBA synchronously. + * @param status Destination pointer for EJStatFlags. + * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ + EJoyReturn GBAGetStatus(u8* status); - /** @brief Send RESET command to GBA asynchronously. - * @param status Destination pointer for EJStatFlags. - * @param callback Functor to execute when operation completes. - * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ - EJoyReturn GBAResetAsync(u8* status, FGBACallback&& callback); + /** @brief Send RESET command to GBA asynchronously. + * @param status Destination pointer for EJStatFlags. + * @param callback Functor to execute when operation completes. + * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ + EJoyReturn GBAResetAsync(u8* status, FGBACallback&& callback); - /** @brief Send RESET command to GBA synchronously. - * @param status Destination pointer for EJStatFlags. - * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ - EJoyReturn GBAReset(u8* status); + /** @brief Send RESET command to GBA synchronously. + * @param status Destination pointer for EJStatFlags. + * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ + EJoyReturn GBAReset(u8* status); - /** @brief Send READ command to GBA asynchronously. - * @param dst Destination pointer for 4-byte packet of data. - * @param status Destination pointer for EJStatFlags. - * @param callback Functor to execute when operation completes. - * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ - EJoyReturn GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback); + /** @brief Send READ command to GBA asynchronously. + * @param dst Destination pointer for 4-byte packet of data. + * @param status Destination pointer for EJStatFlags. + * @param callback Functor to execute when operation completes. + * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ + EJoyReturn GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback); - /** @brief Send READ command to GBA synchronously. - * @param dst Destination pointer for 4-byte packet of data. - * @param status Destination pointer for EJStatFlags. - * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ - EJoyReturn GBARead(u8* dst, u8* status); + /** @brief Send READ command to GBA synchronously. + * @param dst Destination pointer for 4-byte packet of data. + * @param status Destination pointer for EJStatFlags. + * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ + EJoyReturn GBARead(u8* dst, u8* status); - /** @brief Send WRITE command to GBA asynchronously. - * @param src Source pointer for 4-byte packet of data. It is not required to keep resident. - * @param status Destination pointer for EJStatFlags. - * @param callback Functor to execute when operation completes. - * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ - EJoyReturn GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback); + /** @brief Send WRITE command to GBA asynchronously. + * @param src Source pointer for 4-byte packet of data. It is not required to keep resident. + * @param status Destination pointer for EJStatFlags. + * @param callback Functor to execute when operation completes. + * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ + EJoyReturn GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback); - /** @brief Send WRITE command to GBA synchronously. - * @param src Source pointer for 4-byte packet of data. It is not required to keep resident. - * @param status Destination pointer for EJStatFlags. - * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ - EJoyReturn GBAWrite(const u8* src, u8* status); + /** @brief Send WRITE command to GBA synchronously. + * @param src Source pointer for 4-byte packet of data. It is not required to keep resident. + * @param status Destination pointer for EJStatFlags. + * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ + EJoyReturn GBAWrite(const u8* src, u8* status); - /** @brief Initiate JoyBoot sequence on this endpoint. - * @param paletteColor Palette for displaying logo in ROM header [0,6]. - * @param paletteSpeed Palette interpolation speed for displaying logo in ROM header [-4,4]. - * @param programp Pointer to program ROM data. - * @param length Length of program ROM data. - * @param status Destination pointer for EJStatFlags. - * @param callback Functor to execute when operation completes. - * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ - EJoyReturn GBAJoyBootAsync(s32 paletteColor, s32 paletteSpeed, - const u8* programp, s32 length, u8* status, - FGBACallback&& callback); + /** @brief Initiate JoyBoot sequence on this endpoint. + * @param paletteColor Palette for displaying logo in ROM header [0,6]. + * @param paletteSpeed Palette interpolation speed for displaying logo in ROM header [-4,4]. + * @param programp Pointer to program ROM data. + * @param length Length of program ROM data. + * @param status Destination pointer for EJStatFlags. + * @param callback Functor to execute when operation completes. + * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ + EJoyReturn GBAJoyBootAsync(s32 paletteColor, s32 paletteSpeed, const u8* programp, s32 length, u8* status, + FGBACallback&& callback); - /** @brief Get virtual SI channel assigned to this endpoint. - * @return SI channel [0,3] */ - unsigned getChan() const { return m_chan; } + /** @brief Get virtual SI channel assigned to this endpoint. + * @return SI channel [0,3] */ + unsigned getChan() const { return m_chan; } - /** @brief Set virtual SI channel assigned to this endpoint. - * @param chan SI channel [0,3] */ - void setChan(unsigned chan) - { - if (chan > 3) - chan = 3; - m_chan = chan; - } + /** @brief Set virtual SI channel assigned to this endpoint. + * @param chan SI channel [0,3] */ + void setChan(unsigned chan) { + if (chan > 3) + chan = 3; + m_chan = chan; + } - /** @brief Get connection status of this endpoint - * @return true if connected */ - bool connected() const { return m_running; } + /** @brief Get connection status of this endpoint + * @return true if connected */ + bool connected() const { return m_running; } - Endpoint(u8 chan, net::Socket&& data, net::Socket&& clock); - ~Endpoint(); + Endpoint(u8 chan, net::Socket&& data, net::Socket&& clock); + ~Endpoint(); }; /** Lockless wrapper interface for jbus::Endpoint. * This class is constructed internally and supplied as a callback argument. * It should not be constructed directly. */ -class ThreadLocalEndpoint -{ - friend class Endpoint; - Endpoint& m_ep; - ThreadLocalEndpoint(Endpoint& ep) : m_ep(ep) {} +class ThreadLocalEndpoint { + friend class Endpoint; + Endpoint& m_ep; + ThreadLocalEndpoint(Endpoint& ep) : m_ep(ep) {} public: - /** @brief Get JOYSTAT register from GBA asynchronously. - * @param status Destination pointer for EJStatFlags. - * @param callback Functor to execute when operation completes. - * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ - EJoyReturn GBAGetStatusAsync(u8* status, FGBACallback&& callback); + /** @brief Get JOYSTAT register from GBA asynchronously. + * @param status Destination pointer for EJStatFlags. + * @param callback Functor to execute when operation completes. + * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ + EJoyReturn GBAGetStatusAsync(u8* status, FGBACallback&& callback); - /** @brief Send RESET command to GBA asynchronously. - * @param status Destination pointer for EJStatFlags. - * @param callback Functor to execute when operation completes. - * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ - EJoyReturn GBAResetAsync(u8* status, FGBACallback&& callback); + /** @brief Send RESET command to GBA asynchronously. + * @param status Destination pointer for EJStatFlags. + * @param callback Functor to execute when operation completes. + * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ + EJoyReturn GBAResetAsync(u8* status, FGBACallback&& callback); - /** @brief Send READ command to GBA asynchronously. - * @param dst Destination pointer for 4-byte packet of data. - * @param status Destination pointer for EJStatFlags. - * @param callback Functor to execute when operation completes. - * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ - EJoyReturn GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback); + /** @brief Send READ command to GBA asynchronously. + * @param dst Destination pointer for 4-byte packet of data. + * @param status Destination pointer for EJStatFlags. + * @param callback Functor to execute when operation completes. + * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ + EJoyReturn GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback); - /** @brief Send WRITE command to GBA asynchronously. - * @param src Source pointer for 4-byte packet of data. It is not required to keep resident. - * @param status Destination pointer for EJStatFlags. - * @param callback Functor to execute when operation completes. - * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ - EJoyReturn GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback); + /** @brief Send WRITE command to GBA asynchronously. + * @param src Source pointer for 4-byte packet of data. It is not required to keep resident. + * @param status Destination pointer for EJStatFlags. + * @param callback Functor to execute when operation completes. + * @return GBA_READY if submitted, or GBA_NOT_READY if another operation in progress. */ + EJoyReturn GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback); - /** @brief Get virtual SI channel assigned to this endpoint. - * @return SI channel */ - int getChan() const { return m_ep.getChan(); } + /** @brief Get virtual SI channel assigned to this endpoint. + * @return SI channel */ + int getChan() const { return m_ep.getChan(); } }; -} - +} // namespace jbus diff --git a/include/jbus/Listener.hpp b/include/jbus/Listener.hpp index d1de3d2..bbdee65 100644 --- a/include/jbus/Listener.hpp +++ b/include/jbus/Listener.hpp @@ -6,37 +6,34 @@ #include #include -namespace jbus -{ +namespace jbus { /** Server interface for accepting incoming connections from GBA emulator instances. */ -class Listener -{ - net::Socket m_dataServer = {false}; - net::Socket m_clockServer = {false}; - std::thread m_listenerThread; - std::mutex m_queueLock; - std::queue> m_endpointQueue; - bool m_running = false; +class Listener { + net::Socket m_dataServer = {false}; + net::Socket m_clockServer = {false}; + std::thread m_listenerThread; + std::mutex m_queueLock; + std::queue> m_endpointQueue; + bool m_running = false; - static const uint32_t DataPort = 0xd6ba; - static const uint32_t ClockPort = 0xc10c; + static const uint32_t DataPort = 0xd6ba; + static const uint32_t ClockPort = 0xc10c; - void listenerProc(); + void listenerProc(); public: - /** @brief Start listener thread. */ - void start(); + /** @brief Start listener thread. */ + void start(); - /** @brief Request stop of listener thread and block until joined. */ - void stop(); + /** @brief Request stop of listener thread and block until joined. */ + void stop(); - /** @brief Pop jbus::Endpoint off Listener's queue. - * @return Endpoint instance, ready to issue commands. */ - std::unique_ptr accept(); + /** @brief Pop jbus::Endpoint off Listener's queue. + * @return Endpoint instance, ready to issue commands. */ + std::unique_ptr accept(); - ~Listener(); + ~Listener(); }; -} - +} // namespace jbus diff --git a/include/jbus/Socket.hpp b/include/jbus/Socket.hpp index 57d3146..86df640 100644 --- a/include/jbus/Socket.hpp +++ b/include/jbus/Socket.hpp @@ -14,89 +14,71 @@ typedef UINT_PTR SOCKET; struct sockaddr_in; -namespace jbus::net -{ +namespace jbus::net { /** IP address class derived from SFML */ -class IPAddress -{ - uint32_t m_address = 0; - bool m_valid = false; +class IPAddress { + uint32_t m_address = 0; + bool m_valid = false; - void resolve(const std::string& address); + void resolve(const std::string& address); public: - IPAddress(const std::string& address) - { - resolve(address); - } + IPAddress(const std::string& address) { resolve(address); } - uint32_t toInteger() const; - operator bool() const { return m_valid; } + uint32_t toInteger() const; + operator bool() const { return m_valid; } }; /** Server-oriented TCP socket class derived from SFML */ -class Socket -{ +class Socket { #ifndef _WIN32 - using SocketTp = int; + using SocketTp = int; #else - using SocketTp = SOCKET; + using SocketTp = SOCKET; #endif - SocketTp m_socket = -1; - bool m_isBlocking; + SocketTp m_socket = -1; + bool m_isBlocking; - bool openSocket(); - void setRemoteSocket(int remSocket); + bool openSocket(); + void setRemoteSocket(int remSocket); public: - enum class EResult - { - OK, - Error, - Busy - }; + enum class EResult { OK, Error, Busy }; #ifdef _WIN32 - static EResult LastWSAError(); + static EResult LastWSAError(); #endif - Socket(bool blocking) - : m_isBlocking(blocking) {} - ~Socket() { close(); } + Socket(bool blocking) : m_isBlocking(blocking) {} + ~Socket() { close(); } - Socket(const Socket& other) = delete; - Socket& operator=(const Socket& other) = delete; - Socket(Socket&& other) - : m_socket(other.m_socket), m_isBlocking(other.m_isBlocking) - { - other.m_socket = -1; - } - Socket& operator=(Socket&& other) - { - close(); - m_socket = other.m_socket; - other.m_socket = -1; - m_isBlocking = other.m_isBlocking; - return *this; - } + Socket(const Socket& other) = delete; + Socket& operator=(const Socket& other) = delete; + Socket(Socket&& other) : m_socket(other.m_socket), m_isBlocking(other.m_isBlocking) { other.m_socket = -1; } + Socket& operator=(Socket&& other) { + close(); + m_socket = other.m_socket; + other.m_socket = -1; + m_isBlocking = other.m_isBlocking; + return *this; + } - void setBlocking(bool blocking); - bool isOpen() const { return m_socket != -1; } - bool openAndListen(const IPAddress& address, uint32_t port); - EResult accept(Socket& remoteSocketOut, sockaddr_in& fromAddress); - EResult accept(Socket& remoteSocketOut); - EResult accept(Socket& remoteSocketOut, std::string& fromHostname); - void close(); - EResult send(const void* buf, size_t len, size_t& transferred); - EResult send(const void* buf, size_t len); - EResult recv(void* buf, size_t len, size_t& transferred); - EResult recv(void* buf, size_t len); + void setBlocking(bool blocking); + bool isOpen() const { return m_socket != -1; } + bool openAndListen(const IPAddress& address, uint32_t port); + EResult accept(Socket& remoteSocketOut, sockaddr_in& fromAddress); + EResult accept(Socket& remoteSocketOut); + EResult accept(Socket& remoteSocketOut, std::string& fromHostname); + void close(); + EResult send(const void* buf, size_t len, size_t& transferred); + EResult send(const void* buf, size_t len); + EResult recv(void* buf, size_t len, size_t& transferred); + EResult recv(void* buf, size_t len); - operator bool() const { return isOpen(); } + operator bool() const { return isOpen(); } - SocketTp GetInternalSocket() const { return m_socket; } + SocketTp GetInternalSocket() const { return m_socket; } }; -} - +} // namespace jbus::net diff --git a/include/jbus/optional.hpp b/include/jbus/optional.hpp index 2955d04..14eb2ef 100644 --- a/include/jbus/optional.hpp +++ b/include/jbus/optional.hpp @@ -7,151 +7,148 @@ // The idea and interface is based on Boost.Optional library // authored by Fernando Luis Cacciola Carballal -# ifndef ___OPTIONAL_HPP___ -# define ___OPTIONAL_HPP___ +#ifndef ___OPTIONAL_HPP___ +#define ___OPTIONAL_HPP___ -# include -# include -# include -# include -# include -# include -# include "athena/Global.hpp" +#include +#include +#include +#include +#include +#include +#include "athena/Global.hpp" -# define TR2_OPTIONAL_REQUIRES(...) typename enable_if<__VA_ARGS__::value, bool>::type = false +#define TR2_OPTIONAL_REQUIRES(...) typename enable_if<__VA_ARGS__::value, bool>::type = false -# if defined __GNUC__ // NOTE: GNUC is also defined for Clang -# if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 8) -# define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ -# elif (__GNUC__ > 4) -# define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ -# endif +#if defined __GNUC__ // NOTE: GNUC is also defined for Clang +#if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 8) +#define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ +#elif (__GNUC__ > 4) +#define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ +#endif # -# if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7) -# define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___ -# elif (__GNUC__ > 4) -# define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___ -# endif +#if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7) +#define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___ +#elif (__GNUC__ > 4) +#define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___ +#endif # -# if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && (__GNUC_PATCHLEVEL__ >= 1) -# define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ -# elif (__GNUC__ == 4) && (__GNUC_MINOR__ >= 9) -# define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ -# elif (__GNUC__ > 4) -# define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ -# endif -# endif +#if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && (__GNUC_PATCHLEVEL__ >= 1) +#define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ +#elif (__GNUC__ == 4) && (__GNUC_MINOR__ >= 9) +#define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ +#elif (__GNUC__ > 4) +#define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ +#endif +#endif # -# if defined __clang_major__ -# if (__clang_major__ == 3 && __clang_minor__ >= 5) -# define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ -# elif (__clang_major__ > 3) -# define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ -# endif -# if defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ -# define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ -# elif (__clang_major__ == 3 && __clang_minor__ == 4 && __clang_patchlevel__ >= 2) -# define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ -# endif -# endif +#if defined __clang_major__ +#if (__clang_major__ == 3 && __clang_minor__ >= 5) +#define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ +#elif (__clang_major__ > 3) +#define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ +#endif +#if defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ +#define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ +#elif (__clang_major__ == 3 && __clang_minor__ == 4 && __clang_patchlevel__ >= 2) +#define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ +#endif +#endif # -# if defined _MSC_VER -# if (_MSC_VER >= 1900) -# define TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ -# endif -# endif +#if defined _MSC_VER +#if (_MSC_VER >= 1900) +#define TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ +#endif +#endif -# if defined __clang__ -# if (__clang_major__ > 2) || (__clang_major__ == 2) && (__clang_minor__ >= 9) -# define OPTIONAL_HAS_THIS_RVALUE_REFS 1 -# else -# define OPTIONAL_HAS_THIS_RVALUE_REFS 0 -# endif -# elif defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ -# define OPTIONAL_HAS_THIS_RVALUE_REFS 1 -# elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ -# define OPTIONAL_HAS_THIS_RVALUE_REFS 1 -# else -# define OPTIONAL_HAS_THIS_RVALUE_REFS 0 -# endif +#if defined __clang__ +#if (__clang_major__ > 2) || (__clang_major__ == 2) && (__clang_minor__ >= 9) +#define OPTIONAL_HAS_THIS_RVALUE_REFS 1 +#else +#define OPTIONAL_HAS_THIS_RVALUE_REFS 0 +#endif +#elif defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ +#define OPTIONAL_HAS_THIS_RVALUE_REFS 1 +#elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ +#define OPTIONAL_HAS_THIS_RVALUE_REFS 1 +#else +#define OPTIONAL_HAS_THIS_RVALUE_REFS 0 +#endif +#if defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ +#define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 1 +#define OPTIONAL_CONSTEXPR_INIT_LIST constexpr +#else +#define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 0 +#define OPTIONAL_CONSTEXPR_INIT_LIST +#endif -# if defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___ -# define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 1 -# define OPTIONAL_CONSTEXPR_INIT_LIST constexpr -# else -# define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 0 -# define OPTIONAL_CONSTEXPR_INIT_LIST -# endif +#if defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ && (defined __cplusplus) && (__cplusplus != 201103L) +#define OPTIONAL_HAS_MOVE_ACCESSORS 1 +#else +#define OPTIONAL_HAS_MOVE_ACCESSORS 0 +#endif -# if defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ && (defined __cplusplus) && (__cplusplus != 201103L) -# define OPTIONAL_HAS_MOVE_ACCESSORS 1 -# else -# define OPTIONAL_HAS_MOVE_ACCESSORS 0 -# endif +#// In C++11 constexpr implies const, so we need to make non-const members also non-constexpr +#if (defined __cplusplus) && (__cplusplus == 201103L) +#define OPTIONAL_MUTABLE_CONSTEXPR +#else +#define OPTIONAL_MUTABLE_CONSTEXPR constexpr +#endif -# // In C++11 constexpr implies const, so we need to make non-const members also non-constexpr -# if (defined __cplusplus) && (__cplusplus == 201103L) -# define OPTIONAL_MUTABLE_CONSTEXPR -# else -# define OPTIONAL_MUTABLE_CONSTEXPR constexpr -# endif +namespace std { -namespace std{ - -namespace experimental{ +namespace experimental { // BEGIN workaround for missing is_trivially_destructible -# if defined TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ - // leave it: it is already there -# elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ - // leave it: it is already there -# elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ - // leave it: it is already there -# elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS - // leave it: the user doesn't want it -# else - template - using is_trivially_destructible = std::has_trivial_destructor; -# endif +#if defined TR2_OPTIONAL_GCC_4_8_AND_HIGHER___ +// leave it: it is already there +#elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ +// leave it: it is already there +#elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ +// leave it: it is already there +#elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS +// leave it: the user doesn't want it +#else +template +using is_trivially_destructible = std::has_trivial_destructor; +#endif // END workaround for missing is_trivially_destructible -# if (defined TR2_OPTIONAL_GCC_4_7_AND_HIGHER___) - // leave it; our metafunctions are already defined. -# elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ - // leave it; our metafunctions are already defined. -# elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ - // leave it: it is already there -# elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS - // leave it: the user doesn't want it -# else - +#if (defined TR2_OPTIONAL_GCC_4_7_AND_HIGHER___) +// leave it; our metafunctions are already defined. +#elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_ +// leave it; our metafunctions are already defined. +#elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___ +// leave it: it is already there +#elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS +// leave it: the user doesn't want it +#else // workaround for missing traits in GCC and CLANG template -struct is_nothrow_move_constructible -{ +struct is_nothrow_move_constructible { constexpr static bool value = std::is_nothrow_constructible::value; }; - template -struct is_assignable -{ +struct is_assignable { template - constexpr static bool has_assign(...) { return false; } + constexpr static bool has_assign(...) { + return false; + } - template () = std::declval(), true)) > + template () = std::declval(), true))> // the comma operator is necessary for the cases where operator= returns void - constexpr static bool has_assign(bool) { return true; } + constexpr static bool has_assign(bool) { + return true; + } constexpr static bool value = has_assign(true); }; - template -struct is_nothrow_move_assignable -{ +struct is_nothrow_move_assignable { template struct has_nothrow_move_assign { constexpr static bool value = false; @@ -159,102 +156,96 @@ struct is_nothrow_move_assignable template struct has_nothrow_move_assign { - constexpr static bool value = noexcept( std::declval() = std::declval() ); + constexpr static bool value = noexcept(std::declval() = std::declval()); }; constexpr static bool value = has_nothrow_move_assign::value>::value; }; // end workaround - -# endif - - +#endif // 20.5.4, optional for object types -template class optional; +template +class optional; // 20.5.5, optional for lvalue reference types -template class optional; - +template +class optional; // workaround: std utility functions aren't constexpr yet -template inline constexpr T&& constexpr_forward(typename std::remove_reference::type& t) noexcept -{ +template +inline constexpr T&& constexpr_forward(typename std::remove_reference::type& t) noexcept { return static_cast(t); } -template inline constexpr T&& constexpr_forward(typename std::remove_reference::type&& t) noexcept -{ - static_assert(!std::is_lvalue_reference::value, "!!"); - return static_cast(t); +template +inline constexpr T&& constexpr_forward(typename std::remove_reference::type&& t) noexcept { + static_assert(!std::is_lvalue_reference::value, "!!"); + return static_cast(t); } -template inline constexpr typename std::remove_reference::type&& constexpr_move(T&& t) noexcept -{ - return static_cast::type&&>(t); +template +inline constexpr typename std::remove_reference::type&& constexpr_move(T&& t) noexcept { + return static_cast::type&&>(t); } - #if defined NDEBUG -# define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) (EXPR) +#define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) (EXPR) #else -# define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) ((CHECK) ? (EXPR) : ([]{assert(!#CHECK);}(), (EXPR))) +#define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) ((CHECK) ? (EXPR) : ([] { assert(!#CHECK); }(), (EXPR))) #endif - -namespace detail_ -{ +namespace detail_ { // static_addressof: a constexpr version of addressof template -struct has_overloaded_addressof -{ +struct has_overloaded_addressof { template - constexpr static bool has_overload(...) { return false; } + constexpr static bool has_overload(...) { + return false; + } - template ().operator&()) > - constexpr static bool has_overload(bool) { return true; } + template ().operator&())> + constexpr static bool has_overload(bool) { + return true; + } constexpr static bool value = has_overload(true); }; template )> -constexpr T* static_addressof(T& ref) -{ +constexpr T* static_addressof(T& ref) { return &ref; } template )> -T* static_addressof(T& ref) -{ +T* static_addressof(T& ref) { return std::addressof(ref); } - // the call to convert(b) has return type A and converts b to type A iff b decltype(b) is implicitly convertible to A template -U convert(U v) { return v; } +U convert(U v) { + return v; +} -} // namespace detail - - -constexpr struct trivial_init_t{} trivial_init{}; +} // namespace detail_ +constexpr struct trivial_init_t { +} trivial_init{}; // 20.5.6, In-place construction -constexpr struct in_place_t{} in_place{}; - +constexpr struct in_place_t { +} in_place{}; // 20.5.7, Disengaged state indicator -struct nullopt_t -{ - struct init{}; - constexpr explicit nullopt_t(init){} +struct nullopt_t { + struct init {}; + constexpr explicit nullopt_t(init) {} }; constexpr nullopt_t nullopt{nullopt_t::init()}; - // 20.5.8, class bad_optional_access class bad_optional_access : public logic_error { public: @@ -262,132 +253,120 @@ public: explicit bad_optional_access(const char* what_arg) : logic_error{what_arg} {} }; - template -union storage_t -{ +union storage_t { unsigned char dummy_; T value_; - constexpr storage_t( trivial_init_t ) noexcept : dummy_() {}; + constexpr storage_t(trivial_init_t) noexcept : dummy_(){}; template - constexpr storage_t( Args&&... args ) : value_(constexpr_forward(args)...) {} + constexpr storage_t(Args&&... args) : value_(constexpr_forward(args)...) {} - ~storage_t(){} -}; - - -template -union constexpr_storage_t -{ - unsigned char dummy_; - T value_; - - constexpr constexpr_storage_t( trivial_init_t ) noexcept : dummy_() {}; - - template - constexpr constexpr_storage_t( Args&&... args ) : value_(constexpr_forward(args)...) {} - - ~constexpr_storage_t() = default; -}; - - -template -struct optional_base -{ - bool init_; - storage_t storage_; - - constexpr optional_base() noexcept : init_(false), storage_(trivial_init) {}; - - explicit constexpr optional_base(const T& v) : init_(true), storage_(v) {} - - explicit constexpr optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {} - - template explicit optional_base(in_place_t, Args&&... args) - : init_(true), storage_(constexpr_forward(args)...) {} - - template >)> - explicit optional_base(in_place_t, std::initializer_list il, Args&&... args) - : init_(true), storage_(il, std::forward(args)...) {} - - ~optional_base() { if (init_) storage_.value_.T::~T(); } -}; - - -template -struct constexpr_optional_base -{ - bool init_; - constexpr_storage_t storage_; - - constexpr constexpr_optional_base() noexcept : init_(false), storage_(trivial_init) {}; - - explicit constexpr constexpr_optional_base(const T& v) : init_(true), storage_(v) {} - - explicit constexpr constexpr_optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {} - - template explicit constexpr constexpr_optional_base(in_place_t, Args&&... args) - : init_(true), storage_(constexpr_forward(args)...) {} - - template >)> - OPTIONAL_CONSTEXPR_INIT_LIST explicit constexpr_optional_base(in_place_t, std::initializer_list il, Args&&... args) - : init_(true), storage_(il, std::forward(args)...) {} - - ~constexpr_optional_base() = default; + ~storage_t() {} }; template -using OptionalBase = typename std::conditional< - is_trivially_destructible::value, - constexpr_optional_base, - optional_base ->::type; +union constexpr_storage_t { + unsigned char dummy_; + T value_; + constexpr constexpr_storage_t(trivial_init_t) noexcept : dummy_(){}; + template + constexpr constexpr_storage_t(Args&&... args) : value_(constexpr_forward(args)...) {} + + ~constexpr_storage_t() = default; +}; template -class optional : private OptionalBase -{ - static_assert( !std::is_same::type, nullopt_t>::value, "bad T" ); - static_assert( !std::is_same::type, in_place_t>::value, "bad T" ); +struct optional_base { + bool init_; + storage_t storage_; + constexpr optional_base() noexcept : init_(false), storage_(trivial_init){}; + + explicit constexpr optional_base(const T& v) : init_(true), storage_(v) {} + + explicit constexpr optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {} + + template + explicit optional_base(in_place_t, Args&&... args) : init_(true), storage_(constexpr_forward(args)...) {} + + template >)> + explicit optional_base(in_place_t, std::initializer_list il, Args&&... args) + : init_(true), storage_(il, std::forward(args)...) {} + + ~optional_base() { + if (init_) + storage_.value_.T::~T(); + } +}; + +template +struct constexpr_optional_base { + bool init_; + constexpr_storage_t storage_; + + constexpr constexpr_optional_base() noexcept : init_(false), storage_(trivial_init){}; + + explicit constexpr constexpr_optional_base(const T& v) : init_(true), storage_(v) {} + + explicit constexpr constexpr_optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {} + + template + explicit constexpr constexpr_optional_base(in_place_t, Args&&... args) + : init_(true), storage_(constexpr_forward(args)...) {} + + template >)> + OPTIONAL_CONSTEXPR_INIT_LIST explicit constexpr_optional_base(in_place_t, std::initializer_list il, Args&&... args) + : init_(true), storage_(il, std::forward(args)...) {} + + ~constexpr_optional_base() = default; +}; + +template +using OptionalBase = + typename std::conditional::value, constexpr_optional_base, optional_base>::type; + +template +class optional : private OptionalBase { + static_assert(!std::is_same::type, nullopt_t>::value, "bad T"); + static_assert(!std::is_same::type, in_place_t>::value, "bad T"); constexpr bool initialized() const noexcept { return OptionalBase::init_; } - T* dataptr() { return std::addressof(OptionalBase::storage_.value_); } + T* dataptr() { return std::addressof(OptionalBase::storage_.value_); } constexpr const T* dataptr() const { return detail_::static_addressof(OptionalBase::storage_.value_); } -# if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 +#if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 constexpr const T& contained_val() const& { return OptionalBase::storage_.value_; } -# if OPTIONAL_HAS_MOVE_ACCESSORS == 1 +#if OPTIONAL_HAS_MOVE_ACCESSORS == 1 OPTIONAL_MUTABLE_CONSTEXPR T&& contained_val() && { return std::move(OptionalBase::storage_.value_); } OPTIONAL_MUTABLE_CONSTEXPR T& contained_val() & { return OptionalBase::storage_.value_; } -# else +#else T& contained_val() & { return OptionalBase::storage_.value_; } T&& contained_val() && { return std::move(OptionalBase::storage_.value_); } -# endif -# else +#endif +#else constexpr const T& contained_val() const { return OptionalBase::storage_.value_; } T& contained_val() { return OptionalBase::storage_.value_; } -# endif +#endif void clear() noexcept { - if (initialized()) dataptr()->T::~T(); + if (initialized()) + dataptr()->T::~T(); OptionalBase::init_ = false; } template - void initialize(Args&&... args) noexcept(noexcept(T(std::forward(args)...))) - { + void initialize(Args&&... args) noexcept(noexcept(T(std::forward(args)...))) { assert(!OptionalBase::init_); ::new (static_cast(dataptr())) T(std::forward(args)...); OptionalBase::init_ = true; } template - void initialize(std::initializer_list il, Args&&... args) noexcept(noexcept(T(il, std::forward(args)...))) - { + void initialize(std::initializer_list il, Args&&... args) noexcept(noexcept(T(il, std::forward(args)...))) { assert(!OptionalBase::init_); ::new (static_cast(dataptr())) T(il, std::forward(args)...); OptionalBase::init_ = true; @@ -397,24 +376,20 @@ public: typedef T value_type; // 20.5.5.1, constructors - constexpr optional() noexcept : OptionalBase() {}; - constexpr optional(nullopt_t) noexcept : OptionalBase() {}; + constexpr optional() noexcept : OptionalBase(){}; + constexpr optional(nullopt_t) noexcept : OptionalBase(){}; - optional(const optional& rhs) - : OptionalBase() - { + optional(const optional& rhs) : OptionalBase() { if (rhs.initialized()) { - ::new (static_cast(dataptr())) T(*rhs); - OptionalBase::init_ = true; + ::new (static_cast(dataptr())) T(*rhs); + OptionalBase::init_ = true; } } - optional(optional&& rhs) noexcept(is_nothrow_move_constructible::value) - : OptionalBase() - { + optional(optional&& rhs) noexcept(is_nothrow_move_constructible::value) : OptionalBase() { if (rhs.initialized()) { - ::new (static_cast(dataptr())) T(std::move(*rhs)); - OptionalBase::init_ = true; + ::new (static_cast(dataptr())) T(std::move(*rhs)); + OptionalBase::init_ = true; } } @@ -434,122 +409,123 @@ public: ~optional() = default; // 20.5.4.3, assignment - optional& operator=(nullopt_t) noexcept - { + optional& operator=(nullopt_t) noexcept { clear(); return *this; } - optional& operator=(const optional& rhs) - { - if (initialized() == true && rhs.initialized() == false) clear(); - else if (initialized() == false && rhs.initialized() == true) initialize(*rhs); - else if (initialized() == true && rhs.initialized() == true) contained_val() = *rhs; + optional& operator=(const optional& rhs) { + if (initialized() == true && rhs.initialized() == false) + clear(); + else if (initialized() == false && rhs.initialized() == true) + initialize(*rhs); + else if (initialized() == true && rhs.initialized() == true) + contained_val() = *rhs; return *this; } - optional& operator=(optional&& rhs) - noexcept(is_nothrow_move_assignable::value && is_nothrow_move_constructible::value) - { - if (initialized() == true && rhs.initialized() == false) clear(); - else if (initialized() == false && rhs.initialized() == true) initialize(std::move(*rhs)); - else if (initialized() == true && rhs.initialized() == true) contained_val() = std::move(*rhs); + optional& + operator=(optional&& rhs) noexcept(is_nothrow_move_assignable::value&& is_nothrow_move_constructible::value) { + if (initialized() == true && rhs.initialized() == false) + clear(); + else if (initialized() == false && rhs.initialized() == true) + initialize(std::move(*rhs)); + else if (initialized() == true && rhs.initialized() == true) + contained_val() = std::move(*rhs); return *this; } template - auto operator=(U&& v) - -> typename enable_if - < - is_same::type, T>::value, - optional& - >::type - { - if (initialized()) { contained_val() = std::forward(v); } - else { initialize(std::forward(v)); } + auto operator=(U&& v) -> typename enable_if::type, T>::value, optional&>::type { + if (initialized()) { + contained_val() = std::forward(v); + } else { + initialize(std::forward(v)); + } return *this; } - template - void emplace(Args&&... args) - { + void emplace(Args&&... args) { clear(); initialize(std::forward(args)...); } template - void emplace(initializer_list il, Args&&... args) - { + void emplace(initializer_list il, Args&&... args) { clear(); initialize(il, std::forward(args)...); } // 20.5.4.4, Swap - void swap(optional& rhs) noexcept(is_nothrow_move_constructible::value && noexcept(swap(declval(), declval()))) - { - if (initialized() == true && rhs.initialized() == false) { rhs.initialize(std::move(**this)); clear(); } - else if (initialized() == false && rhs.initialized() == true) { initialize(std::move(*rhs)); rhs.clear(); } - else if (initialized() == true && rhs.initialized() == true) { using std::swap; swap(**this, *rhs); } + void swap(optional& rhs) noexcept(is_nothrow_move_constructible::value&& noexcept(swap(declval(), + declval()))) { + if (initialized() == true && rhs.initialized() == false) { + rhs.initialize(std::move(**this)); + clear(); + } else if (initialized() == false && rhs.initialized() == true) { + initialize(std::move(*rhs)); + rhs.clear(); + } else if (initialized() == true && rhs.initialized() == true) { + using std::swap; + swap(**this, *rhs); + } } // 20.5.4.5, Observers explicit constexpr operator bool() const noexcept { return initialized(); } - constexpr T const* operator ->() const { - return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), dataptr()); - } + constexpr T const* operator->() const { return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), dataptr()); } -# if OPTIONAL_HAS_MOVE_ACCESSORS == 1 +#if OPTIONAL_HAS_MOVE_ACCESSORS == 1 - OPTIONAL_MUTABLE_CONSTEXPR T* operator ->() { - assert (initialized()); + OPTIONAL_MUTABLE_CONSTEXPR T* operator->() { + assert(initialized()); return dataptr(); } - constexpr T const& operator *() const& { - return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); - } + constexpr T const& operator*() const& { return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); } - OPTIONAL_MUTABLE_CONSTEXPR T& operator *() & { - assert (initialized()); + OPTIONAL_MUTABLE_CONSTEXPR T& operator*() & { + assert(initialized()); return contained_val(); } - OPTIONAL_MUTABLE_CONSTEXPR T&& operator *() && { - assert (initialized()); + OPTIONAL_MUTABLE_CONSTEXPR T&& operator*() && { + assert(initialized()); return constexpr_move(contained_val()); } constexpr T const& value() const& { - if (!initialized()) atFatal("bad optional access"); + if (!initialized()) + atFatal("bad optional access"); return contained_val(); } OPTIONAL_MUTABLE_CONSTEXPR T& value() & { - if (!initialized()) atFatal("bad optional access"); + if (!initialized()) + atFatal("bad optional access"); return contained_val(); } OPTIONAL_MUTABLE_CONSTEXPR T&& value() && { - if (!initialized()) atFatal("bad optional access"); + if (!initialized()) + atFatal("bad optional access"); return std::move(contained_val()); } -# else +#else - T* operator ->() { - assert (initialized()); + T* operator->() { + assert(initialized()); return dataptr(); } - constexpr T const& operator *() const { - return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); - } + constexpr T const& operator*() const { return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); } - T& operator *() { - assert (initialized()); + T& operator*() { + assert(initialized()); return contained_val(); } @@ -565,56 +541,50 @@ public: return contained_val(); } -# endif +#endif -# if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 +#if OPTIONAL_HAS_THIS_RVALUE_REFS == 1 template - constexpr T value_or(V&& v) const& - { + constexpr T value_or(V&& v) const& { return *this ? **this : detail_::convert(constexpr_forward(v)); } -# if OPTIONAL_HAS_MOVE_ACCESSORS == 1 +#if OPTIONAL_HAS_MOVE_ACCESSORS == 1 template - OPTIONAL_MUTABLE_CONSTEXPR T value_or(V&& v) && - { - return *this ? constexpr_move(const_cast&>(*this).contained_val()) : detail_::convert(constexpr_forward(v)); + OPTIONAL_MUTABLE_CONSTEXPR T value_or(V&& v) && { + return *this ? constexpr_move(const_cast&>(*this).contained_val()) + : detail_::convert(constexpr_forward(v)); } -# else +#else template - T value_or(V&& v) && - { - return *this ? constexpr_move(const_cast&>(*this).contained_val()) : detail_::convert(constexpr_forward(v)); + T value_or(V&& v) && { + return *this ? constexpr_move(const_cast&>(*this).contained_val()) + : detail_::convert(constexpr_forward(v)); } -# endif +#endif -# else +#else template - constexpr T value_or(V&& v) const - { + constexpr T value_or(V&& v) const { return *this ? **this : detail_::convert(constexpr_forward(v)); } -# endif - +#endif }; - template -class optional -{ - static_assert( !std::is_same::value, "bad T" ); - static_assert( !std::is_same::value, "bad T" ); +class optional { + static_assert(!std::is_same::value, "bad T"); + static_assert(!std::is_same::value, "bad T"); T* ref; public: - // 20.5.5.1, construction/destruction constexpr optional() noexcept : ref(nullptr) {} @@ -639,411 +609,373 @@ public: } // optional& operator=(const optional& rhs) noexcept { - // ref = rhs.ref; - // return *this; + // ref = rhs.ref; + // return *this; // } // optional& operator=(optional&& rhs) noexcept { - // ref = rhs.ref; - // return *this; + // ref = rhs.ref; + // return *this; // } template - auto operator=(U&& rhs) noexcept - -> typename enable_if - < - is_same::type, optional>::value, - optional& - >::type - { + auto operator=(U&& rhs) noexcept -> + typename enable_if::type, optional>::value, optional&>::type { ref = rhs.ref; return *this; } template - auto operator=(U&& rhs) noexcept - -> typename enable_if - < - !is_same::type, optional>::value, - optional& - >::type - = delete; + auto operator=(U&& rhs) noexcept -> + typename enable_if::type, optional>::value, optional&>::type = delete; - void emplace(T& v) noexcept { - ref = detail_::static_addressof(v); - } + void emplace(T& v) noexcept { ref = detail_::static_addressof(v); } void emplace(T&&) = delete; - - void swap(optional& rhs) noexcept - { - std::swap(ref, rhs.ref); - } + void swap(optional& rhs) noexcept { std::swap(ref, rhs.ref); } // 20.5.5.3, observers - constexpr T* operator->() const { - return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, ref); - } + constexpr T* operator->() const { return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, ref); } - constexpr T& operator*() const { - return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, *ref); - } + constexpr T& operator*() const { return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, *ref); } constexpr T& value() const { - if (!ref) atFatal("bad optional access"); + if (!ref) + atFatal("bad optional access"); return *ref; } - explicit constexpr operator bool() const noexcept { - return ref != nullptr; - } + explicit constexpr operator bool() const noexcept { return ref != nullptr; } template - constexpr typename decay::type value_or(V&& v) const - { + constexpr typename decay::type value_or(V&& v) const { return *this ? **this : detail_::convert::type>(constexpr_forward(v)); } }; - template -class optional -{ - static_assert( sizeof(T) == 0, "optional rvalue references disallowed" ); +class optional { + static_assert(sizeof(T) == 0, "optional rvalue references disallowed"); }; - // 20.5.8, Relational operators -template constexpr bool operator==(const optional& x, const optional& y) -{ +template +constexpr bool operator==(const optional& x, const optional& y) { return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y; } -template constexpr bool operator!=(const optional& x, const optional& y) -{ +template +constexpr bool operator!=(const optional& x, const optional& y) { return !(x == y); } -template constexpr bool operator<(const optional& x, const optional& y) -{ +template +constexpr bool operator<(const optional& x, const optional& y) { return (!y) ? false : (!x) ? true : *x < *y; } -template constexpr bool operator>(const optional& x, const optional& y) -{ +template +constexpr bool operator>(const optional& x, const optional& y) { return (y < x); } -template constexpr bool operator<=(const optional& x, const optional& y) -{ +template +constexpr bool operator<=(const optional& x, const optional& y) { return !(y < x); } -template constexpr bool operator>=(const optional& x, const optional& y) -{ +template +constexpr bool operator>=(const optional& x, const optional& y) { return !(x < y); } - // 20.5.9, Comparison with nullopt -template constexpr bool operator==(const optional& x, nullopt_t) noexcept -{ +template +constexpr bool operator==(const optional& x, nullopt_t) noexcept { return (!x); } -template constexpr bool operator==(nullopt_t, const optional& x) noexcept -{ +template +constexpr bool operator==(nullopt_t, const optional& x) noexcept { return (!x); } -template constexpr bool operator!=(const optional& x, nullopt_t) noexcept -{ +template +constexpr bool operator!=(const optional& x, nullopt_t) noexcept { return bool(x); } -template constexpr bool operator!=(nullopt_t, const optional& x) noexcept -{ +template +constexpr bool operator!=(nullopt_t, const optional& x) noexcept { return bool(x); } -template constexpr bool operator<(const optional&, nullopt_t) noexcept -{ +template +constexpr bool operator<(const optional&, nullopt_t) noexcept { return false; } -template constexpr bool operator<(nullopt_t, const optional& x) noexcept -{ +template +constexpr bool operator<(nullopt_t, const optional& x) noexcept { return bool(x); } -template constexpr bool operator<=(const optional& x, nullopt_t) noexcept -{ +template +constexpr bool operator<=(const optional& x, nullopt_t) noexcept { return (!x); } -template constexpr bool operator<=(nullopt_t, const optional&) noexcept -{ +template +constexpr bool operator<=(nullopt_t, const optional&) noexcept { return true; } -template constexpr bool operator>(const optional& x, nullopt_t) noexcept -{ +template +constexpr bool operator>(const optional& x, nullopt_t) noexcept { return bool(x); } -template constexpr bool operator>(nullopt_t, const optional&) noexcept -{ +template +constexpr bool operator>(nullopt_t, const optional&) noexcept { return false; } -template constexpr bool operator>=(const optional&, nullopt_t) noexcept -{ +template +constexpr bool operator>=(const optional&, nullopt_t) noexcept { return true; } -template constexpr bool operator>=(nullopt_t, const optional& x) noexcept -{ +template +constexpr bool operator>=(nullopt_t, const optional& x) noexcept { return (!x); } - - // 20.5.10, Comparison with T -template constexpr bool operator==(const optional& x, const T& v) -{ +template +constexpr bool operator==(const optional& x, const T& v) { return bool(x) ? *x == v : false; } -template constexpr bool operator==(const T& v, const optional& x) -{ +template +constexpr bool operator==(const T& v, const optional& x) { return bool(x) ? v == *x : false; } -template constexpr bool operator!=(const optional& x, const T& v) -{ +template +constexpr bool operator!=(const optional& x, const T& v) { return bool(x) ? *x != v : true; } -template constexpr bool operator!=(const T& v, const optional& x) -{ +template +constexpr bool operator!=(const T& v, const optional& x) { return bool(x) ? v != *x : true; } -template constexpr bool operator<(const optional& x, const T& v) -{ +template +constexpr bool operator<(const optional& x, const T& v) { return bool(x) ? *x < v : true; } -template constexpr bool operator>(const T& v, const optional& x) -{ +template +constexpr bool operator>(const T& v, const optional& x) { return bool(x) ? v > *x : true; } -template constexpr bool operator>(const optional& x, const T& v) -{ +template +constexpr bool operator>(const optional& x, const T& v) { return bool(x) ? *x > v : false; } -template constexpr bool operator<(const T& v, const optional& x) -{ +template +constexpr bool operator<(const T& v, const optional& x) { return bool(x) ? v < *x : false; } -template constexpr bool operator>=(const optional& x, const T& v) -{ +template +constexpr bool operator>=(const optional& x, const T& v) { return bool(x) ? *x >= v : false; } -template constexpr bool operator<=(const T& v, const optional& x) -{ +template +constexpr bool operator<=(const T& v, const optional& x) { return bool(x) ? v <= *x : false; } -template constexpr bool operator<=(const optional& x, const T& v) -{ +template +constexpr bool operator<=(const optional& x, const T& v) { return bool(x) ? *x <= v : true; } -template constexpr bool operator>=(const T& v, const optional& x) -{ +template +constexpr bool operator>=(const T& v, const optional& x) { return bool(x) ? v >= *x : true; } - // Comparison of optional with T -template constexpr bool operator==(const optional& x, const T& v) -{ +template +constexpr bool operator==(const optional& x, const T& v) { return bool(x) ? *x == v : false; } -template constexpr bool operator==(const T& v, const optional& x) -{ +template +constexpr bool operator==(const T& v, const optional& x) { return bool(x) ? v == *x : false; } -template constexpr bool operator!=(const optional& x, const T& v) -{ +template +constexpr bool operator!=(const optional& x, const T& v) { return bool(x) ? *x != v : true; } -template constexpr bool operator!=(const T& v, const optional& x) -{ +template +constexpr bool operator!=(const T& v, const optional& x) { return bool(x) ? v != *x : true; } -template constexpr bool operator<(const optional& x, const T& v) -{ +template +constexpr bool operator<(const optional& x, const T& v) { return bool(x) ? *x < v : true; } -template constexpr bool operator>(const T& v, const optional& x) -{ +template +constexpr bool operator>(const T& v, const optional& x) { return bool(x) ? v > *x : true; } -template constexpr bool operator>(const optional& x, const T& v) -{ +template +constexpr bool operator>(const optional& x, const T& v) { return bool(x) ? *x > v : false; } -template constexpr bool operator<(const T& v, const optional& x) -{ +template +constexpr bool operator<(const T& v, const optional& x) { return bool(x) ? v < *x : false; } -template constexpr bool operator>=(const optional& x, const T& v) -{ +template +constexpr bool operator>=(const optional& x, const T& v) { return bool(x) ? *x >= v : false; } -template constexpr bool operator<=(const T& v, const optional& x) -{ +template +constexpr bool operator<=(const T& v, const optional& x) { return bool(x) ? v <= *x : false; } -template constexpr bool operator<=(const optional& x, const T& v) -{ +template +constexpr bool operator<=(const optional& x, const T& v) { return bool(x) ? *x <= v : true; } -template constexpr bool operator>=(const T& v, const optional& x) -{ +template +constexpr bool operator>=(const T& v, const optional& x) { return bool(x) ? v >= *x : true; } // Comparison of optional with T -template constexpr bool operator==(const optional& x, const T& v) -{ +template +constexpr bool operator==(const optional& x, const T& v) { return bool(x) ? *x == v : false; } -template constexpr bool operator==(const T& v, const optional& x) -{ +template +constexpr bool operator==(const T& v, const optional& x) { return bool(x) ? v == *x : false; } -template constexpr bool operator!=(const optional& x, const T& v) -{ +template +constexpr bool operator!=(const optional& x, const T& v) { return bool(x) ? *x != v : true; } -template constexpr bool operator!=(const T& v, const optional& x) -{ +template +constexpr bool operator!=(const T& v, const optional& x) { return bool(x) ? v != *x : true; } -template constexpr bool operator<(const optional& x, const T& v) -{ +template +constexpr bool operator<(const optional& x, const T& v) { return bool(x) ? *x < v : true; } -template constexpr bool operator>(const T& v, const optional& x) -{ +template +constexpr bool operator>(const T& v, const optional& x) { return bool(x) ? v > *x : true; } -template constexpr bool operator>(const optional& x, const T& v) -{ +template +constexpr bool operator>(const optional& x, const T& v) { return bool(x) ? *x > v : false; } -template constexpr bool operator<(const T& v, const optional& x) -{ +template +constexpr bool operator<(const T& v, const optional& x) { return bool(x) ? v < *x : false; } -template constexpr bool operator>=(const optional& x, const T& v) -{ +template +constexpr bool operator>=(const optional& x, const T& v) { return bool(x) ? *x >= v : false; } -template constexpr bool operator<=(const T& v, const optional& x) -{ +template +constexpr bool operator<=(const T& v, const optional& x) { return bool(x) ? v <= *x : false; } -template constexpr bool operator<=(const optional& x, const T& v) -{ +template +constexpr bool operator<=(const optional& x, const T& v) { return bool(x) ? *x <= v : true; } -template constexpr bool operator>=(const T& v, const optional& x) -{ +template +constexpr bool operator>=(const T& v, const optional& x) { return bool(x) ? v >= *x : true; } - // 20.5.12, Specialized algorithms template -void swap(optional& x, optional& y) noexcept(noexcept(x.swap(y))) -{ +void swap(optional& x, optional& y) noexcept(noexcept(x.swap(y))) { x.swap(y); } - template -constexpr optional::type> make_optional(T&& v) -{ +constexpr optional::type> make_optional(T&& v) { return optional::type>(constexpr_forward(v)); } template -constexpr optional make_optional(reference_wrapper v) -{ +constexpr optional make_optional(reference_wrapper v) { return optional(v.get()); } - } // namespace experimental } // namespace std -namespace std -{ - template - struct hash> - { - typedef typename hash::result_type result_type; - typedef std::experimental::optional argument_type; +namespace std { +template +struct hash> { + typedef typename hash::result_type result_type; + typedef std::experimental::optional argument_type; - constexpr result_type operator()(argument_type const& arg) const { - return arg ? std::hash{}(*arg) : result_type{}; - } - }; + constexpr result_type operator()(argument_type const& arg) const { + return arg ? std::hash{}(*arg) : result_type{}; + } +}; - template - struct hash> - { - typedef typename hash::result_type result_type; - typedef std::experimental::optional argument_type; +template +struct hash> { + typedef typename hash::result_type result_type; + typedef std::experimental::optional argument_type; - constexpr result_type operator()(argument_type const& arg) const { - return arg ? std::hash{}(*arg) : result_type{}; - } - }; -} + constexpr result_type operator()(argument_type const& arg) const { + return arg ? std::hash{}(*arg) : result_type{}; + } +}; +} // namespace std -# undef TR2_OPTIONAL_REQUIRES -# undef TR2_OPTIONAL_ASSERTED_EXPRESSION +#undef TR2_OPTIONAL_REQUIRES +#undef TR2_OPTIONAL_ASSERTED_EXPRESSION -# endif //___OPTIONAL_HPP___ +#endif //___OPTIONAL_HPP___ diff --git a/lib/Common.cpp b/lib/Common.cpp index 0b56036..4804ef0 100644 --- a/lib/Common.cpp +++ b/lib/Common.cpp @@ -14,8 +14,7 @@ #include "jbus/Common.hpp" -namespace jbus -{ +namespace jbus { #if __APPLE__ static u64 MachToDolphinNum; @@ -24,61 +23,56 @@ static u64 MachToDolphinDenom; static LARGE_INTEGER PerfFrequency; #endif -u64 GetGCTicks() -{ +u64 GetGCTicks() { #if __APPLE__ - return mach_absolute_time() * MachToDolphinNum / MachToDolphinDenom; + return mach_absolute_time() * MachToDolphinNum / MachToDolphinDenom; #elif __linux__ || __FreeBSD__ - struct timespec tp; - clock_gettime(CLOCK_MONOTONIC, &tp); + struct timespec tp; + clock_gettime(CLOCK_MONOTONIC, &tp); - return u64((tp.tv_sec * 1000000000ull) + tp.tv_nsec) * GetGCTicksPerSec() / 1000000000ull; + return u64((tp.tv_sec * 1000000000ull) + tp.tv_nsec) * GetGCTicksPerSec() / 1000000000ull; #elif _WIN32 - LARGE_INTEGER perf; - QueryPerformanceCounter(&perf); - perf.QuadPart *= GetGCTicksPerSec(); - perf.QuadPart /= PerfFrequency.QuadPart; - return perf.QuadPart; + LARGE_INTEGER perf; + QueryPerformanceCounter(&perf); + perf.QuadPart *= GetGCTicksPerSec(); + perf.QuadPart /= PerfFrequency.QuadPart; + return perf.QuadPart; #else - return 0; + return 0; #endif } -void WaitGCTicks(u64 ticks) -{ +void WaitGCTicks(u64 ticks) { #ifndef _WIN32 - struct timeval tv = {}; - tv.tv_sec = ticks / GetGCTicksPerSec(); - tv.tv_usec = (ticks % GetGCTicksPerSec()) * 1000000 / GetGCTicksPerSec(); - select(0, NULL, NULL, NULL, &tv); + struct timeval tv = {}; + tv.tv_sec = ticks / GetGCTicksPerSec(); + tv.tv_usec = (ticks % GetGCTicksPerSec()) * 1000000 / GetGCTicksPerSec(); + select(0, NULL, NULL, NULL, &tv); #else - if (ticks < GetGCTicksPerSec() / 60) - { - /* NT is useless for scheduling sub-millisecond intervals */ - u64 start = GetGCTicks(); - do { Sleep(0); } while (GetGCTicks() - start < ticks); - } - else - { - /* Use normal Sleep() for durations longer than ~16ms */ - Sleep(ticks * 1000 / GetGCTicksPerSec() + - (ticks % GetGCTicksPerSec()) * 1000 / GetGCTicksPerSec()); - } + if (ticks < GetGCTicksPerSec() / 60) { + /* NT is useless for scheduling sub-millisecond intervals */ + u64 start = GetGCTicks(); + do { + Sleep(0); + } while (GetGCTicks() - start < ticks); + } else { + /* Use normal Sleep() for durations longer than ~16ms */ + Sleep(ticks * 1000 / GetGCTicksPerSec() + (ticks % GetGCTicksPerSec()) * 1000 / GetGCTicksPerSec()); + } #endif } -void Initialize() -{ +void Initialize() { #if __APPLE__ - mach_timebase_info_data_t timebase; - mach_timebase_info(&timebase); - MachToDolphinNum = GetGCTicksPerSec() * timebase.numer; - MachToDolphinDenom = 1000000000ull * timebase.denom; + mach_timebase_info_data_t timebase; + mach_timebase_info(&timebase); + MachToDolphinNum = GetGCTicksPerSec() * timebase.numer; + MachToDolphinDenom = 1000000000ull * timebase.denom; #elif _WIN32 - WSADATA initData; - WSAStartup(MAKEWORD(2, 2), &initData); - QueryPerformanceFrequency(&PerfFrequency); + WSADATA initData; + WSAStartup(MAKEWORD(2, 2), &initData); + QueryPerformanceFrequency(&PerfFrequency); #endif } -} +} // namespace jbus diff --git a/lib/Endpoint.cpp b/lib/Endpoint.cpp index 021818c..3b2bbde 100644 --- a/lib/Endpoint.cpp +++ b/lib/Endpoint.cpp @@ -2,882 +2,762 @@ #define LOG_TRANSFER 0 -namespace jbus -{ +namespace jbus { #define ROUND_UP_8(val) (((val) + 7) & ~7) -void Endpoint::KawasedoChallenge::DSPSecParms::ProcessGBACrypto() -{ - /* Unwrap key from challenge using 'sedo' magic number (to encrypt JoyBoot program) */ - x20_key = x0_gbaChallenge ^ 0x6f646573; +void Endpoint::KawasedoChallenge::DSPSecParms::ProcessGBACrypto() { + /* Unwrap key from challenge using 'sedo' magic number (to encrypt JoyBoot program) */ + x20_key = x0_gbaChallenge ^ 0x6f646573; - /* Pack palette parameters */ - u16 paletteSpeedCoded; - s16 logoSpeed = static_cast(x8_logoSpeed); - if (logoSpeed < 0) - paletteSpeedCoded = ((-logoSpeed + 2) * 2) | (x4_logoPalette << 4); - else if (logoSpeed == 0) - paletteSpeedCoded = (x4_logoPalette * 2) | 0x70; - else /* logo_speed > 0 */ - paletteSpeedCoded = ((logoSpeed - 1) * 2) | (x4_logoPalette << 4); + /* Pack palette parameters */ + u16 paletteSpeedCoded; + s16 logoSpeed = static_cast(x8_logoSpeed); + if (logoSpeed < 0) + paletteSpeedCoded = ((-logoSpeed + 2) * 2) | (x4_logoPalette << 4); + else if (logoSpeed == 0) + paletteSpeedCoded = (x4_logoPalette * 2) | 0x70; + else /* logo_speed > 0 */ + paletteSpeedCoded = ((logoSpeed - 1) * 2) | (x4_logoPalette << 4); - /* JoyBoot ROMs start with a padded header; this is the length beyond that header */ - s32 lengthNoHeader = ROUND_UP_8(xc_progLength) - 0x200; + /* JoyBoot ROMs start with a padded header; this is the length beyond that header */ + s32 lengthNoHeader = ROUND_UP_8(xc_progLength) - 0x200; - /* The JoyBus protocol transmits in 4-byte packets while flipping a state flag; - * so the GBA BIOS counts the program length in 8-byte packet-pairs */ - u16 packetPairCount = (lengthNoHeader < 0) ? 0 : lengthNoHeader / 8; - paletteSpeedCoded |= (packetPairCount & 0x4000) >> 14; + /* The JoyBus protocol transmits in 4-byte packets while flipping a state flag; + * so the GBA BIOS counts the program length in 8-byte packet-pairs */ + u16 packetPairCount = (lengthNoHeader < 0) ? 0 : lengthNoHeader / 8; + paletteSpeedCoded |= (packetPairCount & 0x4000) >> 14; - /* Pack together encoded transmission parameters */ - u32 t1 = (((packetPairCount << 16) | 0x3f80) & 0x3f80ffff) * 2; - t1 += (static_cast(static_cast(t1 >> 8)) & packetPairCount) << 16; - u32 t2 = ((paletteSpeedCoded & 0xff) << 16) + (t1 & 0xff0000) + ((t1 >> 8) & 0xffff00); - u32 t3 = paletteSpeedCoded << 16 | ((t2 << 8) & 0xff000000) | (t1 >> 16) | 0x80808080; + /* Pack together encoded transmission parameters */ + u32 t1 = (((packetPairCount << 16) | 0x3f80) & 0x3f80ffff) * 2; + t1 += (static_cast(static_cast(t1 >> 8)) & packetPairCount) << 16; + u32 t2 = ((paletteSpeedCoded & 0xff) << 16) + (t1 & 0xff0000) + ((t1 >> 8) & 0xffff00); + u32 t3 = paletteSpeedCoded << 16 | ((t2 << 8) & 0xff000000) | (t1 >> 16) | 0x80808080; - /* Wrap with 'Kawa' or 'sedo' (Kawasedo is the author of the BIOS cipher) */ - x24_authInitCode = t3 ^ ((t3 & 0x200) != 0 ? 0x6f646573 : 0x6177614b); + /* Wrap with 'Kawa' or 'sedo' (Kawasedo is the author of the BIOS cipher) */ + x24_authInitCode = t3 ^ ((t3 & 0x200) != 0 ? 0x6f646573 : 0x6177614b); } -void Endpoint::KawasedoChallenge::_0Reset(ThreadLocalEndpoint& endpoint, EJoyReturn status) -{ - if (status != GBA_READY || - (status = endpoint.GBAResetAsync(x10_statusPtr, - bindThis(&KawasedoChallenge::_1GetStatus))) != GBA_READY) - { - x28_ticksAfterXf = 0; - if (x14_callback) - { - x14_callback(endpoint, status); - x14_callback = {}; - } +void Endpoint::KawasedoChallenge::_0Reset(ThreadLocalEndpoint& endpoint, EJoyReturn status) { + if (status != GBA_READY || + (status = endpoint.GBAResetAsync(x10_statusPtr, bindThis(&KawasedoChallenge::_1GetStatus))) != GBA_READY) { + x28_ticksAfterXf = 0; + if (x14_callback) { + x14_callback(endpoint, status); + x14_callback = {}; } + } } -void Endpoint::KawasedoChallenge::_1GetStatus(ThreadLocalEndpoint& endpoint, EJoyReturn status) -{ - if (status == GBA_READY) - if (*x10_statusPtr != GBA_JSTAT_SEND) - status = GBA_JOYBOOT_UNKNOWN_STATE; +void Endpoint::KawasedoChallenge::_1GetStatus(ThreadLocalEndpoint& endpoint, EJoyReturn status) { + if (status == GBA_READY) + if (*x10_statusPtr != GBA_JSTAT_SEND) + status = GBA_JOYBOOT_UNKNOWN_STATE; - if (status != GBA_READY || - (status = endpoint.GBAGetStatusAsync(x10_statusPtr, - bindThis(&KawasedoChallenge::_2ReadChallenge))) != GBA_READY) - { - x28_ticksAfterXf = 0; - if (x14_callback) - { - x14_callback(endpoint, status); - x14_callback = {}; - } + if (status != GBA_READY || (status = endpoint.GBAGetStatusAsync( + x10_statusPtr, bindThis(&KawasedoChallenge::_2ReadChallenge))) != GBA_READY) { + x28_ticksAfterXf = 0; + if (x14_callback) { + x14_callback(endpoint, status); + x14_callback = {}; } + } } -void Endpoint::KawasedoChallenge::_2ReadChallenge(ThreadLocalEndpoint& endpoint, EJoyReturn status) -{ - if (status == GBA_READY) - if (*x10_statusPtr != (GBA_JSTAT_PSF0 | GBA_JSTAT_SEND)) - status = GBA_JOYBOOT_UNKNOWN_STATE; +void Endpoint::KawasedoChallenge::_2ReadChallenge(ThreadLocalEndpoint& endpoint, EJoyReturn status) { + if (status == GBA_READY) + if (*x10_statusPtr != (GBA_JSTAT_PSF0 | GBA_JSTAT_SEND)) + status = GBA_JOYBOOT_UNKNOWN_STATE; - if (status != GBA_READY || - (status = endpoint.GBAReadAsync(x18_readBuf, x10_statusPtr, - bindThis(&KawasedoChallenge::_3DSPCrypto))) != GBA_READY) - { - x28_ticksAfterXf = 0; - if (x14_callback) - { - x14_callback(endpoint, status); - x14_callback = {}; - } + if (status != GBA_READY || (status = endpoint.GBAReadAsync(x18_readBuf, x10_statusPtr, + bindThis(&KawasedoChallenge::_3DSPCrypto))) != GBA_READY) { + x28_ticksAfterXf = 0; + if (x14_callback) { + x14_callback(endpoint, status); + x14_callback = {}; } + } } -void Endpoint::KawasedoChallenge::_3DSPCrypto(ThreadLocalEndpoint& endpoint, EJoyReturn status) -{ - if (status != GBA_READY) - { - x28_ticksAfterXf = 0; - if (x14_callback) - { - x14_callback(endpoint, status); - x14_callback = {}; - } - } - else - { - _DSPCryptoInit(); - _DSPCryptoDone(endpoint); +void Endpoint::KawasedoChallenge::_3DSPCrypto(ThreadLocalEndpoint& endpoint, EJoyReturn status) { + if (status != GBA_READY) { + x28_ticksAfterXf = 0; + if (x14_callback) { + x14_callback(endpoint, status); + x14_callback = {}; } + } else { + _DSPCryptoInit(); + _DSPCryptoDone(endpoint); + } } -void Endpoint::KawasedoChallenge::_DSPCryptoInit() -{ - xf8_dspHmac.x0_gbaChallenge = reinterpret_cast(x18_readBuf); - xf8_dspHmac.x4_logoPalette = x0_pColor; - xf8_dspHmac.x8_logoSpeed = x4_pSpeed; - xf8_dspHmac.xc_progLength = xc_progLen; - xf8_dspHmac.ProcessGBACrypto(); +void Endpoint::KawasedoChallenge::_DSPCryptoInit() { + xf8_dspHmac.x0_gbaChallenge = reinterpret_cast(x18_readBuf); + xf8_dspHmac.x4_logoPalette = x0_pColor; + xf8_dspHmac.x8_logoSpeed = x4_pSpeed; + xf8_dspHmac.xc_progLength = xc_progLen; + xf8_dspHmac.ProcessGBACrypto(); } -void Endpoint::KawasedoChallenge::_DSPCryptoDone(ThreadLocalEndpoint& endpoint) -{ - x58_currentKey = xf8_dspHmac.x20_key; - x5c_initMessage = xf8_dspHmac.x24_authInitCode; +void Endpoint::KawasedoChallenge::_DSPCryptoDone(ThreadLocalEndpoint& endpoint) { + x58_currentKey = xf8_dspHmac.x20_key; + x5c_initMessage = xf8_dspHmac.x24_authInitCode; - x20_byteInWindow = ROUND_UP_8(xc_progLen); - if (x20_byteInWindow < 512) - x20_byteInWindow = 512; - x64_totalBytes = x20_byteInWindow; - x20_byteInWindow -= 512; - x20_byteInWindow /= 8; + x20_byteInWindow = ROUND_UP_8(xc_progLen); + if (x20_byteInWindow < 512) + x20_byteInWindow = 512; + x64_totalBytes = x20_byteInWindow; + x20_byteInWindow -= 512; + x20_byteInWindow /= 8; - reinterpret_cast(x1c_writeBuf) = x5c_initMessage; + reinterpret_cast(x1c_writeBuf) = x5c_initMessage; - x38_crc = 0x15a0; - x34_bytesSent = 0; + x38_crc = 0x15a0; + x34_bytesSent = 0; - x28_ticksAfterXf = GetGCTicks(); - x30_justStarted = 1; + x28_ticksAfterXf = GetGCTicks(); + x30_justStarted = 1; - EJoyReturn status; - if ((status = endpoint.GBAWriteAsync(x1c_writeBuf, x10_statusPtr, - bindThis(&KawasedoChallenge::_4TransmitProgram))) != GBA_READY) - { - x28_ticksAfterXf = 0; - if (x14_callback) - { - x14_callback(endpoint, status); - x14_callback = {}; - } + EJoyReturn status; + if ((status = endpoint.GBAWriteAsync(x1c_writeBuf, x10_statusPtr, bindThis(&KawasedoChallenge::_4TransmitProgram))) != + GBA_READY) { + x28_ticksAfterXf = 0; + if (x14_callback) { + x14_callback(endpoint, status); + x14_callback = {}; } + } } -void Endpoint::KawasedoChallenge::_4TransmitProgram(ThreadLocalEndpoint& endpoint, EJoyReturn status) -{ - if (status != GBA_READY) - { - x28_ticksAfterXf = 0; - if (x14_callback) - { - x14_callback(endpoint, status); - x14_callback = {}; - } - return; +void Endpoint::KawasedoChallenge::_4TransmitProgram(ThreadLocalEndpoint& endpoint, EJoyReturn status) { + if (status != GBA_READY) { + x28_ticksAfterXf = 0; + if (x14_callback) { + x14_callback(endpoint, status); + x14_callback = {}; } + return; + } #if LOG_TRANSFER - printf("PROG [%d/%d]\n", x34_bytesSent, x64_totalBytes); + printf("PROG [%d/%d]\n", x34_bytesSent, x64_totalBytes); #endif - if (x30_justStarted) - { - x30_justStarted = 0; + if (x30_justStarted) { + x30_justStarted = 0; + } else { + if (!(*x10_statusPtr & GBA_JSTAT_PSF1) || (*x10_statusPtr & GBA_JSTAT_PSF0) >> 4 != (x34_bytesSent & 4) >> 2) { + x28_ticksAfterXf = 0; + if (x14_callback) { + x14_callback(endpoint, GBA_JOYBOOT_UNKNOWN_STATE); + x14_callback = {}; + } + return; } - else - { - if (!(*x10_statusPtr & GBA_JSTAT_PSF1) || - (*x10_statusPtr & GBA_JSTAT_PSF0) >> 4 != (x34_bytesSent & 4) >> 2) - { - x28_ticksAfterXf = 0; - if (x14_callback) - { - x14_callback(endpoint, GBA_JOYBOOT_UNKNOWN_STATE); - x14_callback = {}; - } - return; + x34_bytesSent += 4; + } + + if (x34_bytesSent <= x64_totalBytes) { + u32 cryptWindow; + if (x34_bytesSent != x64_totalBytes) { + x20_byteInWindow = 0; + cryptWindow = 0; + while (x20_byteInWindow < 4) { + if (xc_progLen) { + cryptWindow |= *x8_progPtr++ << (x20_byteInWindow * 8); + --xc_progLen; } - x34_bytesSent += 4; + ++x20_byteInWindow; + } + + if (x34_bytesSent == 0xac) { + x60_gameId = cryptWindow; + } else if (x34_bytesSent == 0xc4) { + cryptWindow = endpoint.getChan() << 0x8; + } + + if (x34_bytesSent >= 0xc0) { + u32 shiftWindow = cryptWindow; + u32 shiftCrc = x38_crc; + for (int i = 0; i < 32; ++i) { + if ((shiftWindow ^ shiftCrc) & 0x1) + shiftCrc = (shiftCrc >> 1) ^ 0xa1c1; + else + shiftCrc >>= 1; + + shiftWindow >>= 1; + } + x38_crc = shiftCrc; + } + + if (x34_bytesSent == 0x1f8) { + x3c_checkStore[0] = cryptWindow; + } else if (x34_bytesSent == 0x1fc) { + x20_byteInWindow = 1; + x3c_checkStore[x20_byteInWindow] = cryptWindow; + } + } else { + cryptWindow = x38_crc | x34_bytesSent << 16; } - if (x34_bytesSent <= x64_totalBytes) - { - u32 cryptWindow; - if (x34_bytesSent != x64_totalBytes) - { - x20_byteInWindow = 0; - cryptWindow = 0; - while (x20_byteInWindow < 4) - { - if (xc_progLen) - { - cryptWindow |= *x8_progPtr++ << (x20_byteInWindow * 8); - --xc_progLen; - } - ++x20_byteInWindow; - } + if (x34_bytesSent > 0xbf) { + x58_currentKey = 0x6177614b * x58_currentKey + 1; - if (x34_bytesSent == 0xac) - { - x60_gameId = cryptWindow; - } - else if (x34_bytesSent == 0xc4) - { - cryptWindow = endpoint.getChan() << 0x8; - } - - if (x34_bytesSent >= 0xc0) - { - u32 shiftWindow = cryptWindow; - u32 shiftCrc = x38_crc; - for (int i=0 ; i<32 ; ++i) - { - if ((shiftWindow ^ shiftCrc) & 0x1) - shiftCrc = (shiftCrc >> 1) ^ 0xa1c1; - else - shiftCrc >>= 1; - - shiftWindow >>= 1; - } - x38_crc = shiftCrc; - } - - if (x34_bytesSent == 0x1f8) - { - x3c_checkStore[0] = cryptWindow; - } - else if (x34_bytesSent == 0x1fc) - { - x20_byteInWindow = 1; - x3c_checkStore[x20_byteInWindow] = cryptWindow; - } - } - else - { - cryptWindow = x38_crc | x34_bytesSent << 16; - } - - if (x34_bytesSent > 0xbf) - { - x58_currentKey = 0x6177614b * x58_currentKey + 1; - - cryptWindow ^= x58_currentKey; - cryptWindow ^= -(0x2000000 + x34_bytesSent); - cryptWindow ^= 0x20796220; - } - - x1c_writeBuf[0] = cryptWindow >> 0; - x1c_writeBuf[1] = cryptWindow >> 8; - x1c_writeBuf[2] = cryptWindow >> 16; - x1c_writeBuf[3] = cryptWindow >> 24; - - if (x34_bytesSent == 0x1f8) - x3c_checkStore[2] = cryptWindow; - - if (x20_byteInWindow < 4) - { - x3c_checkStore[2 + x20_byteInWindow] = cryptWindow; - x3c_checkStore[5 - x20_byteInWindow] = - x3c_checkStore[1 + x20_byteInWindow] * x3c_checkStore[4 - x20_byteInWindow]; - x3c_checkStore[4 + x20_byteInWindow] = - x3c_checkStore[1 + x20_byteInWindow] * x3c_checkStore[1 - x20_byteInWindow]; - x3c_checkStore[7 - x20_byteInWindow] = - x3c_checkStore[-1 + x20_byteInWindow] * x3c_checkStore[4 - x20_byteInWindow]; - } - - if ((status = endpoint.GBAWriteAsync(x1c_writeBuf, x10_statusPtr, - bindThis(&KawasedoChallenge::_4TransmitProgram))) != GBA_READY) - { - x28_ticksAfterXf = 0; - if (x14_callback) - { - x14_callback(endpoint, status); - x14_callback = {}; - } - } - } - else // x34_bytesWritten > x64_totalBytes - { - if ((status = endpoint.GBAReadAsync(x18_readBuf, x10_statusPtr, - bindThis(&KawasedoChallenge::_5StartBootPoll))) != GBA_READY) - { - x28_ticksAfterXf = 0; - if (x14_callback) - { - x14_callback(endpoint, status); - x14_callback = {}; - } - } - } -} - -void Endpoint::KawasedoChallenge::_5StartBootPoll(ThreadLocalEndpoint& endpoint, EJoyReturn status) -{ - if (status != GBA_READY || - (status = endpoint.GBAGetStatusAsync(x10_statusPtr, - bindThis(&KawasedoChallenge::_6BootPoll))) != GBA_READY) - { - x28_ticksAfterXf = 0; - if (x14_callback) - { - x14_callback(endpoint, status); - x14_callback = {}; - } - } -} - -void Endpoint::KawasedoChallenge::_6BootPoll(ThreadLocalEndpoint& endpoint, EJoyReturn status) -{ - if (status == GBA_READY) - if (*x10_statusPtr & (GBA_JSTAT_FLAGS_MASK | GBA_JSTAT_RECV)) - status = GBA_JOYBOOT_UNKNOWN_STATE; - - if (status != GBA_READY) - { - x28_ticksAfterXf = 0; - if (x14_callback) - { - x14_callback(endpoint, status); - x14_callback = {}; - } - return; + cryptWindow ^= x58_currentKey; + cryptWindow ^= -(0x2000000 + x34_bytesSent); + cryptWindow ^= 0x20796220; } - if (*x10_statusPtr != GBA_JSTAT_SEND) - { - if ((status = endpoint.GBAGetStatusAsync(x10_statusPtr, - bindThis(&KawasedoChallenge::_6BootPoll))) != GBA_READY) - { - x28_ticksAfterXf = 0; - if (x14_callback) - { - x14_callback(endpoint, status); - x14_callback = {}; - } - } - return; + x1c_writeBuf[0] = cryptWindow >> 0; + x1c_writeBuf[1] = cryptWindow >> 8; + x1c_writeBuf[2] = cryptWindow >> 16; + x1c_writeBuf[3] = cryptWindow >> 24; + + if (x34_bytesSent == 0x1f8) + x3c_checkStore[2] = cryptWindow; + + if (x20_byteInWindow < 4) { + x3c_checkStore[2 + x20_byteInWindow] = cryptWindow; + x3c_checkStore[5 - x20_byteInWindow] = + x3c_checkStore[1 + x20_byteInWindow] * x3c_checkStore[4 - x20_byteInWindow]; + x3c_checkStore[4 + x20_byteInWindow] = + x3c_checkStore[1 + x20_byteInWindow] * x3c_checkStore[1 - x20_byteInWindow]; + x3c_checkStore[7 - x20_byteInWindow] = + x3c_checkStore[-1 + x20_byteInWindow] * x3c_checkStore[4 - x20_byteInWindow]; } - if ((status = endpoint.GBAReadAsync(x18_readBuf, x10_statusPtr, - bindThis(&KawasedoChallenge::_7BootAcknowledge))) != GBA_READY) - { - x28_ticksAfterXf = 0; - if (x14_callback) - { - x14_callback(endpoint, status); - x14_callback = {}; - } - } -} - -void Endpoint::KawasedoChallenge::_7BootAcknowledge(ThreadLocalEndpoint& endpoint, EJoyReturn status) -{ - if (status != GBA_READY || - (status = endpoint.GBAWriteAsync(x18_readBuf, x10_statusPtr, - bindThis(&KawasedoChallenge::_8BootDone))) != GBA_READY) - { - x28_ticksAfterXf = 0; - if (x14_callback) - { - x14_callback(endpoint, status); - x14_callback = {}; - } - } -} - -void Endpoint::KawasedoChallenge::_8BootDone(ThreadLocalEndpoint& endpoint, EJoyReturn status) -{ - if (status == GBA_READY) - *x10_statusPtr = 0; - - x28_ticksAfterXf = 0; - - if (x14_callback) - { + if ((status = endpoint.GBAWriteAsync(x1c_writeBuf, x10_statusPtr, + bindThis(&KawasedoChallenge::_4TransmitProgram))) != GBA_READY) { + x28_ticksAfterXf = 0; + if (x14_callback) { x14_callback(endpoint, status); x14_callback = {}; + } } -} - -Endpoint::KawasedoChallenge::KawasedoChallenge(s32 paletteColor, s32 paletteSpeed, - const u8* programp, s32 length, u8* status, FGBACallback&& callback) -: x0_pColor(paletteColor), x4_pSpeed(paletteSpeed), x8_progPtr(programp), xc_progLen(length), - x10_statusPtr(status), x14_callback(std::move(callback)), x34_bytesSent(0), m_initialized(true) -{} - -void Endpoint::KawasedoChallenge::start(Endpoint& endpoint) -{ - if (endpoint.GBAGetStatusAsync(x10_statusPtr, - bindThis(&KawasedoChallenge::_0Reset)) != GBA_READY) - { + } else // x34_bytesWritten > x64_totalBytes + { + if ((status = endpoint.GBAReadAsync(x18_readBuf, x10_statusPtr, bindThis(&KawasedoChallenge::_5StartBootPoll))) != + GBA_READY) { + x28_ticksAfterXf = 0; + if (x14_callback) { + x14_callback(endpoint, status); x14_callback = {}; - m_started = false; + } } + } } -void Endpoint::clockSync() -{ - if (!m_clockSocket) - { - m_running = false; - return; +void Endpoint::KawasedoChallenge::_5StartBootPoll(ThreadLocalEndpoint& endpoint, EJoyReturn status) { + if (status != GBA_READY || + (status = endpoint.GBAGetStatusAsync(x10_statusPtr, bindThis(&KawasedoChallenge::_6BootPoll))) != GBA_READY) { + x28_ticksAfterXf = 0; + if (x14_callback) { + x14_callback(endpoint, status); + x14_callback = {}; } - - u32 TickDelta = 0; - if (!m_lastGCTick) - { - m_lastGCTick = GetGCTicks(); - TickDelta = GetGCTicksPerSec() / 60; - } - else - TickDelta = GetGCTicks() - m_lastGCTick; - - /* Scale GameCube clock into GBA clock */ - TickDelta = u32(u64(TickDelta) * 16777216 / GetGCTicksPerSec()); - m_lastGCTick = GetGCTicks(); - TickDelta = SBig(TickDelta); - if (m_clockSocket.send(&TickDelta, 4) == net::Socket::EResult::Error) - m_running = false; + } } -void Endpoint::send(const u8* buffer) -{ - m_lastCmd = buffer[0]; +void Endpoint::KawasedoChallenge::_6BootPoll(ThreadLocalEndpoint& endpoint, EJoyReturn status) { + if (status == GBA_READY) + if (*x10_statusPtr & (GBA_JSTAT_FLAGS_MASK | GBA_JSTAT_RECV)) + status = GBA_JOYBOOT_UNKNOWN_STATE; - net::Socket::EResult result; - size_t sentBytes; - if (m_lastCmd == CMD_WRITE) - result = m_dataSocket.send(buffer, 5, sentBytes); - else - result = m_dataSocket.send(buffer, 1, sentBytes); - - if (m_lastCmd != CMD_STATUS) - m_booted = true; - - if (result != net::Socket::EResult::OK) - { - m_running = false; + if (status != GBA_READY) { + x28_ticksAfterXf = 0; + if (x14_callback) { + x14_callback(endpoint, status); + x14_callback = {}; } -#if LOG_TRANSFER - else - { - printf("Send %02x [> %02x%02x%02x%02x] (%lu)\n", buffer[0], - buffer[1], buffer[2], buffer[3], buffer[4], sentBytes); + return; + } + + if (*x10_statusPtr != GBA_JSTAT_SEND) { + if ((status = endpoint.GBAGetStatusAsync(x10_statusPtr, bindThis(&KawasedoChallenge::_6BootPoll))) != GBA_READY) { + x28_ticksAfterXf = 0; + if (x14_callback) { + x14_callback(endpoint, status); + x14_callback = {}; + } } -#endif + return; + } + + if ((status = endpoint.GBAReadAsync(x18_readBuf, x10_statusPtr, bindThis(&KawasedoChallenge::_7BootAcknowledge))) != + GBA_READY) { + x28_ticksAfterXf = 0; + if (x14_callback) { + x14_callback(endpoint, status); + x14_callback = {}; + } + } } -size_t Endpoint::receive(u8* buffer) -{ - if (!m_dataSocket) - { - m_running = false; - return 5; +void Endpoint::KawasedoChallenge::_7BootAcknowledge(ThreadLocalEndpoint& endpoint, EJoyReturn status) { + if (status != GBA_READY || (status = endpoint.GBAWriteAsync(x18_readBuf, x10_statusPtr, + bindThis(&KawasedoChallenge::_8BootDone))) != GBA_READY) { + x28_ticksAfterXf = 0; + if (x14_callback) { + x14_callback(endpoint, status); + x14_callback = {}; } - - size_t recvBytes = 0; - net::Socket::EResult result = m_dataSocket.recv(buffer, 5, recvBytes); - if (result == net::Socket::EResult::Error) - { - m_running = false; - return 5; - } - - if (recvBytes > 5) - recvBytes = 5; - -#if LOG_TRANSFER - if (recvBytes > 0) - { - if (m_lastCmd == CMD_STATUS || m_lastCmd == CMD_RESET) - { - printf("Stat/Reset [< %02x%02x%02x%02x%02x] (%lu)\n", - (u8)buffer[0], (u8)buffer[1], (u8)buffer[2], - (u8)buffer[3], (u8)buffer[4], recvBytes); - } - else - { - printf("Receive [< %02x%02x%02x%02x%02x] (%lu)\n", - (u8)buffer[0], (u8)buffer[1], (u8)buffer[2], - (u8)buffer[3], (u8)buffer[4], recvBytes); - } - } -#endif - - return recvBytes; + } } -size_t Endpoint::runBuffer(u8* buffer, std::unique_lock& lk) -{ - u8 tmpBuffer[5]; +void Endpoint::KawasedoChallenge::_8BootDone(ThreadLocalEndpoint& endpoint, EJoyReturn status) { + if (status == GBA_READY) + *x10_statusPtr = 0; - memmove(tmpBuffer, buffer, 5); - lk.unlock(); - clockSync(); - send(tmpBuffer); - size_t receivedBytes = receive(tmpBuffer); - lk.lock(); - memmove(buffer, tmpBuffer, 5); + x28_ticksAfterXf = 0; - return receivedBytes; + if (x14_callback) { + x14_callback(endpoint, status); + x14_callback = {}; + } } -bool Endpoint::idleGetStatus(std::unique_lock& lk) -{ - u8 buffer[] = { CMD_STATUS, 0, 0, 0, 0 }; - return runBuffer(buffer, lk); +Endpoint::KawasedoChallenge::KawasedoChallenge(s32 paletteColor, s32 paletteSpeed, const u8* programp, s32 length, + u8* status, FGBACallback&& callback) +: x0_pColor(paletteColor) +, x4_pSpeed(paletteSpeed) +, x8_progPtr(programp) +, xc_progLen(length) +, x10_statusPtr(status) +, x14_callback(std::move(callback)) +, x34_bytesSent(0) +, m_initialized(true) {} + +void Endpoint::KawasedoChallenge::start(Endpoint& endpoint) { + if (endpoint.GBAGetStatusAsync(x10_statusPtr, bindThis(&KawasedoChallenge::_0Reset)) != GBA_READY) { + x14_callback = {}; + m_started = false; + } } -void Endpoint::transferProc() -{ -#if LOG_TRANSFER - printf("Starting JoyBus transfer thread for channel %d\n", m_chan); -#endif - - /* This lock is relinquished on I/O cycles or when waiting for next request */ - std::unique_lock lk(m_syncLock); - while (m_running) - { - if (m_cmdIssued) - { - /* Synchronous command write/read cycle */ - runBuffer(m_buffer, lk); - m_cmdIssued = false; - - EJoyReturn xferStatus = m_running ? GBA_READY : GBA_NOT_READY; - - /* Handle message response */ - switch (m_lastCmd) { - case CMD_RESET: - case CMD_STATUS: - if (m_statusPtr) - *m_statusPtr = m_buffer[2]; - break; - case CMD_WRITE: - if (m_statusPtr) - *m_statusPtr = m_buffer[0]; - break; - case CMD_READ: - if (m_statusPtr) - *m_statusPtr = m_buffer[4]; - if (m_readDstPtr) - memmove(m_readDstPtr, m_buffer, 4); - break; - default: - break; - } - - m_statusPtr = nullptr; - m_readDstPtr = nullptr; - if (m_callback) - { - FGBACallback cb = std::move(m_callback); - m_callback = {}; - ThreadLocalEndpoint ep(*this); - cb(ep, xferStatus); - } - } - else if (!m_booted) - { - /* Poll bus with status messages when inactive */ - if (idleGetStatus(lk)) - { - lk.unlock(); - WaitGCTicks(GetGCTicksPerSec() * 4 / 60); - lk.lock(); - } - } - else - { - /* Wait for next user request */ - m_issueCv.wait(lk); - } - } - - 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(ThreadLocalEndpoint& endpoint, u8 status) -{ - m_syncCv.notify_all(); -} - -void Endpoint::stop() -{ +void Endpoint::clockSync() { + if (!m_clockSocket) { + m_running = false; + return; + } + + u32 TickDelta = 0; + if (!m_lastGCTick) { + m_lastGCTick = GetGCTicks(); + TickDelta = GetGCTicksPerSec() / 60; + } else + TickDelta = GetGCTicks() - m_lastGCTick; + + /* Scale GameCube clock into GBA clock */ + TickDelta = u32(u64(TickDelta) * 16777216 / GetGCTicksPerSec()); + m_lastGCTick = GetGCTicks(); + TickDelta = SBig(TickDelta); + if (m_clockSocket.send(&TickDelta, 4) == net::Socket::EResult::Error) m_running = false; - m_issueCv.notify_one(); - if (m_transferThread.joinable()) - m_transferThread.join(); } -EJoyReturn Endpoint::GBAGetProcessStatus(u8& percentOut) -{ - if (!m_running) - return GBA_NOT_READY; +void Endpoint::send(const u8* buffer) { + m_lastCmd = buffer[0]; - std::unique_lock lk(m_syncLock); - if (m_joyBoot) - { - percentOut = m_joyBoot.percentComplete(); - if (!m_joyBoot.isDone()) - return GBA_BUSY; + net::Socket::EResult result; + size_t sentBytes; + if (m_lastCmd == CMD_WRITE) + result = m_dataSocket.send(buffer, 5, sentBytes); + else + result = m_dataSocket.send(buffer, 1, sentBytes); + + if (m_lastCmd != CMD_STATUS) + m_booted = true; + + if (result != net::Socket::EResult::OK) { + 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 +} + +size_t Endpoint::receive(u8* buffer) { + if (!m_dataSocket) { + m_running = false; + return 5; + } + + size_t recvBytes = 0; + net::Socket::EResult result = m_dataSocket.recv(buffer, 5, recvBytes); + if (result == net::Socket::EResult::Error) { + m_running = false; + return 5; + } + + if (recvBytes > 5) + recvBytes = 5; + +#if LOG_TRANSFER + if (recvBytes > 0) { + if (m_lastCmd == CMD_STATUS || m_lastCmd == CMD_RESET) { + printf("Stat/Reset [< %02x%02x%02x%02x%02x] (%lu)\n", (u8)buffer[0], (u8)buffer[1], (u8)buffer[2], (u8)buffer[3], + (u8)buffer[4], recvBytes); + } else { + printf("Receive [< %02x%02x%02x%02x%02x] (%lu)\n", (u8)buffer[0], (u8)buffer[1], (u8)buffer[2], (u8)buffer[3], + (u8)buffer[4], recvBytes); } + } +#endif - if (m_cmdIssued) - return GBA_BUSY; - - return GBA_READY; + return recvBytes; } -EJoyReturn Endpoint::GBAGetStatusAsync(u8* status, FGBACallback&& callback) -{ - if (!m_running) - return GBA_NOT_READY; +size_t Endpoint::runBuffer(u8* buffer, std::unique_lock& lk) { + u8 tmpBuffer[5]; - std::unique_lock lk(m_syncLock); - if (m_cmdIssued) - return GBA_NOT_READY; + memmove(tmpBuffer, buffer, 5); + lk.unlock(); + clockSync(); + send(tmpBuffer); + size_t receivedBytes = receive(tmpBuffer); + lk.lock(); + memmove(buffer, tmpBuffer, 5); - m_cmdIssued = true; - m_statusPtr = status; - m_buffer[0] = CMD_STATUS; - m_callback = std::move(callback); - - m_issueCv.notify_one(); - - return GBA_READY; + return receivedBytes; } -EJoyReturn Endpoint::GBAGetStatus(u8* status) -{ - if (!m_running) - return GBA_NOT_READY; - - std::unique_lock lk(m_syncLock); - if (m_cmdIssued) - return GBA_NOT_READY; - - m_cmdIssued = true; - m_statusPtr = status; - m_buffer[0] = CMD_STATUS; - m_callback = bindSync(); - - m_issueCv.notify_one(); - m_syncCv.wait(lk); - - return GBA_READY; +bool Endpoint::idleGetStatus(std::unique_lock& lk) { + u8 buffer[] = {CMD_STATUS, 0, 0, 0, 0}; + return runBuffer(buffer, lk); } -EJoyReturn Endpoint::GBAResetAsync(u8* status, FGBACallback&& callback) -{ - if (!m_running) - return GBA_NOT_READY; +void Endpoint::transferProc() { +#if LOG_TRANSFER + printf("Starting JoyBus transfer thread for channel %d\n", m_chan); +#endif - std::unique_lock lk(m_syncLock); - if (m_cmdIssued) - return GBA_NOT_READY; + /* This lock is relinquished on I/O cycles or when waiting for next request */ + std::unique_lock lk(m_syncLock); + while (m_running) { + if (m_cmdIssued) { + /* Synchronous command write/read cycle */ + runBuffer(m_buffer, lk); + m_cmdIssued = false; - m_cmdIssued = true; - m_statusPtr = status; - m_buffer[0] = CMD_RESET; - m_callback = std::move(callback); + EJoyReturn xferStatus = m_running ? GBA_READY : GBA_NOT_READY; - m_issueCv.notify_one(); + /* Handle message response */ + switch (m_lastCmd) { + case CMD_RESET: + case CMD_STATUS: + if (m_statusPtr) + *m_statusPtr = m_buffer[2]; + break; + case CMD_WRITE: + if (m_statusPtr) + *m_statusPtr = m_buffer[0]; + break; + case CMD_READ: + if (m_statusPtr) + *m_statusPtr = m_buffer[4]; + if (m_readDstPtr) + memmove(m_readDstPtr, m_buffer, 4); + break; + default: + break; + } - return GBA_READY; + m_statusPtr = nullptr; + m_readDstPtr = nullptr; + if (m_callback) { + FGBACallback cb = std::move(m_callback); + m_callback = {}; + ThreadLocalEndpoint ep(*this); + cb(ep, xferStatus); + } + } else if (!m_booted) { + /* Poll bus with status messages when inactive */ + if (idleGetStatus(lk)) { + lk.unlock(); + WaitGCTicks(GetGCTicksPerSec() * 4 / 60); + lk.lock(); + } + } else { + /* Wait for next user request */ + m_issueCv.wait(lk); + } + } + + 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 } -EJoyReturn Endpoint::GBAReset(u8* status) -{ - if (!m_running) - return GBA_NOT_READY; +void Endpoint::transferWakeup(ThreadLocalEndpoint& endpoint, u8 status) { m_syncCv.notify_all(); } - std::unique_lock lk(m_syncLock); - if (m_cmdIssued) - return GBA_NOT_READY; - - m_cmdIssued = true; - m_statusPtr = status; - m_buffer[0] = CMD_RESET; - m_callback = bindSync(); - - m_issueCv.notify_one(); - m_syncCv.wait(lk); - - return GBA_READY; +void Endpoint::stop() { + m_running = false; + m_issueCv.notify_one(); + if (m_transferThread.joinable()) + m_transferThread.join(); } -EJoyReturn Endpoint::GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback) -{ - if (!m_running) - return GBA_NOT_READY; +EJoyReturn Endpoint::GBAGetProcessStatus(u8& percentOut) { + if (!m_running) + return GBA_NOT_READY; - std::unique_lock lk(m_syncLock); - if (m_cmdIssued) - return GBA_NOT_READY; + std::unique_lock lk(m_syncLock); + if (m_joyBoot) { + percentOut = m_joyBoot.percentComplete(); + if (!m_joyBoot.isDone()) + return GBA_BUSY; + } - m_cmdIssued = true; - m_statusPtr = status; - m_readDstPtr = dst; - m_buffer[0] = CMD_READ; - m_callback = std::move(callback); + if (m_cmdIssued) + return GBA_BUSY; - m_issueCv.notify_one(); - - return GBA_READY; + return GBA_READY; } -EJoyReturn Endpoint::GBARead(u8* dst, u8* status) -{ - if (!m_running) - return GBA_NOT_READY; +EJoyReturn Endpoint::GBAGetStatusAsync(u8* status, FGBACallback&& callback) { + if (!m_running) + return GBA_NOT_READY; - std::unique_lock lk(m_syncLock); - if (m_cmdIssued) - return GBA_NOT_READY; + std::unique_lock lk(m_syncLock); + if (m_cmdIssued) + return GBA_NOT_READY; - m_cmdIssued = true; - m_statusPtr = status; - m_readDstPtr = dst; - m_buffer[0] = CMD_READ; - m_callback = bindSync(); + m_cmdIssued = true; + m_statusPtr = status; + m_buffer[0] = CMD_STATUS; + m_callback = std::move(callback); - m_issueCv.notify_one(); - m_syncCv.wait(lk); + m_issueCv.notify_one(); - return GBA_READY; + return GBA_READY; } -EJoyReturn Endpoint::GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback) -{ - if (!m_running) - return GBA_NOT_READY; +EJoyReturn Endpoint::GBAGetStatus(u8* status) { + if (!m_running) + return GBA_NOT_READY; - std::unique_lock lk(m_syncLock); - if (m_cmdIssued) - return GBA_NOT_READY; + std::unique_lock lk(m_syncLock); + if (m_cmdIssued) + return GBA_NOT_READY; - m_cmdIssued = true; - m_statusPtr = status; - m_buffer[0] = CMD_WRITE; - for (int i=0 ; i<4 ; ++i) - m_buffer[i+1] = src[i]; - m_callback = std::move(callback); + m_cmdIssued = true; + m_statusPtr = status; + m_buffer[0] = CMD_STATUS; + m_callback = bindSync(); - m_issueCv.notify_one(); + m_issueCv.notify_one(); + m_syncCv.wait(lk); - return GBA_READY; + return GBA_READY; } -EJoyReturn Endpoint::GBAWrite(const u8* src, u8* status) -{ - if (!m_running) - return GBA_NOT_READY; +EJoyReturn Endpoint::GBAResetAsync(u8* status, FGBACallback&& callback) { + if (!m_running) + return GBA_NOT_READY; - std::unique_lock lk(m_syncLock); - if (m_cmdIssued) - return GBA_NOT_READY; + std::unique_lock lk(m_syncLock); + if (m_cmdIssued) + return GBA_NOT_READY; - m_cmdIssued = true; - m_statusPtr = status; - m_buffer[0] = CMD_WRITE; - for (int i=0 ; i<4 ; ++i) - m_buffer[i+1] = src[i]; - m_callback = bindSync(); + m_cmdIssued = true; + m_statusPtr = status; + m_buffer[0] = CMD_RESET; + m_callback = std::move(callback); - m_issueCv.notify_one(); - m_syncCv.wait(lk); + m_issueCv.notify_one(); - return GBA_READY; + return GBA_READY; } -EJoyReturn Endpoint::GBAJoyBootAsync(s32 paletteColor, s32 paletteSpeed, - const u8* programp, s32 length, u8* status, - FGBACallback&& callback) -{ - if (!m_running) - return GBA_NOT_READY; +EJoyReturn Endpoint::GBAReset(u8* status) { + if (!m_running) + return GBA_NOT_READY; - if (m_chan > 3) - return GBA_JOYBOOT_ERR_INVALID; + std::unique_lock lk(m_syncLock); + if (m_cmdIssued) + return GBA_NOT_READY; - if (!length || length >= 0x40000) - return GBA_JOYBOOT_ERR_INVALID; + m_cmdIssued = true; + m_statusPtr = status; + m_buffer[0] = CMD_RESET; + m_callback = bindSync(); - if (paletteSpeed < -4 || paletteSpeed > 4) - return GBA_JOYBOOT_ERR_INVALID; + m_issueCv.notify_one(); + m_syncCv.wait(lk); - if (paletteColor < 0 || paletteColor > 6) - return GBA_JOYBOOT_ERR_INVALID; + return GBA_READY; +} - if (programp[0xac] * programp[0xac] * programp[0xac] * programp[0xac] == 0) - return GBA_JOYBOOT_ERR_INVALID; +EJoyReturn Endpoint::GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback) { + if (!m_running) + return GBA_NOT_READY; - m_joyBoot = KawasedoChallenge(paletteColor, paletteSpeed, programp, length, status, - std::move(callback)); - m_joyBoot.start(*this); - if (!m_joyBoot.started()) - return GBA_NOT_READY; + std::unique_lock lk(m_syncLock); + if (m_cmdIssued) + return GBA_NOT_READY; - return GBA_READY; + m_cmdIssued = true; + m_statusPtr = status; + m_readDstPtr = dst; + m_buffer[0] = CMD_READ; + m_callback = std::move(callback); + + m_issueCv.notify_one(); + + return GBA_READY; +} + +EJoyReturn Endpoint::GBARead(u8* dst, u8* status) { + if (!m_running) + return GBA_NOT_READY; + + std::unique_lock lk(m_syncLock); + if (m_cmdIssued) + return GBA_NOT_READY; + + m_cmdIssued = true; + m_statusPtr = status; + m_readDstPtr = dst; + m_buffer[0] = CMD_READ; + m_callback = bindSync(); + + m_issueCv.notify_one(); + m_syncCv.wait(lk); + + return GBA_READY; +} + +EJoyReturn Endpoint::GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback) { + if (!m_running) + return GBA_NOT_READY; + + std::unique_lock lk(m_syncLock); + if (m_cmdIssued) + return GBA_NOT_READY; + + m_cmdIssued = true; + m_statusPtr = status; + m_buffer[0] = CMD_WRITE; + for (int i = 0; i < 4; ++i) + m_buffer[i + 1] = src[i]; + m_callback = std::move(callback); + + m_issueCv.notify_one(); + + return GBA_READY; +} + +EJoyReturn Endpoint::GBAWrite(const u8* src, u8* status) { + if (!m_running) + return GBA_NOT_READY; + + std::unique_lock lk(m_syncLock); + if (m_cmdIssued) + return GBA_NOT_READY; + + m_cmdIssued = true; + m_statusPtr = status; + m_buffer[0] = CMD_WRITE; + for (int i = 0; i < 4; ++i) + m_buffer[i + 1] = src[i]; + m_callback = bindSync(); + + m_issueCv.notify_one(); + m_syncCv.wait(lk); + + return GBA_READY; +} + +EJoyReturn Endpoint::GBAJoyBootAsync(s32 paletteColor, s32 paletteSpeed, const u8* programp, s32 length, u8* status, + FGBACallback&& callback) { + if (!m_running) + return GBA_NOT_READY; + + if (m_chan > 3) + return GBA_JOYBOOT_ERR_INVALID; + + if (!length || length >= 0x40000) + return GBA_JOYBOOT_ERR_INVALID; + + if (paletteSpeed < -4 || paletteSpeed > 4) + return GBA_JOYBOOT_ERR_INVALID; + + if (paletteColor < 0 || paletteColor > 6) + return GBA_JOYBOOT_ERR_INVALID; + + if (programp[0xac] * programp[0xac] * programp[0xac] * programp[0xac] == 0) + return GBA_JOYBOOT_ERR_INVALID; + + m_joyBoot = KawasedoChallenge(paletteColor, paletteSpeed, programp, length, status, std::move(callback)); + m_joyBoot.start(*this); + if (!m_joyBoot.started()) + return GBA_NOT_READY; + + return GBA_READY; } Endpoint::Endpoint(u8 chan, net::Socket&& data, net::Socket&& clock) -: m_dataSocket(std::move(data)), m_clockSocket(std::move(clock)), m_chan(chan) -{ - m_transferThread = std::thread(std::bind(&Endpoint::transferProc, this)); +: m_dataSocket(std::move(data)), m_clockSocket(std::move(clock)), m_chan(chan) { + m_transferThread = std::thread(std::bind(&Endpoint::transferProc, this)); } Endpoint::~Endpoint() { stop(); } -EJoyReturn ThreadLocalEndpoint::GBAGetStatusAsync(u8* status, FGBACallback&& callback) -{ - if (!m_ep.m_running || m_ep.m_cmdIssued) - return GBA_NOT_READY; +EJoyReturn ThreadLocalEndpoint::GBAGetStatusAsync(u8* status, FGBACallback&& callback) { + if (!m_ep.m_running || m_ep.m_cmdIssued) + return GBA_NOT_READY; - m_ep.m_cmdIssued = true; - m_ep.m_statusPtr = status; - m_ep.m_buffer[0] = Endpoint::CMD_STATUS; - m_ep.m_callback = std::move(callback); + m_ep.m_cmdIssued = true; + m_ep.m_statusPtr = status; + m_ep.m_buffer[0] = Endpoint::CMD_STATUS; + m_ep.m_callback = std::move(callback); - return GBA_READY; + return GBA_READY; } -EJoyReturn ThreadLocalEndpoint::GBAResetAsync(u8* status, FGBACallback&& callback) -{ - if (!m_ep.m_running || m_ep.m_cmdIssued) - return GBA_NOT_READY; +EJoyReturn ThreadLocalEndpoint::GBAResetAsync(u8* status, FGBACallback&& callback) { + if (!m_ep.m_running || m_ep.m_cmdIssued) + return GBA_NOT_READY; - m_ep.m_cmdIssued = true; - m_ep.m_statusPtr = status; - m_ep.m_buffer[0] = Endpoint::CMD_RESET; - m_ep.m_callback = std::move(callback); + m_ep.m_cmdIssued = true; + m_ep.m_statusPtr = status; + m_ep.m_buffer[0] = Endpoint::CMD_RESET; + m_ep.m_callback = std::move(callback); - return GBA_READY; + return GBA_READY; } -EJoyReturn ThreadLocalEndpoint::GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback) -{ - if (!m_ep.m_running || m_ep.m_cmdIssued) - return GBA_NOT_READY; +EJoyReturn ThreadLocalEndpoint::GBAReadAsync(u8* dst, u8* status, FGBACallback&& callback) { + if (!m_ep.m_running || m_ep.m_cmdIssued) + return GBA_NOT_READY; - m_ep.m_cmdIssued = true; - m_ep.m_statusPtr = status; - m_ep.m_readDstPtr = dst; - m_ep.m_buffer[0] = Endpoint::CMD_READ; - m_ep.m_callback = std::move(callback); + m_ep.m_cmdIssued = true; + m_ep.m_statusPtr = status; + m_ep.m_readDstPtr = dst; + m_ep.m_buffer[0] = Endpoint::CMD_READ; + m_ep.m_callback = std::move(callback); - return GBA_READY; + return GBA_READY; } -EJoyReturn ThreadLocalEndpoint::GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback) -{ - if (!m_ep.m_running || m_ep.m_cmdIssued) - return GBA_NOT_READY; +EJoyReturn ThreadLocalEndpoint::GBAWriteAsync(const u8* src, u8* status, FGBACallback&& callback) { + if (!m_ep.m_running || m_ep.m_cmdIssued) + return GBA_NOT_READY; - m_ep.m_cmdIssued = true; - m_ep.m_statusPtr = status; - m_ep.m_buffer[0] = Endpoint::CMD_WRITE; - for (int i=0 ; i<4 ; ++i) - m_ep.m_buffer[i+1] = src[i]; - m_ep.m_callback = std::move(callback); + m_ep.m_cmdIssued = true; + m_ep.m_statusPtr = status; + m_ep.m_buffer[0] = Endpoint::CMD_WRITE; + for (int i = 0; i < 4; ++i) + m_ep.m_buffer[i + 1] = src[i]; + m_ep.m_callback = std::move(callback); - return GBA_READY; + return GBA_READY; } -} +} // namespace jbus diff --git a/lib/Listener.cpp b/lib/Listener.cpp index 4b37014..49c3adb 100644 --- a/lib/Listener.cpp +++ b/lib/Listener.cpp @@ -3,117 +3,97 @@ #define LOG_LISTENER 0 -namespace jbus -{ +namespace jbus { -void Listener::listenerProc() -{ +void Listener::listenerProc() { #if LOG_LISTENER - printf("JoyBus listener started\n"); + printf("JoyBus listener started\n"); #endif - net::IPAddress localhost("127.0.0.1"); - bool dataBound = false; - bool clockBound = false; - while (m_running && (!dataBound || !clockBound)) - { - if (!dataBound) - { - if (!(dataBound = m_dataServer.openAndListen(localhost, DataPort))) - { - m_dataServer = net::Socket(false); + net::IPAddress localhost("127.0.0.1"); + bool dataBound = false; + bool clockBound = false; + while (m_running && (!dataBound || !clockBound)) { + if (!dataBound) { + 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)); + printf("data open failed %s; will retry\n", strerror(errno)); #endif - WaitGCTicks(GetGCTicksPerSec()); - } - 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 - WaitGCTicks(GetGCTicksPerSec()); - } - else - { -#if LOG_LISTENER - printf("clock listening on port %d\n", ClockPort); -#endif - } - } - } - - /* We use blocking I/O since we have a dedicated transfer thread */ - net::Socket acceptData = {true}; - net::Socket acceptClock = {true}; - std::string hostname; - while (m_running) - { - 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); - m_endpointQueue.push(std::make_unique( - 0, std::move(acceptData), std::move(acceptClock))); - } WaitGCTicks(GetGCTicksPerSec()); - } - - m_dataServer.close(); - m_clockServer.close(); + } else { #if LOG_LISTENER - printf("JoyBus listener stopped\n"); + 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 + WaitGCTicks(GetGCTicksPerSec()); + } else { +#if LOG_LISTENER + printf("clock listening on port %d\n", ClockPort); +#endif + } + } + } + + /* We use blocking I/O since we have a dedicated transfer thread */ + net::Socket acceptData = {true}; + net::Socket acceptClock = {true}; + std::string hostname; + while (m_running) { + 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); + m_endpointQueue.push(std::make_unique(0, std::move(acceptData), std::move(acceptClock))); + } + WaitGCTicks(GetGCTicksPerSec()); + } + + m_dataServer.close(); + m_clockServer.close(); +#if LOG_LISTENER + printf("JoyBus listener stopped\n"); #endif } -void Listener::start() -{ - stop(); - m_running = true; - m_listenerThread = std::thread(std::bind(&Listener::listenerProc, this)); +void Listener::start() { + stop(); + m_running = true; + m_listenerThread = std::thread(std::bind(&Listener::listenerProc, this)); } -void Listener::stop() -{ - m_running = false; - if (m_listenerThread.joinable()) - m_listenerThread.join(); +void Listener::stop() { + m_running = false; + if (m_listenerThread.joinable()) + m_listenerThread.join(); } -std::unique_ptr Listener::accept() -{ - std::unique_lock lk(m_queueLock); - if (m_endpointQueue.size()) - { - std::unique_ptr ret; - ret = std::move(m_endpointQueue.front()); - m_endpointQueue.pop(); - return ret; - } - return {}; +std::unique_ptr Listener::accept() { + std::unique_lock lk(m_queueLock); + if (m_endpointQueue.size()) { + std::unique_ptr ret; + ret = std::move(m_endpointQueue.front()); + m_endpointQueue.pop(); + return ret; + } + return {}; } Listener::~Listener() { stop(); } -} +} // namespace jbus diff --git a/lib/Socket.cpp b/lib/Socket.cpp index 6d7f056..aae542a 100644 --- a/lib/Socket.cpp +++ b/lib/Socket.cpp @@ -13,8 +13,7 @@ #include #endif -namespace jbus::net -{ +namespace jbus::net { /* Define the low-level send/receive flags, which depend on the OS */ #ifdef __linux__ @@ -23,282 +22,248 @@ static const int _flags = MSG_NOSIGNAL; static const int _flags = 0; #endif -void IPAddress::resolve(const std::string& address) -{ - m_address = 0; - m_valid = false; +void IPAddress::resolve(const std::string& address) { + m_address = 0; + m_valid = false; - if (address == "255.255.255.255") - { - /* The broadcast address needs to be handled explicitly, - * because it is also the value returned by inet_addr on error */ - m_address = INADDR_BROADCAST; - m_valid = true; - } - else if (address == "0.0.0.0") - { - m_address = INADDR_ANY; - m_valid = true; - } - else - { - /* Try to convert the address as a byte representation ("xxx.xxx.xxx.xxx") */ - struct in_addr addr; - if (inet_pton(AF_INET, address.c_str(), &addr) == 1) - { - m_address = addr.s_addr; - m_valid = true; - } - else - { - /* Not a valid address, try to convert it as a host name */ - addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; - addrinfo* result = NULL; - if (getaddrinfo(address.c_str(), NULL, &hints, &result) == 0) - { - if (result) - { - addr = reinterpret_cast(result->ai_addr)->sin_addr; - freeaddrinfo(result); - m_address = addr.s_addr; - m_valid = true; - } - } + if (address == "255.255.255.255") { + /* The broadcast address needs to be handled explicitly, + * because it is also the value returned by inet_addr on error */ + m_address = INADDR_BROADCAST; + m_valid = true; + } else if (address == "0.0.0.0") { + m_address = INADDR_ANY; + m_valid = true; + } else { + /* Try to convert the address as a byte representation ("xxx.xxx.xxx.xxx") */ + struct in_addr addr; + if (inet_pton(AF_INET, address.c_str(), &addr) == 1) { + m_address = addr.s_addr; + m_valid = true; + } else { + /* Not a valid address, try to convert it as a host name */ + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + addrinfo* result = NULL; + if (getaddrinfo(address.c_str(), NULL, &hints, &result) == 0) { + if (result) { + addr = reinterpret_cast(result->ai_addr)->sin_addr; + freeaddrinfo(result); + m_address = addr.s_addr; + m_valid = true; } + } } + } } -uint32_t IPAddress::toInteger() const -{ - return ntohl(m_address); -} +uint32_t IPAddress::toInteger() const { return ntohl(m_address); } -static sockaddr_in createAddress(uint32_t address, unsigned short port) -{ - sockaddr_in addr; - memset(&addr, 0, sizeof(addr)); - addr.sin_addr.s_addr = htonl(address); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); +static sockaddr_in createAddress(uint32_t address, unsigned short port) { + sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_addr.s_addr = htonl(address); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); #ifdef __APPLE__ - addr.sin_len = sizeof(addr); + addr.sin_len = sizeof(addr); #endif - return addr; + return addr; } -bool Socket::openSocket() -{ - if (isOpen()) - return false; +bool Socket::openSocket() { + if (isOpen()) + return false; - m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (m_socket == -1) - { - fprintf(stderr, "Can't allocate socket\n"); - return false; - } + m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_socket == -1) { + fprintf(stderr, "Can't allocate socket\n"); + return false; + } - int one = 1; - setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&one), sizeof(one)); + int one = 1; + setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&one), sizeof(one)); #ifdef __APPLE__ - setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast(&one), sizeof(one)); + setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast(&one), sizeof(one)); #endif - setBlocking(m_isBlocking); + setBlocking(m_isBlocking); - return true; + return true; } -void Socket::setRemoteSocket(int remSocket) -{ - close(); - m_socket = remSocket; - setBlocking(m_isBlocking); +void Socket::setRemoteSocket(int remSocket) { + close(); + m_socket = remSocket; + setBlocking(m_isBlocking); } #ifdef _WIN32 -Socket::EResult Socket::LastWSAError() -{ - switch (WSAGetLastError()) - { - case WSAEWOULDBLOCK: - case WSAEALREADY: - return EResult::Busy; - default: - return EResult::Error; - } +Socket::EResult Socket::LastWSAError() { + switch (WSAGetLastError()) { + case WSAEWOULDBLOCK: + case WSAEALREADY: + return EResult::Busy; + default: + return EResult::Error; + } } #endif -void Socket::setBlocking(bool blocking) -{ - m_isBlocking = blocking; +void Socket::setBlocking(bool blocking) { + m_isBlocking = blocking; #ifndef _WIN32 - int status = fcntl(m_socket, F_GETFL); - if (m_isBlocking) - fcntl(m_socket, F_SETFL, status & ~O_NONBLOCK); - else - fcntl(m_socket, F_SETFL, status | O_NONBLOCK); + int status = fcntl(m_socket, F_GETFL); + if (m_isBlocking) + fcntl(m_socket, F_SETFL, status & ~O_NONBLOCK); + else + fcntl(m_socket, F_SETFL, status | O_NONBLOCK); #else - u_long b = blocking ? 0 : 1; - ioctlsocket(m_socket, FIONBIO, &b); + u_long b = blocking ? 0 : 1; + ioctlsocket(m_socket, FIONBIO, &b); #endif } -bool Socket::openAndListen(const IPAddress& address, uint32_t port) -{ - if (!openSocket()) - return false; +bool Socket::openAndListen(const IPAddress& address, uint32_t port) { + if (!openSocket()) + return false; - sockaddr_in addr = createAddress(address.toInteger(), port); - 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\n", port); - return false; - } + sockaddr_in addr = createAddress(address.toInteger(), port); + 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\n", port); + return false; + } - if (::listen(m_socket, 0) == -1) - { - /* Oops, socket is deaf */ - //fprintf(stderr, "Failed to listen 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\n", port); + return false; + } - return true; + return true; } -Socket::EResult Socket::accept(Socket& remoteSocketOut, sockaddr_in& fromAddress) -{ - if (!isOpen()) - return EResult::Error; +Socket::EResult Socket::accept(Socket& remoteSocketOut, sockaddr_in& fromAddress) { + if (!isOpen()) + return EResult::Error; - /* Accept a new connection */ - socklen_t length = sizeof(sockaddr_in); - int remoteSocket = ::accept(m_socket, reinterpret_cast(&fromAddress), &length); + /* Accept a new connection */ + socklen_t length = sizeof(sockaddr_in); + int remoteSocket = ::accept(m_socket, reinterpret_cast(&fromAddress), &length); + + /* Check for errors */ + if (remoteSocket == -1) { +#ifndef _WIN32 + EResult res = (errno == EAGAIN) ? EResult::Busy : EResult::Error; + if (res == EResult::Error) + fprintf(stderr, "Failed to accept incoming connection: %s\n", strerror(errno)); +#else + EResult res = LastWSAError(); + if (res == EResult::Error) + fprintf(stderr, "Failed to accept incoming connection"); +#endif + return res; + } + + /* Initialize the new connected socket */ + remoteSocketOut.setRemoteSocket(remoteSocket); + + return EResult::OK; +} + +Socket::EResult Socket::accept(Socket& remoteSocketOut) { + sockaddr_in fromAddress; + return accept(remoteSocketOut, fromAddress); +} + +Socket::EResult Socket::accept(Socket& remoteSocketOut, std::string& fromHostname) { + sockaddr_in fromAddress; + socklen_t len = sizeof(fromAddress); + char name[NI_MAXHOST]; + EResult res = accept(remoteSocketOut, fromAddress); + if (res == EResult::OK) + if (getnameinfo((sockaddr*)&fromAddress, len, name, NI_MAXHOST, nullptr, 0, 0) == 0) + fromHostname.assign(name); + return res; +} + +void Socket::close() { + if (!isOpen()) + return; +#ifndef _WIN32 + ::close(m_socket); +#else + closesocket(m_socket); +#endif + m_socket = -1; +} + +Socket::EResult Socket::send(const void* buf, size_t len, size_t& transferred) { + transferred = 0; + if (!isOpen()) + return EResult::Error; + + if (!buf || !len) + return EResult::Error; + + /* Loop until every byte has been sent */ + int result = 0; + for (size_t sent = 0; sent < len; sent += result) { + /* Send a chunk of data */ + result = ::send(m_socket, static_cast(buf) + sent, len - sent, _flags); /* Check for errors */ - if (remoteSocket == -1) - { -#ifndef _WIN32 - EResult res = (errno == EAGAIN) ? EResult::Busy : EResult::Error; - if (res == EResult::Error) - fprintf(stderr, "Failed to accept incoming connection: %s\n", strerror(errno)); -#else - EResult res = LastWSAError(); - if (res == EResult::Error) - fprintf(stderr, "Failed to accept incoming connection"); -#endif - return res; - } - - /* Initialize the new connected socket */ - remoteSocketOut.setRemoteSocket(remoteSocket); - - return EResult::OK; -} - -Socket::EResult Socket::accept(Socket& remoteSocketOut) -{ - sockaddr_in fromAddress; - return accept(remoteSocketOut, fromAddress); -} - -Socket::EResult Socket::accept(Socket& remoteSocketOut, std::string& fromHostname) -{ - sockaddr_in fromAddress; - socklen_t len = sizeof(fromAddress); - char name[NI_MAXHOST]; - EResult res = accept(remoteSocketOut, fromAddress); - if (res == EResult::OK) - if (getnameinfo((sockaddr*)&fromAddress, len, name, NI_MAXHOST, nullptr, 0, 0) == 0) - fromHostname.assign(name); - return res; -} - -void Socket::close() -{ - if (!isOpen()) - return; -#ifndef _WIN32 - ::close(m_socket); -#else - closesocket(m_socket); -#endif - m_socket = -1; -} - -Socket::EResult Socket::send(const void* buf, size_t len, size_t& transferred) -{ - transferred = 0; - if (!isOpen()) - return EResult::Error; - - if (!buf || !len) - return EResult::Error; - - /* Loop until every byte has been sent */ - int result = 0; - for (size_t sent = 0; sent < len; sent += result) - { - /* Send a chunk of data */ - result = ::send(m_socket, static_cast(buf) + sent, len - sent, _flags); - - /* Check for errors */ - if (result < 0) -#ifndef _WIN32 - return (errno == EAGAIN) ? EResult::Busy : EResult::Error; -#else - return LastWSAError(); -#endif - } - - transferred = len; - return EResult::OK; -} - -Socket::EResult Socket::send(const void* buf, size_t len) -{ - size_t transferred; - return send(buf, len, transferred); -} - -Socket::EResult Socket::recv(void* buf, size_t len, size_t& transferred) -{ - transferred = 0; - if (!isOpen()) - return EResult::Error; - - if (!buf) - return EResult::Error; - - if (!len) - return EResult::OK; - - /* Receive a chunk of bytes */ - int result = ::recv(m_socket, static_cast(buf), static_cast(len), _flags); - if (result < 0) #ifndef _WIN32 - return (errno == EAGAIN) ? EResult::Busy : EResult::Error; + return (errno == EAGAIN) ? EResult::Busy : EResult::Error; #else - return LastWSAError(); + return LastWSAError(); #endif - else if (result == 0) - return EResult::Error; + } - transferred = result; + transferred = len; + return EResult::OK; +} + +Socket::EResult Socket::send(const void* buf, size_t len) { + size_t transferred; + return send(buf, len, transferred); +} + +Socket::EResult Socket::recv(void* buf, size_t len, size_t& transferred) { + transferred = 0; + if (!isOpen()) + return EResult::Error; + + if (!buf) + return EResult::Error; + + if (!len) return EResult::OK; + + /* Receive a chunk of bytes */ + int result = ::recv(m_socket, static_cast(buf), static_cast(len), _flags); + + if (result < 0) +#ifndef _WIN32 + return (errno == EAGAIN) ? EResult::Busy : EResult::Error; +#else + return LastWSAError(); +#endif + else if (result == 0) + return EResult::Error; + + transferred = result; + return EResult::OK; } -Socket::EResult Socket::recv(void* buf, size_t len) -{ - size_t transferred; - return recv(buf, len, transferred); +Socket::EResult Socket::recv(void* buf, size_t len) { + size_t transferred; + return recv(buf, len, transferred); } -} +} // namespace jbus::net diff --git a/tools/joyboot.cpp b/tools/joyboot.cpp index fc50dad..7b39653 100644 --- a/tools/joyboot.cpp +++ b/tools/joyboot.cpp @@ -3,128 +3,111 @@ #include "jbus/Endpoint.hpp" #include -static void clientPadComplimentCheck(jbus::u8* buffer) -{ - jbus::u8 check = 0x19; - for (int i=0xa0 ; i<0xbd ; ++i) - check += buffer[i]; - buffer[0xbd] = -check; +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 void JoyBootDone(jbus::ThreadLocalEndpoint& endpoint, jbus::EJoyReturn status) { BootStatus = status; } -static bool DonePoll(jbus::Endpoint& endpoint) -{ - jbus::u8 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) - if (endpoint.GBAReset(&status) == jbus::GBA_NOT_READY) - return false; + return false; - if (endpoint.GBAGetStatus(&status) == jbus::GBA_NOT_READY) - return false; - if (status != (jbus::GBA_JSTAT_PSF1 | jbus::GBA_JSTAT_SEND)) - 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; + return true; } -int main(int argc, char** argv) -{ - if (argc < 2) - { - printf("Usage: joyboot \n"); - return 1; +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, + std::bind(JoyBootDone, std::placeholders::_1, std::placeholders::_2)) != + jbus::GBA_READY) { + fprintf(stderr, "Unable to start JoyBoot\n"); + return 1; + } + + jbus::s64 start = jbus::GetGCTicks(); + jbus::u8 percent = 0; + jbus::u8 lastpercent = 0; + while (endpoint->GBAGetProcessStatus(percent) == jbus::GBA_BUSY) { + if (percent != lastpercent) { + lastpercent = percent; + printf("\rUpload %d%%", percent); + fflush(stdout); } - - FILE* fp = fopen(argv[1], "rb"); - if (!fp) - { - fprintf(stderr, "Unable to open %s\n", argv[1]); - return 1; + 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); + } + printf("\nJoy Boot finished with %d status\n", status); - 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; + while (!DonePoll(*endpoint)) { + jbus::s64 curTime = jbus::GetGCTicks(); + jbus::s64 passedTicks = curTime - start; + if (passedTicks > jbus::GetGCTicksPerSec() * 15) { + fprintf(stderr, "JoyBoot timeout\n"); + return 1; } + jbus::WaitGCTicks(jbus::GetGCTicksPerSec() / 60); + } - 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, std::bind(JoyBootDone, - std::placeholders::_1, - std::placeholders::_2)) != jbus::GBA_READY) - { - fprintf(stderr, "Unable to start JoyBoot\n"); - return 1; - } - - jbus::s64 start = jbus::GetGCTicks(); - jbus::u8 percent = 0; - jbus::u8 lastpercent = 0; - while (endpoint->GBAGetProcessStatus(percent) == jbus::GBA_BUSY) - { - if (percent != lastpercent) - { - lastpercent = percent; - printf("\rUpload %d%%", percent); - fflush(stdout); - } - 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); - } - 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() * 15) - { - fprintf(stderr, "JoyBoot timeout\n"); - return 1; - } - jbus::WaitGCTicks(jbus::GetGCTicksPerSec() / 60); - } - - return 0; + return 0; }