mu3: added lights hook

This commit is contained in:
2024-05-12 22:02:53 +02:00
parent b77ce7b457
commit 9fe98b227b
22 changed files with 669 additions and 58 deletions

View File

@ -14,6 +14,8 @@ void mu3_io_config_load(
assert(cfg != NULL);
assert(filename != NULL);
wchar_t output_path_input[6];
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", VK_F1, filename);
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", VK_F2, filename);
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", VK_F3, filename);
@ -30,4 +32,25 @@ void mu3_io_config_load(
cfg->vk_right_3 = GetPrivateProfileIntW(L"io4", L"right3", 'L', filename);
cfg->vk_left_menu = GetPrivateProfileIntW(L"io4", L"leftMenu", 'U', filename);
cfg->vk_right_menu = GetPrivateProfileIntW(L"io4", L"rightMenu", 'O', filename);
cfg->cab_led_output_pipe = GetPrivateProfileIntW(L"led", L"cabLedOutputPipe", 1, filename);
cfg->cab_led_output_serial = GetPrivateProfileIntW(L"led", L"cabLedOutputSerial", 0, filename);
cfg->controller_led_output_pipe = GetPrivateProfileIntW(L"led", L"controllerLedOutputPipe", 1, filename);
cfg->controller_led_output_serial = GetPrivateProfileIntW(L"led", L"controllerLedOutputSerial", 0, filename);
cfg->led_serial_baud = GetPrivateProfileIntW(L"led", L"serialBaud", 921600, filename);
GetPrivateProfileStringW(
L"led",
L"serialPort",
L"COM5",
output_path_input,
_countof(output_path_input),
filename);
// Sanitize the output path. If it's a serial COM port, it needs to be prefixed
// with `\\.\`.
wcsncpy(cfg->led_serial_port, L"\\\\.\\", 4);
wcsncat_s(cfg->led_serial_port, MAX_PATH, output_path_input, MAX_PATH);
}

View File

@ -22,6 +22,17 @@ struct mu3_io_config {
uint8_t vk_right_3;
uint8_t vk_left_menu;
uint8_t vk_right_menu;
// Which ways to output LED information are enabled
bool cab_led_output_pipe;
bool cab_led_output_serial;
bool controller_led_output_pipe;
bool controller_led_output_serial;
// The name of a COM port to output LED data on, in serial mode
wchar_t led_serial_port[12];
int32_t led_serial_baud;
};
void mu3_io_config_load(

23
mu3io/leddata.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include <windows.h>
#include <stdint.h>
#define LED_PACKET_FRAMING 0xE0
#define LED_PACKET_ESCAPE 0xD0
#define LED_NUM_MAX 66
#define LED_BOARDS_TOTAL 2
#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 button and cab LEDs
struct _ongeki_led_data_buf_t {
byte framing; // Sync byte
uint8_t board; // LED output the data is for (0: cab, 1: control deck)
byte data[LED_OUTPUT_DATA_SIZE_MAX]; // Buffer for LEDs
byte data_len; // How many bytes to output from the buffer
};
static byte ongeki_led_board_data_lens[LED_BOARDS_TOTAL] = {9*3, 6*3};

130
mu3io/ledoutput.c Normal file
View File

@ -0,0 +1,130 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include "mu3io/config.h"
#include "mu3io/leddata.h"
#include "mu3io/ledoutput.h"
#include "mu3io/pipeimpl.h"
#include "mu3io/serialimpl.h"
static struct _ongeki_led_data_buf_t mu3_led_unescaped_buf[LED_BOARDS_TOTAL];
static struct _ongeki_led_data_buf_t mu3_led_escaped_buf[LED_BOARDS_TOTAL];
static bool mu3_led_output_is_init = false;
static struct mu3_io_config* mu3_io_config;
static bool mu3_led_any_outputs_enabled;
HANDLE mu3_led_init_mutex;
HRESULT mu3_led_output_init(struct mu3_io_config* const cfg)
{
DWORD dwWaitResult = WaitForSingleObject(mu3_led_init_mutex, INFINITE);
if (dwWaitResult == WAIT_FAILED)
{
return HRESULT_FROM_WIN32(GetLastError());
}
else if (dwWaitResult != WAIT_OBJECT_0)
{
return E_FAIL;
}
if (!mu3_led_output_is_init)
{
mu3_io_config = cfg;
// Setup the framing bytes for the packets
for (int i = 0; i < LED_BOARDS_TOTAL; i++) {
mu3_led_unescaped_buf[i].framing = LED_PACKET_FRAMING;
mu3_led_unescaped_buf[i].board = i;
mu3_led_unescaped_buf[i].data_len = ongeki_led_board_data_lens[i];
mu3_led_escaped_buf[i].framing = LED_PACKET_FRAMING;
mu3_led_escaped_buf[i].board = i;
mu3_led_escaped_buf[i].data_len = ongeki_led_board_data_lens[i];
}
mu3_led_any_outputs_enabled = mu3_io_config->cab_led_output_pipe || mu3_io_config->controller_led_output_pipe
|| mu3_io_config->cab_led_output_serial || mu3_io_config->controller_led_output_serial;
if (mu3_io_config->cab_led_output_pipe || mu3_io_config->controller_led_output_pipe)
{
mu3_led_pipe_init(); // don't really care about errors here tbh
}
if (mu3_io_config->cab_led_output_serial || mu3_io_config->controller_led_output_serial)
{
mu3_led_serial_init(mu3_io_config->led_serial_port, mu3_io_config->led_serial_baud);
}
}
mu3_led_output_is_init = true;
ReleaseMutex(mu3_led_init_mutex);
return S_OK;
}
struct _ongeki_led_data_buf_t* escape_led_data(struct _ongeki_led_data_buf_t* unescaped)
{
struct _ongeki_led_data_buf_t* out_struct = &mu3_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 mu3_led_output_update(int board, const byte* rgb)
{
if (board < 0 || board > 1 || !mu3_led_any_outputs_enabled)
{
return;
}
memcpy(mu3_led_unescaped_buf[board].data, rgb, mu3_led_unescaped_buf[board].data_len);
struct _ongeki_led_data_buf_t* escaped_data = escape_led_data(&mu3_led_unescaped_buf[board]);
if (board == 0)
{
// cab
if (mu3_io_config->cab_led_output_pipe)
{
mu3_led_pipe_update(escaped_data);
}
if (mu3_io_config->cab_led_output_serial)
{
mu3_led_serial_update(escaped_data);
}
}
else
{
// slider
if (mu3_io_config->controller_led_output_pipe)
{
mu3_led_pipe_update(escaped_data);
}
if (mu3_io_config->controller_led_output_serial)
{
mu3_led_serial_update(escaped_data);
}
}
}

20
mu3io/ledoutput.h Normal file
View File

@ -0,0 +1,20 @@
/*
LED output functions
Credits:
somewhatlurker, skogaby
*/
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include "mu3io/config.h"
extern HANDLE mu3_led_init_mutex;
HRESULT mu3_led_output_init(struct mu3_io_config* const cfg);
void mu3_led_output_update(int board, const byte* rgb);

View File

@ -8,9 +8,16 @@ mu3io_lib = static_library(
xinput_lib,
],
sources : [
'mu3io.c',
'mu3io.h',
'config.c',
'config.h',
'leddata.h',
'ledoutput.c',
'ledoutput.h',
'mu3io.c',
'mu3io.h',
'pipeimpl.c',
'pipeimpl.h',
'serialimpl.c',
'serialimpl.h'
],
)

View File

@ -6,6 +6,8 @@
#include "mu3io/mu3io.h"
#include "mu3io/config.h"
#include "mu3io/ledoutput.h"
#include "util/dprintf.h"
static uint8_t mu3_opbtn;
@ -32,7 +34,17 @@ HRESULT mu3_io_init(void)
dprintf("XInput: Mouse lever emulation : %i\n", mu3_io_cfg.use_mouse);
dprintf("XInput: --- End configuration ---\n");
return S_OK;
mu3_led_init_mutex = CreateMutex(
NULL, // default security attributes
FALSE, // initially not owned
NULL); // unnamed mutex
if (mu3_led_init_mutex == NULL)
{
return E_FAIL;
}
return mu3_led_output_init(&mu3_io_cfg);
}
HRESULT mu3_io_poll(void)
@ -203,5 +215,5 @@ HRESULT mu3_io_led_init(void)
void mu3_io_led_set_colors(uint8_t board, uint8_t *rgb)
{
return;
mu3_led_output_update(board, rgb);
}

160
mu3io/pipeimpl.c Normal file
View File

@ -0,0 +1,160 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include "mu3io/leddata.h"
#include "mu3io/pipeimpl.h"
static bool mu3_pipe_update[LED_BOARDS_TOTAL];
// incoming data is copied into these to ensure it isn't written during output
static struct _ongeki_led_data_buf_t mu3_pipe_write_buf[LED_BOARDS_TOTAL];
static HANDLE mu3_pipe_write_mutex;
static HRESULT mu3_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 mu3_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 mu3_io_led_pipe_thread_proc(void *ctx)
{
HANDLE hPipe;
LPCWSTR lpszPipename = L"\\\\.\\pipe\\ongeki_led";
while (true)
{
hPipe = INVALID_HANDLE_VALUE;
if (mu3_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(mu3_pipe_write_mutex, INFINITE) != WAIT_OBJECT_0)
{
continue;
}
for (int i = 0; i < LED_BOARDS_TOTAL; i++) {
if (mu3_pipe_update[i])
{
HRESULT result = mu3_pipe_write(
hPipe,
&mu3_pipe_write_buf[i],
LED_OUTPUT_HEADER_SIZE + mu3_pipe_write_buf[i].data_len);
if (result != S_OK)
{
//if (result == E_ABORT)
//{
fConnected = false;
//}
break;
}
mu3_pipe_update[i] = false;
}
}
ReleaseMutex(mu3_pipe_write_mutex);
}
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
}
return 0;
}
HRESULT mu3_led_pipe_init()
{
mu3_pipe_write_mutex = CreateMutex(
NULL, // default security attributes
FALSE, // initially not owned
NULL); // unnamed mutex
if (mu3_pipe_write_mutex == NULL)
{
return E_FAIL;
}
// clear out update bools
for (int i = 0; i < LED_BOARDS_TOTAL; i++) {
mu3_pipe_update[i] = false;
}
_beginthreadex(
NULL,
0,
mu3_io_led_pipe_thread_proc,
0,
0,
NULL);
return S_OK;
}
void mu3_led_pipe_update(struct _ongeki_led_data_buf_t* data)
{
if (data->board > 1)
{
return;
}
if (WaitForSingleObject(mu3_pipe_write_mutex, INFINITE) != WAIT_OBJECT_0)
{
return;
}
memcpy(&mu3_pipe_write_buf[data->board], data, sizeof(struct _ongeki_led_data_buf_t));
mu3_pipe_update[data->board] = true;
ReleaseMutex(mu3_pipe_write_mutex);
}

