chuni/chusan: added LED output to DLLs (will break most DLLs)

Credits: somewhatlurker, skogaby
https://dev.s-ul.net/skogaby/segatools/-/blob/ongeki-15093/
This commit is contained in:
Dniel97 2023-12-19 12:43:26 +01:00
parent ed042176d7
commit 8ebdf67d6e
Signed by untrusted user: Dniel97
GPG Key ID: 6180B3C768FB2E08
33 changed files with 743 additions and 107 deletions

View File

@ -103,11 +103,16 @@ static uint16_t led15093_fw_sum;
static uint8_t led15093_board_adr = 1; static uint8_t led15093_board_adr = 1;
static uint8_t led15093_host_adr = 1; static uint8_t led15093_host_adr = 1;
HRESULT led15093_hook_init(const struct led15093_config *cfg, unsigned int first_port, static io_led_init_t led_init;
unsigned int num_boards, uint8_t board_adr, uint8_t host_adr) static io_led_set_leds_t set_leds;
HRESULT led15093_hook_init(const struct led15093_config *cfg, io_led_init_t _led_init,
io_led_set_leds_t _set_leds, unsigned int first_port, unsigned int num_boards, uint8_t board_adr, uint8_t host_adr)
{ {
assert(cfg != NULL); assert(cfg != NULL);
assert(_led_init != NULL);
assert(_set_leds != NULL);
if (!cfg->enable) { if (!cfg->enable) {
return S_FALSE; return S_FALSE;
@ -117,6 +122,8 @@ HRESULT led15093_hook_init(const struct led15093_config *cfg, unsigned int first
first_port = cfg->port_no; first_port = cfg->port_no;
} }
led_init = _led_init;
set_leds = _set_leds;
led15093_board_adr = board_adr; led15093_board_adr = board_adr;
led15093_host_adr = host_adr; led15093_host_adr = host_adr;
@ -207,9 +214,9 @@ static HRESULT led15093_handle_irp_locked(int board, struct irp *irp)
if (!v->started) { if (!v->started) {
dprintf("LED 15093: Starting LED backend\n"); dprintf("LED 15093: Starting LED backend\n");
// hr = fgo_dll.led_init(); hr = led_init();
hr = S_OK;
// hr = S_OK;
v->started = true; v->started = true;
v->start_hr = hr; v->start_hr = hr;
@ -229,6 +236,19 @@ static HRESULT led15093_handle_irp_locked(int board, struct irp *irp)
} }
} }
*/ */
if (irp->op == IRP_OP_OPEN) {
dprintf("LED 15093: Starting backend DLL\n");
int res = led_init();
if (res != 0) {
dprintf("LED 15093: Backend error, LED board disconnected: "
"%d\n",
res);
return E_FAIL;
}
}
hr = uart_handle_irp(boarduart, irp); hr = uart_handle_irp(boarduart, irp);
@ -624,6 +644,9 @@ static HRESULT led15093_req_set_led(int board, const struct led15093_req_set_led
memcpy(v->led, req->data, req->hdr.nbytes - 1); memcpy(v->led, req->data, req->hdr.nbytes - 1);
// Return the current LED data, remove const qualifier
set_leds(board, (uint8_t *) req->data);
if (!v->enable_response) if (!v->enable_response)
return S_OK; return S_OK;

View File

@ -16,6 +16,9 @@ struct led15093_config {
uint16_t fw_sum; uint16_t fw_sum;
}; };
HRESULT led15093_hook_init(const struct led15093_config *cfg, unsigned int first_port, typedef int (*io_led_init_t)();
unsigned int num_boards, uint8_t board_adr, uint8_t host_adr); typedef void (*io_led_set_leds_t)(uint8_t board, uint8_t *rgb);
HRESULT led15093_hook_init(const struct led15093_config *cfg, io_led_init_t _led_init,
io_led_set_leds_t _set_leds, unsigned int first_port, unsigned int num_boards, uint8_t board_adr, uint8_t host_adr);

View File

@ -30,6 +30,12 @@ const struct dll_bind_sym chuni_dll_syms[] = {
}, { }, {
.sym = "chuni_io_slider_set_leds", .sym = "chuni_io_slider_set_leds",
.off = offsetof(struct chuni_dll, slider_set_leds), .off = offsetof(struct chuni_dll, slider_set_leds),
}, {
.sym = "chuni_io_led_init",
.off = offsetof(struct chuni_dll, led_init),
}, {
.sym = "chuni_io_led_set_colors",
.off = offsetof(struct chuni_dll, led_set_leds),
} }
}; };

