Merge pull request 'Include chu2to3 engine' (#1) from CrazyRedMachine/segatools:chuniio into chuniio

Reviewed-on: Dniel97/segatools#1
This commit is contained in:
Dniel97 2023-12-25 14:02:33 +00:00
commit 16bbd87c73
11 changed files with 532 additions and 37 deletions

View File

@ -97,11 +97,16 @@ static DWORD CALLBACK chuni_pre_startup(void)
goto fail; goto fail;
} }
hr = led15093_hook_init(&chuni_hook_cfg.led15093, if ( chuni_dll.led_init == NULL || chuni_dll.led_set_leds == NULL )
chuni_dll.led_init, chuni_dll.led_set_leds, 10, 2, 2, 1); {
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);

357
chuniio/chu2to3.c Normal file
View File

@ -0,0 +1,357 @@
#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 (%d).\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 (%d).\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 (%d).\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 (%d).\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;
_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(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 %d)\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(wchar_t *dllname);

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

View File

@ -5,6 +5,8 @@ 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',

View File

@ -39,6 +39,50 @@ const struct dll_bind_sym chuni_dll_syms[] = {
} }
}; };
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.
@ -58,7 +102,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) {
@ -72,12 +121,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();
@ -97,17 +152,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

@ -19,6 +19,7 @@ struct chuni_dll {
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

@ -22,3 +22,13 @@ EXPORTS
chuni_io_slider_stop chuni_io_slider_stop
chuni_io_led_init chuni_io_led_init
chuni_io_led_set_colors 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

@ -129,11 +129,16 @@ static DWORD CALLBACK chusan_pre_startup(void)
} }
} }
hr = led15093_hook_init(&chusan_hook_cfg.led15093, if ( chuni_dll.led_init == NULL || chuni_dll.led_set_leds == NULL )
chuni_dll.led_init, chuni_dll.led_set_leds, first_port, 2, 2, 1); {
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_cvt ? 2: 3, chusan_hook_mod); hr = sg_reader_hook_init(&chusan_hook_cfg.aime, 4, is_cvt ? 2: 3, chusan_hook_mod);

View File

@ -64,7 +64,11 @@ framed=0
monitor=0 monitor=0
[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=