Merge branch 'chuniio' into develop

This commit is contained in:
Dniel97 2023-12-27 19:29:55 +01:00
commit 477dad2667
Signed by untrusted user: Dniel97
GPG Key ID: 6180B3C768FB2E08
37 changed files with 1332 additions and 155 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,29 @@ 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();
hr = led_init();
/*
if (res != 0) {
dprintf("LED 15093: Backend error, LED board disconnected: "
"%d\n",
res);
return E_FAIL;
}
*/
if (FAILED(hr)) {
dprintf("LED 15093: Backend error, LED board disconnected: "
"%x\n",
(int) hr);
return hr;
}
}
hr = uart_handle_irp(boarduart, irp); hr = uart_handle_irp(boarduart, irp);
@ -657,8 +687,20 @@ static HRESULT led15093_req_set_imm_led(int board, const struct led15093_req_set
return E_INVALIDARG; return E_INVALIDARG;
} }
memcpy(v->led, req->data, req->hdr.nbytes - 1); /*
if (board == 0) {
dprintf("board %d: red: %d, green: %d, blue: %d\n", board, req->data[0x96], req->data[0x97], req->data[0x98]);
}
else if (board == 1)
{
dprintf("board %d: red: %d, green: %d, blue: %d\n", board, req->data[0xb4], req->data[0xb5], req->data[0xb6]);
}
*/
// Return the current LED data, remove const qualifier
set_leds(board, (uint8_t *) req->data);
memcpy(v->led, req->data, req->hdr.nbytes - 1);
// fgo_dll.led_gr_set_imm((const uint8_t*)&v->led); // fgo_dll.led_gr_set_imm((const uint8_t*)&v->led);
if (!v->enable_response) if (!v->enable_response)

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 HRESULT (*io_led_init_t)(void);
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,9 +30,28 @@ 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),
} }
}; };
/* Helper function to determine upon dll_bind failure whether the required functions were found
NOTE: relies on symbols order declared above */
static HRESULT has_enough_symbols(uint16_t version, uint8_t count)
{
if ( version <= 0x0101 && count == 7 )
return S_OK;
if ( version >= 0x0102 && count == 9 )
return S_OK;
return E_FAIL;
}
struct chuni_dll chuni_dll; struct chuni_dll chuni_dll;
// Copypasta DLL binding and diagnostic message boilerplate. // Copypasta DLL binding and diagnostic message boilerplate.
@ -92,16 +111,24 @@ HRESULT chuni_dll_init(const struct chuni_dll_config *cfg, HINSTANCE self)
} }
sym = chuni_dll_syms; sym = chuni_dll_syms;
const struct dll_bind_sym *init_sym = &sym[0];
hr = dll_bind(&chuni_dll, src, &sym, _countof(chuni_dll_syms)); hr = dll_bind(&chuni_dll, src, &sym, _countof(chuni_dll_syms));
if (FAILED(hr)) { if (FAILED(hr)) {
if (src != self) { if (src != self) {
dprintf("Chunithm IO: Custom IO DLL does not provide function " // Might still be ok depending on external dll API version
"\"%s\". Please contact your IO DLL's developer for " int bind_count = sym - init_sym;
"further assistance.\n", if ( has_enough_symbols(chuni_dll.api_version, bind_count) == S_OK )
sym->sym); {
hr = S_OK;
} else {
dprintf("Chunithm IO: Custom IO DLL does not provide function "
"\"%s\". Please contact your IO DLL's developer for "
"further assistance.\n",
sym->sym);
goto end; goto end;
}
} else { } else {
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
} }

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);
HRESULT (*led_init)(void);
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,10 +97,16 @@ static DWORD CALLBACK chuni_pre_startup(void)
goto fail; goto fail;
} }
hr = led15093_hook_init(&chuni_hook_cfg.led15093, 10, 2, 2, 1); if ( chuni_dll.led_init == NULL || chuni_dll.led_set_leds == NULL )
{
dprintf("IO DLL doesn't support led_init/led_set_leds, cannot start LED15093 hook\n");
} else {
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;
}
} }
hr = sg_reader_hook_init(&chuni_hook_cfg.aime, 12, 1, chuni_hook_mod); hr = sg_reader_hook_init(&chuni_hook_cfg.aime, 12, 1, chuni_hook_mod);

