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/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
|
||||
|
||||
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)
|
||||
|
||||
@ -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.
|
||||
|
||||
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
|
||||
|
||||
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
|
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(
|
||||
'TAL_CardIO',
|
||||
'TAL_CardReader',
|
||||
'c',
|
||||
version : '0.1.0'
|
||||
version : '0.2.0'
|
||||
)
|
||||
|
||||
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
|
||||
Init
|
@ -38,7 +38,8 @@ CRITICAL_SECTION CARDIO_HID_CRIT_SECTION;
|
||||
struct cardio_hid_device *CARDIO_HID_CONTEXTS = NULL;
|
||||
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_handle = INVALID_HANDLE_VALUE;
|
||||
ctx->initialized = FALSE;
|
||||
@ -54,30 +55,36 @@ void hid_ctx_init(struct cardio_hid_device *ctx) {
|
||||
memset(&ctx->caps, 0, sizeof(HIDP_CAPS));
|
||||
}
|
||||
|
||||
void hid_ctx_free(struct cardio_hid_device *ctx) {
|
||||
if (ctx->dev_path != NULL) {
|
||||
void hid_ctx_free(struct cardio_hid_device *ctx)
|
||||
{
|
||||
if (ctx->dev_path != NULL)
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, ctx->dev_path);
|
||||
ctx->dev_path = NULL;
|
||||
}
|
||||
|
||||
if (ctx->dev_handle != INVALID_HANDLE_VALUE) {
|
||||
if (ctx->dev_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CancelIo(ctx->dev_handle);
|
||||
CloseHandle(ctx->dev_handle);
|
||||
ctx->dev_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if (ctx->pp_data != NULL) {
|
||||
if (ctx->pp_data != NULL)
|
||||
{
|
||||
HidD_FreePreparsedData(ctx->pp_data);
|
||||
ctx->pp_data = NULL;
|
||||
}
|
||||
|
||||
if (ctx->collection != NULL) {
|
||||
if (ctx->collection != NULL)
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, ctx->collection);
|
||||
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->io_pending = FALSE;
|
||||
ctx->read_size = 0;
|
||||
@ -91,31 +98,37 @@ void hid_ctx_reset(struct cardio_hid_device *ctx) {
|
||||
memset(&ctx->caps, 0, sizeof(HIDP_CAPS));
|
||||
}
|
||||
|
||||
BOOL cardio_hid_init() {
|
||||
BOOL cardio_hid_init()
|
||||
{
|
||||
size_t i, contexts_size;
|
||||
|
||||
InitializeCriticalSectionAndSpinCount(&CARDIO_HID_CRIT_SECTION, 0x00000400);
|
||||
|
||||
contexts_size = DEFAULT_ALLOCATED_CONTEXTS * sizeof(struct cardio_hid_device);
|
||||
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;
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void cardio_hid_close() {
|
||||
void cardio_hid_close()
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (CARDIO_HID_CONTEXTS_LENGTH > 0) {
|
||||
for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) {
|
||||
if (CARDIO_HID_CONTEXTS_LENGTH > 0)
|
||||
{
|
||||
for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; 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;
|
||||
size_t i;
|
||||
|
||||
EnterCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
||||
|
||||
for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) {
|
||||
if (!CARDIO_HID_CONTEXTS[i].initialized) {
|
||||
for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++)
|
||||
{
|
||||
if (!CARDIO_HID_CONTEXTS[i].initialized)
|
||||
{
|
||||
res = cardio_hid_scan_device(&CARDIO_HID_CONTEXTS[i], device_path);
|
||||
break;
|
||||
}
|
||||
@ -145,16 +161,19 @@ BOOL cardio_hid_add_device(LPCWSTR device_path) {
|
||||
return res;
|
||||
}
|
||||
|
||||
BOOL cardio_hid_remove_device(LPCWSTR device_path) {
|
||||
BOOL cardio_hid_remove_device(LPCWSTR device_path)
|
||||
{
|
||||
BOOL res = FALSE;
|
||||
size_t i;
|
||||
|
||||
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
|
||||
// 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]);
|
||||
res = TRUE;
|
||||
break;
|
||||
@ -169,12 +188,14 @@ BOOL cardio_hid_remove_device(LPCWSTR device_path) {
|
||||
/*
|
||||
* 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;
|
||||
|
||||
size_t dev_path_size = (wcslen(device_path) + 1) * sizeof(WCHAR);
|
||||
ctx->dev_path = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dev_path_size);
|
||||
if (ctx->dev_path == NULL) {
|
||||
if (ctx->dev_path == NULL)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -188,33 +209,40 @@ BOOL cardio_hid_scan_device(struct cardio_hid_device *ctx, LPCWSTR device_path)
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED,
|
||||
NULL);
|
||||
if (ctx->dev_handle == INVALID_HANDLE_VALUE) {
|
||||
printWarning ("%s (%s): could not open device: %ls\n", __func__, module, ctx->dev_path);
|
||||
if (ctx->dev_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
printError("%s (%s): Could not open device: %ls\n", __func__, module, ctx->dev_path);
|
||||
HeapFree(GetProcessHeap(), 0, ctx->dev_path);
|
||||
ctx->dev_path = NULL;
|
||||
ctx->dev_handle = INVALID_HANDLE_VALUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!HidD_GetPreparsedData(ctx->dev_handle, &ctx->pp_data)) {
|
||||
if (!HidD_GetPreparsedData(ctx->dev_handle, &ctx->pp_data))
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
res = HidP_GetCaps(ctx->pp_data, &ctx->caps);
|
||||
if (res != HIDP_STATUS_SUCCESS) {
|
||||
if (res != HIDP_STATUS_SUCCESS)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
// 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;
|
||||
} else if (ctx->caps.NumberInputValueCaps == 0) {
|
||||
}
|
||||
else if (ctx->caps.NumberInputValueCaps == 0)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
ctx->collection_length = ctx->caps.NumberInputValueCaps;
|
||||
ctx->collection = (HIDP_VALUE_CAPS *)HeapAlloc(GetProcessHeap(), 0,
|
||||
ctx->collection_length * sizeof(HIDP_VALUE_CAPS));
|
||||
if (ctx->collection == NULL) {
|
||||
if (ctx->collection == NULL)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
res = HidP_GetValueCaps(
|
||||
@ -222,11 +250,12 @@ BOOL cardio_hid_scan_device(struct cardio_hid_device *ctx, LPCWSTR device_path)
|
||||
ctx->collection,
|
||||
&ctx->collection_length,
|
||||
ctx->pp_data);
|
||||
if (res != HIDP_STATUS_SUCCESS) {
|
||||
if (res != HIDP_STATUS_SUCCESS)
|
||||
{
|
||||
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;
|
||||
return TRUE;
|
||||
|
||||
@ -242,7 +271,8 @@ BOOL cardio_hid_scan_device(struct cardio_hid_device *ctx, LPCWSTR device_path)
|
||||
* Usage 0x41 => ISO_15693
|
||||
* Usage 0x42 => ISO_18092 (FeliCa)
|
||||
*/
|
||||
BOOL cardio_hid_scan() {
|
||||
BOOL cardio_hid_scan()
|
||||
{
|
||||
BOOL res = TRUE;
|
||||
SP_DEVINFO_DATA devinfo_data;
|
||||
SP_DEVICE_INTERFACE_DATA device_interface_data;
|
||||
@ -259,7 +289,8 @@ BOOL cardio_hid_scan() {
|
||||
// HID collection opening needs `DIGCF_DEVICEINTERFACE` and ignore
|
||||
// disconnected devices
|
||||
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;
|
||||
goto end;
|
||||
}
|
||||
@ -270,44 +301,51 @@ BOOL cardio_hid_scan() {
|
||||
|
||||
// `SetupDiEnumDeviceInterfaces` must come before `SetupDiEnumDeviceInfo`
|
||||
// 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
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!SetupDiEnumDeviceInfo(device_info_set, device_index, &devinfo_data)) {
|
||||
if (!SetupDiEnumDeviceInfo(device_info_set, device_index, &devinfo_data))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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 = (struct cardio_hid_device *)HeapReAlloc(
|
||||
GetProcessHeap(),
|
||||
HEAP_ZERO_MEMORY,
|
||||
CARDIO_HID_CONTEXTS,
|
||||
CARDIO_HID_CONTEXTS_LENGTH * sizeof(struct cardio_hid_device)
|
||||
);
|
||||
if (CARDIO_HID_CONTEXTS == NULL) {
|
||||
CARDIO_HID_CONTEXTS_LENGTH * sizeof(struct cardio_hid_device));
|
||||
if (CARDIO_HID_CONTEXTS == NULL)
|
||||
{
|
||||
LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
||||
HeapFree(GetProcessHeap(), 0, device_interface_detail_data);
|
||||
res = FALSE;
|
||||
@ -317,14 +355,16 @@ BOOL cardio_hid_scan() {
|
||||
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++;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION);
|
||||
|
||||
cont:
|
||||
if (device_interface_detail_data) {
|
||||
if (device_interface_detail_data)
|
||||
{
|
||||
HeapFree(GetProcessHeap(), 0, device_interface_detail_data);
|
||||
device_interface_detail_data = NULL;
|
||||
}
|
||||
@ -333,29 +373,35 @@ BOOL cardio_hid_scan() {
|
||||
}
|
||||
|
||||
end:
|
||||
if (device_info_set != INVALID_HANDLE_VALUE) {
|
||||
if (device_info_set != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
SetupDiDestroyDeviceInfoList(device_info_set);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!ctx->initialized) {
|
||||
if (!ctx->initialized)
|
||||
{
|
||||
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.
|
||||
// If there are more calls to `ReadFile` than `GetOverlappedResult` then
|
||||
// eventually the working set quota will run out triggering error 1426
|
||||
// (ERROR_WORKING_SET_QUOTA).
|
||||
if (HasOverlappedIoCompleted(&ctx->read_state)) {
|
||||
if (HasOverlappedIoCompleted(&ctx->read_state))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ -363,21 +409,29 @@ cardio_hid_poll_value_t cardio_hid_device_poll(struct cardio_hid_device *ctx) {
|
||||
|
||||
return HID_POLL_CARD_READY;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ReadFile(
|
||||
ctx->dev_handle,
|
||||
&ctx->report_buffer,
|
||||
sizeof(ctx->report_buffer),
|
||||
&ctx->read_size,
|
||||
&ctx->read_state)) {
|
||||
&ctx->read_state))
|
||||
{
|
||||
error = GetLastError();
|
||||
|
||||
if (error == ERROR_IO_PENDING) {
|
||||
if (error == ERROR_IO_PENDING)
|
||||
{
|
||||
ctx->io_pending = TRUE;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return HID_POLL_ERROR;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// The read completed right away
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
if (!hid_ctx->initialized) {
|
||||
if (!hid_ctx->initialized)
|
||||
{
|
||||
return HID_CARD_NONE;
|
||||
}
|
||||
|
||||
// check if IO is pending
|
||||
if (hid_ctx->io_pending) {
|
||||
if (hid_ctx->io_pending)
|
||||
{
|
||||
return HID_CARD_NONE;
|
||||
}
|
||||
|
||||
// check if nothing was read
|
||||
if (hid_ctx->read_size == 0) {
|
||||
if (hid_ctx->read_size == 0)
|
||||
{
|
||||
return HID_CARD_NONE;
|
||||
}
|
||||
|
||||
// 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];
|
||||
|
||||
// get usages
|
||||
@ -420,12 +479,14 @@ cardio_hid_card_type_t cardio_hid_device_read(struct cardio_hid_device *hid_ctx)
|
||||
hid_ctx->read_size);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// check if failed
|
||||
if (res != HIDP_STATUS_SUCCESS) {
|
||||
if (res != HIDP_STATUS_SUCCESS)
|
||||
{
|
||||
return HID_CARD_NONE;
|
||||
}
|
||||
|
@ -26,7 +26,7 @@
|
||||
#ifndef SPICETOOLS_CARDIO_HID_H
|
||||
#define SPICETOOLS_CARDIO_HID_H
|
||||
|
||||
#include "../helpers.h"
|
||||
#include "../../helpers.h"
|
||||
#include <hidsdi.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;
|
||||
}
|
@ -104,7 +104,8 @@ typedef uint64_t u64;
|
||||
#define printError(format, ...) printColour(ERROR_COLOUR, format, __VA_ARGS__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
char *configPath(char *name);
|
||||
toml_table_t *openConfig(char *configFilePath);
|
||||
|
@ -1,22 +1,25 @@
|
||||
compiler = meson.get_compiler('c')
|
||||
hid_lib = compiler.find_library('hid')
|
||||
winscard_lib = compiler.find_library('winscard')
|
||||
setupapi_lib = compiler.find_library('setupapi')
|
||||
|
||||
deps = [
|
||||
dependency('tomlc99'),
|
||||
dependency('minhook'),
|
||||
hid_lib,
|
||||
setupapi_lib
|
||||
winscard_lib,
|
||||
setupapi_lib,
|
||||
]
|
||||
|
||||
shared_library(
|
||||
'cardio',
|
||||
'cardreader',
|
||||
implicit_include_directories : false,
|
||||
vs_module_defs : 'cardio.def',
|
||||
vs_module_defs : 'cardreader.def',
|
||||
sources: [
|
||||
'helpers.c',
|
||||
'cardio_plugin/dllmain.c',
|
||||
'cardio_plugin/cardio.c',
|
||||
'cardreader_plugin/dllmain.c',
|
||||
'cardreader_plugin/CardIO/cardio.c',
|
||||
'cardreader_plugin/SmartCard/scard.c',
|
||||
],
|
||||
dependencies: deps,
|
||||
include_directories: [
|
||||
|
Loading…
Reference in New Issue
Block a user