forked from Dniel97/segatools
Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
c827b4c212 | |||
|
26624f25b1 | ||
|
824bc9abda | ||
|
cc5b87b559 | ||
|
e6794807a6 | ||
|
54cbbffae9 | ||
ac0f9f0587 | |||
0061158188 | |||
c535f18e40 | |||
c91c7db3c7 | |||
6a4cae1165 | |||
383039e16e | |||
37c26ecadb | |||
686d57d3ee | |||
b9204d4765 | |||
5abc593b46 | |||
fe14630b3d | |||
92fe2751e7 | |||
8c839b0d4e |
17
Package.mk
17
Package.mk
@ -203,6 +203,22 @@ $(BUILD_DIR_ZIP)/cm.zip:
|
||||
$(V)strip $(BUILD_DIR_ZIP)/cm/*.{exe,dll}
|
||||
$(V)cd $(BUILD_DIR_ZIP)/cm ; zip -r ../cm.zip *
|
||||
|
||||
$(BUILD_DIR_ZIP)/tokyo.zip:
|
||||
$(V)echo ... $@
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/tokyo
|
||||
$(V)mkdir -p $(BUILD_DIR_ZIP)/tokyo/DEVICE
|
||||
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
|
||||
$(BUILD_DIR_64)/tokyohook/tokyohook.dll \
|
||||
$(DIST_DIR)/tokyo/config_hook.json \
|
||||
$(DIST_DIR)/tokyo/segatools.ini \
|
||||
$(DIST_DIR)/tokyo/start.bat \
|
||||
$(BUILD_DIR_ZIP)/tokyo
|
||||
$(V)cp pki/billing.pub \
|
||||
pki/ca.crt \
|
||||
$(BUILD_DIR_ZIP)/tokyo/DEVICE
|
||||
$(V)strip $(BUILD_DIR_ZIP)/tokyo/*.{exe,dll}
|
||||
$(V)cd $(BUILD_DIR_ZIP)/tokyo ; zip -r ../tokyo.zip *
|
||||
|
||||
$(BUILD_DIR_ZIP)/doc.zip: \
|
||||
$(DOC_DIR)/config \
|
||||
$(DOC_DIR)/chunihook.md \
|
||||
@ -225,6 +241,7 @@ $(BUILD_DIR_ZIP)/segatools.zip: \
|
||||
$(BUILD_DIR_ZIP)/mu3.zip \
|
||||
$(BUILD_DIR_ZIP)/mai2.zip \
|
||||
$(BUILD_DIR_ZIP)/cm.zip \
|
||||
$(BUILD_DIR_ZIP)/tokyo.zip \
|
||||
$(BUILD_DIR_ZIP)/fgo.zip \
|
||||
CHANGELOG.md \
|
||||
README.md \
|
||||
|
24
README.md
24
README.md
@ -1,31 +1,33 @@
|
||||
# Segatools
|
||||
|
||||
Version: `2024-03-13`
|
||||
Version: `2024-08-20`
|
||||
|
||||
Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platforms.
|
||||
|
||||
## List of supported games
|
||||
|
||||
* Card Maker
|
||||
* starting from Card Maker
|
||||
* CHUNITHM
|
||||
* up to [CHUNITHM PARADISE LOST](doc/chunihook.md)
|
||||
* starting from CHUNITHM NEW!!
|
||||
* crossbeats REV.
|
||||
* up to crossbeats REV. SUNRISE
|
||||
* Fate/Grand Order
|
||||
* Fate/Grand Order Arcade
|
||||
* Hatsune Miku: Project DIVA Arcade
|
||||
* up to Future Tone
|
||||
* Initial D
|
||||
* [Initial D Arcade Stage Zero](doc/idzhook.md)
|
||||
* Initial D THE ARCADE
|
||||
* Hatsune Miku: Project DIVA Arcade
|
||||
* up to Future Tone
|
||||
* SEGA World Drivers Championship
|
||||
* SEGA World Drivers Championship 2019
|
||||
* Fate/Grand Order
|
||||
* Fate/Grand Order Arcade
|
||||
* O.N.G.E.K.I.
|
||||
* starting from O.N.G.E.K.I.
|
||||
* maimai DX
|
||||
* starting from maimai DX
|
||||
* Card Maker
|
||||
* starting from Card Maker
|
||||
* Mario & Sonic
|
||||
* Mario & Sonic at the Tokyo 2020 Olympics Arcade
|
||||
* O.N.G.E.K.I.
|
||||
* starting from O.N.G.E.K.I.
|
||||
* SEGA World Drivers Championship
|
||||
* SEGA World Drivers Championship 2019
|
||||
* WACCA
|
||||
* starting from WACCA
|
||||
|
||||
|
@ -90,4 +90,6 @@ void vfd_config_load(struct vfd_config *cfg, const wchar_t *filename)
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"vfd", L"enable", 1, filename);
|
||||
cfg->port = GetPrivateProfileIntW(L"vfd", L"portNo", 0, filename);
|
||||
cfg->utf_conversion = GetPrivateProfileIntW(L"vfd", L"utfConversion", 0, filename);
|
||||
}
|
||||
|
@ -47,5 +47,8 @@ board_lib = static_library(
|
||||
'slider-frame.h',
|
||||
'vfd.c',
|
||||
'vfd.h',
|
||||
'vfd-cmd.h',
|
||||
'vfd-frame.c',
|
||||
'vfd-frame.h',
|
||||
],
|
||||
)
|
||||
|
@ -5,19 +5,21 @@
|
||||
#pragma pack(push, 1)
|
||||
|
||||
enum {
|
||||
SG_NFC_CMD_GET_FW_VERSION = 0x30,
|
||||
SG_NFC_CMD_GET_HW_VERSION = 0x32,
|
||||
SG_NFC_CMD_RADIO_ON = 0x40,
|
||||
SG_NFC_CMD_RADIO_OFF = 0x41,
|
||||
SG_NFC_CMD_POLL = 0x42,
|
||||
SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x50,
|
||||
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x54,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE = 0x55, /* guess based on time sent */
|
||||
SG_NFC_CMD_SEND_HEX_DATA = 0x61,
|
||||
SG_NFC_CMD_RESET = 0x62,
|
||||
SG_NFC_CMD_FELICA_ENCAP = 0x71,
|
||||
SG_NFC_CMD_GET_FW_VERSION = 0x30,
|
||||
SG_NFC_CMD_GET_HW_VERSION = 0x32,
|
||||
SG_NFC_CMD_RADIO_ON = 0x40,
|
||||
SG_NFC_CMD_RADIO_OFF = 0x41,
|
||||
SG_NFC_CMD_POLL = 0x42,
|
||||
SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_A = 0x50,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE_A = 0x51,
|
||||
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_B = 0x54,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE_B = 0x55,
|
||||
SG_NFC_CMD_TO_UPDATE_MODE = 0x60,
|
||||
SG_NFC_CMD_SEND_HEX_DATA = 0x61,
|
||||
SG_NFC_CMD_RESET = 0x62,
|
||||
SG_NFC_CMD_FELICA_ENCAP = 0x71,
|
||||
};
|
||||
|
||||
struct sg_nfc_res_get_fw_version {
|
||||
@ -32,7 +34,7 @@ struct sg_nfc_res_get_hw_version {
|
||||
|
||||
struct sg_nfc_req_mifare_set_key {
|
||||
struct sg_req_header req;
|
||||
uint8_t key_a[6];
|
||||
uint8_t key[6];
|
||||
};
|
||||
|
||||
struct sg_nfc_req_mifare_50 {
|
||||
|
@ -60,6 +60,11 @@ static HRESULT sg_nfc_cmd_felica_encap(
|
||||
const struct sg_nfc_req_felica_encap *req,
|
||||
struct sg_nfc_res_felica_encap *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_send_hex_data(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_dummy(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
@ -185,12 +190,15 @@ static HRESULT sg_nfc_dispatch(
|
||||
&res->felica_encap);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_AUTHENTICATE:
|
||||
case SG_NFC_CMD_SEND_HEX_DATA:
|
||||
return sg_nfc_cmd_send_hex_data(nfc, &req->simple, &res->simple);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_SELECT_TAG:
|
||||
case SG_NFC_CMD_MIFARE_SET_KEY_AIME:
|
||||
case SG_NFC_CMD_MIFARE_SET_KEY_BANA:
|
||||
case SG_NFC_CMD_RADIO_ON:
|
||||
case SG_NFC_CMD_RADIO_OFF:
|
||||
case SG_NFC_CMD_SEND_HEX_DATA: // TODO: implement?
|
||||
case SG_NFC_CMD_TO_UPDATE_MODE:
|
||||
return sg_nfc_cmd_dummy(nfc, &req->simple, &res->simple);
|
||||
|
||||
default:
|
||||
@ -442,6 +450,22 @@ static HRESULT sg_nfc_cmd_felica_encap(
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_send_hex_data(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res)
|
||||
{
|
||||
sg_res_init(res, req, 0);
|
||||
|
||||
/* Firmware checksum length? */
|
||||
if (req->payload_len == 0x2b) {
|
||||
/* The firmware is identical flag? */
|
||||
res->status = 0x20;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_dummy(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
|
123
board/vfd-cmd.h
Normal file
123
board/vfd-cmd.h
Normal file
@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
|
||||
#include "board/vfd-frame.h"
|
||||
|
||||
enum {
|
||||
VFD_CMD_GET_VERSION = 0x5B,
|
||||
VFD_CMD_RESET = 0x0B,
|
||||
VFD_CMD_CLEAR_SCREEN = 0x0C,
|
||||
VFD_CMD_SET_BRIGHTNESS = 0x20,
|
||||
VFD_CMD_SET_SCREEN_ON = 0x21,
|
||||
VFD_CMD_SET_H_SCROLL = 0x22,
|
||||
VFD_CMD_DRAW_IMAGE = 0x2E,
|
||||
VFD_CMD_SET_CURSOR = 0x30,
|
||||
VFD_CMD_SET_ENCODING = 0x32,
|
||||
VFD_CMD_SET_TEXT_WND = 0x40,
|
||||
VFD_CMD_SET_TEXT_SPEED = 0x41,
|
||||
VFD_CMD_WRITE_TEXT = 0x50,
|
||||
VFD_CMD_ENABLE_SCROLL = 0x51,
|
||||
VFD_CMD_DISABLE_SCROLL = 0x52,
|
||||
VFD_CMD_ROTATE = 0x5D,
|
||||
VFD_CMD_CREATE_CHAR = 0xA3,
|
||||
VFD_CMD_CREATE_CHAR2 = 0xA4,
|
||||
};
|
||||
|
||||
enum {
|
||||
VFD_ENC_GB2312 = 0,
|
||||
VFD_ENC_BIG5 = 1,
|
||||
VFD_ENC_SHIFT_JIS = 2,
|
||||
VFD_ENC_KSC5601 = 3,
|
||||
VFD_ENC_MAX = 3,
|
||||
};
|
||||
|
||||
struct vfd_req_hdr {
|
||||
uint8_t sync;
|
||||
uint8_t cmd;
|
||||
};
|
||||
|
||||
struct vfd_req_any {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t payload[2054];
|
||||
};
|
||||
|
||||
struct vfd_req_board_info {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t unk1;
|
||||
};
|
||||
|
||||
struct vfd_resp_board_info { // \x0201.20\x03
|
||||
uint8_t unk1;
|
||||
char version[5];
|
||||
uint8_t unk2;
|
||||
};
|
||||
|
||||
struct vfd_req_reset {
|
||||
struct vfd_req_hdr hdr;
|
||||
};
|
||||
|
||||
struct vfd_req_cls {
|
||||
struct vfd_req_hdr hdr;
|
||||
};
|
||||
|
||||
struct vfd_req_brightness {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t brightness;
|
||||
};
|
||||
|
||||
struct vfd_req_power {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t power_state;
|
||||
};
|
||||
|
||||
struct vfd_req_hscroll {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t x_pos;
|
||||
};
|
||||
|
||||
struct vfd_req_draw {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint16_t x0;
|
||||
uint8_t y0;
|
||||
uint16_t x1;
|
||||
uint8_t y1;
|
||||
uint8_t image[2048];
|
||||
};
|
||||
|
||||
struct vfd_req_cursor {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint16_t x;
|
||||
uint8_t y;
|
||||
};
|
||||
|
||||
struct vfd_req_encoding {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t encoding;
|
||||
};
|
||||
|
||||
struct vfd_req_wnd {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint16_t x0;
|
||||
uint8_t y0;
|
||||
uint16_t x1;
|
||||
uint8_t y1;
|
||||
};
|
||||
|
||||
struct vfd_req_speed {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t encoding;
|
||||
};
|
||||
|
||||
struct vfd_req_scroll {
|
||||
struct vfd_req_hdr hdr;
|
||||
};
|
||||
|
||||
struct vfd_req_rotate {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t unk1;
|
||||
};
|
||||
|
||||
struct vfd_req_create_char {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t type;
|
||||
uint8_t pixels[32];
|
||||
};
|
88
board/vfd-frame.c
Normal file
88
board/vfd-frame.c
Normal file
@ -0,0 +1,88 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define SUPER_VERBOSE 1
|
||||
|
||||
#include "board/vfd-frame.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static HRESULT vfd_frame_encode_byte(struct iobuf *dest, uint8_t byte);
|
||||
|
||||
/* Frame structure:
|
||||
|
||||
REQUEST:
|
||||
[0] Sync byte (0x1A or 0x1B)
|
||||
[1] Packet ID
|
||||
[2...n-1] Data/payload
|
||||
|
||||
--- OR ---
|
||||
|
||||
if no sync byte is given, plain static text in the currently configured encoding is expected.
|
||||
|
||||
RESPONSE:
|
||||
This thing never responds, unless it's VFD_CMD_GET_VERSION
|
||||
*/
|
||||
|
||||
bool vfd_frame_sync(struct const_iobuf *src) {
|
||||
return src->bytes[src->pos] == VFD_SYNC_BYTE || src->bytes[src->pos] == VFD_SYNC_BYTE2;
|
||||
}
|
||||
|
||||
HRESULT vfd_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes) {
|
||||
const uint8_t *src;
|
||||
uint8_t byte;
|
||||
size_t i;
|
||||
HRESULT hr;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(dest->bytes != NULL || dest->nbytes == 0);
|
||||
assert(dest->pos <= dest->nbytes);
|
||||
assert(ptr != NULL);
|
||||
|
||||
src = ptr;
|
||||
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("VFD: RX Buffer:\n");
|
||||
#endif
|
||||
|
||||
for (i = 1; i < nbytes; i++) {
|
||||
byte = src[i];
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("%02x ", byte);
|
||||
#endif
|
||||
|
||||
hr = vfd_frame_encode_byte(dest, byte);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("\n");
|
||||
#endif
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT vfd_frame_encode_byte(struct iobuf *dest, uint8_t byte) {
|
||||
if (dest->pos + 1 > dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
|
||||
return S_OK;
|
||||
}
|
20
board/vfd-frame.h
Normal file
20
board/vfd-frame.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
enum {
|
||||
VFD_SYNC_BYTE = 0x1B,
|
||||
VFD_SYNC_BYTE2 = 0x1A,
|
||||
};
|
||||
|
||||
bool vfd_frame_sync(struct const_iobuf *src);
|
||||
|
||||
HRESULT vfd_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes);
|
400
board/vfd.c
400
board/vfd.c
@ -2,17 +2,16 @@
|
||||
directly by amdaemon, and it has something to do with displaying the status
|
||||
of electronic payments.
|
||||
|
||||
Part number in schematics is "VFD GP1232A02A FUTABA".
|
||||
|
||||
Little else about this board is known. Black-holing the RS232 comms that it
|
||||
receives seems to be sufficient for the time being. */
|
||||
Part number in schematics is "VFD GP1232A02A FUTABA". */
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "board/config.h"
|
||||
#include "board/vfd.h"
|
||||
#include "board/vfd-cmd.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
@ -21,33 +20,101 @@
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
#define SUPER_VERBOSE 0
|
||||
|
||||
static HRESULT vfd_handle_irp(struct irp *irp);
|
||||
|
||||
static struct uart vfd_uart;
|
||||
static uint8_t vfd_written[512];
|
||||
static uint8_t vfd_readable[512];
|
||||
UINT codepage;
|
||||
static uint8_t vfd_written[4096];
|
||||
static uint8_t vfd_readable[4096];
|
||||
|
||||
HRESULT vfd_hook_init(const struct vfd_config *cfg, unsigned int port_no)
|
||||
static int encoding = VFD_ENC_SHIFT_JIS;
|
||||
|
||||
HRESULT vfd_handle_get_version(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_reset(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_clear_screen(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_brightness(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_screen_on(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_h_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_draw_image(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_cursor(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_encoding(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_text_wnd(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_text_speed(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_write_text(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_enable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_disable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_rotate(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_create_char(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_create_char2(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
|
||||
static bool utf_enabled;
|
||||
|
||||
HRESULT vfd_hook_init(struct vfd_config *cfg, int default_port)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
if (!cfg->enable){
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
uart_init(&vfd_uart, port_no);
|
||||
utf_enabled = cfg->utf_conversion;
|
||||
|
||||
int port = cfg->port;
|
||||
if (port == 0){
|
||||
port = default_port;
|
||||
}
|
||||
|
||||
dprintf("VFD: enabling (port=%d)\n", port);
|
||||
uart_init(&vfd_uart, port);
|
||||
vfd_uart.written.bytes = vfd_written;
|
||||
vfd_uart.written.nbytes = sizeof(vfd_written);
|
||||
vfd_uart.readable.bytes = vfd_readable;
|
||||
vfd_uart.readable.nbytes = sizeof(vfd_readable);
|
||||
|
||||
codepage = GetACP();
|
||||
dprintf("VFD: hook enabled.\n");
|
||||
|
||||
return iohook_push_handler(vfd_handle_irp);
|
||||
}
|
||||
|
||||
|
||||
const char* get_encoding_name(int b){
|
||||
switch (b){
|
||||
case 0: return "gb2312";
|
||||
case 1: return "big5";
|
||||
case 2: return "shift-jis";
|
||||
case 3: return "ks_c_5601-1987";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void print_vfd_text(const char* str, int len){
|
||||
|
||||
if (utf_enabled){
|
||||
|
||||
wchar_t encoded[1024];
|
||||
memset(encoded, 0, 1024 * sizeof(wchar_t));
|
||||
|
||||
int codepage = 0;
|
||||
if (encoding == VFD_ENC_GB2312){
|
||||
codepage = 936;
|
||||
} else if (encoding == VFD_ENC_BIG5){
|
||||
codepage = 950;
|
||||
} else if (encoding == VFD_ENC_SHIFT_JIS){
|
||||
codepage = 932;
|
||||
} else if (encoding == VFD_ENC_KSC5601) {
|
||||
codepage = 949;
|
||||
}
|
||||
|
||||
if (!MultiByteToWideChar(codepage, MB_USEGLYPHCHARS, str, len, encoded, 1024)){
|
||||
dprintf("VFD: Text conversion failed: %ld", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf("VFD: Text: %ls\n", encoded);
|
||||
} else {
|
||||
|
||||
dprintf("VFD: Text: %s\n", str);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT vfd_handle_irp(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
@ -58,67 +125,274 @@ static HRESULT vfd_handle_irp(struct irp *irp)
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
if (irp->op == IRP_OP_OPEN){
|
||||
dprintf("VFD: Open\n");
|
||||
} else if (irp->op == IRP_OP_CLOSE){
|
||||
dprintf("VFD: Close\n");
|
||||
}
|
||||
|
||||
hr = uart_handle_irp(&vfd_uart, irp);
|
||||
|
||||
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
uint8_t cmd = 0;
|
||||
uint8_t str_1[512];
|
||||
uint8_t str_2[512];
|
||||
uint8_t str_1_len = 0;
|
||||
uint8_t str_2_len = 0;
|
||||
for (size_t i = 0; i < vfd_uart.written.pos; i++) {
|
||||
if (vfd_uart.written.bytes[i] == 0x1B) {
|
||||
i++;
|
||||
cmd = vfd_uart.written.bytes[i];
|
||||
if (cmd == 0x30) {
|
||||
i += 3;
|
||||
}
|
||||
else if (cmd == 0x50) {
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (cmd == 0x30) {
|
||||
str_1[str_1_len++] = vfd_uart.written.bytes[i];
|
||||
}
|
||||
else if (cmd == 0x50) {
|
||||
str_2[str_2_len++] = vfd_uart.written.bytes[i];
|
||||
}
|
||||
}
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("VFD TX:\n");
|
||||
dump_iobuf(&vfd_uart.written);
|
||||
#endif
|
||||
|
||||
if (str_1_len) {
|
||||
str_1[str_1_len++] = '\0';
|
||||
if (codepage != 932) {
|
||||
WCHAR buffer[512];
|
||||
MultiByteToWideChar(932, 0, (LPCSTR)str_1, str_1_len, buffer, str_1_len);
|
||||
char str_recode[str_1_len * 3];
|
||||
WideCharToMultiByte(codepage, 0, buffer, str_1_len, str_recode, str_1_len * 3, NULL, NULL);
|
||||
dprintf("VFD: %s\n", str_recode);
|
||||
}
|
||||
else {
|
||||
dprintf("VFD: %s\n", str_1);
|
||||
}
|
||||
}
|
||||
struct const_iobuf reader;
|
||||
iobuf_flip(&reader, &vfd_uart.written);
|
||||
|
||||
if (str_2_len) {
|
||||
str_2[str_2_len++] = '\0';
|
||||
if (codepage != 932) {
|
||||
WCHAR buffer[512];
|
||||
MultiByteToWideChar(932, 0, (LPCSTR)str_2, str_2_len, buffer, str_2_len);
|
||||
char str_recode[str_2_len * 3];
|
||||
WideCharToMultiByte(codepage, 0, buffer, str_2_len, str_recode, str_2_len * 3, NULL, NULL);
|
||||
dprintf("VFD: %s\n", str_recode);
|
||||
struct iobuf* writer = &vfd_uart.readable;
|
||||
for (; reader.pos < reader.nbytes ; ){
|
||||
|
||||
if (vfd_frame_sync(&reader)) {
|
||||
|
||||
reader.pos++; // get the sync byte out of the way
|
||||
|
||||
uint8_t cmd;
|
||||
iobuf_read_8(&reader, &cmd);
|
||||
|
||||
if (cmd == VFD_CMD_GET_VERSION) {
|
||||
hr = vfd_handle_get_version(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_RESET) {
|
||||
hr = vfd_handle_reset(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_CLEAR_SCREEN) {
|
||||
hr = vfd_handle_clear_screen(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_BRIGHTNESS) {
|
||||
hr = vfd_handle_set_brightness(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_SCREEN_ON) {
|
||||
hr = vfd_handle_set_screen_on(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_H_SCROLL) {
|
||||
hr = vfd_handle_set_h_scroll(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_DRAW_IMAGE) {
|
||||
hr = vfd_handle_draw_image(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_CURSOR) {
|
||||
hr = vfd_handle_set_cursor(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_ENCODING) {
|
||||
hr = vfd_handle_set_encoding(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_TEXT_WND) {
|
||||
hr = vfd_handle_set_text_wnd(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_TEXT_SPEED) {
|
||||
hr = vfd_handle_set_text_speed(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_WRITE_TEXT) {
|
||||
hr = vfd_handle_write_text(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_ENABLE_SCROLL) {
|
||||
hr = vfd_handle_enable_scroll(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_DISABLE_SCROLL) {
|
||||
hr = vfd_handle_disable_scroll(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_ROTATE) {
|
||||
hr = vfd_handle_rotate(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_CREATE_CHAR) {
|
||||
hr = vfd_handle_create_char(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_CREATE_CHAR2) {
|
||||
hr = vfd_handle_create_char2(&reader, writer, &vfd_uart);
|
||||
} else {
|
||||
dprintf("VFD: Unknown command 0x%x\n", cmd);
|
||||
dump_const_iobuf(&reader);
|
||||
hr = S_FALSE;
|
||||
}
|
||||
} else {
|
||||
dprintf("VFD: %s\n", str_2);
|
||||
|
||||
// if no sync byte is sent, we are just getting plain text...
|
||||
|
||||
if (reader.pos < reader.nbytes){
|
||||
int len = 0;
|
||||
|
||||
// read chars until we hit a new sync byte or the data ends
|
||||
while (reader.pos + len + 1 < reader.nbytes && reader.bytes[reader.pos + len] != VFD_SYNC_BYTE && reader.bytes[reader.pos + len] != VFD_SYNC_BYTE2){
|
||||
len++;
|
||||
}
|
||||
|
||||
char* str = malloc(len);
|
||||
memset(str, 0, len);
|
||||
iobuf_read(&reader, str, len);
|
||||
print_vfd_text(str, len);
|
||||
free(str);
|
||||
|
||||
reader.pos += len;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(hr)){
|
||||
return hr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// dprintf("VFD TX:\n");
|
||||
// dump_iobuf(&vfd_uart.written);
|
||||
vfd_uart.written.pos = 0;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT vfd_handle_get_version(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Get Version\n");
|
||||
|
||||
struct vfd_resp_board_info resp;
|
||||
|
||||
memset(&resp, 0, sizeof(resp));
|
||||
resp.unk1 = 2;
|
||||
strcpy(resp.version, "01.20");
|
||||
resp.unk2 = 1;
|
||||
|
||||
return vfd_frame_encode(writer, &resp, sizeof(resp));
|
||||
}
|
||||
HRESULT vfd_handle_reset(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Reset\n");
|
||||
|
||||
encoding = VFD_ENC_SHIFT_JIS;
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_clear_screen(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Clear Screen\n");
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_brightness(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
if (b > 4){
|
||||
dprintf("VFD: Brightness, invalid argument\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
dprintf("VFD: Brightness, %d\n", b);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_screen_on(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
if (b > 1){
|
||||
dprintf("VFD: Screen Power, invalid argument\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
dprintf("VFD: Screen Power, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_h_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t x;
|
||||
iobuf_read_8(reader, &x);
|
||||
|
||||
dprintf("VFD: Horizontal Scroll, X=%d\n", x);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_draw_image(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
int w, h;
|
||||
uint16_t x0, x1;
|
||||
uint8_t y0, y1;
|
||||
uint8_t image[2048];
|
||||
|
||||
iobuf_read_be16(reader, &x0);
|
||||
iobuf_read_8(reader, &y0);
|
||||
iobuf_read_be16(reader, &x1);
|
||||
iobuf_read_8(reader, &y1);
|
||||
w = x1 - x0;
|
||||
h = y1 - y0;
|
||||
iobuf_read(reader, image, w*h);
|
||||
|
||||
dprintf("VFD: Draw image, %dx%d\n", w, h);
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT vfd_handle_set_cursor(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint16_t x;
|
||||
uint8_t y;
|
||||
|
||||
iobuf_read_be16(reader, &x);
|
||||
iobuf_read_8(reader, &y);
|
||||
|
||||
dprintf("VFD: Set Cursor, x=%d,y=%d\n", x, y);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT vfd_handle_set_encoding(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
dprintf("VFD: Set Encoding, %d (%s)\n", b, get_encoding_name(b));
|
||||
|
||||
if (b < 0 || b > VFD_ENC_MAX){
|
||||
dprintf("Invalid encoding specified\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
encoding = b;
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_text_wnd(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint16_t x0, x1;
|
||||
uint8_t y0, y1;
|
||||
|
||||
iobuf_read_be16(reader, &x0);
|
||||
iobuf_read_8(reader, &y0);
|
||||
iobuf_read_be16(reader, &x1);
|
||||
iobuf_read_8(reader, &y1);
|
||||
|
||||
dprintf("VFD: Set Text Window, p0:%d,%d, p1:%d,%d\n", x0, y0, x1, y1);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_text_speed(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
dprintf("VFD: Set Text Speed, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_write_text(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t len;
|
||||
iobuf_read_8(reader, &len);
|
||||
|
||||
char* str = malloc(len);
|
||||
iobuf_read(reader, str, len);
|
||||
|
||||
print_vfd_text(str, len);
|
||||
free(str);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_enable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Enable Scrolling\n");
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_disable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Disable Scrolling\n");
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_rotate(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
dprintf("VFD: Rotate, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_create_char(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
char buf[32];
|
||||
|
||||
iobuf_read(reader, buf, 32);
|
||||
|
||||
dprintf("VFD: Create character, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_create_char2(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b, b2;
|
||||
iobuf_read_8(reader, &b);
|
||||
iobuf_read_8(reader, &b2);
|
||||
char buf[16];
|
||||
|
||||
iobuf_read(reader, buf, 16);
|
||||
|
||||
dprintf("VFD: Create character, %d, %d\n", b, b2);
|
||||
return S_FALSE;
|
||||
}
|
||||
|
@ -4,7 +4,10 @@
|
||||
|
||||
struct vfd_config {
|
||||
bool enable;
|
||||
int port;
|
||||
bool utf_conversion;
|
||||
};
|
||||
|
||||
|
||||
HRESULT vfd_hook_init(const struct vfd_config *cfg, unsigned int port_no);
|
||||
HRESULT vfd_hook_init(struct vfd_config *cfg, int default_port);
|
||||
|
||||
|
@ -139,9 +139,10 @@ void chuni_io_slider_start(chuni_io_slider_callback_t callback);
|
||||
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.
|
||||
bytes is supplied, organized in BRG format.
|
||||
The first set of bytes is the right-most slider key, and from there the bytes
|
||||
alternate between the dividers and the keys until the left-most key.
|
||||
There are 31 illuminated sections in total.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
|
@ -63,6 +63,8 @@ void chuni_io_config_load(
|
||||
cfg->controller_led_output_pipe = GetPrivateProfileIntW(L"led", L"controllerLedOutputPipe", 1, filename);
|
||||
cfg->controller_led_output_serial = GetPrivateProfileIntW(L"led", L"controllerLedOutputSerial", 0, filename);
|
||||
|
||||
cfg->controller_led_output_openithm = GetPrivateProfileIntW(L"led", L"controllerLedOutputOpeNITHM", 0, filename);
|
||||
|
||||
cfg->led_serial_baud = GetPrivateProfileIntW(L"led", L"serialBaud", 921600, filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
|
@ -18,6 +18,8 @@ struct chuni_io_config {
|
||||
bool controller_led_output_pipe;
|
||||
bool controller_led_output_serial;
|
||||
|
||||
bool controller_led_output_openithm;
|
||||
|
||||
// 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;
|
||||
|
@ -127,7 +127,11 @@ void led_output_update(uint8_t board, const byte* rgb)
|
||||
|
||||
if (config->controller_led_output_serial)
|
||||
{
|
||||
led_serial_update(escaped_data);
|
||||
if (config->controller_led_output_openithm){
|
||||
led_serial_update_openithm(rgb);
|
||||
} else {
|
||||
led_serial_update(escaped_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ chuniio_lib = static_library(
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
c_pch : '../precompiled.h',
|
||||
|
||||
sources : [
|
||||
'chu2to3.c',
|
||||
'chu2to3.h',
|
||||
|
@ -97,3 +97,27 @@ void led_serial_update(struct _chuni_led_data_buf_t* data)
|
||||
|
||||
ReleaseMutex(serial_write_mutex);
|
||||
}
|
||||
|
||||
void led_serial_update_openithm(const byte* rgb)
|
||||
{
|
||||
if (serial_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(serial_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);
|
||||
}
|
||||
}
|
||||
|
@ -13,3 +13,4 @@
|
||||
|
||||
HRESULT led_serial_init(wchar_t led_com[12], DWORD baud);
|
||||
void led_serial_update(struct _chuni_led_data_buf_t* data);
|
||||
void led_serial_update_openithm(const byte* rgb);
|
@ -123,7 +123,7 @@ static DWORD CALLBACK chusan_pre_startup(void)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bool *dipsw = &chusan_hook_cfg.platform.dipsw.dipsw[0];
|
||||
bool *dipsw = &chusan_hook_cfg.platform.system.dipsw[0];
|
||||
bool is_cvt = dipsw[2];
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
|
@ -15,3 +15,59 @@ EXPORTS
|
||||
cm_io_get_opbtns
|
||||
cm_io_init
|
||||
cm_io_poll
|
||||
CFW_init
|
||||
CFW_term
|
||||
CFW_open
|
||||
CFW_close
|
||||
CFW_listupPrinter
|
||||
CFW_listupPrinterSN
|
||||
CFW_selectPrinter
|
||||
CFW_selectPrinterSN
|
||||
CFW_getPrinterInfo
|
||||
CFW_status
|
||||
CFW_statusAll
|
||||
CFW_resetPrinter
|
||||
CFW_updateFirmware
|
||||
CFW_getFirmwareInfo
|
||||
CHCUSB_init
|
||||
CHCUSB_term
|
||||
CHCUSB_MakeThread
|
||||
CHCUSB_open
|
||||
CHCUSB_close
|
||||
CHCUSB_ReleaseThread
|
||||
CHCUSB_listupPrinter
|
||||
CHCUSB_listupPrinterSN
|
||||
CHCUSB_selectPrinter
|
||||
CHCUSB_selectPrinterSN
|
||||
CHCUSB_getPrinterInfo
|
||||
CHCUSB_imageformat
|
||||
CHCUSB_setmtf
|
||||
CHCUSB_makeGamma
|
||||
CHCUSB_setIcctableProfile
|
||||
CHCUSB_setIcctable
|
||||
CHCUSB_copies
|
||||
CHCUSB_status
|
||||
CHCUSB_statusAll
|
||||
CHCUSB_startpage
|
||||
CHCUSB_endpage
|
||||
CHCUSB_write
|
||||
CHCUSB_writeLaminate
|
||||
CHCUSB_writeHolo
|
||||
CHCUSB_setPrinterInfo
|
||||
CHCUSB_setPrinterToneCurve
|
||||
CHCUSB_getGamma
|
||||
CHCUSB_getMtf
|
||||
CHCUSB_cancelCopies
|
||||
CHCUSB_getPrinterToneCurve
|
||||
CHCUSB_blinkLED
|
||||
CHCUSB_resetPrinter
|
||||
CHCUSB_AttachThreadCount
|
||||
CHCUSB_getPrintIDStatus
|
||||
CHCUSB_setPrintStandby
|
||||
CHCUSB_testCardFeed
|
||||
CHCUSB_exitCard
|
||||
CHCUSB_getCardRfidTID
|
||||
CHCUSB_commCardRfidReader
|
||||
CHCUSB_updateCardRfidReader
|
||||
CHCUSB_getErrorLog
|
||||
CHCUSB_getErrorStatus
|
||||
|
@ -39,6 +39,7 @@ void cm_hook_config_load(
|
||||
io4_config_load(&cfg->io4, filename);
|
||||
vfd_config_load(&cfg->vfd, filename);
|
||||
touch_screen_config_load(&cfg->touch, filename);
|
||||
printer_config_load(&cfg->printer, filename);
|
||||
cm_dll_config_load(&cfg->dll, filename);
|
||||
unity_config_load(&cfg->unity, filename);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "hooklib/dvd.h"
|
||||
#include "hooklib/touch.h"
|
||||
#include "hooklib/printer.h"
|
||||
|
||||
#include "cmhook/cm-dll.h"
|
||||
|
||||
@ -21,6 +22,7 @@ struct cm_hook_config {
|
||||
struct vfd_config vfd;
|
||||
struct cm_dll_config dll;
|
||||
struct touch_screen_config touch;
|
||||
struct printer_config printer;
|
||||
struct unity_config unity;
|
||||
};
|
||||
|
||||
|
@ -56,6 +56,10 @@ static DWORD CALLBACK cm_pre_startup(void)
|
||||
touch_screen_hook_init(&cm_hook_cfg.touch, cm_hook_mod);
|
||||
serial_hook_init();
|
||||
|
||||
/* Hook external DLL APIs */
|
||||
|
||||
printer_hook_init(&cm_hook_cfg.printer, 0, cm_hook_mod);
|
||||
|
||||
/* Initialize emulation hooks */
|
||||
|
||||
hr = platform_hook_init(
|
||||
|
@ -36,9 +36,9 @@ HRESULT cm_io_init(void);
|
||||
HRESULT cm_io_poll(void);
|
||||
|
||||
/* Get the state of the cabinet's operator buttons as of the last poll. See
|
||||
cm_IO_OPBTN enum above: this contains bit mask definitions for button
|
||||
CM_IO_OPBTN enum above: this contains bit mask definitions for button
|
||||
states returned in *opbtn. All buttons are active-high.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
void cm_io_get_opbtns(uint8_t *opbtn);
|
||||
void cm_io_get_opbtns(uint8_t *opbtn);
|
||||
|
4
dist/chuni/segatools.ini
vendored
4
dist/chuni/segatools.ini
vendored
@ -82,10 +82,12 @@ cabLedOutputSerial=0
|
||||
controllerLedOutputPipe=1
|
||||
; Output slider LED data to the serial port
|
||||
controllerLedOutputSerial=0
|
||||
; Use the OpeNITHM protocol for serial LED output
|
||||
controllerLedOutputOpeNITHM=0
|
||||
|
||||
; Serial port to send data to if using serial output. Default is COM5.
|
||||
;serialPort=COM5
|
||||
; Baud rate for serial data
|
||||
; Baud rate for serial data (set to 115200 if using OpeNITHM)
|
||||
;serialBaud=921600
|
||||
|
||||
; Data output a sequence of bytes, with JVS-like framing.
|
||||
|
14
dist/chusan/segatools.ini
vendored
14
dist/chusan/segatools.ini
vendored
@ -25,7 +25,7 @@ aimePath=DEVICE\aime.txt
|
||||
;highBaud=1
|
||||
|
||||
[vfd]
|
||||
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||
; Enable VFD emulation. Disable to use a real VFD
|
||||
; GP1232A02A FUTABA assembly.
|
||||
enable=1
|
||||
|
||||
@ -59,8 +59,8 @@ addrSuffix=11
|
||||
; that subnet must start with 192.168.
|
||||
subnet=192.168.139.0
|
||||
|
||||
[gpio]
|
||||
; ALLS DIP switches.
|
||||
[system]
|
||||
; Enable ALLS system settings.
|
||||
enable=1
|
||||
|
||||
; Enable freeplay mode. This will disable the coin slot and set the game to
|
||||
@ -108,10 +108,12 @@ cabLedOutputSerial=0
|
||||
controllerLedOutputPipe=1
|
||||
; Output slider LED data to the serial port
|
||||
controllerLedOutputSerial=0
|
||||
; Use the OpeNITHM protocol for serial LED output
|
||||
controllerLedOutputOpeNITHM=0
|
||||
|
||||
; Serial port to send data to if using serial output. Default is COM5.
|
||||
;serialPort=COM5
|
||||
; Baud rate for serial data
|
||||
; Baud rate for serial data (set to 115200 if using OpeNITHM)
|
||||
;serialBaud=921600
|
||||
|
||||
; Data output a sequence of bytes, with JVS-like framing.
|
||||
@ -202,7 +204,3 @@ ir=0x20
|
||||
; ... etc ...
|
||||
;cell31=0x53
|
||||
;cell32=0x53
|
||||
|
||||
; Enable slider LED serial output. This follows OpeNITHM Serial LED Protocol.
|
||||
; eg. COM5
|
||||
;ledport=
|
||||
|
16
dist/cm/segatools.ini
vendored
16
dist/cm/segatools.ini
vendored
@ -23,7 +23,7 @@ enable=1
|
||||
aimePath=DEVICE\aime.txt
|
||||
|
||||
[vfd]
|
||||
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||
; Enable VFD emulation. Disable to use a real VFD
|
||||
; GP1232A02A FUTABA assembly.
|
||||
enable=1
|
||||
|
||||
@ -50,10 +50,10 @@ enable=1
|
||||
; The /24 LAN subnet that the emulated keychip will tell the game to expect.
|
||||
; If you disable netenv then you must set this to your LAN's IP subnet, and
|
||||
; that subnet must start with 192.168.
|
||||
subnet=192.168.100.0
|
||||
subnet=192.168.165.0
|
||||
|
||||
[gpio]
|
||||
; ALLS DIP switches.
|
||||
[system]
|
||||
; Enable ALLS system settings.
|
||||
enable=1
|
||||
|
||||
; LAN Install: If multiple machines are present on the same LAN then set
|
||||
@ -73,6 +73,14 @@ enable=0
|
||||
; modding frameworks such as BepInEx.
|
||||
targetAssembly=
|
||||
|
||||
[printer]
|
||||
; Sinfonia CHC-C330 printer emulation setting.
|
||||
enable=1
|
||||
; Change the printer serial number here.
|
||||
serial_no="5A-A123"
|
||||
; Insert the path to the image output directory here.
|
||||
printerOutPath="DEVICE\print"
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Custom IO settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
6
dist/fgo/segatools.ini
vendored
6
dist/fgo/segatools.ini
vendored
@ -23,7 +23,7 @@ enable=1
|
||||
aimePath=DEVICE\aime.txt
|
||||
|
||||
[vfd]
|
||||
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||
; Enable VFD emulation. Disable to use a real VFD
|
||||
; GP1232A02A FUTABA assembly.
|
||||
enable=1
|
||||
|
||||
@ -72,8 +72,8 @@ addrSuffix=11
|
||||
; that subnet must start with 192.168.
|
||||
subnet=192.168.167.0
|
||||
|
||||
[gpio]
|
||||
; ALLS DIP switches.
|
||||
[system]
|
||||
; Enable ALLS system settings.
|
||||
enable=1
|
||||
|
||||
; Enable freeplay mode. This will disable the coin slot and set the game to
|
||||
|
4
dist/idac/segatools.ini
vendored
4
dist/idac/segatools.ini
vendored
@ -54,8 +54,8 @@ subnet=192.168.158.0
|
||||
; 1: JPN: Japan, 4: EXP: Export (for Asian markets)
|
||||
region=4
|
||||
|
||||
[gpio]
|
||||
; ALLS DIP switches.
|
||||
[system]
|
||||
; Enable ALLS system settings.
|
||||
enable=1
|
||||
|
||||
; Enable freeplay mode. This will disable the coin slot and set the game to
|
||||
|
6
dist/mai2/segatools.ini
vendored
6
dist/mai2/segatools.ini
vendored
@ -23,7 +23,7 @@ enable=1
|
||||
aimePath=DEVICE\aime.txt
|
||||
|
||||
[vfd]
|
||||
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||
; Enable VFD emulation. Disable to use a real VFD
|
||||
; GP1232A02A FUTABA assembly.
|
||||
enable=1
|
||||
|
||||
@ -56,8 +56,8 @@ addrSuffix=11
|
||||
; that subnet must start with 192.168.
|
||||
subnet=192.168.172.0
|
||||
|
||||
[gpio]
|
||||
; ALLS DIP switches.
|
||||
[system]
|
||||
; Enable ALLS system settings.
|
||||
enable=1
|
||||
|
||||
; Enable freeplay mode. This will disable the coin slot and set the game to
|
||||
|
6
dist/mercury/segatools.ini
vendored
6
dist/mercury/segatools.ini
vendored
@ -23,7 +23,7 @@ enable=1
|
||||
aimePath=DEVICE\aime.txt
|
||||
|
||||
[vfd]
|
||||
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||
; Enable VFD emulation. Disable to use a real VFD
|
||||
; GP1232A02A FUTABA assembly.
|
||||
enable=1
|
||||
|
||||
@ -56,8 +56,8 @@ addrSuffix=11
|
||||
; that subnet must start with 192.168.
|
||||
subnet=192.168.174.0
|
||||
|
||||
[gpio]
|
||||
; ALLS DIP switches.
|
||||
[system]
|
||||
; Enable ALLS system settings.
|
||||
enable=1
|
||||
|
||||
; Enable freeplay mode. This will disable the coin slot and set the game to
|
||||
|
8
dist/mu3/segatools.ini
vendored
8
dist/mu3/segatools.ini
vendored
@ -23,7 +23,7 @@ enable=1
|
||||
aimePath=DEVICE\aime.txt
|
||||
|
||||
[vfd]
|
||||
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||
; Enable VFD emulation. Disable to use a real VFD
|
||||
; GP1232A02A FUTABA assembly.
|
||||
enable=1
|
||||
|
||||
@ -52,8 +52,8 @@ enable=1
|
||||
; that subnet must start with 192.168.
|
||||
subnet=192.168.162.0
|
||||
|
||||
[gpio]
|
||||
; ALLS DIP switches.
|
||||
[system]
|
||||
; Enable ALLS system settings.
|
||||
enable=1
|
||||
|
||||
; Enable freeplay mode. This will disable the coin slot and set the game to
|
||||
@ -190,7 +190,7 @@ leftSide=0x01 ; Mouse Left
|
||||
rightSide=0x02 ; Mouse Right
|
||||
|
||||
right1=0x4A ; J
|
||||
right1=0x4B ; K
|
||||
right2=0x4B ; K
|
||||
right3=0x4C ; L
|
||||
|
||||
leftMenu=0x55 ; U
|
||||
|
6
dist/swdc/segatools.ini
vendored
6
dist/swdc/segatools.ini
vendored
@ -23,7 +23,7 @@ enable=1
|
||||
aimePath=DEVICE\aime.txt
|
||||
|
||||
[vfd]
|
||||
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||
; Enable VFD emulation. Disable to use a real VFD
|
||||
; GP1232A02A FUTABA assembly.
|
||||
enable=1
|
||||
|
||||
@ -56,8 +56,8 @@ addrSuffix=11
|
||||
; in order to find the MAIN cabinet.
|
||||
subnet=192.168.160.0
|
||||
|
||||
[gpio]
|
||||
; ALLS DIP switches.
|
||||
[system]
|
||||
; Enable ALLS system settings.
|
||||
enable=1
|
||||
|
||||
; Enable freeplay mode. This will disable the coin slot and set the game to
|
||||
|
9
dist/tokyo/config_hook.json
vendored
Normal file
9
dist/tokyo/config_hook.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"network" :
|
||||
{
|
||||
"property" :
|
||||
{
|
||||
"dhcp" : true
|
||||
}
|
||||
}
|
||||
}
|
199
dist/tokyo/segatools.ini
vendored
Normal file
199
dist/tokyo/segatools.ini
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
; -----------------------------------------------------------------------------
|
||||
; Path settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
[vfs]
|
||||
; Insert the path to the game AMFS directory here (contains ICF1 and ICF2)
|
||||
amfs=
|
||||
; Insert the path to the game Option directory here (contains OPxx directories)
|
||||
option=
|
||||
; Create an empty directory somewhere and insert the path here.
|
||||
; This directory may be shared between multiple SEGA games.
|
||||
; NOTE: This has nothing to do with Windows %APPDATA%.
|
||||
appdata=
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Network settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
[dns]
|
||||
; Insert the hostname or IP address of the server you wish to use here.
|
||||
; Note that 127.0.0.1, localhost etc are specifically rejected.
|
||||
default=127.0.0.1
|
||||
|
||||
[netenv]
|
||||
; Simulate an ideal LAN environment. This may interfere with head-to-head play.
|
||||
; SEGA games are somewhat picky about their LAN environment, so leaving this
|
||||
; setting enabled is recommended.
|
||||
enable=1
|
||||
|
||||
; The final octet of the local host's IP address on the virtualized subnet (so,
|
||||
; if the keychip subnet is `192.168.149.0` and this value is set to `205`, then the
|
||||
; local host's virtualized LAN IP is `192.168.149.205`).
|
||||
addrSuffix=205
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Board settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
[keychip]
|
||||
; The /24 LAN subnet that the emulated keychip will tell the game to expect.
|
||||
; If you disable netenv then you must set this to your LAN's IP subnet, and
|
||||
; that subnet must start with 192.168.
|
||||
subnet=192.168.149.0
|
||||
|
||||
; Override the keychip's region code.
|
||||
; 1: JAPAN (ALL.Net, Japanese language, Option support enabled)
|
||||
; 4: EXPORT (Local networking only, English language, No option support)
|
||||
; 8: CHINA
|
||||
;
|
||||
; NOTE: Changing this setting causes a factory reset. The language can be
|
||||
; changed in the game settings, so it's possible to run the JAPAN region
|
||||
; with English language.
|
||||
region=1
|
||||
|
||||
[system]
|
||||
; Enable ALLS system settings.
|
||||
enable=1
|
||||
|
||||
; Enable freeplay mode. This will disable the coin slot and set the game to
|
||||
; freeplay. Keep in mind that some game modes (e.g. Freedom/Time Modes) will not
|
||||
; allow you to start a game in freeplay mode.
|
||||
freeplay=0
|
||||
|
||||
; For Mario & Sonic at the Tokyo 2020 Olympics Arcade, DipSw 1/2/3 must be set
|
||||
; as the following:
|
||||
; Cabinet ID 1 (Server): 1 0 0
|
||||
; Cabinet ID 2 (Client): 0 1 0
|
||||
; Cabinet ID 3 (Client): 0 0 1
|
||||
; Cabinet ID 4 (Client): 0 1 1
|
||||
dipsw1=1
|
||||
dipsw2=0
|
||||
dipsw3=0
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; LED settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
[led15093]
|
||||
; Enable emulation of the 15093-04 controlled lights, which handle the cabinet
|
||||
; LEDs.
|
||||
enable=1
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Misc. hook settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
[zinput]
|
||||
; Disables the built-in DirectInput support, which is used to support a
|
||||
; controller out of the box.
|
||||
enable=1
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Custom IO settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
[tokyoio]
|
||||
; To use a custom Mario & Sonic at the Tokyo 2020 Olympics Arcade IO DLL enter
|
||||
; its path here. Leave empty if you want to use Segatools built-in keyboard/
|
||||
; gamepad input.
|
||||
path=
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Input settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
; Keyboard bindings are specified as hexadecimal (prefixed with 0x) or decimal
|
||||
; (not prefixed with 0x) virtual-key codes, a list of which can be found here:
|
||||
;
|
||||
; https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
|
||||
;
|
||||
; This is, admittedly, not the most user-friendly configuration method in the
|
||||
; world. An improved solution will be provided later.
|
||||
|
||||
[io4]
|
||||
; Test button virtual-key code. Default is the F1 key.
|
||||
test=0x70
|
||||
; Service button virtual-key code. Default is the F2 key.
|
||||
service=0x71
|
||||
; Keyboard button to increment coin counter. Default is the F3 key.
|
||||
coin=0x72
|
||||
|
||||
; Input API selection for IO4 input emulator.
|
||||
; Set "xinput" to use a gamepad and "keyboard" to use a keyboard.
|
||||
mode=xinput
|
||||
|
||||
; Mario & Sonic at the Tokyo 2020 Olympics Arcade Control Panel
|
||||
;
|
||||
; |--|------------------ Main-Assy ------------------|--|
|
||||
; | | YELLOW | |
|
||||
; | | --- | |
|
||||
; | | ( O ) | |
|
||||
; |--| BLUE --- RED |--|
|
||||
; | | --- PUSH CENTER --- | |
|
||||
; | | ( O ) /---------------\ ( O ) | |
|
||||
; | | --- / \ --- | |
|
||||
; | | PUSH LEFT / \ PUSH RIGHT| |
|
||||
; |--|---------/ Floor Assy \---------|--|
|
||||
; | | |JUMP SENSE JUMP SENSE| | |
|
||||
; | | |1|---------------|-|-------------->|1| | |
|
||||
; | | | | Foot Panel | | Foot Panel | | | |
|
||||
; | | |2|<- - - - - - - |-| - - - - - - - |2| | |
|
||||
; | | | | | | | | | |
|
||||
; | | |3| -FOOT SENSE - |-| - FOOT SENSE->|3| | |
|
||||
; | | | | L | | R | | | |
|
||||
; | | |4|<- - - - - - - |-| - - - - - - - |4| | |
|
||||
; | | | | | | | | | |
|
||||
; | | |5| - - - - - - - |-| - - - - - - ->|5| | |
|
||||
; | | | | | | | | | |
|
||||
; | | |6|<--------------|-|---------------|6| | |
|
||||
; | | | | | |
|
||||
; | | | | | |
|
||||
; |--|----|-------------------------------------|----|--|
|
||||
;
|
||||
|
||||
; XInput bindings
|
||||
;
|
||||
; X Push Left Blue
|
||||
; Y Push Center Yellow
|
||||
; B Push Right Red
|
||||
; D-Pad Left Push Left Blue
|
||||
; D-Pad Right Push Right Red
|
||||
; Left Trigger Foot Sense L/Jump Sense
|
||||
; Right Trigger Foot Sense R/Jump Sense
|
||||
|
||||
[keyboard]
|
||||
; Keyboard bindings
|
||||
|
||||
; Keyoard: Push button settings
|
||||
|
||||
; PUSH LEFT (BLUE) button virtual-key code. Default is the A key.
|
||||
leftBlue=0x41
|
||||
; PUSH CENTER (YELLOW) button virtual-key code. Default is the S key.
|
||||
centerYellow=0x53
|
||||
; PUSH RIGHT (RED) button virtual-key code. Default is the D key.
|
||||
rightRed=0x44
|
||||
|
||||
; Keyboard: Sensor settings
|
||||
; FOOT SENSE L (LEFT) button virtual-key code. Default is the Left Arrow key.
|
||||
footLeft=0x25
|
||||
; FOOT SENSE R (RIGHT) button virtual-key code. Default is the Right Arrow key.
|
||||
footRight=0x27
|
||||
|
||||
; Keyboard: Jump sensor settings
|
||||
; All jump sensors will also trigger the FOOT SENSE L and FOOT SENSE R buttons.
|
||||
; JUMP SENSOR 1 button virtual-key code. Default is the Z key.
|
||||
jump1=0x5A
|
||||
; JUMP SENSOR 2 button virtual-key code. Default is the X key.
|
||||
jump2=0x58
|
||||
; JUMP SENSOR 3 button virtual-key code. Default is the C key.
|
||||
jump3=0x43
|
||||
; JUMP SENSOR 4 button virtual-key code. Default is the B key.
|
||||
jump4=0x42
|
||||
; JUMP SENSOR 5 button virtual-key code. Default is the N key.
|
||||
jump5=0x4E
|
||||
; JUMP SENSOR 6 button virtual-key code. Default is the M key.
|
||||
jump6=0x4D
|
||||
|
||||
; Virtual-key code for all jump sensors. Default is the Space key.
|
||||
jumpAll=0x20
|
57
dist/tokyo/start.bat
vendored
Normal file
57
dist/tokyo/start.bat
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
@echo off
|
||||
pushd %~dp0
|
||||
|
||||
set DAEMON_WAIT_SECONDS=5
|
||||
|
||||
set AMDAEMON_CFG=config_common.json ^
|
||||
config_ch.json ^
|
||||
config_ex.json ^
|
||||
config_jp.json ^
|
||||
config_st1_ch.json ^
|
||||
config_st1_ex.json ^
|
||||
config_st1_jp.json ^
|
||||
config_st2_ch.json ^
|
||||
config_st2_ex.json ^
|
||||
config_st2_jp.json ^
|
||||
config_st3_ch.json ^
|
||||
config_st3_ex.json ^
|
||||
config_st3_jp.json ^
|
||||
config_st4_ch.json ^
|
||||
config_st4_ex.json ^
|
||||
config_st4_jp.json ^
|
||||
config_laninstall_server_ch.json ^
|
||||
config_laninstall_client1_ch.json ^
|
||||
config_laninstall_client2_ch.json ^
|
||||
config_laninstall_client3_ch.json ^
|
||||
config_laninstall_server_ex.json ^
|
||||
config_laninstall_client1_ex.json ^
|
||||
config_laninstall_client2_ex.json ^
|
||||
config_laninstall_client3_ex.json ^
|
||||
config_laninstall_server_jp.json ^
|
||||
config_laninstall_client1_jp.json ^
|
||||
config_laninstall_client2_jp.json ^
|
||||
config_laninstall_client3_jp.json ^
|
||||
config_hook.json
|
||||
|
||||
start /min "AM Daemon" inject -d -k tokyohook.dll amdaemon.exe -c %AMDAEMON_CFG%
|
||||
timeout %DAEMON_WAIT_SECONDS% > nul 2>&1
|
||||
|
||||
REM ---------------------------------------------------------------------------
|
||||
REM Set configuration
|
||||
REM ---------------------------------------------------------------------------
|
||||
|
||||
REM Configuration values to be passed to the game executable.
|
||||
REM All known values:
|
||||
REM -forceapi:11
|
||||
REM -forcehal
|
||||
REM -forcevsync:0/1
|
||||
REM -fullscreen
|
||||
REM -windowed
|
||||
REM Note: -windowed is recommended as the game looks sharper in windowed mode.
|
||||
inject -d -k tokyohook.dll app.exe -windowed
|
||||
|
||||
taskkill /f /im amdaemon.exe > nul 2>&1
|
||||
|
||||
echo.
|
||||
echo Game processes have terminated
|
||||
pause
|
@ -92,9 +92,21 @@ Controls emulation of the VFD GP1232A02A FUTABA assembly.
|
||||
|
||||
Default: `1`
|
||||
|
||||
Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||
Enable VFD emulation. Disable to use a real VFD
|
||||
GP1232A02A FUTABA assembly (COM port number varies by game).
|
||||
|
||||
### `portNo`
|
||||
|
||||
Default: (game specific)
|
||||
|
||||
Sets the COM port to use for the VFD.
|
||||
|
||||
### `utfConversion`
|
||||
|
||||
Default: `0`
|
||||
|
||||
Converts the strings from the VFD from their respective encoding to UTF, so console output will display as it should on non-Japanese locales.
|
||||
|
||||
## `[amvideo]`
|
||||
|
||||
Controls the `amvideo.dll` stub built into Segatools. This is a DLL that is
|
||||
|
@ -52,16 +52,12 @@ HRESULT fgo_io_poll(void);
|
||||
void fgo_io_get_opbtns(uint8_t *opbtn);
|
||||
|
||||
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
|
||||
FGO_IO_GAMEBTN enum above for bit mask definitions. Inputs are split into
|
||||
a left hand side set of inputs and a right hand side set of inputs: the bit
|
||||
mappings are the same in both cases.
|
||||
|
||||
All buttons are active-high, even though some buttons' electrical signals
|
||||
on a real cabinet are active-low.
|
||||
FGO_IO_GAMEBTN enum above: this contains bit mask definitions for button
|
||||
states returned in *gamebtn. All buttons are active-high.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
void fgo_io_get_gamebtns(uint8_t *btn);
|
||||
void fgo_io_get_gamebtns(uint8_t *gamebtn);
|
||||
|
||||
/* Get the position of the cabinet stick as of the last poll. The center
|
||||
position should be equal to or close to 32767.
|
||||
|
@ -224,9 +224,19 @@ static HRESULT STDMETHODCALLTYPE my_IDirect3D9_CreateDevice(
|
||||
gfx_util_frame_window(hwnd);
|
||||
}
|
||||
|
||||
dprintf("Gfx: Using adapter %d\n", gfx_config.monitor);
|
||||
UINT max_adapter = IDirect3D9_GetAdapterCount(real);
|
||||
adapter = gfx_config.monitor;
|
||||
if (adapter >= max_adapter) {
|
||||
dprintf(
|
||||
"Gfx: Requested adapter %d but maximum is %d. Using primary monitor\n",
|
||||
gfx_config.monitor, max_adapter - 1
|
||||
);
|
||||
adapter = D3DADAPTER_DEFAULT;
|
||||
} else {
|
||||
dprintf("Gfx: Using adapter %d\n", gfx_config.monitor);
|
||||
}
|
||||
|
||||
return IDirect3D9_CreateDevice(real, gfx_config.monitor, type, hwnd, flags, pp, pdev);
|
||||
return IDirect3D9_CreateDevice(real, adapter, type, hwnd, flags, pp, pdev);
|
||||
}
|
||||
|
||||
static HRESULT STDMETHODCALLTYPE my_IDirect3D9Ex_CreateDevice(
|
||||
|
1602
hooklib/printer.c
1602
hooklib/printer.c
File diff suppressed because it is too large
Load Diff
@ -14,3 +14,4 @@ struct printer_config {
|
||||
};
|
||||
|
||||
void printer_hook_init(const struct printer_config *cfg, int rfid_port_no, HINSTANCE self);
|
||||
void printer_hook_insert_hooks(HMODULE target);
|
||||
|
@ -108,6 +108,7 @@ subdir('mai2io')
|
||||
subdir('cmio')
|
||||
subdir('mercuryio')
|
||||
subdir('cxbio')
|
||||
subdir('tokyoio')
|
||||
subdir('fgoio')
|
||||
|
||||
subdir('chunihook')
|
||||
@ -123,4 +124,5 @@ subdir('mai2hook')
|
||||
subdir('cmhook')
|
||||
subdir('mercuryhook')
|
||||
subdir('cxbhook')
|
||||
subdir('tokyohook')
|
||||
subdir('fgohook')
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
#include "hook/procaddr.h"
|
||||
|
||||
#include "platform/clock.h"
|
||||
|
||||
@ -19,7 +20,9 @@ static BOOL WINAPI my_SetTimeZoneInformation(TIME_ZONE_INFORMATION *tzinfo);
|
||||
|
||||
static BOOL (WINAPI * next_GetSystemTimeAsFileTime)(FILETIME *out);
|
||||
static int64_t clock_current_day;
|
||||
static bool clock_timezone;
|
||||
static bool clock_time_warp;
|
||||
static bool clock_writeable;
|
||||
|
||||
static const struct hook_symbol clock_base_hook_syms[] = {
|
||||
{
|
||||
@ -225,35 +228,41 @@ HRESULT clock_hook_init(const struct clock_config *cfg)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
|
||||
clock_timezone = cfg->timezone;
|
||||
clock_time_warp = cfg->timewarp;
|
||||
clock_writeable = cfg->writeable;
|
||||
|
||||
if (cfg->timezone || cfg->timewarp || !cfg->writeable) {
|
||||
clock_hook_insert_hooks(NULL);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void clock_hook_insert_hooks(HMODULE target) {
|
||||
if (clock_timezone || clock_time_warp || !clock_writeable) {
|
||||
/* All the clock hooks require the core GSTAFT hook to be installed */
|
||||
/* Note the ! up there btw. */
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
target,
|
||||
"kernel32.dll",
|
||||
clock_base_hook_syms,
|
||||
_countof(clock_base_hook_syms));
|
||||
}
|
||||
|
||||
if (cfg->timezone) {
|
||||
if (clock_timezone) {
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
target,
|
||||
"kernel32.dll",
|
||||
clock_read_hook_syms,
|
||||
_countof(clock_read_hook_syms));
|
||||
}
|
||||
|
||||
if (!cfg->writeable) {
|
||||
if (!clock_writeable) {
|
||||
/* Install hook if this config parameter is FALSE! */
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
target,
|
||||
"kernel32.dll",
|
||||
clock_write_hook_syms,
|
||||
_countof(clock_write_hook_syms));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -11,3 +11,4 @@ struct clock_config {
|
||||
};
|
||||
|
||||
HRESULT clock_hook_init(const struct clock_config *cfg);
|
||||
void clock_hook_insert_hooks(HMODULE target);
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "platform/pcbid.h"
|
||||
#include "platform/platform.h"
|
||||
#include "platform/vfs.h"
|
||||
#include "platform/dipsw.h"
|
||||
#include "platform/system.h"
|
||||
|
||||
void platform_config_load(struct platform_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
@ -40,7 +40,7 @@ void platform_config_load(struct platform_config *cfg, const wchar_t *filename)
|
||||
netenv_config_load(&cfg->netenv, filename);
|
||||
nusec_config_load(&cfg->nusec, filename);
|
||||
vfs_config_load(&cfg->vfs, filename);
|
||||
dipsw_config_load(&cfg->dipsw, filename);
|
||||
system_config_load(&cfg->system, filename);
|
||||
}
|
||||
|
||||
void amvideo_config_load(struct amvideo_config *cfg, const wchar_t *filename)
|
||||
@ -329,7 +329,7 @@ void vfs_config_load(struct vfs_config *cfg, const wchar_t *filename)
|
||||
filename);
|
||||
}
|
||||
|
||||
void dipsw_config_load(struct dipsw_config *cfg, const wchar_t *filename)
|
||||
void system_config_load(struct system_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
wchar_t name[7];
|
||||
size_t i;
|
||||
@ -337,14 +337,14 @@ void dipsw_config_load(struct dipsw_config *cfg, const wchar_t *filename)
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"gpio", L"enable", 0, filename);
|
||||
cfg->freeplay = GetPrivateProfileIntW(L"gpio", L"freeplay", 0, filename);
|
||||
cfg->enable = GetPrivateProfileIntW(L"system", L"enable", 0, filename);
|
||||
cfg->freeplay = GetPrivateProfileIntW(L"system", L"freeplay", 0, filename);
|
||||
|
||||
wcscpy_s(name, _countof(name), L"dipsw0");
|
||||
|
||||
for (i = 0 ; i < 8 ; i++) {
|
||||
name[5] = L'1' + i;
|
||||
cfg->dipsw[i] = GetPrivateProfileIntW(L"gpio", name, 0, filename);
|
||||
cfg->dipsw[i] = GetPrivateProfileIntW(L"system", name, 0, filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include "platform/pcbid.h"
|
||||
#include "platform/platform.h"
|
||||
#include "platform/vfs.h"
|
||||
#include "platform/dipsw.h"
|
||||
#include "platform/system.h"
|
||||
|
||||
void platform_config_load(
|
||||
struct platform_config *cfg,
|
||||
@ -35,4 +35,4 @@ void netenv_config_load(struct netenv_config *cfg, const wchar_t *filename);
|
||||
void nusec_config_load(struct nusec_config *cfg, const wchar_t *filename);
|
||||
void pcbid_config_load(struct pcbid_config *cfg, const wchar_t *filename);
|
||||
void vfs_config_load(struct vfs_config *cfg, const wchar_t *filename);
|
||||
void dipsw_config_load(struct dipsw_config *cfg, const wchar_t *filename);
|
||||
void system_config_load(struct system_config *cfg, const wchar_t *filename);
|
||||
|
@ -109,17 +109,23 @@ HRESULT dns_platform_hook_init(const struct dns_config *cfg)
|
||||
return hr;
|
||||
}
|
||||
|
||||
// CHN
|
||||
// PowerOn
|
||||
// WAHLAP PowerOn
|
||||
hr = dns_hook_push(L"at.sys-all.cn", cfg->startup);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// WeChat AimeDB Server
|
||||
// WAHLAP WeChat AimeDB Server
|
||||
hr = dns_hook_push(L"ai.sys-all.cn", cfg->aimedb);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// WAHLAP Billing
|
||||
hr = dns_hook_push(L"ib.sys-all.cn", cfg->billing);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ platform_lib = static_library(
|
||||
'platform.h',
|
||||
'vfs.c',
|
||||
'vfs.h',
|
||||
'dipsw.c',
|
||||
'dipsw.h',
|
||||
'system.c',
|
||||
'system.h',
|
||||
],
|
||||
)
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "platform/pcbid.h"
|
||||
#include "platform/platform.h"
|
||||
#include "platform/vfs.h"
|
||||
#include "platform/dipsw.h"
|
||||
#include "platform/system.h"
|
||||
|
||||
HRESULT platform_hook_init(
|
||||
const struct platform_config *cfg,
|
||||
@ -82,7 +82,7 @@ HRESULT platform_hook_init(
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = dipsw_init(&cfg->dipsw, &cfg->vfs);
|
||||
hr = system_init(&cfg->system, &cfg->vfs);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "platform/nusec.h"
|
||||
#include "platform/pcbid.h"
|
||||
#include "platform/vfs.h"
|
||||
#include "platform/dipsw.h"
|
||||
#include "platform/system.h"
|
||||
|
||||
struct platform_config {
|
||||
struct amvideo_config amvideo;
|
||||
@ -27,7 +27,7 @@ struct platform_config {
|
||||
struct netenv_config netenv;
|
||||
struct nusec_config nusec;
|
||||
struct vfs_config vfs;
|
||||
struct dipsw_config dipsw;
|
||||
struct system_config system;
|
||||
};
|
||||
|
||||
HRESULT platform_hook_init(
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "platform/dipsw.h"
|
||||
#include "platform/system.h"
|
||||
#include "platform/vfs.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
@ -30,12 +30,12 @@ typedef struct
|
||||
char padding[4];
|
||||
uint8_t dip_switches;
|
||||
char data[DATA_SIZE];
|
||||
} DipSwitchBlock;
|
||||
} DipSwBlock;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CreditBlock credit_block;
|
||||
DipSwitchBlock dip_switch_block;
|
||||
DipSwBlock dip_switch_block;
|
||||
char *data;
|
||||
} SystemInfo;
|
||||
|
||||
@ -43,13 +43,13 @@ typedef struct
|
||||
|
||||
static SystemInfo system_info;
|
||||
|
||||
static struct dipsw_config dipsw_config;
|
||||
static struct system_config system_config;
|
||||
static struct vfs_config vfs_config;
|
||||
|
||||
static void dipsw_read_sysfile(const wchar_t *sys_file);
|
||||
static void dipsw_save_sysfile(const wchar_t *sys_file);
|
||||
static void system_read_sysfile(const wchar_t *sys_file);
|
||||
static void system_save_sysfile(const wchar_t *sys_file);
|
||||
|
||||
HRESULT dipsw_init(const struct dipsw_config *cfg, const struct vfs_config *vfs_cfg)
|
||||
HRESULT system_init(const struct system_config *cfg, const struct vfs_config *vfs_cfg)
|
||||
{
|
||||
HRESULT hr;
|
||||
wchar_t sys_file_path[MAX_PATH];
|
||||
@ -62,28 +62,28 @@ HRESULT dipsw_init(const struct dipsw_config *cfg, const struct vfs_config *vfs_
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
memcpy(&dipsw_config, cfg, sizeof(*cfg));
|
||||
memcpy(&system_config, cfg, sizeof(*cfg));
|
||||
|
||||
sys_file_path[0] = L'\0';
|
||||
// concatenate vfs_config.amfs with L"sysfile.dat"
|
||||
wcsncpy(sys_file_path, vfs_cfg->amfs, MAX_PATH);
|
||||
wcsncat(sys_file_path, L"\\sysfile.dat", MAX_PATH);
|
||||
|
||||
dipsw_read_sysfile(sys_file_path);
|
||||
system_read_sysfile(sys_file_path);
|
||||
|
||||
// now write the dipsw_config.dipsw to the dip_switch_block
|
||||
dipsw_save_sysfile(sys_file_path);
|
||||
// now write the system_config.system to the dip_switch_block
|
||||
system_save_sysfile(sys_file_path);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void dipsw_read_sysfile(const wchar_t *sys_file)
|
||||
static void system_read_sysfile(const wchar_t *sys_file)
|
||||
{
|
||||
FILE *f = _wfopen(sys_file, L"r");
|
||||
|
||||
if (f == NULL)
|
||||
{
|
||||
dprintf("DipSw: First run detected, DipSw settings can only be applied AFTER the first run\n");
|
||||
dprintf("System: First run detected, system settings can only be applied AFTER the first run\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ static void dipsw_read_sysfile(const wchar_t *sys_file)
|
||||
|
||||
if (file_size != 0x6000)
|
||||
{
|
||||
dprintf("DipSw: Invalid sysfile.dat file size\n");
|
||||
dprintf("System: Invalid sysfile.dat file size\n");
|
||||
fclose(f);
|
||||
|
||||
return;
|
||||
@ -108,10 +108,10 @@ static void dipsw_read_sysfile(const wchar_t *sys_file)
|
||||
memcpy(&system_info.dip_switch_block, system_info.data + 0x2800, BLOCK_SIZE);
|
||||
}
|
||||
|
||||
static void dipsw_save_sysfile(const wchar_t *sys_file)
|
||||
static void system_save_sysfile(const wchar_t *sys_file)
|
||||
{
|
||||
char block[BLOCK_SIZE];
|
||||
uint8_t dipsw = 0;
|
||||
uint8_t system = 0;
|
||||
uint8_t freeplay = 0;
|
||||
|
||||
// open the sysfile.dat for writing in bytes mode
|
||||
@ -122,28 +122,28 @@ static void dipsw_save_sysfile(const wchar_t *sys_file)
|
||||
return;
|
||||
}
|
||||
|
||||
// write the dipsw_config.dipsw to the dip_switch_block
|
||||
// write the system_config.system to the dip_switch_block
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (dipsw_config.dipsw[i])
|
||||
if (system_config.dipsw[i])
|
||||
{
|
||||
// print which dipsw is enabled
|
||||
dprintf("DipSw: DipSw%d=1 set\n", i + 1);
|
||||
dipsw |= (1 << i);
|
||||
// print which system is enabled
|
||||
dprintf("System: DipSw%d=1 set\n", i + 1);
|
||||
system |= (1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
if (dipsw_config.freeplay)
|
||||
if (system_config.freeplay)
|
||||
{
|
||||
// print that freeplay is enabled
|
||||
dprintf("DipSw: Freeplay enabled\n");
|
||||
dprintf("System: Freeplay enabled\n");
|
||||
freeplay = 1;
|
||||
}
|
||||
|
||||
// set the new credit block
|
||||
system_info.credit_block.freeplay = freeplay;
|
||||
// set the new dip_switch_block
|
||||
system_info.dip_switch_block.dip_switches = dipsw;
|
||||
system_info.dip_switch_block.dip_switches = system;
|
||||
|
||||
// calculate the new checksum, skip the old crc32 value
|
||||
// which is at the beginning of the block, thats's why the +4
|
||||
@ -167,7 +167,7 @@ static void dipsw_save_sysfile(const wchar_t *sys_file)
|
||||
|
||||
// print the dip_switch_block in hex
|
||||
/*
|
||||
dprintf("DipSw Block: ");
|
||||
dprintf("System Block: ");
|
||||
for (size_t i = 0; i < BLOCK_SIZE; i++)
|
||||
{
|
||||
dprintf("%02X ", ((uint8_t *)&system_info.dip_switch_block)[i]);
|
@ -7,10 +7,10 @@
|
||||
|
||||
#include "platform/vfs.h"
|
||||
|
||||
struct dipsw_config {
|
||||
struct system_config {
|
||||
bool enable;
|
||||
bool freeplay;
|
||||
bool dipsw[8];
|
||||
};
|
||||
|
||||
HRESULT dipsw_init(const struct dipsw_config *cfg, const struct vfs_config *vfs_cfg);
|
||||
HRESULT system_init(const struct system_config *cfg, const struct vfs_config *vfs_cfg);
|
110
tokyohook/config.c
Normal file
110
tokyohook/config.c
Normal file
@ -0,0 +1,110 @@
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "board/config.h"
|
||||
|
||||
#include "gfxhook/config.h"
|
||||
|
||||
#include "hooklib/config.h"
|
||||
#include "hooklib/dvd.h"
|
||||
|
||||
#include "platform/config.h"
|
||||
|
||||
#include "tokyohook/config.h"
|
||||
|
||||
void tokyo_dll_config_load(
|
||||
struct tokyo_dll_config *cfg,
|
||||
const wchar_t *filename) {
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"tokyoio",
|
||||
L"path",
|
||||
L"",
|
||||
cfg->path,
|
||||
_countof(cfg->path),
|
||||
filename);
|
||||
}
|
||||
|
||||
void led15093_config_load(struct led15093_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
wchar_t tmpstr[16];
|
||||
|
||||
memset(cfg->board_number, ' ', sizeof(cfg->board_number));
|
||||
memset(cfg->chip_number, ' ', sizeof(cfg->chip_number));
|
||||
memset(cfg->boot_chip_number, ' ', sizeof(cfg->boot_chip_number));
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"led15093", L"enable", 1, filename);
|
||||
cfg->port_no = GetPrivateProfileIntW(L"led15093", L"portNo", 0, filename);
|
||||
cfg->high_baudrate = GetPrivateProfileIntW(L"led15093", L"highBaud", 0, filename);
|
||||
cfg->fw_ver = GetPrivateProfileIntW(L"led15093", L"fwVer", 0x90, filename);
|
||||
cfg->fw_sum = GetPrivateProfileIntW(L"led15093", L"fwSum", 0xAED9, filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"led15093",
|
||||
L"boardNumber",
|
||||
L"15093-04",
|
||||
tmpstr,
|
||||
_countof(tmpstr),
|
||||
filename);
|
||||
|
||||
size_t n = wcstombs(cfg->board_number, tmpstr, sizeof(cfg->board_number));
|
||||
for (int i = n; i < sizeof(cfg->board_number); i++)
|
||||
{
|
||||
cfg->board_number[i] = ' ';
|
||||
}
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"led15093",
|
||||
L"chipNumber",
|
||||
L"6704 ",
|
||||
tmpstr,
|
||||
_countof(tmpstr),
|
||||
filename);
|
||||
|
||||
n = wcstombs(cfg->chip_number, tmpstr, sizeof(cfg->chip_number));
|
||||
for (int i = n; i < sizeof(cfg->chip_number); i++)
|
||||
{
|
||||
cfg->chip_number[i] = ' ';
|
||||
}
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"led15093",
|
||||
L"bootChipNumber",
|
||||
L"6709 ",
|
||||
tmpstr,
|
||||
_countof(tmpstr),
|
||||
filename);
|
||||
|
||||
n = wcstombs(cfg->boot_chip_number, tmpstr, sizeof(cfg->boot_chip_number));
|
||||
for (int i = n; i < sizeof(cfg->boot_chip_number); i++)
|
||||
{
|
||||
cfg->boot_chip_number[i] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
void zinput_config_load(struct zinput_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"zinput", L"enable", 1, filename);
|
||||
}
|
||||
|
||||
void tokyo_hook_config_load(
|
||||
struct tokyo_hook_config *cfg,
|
||||
const wchar_t *filename) {
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
platform_config_load(&cfg->platform, filename);
|
||||
dvd_config_load(&cfg->dvd, filename);
|
||||
io4_config_load(&cfg->io4, filename);
|
||||
zinput_config_load(&cfg->zinput, filename);
|
||||
led15093_config_load(&cfg->led15093, filename);
|
||||
tokyo_dll_config_load(&cfg->dll, filename);
|
||||
}
|
30
tokyohook/config.h
Normal file
30
tokyohook/config.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "board/config.h"
|
||||
#include "board/led15093.h"
|
||||
|
||||
#include "hooklib/dvd.h"
|
||||
|
||||
#include "tokyohook/tokyo-dll.h"
|
||||
#include "tokyohook/zinput.h"
|
||||
|
||||
#include "platform/config.h"
|
||||
|
||||
struct tokyo_hook_config {
|
||||
struct platform_config platform;
|
||||
struct dvd_config dvd;
|
||||
struct io4_config io4;
|
||||
struct led15093_config led15093;
|
||||
struct zinput_config zinput;
|
||||
struct tokyo_dll_config dll;
|
||||
};
|
||||
|
||||
void tokyo_dll_config_load(
|
||||
struct tokyo_dll_config *cfg,
|
||||
const wchar_t *filename);
|
||||
|
||||
void tokyo_hook_config_load(
|
||||
struct tokyo_hook_config *cfg,
|
||||
const wchar_t *filename);
|
112
tokyohook/dllmain.c
Normal file
112
tokyohook/dllmain.c
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
"Mario & Sonic at the Tokyo 2020 Olympics Arcade" (tokyo) hook
|
||||
|
||||
Devices
|
||||
|
||||
USB: 837-15257 "Type 4" I/O Board
|
||||
COM1: 837-15093-04 LED Controller Board
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "board/io4.h"
|
||||
|
||||
#include "hook/process.h"
|
||||
|
||||
#include "hooklib/dvd.h"
|
||||
#include "hooklib/serial.h"
|
||||
#include "hooklib/spike.h"
|
||||
|
||||
#include "tokyohook/config.h"
|
||||
#include "tokyohook/io4.h"
|
||||
#include "tokyohook/tokyo-dll.h"
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static HMODULE tokyo_hook_mod;
|
||||
static process_entry_t tokyo_startup;
|
||||
static struct tokyo_hook_config tokyo_hook_cfg;
|
||||
|
||||
static DWORD CALLBACK tokyo_pre_startup(void)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
dprintf("--- Begin tokyo_pre_startup ---\n");
|
||||
|
||||
/* Load config */
|
||||
|
||||
tokyo_hook_config_load(&tokyo_hook_cfg, L".\\segatools.ini");
|
||||
|
||||
/* Hook Win32 APIs */
|
||||
|
||||
dvd_hook_init(&tokyo_hook_cfg.dvd, tokyo_hook_mod);
|
||||
zinput_hook_init(&tokyo_hook_cfg.zinput);
|
||||
serial_hook_init();
|
||||
|
||||
/* Initialize emulation hooks */
|
||||
|
||||
hr = platform_hook_init(
|
||||
&tokyo_hook_cfg.platform,
|
||||
"SDFV",
|
||||
"ACA1",
|
||||
tokyo_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = tokyo_dll_init(&tokyo_hook_cfg.dll, tokyo_hook_mod);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = led15093_hook_init(&tokyo_hook_cfg.led15093,
|
||||
tokyo_dll.led_init, tokyo_dll.led_set_leds, 1, 1, 1, 2);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = tokyo_io4_hook_init(&tokyo_hook_cfg.io4);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Initialize debug helpers */
|
||||
|
||||
spike_hook_init(L".\\segatools.ini");
|
||||
|
||||
dprintf("--- End tokyo_pre_startup ---\n");
|
||||
|
||||
/* Jump to EXE start address */
|
||||
|
||||
return tokyo_startup();
|
||||
|
||||
fail:
|
||||
ExitProcess(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (cause != DLL_PROCESS_ATTACH) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
tokyo_hook_mod = mod;
|
||||
|
||||
hr = process_hijack_startup(tokyo_pre_startup, &tokyo_startup);
|
||||
|
||||
if (!SUCCEEDED(hr)) {
|
||||
dprintf("Failed to hijack process startup: %x\n", (int) hr);
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
}
|
163
tokyohook/io4.c
Normal file
163
tokyohook/io4.c
Normal file
@ -0,0 +1,163 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "board/io4.h"
|
||||
|
||||
#include "tokyohook/tokyo-dll.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static HRESULT tokyo_io4_poll(void *ctx, struct io4_state *state);
|
||||
static HRESULT tokyo_io4_write_gpio(uint8_t* payload, size_t len);
|
||||
|
||||
static uint16_t coins;
|
||||
|
||||
static const struct io4_ops tokyo_io4_ops = {
|
||||
.poll = tokyo_io4_poll,
|
||||
.write_gpio = tokyo_io4_write_gpio,
|
||||
};
|
||||
|
||||
HRESULT tokyo_io4_hook_init(const struct io4_config *cfg)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(tokyo_dll.init != NULL);
|
||||
|
||||
hr = io4_hook_init(cfg, &tokyo_io4_ops, NULL);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return tokyo_dll.init();
|
||||
}
|
||||
|
||||
static HRESULT tokyo_io4_poll(void *ctx, struct io4_state *state)
|
||||
{
|
||||
uint8_t opbtn;
|
||||
uint8_t gamebtn;
|
||||
uint8_t sense;
|
||||
HRESULT hr;
|
||||
|
||||
assert(tokyo_dll.get_opbtns != NULL);
|
||||
assert(tokyo_dll.get_gamebtns != NULL);
|
||||
assert(tokyo_dll.get_sensors != NULL);
|
||||
|
||||
memset(state, 0, sizeof(*state));
|
||||
|
||||
opbtn = 0;
|
||||
gamebtn = 0;
|
||||
sense = 0;
|
||||
|
||||
tokyo_dll.get_opbtns(&opbtn);
|
||||
tokyo_dll.get_gamebtns(&gamebtn);
|
||||
tokyo_dll.get_sensors(&sense);
|
||||
|
||||
if (opbtn & TOKYO_IO_OPBTN_TEST) {
|
||||
state->buttons[0] |= IO4_BUTTON_TEST;
|
||||
}
|
||||
|
||||
if (opbtn & TOKYO_IO_OPBTN_SERVICE) {
|
||||
state->buttons[0] |= IO4_BUTTON_SERVICE;
|
||||
}
|
||||
|
||||
if (opbtn & TOKYO_IO_OPBTN_COIN) {
|
||||
coins++;
|
||||
}
|
||||
state->chutes[0] = coins << 8;
|
||||
|
||||
/* Update gamebtns */
|
||||
|
||||
if (gamebtn & TOKYO_IO_GAMEBTN_BLUE) {
|
||||
state->buttons[0] |= 1 << 1;
|
||||
}
|
||||
|
||||
if (gamebtn & TOKYO_IO_GAMEBTN_YELLOW) {
|
||||
state->buttons[0] |= 1 << 0;
|
||||
}
|
||||
|
||||
if (gamebtn & TOKYO_IO_GAMEBTN_RED) {
|
||||
state->buttons[0] |= 1 << 15;
|
||||
}
|
||||
|
||||
/* Update sensors */
|
||||
|
||||
// Invert the logic so that it's active high
|
||||
if (!(sense & TOKYO_IO_SENSE_FOOT_LEFT)) {
|
||||
state->buttons[0] |= 1 << 13;
|
||||
}
|
||||
|
||||
if (!(sense & TOKYO_IO_SENSE_FOOT_RIGHT)) {
|
||||
state->buttons[1] |= 1 << 13;
|
||||
}
|
||||
|
||||
if (sense & TOKYO_IO_SENSE_JUMP_1) {
|
||||
state->buttons[0] |= 1 << 12;
|
||||
}
|
||||
|
||||
if (sense & TOKYO_IO_SENSE_JUMP_2) {
|
||||
state->buttons[1] |= 1 << 12;
|
||||
}
|
||||
|
||||
if (sense & TOKYO_IO_SENSE_JUMP_3) {
|
||||
state->buttons[0] |= 1 << 11;
|
||||
}
|
||||
|
||||
if (sense & TOKYO_IO_SENSE_JUMP_4) {
|
||||
state->buttons[1] |= 1 << 11;
|
||||
}
|
||||
|
||||
if (sense & TOKYO_IO_SENSE_JUMP_5) {
|
||||
state->buttons[0] |= 1 << 10;
|
||||
}
|
||||
|
||||
if (sense & TOKYO_IO_SENSE_JUMP_6) {
|
||||
state->buttons[1] |= 1 << 10;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT tokyo_io4_write_gpio(uint8_t* payload, size_t len)
|
||||
{
|
||||
// Just fast fail if there aren't enough bytes in the payload
|
||||
if (len < 3)
|
||||
return S_OK;
|
||||
|
||||
// This command is used for lights in Mario & Sonic at the Tokyo 2020 Olympics
|
||||
// Arcade, but it only contains button lights, and only in the first 3 bytes of
|
||||
// the payload; everything else is padding to make the payload 62 bytes. The
|
||||
// rest of the cabinet lights and the side button lights are handled separately,
|
||||
// by the 15093 lights controller.
|
||||
uint32_t lights_data = (uint32_t) ((uint8_t)(payload[0]) << 24 |
|
||||
(uint8_t)(payload[1]) << 16 |
|
||||
(uint8_t)(payload[2]) << 8);
|
||||
|
||||
// Since Sega uses an odd ordering for the first part of the bitfield,
|
||||
// let's normalize the data and just send over bytes for the receiver
|
||||
// to interpret as RGB values.
|
||||
uint8_t rgb_out[5 * 3] = {
|
||||
lights_data & TOKYO_IO_LED_LEFT_BLUE ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_CENTER_YELLOW ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_RIGHT_RED ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_CONTROL_LEFT_R ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_CONTROL_LEFT_G ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_CONTROL_LEFT_B ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_CONTROL_RIGHT_R ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_CONTROL_RIGHT_G ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_CONTROL_RIGHT_B ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_FLOOR_LEFT_R ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_FLOOR_LEFT_G ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_FLOOR_LEFT_B ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_FLOOR_RIGHT_R ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_FLOOR_RIGHT_G ? 0xFF : 0x00,
|
||||
lights_data & TOKYO_IO_LED_FLOOR_RIGHT_B ? 0xFF : 0x00,
|
||||
};
|
||||
|
||||
tokyo_dll.led_set_leds(1, rgb_out);
|
||||
|
||||
return S_OK;
|
||||
}
|
7
tokyohook/io4.h
Normal file
7
tokyohook/io4.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "board/io4.h"
|
||||
|
||||
HRESULT tokyo_io4_hook_init(const struct io4_config *cfg);
|
32
tokyohook/meson.build
Normal file
32
tokyohook/meson.build
Normal file
@ -0,0 +1,32 @@
|
||||
shared_library(
|
||||
'tokyohook',
|
||||
name_prefix : '',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
vs_module_defs : 'tokyohook.def',
|
||||
c_pch : '../precompiled.h',
|
||||
dependencies : [
|
||||
capnhook.get_variable('hook_dep'),
|
||||
capnhook.get_variable('hooklib_dep'),
|
||||
xinput_lib,
|
||||
],
|
||||
link_with : [
|
||||
aimeio_lib,
|
||||
board_lib,
|
||||
hooklib_lib,
|
||||
tokyoio_lib,
|
||||
platform_lib,
|
||||
util_lib,
|
||||
],
|
||||
sources : [
|
||||
'config.c',
|
||||
'config.h',
|
||||
'dllmain.c',
|
||||
'io4.c',
|
||||
'io4.h',
|
||||
'zinput.c',
|
||||
'zinput.h',
|
||||
'tokyo-dll.c',
|
||||
'tokyo-dll.h',
|
||||
],
|
||||
)
|
115
tokyohook/tokyo-dll.c
Normal file
115
tokyohook/tokyo-dll.c
Normal file
@ -0,0 +1,115 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tokyohook/tokyo-dll.h"
|
||||
|
||||
#include "util/dll-bind.h"
|
||||
#include "util/dprintf.h"
|
||||
|
||||
const struct dll_bind_sym tokyo_dll_syms[] = {
|
||||
{
|
||||
.sym = "tokyo_io_init",
|
||||
.off = offsetof(struct tokyo_dll, init),
|
||||
}, {
|
||||
.sym = "tokyo_io_get_opbtns",
|
||||
.off = offsetof(struct tokyo_dll, get_opbtns),
|
||||
}, {
|
||||
.sym = "tokyo_io_get_gamebtns",
|
||||
.off = offsetof(struct tokyo_dll, get_gamebtns),
|
||||
}, {
|
||||
.sym = "tokyo_io_get_sensors",
|
||||
.off = offsetof(struct tokyo_dll, get_sensors),
|
||||
}, {
|
||||
.sym = "tokyo_io_led_init",
|
||||
.off = offsetof(struct tokyo_dll, led_init),
|
||||
}, {
|
||||
.sym = "tokyo_io_led_set_colors",
|
||||
.off = offsetof(struct tokyo_dll, led_set_leds),
|
||||
}
|
||||
};
|
||||
|
||||
struct tokyo_dll tokyo_dll;
|
||||
|
||||
// Copypasta DLL binding and diagnostic message boilerplate.
|
||||
// Not much of this lends itself to being easily factored out. Also there
|
||||
// will be a lot of API-specific branching code here eventually as new API
|
||||
// versions get defined, so even though these functions all look the same
|
||||
// now this won't remain the case forever.
|
||||
|
||||
HRESULT tokyo_dll_init(const struct tokyo_dll_config *cfg, HINSTANCE self)
|
||||
{
|
||||
uint16_t (*get_api_version)(void);
|
||||
const struct dll_bind_sym *sym;
|
||||
HINSTANCE owned;
|
||||
HINSTANCE src;
|
||||
HRESULT hr;
|
||||
|
||||
assert(cfg != NULL);
|
||||
assert(self != NULL);
|
||||
|
||||
if (cfg->path[0] != L'\0') {
|
||||
owned = LoadLibraryW(cfg->path);
|
||||
|
||||
if (owned == NULL) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("Tokyo IO: Failed to load IO DLL: %lx: %S\n",
|
||||
hr,
|
||||
cfg->path);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
dprintf("Tokyo IO: Using custom IO DLL: %S\n", cfg->path);
|
||||
src = owned;
|
||||
} else {
|
||||
owned = NULL;
|
||||
src = self;
|
||||
}
|
||||
|
||||
get_api_version = (void *) GetProcAddress(src, "tokyo_io_get_api_version");
|
||||
|
||||
if (get_api_version != NULL) {
|
||||
tokyo_dll.api_version = get_api_version();
|
||||
} else {
|
||||
tokyo_dll.api_version = 0x0100;
|
||||
dprintf("Custom IO DLL does not expose tokyo_io_get_api_version, "
|
||||
"assuming API version 1.0.\n"
|
||||
"Please ask the developer to update their DLL.\n");
|
||||
}
|
||||
|
||||
if (tokyo_dll.api_version >= 0x0200) {
|
||||
hr = E_NOTIMPL;
|
||||
dprintf("Tokyo IO: Custom IO DLL implements an unsupported "
|
||||
"API version (%#04x). Please update Segatools.\n",
|
||||
tokyo_dll.api_version);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
sym = tokyo_dll_syms;
|
||||
hr = dll_bind(&tokyo_dll, src, &sym, _countof(tokyo_dll_syms));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
if (src != self) {
|
||||
dprintf("Tokyo IO: Custom IO DLL does not provide function "
|
||||
"\"%s\". Please contact your IO DLL's developer for "
|
||||
"further assistance.\n",
|
||||
sym->sym);
|
||||
|
||||
goto end;
|
||||
} else {
|
||||
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
|
||||
}
|
||||
}
|
||||
|
||||
owned = NULL;
|
||||
|
||||
end:
|
||||
if (owned != NULL) {
|
||||
FreeLibrary(owned);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
24
tokyohook/tokyo-dll.h
Normal file
24
tokyohook/tokyo-dll.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "tokyoio/tokyoio.h"
|
||||
|
||||
struct tokyo_dll {
|
||||
uint16_t api_version;
|
||||
HRESULT (*init)(void);
|
||||
void (*get_opbtns)(uint8_t *opbtn);
|
||||
void (*get_gamebtns)(uint8_t *gamebtn);
|
||||
void (*get_sensors)(uint8_t *sense);
|
||||
HRESULT (*gpio_out)(uint32_t state);
|
||||
HRESULT (*led_init)(void);
|
||||
void (*led_set_leds)(uint8_t board, uint8_t *rgb);
|
||||
};
|
||||
|
||||
struct tokyo_dll_config {
|
||||
wchar_t path[MAX_PATH];
|
||||
};
|
||||
|
||||
extern struct tokyo_dll tokyo_dll;
|
||||
|
||||
HRESULT tokyo_dll_init(const struct tokyo_dll_config *cfg, HINSTANCE self);
|
20
tokyohook/tokyohook.def
Normal file
20
tokyohook/tokyohook.def
Normal file
@ -0,0 +1,20 @@
|
||||
LIBRARY tokyohook
|
||||
|
||||
EXPORTS
|
||||
aime_io_get_api_version
|
||||
aime_io_init
|
||||
aime_io_led_set_color
|
||||
aime_io_nfc_get_aime_id
|
||||
aime_io_nfc_get_felica_id
|
||||
aime_io_nfc_poll
|
||||
amDllVideoClose @2
|
||||
amDllVideoGetVBiosVersion @4
|
||||
amDllVideoOpen @1
|
||||
amDllVideoSetResolution @3
|
||||
tokyo_io_get_api_version
|
||||
tokyo_io_init
|
||||
tokyo_io_get_opbtns
|
||||
tokyo_io_get_gamebtns
|
||||
tokyo_io_get_sensors
|
||||
tokyo_io_led_init
|
||||
tokyo_io_led_set_colors
|
118
tokyohook/zinput.c
Normal file
118
tokyohook/zinput.c
Normal file
@ -0,0 +1,118 @@
|
||||
#include <windows.h>
|
||||
#include <shlwapi.h>
|
||||
#include <dinput.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tokyohook/config.h"
|
||||
#include "tokyohook/zinput.h"
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "util/lib.h"
|
||||
#include "util/dprintf.h"
|
||||
|
||||
HRESULT WINAPI hook_DirectInput8Create(
|
||||
HINSTANCE hinst,
|
||||
DWORD dwVersion,
|
||||
REFIID riidltf,
|
||||
LPVOID *ppvOut,
|
||||
LPUNKNOWN punkOuter);
|
||||
|
||||
static HRESULT WINAPI hook_EnumDevices(
|
||||
IDirectInput8W *self,
|
||||
DWORD dwDevType,
|
||||
LPDIENUMDEVICESCALLBACKW lpCallback,
|
||||
LPVOID pvRef,
|
||||
DWORD dwFlags);
|
||||
|
||||
static unsigned long WINAPI hook_AddRef(IUnknown *self);
|
||||
static unsigned long WINAPI hook_Release(IUnknown *self);
|
||||
|
||||
static const IDirectInput8WVtbl api_vtbl = {
|
||||
.EnumDevices = hook_EnumDevices,
|
||||
.AddRef = (void *) hook_AddRef,
|
||||
.Release = (void *) hook_Release,
|
||||
};
|
||||
|
||||
static const IDirectInput8W api = { (void *) &api_vtbl };
|
||||
|
||||
static const struct hook_symbol zinput_hook_syms[] = {
|
||||
{
|
||||
.name = "DirectInput8Create",
|
||||
.patch = hook_DirectInput8Create,
|
||||
.link = NULL,
|
||||
}
|
||||
};
|
||||
|
||||
HRESULT zinput_hook_init(struct zinput_config *cfg)
|
||||
{
|
||||
wchar_t *module_path;
|
||||
wchar_t *file_name;
|
||||
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
module_path = module_file_name(NULL);
|
||||
|
||||
if (module_path != NULL) {
|
||||
file_name = PathFindFileNameW(module_path);
|
||||
|
||||
free(module_path);
|
||||
module_path = NULL;
|
||||
|
||||
_wcslwr(file_name);
|
||||
|
||||
if (wcsstr(file_name, L"amdaemon") != NULL) {
|
||||
// dprintf("Executable filename contains 'amdaemon', disabling zinput\n");
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"dinput8.dll",
|
||||
zinput_hook_syms,
|
||||
_countof(zinput_hook_syms));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT WINAPI hook_DirectInput8Create(
|
||||
HINSTANCE hinst,
|
||||
DWORD dwVersion,
|
||||
REFIID riidltf,
|
||||
LPVOID *ppvOut,
|
||||
LPUNKNOWN punkOuter)
|
||||
{
|
||||
dprintf("ZInput: Blocking built-in DirectInput support\n");
|
||||
*ppvOut = (void *) &api;
|
||||
|
||||
return DI_OK;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI hook_EnumDevices(
|
||||
IDirectInput8W *self,
|
||||
DWORD dwDevType,
|
||||
LPDIENUMDEVICESCALLBACKW lpCallback,
|
||||
LPVOID pvRef,
|
||||
DWORD dwFlags)
|
||||
{
|
||||
dprintf("ZInput: %s\n", __func__);
|
||||
|
||||
return DI_OK;
|
||||
}
|
||||
|
||||
static unsigned long WINAPI hook_AddRef(IUnknown *self)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static unsigned long WINAPI hook_Release(IUnknown *self)
|
||||
{
|
||||
return 1;
|
||||
}
|
11
tokyohook/zinput.h
Normal file
11
tokyohook/zinput.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct zinput_config {
|
||||
bool enable;
|
||||
};
|
||||
|
||||
HRESULT zinput_hook_init(struct zinput_config *cfg);
|
10
tokyoio/backend.h
Normal file
10
tokyoio/backend.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tokyoio/tokyoio.h"
|
||||
|
||||
struct tokyo_io_backend {
|
||||
void (*get_gamebtns)(uint8_t *gamebtn);
|
||||
void (*get_sensors)(uint8_t *sense);
|
||||
};
|
54
tokyoio/config.c
Normal file
54
tokyoio/config.c
Normal file
@ -0,0 +1,54 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "tokyoio/config.h"
|
||||
|
||||
|
||||
void tokyo_kb_config_load(
|
||||
struct tokyo_kb_config *cfg,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
/* Load game button keyboard bindings */
|
||||
cfg->vk_push_left_b = GetPrivateProfileIntW(L"keyboard", L"leftBlue", 'A', filename);
|
||||
cfg->vk_push_center_y = GetPrivateProfileIntW(L"keyboard", L"centerYellow", 'S', filename);
|
||||
cfg->vk_push_right_r = GetPrivateProfileIntW(L"keyboard", L"rightRed", 'D', filename);
|
||||
|
||||
/* Load sensor keyboard bindings */
|
||||
cfg->vk_foot_l = GetPrivateProfileIntW(L"keyboard", L"footLeft", VK_LEFT, filename);
|
||||
cfg->vk_foot_r = GetPrivateProfileIntW(L"keyboard", L"footRight", VK_RIGHT, filename);
|
||||
cfg->vk_jump_1 = GetPrivateProfileIntW(L"keyboard", L"jump1", 'Z', filename);
|
||||
cfg->vk_jump_2 = GetPrivateProfileIntW(L"keyboard", L"jump2", 'X', filename);
|
||||
cfg->vk_jump_3 = GetPrivateProfileIntW(L"keyboard", L"jump3", 'C', filename);
|
||||
cfg->vk_jump_4 = GetPrivateProfileIntW(L"keyboard", L"jump4", 'B', filename);
|
||||
cfg->vk_jump_5 = GetPrivateProfileIntW(L"keyboard", L"jump5", 'N', filename);
|
||||
cfg->vk_jump_6 = GetPrivateProfileIntW(L"keyboard", L"jump6", 'M', filename);
|
||||
cfg->vk_jump_all = GetPrivateProfileIntW(L"keyboard", L"jumpAll", VK_SPACE, filename);
|
||||
}
|
||||
|
||||
void tokyo_io_config_load(
|
||||
struct tokyo_io_config *cfg,
|
||||
const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->vk_test = GetPrivateProfileIntW(L"io4", L"test", '1', filename);
|
||||
cfg->vk_service = GetPrivateProfileIntW(L"io4", L"service", '2', filename);
|
||||
cfg->vk_coin = GetPrivateProfileIntW(L"io4", L"coin", '3', filename);
|
||||
|
||||
GetPrivateProfileStringW(
|
||||
L"io4",
|
||||
L"mode",
|
||||
L"xinput",
|
||||
cfg->mode,
|
||||
_countof(cfg->mode),
|
||||
filename);
|
||||
|
||||
tokyo_kb_config_load(&cfg->kb, filename);
|
||||
}
|
34
tokyoio/config.h
Normal file
34
tokyoio/config.h
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct tokyo_kb_config {
|
||||
uint8_t vk_push_left_b;
|
||||
uint8_t vk_push_center_y;
|
||||
uint8_t vk_push_right_r;
|
||||
uint8_t vk_foot_l;
|
||||
uint8_t vk_foot_r;
|
||||
uint8_t vk_jump_1;
|
||||
uint8_t vk_jump_2;
|
||||
uint8_t vk_jump_3;
|
||||
uint8_t vk_jump_4;
|
||||
uint8_t vk_jump_5;
|
||||
uint8_t vk_jump_6;
|
||||
uint8_t vk_jump_all;
|
||||
};
|
||||
|
||||
struct tokyo_io_config {
|
||||
uint8_t vk_test;
|
||||
uint8_t vk_service;
|
||||
uint8_t vk_coin;
|
||||
wchar_t mode[9];
|
||||
|
||||
struct tokyo_kb_config kb;
|
||||
};
|
||||
|
||||
void tokyo_io_config_load(
|
||||
struct tokyo_io_config *cfg,
|
||||
const wchar_t *filename);
|
135
tokyoio/dllmain.c
Normal file
135
tokyoio/dllmain.c
Normal file
@ -0,0 +1,135 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tokyoio/backend.h"
|
||||
#include "tokyoio/config.h"
|
||||
#include "tokyoio/kb.h"
|
||||
#include "tokyoio/tokyoio.h"
|
||||
#include "tokyoio/xi.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static struct tokyo_io_config tokyo_io_cfg;
|
||||
static const struct tokyo_io_backend *tokyo_io_backend;
|
||||
static bool tokyo_io_coin;
|
||||
|
||||
uint16_t tokyo_io_get_api_version(void)
|
||||
{
|
||||
return 0x0100;
|
||||
}
|
||||
|
||||
HRESULT tokyo_io_init(void)
|
||||
{
|
||||
HINSTANCE inst;
|
||||
HRESULT hr;
|
||||
|
||||
assert(tokyo_io_backend == NULL);
|
||||
|
||||
inst = GetModuleHandleW(NULL);
|
||||
|
||||
if (inst == NULL) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
dprintf("GetModuleHandleW failed: %lx\n", hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
tokyo_io_config_load(&tokyo_io_cfg, L".\\segatools.ini");
|
||||
|
||||
if (wstr_ieq(tokyo_io_cfg.mode, L"keyboard")) {
|
||||
hr = tokyo_kb_init(&tokyo_io_cfg.kb, &tokyo_io_backend);
|
||||
} else if (wstr_ieq(tokyo_io_cfg.mode, L"xinput")) {
|
||||
hr = tokyo_xi_init(&tokyo_io_backend);
|
||||
} else {
|
||||
hr = E_INVALIDARG;
|
||||
dprintf("IDAC IO: Invalid IO mode \"%S\", use keyboard or xinput\n",
|
||||
tokyo_io_cfg.mode);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
void tokyo_io_get_opbtns(uint8_t *opbtn_out)
|
||||
{
|
||||
uint8_t opbtn;
|
||||
|
||||
assert(tokyo_io_backend != NULL);
|
||||
assert(opbtn_out != NULL);
|
||||
|
||||
opbtn = 0;
|
||||
|
||||
/* Common operator buttons, not backend-specific */
|
||||
|
||||
if (GetAsyncKeyState(tokyo_io_cfg.vk_test) & 0x8000) {
|
||||
opbtn |= TOKYO_IO_OPBTN_TEST;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_io_cfg.vk_service) & 0x8000) {
|
||||
opbtn |= TOKYO_IO_OPBTN_SERVICE;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_io_cfg.vk_coin) & 0x8000) {
|
||||
if (!tokyo_io_coin) {
|
||||
tokyo_io_coin = true;
|
||||
opbtn |= TOKYO_IO_OPBTN_COIN;
|
||||
}
|
||||
} else {
|
||||
tokyo_io_coin = false;
|
||||
}
|
||||
|
||||
*opbtn_out = opbtn;
|
||||
}
|
||||
|
||||
|
||||
void tokyo_io_get_gamebtns(uint8_t *gamebtn_out)
|
||||
{
|
||||
assert(tokyo_io_backend != NULL);
|
||||
assert(gamebtn_out != NULL);
|
||||
|
||||
tokyo_io_backend->get_gamebtns(gamebtn_out);
|
||||
}
|
||||
|
||||
void tokyo_io_get_sensors(uint8_t *sense_out)
|
||||
{
|
||||
assert(sense_out != NULL);
|
||||
assert(tokyo_io_backend != NULL);
|
||||
|
||||
tokyo_io_backend->get_sensors(sense_out);
|
||||
}
|
||||
|
||||
HRESULT tokyo_io_led_init(void)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void tokyo_io_led_set_colors(uint8_t board, uint8_t *rgb)
|
||||
{
|
||||
#if 0
|
||||
if (board == 0) {
|
||||
dprintf("Board 0:\n");
|
||||
// Change GRB order to RGB order
|
||||
for (int i = 0; i < 27; i++) {
|
||||
dprintf("Tokyo LED: MONITOR LEFT: %02X %02X %02X\n", rgb[i * 3 + 1], rgb[i * 3], rgb[i * 3 + 2]);
|
||||
}
|
||||
|
||||
for (int i = 27; i < 54; i++) {
|
||||
dprintf("Tokyo LED: MONITOR RIGHT: %d, %02X %02X %02X\n", i, rgb[i * 3 + 1], rgb[i * 3], rgb[i * 3 + 2]);
|
||||
}
|
||||
} else {
|
||||
dprintf("Board 1:\n");
|
||||
dprintf("Tokyo LED: LEFT BLUE: %02X\n", rgb[0]);
|
||||
dprintf("Tokyo LED: CENTER YELLOW: %02X\n", rgb[1]);
|
||||
dprintf("Tokyo LED: RIGHT RED: %02X\n", rgb[2]);
|
||||
dprintf("Tokyo LED: CONTROL LEFT: %02X %02X %02X\n", rgb[3], rgb[4], rgb[5]);
|
||||
dprintf("Tokyo LED: CONTROL RIGHT: %02X %02X %02X\n", rgb[6], rgb[7], rgb[8]);
|
||||
dprintf("Tokyo LED: FLOOR LEFT: %02X %02X %02X\n", rgb[9], rgb[10], rgb[11]);
|
||||
dprintf("Tokyo LED: FLOOR RIGHT: %02X %02X %02X\n", rgb[12], rgb[13], rgb[14]);
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
135
tokyoio/kb.c
Normal file
135
tokyoio/kb.c
Normal file
@ -0,0 +1,135 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tokyoio/backend.h"
|
||||
#include "tokyoio/config.h"
|
||||
#include "tokyoio/kb.h"
|
||||
#include "tokyoio/tokyoio.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static HRESULT tokyo_kb_config_apply(const struct tokyo_kb_config *cfg);
|
||||
|
||||
static void tokyo_kb_get_gamebtns(uint8_t *gamebtn_out);
|
||||
static void tokyo_kb_get_sensors(uint8_t *sense_out);
|
||||
|
||||
static const struct tokyo_io_backend tokyo_kb_backend = {
|
||||
.get_gamebtns = tokyo_kb_get_gamebtns,
|
||||
.get_sensors = tokyo_kb_get_sensors,
|
||||
};
|
||||
|
||||
static struct tokyo_kb_config tokyo_kb_cfg;
|
||||
|
||||
HRESULT tokyo_kb_init(const struct tokyo_kb_config *cfg, const struct tokyo_io_backend **backend)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(cfg != NULL);
|
||||
assert(backend != NULL);
|
||||
|
||||
hr = tokyo_kb_config_apply(cfg);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
dprintf("TokyoIO: Using keyboard input\n");
|
||||
*backend = &tokyo_kb_backend;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT tokyo_kb_config_apply(const struct tokyo_kb_config *cfg)
|
||||
{
|
||||
tokyo_kb_cfg = *cfg;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void tokyo_kb_get_gamebtns(uint8_t *gamebtn_out)
|
||||
{
|
||||
uint8_t gamebtn;
|
||||
|
||||
assert(gamebtn_out != NULL);
|
||||
|
||||
gamebtn = 0;
|
||||
|
||||
/* PUSH BUTTON inputs */
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_push_left_b) & 0x8000) {
|
||||
gamebtn |= TOKYO_IO_GAMEBTN_BLUE;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_push_center_y) & 0x8000) {
|
||||
gamebtn |= TOKYO_IO_GAMEBTN_YELLOW;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_push_right_r) & 0x8000) {
|
||||
gamebtn |= TOKYO_IO_GAMEBTN_RED;
|
||||
}
|
||||
|
||||
*gamebtn_out = gamebtn;
|
||||
}
|
||||
|
||||
static void tokyo_kb_get_sensors(uint8_t *sense_out)
|
||||
{
|
||||
uint8_t sense;
|
||||
|
||||
assert(sense_out != NULL);
|
||||
|
||||
sense = 0;
|
||||
|
||||
/* FOOT SENSOR inputs */
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_foot_l) & 0x8000) {
|
||||
sense |= TOKYO_IO_SENSE_FOOT_LEFT;
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_foot_r) & 0x8000) {
|
||||
sense |= TOKYO_IO_SENSE_FOOT_RIGHT;
|
||||
}
|
||||
|
||||
/* JUMP SENSOR inputs */
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_jump_1) & 0x8000) {
|
||||
sense |= (TOKYO_IO_SENSE_FOOT_LEFT + TOKYO_IO_SENSE_FOOT_RIGHT +
|
||||
TOKYO_IO_SENSE_JUMP_1);
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_jump_2) & 0x8000) {
|
||||
sense |= (TOKYO_IO_SENSE_FOOT_LEFT + TOKYO_IO_SENSE_FOOT_RIGHT +
|
||||
TOKYO_IO_SENSE_JUMP_2);
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_jump_3) & 0x8000) {
|
||||
sense |= (TOKYO_IO_SENSE_FOOT_LEFT + TOKYO_IO_SENSE_FOOT_RIGHT +
|
||||
TOKYO_IO_SENSE_JUMP_3);
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_jump_4) & 0x8000) {
|
||||
sense |= (TOKYO_IO_SENSE_FOOT_LEFT + TOKYO_IO_SENSE_FOOT_RIGHT +
|
||||
TOKYO_IO_SENSE_JUMP_4);
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_jump_5) & 0x8000) {
|
||||
sense |= (TOKYO_IO_SENSE_FOOT_LEFT + TOKYO_IO_SENSE_FOOT_RIGHT +
|
||||
TOKYO_IO_SENSE_JUMP_5);
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_jump_6) & 0x8000) {
|
||||
sense |= (TOKYO_IO_SENSE_FOOT_LEFT + TOKYO_IO_SENSE_FOOT_RIGHT +
|
||||
TOKYO_IO_SENSE_JUMP_6);
|
||||
}
|
||||
|
||||
if (GetAsyncKeyState(tokyo_kb_cfg.vk_jump_all) & 0x8000) {
|
||||
sense |= (TOKYO_IO_SENSE_FOOT_LEFT+ TOKYO_IO_SENSE_FOOT_RIGHT +
|
||||
TOKYO_IO_SENSE_JUMP_1 + TOKYO_IO_SENSE_JUMP_2 +
|
||||
TOKYO_IO_SENSE_JUMP_3 + TOKYO_IO_SENSE_JUMP_4 +
|
||||
TOKYO_IO_SENSE_JUMP_5 + TOKYO_IO_SENSE_JUMP_6);
|
||||
}
|
||||
|
||||
*sense_out = sense;
|
||||
}
|
10
tokyoio/kb.h
Normal file
10
tokyoio/kb.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "tokyoio/backend.h"
|
||||
#include "tokyoio/config.h"
|
||||
|
||||
HRESULT tokyo_kb_init(
|
||||
const struct tokyo_kb_config *cfg,
|
||||
const struct tokyo_io_backend **backend);
|
21
tokyoio/meson.build
Normal file
21
tokyoio/meson.build
Normal file
@ -0,0 +1,21 @@
|
||||
tokyoio_lib = static_library(
|
||||
'tokyoio',
|
||||
name_prefix : '',
|
||||
include_directories : inc,
|
||||
implicit_include_directories : false,
|
||||
c_pch : '../precompiled.h',
|
||||
dependencies : [
|
||||
xinput_lib,
|
||||
],
|
||||
sources : [
|
||||
'backend.h',
|
||||
'config.c',
|
||||
'config.h',
|
||||
'dllmain.c',
|
||||
'tokyoio.h',
|
||||
'kb.c',
|
||||
'kb.h',
|
||||
'xi.c',
|
||||
'xi.h',
|
||||
],
|
||||
)
|
139
tokyoio/tokyoio.h
Normal file
139
tokyoio/tokyoio.h
Normal file
@ -0,0 +1,139 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum {
|
||||
TOKYO_IO_OPBTN_TEST = 0x01,
|
||||
TOKYO_IO_OPBTN_SERVICE = 0x02,
|
||||
TOKYO_IO_OPBTN_COIN = 0x04,
|
||||
};
|
||||
|
||||
enum {
|
||||
TOKYO_IO_GAMEBTN_BLUE = 0x01,
|
||||
TOKYO_IO_GAMEBTN_YELLOW = 0x02,
|
||||
TOKYO_IO_GAMEBTN_RED = 0x04,
|
||||
};
|
||||
|
||||
enum {
|
||||
TOKYO_IO_SENSE_FOOT_LEFT = 0x01,
|
||||
TOKYO_IO_SENSE_FOOT_RIGHT = 0x02,
|
||||
TOKYO_IO_SENSE_JUMP_1 = 0x04,
|
||||
TOKYO_IO_SENSE_JUMP_2 = 0x08,
|
||||
TOKYO_IO_SENSE_JUMP_3 = 0x10,
|
||||
TOKYO_IO_SENSE_JUMP_4 = 0x20,
|
||||
TOKYO_IO_SENSE_JUMP_5 = 0x40,
|
||||
TOKYO_IO_SENSE_JUMP_6 = 0x80,
|
||||
};
|
||||
|
||||
enum {
|
||||
/* These are the bitmasks to use when checking which
|
||||
lights are triggered on incoming IO4 GPIO writes. */
|
||||
TOKYO_IO_LED_LEFT_BLUE = 1 << 31,
|
||||
TOKYO_IO_LED_CENTER_YELLOW = 1 << 30,
|
||||
TOKYO_IO_LED_RIGHT_RED = 1 << 29,
|
||||
TOKYO_IO_LED_CONTROL_LEFT_R = 1 << 25,
|
||||
TOKYO_IO_LED_CONTROL_LEFT_G = 1 << 24,
|
||||
TOKYO_IO_LED_CONTROL_LEFT_B = 1 << 23,
|
||||
TOKYO_IO_LED_CONTROL_RIGHT_R = 1 << 22,
|
||||
TOKYO_IO_LED_CONTROL_RIGHT_G = 1 << 21,
|
||||
TOKYO_IO_LED_CONTROL_RIGHT_B = 1 << 20,
|
||||
TOKYO_IO_LED_FLOOR_LEFT_R = 1 << 19,
|
||||
TOKYO_IO_LED_FLOOR_LEFT_G = 1 << 18,
|
||||
TOKYO_IO_LED_FLOOR_LEFT_B = 1 << 17,
|
||||
TOKYO_IO_LED_FLOOR_RIGHT_R = 1 << 16,
|
||||
TOKYO_IO_LED_FLOOR_RIGHT_G = 1 << 15,
|
||||
TOKYO_IO_LED_FLOOR_RIGHT_B = 1 << 14,
|
||||
};
|
||||
|
||||
/* Get the version of the Mario & Sonic at the Olympic Games Tokyo 2020 Arcade
|
||||
Edition 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 0x0100. */
|
||||
|
||||
uint16_t tokyo_io_get_api_version(void);
|
||||
|
||||
/* Initialize the IO DLL. This is the second function that will be called on
|
||||
your DLL, after tokyo_io_get_api_version.
|
||||
|
||||
All subsequent calls to this API may originate from arbitrary threads.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
HRESULT tokyo_io_init(void);
|
||||
|
||||
/* Send any queued outputs (of which there are currently none, though this may
|
||||
change in subsequent API versions) and retrieve any new inputs.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
HRESULT tokyo_io_poll(void);
|
||||
|
||||
/* Get the state of the cabinet's operator buttons as of the last poll. See
|
||||
TOKYO_IO_OPBTN enum above: this contains bit mask definitions for button
|
||||
states returned in *opbtn. All buttons are active-high.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
void tokyo_io_get_opbtns(uint8_t *opbtn);
|
||||
|
||||
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
|
||||
TOKYO_IO_GAMEBTN enum above: this contains bit mask definitions for button
|
||||
states returned in *gamebtn. All buttons are active-high.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
void tokyo_io_get_gamebtns(uint8_t *gamebtn);
|
||||
|
||||
/* Get the state of the cabinet's gameplay buttons as of the last poll. See
|
||||
TOKYO_IO_SENSE enum above: this contains bit mask definitions for button
|
||||
states returned in *sense. All buttons are active-high.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
void tokyo_io_get_sensors(uint8_t *sense);
|
||||
|
||||
/* Initialize LED emulation. This function will be called before any
|
||||
other tokyo_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: 0x0100 */
|
||||
|
||||
HRESULT tokyo_io_led_init(void);
|
||||
|
||||
/* Update the RGB LEDs. rgb is a pointer to an array of up to 54 * 3 = 162 bytes.
|
||||
|
||||
Mario & Sonic at the Tokyo 2020 Olympics Arcade uses one board with 15 LEDs for
|
||||
all buttons, control panel, and floor LEDs. Board 1 is just used for the
|
||||
left and right monitor LEDs.
|
||||
|
||||
Board 0 has 54 LEDs (GRB order):
|
||||
[0]-[26]: left monitor LEDs
|
||||
[27]-[53]: right monitor LEDs
|
||||
|
||||
Board 1 has 15 LEDs (RGB order):
|
||||
[0]: left blue LED
|
||||
[1]: center yellow LED
|
||||
[2]: right red LED
|
||||
[3]-[5]: left control panel LEDs
|
||||
[6]-[8]: right control panel LEDs
|
||||
[9]-[11]: left floor LEDs
|
||||
[12]-[14]: right floor LEDs
|
||||
|
||||
Each rgb value is comprised of 3 bytes in G,R,B order for board 0 and R,G,B
|
||||
order for board 1. The tricky part is that the board 0 is called from app and
|
||||
the board 1 is called from amdaemon. So the library must be able to handle both
|
||||
calls, using shared memory f.e. This is up to the developer to decide how to
|
||||
handle this, recommended way is to use the amdaemon process as the main one
|
||||
and the app process as a sub one.
|
||||
|
||||
Minimum API version: 0x0100 */
|
||||
|
||||
void tokyo_io_led_set_colors(uint8_t board, uint8_t *rgb);
|
130
tokyoio/xi.c
Normal file
130
tokyoio/xi.c
Normal file
@ -0,0 +1,130 @@
|
||||
#include <windows.h>
|
||||
#include <xinput.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tokyoio/backend.h"
|
||||
#include "tokyoio/config.h"
|
||||
#include "tokyoio/tokyoio.h"
|
||||
#include "tokyoio/xi.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static void tokyo_xi_get_gamebtns(uint8_t *gamebtn_out);
|
||||
static void tokyo_xi_get_sensors(uint8_t *sense_out);
|
||||
|
||||
static const struct tokyo_io_backend tokyo_xi_backend = {
|
||||
.get_gamebtns = tokyo_xi_get_gamebtns,
|
||||
.get_sensors = tokyo_xi_get_sensors,
|
||||
};
|
||||
|
||||
HRESULT tokyo_xi_init(const struct tokyo_io_backend **backend)
|
||||
{
|
||||
wchar_t dll_path[MAX_PATH];
|
||||
HMODULE xinput;
|
||||
HRESULT hr;
|
||||
UINT path_pos;
|
||||
|
||||
assert(backend != NULL);
|
||||
|
||||
dprintf("TokyoIO: IO4: Using XInput controller\n");
|
||||
*backend = &tokyo_xi_backend;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void tokyo_xi_get_gamebtns(uint8_t *gamebtn_out)
|
||||
{
|
||||
uint8_t gamebtn;
|
||||
|
||||
assert(gamebtn_out != NULL);
|
||||
|
||||
gamebtn = 0;
|
||||
|
||||
XINPUT_STATE xi;
|
||||
WORD xb;
|
||||
|
||||
memset(&xi, 0, sizeof(xi));
|
||||
XInputGetState(0, &xi);
|
||||
xb = xi.Gamepad.wButtons;
|
||||
|
||||
/* PUSH BUTTON inputs */
|
||||
|
||||
if ((xb & XINPUT_GAMEPAD_X) || (xb & XINPUT_GAMEPAD_DPAD_LEFT)) {
|
||||
gamebtn |= TOKYO_IO_GAMEBTN_BLUE;
|
||||
}
|
||||
|
||||
if (xb & XINPUT_GAMEPAD_Y || (xb & XINPUT_GAMEPAD_A)) {
|
||||
gamebtn |= TOKYO_IO_GAMEBTN_YELLOW;
|
||||
}
|
||||
|
||||
if ((xb & XINPUT_GAMEPAD_B) || (xb & XINPUT_GAMEPAD_DPAD_RIGHT)) {
|
||||
gamebtn |= TOKYO_IO_GAMEBTN_RED;
|
||||
}
|
||||
|
||||
*gamebtn_out = gamebtn;
|
||||
}
|
||||
|
||||
static void tokyo_xi_get_sensors(uint8_t *sense_out)
|
||||
{
|
||||
uint8_t sense;
|
||||
|
||||
XINPUT_STATE xi;
|
||||
WORD xb;
|
||||
BYTE xt_l;
|
||||
BYTE xt_r;
|
||||
|
||||
assert(sense_out != NULL);
|
||||
|
||||
sense = 0;
|
||||
|
||||
memset(&xi, 0, sizeof(xi));
|
||||
XInputGetState(0, &xi);
|
||||
xb = xi.Gamepad.wButtons;
|
||||
xt_l = xi.Gamepad.bLeftTrigger;
|
||||
xt_r = xi.Gamepad.bRightTrigger;
|
||||
|
||||
float xt_l_f = xt_l / 255.0f;
|
||||
float xt_r_f = xt_r / 255.0f;
|
||||
|
||||
// Normalize both triggers to 0..1 and find the max directly
|
||||
float trigger = fmaxf(xt_l_f, xt_r_f);
|
||||
|
||||
const int max_jump_levels = 6;
|
||||
float jump_threshold = 1.0f / max_jump_levels;
|
||||
|
||||
/* FOOT SENSOR inputs */
|
||||
|
||||
// Determine if both foot sensors should be set
|
||||
bool left_active = xt_l_f > jump_threshold;
|
||||
bool right_active = xt_r_f > jump_threshold;
|
||||
|
||||
// Set foot sensors based on individual trigger activity
|
||||
if (left_active) {
|
||||
sense |= TOKYO_IO_SENSE_FOOT_LEFT;
|
||||
}
|
||||
if (right_active) {
|
||||
sense |= TOKYO_IO_SENSE_FOOT_RIGHT;
|
||||
}
|
||||
|
||||
/* JUMP SENSOR inputs */
|
||||
|
||||
// If both triggers are active, set jump levels and both foot sensors
|
||||
if (left_active && right_active) {
|
||||
float trigger_avg = (xt_l_f + xt_r_f) / 2.0f;
|
||||
|
||||
// Calculate the appropriate jump level
|
||||
for (int i = 1; i <= max_jump_levels; ++i) {
|
||||
if (trigger_avg >= i * jump_threshold) {
|
||||
sense |= (TOKYO_IO_SENSE_JUMP_1 << (i - 1));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*sense_out = sense;
|
||||
}
|
8
tokyoio/xi.h
Normal file
8
tokyoio/xi.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "tokyoio/backend.h"
|
||||
#include "tokyoio/config.h"
|
||||
|
||||
HRESULT tokyo_xi_init(const struct tokyo_io_backend **backend);
|
@ -1,6 +1,8 @@
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "platform/clock.h"
|
||||
|
||||
#include "hook/table.h"
|
||||
#include "hook/procaddr.h"
|
||||
#include "hook/iohook.h"
|
||||
@ -139,11 +141,14 @@ static HMODULE WINAPI hook_LoadLibraryW(const wchar_t *name)
|
||||
dll_hook_insert_hooks(result);
|
||||
path_hook_insert_hooks(result);
|
||||
|
||||
// printer_hook_insert_hooks(result);
|
||||
reg_hook_insert_hooks(result);
|
||||
clock_hook_insert_hooks(result);
|
||||
proc_addr_insert_hooks(result);
|
||||
serial_hook_apply_hooks(result);
|
||||
iohook_apply_hooks(result);
|
||||
|
||||
// Not needed?
|
||||
// serial_hook_apply_hooks(result);
|
||||
// Unity will crash during option loading when we hook this twice
|
||||
// iohook_apply_hooks(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user