Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
aeae89ab8e | |||
68e9fb938b | |||
3a9725e463 | |||
9eedc62e04 |
@ -1,7 +1,7 @@
|
|||||||
project(
|
project(
|
||||||
'aimeio_scard',
|
'aimeio_scard',
|
||||||
'c',
|
'c',
|
||||||
version : '0.4.0'
|
version : '0.0.2'
|
||||||
)
|
)
|
||||||
|
|
||||||
inc = include_directories('.')
|
inc = include_directories('.')
|
||||||
|
22
readme.md
22
readme.md
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
This allows you to a smartcard reader (specifically the acr122u) with segatools
|
This allows you to a smartcard reader (specifically the acr122u) with segatools
|
||||||
|
|
||||||
**This has only been confirmed working with Chunithm Sun and Sun + so far. Luminous doesn't seem to work yet !**
|
|
||||||
|
|
||||||
# Acknowledgments
|
# Acknowledgments
|
||||||
|
|
||||||
This is a plugin destined to be used with [Dniel97](https://gitea.tendokyu.moe/Dniel97)'s [segatools fork](https://gitea.tendokyu.moe/Dniel97/segatools) (but should work on others too).
|
This is a plugin destined to be used with [Dniel97](https://gitea.tendokyu.moe/Dniel97)'s [segatools fork](https://gitea.tendokyu.moe/Dniel97/segatools) (but should work on others too).
|
||||||
@ -16,30 +14,26 @@ All the logic for reading cards from a file has been taken from [CrazyRedMachine
|
|||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
Make sure to install the ACR122U's [driver](https://www.acs.com.hk/en/driver/3/acr122u-usb-nfc-reader/) before using this.
|
|
||||||
|
|
||||||
To use it with a game, copy `aimeio.dll` to your `segatools` folder and add the following to your `segatools.ini`:
|
To use it with a game, copy `aimeio.dll` to your `segatools` folder and add the following to your `segatools.ini`:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
[aimeio]
|
[aimeio]
|
||||||
path=aimeio.dll ;Path to aimeio.dll
|
path=aimeio.dll
|
||||||
scan=0x0D ;Sets the key which will be used to insert a card in game. The default is 'Return'
|
scan=0x0D ;Sets the key which will be used to insert a card in game. The default is 'Return'
|
||||||
|
|
||||||
|
|
||||||
;Everything below this line is optional.
|
;Everything below this line is optional.
|
||||||
|
|
||||||
readerOptional=1 ;Make reader optional, so that you can still use the keyboard
|
;aimePath= ;Manually specify an aime.txt file
|
||||||
readerName=ACS ACR122 0 ;Manually select which reader to use
|
;felicaPath= ;Manually specify a felica.txt file
|
||||||
disableBuzzer=1 ;Disable the buzzer
|
;felicaGen=0 ;Generate a new random card if it's missing from the file
|
||||||
;aimePath="" ;Manually specify an aime.txt file
|
|
||||||
;felicaPath="" ;Manually specify a felica.txt file
|
|
||||||
;debug=0 ;Display function calls
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Scanning cards
|
## Scanning cards
|
||||||
|
|
||||||
If you scan a newer AIC-based Aime, its FeliCa IDm will be provided to the game. The game will not see the correct "access code," but the IDm should be unique to each card so that particular card can still track your plays.
|
If you scan a newer AIC-based Aime, its FeliCa IDm will be provided to the game. The game will not see the correct "access code," but the IDm should be unique to each card so that particular card can still track your plays.
|
||||||
|
|
||||||
|
As for Mifare cards, their access code won't be 1:1 with a real reader (i still need to fix this). You can still use them and they will work though !
|
||||||
|
|
||||||
## Inserting cards
|
## Inserting cards
|
||||||
|
|
||||||
By pressing the key you have set in segatools.ini and holding it for a few moments, you will insert a card set in either aime.txt or felica.txt
|
By pressing the key you have set in segatools.ini and holding it for a few moments, you will insert a card set in either aime.txt or felica.txt
|
||||||
@ -69,4 +63,4 @@ To build this, you'll need two things :
|
|||||||
- [Meson 1.1.0](https://mesonbuild.com)
|
- [Meson 1.1.0](https://mesonbuild.com)
|
||||||
- [Build Tools pour Visual Studio 2022](https://visualstudio.microsoft.com/fr/downloads/)
|
- [Build Tools pour Visual Studio 2022](https://visualstudio.microsoft.com/fr/downloads/)
|
||||||
|
|
||||||
Once you've edited your build64.bat file to point to your local installation of the VS2022 build tools, run build64.bat and the output will be located in `bin/aime.dll`.
|
Once you've edited your build64.bat file to point to your local installation of the VS2022 build tools, run build64.bat and the output will be located in `bin/aime.dll`.
|
284
src/aimeio.c
284
src/aimeio.c
@ -1,46 +1,46 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include "aimeio.h"
|
#include "aimeio.h"
|
||||||
#include "scard/scard.h"
|
#include "scard/scard.h"
|
||||||
|
|
||||||
|
char module[] = "CardReader";
|
||||||
|
|
||||||
// Reader Thread
|
// Reader Thread
|
||||||
|
static bool READER_RUNNER_INITIALIZED = false;
|
||||||
static HANDLE READER_POLL_THREAD;
|
static HANDLE READER_POLL_THREAD;
|
||||||
static bool READER_POLL_STOP_FLAG;
|
static bool READER_POLL_STOP_FLAG;
|
||||||
|
|
||||||
|
static bool HasCard = false;
|
||||||
|
uint8_t UID[8] = {0};
|
||||||
|
|
||||||
|
struct aime_io_config
|
||||||
|
{
|
||||||
|
wchar_t aime_path[MAX_PATH];
|
||||||
|
wchar_t felica_path[MAX_PATH];
|
||||||
|
bool felica_gen;
|
||||||
|
uint8_t vk_scan;
|
||||||
|
};
|
||||||
|
|
||||||
static struct aime_io_config aime_io_cfg;
|
static struct aime_io_config aime_io_cfg;
|
||||||
static struct card_data card_data;
|
static uint8_t aime_io_aime_id[10];
|
||||||
|
static uint8_t aime_io_felica_id[8];
|
||||||
|
static bool aime_io_aime_id_present;
|
||||||
|
static bool aime_io_felica_id_present;
|
||||||
|
|
||||||
#pragma region CONFIG
|
#pragma region CONFIG
|
||||||
// This is used because otherwise loading a reader with a given name while keeping the comment inline in the config file fails..
|
|
||||||
void RemoveCommentAndTruncate(wchar_t *value)
|
|
||||||
{
|
|
||||||
size_t len = wcslen(value);
|
|
||||||
wchar_t *semicolon = NULL;
|
|
||||||
|
|
||||||
// Remove comments
|
|
||||||
semicolon = wcschr(value, L';');
|
|
||||||
if (semicolon != NULL)
|
|
||||||
{
|
|
||||||
*semicolon = L'\0';
|
|
||||||
len = wcslen(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trim trailing whitespaces
|
|
||||||
while (len > 0 && (value[len - 1] == L' ' || value[len - 1] == L'\t' || value[len - 1] == L'\r' || value[len - 1] == L'\n'))
|
|
||||||
{
|
|
||||||
value[--len] = L'\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void aime_io_config_read(struct aime_io_config *cfg, const wchar_t *filename)
|
static void aime_io_config_read(struct aime_io_config *cfg, const wchar_t *filename)
|
||||||
{
|
{
|
||||||
assert(cfg != NULL);
|
assert(cfg != NULL);
|
||||||
assert(filename != NULL);
|
assert(filename != NULL);
|
||||||
|
|
||||||
cfg->debug = GetPrivateProfileIntW(
|
|
||||||
L"aimeio",
|
|
||||||
L"debug",
|
|
||||||
0,
|
|
||||||
filename);
|
|
||||||
|
|
||||||
GetPrivateProfileStringW(
|
GetPrivateProfileStringW(
|
||||||
L"aimeio",
|
L"aimeio",
|
||||||
L"aimePath",
|
L"aimePath",
|
||||||
@ -57,25 +57,10 @@ static void aime_io_config_read(struct aime_io_config *cfg, const wchar_t *filen
|
|||||||
_countof(cfg->felica_path),
|
_countof(cfg->felica_path),
|
||||||
filename);
|
filename);
|
||||||
|
|
||||||
GetPrivateProfileStringW(
|
cfg->felica_gen = GetPrivateProfileIntW(
|
||||||
L"aimeio",
|
L"aimeio",
|
||||||
L"readerName",
|
L"felicaGen",
|
||||||
L"",
|
1,
|
||||||
cfg->reader_name,
|
|
||||||
_countof(cfg->reader_name),
|
|
||||||
filename);
|
|
||||||
RemoveCommentAndTruncate(cfg->reader_name);
|
|
||||||
|
|
||||||
cfg->reader_optional = GetPrivateProfileIntW(
|
|
||||||
L"aimeio",
|
|
||||||
L"readerOptional",
|
|
||||||
0,
|
|
||||||
filename);
|
|
||||||
|
|
||||||
cfg->disable_buzzer = GetPrivateProfileIntW(
|
|
||||||
L"aimeio",
|
|
||||||
L"disableBuzzer",
|
|
||||||
0,
|
|
||||||
filename);
|
filename);
|
||||||
|
|
||||||
cfg->vk_scan = GetPrivateProfileIntW(
|
cfg->vk_scan = GetPrivateProfileIntW(
|
||||||
@ -83,9 +68,6 @@ static void aime_io_config_read(struct aime_io_config *cfg, const wchar_t *filen
|
|||||||
L"scan",
|
L"scan",
|
||||||
VK_RETURN,
|
VK_RETURN,
|
||||||
filename);
|
filename);
|
||||||
|
|
||||||
if (aime_io_cfg.debug)
|
|
||||||
printf("DEBUG: aime_io_config_read(filename : %ls). \r\n", filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT aime_io_read_id_file(const wchar_t *path, uint8_t *bytes, size_t nbytes, int LineToRead)
|
static HRESULT aime_io_read_id_file(const wchar_t *path, uint8_t *bytes, size_t nbytes, int LineToRead)
|
||||||
@ -111,16 +93,13 @@ static HRESULT aime_io_read_id_file(const wchar_t *path, uint8_t *bytes, size_t
|
|||||||
if (c == EOF)
|
if (c == EOF)
|
||||||
{
|
{
|
||||||
// If the end of the file is reached before the desired line, print an error
|
// If the end of the file is reached before the desired line, print an error
|
||||||
printf("aime_io_read_id_file: %S: Error: Line %d does not exist\n", path, LineToRead);
|
printf("%s: %S: Error: Line %d does not exist\n", module, path, LineToRead);
|
||||||
hr = E_FAIL;
|
hr = E_FAIL;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c == '\n')
|
if (c == '\n')
|
||||||
{
|
|
||||||
currentLine++;
|
currentLine++;
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset++;
|
offset++;
|
||||||
}
|
}
|
||||||
@ -135,7 +114,7 @@ static HRESULT aime_io_read_id_file(const wchar_t *path, uint8_t *bytes, size_t
|
|||||||
|
|
||||||
if (r != 1)
|
if (r != 1)
|
||||||
{
|
{
|
||||||
printf("aime_io_read_id_file: %S: Error parsing line %d\n", path, LineToRead);
|
printf("%s: %S: Error parsing line %d\n", module, path, LineToRead);
|
||||||
hr = E_FAIL;
|
hr = E_FAIL;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
@ -146,7 +125,7 @@ static HRESULT aime_io_read_id_file(const wchar_t *path, uint8_t *bytes, size_t
|
|||||||
// Check if the line is not nbytes long
|
// Check if the line is not nbytes long
|
||||||
if (fgetc(f) != '\n' && !feof(f))
|
if (fgetc(f) != '\n' && !feof(f))
|
||||||
{
|
{
|
||||||
printf("aime_io_read_id_file: %S: Error: Line %d is not %zu bytes long\n", path, LineToRead, nbytes);
|
printf("%s: %S: Error: Line %d is not %zu bytes long\n", module, path, LineToRead, nbytes);
|
||||||
hr = E_FAIL;
|
hr = E_FAIL;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
@ -161,17 +140,64 @@ end:
|
|||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static HRESULT aime_io_generate_felica(const wchar_t *path, uint8_t *bytes, size_t nbytes)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
assert(path != NULL);
|
||||||
|
assert(bytes != NULL);
|
||||||
|
assert(nbytes > 0);
|
||||||
|
|
||||||
|
srand(time(NULL));
|
||||||
|
|
||||||
|
for (i = 0; i < nbytes; i++)
|
||||||
|
bytes[i] = rand();
|
||||||
|
|
||||||
|
/* FeliCa IDm values should have a 0 in their high nibble. I think. */
|
||||||
|
bytes[0] &= 0x0F;
|
||||||
|
|
||||||
|
f = _wfopen(path, L"w");
|
||||||
|
|
||||||
|
if (f == NULL) // If we somehow can't create the file, we error out.
|
||||||
|
{
|
||||||
|
printf("%s: %S: fopen failed: %i\n", module, path, (int)errno);
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < nbytes; i++)
|
||||||
|
fprintf(f, "%02X", bytes[i]);
|
||||||
|
|
||||||
|
fprintf(f, "\n");
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
printf("%s: Generated random FeliCa ID\n", module);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region READER
|
#pragma region READER SPECIFIC
|
||||||
static unsigned int __stdcall aime_io_poll_thread_proc(void *ctx)
|
static unsigned int __stdcall reader_poll_thread_proc(void *ctx)
|
||||||
{
|
{
|
||||||
if (aime_io_cfg.debug)
|
|
||||||
printf("DEBUG: aime_io_poll_thread_proc(). \r\n");
|
|
||||||
while (!READER_POLL_STOP_FLAG)
|
while (!READER_POLL_STOP_FLAG)
|
||||||
{
|
{
|
||||||
if (card_data.card_type == 0) // Halting polling once a card is found, waiting for the game to read it's value.
|
if (!HasCard)
|
||||||
scard_poll(&card_data); // We're trying to find a card. If we do, the card's id and type are written to card_data.
|
{
|
||||||
|
uint8_t _UID[8] = {0};
|
||||||
|
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.
|
||||||
|
{
|
||||||
|
printf("%s: Read card %02X%02X%02X%02X%02X%02X%02X%02X\n", module, _UID[0], _UID[1], _UID[2], _UID[3], _UID[4], _UID[5], _UID[6], _UID[7]);
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
UID[i] = _UID[i];
|
||||||
|
|
||||||
|
HasCard = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -186,36 +212,37 @@ uint16_t aime_io_get_api_version(void)
|
|||||||
|
|
||||||
HRESULT aime_io_init(void)
|
HRESULT aime_io_init(void)
|
||||||
{
|
{
|
||||||
int ret = AllocConsole(); // At init we want to open a console...
|
// At init we want to open a console...
|
||||||
|
int ret = AllocConsole();
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
// someone might already allocated a console - seeing this on fufubot's segatools
|
||||||
if (ret != 0) // someone might already allocated a console - seeing this on fufubot's segatools
|
if (ret != 0)
|
||||||
freopen_s(&fp, "CONOUT$", "w", stdout); // only when we allocate a console, we need to redirect stdout
|
freopen_s(&fp, "CONOUT$", "w", stdout); // only when we allocate a console, we need to redirect stdout
|
||||||
|
|
||||||
memset(&card_data, 0, sizeof(card_data)); // We init the card_data structure
|
|
||||||
|
|
||||||
// We then read the segatools config file to get settings.
|
// We then read the segatools config file to get settings.
|
||||||
aime_io_config_read(&aime_io_cfg, L".\\segatools.ini");
|
aime_io_config_read(&aime_io_cfg, L".\\segatools.ini");
|
||||||
|
|
||||||
printf("aime_io_init: Initializing SmartCard\n"); // Find and initialize reader(s)
|
// Find and initialize reader(s)
|
||||||
if (!scard_init(aime_io_cfg))
|
if (!READER_RUNNER_INITIALIZED)
|
||||||
{
|
{
|
||||||
printf("aime_io_init: Couldn't init SmartCard\n"); // If we couldn't init reader, error out.
|
READER_RUNNER_INITIALIZED = true;
|
||||||
if (!aime_io_cfg.reader_optional)
|
printf("%s: Initializing SmartCard\n", module);
|
||||||
return E_FAIL;
|
|
||||||
|
|
||||||
printf("aime_io_init: Reader is optional, using keyboard only !\n"); // If however the readerOptional flag is set to 1 in segatools.ini, continue with keyboard only.
|
if (!scard_init())
|
||||||
return S_OK;
|
{
|
||||||
|
printf("%s: Couldn't init SmartCard\n", module);
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("aime_io_init: Starting reader thread.\n");
|
printf("%s: Starting reader thread.\n", module);
|
||||||
|
|
||||||
// Start reader thread
|
// Start reader thread
|
||||||
READER_POLL_STOP_FLAG = false;
|
READER_POLL_STOP_FLAG = false;
|
||||||
READER_POLL_THREAD = (HANDLE)_beginthreadex(
|
READER_POLL_THREAD = (HANDLE)_beginthreadex(
|
||||||
NULL,
|
NULL,
|
||||||
0,
|
0,
|
||||||
aime_io_poll_thread_proc,
|
reader_poll_thread_proc,
|
||||||
NULL,
|
NULL,
|
||||||
0,
|
0,
|
||||||
NULL);
|
NULL);
|
||||||
@ -225,15 +252,20 @@ HRESULT aime_io_init(void)
|
|||||||
|
|
||||||
HRESULT aime_io_nfc_poll(uint8_t unit_no)
|
HRESULT aime_io_nfc_poll(uint8_t unit_no)
|
||||||
{
|
{
|
||||||
if (aime_io_cfg.debug)
|
|
||||||
printf("\n\nDEBUG: aime_io_nfc_poll(unit_no %d). \r\n", unit_no);
|
|
||||||
|
|
||||||
if (unit_no != 0)
|
if (unit_no != 0)
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
||||||
|
bool sense;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
// Don't do anything more if the scan key is not held
|
// Don't do anything more if the scan key is not held
|
||||||
if (!(GetAsyncKeyState(aime_io_cfg.vk_scan) & 0x8000))
|
sense = GetAsyncKeyState(aime_io_cfg.vk_scan) & 0x8000;
|
||||||
|
if (!sense)
|
||||||
|
{
|
||||||
|
aime_io_aime_id_present = false;
|
||||||
|
aime_io_felica_id_present = false;
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
// Set which card we want to read (we will read the x'th line in the card's file, x being determined by which key is pressed on the keypad).
|
// Set which card we want to read (we will read the x'th line in the card's file, x being determined by which key is pressed on the keypad).
|
||||||
int card = 0;
|
int card = 0;
|
||||||
@ -247,46 +279,66 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT hr;
|
printf("%s: Attempting to read card %d from file. \r\n", module, card);
|
||||||
printf("aime_io_nfc_poll: Attempting to read card %d from file. \r\n", card);
|
|
||||||
|
|
||||||
// Try AiMe IC
|
// Try AiMe IC
|
||||||
hr = aime_io_read_id_file(aime_io_cfg.aime_path, card_data.card_id, 10, card);
|
hr = aime_io_read_id_file(
|
||||||
|
aime_io_cfg.aime_path,
|
||||||
|
aime_io_aime_id,
|
||||||
|
sizeof(aime_io_aime_id),
|
||||||
|
card);
|
||||||
|
|
||||||
if (SUCCEEDED(hr) && hr != S_FALSE)
|
if (SUCCEEDED(hr) && hr != S_FALSE)
|
||||||
{
|
{
|
||||||
card_data.card_type = Mifare;
|
aime_io_aime_id_present = true;
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try FeliCa IC
|
// Try FeliCa IC
|
||||||
hr = aime_io_read_id_file(aime_io_cfg.felica_path, card_data.card_id, 8, card);
|
hr = aime_io_read_id_file(
|
||||||
|
aime_io_cfg.felica_path,
|
||||||
|
aime_io_felica_id,
|
||||||
|
sizeof(aime_io_felica_id),
|
||||||
|
card);
|
||||||
|
|
||||||
if (SUCCEEDED(hr) && hr != S_FALSE)
|
if (SUCCEEDED(hr) && hr != S_FALSE)
|
||||||
{
|
{
|
||||||
card_data.card_type = FeliCa;
|
aime_io_felica_id_present = true;
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We found nothing.
|
// Try generating FeliCa IC (if enabled)
|
||||||
|
if (aime_io_cfg.felica_gen)
|
||||||
|
{
|
||||||
|
hr = aime_io_generate_felica(
|
||||||
|
aime_io_cfg.felica_path,
|
||||||
|
aime_io_felica_id,
|
||||||
|
sizeof(aime_io_felica_id));
|
||||||
|
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
aime_io_felica_id_present = true;
|
||||||
|
}
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT aime_io_nfc_get_aime_id(uint8_t unit_no, uint8_t *luid, size_t luid_size)
|
HRESULT aime_io_nfc_get_aime_id(uint8_t unit_no, uint8_t *luid, size_t luid_size)
|
||||||
{
|
{
|
||||||
if (aime_io_cfg.debug)
|
|
||||||
printf("DEBUG: aime_io_nfc_get_aime_id(unit_no : %d). \r\n", unit_no);
|
|
||||||
|
|
||||||
assert(luid != NULL);
|
assert(luid != NULL);
|
||||||
assert(luid_size == 10);
|
assert(luid_size == sizeof(aime_io_aime_id));
|
||||||
|
|
||||||
if (unit_no != 0)
|
if (unit_no != 0)
|
||||||
return S_FALSE;
|
return S_FALSE;
|
||||||
|
|
||||||
if (card_data.card_type == Mifare)
|
if (aime_io_aime_id_present)
|
||||||
{
|
{
|
||||||
memcpy(luid, card_data.card_id, luid_size);
|
memcpy(luid, aime_io_aime_id, luid_size);
|
||||||
printf("aime_io_nfc_get_aime_id: Sending Aime card with luID %02X%02X %02X%02X %02X%02X %02X%02X %02X%02X\r\n\n", card_data.card_id[0], card_data.card_id[1], card_data.card_id[2], card_data.card_id[3], card_data.card_id[4], card_data.card_id[5], card_data.card_id[6], card_data.card_id[7], card_data.card_id[8], card_data.card_id[9]);
|
printf("%s: Read Aime card from file with uid ", module);
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
memset(&card_data, 0, sizeof(card_data)); // Reset card_data structure
|
printf("%02x ", aime_io_aime_id[i]);
|
||||||
|
printf("\r\n");
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,47 +347,43 @@ HRESULT aime_io_nfc_get_aime_id(uint8_t unit_no, uint8_t *luid, size_t luid_size
|
|||||||
|
|
||||||
HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm)
|
HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm)
|
||||||
{
|
{
|
||||||
if (aime_io_cfg.debug)
|
uint64_t val;
|
||||||
printf("DEBUG: aime_io_nfc_get_felica_id(unit_no : %d). \r\n", unit_no);
|
size_t i;
|
||||||
|
|
||||||
assert(IDm != NULL);
|
assert(IDm != NULL);
|
||||||
if (unit_no != 0)
|
if (unit_no != 0)
|
||||||
return S_FALSE;
|
return S_FALSE;
|
||||||
|
|
||||||
if (card_data.card_type == FeliCa)
|
if (aime_io_felica_id_present)
|
||||||
{
|
{
|
||||||
uint64_t val = 0;
|
val = 0;
|
||||||
for (size_t i = 0; i < 8; i++)
|
for (i = 0; i < 8; i++)
|
||||||
val = (val << 8) | card_data.card_id[i];
|
val = (val << 8) | aime_io_felica_id[i];
|
||||||
|
|
||||||
*IDm = val;
|
*IDm = val;
|
||||||
printf("aime_io_nfc_get_felica_id: Sending FeliCa card with serial %02X%02X %02X%02X %02X%02X %02X%02X\r\n\n", card_data.card_id[0], card_data.card_id[1], card_data.card_id[2], card_data.card_id[3], card_data.card_id[4], card_data.card_id[5], card_data.card_id[6], card_data.card_id[7]);
|
printf("%s: Read FeliCa card from file with uid %llx\r\n", module, val);
|
||||||
|
|
||||||
memset(&card_data, 0, sizeof(card_data)); // Reset card_data structure
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (HasCard)
|
if (HasCard)
|
||||||
// {
|
{
|
||||||
// polling = false;
|
HasCard = false;
|
||||||
// HasCard = false;
|
|
||||||
|
|
||||||
// uint64_t val;
|
uint64_t val;
|
||||||
// for (int i = 0; i < 8; i++)
|
for (int i = 0; i < 8; i++)
|
||||||
// {
|
{
|
||||||
// val = (val << 8) | UID[i];
|
val = (val << 8) | UID[i];
|
||||||
// }
|
}
|
||||||
// *IDm = val;
|
*IDm = val;
|
||||||
// printf("aime_io_nfc_get_felica_id: FeliCa card has been scanned ! %llx\r\n", val);
|
printf("%s: FeliCa card has been scanned ! %llx\r\n", module, val);
|
||||||
|
|
||||||
// return S_OK;
|
return S_OK;
|
||||||
// }
|
}
|
||||||
return S_FALSE;
|
return S_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void aime_io_led_set_color(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b)
|
void aime_io_led_set_color(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b)
|
||||||
{
|
{
|
||||||
if (aime_io_cfg.debug)
|
|
||||||
printf("DEBUG: aime_io_led_set_color(unit_no : %d, r : %d, g : %d, b : %d). \r\n", unit_no, r, g, b);
|
|
||||||
}
|
}
|
||||||
#pragma endregion
|
#pragma endregion
|
16
src/aimeio.h
16
src/aimeio.h
@ -1,21 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
struct aime_io_config
|
#include <stddef.h>
|
||||||
{
|
#include <stdint.h>
|
||||||
bool debug;
|
|
||||||
wchar_t aime_path[MAX_PATH];
|
|
||||||
wchar_t felica_path[MAX_PATH];
|
|
||||||
wchar_t reader_name[MAX_PATH];
|
|
||||||
bool disable_buzzer;
|
|
||||||
bool reader_optional;
|
|
||||||
uint8_t vk_scan;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get the version of the Aime IO API that this DLL supports. This function
|
Get the version of the Aime IO API that this DLL supports. This function
|
||||||
|
@ -14,16 +14,16 @@ int main()
|
|||||||
switch (aime_io_init())
|
switch (aime_io_init())
|
||||||
{
|
{
|
||||||
case E_FAIL:
|
case E_FAIL:
|
||||||
printf("AIMETEST: aime_io_init() returned E_FAIL. Reader is either missing or incompatible !\r\n");
|
printf("aime_io_init() returned E_FAIL. Reader is either missing or incompatible !\r\n");
|
||||||
return E_FAIL;
|
break;
|
||||||
|
|
||||||
case S_OK:
|
case S_OK:
|
||||||
printf("AIMETEST: aime_io_init() returned S_OK !\r\n");
|
printf("aime_io_init() returned S_OK !\r\n");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
printf("AIMETEST: aime_io_init() returned an unknown state !\r\n");
|
printf("aime_io_init() returned an unknown state !\r\n");
|
||||||
return E_FAIL;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// printf("aime_io_led_set_color(red) : ");
|
// printf("aime_io_led_set_color(red) : ");
|
||||||
@ -40,7 +40,7 @@ int main()
|
|||||||
// Sleep(2000);
|
// Sleep(2000);
|
||||||
// aime_io_led_set_color(0, 0, 0, 0);
|
// aime_io_led_set_color(0, 0, 0, 0);
|
||||||
|
|
||||||
printf("AIMETEST: Running input loop. Press Ctrl+C to exit.\r\n");
|
printf("Running input loop. Press Ctrl+C to exit.\r\n");
|
||||||
|
|
||||||
uint8_t luid[10] = {0};
|
uint8_t luid[10] = {0};
|
||||||
uint64_t IDm = 0;
|
uint64_t IDm = 0;
|
||||||
@ -48,17 +48,24 @@ int main()
|
|||||||
{
|
{
|
||||||
if (aime_io_nfc_poll(0) == S_OK)
|
if (aime_io_nfc_poll(0) == S_OK)
|
||||||
{
|
{
|
||||||
|
Sleep(500);
|
||||||
if (aime_io_nfc_get_felica_id(0, &IDm) == S_OK)
|
if (aime_io_nfc_get_felica_id(0, &IDm) == S_OK)
|
||||||
{
|
{
|
||||||
// aime_io_led_set_color(0, 0, 255, 0);
|
// aime_io_led_set_color(0, 0, 255, 0);
|
||||||
printf("AIMETEST: Found FeliCa card with IDm %llx\r\n\n", IDm);
|
printf("Found FeliCa card with uid %llx\r\n\n", IDm);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (aime_io_nfc_get_aime_id(0, luid, 10) == S_OK)
|
if (aime_io_nfc_get_aime_id(0, luid, 10) == S_OK)
|
||||||
{
|
{
|
||||||
// aime_io_led_set_color(0, 0, 0, 255);
|
// aime_io_led_set_color(0, 0, 0, 255);
|
||||||
printf("AIMETEST: Found old card with luID %02X%02X %02X%02X %02X%02X %02X%02X %02X%02X\r\n", luid[0], luid[1], luid[2], luid[3], luid[4], luid[5], luid[6], luid[7], luid[8], luid[9]);
|
printf("Found old card with uid ");
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
printf("%02x ", luid[i]);
|
||||||
|
}
|
||||||
|
printf("\r\n\n");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
Sleep(500);
|
|
||||||
// printf("poll ok but no card?!\r\n");
|
// printf("poll ok but no card?!\r\n");
|
||||||
}
|
}
|
||||||
// Sleep(300);
|
// Sleep(300);
|
||||||
|
@ -1,4 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* 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 "scard.h"
|
||||||
|
// #include <tchar.h>
|
||||||
|
// #include <thread>
|
||||||
|
|
||||||
|
extern char module[];
|
||||||
|
|
||||||
#define MAX_APDU_SIZE 255
|
#define MAX_APDU_SIZE 255
|
||||||
int readCooldown = 500;
|
int readCooldown = 500;
|
||||||
@ -6,175 +35,35 @@ int readCooldown = 500;
|
|||||||
// based off acr122u reader, see page 26 in api document.
|
// based off acr122u reader, see page 26 in api document.
|
||||||
// https://www.acs.com.hk/en/download-manual/419/API-ACR122U-2.04.pdf
|
// https://www.acs.com.hk/en/download-manual/419/API-ACR122U-2.04.pdf
|
||||||
|
|
||||||
// #define PARAM_POLLRATE 0xDFu
|
#define PICC_OPERATING_PARAMS 0xDFu
|
||||||
#define PARAM_POLLRATE 0x9Bu
|
BYTE PICC_OPERATING_PARAM_CMD[5] = {0xFFu, 0x00u, 0x51u, PICC_OPERATING_PARAMS, 0x00u};
|
||||||
static const BYTE PARAM_SET_PICC[5] = {0xFFu, 0x00u, 0x51u, PARAM_POLLRATE, 0x00u};
|
|
||||||
static const BYTE PARAM_LOAD_KEY[11] = {0xFFu, 0x82u, 0x00u, 0x00u, 0x06u, 0x57u, 0x43u, 0x43u, 0x46u, 0x76u, 0x32u};
|
|
||||||
static const BYTE PARAM_ENABLE_BUZZER[5] = {0xFFu, 0x00u, 0x52u, 0xFFu, 0x00u};
|
|
||||||
static const BYTE PARAM_DISABLE_BUZZER[5] = {0xFFu, 0x00u, 0x52u, 0x00u, 0x00u};
|
|
||||||
|
|
||||||
static const BYTE COMMAND_GET_UID[5] = {0xFFu, 0xCAu, 0x00u, 0x00u, 0x00u};
|
|
||||||
static const BYTE COMMAND_AUTH_BLOCK2[10] = {0xFFu, 0x86u, 0x00u, 0x00u, 0x05u, 0x01u, 0x00u, 0x02u, 0x61u, 0x00u};
|
|
||||||
static const BYTE COMMAND_READ_BLOCK2[5] = {0xFFu, 0xB0u, 0x00u, 0x02u, 0x10u};
|
|
||||||
|
|
||||||
// return bytes from device
|
// return bytes from device
|
||||||
#define PICC_SUCCESS 0x90u
|
#define PICC_SUCCESS 0x90u
|
||||||
#define PICC_ERROR 0x63u
|
#define PICC_ERROR 0x63u
|
||||||
|
|
||||||
|
static const BYTE UID_CMD[5] = {0xFFu, 0xCAu, 0x00u, 0x00u, 0x00u};
|
||||||
|
|
||||||
enum scard_atr_protocol
|
enum scard_atr_protocol
|
||||||
{
|
{
|
||||||
SCARD_ATR_PROTOCOL_ISO14443_PART3 = 0x03,
|
SCARD_ATR_PROTOCOL_ISO14443_PART3 = 0x03,
|
||||||
|
SCARD_ATR_PROTOCOL_ISO15693_PART3 = 0x0B,
|
||||||
SCARD_ATR_PROTOCOL_FELICA_212K = 0x11,
|
SCARD_ATR_PROTOCOL_FELICA_212K = 0x11,
|
||||||
|
SCARD_ATR_PROTOCOL_FELICA_424K = 0x12,
|
||||||
};
|
};
|
||||||
|
|
||||||
// winscard_config_t WINSCARD_CONFIG;
|
// winscard_config_t WINSCARD_CONFIG;
|
||||||
static SCARDCONTEXT hContext = 0;
|
SCARDCONTEXT hContext = 0;
|
||||||
static SCARD_READERSTATE reader_state;
|
SCARD_READERSTATE reader_states[2];
|
||||||
static LONG lRet = 0;
|
LPTSTR reader_name_slots[2] = {NULL, NULL};
|
||||||
|
int reader_count = 0;
|
||||||
|
LONG lRet = 0;
|
||||||
|
|
||||||
bool scard_init(struct aime_io_config config)
|
void scard_poll(uint8_t *buf, SCARDCONTEXT _hContext, LPCTSTR _readerName, uint8_t unit_no)
|
||||||
{
|
{
|
||||||
if ((lRet = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hContext)) != SCARD_S_SUCCESS)
|
printf("%s: Update on reader : %s\n", module, reader_states[unit_no].szReader);
|
||||||
{
|
|
||||||
// log_warning("scard", "failed to establish SCard context: {}", bin2hex(&lRet, sizeof(LONG)));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get list of readers
|
|
||||||
LPTSTR reader_list = NULL;
|
|
||||||
auto pcchReaders = SCARD_AUTOALLOCATE;
|
|
||||||
lRet = SCardListReaders(hContext, NULL, (LPTSTR)&reader_list, &pcchReaders);
|
|
||||||
|
|
||||||
switch (lRet)
|
|
||||||
{
|
|
||||||
case SCARD_E_NO_READERS_AVAILABLE:
|
|
||||||
printf("scard_init: No readers available\n");
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
case SCARD_S_SUCCESS:
|
|
||||||
LPTSTR reader_name = NULL;
|
|
||||||
int readerNameLen = 0;
|
|
||||||
|
|
||||||
// Iterate through the multi-string to get individual reader names
|
|
||||||
printf("scard_init: listing all readers : ");
|
|
||||||
while (*reader_list != '\0')
|
|
||||||
{
|
|
||||||
printf("%s, ", reader_list);
|
|
||||||
reader_list += strlen(reader_list) + 1;
|
|
||||||
}
|
|
||||||
printf("\r\n", reader_list);
|
|
||||||
|
|
||||||
// if the readerName array is populated, replace the first reader in the list
|
|
||||||
char ReaderCharArray[sizeof(config.reader_name)];
|
|
||||||
wcstombs(ReaderCharArray, config.reader_name, sizeof(ReaderCharArray));
|
|
||||||
if (strcmp(ReaderCharArray, "") != 0)
|
|
||||||
{
|
|
||||||
size_t newLen = strlen(ReaderCharArray) + 1;
|
|
||||||
LPSTR newMszReaders = (LPSTR)malloc(newLen);
|
|
||||||
if (newMszReaders != NULL)
|
|
||||||
{
|
|
||||||
// Copy the new selected reader
|
|
||||||
strcpy(newMszReaders, ReaderCharArray);
|
|
||||||
|
|
||||||
// Update the original pointer to the new modified list
|
|
||||||
reader_list = newMszReaders;
|
|
||||||
}
|
|
||||||
printf("scard_init: Forced using reader : %hs\n", ReaderCharArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to reader and send PICC operating params command
|
|
||||||
SCARDHANDLE hCard;
|
|
||||||
DWORD dwActiveProtocol;
|
|
||||||
lRet = SCardConnect(hContext, reader_list, SCARD_SHARE_DIRECT, 0, &hCard, &dwActiveProtocol);
|
|
||||||
if (lRet != SCARD_S_SUCCESS)
|
|
||||||
{
|
|
||||||
printf("scard_init: Error connecting to the reader: 0x%08X\n", lRet);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
printf("scard_init: Connected to reader: %s, sending PICC params\n", reader_list);
|
|
||||||
|
|
||||||
// Enable/Disable the buzzer output
|
|
||||||
DWORD cbRecv = MAX_APDU_SIZE;
|
|
||||||
BYTE pbRecv[MAX_APDU_SIZE];
|
|
||||||
lRet = SCardControl(hCard, SCARD_CTL_CODE(3500), config.disable_buzzer ? PARAM_DISABLE_BUZZER : PARAM_ENABLE_BUZZER, sizeof(config.disable_buzzer ? PARAM_DISABLE_BUZZER : PARAM_ENABLE_BUZZER), pbRecv, cbRecv, &cbRecv);
|
|
||||||
if (lRet != SCARD_S_SUCCESS)
|
|
||||||
printf("scard_init: Couldn't %s buzzer : 0x%08X\n", config.disable_buzzer ? "disable" : "enable", lRet);
|
|
||||||
else
|
|
||||||
printf("scard_init: %s buzzer\n", config.disable_buzzer ? "Disabled" : "Enabled");
|
|
||||||
|
|
||||||
// set the reader params to allow reading FeliCa cards.
|
|
||||||
lRet = SCardControl(hCard, SCARD_CTL_CODE(3500), PARAM_SET_PICC, sizeof(PARAM_SET_PICC), pbRecv, cbRecv, &cbRecv);
|
|
||||||
if (lRet != SCARD_S_SUCCESS)
|
|
||||||
{
|
|
||||||
printf("scard_init: Error setting PICC params : 0x%08X\n", lRet);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cbRecv > 2 && pbRecv[0] != PICC_SUCCESS && pbRecv[1] != PARAM_POLLRATE)
|
|
||||||
{
|
|
||||||
printf("scard_init: PICC params not valid 0x%02X != 0x%02X\n", pbRecv[1], PARAM_POLLRATE);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disconnect from reader
|
|
||||||
if ((lRet = SCardDisconnect(hCard, SCARD_LEAVE_CARD)) != SCARD_S_SUCCESS)
|
|
||||||
printf("scard_init: Failed SCardDisconnect : 0x%08X\n", lRet);
|
|
||||||
|
|
||||||
printf("scard_init: Using reader : %s\n", reader_list);
|
|
||||||
|
|
||||||
memset(&reader_state, 0, sizeof(SCARD_READERSTATE));
|
|
||||||
reader_state.szReader = reader_list;
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
default:
|
|
||||||
printf("scard_init: Failed SCardListReaders: 0x%08X\n", lRet);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void scard_poll(struct card_data *card_data)
|
|
||||||
{
|
|
||||||
lRet = SCardGetStatusChange(hContext, readCooldown, &reader_state, 1);
|
|
||||||
if (lRet == SCARD_E_TIMEOUT)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (lRet != SCARD_S_SUCCESS)
|
|
||||||
{
|
|
||||||
printf("scard_poll: Failed SCardGetStatusChange: 0x%08X\n", lRet);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(reader_state.dwEventState & SCARD_STATE_CHANGED))
|
|
||||||
return;
|
|
||||||
|
|
||||||
DWORD newState = reader_state.dwEventState ^ SCARD_STATE_CHANGED;
|
|
||||||
bool wasCardPresent = (reader_state.dwCurrentState & SCARD_STATE_PRESENT) > 0;
|
|
||||||
if (newState & SCARD_STATE_UNAVAILABLE)
|
|
||||||
{
|
|
||||||
printf("scard_poll: New card state: unavailable\n");
|
|
||||||
Sleep(readCooldown);
|
|
||||||
}
|
|
||||||
else if (newState & SCARD_STATE_EMPTY)
|
|
||||||
{
|
|
||||||
printf("scard_poll: New card state: empty\n");
|
|
||||||
// scard_clear(unit_no);
|
|
||||||
}
|
|
||||||
else if (newState & SCARD_STATE_PRESENT && !wasCardPresent)
|
|
||||||
{
|
|
||||||
printf("scard_poll: New card state: present\n");
|
|
||||||
scard_update(card_data, hContext, reader_state.szReader);
|
|
||||||
}
|
|
||||||
|
|
||||||
reader_state.dwCurrentState = reader_state.dwEventState;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void scard_update(struct card_data *card_data, SCARDCONTEXT _hContext, LPCTSTR _readerName)
|
|
||||||
{
|
|
||||||
printf("scard_update: Update on reader : %s\n", reader_state.szReader);
|
|
||||||
// Connect to the smart card.
|
// Connect to the smart card.
|
||||||
|
LONG lRet = 0;
|
||||||
SCARDHANDLE hCard;
|
SCARDHANDLE hCard;
|
||||||
DWORD dwActiveProtocol;
|
DWORD dwActiveProtocol;
|
||||||
for (int retry = 0; retry < 100; retry++) // retry times has to be increased since poll rate is set to 500ms
|
for (int retry = 0; retry < 100; retry++) // retry times has to be increased since poll rate is set to 500ms
|
||||||
@ -187,11 +76,12 @@ void scard_update(struct card_data *card_data, SCARDCONTEXT _hContext, LPCTSTR _
|
|||||||
|
|
||||||
if (lRet != SCARD_S_SUCCESS)
|
if (lRet != SCARD_S_SUCCESS)
|
||||||
{
|
{
|
||||||
printf("scard_update: Error connecting to the card: 0x%08X\n", lRet);
|
printf("%s: Error connecting to the card: 0x%08X\n", module, lRet);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the reader params
|
// set the reader params
|
||||||
|
lRet = 0;
|
||||||
LPCSCARD_IO_REQUEST pci = dwActiveProtocol == SCARD_PROTOCOL_T1 ? SCARD_PCI_T1 : SCARD_PCI_T0;
|
LPCSCARD_IO_REQUEST pci = dwActiveProtocol == SCARD_PROTOCOL_T1 ? SCARD_PCI_T1 : SCARD_PCI_T0;
|
||||||
DWORD cbRecv = MAX_APDU_SIZE;
|
DWORD cbRecv = MAX_APDU_SIZE;
|
||||||
BYTE pbRecv[MAX_APDU_SIZE];
|
BYTE pbRecv[MAX_APDU_SIZE];
|
||||||
@ -204,96 +94,263 @@ void scard_update(struct card_data *card_data, SCARDCONTEXT _hContext, LPCTSTR _
|
|||||||
lRet = SCardStatus(hCard, szReader, &cchReader, NULL, NULL, atr, &cByteAtr);
|
lRet = SCardStatus(hCard, szReader, &cchReader, NULL, NULL, atr, &cByteAtr);
|
||||||
if (lRet != SCARD_S_SUCCESS)
|
if (lRet != SCARD_S_SUCCESS)
|
||||||
{
|
{
|
||||||
printf("scard_update: Error getting card status: 0x%08X\n", lRet);
|
printf("%s: Error getting card status: 0x%08X\n", module, lRet);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only care about 20-byte ATRs returned by arcade-type smart cards
|
// Only care about 20-byte ATRs returned by arcade-type smart cards
|
||||||
if (cByteAtr != 20)
|
if (cByteAtr != 20)
|
||||||
{
|
{
|
||||||
printf("scard_update: Ignoring card with len(%zu) = %02x (%08X)\n", sizeof(cByteAtr), (unsigned int)atr, cByteAtr);
|
printf("%s: Ignoring card with len(%d) = %02X (%08X)\n", module, cByteAtr, atr, cByteAtr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("%s: atr Return: len(%d) = %02X (%08X)\n", 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];
|
BYTE cardProtocol = atr[12];
|
||||||
if (cardProtocol == SCARD_ATR_PROTOCOL_ISO14443_PART3) // Handling Aime
|
BOOL shouldReverseUid = false;
|
||||||
|
if (cardProtocol == SCARD_ATR_PROTOCOL_ISO15693_PART3)
|
||||||
{
|
{
|
||||||
printf("scard_update: Card protocol: ISO14443_PART3\n");
|
printf("%s: Card protocol: ISO15693_PART3\n", module);
|
||||||
|
shouldReverseUid = true;
|
||||||
printf("scard_update: Loading key for block auth onto reader...\n");
|
|
||||||
cbRecv = MAX_APDU_SIZE;
|
|
||||||
if ((lRet = SCardTransmit(hCard, pci, PARAM_LOAD_KEY, sizeof(PARAM_LOAD_KEY), NULL, pbRecv, &cbRecv)) != SCARD_S_SUCCESS)
|
|
||||||
{
|
|
||||||
printf("scard_update: Error loading key to reader : 0x%08X\n", lRet);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cbRecv > 1 && pbRecv[0] == PICC_ERROR)
|
|
||||||
{
|
|
||||||
printf("scard_update: loading key failed\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("scard_update: key has been loaded, authenticating block 2...\n");
|
|
||||||
|
|
||||||
cbRecv = MAX_APDU_SIZE;
|
|
||||||
if ((lRet = SCardTransmit(hCard, pci, COMMAND_AUTH_BLOCK2, sizeof(COMMAND_AUTH_BLOCK2), NULL, pbRecv, &cbRecv)) != SCARD_S_SUCCESS)
|
|
||||||
{
|
|
||||||
printf("scard_update: Couldn't authenticate for block 2 : 0x%08X\n", lRet);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("scard_update: authentication successful, reading block 2...\n");
|
|
||||||
|
|
||||||
cbRecv = MAX_APDU_SIZE;
|
|
||||||
if ((lRet = SCardTransmit(hCard, pci, COMMAND_READ_BLOCK2, sizeof(COMMAND_READ_BLOCK2), NULL, pbRecv, &cbRecv)) != SCARD_S_SUCCESS)
|
|
||||||
{
|
|
||||||
printf("scard_update: Couldn't read block 2 : 0x%08X\n", lRet);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(card_data->card_id, pbRecv + 6, 10);
|
|
||||||
card_data->card_type = Mifare;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (cardProtocol == SCARD_ATR_PROTOCOL_FELICA_212K) // Handling FeliCa
|
else if (cardProtocol == SCARD_ATR_PROTOCOL_ISO14443_PART3)
|
||||||
{
|
printf("%s: Card protocol: ISO14443_PART3\n", module);
|
||||||
printf("scard_update: Card protocol: FELICA_212K\n");
|
|
||||||
|
|
||||||
// Read mID
|
else if (cardProtocol == SCARD_ATR_PROTOCOL_FELICA_212K)
|
||||||
cbRecv = MAX_APDU_SIZE;
|
printf("%s: Card protocol: FELICA_212K\n", module);
|
||||||
if ((lRet = SCardTransmit(hCard, pci, COMMAND_GET_UID, sizeof(COMMAND_GET_UID), NULL, pbRecv, &cbRecv)) != SCARD_S_SUCCESS)
|
|
||||||
{
|
|
||||||
printf("scard_update: Error querying card UID: 0x%08X\n", lRet);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cbRecv > 1 && pbRecv[0] == PICC_ERROR)
|
else if (cardProtocol == SCARD_ATR_PROTOCOL_FELICA_424K)
|
||||||
{
|
printf("%s: Card protocol: FELICA_424K\n", module);
|
||||||
printf("scard_update: UID query failed\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((lRet = SCardDisconnect(hCard, SCARD_LEAVE_CARD)) != SCARD_S_SUCCESS)
|
|
||||||
printf("scard_update: Failed SCardDisconnect: 0x%08X\n", lRet);
|
|
||||||
|
|
||||||
if (cbRecv < 8)
|
|
||||||
{
|
|
||||||
printf("scard_update: Padding card uid to 8 bytes\n");
|
|
||||||
memset(&pbRecv[cbRecv], 0, 8 - cbRecv);
|
|
||||||
}
|
|
||||||
else if (cbRecv > 8)
|
|
||||||
printf("scard_update: taking first 8 bytes of %d received\n", cbRecv);
|
|
||||||
|
|
||||||
memcpy(card_data->card_id, pbRecv, 8);
|
|
||||||
card_data->card_type = FeliCa;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf("scard_update: Unknown NFC Protocol: 0x%02X\n", cardProtocol);
|
printf("%s: Unknown NFC Protocol: 0x%02X\n", module, cardProtocol);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read UID
|
||||||
|
cbRecv = MAX_APDU_SIZE;
|
||||||
|
if ((lRet = SCardTransmit(hCard, pci, UID_CMD, sizeof(UID_CMD), NULL, pbRecv, &cbRecv)) != SCARD_S_SUCCESS)
|
||||||
|
{
|
||||||
|
printf("%s: Error querying card UID: 0x%08X\n", module, lRet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cbRecv > 1 && pbRecv[0] == PICC_ERROR)
|
||||||
|
{
|
||||||
|
printf("%s: UID query failed\n", module);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((lRet = SCardDisconnect(hCard, SCARD_LEAVE_CARD)) != SCARD_S_SUCCESS)
|
||||||
|
printf("%s: Failed SCardDisconnect: 0x%08X\n", module, lRet);
|
||||||
|
|
||||||
|
if (cbRecv < 8)
|
||||||
|
{
|
||||||
|
printf("%s: Padding card uid to 8 bytes\n", module);
|
||||||
|
memset(&pbRecv[cbRecv], 0, 8 - cbRecv);
|
||||||
|
}
|
||||||
|
else if (cbRecv > 8)
|
||||||
|
printf("%s: taking first 8 bytes of len(uid) = %02X\n", 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)
|
||||||
|
{
|
||||||
|
printf("%s: Failed SCardGetStatusChange: 0x%08X\n", 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)
|
||||||
|
{
|
||||||
|
printf("%s: New card state: unavailable\n", module);
|
||||||
|
Sleep(readCooldown);
|
||||||
|
}
|
||||||
|
else if (newState & SCARD_STATE_EMPTY)
|
||||||
|
{
|
||||||
|
printf("%s: New card state: empty\n", module);
|
||||||
|
// scard_clear(unit_no);
|
||||||
|
}
|
||||||
|
else if (newState & SCARD_STATE_PRESENT && !wasCardPresent)
|
||||||
|
{
|
||||||
|
printf("%s: New card state: present\n", 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:
|
||||||
|
printf("%s: No readers available\n", 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)
|
||||||
|
{
|
||||||
|
printf("%s: Found reader: %s\n", module, reader);
|
||||||
|
readerCount++;
|
||||||
|
|
||||||
|
// Connect to reader and send PICC operating params command
|
||||||
|
LONG lRet = 0;
|
||||||
|
SCARDHANDLE hCard;
|
||||||
|
DWORD dwActiveProtocol;
|
||||||
|
lRet = SCardConnect(hContext, reader, SCARD_SHARE_DIRECT, 0, &hCard, &dwActiveProtocol);
|
||||||
|
if (lRet != SCARD_S_SUCCESS)
|
||||||
|
{
|
||||||
|
printf("%s: Error connecting to the reader: 0x%08X\n", module, lRet);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
printf("%s: Connected to reader: %s, sending PICC operating params command\n", module, reader);
|
||||||
|
|
||||||
|
// set the reader params
|
||||||
|
lRet = 0;
|
||||||
|
DWORD cbRecv = MAX_APDU_SIZE;
|
||||||
|
BYTE pbRecv[MAX_APDU_SIZE];
|
||||||
|
lRet = SCardControl(hCard, SCARD_CTL_CODE(3500), PICC_OPERATING_PARAM_CMD, sizeof(PICC_OPERATING_PARAM_CMD), pbRecv, cbRecv, &cbRecv);
|
||||||
|
Sleep(100);
|
||||||
|
if (lRet != SCARD_S_SUCCESS)
|
||||||
|
{
|
||||||
|
printf("%s: Error setting PICC params: 0x%08X\n", module, lRet);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cbRecv > 2 && pbRecv[0] != PICC_SUCCESS && pbRecv[1] != PICC_OPERATING_PARAMS)
|
||||||
|
{
|
||||||
|
printf("%s: PICC params not valid 0x%02X != 0x%02X\n", module, pbRecv[1], PICC_OPERATING_PARAMS);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect from reader
|
||||||
|
if ((lRet = SCardDisconnect(hCard, SCARD_LEAVE_CARD)) != SCARD_S_SUCCESS)
|
||||||
|
{
|
||||||
|
printf("%s: Failed SCardDisconnect: 0x%08X\n", module, lRet);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("%s: Disconnected from reader: %s, this is expected behavior\n", module, reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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])
|
||||||
|
printf("%s: Using reader slot 0: %s\n", module, reader_name_slots[0]);
|
||||||
|
|
||||||
|
if (reader_name_slots[1])
|
||||||
|
printf("%s: Using reader slot 1: %s\n", 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:
|
||||||
|
printf("%s: Failed SCardListReaders: 0x%08X\n", module, lRet);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
@ -24,42 +24,23 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <winscard.h>
|
#include <winscard.h>
|
||||||
#include <src/aimeio.h>
|
#include <windows.h>
|
||||||
|
|
||||||
// Card types
|
// cardinfo_t is a description of a card that was presented to a reader
|
||||||
enum AIME_CARDTYPE
|
typedef struct card_info
|
||||||
{
|
{
|
||||||
Mifare = 0x01,
|
int card_type;
|
||||||
FeliCa = 0x02
|
uint8_t uid[8];
|
||||||
};
|
} card_info_t;
|
||||||
|
|
||||||
// Structure containing card_type and card_id
|
void scard_update(uint8_t *buf);
|
||||||
struct card_data
|
|
||||||
{
|
|
||||||
uint8_t card_type;
|
|
||||||
uint8_t card_id[32];
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
void scard_poll(uint8_t *buf, SCARDCONTEXT _hContext, LPCTSTR _readerName, uint8_t unit_no);
|
||||||
Initialize the smartcard reader
|
|
||||||
|
|
||||||
- config: struct loaded from segatools.ini
|
void scard_clear(uint8_t unitNo);
|
||||||
*/
|
|
||||||
bool scard_init(struct aime_io_config config);
|
|
||||||
|
|
||||||
/*
|
bool scard_init();
|
||||||
Checks if a new card has been detected
|
|
||||||
|
|
||||||
- card_data: struct containing the card data
|
|
||||||
*/
|
|
||||||
void scard_poll(struct card_data *card_data);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Read the card's data
|
|
||||||
|
|
||||||
- card_data: struct containing the card data
|
|
||||||
- _hContext: context for the card reader
|
|
||||||
- _readerName: name of the card reader to use
|
|
||||||
*/
|
|
||||||
void scard_update(struct card_data *card_data, SCARDCONTEXT _hContext, LPCTSTR _readerName);
|
|
Loading…
Reference in New Issue
Block a user