View File

@ -13,6 +13,8 @@ struct chuni_dll {
void (*slider_start)(chuni_io_slider_callback_t callback); void (*slider_start)(chuni_io_slider_callback_t callback);
void (*slider_stop)(void); void (*slider_stop)(void);
void (*slider_set_leds)(const uint8_t *rgb); void (*slider_set_leds)(const uint8_t *rgb);
int (*led_init)();
void (*led_set_leds)(uint8_t board, uint8_t *rgb);
}; };
struct chuni_dll_config { struct chuni_dll_config {

View File

@ -20,3 +20,5 @@ EXPORTS
chuni_io_slider_set_leds chuni_io_slider_set_leds
chuni_io_slider_start chuni_io_slider_start
chuni_io_slider_stop chuni_io_slider_stop
chuni_io_led_init
chuni_io_led_set_colors

View File

@ -4,6 +4,7 @@
#include "amex/amex.h" #include "amex/amex.h"
#include "board/led15093.h"
#include "board/sg-reader.h" #include "board/sg-reader.h"
#include "chunihook/config.h" #include "chunihook/config.h"
@ -96,7 +97,8 @@ static DWORD CALLBACK chuni_pre_startup(void)
goto fail; goto fail;
} }
hr = led15093_hook_init(&chuni_hook_cfg.led15093, 10, 2, 2, 1); hr = led15093_hook_init(&chuni_hook_cfg.led15093,
chuni_dll.led_init, chuni_dll.led_set_leds, 10, 2, 2, 1);
if (FAILED(hr)) { if (FAILED(hr)) {
goto fail; goto fail;

View File

@ -7,6 +7,7 @@
#include "chuniio/chuniio.h" #include "chuniio/chuniio.h"
#include "chuniio/config.h" #include "chuniio/config.h"
#include "chuniio/ledoutput.h"
#include "util/dprintf.h" #include "util/dprintf.h"
@ -18,7 +19,6 @@ static uint8_t chuni_io_hand_pos;
static HANDLE chuni_io_slider_thread; static HANDLE chuni_io_slider_thread;
static bool chuni_io_slider_stop_flag; static bool chuni_io_slider_stop_flag;
static struct chuni_io_config chuni_io_cfg; static struct chuni_io_config chuni_io_cfg;
static HANDLE chuni_io_slider_led_port;
uint16_t chuni_io_get_api_version(void) uint16_t chuni_io_get_api_version(void)
{ {
@ -28,7 +28,17 @@ uint16_t chuni_io_get_api_version(void)
HRESULT chuni_io_jvs_init(void) HRESULT chuni_io_jvs_init(void)
{ {
chuni_io_config_load(&chuni_io_cfg, L".\\segatools.ini"); chuni_io_config_load(&chuni_io_cfg, L".\\segatools.ini");
led_init_mutex = CreateMutex(
NULL, // default security attributes
FALSE, // initially not owned
NULL); // unnamed mutex
if (led_init_mutex == NULL)
{
return E_FAIL;
}
return S_OK; return S_OK;
} }
@ -93,7 +103,7 @@ void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams)
HRESULT chuni_io_slider_init(void) HRESULT chuni_io_slider_init(void)
{ {
return S_OK; return led_output_init(&chuni_io_cfg); // because of slider LEDs
} }
void chuni_io_slider_start(chuni_io_slider_callback_t callback) void chuni_io_slider_start(chuni_io_slider_callback_t callback)
@ -111,39 +121,6 @@ void chuni_io_slider_start(chuni_io_slider_callback_t callback)
callback, callback,
0, 0,
NULL); NULL);
chuni_io_slider_led_port = CreateFileW(chuni_io_cfg.led_com,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if (chuni_io_slider_led_port == INVALID_HANDLE_VALUE)
dprintf("Chunithm LEDs: Failed to open COM port (Attempted on %S)\n", chuni_io_cfg.led_com);
else
dprintf("Chunithm LEDs: COM Port Success!\n");
DCB dcb_serial_params = { 0 };
dcb_serial_params.DCBlength = sizeof(dcb_serial_params);
status = GetCommState(chuni_io_slider_led_port, &dcb_serial_params);
dcb_serial_params.BaudRate = CBR_115200; // Setting BaudRate = 115200
dcb_serial_params.ByteSize = 8; // Setting ByteSize = 8
dcb_serial_params.StopBits = ONESTOPBIT;// Setting StopBits = 1
dcb_serial_params.Parity = NOPARITY; // Setting Parity = None
SetCommState(chuni_io_slider_led_port, &dcb_serial_params);
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 50; // in milliseconds
timeouts.ReadTotalTimeoutConstant = 50; // in milliseconds
timeouts.ReadTotalTimeoutMultiplier = 10; // in milliseconds
timeouts.WriteTotalTimeoutConstant = 50; // in milliseconds
timeouts.WriteTotalTimeoutMultiplier = 10; // in milliseconds
SetCommTimeouts(chuni_io_slider_led_port, &timeouts);
} }
void chuni_io_slider_stop(void) void chuni_io_slider_stop(void)
@ -158,34 +135,11 @@ void chuni_io_slider_stop(void)
CloseHandle(chuni_io_slider_thread); CloseHandle(chuni_io_slider_thread);
chuni_io_slider_thread = NULL; chuni_io_slider_thread = NULL;
chuni_io_slider_stop_flag = false; chuni_io_slider_stop_flag = false;
dprintf("Chunithm LEDs: Closing COM port\n");
CloseHandle(chuni_io_slider_led_port);
} }
void chuni_io_slider_set_leds(const uint8_t *rgb) void chuni_io_slider_set_leds(const uint8_t *rgb)
{ {
if (chuni_io_slider_led_port != INVALID_HANDLE_VALUE) led_output_update(2, rgb);
{
char led_buffer[100];
DWORD bytes_to_write; // No of bytes to write into the port
DWORD bytes_written = 0; // No of bytes written to the port
bytes_to_write = sizeof(led_buffer);
BOOL status;
led_buffer[0] = 0xAA;
led_buffer[1] = 0xAA;
memcpy(led_buffer+2, rgb, sizeof(uint8_t) * 96);
led_buffer[98] = 0xDD;
led_buffer[99] = 0xDD;
status = WriteFile(chuni_io_slider_led_port, // Handle to the Serial port
led_buffer, // Data to be written to the port
bytes_to_write, //No of bytes to write
&bytes_written, //Bytes written
NULL);
}
} }
static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx) static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx)
@ -211,3 +165,13 @@ static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx)
return 0; return 0;
} }
int chuni_io_led_init()
{
return led_output_init(&chuni_io_cfg);
}
void chuni_io_led_set_colors(uint8_t board, uint8_t *rgb)
{
led_output_update(board, rgb);
}

