First working version

The game sometimes crashes, throwing an access violation at offset 00000000002f399a.

I haven't been able to recreate accurately.
This commit is contained in:
Farewell_ 2023-05-12 09:52:01 +02:00
parent 6c6aabed78
commit c047663c90
17 changed files with 1089 additions and 2 deletions

6
.gitignore vendored
View File

@ -50,3 +50,9 @@ modules.order
Module.symvers
Mkfile.old
dkms.conf
# Vscode specific
.vscode/
subprojects/minhook
subprojects/tomlc99

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
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 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.
For more information, please refer to <https://unlicense.org>

64
PLUGINS.md Normal file
View File

@ -0,0 +1,64 @@
# Guide for making additional plugins
Plugins are just libraries with certain exported functions that go in the plugins folder.
```C
void PreInit()
```
Runs in dllmain, may run into loader locks. You can also just use your own dllmain for this but that involves writing out all the arguments.
```C
void Init()
```
Runs on bngrw_Init, may be a bit late for some things but should generally allow functions which would cause loader locks to run fine if a bit late.
```C
void Exit()
```
Runs on bnusio_Close, dispose of any data here.
```C
void Update()
```
Runs once per frame.
```C
void WaitTouch(i32 (*callback) (i32, i32, u8[168], u64), u64 data)
```
Runs on bngrw_reqWaitTouch. Call the callback like so when you wish to have a card scanned.
```C
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 };
memcpy (cardData + 0x2C, chipId, 33);
memcpy (cardData + 0x50, accessCode, 21);
callback(0, 0, cardData, data);
```
```C
void Card1Insert()
```
Runs when user presses CARD_INSERT_1, causes TAL to not insert a card if any plugins have this present
```C
void Card2Insert()
```
Runs when user presses CARD_INSERT_2, causes TAL to not insert a card if any plugins have this present
```C
void BeforeCard1Insert()
```
Runs before CARD_INSERT_1 is handled
```C
void BeforeCard2Insert()
```
Runs before CARD_INSERT_2 is handled

View File

