Compare commits

...

19 Commits

Author SHA1 Message Date
c827b4c212 Merge pull request 'add almost full vfd implementation' (#31) from Haruka/segatools:vfd into develop
Reviewed-on: Dniel97/segatools#31
Reviewed-by: Dniel97 <dniel97@noreply.gitea.tendokyu.moe>
2024-08-24 21:56:38 +00:00
kyoubate-haruka
26624f25b1 hide default vfd ports from configs 2024-08-24 11:14:13 +02:00
kyoubate-haruka
824bc9abda default vfd port number to zero (use game-specific port) 2024-08-23 17:24:47 +02:00
kyoubate-haruka
cc5b87b559 add vfd settings to docs 2024-08-23 17:23:59 +02:00
kyoubate-haruka
e6794807a6 add default port fallback for vfd 2024-08-23 17:20:05 +02:00
kyoubate-haruka
54cbbffae9 add almost full vfd implementation 2024-08-23 16:30:22 +02:00
ac0f9f0587
aime firmware fix, mu3 keybinding fix 2024-08-21 15:13:09 +02:00
0061158188
printer: changed filename for holo cards 2024-08-20 13:40:47 +02:00
c535f18e40
added support for tokyo 2024-08-20 13:32:35 +02:00
c91c7db3c7
renamed [gpio] dipsw settings to [system] 2024-08-20 10:48:08 +02:00
6a4cae1165 Merge pull request 'Add bounds checking for D3D9 adapter number' (#29) from Bottersnike/segatools:fix/adapter_range into develop
Reviewed-on: Dniel97/segatools#29
2024-08-19 13:54:58 +00:00
383039e16e
Add bounds checking for D3D9 adapter number 2024-08-17 21:17:23 +01:00
37c26ecadb chuniio: Add OpeNITHM LED protocol support (#26)
This commit basically copy-pastes the last commits from https://dev.s-ul.net/VeroxZik/segatools/-/commits/master to add OpenITHM LED support. Doesn't need to edit segatools.ini because the relevant config lines are already there for some reason.

Tested with my OpenITHM controller and behaves exactly like the other fork.

Reviewed-on: Dniel97/segatools#26
Co-authored-by: d4nin3u <d4nin3u@gmail.com>
Co-committed-by: d4nin3u <d4nin3u@gmail.com>
2024-08-06 21:35:51 +00:00
686d57d3ee
unity: timezone spoofing fixed, close #27 2024-08-06 11:14:27 +02:00
b9204d4765
unity: hopefully fixes timezone spoofing #27 2024-08-05 21:59:59 +02:00
5abc593b46
cm: added printer support 2024-08-05 20:53:56 +02:00
fe14630b3d
unity: fixed option loading crash 2024-08-05 20:49:47 +02:00
92fe2751e7 Merge pull request 'dns: added WAHLAP billing DNS block' (#23) from zaphkito/segatools:develop into develop
Reviewed-on: Dniel97/segatools#23
2024-07-06 23:06:31 +00:00
8c839b0d4e dns: added WAHLAP billing DNS block
China have another company named universal service WACCA, but they use same PowerOn and Download Order domain with SEGA official, so we need express `sys-all.cn` is only used for WAHLAP, not all China SEGA games.
WAHLAP have a unused billing domain, just in case, we blocked it now
2024-07-03 18:04:21 +00:00
74 changed files with 3835 additions and 542 deletions

View File

@ -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 \

View File

@ -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

View File

@ -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);
}

View File

@ -47,5 +47,8 @@ board_lib = static_library(
'slider-frame.h',
'vfd.c',
'vfd.h',
'vfd-cmd.h',
'vfd-frame.c',
'vfd-frame.h',
],
)

View File

@ -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 {

View File

@ -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
View 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
View 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
View 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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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 */

View File

@ -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(

View File

@ -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;

View File

@ -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);
}
}
}
}

View File

@ -4,6 +4,7 @@ chuniio_lib = static_library(
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
sources : [
'chu2to3.c',
'chu2to3.h',

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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++) {

View File

@ -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

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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(

View File

@ -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);

View File

@ -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.

View File

@ -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
View File

@ -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
; -----------------------------------------------------------------------------

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -0,0 +1,9 @@
{
"network" :
{
"property" :
{
"dhcp" : true
}
}
}

199
dist/tokyo/segatools.ini vendored Normal file
View 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
View 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

View File

@ -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

View File

@ -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.

View File

@ -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(

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -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')

View File

@ -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;
}

View File

@ -11,3 +11,4 @@ struct clock_config {
};
HRESULT clock_hook_init(const struct clock_config *cfg);
void clock_hook_insert_hooks(HMODULE target);

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -34,7 +34,7 @@ platform_lib = static_library(
'platform.h',
'vfs.c',
'vfs.h',
'dipsw.c',
'dipsw.h',
'system.c',
'system.h',
],
)

View File

@ -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;

View File

@ -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(

View File

@ -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]);

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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);

View File

@ -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);
}
}