chuniio: Break out Chunithm IO DLL

This commit is contained in:
Tau 2019-05-02 22:12:06 -04:00
parent 23257f272e
commit 46ab6c3d96
9 changed files with 321 additions and 111 deletions

View File

@ -12,6 +12,8 @@
#include "chunihook/jvs.h"
#include "chunihook/slider-hook.h"
#include "chuniio/chuniio.h"
#include "hook/process.h"
#include "hooklib/serial.h"
@ -73,6 +75,10 @@ static DWORD CALLBACK chuni_pre_startup(void)
dprintf("--- End chuni_pre_startup ---\n");
/* Initialize IO DLL */
chuni_io_init();
/* Jump to EXE start address */
return chuni_startup();

View File

@ -9,10 +9,17 @@
#include "board/io3.h"
#include "chuniio/chuniio.h"
#include "jvs/jvs-bus.h"
#include "util/dprintf.h"
struct chunithm_jvs_ir_mask {
uint16_t p1;
uint16_t p2;
};
static void chunithm_jvs_read_switches(void *ctx, struct io3_switch_state *out);
static uint16_t chunithm_jvs_read_coin_counter(void *ctx, uint8_t slot_no);
@ -21,10 +28,16 @@ static const struct io3_ops chunithm_jvs_io3_ops = {
.read_coin_counter = chunithm_jvs_read_coin_counter,
};
static const struct chunithm_jvs_ir_mask chunithm_jvs_ir_masks[] = {
{ 0x0000, 0x0020 },
{ 0x0020, 0x0000 },
{ 0x0000, 0x0010 },
{ 0x0010, 0x0000 },
{ 0x0000, 0x0008 },
{ 0x0008, 0x0000 },
};
static struct io3 chunithm_jvs_io3;
static size_t chunithm_jvs_rise_pos;
static bool chunithm_jvs_coin;
static uint16_t chunithm_jvs_coins;
void chunithm_jvs_init(void)
{
@ -34,45 +47,37 @@ void chunithm_jvs_init(void)
static void chunithm_jvs_read_switches(void *ctx, struct io3_switch_state *out)
{
uint8_t opbtn;
uint8_t beams;
size_t i;
assert(out != NULL);
/* Update simulated raise/lower state */
opbtn = 0;
beams = 0;
if (GetAsyncKeyState(VK_SPACE)) {
if (chunithm_jvs_rise_pos < 6) {
chunithm_jvs_rise_pos++;
}
} else {
if (chunithm_jvs_rise_pos > 0) {
chunithm_jvs_rise_pos--;
}
}
chuni_io_jvs_poll(&opbtn, &beams);
/* Render the state. Every case falls through, this is intentional. */
out->system = 0x00;
out->p1 = 0x0000;
out->p2 = 0x0000;
out->p1 = 0;
out->p2 = 0;
switch (chunithm_jvs_rise_pos) {
case 0: out->p2 |= 0x0020;
case 1: out->p1 |= 0x0020;
case 2: out->p2 |= 0x0010;
case 3: out->p1 |= 0x0010;
case 4: out->p2 |= 0x0008;
case 5: out->p1 |= 0x0008;
}
/* Update test/service buttons */
if (GetAsyncKeyState('1')) {
if (opbtn & 0x01) {
out->system = 0x80;
} else {
out->system = 0;
out->system = 0x00;
}
if (GetAsyncKeyState('2')) {
if (opbtn & 0x02) {
out->p1 |= 0x4000;
}
for (i = 0 ; i < 6 ; i++) {
if (beams & (1 << i)) {
out->p1 |= chunithm_jvs_ir_masks[i].p1;
out->p2 |= chunithm_jvs_ir_masks[i].p2;
}
}
}
static uint16_t chunithm_jvs_read_coin_counter(void *ctx, uint8_t slot_no)
@ -81,15 +86,5 @@ static uint16_t chunithm_jvs_read_coin_counter(void *ctx, uint8_t slot_no)
return 0;
}
if (GetAsyncKeyState('3')) {
if (!chunithm_jvs_coin) {
dprintf("Chunithm JVS: Coin drop\n");
chunithm_jvs_coin = true;
chunithm_jvs_coins++;
}
} else {
chunithm_jvs_coin = false;
}
return chunithm_jvs_coins;
return chuni_io_jvs_read_coin_counter();
}

View File

@ -11,6 +11,7 @@ shared_library(
link_with : [
amex_lib,
board_lib,
chuniio_dll,
jvs_lib,
platform_lib,
util_lib,

View File

@ -11,6 +11,8 @@
#include "chunihook/slider-hook.h"
#include "chuniio/chuniio.h"
#include "hook/iobuf.h"
#include "hook/iohook.h"
@ -29,16 +31,13 @@ static HRESULT slider_req_auto_scan_start(void);
static HRESULT slider_req_auto_scan_stop(void);
static HRESULT slider_req_set_led(const struct slider_req_set_led *req);
static unsigned int __stdcall slider_thread_proc(void *ctx);
static void slider_res_auto_scan(const uint8_t *state);
static CRITICAL_SECTION slider_lock;
static struct uart slider_uart;
static uint8_t slider_written_bytes[520];
static uint8_t slider_readable_bytes[520];
static HANDLE slider_thread;
static bool slider_stop;
void slider_hook_init(void)
{
InitializeCriticalSection(&slider_lock);
@ -173,25 +172,8 @@ static HRESULT slider_req_get_board_info(void)
static HRESULT slider_req_auto_scan_start(void)
{
dprintf("Chunithm slider: Start slider thread\n");
if (slider_thread != NULL) {
dprintf("Thread is already running\n");
return S_OK;
}
slider_thread = (HANDLE) _beginthreadex(
NULL,
0,
slider_thread_proc,
NULL,
0,
NULL);
if (slider_thread == NULL) {
dprintf("Thread launch failed\n");
}
dprintf("Chunithm slider: Start slider notifications\n");
chuni_io_slider_start(slider_res_auto_scan);
/* This message is not acknowledged */
@ -202,21 +184,16 @@ static HRESULT slider_req_auto_scan_stop(void)
{
struct slider_hdr resp;
dprintf("Chunithm slider: Stop slider thread\n");
dprintf("Chunithm slider: Stop slider notifications\n");
if (slider_thread != NULL) {
slider_stop = true;
LeaveCriticalSection(&slider_lock);
/* IO DLL worker thread might attempt to invoke the callback (which needs
to take slider_lock, which we are currently holding) before noticing that
it needs to shut down. Unlock here so that we don't deadlock in that
situation. */
WaitForSingleObject(slider_thread, INFINITE);
CloseHandle(slider_thread);
slider_thread = NULL;
slider_stop = false;
dprintf("Chunithm slider: Thread has terminated\n");
EnterCriticalSection(&slider_lock);
}
LeaveCriticalSection(&slider_lock);
chuni_io_slider_stop();
EnterCriticalSection(&slider_lock);
resp.sync = SLIDER_FRAME_SYNC;
resp.cmd = SLIDER_CMD_AUTO_SCAN_STOP;
@ -227,45 +204,23 @@ static HRESULT slider_req_auto_scan_stop(void)
static HRESULT slider_req_set_led(const struct slider_req_set_led *req)
{
chuni_io_slider_set_leds(req->payload.rgb);
/* This message is not acknowledged */
return S_OK;
}
static unsigned int WINAPI slider_thread_proc(void *ctx)
static void slider_res_auto_scan(const uint8_t *state)
{
static const int keys[] = {
'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L',
};
struct slider_resp_auto_scan resp;
uint8_t pressure;
bool stop;
size_t i;
for (;;) {
resp.hdr.sync = SLIDER_FRAME_SYNC;
resp.hdr.cmd = SLIDER_CMD_AUTO_SCAN;
resp.hdr.nbytes = sizeof(resp.pressure);
resp.hdr.sync = SLIDER_FRAME_SYNC;
resp.hdr.cmd = SLIDER_CMD_AUTO_SCAN;
resp.hdr.nbytes = sizeof(resp.pressure);
memcpy(resp.pressure, state, sizeof(resp.pressure));
for (i = 0 ; i < 8 ; i++) {
pressure = GetAsyncKeyState(keys[i]) ? 20 : 0;
memset(&resp.pressure[28 - 4 * i], pressure, 4);
}
EnterCriticalSection(&slider_lock);
stop = slider_stop;
if (!stop) {
slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp));
}
LeaveCriticalSection(&slider_lock);
if (stop) {
return 0;
}
Sleep(1);
}
EnterCriticalSection(&slider_lock);
slider_frame_encode(&slider_uart.readable, &resp, sizeof(resp));
LeaveCriticalSection(&slider_lock);
}

130
chuniio/chuniio.c Normal file
View File

@ -0,0 +1,130 @@
#include <windows.h>
#include <process.h>
#include <stdbool.h>
#include <stdint.h>
#include "chuniio/chuniio.h"
static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx);
static const int chuni_io_slider_keys[] = {
'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L',
};
static bool chuni_io_coin;
static uint16_t chuni_io_coins;
static uint8_t chuni_io_hand_pos;
static HANDLE chuni_io_slider_thread;
static bool chuni_io_slider_stop_flag;
HRESULT chuni_io_init(void)
{
return S_OK;
}
uint16_t chuni_io_jvs_read_coin_counter(void)
{
if (GetAsyncKeyState('3')) {
if (!chuni_io_coin) {
chuni_io_coin = true;
chuni_io_coins++;
}
} else {
chuni_io_coin = false;
}
return chuni_io_coins;
}
void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams)
{
size_t i;
if (GetAsyncKeyState('1')) {
*opbtn |= 0x01; /* Test */
}
if (GetAsyncKeyState('2')) {
*opbtn |= 0x02; /* Service */
}
if (GetAsyncKeyState(VK_SPACE)) {
if (chuni_io_hand_pos < 6) {
chuni_io_hand_pos++;
}
} else {
if (chuni_io_hand_pos > 0) {
chuni_io_hand_pos--;
}
}
for (i = 0 ; i < 6 ; i++) {
if (chuni_io_hand_pos > i) {
*beams |= (1 << i);
}
}
}
void chuni_io_jvs_set_coin_blocker(bool open)
{}
void chuni_io_slider_start(chuni_io_slider_callback_t callback)
{
if (chuni_io_slider_thread != NULL) {
return;
}
chuni_io_slider_thread = (HANDLE) _beginthreadex(
NULL,
0,
chuni_io_slider_thread_proc,
callback,
0,
NULL);
}
void chuni_io_slider_stop(void)
{
if (chuni_io_slider_thread == NULL) {
return;
}
chuni_io_slider_stop_flag = true;
WaitForSingleObject(chuni_io_slider_thread, INFINITE);
CloseHandle(chuni_io_slider_thread);
chuni_io_slider_thread = NULL;
chuni_io_slider_stop_flag = false;
}
void chuni_io_slider_set_leds(const uint8_t *rgb)
{
}
static unsigned int __stdcall chuni_io_slider_thread_proc(void *ctx)
{
chuni_io_slider_callback_t callback;
uint8_t pressure_val;
uint8_t pressure[32];
size_t i;
callback = ctx;
while (!chuni_io_slider_stop_flag) {
for (i = 0 ; i < 8 ; i++) {
if (GetAsyncKeyState(chuni_io_slider_keys[i]) & 0x8000) {
pressure_val = 20;
} else {
pressure_val = 0;
}
memset(&pressure[28 - 4 * i], pressure_val, 4);
}
callback(pressure);
Sleep(1);
}
return 0;
}

10
chuniio/chuniio.def Normal file
View File

@ -0,0 +1,10 @@
LIBRARY chuniio
EXPORTS
chuni_io_init
chuni_io_jvs_poll
chuni_io_jvs_read_coin_counter
chuni_io_jvs_set_coin_blocker
chuni_io_slider_set_leds
chuni_io_slider_start
chuni_io_slider_stop

99
chuniio/chuniio.h Normal file
View File

@ -0,0 +1,99 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
/* Initialize the Chunithm IO provider DLL. This is the first function to be
called on this DLL. Returning failure from this function will cause the
main application to immediately exit.
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 chuni_io_init(void);
/* Poll JVS input.
opbtn returns the cabinet test/service state, where bit 0 is Test and Bit 1
is Service.
beam returns the IR beams that are currently broken, where bit 0 is the
lowest IR beam and bit 5 is the highest IR beam, for a total of six beams.
Both bit masks are active-high.
Note that you cannot instantly break the entire IR grid in a single frame to
simulate hand movement; this will be judged as a miss. You need to simulate
a gradual raising and lowering of the hands. Consult the proof-of-concept
implementation for details. */
void chuni_io_jvs_poll(uint8_t *opbtn, uint8_t *beams);
/* Read the current state of the coin counter. This value should be incremented
for every coin detected by the coin acceptor mechanism. This count does not
need to persist beyond the lifetime of the process. */
uint16_t chuni_io_jvs_read_coin_counter(void);
/* Set the state of the coin blocker. Parameter is true if the blocker is
disengaged (i.e. coins can be inserted) and false if the blocker is engaged
(i.e. the coin slot should be physically blocked). */
void chuni_io_jvs_set_coin_blocker(bool open);
/* Chunithm touch slider layout:
^^^ Toward screen ^^^
----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
31 | 29 | 27 | 25 | 23 | 21 | 19 | 17 | 15 | 13 | 11 | 9 | 7 | 5 | 3 | 1 |
----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
32 | 30 | 28 | 26 | 24 | 22 | 20 | 18 | 16 | 14 | 12 | 10 | 8 | 6 | 4 | 2 |
----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
There are a total of 32 regions on the touch slider. Each region can return
an 8-bit pressure value. The operator menu allows the operator to adjust the
pressure level at which a region is considered to be pressed; the factory
default value for this setting is 20. */
/* Callback function supplied to your IO DLL. This must be called with a
pointer to a 32-byte array of pressure values, one byte per slider cell.
See above for layout and pressure threshold information.
The callback will copy the pressure state data out of your buffer before
returning. The pointer will not be retained. */
typedef void (*chuni_io_slider_callback_t)(const uint8_t *state);
/* Start polling the slider. Your DLL must start a polling thread and call the
supplied function periodically from that thread with new input state. The
update interval is up to you, but if your input device doesn't have any
preferred interval then 1 kHz is a reasonable maximum frequency.
Note that you do have to have to call the callback "occasionally" even if
nothing is changing, otherwise the game will raise a comm timeout error. */
void chuni_io_slider_start(chuni_io_slider_callback_t callback);
/* Stop polling the slider. You must cease to invoke the input callback before
returning from this function.
This *will* be called in the course of regular operation. For example,
every time you go into the operator menu the slider and all of the other I/O
on the cabinet gets restarted.
Following on from the above, the slider polling loop *will* be restarted
after being stopped in the course of regular operation. Do not permanently
tear down your input driver in response to this function call. */
void chuni_io_slider_stop(void);
/* Update the RGB lighting on the slider. A pointer to an array of 32 * 3 = 96
bytes is supplied. The illuminated areas on the touch slider are some
combination of rectangular regions and dividing lines between these regions
but the exact mapping of this lighting control buffer is still TBD. */
void chuni_io_slider_set_leds(const uint8_t *rgb);

12
chuniio/meson.build Normal file
View File

@ -0,0 +1,12 @@
chuniio_dll = shared_library(
'chuniio',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'chuniio.def',
c_pch : '../precompiled.h',
sources : [
'chuniio.c',
'chuniio.h',
],
)

View File

@ -37,6 +37,8 @@ subdir('platform')
subdir('util')
subdir('aimeio')
subdir('chuniio')
subdir('cardhook')
subdir('chunihook')
subdir('divahook')