358
chuniio/chu2to3.c Normal file
View File

@ -0,0 +1,358 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "chuniio/chu2to3.h"
#include "util/dprintf.h"
// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
/* chuniio.dll dynamic loading */
HMODULE hinstLib;
typedef uint16_t (*chuni_io_get_api_version_t)(void);
typedef HRESULT (*chuni_io_jvs_init_t)(void);
typedef void (*chuni_io_jvs_poll_t)(uint8_t*, uint8_t*);
typedef void (*chuni_io_jvs_read_coin_counter_t)(uint16_t *);
typedef HRESULT (*chuni_io_slider_init_t)(void);
typedef void (*chuni_io_slider_set_leds_t)(const uint8_t *);
typedef void (*chuni_io_slider_start_t)(chuni_io_slider_callback_t);
typedef void (*chuni_io_slider_stop_t)(void);
typedef HRESULT (*chuni_io_led_init_t)(void);
typedef void (*chuni_io_led_set_colors_t)(uint8_t, uint8_t *);
chuni_io_get_api_version_t _chuni_io_get_api_version;
chuni_io_jvs_init_t _chuni_io_jvs_init;
chuni_io_jvs_poll_t _chuni_io_jvs_poll;
chuni_io_jvs_read_coin_counter_t _chuni_io_jvs_read_coin_counter;
chuni_io_slider_init_t _chuni_io_slider_init;
chuni_io_slider_set_leds_t _chuni_io_slider_set_leds;
chuni_io_slider_start_t _chuni_io_slider_start;
chuni_io_slider_stop_t _chuni_io_slider_stop;
chuni_io_led_init_t _chuni_io_led_init;
chuni_io_led_set_colors_t _chuni_io_led_set_colors;
/* SHMEM Handling */
#define BUF_SIZE 1024
#define SHMEM_WRITE(buf, size) CopyMemory((PVOID)g_pBuf, buf, size)
#define SHMEM_READ(buf, size) CopyMemory(buf,(PVOID)g_pBuf, size)
TCHAR g_shmem_name[]=TEXT("Local\\Chu2to3Shmem");
HANDLE g_hMapFile;
LPVOID g_pBuf;
#pragma pack(1)
typedef struct shared_data_s {
uint16_t coin_counter;
uint8_t opbtn;
uint8_t beams;
uint16_t version;
} shared_data_t;
shared_data_t g_shared_data;
bool shmem_create()
{
g_hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // use paging file
NULL, // default security
PAGE_READWRITE, // read/write access
0, // maximum object size (high-order DWORD)
BUF_SIZE, // maximum object size (low-order DWORD)
g_shmem_name); // name of mapping object
if (g_hMapFile == NULL)
{
dprintf("shmem_create : Could not create file mapping object (%ld).\n",
GetLastError());
return 0;
}
g_pBuf = MapViewOfFile(g_hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (g_pBuf == NULL)
{
dprintf("shmem_create : Could not map view of file (%ld).\n",
GetLastError());
CloseHandle(g_hMapFile);
return 0;
}
return 1;
}
bool shmem_load()
{
g_hMapFile = OpenFileMapping(
FILE_MAP_ALL_ACCESS, // read/write access
FALSE, // do not inherit the name
g_shmem_name); // name of mapping object
if (g_hMapFile == NULL)
{
dprintf("shmem_load : Could not open file mapping object (%ld).\n", GetLastError());
return 0;
}
g_pBuf = MapViewOfFile(g_hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if (g_pBuf == NULL)
{
dprintf("shmem_load : Could not map view of file (%ld).\n", GetLastError());
CloseHandle(g_hMapFile);
return 0;
}
dprintf("shmem_load : shmem loaded succesfully.\n");
return 1;
}
void shmem_free()
{
UnmapViewOfFile(g_pBuf);
CloseHandle(g_hMapFile);
}
/* jvs polling thread (to forward info to x64 dll) */
static HANDLE jvs_poll_thread;
static bool jvs_poll_stop_flag;
static unsigned int __stdcall jvs_poll_thread_proc(void *ctx)
{
while (1) {
_chuni_io_jvs_read_coin_counter(&g_shared_data.coin_counter);
g_shared_data.opbtn = 0;
g_shared_data.beams = 0;
_chuni_io_jvs_poll(&g_shared_data.opbtn, &g_shared_data.beams);
SHMEM_WRITE(&g_shared_data, sizeof(shared_data_t));
Sleep(1);
}
return 0;
}
uint16_t chu2to3_load_dll(const wchar_t *dllname)
{
#if defined(ENV64BIT)
/* x64 must just open the shmem and do nothing else */
int errcount = 0;
while (!shmem_load())
{
if (errcount >= 10)
return -1;
Sleep(5000);
errcount++;
}
Sleep(1000);
return S_OK;
#endif
/* this is the first function called so let's setup the chuniio forwarding */
hinstLib = LoadLibraryW(dllname);
if (hinstLib == NULL) {
dprintf("ERROR: unable to load %S (error %ld)\n",dllname, GetLastError());
return -1;
}
_chuni_io_get_api_version = (chuni_io_get_api_version_t)GetProcAddress(hinstLib, "chuni_io_get_api_version");
_chuni_io_jvs_init = (chuni_io_jvs_init_t)GetProcAddress(hinstLib, "chuni_io_jvs_init");
_chuni_io_jvs_poll = (chuni_io_jvs_poll_t)GetProcAddress(hinstLib, "chuni_io_jvs_poll");
_chuni_io_jvs_read_coin_counter = (chuni_io_jvs_read_coin_counter_t)GetProcAddress(hinstLib, "chuni_io_jvs_read_coin_counter");
_chuni_io_slider_init = (chuni_io_slider_init_t)GetProcAddress(hinstLib, "chuni_io_slider_init");
_chuni_io_slider_set_leds = (chuni_io_slider_set_leds_t)GetProcAddress(hinstLib, "chuni_io_slider_set_leds");
_chuni_io_slider_start = (chuni_io_slider_start_t)GetProcAddress(hinstLib, "chuni_io_slider_start");
_chuni_io_slider_stop = (chuni_io_slider_stop_t)GetProcAddress(hinstLib, "chuni_io_slider_stop");
_chuni_io_led_init = (chuni_io_led_init_t)GetProcAddress(hinstLib, "chuni_io_led_init");
_chuni_io_led_set_colors = (chuni_io_led_set_colors_t)GetProcAddress(hinstLib, "chuni_io_led_set_colors");
/* x86 has to create the shmem */
if (!shmem_create())
{
return -1;
}
return 0;
}
/* chuniio exports */
uint16_t chu2to3_io_get_api_version(void)
{
#if defined(ENV64BIT)
/* This might be called too soon so let's make sure x86 has time to write to the shmem */
SHMEM_READ(&g_shared_data, sizeof(shared_data_t));
int errcount = 0;
while (g_shared_data.version == 0)
{
if (errcount >= 3)
{
dprintf("CHU2TO3 X64: Couldn't retrieve api version from shmem, assuming 0x0100\n");
return 0x0100;
}
Sleep(5000);
errcount++;
SHMEM_READ(&g_shared_data, sizeof(shared_data_t));
}
dprintf("CHU2TO3 X64: api version is %04X\n", g_shared_data.version);
return g_shared_data.version;
#endif
if ( _chuni_io_get_api_version == NULL )
{
g_shared_data.version = 0x0100;
}
else
{
g_shared_data.version = _chuni_io_get_api_version();
}
dprintf("CHU2TO3: api version is %04X\n", g_shared_data.version);
SHMEM_WRITE(&g_shared_data, sizeof(shared_data_t));
return g_shared_data.version;
}
HRESULT chu2to3_io_jvs_init(void)
{
#if defined(ENV64BIT)
/* x86 only */
return S_OK;
#endif
_chuni_io_jvs_init();
/* start jvs poll thread now that jvs_init is done */
if (jvs_poll_thread != NULL) {
return S_OK;
}
jvs_poll_thread = (HANDLE) _beginthreadex(NULL,
0,
jvs_poll_thread_proc,
NULL,
0,
NULL);
return S_OK;
}
void chu2to3_io_jvs_read_coin_counter(uint16_t *out)
{
#if defined(ENV32BIT)
/* x86 can perform the call and update shmem (although this call never happens) */
_chuni_io_jvs_read_coin_counter(&g_shared_data.coin_counter);
SHMEM_WRITE(&g_shared_data, sizeof(shared_data_t));
return;
#endif
/* x64 must read value from shmem and update arg */
SHMEM_READ(&g_shared_data, sizeof(shared_data_t));
if (out == NULL) {
return;
}
*out = g_shared_data.coin_counter;
}
void chu2to3_io_jvs_poll(uint8_t *opbtn, uint8_t *beams)
{
#if defined(ENV32BIT)
/* x86 can perform the call and update shmem (although this call never happens) */
_chuni_io_jvs_poll(&g_shared_data.opbtn, &g_shared_data.beams);
SHMEM_WRITE(&g_shared_data, sizeof(shared_data_t));
return;
#endif
/* x64 must read value from shmem and update args */
SHMEM_READ(&g_shared_data, sizeof(shared_data_t));
*opbtn = g_shared_data.opbtn;
*beams = g_shared_data.beams;
}
HRESULT chu2to3_io_slider_init(void)
{
#if defined(ENV64BIT)
/* x86 only */
return S_OK;
#endif
return _chuni_io_slider_init();
}
void chu2to3_io_slider_start(chuni_io_slider_callback_t callback)
{
#if defined(ENV64BIT)
/* x86 only */
return;
#endif
_chuni_io_slider_start(callback);
}
void chu2to3_io_slider_stop(void)
{
#if defined(ENV64BIT)
/* x86 only */
return;
#endif
_chuni_io_slider_stop();
}
void chu2to3_io_slider_set_leds(const uint8_t *rgb)
{
#if defined(ENV64BIT)
/* x86 only */
return;
#endif
_chuni_io_slider_set_leds(rgb);
}
HRESULT chu2to3_io_led_init(void)
{
#if defined(ENV64BIT)
/* x86 only */
return S_OK;
#endif
if (_chuni_io_led_init != NULL)
return _chuni_io_led_init();
return S_OK;
}
void chu2to3_io_led_set_colors(uint8_t board, uint8_t *rgb)
{
#if defined(ENV64BIT)
/* x86 only */
return;
#endif
if (_chuni_io_led_set_colors != NULL)
{
_chuni_io_led_set_colors(board, rgb);
}
}

26
chuniio/chu2to3.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
/*
CHU2TO3 CUSTOM IO API
This dll just mirrors chuniio dll binds but with a dynamic library loading and
a SHMEM system to let a single 32bit dll talk with x86 and x64 processes at once
*/
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
uint16_t chu2to3_io_get_api_version(void);
HRESULT chu2to3_io_jvs_init(void);
void chu2to3_io_jvs_poll(uint8_t *opbtn, uint8_t *beams);
void chu2to3_io_jvs_read_coin_counter(uint16_t *total);
HRESULT chu2to3_io_slider_init(void);
typedef void (*chuni_io_slider_callback_t)(const uint8_t *state);
void chu2to3_io_slider_start(chuni_io_slider_callback_t callback);
void chu2to3_io_slider_stop(void);
void chu2to3_io_slider_set_leds(const uint8_t *rgb);
HRESULT chu2to3_io_led_init(void);
void chu2to3_io_led_set_colors(uint8_t board, uint8_t *rgb);
uint16_t chu2to3_load_dll(const wchar_t *dllname);

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,17 +19,26 @@ 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)
{ {
return 0x0101; return 0x0102;
} }
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;
} }
@ -81,19 +91,19 @@ void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams)
} }
} else { } else {
// Use actual AIR // Use actual AIR
// IR format is beams[5:0] = {b5,b6,b3,b4,b1,b2}; for (i = 0; i < 6; i++) {
for (i = 0 ; i < 3 ; i++) { if(GetAsyncKeyState(chuni_io_cfg.vk_ir[i]) & 0x8000) {
if (GetAsyncKeyState(chuni_io_cfg.vk_ir[i*2]) & 0x8000) *beams |= (1 << i);
*beams |= (1 << (i*2+1)); } else {
if (GetAsyncKeyState(chuni_io_cfg.vk_ir[i*2+1]) & 0x8000) *beams &= ~(1 << i);
*beams |= (1 << (i*2)); }
} }
} }
} }
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;
} }
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)
{
led_output_update(board, rgb);
}