View File

@ -145,3 +145,21 @@ void chuni_io_slider_stop(void);
Minimum API version: 0x0100 */ Minimum API version: 0x0100 */
void chuni_io_slider_set_leds(const uint8_t *rgb); void 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.
All subsequent calls may originate from arbitrary threads and some may
overlap with each other. Ensuring synchronization inside your IO DLL is
your responsibility. */
int chuni_io_led_init();
/* Update the RGB LEDs. rgb is a pointer to an array of 66 * 3 = 198
bytes. The majority of these are for the marquee display, but the final
LEDs are for the side partitions.
Chunithm uses two chains/boards. One is on the left side and one on the
right side of the cab. Exact layout is TBD. */
void chuni_io_led_set_colors(uint8_t board, uint8_t *rgb);

View File

@ -57,7 +57,24 @@ void chuni_io_config_load(
filename); filename);
} }
GetPrivateProfileStringW(L"slider", L"ledport", L"COM5", port_input, 6, filename); cfg->led_output_pipe = GetPrivateProfileIntW(L"led", L"cabLedOutputPipe", 1, filename);
wcsncpy(cfg->led_com, L"\\\\.\\", 4); cfg->led_output_serial = GetPrivateProfileIntW(L"led", L"cabLedOutputSerial", 0, filename);
wcsncat_s(cfg->led_com, 11, port_input, 6);
cfg->slider_led_output_pipe = GetPrivateProfileIntW(L"led", L"controllerLedOutputPipe", 1, filename);
cfg->slider_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",
port_input,
6,
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, 11, port_input, 6);
} }

