diff --git a/segatools/aimeio/aimeio.c b/segatools/aimeio/aimeio.c new file mode 100644 index 0000000..f36755a --- /dev/null +++ b/segatools/aimeio/aimeio.c @@ -0,0 +1,323 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "aimeio/aimeio.h" + +#include "util/crc.h" +#include "util/dprintf.h" + +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; + +struct IPCMemoryInfo +{ + uint8_t airIoStatus[6]; + uint8_t sliderIoStatus[32]; + uint8_t ledRgbData[32 * 3]; + uint8_t testBtn; + uint8_t serviceBtn; + uint8_t coinInsertion; + uint8_t cardRead; +}; +typedef struct IPCMemoryInfo IPCMemoryInfo; +static HANDLE FileMappingHandle; +IPCMemoryInfo* FileMapping; + +void initSharedMemory() +{ + if (FileMapping) + { + return; + } + if ((FileMappingHandle = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, sizeof(IPCMemoryInfo), "Local\\BROKENITHM_SHARED_BUFFER")) == 0) + { + return; + } + + if ((FileMapping = (IPCMemoryInfo*)MapViewOfFile(FileMappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(IPCMemoryInfo))) == 0) + { + return; + } + + memset(FileMapping, 0, sizeof(IPCMemoryInfo)); + SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_CONTINUOUS); +} + +static void aime_io_config_read( + struct aime_io_config *cfg, + const wchar_t *filename); + +static HRESULT aime_io_read_id_file( + const wchar_t *path, + uint8_t *bytes, + size_t nbytes); + +static HRESULT aime_io_generate_felica( + const wchar_t *path, + uint8_t *bytes, + size_t nbytes); + +static void aime_io_config_read( + struct aime_io_config *cfg, + const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + GetPrivateProfileStringW( + L"aime", + L"aimePath", + L"DEVICE\\aime.txt", + cfg->aime_path, + _countof(cfg->aime_path), + filename); + + GetPrivateProfileStringW( + L"aime", + L"felicaPath", + L"DEVICE\\felica.txt", + cfg->felica_path, + _countof(cfg->felica_path), + filename); + + cfg->felica_gen = GetPrivateProfileIntW( + L"aime", + L"felicaGen", + 1, + filename); + + cfg->vk_scan = GetPrivateProfileIntW( + L"aime", + L"scan", + VK_RETURN, + filename); +} + +static HRESULT aime_io_read_id_file( + const wchar_t *path, + uint8_t *bytes, + size_t nbytes) +{ + HRESULT hr; + FILE *f; + size_t i; + int byte; + int r; + + f = _wfopen(path, L"r"); + + if (f == NULL) { + return S_FALSE; + } + + memset(bytes, 0, nbytes); + + for (i = 0 ; i < nbytes ; i++) { + r = fscanf(f, "%02x ", &byte); + + if (r != 1) { + hr = E_FAIL; + dprintf("AimeIO DLL: %S: fscanf[%i] failed: %i\n", + path, + (int) i, + r); + + goto end; + } + + bytes[i] = byte; + } + + 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) { + dprintf("AimeIO DLL: %S: fopen failed: %i\n", path, (int) errno); + + return E_FAIL; + } + + for (i = 0 ; i < nbytes ; i++) { + fprintf(f, "%02X", bytes[i]); + } + + fprintf(f, "\n"); + fclose(f); + + dprintf("AimeIO DLL: Generated random FeliCa ID\n"); + + return S_OK; +} + +HRESULT aime_io_init(void) +{ + aime_io_config_read(&aime_io_cfg, L".\\segatools.ini"); + + initSharedMemory(); + + return S_OK; +} + +void aime_io_fini(void) +{ +} + +HRESULT aime_io_nfc_poll(uint8_t unit_no) +{ + bool sense; + HRESULT hr; + + if (unit_no != 0) { + return S_OK; + } + + /* Reset presence flags */ + + aime_io_aime_id_present = false; + aime_io_felica_id_present = false; + + /* Don't do anything more if the scan key is not held */ + + if (FileMapping && FileMapping->cardRead) { + sense = true; + FileMapping->cardRead = 0; + } else { + sense = GetAsyncKeyState(aime_io_cfg.vk_scan) & 0x8000; + } + + if (!sense) { + return S_OK; + } + + /* Try AiMe IC */ + + hr = aime_io_read_id_file( + aime_io_cfg.aime_path, + aime_io_aime_id, + sizeof(aime_io_aime_id)); + + 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)); + + 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) +{ + assert(luid != NULL); + assert(luid_size == sizeof(aime_io_aime_id)); + + if (unit_no != 0 || !aime_io_aime_id_present) { + return S_FALSE; + } + + memcpy(luid, aime_io_aime_id, luid_size); + + return S_OK; +} + +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 || !aime_io_felica_id_present) { + return S_FALSE; + } + + val = 0; + + for (i = 0 ; i < 8 ; i++) { + val = (val << 8) | aime_io_felica_id[i]; + } + + *IDm = val; + + return S_OK; +} + +void aime_io_led_set_color(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b) +{} \ No newline at end of file diff --git a/segatools/chuniio/chuniio.c b/segatools/chuniio/chuniio.c new file mode 100644 index 0000000..ccb34c1 --- /dev/null +++ b/segatools/chuniio/chuniio.c @@ -0,0 +1,165 @@ +#include + +#include +#include +#include + +#include "chuniio/chuniio.h" +#include "chuniio/config.h" + +static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx); + +static bool chuni_io_coin; +static uint16_t chuni_io_coins; +static HANDLE chuni_io_slider_thread; +static bool chuni_io_slider_stop_flag; +static struct chuni_io_config chuni_io_cfg; + +struct IPCMemoryInfo +{ + uint8_t airIoStatus[6]; + uint8_t sliderIoStatus[32]; + uint8_t ledRgbData[32 * 3]; + uint8_t testBtn; + uint8_t serviceBtn; + uint8_t coinInsertion; + uint8_t cardRead; +}; +typedef struct IPCMemoryInfo IPCMemoryInfo; +static HANDLE FileMappingHandle; +IPCMemoryInfo* FileMapping; + +void initSharedMemory() +{ + if (FileMapping) + { + return; + } + if ((FileMappingHandle = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, sizeof(IPCMemoryInfo), "Local\\BROKENITHM_SHARED_BUFFER")) == 0) + { + return; + } + + if ((FileMapping = (IPCMemoryInfo*)MapViewOfFile(FileMappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(IPCMemoryInfo))) == 0) + { + return; + } + + memset(FileMapping, 0, sizeof(IPCMemoryInfo)); + SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_CONTINUOUS); +} + +HRESULT chuni_io_jvs_init(void) +{ + chuni_io_config_load(&chuni_io_cfg, L".\\segatools.ini"); + + initSharedMemory(); + + return S_OK; +} + +void chuni_io_jvs_read_coin_counter(uint16_t *out) +{ + if (out == NULL) { + return; + } + + if (FileMapping && FileMapping->coinInsertion) { + chuni_io_coins++; + FileMapping->coinInsertion = 0; + } else { + if (GetAsyncKeyState(chuni_io_cfg.vk_coin)) { + if (!chuni_io_coin) { + chuni_io_coin = true; + chuni_io_coins++; + } + } else { + chuni_io_coin = false; + } + } + + *out = chuni_io_coins; +} + +void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams) +{ + size_t i; + + if ((FileMapping && FileMapping->testBtn) || GetAsyncKeyState(chuni_io_cfg.vk_test)) { + *opbtn |= 0x01; /* Test */ + } + + if ((FileMapping && FileMapping->serviceBtn) || GetAsyncKeyState(chuni_io_cfg.vk_service)) { + *opbtn |= 0x02; /* Service */ + } + + for (i = 0 ; i < 6 ; i++) { + if (FileMapping && FileMapping->airIoStatus[i]) { + *beams |= (1 << i); + } + } +} + +void chuni_io_jvs_set_coin_blocker(bool open) +{} + +HRESULT chuni_io_slider_init(void) +{ + return S_OK; +} + +void chuni_io_slider_start(chuni_io_slider_callback_t callback) +{ + if (chuni_io_slider_thread != NULL) { + return; + } + + chuni_io_slider_thread = (HANDLE) _beginthreadex( + NULL, + 0, + chuni_io_slider_thread_proc, + callback, + 0, + NULL); +} + +void chuni_io_slider_stop(void) +{ + if (chuni_io_slider_thread == NULL) { + return; + } + + chuni_io_slider_stop_flag = true; + + WaitForSingleObject(chuni_io_slider_thread, INFINITE); + CloseHandle(chuni_io_slider_thread); + chuni_io_slider_thread = NULL; + chuni_io_slider_stop_flag = false; +} + +void chuni_io_slider_set_leds(const uint8_t *rgb) +{ + if (FileMapping) + { + memcpy(FileMapping->ledRgbData, rgb, 32 * 3); + } +} + +static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx) +{ + chuni_io_slider_callback_t callback; + uint8_t pressure[32] = { 0 }; + + callback = ctx; + + while (!chuni_io_slider_stop_flag) { + if (FileMapping) { + memcpy(pressure, FileMapping->sliderIoStatus, 32); + } + + callback(pressure); + Sleep(1); + } + + return 0; +} \ No newline at end of file