mirror of
https://github.com/AkaiiKitsune/TAL_CardReader
synced 2024-11-27 18:30:48 +01:00
Added support for SmartCard readers
Added configuration file; Fixed a possible race condition; Cleaned up the mess that used to be my console logs;
This commit is contained in:
parent
1cb7165bdf
commit
0f8ce56d96
2
.gitignore
vendored
2
.gitignore
vendored
@ -56,3 +56,5 @@ dkms.conf
|
|||||||
|
|
||||||
subprojects/minhook
|
subprojects/minhook
|
||||||
subprojects/tomlc99
|
subprojects/tomlc99
|
||||||
|
|
||||||
|
bin/*
|
16
README.md
16
README.md
@ -1,12 +1,12 @@
|
|||||||
# TAL CardIO
|
# TAL CardReader
|
||||||
|
|
||||||
This is a plugin that allows you to use USB HID card readers (cardio) with [TaikoArcadeLoader](https://github.com/BroGamer4256/TaikoArcadeLoader)
|
This is a plugin that allows you to use USB HID card (cardio) and SmartCard readers with [TaikoArcadeLoader](https://github.com/BroGamer4256/TaikoArcadeLoader)
|
||||||
|
|
||||||
# Acknowledgments
|
# Acknowledgments
|
||||||
|
|
||||||
This is a plugin destined to be used with [BroGamer4256](https://github.com/BroGamer4256)'s TaikoArcadeLoader.
|
This is a plugin destined to be used with [BroGamer4256](https://github.com/BroGamer4256)'s TaikoArcadeLoader.
|
||||||
|
|
||||||
CardIO implementation taken from [Spicetools](https://github.com/spicetools/spicetools) and adapted with the help of [CrazyRedMachine](https://github.com/CrazyRedMachine) (Huge thanks!)
|
CardIO and SmartCard implementation taken from [Spicetools](https://github.com/spicetools/spicetools) and adapted with the help of [CrazyRedMachine](https://github.com/CrazyRedMachine) (Huge thanks!)
|
||||||
|
|
||||||
Initial project setup done with the help of [Stepland](https://github.com/Stepland)
|
Initial project setup done with the help of [Stepland](https://github.com/Stepland)
|
||||||
|
|
||||||
@ -19,4 +19,12 @@ To build this, you'll need two things :
|
|||||||
|
|
||||||
Once you've edited your build64.bat file to point to your local installation of the VS2022 build tools, you should be good to go.
|
Once you've edited your build64.bat file to point to your local installation of the VS2022 build tools, you should be good to go.
|
||||||
|
|
||||||
Copy the cardio.dll to your TAL's plugin folder. You should be able to see the plugin initializing in the game's console.
|
Copy cardreader.dll to your TAL's plugin folder. You should be able to see the plugin initializing in the game's console.
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
|
||||||
|
- using_smartcard (default : false)
|
||||||
|
_Reader type defaults to CardIO, if you want to use a SmartCard reader, set this to true_.
|
||||||
|
|
||||||
|
- read_cooldown (default : 20)
|
||||||
|
_Cooldown between each read attempt, a low value (below 15) can lead to game crashes, a high value (above 500) will break SmartCard readers_.
|
||||||
|
@ -7,5 +7,6 @@ meson configure build64
|
|||||||
ninja -C build64
|
ninja -C build64
|
||||||
|
|
||||||
mkdir bin
|
mkdir bin
|
||||||
copy build64\src\cardio.dll bin\cardio.dll
|
copy build64\src\cardreader.dll bin\cardreader.dll
|
||||||
|
copy dist\cardreader.toml bin\cardreader.toml
|
||||||
pause
|
pause
|
8
dist/cardreader.toml
vendored
Normal file
8
dist/cardreader.toml
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Smart Card Reader ==================
|
||||||
|
using_smartcard = false
|
||||||
|
|
||||||
|
# Card Read Cooldown =================
|
||||||
|
read_cooldown = 20
|
||||||
|
# Default is 20. Set to a higher value if you notice any weird behaviour.
|
||||||
|
# Too small of a value can lead to weird behaviour and game crashes.
|
||||||
|
# Too high of a value will result in an unresponsive card reader.
|
@ -1,7 +1,7 @@
|
|||||||
project(
|
project(
|
||||||
'TAL_CardIO',
|
'TAL_CardReader',
|
||||||
'c',
|
'c',
|
||||||
version : '0.1.0'
|
version : '0.2.0'
|
||||||
)
|
)
|
||||||
|
|
||||||
inc = include_directories('.')
|
inc = include_directories('.')
|
||||||
|
@ -1,162 +0,0 @@
|
|||||||
#include "cardio.h"
|
|
||||||
char module[] = "TAL HID";
|
|
||||||
|
|
||||||
static bool CARDIO_RUNNER_INITIALIZED = false;
|
|
||||||
|
|
||||||
static HANDLE CARDIO_POLL_THREAD;
|
|
||||||
static bool CARDIO_POLL_STOP_FLAG;
|
|
||||||
static bool waitingForTouch = false;
|
|
||||||
static bool HasCard = false;
|
|
||||||
|
|
||||||
typedef void (*callbackTouch)(i32, i32, u8[168], u64);
|
|
||||||
callbackTouch touchCallback;
|
|
||||||
u64 touchData;
|
|
||||||
char AccessID[21] = "00000000000000000001";
|
|
||||||
static u8 cardData[168] = {0x01, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x2E, 0x58, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x5C, 0x97, 0x44, 0xF0, 0x88, 0x04, 0x00, 0x43, 0x26, 0x2C, 0x33, 0x00, 0x04,
|
|
||||||
0x06, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
|
|
||||||
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30,
|
|
||||||
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x42, 0x47, 0x49, 0x43, 0x36,
|
|
||||||
0x00, 0x00, 0xFA, 0xE9, 0x69, 0x00, 0xF6, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
||||||
|
|
||||||
void PollReader()
|
|
||||||
{
|
|
||||||
if (HasCard) // A card scan is already in progress
|
|
||||||
{
|
|
||||||
Sleep(1000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update HID devices
|
|
||||||
EnterCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
|
||||||
for (size_t device_no = 0; device_no < CARDIO_HID_CONTEXTS_LENGTH; device_no++)
|
|
||||||
{
|
|
||||||
struct cardio_hid_device *device = &CARDIO_HID_CONTEXTS[device_no];
|
|
||||||
|
|
||||||
// get status
|
|
||||||
cardio_hid_poll_value_t status = cardio_hid_device_poll(device);
|
|
||||||
if (status == HID_POLL_CARD_READY)
|
|
||||||
{
|
|
||||||
|
|
||||||
// read card
|
|
||||||
if (cardio_hid_device_read(device) == HID_CARD_NONE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// if card not empty
|
|
||||||
if (*((uint64_t *)&device->u.usage_value[0]) > 0)
|
|
||||||
{
|
|
||||||
printWarning("%s (%s): Read card %02X%02X%02X%02X%02X%02X%02X%02X\n", __func__, module, device->u.usage_value[0], device->u.usage_value[1], device->u.usage_value[2], device->u.usage_value[3], device->u.usage_value[4], device->u.usage_value[5], device->u.usage_value[6], device->u.usage_value[7]);
|
|
||||||
|
|
||||||
if (waitingForTouch)
|
|
||||||
{
|
|
||||||
// Properly format the AccessID
|
|
||||||
u64 ReversedAccessID;
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
ReversedAccessID = (ReversedAccessID << 8) | device->u.usage_value[i];
|
|
||||||
sprintf(AccessID, "%020llu", ReversedAccessID);
|
|
||||||
|
|
||||||
HasCard = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
|
||||||
Sleep(15);
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned int __stdcall cardio_poll_thread_proc(void *ctx)
|
|
||||||
{
|
|
||||||
while (!CARDIO_POLL_STOP_FLAG)
|
|
||||||
{
|
|
||||||
PollReader();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cardio_runner_start()
|
|
||||||
{
|
|
||||||
// initialize
|
|
||||||
if (!CARDIO_RUNNER_INITIALIZED)
|
|
||||||
{
|
|
||||||
CARDIO_RUNNER_INITIALIZED = true;
|
|
||||||
printWarning("%s (%s): Initializing CARDIO\n", __func__, module);
|
|
||||||
|
|
||||||
// initialize
|
|
||||||
if (!cardio_hid_init())
|
|
||||||
{
|
|
||||||
printWarning("%s (%s): Couldn't init CARDIO HID\n", __func__, module);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// scan HID devices
|
|
||||||
if (!cardio_hid_scan())
|
|
||||||
{
|
|
||||||
printWarning("%s (%s): Couldn't scan for CARDIO devices\n", __func__, module);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void cardio_runner_stop()
|
|
||||||
{
|
|
||||||
// shutdown HID
|
|
||||||
cardio_hid_close();
|
|
||||||
|
|
||||||
// set initialized to false
|
|
||||||
CARDIO_RUNNER_INITIALIZED = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Init()
|
|
||||||
{
|
|
||||||
printWarning("%s (%s): Starting HID Service...\n", __func__, module);
|
|
||||||
// Find and initialize reader(s)
|
|
||||||
cardio_runner_start();
|
|
||||||
|
|
||||||
// Start reader thread
|
|
||||||
CARDIO_POLL_STOP_FLAG = false;
|
|
||||||
CARDIO_POLL_THREAD = (HANDLE)_beginthreadex(
|
|
||||||
NULL,
|
|
||||||
0,
|
|
||||||
cardio_poll_thread_proc,
|
|
||||||
NULL,
|
|
||||||
0,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Update()
|
|
||||||
{
|
|
||||||
if (HasCard)
|
|
||||||
{
|
|
||||||
HasCard = false;
|
|
||||||
|
|
||||||
// Generating a ChipID Derived from the AccessID
|
|
||||||
char ChipID[33] = "000000000000";
|
|
||||||
strcat(ChipID, AccessID);
|
|
||||||
|
|
||||||
// Insert card in game
|
|
||||||
printWarning("%s (%s): Inserting Card %s, with ChipID %s\n", __func__, module, AccessID, ChipID);
|
|
||||||
memcpy(cardData + 0x2C, ChipID, 33);
|
|
||||||
memcpy(cardData + 0x50, AccessID, 21);
|
|
||||||
touchCallback(0, 0, cardData, touchData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WaitTouch(i32 (*callback)(i32, i32, u8[168], u64), u64 data)
|
|
||||||
{
|
|
||||||
printWarning("%s (%s): Waiting for touch\n", __func__, module);
|
|
||||||
waitingForTouch = true;
|
|
||||||
touchCallback = callback;
|
|
||||||
touchData = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exit()
|
|
||||||
{
|
|
||||||
cardio_runner_stop();
|
|
||||||
CARDIO_POLL_STOP_FLAG = true;
|
|
||||||
WaitForSingleObject(CARDIO_POLL_THREAD, INFINITE);
|
|
||||||
CloseHandle(CARDIO_POLL_THREAD);
|
|
||||||
CARDIO_POLL_THREAD = NULL;
|
|
||||||
CARDIO_POLL_STOP_FLAG = false;
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
LIBRARY cardio
|
LIBRARY cardreader
|
||||||
|
|
||||||
EXPORTS
|
EXPORTS
|
||||||
Init
|
Init
|
@ -38,7 +38,8 @@ CRITICAL_SECTION CARDIO_HID_CRIT_SECTION;
|
|||||||
struct cardio_hid_device *CARDIO_HID_CONTEXTS = NULL;
|
struct cardio_hid_device *CARDIO_HID_CONTEXTS = NULL;
|
||||||
size_t CARDIO_HID_CONTEXTS_LENGTH = 0;
|
size_t CARDIO_HID_CONTEXTS_LENGTH = 0;
|
||||||
|
|
||||||
void hid_ctx_init(struct cardio_hid_device *ctx) {
|
void hid_ctx_init(struct cardio_hid_device *ctx)
|
||||||
|
{
|
||||||
ctx->dev_path = NULL;
|
ctx->dev_path = NULL;
|
||||||
ctx->dev_handle = INVALID_HANDLE_VALUE;
|
ctx->dev_handle = INVALID_HANDLE_VALUE;
|
||||||
ctx->initialized = FALSE;
|
ctx->initialized = FALSE;
|
||||||
@ -54,30 +55,36 @@ void hid_ctx_init(struct cardio_hid_device *ctx) {
|
|||||||
memset(&ctx->caps, 0, sizeof(HIDP_CAPS));
|
memset(&ctx->caps, 0, sizeof(HIDP_CAPS));
|
||||||
}
|
}
|
||||||
|
|
||||||
void hid_ctx_free(struct cardio_hid_device *ctx) {
|
void hid_ctx_free(struct cardio_hid_device *ctx)
|
||||||
if (ctx->dev_path != NULL) {
|
{
|
||||||
|
if (ctx->dev_path != NULL)
|
||||||
|
{
|
||||||
HeapFree(GetProcessHeap(), 0, ctx->dev_path);
|
HeapFree(GetProcessHeap(), 0, ctx->dev_path);
|
||||||
ctx->dev_path = NULL;
|
ctx->dev_path = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->dev_handle != INVALID_HANDLE_VALUE) {
|
if (ctx->dev_handle != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
CancelIo(ctx->dev_handle);
|
CancelIo(ctx->dev_handle);
|
||||||
CloseHandle(ctx->dev_handle);
|
CloseHandle(ctx->dev_handle);
|
||||||
ctx->dev_handle = INVALID_HANDLE_VALUE;
|
ctx->dev_handle = INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->pp_data != NULL) {
|
if (ctx->pp_data != NULL)
|
||||||
|
{
|
||||||
HidD_FreePreparsedData(ctx->pp_data);
|
HidD_FreePreparsedData(ctx->pp_data);
|
||||||
ctx->pp_data = NULL;
|
ctx->pp_data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->collection != NULL) {
|
if (ctx->collection != NULL)
|
||||||
|
{
|
||||||
HeapFree(GetProcessHeap(), 0, ctx->collection);
|
HeapFree(GetProcessHeap(), 0, ctx->collection);
|
||||||
ctx->collection = NULL;
|
ctx->collection = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void hid_ctx_reset(struct cardio_hid_device *ctx) {
|
void hid_ctx_reset(struct cardio_hid_device *ctx)
|
||||||
|
{
|
||||||
ctx->initialized = FALSE;
|
ctx->initialized = FALSE;
|
||||||
ctx->io_pending = FALSE;
|
ctx->io_pending = FALSE;
|
||||||
ctx->read_size = 0;
|
ctx->read_size = 0;
|
||||||
@ -91,31 +98,37 @@ void hid_ctx_reset(struct cardio_hid_device *ctx) {
|
|||||||
memset(&ctx->caps, 0, sizeof(HIDP_CAPS));
|
memset(&ctx->caps, 0, sizeof(HIDP_CAPS));
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL cardio_hid_init() {
|
BOOL cardio_hid_init()
|
||||||
|
{
|
||||||
size_t i, contexts_size;
|
size_t i, contexts_size;
|
||||||
|
|
||||||
InitializeCriticalSectionAndSpinCount(&CARDIO_HID_CRIT_SECTION, 0x00000400);
|
InitializeCriticalSectionAndSpinCount(&CARDIO_HID_CRIT_SECTION, 0x00000400);
|
||||||
|
|
||||||
contexts_size = DEFAULT_ALLOCATED_CONTEXTS * sizeof(struct cardio_hid_device);
|
contexts_size = DEFAULT_ALLOCATED_CONTEXTS * sizeof(struct cardio_hid_device);
|
||||||
CARDIO_HID_CONTEXTS = (struct cardio_hid_device *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, contexts_size);
|
CARDIO_HID_CONTEXTS = (struct cardio_hid_device *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, contexts_size);
|
||||||
if (CARDIO_HID_CONTEXTS == NULL) {
|
if (CARDIO_HID_CONTEXTS == NULL)
|
||||||
|
{
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
CARDIO_HID_CONTEXTS_LENGTH = DEFAULT_ALLOCATED_CONTEXTS;
|
CARDIO_HID_CONTEXTS_LENGTH = DEFAULT_ALLOCATED_CONTEXTS;
|
||||||
|
|
||||||
for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) {
|
for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++)
|
||||||
|
{
|
||||||
hid_ctx_init(&CARDIO_HID_CONTEXTS[i]);
|
hid_ctx_init(&CARDIO_HID_CONTEXTS[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cardio_hid_close() {
|
void cardio_hid_close()
|
||||||
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
if (CARDIO_HID_CONTEXTS_LENGTH > 0) {
|
if (CARDIO_HID_CONTEXTS_LENGTH > 0)
|
||||||
for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) {
|
{
|
||||||
|
for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++)
|
||||||
|
{
|
||||||
hid_ctx_free(&CARDIO_HID_CONTEXTS[i]);
|
hid_ctx_free(&CARDIO_HID_CONTEXTS[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,14 +140,17 @@ void cardio_hid_close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL cardio_hid_add_device(LPCWSTR device_path) {
|
BOOL cardio_hid_add_device(LPCWSTR device_path)
|
||||||
|
{
|
||||||
BOOL res = FALSE;
|
BOOL res = FALSE;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
EnterCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
EnterCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
||||||
|
|
||||||
for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) {
|
for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++)
|
||||||
if (!CARDIO_HID_CONTEXTS[i].initialized) {
|
{
|
||||||
|
if (!CARDIO_HID_CONTEXTS[i].initialized)
|
||||||
|
{
|
||||||
res = cardio_hid_scan_device(&CARDIO_HID_CONTEXTS[i], device_path);
|
res = cardio_hid_scan_device(&CARDIO_HID_CONTEXTS[i], device_path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -145,16 +161,19 @@ BOOL cardio_hid_add_device(LPCWSTR device_path) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL cardio_hid_remove_device(LPCWSTR device_path) {
|
BOOL cardio_hid_remove_device(LPCWSTR device_path)
|
||||||
|
{
|
||||||
BOOL res = FALSE;
|
BOOL res = FALSE;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
EnterCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
EnterCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
||||||
|
|
||||||
for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) {
|
for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++)
|
||||||
|
{
|
||||||
// The device paths in `hid_scan` are partially lower-case, so perform a
|
// The device paths in `hid_scan` are partially lower-case, so perform a
|
||||||
// case-insensitive comparison here
|
// case-insensitive comparison here
|
||||||
if (CARDIO_HID_CONTEXTS[i].initialized && (_wcsicmp(device_path, CARDIO_HID_CONTEXTS[i].dev_path) == 0)) {
|
if (CARDIO_HID_CONTEXTS[i].initialized && (_wcsicmp(device_path, CARDIO_HID_CONTEXTS[i].dev_path) == 0))
|
||||||
|
{
|
||||||
hid_ctx_reset(&CARDIO_HID_CONTEXTS[i]);
|
hid_ctx_reset(&CARDIO_HID_CONTEXTS[i]);
|
||||||
res = TRUE;
|
res = TRUE;
|
||||||
break;
|
break;
|
||||||
@ -169,12 +188,14 @@ BOOL cardio_hid_remove_device(LPCWSTR device_path) {
|
|||||||
/*
|
/*
|
||||||
* Scan HID device to see if it is a HID reader
|
* Scan HID device to see if it is a HID reader
|
||||||
*/
|
*/
|
||||||
BOOL cardio_hid_scan_device(struct cardio_hid_device *ctx, LPCWSTR device_path) {
|
BOOL cardio_hid_scan_device(struct cardio_hid_device *ctx, LPCWSTR device_path)
|
||||||
|
{
|
||||||
NTSTATUS res;
|
NTSTATUS res;
|
||||||
|
|
||||||
size_t dev_path_size = (wcslen(device_path) + 1) * sizeof(WCHAR);
|
size_t dev_path_size = (wcslen(device_path) + 1) * sizeof(WCHAR);
|
||||||
ctx->dev_path = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, dev_path_size);
|
ctx->dev_path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dev_path_size);
|
||||||
if (ctx->dev_path == NULL) {
|
if (ctx->dev_path == NULL)
|
||||||
|
{
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,33 +209,40 @@ BOOL cardio_hid_scan_device(struct cardio_hid_device *ctx, LPCWSTR device_path)
|
|||||||
OPEN_EXISTING,
|
OPEN_EXISTING,
|
||||||
FILE_FLAG_OVERLAPPED,
|
FILE_FLAG_OVERLAPPED,
|
||||||
NULL);
|
NULL);
|
||||||
if (ctx->dev_handle == INVALID_HANDLE_VALUE) {
|
if (ctx->dev_handle == INVALID_HANDLE_VALUE)
|
||||||
printWarning ("%s (%s): could not open device: %ls\n", __func__, module, ctx->dev_path);
|
{
|
||||||
|
printError("%s (%s): Could not open device: %ls\n", __func__, module, ctx->dev_path);
|
||||||
HeapFree(GetProcessHeap(), 0, ctx->dev_path);
|
HeapFree(GetProcessHeap(), 0, ctx->dev_path);
|
||||||
ctx->dev_path = NULL;
|
ctx->dev_path = NULL;
|
||||||
ctx->dev_handle = INVALID_HANDLE_VALUE;
|
ctx->dev_handle = INVALID_HANDLE_VALUE;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!HidD_GetPreparsedData(ctx->dev_handle, &ctx->pp_data)) {
|
if (!HidD_GetPreparsedData(ctx->dev_handle, &ctx->pp_data))
|
||||||
|
{
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = HidP_GetCaps(ctx->pp_data, &ctx->caps);
|
res = HidP_GetCaps(ctx->pp_data, &ctx->caps);
|
||||||
if (res != HIDP_STATUS_SUCCESS) {
|
if (res != HIDP_STATUS_SUCCESS)
|
||||||
|
{
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0xffca is the card reader usage page ID
|
// 0xffca is the card reader usage page ID
|
||||||
if (ctx->caps.UsagePage != CARD_READER_USAGE_PAGE) {
|
if (ctx->caps.UsagePage != CARD_READER_USAGE_PAGE)
|
||||||
|
{
|
||||||
goto end;
|
goto end;
|
||||||
} else if (ctx->caps.NumberInputValueCaps == 0) {
|
}
|
||||||
|
else if (ctx->caps.NumberInputValueCaps == 0)
|
||||||
|
{
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
ctx->collection_length = ctx->caps.NumberInputValueCaps;
|
ctx->collection_length = ctx->caps.NumberInputValueCaps;
|
||||||
ctx->collection = (HIDP_VALUE_CAPS *) HeapAlloc(GetProcessHeap(), 0,
|
ctx->collection = (HIDP_VALUE_CAPS *)HeapAlloc(GetProcessHeap(), 0,
|
||||||
ctx->collection_length * sizeof(HIDP_VALUE_CAPS));
|
ctx->collection_length * sizeof(HIDP_VALUE_CAPS));
|
||||||
if (ctx->collection == NULL) {
|
if (ctx->collection == NULL)
|
||||||
|
{
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
res = HidP_GetValueCaps(
|
res = HidP_GetValueCaps(
|
||||||
@ -222,15 +250,16 @@ BOOL cardio_hid_scan_device(struct cardio_hid_device *ctx, LPCWSTR device_path)
|
|||||||
ctx->collection,
|
ctx->collection,
|
||||||
&ctx->collection_length,
|
&ctx->collection_length,
|
||||||
ctx->pp_data);
|
ctx->pp_data);
|
||||||
if (res != HIDP_STATUS_SUCCESS) {
|
if (res != HIDP_STATUS_SUCCESS)
|
||||||
|
{
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
printWarning ("%s (%s): detected reader: %ls\n", __func__, module, ctx->dev_path);
|
printInfo("%s (%s): Detected reader: %ls\n", __func__, module, ctx->dev_path);
|
||||||
ctx->initialized = TRUE;
|
ctx->initialized = TRUE;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
end:
|
end:
|
||||||
hid_ctx_reset(ctx);
|
hid_ctx_reset(ctx);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@ -242,7 +271,8 @@ BOOL cardio_hid_scan_device(struct cardio_hid_device *ctx, LPCWSTR device_path)
|
|||||||
* Usage 0x41 => ISO_15693
|
* Usage 0x41 => ISO_15693
|
||||||
* Usage 0x42 => ISO_18092 (FeliCa)
|
* Usage 0x42 => ISO_18092 (FeliCa)
|
||||||
*/
|
*/
|
||||||
BOOL cardio_hid_scan() {
|
BOOL cardio_hid_scan()
|
||||||
|
{
|
||||||
BOOL res = TRUE;
|
BOOL res = TRUE;
|
||||||
SP_DEVINFO_DATA devinfo_data;
|
SP_DEVINFO_DATA devinfo_data;
|
||||||
SP_DEVICE_INTERFACE_DATA device_interface_data;
|
SP_DEVICE_INTERFACE_DATA device_interface_data;
|
||||||
@ -259,7 +289,8 @@ BOOL cardio_hid_scan() {
|
|||||||
// HID collection opening needs `DIGCF_DEVICEINTERFACE` and ignore
|
// HID collection opening needs `DIGCF_DEVICEINTERFACE` and ignore
|
||||||
// disconnected devices
|
// disconnected devices
|
||||||
device_info_set = SetupDiGetClassDevs(&hid_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
device_info_set = SetupDiGetClassDevs(&hid_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||||
if (device_info_set == INVALID_HANDLE_VALUE) {
|
if (device_info_set == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
res = FALSE;
|
res = FALSE;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
@ -270,44 +301,51 @@ BOOL cardio_hid_scan() {
|
|||||||
|
|
||||||
// `SetupDiEnumDeviceInterfaces` must come before `SetupDiEnumDeviceInfo`
|
// `SetupDiEnumDeviceInterfaces` must come before `SetupDiEnumDeviceInfo`
|
||||||
// else `SetupDiEnumDeviceInterfaces` will fail with error 259
|
// else `SetupDiEnumDeviceInterfaces` will fail with error 259
|
||||||
while (SetupDiEnumDeviceInterfaces(device_info_set, NULL, &hid_guid, device_index, &device_interface_data)) {
|
while (SetupDiEnumDeviceInterfaces(device_info_set, NULL, &hid_guid, device_index, &device_interface_data))
|
||||||
|
{
|
||||||
|
|
||||||
// Get the required size
|
// Get the required size
|
||||||
if (SetupDiGetDeviceInterfaceDetailW(device_info_set, &device_interface_data, NULL, 0, &dwSize, NULL)) {
|
if (SetupDiGetDeviceInterfaceDetailW(device_info_set, &device_interface_data, NULL, 0, &dwSize, NULL))
|
||||||
|
{
|
||||||
goto cont;
|
goto cont;
|
||||||
}
|
}
|
||||||
|
|
||||||
device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *) HeapAlloc(GetProcessHeap(), 0, dwSize);
|
device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *)HeapAlloc(GetProcessHeap(), 0, dwSize);
|
||||||
if (device_interface_detail_data == NULL) {
|
if (device_interface_detail_data == NULL)
|
||||||
|
{
|
||||||
goto cont;
|
goto cont;
|
||||||
}
|
}
|
||||||
|
|
||||||
device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
|
device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
|
||||||
|
|
||||||
if (!SetupDiGetDeviceInterfaceDetailW(device_info_set, &device_interface_data, device_interface_detail_data, dwSize, NULL, NULL)) {
|
if (!SetupDiGetDeviceInterfaceDetailW(device_info_set, &device_interface_data, device_interface_detail_data, dwSize, NULL, NULL))
|
||||||
|
{
|
||||||
goto cont;
|
goto cont;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SetupDiEnumDeviceInfo(device_info_set, device_index, &devinfo_data)) {
|
if (!SetupDiEnumDeviceInfo(device_info_set, device_index, &devinfo_data))
|
||||||
|
{
|
||||||
goto cont;
|
goto cont;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsEqualGUID((const void *)&hidclass_guid, (const void *)&devinfo_data.ClassGuid)) {
|
if (!IsEqualGUID((const void *)&hidclass_guid, (const void *)&devinfo_data.ClassGuid))
|
||||||
|
{
|
||||||
goto cont;
|
goto cont;
|
||||||
}
|
}
|
||||||
|
|
||||||
EnterCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
EnterCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
||||||
|
|
||||||
if (hid_devices == CARDIO_HID_CONTEXTS_LENGTH) {
|
if (hid_devices == CARDIO_HID_CONTEXTS_LENGTH)
|
||||||
|
{
|
||||||
CARDIO_HID_CONTEXTS_LENGTH++;
|
CARDIO_HID_CONTEXTS_LENGTH++;
|
||||||
|
|
||||||
CARDIO_HID_CONTEXTS = (struct cardio_hid_device *) HeapReAlloc(
|
CARDIO_HID_CONTEXTS = (struct cardio_hid_device *)HeapReAlloc(
|
||||||
GetProcessHeap(),
|
GetProcessHeap(),
|
||||||
HEAP_ZERO_MEMORY,
|
HEAP_ZERO_MEMORY,
|
||||||
CARDIO_HID_CONTEXTS,
|
CARDIO_HID_CONTEXTS,
|
||||||
CARDIO_HID_CONTEXTS_LENGTH * sizeof(struct cardio_hid_device)
|
CARDIO_HID_CONTEXTS_LENGTH * sizeof(struct cardio_hid_device));
|
||||||
);
|
if (CARDIO_HID_CONTEXTS == NULL)
|
||||||
if (CARDIO_HID_CONTEXTS == NULL) {
|
{
|
||||||
LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
||||||
HeapFree(GetProcessHeap(), 0, device_interface_detail_data);
|
HeapFree(GetProcessHeap(), 0, device_interface_detail_data);
|
||||||
res = FALSE;
|
res = FALSE;
|
||||||
@ -317,14 +355,16 @@ BOOL cardio_hid_scan() {
|
|||||||
hid_ctx_init(&CARDIO_HID_CONTEXTS[hid_devices]);
|
hid_ctx_init(&CARDIO_HID_CONTEXTS[hid_devices]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardio_hid_scan_device(&CARDIO_HID_CONTEXTS[hid_devices], device_interface_detail_data->DevicePath)) {
|
if (cardio_hid_scan_device(&CARDIO_HID_CONTEXTS[hid_devices], device_interface_detail_data->DevicePath))
|
||||||
|
{
|
||||||
hid_devices++;
|
hid_devices++;
|
||||||
}
|
}
|
||||||
|
|
||||||
LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
||||||
|
|
||||||
cont:
|
cont:
|
||||||
if (device_interface_detail_data) {
|
if (device_interface_detail_data)
|
||||||
|
{
|
||||||
HeapFree(GetProcessHeap(), 0, device_interface_detail_data);
|
HeapFree(GetProcessHeap(), 0, device_interface_detail_data);
|
||||||
device_interface_detail_data = NULL;
|
device_interface_detail_data = NULL;
|
||||||
}
|
}
|
||||||
@ -332,30 +372,36 @@ BOOL cardio_hid_scan() {
|
|||||||
device_index++;
|
device_index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
end:
|
end:
|
||||||
if (device_info_set != INVALID_HANDLE_VALUE) {
|
if (device_info_set != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
SetupDiDestroyDeviceInfoList(device_info_set);
|
SetupDiDestroyDeviceInfoList(device_info_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
cardio_hid_poll_value_t cardio_hid_device_poll(struct cardio_hid_device *ctx) {
|
cardio_hid_poll_value_t cardio_hid_device_poll(struct cardio_hid_device *ctx)
|
||||||
|
{
|
||||||
DWORD error = 0;
|
DWORD error = 0;
|
||||||
|
|
||||||
if (!ctx->initialized) {
|
if (!ctx->initialized)
|
||||||
|
{
|
||||||
return HID_POLL_ERROR;
|
return HID_POLL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->io_pending) {
|
if (ctx->io_pending)
|
||||||
|
{
|
||||||
// Do this if inside to not have more `ReadFile` overlapped I/O requests.
|
// Do this if inside to not have more `ReadFile` overlapped I/O requests.
|
||||||
// If there are more calls to `ReadFile` than `GetOverlappedResult` then
|
// If there are more calls to `ReadFile` than `GetOverlappedResult` then
|
||||||
// eventually the working set quota will run out triggering error 1426
|
// eventually the working set quota will run out triggering error 1426
|
||||||
// (ERROR_WORKING_SET_QUOTA).
|
// (ERROR_WORKING_SET_QUOTA).
|
||||||
if (HasOverlappedIoCompleted(&ctx->read_state)) {
|
if (HasOverlappedIoCompleted(&ctx->read_state))
|
||||||
|
{
|
||||||
ctx->io_pending = FALSE;
|
ctx->io_pending = FALSE;
|
||||||
|
|
||||||
if (!GetOverlappedResult(ctx->dev_handle, &ctx->read_state, &ctx->read_size, FALSE)) {
|
if (!GetOverlappedResult(ctx->dev_handle, &ctx->read_state, &ctx->read_size, FALSE))
|
||||||
|
{
|
||||||
return HID_POLL_ERROR;
|
return HID_POLL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,21 +409,29 @@ cardio_hid_poll_value_t cardio_hid_device_poll(struct cardio_hid_device *ctx) {
|
|||||||
|
|
||||||
return HID_POLL_CARD_READY;
|
return HID_POLL_CARD_READY;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if (!ReadFile(
|
if (!ReadFile(
|
||||||
ctx->dev_handle,
|
ctx->dev_handle,
|
||||||
&ctx->report_buffer,
|
&ctx->report_buffer,
|
||||||
sizeof(ctx->report_buffer),
|
sizeof(ctx->report_buffer),
|
||||||
&ctx->read_size,
|
&ctx->read_size,
|
||||||
&ctx->read_state)) {
|
&ctx->read_state))
|
||||||
|
{
|
||||||
error = GetLastError();
|
error = GetLastError();
|
||||||
|
|
||||||
if (error == ERROR_IO_PENDING) {
|
if (error == ERROR_IO_PENDING)
|
||||||
|
{
|
||||||
ctx->io_pending = TRUE;
|
ctx->io_pending = TRUE;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
return HID_POLL_ERROR;
|
return HID_POLL_ERROR;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// The read completed right away
|
// The read completed right away
|
||||||
return HID_POLL_CARD_READY;
|
return HID_POLL_CARD_READY;
|
||||||
}
|
}
|
||||||
@ -386,25 +440,30 @@ cardio_hid_poll_value_t cardio_hid_device_poll(struct cardio_hid_device *ctx) {
|
|||||||
return HID_POLL_CARD_NOT_READY;
|
return HID_POLL_CARD_NOT_READY;
|
||||||
}
|
}
|
||||||
|
|
||||||
cardio_hid_card_type_t cardio_hid_device_read(struct cardio_hid_device *hid_ctx) {
|
cardio_hid_card_type_t cardio_hid_device_read(struct cardio_hid_device *hid_ctx)
|
||||||
|
{
|
||||||
|
|
||||||
// check if not initialized
|
// check if not initialized
|
||||||
if (!hid_ctx->initialized) {
|
if (!hid_ctx->initialized)
|
||||||
|
{
|
||||||
return HID_CARD_NONE;
|
return HID_CARD_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if IO is pending
|
// check if IO is pending
|
||||||
if (hid_ctx->io_pending) {
|
if (hid_ctx->io_pending)
|
||||||
|
{
|
||||||
return HID_CARD_NONE;
|
return HID_CARD_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if nothing was read
|
// check if nothing was read
|
||||||
if (hid_ctx->read_size == 0) {
|
if (hid_ctx->read_size == 0)
|
||||||
|
{
|
||||||
return HID_CARD_NONE;
|
return HID_CARD_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterate collections
|
// iterate collections
|
||||||
for (int i = 0; i < hid_ctx->collection_length; i++) {
|
for (int i = 0; i < hid_ctx->collection_length; i++)
|
||||||
|
{
|
||||||
HIDP_VALUE_CAPS *item = &hid_ctx->collection[i];
|
HIDP_VALUE_CAPS *item = &hid_ctx->collection[i];
|
||||||
|
|
||||||
// get usages
|
// get usages
|
||||||
@ -413,24 +472,26 @@ cardio_hid_card_type_t cardio_hid_device_read(struct cardio_hid_device *hid_ctx)
|
|||||||
CARD_READER_USAGE_PAGE,
|
CARD_READER_USAGE_PAGE,
|
||||||
0, // LinkCollection
|
0, // LinkCollection
|
||||||
item->NotRange.Usage,
|
item->NotRange.Usage,
|
||||||
(PCHAR) &hid_ctx->u.usage_value,
|
(PCHAR)&hid_ctx->u.usage_value,
|
||||||
sizeof(hid_ctx->u.usage_value),
|
sizeof(hid_ctx->u.usage_value),
|
||||||
hid_ctx->pp_data,
|
hid_ctx->pp_data,
|
||||||
(PCHAR) &hid_ctx->report_buffer,
|
(PCHAR)&hid_ctx->report_buffer,
|
||||||
hid_ctx->read_size);
|
hid_ctx->read_size);
|
||||||
|
|
||||||
// loop through the collection to find the entry that handles this report ID
|
// loop through the collection to find the entry that handles this report ID
|
||||||
if (res == HIDP_STATUS_INCOMPATIBLE_REPORT_ID) {
|
if (res == HIDP_STATUS_INCOMPATIBLE_REPORT_ID)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if failed
|
// check if failed
|
||||||
if (res != HIDP_STATUS_SUCCESS) {
|
if (res != HIDP_STATUS_SUCCESS)
|
||||||
|
{
|
||||||
return HID_CARD_NONE;
|
return HID_CARD_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// return card type
|
// return card type
|
||||||
return (cardio_hid_card_type_t) item->NotRange.Usage;
|
return (cardio_hid_card_type_t)item->NotRange.Usage;
|
||||||
}
|
}
|
||||||
|
|
||||||
return HID_CARD_NONE;
|
return HID_CARD_NONE;
|
@ -26,7 +26,7 @@
|
|||||||
#ifndef SPICETOOLS_CARDIO_HID_H
|
#ifndef SPICETOOLS_CARDIO_HID_H
|
||||||
#define SPICETOOLS_CARDIO_HID_H
|
#define SPICETOOLS_CARDIO_HID_H
|
||||||
|
|
||||||
#include "../helpers.h"
|
#include "../../helpers.h"
|
||||||
#include <hidsdi.h>
|
#include <hidsdi.h>
|
||||||
#include <setupapi.h>
|
#include <setupapi.h>
|
||||||
|
|
334
src/cardreader_plugin/SmartCard/scard.c
Normal file
334
src/cardreader_plugin/SmartCard/scard.c
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
/**
|
||||||
|
* MIT-License
|
||||||
|
* Copyright (c) 2018 by nolm <nolan@nolm.name>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
* deal in the Software without restriction, including without limitation the
|
||||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
* sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* Modified version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scard.h"
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winscard.h>
|
||||||
|
// #include <tchar.h>
|
||||||
|
// #include <thread>
|
||||||
|
|
||||||
|
extern char module[];
|
||||||
|
|
||||||
|
#define MAX_APDU_SIZE 255
|
||||||
|
extern int readCooldown;
|
||||||
|
// set to detect all cards, reduce polling rate to 500ms.
|
||||||
|
// based off acr122u reader, see page 26 in api document.
|
||||||
|
// https://www.acs.com.hk/en/download-manual/419/API-ACR122U-2.04.pdf
|
||||||
|
|
||||||
|
#define PICC_OPERATING_PARAMS 0xDFu
|
||||||
|
BYTE PICC_OPERATING_PARAM_CMD[5] = {0xFFu, 0x00u, 0x51u, PICC_OPERATING_PARAMS, 0x00u};
|
||||||
|
|
||||||
|
// return bytes from device
|
||||||
|
#define PICC_SUCCESS 0x90u
|
||||||
|
#define PICC_ERROR 0x63u
|
||||||
|
|
||||||
|
static const BYTE UID_CMD[5] = {0xFFu, 0xCAu, 0x00u, 0x00u, 0x00u};
|
||||||
|
|
||||||
|
enum scard_atr_protocol
|
||||||
|
{
|
||||||
|
SCARD_ATR_PROTOCOL_ISO14443_PART3 = 0x03,
|
||||||
|
SCARD_ATR_PROTOCOL_ISO15693_PART3 = 0x0B,
|
||||||
|
SCARD_ATR_PROTOCOL_FELICA_212K = 0x11,
|
||||||
|
SCARD_ATR_PROTOCOL_FELICA_424K = 0x12,
|
||||||
|
};
|
||||||
|
|
||||||
|
// winscard_config_t WINSCARD_CONFIG;
|
||||||
|
SCARDCONTEXT hContext = 0;
|
||||||
|
SCARD_READERSTATE reader_states[2];
|
||||||
|
LPTSTR reader_name_slots[2] = {NULL, NULL};
|
||||||
|
int reader_count = 0;
|
||||||
|
LONG lRet = 0;
|
||||||
|
|
||||||
|
void scard_poll(uint8_t *buf, SCARDCONTEXT _hContext, LPCTSTR _readerName, uint8_t unit_no)
|
||||||
|
{
|
||||||
|
printInfo("%s (%s): Update on reader : %s\n", __func__, module, reader_states[unit_no].szReader);
|
||||||
|
// Connect to the smart card.
|
||||||
|
LONG lRet = 0;
|
||||||
|
SCARDHANDLE hCard;
|
||||||
|
DWORD dwActiveProtocol;
|
||||||
|
for (int retry = 0; retry < 10; retry++)
|
||||||
|
{
|
||||||
|
if ((lRet = SCardConnect(_hContext, _readerName, SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol)) == SCARD_S_SUCCESS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
Sleep(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lRet != SCARD_S_SUCCESS)
|
||||||
|
{
|
||||||
|
printError("%s (%s): Error connecting to the card: 0x%08X\n", __func__, module, lRet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the reader params
|
||||||
|
lRet = 0;
|
||||||
|
LPCSCARD_IO_REQUEST pci = dwActiveProtocol == SCARD_PROTOCOL_T1 ? SCARD_PCI_T1 : SCARD_PCI_T0;
|
||||||
|
DWORD cbRecv = MAX_APDU_SIZE;
|
||||||
|
BYTE pbRecv[MAX_APDU_SIZE];
|
||||||
|
if ((lRet = SCardTransmit(hCard, pci, PICC_OPERATING_PARAM_CMD, sizeof(PICC_OPERATING_PARAM_CMD), NULL, pbRecv, &cbRecv)) != SCARD_S_SUCCESS)
|
||||||
|
{
|
||||||
|
printError("%s (%s): Error setting PICC params: 0x%08X\n", __func__, module, lRet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cbRecv > 2 && pbRecv[0] != PICC_SUCCESS && pbRecv[1] != PICC_OPERATING_PARAMS)
|
||||||
|
{
|
||||||
|
printError("%s (%s): PICC params not valid 0x%02X != 0x%02X\n", __func__, module, pbRecv[1], PICC_OPERATING_PARAMS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read ATR to determine card type.
|
||||||
|
TCHAR szReader[200];
|
||||||
|
DWORD cchReader = 200;
|
||||||
|
BYTE atr[32];
|
||||||
|
DWORD cByteAtr = 32;
|
||||||
|
lRet = SCardStatus(hCard, szReader, &cchReader, NULL, NULL, atr, &cByteAtr);
|
||||||
|
if (lRet != SCARD_S_SUCCESS)
|
||||||
|
{
|
||||||
|
printError("%s (%s): Error getting card status: 0x%08X\n", __func__, module, lRet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only care about 20-byte ATRs returned by arcade-type smart cards
|
||||||
|
if (cByteAtr != 20)
|
||||||
|
{
|
||||||
|
printError("%s (%s): Ignoring card with len(%d) = %02X (%08X)\n", __func__, module, cByteAtr, atr, cByteAtr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printInfo("%s (%s): atr Return: len(%d) = %02X (%08X)\n", __func__, module, cByteAtr, atr, cByteAtr);
|
||||||
|
|
||||||
|
// Figure out if we should reverse the UID returned by the card based on the ATR protocol
|
||||||
|
BYTE cardProtocol = atr[12];
|
||||||
|
BOOL shouldReverseUid = false;
|
||||||
|
if (cardProtocol == SCARD_ATR_PROTOCOL_ISO15693_PART3)
|
||||||
|
{
|
||||||
|
printWarning("%s (%s): Card protocol: ISO15693_PART3\n", __func__, module);
|
||||||
|
shouldReverseUid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (cardProtocol == SCARD_ATR_PROTOCOL_ISO14443_PART3)
|
||||||
|
printWarning("%s (%s): Card protocol: ISO14443_PART3\n", __func__, module);
|
||||||
|
|
||||||
|
else if (cardProtocol == SCARD_ATR_PROTOCOL_FELICA_212K)
|
||||||
|
printWarning("%s (%s): Card protocol: FELICA_212K\n", __func__, module);
|
||||||
|
|
||||||
|
else if (cardProtocol == SCARD_ATR_PROTOCOL_FELICA_424K)
|
||||||
|
printWarning("%s (%s): Card protocol: FELICA_424K\n", __func__, module);
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printError("%s (%s): Unknown NFC Protocol: 0x%02X\n", __func__, module, cardProtocol);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read UID
|
||||||
|
cbRecv = MAX_APDU_SIZE;
|
||||||
|
if ((lRet = SCardTransmit(hCard, pci, UID_CMD, sizeof(UID_CMD), NULL, pbRecv, &cbRecv)) != SCARD_S_SUCCESS)
|
||||||
|
{
|
||||||
|
printError("%s (%s): Error querying card UID: 0x%08X\n", __func__, module, lRet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cbRecv > 1 && pbRecv[0] == PICC_ERROR)
|
||||||
|
{
|
||||||
|
printError("%s (%s): UID query failed\n", __func__, module);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((lRet = SCardDisconnect(hCard, SCARD_LEAVE_CARD)) != SCARD_S_SUCCESS)
|
||||||
|
printError("%s (%s): Failed SCardDisconnect: 0x%08X\n", __func__, module, lRet);
|
||||||
|
|
||||||
|
if (cbRecv < 8)
|
||||||
|
{
|
||||||
|
printWarning("%s (%s): Padding card uid to 8 bytes\n", __func__, module);
|
||||||
|
memset(&pbRecv[cbRecv], 0, 8 - cbRecv);
|
||||||
|
}
|
||||||
|
else if (cbRecv > 8)
|
||||||
|
printWarning("%s (%s): taking first 8 bytes of len(uid) = %02X\n", __func__, module, cbRecv);
|
||||||
|
|
||||||
|
// Copy UID to struct, reversing if necessary
|
||||||
|
card_info_t card_info;
|
||||||
|
if (shouldReverseUid)
|
||||||
|
for (DWORD i = 0; i < 8; i++)
|
||||||
|
card_info.uid[i] = pbRecv[7 - i];
|
||||||
|
else
|
||||||
|
memcpy(card_info.uid, pbRecv, 8);
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; ++i)
|
||||||
|
buf[i] = card_info.uid[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void scard_clear(uint8_t unitNo)
|
||||||
|
{
|
||||||
|
card_info_t empty_cardinfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scard_update(uint8_t *buf)
|
||||||
|
{
|
||||||
|
if (reader_count < 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lRet = SCardGetStatusChange(hContext, readCooldown, reader_states, reader_count);
|
||||||
|
if (lRet == SCARD_E_TIMEOUT)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (lRet != SCARD_S_SUCCESS)
|
||||||
|
{
|
||||||
|
printError("%s (%s): Failed SCardGetStatusChange: 0x%08X\n", __func__, module, lRet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t unit_no = 0; unit_no < reader_count; unit_no++)
|
||||||
|
{
|
||||||
|
if (!(reader_states[unit_no].dwEventState & SCARD_STATE_CHANGED))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DWORD newState = reader_states[unit_no].dwEventState ^ SCARD_STATE_CHANGED;
|
||||||
|
bool wasCardPresent = (reader_states[unit_no].dwCurrentState & SCARD_STATE_PRESENT) > 0;
|
||||||
|
if (newState & SCARD_STATE_UNAVAILABLE)
|
||||||
|
{
|
||||||
|
printWarning("%s (%s): New card state: unavailable\n", __func__, module);
|
||||||
|
Sleep(readCooldown);
|
||||||
|
}
|
||||||
|
else if (newState & SCARD_STATE_EMPTY)
|
||||||
|
{
|
||||||
|
printWarning("%s (%s): New card state: empty\n", __func__, module);
|
||||||
|
scard_clear(unit_no);
|
||||||
|
}
|
||||||
|
else if (newState & SCARD_STATE_PRESENT && !wasCardPresent)
|
||||||
|
{
|
||||||
|
printInfo("%s (%s): New card state: present\n", __func__, module);
|
||||||
|
scard_poll(buf, hContext, reader_states[unit_no].szReader, unit_no);
|
||||||
|
}
|
||||||
|
|
||||||
|
reader_states[unit_no].dwCurrentState = reader_states[unit_no].dwEventState;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool scard_init()
|
||||||
|
{
|
||||||
|
if ((lRet = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hContext)) != SCARD_S_SUCCESS)
|
||||||
|
{
|
||||||
|
// log_warning("scard", "failed to establish SCard context: {}", bin2hex(&lRet, sizeof(LONG)));
|
||||||
|
return lRet;
|
||||||
|
}
|
||||||
|
|
||||||
|
LPCTSTR reader = NULL;
|
||||||
|
|
||||||
|
int readerNameLen = 0;
|
||||||
|
|
||||||
|
// get list of readers
|
||||||
|
LPTSTR reader_list = NULL;
|
||||||
|
auto pcchReaders = SCARD_AUTOALLOCATE;
|
||||||
|
lRet = SCardListReaders(hContext, NULL, (LPTSTR)&reader_list, &pcchReaders);
|
||||||
|
|
||||||
|
int slot0_idx = -1;
|
||||||
|
int slot1_idx = -1;
|
||||||
|
int readerCount = 0;
|
||||||
|
switch (lRet)
|
||||||
|
{
|
||||||
|
case SCARD_E_NO_READERS_AVAILABLE:
|
||||||
|
printError("%s (%s): No readers available\n", __func__, module);
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case SCARD_S_SUCCESS:
|
||||||
|
|
||||||
|
// So WinAPI has this terrible "multi-string" concept wherein you have a list
|
||||||
|
// of null-terminated strings, terminated by a double-null.
|
||||||
|
for (reader = reader_list; *reader; reader = reader + lstrlen(reader) + 1)
|
||||||
|
{
|
||||||
|
printInfo("%s (%s): Found reader: %s\n", __func__, module, reader);
|
||||||
|
readerCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have at least two readers, assign readers to slots as necessary.
|
||||||
|
if (readerCount >= 2)
|
||||||
|
{
|
||||||
|
if (slot1_idx != 0)
|
||||||
|
slot0_idx = 0;
|
||||||
|
if (slot0_idx != 1)
|
||||||
|
slot1_idx = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the reader count is 1 and no reader was set, set first reader
|
||||||
|
if (readerCount == 1 && slot0_idx < 0 && slot1_idx < 0)
|
||||||
|
slot0_idx = 0;
|
||||||
|
|
||||||
|
// If we somehow only found slot 1, promote slot 1 to slot 0.
|
||||||
|
if (slot0_idx < 0 && slot1_idx >= 0)
|
||||||
|
{
|
||||||
|
slot0_idx = slot1_idx;
|
||||||
|
slot1_idx = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the relevant names from the multi-string.
|
||||||
|
int i;
|
||||||
|
for (i = 0, reader = reader_list; *reader; reader = reader + lstrlen(reader) + 1, i++)
|
||||||
|
{
|
||||||
|
if (slot0_idx == i)
|
||||||
|
{
|
||||||
|
readerNameLen = lstrlen(reader);
|
||||||
|
reader_name_slots[0] = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sizeof(TCHAR) * (readerNameLen + 1));
|
||||||
|
memcpy(reader_name_slots[0], &reader[0], (size_t)(readerNameLen + 1));
|
||||||
|
}
|
||||||
|
if (slot1_idx == i)
|
||||||
|
{
|
||||||
|
readerNameLen = lstrlen(reader);
|
||||||
|
reader_name_slots[1] = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sizeof(TCHAR) * (readerNameLen + 1));
|
||||||
|
memcpy(reader_name_slots[1], &reader[0], (size_t)(readerNameLen + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader_name_slots[0])
|
||||||
|
printInfo("%s (%s): Using reader slot 0: %s\n", __func__, module, reader_name_slots[0]);
|
||||||
|
|
||||||
|
if (reader_name_slots[1])
|
||||||
|
printInfo("%s (%s): Using reader slot 1: %s\n", __func__, module, reader_name_slots[1]);
|
||||||
|
|
||||||
|
reader_count = reader_name_slots[1] ? 2 : 1;
|
||||||
|
|
||||||
|
memset(&reader_states[0], 0, sizeof(SCARD_READERSTATE));
|
||||||
|
reader_states[0].szReader = reader_name_slots[0];
|
||||||
|
|
||||||
|
memset(&reader_states[1], 0, sizeof(SCARD_READERSTATE));
|
||||||
|
reader_states[1].szReader = reader_name_slots[1];
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printWarning("%s (%s): Failed SCardListReaders: 0x%08X\n", __func__, module, lRet);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader_list)
|
||||||
|
{
|
||||||
|
SCardFreeMemory(hContext, reader_list);
|
||||||
|
}
|
||||||
|
}
|
44
src/cardreader_plugin/SmartCard/scard.h
Normal file
44
src/cardreader_plugin/SmartCard/scard.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* MIT-License
|
||||||
|
* Copyright (c) 2018 by nolm <nolan@nolm.name>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to
|
||||||
|
* deal in the Software without restriction, including without limitation the
|
||||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
* sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
* IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* Modified version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// #include <cstdint>
|
||||||
|
#include "../../helpers.h"
|
||||||
|
|
||||||
|
// cardinfo_t is a description of a card that was presented to a reader
|
||||||
|
typedef struct card_info
|
||||||
|
{
|
||||||
|
int card_type;
|
||||||
|
uint8_t uid[8];
|
||||||
|
} card_info_t;
|
||||||
|
|
||||||
|
void scard_update(uint8_t *buf);
|
||||||
|
|
||||||
|
void scard_poll(uint8_t *buf, SCARDCONTEXT _hContext, LPCTSTR _readerName, uint8_t unit_no);
|
||||||
|
|
||||||
|
void scard_clear(uint8_t unitNo);
|
||||||
|
|
||||||
|
bool scard_init();
|
226
src/cardreader_plugin/dllmain.c
Normal file
226
src/cardreader_plugin/dllmain.c
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
#include "CardIO/cardio.h"
|
||||||
|
#include "SmartCard/scard.h"
|
||||||
|
char module[] = "CardReader";
|
||||||
|
|
||||||
|
// Reader Thread
|
||||||
|
static bool READER_RUNNER_INITIALIZED = false;
|
||||||
|
static HANDLE READER_POLL_THREAD;
|
||||||
|
static bool READER_POLL_STOP_FLAG;
|
||||||
|
|
||||||
|
// SmartCard Specific
|
||||||
|
|
||||||
|
// Game specific
|
||||||
|
static bool waitingForTouch = false;
|
||||||
|
static bool HasCard = false;
|
||||||
|
|
||||||
|
// Misc
|
||||||
|
bool usingSmartCard = false;
|
||||||
|
int readCooldown = 200;
|
||||||
|
|
||||||
|
typedef void (*callbackTouch)(i32, i32, u8[168], u64);
|
||||||
|
callbackTouch touchCallback;
|
||||||
|
u64 touchData;
|
||||||
|
char AccessID[21] = "00000000000000000001";
|
||||||
|
static u8 cardData[168] = {0x01, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x2E, 0x58, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x5C, 0x97, 0x44, 0xF0, 0x88, 0x04, 0x00, 0x43, 0x26, 0x2C, 0x33, 0x00, 0x04,
|
||||||
|
0x06, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
|
||||||
|
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30,
|
||||||
|
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x42, 0x47, 0x49, 0x43, 0x36,
|
||||||
|
0x00, 0x00, 0xFA, 0xE9, 0x69, 0x00, 0xF6, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
|
||||||
|
static unsigned int __stdcall reader_poll_thread_proc(void *ctx)
|
||||||
|
{
|
||||||
|
while (!READER_POLL_STOP_FLAG)
|
||||||
|
{
|
||||||
|
if (HasCard && !usingSmartCard) // A Cardio scan is already in progress, SmartCard doesn't need any extra cooldown.
|
||||||
|
{
|
||||||
|
Sleep(500);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t UID[8] = {0};
|
||||||
|
|
||||||
|
// update devices
|
||||||
|
if (!usingSmartCard) // CardIO
|
||||||
|
{
|
||||||
|
EnterCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
||||||
|
for (size_t device_no = 0; device_no < CARDIO_HID_CONTEXTS_LENGTH; device_no++)
|
||||||
|
{
|
||||||
|
struct cardio_hid_device *device = &CARDIO_HID_CONTEXTS[device_no];
|
||||||
|
|
||||||
|
// get status
|
||||||
|
cardio_hid_poll_value_t status = cardio_hid_device_poll(device);
|
||||||
|
if (status == HID_POLL_CARD_READY)
|
||||||
|
{
|
||||||
|
|
||||||
|
// read card
|
||||||
|
if (cardio_hid_device_read(device) == HID_CARD_NONE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// if card not empty
|
||||||
|
if (*((uint64_t *)&device->u.usage_value[0]) > 0)
|
||||||
|
for (int i = 0; i < 8; ++i)
|
||||||
|
UID[i] = device->u.usage_value[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
||||||
|
Sleep(readCooldown);
|
||||||
|
}
|
||||||
|
else // SmartCard
|
||||||
|
{
|
||||||
|
scard_update(UID);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UID[0] > 0) // If a card was read, format it properly and set HasCard to true so the game can insert it on next frame.
|
||||||
|
{
|
||||||
|
printWarning("%s (%s): Read card %02X%02X%02X%02X%02X%02X%02X%02X\n", __func__, module, UID[0], UID[1], UID[2], UID[3], UID[4], UID[5], UID[6], UID[7]);
|
||||||
|
|
||||||
|
if (waitingForTouch) // Check if game is waiting for a card.
|
||||||
|
{
|
||||||
|
// Properly format the AccessID
|
||||||
|
u64 ReversedAccessID;
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
ReversedAccessID = (ReversedAccessID << 8) | UID[i];
|
||||||
|
sprintf(AccessID, "%020llu", ReversedAccessID);
|
||||||
|
|
||||||
|
waitingForTouch = false; // We don't want to read more cards unless the game asks us to, this is a failsafe to avoid weird behaviour.
|
||||||
|
HasCard = true;
|
||||||
|
}
|
||||||
|
else // Game wasn't checking for cards yet.
|
||||||
|
printError("%s (%s): Card %02X%02X%02X%02X%02X%02X%02X%02X was rejected, still waiting for WaitTouch()\n", __func__, module, UID[0], UID[1], UID[2], UID[3], UID[4], UID[5], UID[6], UID[7]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reader_runner_start()
|
||||||
|
{
|
||||||
|
// initialize
|
||||||
|
if (!READER_RUNNER_INITIALIZED)
|
||||||
|
{
|
||||||
|
READER_RUNNER_INITIALIZED = true;
|
||||||
|
|
||||||
|
if (!usingSmartCard)
|
||||||
|
{ // CardIO INIT
|
||||||
|
printWarning("%s (%s): Initializing CardIO\n", __func__, module);
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
if (!cardio_hid_init())
|
||||||
|
{
|
||||||
|
printError("%s (%s): Couldn't init CardIO\n", __func__, module);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan HID devices
|
||||||
|
if (!cardio_hid_scan())
|
||||||
|
{
|
||||||
|
printError("%s (%s): Couldn't scan for CardIO devices\n", __func__, module);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // SmartCard INIT
|
||||||
|
printWarning("%s (%s): Initializing SmartCard\n", __func__, module);
|
||||||
|
|
||||||
|
if (!scard_init())
|
||||||
|
{
|
||||||
|
// Initialize
|
||||||
|
printError("%s (%s): Couldn't init SmartCard\n", __func__, module);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE; // Init success
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reader_runner_stop()
|
||||||
|
{
|
||||||
|
// shutdown HID
|
||||||
|
if (!usingSmartCard)
|
||||||
|
{
|
||||||
|
cardio_hid_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set initialized to false
|
||||||
|
READER_RUNNER_INITIALIZED = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init()
|
||||||
|
{
|
||||||
|
printWarning("%s (%s): Starting HID Service...\n", __func__, module);
|
||||||
|
|
||||||
|
// Read config
|
||||||
|
toml_table_t *config = openConfig(configPath("plugins/cardreader.toml"));
|
||||||
|
if (config)
|
||||||
|
{
|
||||||
|
usingSmartCard = readConfigBool(config, "using_smartcard", false);
|
||||||
|
readCooldown = readConfigInt(config, "read_cooldown", usingSmartCard ? 500 : 50);
|
||||||
|
toml_free(config);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printError("%s (%s): Config file not found\n", __func__, module);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printInfo("%s (%s): Using %s as reader type\n", __func__, module, usingSmartCard ? "Smart Card" : "cardIO");
|
||||||
|
printInfo("%s (%s): Card read cooldown set to %d ms\n", __func__, module, readCooldown);
|
||||||
|
|
||||||
|
// Find and initialize reader(s)
|
||||||
|
if (!reader_runner_start())
|
||||||
|
return;
|
||||||
|
|
||||||
|
printWarning("%s (%s): Starting reader thread.\n", __func__, module);
|
||||||
|
|
||||||
|
// Start reader thread
|
||||||
|
READER_POLL_STOP_FLAG = false;
|
||||||
|
READER_POLL_THREAD = (HANDLE)_beginthreadex(
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
reader_poll_thread_proc,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (HasCard)
|
||||||
|
{
|
||||||
|
// Generating a ChipID Derived from the AccessID
|
||||||
|
char ChipID[33] = "000000000000";
|
||||||
|
strcat(ChipID, AccessID);
|
||||||
|
|
||||||
|
// Insert card in game
|
||||||
|
printInfo("%s (%s): Inserting Card %s, with ChipID %s\n", __func__, module, AccessID, ChipID);
|
||||||
|
memcpy(cardData + 0x2C, ChipID, 33);
|
||||||
|
memcpy(cardData + 0x50, AccessID, 21);
|
||||||
|
touchCallback(0, 0, cardData, touchData);
|
||||||
|
|
||||||
|
HasCard = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitTouch(i32 (*callback)(i32, i32, u8[168], u64), u64 data) // This is called everytime the game expects a card to be read
|
||||||
|
{
|
||||||
|
printInfo("%s (%s): Waiting for touch\n", __func__, module);
|
||||||
|
waitingForTouch = true;
|
||||||
|
touchCallback = callback;
|
||||||
|
touchData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Exit()
|
||||||
|
{
|
||||||
|
reader_runner_stop();
|
||||||
|
READER_POLL_STOP_FLAG = true;
|
||||||
|
WaitForSingleObject(READER_POLL_THREAD, INFINITE);
|
||||||
|
CloseHandle(READER_POLL_THREAD);
|
||||||
|
READER_POLL_THREAD = NULL;
|
||||||
|
READER_POLL_STOP_FLAG = false;
|
||||||
|
}
|
@ -19,100 +19,101 @@ typedef uint64_t u64;
|
|||||||
#undef DEFINE_GUID
|
#undef DEFINE_GUID
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}}
|
||||||
|
|
||||||
#ifdef BASE_ADDRESS
|
#ifdef BASE_ADDRESS
|
||||||
#define ASLR(address, handle) ((u64)handle + (u64)address - (u64)BASE_ADDRESS)
|
#define ASLR(address, handle) ((u64)handle + (u64)address - (u64)BASE_ADDRESS)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define FUNCTION_PTR(returnType, callingConvention, function, location, ...) \
|
#define FUNCTION_PTR(returnType, callingConvention, function, location, ...) \
|
||||||
returnType (callingConvention *function) (__VA_ARGS__) = (returnType (callingConvention *) (__VA_ARGS__)) (location)
|
returnType(callingConvention *function)(__VA_ARGS__) = (returnType(callingConvention *)(__VA_ARGS__))(location)
|
||||||
|
|
||||||
#define PROC_ADDRESS(libraryName, procName) GetProcAddress (LoadLibrary (TEXT (libraryName)), procName)
|
#define PROC_ADDRESS(libraryName, procName) GetProcAddress(LoadLibrary(TEXT(libraryName)), procName)
|
||||||
|
|
||||||
#define HOOK(returnType, callingConvention, functionName, location, ...) \
|
#define HOOK(returnType, callingConvention, functionName, location, ...) \
|
||||||
typedef returnType callingConvention (*functionName) (__VA_ARGS__); \
|
typedef returnType callingConvention (*functionName)(__VA_ARGS__); \
|
||||||
functionName original##functionName = NULL; \
|
functionName original##functionName = NULL; \
|
||||||
void *where##functionName = (void *)location; \
|
void *where##functionName = (void *)location; \
|
||||||
returnType callingConvention implOf##functionName (__VA_ARGS__)
|
returnType callingConvention implOf##functionName(__VA_ARGS__)
|
||||||
|
|
||||||
#define HOOK_DYNAMIC(returnType, callingConvention, functionName, ...) \
|
#define HOOK_DYNAMIC(returnType, callingConvention, functionName, ...) \
|
||||||
typedef returnType callingConvention (*functionName) (__VA_ARGS__); \
|
typedef returnType callingConvention (*functionName)(__VA_ARGS__); \
|
||||||
functionName original##functionName = NULL; \
|
functionName original##functionName = NULL; \
|
||||||
void *where##functionName = NULL; \
|
void *where##functionName = NULL; \
|
||||||
returnType callingConvention implOf##functionName (__VA_ARGS__)
|
returnType callingConvention implOf##functionName(__VA_ARGS__)
|
||||||
|
|
||||||
#define INSTALL_HOOK(functionName) \
|
#define INSTALL_HOOK(functionName) \
|
||||||
{ \
|
{ \
|
||||||
MH_Initialize (); \
|
MH_Initialize(); \
|
||||||
MH_CreateHook ((void *)where##functionName, (void *)implOf##functionName, (void **)(&original##functionName)); \
|
MH_CreateHook((void *)where##functionName, (void *)implOf##functionName, (void **)(&original##functionName)); \
|
||||||
MH_EnableHook ((void *)where##functionName); \
|
MH_EnableHook((void *)where##functionName); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define INSTALL_HOOK_DYNAMIC(functionName, location) \
|
#define INSTALL_HOOK_DYNAMIC(functionName, location) \
|
||||||
{ \
|
{ \
|
||||||
where##functionName = (void *)location; \
|
where##functionName = (void *)location; \
|
||||||
INSTALL_HOOK (functionName); \
|
INSTALL_HOOK(functionName); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define WRITE_MEMORY(location, type, ...) \
|
#define WRITE_MEMORY(location, type, ...) \
|
||||||
{ \
|
{ \
|
||||||
const type data[] = { __VA_ARGS__ }; \
|
const type data[] = {__VA_ARGS__}; \
|
||||||
DWORD oldProtect; \
|
DWORD oldProtect; \
|
||||||
VirtualProtect ((void *)(location), sizeof (data), PAGE_EXECUTE_READWRITE, &oldProtect); \
|
VirtualProtect((void *)(location), sizeof(data), PAGE_EXECUTE_READWRITE, &oldProtect); \
|
||||||
memcpy ((void *)(location), data, sizeof (data)); \
|
memcpy((void *)(location), data, sizeof(data)); \
|
||||||
VirtualProtect ((void *)(location), sizeof (data), oldProtect, &oldProtect); \
|
VirtualProtect((void *)(location), sizeof(data), oldProtect, &oldProtect); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define WRITE_MEMORY_STRING(location, data, length) \
|
#define WRITE_MEMORY_STRING(location, data, length) \
|
||||||
{ \
|
{ \
|
||||||
DWORD oldProtect; \
|
DWORD oldProtect; \
|
||||||
VirtualProtect ((void *)(location), length, PAGE_EXECUTE_READWRITE, &oldProtect); \
|
VirtualProtect((void *)(location), length, PAGE_EXECUTE_READWRITE, &oldProtect); \
|
||||||
memcpy ((void *)(location), data, length); \
|
memcpy((void *)(location), data, length); \
|
||||||
VirtualProtect ((void *)(location), length, oldProtect, &oldProtect); \
|
VirtualProtect((void *)(location), length, oldProtect, &oldProtect); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define WRITE_NOP(location, count) \
|
#define WRITE_NOP(location, count) \
|
||||||
{ \
|
{ \
|
||||||
DWORD oldProtect; \
|
DWORD oldProtect; \
|
||||||
VirtualProtect ((void *)(location), (size_t)(count), PAGE_EXECUTE_READWRITE, &oldProtect); \
|
VirtualProtect((void *)(location), (size_t)(count), PAGE_EXECUTE_READWRITE, &oldProtect); \
|
||||||
for (size_t i = 0; i < (size_t)(count); i++) \
|
for (size_t i = 0; i < (size_t)(count); i++) \
|
||||||
*((uint8_t *)(location) + i) = 0x90; \
|
*((uint8_t *)(location) + i) = 0x90; \
|
||||||
VirtualProtect ((void *)(location), (size_t)(count), oldProtect, &oldProtect); \
|
VirtualProtect((void *)(location), (size_t)(count), oldProtect, &oldProtect); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define WRITE_NULL(location, count) \
|
#define WRITE_NULL(location, count) \
|
||||||
{ \
|
{ \
|
||||||
DWORD oldProtect; \
|
DWORD oldProtect; \
|
||||||
VirtualProtect ((void *)(location), (size_t)(count), PAGE_EXECUTE_READWRITE, &oldProtect); \
|
VirtualProtect((void *)(location), (size_t)(count), PAGE_EXECUTE_READWRITE, &oldProtect); \
|
||||||
for (size_t i = 0; i < (size_t)(count); i++) \
|
for (size_t i = 0; i < (size_t)(count); i++) \
|
||||||
*((uint8_t *)(location) + i) = 0x00; \
|
*((uint8_t *)(location) + i) = 0x00; \
|
||||||
VirtualProtect ((void *)(location), (size_t)(count), oldProtect, &oldProtect); \
|
VirtualProtect((void *)(location), (size_t)(count), oldProtect, &oldProtect); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define COUNTOFARR(arr) sizeof (arr) / sizeof (arr[0])
|
#define COUNTOFARR(arr) sizeof(arr) / sizeof(arr[0])
|
||||||
#define RETURN_FALSE(returnType, callingConvention, functionName, ...) \
|
#define RETURN_FALSE(returnType, callingConvention, functionName, ...) \
|
||||||
returnType callingConvention functionName (__VA_ARGS__) { return 0; }
|
returnType callingConvention functionName(__VA_ARGS__) { return 0; }
|
||||||
#define RETURN_TRUE(returnType, callingConvention, functionName, ...) \
|
#define RETURN_TRUE(returnType, callingConvention, functionName, ...) \
|
||||||
returnType callingConvention functionName (__VA_ARGS__) { return 1; }
|
returnType callingConvention functionName(__VA_ARGS__) { return 1; }
|
||||||
|
|
||||||
#define INFO_COLOUR FOREGROUND_GREEN
|
#define INFO_COLOUR FOREGROUND_GREEN
|
||||||
#define WARNING_COLOUR (FOREGROUND_RED | FOREGROUND_GREEN)
|
#define WARNING_COLOUR (FOREGROUND_RED | FOREGROUND_GREEN)
|
||||||
#define ERROR_COLOUR FOREGROUND_RED
|
#define ERROR_COLOUR FOREGROUND_RED
|
||||||
#define printInfo(format, ...) printColour (INFO_COLOUR, format, __VA_ARGS__)
|
#define printInfo(format, ...) printColour(INFO_COLOUR, format, __VA_ARGS__)
|
||||||
#define printWarning(format, ...) printColour (WARNING_COLOUR, format, __VA_ARGS__)
|
#define printWarning(format, ...) printColour(WARNING_COLOUR, format, __VA_ARGS__)
|
||||||
#define printError(format, ...) printColour (ERROR_COLOUR, format, __VA_ARGS__)
|
#define printError(format, ...) printColour(ERROR_COLOUR, format, __VA_ARGS__)
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C"
|
||||||
|
{
|
||||||
#endif
|
#endif
|
||||||
char *configPath (char *name);
|
char *configPath(char *name);
|
||||||
toml_table_t *openConfig (char *configFilePath);
|
toml_table_t *openConfig(char *configFilePath);
|
||||||
toml_table_t *openConfigSection (toml_table_t *config, char *sectionName);
|
toml_table_t *openConfigSection(toml_table_t *config, char *sectionName);
|
||||||
bool readConfigBool (toml_table_t *table, char *key, bool notFoundValue);
|
bool readConfigBool(toml_table_t *table, char *key, bool notFoundValue);
|
||||||
int64_t readConfigInt (toml_table_t *table, char *key, int64_t notFoundValue);
|
int64_t readConfigInt(toml_table_t *table, char *key, int64_t notFoundValue);
|
||||||
char *readConfigString (toml_table_t *table, char *key, char *notFoundValue);
|
char *readConfigString(toml_table_t *table, char *key, char *notFoundValue);
|
||||||
void printColour (int colour, const char *format, ...);
|
void printColour(int colour, const char *format, ...);
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,22 +1,25 @@
|
|||||||
compiler = meson.get_compiler('c')
|
compiler = meson.get_compiler('c')
|
||||||
hid_lib = compiler.find_library('hid')
|
hid_lib = compiler.find_library('hid')
|
||||||
|
winscard_lib = compiler.find_library('winscard')
|
||||||
setupapi_lib = compiler.find_library('setupapi')
|
setupapi_lib = compiler.find_library('setupapi')
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
dependency('tomlc99'),
|
dependency('tomlc99'),
|
||||||
dependency('minhook'),
|
dependency('minhook'),
|
||||||
hid_lib,
|
hid_lib,
|
||||||
setupapi_lib
|
winscard_lib,
|
||||||
|
setupapi_lib,
|
||||||
]
|
]
|
||||||
|
|
||||||
shared_library(
|
shared_library(
|
||||||
'cardio',
|
'cardreader',
|
||||||
implicit_include_directories : false,
|
implicit_include_directories : false,
|
||||||
vs_module_defs : 'cardio.def',
|
vs_module_defs : 'cardreader.def',
|
||||||
sources: [
|
sources: [
|
||||||
'helpers.c',
|
'helpers.c',
|
||||||
'cardio_plugin/dllmain.c',
|
'cardreader_plugin/dllmain.c',
|
||||||
'cardio_plugin/cardio.c',
|
'cardreader_plugin/CardIO/cardio.c',
|
||||||
|
'cardreader_plugin/SmartCard/scard.c',
|
||||||
],
|
],
|
||||||
dependencies: deps,
|
dependencies: deps,
|
||||||
include_directories: [
|
include_directories: [
|
||||||
|
Loading…
Reference in New Issue
Block a user