View File

@ -8,6 +8,7 @@
- 0x0100: Initial API version (assumed if chuni_io_get_api_version is not - 0x0100: Initial API version (assumed if chuni_io_get_api_version is not
exported) exported)
- 0x0101: Fix IR beam mappings - 0x0101: Fix IR beam mappings
- 0x0102: Add air tower led and billboard support
*/ */
#include <windows.h> #include <windows.h>
@ -145,3 +146,30 @@ 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.
Minimum API version: 0x0102 */
HRESULT chuni_io_led_init(void);
/* Update the RGB LEDs. rgb is a pointer to an array of up to 63 * 3 = 189 bytes.
Chunithm uses two chains/boards with WS2811 protocol (each logical led corresponds to 3 physical leds).
board 0 is on the left side and board 1 on the right side of the cab
left side has 5*10 rgb values for the billboard, followed by 3 rgb values for the air tower
right side has 6*10 rgb values for the billboard, followed by 3 rgb values for the air tower
Each rgb value is comprised of 3 bytes in R,G,B order
NOTE: billboard strips have alternating direction (bottom to top, top to bottom, ...)
Minimum API version: 0x0102 */
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;
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;
// 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;
HRESULT led_output_init(struct chuni_io_config* const cfg);
void led_output_update(uint8_t board, const byte* rgb);