View File

@ -10,7 +10,18 @@ struct chuni_io_config {
uint8_t vk_ir_emu; uint8_t vk_ir_emu;
uint8_t vk_ir[6]; uint8_t vk_ir[6];
uint8_t vk_cell[32]; uint8_t vk_cell[32];
wchar_t led_com[12];
// Which ways to output LED information are enabled
bool led_output_pipe;
bool led_output_serial;
bool slider_led_output_pipe;
bool slider_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 chuni_io_config_load( void chuni_io_config_load(

22
chuniio/leddata.h Normal file
View File

@ -0,0 +1,22 @@
#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 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};

133
chuniio/ledoutput.c Normal file
View File

@ -0,0 +1,133 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include "chuniio/config.h"
#include "chuniio/leddata.h"
#include "chuniio/ledoutput.h"
#include "chuniio/pipeimpl.h"
#include "chuniio/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;
int 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;
// Setup 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 byte* 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);
}
}
}

19
chuniio/ledoutput.h Normal file
View File

@ -0,0 +1,19 @@
/*
LED output functions
Credits:
somewhatlurker, skogaby
*/
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include "chuniio/config.h"
extern HANDLE led_init_mutex;
int led_output_init(struct chuni_io_config* const cfg);
void led_output_update(uint8_t board, const byte* rgb);

View File

@ -9,5 +9,12 @@ chuniio_lib = static_library(
'chuniio.h', 'chuniio.h',
'config.c', 'config.c',
'config.h', 'config.h',
'leddata.h',
'ledoutput.c',
'ledoutput.h',
'pipeimpl.c',
'pipeimpl.h',
'serialimpl.c',
'serialimpl.h'
], ],
) )

160
chuniio/pipeimpl.c Normal file
View File

@ -0,0 +1,160 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include "chuniio/leddata.h"
#include "chuniio/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);
}

14
chuniio/pipeimpl.h Normal file
View File

@ -0,0 +1,14 @@
/*
Pipe implementation for chuniio
Credits:
somewhatlurker, skogaby
*/
#pragma once
#include <windows.h>
#include "chuniio/leddata.h"
HRESULT led_pipe_init();
void led_pipe_update(struct _chuni_led_data_buf_t* data);

99
chuniio/serialimpl.c Normal file
View File

@ -0,0 +1,99 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include "chuniio/leddata.h"
#include "chuniio/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);
}

15
chuniio/serialimpl.h Normal file
View File

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

View File

@ -30,6 +30,12 @@ const struct dll_bind_sym chuni_dll_syms[] = {
}, { }, {
.sym = "chuni_io_slider_set_leds", .sym = "chuni_io_slider_set_leds",
.off = offsetof(struct chuni_dll, slider_set_leds), .off = offsetof(struct chuni_dll, slider_set_leds),
}, {
.sym = "chuni_io_led_init",
.off = offsetof(struct chuni_dll, led_init),
}, {
.sym = "chuni_io_led_set_colors",
.off = offsetof(struct chuni_dll, led_set_leds),
} }
}; };

View File

