From 5dd9cd643004542461407e60cbdb35cc34fbf8aa Mon Sep 17 00:00:00 2001 From: KIT! Date: Mon, 26 Feb 2024 17:34:00 +0100 Subject: [PATCH] Added ability to insert cards from files Merged logic to read cards from a local file from CrazyRedMachine's redboard repository. Added the ability to select a card from the files by pressing and holding a number on the keypad. Bumped version --- build64.bat | 1 + meson.build | 2 +- readme.md | 39 +++++- src/aimeio.c | 329 +++++++++++++++++++++++++++++++++++++++--------- src/aimetest.c | 76 +++++++++++ src/meson.build | 6 +- 6 files changed, 386 insertions(+), 67 deletions(-) create mode 100644 src/aimetest.c diff --git a/build64.bat b/build64.bat index 11dcfa0..2e9fbb8 100644 --- a/build64.bat +++ b/build64.bat @@ -10,3 +10,4 @@ ninja -C build64 mkdir bin copy build64\src\aimeio.dll bin\aimeio.dll +copy build64\src\aimetest.exe bin\aimetest.exe \ No newline at end of file diff --git a/meson.build b/meson.build index 0cfafac..9f05687 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'aimeio_scard', 'c', - version : '0.0.1' + version : '0.0.2' ) inc = include_directories('.') diff --git a/readme.md b/readme.md index 838c867..6f4d4b0 100644 --- a/readme.md +++ b/readme.md @@ -1,15 +1,17 @@ # AimeIO CardReader -This allows you to use smartcard readers (specifically the acr122u) with [segatools](https://gitea.tendokyu.moe/Dniel97/segatools) +This allows you to a smartcard reader (specifically the acr122u) with segatools # Acknowledgments -This is a plugin destined to be used with [Dniel97's](https://gitea.tendokyu.moe/Dniel97)'s segatools fork (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). SmartCard implementation taken from [spicetools](https://github.com/spicetools/spicetools) Initial project made by [nat](https://gitea.tendokyu.moe/nat/aimeio-pcsc) +All the logic for reading cards from a file has been taken from [CrazyRedMachine](https://github.com/CrazyRedMachine)'s [RedBoard](https://github.com/CrazyRedMachine/RedBoard) aimeio. + # Usage To use it with a game, copy `aimeio.dll` to your `segatools` folder and add the following to your `segatools.ini`: @@ -17,12 +19,43 @@ To use it with a game, copy `aimeio.dll` to your `segatools` folder and add the ```ini [aimeio] path=aimeio.dll +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. + +;aimePath= ;Manually specify an aime.txt file +;felicaPath= ;Manually specify a felica.txt file +;felicaGen=0 ;Generate a new random card if it's missing from the file ``` -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. +## 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. 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 + +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 + +You can have multiple cards in those files! +By holding a number from 0 to 9 on your keypad, and then holding enter, you can choose what card to insert. + +For example... + +```text +aime.txt +--- + +1a7f3e925cb866d45a3b +a5d04d668bc529e35aaa +``` + +By only pressing the insert key, you will insert card0, "1a7f3e925cb866d45a3b". +If you hold 1 on your numpad and press the insert key, card1, "a5d04d668bc529e35aaa" will be inserted ! + +This works for up to 10 cards. + # Build To build this, you'll need two things : diff --git a/src/aimeio.c b/src/aimeio.c index add2d26..4ace591 100644 --- a/src/aimeio.c +++ b/src/aimeio.c @@ -18,12 +18,166 @@ static bool READER_RUNNER_INITIALIZED = false; static HANDLE READER_POLL_THREAD; static bool READER_POLL_STOP_FLAG; -char AccessID[21] = "00000000000000000001"; static bool HasCard = false; uint8_t UID[8] = {0}; -static struct aimepcsc_context *ctx; -// static struct aime_data data; +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 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 +static void aime_io_config_read(struct aime_io_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileStringW( + L"aimeio", + L"aimePath", + L"DEVICE\\aime.txt", + cfg->aime_path, + _countof(cfg->aime_path), + filename); + + GetPrivateProfileStringW( + L"aimeio", + L"felicaPath", + L"DEVICE\\felica.txt", + cfg->felica_path, + _countof(cfg->felica_path), + filename); + + cfg->felica_gen = GetPrivateProfileIntW( + L"aimeio", + L"felicaGen", + 1, + filename); + + cfg->vk_scan = GetPrivateProfileIntW( + L"aimeio", + L"scan", + VK_RETURN, + filename); +} + +static HRESULT aime_io_read_id_file(const wchar_t *path, uint8_t *bytes, size_t nbytes, int LineToRead) +{ + HRESULT hr; + FILE *f; + size_t i; + int byte; + long offset; + int currentLine = 0; + + f = _wfopen(path, L"r"); + if (f == NULL) // If the file doesn't exist, we bail. + return S_FALSE; + + memset(bytes, 0, nbytes); + + // Calculate the offset to the start of the desired line + offset = 0; + while (currentLine < LineToRead) + { + int c = fgetc(f); + if (c == EOF) + { + // If the end of the file is reached before the desired line, print an error + printf("%s: %S: Error: Line %d does not exist\n", module, path, LineToRead); + hr = E_FAIL; + goto end; + } + + if (c == '\n') + currentLine++; + + offset++; + } + + // Seek to the calculated offset + fseek(f, offset, SEEK_SET); + + // Read the desired line and extract hexadecimal values + for (i = 0; i < nbytes; i++) + { + int r = fscanf(f, "%02x", &byte); + + if (r != 1) + { + printf("%s: %S: Error parsing line %d\n", module, path, LineToRead); + hr = E_FAIL; + goto end; + } + + bytes[i] = byte; + } + + // Check if the line is not nbytes long + if (fgetc(f) != '\n' && !feof(f)) + { + printf("%s: %S: Error: Line %d is not %zu bytes long\n", module, path, LineToRead, nbytes); + hr = E_FAIL; + goto end; + } + + hr = S_OK; + +end: + if (f != NULL) + { + fclose(f); + } + + 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 region READER SPECIFIC static unsigned int __stdcall reader_poll_thread_proc(void *ctx) @@ -32,17 +186,14 @@ static unsigned int __stdcall reader_poll_thread_proc(void *ctx) { if (!HasCard) { - scard_update(UID); + 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. + 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]); - - // Properly format the AccessID - uint64_t ReversedAccessID; + 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++) - ReversedAccessID = (ReversedAccessID << 8) | UID[i]; - sprintf(AccessID, "%020llu", ReversedAccessID); + UID[i] = _UID[i]; HasCard = true; } @@ -68,6 +219,9 @@ HRESULT aime_io_init(void) if (ret != 0) freopen_s(&fp, "CONOUT$", "w", stdout); // only when we allocate a console, we need to redirect stdout + // We then read the segatools config file to get settings. + aime_io_config_read(&aime_io_cfg, L".\\segatools.ini"); + // Find and initialize reader(s) if (!READER_RUNNER_INITIALIZED) { @@ -93,86 +247,139 @@ HRESULT aime_io_init(void) 0, NULL); - // // int ret; - // // FILE *fp; - - // // ret = AllocConsole(); - - // // // someone might already allocated a console - seeing this on fufubot's segatools - // // if (ret != 0) - // // { - // // // only when we allocate a console, we need to redirect stdout - // // freopen_s(&fp, "CONOUT$", "w", stdout); - // // } - - // // ctx = aimepcsc_create(); - // // if (!ctx) - // // { - // // return E_OUTOFMEMORY; - // // } - - // // ret = aimepcsc_init(ctx); - - // // if (ret != 0) - // // { - // // printf("aimeio-pcsc: failed to initialize: %s\n", aimepcsc_error(ctx)); - // // aimepcsc_destroy(ctx); - // // ctx = NULL; - // // return E_FAIL; - // // } - - // // printf("aimeio-pcsc: initialized with reader: %s\n", aimepcsc_reader_name(ctx)); - return S_OK; } HRESULT aime_io_nfc_poll(uint8_t unit_no) { + if (unit_no != 0) + return S_OK; + + bool sense; + HRESULT hr; + + // Don't do anything more if the scan key is not held + 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; + } + + // 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; + for (int key = VK_NUMPAD0; key <= VK_NUMPAD9; key++) + { + short keyState = GetAsyncKeyState(key); + if (keyState & 0x8000) // Check if the most significant bit is set, indicating that the key is down + { + card = key - VK_NUMPAD0; + continue; + } + } + + printf("%s: Attempting to read card %d from file. \r\n", module, card); + + // Try AiMe IC + 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) + { + aime_io_aime_id_present = true; + return S_OK; + } + + // Try FeliCa IC + 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) + { + aime_io_felica_id_present = true; + return S_OK; + } + + // 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; } HRESULT aime_io_nfc_get_aime_id(uint8_t unit_no, uint8_t *luid, size_t luid_size) { - // Lol what + assert(luid != NULL); + assert(luid_size == sizeof(aime_io_aime_id)); + + if (unit_no != 0) + return S_FALSE; + + if (aime_io_aime_id_present) + { + memcpy(luid, aime_io_aime_id, luid_size); + printf("%s: Read Aime card from file with uid ", module); + for (int i = 0; i < 10; i++) + printf("%02x ", aime_io_aime_id[i]); + printf("\r\n"); + return S_OK; + } + return S_FALSE; } HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm) { + uint64_t val; + size_t i; + assert(IDm != NULL); + if (unit_no != 0) + return S_FALSE; + + if (aime_io_felica_id_present) + { + val = 0; + for (i = 0; i < 8; i++) + val = (val << 8) | aime_io_felica_id[i]; + + *IDm = val; + printf("%s: Read FeliCa card from file with uid %llx\r\n", module, val); + + return S_OK; + } if (HasCard) { - printf("%s: Card has been scanned ! : %s\n", module, AccessID); HasCard = false; uint64_t val; for (int i = 0; i < 8; i++) { val = (val << 8) | UID[i]; - UID[i] = 0; } *IDm = val; + printf("%s: FeliCa card has been scanned ! %llx\r\n", module, val); return S_OK; } - - // // assert(IDm != NULL); - - // // if (unit_no != 0 || data.card_type != FeliCa || data.card_id_len != 8) - // // { - // // return S_FALSE; - // // } - - // // val = 0; - - // // for (i = 0; i < 8; i++) - // // { - // // val = (val << 8) | data.card_id[i]; - // // } - - // // *IDm = val; - return S_FALSE; } diff --git a/src/aimetest.c b/src/aimetest.c new file mode 100644 index 0000000..a845c0b --- /dev/null +++ b/src/aimetest.c @@ -0,0 +1,76 @@ +#include + +#include +#include +#include + +#include "aimeio.h" + +int main() +{ + printf("AIMETEST\r\n---------\r\n"); + // printf("api version = %04x\r\n", chuni_io_get_api_version()); /* not compatible with older dlls */ + printf("aime_io_init() : \n"); + switch (aime_io_init()) + { + case E_FAIL: + printf("aime_io_init() returned E_FAIL. Reader is either missing or incompatible !\r\n"); + break; + + case S_OK: + printf("aime_io_init() returned S_OK !\r\n"); + break; + + default: + printf("aime_io_init() returned an unknown state !\r\n"); + break; + } + + // printf("aime_io_led_set_color(red) : "); + // aime_io_led_set_color(0, 255, 0, 0); + // printf("OK\r\n"); + // Sleep(2000); + // printf("aime_io_led_set_color(green) : "); + // aime_io_led_set_color(0, 0, 255, 0); + // printf("OK\r\n"); + // Sleep(2000); + // printf("aime_io_led_set_color(blue) : "); + // aime_io_led_set_color(0, 0, 0, 255); + // printf("OK\r\n"); + // Sleep(2000); + // aime_io_led_set_color(0, 0, 0, 0); + + printf("Running input loop. Press Ctrl+C to exit.\r\n"); + + uint8_t luid[10] = {0}; + uint64_t IDm = 0; + while (1) + { + if (aime_io_nfc_poll(0) == S_OK) + { + Sleep(500); + if (aime_io_nfc_get_felica_id(0, &IDm) == S_OK) + { + // aime_io_led_set_color(0, 0, 255, 0); + printf("Found FeliCa card with uid %llx\r\n\n", IDm); + continue; + } + if (aime_io_nfc_get_aime_id(0, luid, 10) == S_OK) + { + // aime_io_led_set_color(0, 0, 0, 255); + printf("Found old card with uid "); + for (int i = 0; i < 10; i++) + { + printf("%02x ", luid[i]); + } + printf("\r\n\n"); + continue; + } + // printf("poll ok but no card?!\r\n"); + } + // Sleep(300); + // aime_io_led_set_color(0, 0, 0, 0); + } + + return 0; +} \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index 4d3ee88..1963c03 100644 --- a/src/meson.build +++ b/src/meson.build @@ -9,7 +9,7 @@ deps = [ setupapi_lib, ] -shared_library( +aimeio_dll = shared_library( 'aimeio', implicit_include_directories : false, vs_module_defs : 'aimeio.def', @@ -21,4 +21,6 @@ shared_library( include_directories: [ inc ] -) \ No newline at end of file +) + +executable('aimetest', 'aimetest.c', link_with : aimeio_dll) \ No newline at end of file