@ -1,2 +1,22 @@
# TAL_CardIO
Allows you to use USB HID card readers (cardio) with TAL
# TAL CardIO
This is a plugin that allows you to use USB HID card readers (cardio) 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!)
Initial project setup done with the help of [Stepland](https://github.com/Stepland)
# How to use
To build this, you'll need two things :
- [Meson 1.1.0](https://mesonbuild.com)
- [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, 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.

11
build64.bat Normal file
View File

@ -0,0 +1,11 @@
cd /d %~dp0
REM Edit this with the right path to vcvarsall.bat.
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
meson setup build64 --buildtype=release
meson configure build64
ninja -C build64
mkdir bin
copy build64\src\cardio.dll bin\cardio.dll
pause

9
meson.build Normal file
View File

@ -0,0 +1,9 @@
project(
'TAL_CardIO',
'c',
version : '0.1.0'
)
inc = include_directories('.')
subdir('src/')

6
src/cardio.def Normal file
View File

@ -0,0 +1,6 @@
LIBRARY cardio
EXPORTS
Init
WaitTouch
Exit

437
src/cardio_plugin/cardio.c Normal file
View File

@ -0,0 +1,437 @@
/**
* MIT-License
* Copyright (c) 2018 by Felix
*
* 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 "cardio.h"
#define DEFAULT_ALLOCATED_CONTEXTS 2
#define CARD_READER_USAGE_PAGE 0xffca
extern char module[];
// GUID_DEVCLASS_HIDCLASS
static GUID hidclass_guid = {0x745a17a0, 0x74d3, 0x11d0, {0xb6, 0xfe, 0x00, 0xa0, 0xc9, 0x0f, 0x57, 0xda}};
// globals
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) {
ctx->dev_path = NULL;
ctx->dev_handle = INVALID_HANDLE_VALUE;
ctx->initialized = FALSE;
ctx->io_pending = FALSE;
ctx->read_size = 0;
ctx->pp_data = NULL;
ctx->collection = NULL;
ctx->collection_length = 0;
memset(&ctx->read_state, 0, sizeof(OVERLAPPED));
memset(&ctx->report_buffer, 0, sizeof(ctx->report_buffer));
memset(&ctx->u.usage_value, 0, sizeof(ctx->u.usage_value));
memset(&ctx->caps, 0, sizeof(HIDP_CAPS));
}
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) {
CancelIo(ctx->dev_handle);
CloseHandle(ctx->dev_handle);
ctx->dev_handle = INVALID_HANDLE_VALUE;
}
if (ctx->pp_data != NULL) {
HidD_FreePreparsedData(ctx->pp_data);
ctx->pp_data = NULL;
}
if (ctx->collection != NULL) {
HeapFree(GetProcessHeap(), 0, ctx->collection);
ctx->collection = NULL;
}
}
void hid_ctx_reset(struct cardio_hid_device *ctx) {
ctx->initialized = FALSE;
ctx->io_pending = FALSE;
ctx->read_size = 0;
ctx->collection_length = 0;
hid_ctx_free(ctx);
memset(&ctx->read_state, 0, sizeof(OVERLAPPED));
memset(&ctx->report_buffer, 0, sizeof(ctx->report_buffer));
memset(&ctx->u.usage_value, 0, sizeof(ctx->u.usage_value));
memset(&ctx->caps, 0, sizeof(HIDP_CAPS));
}
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) {
return FALSE;
}
CARDIO_HID_CONTEXTS_LENGTH = DEFAULT_ALLOCATED_CONTEXTS;
for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) {
hid_ctx_init(&CARDIO_HID_CONTEXTS[i]);
}
return TRUE;
}
void cardio_hid_close() {
size_t i;
if (CARDIO_HID_CONTEXTS_LENGTH > 0) {
for (i = 0; i < CARDIO_HID_CONTEXTS_LENGTH; i++) {
hid_ctx_free(&CARDIO_HID_CONTEXTS[i]);
}
HeapFree(GetProcessHeap(), 0, CARDIO_HID_CONTEXTS);
CARDIO_HID_CONTEXTS = NULL;
CARDIO_HID_CONTEXTS_LENGTH = 0;
DeleteCriticalSection(&CARDIO_HID_CRIT_SECTION);
}
}
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) {
res = cardio_hid_scan_device(&CARDIO_HID_CONTEXTS[i], device_path);
break;
}
}
LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION);
return res;
}
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++) {
// 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)) {
hid_ctx_reset(&CARDIO_HID_CONTEXTS[i]);
res = TRUE;
break;
}
}
LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION);
return res;
}
/*
* Scan HID device to see if it is a HID reader
*/
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) {
return FALSE;
}
memcpy(ctx->dev_path, device_path, dev_path_size);
ctx->dev_path[dev_path_size - 1] = '\0';
ctx->dev_handle = CreateFileW(
ctx->dev_path,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
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);
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)) {
goto end;
}
res = HidP_GetCaps(ctx->pp_data, &ctx->caps);
if (res != HIDP_STATUS_SUCCESS) {
goto end;
}
// 0xffca is the card reader usage page ID
if (ctx->caps.UsagePage != CARD_READER_USAGE_PAGE) {
goto end;
} 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) {
goto end;
}
res = HidP_GetValueCaps(
HidP_Input,
ctx->collection,
&ctx->collection_length,
ctx->pp_data);
if (res != HIDP_STATUS_SUCCESS) {
goto end;
}
printWarning ("%s (%s): detected reader: %ls\n", __func__, module, ctx->dev_path);
ctx->initialized = TRUE;
return TRUE;
end:
hid_ctx_reset(ctx);
return FALSE;
}
/*
* Checks all devices registered with the HIDClass GUID. If the usage page of
* the device is 0xffca, then a compatible card reader was found.
*
* Usage 0x41 => ISO_15693
* Usage 0x42 => ISO_18092 (FeliCa)
*/
BOOL cardio_hid_scan() {
BOOL res = TRUE;
SP_DEVINFO_DATA devinfo_data;
SP_DEVICE_INTERFACE_DATA device_interface_data;
SP_DEVICE_INTERFACE_DETAIL_DATA_W *device_interface_detail_data = NULL;
HDEVINFO device_info_set;
GUID hid_guid;
DWORD device_index = 0;
DWORD dwSize = 0;
size_t hid_devices = 0;
// get GUID
HidD_GetHidGuid(&hid_guid);
// 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) {
res = FALSE;
goto end;
}
memset(&devinfo_data, 0, sizeof(SP_DEVINFO_DATA));
devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
// `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)) {
// Get the required size
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) {
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)) {
goto cont;
}
if (!SetupDiEnumDeviceInfo(device_info_set, device_index, &devinfo_data)) {
goto cont;
}
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) {
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) {
LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION);
HeapFree(GetProcessHeap(), 0, device_interface_detail_data);
res = FALSE;
goto end;
}
hid_ctx_init(&CARDIO_HID_CONTEXTS[hid_devices]);
}
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) {
HeapFree(GetProcessHeap(), 0, device_interface_detail_data);
device_interface_detail_data = NULL;
}
device_index++;
}
end:
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) {
DWORD error = 0;
if (!ctx->initialized) {
return HID_POLL_ERROR;
}
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)) {
ctx->io_pending = FALSE;
if (!GetOverlappedResult(ctx->dev_handle, &ctx->read_state, &ctx->read_size, FALSE)) {
return HID_POLL_ERROR;
}
memset(&ctx->read_state, 0, sizeof(OVERLAPPED));
return HID_POLL_CARD_READY;
}
} else {
if (!ReadFile(
ctx->dev_handle,
&ctx->report_buffer,
sizeof(ctx->report_buffer),
&ctx->read_size,
&ctx->read_state)) {
error = GetLastError();
if (error == ERROR_IO_PENDING) {
ctx->io_pending = TRUE;
} else {
return HID_POLL_ERROR;
}
} else {
// The read completed right away
return HID_POLL_CARD_READY;
}
}
return HID_POLL_CARD_NOT_READY;
}
cardio_hid_card_type_t cardio_hid_device_read(struct cardio_hid_device *hid_ctx) {
// check if not initialized
if (!hid_ctx->initialized) {
return HID_CARD_NONE;
}
// check if IO is pending
if (hid_ctx->io_pending) {
return HID_CARD_NONE;
}
// check if nothing was read
if (hid_ctx->read_size == 0) {
return HID_CARD_NONE;
}
// iterate collections
for (int i = 0; i < hid_ctx->collection_length; i++) {
HIDP_VALUE_CAPS *item = &hid_ctx->collection[i];
// get usages
NTSTATUS res = HidP_GetUsageValueArray(
HidP_Input,
CARD_READER_USAGE_PAGE,
0, // LinkCollection
item->NotRange.Usage,
(PCHAR) &hid_ctx->u.usage_value,
sizeof(hid_ctx->u.usage_value),
hid_ctx->pp_data,
(PCHAR) &hid_ctx->report_buffer,
hid_ctx->read_size);
// loop through the collection to find the entry that handles this report ID
if (res == HIDP_STATUS_INCOMPATIBLE_REPORT_ID) {
continue;
}
// check if failed
if (res != HIDP_STATUS_SUCCESS) {
return HID_CARD_NONE;
}
// return card type
return (cardio_hid_card_type_t) item->NotRange.Usage;
}
return HID_CARD_NONE;
}

