diff --git a/chuniio/CMakeLists.txt b/chuniio/CMakeLists.txt index 00dadc3..5a0e23d 100644 --- a/chuniio/CMakeLists.txt +++ b/chuniio/CMakeLists.txt @@ -1,33 +1,40 @@ -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/servers/socket.h - src/ipc_memory_info.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 () +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/leddata.h + src/ledoutput.c + src/ledoutput.h + src/pipeimpl.c + src/pipeimpl.h + src/serialimpl.c + src/serialimpl.h + src/servers/socket.h + src/ipc_memory_info.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/chuniio.c b/chuniio/src/chuniio.c index eb81b14..63518a8 100644 --- a/chuniio/src/chuniio.c +++ b/chuniio/src/chuniio.c @@ -9,13 +9,16 @@ #include "arch.h" #include "config.h" +#include "ledoutput.h" #include "ipc_memory_info.h" #include "util/dprintf.h" #ifdef ENV32BIT + #include "servers/android.h" #include "servers/common.h" #include "servers/ios.h" + #endif #define MEM_FILE_NAME "Local\\BROKENITHM_SHARED_BUFFER" @@ -30,16 +33,16 @@ HRESULT chuni_io_init_shared_memory() { } if ((chuni_io_file_mapping_handle = - CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, - sizeof(struct IPCMemoryInfo), MEM_FILE_NAME)) == 0) { + 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 E_FAIL; } - if ((chuni_io_file_mapping = (struct IPCMemoryInfo *)MapViewOfFile( - chuni_io_file_mapping_handle, FILE_MAP_ALL_ACCESS, 0, 0, - sizeof(struct IPCMemoryInfo))) == 0) { + 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 E_FAIL; @@ -71,6 +74,12 @@ HRESULT chuni_io_jvs_init() { return result; } + led_init_mutex = CreateMutex(NULL, FALSE, NULL); + + if (led_init_mutex == NULL) { + return E_FAIL; + } + #ifdef ENV32BIT if ((result = android_init_server(chuni_io_file_mapping))) { print_err("[ERROR] Android server intialization failed: %ld", result); @@ -148,7 +157,9 @@ void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams) { } } -HRESULT chuni_io_slider_init() { return S_OK; } +HRESULT chuni_io_slider_init() { + return led_output_init(&chuni_io_cfg); +} void chuni_io_slider_start(void *callback) { if (chuni_io_slider_thread != NULL) { @@ -158,7 +169,7 @@ void chuni_io_slider_start(void *callback) { atomic_flag_test_and_set(&chuni_io_slider_run_flag); chuni_io_slider_thread = - (HANDLE)_beginthreadex(NULL, 0, chuni_io_slider_thread_proc, callback, 0, NULL); + (HANDLE) _beginthreadex(NULL, 0, chuni_io_slider_thread_proc, callback, 0, NULL); } void chuni_io_slider_stop(void) { @@ -174,14 +185,20 @@ void chuni_io_slider_stop(void) { } void chuni_io_slider_set_leds(const uint8_t *rgb) { + led_output_update(2, rgb); + if (chuni_io_file_mapping) { memcpy(chuni_io_file_mapping->ledRgbData, rgb, 32 * 3); } } -HRESULT chuni_io_led_init(void) { return S_OK; } +HRESULT chuni_io_led_init(void) { + return led_output_init(&chuni_io_cfg); +} -void chuni_io_led_set_colors(uint8_t board, uint8_t *rgb) {} +void chuni_io_led_set_colors(uint8_t board, uint8_t *rgb) { + led_output_update(board, rgb); +} static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx) { const chuni_io_slider_callback_t callback = ctx; diff --git a/chuniio/src/leddata.h b/chuniio/src/leddata.h new file mode 100644 index 0000000..0764c9f --- /dev/null +++ b/chuniio/src/leddata.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +#define LED_PACKET_FRAMING 0xE0 +#define LED_PACKET_ESCAPE 0xD0 +#define LED_NUM_MAX 66 +#define LED_BOARDS_TOTAL 3 +#define LED_OUTPUT_HEADER_SIZE 2 +#define LED_OUTPUT_DATA_SIZE_MAX LED_NUM_MAX * 3 * 2 // max if every byte's escaped +#define LED_OUTPUT_TOTAL_SIZE_MAX LED_OUTPUT_HEADER_SIZE + LED_OUTPUT_DATA_SIZE_MAX + +// This struct is used to send data related to the slider and billboard LEDs +struct _chuni_led_data_buf_t { + byte framing; // Sync byte + uint8_t board; // LED output the data is for (0-1: billboard, 2: slider) + byte data[LED_OUTPUT_DATA_SIZE_MAX]; // Buffer for LEDs + byte data_len; // How many bytes to output from the buffer +}; + +static byte chuni_led_board_data_lens[LED_BOARDS_TOTAL] = {53*3, 63*3, 31*3}; \ No newline at end of file diff --git a/chuniio/src/ledoutput.c b/chuniio/src/ledoutput.c new file mode 100644 index 0000000..e269e1b --- /dev/null +++ b/chuniio/src/ledoutput.c @@ -0,0 +1,131 @@ +#include + +#include + +#include "config.h" +#include "leddata.h" +#include "ledoutput.h" +#include "pipeimpl.h" +#include "serialimpl.h" + +static struct _chuni_led_data_buf_t led_unescaped_buf[LED_BOARDS_TOTAL]; +static struct _chuni_led_data_buf_t led_escaped_buf[LED_BOARDS_TOTAL]; + +static bool led_output_is_init = false; +static struct chuni_io_config* config; +static bool any_outputs_enabled; + +HANDLE led_init_mutex; + +HRESULT led_output_init(struct chuni_io_config* const cfg) +{ + DWORD dwWaitResult = WaitForSingleObject(led_init_mutex, INFINITE); + if (dwWaitResult == WAIT_FAILED) + { + return HRESULT_FROM_WIN32(GetLastError()); + // return 1; + } + else if (dwWaitResult != WAIT_OBJECT_0) + { + return E_FAIL; + // return 1; + } + + if (!led_output_is_init) + { + config = cfg; + + // Set up the framing bytes for the packets + for (int i = 0; i < LED_BOARDS_TOTAL; i++) { + led_unescaped_buf[i].framing = LED_PACKET_FRAMING; + led_unescaped_buf[i].board = i; + led_unescaped_buf[i].data_len = chuni_led_board_data_lens[i]; + + led_escaped_buf[i].framing = LED_PACKET_FRAMING; + led_escaped_buf[i].board = i; + led_escaped_buf[i].data_len = chuni_led_board_data_lens[i]; + } + + any_outputs_enabled = config->led_output_pipe || config->slider_led_output_pipe + || config->led_output_serial || config->slider_led_output_serial; + + if (config->led_output_pipe || config->slider_led_output_pipe) + { + led_pipe_init(); // don't really care about errors here tbh + } + + if (config->led_output_serial || config->slider_led_output_serial) + { + led_serial_init(config->led_serial_port, config->led_serial_baud); + } + } + + led_output_is_init = true; + + ReleaseMutex(led_init_mutex); + return S_OK; + // return 0; +} + +struct _chuni_led_data_buf_t* escape_led_data(struct _chuni_led_data_buf_t* unescaped) +{ + struct _chuni_led_data_buf_t* out_struct = &led_escaped_buf[unescaped->board]; + + byte* in_buf = unescaped->data; + byte* out_buf = out_struct->data; + int i = 0; + int o = 0; + + while (i < unescaped->data_len) + { + byte b = in_buf[i++]; + if (b == LED_PACKET_FRAMING || b == LED_PACKET_ESCAPE) + { + out_buf[o++] = LED_PACKET_ESCAPE; + b--; + } + out_buf[o++] = b; + } + + out_struct->data_len = o; + + return out_struct; +} + +void led_output_update(uint8_t board, const uint8_t* rgb) +{ + if (board < 0 || board > 2 || !any_outputs_enabled) + { + return; + } + + memcpy(led_unescaped_buf[board].data, rgb, led_unescaped_buf[board].data_len); + struct _chuni_led_data_buf_t* escaped_data = escape_led_data(&led_unescaped_buf[board]); + + if (board < 2) + { + // billboard + if (config->led_output_pipe) + { + led_pipe_update(escaped_data); + } + + if (config->led_output_serial) + { + led_serial_update(escaped_data); + } + } + else + { + // slider + if (config->slider_led_output_pipe) + { + led_pipe_update(escaped_data); + } + + if (config->slider_led_output_serial) + { + led_serial_update(escaped_data); + } + } +} diff --git a/chuniio/src/ledoutput.h b/chuniio/src/ledoutput.h new file mode 100644 index 0000000..8e4c7ee --- /dev/null +++ b/chuniio/src/ledoutput.h @@ -0,0 +1,19 @@ +/* + LED output functions + + Credits: + somewhatlurker, skogaby +*/ + +#pragma once + +#include + +#include +#include + +#include "config.h" + +extern HANDLE led_init_mutex; +HRESULT led_output_init(struct chuni_io_config* const cfg); +void led_output_update(uint8_t board, const uint8_t* rgb); diff --git a/chuniio/src/pipeimpl.c b/chuniio/src/pipeimpl.c new file mode 100644 index 0000000..e4e2e3a --- /dev/null +++ b/chuniio/src/pipeimpl.c @@ -0,0 +1,160 @@ +#include + +#include +#include +#include + +#include "leddata.h" +#include "pipeimpl.h" + +static bool pipe_update[LED_BOARDS_TOTAL]; + +// incoming data is copied into these to ensure it isn't written during output +static struct _chuni_led_data_buf_t pipe_write_buf[LED_BOARDS_TOTAL]; +static HANDLE pipe_write_mutex; + +static HRESULT pipe_create(LPHANDLE hPipe, LPCWSTR lpszPipename, DWORD dwBufSize) +{ + *hPipe = INVALID_HANDLE_VALUE; + + *hPipe = CreateNamedPipeW( + lpszPipename, // pipe name + PIPE_ACCESS_OUTBOUND, // read/write access + PIPE_TYPE_BYTE | // byte type pipe + PIPE_WAIT, // blocking mode + PIPE_UNLIMITED_INSTANCES, // max. instances + dwBufSize, // output buffer size + 0, // input buffer size + 0, // client time-out + NULL); // default security attribute + + if (*hPipe == INVALID_HANDLE_VALUE) + { + return E_FAIL; + } + + return S_OK; +} + +static HRESULT pipe_write(HANDLE hPipe, LPCVOID lpBuffer, DWORD dwSize) +{ + DWORD cbWritten = 0; + + bool fSuccess = WriteFile( + hPipe, + lpBuffer, + dwSize, + &cbWritten, + NULL); + + if (!fSuccess || cbWritten != dwSize) + { + DWORD last_err = GetLastError(); + return (last_err == ERROR_BROKEN_PIPE) ? E_ABORT : E_FAIL; + } + + return S_OK; +} + +static unsigned int __stdcall chuni_io_led_pipe_thread_proc(void *ctx) +{ + HANDLE hPipe; + LPCWSTR lpszPipename = L"\\\\.\\pipe\\chuni_led"; + + while (true) + { + hPipe = INVALID_HANDLE_VALUE; + + if (pipe_create(&hPipe, lpszPipename, LED_OUTPUT_TOTAL_SIZE_MAX) != S_OK) + { + continue; + } + + // wait for a connection to the pipe + bool fConnected = ConnectNamedPipe(hPipe, NULL) ? + true : (GetLastError() == ERROR_PIPE_CONNECTED); + + while (fConnected) + { + if (WaitForSingleObject(pipe_write_mutex, INFINITE) != WAIT_OBJECT_0) + { + continue; + } + + for (int i = 0; i < LED_BOARDS_TOTAL; i++) { + if (pipe_update[i]) + { + HRESULT result = pipe_write( + hPipe, + &pipe_write_buf[i], + LED_OUTPUT_HEADER_SIZE + pipe_write_buf[i].data_len); + + if (result != S_OK) + { + //if (result == E_ABORT) + //{ + fConnected = false; + //} + break; + } + + pipe_update[i] = false; + } + } + + ReleaseMutex(pipe_write_mutex); + } + + FlushFileBuffers(hPipe); + DisconnectNamedPipe(hPipe); + CloseHandle(hPipe); + } + + return 0; +} + +HRESULT led_pipe_init() +{ + pipe_write_mutex = CreateMutex( + NULL, // default security attributes + FALSE, // initially not owned + NULL); // unnamed mutex + + if (pipe_write_mutex == NULL) + { + return E_FAIL; + } + + // clear out update bools + for (int i = 0; i < LED_BOARDS_TOTAL; i++) { + pipe_update[i] = false; + } + + _beginthreadex( + NULL, + 0, + chuni_io_led_pipe_thread_proc, + 0, + 0, + NULL); + + return S_OK; +} + +void led_pipe_update(struct _chuni_led_data_buf_t* data) +{ + if (data->board > 2) + { + return; + } + + if (WaitForSingleObject(pipe_write_mutex, INFINITE) != WAIT_OBJECT_0) + { + return; + } + + memcpy(&pipe_write_buf[data->board], data, sizeof(struct _chuni_led_data_buf_t)); + pipe_update[data->board] = true; + + ReleaseMutex(pipe_write_mutex); +} diff --git a/chuniio/src/pipeimpl.h b/chuniio/src/pipeimpl.h new file mode 100644 index 0000000..8511256 --- /dev/null +++ b/chuniio/src/pipeimpl.h @@ -0,0 +1,14 @@ +/* + Pipe implementation for chuniio + + Credits: + somewhatlurker, skogaby +*/ +#pragma once + +#include + +#include "leddata.h" + +HRESULT led_pipe_init(); +void led_pipe_update(struct _chuni_led_data_buf_t* data); diff --git a/chuniio/src/serialimpl.c b/chuniio/src/serialimpl.c new file mode 100644 index 0000000..3b75a15 --- /dev/null +++ b/chuniio/src/serialimpl.c @@ -0,0 +1,99 @@ +#include + +#include +#include +#include + +#include "leddata.h" +#include "serialimpl.h" +#include "util/dprintf.h" + +static HANDLE serial_port; +static HANDLE serial_write_mutex; + +HRESULT led_serial_init(wchar_t led_com[12], DWORD baud) +{ + // Setup the serial communications + BOOL status; + + serial_port = CreateFileW(led_com, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (serial_port == INVALID_HANDLE_VALUE) + dprintf("Chunithm Serial LEDs: Failed to open COM port (Attempted on %S)\n", led_com); + else + dprintf("Chunithm Serial LEDs: COM port success!\n"); + + DCB dcb_serial_params = { 0 }; + dcb_serial_params.DCBlength = sizeof(dcb_serial_params); + status = GetCommState(serial_port, &dcb_serial_params); + + dcb_serial_params.BaudRate = baud; + dcb_serial_params.ByteSize = 8; + dcb_serial_params.StopBits = ONESTOPBIT; + dcb_serial_params.Parity = NOPARITY; + SetCommState(serial_port, &dcb_serial_params); + + COMMTIMEOUTS timeouts = { 0 }; + timeouts.ReadIntervalTimeout = 50; + timeouts.ReadTotalTimeoutConstant = 50; + timeouts.ReadTotalTimeoutMultiplier = 10; + timeouts.WriteTotalTimeoutConstant = 50; + timeouts.WriteTotalTimeoutMultiplier = 10; + + SetCommTimeouts(serial_port, &timeouts); + + if (!status) + { + return E_FAIL; + } + + serial_write_mutex = CreateMutex( + NULL, // default security attributes + FALSE, // initially not owned + NULL); // unnamed mutex + + if (serial_write_mutex == NULL) + { + return E_FAIL; + } + + return S_OK; +} + +void led_serial_update(struct _chuni_led_data_buf_t* data) +{ + if (data->board > 2) + { + return; + } + + if (WaitForSingleObject(serial_write_mutex, INFINITE) != WAIT_OBJECT_0) + { + return; + } + + BOOL status = true; + DWORD bytes_written = 0; + + if (serial_port != INVALID_HANDLE_VALUE) { + status = WriteFile( + serial_port, + data, + LED_OUTPUT_HEADER_SIZE + data->data_len, + &bytes_written, + NULL); + } + + if (!status) { + DWORD last_err = GetLastError(); + // dprintf("Chunithm Serial LEDs: Serial port write failed -- %d\n", last_err); + } + + ReleaseMutex(serial_write_mutex); +} diff --git a/chuniio/src/serialimpl.h b/chuniio/src/serialimpl.h new file mode 100644 index 0000000..365bc6a --- /dev/null +++ b/chuniio/src/serialimpl.h @@ -0,0 +1,15 @@ +/* + Serial LED implementation for chuniio + + Credits: + somewhatlurker, skogaby +*/ + +#pragma once + +#include + +#include "leddata.h" + +HRESULT led_serial_init(wchar_t led_com[12], DWORD baud); +void led_serial_update(struct _chuni_led_data_buf_t* data); diff --git a/chuniio/src/servers/android.h b/chuniio/src/servers/android.h index 5ff1931..65f6b67 100644 --- a/chuniio/src/servers/android.h +++ b/chuniio/src/servers/android.h @@ -1,66 +1,66 @@ -// -// Created by beerpsi on 12/31/2023. -// - -#ifndef CHUNIIO_BROKENITHM_ANDROID_H -#define CHUNIIO_BROKENITHM_ANDROID_H - -#define WIN32_LEAN_AND_MEAN -#include - -#include "ipc_memory_info.h" - -; -#pragma pack(push) -#pragma pack(1) -struct AndroidPacketInput { - 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 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) - -HRESULT android_init_server(struct IPCMemoryInfo *memory); - -#endif // CHUNIIO_BROKENITHM_ANDROID_H +// +// Created by beerpsi on 12/31/2023. +// + +#ifndef CHUNIIO_BROKENITHM_ANDROID_H +#define CHUNIIO_BROKENITHM_ANDROID_H + +#define WIN32_LEAN_AND_MEAN +#include + +#include "ipc_memory_info.h" + +; +#pragma pack(push) +#pragma pack(1) +struct AndroidPacketInput { + 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 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) + +HRESULT android_init_server(struct IPCMemoryInfo *memory); + +#endif // CHUNIIO_BROKENITHM_ANDROID_H