@ -13,6 +13,8 @@ struct chuni_dll {
void (*slider_start)(chuni_io_slider_callback_t callback); void (*slider_start)(chuni_io_slider_callback_t callback);
void (*slider_stop)(void); void (*slider_stop)(void);
void (*slider_set_leds)(const uint8_t *rgb); void (*slider_set_leds)(const uint8_t *rgb);
int (*led_init)();
void (*led_set_leds)(uint8_t board, uint8_t *rgb);
}; };
struct chuni_dll_config { struct chuni_dll_config {

View File

@ -20,3 +20,5 @@ EXPORTS
chuni_io_slider_set_leds chuni_io_slider_set_leds
chuni_io_slider_start chuni_io_slider_start
chuni_io_slider_stop chuni_io_slider_stop
chuni_io_led_init
chuni_io_led_set_colors

View File

@ -129,7 +129,8 @@ static DWORD CALLBACK chusan_pre_startup(void)
} }
} }
hr = led15093_hook_init(&chusan_hook_cfg.led15093, first_port, 2, 2, 1); hr = led15093_hook_init(&chusan_hook_cfg.led15093,
chuni_dll.led_init, chuni_dll.led_set_leds, first_port, 2, 2, 1);
if (FAILED(hr)) { if (FAILED(hr)) {
goto fail; goto fail;

View File

@ -1,16 +0,0 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
struct led1509306_config {
bool enable;
bool cvt_port;
char board_number[8];
char chip_number[5];
uint8_t fw_ver;
uint16_t fw_sum;
};
HRESULT led1509306_hook_init(const struct led1509306_config *cfg);

View File

@ -38,15 +38,52 @@ monitor=0
; Leave empty if you want to use Segatools built-in keyboard input. ; Leave empty if you want to use Segatools built-in keyboard input.
path= path=
[led15093]
; 837-15093-06 LED strip emulation setting.
enable=1
[chuniio] [chuniio]
; To use a custom Chunithm IO DLL enter its path here. ; To use a custom Chunithm IO DLL enter its path here.
; Leave empty if you want to use Segatools built-in keyboard input. ; Leave empty if you want to use Segatools built-in keyboard input.
path= path=
[led15093]
; Enable emulation of the 15093-06 controlled lights, which handle the air tower
; RGBs and the rear LED panel (billboard) on the cabinet.
enable=1
[led]
; Output billboard LED strip data to a named pipe called "\\.\pipe\chuni_ledstrip"
cabLedOutputPipe=1
; Output billboard LED strip data to serial
cabLedOutputSerial=0
; Output slider LED data to the named pipe
controllerLedOutputPipe=1
; Output slider LED data to the serial port
controllerLedOutputSerial=0
; Serial port to send data to if using serial output. Default is COM5.
;serialPort=COM5
; Baud rate for serial data
;serialBaud=921600
; Data output a sequence of bytes, with JVS-like framing.
; Each "packet" starts with 0xE0 as a sync. To avoid E0 appearing elsewhere,
; 0xD0 is used as an escape character -- if you receive D0 in the output, ignore
; it and use the next sent byte plus one instead.
;
; After the sync is one byte for the board number that was updated, followed by
; the red, green and blue values for each LED.
;
; Board 0 has 53 LEDs:
; [0]-[49]: snakes through left half of billboard (first column starts at top)
; [50]-[52]: left side partition LEDs
;
; Board 1 has 63 LEDs:
; [0]-[59]: right half of billboard (first column starts at bottom)
; [60]-[62]: right side partition LEDs
;
; Board 2 is the slider and has 31 LEDs:
; [0]-[31]: slider LEDs right to left BRG, alternating between keys and dividers
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------
; Input settings ; Input settings
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------
@ -66,6 +103,20 @@ test=0x31
service=0x32 service=0x32
; Keyboard button to increment coin counter. Default is the 3 key. ; Keyboard button to increment coin counter. Default is the 3 key.
coin=0x33 coin=0x33
; Set to 0 for enable separate ir control. Deafult is space key.
ir=0x20
[ir]
; Uncomment and complete the following sequence of settings to configure a
; custom ir-cappable controller if you have one.
;ir6=0x53
; ... etc ...
;ir1=0x53
[slider]
; Enable slider emulation. If you have real AC slider, set this to 0.
; Slider serial port must be COM1.
;enable=1
; Key bindings for each of the 32 touch cells. The default key map, depicted ; Key bindings for each of the 32 touch cells. The default key map, depicted
; in left-to-right order, is as follows: ; in left-to-right order, is as follows:
@ -77,12 +128,8 @@ coin=0x33
; ;
; Uncomment and complete the following sequence of settings to configure a ; Uncomment and complete the following sequence of settings to configure a
; custom high-precision touch strip controller if you have one. ; custom high-precision touch strip controller if you have one.
[slider] ;cell1=0x53
;cell32=0x53 ;cell2=0x53
;cell31=0x53
;cell30=0x53
; ... etc ... ; ... etc ...
;cell31=0x53
; Enable slider LED serial output. This follows OpeNITHM Serial LED Protocol. ;cell32=0x53
; eg. COM5
;ledport=

View File

@ -63,16 +63,53 @@ framed=0
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen) ; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
monitor=0 monitor=0
[led15093]
; 837-15093-06 LED strip emulation setting.
enable=1
[chuniio] [chuniio]
; Uncomment this if you have custom chuniio implementation. ; Uncomment this if you have custom chuniio implementation.
; x86 chuniio to path32, x64 to path64. Both are necessary. ; x86 chuniio to path32, x64 to path64. Both are necessary.
;path32= ;path32=
;path64= ;path64=
[led15093]
; Enable emulation of the 15093-06 controlled lights, which handle the air tower
; RGBs and the rear LED panel (billboard) on the cabinet.
enable=1
[led]
; Output billboard LED strip data to a named pipe called "\\.\pipe\chuni_led"
cabLedOutputPipe=1
; Output billboard LED strip data to serial
cabLedOutputSerial=0
; Output slider LED data to the named pipe
controllerLedOutputPipe=1
; Output slider LED data to the serial port
controllerLedOutputSerial=0
; Serial port to send data to if using serial output. Default is COM5.
;serialPort=COM5
; Baud rate for serial data
;serialBaud=921600
; Data output a sequence of bytes, with JVS-like framing.
; Each "packet" starts with 0xE0 as a sync. To avoid E0 appearing elsewhere,
; 0xD0 is used as an escape character -- if you receive D0 in the output, ignore
; it and use the next sent byte plus one instead.
;
; After the sync is one byte for the board number that was updated, followed by
; the red, green and blue values for each LED.
;
; Board 0 has 53 LEDs:
; [0]-[49]: snakes through left half of billboard (first column starts at top)
; [50]-[52]: left side partition LEDs
;
; Board 1 has 63 LEDs:
; [0]-[59]: right half of billboard (first column starts at bottom)
; [60]-[62]: right side partition LEDs
;
; Board 2 is the slider and has 31 LEDs:
; [0]-[31]: slider LEDs right to left BRG, alternating between keys and dividers
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------
; Input settings ; Input settings
; ----------------------------------------------------------------------------- ; -----------------------------------------------------------------------------

View File

@ -3,6 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "board/io4.h" #include "board/io4.h"
#include "board/led15093.h"
#include "board/sg-reader.h" #include "board/sg-reader.h"
#include "board/vfd.h" #include "board/vfd.h"
@ -96,7 +97,8 @@ static DWORD CALLBACK fgo_pre_startup(void)
goto fail; goto fail;
} }
hr = led15093_hook_init(&fgo_hook_cfg.led15093, 17, 1, 1, 2); hr = led15093_hook_init(&fgo_hook_cfg.led15093,
fgo_dll.led_init, fgo_dll.led_set_leds, 17, 1, 1, 2);
if (FAILED(hr)) { if (FAILED(hr)) {
goto fail; goto fail;

View File

@ -24,6 +24,12 @@ const struct dll_bind_sym fgo_dll_syms[] = {
}, { }, {
.sym = "fgo_io_get_analogs", .sym = "fgo_io_get_analogs",
.off = offsetof(struct fgo_dll, get_analogs), .off = offsetof(struct fgo_dll, get_analogs),
}, {
.sym = "fgo_io_led_init",
.off = offsetof(struct fgo_dll, led_init),
}, {
.sym = "fgo_io_led_set_leds",
.off = offsetof(struct fgo_dll, led_set_leds),
} }
}; };