View File

@ -5,9 +5,18 @@ chuniio_lib = static_library(
implicit_include_directories : false, implicit_include_directories : false,
c_pch : '../precompiled.h', c_pch : '../precompiled.h',
sources : [ sources : [
'chu2to3.c',
'chu2to3.h',
'chuniio.c', 'chuniio.c',
'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

@ -3,6 +3,7 @@
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include "chuniio/chu2to3.h"
#include "chusanhook/chuni-dll.h" #include "chusanhook/chuni-dll.h"
#include "util/dll-bind.h" #include "util/dll-bind.h"
@ -30,9 +31,59 @@ 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),
} }
}; };
const struct dll_bind_sym chu2to3_dll_syms[] = {
{
.sym = "chu2to3_io_jvs_init",
.off = offsetof(struct chuni_dll, jvs_init),
}, {
.sym = "chu2to3_io_jvs_poll",
.off = offsetof(struct chuni_dll, jvs_poll),
}, {
.sym = "chu2to3_io_jvs_read_coin_counter",
.off = offsetof(struct chuni_dll, jvs_read_coin_counter),
}, {
.sym = "chu2to3_io_slider_init",
.off = offsetof(struct chuni_dll, slider_init),
}, {
.sym = "chu2to3_io_slider_start",
.off = offsetof(struct chuni_dll, slider_start),
}, {
.sym = "chu2to3_io_slider_stop",
.off = offsetof(struct chuni_dll, slider_stop),
}, {
.sym = "chu2to3_io_slider_set_leds",
.off = offsetof(struct chuni_dll, slider_set_leds),
}, {
.sym = "chu2to3_io_led_init",
.off = offsetof(struct chuni_dll, led_init),
}, {
.sym = "chu2to3_io_led_set_colors",
.off = offsetof(struct chuni_dll, led_set_leds),
}
};
/* Helper function to determine upon dll_bind failure whether the required functions were found
NOTE: relies on symbols order declared above */
static HRESULT has_enough_symbols(uint16_t version, uint8_t count)
{
if ( version <= 0x0101 && count == 7 )
return S_OK;
if ( version >= 0x0102 && count == 9 )
return S_OK;
return E_FAIL;
}
struct chuni_dll chuni_dll; struct chuni_dll chuni_dll;
// Copypasta DLL binding and diagnostic message boilerplate. // Copypasta DLL binding and diagnostic message boilerplate.
@ -52,7 +103,12 @@ HRESULT chuni_dll_init(const struct chuni_dll_config *cfg, HINSTANCE self)
assert(cfg != NULL); assert(cfg != NULL);
assert(self != NULL); assert(self != NULL);
if (cfg->path[0] != L'\0') { owned = NULL;
src = self;
if (cfg->chu2to3) {
dprintf("Chunithm IO: using chu2to3 engine for IO DLL: %S\n", cfg->path);
} else if (cfg->path[0] != L'\0') {
owned = LoadLibraryW(cfg->path); owned = LoadLibraryW(cfg->path);
if (owned == NULL) { if (owned == NULL) {
@ -66,12 +122,18 @@ HRESULT chuni_dll_init(const struct chuni_dll_config *cfg, HINSTANCE self)
dprintf("Chunithm IO: Using custom IO DLL: %S\n", cfg->path); dprintf("Chunithm IO: Using custom IO DLL: %S\n", cfg->path);
src = owned; src = owned;
} else {
owned = NULL;
src = self;
} }
get_api_version = (void *) GetProcAddress(src, "chuni_io_get_api_version"); if (cfg->chu2to3) {
if (chu2to3_load_dll(cfg->path) != 0)
dprintf("Could not init chu2to3 engine\n");
get_api_version = (void *) GetProcAddress(src, "chu2to3_io_get_api_version");
}
else
{
get_api_version = (void *) GetProcAddress(src, "chuni_io_get_api_version");
}
if (get_api_version != NULL) { if (get_api_version != NULL) {
chuni_dll.api_version = get_api_version(); chuni_dll.api_version = get_api_version();
@ -91,17 +153,26 @@ HRESULT chuni_dll_init(const struct chuni_dll_config *cfg, HINSTANCE self)
goto end; goto end;
} }
sym = chuni_dll_syms; sym = cfg->chu2to3 ? chu2to3_dll_syms : chuni_dll_syms;
const struct dll_bind_sym *init_sym = &sym[0];
hr = dll_bind(&chuni_dll, src, &sym, _countof(chuni_dll_syms)); hr = dll_bind(&chuni_dll, src, &sym, _countof(chuni_dll_syms));
if (FAILED(hr)) { if (FAILED(hr)) {
if (src != self) { if (src != self) {
dprintf("Chunithm IO: Custom IO DLL does not provide function " // Might still be ok depending on external dll API version
"\"%s\". Please contact your IO DLL's developer for " int bind_count = sym - init_sym;
"further assistance.\n", if ( has_enough_symbols(chuni_dll.api_version, bind_count) == S_OK )
sym->sym); {
hr = S_OK;
goto end; } else {
dprintf("Chunithm IO: Custom IO DLL does not provide function "
"\"%s\". Please contact your IO DLL's developer for "
"further assistance.\n",
sym->sym);
dprintf("imported %d symbols\n",bind_count);
goto end;
}
} else { } else {
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym); dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
} }

View File

@ -13,10 +13,13 @@ 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);
HRESULT (*led_init)(void);
void (*led_set_leds)(uint8_t board, uint8_t *rgb);
}; };
struct chuni_dll_config { struct chuni_dll_config {
wchar_t path[MAX_PATH]; wchar_t path[MAX_PATH];
uint8_t chu2to3;
}; };
extern struct chuni_dll chuni_dll; extern struct chuni_dll chuni_dll;

