forked from Dniel97/segatools
Merge branch 'chuniio' into develop
This commit is contained in:
commit
477dad2667
@ -103,11 +103,16 @@ static uint16_t led15093_fw_sum;
|
||||
static uint8_t led15093_board_adr = 1;
|
||||
static uint8_t led15093_host_adr = 1;
|
||||
|
||||
HRESULT led15093_hook_init(const struct led15093_config *cfg, unsigned int first_port,
|
||||
unsigned int num_boards, uint8_t board_adr, uint8_t host_adr)
|
||||
static io_led_init_t led_init;
|
||||
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(_led_init != NULL);
|
||||
assert(_set_leds != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return S_FALSE;
|
||||
@ -117,6 +122,8 @@ HRESULT led15093_hook_init(const struct led15093_config *cfg, unsigned int first
|
||||
first_port = cfg->port_no;
|
||||
}
|
||||
|
||||
led_init = _led_init;
|
||||
set_leds = _set_leds;
|
||||
led15093_board_adr = board_adr;
|
||||
led15093_host_adr = host_adr;
|
||||
|
||||
@ -207,9 +214,9 @@ static HRESULT led15093_handle_irp_locked(int board, struct irp *irp)
|
||||
|
||||
if (!v->started) {
|
||||
dprintf("LED 15093: Starting LED backend\n");
|
||||
// hr = fgo_dll.led_init();
|
||||
hr = S_OK;
|
||||
hr = led_init();
|
||||
|
||||
// hr = S_OK;
|
||||
v->started = true;
|
||||
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);
|
||||
|
||||
@ -657,8 +687,20 @@ static HRESULT led15093_req_set_imm_led(int board, const struct led15093_req_set
|
||||
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);
|
||||
|
||||
if (!v->enable_response)
|
||||
|
@ -16,6 +16,9 @@ struct led15093_config {
|
||||
uint16_t fw_sum;
|
||||
};
|
||||
|
||||
HRESULT led15093_hook_init(const struct led15093_config *cfg, unsigned int first_port,
|
||||
unsigned int num_boards, uint8_t board_adr, uint8_t host_adr);
|
||||
typedef HRESULT (*io_led_init_t)(void);
|
||||
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);
|
||||
|
||||
|
@ -30,9 +30,28 @@ const struct dll_bind_sym chuni_dll_syms[] = {
|
||||
}, {
|
||||
.sym = "chuni_io_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;
|
||||
|
||||
// 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;
|
||||
const struct dll_bind_sym *init_sym = &sym[0];
|
||||
hr = dll_bind(&chuni_dll, src, &sym, _countof(chuni_dll_syms));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
if (src != self) {
|
||||
dprintf("Chunithm IO: Custom IO DLL does not provide function "
|
||||
"\"%s\". Please contact your IO DLL's developer for "
|
||||
"further assistance.\n",
|
||||
sym->sym);
|
||||
// Might still be ok depending on external dll API version
|
||||
int bind_count = sym - init_sym;
|
||||
if ( has_enough_symbols(chuni_dll.api_version, bind_count) == S_OK )
|
||||
{
|
||||
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 {
|
||||
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ struct chuni_dll {
|
||||
void (*slider_start)(chuni_io_slider_callback_t callback);
|
||||
void (*slider_stop)(void);
|
||||
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 {
|
||||
|
@ -20,3 +20,5 @@ EXPORTS
|
||||
chuni_io_slider_set_leds
|
||||
chuni_io_slider_start
|
||||
chuni_io_slider_stop
|
||||
chuni_io_led_init
|
||||
chuni_io_led_set_colors
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "amex/amex.h"
|
||||
|
||||
#include "board/led15093.h"
|
||||
#include "board/sg-reader.h"
|
||||
|
||||
#include "chunihook/config.h"
|
||||
@ -96,10 +97,16 @@ static DWORD CALLBACK chuni_pre_startup(void)
|
||||
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)) {
|
||||
goto fail;
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
hr = sg_reader_hook_init(&chuni_hook_cfg.aime, 12, 1, chuni_hook_mod);
|
||||
|
358
chuniio/chu2to3.c
Normal file
358
chuniio/chu2to3.c
Normal 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
26
chuniio/chu2to3.h
Normal 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);
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "chuniio/chuniio.h"
|
||||
#include "chuniio/config.h"
|
||||
#include "chuniio/ledoutput.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
@ -18,17 +19,26 @@ static uint8_t chuni_io_hand_pos;
|
||||
static HANDLE chuni_io_slider_thread;
|
||||
static bool chuni_io_slider_stop_flag;
|
||||
static struct chuni_io_config chuni_io_cfg;
|
||||
static HANDLE chuni_io_slider_led_port;
|
||||
|
||||
uint16_t chuni_io_get_api_version(void)
|
||||
{
|
||||
return 0x0101;
|
||||
return 0x0102;
|
||||
}
|
||||
|
||||
HRESULT chuni_io_jvs_init(void)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ -81,19 +91,19 @@ void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams)
|
||||
}
|
||||
} else {
|
||||
// Use actual AIR
|
||||
// IR format is beams[5:0] = {b5,b6,b3,b4,b1,b2};
|
||||
for (i = 0 ; i < 3 ; i++) {
|
||||
if (GetAsyncKeyState(chuni_io_cfg.vk_ir[i*2]) & 0x8000)
|
||||
*beams |= (1 << (i*2+1));
|
||||
if (GetAsyncKeyState(chuni_io_cfg.vk_ir[i*2+1]) & 0x8000)
|
||||
*beams |= (1 << (i*2));
|
||||
for (i = 0; i < 6; i++) {
|
||||
if(GetAsyncKeyState(chuni_io_cfg.vk_ir[i]) & 0x8000) {
|
||||
*beams |= (1 << i);
|
||||
} else {
|
||||
*beams &= ~(1 << i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@ -111,39 +121,6 @@ void chuni_io_slider_start(chuni_io_slider_callback_t callback)
|
||||
callback,
|
||||
0,
|
||||
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)
|
||||
@ -158,34 +135,11 @@ void chuni_io_slider_stop(void)
|
||||
CloseHandle(chuni_io_slider_thread);
|
||||
chuni_io_slider_thread = NULL;
|
||||
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)
|
||||
{
|
||||
if (chuni_io_slider_led_port != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
led_output_update(2, rgb);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
- 0x0100: Initial API version (assumed if chuni_io_get_api_version is not
|
||||
exported)
|
||||
- 0x0101: Fix IR beam mappings
|
||||
- 0x0102: Add air tower led and billboard support
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
@ -145,3 +146,30 @@ void chuni_io_slider_stop(void);
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
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);
|
||||
|
@ -57,7 +57,24 @@ void chuni_io_config_load(
|
||||
filename);
|
||||
}
|
||||
|
||||
GetPrivateProfileStringW(L"slider", L"ledport", L"COM5", port_input, 6, filename);
|
||||
wcsncpy(cfg->led_com, L"\\\\.\\", 4);
|
||||
wcsncat_s(cfg->led_com, 11, port_input, 6);
|
||||
cfg->led_output_pipe = GetPrivateProfileIntW(L"led", L"cabLedOutputPipe", 1, filename);
|
||||
cfg->led_output_serial = GetPrivateProfileIntW(L"led", L"cabLedOutputSerial", 0, filename);
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -10,7 +10,18 @@ struct chuni_io_config {
|
||||
uint8_t vk_ir_emu;
|
||||
uint8_t vk_ir[6];
|
||||
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(
|
||||
|
22
chuniio/leddata.h
Normal file
22
chuniio/leddata.h
Normal 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
133
chuniio/ledoutput.c
Normal 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
19
chuniio/ledoutput.h
Normal 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);
|
@ -5,9 +5,18 @@ chuniio_lib = static_library(
|
||||
implicit_include_directories : false,
|
||||
c_pch : '../precompiled.h',
|
||||
sources : [
|
||||
'chu2to3.c',
|
||||
'chu2to3.h',
|
||||
'chuniio.c',
|
||||
'chuniio.h',
|
||||
'config.c',
|
||||
'config.h',
|
||||
'leddata.h',
|
||||
'ledoutput.c',
|
||||
'ledoutput.h',
|
||||
'pipeimpl.c',
|
||||
'pipeimpl.h',
|
||||
'serialimpl.c',
|
||||
'serialimpl.h'
|
||||
],
|
||||
)
|
||||
|
160
chuniio/pipeimpl.c
Normal file
160
chuniio/pipeimpl.c
Normal 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
14
chuniio/pipeimpl.h
Normal 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
99
chuniio/serialimpl.c
Normal 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
15
chuniio/serialimpl.h
Normal 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);
|
@ -3,6 +3,7 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "chuniio/chu2to3.h"
|
||||
#include "chusanhook/chuni-dll.h"
|
||||
|
||||
#include "util/dll-bind.h"
|
||||
@ -30,9 +31,59 @@ const struct dll_bind_sym chuni_dll_syms[] = {
|
||||
}, {
|
||||
.sym = "chuni_io_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;
|
||||
|
||||
// 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(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);
|
||||
|
||||
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);
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
if (src != self) {
|
||||
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;
|
||||
// Might still be ok depending on external dll API version
|
||||
int bind_count = sym - init_sym;
|
||||
if ( has_enough_symbols(chuni_dll.api_version, bind_count) == S_OK )
|
||||
{
|
||||
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);
|
||||
dprintf("imported %d symbols\n",bind_count);
|
||||
goto end;
|
||||
}
|
||||
} else {
|
||||
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
|
||||
}
|
||||
|
@ -13,10 +13,13 @@ struct chuni_dll {
|
||||
void (*slider_start)(chuni_io_slider_callback_t callback);
|
||||
void (*slider_stop)(void);
|
||||
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 {
|
||||
wchar_t path[MAX_PATH];
|
||||
uint8_t chu2to3;
|
||||
};
|
||||
|
||||
extern struct chuni_dll chuni_dll;
|
||||
|
@ -20,3 +20,15 @@ EXPORTS
|
||||
chuni_io_slider_set_leds
|
||||
chuni_io_slider_start
|
||||
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
|
||||
|
@ -39,16 +39,27 @@ void chuni_dll_config_load(
|
||||
|
||||
// Workaround for x64/x86 external IO dlls
|
||||
// path32 for 32bit, path64 for 64bit
|
||||
// for else.. is that possible? idk
|
||||
// path for 32bit only dlls (internal chu2to3 engine)
|
||||
|
||||
#if defined(ENV32BIT)
|
||||
GetPrivateProfileStringW(
|
||||
L"chuniio",
|
||||
L"path32",
|
||||
L"",
|
||||
cfg->path,
|
||||
_countof(cfg->path),
|
||||
filename);
|
||||
GetPrivateProfileStringW(
|
||||
L"chuniio",
|
||||
L"path",
|
||||
L"",
|
||||
cfg->path,
|
||||
_countof(cfg->path),
|
||||
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)
|
||||
GetPrivateProfileStringW(
|
||||
L"chuniio",
|
||||
@ -60,7 +71,7 @@ void chuni_dll_config_load(
|
||||
#else
|
||||
#error "Unknown environment"
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void slider_config_load(struct slider_config *cfg, const wchar_t *filename)
|
||||
|
@ -100,7 +100,7 @@ static DWORD CALLBACK chusan_pre_startup(void)
|
||||
}
|
||||
|
||||
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++) {
|
||||
switch (i) {
|
||||
@ -113,15 +113,15 @@ static DWORD CALLBACK chusan_pre_startup(void)
|
||||
break;
|
||||
|
||||
case 2:
|
||||
dprintf("DipSw: Cab Type: %s\n", is_sp ? "SP" : "CVT");
|
||||
dprintf("DipSw: Cab Type: %s\n", is_cvt ? "CVT" : "SP");
|
||||
|
||||
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);
|
||||
|
||||
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)) {
|
||||
goto fail;
|
||||
if (FAILED(hr)) {
|
||||
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)) {
|
||||
goto fail;
|
||||
|
@ -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);
|
71
dist/chuni/segatools.ini
vendored
71
dist/chuni/segatools.ini
vendored
@ -38,15 +38,52 @@ monitor=0
|
||||
; Leave empty if you want to use Segatools built-in keyboard input.
|
||||
path=
|
||||
|
||||
[led15093]
|
||||
; 837-15093-06 LED strip emulation setting.
|
||||
enable=1
|
||||
|
||||
[chuniio]
|
||||
; To use a custom Chunithm IO DLL enter its path here.
|
||||
; Leave empty if you want to use Segatools built-in keyboard input.
|
||||
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
|
||||
; -----------------------------------------------------------------------------
|
||||
@ -66,6 +103,20 @@ test=0x31
|
||||
service=0x32
|
||||
; Keyboard button to increment coin counter. Default is the 3 key.
|
||||
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
|
||||
; 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
|
||||
; custom high-precision touch strip controller if you have one.
|
||||
[slider]
|
||||
;cell32=0x53
|
||||
;cell31=0x53
|
||||
;cell30=0x53
|
||||
;cell1=0x53
|
||||
;cell2=0x53
|
||||
; ... etc ...
|
||||
|
||||
; Enable slider LED serial output. This follows OpeNITHM Serial LED Protocol.
|
||||
; eg. COM5
|
||||
;ledport=
|
||||
;cell31=0x53
|
||||
;cell32=0x53
|
||||
|
51
dist/chusan/segatools.ini
vendored
51
dist/chusan/segatools.ini
vendored
@ -63,16 +63,57 @@ framed=0
|
||||
; Select the monitor to run the game on. (Fullscreen only, 0 =primary screen)
|
||||
monitor=0
|
||||
|
||||
[led15093]
|
||||
; 837-15093-06 LED strip emulation setting.
|
||||
enable=1
|
||||
|
||||
[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.
|
||||
;path32=
|
||||
;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
|
||||
; -----------------------------------------------------------------------------
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "board/io4.h"
|
||||
#include "board/led15093.h"
|
||||
#include "board/sg-reader.h"
|
||||
#include "board/vfd.h"
|
||||
|
||||
@ -96,7 +97,8 @@ static DWORD CALLBACK fgo_pre_startup(void)
|
||||
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)) {
|
||||
goto fail;
|
||||
|
@ -24,6 +24,12 @@ const struct dll_bind_sym fgo_dll_syms[] = {
|
||||
}, {
|
||||
.sym = "fgo_io_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),
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -11,6 +11,8 @@ struct fgo_dll {
|
||||
void (*get_opbtns)(uint8_t *opbtn);
|
||||
void (*get_gamebtns)(uint8_t *gamebtn);
|
||||
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 {
|
||||
|
@ -17,6 +17,8 @@ EXPORTS
|
||||
fgo_io_get_opbtns
|
||||
fgo_io_init
|
||||
fgo_io_poll
|
||||
fgo_io_led_init
|
||||
fgo_io_led_set_leds
|
||||
fwdlusb_open
|
||||
fwdlusb_close
|
||||
fwdlusb_listupPrinter
|
||||
|
@ -139,3 +139,14 @@ void fgo_io_get_analogs(int16_t *stick_x, int16_t *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;
|
||||
}
|
@ -69,3 +69,18 @@ void fgo_io_get_gamebtns(uint8_t *btn);
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
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);
|
||||
|
@ -17,11 +17,13 @@ add_project_arguments(
|
||||
language: 'c',
|
||||
)
|
||||
|
||||
# Use get_argument_syntax() instead once Meson 0.49.0 releases
|
||||
if meson.get_compiler('c').get_id() != 'msvc'
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
if cc.get_id() != 'msvc'
|
||||
add_project_arguments(
|
||||
'-ffunction-sections',
|
||||
'-fdata-sections',
|
||||
'-flto', # Enable Link-Time Optimization
|
||||
language: 'c',
|
||||
)
|
||||
|
||||
@ -30,11 +32,12 @@ if meson.get_compiler('c').get_id() != 'msvc'
|
||||
'-Wl,--exclude-all-symbols',
|
||||
'-Wl,--gc-sections',
|
||||
'-static-libgcc',
|
||||
'-flto', # Enable Link-Time Optimization
|
||||
'-Wl,-s', # Strip debug symbols
|
||||
language: 'c',
|
||||
)
|
||||
endif
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
shlwapi_lib = cc.find_library('shlwapi')
|
||||
dinput8_lib = cc.find_library('dinput8')
|
||||
dxguid_lib = cc.find_library('dxguid')
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include "board/config.h"
|
||||
#include "board/led15093.h"
|
||||
// #include "board/led15093.h"
|
||||
|
||||
#include "gfxhook/gfx.h"
|
||||
|
||||
@ -19,7 +19,7 @@ struct mu3_hook_config {
|
||||
struct dvd_config dvd;
|
||||
struct io4_config io4;
|
||||
struct gfx_config gfx;
|
||||
struct led15093_config led15093;
|
||||
// struct led15093_config led15093;
|
||||
struct mu3_dll_config dll;
|
||||
};
|
||||
|
||||
|
@ -61,11 +61,14 @@ static DWORD CALLBACK mu3_pre_startup(void)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
// Does not work, Unity moment
|
||||
hr = led15093_hook_init(&mu3_hook_cfg.led15093, 3, 1, 1, 2);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
*/
|
||||
|
||||
hr = sg_reader_hook_init(&mu3_hook_cfg.aime, 1, 1, mu3_hook_mod);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user