Major refactor of ACIO Launcher.

Moved debug stuff into Debug.cpp/h.
Moved ACIO driver stuff into its own class.
This commit is contained in:
DragonMinded 2017-09-06 22:13:46 -07:00
parent 501b54b98e
commit 77a8d517be
8 changed files with 757 additions and 588 deletions

554
ACIO.cpp Normal file
View File

@ -0,0 +1,554 @@
#include <stdio.h>
#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, &currentpresses, 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, &currentpresses, 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();
}
}

81
ACIO.h Normal file
View File

@ -0,0 +1,81 @@
#pragma once
#include <tchar.h>
#include <windows.h>
/* 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 );
};

View File

@ -5,13 +5,9 @@
#include <stdio.h> #include <stdio.h>
#include <tchar.h> #include <tchar.h>
#include <windows.h> #include <windows.h>
#include "ACIO.h"
#include "Menu.h" #include "Menu.h"
#include "Debug.h"
#define WAIT() Sleep( 100 )
#define LONGWAIT() Sleep( 1000 )
/* Define to 1 to get verbose debugging */
#define VERBOSE_DEBUG 0
/** /**
* Define to 1 to enable card reader mode, currently only works with * Define to 1 to enable card reader mode, currently only works with
@ -22,458 +18,6 @@
*/ */
#define CARD_READER_MODE 0 #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[]) int _tmain(int argc, _TCHAR* argv[])
{ {
/* Ensure command is good */ /* Ensure command is good */
@ -522,157 +66,118 @@ int _tmain(int argc, _TCHAR* argv[])
DEBUG_PRINTF( "Enabling debug mode!\n" ); DEBUG_PRINTF( "Enabling debug mode!\n" );
/* Initialize menu */ /* 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" ); fprintf( stderr, "No games configured to launch!\n" );
return 1; return 1;
} }
/* Walk serial chain, finding readers */ /* Walk serial chain, finding readers */
unsigned int count = 0; ACIO *readers = new ACIO();
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 );
}
/* Whether we're in read mode */ /* Whether we're in read mode */
unsigned int read = 0; unsigned int read = 0;
/* For key input debouncing */
unsigned int *old_keypresses = (unsigned int *)malloc(sizeof(unsigned int) * count);
/* Actual game to load */ /* Actual game to load */
char *path = NULL; char *path = NULL;
bool selected = false; bool selected = false;
/* It may have taken a long time to init */ /* It may have taken a long time to init */
menu.ResetTimeout(); menu->ResetTimeout();
/* Display user prompts */ /* Display user prompts */
menu.DisplayPrompt(); menu->DisplayPrompt();
menu.DisplayGames(); menu->DisplayGames();
/* Loop until time's up, then boot */ /* Loop until time's up, then boot */
while( !selected ) while( !selected )
{ {
menu.Tick(); menu->Tick();
if (menu.ShouldBootDefault()) if (menu->ShouldBootDefault())
{ {
printf( "No selection made, booting %s.\n", menu.GetSelectedName() ); printf( "No selection made, booting %s.\n", menu->GetSelectedName() );
path = menu.GetSelectedPath(); path = menu->GetSelectedPath();
selected = true; selected = true;
break; break;
} }
for( unsigned int x = 0; x < count; x++ ) for( unsigned int x = 0; x < readers->getCount(); x++ )
{ {
unsigned int game = 0; unsigned int game = 0;
unsigned int currentpresses; bool request_reader_toggle = false;
unsigned char cardId[CARD_LENGTH];
card_state_t state;
/* Find new presses only */ switch(readers->getPressedKey(x)) {
getReaderState( serial, x, &state, &currentpresses, cardId ); case KEY_1:
unsigned int keypresses = currentpresses & (~old_keypresses[x]); game = 1;
old_keypresses[x] = currentpresses; break;
case KEY_2:
if (keypresses & 0x0002 ) game = 2;
{ break;
game = 1; case KEY_3:
} game = 3;
if (keypresses & 0x0020 ) break;
{ case KEY_4:
game = 2; game = 4;
} break;
if (keypresses & 0x0200 ) case KEY_5:
{ game = 5;
game = 3; break;
} case KEY_6:
if (keypresses & 0x0004 ) game = 6;
{ break;
game = 4; case KEY_7:
} game = 7;
if (keypresses & 0x0040 ) break;
{ case KEY_8:
game = 5; game = 8;
} break;
if (keypresses & 0x0400 ) case KEY_9:
{ game = 9;
game = 6; break;
} case KEY_0:
if (keypresses & 0x0008 ) menu->PageUp();
{ break;
game = 7; case KEY_00:
} menu->PageDown();
if (keypresses & 0x0080 ) break;
{ case KEY_BLANK:
game = 8; request_reader_toggle = true;
} break;
if (keypresses & 0x0800 )
{
game = 9;
}
if (keypresses & 0x0001 )
{
menu.PageUp();
}
if (keypresses & 0x0010 )
{
menu.PageDown();
} }
if (game != 0) if (game != 0)
{ {
/* Chose a game! */ /* Chose a game! */
if (menu.SelectGame(game)) if (menu->SelectGame(game))
{ {
/* Time to boot this game! */ /* Time to boot this game! */
printf( "Booting %s.\n", menu.GetSelectedName() ); printf( "Booting %s.\n", menu->GetSelectedName() );
path = menu.GetSelectedPath(); path = menu->GetSelectedPath();
selected = true; selected = true;
break; break;
} }
} }
if( CARD_READER_MODE && (keypresses & 0x0100) ) if( CARD_READER_MODE && request_reader_toggle )
{ {
/* Pressed empty button, go into/out of card read mode */ /* Pressed empty button, go into/out of card read mode */
if (!read) if (!read)
{ {
printf( "Entering card read mode, insert card!\n" ); 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 ); readers->requestCardInsert(y);
setReaderState( serial, y, STATE_NOT_READY );
setReaderState( serial, y, STATE_GET_READY );
setReaderState( serial, y, STATE_READY );
} }
read = 1; read = 1;
} }
else else
{ {
printf( "Entering menu mode!\n" ); 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 ); readers->requestCardEject(y);
setReaderState( serial, y, STATE_NOT_READY );
setReaderState( serial, y, STATE_GET_READY );
} }
read = 0; read = 0;
} }
@ -683,46 +188,28 @@ int _tmain(int argc, _TCHAR* argv[])
if (!read) { continue; } if (!read) { continue; }
/* Reset time, so we stay here forever */ /* Reset time, so we stay here forever */
menu.ResetTimeout(); menu->ResetTimeout();
if( state == STATE_INSERTED ) if( readers->getCardInserted(x) )
{ {
/* Ask for card */ /* Ask for card */
requestCardId( serial, x ); unsigned char cardId[CARD_LENGTH];
} readers->getCardID(x, cardId);
if( state == STATE_READ )
{
/* Display card */ /* Display card */
printf( "Card read: %02X%02X%02X%02X%02X%02X%02X%02X\n", printf( "Card read: %02X%02X%02X%02X%02X%02X%02X%02X\n",
cardId[0], cardId[1], cardId[2], cardId[3], cardId[0], cardId[1], cardId[2], cardId[3],
cardId[4], cardId[5], cardId[6], cardId[7] ); cardId[4], cardId[5], cardId[6], cardId[7] );
LONGWAIT(); readers->requestCardEject(x);
setReaderState( serial, x, STATE_EJECT ); readers->requestCardInsert(x);
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 );
} }
} }
} }
/* Close the reader so we can let the game talk to it */ /* Kill menu and ACIO connection */
for( unsigned int y = 0; y < count; y++ ) delete readers;
{ delete menu;
setReaderState( serial, y, STATE_EJECT );
setReaderState( serial, y, STATE_NOT_READY );
setReaderState( serial, y, STATE_GET_READY );
}
CloseHandle( serial );
if (path != NULL) if (path != NULL)
{ {

View File

@ -170,10 +170,18 @@
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
> >
<File
RelativePath=".\ACIO.cpp"
>
</File>
<File <File
RelativePath=".\ACIOLauncher.cpp" RelativePath=".\ACIOLauncher.cpp"
> >
</File> </File>
<File
RelativePath=".\Debug.cpp"
>
</File>
<File <File
RelativePath=".\Menu.cpp" RelativePath=".\Menu.cpp"
> >
@ -184,6 +192,14 @@
Filter="h;hpp;hxx;hm;inl;inc;xsd" Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
> >
<File
RelativePath=".\ACIO.h"
>
</File>
<File
RelativePath=".\Debug.h"
>
</File>
<File <File
RelativePath=".\Menu.h" RelativePath=".\Menu.h"
> >

18
Debug.cpp Normal file
View File

@ -0,0 +1,18 @@
#include <stdio.h>
#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" );
}

16
Debug.h Normal file
View File

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

View File

@ -2,12 +2,10 @@
#include <windows.h> #include <windows.h>
#include "Menu.h" #include "Menu.h"
#include "Debug.h"
Menu::Menu(_TCHAR *inifile, bool isDebugEnabled) Menu::Menu(_TCHAR *inifile)
{ {
/* Set up debug */
debug = isDebugEnabled;
/* Read settings */ /* Read settings */
settings = LoadSettings( inifile, &num_programs ); settings = LoadSettings( inifile, &num_programs );

3
Menu.h
View File

@ -22,7 +22,7 @@ typedef struct
class Menu class Menu
{ {
public: public:
Menu(_TCHAR *inifile, bool isDebugEnabled); Menu(_TCHAR *inifile);
~Menu(void); ~Menu(void);
unsigned int NumberOfEntries() { return num_programs; } unsigned int NumberOfEntries() { return num_programs; }
@ -38,7 +38,6 @@ class Menu
void ResetTimeout(); void ResetTimeout();
bool ShouldBootDefault(); bool ShouldBootDefault();
private: private:
bool debug;
unsigned int num_programs; unsigned int num_programs;
unsigned int start; unsigned int start;
unsigned int end; unsigned int end;