View File

@ -20,3 +20,15 @@ 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
chu2to3_io_get_api_version
chu2to3_io_jvs_init
chu2to3_io_jvs_poll
chu2to3_io_jvs_read_coin_counter
chu2to3_io_slider_init
chu2to3_io_slider_set_leds
chu2to3_io_slider_start
chu2to3_io_slider_stop
chu2to3_io_led_init
chu2to3_io_led_set_colors

View File

@ -39,16 +39,27 @@ void chuni_dll_config_load(
// Workaround for x64/x86 external IO dlls // Workaround for x64/x86 external IO dlls
// path32 for 32bit, path64 for 64bit // path32 for 32bit, path64 for 64bit
// for else.. is that possible? idk // path for 32bit only dlls (internal chu2to3 engine)
#if defined(ENV32BIT) GetPrivateProfileStringW(
GetPrivateProfileStringW( L"chuniio",
L"chuniio", L"path",
L"path32", L"",
L"", cfg->path,
cfg->path, _countof(cfg->path),
_countof(cfg->path), filename);
filename); if (cfg->path[0] != L'\0') {
cfg->chu2to3 = 1;
} else {
cfg->chu2to3 = 0;
#if defined(ENV32BIT)
GetPrivateProfileStringW(
L"chuniio",
L"path32",
L"",
cfg->path,
_countof(cfg->path),
filename);
#elif defined(ENV64BIT) #elif defined(ENV64BIT)
GetPrivateProfileStringW( GetPrivateProfileStringW(
L"chuniio", L"chuniio",
@ -60,7 +71,7 @@ void chuni_dll_config_load(
#else #else
#error "Unknown environment" #error "Unknown environment"
#endif #endif
}
} }
void slider_config_load(struct slider_config *cfg, const wchar_t *filename) void slider_config_load(struct slider_config *cfg, const wchar_t *filename)

View File

@ -100,7 +100,7 @@ static DWORD CALLBACK chusan_pre_startup(void)
} }
bool *dipsw = &chusan_hook_cfg.platform.dipsw.dipsw[0]; bool *dipsw = &chusan_hook_cfg.platform.dipsw.dipsw[0];
bool *is_sp = dipsw + 2; bool is_cvt = dipsw[2];
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
switch (i) { switch (i) {
@ -113,15 +113,15 @@ static DWORD CALLBACK chusan_pre_startup(void)
break; break;
case 2: case 2:
dprintf("DipSw: Cab Type: %s\n", is_sp ? "SP" : "CVT"); dprintf("DipSw: Cab Type: %s\n", is_cvt ? "CVT" : "SP");
break; break;
} }
} }
unsigned int first_port = is_sp ? 20 : 2; unsigned int first_port = is_cvt ? 2 : 20;
if (is_sp) { if (!is_cvt) {
hr = vfd_hook_init(2); hr = vfd_hook_init(2);
if (FAILED(hr)) { if (FAILED(hr)) {
@ -129,13 +129,19 @@ static DWORD CALLBACK chusan_pre_startup(void)
} }
} }
hr = led15093_hook_init(&chusan_hook_cfg.led15093, first_port, 2, 2, 1); if ( chuni_dll.led_init == NULL || chuni_dll.led_set_leds == NULL )
{
dprintf("IO DLL doesn't support led_init/led_set_leds, cannot start LED15093 hook\n");
} else {
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;
}
} }
hr = sg_reader_hook_init(&chusan_hook_cfg.aime, 4, is_sp ? 3: 2, chusan_hook_mod); hr = sg_reader_hook_init(&chusan_hook_cfg.aime, 4, is_cvt ? 2: 3, chusan_hook_mod);
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,57 @@ 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 comprised of a single 32bit DLL.
; (will use chu2to3 engine internally)
;path=
; Uncomment both of these if you have custom chuniio implementation comprised of two DLLs.
; 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);
HRESULT (*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,14 @@ void fgo_io_get_analogs(int16_t *stick_x, int16_t *stick_y)
*stick_y = fgo_stick_y; *stick_y = fgo_stick_y;
} }
} }
HRESULT fgo_io_led_init(void)
{
// return 0;
return S_OK;
}
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. */
HRESULT fgo_io_led_init(void);
/* Update the RGB LEDs.
Exact layout is TBD. */
void fgo_io_led_set_leds(uint8_t board, uint8_t *rgb);