15
mu3io/pipeimpl.h Normal file
View File

@ -0,0 +1,15 @@
/*
Pipe implementation for chuniio
Credits:
somewhatlurker, skogaby
*/
#pragma once
#include <windows.h>
#include "mu3io/leddata.h"
HRESULT mu3_led_pipe_init();
void mu3_led_pipe_update(struct _ongeki_led_data_buf_t* data);

88
mu3io/serialimpl.c Normal file
View File

@ -0,0 +1,88 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include "mu3io/leddata.h"
#include "mu3io/serialimpl.h"
#include "util/dprintf.h"
static HANDLE mu3_serial_port;
HRESULT mu3_led_serial_init(wchar_t led_com[12], DWORD baud)
{
// Setup the serial communications
BOOL status;
mu3_serial_port = CreateFileW(led_com,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if (mu3_serial_port == INVALID_HANDLE_VALUE)
{
dprintf("Ongeki Serial LEDs: Failed to open COM port (attempted on %S)\n", led_com);
return E_FAIL;
}
DCB dcb_serial_params = { 0 };
dcb_serial_params.DCBlength = sizeof(dcb_serial_params);
status = GetCommState(mu3_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(mu3_serial_port, &dcb_serial_params);
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
SetCommTimeouts(mu3_serial_port, &timeouts);
if (!status)
{
return E_FAIL;
}
return S_OK;
}
void mu3_led_serial_update(struct _ongeki_led_data_buf_t* data)
{
if (data->board > 1)
{
return;
}
if (mu3_serial_port != INVALID_HANDLE_VALUE)
{
DWORD bytes_written = 0;
BOOL status = WriteFile(
mu3_serial_port,
data,
LED_OUTPUT_HEADER_SIZE + data->data_len,
&bytes_written,
NULL);
if (!status)
{
DWORD last_err = GetLastError();
dprintf("Ongeki Serial LEDs: Serial port write failed -- %d\n", (int) last_err);
}
}
else
{
dprintf("Ongeki Serial LEDs: Invalid serial port handle\n");
}
}

15
mu3io/serialimpl.h Normal file
View File

@ -0,0 +1,15 @@
/*
Serial LED implementation for chuniio
Credits:
somewhatlurker, skogaby
*/
#pragma once
#include <windows.h>
#include "mu3io/leddata.h"
HRESULT mu3_led_serial_init(wchar_t led_com[12], DWORD baud);
void mu3_led_serial_update(struct _ongeki_led_data_buf_t* data);