mirror of
https://github.com/AkaiiKitsune/TAL_CardReader
synced 2024-11-24 01:20:10 +01:00
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:
parent
6c6aabed78
commit
c047663c90
6
.gitignore
vendored
6
.gitignore
vendored
@ -50,3 +50,9 @@ modules.order
|
|||||||
Module.symvers
|
Module.symvers
|
||||||
Mkfile.old
|
Mkfile.old
|
||||||
dkms.conf
|
dkms.conf
|
||||||
|
|
||||||
|
# Vscode specific
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
subprojects/minhook
|
||||||
|
subprojects/tomlc99
|
24
LICENSE
Normal file
24
LICENSE
Normal 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
64
PLUGINS.md
Normal 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
|
24
README.md
24
README.md
@ -1,2 +1,22 @@
|
|||||||
# TAL_CardIO
|
# TAL CardIO
|
||||||
Allows you to use USB HID card readers (cardio) with TAL
|
|
||||||
|
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
11
build64.bat
Normal 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
9
meson.build
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
project(
|
||||||
|
'TAL_CardIO',
|
||||||
|
'c',
|
||||||
|
version : '0.1.0'
|
||||||
|
)
|
||||||
|
|
||||||
|
inc = include_directories('.')
|
||||||
|
|
||||||
|
subdir('src/')
|
6
src/cardio.def
Normal file
6
src/cardio.def
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
LIBRARY cardio
|
||||||
|
|
||||||
|
EXPORTS
|
||||||
|
Init
|
||||||
|
WaitTouch
|
||||||
|
Exit
|
437
src/cardio_plugin/cardio.c
Normal file
437
src/cardio_plugin/cardio.c
Normal 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;
|
||||||
|
}
|
86
src/cardio_plugin/cardio.h
Normal file
86
src/cardio_plugin/cardio.h
Normal 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
138
src/cardio_plugin/dllmain.c
Normal 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
80
src/helpers.c
Normal 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
118
src/helpers.h
Normal 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
25
src/meson.build
Normal 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
6
subprojects/minhook.wrap
Normal 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
|
29
subprojects/packagefiles/minhook/meson.build
Normal file
29
subprojects/packagefiles/minhook/meson.build
Normal 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)
|
||||||
|
|
22
subprojects/packagefiles/tomlc99/meson.build
Normal file
22
subprojects/packagefiles/tomlc99/meson.build
Normal 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
6
subprojects/tomlc99.wrap
Normal 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
|
Loading…
Reference in New Issue
Block a user