From 77a8d517be58f36e6b4207dd85b8f3e28223306e Mon Sep 17 00:00:00 2001 From: DragonMinded Date: Wed, 6 Sep 2017 22:13:46 -0700 Subject: [PATCH] Major refactor of ACIO Launcher. Moved debug stuff into Debug.cpp/h. Moved ACIO driver stuff into its own class. --- ACIO.cpp | 554 +++++++++++++++++++++++++++++++++++++ ACIO.h | 81 ++++++ ACIOLauncher.cpp | 651 +++++--------------------------------------- ACIOLauncher.vcproj | 16 ++ Debug.cpp | 18 ++ Debug.h | 16 ++ Menu.cpp | 6 +- Menu.h | 3 +- 8 files changed, 757 insertions(+), 588 deletions(-) create mode 100644 ACIO.cpp create mode 100644 ACIO.h create mode 100644 Debug.cpp create mode 100644 Debug.h diff --git a/ACIO.cpp b/ACIO.cpp new file mode 100644 index 0000000..b28265f --- /dev/null +++ b/ACIO.cpp @@ -0,0 +1,554 @@ +#include + +#include "ACIO.h" +#include "Debug.h" + +/* Length of an ACIO packet without payload or checksum */ +#define HEADER_LENGTH 5 + +/* Minimum packet length including header and checksum */ +#define MINIMUM_PACKET_LENGTH (HEADER_LENGTH + 1) + +/* Start of Message */ +#define SOM 0xAA + +/* Location of various parts of the protocol */ +#define ID_LOCATION 0 +#define COMMAND_HIGH_LOCATION 1 +#define COMMAND_LOW_LOCATION 2 +#define LENGTH_HIGH_LOCATION 3 +#define LENGTH_LOW_LOCATION 4 + +/* Number of times to try initializing a reader by sending baud probe */ +#define INITIALIZATION_TRIES 16 + +/* Number of return baud probes to receive before we consider ourselves initialized */ +#define MIN_PROBES_RECEIVED 5 + +#define WAIT() Sleep( 100 ) +#define LONGWAIT() Sleep( 1000 ) + +ACIO::ACIO() +{ + /* Initialize readers first */ + printf( "Initializing readers" ); + serial = InitReaders(readerCount); + printf( "\n" ); + + /* Get version of all readers */ + for( unsigned int x = 0; x < readerCount; x++ ) + { + /* Print out reader version in debug mode */ + DEBUG_PRINTF( "Reader %d returned version %s\n", x + 1, getReaderVersion( serial, x ) ); + + /* Walk init routine */ + initReader( serial, x, 0 ); + initReader( serial, x, 1 ); + initReader( serial, x, 2 ); + + /* Set ready for keys only */ + setReaderState( serial, x, STATE_EJECT ); + } + + /* For key input debouncing */ + old_keypresses = (unsigned int *)malloc(sizeof(unsigned int) * readerCount); +} + +ACIO::~ACIO(void) +{ + /* Kill memory used for keypresses */ + free(old_keypresses); + + /* Close the reader so we can let the game talk to it */ + for( unsigned int y = 0; y < readerCount; y++ ) + { + setReaderState( serial, y, STATE_EJECT ); + } + CloseHandle( serial ); +} + +bool ACIO::getCardInserted(int reader) +{ + unsigned int currentpresses; + getReaderState( serial, reader, &state, ¤tpresses, cardId ); + return (state == STATE_READ || state == STATE_INSERTED); +} + +/** + * TODO: For some reason this returns one card read in the past. So, on first + * read it returns garbage, on second read it returns the first card, etc. Fix + * that at some point. + */ +void ACIO::getCardID(int reader, unsigned char *outCardId) +{ + while( !getCardInserted(reader) ) { requestCardId( serial, reader ); } + while( !getCardInserted(reader) ) { requestCardId( serial, reader ); } + memcpy(outCardId, cardId, CARD_LENGTH); +} + +reader_keypress_t ACIO::getPressedKey(int reader) +{ + unsigned int currentpresses; + getReaderState( serial, reader, &state, ¤tpresses, cardId ); + unsigned int keypresses = currentpresses & (~old_keypresses[reader]); + old_keypresses[reader] = currentpresses; + + if (keypresses & 0x0002 ) + { + return KEY_1; + } + if (keypresses & 0x0020 ) + { + return KEY_2; + } + if (keypresses & 0x0200 ) + { + return KEY_3; + } + if (keypresses & 0x0004 ) + { + return KEY_4; + } + if (keypresses & 0x0040 ) + { + return KEY_5; + } + if (keypresses & 0x0400 ) + { + return KEY_6; + } + if (keypresses & 0x0008 ) + { + return KEY_7; + } + if (keypresses & 0x0080 ) + { + return KEY_8; + } + if (keypresses & 0x0800 ) + { + return KEY_9; + } + if (keypresses & 0x0001 ) + { + return KEY_0; + } + if (keypresses & 0x0010 ) + { + return KEY_00; + } + if (keypresses & 0x0100 ) + { + return KEY_BLANK; + } + + return KEY_NONE; +} + +void ACIO::requestCardInsert(int reader) +{ + setReaderState( serial, reader, STATE_EJECT ); + + /* Walk init routine again. For some reason, it reads the same + * card ID the second time through, so I just reinit. */ + initReader( serial, reader, 0 ); + initReader( serial, reader, 1 ); + initReader( serial, reader, 2 ); + + setReaderState( serial, reader, STATE_NOT_READY ); + setReaderState( serial, reader, STATE_GET_READY ); + setReaderState( serial, reader, STATE_READY ); +} + +void ACIO::requestCardEject(int reader) +{ + setReaderState( serial, reader, STATE_EJECT ); +} + +HANDLE ACIO::openSerial( const _TCHAR *arg, int baud ) +{ + HANDLE hSerial = CreateFile(arg, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (hSerial == INVALID_HANDLE_VALUE) { return hSerial; } + + int rate; + + switch( baud ) + { + case 4800: + rate = CBR_4800; + break; + case 9600: + rate = CBR_9600; + break; + case 19200: + rate = CBR_19200; + break; + case 38400: + rate = CBR_38400; + break; + case 57600: + rate = CBR_57600; + break; + case 115200: + rate = CBR_115200; + break; + default: + rate = CBR_9600; + break; + } + + DCB dcbSerialParams = {0}; + + dcbSerialParams.DCBlength = sizeof(dcbSerialParams); + dcbSerialParams.BaudRate = rate; + dcbSerialParams.ByteSize = 8; + dcbSerialParams.StopBits = ONESTOPBIT; + dcbSerialParams.Parity = NOPARITY; + dcbSerialParams.fOutxCtsFlow = 0; + dcbSerialParams.fOutxDsrFlow = 0; + dcbSerialParams.fDtrControl = DTR_CONTROL_DISABLE; + dcbSerialParams.fDsrSensitivity = 0; + dcbSerialParams.fOutX = 0; + dcbSerialParams.fInX = 0; + dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE; + + SetCommState(hSerial, &dcbSerialParams); + + COMMTIMEOUTS timeouts = { 0 }; + + timeouts.ReadIntervalTimeout = 1; + timeouts.ReadTotalTimeoutConstant = 1; + timeouts.ReadTotalTimeoutMultiplier = 1; + + SetCommTimeouts(hSerial, &timeouts); + return hSerial; +} + +const unsigned char * const ACIO::getPacketData( + unsigned int *readerId, + unsigned int *command, + unsigned int *len, + unsigned int *checksum, + const unsigned char * const packet, + unsigned int length +) { + /* Exit early if we didn't get a good packet */ + if (length < MINIMUM_PACKET_LENGTH) { return NULL; } + + for( unsigned int i = 0; i <= length - MINIMUM_PACKET_LENGTH; i++ ) + { + const unsigned char * const data = &packet[i]; + + if( data[0] != SOM ) + { + /* Get command */ + *readerId = data[ID_LOCATION]; + *command = (data[COMMAND_HIGH_LOCATION] << 8) | data[COMMAND_LOW_LOCATION]; + *len = (data[LENGTH_HIGH_LOCATION] << 8) | data[LENGTH_LOW_LOCATION]; + *checksum = data[(*len) + HEADER_LENGTH]; + return data + HEADER_LENGTH; + } + } + + return NULL; +} + +unsigned char ACIO::calcPacketChecksum( const unsigned char * const data ) +{ + unsigned char *packet = (unsigned char *)data; + + /* Dirty */ + while( 1 ) + { + /* Get to start of packet */ + if( packet[0] == SOM ) + { + packet++; + continue; + } + + unsigned int length = ((packet[LENGTH_HIGH_LOCATION] << 8) | packet[LENGTH_LOW_LOCATION]); + unsigned int sum = 0; + + /* Skip SOM, not part of packet CRC */ + for( unsigned int i = 0; i < length + HEADER_LENGTH; i++ ) + { + sum += packet[i]; + } + + return sum & 0xFF; + } +} + +int ACIO::probeReader( HANDLE serial ) +{ + const unsigned char packet_probe[] = { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, }; + unsigned char data[1024]; + DWORD length; + + WriteFile( serial, packet_probe, sizeof( packet_probe ), &length, 0 ); + ReadFile( serial, data, sizeof( data ), &length, 0 ); + + if (length < MIN_PROBES_RECEIVED) + { + return 0; + } + + for (unsigned int i = 0; i < length; i++) + { + if (data[i] != 0xAA) + { + return 0; + } + } + + /* Clear out any additional init the reader sends */ + while (true) + { + ReadFile( serial, data, sizeof( data ), &length, 0 ); + if (length == 0) + { + return 1; + } + } +} + +unsigned int ACIO::getReaderCount( HANDLE serial ) +{ + const unsigned char count_probe[] = { 0xAA, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, }; + unsigned char data[1024]; + DWORD length; + + WriteFile( serial, count_probe, sizeof( count_probe ), &length, 0 ); + WAIT(); + ReadFile( serial, data, sizeof( data ), &length, 0 ); + + unsigned int readerId; + unsigned int command; + unsigned int len; + unsigned int checksum; + const unsigned char * const payload = getPacketData( &readerId, &command, &len, &checksum, data, length ); + + if( payload == NULL ) { return 0; } + if( len != 1 ) { return 0; } + + return payload[0]; +} + +void ACIO::initReader( HANDLE serial, unsigned int id, int whichInit ) +{ + unsigned int init_length[3] = { 7, 7, 8 }; + unsigned char init_probe[3][8] = { { 0xAA, (id + 1), 0x00, 0x03, 0x00, 0x00, 0xFF, }, + { 0xAA, (id + 1), 0x01, 0x00, 0x00, 0x00, 0xFF, }, + { 0xAA, (id + 1), 0x01, 0x30, 0x00, 0x01, 0x00, 0xFF, } }; + unsigned char data[1024]; + DWORD length; + + /* Fix up checksum since ID is variable */ + init_probe[whichInit][init_length[whichInit] - 1] = calcPacketChecksum( init_probe[whichInit] ); + + WriteFile( serial, init_probe[whichInit], init_length[whichInit], &length, 0 ); + WAIT(); + ReadFile( serial, data, sizeof( data ), &length, 0 ); +} + +const char * const ACIO::getReaderVersion( HANDLE serial, unsigned int id ) +{ + unsigned char version_probe[] = { 0xAA, (id + 1), 0x00, 0x02, 0x00, 0x00, 0xFF, }; + static char version[33] = { 0x00 }; + unsigned char data[1024]; + DWORD length; + + /* Fix up checksum since ID is variable */ + version_probe[6] = calcPacketChecksum( version_probe ); + + WriteFile( serial, version_probe, sizeof( version_probe ), &length, 0 ); + WAIT(); + ReadFile( serial, data, sizeof( data ), &length, 0 ); + + unsigned int readerId; + unsigned int command; + unsigned int len; + unsigned int checksum; + const unsigned char * const payload = getPacketData( &readerId, &command, &len, &checksum, data, length ); + + if( payload != NULL && len == 44 ) + { + memcpy( version, &payload[12], 32 ); + version[32] = 0x00; + + /* Spaces, for display */ + for( int i = 0; i < 32; i++ ) + { + if( version[i] == 0x00 ) { version[i] = 0x20; } + } + } + + return (const char * const)version; +} + +void ACIO::getReaderState( HANDLE serial, unsigned int id, card_state_t *state, unsigned int *keypresses, unsigned char *cardId ) +{ + unsigned char state_probe[] = { 0xAA, (id + 1), 0x01, 0x34, 0x00, 0x01, 0x10, 0xFF, }; + unsigned char data[1024]; + DWORD length; + + /* Fix up checksum since ID is variable */ + state_probe[7] = calcPacketChecksum( state_probe ); + + /* Sane return */ + *keypresses = 0; + + WriteFile( serial, state_probe, sizeof( state_probe ), &length, 0 ); + WAIT(); + ReadFile( serial, data, sizeof( data ), &length, 0 ); + + unsigned int readerId; + unsigned int command; + unsigned int len; + unsigned int checksum; + const unsigned char * const payload = getPacketData( &readerId, &command, &len, &checksum, data, length ); + + if( payload != NULL && len == 16 ) + { + *keypresses = (payload[14] << 8) | payload[15]; + + *state = STATE_UNKNOWN; + switch( payload[1] ) + { + case 0x00: + *state = STATE_REMOVED; + break; + case 0x10: + *state = STATE_FRONT_SENSOR; + break; + case 0x30: + *state = STATE_INSERTED; + break; + default: + if( VERBOSE_DEBUG ) fprintf( stderr, "Unknown card state %02X!\n", payload[1] ); + break; + } + + if( payload[0] != 0x00 && payload[11] == 0x00 && *state == STATE_INSERTED ) + { + memcpy( cardId, &payload[2], CARD_LENGTH ); + *state = STATE_READ; + } + } +} + +void ACIO::setReaderState( HANDLE serial, unsigned int id, reader_state_t state ) +{ + unsigned char state_request[] = { 0xAA, (id + 1), 0x01, 0x35, 0x00, 0x02, 0x10, 0xFF, 0xFF, }; + unsigned char data[1024]; + DWORD length; + + /* Set state */ + switch( state ) + { + case STATE_NOT_READY: + state_request[7] = 0x00; + break; + case STATE_GET_READY: + state_request[7] = 0x03; + break; + case STATE_READY: + state_request[7] = 0x11; + break; + case STATE_EJECT: + state_request[7] = 0x12; + break; + default: + if( VERBOSE_DEBUG ) fprintf( stderr, "Unknown reader state %02X!\n", state ); + state_request[7] = 0xFF; + break; + } + + /* Fix up checksum since ID is variable */ + state_request[8] = calcPacketChecksum( state_request ); + + WriteFile( serial, state_request, sizeof( state_request ), &length, 0 ); + WAIT(); + ReadFile( serial, data, sizeof( data ), &length, 0 ); +} + +void ACIO::requestCardId( HANDLE serial, unsigned int id ) +{ + unsigned char id_request[] = { 0xAA, (id + 1), 0x01, 0x31, 0x00, 0x01, 0x10, 0xFF, }; + unsigned char data[1024]; + DWORD length; + + /* Fix up checksum since ID is variable */ + id_request[7] = calcPacketChecksum( id_request ); + + WriteFile( serial, id_request, sizeof( id_request ), &length, 0 ); + WAIT(); + ReadFile( serial, data, sizeof( data ), &length, 0 ); +} + +HANDLE ACIO::InitReaders(unsigned int &readerCount) +{ + /* Walk serial chain, finding readers */ + HANDLE serial = NULL; + const _TCHAR *comport[4] = { L"COM1", L"COM2", L"COM3", L"COM4" }; + const unsigned int baudrate[2] = { 57600, 9600 }; + const char *pattern[4] = { "\b\b\b ", "\b\b\b.", ".", "." }; + unsigned int which = 3; + unsigned int pno = 0; + + /* Put a space into location where we will be backspacing, for laziness */ + NON_DEBUG_PRINTF( " " ); + DEBUG_PRINTF( "...\n" ); + + /* Try to open the reader indefinitely */ + while( 1 ) + { + /* Try next reader */ + which = (which + 1) % 4; + + for (unsigned int baud = 0; baud < 2; baud++) + { + NON_DEBUG_PRINTF( "%s", pattern[(pno++) % 4] ); + DEBUG_PRINTF("Attempting to probe readers on %ls,%d\n", comport[which], baudrate[baud]); + serial = openSerial( comport[which], baudrate[baud] ); + + if (serial == INVALID_HANDLE_VALUE) + { + DEBUG_PRINTF("Couldn't open com port!\n"); + serial = NULL; + LONGWAIT(); + continue; + } + + /* Ensure we start fresh */ + PurgeComm( serial, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR ); + + /* Init */ + for (int i = 0; i < INITIALIZATION_TRIES; i++) + { + if( probeReader( serial ) ) + { + /* Get count */ + unsigned int count = getReaderCount( serial ); + if (count > 0) + { + DEBUG_PRINTF( "Saw %d readers!\n", count ); + readerCount = count; + return serial; + } + } + + NON_DEBUG_PRINTF( "%s", pattern[(pno++) % 4] ); + } + + /* Failed, close */ + CloseHandle( serial ); + } + + /* Failed to probe this port, try again on next one */ + DEBUG_PRINTF("Failed to probe readers!\n"); + serial = NULL; + LONGWAIT(); + } +} \ No newline at end of file diff --git a/ACIO.h b/ACIO.h new file mode 100644 index 0000000..123041a --- /dev/null +++ b/ACIO.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include + +/* Length of the card ID in bytes */ +#define CARD_LENGTH 8 + +typedef enum +{ + STATE_FRONT_SENSOR, + STATE_INSERTED, + STATE_READ, + STATE_REMOVED, + STATE_UNKNOWN, +} card_state_t; + +typedef enum +{ + STATE_NOT_READY, + STATE_GET_READY, + STATE_READY, + STATE_EJECT, +} reader_state_t; + +typedef enum +{ + KEY_NONE, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_0, + KEY_00, + KEY_BLANK, +} reader_keypress_t; + +class ACIO +{ + public: + ACIO(void); + ~ACIO(void); + + unsigned int getCount() { return readerCount; } + reader_keypress_t getPressedKey(int reader); + void requestCardInsert(int reader); + void requestCardEject(int reader); + bool getCardInserted(int reader); + void getCardID(int reader, unsigned char *cardId); + + private: + unsigned int readerCount; + HANDLE serial; + unsigned int *old_keypresses; + unsigned char cardId[CARD_LENGTH]; + card_state_t state; + + HANDLE openSerial( const _TCHAR *arg, int baud ); + const unsigned char * const getPacketData( + unsigned int *readerId, + unsigned int *command, + unsigned int *len, + unsigned int *checksum, + const unsigned char * const packet, + unsigned int length + ); + unsigned char calcPacketChecksum( const unsigned char * const data ); + int probeReader( HANDLE serial ); + unsigned int getReaderCount( HANDLE serial ); + void initReader( HANDLE serial, unsigned int id, int whichInit ); + const char * const getReaderVersion( HANDLE serial, unsigned int id ); + void getReaderState( HANDLE serial, unsigned int id, card_state_t *state, unsigned int *keypresses, unsigned char *cardId ); + void setReaderState( HANDLE serial, unsigned int id, reader_state_t state ); + void requestCardId( HANDLE serial, unsigned int id ); + HANDLE InitReaders( unsigned int &readerCount ); +}; diff --git a/ACIOLauncher.cpp b/ACIOLauncher.cpp index ab570e4..6f86a5b 100644 --- a/ACIOLauncher.cpp +++ b/ACIOLauncher.cpp @@ -5,13 +5,9 @@ #include #include #include +#include "ACIO.h" #include "Menu.h" - -#define WAIT() Sleep( 100 ) -#define LONGWAIT() Sleep( 1000 ) - -/* Define to 1 to get verbose debugging */ -#define VERBOSE_DEBUG 0 +#include "Debug.h" /** * Define to 1 to enable card reader mode, currently only works with @@ -22,458 +18,6 @@ */ #define CARD_READER_MODE 0 -/* Length of an ACIO packet without payload or checksum */ -#define HEADER_LENGTH 5 - -/* Minimum packet length including header and checksum */ -#define MINIMUM_PACKET_LENGTH (HEADER_LENGTH + 1) - -/* Start of Message */ -#define SOM 0xAA - -/* Location of various parts of the protocol */ -#define ID_LOCATION 0 -#define COMMAND_HIGH_LOCATION 1 -#define COMMAND_LOW_LOCATION 2 -#define LENGTH_HIGH_LOCATION 3 -#define LENGTH_LOW_LOCATION 4 - -/* Length of the card ID in bytes */ -#define CARD_LENGTH 8 - -/* Number of times to try initializing a reader by sending baud probe */ -#define INITIALIZATION_TRIES 16 - -/* Number of return baud probes to receive before we consider ourselves initialized */ -#define MIN_PROBES_RECEIVED 5 - -typedef enum -{ - STATE_FRONT_SENSOR, - STATE_INSERTED, - STATE_READ, - STATE_REMOVED, - STATE_UNKNOWN, -} card_state_t; - -typedef enum -{ - STATE_NOT_READY, - STATE_GET_READY, - STATE_READY, - STATE_EJECT, -} reader_state_t; - -/* Debug global which can be set via command line flag --debug */ -bool debug = false; - -/* Debug macros that are enabled via the --debug flag */ -#define DEBUG_PRINTF(...) do { if(debug) { printf(__VA_ARGS__); } } while(0) -#define DEBUG_PRINT_HEX(data, length) do { if(debug) { printHex(data, length); } } while(0) - -/* Macros that are enabled when debug is not enabled */ -#define NON_DEBUG_PRINTF(...) do { if(!debug) { printf(__VA_ARGS__); } } while(0) - -void printHex(const unsigned char * const data, int length ) -{ - printf( "Length: %d bytes\n", length ); - - for( int x = 0; x < length; x++ ) - { - printf( "%02X ", data[x] ); - } - - printf( "\n" ); -} - -HANDLE OpenSerial( const _TCHAR *arg, int baud ) -{ - HANDLE hSerial = CreateFile(arg, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - if (hSerial == INVALID_HANDLE_VALUE) { return hSerial; } - - int rate; - - switch( baud ) - { - case 4800: - rate = CBR_4800; - break; - case 9600: - rate = CBR_9600; - break; - case 19200: - rate = CBR_19200; - break; - case 38400: - rate = CBR_38400; - break; - case 57600: - rate = CBR_57600; - break; - case 115200: - rate = CBR_115200; - break; - default: - rate = CBR_9600; - break; - } - - DCB dcbSerialParams = {0}; - - dcbSerialParams.DCBlength = sizeof(dcbSerialParams); - dcbSerialParams.BaudRate = rate; - dcbSerialParams.ByteSize = 8; - dcbSerialParams.StopBits = ONESTOPBIT; - dcbSerialParams.Parity = NOPARITY; - dcbSerialParams.fOutxCtsFlow = 0; - dcbSerialParams.fOutxDsrFlow = 0; - dcbSerialParams.fDtrControl = DTR_CONTROL_DISABLE; - dcbSerialParams.fDsrSensitivity = 0; - dcbSerialParams.fOutX = 0; - dcbSerialParams.fInX = 0; - dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE; - - SetCommState(hSerial, &dcbSerialParams); - - COMMTIMEOUTS timeouts = { 0 }; - - timeouts.ReadIntervalTimeout = 1; - timeouts.ReadTotalTimeoutConstant = 1; - timeouts.ReadTotalTimeoutMultiplier = 1; - - SetCommTimeouts(hSerial, &timeouts); - return hSerial; -} - -const unsigned char * const getPacketData( unsigned int *readerId, - unsigned int *command, - unsigned int *len, - unsigned int *checksum, - const unsigned char * const packet, - unsigned int length ) -{ - /* Exit early if we didn't get a good packet */ - if (length < MINIMUM_PACKET_LENGTH) { return NULL; } - - for( unsigned int i = 0; i <= length - MINIMUM_PACKET_LENGTH; i++ ) - { - const unsigned char * const data = &packet[i]; - - if( data[0] != SOM ) - { - /* Get command */ - *readerId = data[ID_LOCATION]; - *command = (data[COMMAND_HIGH_LOCATION] << 8) | data[COMMAND_LOW_LOCATION]; - *len = (data[LENGTH_HIGH_LOCATION] << 8) | data[LENGTH_LOW_LOCATION]; - *checksum = data[(*len) + HEADER_LENGTH]; - return data + HEADER_LENGTH; - } - } - - return NULL; -} - -static unsigned char calcPacketChecksum( const unsigned char * const data ) -{ - unsigned char *packet = (unsigned char *)data; - - /* Dirty */ - while( 1 ) - { - /* Get to start of packet */ - if( packet[0] == SOM ) - { - packet++; - continue; - } - - unsigned int length = ((packet[LENGTH_HIGH_LOCATION] << 8) | packet[LENGTH_LOW_LOCATION]); - unsigned int sum = 0; - - /* Skip SOM, not part of packet CRC */ - for( unsigned int i = 0; i < length + HEADER_LENGTH; i++ ) - { - sum += packet[i]; - } - - return sum & 0xFF; - } -} - -int probeReader( HANDLE serial ) -{ - const unsigned char packet_probe[] = { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, }; - unsigned char data[1024]; - DWORD length; - - WriteFile( serial, packet_probe, sizeof( packet_probe ), &length, 0 ); - ReadFile( serial, data, sizeof( data ), &length, 0 ); - - if (length < MIN_PROBES_RECEIVED) - { - return 0; - } - - for (unsigned int i = 0; i < length; i++) - { - if (data[i] != 0xAA) - { - return 0; - } - } - - /* Clear out any additional init the reader sends */ - while (true) - { - ReadFile( serial, data, sizeof( data ), &length, 0 ); - if (length == 0) - { - return 1; - } - } -} - -unsigned int getReaderCount( HANDLE serial ) -{ - const unsigned char count_probe[] = { 0xAA, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, }; - unsigned char data[1024]; - DWORD length; - - WriteFile( serial, count_probe, sizeof( count_probe ), &length, 0 ); - WAIT(); - ReadFile( serial, data, sizeof( data ), &length, 0 ); - - unsigned int readerId; - unsigned int command; - unsigned int len; - unsigned int checksum; - const unsigned char * const payload = getPacketData( &readerId, &command, &len, &checksum, data, length ); - - if( payload == NULL ) { return 0; } - if( len != 1 ) { return 0; } - - return payload[0]; -} - -void initReader( HANDLE serial, unsigned int id, int whichInit ) -{ - unsigned int init_length[3] = { 7, 7, 8 }; - unsigned char init_probe[3][8] = { { 0xAA, (id + 1), 0x00, 0x03, 0x00, 0x00, 0xFF, }, - { 0xAA, (id + 1), 0x01, 0x00, 0x00, 0x00, 0xFF, }, - { 0xAA, (id + 1), 0x01, 0x30, 0x00, 0x01, 0x00, 0xFF, } }; - unsigned char data[1024]; - DWORD length; - - /* Fix up checksum since ID is variable */ - init_probe[whichInit][init_length[whichInit] - 1] = calcPacketChecksum( init_probe[whichInit] ); - - WriteFile( serial, init_probe[whichInit], init_length[whichInit], &length, 0 ); - WAIT(); - ReadFile( serial, data, sizeof( data ), &length, 0 ); -} - -const char * const getReaderVersion( HANDLE serial, unsigned int id ) -{ - unsigned char version_probe[] = { 0xAA, (id + 1), 0x00, 0x02, 0x00, 0x00, 0xFF, }; - static char version[33] = { 0x00 }; - unsigned char data[1024]; - DWORD length; - - /* Fix up checksum since ID is variable */ - version_probe[6] = calcPacketChecksum( version_probe ); - - WriteFile( serial, version_probe, sizeof( version_probe ), &length, 0 ); - WAIT(); - ReadFile( serial, data, sizeof( data ), &length, 0 ); - - unsigned int readerId; - unsigned int command; - unsigned int len; - unsigned int checksum; - const unsigned char * const payload = getPacketData( &readerId, &command, &len, &checksum, data, length ); - - if( payload != NULL && len == 44 ) - { - memcpy( version, &payload[12], 32 ); - version[32] = 0x00; - - /* Spaces, for display */ - for( int i = 0; i < 32; i++ ) - { - if( version[i] == 0x00 ) { version[i] = 0x20; } - } - } - - return (const char * const)version; -} - -void getReaderState( HANDLE serial, unsigned int id, card_state_t *state, unsigned int *keypresses, unsigned char *cardId ) -{ - unsigned char state_probe[] = { 0xAA, (id + 1), 0x01, 0x34, 0x00, 0x01, 0x10, 0xFF, }; - unsigned char data[1024]; - DWORD length; - - /* Fix up checksum since ID is variable */ - state_probe[7] = calcPacketChecksum( state_probe ); - - /* Sane return */ - *keypresses = 0; - memset( cardId, 0, CARD_LENGTH ); - - WriteFile( serial, state_probe, sizeof( state_probe ), &length, 0 ); - WAIT(); - ReadFile( serial, data, sizeof( data ), &length, 0 ); - - unsigned int readerId; - unsigned int command; - unsigned int len; - unsigned int checksum; - const unsigned char * const payload = getPacketData( &readerId, &command, &len, &checksum, data, length ); - - if( payload != NULL && len == 16 ) - { - *keypresses = (payload[14] << 8) | payload[15]; - - *state = STATE_UNKNOWN; - switch( payload[1] ) - { - case 0x00: - *state = STATE_REMOVED; - break; - case 0x10: - *state = STATE_FRONT_SENSOR; - break; - case 0x30: - *state = STATE_INSERTED; - break; - default: - if( VERBOSE_DEBUG ) fprintf( stderr, "Unknown card state %02X!\n", payload[1] ); - break; - } - - if( payload[0] != 0x00 && payload[11] == 0x00 && *state == STATE_INSERTED ) - { - memcpy( cardId, &payload[2], CARD_LENGTH ); - *state = STATE_READ; - } - } -} - -void setReaderState( HANDLE serial, unsigned int id, reader_state_t state ) -{ - unsigned char state_request[] = { 0xAA, (id + 1), 0x01, 0x35, 0x00, 0x02, 0x10, 0xFF, 0xFF, }; - unsigned char data[1024]; - DWORD length; - - /* Set state */ - switch( state ) - { - case STATE_NOT_READY: - state_request[7] = 0x00; - break; - case STATE_GET_READY: - state_request[7] = 0x03; - break; - case STATE_READY: - state_request[7] = 0x11; - break; - case STATE_EJECT: - state_request[7] = 0x12; - break; - default: - if( VERBOSE_DEBUG ) fprintf( stderr, "Unknown reader state %02X!\n", state ); - state_request[7] = 0xFF; - break; - } - - /* Fix up checksum since ID is variable */ - state_request[8] = calcPacketChecksum( state_request ); - - WriteFile( serial, state_request, sizeof( state_request ), &length, 0 ); - WAIT(); - ReadFile( serial, data, sizeof( data ), &length, 0 ); -} - -void requestCardId( HANDLE serial, unsigned int id ) -{ - unsigned char id_request[] = { 0xAA, (id + 1), 0x01, 0x31, 0x00, 0x01, 0x10, 0xFF, }; - unsigned char data[1024]; - DWORD length; - - /* Fix up checksum since ID is variable */ - id_request[7] = calcPacketChecksum( id_request ); - - WriteFile( serial, id_request, sizeof( id_request ), &length, 0 ); - WAIT(); - ReadFile( serial, data, sizeof( data ), &length, 0 ); -} - -HANDLE InitReaders(unsigned int &readerCount) -{ - /* Walk serial chain, finding readers */ - HANDLE serial = NULL; - const _TCHAR *comport[4] = { L"COM1", L"COM2", L"COM3", L"COM4" }; - const unsigned int baudrate[2] = { 57600, 9600 }; - const char *pattern[4] = { "\b\b\b ", "\b\b\b.", ".", "." }; - unsigned int which = 3; - unsigned int pno = 0; - - /* Put a space into location where we will be backspacing, for laziness */ - NON_DEBUG_PRINTF( " " ); - DEBUG_PRINTF( "...\n" ); - - /* Try to open the reader indefinitely */ - while( 1 ) - { - /* Try next reader */ - which = (which + 1) % 4; - - for (unsigned int baud = 0; baud < 2; baud++) - { - NON_DEBUG_PRINTF( "%s", pattern[(pno++) % 4] ); - DEBUG_PRINTF("Attempting to probe readers on %ls,%d\n", comport[which], baudrate[baud]); - serial = OpenSerial( comport[which], baudrate[baud] ); - - if (serial == INVALID_HANDLE_VALUE) - { - DEBUG_PRINTF("Couldn't open com port!\n"); - serial = NULL; - LONGWAIT(); - continue; - } - - /* Ensure we start fresh */ - PurgeComm( serial, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR ); - - /* Init */ - for (int i = 0; i < INITIALIZATION_TRIES; i++) - { - if( probeReader( serial ) ) - { - /* Get count */ - unsigned int count = getReaderCount( serial ); - if (count > 0) - { - DEBUG_PRINTF( "Saw %d readers!\n", count ); - readerCount = count; - return serial; - } - } - - NON_DEBUG_PRINTF( "%s", pattern[(pno++) % 4] ); - } - - /* Failed, close */ - CloseHandle( serial ); - } - - /* Failed to probe this port, try again on next one */ - DEBUG_PRINTF("Failed to probe readers!\n"); - serial = NULL; - LONGWAIT(); - } -} - int _tmain(int argc, _TCHAR* argv[]) { /* Ensure command is good */ @@ -522,157 +66,118 @@ int _tmain(int argc, _TCHAR* argv[]) DEBUG_PRINTF( "Enabling debug mode!\n" ); /* Initialize menu */ - Menu menu = Menu(inifile, debug); + Menu *menu = new Menu(inifile); - if( menu.NumberOfEntries() < 1 ) + if( menu->NumberOfEntries() < 1 ) { fprintf( stderr, "No games configured to launch!\n" ); return 1; } /* Walk serial chain, finding readers */ - unsigned int count = 0; - printf( "Initializing readers" ); - HANDLE serial = InitReaders(count); - printf( "\n" ); - - /* Get version of all readers */ - for( unsigned int x = 0; x < count; x++ ) - { - /* Print out reader version in debug mode */ - DEBUG_PRINTF( "Reader %d returned version %s\n", x + 1, getReaderVersion( serial, x ) ); - - /* Walk init routine */ - initReader( serial, x, 0 ); - initReader( serial, x, 1 ); - initReader( serial, x, 2 ); - - /* Set ready for keys only */ - setReaderState( serial, x, STATE_EJECT ); - } + ACIO *readers = new ACIO(); /* Whether we're in read mode */ unsigned int read = 0; - /* For key input debouncing */ - unsigned int *old_keypresses = (unsigned int *)malloc(sizeof(unsigned int) * count); - /* Actual game to load */ char *path = NULL; bool selected = false; /* It may have taken a long time to init */ - menu.ResetTimeout(); + menu->ResetTimeout(); /* Display user prompts */ - menu.DisplayPrompt(); - menu.DisplayGames(); + menu->DisplayPrompt(); + menu->DisplayGames(); /* Loop until time's up, then boot */ while( !selected ) { - menu.Tick(); - if (menu.ShouldBootDefault()) + menu->Tick(); + if (menu->ShouldBootDefault()) { - printf( "No selection made, booting %s.\n", menu.GetSelectedName() ); - path = menu.GetSelectedPath(); + printf( "No selection made, booting %s.\n", menu->GetSelectedName() ); + path = menu->GetSelectedPath(); selected = true; break; } - for( unsigned int x = 0; x < count; x++ ) + for( unsigned int x = 0; x < readers->getCount(); x++ ) { unsigned int game = 0; - unsigned int currentpresses; - unsigned char cardId[CARD_LENGTH]; - card_state_t state; + bool request_reader_toggle = false; - /* Find new presses only */ - getReaderState( serial, x, &state, ¤tpresses, cardId ); - unsigned int keypresses = currentpresses & (~old_keypresses[x]); - old_keypresses[x] = currentpresses; - - if (keypresses & 0x0002 ) - { - game = 1; - } - if (keypresses & 0x0020 ) - { - game = 2; - } - if (keypresses & 0x0200 ) - { - game = 3; - } - if (keypresses & 0x0004 ) - { - game = 4; - } - if (keypresses & 0x0040 ) - { - game = 5; - } - if (keypresses & 0x0400 ) - { - game = 6; - } - if (keypresses & 0x0008 ) - { - game = 7; - } - if (keypresses & 0x0080 ) - { - game = 8; - } - if (keypresses & 0x0800 ) - { - game = 9; - } - if (keypresses & 0x0001 ) - { - menu.PageUp(); - } - if (keypresses & 0x0010 ) - { - menu.PageDown(); + switch(readers->getPressedKey(x)) { + case KEY_1: + game = 1; + break; + case KEY_2: + game = 2; + break; + case KEY_3: + game = 3; + break; + case KEY_4: + game = 4; + break; + case KEY_5: + game = 5; + break; + case KEY_6: + game = 6; + break; + case KEY_7: + game = 7; + break; + case KEY_8: + game = 8; + break; + case KEY_9: + game = 9; + break; + case KEY_0: + menu->PageUp(); + break; + case KEY_00: + menu->PageDown(); + break; + case KEY_BLANK: + request_reader_toggle = true; + break; } if (game != 0) { /* Chose a game! */ - if (menu.SelectGame(game)) + if (menu->SelectGame(game)) { /* Time to boot this game! */ - printf( "Booting %s.\n", menu.GetSelectedName() ); - path = menu.GetSelectedPath(); + printf( "Booting %s.\n", menu->GetSelectedName() ); + path = menu->GetSelectedPath(); selected = true; break; } } - if( CARD_READER_MODE && (keypresses & 0x0100) ) - { + if( CARD_READER_MODE && request_reader_toggle ) + { /* Pressed empty button, go into/out of card read mode */ if (!read) { printf( "Entering card read mode, insert card!\n" ); - for( unsigned int y = 0; y < count; y++ ) + for( unsigned int y = 0; y < readers->getCount(); y++ ) { - setReaderState( serial, y, STATE_EJECT ); - setReaderState( serial, y, STATE_NOT_READY ); - setReaderState( serial, y, STATE_GET_READY ); - setReaderState( serial, y, STATE_READY ); + readers->requestCardInsert(y); } read = 1; } else { printf( "Entering menu mode!\n" ); - for( unsigned int y = 0; y < count; y++ ) + for( unsigned int y = 0; y < readers->getCount(); y++ ) { - setReaderState( serial, y, STATE_EJECT ); - setReaderState( serial, y, STATE_NOT_READY ); - setReaderState( serial, y, STATE_GET_READY ); + readers->requestCardEject(y); } read = 0; } @@ -683,46 +188,28 @@ int _tmain(int argc, _TCHAR* argv[]) if (!read) { continue; } /* Reset time, so we stay here forever */ - menu.ResetTimeout(); + menu->ResetTimeout(); - if( state == STATE_INSERTED ) + if( readers->getCardInserted(x) ) { /* Ask for card */ - requestCardId( serial, x ); - } + unsigned char cardId[CARD_LENGTH]; + readers->getCardID(x, cardId); - if( state == STATE_READ ) - { /* Display card */ printf( "Card read: %02X%02X%02X%02X%02X%02X%02X%02X\n", cardId[0], cardId[1], cardId[2], cardId[3], cardId[4], cardId[5], cardId[6], cardId[7] ); - LONGWAIT(); - setReaderState( serial, x, STATE_EJECT ); - LONGWAIT(); - - /* Walk init routine again. For some reason, it reads the same - * card ID the second time through, so I just reinit. */ - initReader( serial, x, 0 ); - initReader( serial, x, 1 ); - initReader( serial, x, 2 ); - - setReaderState( serial, x, STATE_NOT_READY ); - setReaderState( serial, x, STATE_GET_READY ); - setReaderState( serial, x, STATE_READY ); + readers->requestCardEject(x); + readers->requestCardInsert(x); } } } - /* Close the reader so we can let the game talk to it */ - for( unsigned int y = 0; y < count; y++ ) - { - setReaderState( serial, y, STATE_EJECT ); - setReaderState( serial, y, STATE_NOT_READY ); - setReaderState( serial, y, STATE_GET_READY ); - } - CloseHandle( serial ); + /* Kill menu and ACIO connection */ + delete readers; + delete menu; if (path != NULL) { diff --git a/ACIOLauncher.vcproj b/ACIOLauncher.vcproj index a352a40..c6ec3a7 100644 --- a/ACIOLauncher.vcproj +++ b/ACIOLauncher.vcproj @@ -170,10 +170,18 @@ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > + + + + @@ -184,6 +192,14 @@ Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > + + + + diff --git a/Debug.cpp b/Debug.cpp new file mode 100644 index 0000000..c373c31 --- /dev/null +++ b/Debug.cpp @@ -0,0 +1,18 @@ +#include + +#include "Debug.h" + +/* Debug global which can be set via command line flag --debug */ +bool debug = false; + +void printHex(const unsigned char * const data, int length ) +{ + printf( "Length: %d bytes\n", length ); + + for( int x = 0; x < length; x++ ) + { + printf( "%02X ", data[x] ); + } + + printf( "\n" ); +} \ No newline at end of file diff --git a/Debug.h b/Debug.h new file mode 100644 index 0000000..f772ae5 --- /dev/null +++ b/Debug.h @@ -0,0 +1,16 @@ +#pragma once + +/* Define to 1 to get verbose debugging */ +#define VERBOSE_DEBUG 0 + +/* Debug global which can be set via command line flag --debug */ +extern bool debug; + +/* Debug macros that are enabled via the --debug flag */ +#define DEBUG_PRINTF(...) do { if(debug) { printf(__VA_ARGS__); } } while(0) +#define DEBUG_PRINT_HEX(data, length) do { if(debug) { printHex(data, length); } } while(0) + +/* Macros that are enabled when debug is not enabled */ +#define NON_DEBUG_PRINTF(...) do { if(!debug) { printf(__VA_ARGS__); } } while(0) + +void printHex(const unsigned char * const data, int length ); \ No newline at end of file diff --git a/Menu.cpp b/Menu.cpp index dd8f560..a7bf58a 100644 --- a/Menu.cpp +++ b/Menu.cpp @@ -2,12 +2,10 @@ #include #include "Menu.h" +#include "Debug.h" -Menu::Menu(_TCHAR *inifile, bool isDebugEnabled) +Menu::Menu(_TCHAR *inifile) { - /* Set up debug */ - debug = isDebugEnabled; - /* Read settings */ settings = LoadSettings( inifile, &num_programs ); diff --git a/Menu.h b/Menu.h index 7b2764f..59763ae 100644 --- a/Menu.h +++ b/Menu.h @@ -22,7 +22,7 @@ typedef struct class Menu { public: - Menu(_TCHAR *inifile, bool isDebugEnabled); + Menu(_TCHAR *inifile); ~Menu(void); unsigned int NumberOfEntries() { return num_programs; } @@ -38,7 +38,6 @@ class Menu void ResetTimeout(); bool ShouldBootDefault(); private: - bool debug; unsigned int num_programs; unsigned int start; unsigned int end;