View File

@ -17,11 +17,13 @@ add_project_arguments(
language: 'c', language: 'c',
) )
# Use get_argument_syntax() instead once Meson 0.49.0 releases cc = meson.get_compiler('c')
if meson.get_compiler('c').get_id() != 'msvc'
if cc.get_id() != 'msvc'
add_project_arguments( add_project_arguments(
'-ffunction-sections', '-ffunction-sections',
'-fdata-sections', '-fdata-sections',
'-flto', # Enable Link-Time Optimization
language: 'c', language: 'c',
) )
@ -30,11 +32,12 @@ if meson.get_compiler('c').get_id() != 'msvc'
'-Wl,--exclude-all-symbols', '-Wl,--exclude-all-symbols',
'-Wl,--gc-sections', '-Wl,--gc-sections',
'-static-libgcc', '-static-libgcc',
'-flto', # Enable Link-Time Optimization
'-Wl,-s', # Strip debug symbols
language: 'c', language: 'c',
) )
endif endif
cc = meson.get_compiler('c')
shlwapi_lib = cc.find_library('shlwapi') shlwapi_lib = cc.find_library('shlwapi')
dinput8_lib = cc.find_library('dinput8') dinput8_lib = cc.find_library('dinput8')
dxguid_lib = cc.find_library('dxguid') dxguid_lib = cc.find_library('dxguid')

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);