View File

@ -11,6 +11,8 @@ struct fgo_dll {
void (*get_opbtns)(uint8_t *opbtn); void (*get_opbtns)(uint8_t *opbtn);
void (*get_gamebtns)(uint8_t *gamebtn); void (*get_gamebtns)(uint8_t *gamebtn);
void (*get_analogs)(int16_t *stick_x, int16_t *stick_y); void (*get_analogs)(int16_t *stick_x, int16_t *stick_y);
int (*led_init)();
void (*led_set_leds)(uint8_t board, uint8_t *rgb);
}; };
struct fgo_dll_config { struct fgo_dll_config {

View File

@ -17,6 +17,8 @@ EXPORTS
fgo_io_get_opbtns fgo_io_get_opbtns
fgo_io_init fgo_io_init
fgo_io_poll fgo_io_poll
fgo_io_led_init
fgo_io_led_set_leds
fwdlusb_open fwdlusb_open
fwdlusb_close fwdlusb_close
fwdlusb_listupPrinter fwdlusb_listupPrinter

View File

@ -139,3 +139,13 @@ void fgo_io_get_analogs(int16_t *stick_x, int16_t *stick_y)
*stick_y = fgo_stick_y; *stick_y = fgo_stick_y;
} }
} }
int fgo_io_led_init()
{
return 0;
}
void fgo_io_led_set_leds(uint8_t board, uint8_t *rgb)
{
return;
}

