diff --git a/CMakeLists.txt b/CMakeLists.txt index b1fc980..6d3422c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,20 +1,12 @@ cmake_minimum_required(VERSION 3.27) -project(chuniio_brokenithm) +project(brokenithm) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") include_directories("${CMAKE_SOURCE_DIR}/include/") set(CMAKE_C_STANDARD 11) -link_directories(src) -add_library(chuniio_brokenithm SHARED src/chuniio.c - src/chuniio.h - src/config.c - src/config.h - src/socket.h - src/struct.h - src/util/dprintf.c - src/util/dprintf.h) +include_directories(.) -set_target_properties(chuniio_brokenithm PROPERTIES PREFIX "") -# set_target_properties(chuniio_brokenithm PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") -target_link_libraries(chuniio_brokenithm ws2_32) +add_subdirectory(aimeio) +add_subdirectory(chuniio) +add_subdirectory(util) diff --git a/README.md b/README.md index d8c3458..4e17ded 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,19 @@ # chuniio-brokenithm -ChuniIO driver for [Brokenithm-Android](https://github.com/tindy2013/Brokenithm-Android) +ChuniIO/AimeIO driver for [Brokenithm-Android](https://github.com/tindy2013/Brokenithm-Android) and +[Brokenithm-iOS](https://github.com/esterTion/Brokenithm-iOS) without needing an external server. -It is recommended to use this with [Dniel97's segatools](https://gitea.tendokyu.moe/Dniel97/segatools/releases), -since it allows loading 32-bit chuniio DLLs without any messy hacks. - ## Configuration segatools.ini ```ini +[aimeio] +path64=aimeio_brokenithm.dll + [chuniio] -path=chuniio_brokenithm.dll +path32=chuniio_brokenithm_x86.dll +path64=chuniio_brokenithm_x64.dll [io3] ; Test button virtual-key code. Default is the 1 key. @@ -34,11 +36,21 @@ port=52468 ## Build instructions ```shell -mkdir cmake-build -cd cmake-build +# In MinGW32 +pacman -S mingw-w64-i686-libimobiledevice -cmake .. +mkdir -p build/build32 +cd build/build32 +cmake ../.. ninja -ls chuniio_brokenithm.dll +# In MinGW64 +mkdir -p build/build64 +cd build/build64 +cmake ../.. +ninja + +ls build/build32/chuniio/chuniio_brokenithm.dll +ls build/build64/chuniio/chuniio_brokenithm.dll +ls build/build64/aimeio/aimeio_brokenithm.dll ``` \ No newline at end of file diff --git a/aimeio/CMakeLists.txt b/aimeio/CMakeLists.txt new file mode 100644 index 0000000..ac54351 --- /dev/null +++ b/aimeio/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.27) +project(aimeio_brokenithm) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") +include_directories("${CMAKE_SOURCE_DIR}/include/") +set(CMAKE_C_STANDARD 11) + +link_directories(src) +add_library(aimeio_brokenithm SHARED src/aimeio.c + src/aimeio.h) + +target_link_libraries(aimeio_brokenithm util) + +set_target_properties(aimeio_brokenithm PROPERTIES PREFIX "") +target_include_directories(aimeio_brokenithm PRIVATE src) + +target_link_libraries(aimeio_brokenithm "-static-libgcc") diff --git a/aimeio/src/aimeio.c b/aimeio/src/aimeio.c new file mode 100644 index 0000000..eb5a0f4 --- /dev/null +++ b/aimeio/src/aimeio.c @@ -0,0 +1,347 @@ +// +// Created by beerpsi on 12/31/2023. +// + +#include "aimeio.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "util/dprintf.h" + +struct aime_io_config { + wchar_t aime_path[MAX_PATH]; + wchar_t felica_path[MAX_PATH]; + bool felica_gen; + bool aime_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 aime_io_ipc_memory_info { + 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; + uint8_t remoteCardRead; + uint8_t remoteCardType; + uint8_t remoteCardId[10]; +}; +typedef struct aime_io_ipc_memory_info aime_io_ipc_memory_info; +static HANDLE aime_io_file_mapping_handle; +aime_io_ipc_memory_info *aime_io_file_mapping; + +void aime_io_init_shared_memory() { + if (aime_io_file_mapping) { + return; + } + if ((aime_io_file_mapping_handle = + CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, + sizeof(aime_io_ipc_memory_info), + "Local\\BROKENITHM_SHARED_BUFFER")) == 0) { + return; + } + + if ((aime_io_file_mapping = (aime_io_ipc_memory_info *)MapViewOfFile( + aime_io_file_mapping_handle, FILE_MAP_ALL_ACCESS, 0, 0, + sizeof(aime_io_ipc_memory_info))) == 0) { + return; + } + + memset(aime_io_file_mapping, 0, sizeof(aime_io_ipc_memory_info)); + 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); + dprintf("NFC: felicaPath GetLastError %lx\n", GetLastError()); + + cfg->felica_gen = GetPrivateProfileIntW(L"aime", L"felicaGen", 0, filename); + + cfg->aime_gen = GetPrivateProfileIntW(L"aime", L"aimeGen", 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; +} + +static HRESULT aime_io_generate_aime(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)); + + /* AiMe IDs should not start with 3, due to a missing check for BananaPass IDs */ + do { + for (i = 0; i < nbytes; i++) { + bytes[i] = rand() % 10 << 4 | rand() % 10; + } + } while (bytes[0] >> 4 == 3); + + 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 AiMe ID\n"); + + return S_OK; +} + +uint16_t aime_io_get_api_version(void) { return 0x0100; } + +HRESULT aime_io_init(void) { + aime_io_config_read(&aime_io_cfg, L".\\segatools.ini"); + + aime_io_init_shared_memory(); + + return S_OK; +} + +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; + + /* First check remote card status, if there is one report it */ + + if (aime_io_file_mapping && aime_io_file_mapping->remoteCardRead) { + switch (aime_io_file_mapping->remoteCardType) { + case 0: // Aime + memcpy(aime_io_aime_id, aime_io_file_mapping->remoteCardId, 10); + aime_io_aime_id_present = true; + break; + case 1: // FeliCa + memcpy(aime_io_felica_id, aime_io_file_mapping->remoteCardId, 8); + aime_io_felica_id_present = true; + break; + } + return S_OK; + } + + /* Don't do anything more if the scan key is not held */ + + if (aime_io_file_mapping && aime_io_file_mapping->cardRead) { + sense = true; + aime_io_file_mapping->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 generating AiMe IC (if enabled) */ + + if (aime_io_cfg.aime_gen) { + hr = aime_io_generate_aime(aime_io_cfg.aime_path, aime_io_aime_id, + sizeof(aime_io_aime_id)); + + if (FAILED(hr)) { + return hr; + } + + 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) {} diff --git a/aimeio/src/aimeio.h b/aimeio/src/aimeio.h new file mode 100644 index 0000000..50a9bc2 --- /dev/null +++ b/aimeio/src/aimeio.h @@ -0,0 +1,94 @@ +// +// Created by beerpsi on 12/31/2023. +// + +#ifndef BROKENITHM_AIMEIO_H +#define BROKENITHM_AIMEIO_H + +#include + +#include +#include + +/* + Get the version of the Aime IO API that this DLL supports. This function + should return a positive 16-bit integer, where the high byte is the major + version and the low byte is the minor version (as defined by the Semantic + Versioning standard). + + The latest API version as of this writing is 0x0100. + */ +uint16_t aime_io_get_api_version(void); + +/* + Initialize Aime IO provider DLL. Only called once, before any other + functions exported from this DLL are called (except for + aime_io_get_api_version). + + Minimum API version: 0x0100 + */ +HRESULT aime_io_init(void); + +/* + Poll for IC cards in the vicinity. + + - unit_no: Always 0 as of the current API version + + Minimum API version: 0x0100 + */ +HRESULT aime_io_nfc_poll(uint8_t unit_no); + +/* + Attempt to read out a classic Aime card ID + + - unit_no: Always 0 as of the current API version + - luid: Pointer to a ten-byte buffer that will receive the ID + - luid_size: Size of the buffer at *luid. Always 10. + + Returns: + + - S_OK if a classic Aime is present and was read successfully + - S_FALSE if no classic Aime card is present (*luid will be ignored) + - Any HRESULT error if an error occured. + + Minimum API version: 0x0100 +*/ +HRESULT aime_io_nfc_get_aime_id( + uint8_t unit_no, + uint8_t *luid, + size_t luid_size); + +/* + Attempt to read out a FeliCa card ID ("IDm"). The following are examples + of FeliCa cards: + + - Amuse IC (which includes new-style Aime-branded cards, among others) + - Smartphones with FeliCa NFC capability (uncommon outside Japan) + - Various Japanese e-cash cards and train passes + + Parameters: + + - unit_no: Always 0 as of the current API version + - IDm: Output parameter that will receive the card ID + + Returns: + + - S_OK if a FeliCa device is present and was read successfully + - S_FALSE if no FeliCa device is present (*IDm will be ignored) + - Any HRESULT error if an error occured. + + Minimum API version: 0x0100 +*/ +HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm); + +/* + Change the color and brightness of the card reader's RGB lighting + + - unit_no: Always 0 as of the current API version + - r, g, b: Primary color intensity, from 0 to 255 inclusive. + + Minimum API version: 0x0100 +*/ +void aime_io_led_set_color(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b); + +#endif // BROKENITHM_AIMEIO_H diff --git a/chuniio/CMakeLists.txt b/chuniio/CMakeLists.txt new file mode 100644 index 0000000..1fe130f --- /dev/null +++ b/chuniio/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.27) +project(chuniio_brokenithm) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") +include_directories("${CMAKE_SOURCE_DIR}/include/") +set(CMAKE_C_STANDARD 11) + +link_directories(src) +add_library(chuniio_brokenithm SHARED src/chuniio.c + src/chuniio.h + src/config.c + src/config.h + src/socket.h + src/struct.h + src/servers/android.c + src/servers/android.h + src/servers/common.h + src/servers/common.c + src/servers/ios.c + src/servers/ios.h) + +target_link_libraries(chuniio_brokenithm util) +set_target_properties(chuniio_brokenithm PROPERTIES PREFIX "") +target_include_directories(chuniio_brokenithm PRIVATE src) + +if (CMAKE_SIZEOF_VOID_P EQUAL 4) + # Ugly hack around libimobiledevice shipping a DllMain for some reason??? + set_target_properties(chuniio_brokenithm PROPERTIES LINK_FLAGS "-Wl,--allow-multiple-definition") + + target_link_libraries(chuniio_brokenithm "-static-libgcc -Wl,-Bstatic -limobiledevice-1.0 -lssl -lcrypto -lplist-2.0 -lusbmuxd-2.0 -lpthread -Wl,-Bdynamic -lws2_32 -lcrypt32 -liphlpapi") +elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + target_link_libraries(chuniio_brokenithm "-static-libgcc") +endif () diff --git a/chuniio/src/arch.h b/chuniio/src/arch.h new file mode 100644 index 0000000..697f2aa --- /dev/null +++ b/chuniio/src/arch.h @@ -0,0 +1,24 @@ +// +// Created by beerpsi on 12/31/2023. +// + +#ifndef BROKENITHM_ARCH_H +#define BROKENITHM_ARCH_H + +#if _WIN32 || _WIN64 +#if _WIN64 +#define ENV64BIT +#else +#define ENV32BIT +#endif // _WIN64 +#endif // _WIN32 || _WIN64 + +#if __GNUC__ +#if __x86_64__ || __ppc64__ +#define ENV64BIT +#else +#define ENV32BIT +#endif // __x86_64__ || __ppc64__ +#endif + +#endif // BROKENITHM_ARCH_H diff --git a/chuniio/src/chuniio.c b/chuniio/src/chuniio.c new file mode 100644 index 0000000..4d35edf --- /dev/null +++ b/chuniio/src/chuniio.c @@ -0,0 +1,231 @@ +// +// Created by beerpsi on 12/30/2023. +// + +#include "chuniio.h" + +#include + +#include "arch.h" +#include "config.h" +#include "struct.h" + +#define MEM_FILE_NAME "Local\\BROKENITHM_SHARED_BUFFER" + +struct IPCMemoryInfo *chuni_io_file_mapping; + +#ifdef ENV32BIT +#include "servers/android.h" +#include "servers/common.h" +#include "servers/ios.h" + +HRESULT server_start() { + HANDLE hMapFile = OpenFileMappingA(FILE_MAP_ALL_ACCESS, false, MEM_FILE_NAME); + + if (hMapFile == NULL) { + hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, + 1024, MEM_FILE_NAME); + if (hMapFile == NULL) { + print_err("[ERROR] CreateFileMapping failed! error: %lu\n", GetLastError()); + return E_FAIL; + } + } + + struct IPCMemoryInfo *memory = + MapViewOfFileEx(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 1024, NULL); + chuni_io_file_mapping = memory; + + HRESULT result; + + if ((result = android_init_server(memory))) { + print_err("[ERROR] Android server intialization failed: %ld", result); + + return result; + } + + if ((result = ios_init_server(memory))) { + print_err("[ERROR] iOS server initialization failed: %ld", result); + + return result; + } + + return S_OK; +} +#endif + +#ifdef ENV64BIT +#include "util/dprintf.h" + +static HANDLE chuni_io_file_mapping_handle; + +void chuni_io_init_shared_memory() { + if (chuni_io_file_mapping) { + dprintf("chuni_io_init_shared_memory: shared memory already exists\n"); + return; + } + + if ((chuni_io_file_mapping_handle = + CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, + sizeof(struct IPCMemoryInfo), MEM_FILE_NAME)) == 0) { + dprintf("chuni_io_init_shared_memory: could not create file mapping: %ld\n", + GetLastError()); + return; + } + + if ((chuni_io_file_mapping = (struct IPCMemoryInfo *)MapViewOfFile( + chuni_io_file_mapping_handle, FILE_MAP_ALL_ACCESS, 0, 0, + sizeof(struct IPCMemoryInfo))) == 0) { + dprintf("chuni_io_init_shared_memory: could not get view of file: %ld\n", + GetLastError()); + return; + } + + memset(chuni_io_file_mapping, 0, sizeof(struct IPCMemoryInfo)); + SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_CONTINUOUS); +} +#endif + +static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx); + +static bool chuni_io_coin; +static uint16_t chuni_io_coins; +static uint8_t chuni_io_hand_pos; +static HANDLE chuni_io_slider_thread; +static bool chuni_io_slider_stop_flag; +static struct chuni_io_config chuni_io_cfg; + +uint16_t chuni_io_get_api_version() { return 0x0102; } + +HRESULT chuni_io_jvs_init() { + chuni_io_config_load(&chuni_io_cfg, L".\\segatools.ini"); + +#ifdef ENV32BIT + HRESULT result = server_start(); + if (result != S_OK) { + return result; + } +#endif +#ifdef ENV64BIT + chuni_io_init_shared_memory(); +#endif + + return S_OK; +} + +void chuni_io_jvs_read_coin_counter(uint16_t *total) { + if (total == NULL) { + return; + } + + if (chuni_io_file_mapping && chuni_io_file_mapping->coinInsertion) { + chuni_io_coins++; + chuni_io_file_mapping->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; + } + } + + *total = chuni_io_coins; +} + +void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams) { + size_t i; + + if ((chuni_io_file_mapping && chuni_io_file_mapping->testBtn) || + GetAsyncKeyState(chuni_io_cfg.vk_test)) { + *opbtn |= CHUNI_IO_OPBTN_TEST; /* Test */ + } + + if ((chuni_io_file_mapping && chuni_io_file_mapping->serviceBtn) || + GetAsyncKeyState(chuni_io_cfg.vk_service)) { + *opbtn |= CHUNI_IO_OPBTN_SERVICE; /* Service */ + } + + if (GetAsyncKeyState(chuni_io_cfg.vk_ir_emu)) { + if (chuni_io_hand_pos < 6) { + chuni_io_hand_pos++; + } + } else { + if (chuni_io_hand_pos > 0) { + chuni_io_hand_pos--; + } + } + + for (i = 0; i < 6; i++) { + if (chuni_io_hand_pos > i) { + *beams |= (1 << i); + } + } + + // IR format is beams[5:0] = {b5,b6,b3,b4,b1,b2}; + for (i = 0; i < 3; i++) { + if (chuni_io_file_mapping && chuni_io_file_mapping->airIoStatus[i * 2]) + *beams |= 1 << (i * 2 + 1); + if (chuni_io_file_mapping && chuni_io_file_mapping->airIoStatus[i * 2 + 1]) + *beams |= 1 << i * 2; + } +} + +HRESULT chuni_io_slider_init() { return S_OK; } + +void chuni_io_slider_start(void *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 (chuni_io_file_mapping) { + memcpy(chuni_io_file_mapping->ledRgbData, rgb, 32 * 3); + } +} + +HRESULT chuni_io_led_init(void) { return S_OK; } + +void chuni_io_led_set_colors(uint8_t board, uint8_t *rgb) {} + +static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx) { + const chuni_io_slider_callback_t callback = ctx; + +#pragma clang diagnostic push +#pragma ide diagnostic ignored "LoopDoesntUseConditionVariableInspection" + // ReSharper disable once CppDFALoopConditionNotUpdated + while (!chuni_io_slider_stop_flag) { + uint8_t pressure[32]; + if (chuni_io_file_mapping) { + memcpy(pressure, chuni_io_file_mapping->sliderIoStatus, 32); + } + + callback(pressure); + Sleep(1); + } +#pragma clang diagnostic pop + + return 0; +} + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { + return TRUE; +} diff --git a/src/chuniio.h b/chuniio/src/chuniio.h similarity index 87% rename from src/chuniio.h rename to chuniio/src/chuniio.h index 8c60729..62b93fc 100644 --- a/src/chuniio.h +++ b/chuniio/src/chuniio.h @@ -5,22 +5,6 @@ #ifndef CHUNIIO_BROKENITHM_CHUNIIO_H #define CHUNIIO_BROKENITHM_CHUNIIO_H -#if _WIN32 || _WIN64 -#if _WIN64 -#define ENV64BIT -#else -#define ENV32BIT -#endif // _WIN64 -#endif // _WIN32 || _WIN64 - -#if __GNUC__ -#if __x86_64__ || __ppc64__ -#define ENV64BIT -#else -#define ENV32BIT -#endif // __x86_64__ || __ppc64__ -#endif - #define WIN32_LEAN_AND_MEAN #include @@ -40,7 +24,7 @@ enum { The latest API version as of this writing is 0x0101. */ -uint16_t chuni_io_get_api_version(); +uint16_t __declspec(dllexport) chuni_io_get_api_version(); /* Initialize JVS-based input. This function will be called before any other chuni_io_jvs_*() function calls. Errors returned from this function will @@ -52,7 +36,7 @@ uint16_t chuni_io_get_api_version(); Minimum API version: 0x0100 */ -HRESULT chuni_io_jvs_init(); +HRESULT __declspec(dllexport) chuni_io_jvs_init(); /* Poll JVS input. @@ -76,7 +60,7 @@ HRESULT chuni_io_jvs_init(); Minimum API version: 0x0100 Latest API version: 0x0101 */ -void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams); +void __declspec(dllexport) chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams); /* Read the current state of the coin counter. This value should be incremented for every coin detected by the coin acceptor mechanism. This count does not @@ -84,7 +68,7 @@ void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams); Minimum API version: 0x0100 */ -void chuni_io_jvs_read_coin_counter(uint16_t *total); +void __declspec(dllexport) chuni_io_jvs_read_coin_counter(uint16_t *total); /* Initialize touch slider emulation. This function will be called before any @@ -96,7 +80,7 @@ void chuni_io_jvs_read_coin_counter(uint16_t *total); Minimum API version: 0x0100 */ -HRESULT chuni_io_slider_init(void); +HRESULT __declspec(dllexport) chuni_io_slider_init(void); /* Chunithm touch slider layout: @@ -132,7 +116,7 @@ typedef void (*chuni_io_slider_callback_t)(const uint8_t *state); Minimum API version: 0x0100 */ -void chuni_io_slider_start(void *callback); +void __declspec(dllexport) chuni_io_slider_start(void *callback); /* Stop polling the slider. You must cease to invoke the input callback before returning from this function. @@ -147,7 +131,7 @@ void chuni_io_slider_start(void *callback); Minimum API version: 0x0100 */ -void chuni_io_slider_stop(void); +void __declspec(dllexport) chuni_io_slider_stop(void); /* Update the RGB lighting on the slider. A pointer to an array of 32 * 3 = 96 bytes is supplied. The illuminated areas on the touch slider are some @@ -156,7 +140,7 @@ void chuni_io_slider_stop(void); Minimum API version: 0x0100 */ -void chuni_io_slider_set_leds(const uint8_t *rgb); +void __declspec(dllexport) chuni_io_slider_set_leds(const uint8_t *rgb); /* Initialize LED emulation. This function will be called before any other chuni_io_led_*() function calls. @@ -167,7 +151,7 @@ void chuni_io_slider_set_leds(const uint8_t *rgb); Minimum API version: 0x0102 */ -HRESULT chuni_io_led_init(void); +HRESULT __declspec(dllexport) chuni_io_led_init(void); /* Update the RGB LEDs. rgb is a pointer to an array of up to 63 * 3 = 189 bytes. @@ -183,6 +167,6 @@ HRESULT chuni_io_led_init(void); Minimum API version: 0x0102 */ -void chuni_io_led_set_colors(uint8_t board, uint8_t *rgb); +void __declspec(dllexport) chuni_io_led_set_colors(uint8_t board, uint8_t *rgb); #endif //CHUNIIO_BROKENITHM_CHUNIIO_H diff --git a/src/config.c b/chuniio/src/config.c similarity index 100% rename from src/config.c rename to chuniio/src/config.c diff --git a/src/config.h b/chuniio/src/config.h similarity index 100% rename from src/config.h rename to chuniio/src/config.h diff --git a/chuniio/src/servers/android.c b/chuniio/src/servers/android.c new file mode 100644 index 0000000..78e9c91 --- /dev/null +++ b/chuniio/src/servers/android.c @@ -0,0 +1,466 @@ +// +// Created by beerpsi on 12/31/2023. +// + +#include "android.h" +#include "arch.h" + +#ifdef ENV32BIT +#include +#include +#include +#include +#include + +#include "servers/common.h" +#include "socket.h" + +#define BUFSIZ 512 + +bool tcp_mode = true; +uint16_t server_port = 52468; + +enum CardType { + CARD_AIME, + CARD_FELICA, +}; + +typedef struct { + SOCKET sock; + char remote_address[40]; + uint16_t remote_port; + + atomic_bool exit_flag; + atomic_bool connected; + + uint32_t last_input_packet_id; + uint8_t last_card_id[10]; + + bool has_previous_led_status; + uint8_t previous_led_status[3 * 32]; + uint8_t led_skip_count; + + struct IPCMemoryInfo *memory; +} android_thread_ctx; + +void socket_set_timeout(const SOCKET sHost, int timeout) { + setsockopt(sHost, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(int)); + setsockopt(sHost, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(int)); +} + +int socket_bind(const SOCKET sHost, const unsigned long addr, const uint16_t port) { + struct sockaddr_in srcaddr = {}; + memset(&srcaddr, 0, sizeof(srcaddr)); + srcaddr.sin_family = AF_INET; + srcaddr.sin_addr.s_addr = addr; + srcaddr.sin_port = htons(port); + return bind(sHost, (struct sockaddr *)&srcaddr, sizeof(srcaddr)); +} + +int socket_send_to(const SOCKET sHost, const struct sockaddr_in *addr, const char *buf, + const int len) { + return sendto(sHost, buf, len, 0, (struct sockaddr *)&addr, sizeof(&addr)); +} + +int make_ipv4_address(struct sockaddr_in *addr, const char *host, const uint16_t port) { + addr->sin_family = AF_INET; + addr->sin_port = htons(port); + return inet_pton(AF_INET, host, (struct in_addr *)&addr->sin_addr.s_addr); +} + +void get_socks_address(const struct PacketConnect *pkt, char *address, + const int address_len, uint16_t *port) { + if (!pkt || !address || !port) { + return; + } + + *port = ntohs(pkt->port); + + switch (pkt->addrType) { + case 1: + inet_ntop(AF_INET, pkt->addr.addr4.addr, address, address_len); + break; + case 2: + inet_ntop(AF_INET6, pkt->addr.addr6, address, address_len); + break; + default: + return; + } +} + +void print_card_info(const uint8_t card_type, const uint8_t *card_id) { + switch (card_type) { + case CARD_AIME: + print_err("[Android: INFO] Card type: AiMe, ID: "); + dump_bytes(card_id, 10, true); + break; + case CARD_FELICA: + print_err("[Android: INFO] Card type: FeliCa, ID: "); + dump_bytes(card_id, 8, true); + break; + default: + break; + } +} + +void update_packet_id(android_thread_ctx *ctx, const uint32_t new_packet_id) { + if (ctx->last_input_packet_id > new_packet_id) { + print_err("[WARN] Packet #%" PRIu32 " came too late\n", new_packet_id); + } else if (new_packet_id > ctx->last_input_packet_id + 1) { + print_err("[WARN] Packets between #%" PRIu32 " and #%" PRIu32 " total %" PRIu32 + " packet(s) are missing, probably too late or dropped\n", + ctx->last_input_packet_id, new_packet_id, + new_packet_id - ctx->last_input_packet_id - 1); + } else if (new_packet_id == ctx->last_input_packet_id) { + print_err("[WARN] Packet #%" PRIu32 " duplicated\n", new_packet_id); + } + ctx->last_input_packet_id = new_packet_id; +} + +unsigned int __stdcall led_broadcast_thread_proc(void *v) { + android_thread_ctx *ctx = v; + + const SOCKET sock = ctx->sock; + const struct IPCMemoryInfo* memory = ctx->memory; + + struct sockaddr_in addr = {}; + make_ipv4_address(&addr, ctx->remote_address, ctx->remote_port); + + char send_buffer[4 + 3 * 32]; + send_buffer[0] = 99; + send_buffer[1] = 'L'; + send_buffer[2] = 'E'; + send_buffer[3] = 'D'; + + while (!atomic_load(&ctx->exit_flag)) { + uint8_t current_led_status[3 * 32]; + + if (!atomic_load(&ctx->connected)) { + Sleep(50); + continue; + } + + memcpy(current_led_status, memory->ledRgbData, 3 * 32); + + bool same; + + if (!ctx->has_previous_led_status) { + same = memcmp(ctx->previous_led_status, current_led_status, 3 * 32) == 0; + } else { + same = false; + } + + memcpy(ctx->previous_led_status, current_led_status, 3 * 32); + ctx->has_previous_led_status = true; + + if (!same || ++ctx->led_skip_count > 50) { + memcpy(send_buffer + 4, current_led_status, 3 * 32); + + if (socket_send_to(sock, &addr, send_buffer, 100) < 0) { + print_err("[Android:ERROR] Cannot send packet: error %lu\n", WSAGetLastError()); + + if (tcp_mode) { + if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) { + continue; + } + + print_err("[Android: INFO] Device disconnected!\n"); + ctx->connected = false; + ctx->exit_flag = true; + break; + } + } + + ctx->led_skip_count = 0; + } + + Sleep(10); + } + + return 0; +} + +unsigned int __stdcall input_recv_thread_proc(void *v) { + android_thread_ctx *ctx = v; + + const SOCKET sock = ctx->sock; + struct IPCMemoryInfo *memory = ctx->memory; + + struct sockaddr_in addr = {}; + make_ipv4_address(&addr, ctx->remote_address, ctx->remote_port); + + int recv_len, packet_len; + uint8_t real_len; + + while (!atomic_load(&ctx->exit_flag)) { + char buffer[BUFSIZ]; + + if (!tcp_mode) { + /** + on UDP mode data is sent as packets, so just receive into a buffer big + enough for 1 packet each recvfrom call will only get 1 packet of data, the + remaining data is discarded + **/ + + if ((recv_len = recvfrom(sock, buffer, BUFSIZ - 1, 0, NULL, NULL)) == -1) { + continue; + } + + real_len = (unsigned char)buffer[0]; + + if (real_len > recv_len) { + continue; + } + + packet_len = real_len + 1; + } else { + /** + on TCP mode packets are length-prefixed, so we read in the first 4 bytes + to figure out how much we need to read, then read in the full data. + **/ + recv_len = 0; + + while (recv_len < 4) { + const int read = recv(sock, buffer + recv_len, 4 - recv_len, 0); + + if (read == -1) { + int error = WSAGetLastError(); + + if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN || + error == WSAETIMEDOUT) { + continue; + } + + print_err("[Android: INFO] Device disconnected (could not read data, errno " + "%d, os error %ld)\n", + errno, error); + atomic_store(&ctx->connected, false); + atomic_store(&ctx->exit_flag, true); + break; + } + + recv_len = recv_len + read; + } + + real_len = buffer[0]; + packet_len = real_len + 1; + + while (recv_len < packet_len) { + const int read = + recv(sock, buffer + recv_len, packet_len - recv_len, 0); + + if (read == -1) { + int error = WSAGetLastError(); + + if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN || + error == WSAETIMEDOUT) { + continue; + } + + print_err("[Android: INFO] Device disconnected (could not read data, errno " + "%d, os error %ld)\n", + errno, error); + atomic_store(&ctx->connected, false); + atomic_store(&ctx->exit_flag, true); + break; + } + + recv_len = recv_len + read; + } + } + + if (packet_len >= sizeof(struct PacketInput) && + memcmp(buffer + 1, "INP", 3) == 0) { + const struct PacketInput *pkt = (struct PacketInput *)buffer; + + memcpy(memory->airIoStatus, pkt->airIoStatus, sizeof(pkt->airIoStatus)); + memcpy(memory->sliderIoStatus, pkt->sliderIoStatus, + sizeof(pkt->sliderIoStatus)); + memory->testBtn = pkt->testBtn; + memory->serviceBtn = pkt->serviceBtn; + + update_packet_id(ctx, ntohl(pkt->packetId)); + } else if (packet_len >= sizeof(struct PacketInputNoAir) && + memcmp(buffer + 1, "IPT", 3) == 0) { // without air + const struct PacketInputNoAir *pkt = (struct PacketInputNoAir *)buffer; + + memcpy(memory->sliderIoStatus, pkt->sliderIoStatus, + sizeof(pkt->sliderIoStatus)); + memory->testBtn = pkt->testBtn; + memory->serviceBtn = pkt->serviceBtn; + + update_packet_id(ctx, ntohl(pkt->packetId)); + } else if (packet_len >= sizeof(struct PacketFunction) && + memcmp(buffer + 1, "FNC", 3) == 0) { + const struct PacketFunction *pkt = (struct PacketFunction *)buffer; + + switch (pkt->funcBtn) { + case FUNCTION_COIN: + memory->coinInsertion = 1; + break; + case FUNCTION_CARD: + memory->cardRead = 1; + break; + default: + break; + } + } else if (packet_len >= sizeof(struct PacketConnect) && + memcmp(buffer + 1, "CON", 3) == 0) { + const struct PacketConnect *pkt = (struct PacketConnect *)buffer; + + get_socks_address(pkt, ctx->remote_address, BUFSIZ - 1, &ctx->remote_port); + print_err("[Android: INFO] Device %s:%d connected.\n", ctx->remote_address, + ctx->remote_port); + + ctx->last_input_packet_id = 0; + atomic_store(&ctx->connected, true); + } else if (packet_len >= 4 && memcmp(buffer + 1, "DIS", 3) == 0) { + atomic_store(&ctx->connected, false); + + if (tcp_mode) { + atomic_store(&ctx->exit_flag, true); + print_err("[Android: INFO] Device disconnected (clean disconnect).\n"); + break; + } + + if (strlen(ctx->remote_address)) { + print_err("[Android: INFO] Device %s:%d disconnected.\n", ctx->remote_address, + ctx->remote_port); + memset(ctx->remote_address, 0, BUFSIZ); + } + + if (tcp_mode) { + break; + } + } else if (packet_len >= sizeof(struct PacketPing) && + memcmp(buffer + 1, "PIN", 3) == 0) { + if (!atomic_load(&ctx->connected)) { + continue; + } + + char response[13]; + memcpy(response, buffer, 12); + response[2] = 'O'; + + socket_send_to(sock, &addr, response, 13); + } else if (packet_len >= sizeof(struct PacketCard) && + memcmp(buffer + 1, "CRD", 3) == 0) { + const struct PacketCard *pkt = (struct PacketCard *)buffer; + + if (pkt->remoteCardRead) { + if (memcmp(ctx->last_card_id, pkt->remoteCardId, 10) != 0) { + print_err("[Android: INFO] Got remote card.\n"); + print_card_info(pkt->remoteCardType, pkt->remoteCardId); + memcpy(ctx->last_card_id, pkt->remoteCardId, 10); + } + } else if (memory->remoteCardRead) { + print_err("[Android: INFO] Remote card removed.\n"); + memset(ctx->last_card_id, 0, 10); + } + + memory->remoteCardRead = pkt->remoteCardRead; + memory->remoteCardType = pkt->remoteCardType; + memcpy(memory->remoteCardId, pkt->remoteCardId, 10); + } + } + + free(ctx); + + return 0; +} + +unsigned int __stdcall server_thread_proc(void *v) { + struct IPCMemoryInfo *memory = v; + + if (!tcp_mode) { + print_err("[Android: INFO] Mode: UDP\n"); + + const SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + socket_set_timeout(sock, 2000); + socket_bind(sock, htonl(INADDR_ANY), server_port); + + print_err("[Android: INFO] Waiting for device on port %d...\n", server_port); + + android_thread_ctx *ctx = malloc(sizeof(android_thread_ctx)); + ctx->sock = sock; + ctx->exit_flag = ATOMIC_VAR_INIT(false); + ctx->connected = ATOMIC_VAR_INIT(false); + ctx->last_input_packet_id = 0; + ctx->memory = memory; + ctx->has_previous_led_status = false; + ctx->led_skip_count = 0; + + HANDLE led_thread = + (HANDLE)_beginthreadex(NULL, 0, led_broadcast_thread_proc, ctx, 0, NULL); + HANDLE input_thread = + (HANDLE)_beginthreadex(NULL, 0, input_recv_thread_proc, ctx, 0, NULL); + + WaitForSingleObject(led_thread, INFINITE); + WaitForSingleObject(input_thread, INFINITE); + + CloseHandle(led_thread); + CloseHandle(input_thread); + + free(ctx); + } else { + print_err("[Android: INFO] Mode: TCP\n"); + + const SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + socket_set_timeout(sock, 50); + socket_bind(sock, htonl(INADDR_ANY), server_port); + + listen(sock, 10); + + print_err("[Android: INFO] Waiting for device on port %d...\n", server_port); + + struct sockaddr_in user_socket = {}; + socklen_t sock_size = sizeof(struct sockaddr_in); + SOCKET acc_socket; + + while ( + (acc_socket = accept(sock, (struct sockaddr *)&user_socket, &sock_size))) { + char buffer[20] = {}; + const char *user_address = + inet_ntop(AF_INET, &user_socket.sin_addr, buffer, 20); + + if (user_address != NULL) { + print_err("[Android: INFO] Device %s:%d connected.\n", user_address, + user_socket.sin_port); + } + + android_thread_ctx *ctx = malloc(sizeof(android_thread_ctx)); + ctx->sock = acc_socket; + ctx->exit_flag = ATOMIC_VAR_INIT(false); + ctx->connected = ATOMIC_VAR_INIT(true); + ctx->last_input_packet_id = 0; + ctx->memory = memory; + ctx->has_previous_led_status = false; + ctx->led_skip_count = 0; + + _beginthreadex(NULL, 0, led_broadcast_thread_proc, ctx, 0, NULL); + _beginthreadex(NULL, 0, input_recv_thread_proc, ctx, 0, NULL); + } + } +} +#endif // defined(ENV32BIT) + +HRESULT android_init_server(struct IPCMemoryInfo *memory) { +#ifdef ENV32BIT + tcp_mode = + GetPrivateProfileIntW(L"brokenithm", L"tcp", 1, L".\\segatools.ini") == 1; + server_port = + GetPrivateProfileIntW(L"brokenithm", L"port", 52468, L".\\segatools.ini"); + + struct WSAData wsaData = {}; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + print_err("[Android:ERROR] WSA startup failed, os error %ld\n", + WSAGetLastError()); + return E_FAIL; + } + + _beginthreadex(NULL, 0, server_thread_proc, memory, 0, NULL); +#endif // defined(ENV32BIT) + + return S_OK; +} diff --git a/chuniio/src/servers/android.h b/chuniio/src/servers/android.h new file mode 100644 index 0000000..8ddef6c --- /dev/null +++ b/chuniio/src/servers/android.h @@ -0,0 +1,15 @@ +// +// Created by beerpsi on 12/31/2023. +// + +#ifndef CHUNIIO_BROKENITHM_ANDROID_H +#define CHUNIIO_BROKENITHM_ANDROID_H + +#define WIN32_LEAN_AND_MEAN +#include + +#include "struct.h" + +HRESULT android_init_server(struct IPCMemoryInfo* memory); + +#endif // CHUNIIO_BROKENITHM_ANDROID_H diff --git a/chuniio/src/servers/common.c b/chuniio/src/servers/common.c new file mode 100644 index 0000000..2334671 --- /dev/null +++ b/chuniio/src/servers/common.c @@ -0,0 +1,72 @@ +// +// Created by beerpsi on 12/31/2023. +// + +#include "common.h" + +#include + +#include "util/dprintf.h" + +void print_err(const char* fmt, ...) { + const time_t lt = time(NULL); + const struct tm *local = localtime(<); + char tmpbuf[32]; + + strftime(tmpbuf, 32, "%Y-%m-%d %H:%M:%S", local); + + dprintf("brokenithm_server: [%s] ", tmpbuf); + + va_list ap; + va_start(ap, fmt); + dprintfv(fmt, ap); + va_end(ap); +} + +void dump_bytes(const void *ptr, const size_t nbytes, const bool hex_string) { + size_t i; + size_t j; + + if (nbytes == 0) { + dprintf("\t--- Empty ---\n"); + } + + const uint8_t* bytes = ptr; + + if (hex_string) { + for (i = 0 ; i < nbytes ; i++) { + dprintf("%02x", bytes[i]); + } + dprintf("\n"); + return; + } + + for (i = 0 ; i < nbytes ; i += 16) { + dprintf(" %08x:", (int) i); + + for (j = 0 ; i + j < nbytes && j < 16 ; j++) { + dprintf(" %02x", bytes[i + j]); + } + + while (j < 16) { + dprintf(" "); + j++; + } + + dprintf(" "); + + for (j = 0 ; i + j < nbytes && j < 16 ; j++) { + uint8_t c = bytes[i + j]; + + if (c < 0x20 || c >= 0x7F) { + c = '.'; + } + + dprintf("%c", c); + } + + dprintf("\n"); + } + + dprintf("\n"); +} \ No newline at end of file diff --git a/chuniio/src/servers/common.h b/chuniio/src/servers/common.h new file mode 100644 index 0000000..66e7e65 --- /dev/null +++ b/chuniio/src/servers/common.h @@ -0,0 +1,21 @@ +// +// Created by beerpsi on 12/31/2023. +// + +#ifndef CHUNIIO_BROKENITHM_COMMON_H +#define CHUNIIO_BROKENITHM_COMMON_H + +#include +#include + +enum FunctionButton { + INVALID, + FUNCTION_COIN, + FUNCTION_CARD, +}; + +void print_err(const char* fmt, ...); + +void dump_bytes(const void *ptr, const size_t nbytes, const bool hex_string); + +#endif // CHUNIIO_BROKENITHM_COMMON_H diff --git a/chuniio/src/servers/ios.c b/chuniio/src/servers/ios.c new file mode 100644 index 0000000..20f4283 --- /dev/null +++ b/chuniio/src/servers/ios.c @@ -0,0 +1,239 @@ +// +// Created by beerpsi on 12/31/2023. +// + +#include "ios.h" +#include "arch.h" + +#ifdef ENV32BIT +#include +#include +#include + +#include "servers/common.h" + +typedef struct { + char remote_udid[41]; + idevice_t device; + idevice_connection_t connection; + + atomic_bool exit_flag; + + bool has_previous_led_status; + uint8_t previous_led_status[3 * 32]; + uint8_t led_skip_count; + + struct IPCMemoryInfo* memory; +} ios_thread_ctx; + +unsigned int __stdcall ios_led_broadcast_thread_proc(void *v) { + ios_thread_ctx* ctx = v; + + char send_buffer[4 + 3 * 32]; + send_buffer[0] = 99; + send_buffer[1] = 'L'; + send_buffer[2] = 'E'; + send_buffer[3] = 'D'; + + while (!atomic_load(&ctx->exit_flag)) { + uint8_t current_led_status[3 * 32]; + + memcpy(current_led_status, ctx->memory->ledRgbData, 3 * 32); + + bool same; + + if (!ctx->has_previous_led_status) { + same = memcmp(ctx->previous_led_status, current_led_status, 3 * 32) == 0; + } else { + same = false; + } + + memcpy(ctx->previous_led_status, current_led_status, 3 * 32); + ctx->has_previous_led_status = true; + + if (!same || ++ctx->led_skip_count > 50) { + memcpy(send_buffer + 4, current_led_status, 3 * 32); + + int status; + uint32_t sent; + if ((status = idevice_connection_send(ctx->connection, send_buffer, 100, &sent))) { + print_err("[iOS:ERROR] Cannot send LED packet: error %d\n", status); + } + + ctx->led_skip_count = 0; + } + + Sleep(10); + } + + return 0; +} + +unsigned int __stdcall ios_input_recv_thread_proc(void *v) { + ios_thread_ctx* ctx = v; + + bool air_enabled = true; + + while (!atomic_load(&ctx->exit_flag)) { + char buffer[BUFSIZ]; + int status; + uint32_t read; + + if ((status = idevice_connection_receive_timeout(ctx->connection, buffer, 4, &read, 5))) { + if (status == IDEVICE_E_TIMEOUT) { + continue; + } + + print_err("[iOS:ERROR] Could not read data from device: %d\n", status); + atomic_store(&ctx->exit_flag, true); + + break; + } + + int len = (unsigned char)buffer[0]; + + if ((status = idevice_connection_receive_timeout(ctx->connection, buffer + 4, len - 3, &read, 5))) { + print_err("[iOS:ERROR] Could not read data from device: %d\n", status); + atomic_store(&ctx->exit_flag, true); + + break; + } + + if (len >= sizeof(struct iOSPacketInput) && memcmp(buffer + 1, "INP", 3) == 0) { + struct iOSPacketInput* pkt = (struct iOSPacketInput*)buffer; + + if (air_enabled) { + memcpy(ctx->memory->airIoStatus, pkt->airIoStatus, sizeof(pkt->airIoStatus)); + } + + memcpy(ctx->memory->sliderIoStatus, pkt->sliderIoStatus, sizeof(pkt->sliderIoStatus)); + ctx->memory->testBtn = pkt->testBtn; + ctx->memory->serviceBtn = pkt->serviceBtn; + } else if (len >= 4 && memcmp(buffer + 1, "AIR", 3) == 0) { + air_enabled = buffer[3] != 0; + + print_err("[iOS: INFO] Air input %s", air_enabled ? "enabled" : "disabled"); + } else if (len >= sizeof(struct PacketFunction) && memcmp(buffer + 1, "FNC", 3) == 0) { + const struct PacketFunction *pkt = (struct PacketFunction *) buffer; + + switch (pkt->funcBtn) { + case FUNCTION_COIN: + ctx->memory->coinInsertion = 1; + break; + case FUNCTION_CARD: + ctx->memory->cardRead = 1; + break; + default: + break; + } + } + } + + print_err("[iOS: INFO] Device disconnected."); + + idevice_disconnect(ctx->connection); + ctx->connection = NULL; + + idevice_free(ctx->device); + free(ctx); + + return 0; +} + +unsigned int __stdcall connect_device(void* v) { + ios_thread_ctx* ctx = v; + + int status; + if ((status = idevice_new(&ctx->device, ctx->remote_udid))) { + print_err("[iOS:ERROR] Create device failed: %d\n", status); + idevice_free(ctx->device); + return 1; + } + + if ((status = idevice_connect(ctx->device, 24864, &ctx->connection))) { + print_err("[iOS:ERROR] Connection failed: %d, retrying in 5 seconds\n", status); + + ctx->connection = NULL; + idevice_free(ctx->device); + + Sleep(5000); + + _beginthreadex(NULL, 0, connect_device, ctx, 0, NULL); + + return 1; + } + + char buf[5]; + uint32_t read; + + if ((status = idevice_connection_receive(ctx->connection, buf, 4, &read))) { + print_err("[iOS:ERROR] Receiving data failed: %d\n", status); + + idevice_disconnect(ctx->connection); + ctx->connection = NULL; + + idevice_free(ctx->device); + + return 1; + } + + if (memcmp(buf, "\x03WEL", 4) != 0) { + print_err("[iOS:ERROR] Client sent invalid data\n"); + + idevice_disconnect(ctx->connection); + ctx->connection = NULL; + + idevice_free(ctx->device); + + return 1; + } + + print_err("[iOS: INFO] Connected to device\n"); + atomic_store(&ctx->exit_flag, false); + _beginthreadex(NULL, 0, ios_input_recv_thread_proc, ctx, 0, NULL); + _beginthreadex(NULL, 0, ios_led_broadcast_thread_proc, ctx, 0, NULL); + + return 0; +} + +void device_event_callback(const idevice_event_t* event, void* user_data) { + struct IPCMemoryInfo* memory = user_data; + + switch (event->event) { + case IDEVICE_DEVICE_ADD: + print_err("[iOS: INFO] iDevice added, udid: %s\n", event->udid); + + ios_thread_ctx* args = malloc(sizeof(ios_thread_ctx)); + memcpy(args->remote_udid, event->udid, strlen(event->udid)); + args->device = NULL; + args->connection = NULL; + args->exit_flag = ATOMIC_VAR_INIT(false); + args->has_previous_led_status = false; + args->led_skip_count = 0; + args->memory = memory; + + _beginthreadex(NULL, 0, connect_device, args, 0, NULL); + break; + case IDEVICE_DEVICE_REMOVE: + print_err("[iOS: INFO] iDevice removed, udid: %s\n", event->udid); + break; + case IDEVICE_DEVICE_PAIRED: + print_err("[iOS: INFO] iDevice paired, udid: %s\n", event->udid); + break; + } +} +#endif // defined(ENV32BIT) + +HRESULT ios_init_server(struct IPCMemoryInfo *memory) { +#ifdef ENV32BIT + int status; + if ((status = idevice_event_subscribe(device_event_callback, memory))) { + print_err("[iOS:ERROR] Subscribing for iDevice events failed: %d\n", status); + return E_FAIL; + } else { + print_err("[iOS: INFO] Waiting for iDevices...\n"); + } +#endif // defined(ENV32BIT) + + return S_OK; +} diff --git a/chuniio/src/servers/ios.h b/chuniio/src/servers/ios.h new file mode 100644 index 0000000..5db3098 --- /dev/null +++ b/chuniio/src/servers/ios.h @@ -0,0 +1,15 @@ +// +// Created by beerpsi on 12/31/2023. +// + +#ifndef CHUNIIO_BROKENITHM_IOS_H +#define CHUNIIO_BROKENITHM_IOS_H + +#define WIN32_LEAN_AND_MEAN +#include + +#include "struct.h" + +HRESULT ios_init_server(struct IPCMemoryInfo* memory); + +#endif // CHUNIIO_BROKENITHM_IOS_H diff --git a/src/socket.h b/chuniio/src/socket.h similarity index 95% rename from src/socket.h rename to chuniio/src/socket.h index eb18da1..759a594 100644 --- a/src/socket.h +++ b/chuniio/src/socket.h @@ -1,44 +1,44 @@ -// -// Created by beerpsi on 12/29/2023. -// - -#ifndef CHUNIIO_BROKENITHM_SOCKET_H -#define CHUNIIO_BROKENITHM_SOCKET_H - -#ifdef _WIN32 -#ifndef WINVER -#define WINVER 0x0501 -#endif // WINVER -#include -#include -#else -//translate windows functions to linux functions -#include -#include -#define SOCKET int -#define INVALID_SOCKET (SOCKET)(~0) -#define SOCKET_ERROR (-1) -#define closesocket close -#define SOCKADDR_IN sockaddr_in -#define ZeroMemory(d,l) memset((d), 0, (l)) -#define ioctlsocket ioctl -#ifndef SA_INTERRUPT -#define SA_INTERRUPT 0 //ignore this setting -#endif -#define SD_BOTH SHUT_RDWR -#ifndef __hpux -#include -#endif /* __hpux */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -typedef sockaddr *LPSOCKADDR; -#endif // _WIN32 - -#endif //CHUNIIO_BROKENITHM_SOCKET_H +// +// Created by beerpsi on 12/29/2023. +// + +#ifndef CHUNIIO_BROKENITHM_SOCKET_H +#define CHUNIIO_BROKENITHM_SOCKET_H + +#ifdef _WIN32 +#ifndef WINVER +#define WINVER 0x0501 +#endif // WINVER +#include +#include +#else +//translate windows functions to linux functions +#include +#include +#define SOCKET int +#define INVALID_SOCKET (SOCKET)(~0) +#define SOCKET_ERROR (-1) +#define closesocket close +#define SOCKADDR_IN sockaddr_in +#define ZeroMemory(d,l) memset((d), 0, (l)) +#define ioctlsocket ioctl +#ifndef SA_INTERRUPT +#define SA_INTERRUPT 0 //ignore this setting +#endif +#define SD_BOTH SHUT_RDWR +#ifndef __hpux +#include +#endif /* __hpux */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +typedef sockaddr *LPSOCKADDR; +#endif // _WIN32 + +#endif //CHUNIIO_BROKENITHM_SOCKET_H diff --git a/src/struct.h b/chuniio/src/struct.h similarity index 89% rename from src/struct.h rename to chuniio/src/struct.h index e8f5cda..56ae49d 100644 --- a/src/struct.h +++ b/chuniio/src/struct.h @@ -1,91 +1,100 @@ -// -// Created by beerpsi on 12/29/2023. -// - -#ifndef CHUNIIO_BROKENITHM_STRUCT_H -#define CHUNIIO_BROKENITHM_STRUCT_H - -#include - -; -#pragma pack(push) -#pragma pack(1) - -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; - uint8_t remoteCardRead; - uint8_t remoteCardType; - uint8_t remoteCardId[10]; -}; - -struct PacketInput -{ - uint8_t packetSize; - uint8_t packetName[3]; - uint32_t packetId; - uint8_t airIoStatus[6]; - uint8_t sliderIoStatus[32]; - uint8_t testBtn; - uint8_t serviceBtn; -}; - -struct PacketInputNoAir -{ - uint8_t packetSize; - uint8_t packetName[3]; - uint32_t packetId; - uint8_t sliderIoStatus[32]; - uint8_t testBtn; - uint8_t serviceBtn; -}; - -struct PacketFunction -{ - uint8_t packetSize; - uint8_t packetName[3]; - uint8_t funcBtn; -}; - -struct PacketConnect -{ - uint8_t packetSize; - uint8_t packetName[3]; - uint8_t addrType; - uint16_t port; - union - { - struct - { - uint8_t addr[4]; - uint8_t padding[12]; - } addr4; - uint8_t addr6[16]; - } addr; -}; - -struct PacketCard -{ - uint8_t packetSize; - uint8_t packetName[3]; - uint8_t remoteCardRead; - uint8_t remoteCardType; - uint8_t remoteCardId[10]; -}; - -struct PacketPing -{ - uint8_t packetSize; - uint8_t packetName[3]; - uint64_t remotePingTime; -}; - -#pragma pack(pop) - -#endif //CHUNIIO_BROKENITHM_STRUCT_H +// +// Created by beerpsi on 12/29/2023. +// + +#ifndef CHUNIIO_BROKENITHM_STRUCT_H +#define CHUNIIO_BROKENITHM_STRUCT_H + +#include + +; +#pragma pack(push) +#pragma pack(1) + +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; + uint8_t remoteCardRead; + uint8_t remoteCardType; + uint8_t remoteCardId[10]; +}; + +struct iOSPacketInput { + uint8_t packetSize; + uint8_t packetName[3]; + uint8_t airIoStatus[6]; + uint8_t sliderIoStatus[32]; + uint8_t testBtn; + uint8_t serviceBtn; +}; + +struct PacketInput +{ + uint8_t packetSize; + uint8_t packetName[3]; + uint32_t packetId; + uint8_t airIoStatus[6]; + uint8_t sliderIoStatus[32]; + uint8_t testBtn; + uint8_t serviceBtn; +}; + +struct PacketInputNoAir +{ + uint8_t packetSize; + uint8_t packetName[3]; + uint32_t packetId; + uint8_t sliderIoStatus[32]; + uint8_t testBtn; + uint8_t serviceBtn; +}; + +struct PacketFunction +{ + uint8_t packetSize; + uint8_t packetName[3]; + uint8_t funcBtn; +}; + +struct PacketConnect +{ + uint8_t packetSize; + uint8_t packetName[3]; + uint8_t addrType; + uint16_t port; + union + { + struct + { + uint8_t addr[4]; + uint8_t padding[12]; + } addr4; + uint8_t addr6[16]; + } addr; +}; + +struct PacketCard +{ + uint8_t packetSize; + uint8_t packetName[3]; + uint8_t remoteCardRead; + uint8_t remoteCardType; + uint8_t remoteCardId[10]; +}; + +struct PacketPing +{ + uint8_t packetSize; + uint8_t packetName[3]; + uint64_t remotePingTime; +}; + +#pragma pack(pop) + +#endif //CHUNIIO_BROKENITHM_STRUCT_H diff --git a/src/chuniio.c b/src/chuniio.c deleted file mode 100644 index a005632..0000000 --- a/src/chuniio.c +++ /dev/null @@ -1,733 +0,0 @@ -// -// Created by beerpsi on 12/30/2023. -// - -#include "chuniio.h" - -#include -#include - -#include "config.h" -#include "struct.h" -#include "util/dprintf.h" - -#ifdef ENV32BIT -#include -#include -#include - -#include "socket.h" -#endif - -// region Brokenithm -struct IPCMemoryInfo *chuni_io_file_mapping; - -const char *memFileName = "Local\\BROKENITHM_SHARED_BUFFER"; - -#ifdef ENV32BIT -uint16_t server_port = 52468; -bool tcp_mode = false; - -enum { - CARD_AIME, - CARD_FELICA, -}; - -enum { FUNCTION_COIN = 1, FUNCTION_CARD }; - -typedef struct { - SOCKET sock; - char remote_address[BUFSIZ]; - uint16_t remote_port; - atomic_bool exit_flag; - atomic_bool connected; - uint32_t last_input_packet_id; - struct IPCMemoryInfo *memory; -} thread_ctx; - -void socket_set_timeout(SOCKET sHost, int timeout) { - setsockopt(sHost, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(int)); - setsockopt(sHost, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(int)); -} - -int socket_bind(SOCKET sHost, unsigned long addr, uint16_t port) { - struct sockaddr_in srcaddr = {}; - memset(&srcaddr, 0, sizeof(srcaddr)); - srcaddr.sin_family = AF_INET; - srcaddr.sin_addr.s_addr = addr; - srcaddr.sin_port = htons(port); - return bind(sHost, (struct sockaddr *)&srcaddr, sizeof(srcaddr)); -} - -int socket_send_to(SOCKET sHost, const struct sockaddr_in *addr, const char *buf, - int len) { - return sendto(sHost, buf, len, 0, (struct sockaddr *)&addr, sizeof(&addr)); -} - -void print_err(const char *fmt, ...) { - time_t lt = time(NULL); - struct tm *local = localtime(<); - char tmpbuf[32]; - - strftime(tmpbuf, 32, "%Y-%m-%d %H:%M:%S", local); - - dprintf("brokenithm_server: [%s] ", tmpbuf); - - va_list ap; - va_start(ap, fmt); - dprintfv(fmt, ap); - va_end(ap); -} - -void get_socks_address(const struct PacketConnect *pkt, char *address, int address_len, - uint16_t *port) { - if (!pkt || !address || !port) { - return; - } - - *port = ntohs(pkt->port); - - switch (pkt->addrType) { - case 1: - inet_ntop(AF_INET, pkt->addr.addr4.addr, address, address_len); - break; - case 2: - inet_ntop(AF_INET6, pkt->addr.addr6, address, address_len); - break; - default: - return; - } -} - -void update_packet_id(thread_ctx *ctx, uint32_t new_packet_id) { - if (ctx->last_input_packet_id > new_packet_id) { - print_err("[WARN] Packet #%" PRIu32 " came too late\n", new_packet_id); - } else if (new_packet_id > ctx->last_input_packet_id + 1) { - print_err("[WARN] Packets between #%" PRIu32 " and #%" PRIu32 " total %" PRIu32 - " packet(s) are missing, probably too late or dropped\n", - ctx->last_input_packet_id, new_packet_id, - new_packet_id - ctx->last_input_packet_id - 1); - } else if (new_packet_id == ctx->last_input_packet_id) { - print_err("[WARN] Packet #%" PRIu32 " duplicated\n", new_packet_id); - } - ctx->last_input_packet_id = new_packet_id; -} - -void dump_bytes(const void *ptr, size_t nbytes, bool hex_string) { - const uint8_t *bytes; - uint8_t c; - size_t i; - size_t j; - - if (nbytes == 0) { - dprintf("\t--- Empty ---\n"); - } - - bytes = (const unsigned char *)ptr; - - if (hex_string) { - for (i = 0; i < nbytes; i++) { - dprintf("%02x", bytes[i]); - } - dprintf("\n"); - return; - } - - for (i = 0; i < nbytes; i += 16) { - dprintf(" %08x:", (int)i); - - for (j = 0; i + j < nbytes && j < 16; j++) { - dprintf(" %02x", bytes[i + j]); - } - - while (j < 16) { - dprintf(" "); - j++; - } - - dprintf(" "); - - for (j = 0; i + j < nbytes && j < 16; j++) { - c = bytes[i + j]; - - if (c < 0x20 || c >= 0x7F) { - c = '.'; - } - - dprintf("%c", c); - } - - dprintf("\n"); - } - - dprintf("\n"); -} - -void print_card_info(uint8_t card_type, uint8_t *card_id) { - switch (card_type) { - case CARD_AIME: - print_err("[INFO] Card type: AiMe, ID: "); - dump_bytes(card_id, 10, true); - break; - case CARD_FELICA: - print_err("[INFO] Card type: FeliCa, ID: "); - dump_bytes(card_id, 8, true); - break; - default: - break; - } -} - -int make_ipv4_address(struct sockaddr_in *addr, char *host, uint16_t port) { - addr->sin_family = AF_INET; - addr->sin_port = htons(port); - return inet_pton(AF_INET, host, (struct in_addr *)&addr->sin_addr.s_addr); -} - -uint8_t previous_led_status[3 * 32]; -bool has_previous_led_status = false; -int skip_count = 0; - -unsigned int __stdcall thread_led_broadcast(void *v) { - thread_ctx *ctx = (thread_ctx *)v; - - SOCKET sHost = ctx->sock; - struct IPCMemoryInfo *memory = ctx->memory; - - struct sockaddr_in addr = {}; - make_ipv4_address(&addr, ctx->remote_address, ctx->remote_port); - - char send_buffer[4 + 3 * 32]; - send_buffer[0] = 99; - send_buffer[1] = 'L'; - send_buffer[2] = 'E'; - send_buffer[3] = 'D'; - - uint8_t current_led_status[3 * 32]; - - while (!atomic_load(&ctx->exit_flag)) { - if (!atomic_load(&ctx->connected)) { - Sleep(50); - continue; - } - - memcpy(current_led_status, memory->ledRgbData, 3 * 32); - - bool same; - - if (!has_previous_led_status) { - same = (memcmp(previous_led_status, current_led_status, 3 * 32) == 0); - } else { - same = false; - } - - memcpy(previous_led_status, current_led_status, 3 * 32); - has_previous_led_status = true; - - if (!same || ++skip_count > 50) { - memcpy(send_buffer + 4, current_led_status, 3 * 32); - - if (socket_send_to(sHost, &addr, send_buffer, 100) < 0) { - print_err("[ERROR] Cannot send packet: error %lu\n", GetLastError()); - - if (tcp_mode) { - int error = WSAGetLastError(); - - if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN || - error == WSAETIMEDOUT) { - continue; - } - - print_err("[INFO] Device disconnected (could not send packet), " - "error %d, os error %ld\n", - errno, error); - - atomic_store(&ctx->connected, false); - atomic_store(&ctx->exit_flag, true); - - break; - } - } - - skip_count = 0; - } - - Sleep(10); - } - - return 0; -} - -uint8_t last_card_id[10]; - -unsigned int __stdcall thread_input_recv(void *v) { - thread_ctx *ctx = (thread_ctx *)v; - - SOCKET sHost = ctx->sock; - struct IPCMemoryInfo *memory = ctx->memory; - - char buffer[BUFSIZ]; - - struct sockaddr_in addr = {}; - make_ipv4_address(&addr, ctx->remote_address, ctx->remote_port); - - int recv_len, packet_len; - uint8_t real_len; - - while (!atomic_load(&ctx->exit_flag)) { - if (!tcp_mode) { - /** - on UDP mode data is sent as packets, so just receive into a buffer big - enough for 1 packet each recvfrom call will only get 1 packet of data, the - remaining data is discarded - **/ - - if ((recv_len = recvfrom(sHost, buffer, BUFSIZ - 1, 0, NULL, NULL)) == -1) { - continue; - } - - real_len = (unsigned char)buffer[0]; - - if (real_len > recv_len) { - continue; - } - - packet_len = real_len + 1; - } else { - /** - on TCP mode packets are length-prefixed, so we read in the first 4 bytes - to figure out how much we need to read, then read in the full data. - **/ - recv_len = 0; - - while (recv_len < 4) { - int read = recv(sHost, buffer + recv_len, 4 - recv_len, 0); - - if (read == -1) { - int error = WSAGetLastError(); - - if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN || - error == WSAETIMEDOUT) { - continue; - } - - print_err("[INFO] Device disconnected (could not receive packet), " - "errno %d, os error %ld\n", - errno, error); - atomic_store(&ctx->connected, false); - atomic_store(&ctx->exit_flag, true); - - break; - } - - recv_len = recv_len + read; - } - - real_len = buffer[0]; - packet_len = real_len + 1; // 1 for the packetSize - - while (recv_len < packet_len) { - int read = recv(sHost, buffer + recv_len, packet_len - recv_len, 0); - - if (read == -1) { - int error = WSAGetLastError(); - - if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN || - error == WSAETIMEDOUT) { - continue; - } - - print_err("[INFO] Device disconnected (could not receive packet), " - "errno %d, os error %ld\n", - errno, error); - atomic_store(&ctx->connected, false); - atomic_store(&ctx->exit_flag, true); - - break; - } - - recv_len = recv_len + read; - } - } - - if (packet_len >= sizeof(struct PacketInput) && buffer[1] == 'I' && - buffer[2] == 'N' && buffer[3] == 'P') { - struct PacketInput *pkt = (struct PacketInput *)buffer; - - memcpy(memory->airIoStatus, pkt->airIoStatus, sizeof(pkt->airIoStatus)); - memcpy(memory->sliderIoStatus, pkt->sliderIoStatus, - sizeof(pkt->sliderIoStatus)); - memory->testBtn = pkt->testBtn; - memory->serviceBtn = pkt->serviceBtn; - - update_packet_id(ctx, ntohl(pkt->packetId)); - } else if (packet_len >= sizeof(struct PacketInputNoAir) && buffer[1] == 'I' && - buffer[2] == 'P' && buffer[3] == 'T') { // without air - struct PacketInputNoAir *pkt = (struct PacketInputNoAir *)buffer; - - memcpy(memory->sliderIoStatus, pkt->sliderIoStatus, - sizeof(pkt->sliderIoStatus)); - memory->testBtn = pkt->testBtn; - memory->serviceBtn = pkt->serviceBtn; - - update_packet_id(ctx, ntohl(pkt->packetId)); - } else if (packet_len >= sizeof(struct PacketFunction) && buffer[1] == 'F' && - buffer[2] == 'N' && buffer[3] == 'C') { - struct PacketFunction *pkt = (struct PacketFunction *)buffer; - - switch (pkt->funcBtn) { - case FUNCTION_COIN: - memory->coinInsertion = 1; - break; - case FUNCTION_CARD: - memory->cardRead = 1; - break; - } - } else if (packet_len >= sizeof(struct PacketConnect) && buffer[1] == 'C' && - buffer[2] == 'O' && buffer[3] == 'N') { - struct PacketConnect *pkt = (struct PacketConnect *)buffer; - - get_socks_address(pkt, ctx->remote_address, BUFSIZ - 1, &ctx->remote_port); - print_err("[INFO] Device %s:%d connected.\n", ctx->remote_address, - ctx->remote_port); - - ctx->last_input_packet_id = 0; - atomic_store(&ctx->connected, true); - } else if (packet_len >= 4 && buffer[1] == 'D' && buffer[2] == 'I' && - buffer[3] == 'S') { - atomic_store(&ctx->connected, false); - - if (tcp_mode) { - atomic_store(&ctx->exit_flag, true); - print_err("[INFO] Device disconnected (clean disconnect)\n"); - break; - } - - if (strlen(ctx->remote_address)) { - print_err("[INFO] Device %s:%d disconnected.\n", ctx->remote_address, - ctx->remote_port); - memset(ctx->remote_address, 0, BUFSIZ); - } - } else if (packet_len >= sizeof(struct PacketPing) && buffer[1] == 'P' && - buffer[2] == 'I' && buffer[3] == 'N') { - if (!atomic_load(&ctx->connected)) { - continue; - } - - char response[13]; - memcpy(response, buffer, 12); - response[2] = 'O'; - - socket_send_to(sHost, &addr, response, 13); - } else if (packet_len >= sizeof(struct PacketCard) && buffer[1] == 'C' && - buffer[2] == 'R' && buffer[3] == 'D') { - struct PacketCard *pkt = (struct PacketCard *)buffer; - - if (pkt->remoteCardRead) { - if (memcmp(last_card_id, pkt->remoteCardId, 10) != 0) { - print_err("[INFO] Got remote card.\n"); - print_card_info(pkt->remoteCardType, pkt->remoteCardId); - memcpy(last_card_id, pkt->remoteCardId, 10); - } - } else if (memory->remoteCardRead) { - print_err("[INFO] Remote card removed.\n"); - memset(last_card_id, 0, 10); - } - - memory->remoteCardRead = pkt->remoteCardRead; - memory->remoteCardType = pkt->remoteCardType; - memcpy(memory->remoteCardId, pkt->remoteCardId, 10); - } - } - - return 0; -} - -unsigned int __stdcall server_thread_proc(void *ctx) { - struct IPCMemoryInfo *memory = (struct IPCMemoryInfo *)ctx; - - if (!tcp_mode) { - print_err("[INFO] Mode: UDP\n"); - - SOCKET sHost = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - socket_set_timeout(sHost, 2000); - socket_bind(sHost, htonl(INADDR_ANY), server_port); - - print_err("[INFO] Waiting for device on port %d...\n", server_port); - - thread_ctx args = {.sock = sHost, - .exit_flag = ATOMIC_VAR_INIT(false), - .connected = ATOMIC_VAR_INIT(false), - .last_input_packet_id = 0, - .memory = memory}; - - HANDLE led_thread = - (HANDLE)_beginthreadex(NULL, 0, thread_led_broadcast, &args, 0, NULL); - HANDLE input_thread = - (HANDLE)_beginthreadex(NULL, 0, thread_input_recv, &args, 0, NULL); - - WaitForSingleObject(led_thread, INFINITE); - WaitForSingleObject(input_thread, INFINITE); - - CloseHandle(led_thread); - CloseHandle(input_thread); - } else { - print_err("[INFO] Mode: TCP\n"); - SOCKET sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - - socket_set_timeout(sHost, 50); - socket_bind(sHost, htonl(INADDR_ANY), server_port); - - listen(sHost, 10); - - print_err("[INFO] Waiting for device on port %d...\n", server_port); - -#pragma clang diagnostic push -#pragma ide diagnostic ignored "EndlessLoop" - for (;;) { - struct sockaddr_in user_socket = {}; - socklen_t sock_size = sizeof(struct sockaddr_in); - SOCKET acc_socket = - accept(sHost, (struct sockaddr *)&user_socket, &sock_size); - - char buffer[20] = {}; - const char *user_address = - inet_ntop(AF_INET, &user_socket.sin_addr, buffer, 20); - if (user_address != NULL) { - print_err("[INFO] Device %s:%d connected.\n", user_address, - user_socket.sin_port); - } - - thread_ctx *args = malloc(sizeof(thread_ctx)); - args->sock = acc_socket; - args->exit_flag = ATOMIC_VAR_INIT(false); - args->connected = ATOMIC_VAR_INIT(true); - args->last_input_packet_id = 0; - args->memory = memory; - - _beginthreadex(NULL, 0, thread_led_broadcast, args, 0, NULL); - _beginthreadex(NULL, 0, thread_input_recv, args, 0, NULL); - } -#pragma clang diagnostic pop - } - - return 0; -} - -HRESULT server_start() { - tcp_mode = - GetPrivateProfileIntW(L"brokenithm", L"tcp", 0, L".\\segatools.ini") == 1; - server_port = - GetPrivateProfileIntW(L"brokenithm", L"port", 52468, L".\\segatools.ini"); - - struct WSAData wsaData = {}; - if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { - print_err("[ERROR] WSA startup failed!\n"); - return E_FAIL; - } - - HANDLE hMapFile = OpenFileMappingA(FILE_MAP_ALL_ACCESS, false, memFileName); - - if (hMapFile == NULL) { - hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, - 1024, memFileName); - if (hMapFile == NULL) { - print_err("[ERROR] CreateFileMapping failed! error: %lu\n", GetLastError()); - return E_FAIL; - } - } - - struct IPCMemoryInfo *memory = (struct IPCMemoryInfo *)MapViewOfFileEx( - hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 1024, NULL); - chuni_io_file_mapping = memory; - - if (memory == NULL) { - print_err("[ERROR] Cannot get view of memory map! error: %lu\n", - GetLastError()); - return E_FAIL; - } - - _beginthreadex(NULL, 0, server_thread_proc, memory, 0, NULL); - - return S_OK; -} -#endif -// endregion - -// region ChuniIO stuff -#ifdef ENV64BIT -static HANDLE chuni_io_file_mapping_handle; - -void chuni_io_init_shared_memory() { - if (chuni_io_file_mapping) { - dprintf("chuni_io_init_shared_memory: shared memory already exists\n"); - return; - } - - if ((chuni_io_file_mapping_handle = - CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, - sizeof(struct IPCMemoryInfo), memFileName)) == 0) { - dprintf("chuni_io_init_shared_memory: could not create file mapping: %ld\n", - GetLastError()); - return; - } - - if ((chuni_io_file_mapping = (struct IPCMemoryInfo *)MapViewOfFile( - chuni_io_file_mapping_handle, FILE_MAP_ALL_ACCESS, 0, 0, - sizeof(struct IPCMemoryInfo))) == 0) { - dprintf("chuni_io_init_shared_memory: could not get view of file: %ld\n", - GetLastError()); - return; - } - - memset(chuni_io_file_mapping, 0, sizeof(struct IPCMemoryInfo)); - SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_CONTINUOUS); -} -#endif - -static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx); - -static bool chuni_io_coin; -static uint16_t chuni_io_coins; -static uint8_t chuni_io_hand_pos; -static HANDLE chuni_io_slider_thread; -static bool chuni_io_slider_stop_flag; -static struct chuni_io_config chuni_io_cfg; - -uint16_t chuni_io_get_api_version() { return 0x0102; } - -HRESULT chuni_io_jvs_init() { - chuni_io_config_load(&chuni_io_cfg, L".\\segatools.ini"); - -#ifdef ENV32BIT - HRESULT result = server_start(); - if (result != S_OK) { - return result; - } -#endif -#ifdef ENV64BIT - chuni_io_init_shared_memory(); -#endif - - return S_OK; -} - -void chuni_io_jvs_read_coin_counter(uint16_t *out) { - if (out == NULL) { - return; - } - - if (chuni_io_file_mapping && chuni_io_file_mapping->coinInsertion) { - chuni_io_coins++; - chuni_io_file_mapping->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 ((chuni_io_file_mapping && chuni_io_file_mapping->testBtn) || - GetAsyncKeyState(chuni_io_cfg.vk_test)) { - *opbtn |= CHUNI_IO_OPBTN_TEST; /* Test */ - } - - if ((chuni_io_file_mapping && chuni_io_file_mapping->serviceBtn) || - GetAsyncKeyState(chuni_io_cfg.vk_service)) { - *opbtn |= CHUNI_IO_OPBTN_SERVICE; /* Service */ - } - - if (GetAsyncKeyState(chuni_io_cfg.vk_ir_emu)) { - if (chuni_io_hand_pos < 6) { - chuni_io_hand_pos++; - } - } else { - if (chuni_io_hand_pos > 0) { - chuni_io_hand_pos--; - } - } - - for (i = 0; i < 6; i++) { - if (chuni_io_hand_pos > i) { - *beams |= (1 << i); - } - } - - // IR format is beams[5:0] = {b5,b6,b3,b4,b1,b2}; - for (i = 0; i < 3; i++) { - if (chuni_io_file_mapping && chuni_io_file_mapping->airIoStatus[i * 2]) - *beams |= (1 << (i * 2 + 1)); - if (chuni_io_file_mapping && chuni_io_file_mapping->airIoStatus[i * 2 + 1]) - *beams |= (1 << (i * 2)); - } -} - -HRESULT chuni_io_slider_init() { return S_OK; } - -void chuni_io_slider_start(void *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 (chuni_io_file_mapping) { - memcpy(chuni_io_file_mapping->ledRgbData, rgb, 32 * 3); - } -} - -HRESULT chuni_io_led_init(void) { return S_OK; } - -void chuni_io_led_set_colors(uint8_t board, uint8_t *rgb) {} - -static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx) { - chuni_io_slider_callback_t callback; - uint8_t pressure[32]; - - callback = (chuni_io_slider_callback_t)ctx; - -#pragma clang diagnostic push -#pragma ide diagnostic ignored "LoopDoesntUseConditionVariableInspection" - while (!chuni_io_slider_stop_flag) { - if (chuni_io_file_mapping) { - memcpy(pressure, chuni_io_file_mapping->sliderIoStatus, 32); - } - - callback(pressure); - Sleep(1); - } -#pragma clang diagnostic pop - - return 0; -} -// endregion - -BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { - return TRUE; -} diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt new file mode 100644 index 0000000..a62e187 --- /dev/null +++ b/util/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.27) +project(util) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") +include_directories("${CMAKE_SOURCE_DIR}/include/") +set(CMAKE_CXX_STANDARD 17) + +add_library(util OBJECT dprintf.c dprintf.h) diff --git a/src/util/dprintf.c b/util/dprintf.c similarity index 100% rename from src/util/dprintf.c rename to util/dprintf.c diff --git a/src/util/dprintf.h b/util/dprintf.h similarity index 100% rename from src/util/dprintf.h rename to util/dprintf.h