View File

@ -0,0 +1,86 @@
/**
* MIT-License
* Copyright (c) 2018 by Felix
*
* 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
#ifndef SPICETOOLS_CARDIO_HID_H
#define SPICETOOLS_CARDIO_HID_H
#include "../helpers.h"
#include <hidsdi.h>
#include <setupapi.h>
extern CRITICAL_SECTION CARDIO_HID_CRIT_SECTION;
extern struct cardio_hid_device *CARDIO_HID_CONTEXTS;
extern size_t CARDIO_HID_CONTEXTS_LENGTH;
struct cardio_hid_device {
LPWSTR dev_path;
HANDLE dev_handle;
OVERLAPPED read_state;
BOOL initialized;
BOOL io_pending;
BYTE report_buffer[128];
union {
unsigned char usage_value[128];
uint64_t usage64[8];
} u;
DWORD read_size;
PHIDP_PREPARSED_DATA pp_data;
HIDP_CAPS caps;
PHIDP_VALUE_CAPS collection;
USHORT collection_length;
};
typedef enum cardio_poll_value {
HID_POLL_ERROR = 0,
HID_POLL_CARD_NOT_READY = 1,
HID_POLL_CARD_READY = 2,
} cardio_hid_poll_value_t;
typedef enum cardio_hid_card_type {
HID_CARD_NONE = 0,
HID_CARD_ISO_15693 = 0x41,
HID_CARD_ISO_18092 = 0x42,
} cardio_hid_card_type_t;
BOOL cardio_hid_init();
void cardio_hid_close();
BOOL cardio_hid_add_device(LPCWSTR device_path);
BOOL cardio_hid_remove_device(LPCWSTR device_path);
BOOL cardio_hid_scan_device(struct cardio_hid_device *ctx, LPCWSTR device_path);
BOOL cardio_hid_scan();
cardio_hid_poll_value_t cardio_hid_device_poll(struct cardio_hid_device *ctx);
cardio_hid_card_type_t cardio_hid_device_read(struct cardio_hid_device *ctx);
#endif //SPICETOOLS_CARDIO_HID_H

138
src/cardio_plugin/dllmain.c Normal file
View File

@ -0,0 +1,138 @@
#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;
typedef void (*callbackTouch) (i32, i32, u8[168], u64);
callbackTouch touchCallback;
u64 touchData;
static char AccessID[21] = "00000000000000000001";
// static char ChipID[33] = "00000000000000000000000000000001";
void PollReader(){
// 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){
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 };
//Convert card to proper format
sprintf(AccessID, "%020llu", device->u.usage64[0]);
LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION);
memset(AccessID, '0', 10);
//Generating a ChipID Derived from the AccessID
char ChipID[33] = "000000000000";
strcat(ChipID, AccessID);
memset(ChipID, '0', 25);
//Insert card in game
printWarning ("%s (%s): AccessID %s, ChipID %s\n", __func__, module, AccessID, ChipID);
memcpy (cardData + 0x2C, ChipID, 33);
memcpy (cardData + 0x50, AccessID, 21);
touchCallback(0, 0, cardData, touchData);
}else{
LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION);
}
}else{
LeaveCriticalSection(&CARDIO_HID_CRIT_SECTION);
}
}
}
Sleep(500);
}
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 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;
}

80
src/helpers.c Normal file
View File

@ -0,0 +1,80 @@
#include "helpers.h"
void *consoleHandle = 0;
char *
configPath (char *name) {
static char buffer[MAX_PATH];
GetModuleFileNameA (NULL, buffer, MAX_PATH);
*(strrchr (buffer, '\\') + 1) = 0;
strcat_s (buffer, MAX_PATH, name);
return buffer;
}
toml_table_t *
openConfig (char *configFilePath) {
FILE *file = fopen (configFilePath, "r");
if (!file) {
printWarning ("%s (%s): cannot open file\n", __func__, configFilePath);
return 0;
}
char errorbuf[200];
toml_table_t *config = toml_parse_file (file, errorbuf, 200);
fclose (file);
if (!config) {
printWarning ("%s (%s): %s\n", __func__, configFilePath, errorbuf);
return 0;
}
return config;
}
toml_table_t *
openConfigSection (toml_table_t *config, char *sectionName) {
toml_table_t *section = toml_table_in (config, sectionName);
if (!section) {
printWarning ("%s (%s): cannot find section\n", __func__, sectionName);
return 0;
}
return section;
}
bool
readConfigBool (toml_table_t *table, char *key, bool notFoundValue) {
toml_datum_t data = toml_bool_in (table, key);
if (!data.ok) return notFoundValue;
return (bool)data.u.b;
}
int64_t
readConfigInt (toml_table_t *table, char *key, int64_t notFoundValue) {
toml_datum_t data = toml_int_in (table, key);
if (!data.ok) return notFoundValue;
return data.u.i;
}
char *
readConfigString (toml_table_t *table, char *key, char *notFoundValue) {
toml_datum_t data = toml_string_in (table, key);
if (!data.ok) return notFoundValue;
return data.u.s;
}
void
printColour (int colour, const char *format, ...) {
va_list args;
va_start (args, format);
if (consoleHandle == 0) consoleHandle = GetStdHandle (STD_OUTPUT_HANDLE);
SetConsoleTextAttribute (consoleHandle, colour);
vprintf (format, args);
SetConsoleTextAttribute (consoleHandle, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
va_end (args);
}

118
src/helpers.h Normal file
View File

@ -0,0 +1,118 @@
#pragma once
#include <MinHook.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <toml.h>
#include <windows.h>
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
#ifdef DEFINE_GUID
#undef DEFINE_GUID
#endif
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
#ifdef BASE_ADDRESS
#define ASLR(address, handle) ((u64)handle + (u64)address - (u64)BASE_ADDRESS)
#endif
#define FUNCTION_PTR(returnType, callingConvention, function, location, ...) \
returnType (callingConvention *function) (__VA_ARGS__) = (returnType (callingConvention *) (__VA_ARGS__)) (location)
#define PROC_ADDRESS(libraryName, procName) GetProcAddress (LoadLibrary (TEXT (libraryName)), procName)
#define HOOK(returnType, callingConvention, functionName, location, ...) \
typedef returnType callingConvention (*functionName) (__VA_ARGS__); \
functionName original##functionName = NULL; \
void *where##functionName = (void *)location; \
returnType callingConvention implOf##functionName (__VA_ARGS__)
#define HOOK_DYNAMIC(returnType, callingConvention, functionName, ...) \
typedef returnType callingConvention (*functionName) (__VA_ARGS__); \
functionName original##functionName = NULL; \
void *where##functionName = NULL; \
returnType callingConvention implOf##functionName (__VA_ARGS__)
#define INSTALL_HOOK(functionName) \
{ \
MH_Initialize (); \
MH_CreateHook ((void *)where##functionName, (void *)implOf##functionName, (void **)(&original##functionName)); \
MH_EnableHook ((void *)where##functionName); \
}
#define INSTALL_HOOK_DYNAMIC(functionName, location) \
{ \
where##functionName = (void *)location; \
INSTALL_HOOK (functionName); \
}
#define WRITE_MEMORY(location, type, ...) \
{ \
const type data[] = { __VA_ARGS__ }; \
DWORD oldProtect; \
VirtualProtect ((void *)(location), sizeof (data), PAGE_EXECUTE_READWRITE, &oldProtect); \
memcpy ((void *)(location), data, sizeof (data)); \
VirtualProtect ((void *)(location), sizeof (data), oldProtect, &oldProtect); \
}
#define WRITE_MEMORY_STRING(location, data, length) \
{ \
DWORD oldProtect; \
VirtualProtect ((void *)(location), length, PAGE_EXECUTE_READWRITE, &oldProtect); \
memcpy ((void *)(location), data, length); \
VirtualProtect ((void *)(location), length, oldProtect, &oldProtect); \
}
#define WRITE_NOP(location, count) \
{ \
DWORD oldProtect; \
VirtualProtect ((void *)(location), (size_t)(count), PAGE_EXECUTE_READWRITE, &oldProtect); \
for (size_t i = 0; i < (size_t)(count); i++) \
*((uint8_t *)(location) + i) = 0x90; \
VirtualProtect ((void *)(location), (size_t)(count), oldProtect, &oldProtect); \
}
#define WRITE_NULL(location, count) \
{ \
DWORD oldProtect; \
VirtualProtect ((void *)(location), (size_t)(count), PAGE_EXECUTE_READWRITE, &oldProtect); \
for (size_t i = 0; i < (size_t)(count); i++) \
*((uint8_t *)(location) + i) = 0x00; \
VirtualProtect ((void *)(location), (size_t)(count), oldProtect, &oldProtect); \
}
#define COUNTOFARR(arr) sizeof (arr) / sizeof (arr[0])
#define RETURN_FALSE(returnType, callingConvention, functionName, ...) \
returnType callingConvention functionName (__VA_ARGS__) { return 0; }
#define RETURN_TRUE(returnType, callingConvention, functionName, ...) \
returnType callingConvention functionName (__VA_ARGS__) { return 1; }
#define INFO_COLOUR FOREGROUND_GREEN
#define WARNING_COLOUR (FOREGROUND_RED | FOREGROUND_GREEN)
#define ERROR_COLOUR FOREGROUND_RED
#define printInfo(format, ...) printColour (INFO_COLOUR, format, __VA_ARGS__)
#define printWarning(format, ...) printColour (WARNING_COLOUR, format, __VA_ARGS__)
#define printError(format, ...) printColour (ERROR_COLOUR, format, __VA_ARGS__)
#ifdef __cplusplus
extern "C" {
#endif
char *configPath (char *name);
toml_table_t *openConfig (char *configFilePath);
toml_table_t *openConfigSection (toml_table_t *config, char *sectionName);
bool readConfigBool (toml_table_t *table, char *key, bool notFoundValue);
int64_t readConfigInt (toml_table_t *table, char *key, int64_t notFoundValue);
char *readConfigString (toml_table_t *table, char *key, char *notFoundValue);
void printColour (int colour, const char *format, ...);
#ifdef __cplusplus
}
#endif

25
src/meson.build Normal file
View File

@ -0,0 +1,25 @@
compiler = meson.get_compiler('c')
hid_lib = compiler.find_library('hid')
setupapi_lib = compiler.find_library('setupapi')
deps = [
dependency('tomlc99'),
dependency('minhook'),
hid_lib,
setupapi_lib
]
shared_library(
'cardio',
implicit_include_directories : false,
vs_module_defs : 'cardio.def',
sources: [
'helpers.c',
'cardio_plugin/dllmain.c',
'cardio_plugin/cardio.c',
],
dependencies: deps,
include_directories: [
inc
]
)

6
subprojects/minhook.wrap Normal file
View File

@ -0,0 +1,6 @@
[wrap-git]
url = https://github.com/TsudaKageyu/minhook
revision = 426cb6880035ee3cceed05384bb3f2db01a20a15
# Overlays subproject/packagefiles/minhook over the fresh git
# clone of minhook
patch_directory = minhook

View File

@ -0,0 +1,29 @@
project(
'minhook',
'c',
default_options : ['c_std=c99']
)
sources = files(
'src/buffer.c',
'src/hde/hde32.c',
'src/hde/hde64.c',
'src/hook.c',
'src/trampoline.c'
)
includes = include_directories('include')
minhook_lib = static_library(
'minhook',
sources,
include_directories: includes,
)
minhook_dep = declare_dependency(
include_directories: includes,
link_with: minhook_lib,
)
meson.override_dependency('minhook', minhook_dep)

View File

@ -0,0 +1,22 @@
project(
'tomlc99',
'c',
default_options : ['c_std=c99']
)
sources = files('toml.c')
includes = include_directories('.')
tomlc99_lib = static_library(
'tomlc99',
sources,
include_directories: includes,
)
tomlc99_dep = declare_dependency(
include_directories: includes,
link_with: tomlc99_lib,
)
meson.override_dependency('tomlc99', tomlc99_dep)

6
subprojects/tomlc99.wrap Normal file
View File

@ -0,0 +1,6 @@
[wrap-git]
url = https://github.com/cktan/tomlc99
revision = 894902820a3ea2f1ec470cd7fe338bde54045cf5
# Overlays subproject/packagefiles/tomlc99 over the fresh git
# clone of tomlc99
patch_directory = tomlc99