From f2b2615e47e9f83c6280fbb23baa275aeccf786c Mon Sep 17 00:00:00 2001 From: Luna Nightshade Date: Mon, 22 May 2023 22:24:41 +0100 Subject: [PATCH] Add project files. --- chuniio/chuniio.c | 141 +++++++++++++++++++++++++++++ chuniio/chuniio.h | 143 +++++++++++++++++++++++++++++ chuniio/chuniio.vcxproj | 153 ++++++++++++++++++++++++++++++++ chuniio/chuniio.vcxproj.filters | 36 ++++++++ chuniio/config.c | 43 +++++++++ chuniio/config.h | 16 ++++ chuniio/dllmain.cpp | 20 +++++ chuniio_air.sln | 31 +++++++ 8 files changed, 583 insertions(+) create mode 100644 chuniio/chuniio.c create mode 100644 chuniio/chuniio.h create mode 100644 chuniio/chuniio.vcxproj create mode 100644 chuniio/chuniio.vcxproj.filters create mode 100644 chuniio/config.c create mode 100644 chuniio/config.h create mode 100644 chuniio/dllmain.cpp create mode 100644 chuniio_air.sln diff --git a/chuniio/chuniio.c b/chuniio/chuniio.c new file mode 100644 index 0000000..8e07f56 --- /dev/null +++ b/chuniio/chuniio.c @@ -0,0 +1,141 @@ +#include + +#include +#include +#include + +#include "chuniio.h" +#include "config.h" + +static unsigned int __stdcall chuni_io_slider_thread_proc(void* ctx); + +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; +static struct chuni_io_config chuni_io_cfg; + +API uint16_t chuni_io_get_api_version(void) +{ + return 0x0101; +} + +API HRESULT chuni_io_jvs_init(void) +{ + chuni_io_config_load(&chuni_io_cfg, L".\\segatools.ini"); + + return S_OK; +} + +API void chuni_io_jvs_read_coin_counter(uint16_t* out) +{ + if (out == NULL) { + return; + } + + if (GetAsyncKeyState(chuni_io_cfg.vk_coin)) { + if (!chuni_io_coin) { + chuni_io_coin = true; + chuni_io_coins++; + } + } + else { + chuni_io_coin = false; + } + + *out = chuni_io_coins; +} + +API void chuni_io_jvs_poll(uint8_t* opbtn, uint8_t* beams) +{ + size_t i; + + if (GetAsyncKeyState(chuni_io_cfg.vk_test)) { + *opbtn |= 0x01; /* Test */ + } + + if (GetAsyncKeyState(chuni_io_cfg.vk_service)) { + *opbtn |= 0x02; /* Service */ + } + + if (GetAsyncKeyState(chuni_io_cfg.vk_ir)) { + 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); + } + } +} + +API HRESULT chuni_io_slider_init(void) +{ + return S_OK; +} + +API 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; +} + +API 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[32]; + size_t i; + + callback = ctx; + + while (!chuni_io_slider_stop_flag) { + for (i = 0; i < _countof(pressure); i++) { + if (GetAsyncKeyState(chuni_io_cfg.vk_cell[i]) & 0x8000) { + pressure[i] = 128; + } + else { + pressure[i] = 0; + } + } + + callback(pressure); + Sleep(1); + } + + return 0; +} diff --git a/chuniio/chuniio.h b/chuniio/chuniio.h new file mode 100644 index 0000000..7278342 --- /dev/null +++ b/chuniio/chuniio.h @@ -0,0 +1,143 @@ +#pragma once + +/* + CHUNITHM CUSTOM IO API + + Changelog: + + - 0x0100: Initial API version (assumed if chuni_io_get_api_version is not + exported) + - 0x0101: Fix IR beam mappings +*/ + +#include + +#include +#include + +#define API __declspec(dllexport) + +/* Get the version of the Chunithm IO API that this DLL supports. This + function should return a positive 16-bit integer, where the high byte is + the major version and the low byte is the minor version (as defined by the + Semantic Versioning standard). + + The latest API version as of this writing is 0x0101. */ + +API uint16_t chuni_io_get_api_version(void); + +/* Initialize JVS-based input. This function will be called before any other + chuni_io_jvs_*() function calls. Errors returned from this function will + manifest as a disconnected JVS bus. + + 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: 0x0100 */ + +API HRESULT chuni_io_jvs_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. + + NOTE: Previous releases of Segatools mapped the IR beam inputs incorrectly. + Please ensure that you advertise an API version of at least 0x0101 so that + the correct mapping can be used. + + Minimum API version: 0x0100 + Latest API version: 0x0101 */ + +API 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. + + Minimum API version: 0x0100 */ + +API void chuni_io_jvs_read_coin_counter(uint16_t* total); + + +/* Initialize touch slider emulation. This function will be called before any + other chuni_io_slider_*() 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: 0x0100 */ + +API HRESULT chuni_io_slider_init(void); + +/* 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. + + Minimum API version: 0x0100 */ + +API 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. + + Minimum API version: 0x0100 */ + +API 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. + + Minimum API version: 0x0100 */ + +API void chuni_io_slider_set_leds(const uint8_t* rgb); diff --git a/chuniio/chuniio.vcxproj b/chuniio/chuniio.vcxproj new file mode 100644 index 0000000..40d08ad --- /dev/null +++ b/chuniio/chuniio.vcxproj @@ -0,0 +1,153 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {2f7fffc6-df90-4a0e-a37a-314bd67af685} + chuniio + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;CHUNIIO_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + WIN32;NDEBUG;CHUNIIO_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + true + true + false + + + + + Level3 + true + _DEBUG;CHUNIIO_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + NDEBUG;CHUNIIO_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + true + true + false + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/chuniio/chuniio.vcxproj.filters b/chuniio/chuniio.vcxproj.filters new file mode 100644 index 0000000..8cda577 --- /dev/null +++ b/chuniio/chuniio.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/chuniio/config.c b/chuniio/config.c new file mode 100644 index 0000000..ed032b8 --- /dev/null +++ b/chuniio/config.c @@ -0,0 +1,43 @@ +#include + +#include +#include +#include + +#include "config.h" + +static const int chuni_io_default_cells[] = { + 'L', 'L', 'L', 'L', + 'K', 'K', 'K', 'K', + 'J', 'J', 'J', 'J', + 'H', 'H', 'H', 'H', + 'G', 'G', 'G', 'G', + 'F', 'F', 'F', 'F', + 'D', 'D', 'D', 'D', + 'S', 'S', 'S', 'S', +}; + +void chuni_io_config_load( + struct chuni_io_config* cfg, + const wchar_t* filename) +{ + wchar_t key[16]; + int i; + + assert(cfg != NULL); + assert(filename != NULL); + + cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", '1', filename); + cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", '2', filename); + cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", '3', filename); + cfg->vk_ir = GetPrivateProfileIntW(L"io3", L"ir", VK_SPACE, filename); + + for (i = 0; i < 32; i++) { + swprintf_s(key, _countof(key), L"cell%i", i + 1); + cfg->vk_cell[i] = GetPrivateProfileIntW( + L"slider", + key, + chuni_io_default_cells[i], + filename); + } +} diff --git a/chuniio/config.h b/chuniio/config.h new file mode 100644 index 0000000..1435dd6 --- /dev/null +++ b/chuniio/config.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +struct chuni_io_config { + uint8_t vk_test; + uint8_t vk_service; + uint8_t vk_coin; + uint8_t vk_ir; + uint8_t vk_cell[32]; +}; + +void chuni_io_config_load( + struct chuni_io_config* cfg, + const wchar_t* filename); diff --git a/chuniio/dllmain.cpp b/chuniio/dllmain.cpp new file mode 100644 index 0000000..7ef713d --- /dev/null +++ b/chuniio/dllmain.cpp @@ -0,0 +1,20 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +//#include "pch.h" +#include + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/chuniio_air.sln b/chuniio_air.sln new file mode 100644 index 0000000..eefc2ab --- /dev/null +++ b/chuniio_air.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33516.290 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "chuniio", "chuniio\chuniio.vcxproj", "{2F7FFFC6-DF90-4A0E-A37A-314BD67AF685}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2F7FFFC6-DF90-4A0E-A37A-314BD67AF685}.Debug|x64.ActiveCfg = Debug|x64 + {2F7FFFC6-DF90-4A0E-A37A-314BD67AF685}.Debug|x64.Build.0 = Debug|x64 + {2F7FFFC6-DF90-4A0E-A37A-314BD67AF685}.Debug|x86.ActiveCfg = Debug|Win32 + {2F7FFFC6-DF90-4A0E-A37A-314BD67AF685}.Debug|x86.Build.0 = Debug|Win32 + {2F7FFFC6-DF90-4A0E-A37A-314BD67AF685}.Release|x64.ActiveCfg = Release|x64 + {2F7FFFC6-DF90-4A0E-A37A-314BD67AF685}.Release|x64.Build.0 = Release|x64 + {2F7FFFC6-DF90-4A0E-A37A-314BD67AF685}.Release|x86.ActiveCfg = Release|Win32 + {2F7FFFC6-DF90-4A0E-A37A-314BD67AF685}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {235185B2-5840-4D82-B4ED-E75F1F6B1BC5} + EndGlobalSection +EndGlobal