View File

@ -69,3 +69,18 @@ void fgo_io_get_gamebtns(uint8_t *btn);
Minimum API version: 0x0100 */ Minimum API version: 0x0100 */
void fgo_io_get_analogs(int16_t *stick_x, int16_t *stick_y); void fgo_io_get_analogs(int16_t *stick_x, int16_t *stick_y);
/* Initialize LED emulation. This function will be called before any
other fgo_io_led_*() function calls.
All subsequent calls may originate from arbitrary threads and some may
overlap with each other. Ensuring synchronization inside your IO DLL is
your responsibility. */
int fgo_io_led_init();
/* Update the RGB LEDs.
Exact layout is TBD. */
void fgo_io_led_set_leds(uint8_t board, uint8_t *rgb);

View File

@ -3,7 +3,7 @@
#include <stddef.h> #include <stddef.h>
#include "board/config.h" #include "board/config.h"
#include "board/led15093.h" // #include "board/led15093.h"
#include "gfxhook/gfx.h" #include "gfxhook/gfx.h"
@ -19,7 +19,7 @@ struct mu3_hook_config {
struct dvd_config dvd; struct dvd_config dvd;
struct io4_config io4; struct io4_config io4;
struct gfx_config gfx; struct gfx_config gfx;
struct led15093_config led15093; // struct led15093_config led15093;
struct mu3_dll_config dll; struct mu3_dll_config dll;
}; };

View File

@ -61,11 +61,14 @@ static DWORD CALLBACK mu3_pre_startup(void)
goto fail; goto fail;
} }
/*
// Does not work, Unity moment
hr = led15093_hook_init(&mu3_hook_cfg.led15093, 3, 1, 1, 2); hr = led15093_hook_init(&mu3_hook_cfg.led15093, 3, 1, 1, 2);
if (FAILED(hr)) { if (FAILED(hr)) {
return hr; return hr;
} }
*/
hr = sg_reader_hook_init(&mu3_hook_cfg.aime, 1, 1, mu3_hook_mod); hr = sg_reader_hook_init(&mu3_hook_cfg.aime, 1, 1, mu3_hook_mod);