Compare commits

..

31 Commits

Author SHA1 Message Date
5f817c8a36 swdc: minor improvements 2024-09-30 23:17:37 +02:00
259b763a13 idz: add ffb and led emulation 2024-09-30 23:10:16 +02:00
2251585ef0 swdc: add ffb and led emulation 2024-09-30 20:23:28 +02:00
c06bb408e7 idac: add ffb emulation 2024-09-30 18:50:46 +02:00
53fb8c28ea Merge pull request 'kemono: only load I/O dll inside amdaemon' (#38) from Haruka/segatools:kemonofr64bit into develop
Reviewed-on: Dniel97/segatools#38
2024-09-28 13:36:06 +00:00
33452394e6 kemono: also only load aimeio dll in x64 process 2024-09-27 17:50:40 +02:00
88a5bdcd14 kemono: only load I/O dll inside amdaemon 2024-09-26 11:52:00 +02:00
bb773a63ce Merge pull request 'Kemono Friends support / 32-bit CHC300 support' (#36) from Haruka/segatools:kemonofr into develop
Reviewed-on: Dniel97/segatools#36
2024-09-21 15:12:05 +00:00
25e79f87c2 Merge pull request 'felica: fix rare card scan error (correct PMm)' (#33) from zaphkito/segatools:develop into develop
Reviewed-on: Dniel97/segatools#33
2024-09-21 15:10:28 +00:00
79592514ba fgo: fix printer 2024-09-20 11:14:41 +02:00
cdfd3bf655 kemono: not sure why that went missing 2024-09-20 11:09:21 +02:00
f6c12fd230 kemono: Pre-generate printer firmware files 2024-09-19 13:46:49 +02:00
86556ed2c8 kemono: Update start.bat 2024-09-16 14:29:49 +02:00
9de48dd6ce kemono: flip declarations 2024-09-13 16:52:55 +02:00
d257887f6e kemono: fix packagefile again 2024-09-12 13:29:17 +02:00
3eef5dd209 kemono: fix LED board check error 2024-09-12 13:25:38 +02:00
599d5e3211 kemono: fix LED hooking, add button LEDs 2024-09-12 13:25:19 +02:00
f18d074c5f kemono: add missed declarations 2024-09-12 12:50:57 +02:00
6bd1bce419 kemono: mention in readme 2024-09-12 12:50:51 +02:00
96bdacfa7c kemono: remove old amdaemon workaround 2024-09-12 12:40:57 +02:00
d4bb7b6e0e kemono: correct keychip IP range 2024-09-12 12:40:10 +02:00
70ac873d11 kemono: add to package creation 2024-09-12 12:39:42 +02:00
068651b6fa kemono: add support 2024-09-11 13:31:23 +02:00
84e9ed3c9a felica: fix rare card scan error (cucorrect PMm)
from real aime card with official card reader
2024-08-31 13:57:18 +00:00
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
26624f25b1 hide default vfd ports from configs 2024-08-24 11:14:13 +02:00
824bc9abda default vfd port number to zero (use game-specific port) 2024-08-23 17:24:47 +02:00
cc5b87b559 add vfd settings to docs 2024-08-23 17:23:59 +02:00
e6794807a6 add default port fallback for vfd 2024-08-23 17:20:05 +02:00
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
117 changed files with 4795 additions and 703 deletions

View File

@ -219,6 +219,27 @@ $(BUILD_DIR_ZIP)/tokyo.zip:
$(V)strip $(BUILD_DIR_ZIP)/tokyo/*.{exe,dll}
$(V)cd $(BUILD_DIR_ZIP)/tokyo ; zip -r ../tokyo.zip *
$(BUILD_DIR_ZIP)/kemono.zip:
$(V)echo ... $@
$(V)mkdir -p $(BUILD_DIR_ZIP)/kemono
$(V)mkdir -p $(BUILD_DIR_ZIP)/kemono/DEVICE
$(V)cp $(DIST_DIR)/kemono/segatools.ini \
$(DIST_DIR)/kemono/start.bat \
$(BUILD_DIR_ZIP)/kemono
$(V)cp $(BUILD_DIR_32)/kemonohook/kemonohook.dll \
$(BUILD_DIR_ZIP)/kemono/kemonohook_x86.dll
$(V)cp $(BUILD_DIR_64)/kemonohook/kemonohook.dll \
$(BUILD_DIR_ZIP)/kemono/kemonohook_x64.dll
$(V)cp $(BUILD_DIR_32)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_ZIP)/kemono/inject_x86.exe
$(V)cp $(BUILD_DIR_64)/subprojects/capnhook/inject/inject.exe \
$(BUILD_DIR_ZIP)/kemono/inject_x64.exe
$(V)cp pki/billing.pub \
pki/ca.crt \
$(BUILD_DIR_ZIP)/kemono/DEVICE
for x in exe dll; do strip $(BUILD_DIR_ZIP)/kemono/*.$$x; done
$(V)cd $(BUILD_DIR_ZIP)/kemono ; zip -r ../kemono.zip *
$(BUILD_DIR_ZIP)/doc.zip: \
$(DOC_DIR)/config \
$(DOC_DIR)/chunihook.md \
@ -243,6 +264,7 @@ $(BUILD_DIR_ZIP)/segatools.zip: \
$(BUILD_DIR_ZIP)/cm.zip \
$(BUILD_DIR_ZIP)/tokyo.zip \
$(BUILD_DIR_ZIP)/fgo.zip \
$(BUILD_DIR_ZIP)/kemono.zip \
CHANGELOG.md \
README.md \

View File

@ -30,6 +30,8 @@ Loaders and hardware emulators for SEGA games that run on the Nu and ALLS platfo
* SEGA World Drivers Championship 2019
* WACCA
* starting from WACCA
* Kemono Friends
* Kemono Friends 3: Planet Tours
## End-users

View File

@ -90,4 +90,14 @@ 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);
}
void ffb_config_load(struct ffb_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
cfg->enable = GetPrivateProfileIntW(L"ffb", L"enable", 1, filename);
}

View File

@ -6,7 +6,9 @@
#include "board/io4.h"
#include "board/sg-reader.h"
#include "board/vfd.h"
#include "board/ffb.h"
void aime_config_load(struct aime_config *cfg, const wchar_t *filename);
void io4_config_load(struct io4_config *cfg, const wchar_t *filename);
void vfd_config_load(struct vfd_config *cfg, const wchar_t *filename);
void ffb_config_load(struct ffb_config *cfg, const wchar_t *filename);

235
board/ffb.c Normal file
View File

@ -0,0 +1,235 @@
/*
Force Feedback Board (FFB)
This board is used by many SEGA games to provide force feedback to the player.
It is driven by the game software over a serial connection and is used by many
games such as SEGA World Drivers Championship, Initial D Arcade, ...
Part number in schematics is "838-15069 MOTOR DRIVE BD RS232/422 Board".
Some observations:
The maximal strength for any effect is 127, except Damper which maxes out at 40.
The period for rumble effects is in the range 0-40.
*/
#include "board/ffb.h"
#include <assert.h>
#include <stdint.h>
#include <windows.h>
#include "hook/iohook.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
// request format:
// 0x?? - sync + command
// 0x?? - direction/additional command
// 0x?? - strength
// 0x?? - checksum (sum of everything except the sync byte)
enum {
FFB_CMD_TOGGLE = 0x80,
FFB_CMD_CONSTANT_FORCE = 0x84,
FFB_CMD_RUMBLE = 0x85,
FFB_CMD_DAMPER = 0x86,
};
struct ffb_hdr {
uint8_t cmd;
};
union ffb_req_any {
struct ffb_hdr hdr;
uint8_t bytes[3];
};
static HRESULT ffb_handle_irp(struct irp *irp);
static HRESULT ffb_req_dispatch(const union ffb_req_any *req);
static HRESULT ffb_req_toggle(const uint8_t *bytes);
static HRESULT ffb_req_constant_force(const uint8_t *bytes);
static HRESULT ffb_req_rumble(const uint8_t *bytes);
static HRESULT ffb_req_damper(const uint8_t *bytes);
static const struct ffb_ops *ffb_ops;
static struct uart ffb_uart;
static bool ffb_started;
static HRESULT ffb_start_hr;
static uint8_t ffb_written[4];
static uint8_t ffb_readable[4];
/* Static variables to store maximum strength values */
static uint8_t max_constant_force = 0;
static uint8_t max_rumble = 0;
static uint8_t max_period = 0;
static uint8_t max_damper = 0;
HRESULT ffb_hook_init(
const struct ffb_config *cfg,
const struct ffb_ops *ops,
unsigned int port_no)
{
assert(cfg != NULL);
assert(ops != NULL);
if (!cfg->enable) {
return S_FALSE;
}
ffb_ops = ops;
uart_init(&ffb_uart, port_no);
ffb_uart.written.bytes = ffb_written;
ffb_uart.written.nbytes = sizeof(ffb_written);
ffb_uart.readable.bytes = ffb_readable;
ffb_uart.readable.nbytes = sizeof(ffb_readable);
dprintf("FFB: hook enabled.\n");
return iohook_push_handler(ffb_handle_irp);
}
static HRESULT ffb_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&ffb_uart, irp)) {
return iohook_invoke_next(irp);
}
hr = uart_handle_irp(&ffb_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
assert(&ffb_uart.written != NULL);
assert(ffb_uart.written.bytes != NULL || ffb_uart.written.nbytes == 0);
assert(ffb_uart.written.pos <= ffb_uart.written.nbytes);
// dprintf("FFB TX:\n");
hr = ffb_req_dispatch((const union ffb_req_any *) ffb_uart.written.bytes);
if (FAILED(hr)) {
dprintf("FFB: Processing error: %x\n", (int)hr);
}
// dump_iobuf(&ffb_uart.written);
ffb_uart.written.pos = 0;
return hr;
}
static HRESULT ffb_req_dispatch(const union ffb_req_any *req)
{
switch (req->hdr.cmd) {
case FFB_CMD_TOGGLE:
return ffb_req_toggle(req->bytes);
case FFB_CMD_CONSTANT_FORCE:
return ffb_req_constant_force(req->bytes);
case FFB_CMD_RUMBLE:
return ffb_req_rumble(req->bytes);
case FFB_CMD_DAMPER:
return ffb_req_damper(req->bytes);
/* There are some test mode specfic commands which doesn't seem to be used in
game at all. The same is true for the initialization phase. */
default:
dprintf("FFB: Unhandled command %02x\n", req->hdr.cmd);
return S_OK;
}
}
static HRESULT ffb_req_toggle(const uint8_t *bytes)
{
uint8_t activate = bytes[2];
if (activate == 0x01) {
dprintf("FFB: Activated\n");
} else {
dprintf("FFB: Deactivated\n");
}
if (ffb_ops->toggle != NULL) {
ffb_ops->toggle(activate == 0x01);
}
return S_OK;
}
static HRESULT ffb_req_constant_force(const uint8_t *bytes)
{
// dprintf("FFB: Constant force\n");
uint8_t direction = bytes[1];
uint8_t force = bytes[2];
if (direction == 0x0) {
// Right
force = 128 - force;
}
// Update max strength if the current force is greater
if (force > max_constant_force) {
max_constant_force = force;
}
// dprintf("FFB: Constant Force Strength: %d (Max: %d)\n", force, max_constant_force);
if (ffb_ops->constant_force != NULL) {
ffb_ops->constant_force(direction, force);
}
return S_OK;
}
static HRESULT ffb_req_rumble(const uint8_t *bytes)
{
// dprintf("FFB: Rumble\n");
uint8_t force = bytes[1];
uint8_t period = bytes[2];
// Update max strength if the current force is greater
if (force > max_rumble) {
max_rumble = force;
}
if (period > max_period) {
max_period = period;
}
// dprintf("FFB: Rumble Period: %d (Max %d), Strength: %d (Max: %d)\n", period, max_period, force, max_rumble);
if (ffb_ops->rumble != NULL) {
ffb_ops->rumble(force, period);
}
return S_OK;
}
static HRESULT ffb_req_damper(const uint8_t *bytes)
{
// dprintf("FFB: Damper\n");
uint8_t force = bytes[2];
// Update max strength if the current force is greater
if (force > max_damper) {
max_damper = force;
}
// dprintf("FFB: Damper Strength: %d (Max: %d)\n", force, max_damper);
if (ffb_ops->damper != NULL) {
ffb_ops->damper(force);
}
return S_OK;
}

20
board/ffb.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
struct ffb_config {
bool enable;
};
struct ffb_ops {
void (*toggle)(bool active);
void (*constant_force)(uint8_t direction, uint8_t force);
void (*rumble)(uint8_t force, uint8_t period);
void (*damper)(uint8_t force);
};
HRESULT ffb_hook_init(
const struct ffb_config *cfg,
const struct ffb_ops *ops,
unsigned int port_no);

View File

@ -199,7 +199,7 @@ struct led15093_resp_board_info {
char chip_num[5];
uint8_t endcode; // Always 0xFF
uint8_t fw_ver;
uint8_t rx_buf;
uint16_t rx_buf;
};
struct led15093_resp_protocol_ver {

View File

@ -47,5 +47,10 @@ board_lib = static_library(
'slider-frame.h',
'vfd.c',
'vfd.h',
'vfd-cmd.h',
'vfd-frame.c',
'vfd-frame.h',
'ffb.c',
'ffb.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_AIME = 0x50,
SG_NFC_CMD_MIFARE_AUTHENTICATE_A = 0x51,
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
SG_NFC_CMD_MIFARE_SET_KEY_BANA = 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,
@ -184,13 +189,17 @@ static HRESULT sg_nfc_dispatch(
&req->felica_encap,
&res->felica_encap);
case SG_NFC_CMD_MIFARE_AUTHENTICATE:
case SG_NFC_CMD_MIFARE_AUTHENTICATE_A:
case SG_NFC_CMD_MIFARE_AUTHENTICATE_B:
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 +451,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

@ -101,7 +101,7 @@ static DWORD CALLBACK cm_pre_startup(void)
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
hooked earlier in the `cmhook` initialization. */
unity_hook_init(&cm_hook_cfg.unity, cm_hook_mod);
unity_hook_init(&cm_hook_cfg.unity, cm_hook_mod, NULL);
/* Initialize debug helpers */

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

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

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

View File

@ -75,6 +75,11 @@ dipsw3=0
dipsw4=0
dipsw5=0
[ffb]
; Enable force feedback (838-15069) board emulation. This is required for
; both DirectInput and XInput steering wheel effects.
enable=1
; -----------------------------------------------------------------------------
; LED settings
; -----------------------------------------------------------------------------
@ -231,6 +236,21 @@ reverseAccelAxis=0
reverseBrakeAxis=0
; Force feedback settings.
; Strength of the force feedback spring effect in percent. Possible values
; are 0-100.
centerSpringStrength=30
; Only works when FFB board emulation is enabled!
;
; It is recommended to change the strength inside the Game Test Mode!
;
; These settings are only used when using DirectInput for the wheel.
; The values are in the range 0%-100%, where 0 disables the effect and
; 100 is the maximum.
; Constant force strength, used for centering spring effect.
constantForceStrength=100
; Damper strength, used for steering wheel damper effect.
damperStrength=100
; Rumble strength, used for road surface effects.
; WARNING: THIS WILL CRASH ON FANATEC (maybe even more) WHEELS!
rumbleStrength=100
; Rumble duration factor from ms to µs, used to scale the duration of the rumble effect.
rumbleDuration=1000

View File

@ -69,6 +69,20 @@ region=4
; exactly one machine and set this to 0 on all others.
dipsw1=1
[ffb]
; Enable force feedback (838-15069) board emulation. This is required for
; both DirectInput and XInput steering wheel effects.
enable=1
; -----------------------------------------------------------------------------
; LED settings
; -----------------------------------------------------------------------------
[led15070]
; Enable emulation of the 837-15070-02 controlled lights, which handle the
; cabinet and seat LEDs.
enable=1
; -----------------------------------------------------------------------------
; Misc. hooks settings
; -----------------------------------------------------------------------------
@ -212,6 +226,21 @@ reverseAccelAxis=0
reverseBrakeAxis=0
; Force feedback settings.
; Strength of the force feedback spring effect in percent. Possible values
; are 0-100.
centerSpringStrength=30
; Only works when FFB board emulation is enabled!
;
; It is recommended to change the strength inside the Game Test Mode!
;
; These settings are only used when using DirectInput for the wheel.
; The values are in the range 0%-100%, where 0 disables the effect and
; 100 is the maximum.
; Constant force strength, used for centering spring effect.
constantForceStrength=100
; Damper strength, used for steering wheel damper effect.
damperStrength=100
; Rumble strength, used for road surface effects.
; WARNING: THIS WILL CRASH ON FANATEC (maybe even more) WHEELS!
rumbleStrength=100
; Rumble duration factor from ms to µs, used to scale the duration of the rumble effect.
rumbleDuration=1000

5
dist/idz/start.bat vendored
View File

@ -2,10 +2,11 @@
pushd %~dp0
inject -k idzhook.dll InitialD0_DX11_Nu.exe
start /min "AM Daemon" inject -d -k idzhook.dll amdaemon.exe -c configDHCP_Final_Common.json configDHCP_Final_JP.json configDHCP_Final_JP_ST1.json configDHCP_Final_JP_ST2.json configDHCP_Final_EX.json configDHCP_Final_EX_ST1.json configDHCP_Final_EX_ST2.json
rem Set dipsw1=0 and uncomment the ServerBox for in store battle?
rem inject -k idzhook.dll ServerBoxD8_Nu_x64.exe
inject -d -k idzhook.dll amdaemon.exe -c configDHCP_Final_Common.json configDHCP_Final_JP.json configDHCP_Final_JP_ST1.json configDHCP_Final_JP_ST2.json configDHCP_Final_EX.json configDHCP_Final_EX_ST1.json configDHCP_Final_EX_ST2.json
inject -d -k idzhook.dll InitialD0_DX11_Nu.exe
taskkill /im ServerBoxD8_Nu_x64.exe > nul 2>&1

147
dist/kemono/segatools.ini vendored Normal file
View File

@ -0,0 +1,147 @@
; -----------------------------------------------------------------------------
; 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 Axxx 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=
; -----------------------------------------------------------------------------
; Device settings
; -----------------------------------------------------------------------------
[aime]
; Enable Aime card reader assembly emulation. Disable to use a real SEGA Aime
; reader.
enable=1
aimePath=DEVICE\aime.txt
[vfd]
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
; GP1232A02A FUTABA assembly.
enable=1
; -----------------------------------------------------------------------------
; 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 its 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.32.0` and this value is set to `11`, then the
; local host's virtualized LAN IP is `192.168.32.11`).
addrSuffix=11
; -----------------------------------------------------------------------------
; 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.179.0
[gpio]
; Emulated Nu DIP switch for Distribution Server setting.
;
; If multiple machines are present on the same LAN then set this to 1 on
; exactly one machine and set this to 0 on all others.
dipsw1=1
; Chassis Test button virtual-key code. Default is the 4 key.
test=0x34
; Chassis Service button virtual-key code. Default is the 5 key.
service=0x35
; -----------------------------------------------------------------------------
; Misc. hook settings
; -----------------------------------------------------------------------------
[unity]
; Enable Unity hook. This will allow you to run custom .NET code before the game
enable=1
; Path to a .NET DLL that should run before the game. Useful for loading
; modding frameworks such as BepInEx.
;
; NOTE: For Kemono Friends, BepInEx (or similar) should be installed to the main folder, not the "UnityApp" folder.
targetAssembly=
[printer]
; Sinfonia CHC-C300 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"
; -----------------------------------------------------------------------------
; LED settings
; -----------------------------------------------------------------------------
[led15093]
; 837-15093-06 LED board emulation setting.
enable=1
; -----------------------------------------------------------------------------
; Custom IO settings
; -----------------------------------------------------------------------------
[aimeio]
; To use a custom card reader IO DLL enter its path here.
; Leave empty if you want to use Segatools built-in keyboard input.
path=
[kemonoio]
; To use a custom Kemono IO DLL enter its path here.
; Leave empty if you want to use Segatools built-in keyboard 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.
[io3]
; Test button virtual-key code. Default is the 1 key.
test=0x31
; Service button virtual-key code. Default is the 2 key.
service=0x32
; Keyboard button to increment coin counter. Default is the 3 key.
coin=0x33
; Analog lever (which is actually just four buttons, and not real analog input, default: Arrow keys)
left=0x25
right=0x27
up=0x26
down=0x28
; Controller face buttons (default A, S, D)
red=0x41
green=0x53
blue=0x44
; Menu confirmation key (default RETURN)
start=0x0D

12
dist/kemono/start.bat vendored Normal file
View File

@ -0,0 +1,12 @@
@echo off
pushd %~dp0
start "AM Daemon" /min inject_x64 -d -k kemonohook_x64.dll amdaemon.exe -c config.json
inject_x86 -d -k kemonohook_x86.dll UnityApp\Parade -screen-fullscreen 0 -popupwindow -screen-width 720 -screen-height 1280 -silent-crashes
taskkill /f /im amdaemon.exe > nul 2>&1
echo.
echo Game processes have terminated
pause

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

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

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
@ -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
@ -65,6 +65,11 @@ enable=1
; allow you to start a game in freeplay mode.
freeplay=0
[ffb]
; Enable force feedback (838-15069) board emulation. This is required for
; both DirectInput and XInput steering wheel effects.
enable=1
; -----------------------------------------------------------------------------
; Custom IO settings
; -----------------------------------------------------------------------------
@ -181,6 +186,21 @@ reverseAccelAxis=0
reverseBrakeAxis=0
; Force feedback settings.
; Strength of the force feedback spring effect in percent. Possible values
; are 0-100.
centerSpringStrength=30
; Only works when FFB board emulation is enabled!
;
; It is recommended to change the strength inside the Game Test Mode!
;
; These settings are only used when using DirectInput for the wheel.
; The values are in the range 0%-100%, where 0 disables the effect and
; 100 is the maximum.
; Constant force strength, used for centering spring effect.
constantForceStrength=100
; Damper strength, used for steering wheel damper effect.
damperStrength=100
; Rumble strength, used for road surface effects.
; WARNING: THIS WILL CRASH ON FANATEC (maybe even more) WHEELS!
rumbleStrength=100
; Rumble duration factor from ms to µs, used to scale the duration of the rumble effect.
rumbleDuration=1000

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

@ -26,6 +26,7 @@
#include "hook/process.h"
#include "hooklib/dll.h"
#include "hooklib/dvd.h"
#include "hooklib/touch.h"
#include "hooklib/printer.h"
@ -65,6 +66,8 @@ static DWORD CALLBACK fgo_pre_startup(void)
/* Hook external DLL APIs */
printer_hook_init(&fgo_hook_cfg.printer, 4, fgo_hook_mod);
dll_hook_push(fgo_hook_mod, L"C330Ausb.dll");
dll_hook_push(fgo_hook_mod, L"C330AFWDLusb.dll");
/* Initialize emulation hooks */

View File

@ -44,7 +44,7 @@ EXPORTS
chcusb_selectPrinter
chcusb_selectPrinterSN
chcusb_getPrinterInfo
chcusb_imageformat
chcusb_imageformat=chcusb_imageformat_330
chcusb_setmtf
chcusb_makeGamma
chcusb_setIcctable

View File

@ -11,6 +11,7 @@
#include "hooklib/printer.h"
#include <assert.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
@ -60,44 +61,45 @@ static uint8_t STATUS = 0;
/* C3XXFWDLusb API hooks */
int fwdlusb_open(uint16_t *rResult);
void fwdlusb_close();
int fwdlusb_listupPrinter(uint8_t *rIdArray);
int fwdlusb_listupPrinterSN(uint64_t *rSerialArray);
int fwdlusb_selectPrinter(uint8_t printerId, uint16_t *rResult);
int fwdlusb_selectPrinterSN(uint64_t printerSN, uint16_t *rResult);
int fwdlusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen);
int fwdlusb_status(uint16_t *rResult);
int fwdlusb_statusAll(uint8_t *idArray, uint16_t *rResultArray);
int fwdlusb_resetPrinter(uint16_t *rResult);
int fwdlusb_updateFirmware(uint8_t update, LPCSTR filename, uint16_t *rResult);
int fwdlusb_getFirmwareInfo(uint8_t update, LPCSTR filename, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult);
int fwdlusb_MakeThread(uint16_t maxCount);
int fwdlusb_ReleaseThread(uint16_t *rResult);
int fwdlusb_AttachThreadCount(uint16_t *rCount, uint16_t *rMaxCount);
int fwdlusb_getErrorLog(uint16_t index, uint8_t *rData, uint16_t *rResult);
int WINAPI fwdlusb_open(uint16_t *rResult);
void WINAPI fwdlusb_close();
int WINAPI fwdlusb_listupPrinter(uint8_t *rIdArray);
int WINAPI fwdlusb_listupPrinterSN(uint64_t *rSerialArray);
int WINAPI fwdlusb_selectPrinter(uint8_t printerId, uint16_t *rResult);
int WINAPI fwdlusb_selectPrinterSN(uint64_t printerSN, uint16_t *rResult);
int WINAPI fwdlusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen);
int WINAPI fwdlusb_status(uint16_t *rResult);
int WINAPI fwdlusb_statusAll(uint8_t *idArray, uint16_t *rResultArray);
int WINAPI fwdlusb_resetPrinter(uint16_t *rResult);
int WINAPI fwdlusb_updateFirmware(uint8_t update, LPCSTR filename, uint16_t *rResult);
int WINAPI fwdlusb_getFirmwareInfo(uint8_t update, LPCSTR filename, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult);
int WINAPI fwdlusb_MakeThread(uint16_t maxCount);
int WINAPI fwdlusb_ReleaseThread(uint16_t *rResult);
int WINAPI fwdlusb_AttachThreadCount(uint16_t *rCount, uint16_t *rMaxCount);
int WINAPI fwdlusb_getErrorLog(uint16_t index, uint8_t *rData, uint16_t *rResult);
/* C3XXusb API hooks */
int chcusb_MakeThread(uint16_t maxCount);
int chcusb_open(uint16_t *rResult);
void chcusb_close();
int chcusb_ReleaseThread(uint16_t *rResult);
int chcusb_listupPrinter(uint8_t *rIdArray);
int chcusb_listupPrinterSN(uint64_t *rSerialArray);
int chcusb_selectPrinter(uint8_t printerId, uint16_t *rResult);
int chcusb_selectPrinterSN(uint64_t printerSN, uint16_t *rResult);
int chcusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen);
int chcusb_imageformat(
uint16_t format,
uint16_t ncomp,
uint16_t depth,
uint16_t width,
uint16_t height,
uint16_t *rResult);
int chcusb_setmtf(int32_t *mtf);
int chcusb_makeGamma(uint16_t k, uint8_t *intoneR, uint8_t *intoneG, uint8_t *intoneB);
int chcusb_setIcctable(
int WINAPI chcusb_MakeThread(uint16_t maxCount);
int WINAPI chcusb_open(uint16_t *rResult);
void WINAPI chcusb_close();
int WINAPI chcusb_ReleaseThread(uint16_t *rResult);
int WINAPI chcusb_listupPrinter(uint8_t *rIdArray);
int WINAPI chcusb_listupPrinterSN(uint64_t *rSerialArray);
int WINAPI chcusb_selectPrinter(uint8_t printerId, uint16_t *rResult);
int WINAPI chcusb_selectPrinterSN(uint64_t printerSN, uint16_t *rResult);
int WINAPI chcusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen);
int WINAPI chcusb_imageformat(uint16_t format, uint16_t ncomp, uint16_t depth, uint16_t width, uint16_t height, uint8_t * image, uint16_t* rResult);
int WINAPI chcusb_imageformat_330(
uint16_t format,
uint16_t ncomp,
uint16_t depth,
uint16_t width,
uint16_t height,
uint16_t *rResult);
int __thiscall chcusb_setmtf(int32_t *mtf);
int WINAPI chcusb_makeGamma(uint16_t k, uint8_t *intoneR, uint8_t *intoneG, uint8_t *intoneB);
int WINAPI chcusb_setIcctable(
LPCSTR icc1,
LPCSTR icc2,
uint16_t intents,
@ -108,40 +110,42 @@ int chcusb_setIcctable(
uint8_t *outtoneG,
uint8_t *outtoneB,
uint16_t *rResult);
int chcusb_copies(uint16_t copies, uint16_t *rResult);
int chcusb_status(uint16_t *rResult);
int chcusb_statusAll(uint8_t *idArray, uint16_t *rResultArray);
int chcusb_startpage(uint16_t postCardState, uint16_t *pageId, uint16_t *rResult);
int chcusb_endpage(uint16_t *rResult);
int chcusb_write(uint8_t *data, uint32_t *writeSize, uint16_t *rResult);
int chcusb_writeLaminate(uint8_t *data, uint32_t *writeSize, uint16_t *rResult);
int chcusb_writeHolo(uint8_t *data, uint32_t *writeSize, uint16_t *rResult);
int chcusb_setPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult);
int chcusb_getGamma(LPCSTR filename, uint8_t *r, uint8_t *g, uint8_t *b, uint16_t *rResult);
int chcusb_getMtf(LPCSTR filename, int32_t *mtf, uint16_t *rResult);
int chcusb_cancelCopies(uint16_t pageId, uint16_t *rResult);
int chcusb_setPrinterToneCurve(uint16_t type, uint16_t number, uint16_t *data, uint16_t *rResult);
int chcusb_getPrinterToneCurve(uint16_t type, uint16_t number, uint16_t *data, uint16_t *rResult);
int chcusb_blinkLED(uint16_t *rResult);
int chcusb_resetPrinter(uint16_t *rResult);
int chcusb_AttachThreadCount(uint16_t *rCount, uint16_t *rMaxCount);
int chcusb_getPrintIDStatus(uint16_t pageId, uint8_t *rBuffer, uint16_t *rResult);
int chcusb_setPrintStandby(uint16_t position, uint16_t *rResult);
int chcusb_testCardFeed(uint16_t mode, uint16_t times, uint16_t *rResult);
int chcusb_exitCard(uint16_t *rResult);
int chcusb_getCardRfidTID(uint8_t *rCardTID, uint16_t *rResult);
int chcusb_commCardRfidReader(uint8_t *sendData, uint8_t *rRecvData, uint32_t sendSize, uint32_t *rRecvSize, uint16_t *rResult);
int chcusb_updateCardRfidReader(uint8_t *data, uint32_t size, uint16_t *rResult);
int chcusb_getErrorLog(uint16_t index, uint8_t *rData, uint16_t *rResult);
int chcusb_getErrorStatus(uint16_t *rBuffer);
int chcusb_setCutList(uint8_t *rData, uint16_t *rResult);
int chcusb_setLaminatePattern(uint16_t index, uint8_t *rData, uint16_t *rResult);
int chcusb_color_adjustment(LPCSTR filename, int32_t a2, int32_t a3, int16_t a4, int16_t a5, int64_t a6, int64_t a7, uint16_t *rResult);
int chcusb_color_adjustmentEx(int32_t a1, int32_t a2, int32_t a3, int16_t a4, int16_t a5, int64_t a6, int64_t a7, uint16_t *rResult);
int chcusb_getEEPROM(uint8_t index, uint8_t *rData, uint16_t *rResult);
int chcusb_setParameter(uint8_t a1, uint32_t a2, uint16_t *rResult);
int chcusb_getParameter(uint8_t a1, uint8_t *a2, uint16_t *rResult);
int chcusb_universal_command(int32_t a1, uint8_t a2, int32_t a3, uint8_t *a4, uint16_t *rResult);
int WINAPI chcusb_copies(uint16_t copies, uint16_t *rResult);
int WINAPI chcusb_status(uint16_t *rResult);
int WINAPI chcusb_statusAll(uint8_t *idArray, uint16_t *rResultArray);
int WINAPI chcusb_startpage(uint16_t postCardState, uint16_t *pageId, uint16_t *rResult);
int WINAPI chcusb_startpage_300(uint16_t postCardState, uint16_t *rResult);
int WINAPI chcusb_endpage(uint16_t *rResult);
int WINAPI chcusb_write(uint8_t *data, uint32_t *writeSize, uint16_t *rResult);
int WINAPI chcusb_writeLaminate(uint8_t *data, uint32_t *writeSize, uint16_t *rResult);
int WINAPI chcusb_writeHolo(uint8_t *data, uint32_t *writeSize, uint16_t *rResult);
int WINAPI chcusb_setPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult);
int WINAPI chcusb_getGamma(LPCSTR filename, uint8_t *r, uint8_t *g, uint8_t *b, uint16_t *rResult);
int WINAPI chcusb_getMtf(LPCSTR filename, int32_t *mtf, uint16_t *rResult);
int WINAPI chcusb_cancelCopies(uint16_t pageId, uint16_t *rResult);
int WINAPI chcusb_setPrinterToneCurve(uint16_t type, uint16_t number, uint16_t *data, uint16_t *rResult);
int WINAPI chcusb_getPrinterToneCurve(uint16_t type, uint16_t number, uint16_t *data, uint16_t *rResult);
int WINAPI chcusb_blinkLED(uint16_t *rResult);
int WINAPI chcusb_resetPrinter(uint16_t *rResult);
int WINAPI chcusb_AttachThreadCount(uint16_t *rCount, uint16_t *rMaxCount);
int WINAPI chcusb_getPrintIDStatus(uint16_t pageId, uint8_t *rBuffer, uint16_t *rResult);
int WINAPI chcusb_setPrintStandby(uint16_t position, uint16_t *rResult);
int WINAPI chcusb_setPrintStandby_300(uint16_t *rResult);
int WINAPI chcusb_testCardFeed(uint16_t mode, uint16_t times, uint16_t *rResult);
int WINAPI chcusb_exitCard(uint16_t *rResult);
int WINAPI chcusb_getCardRfidTID(uint8_t *rCardTID, uint16_t *rResult);
int WINAPI chcusb_commCardRfidReader(uint8_t *sendData, uint8_t *rRecvData, uint32_t sendSize, uint32_t *rRecvSize, uint16_t *rResult);
int WINAPI chcusb_updateCardRfidReader(uint8_t *data, uint32_t size, uint16_t *rResult);
int WINAPI chcusb_getErrorLog(uint16_t index, uint8_t *rData, uint16_t *rResult);
int WINAPI chcusb_getErrorStatus(uint16_t *rBuffer);
int WINAPI chcusb_setCutList(uint8_t *rData, uint16_t *rResult);
int WINAPI chcusb_setLaminatePattern(uint16_t index, uint8_t *rData, uint16_t *rResult);
int WINAPI chcusb_color_adjustment(LPCSTR filename, int32_t a2, int32_t a3, int16_t a4, int16_t a5, int64_t a6, int64_t a7, uint16_t *rResult);
int WINAPI chcusb_color_adjustmentEx(int32_t a1, int32_t a2, int32_t a3, int16_t a4, int16_t a5, int64_t a6, int64_t a7, uint16_t *rResult);
int WINAPI chcusb_getEEPROM(uint8_t index, uint8_t *rData, uint16_t *rResult);
int WINAPI chcusb_setParameter(uint8_t a1, uint32_t a2, uint16_t *rResult);
int WINAPI chcusb_getParameter(uint8_t a1, uint8_t *a2, uint16_t *rResult);
int WINAPI chcusb_universal_command(int32_t a1, uint8_t a2, int32_t a3, uint8_t *a4, uint16_t *rResult);
/* PrintDLL API hooks */
@ -491,7 +495,7 @@ static const struct hook_symbol C300usb_hooks[] = {
}, {
.name = "chcusb_startpage",
.ordinal = 0x0011,
.patch = chcusb_startpage,
.patch = chcusb_startpage_300,
.link = NULL
}, {
.name = "chcusb_endpage",
@ -561,7 +565,7 @@ static const struct hook_symbol C300usb_hooks[] = {
}, {
.name = "chcusb_setPrintStandby",
.ordinal = 0x001f,
.patch = chcusb_setPrintStandby,
.patch = chcusb_setPrintStandby_300,
.link = NULL
}, {
.name = "chcusb_testCardFeed",
@ -1619,7 +1623,7 @@ static HRESULT deck_frame_encode_byte(struct iobuf *dest, uint8_t byte) {
// C3XXFWDLusb stubs
int fwdlusb_open(uint16_t *rResult) {
int WINAPI fwdlusb_open(uint16_t *rResult) {
dprintf("Printer: C3XXFWDLusb: %s\n", __func__);
*rResult = 0;
return 1;
@ -1627,33 +1631,33 @@ int fwdlusb_open(uint16_t *rResult) {
void fwdlusb_close() {}
int fwdlusb_listupPrinter(uint8_t *rIdArray) {
int WINAPI fwdlusb_listupPrinter(uint8_t *rIdArray) {
dprintf("Printer: C3XXFWDLusb: %s\n", __func__);
memset(rIdArray, 0xFF, 0x80);
rIdArray[0] = idNumber;
return 1;
}
int fwdlusb_listupPrinterSN(uint64_t *rSerialArray) {
int WINAPI fwdlusb_listupPrinterSN(uint64_t *rSerialArray) {
dprintf("Printer: C3XXFWDLusb: %s\n", __func__);
memset(rSerialArray, 0xFF, 0x400);
rSerialArray[0] = serialNo;
return 1;
}
int fwdlusb_selectPrinter(uint8_t printerId, uint16_t *rResult) {
int WINAPI fwdlusb_selectPrinter(uint8_t printerId, uint16_t *rResult) {
dprintf("Printer: C3XXFWDLusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int fwdlusb_selectPrinterSN(uint64_t printerSN, uint16_t *rResult) {
int WINAPI fwdlusb_selectPrinterSN(uint64_t printerSN, uint16_t *rResult) {
dprintf("Printer: C3XXFWDLusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int fwdlusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen) {
int WINAPI fwdlusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen) {
dprintf("Printer: C3XXFWDLusb: %s\n", __func__);
switch (tagNumber) {
case 0: // getPaperInfo
@ -1664,6 +1668,7 @@ int fwdlusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen)
if (*rLen != 0x99) *rLen = 0x99;
if (rBuffer) {
memset(rBuffer, 0, *rLen);
rBuffer[0] = 4; // firmware count
// bootFirmware
int i = 1;
memcpy(rBuffer + i, mainFirmware, sizeof(mainFirmware));
@ -1734,13 +1739,13 @@ int fwdlusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen)
return 1;
}
int fwdlusb_status(uint16_t *rResult) {
int WINAPI fwdlusb_status(uint16_t *rResult) {
dprintf("Printer: C3XXFWDLusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int fwdlusb_statusAll(uint8_t *idArray, uint16_t *rResultArray) {
int WINAPI fwdlusb_statusAll(uint8_t *idArray, uint16_t *rResultArray) {
dprintf("Printer: C3XXFWDLusb: %s\n", __func__);
for (int i = 0; *(uint8_t *)(idArray + i) != 255 && i < 128; ++i) {
*(uint16_t *)(rResultArray + 2 * i) = 0;
@ -1749,13 +1754,13 @@ int fwdlusb_statusAll(uint8_t *idArray, uint16_t *rResultArray) {
return 1;
}
int fwdlusb_resetPrinter(uint16_t *rResult) {
int WINAPI fwdlusb_resetPrinter(uint16_t *rResult) {
dprintf("Printer: C3XXFWDLusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int fwdlusb_getFirmwareVersion(uint8_t *buffer, int size) {
int WINAPI fwdlusb_getFirmwareVersion(uint8_t *buffer, int size) {
int8_t a;
uint32_t b = 0;
for (int32_t i = 0; i < size; ++i) {
@ -1776,7 +1781,7 @@ int fwdlusb_getFirmwareVersion(uint8_t *buffer, int size) {
return b;
}
int fwdlusb_updateFirmware_main(uint8_t update, LPCSTR filename, uint16_t *rResult) {
int WINAPI fwdlusb_updateFirmware_main(uint8_t update, LPCSTR filename, uint16_t *rResult) {
DWORD result;
HANDLE fwFile = NULL;
DWORD bytesWritten = 0;
@ -1825,7 +1830,7 @@ int fwdlusb_updateFirmware_main(uint8_t update, LPCSTR filename, uint16_t *rResu
return result;
}
int fwdlusb_updateFirmware_dsp(uint8_t update, LPCSTR filename, uint16_t *rResult) {
int WINAPI fwdlusb_updateFirmware_dsp(uint8_t update, LPCSTR filename, uint16_t *rResult) {
DWORD result;
HANDLE fwFile = NULL;
DWORD bytesWritten = 0;
@ -1874,7 +1879,7 @@ int fwdlusb_updateFirmware_dsp(uint8_t update, LPCSTR filename, uint16_t *rResul
return result;
}
int fwdlusb_updateFirmware_param(uint8_t update, LPCSTR filename, uint16_t *rResult) {
int WINAPI fwdlusb_updateFirmware_param(uint8_t update, LPCSTR filename, uint16_t *rResult) {
DWORD result;
HANDLE fwFile = NULL;
DWORD bytesWritten = 0;
@ -1923,7 +1928,7 @@ int fwdlusb_updateFirmware_param(uint8_t update, LPCSTR filename, uint16_t *rRes
return result;
}
int fwdlusb_updateFirmware(uint8_t update, LPCSTR filename, uint16_t *rResult) {
int WINAPI fwdlusb_updateFirmware(uint8_t update, LPCSTR filename, uint16_t *rResult) {
dprintf("Printer: C3XXFWDLusb: %s\n", __func__);
if (update == 1) {
return fwdlusb_updateFirmware_main(update, filename, rResult);
@ -1937,7 +1942,7 @@ int fwdlusb_updateFirmware(uint8_t update, LPCSTR filename, uint16_t *rResult) {
}
}
int fwdlusb_getFirmwareInfo_main(LPCSTR filename, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult) {
int WINAPI fwdlusb_getFirmwareInfo_main(LPCSTR filename, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult) {
DWORD result;
if (filename) {
@ -1973,7 +1978,7 @@ int fwdlusb_getFirmwareInfo_main(LPCSTR filename, uint8_t *rBuffer, uint32_t *rL
return result;
}
int fwdlusb_getFirmwareInfo_dsp(LPCSTR filename, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult) {
int WINAPI fwdlusb_getFirmwareInfo_dsp(LPCSTR filename, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult) {
DWORD result;
if (filename) {
@ -2009,7 +2014,7 @@ int fwdlusb_getFirmwareInfo_dsp(LPCSTR filename, uint8_t *rBuffer, uint32_t *rLe
return result;
}
int fwdlusb_getFirmwareInfo_param(LPCSTR filename, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult) {
int WINAPI fwdlusb_getFirmwareInfo_param(LPCSTR filename, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult) {
DWORD result;
if (filename) {
@ -2045,7 +2050,7 @@ int fwdlusb_getFirmwareInfo_param(LPCSTR filename, uint8_t *rBuffer, uint32_t *r
return result;
}
int fwdlusb_getFirmwareInfo(uint8_t update, LPCSTR filename, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult) {
int WINAPI fwdlusb_getFirmwareInfo(uint8_t update, LPCSTR filename, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult) {
dprintf("Printer: C3XXFWDLusb: %s(update: %d, filename: %s)\n", __func__, update, filename);
if (!rBuffer) {
*rLen = 38;
@ -2064,25 +2069,25 @@ int fwdlusb_getFirmwareInfo(uint8_t update, LPCSTR filename, uint8_t *rBuffer, u
}
}
int fwdlusb_MakeThread(uint16_t maxCount) {
int WINAPI fwdlusb_MakeThread(uint16_t maxCount) {
dprintf("Printer: C3XXFWDLusb: %s\n", __func__);
return 1;
}
int fwdlusb_ReleaseThread(uint16_t *rResult) {
int WINAPI fwdlusb_ReleaseThread(uint16_t *rResult) {
dprintf("Printer: C3XXFWDLusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int fwdlusb_AttachThreadCount(uint16_t *rCount, uint16_t *rMaxCount) {
int WINAPI fwdlusb_AttachThreadCount(uint16_t *rCount, uint16_t *rMaxCount) {
dprintf("Printer: C3XXFWDLusb: %s\n", __func__);
*rCount = 0;
*rMaxCount = 1;
return 1;
}
int fwdlusb_getErrorLog(uint16_t index, uint8_t *rData, uint16_t *rResult) {
int WINAPI fwdlusb_getErrorLog(uint16_t index, uint8_t *rData, uint16_t *rResult) {
dprintf("Printer: C3XXFWDLusb: %s\n", __func__);
*rResult = 0;
return 1;
@ -2090,12 +2095,12 @@ int fwdlusb_getErrorLog(uint16_t index, uint8_t *rData, uint16_t *rResult) {
// C3XXusb stubs
int chcusb_MakeThread(uint16_t maxCount) {
int WINAPI chcusb_MakeThread(uint16_t maxCount) {
dprintf("Printer: C3XXusb: %s\n", __func__);
return 1;
}
int chcusb_open(uint16_t *rResult) {
int WINAPI chcusb_open(uint16_t *rResult) {
// Seed random for card id generation
srand(time(NULL));
generate_rfid();
@ -2109,38 +2114,38 @@ void chcusb_close() {
dprintf("Printer: C3XXusb: %s\n", __func__);
}
int chcusb_ReleaseThread(uint16_t *rResult) {
int WINAPI chcusb_ReleaseThread(uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
return 1;
}
int chcusb_listupPrinter(uint8_t *rIdArray) {
int WINAPI chcusb_listupPrinter(uint8_t *rIdArray) {
dprintf("Printer: C3XXusb: %s\n", __func__);
memset(rIdArray, 0xFF, 0x80);
rIdArray[0] = idNumber;
return 1;
}
int chcusb_listupPrinterSN(uint64_t *rSerialArray) {
int WINAPI chcusb_listupPrinterSN(uint64_t *rSerialArray) {
dprintf("Printer: C3XXusb: %s\n", __func__);
memset(rSerialArray, 0xFF, 0x400);
rSerialArray[0] = serialNo;
return 1;
}
int chcusb_selectPrinter(uint8_t printerId, uint16_t *rResult) {
int WINAPI chcusb_selectPrinter(uint8_t printerId, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_selectPrinterSN(uint64_t printerSN, uint16_t *rResult) {
int WINAPI chcusb_selectPrinterSN(uint64_t printerSN, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen) {
int WINAPI chcusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen) {
// dprintf("Printer: C3XXusb: %s\n", __func__);
switch (tagNumber) {
@ -2154,6 +2159,7 @@ int chcusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen)
if (*rLen != 0x99) *rLen = 0x99;
if (rBuffer) {
memset(rBuffer, 0, *rLen);
rBuffer[0] = 4; // firmware count
// bootFirmware
int i = 1;
memcpy(rBuffer + i, mainFirmware, sizeof(mainFirmware));
@ -2239,6 +2245,8 @@ int chcusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen)
*rLen = 1;
if (awaitingCardExit)
*rBuffer = 0xF0;
else if (STATUS == 1)
*rBuffer = 1;
else
*rBuffer = 0;
break;
@ -2294,7 +2302,18 @@ int chcusb_getPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen)
return 1;
}
int chcusb_imageformat(
int WINAPI chcusb_imageformat(
uint16_t format,
uint16_t ncomp,
uint16_t depth,
uint16_t width,
uint16_t height,
uint8_t *image,
uint16_t *rResult) {
return chcusb_imageformat_330(format, ncomp, depth, width, height, rResult);
}
int WINAPI chcusb_imageformat_330(
uint16_t format,
uint16_t ncomp,
uint16_t depth,
@ -2310,14 +2329,14 @@ int chcusb_imageformat(
return 1;
}
int chcusb_setmtf(int32_t *mtf) {
int __thiscall chcusb_setmtf(int32_t *mtf) {
dprintf("Printer: C3XXusb: %s\n", __func__);
memcpy(MTF, mtf, sizeof(MTF));
return 1;
}
int chcusb_makeGamma(uint16_t k, uint8_t *intoneR, uint8_t *intoneG, uint8_t *intoneB) {
int WINAPI chcusb_makeGamma(uint16_t k, uint8_t *intoneR, uint8_t *intoneG, uint8_t *intoneB) {
dprintf("Printer: C3XXusb: %s\n", __func__);
uint8_t tone;
@ -2348,7 +2367,7 @@ int chcusb_makeGamma(uint16_t k, uint8_t *intoneR, uint8_t *intoneG, uint8_t *in
return 1;
}
int chcusb_setIcctable(
int WINAPI chcusb_setIcctable(
LPCSTR icc1,
LPCSTR icc2,
uint16_t intents,
@ -2361,32 +2380,34 @@ int chcusb_setIcctable(
uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
for (int i = 0; i < 256; ++i) {
intoneR[i] = i;
intoneG[i] = i;
intoneB[i] = i;
outtoneR[i] = i;
outtoneG[i] = i;
outtoneB[i] = i;
if (intoneR != NULL && intoneG != NULL && intoneB != NULL && outtoneR != NULL && outtoneG != NULL && outtoneB != NULL) {
for (int i = 0; i < 256; ++i) {
intoneR[i] = i;
intoneG[i] = i;
intoneB[i] = i;
outtoneR[i] = i;
outtoneG[i] = i;
outtoneB[i] = i;
}
}
*rResult = 0;
return 1;
}
int chcusb_copies(uint16_t copies, uint16_t *rResult) {
int WINAPI chcusb_copies(uint16_t copies, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_status(uint16_t *rResult) {
int WINAPI chcusb_status(uint16_t *rResult) {
// dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_statusAll(uint8_t *idArray, uint16_t *rResultArray) {
int WINAPI chcusb_statusAll(uint8_t *idArray, uint16_t *rResultArray) {
dprintf("Printer: C3XXusb: %s\n", __func__);
for (int i = 0; *(uint8_t *)(idArray + i) != 255 && i < 128; ++i) {
@ -2396,7 +2417,7 @@ int chcusb_statusAll(uint8_t *idArray, uint16_t *rResultArray) {
return 1;
}
int chcusb_startpage(uint16_t postCardState, uint16_t *pageId, uint16_t *rResult) {
int WINAPI chcusb_startpage(uint16_t postCardState, uint16_t *pageId, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
STATUS = 2;
@ -2406,7 +2427,16 @@ int chcusb_startpage(uint16_t postCardState, uint16_t *pageId, uint16_t *rResult
return 1;
}
int chcusb_endpage(uint16_t *rResult) {
int WINAPI chcusb_startpage_300(uint16_t postCardState, uint16_t *rResult) {
dprintf("Printer: C300usb: %s\n", __func__);
STATUS = 2;
*rResult = 0;
return 1;
}
int WINAPI chcusb_endpage(uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
awaitingCardExit = true;
@ -2415,7 +2445,7 @@ int chcusb_endpage(uint16_t *rResult) {
return 1;
}
int chcusb_write(uint8_t *data, uint32_t *writeSize, uint16_t *rResult) {
int WINAPI chcusb_write(uint8_t *data, uint32_t *writeSize, uint16_t *rResult) {
SYSTEMTIME t;
GetLocalTime(&t);
@ -2439,7 +2469,7 @@ int chcusb_write(uint8_t *data, uint32_t *writeSize, uint16_t *rResult) {
return 1;
}
int chcusb_writeLaminate(uint8_t *data, uint32_t *writeSize, uint16_t *rResult) {
int WINAPI chcusb_writeLaminate(uint8_t *data, uint32_t *writeSize, uint16_t *rResult) {
SYSTEMTIME t;
GetLocalTime(&t);
@ -2458,7 +2488,7 @@ int chcusb_writeLaminate(uint8_t *data, uint32_t *writeSize, uint16_t *rResult)
return 1;
}
int chcusb_writeHolo(uint8_t *data, uint32_t *writeSize, uint16_t *rResult) {
int WINAPI chcusb_writeHolo(uint8_t *data, uint32_t *writeSize, uint16_t *rResult) {
SYSTEMTIME t;
GetLocalTime(&t);
@ -2477,7 +2507,7 @@ int chcusb_writeHolo(uint8_t *data, uint32_t *writeSize, uint16_t *rResult) {
return 1;
}
int chcusb_setPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult) {
int WINAPI chcusb_setPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
switch (tagNumber) {
@ -2495,7 +2525,7 @@ int chcusb_setPrinterInfo(uint16_t tagNumber, uint8_t *rBuffer, uint32_t *rLen,
return 1;
}
int chcusb_getGamma(LPCSTR filename, uint8_t *r, uint8_t *g, uint8_t *b, uint16_t *rResult) {
int WINAPI chcusb_getGamma(LPCSTR filename, uint8_t *r, uint8_t *g, uint8_t *b, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
for (int i = 0; i < 256; ++i) {
@ -2508,7 +2538,7 @@ int chcusb_getGamma(LPCSTR filename, uint8_t *r, uint8_t *g, uint8_t *b, uint16_
return 1;
}
int chcusb_getMtf(LPCSTR filename, int32_t *mtf, uint16_t *rResult) {
int WINAPI chcusb_getMtf(LPCSTR filename, int32_t *mtf, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
@ -2545,13 +2575,13 @@ int chcusb_getMtf(LPCSTR filename, int32_t *mtf, uint16_t *rResult) {
return 1;
}
int chcusb_cancelCopies(uint16_t pageId, uint16_t *rResult) {
int WINAPI chcusb_cancelCopies(uint16_t pageId, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_setPrinterToneCurve(uint16_t type, uint16_t number, uint16_t *data, uint16_t *rResult) {
int WINAPI chcusb_setPrinterToneCurve(uint16_t type, uint16_t number, uint16_t *data, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
if ((type > 0 && type < 3) && (number > 0 && number < 3)) {
CURVE[type][number] = *data;
@ -2560,7 +2590,7 @@ int chcusb_setPrinterToneCurve(uint16_t type, uint16_t number, uint16_t *data, u
return 1;
}
int chcusb_getPrinterToneCurve(uint16_t type, uint16_t number, uint16_t *data, uint16_t *rResult) {
int WINAPI chcusb_getPrinterToneCurve(uint16_t type, uint16_t number, uint16_t *data, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
if ((type > 0 && type < 3) && (number > 0 && number < 3)) {
*data = CURVE[type][number];
@ -2569,26 +2599,26 @@ int chcusb_getPrinterToneCurve(uint16_t type, uint16_t number, uint16_t *data, u
return 1;
}
int chcusb_blinkLED(uint16_t *rResult) {
int WINAPI chcusb_blinkLED(uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_resetPrinter(uint16_t *rResult) {
int WINAPI chcusb_resetPrinter(uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_AttachThreadCount(uint16_t *rCount, uint16_t *rMaxCount) {
int WINAPI chcusb_AttachThreadCount(uint16_t *rCount, uint16_t *rMaxCount) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rCount = 0;
*rMaxCount = 1;
return 1;
}
int chcusb_getPrintIDStatus(uint16_t pageId, uint8_t *rBuffer, uint16_t *rResult) {
int WINAPI chcusb_getPrintIDStatus(uint16_t pageId, uint8_t *rBuffer, uint16_t *rResult) {
// dprintf("Printer: C3XXusb: %s\n", __func__);
memset(rBuffer, 0, 8);
@ -2606,7 +2636,7 @@ int chcusb_getPrintIDStatus(uint16_t pageId, uint8_t *rBuffer, uint16_t *rResult
return 1;
}
int chcusb_setPrintStandby(uint16_t position, uint16_t *rResult) {
int WINAPI chcusb_setPrintStandby(uint16_t position, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
if (STATUS == 0)
@ -2622,13 +2652,29 @@ int chcusb_setPrintStandby(uint16_t position, uint16_t *rResult) {
return 1;
}
int chcusb_testCardFeed(uint16_t mode, uint16_t times, uint16_t *rResult) {
int WINAPI chcusb_setPrintStandby_300(uint16_t *rResult) {
dprintf("Printer: C300usb: %s\n", __func__);
*rResult = 0;
if (awaitingCardExit){ // 300 does not use exitCard, so reset this for getPrinterInfo.
awaitingCardExit = false;
STATUS = 1;
}
if (STATUS == 0)
{
STATUS = 1;
}
return 1;
}
int WINAPI chcusb_testCardFeed(uint16_t mode, uint16_t times, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_exitCard(uint16_t *rResult) {
int WINAPI chcusb_exitCard(uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
awaitingCardExit = false;
@ -2638,7 +2684,7 @@ int chcusb_exitCard(uint16_t *rResult) {
return 1;
}
int chcusb_getCardRfidTID(uint8_t *rCardTID, uint16_t *rResult) {
int WINAPI chcusb_getCardRfidTID(uint8_t *rCardTID, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
memcpy(rCardTID, cardRFID, sizeof(cardRFID));
@ -2646,7 +2692,7 @@ int chcusb_getCardRfidTID(uint8_t *rCardTID, uint16_t *rResult) {
return 1;
}
int chcusb_commCardRfidReader(uint8_t *sendData, uint8_t *rRecvData, uint32_t sendSize, uint32_t *rRecvSize, uint16_t *rResult) {
int WINAPI chcusb_commCardRfidReader(uint8_t *sendData, uint8_t *rRecvData, uint32_t sendSize, uint32_t *rRecvSize, uint16_t *rResult) {
uint8_t off;
dprintf("Printer: C3XXusb: %s\n", __func__);
@ -2709,67 +2755,67 @@ int chcusb_commCardRfidReader(uint8_t *sendData, uint8_t *rRecvData, uint32_t se
return 1;
}
int chcusb_updateCardRfidReader(uint8_t *data, uint32_t size, uint16_t *rResult) {
int WINAPI chcusb_updateCardRfidReader(uint8_t *data, uint32_t size, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_getErrorLog(uint16_t index, uint8_t *rData, uint16_t *rResult) {
int WINAPI chcusb_getErrorLog(uint16_t index, uint8_t *rData, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_getErrorStatus(uint16_t *rBuffer) {
int WINAPI chcusb_getErrorStatus(uint16_t *rBuffer) {
dprintf("Printer: C3XXusb: %s\n", __func__);
memset(rBuffer, 0, 0x80);
return 1;
}
int chcusb_setCutList(uint8_t *rData, uint16_t *rResult) {
int WINAPI chcusb_setCutList(uint8_t *rData, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_setLaminatePattern(uint16_t index, uint8_t *rData, uint16_t *rResult) {
int WINAPI chcusb_setLaminatePattern(uint16_t index, uint8_t *rData, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_color_adjustment(LPCSTR filename, int32_t a2, int32_t a3, int16_t a4, int16_t a5, int64_t a6, int64_t a7, uint16_t *rResult) {
int WINAPI chcusb_color_adjustment(LPCSTR filename, int32_t a2, int32_t a3, int16_t a4, int16_t a5, int64_t a6, int64_t a7, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_color_adjustmentEx(int32_t a1, int32_t a2, int32_t a3, int16_t a4, int16_t a5, int64_t a6, int64_t a7, uint16_t *rResult) {
int WINAPI chcusb_color_adjustmentEx(int32_t a1, int32_t a2, int32_t a3, int16_t a4, int16_t a5, int64_t a6, int64_t a7, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_getEEPROM(uint8_t index, uint8_t *rData, uint16_t *rResult) {
int WINAPI chcusb_getEEPROM(uint8_t index, uint8_t *rData, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_setParameter(uint8_t a1, uint32_t a2, uint16_t *rResult) {
int WINAPI chcusb_setParameter(uint8_t a1, uint32_t a2, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_getParameter(uint8_t a1, uint8_t *a2, uint16_t *rResult) {
int WINAPI chcusb_getParameter(uint8_t a1, uint8_t *a2, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
}
int chcusb_universal_command(int32_t a1, uint8_t a2, int32_t a3, uint8_t *a4, uint16_t *rResult) {
int WINAPI chcusb_universal_command(int32_t a1, uint8_t a2, int32_t a3, uint8_t *a4, uint16_t *rResult) {
dprintf("Printer: C3XXusb: %s\n", __func__);
*rResult = 0;
return 1;
@ -2960,7 +3006,7 @@ int CHCUSB_getPrinterToneCurve(const void *handle, uint16_t type, uint16_t numbe
int CHCUSB_imageformat(const void *handle, uint16_t format, uint16_t ncomp, uint16_t depth, uint16_t width, uint16_t height, uint8_t *inputImage, uint16_t *rResult)
{
return chcusb_imageformat(format, ncomp, depth, width, height, rResult);
return chcusb_imageformat(format, ncomp, depth, width, height, inputImage, rResult);
}
int CHCUSB_init(LPCSTR dllpath)
@ -3282,3 +3328,8 @@ DWORD WriteArrayToFile(LPCSTR lpOutputFilePath, LPVOID lpDataTemp, DWORD nDataSi
#endif
}
void printer_set_dimensions(int width, int height){
WIDTH = width;
HEIGHT = height;
}

View File

@ -2,6 +2,7 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
struct printer_config {
bool enable;
@ -15,3 +16,8 @@ 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);
void printer_set_dimensions(int width, int height);
int WINAPI fwdlusb_updateFirmware_main(uint8_t update, LPCSTR filename, uint16_t *rResult);
int WINAPI fwdlusb_updateFirmware_dsp(uint8_t update, LPCSTR filename, uint16_t *rResult);
int WINAPI fwdlusb_updateFirmware_param(uint8_t update, LPCSTR filename, uint16_t *rResult);

View File

@ -37,7 +37,7 @@ uint64_t felica_get_generic_PMm(void)
Suica passes and other payment and transportation cards
do not seem to be supported anymore. */
return 0x01168B868FBECBFFULL;
return 0x00F1000000014300;
}
HRESULT felica_transact(

View File

@ -89,6 +89,7 @@ void idac_hook_config_load(
zinput_config_load(&cfg->zinput, filename);
dvd_config_load(&cfg->dvd, filename);
io4_config_load(&cfg->io4, filename);
ffb_config_load(&cfg->ffb, filename);
led15070_config_load(&cfg->led15070, filename);
indrun_config_load(&cfg->indrun, filename);
}

View File

@ -19,6 +19,7 @@ struct idac_hook_config {
struct aime_config aime;
struct dvd_config dvd;
struct io4_config io4;
struct ffb_config ffb;
struct idac_dll_config dll;
struct zinput_config zinput;
struct led15070_config led15070;

View File

@ -5,7 +5,9 @@
USB: 837-15257 "Type 4" I/O Board
COM1: 838-15069 MOTOR DRIVE BD RS232/422 Board
COM2: 837-15070-02 IC BD LED Controller Board
COM2: 837-15070-02 IC BD LED Controller Board (DIPSW2 OFF)
OR
837-15070-04 IC BD LED Controller Board (DIPSW2 ON)
COM3: 837-15286 "Gen 2" Aime Reader (DIPSW2 OFF)
OR
837-15396 "Gen 3" Aime Reader (DIPSW2 ON)
@ -28,6 +30,7 @@
#include "idachook/config.h"
#include "idachook/idac-dll.h"
#include "idachook/io4.h"
#include "idachook/ffb.h"
#include "idachook/zinput.h"
#include "platform/platform.h"
@ -84,6 +87,12 @@ static DWORD CALLBACK idac_pre_startup(void)
goto fail;
}
hr = idac_ffb_hook_init(&idac_hook_cfg.ffb, 1);
if (FAILED(hr)) {
goto fail;
}
hr = led15070_hook_init(&idac_hook_cfg.led15070, idac_dll.led_init,
idac_dll.led_set_fet_output, NULL, idac_dll.led_gs_update, 2, 1);

59
idachook/ffb.c Normal file
View File

@ -0,0 +1,59 @@
#include <windows.h>
#include <xinput.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "board/ffb.h"
#include "idachook/idac-dll.h"
#include "util/dprintf.h"
static void idac_ffb_toggle(bool active);
static void idac_ffb_constant_force(uint8_t direction, uint8_t force);
static void idac_ffb_rumble(uint8_t force, uint8_t period);
static void idac_ffb_damper(uint8_t force);
static const struct ffb_ops idac_ffb_ops = {
.toggle = idac_ffb_toggle,
.constant_force = idac_ffb_constant_force,
.rumble = idac_ffb_rumble,
.damper = idac_ffb_damper
};
HRESULT idac_ffb_hook_init(const struct ffb_config *cfg, unsigned int port_no)
{
HRESULT hr;
assert(idac_dll.init != NULL);
hr = ffb_hook_init(cfg, &idac_ffb_ops, port_no);
if (FAILED(hr)) {
return hr;
}
return idac_dll.ffb_init();
}
static void idac_ffb_toggle(bool active)
{
idac_dll.ffb_toggle(active);
}
static void idac_ffb_constant_force(uint8_t direction, uint8_t force)
{
idac_dll.ffb_constant_force(direction, force);
}
static void idac_ffb_rumble(uint8_t force, uint8_t period)
{
idac_dll.ffb_rumble(force, period);
}
static void idac_ffb_damper(uint8_t force)
{
idac_dll.ffb_damper(force);
}

7
idachook/ffb.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <windows.h>
#include "board/ffb.h"
HRESULT idac_ffb_hook_init(const struct ffb_config *cfg, unsigned int port_no);

View File

@ -36,6 +36,21 @@ const struct dll_bind_sym idac_dll_syms[] = {
}, {
.sym = "idac_io_led_set_leds",
.off = offsetof(struct idac_dll, led_set_leds),
}, {
.sym = "idac_io_ffb_init",
.off = offsetof(struct idac_dll, ffb_init),
}, {
.sym = "idac_io_ffb_toggle",
.off = offsetof(struct idac_dll, ffb_toggle),
}, {
.sym = "idac_io_ffb_constant_force",
.off = offsetof(struct idac_dll, ffb_constant_force),
}, {
.sym = "idac_io_ffb_rumble",
.off = offsetof(struct idac_dll, ffb_rumble),
}, {
.sym = "idac_io_ffb_damper",
.off = offsetof(struct idac_dll, ffb_damper),
}
};

View File

@ -15,6 +15,11 @@ struct idac_dll {
void (*led_set_fet_output)(const uint8_t *rgb);
void (*led_gs_update)(const uint8_t *rgb);
void (*led_set_leds)(const uint8_t *rgb);
HRESULT (*ffb_init)(void);
void (*ffb_toggle)(bool active);
void (*ffb_constant_force)(uint8_t direction, uint8_t force);
void (*ffb_rumble)(uint8_t period, uint8_t force);
void (*ffb_damper)(uint8_t force);
};
struct idac_dll_config {

View File

@ -21,3 +21,8 @@ EXPORTS
idac_io_led_set_fet_output
idac_io_led_gs_update
idac_io_led_set_leds
idac_io_ffb_init
idac_io_ffb_toggle
idac_io_ffb_constant_force
idac_io_ffb_rumble
idac_io_ffb_damper

View File

@ -15,7 +15,7 @@ static HRESULT idac_io4_write_gpio(uint8_t* payload, size_t len);
static uint16_t coins;
static const struct io4_ops idac_io4_ops = {
.poll = idac_io4_poll,
.poll = idac_io4_poll,
.write_gpio = idac_io4_write_gpio
};
@ -133,6 +133,8 @@ static HRESULT idac_io4_poll(void *ctx, struct io4_state *state)
static HRESULT idac_io4_write_gpio(uint8_t* payload, size_t len)
{
assert(idac_dll.led_set_leds != NULL);
// Just fast fail if there aren't enough bytes in the payload
if (len < 3)
return S_OK;

View File

@ -30,5 +30,7 @@ shared_library(
'zinput.h',
'indrun.c',
'indrun.h',
'ffb.c',
'ffb.h',
],
)

View File

@ -9,4 +9,9 @@ struct idac_io_backend {
void (*get_gamebtns)(uint8_t *gamebtn);
void (*get_shifter)(uint8_t *gear);
void (*get_analogs)(struct idac_io_analog_state *state);
HRESULT (*ffb_init)(void);
void (*ffb_toggle)(bool active);
void (*ffb_constant_force)(uint8_t direction, uint8_t force);
void (*ffb_rumble)(uint8_t period, uint8_t force);
void (*ffb_damper)(uint8_t force);
};

View File

@ -80,13 +80,29 @@ void idac_di_config_load(struct idac_di_config *cfg, const wchar_t *filename)
}
// FFB configuration
cfg->ffb_constant_force_strength = GetPrivateProfileIntW(
L"dinput",
L"constantForceStrength",
100,
filename);
cfg->center_spring_strength = GetPrivateProfileIntW(
L"dinput",
L"centerSpringStrength",
30,
filename);
cfg->ffb_rumble_strength = GetPrivateProfileIntW(
L"dinput",
L"rumbleStrength",
100,
filename);
cfg->ffb_damper_strength = GetPrivateProfileIntW(
L"dinput",
L"damperStrength",
100,
filename);
cfg->ffb_rumble_duration = GetPrivateProfileIntW(
L"dinput",
L"rumbleDuration",
1000,
filename);
}
void idac_xi_config_load(struct idac_xi_config *cfg, const wchar_t *filename)

View File

@ -25,7 +25,11 @@ struct idac_di_config {
bool reverse_accel_axis;
// FFB configuration
uint16_t center_spring_strength;
uint8_t ffb_constant_force_strength;
uint8_t ffb_rumble_strength;
uint8_t ffb_damper_strength;
uint32_t ffb_rumble_duration;
};
struct idac_xi_config {

View File

@ -1,134 +1,39 @@
#include <windows.h>
#include <dinput.h>
#include <stdbool.h>
#include <assert.h>
#include "idacio/di-dev.h"
#include "util/dprintf.h"
HRESULT idac_di_dev_start(IDirectInputDevice8W *dev, HWND wnd)
const struct idac_di_config *idac_di_cfg;
static HWND idac_di_wnd;
static IDirectInputDevice8W *idac_di_dev;
/* Individual DI Effects */
static IDirectInputEffect *idac_di_fx;
static IDirectInputEffect *idac_di_fx_rumble;
static IDirectInputEffect *idac_di_fx_damper;
/* Max FFB Board value is 127 */
static const double idac_di_ffb_scale = 127.0;
HRESULT idac_di_dev_init(
const struct idac_di_config *cfg,
IDirectInputDevice8W *dev,
HWND wnd)
{
HRESULT hr;
assert(dev != NULL);
assert(wnd != NULL);
hr = IDirectInputDevice8_SetCooperativeLevel(
dev,
wnd,
DISCL_BACKGROUND | DISCL_EXCLUSIVE);
idac_di_cfg = cfg;
idac_di_dev = dev;
idac_di_wnd = wnd;
if (FAILED(hr)) {
dprintf("DirectInput: SetCooperativeLevel failed: %08x\n", (int) hr);
return hr;
}
hr = IDirectInputDevice8_SetDataFormat(dev, &c_dfDIJoystick);
if (FAILED(hr)) {
dprintf("DirectInput: SetDataFormat failed: %08x\n", (int) hr);
return hr;
}
hr = IDirectInputDevice8_Acquire(dev);
if (FAILED(hr)) {
dprintf("DirectInput: Acquire failed: %08x\n", (int) hr);
return hr;
}
return hr;
}
void idac_di_dev_start_fx(
IDirectInputDevice8W *dev, IDirectInputEffect **out, uint16_t strength)
{
/* Set up force-feedback on devices that support it. This is just a stub
for the time being, since we don't yet know how the serial port force
feedback protocol works.
I'm currently developing with an Xbox One Thrustmaster TMX wheel, if
we don't perform at least some perfunctory FFB initialization of this
nature (or indeed if no DirectInput application is running) then the
wheel exhibits considerable resistance, similar to that of a stationary
car. Changing cf.lMagnitude to a nonzero value does cause the wheel to
continuously turn in the given direction with the given force as one
would expect (max magnitude per DirectInput docs is +/- 10000).
Failure here is non-fatal, we log any errors and move on.
https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416353(v=vs.85)
*/
IDirectInputEffect *obj;
DWORD axis;
LONG direction;
DIEFFECT fx;
DICONDITION cond;
HRESULT hr;
assert(dev != NULL);
assert(out != NULL);
*out = NULL;
dprintf("DirectInput: Starting force feedback (may take a sec)\n");
// Auto-centering effect
axis = DIJOFS_X;
direction = 0;
memset(&cond, 0, sizeof(cond));
cond.lOffset = 0;
cond.lPositiveCoefficient = strength;
cond.lNegativeCoefficient = strength;
cond.dwPositiveSaturation = strength; // For FG920?
cond.dwNegativeSaturation = strength; // For FG920?
cond.lDeadBand = 0;
memset(&fx, 0, sizeof(fx));
fx.dwSize = sizeof(fx);
fx.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
fx.dwDuration = INFINITE;
fx.dwGain = DI_FFNOMINALMAX;
fx.dwTriggerButton = DIEB_NOTRIGGER;
fx.dwTriggerRepeatInterval = INFINITE;
fx.cAxes = 1;
fx.rgdwAxes = &axis;
fx.rglDirection = &direction;
fx.cbTypeSpecificParams = sizeof(cond);
fx.lpvTypeSpecificParams = &cond;
hr = IDirectInputDevice8_CreateEffect(
dev,
&GUID_Spring,
&fx,
&obj,
NULL);
if (FAILED(hr)) {
dprintf("DirectInput: Centering spring force feedback unavailable: %08x\n",
(int) hr);
return;
}
hr = IDirectInputEffect_Start(obj, INFINITE, 0);
if (FAILED(hr)) {
IDirectInputEffect_Release(obj);
dprintf("DirectInput: Centering spring force feedback start failed: %08x\n",
(int) hr);
return;
}
*out = obj;
dprintf("DirectInput: Centering spring effects initialized with strength %d%%\n",
strength / 100);
return S_OK;
}
HRESULT idac_di_dev_poll(
@ -167,3 +72,312 @@ HRESULT idac_di_dev_poll(
return hr;
}
HRESULT idac_di_dev_start(IDirectInputDevice8W *dev, HWND wnd) {
HRESULT hr;
assert(dev != NULL);
assert(wnd != NULL);
hr = IDirectInputDevice8_SetCooperativeLevel(
dev,
wnd,
DISCL_BACKGROUND | DISCL_EXCLUSIVE);
if (FAILED(hr)) {
dprintf("DirectInput: SetCooperativeLevel failed: %08x\n", (int) hr);
return hr;
}
hr = IDirectInputDevice8_SetDataFormat(dev, &c_dfDIJoystick);
if (FAILED(hr)) {
dprintf("DirectInput: SetDataFormat failed: %08x\n", (int) hr);
return hr;
}
hr = IDirectInputDevice8_Acquire(dev);
if (FAILED(hr)) {
dprintf("DirectInput: Acquire failed: %08x\n", (int) hr);
return hr;
}
return hr;
}
HRESULT idac_di_ffb_init(void)
{
HRESULT hr;
hr = idac_di_dev_start(idac_di_dev, idac_di_wnd);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
void idac_di_ffb_toggle(bool active)
{
if (active) {
return;
}
/* Stop and release all effects */
/* I never programmed DirectInput Effects, so this might be bad practice. */
if (idac_di_fx != NULL) {
IDirectInputEffect_Stop(idac_di_fx);
IDirectInputEffect_Release(idac_di_fx);
idac_di_fx = NULL;
}
if (idac_di_fx_rumble != NULL) {
IDirectInputEffect_Stop(idac_di_fx_rumble);
IDirectInputEffect_Release(idac_di_fx_rumble);
idac_di_fx_rumble = NULL;
}
if (idac_di_fx_damper != NULL) {
IDirectInputEffect_Stop(idac_di_fx_damper);
IDirectInputEffect_Release(idac_di_fx_damper);
idac_di_fx_damper = NULL;
}
}
void idac_di_ffb_constant_force(uint8_t direction_ffb, uint8_t force)
{
/* DI expects a magnitude in the range of -10.000 to 10.000 */
uint16_t ffb_strength = idac_di_cfg->ffb_constant_force_strength * 100;
if (ffb_strength == 0) {
return;
}
DWORD axis;
LONG direction;
DIEFFECT fx;
DICONSTANTFORCE cf;
HRESULT hr;
/* Direction 0: move to the right, 1: move to the left */
LONG magnitude = (LONG)(((double)force / idac_di_ffb_scale) * ffb_strength);
cf.lMagnitude = (direction_ffb == 0) ? -magnitude : magnitude;
axis = DIJOFS_X;
/* Irrelevant as magnitude descripbes the direction */
direction = 0;
memset(&fx, 0, sizeof(fx));
fx.dwSize = sizeof(fx);
fx.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
fx.dwDuration = INFINITE;
fx.dwGain = DI_FFNOMINALMAX;
fx.dwTriggerButton = DIEB_NOTRIGGER;
fx.dwTriggerRepeatInterval = INFINITE;
fx.cAxes = 1;
fx.rgdwAxes = &axis;
fx.rglDirection = &direction;
fx.cbTypeSpecificParams = sizeof(cf);
fx.lpvTypeSpecificParams = &cf;
if (idac_di_fx != NULL) {
// Try to update the existing effect
hr = IDirectInputEffect_SetParameters(idac_di_fx, &fx, DIEP_TYPESPECIFICPARAMS);
if (SUCCEEDED(hr)) {
return;
} else {
dprintf("DirectInput: Failed to update constant force feedback, recreating effect: %08x\n", (int)hr);
// Stop and release the current effect if updating fails
IDirectInputEffect_Stop(idac_di_fx);
IDirectInputEffect_Release(idac_di_fx);
idac_di_fx = NULL;
}
}
// Create a new constant force effect
IDirectInputEffect *obj;
hr = IDirectInputDevice8_CreateEffect(
idac_di_dev,
&GUID_ConstantForce,
&fx,
&obj,
NULL);
if (FAILED(hr)) {
dprintf("DirectInput: Constant force feedback creation failed: %08x\n", (int) hr);
return;
}
hr = IDirectInputEffect_Start(obj, INFINITE, 0);
if (FAILED(hr)) {
dprintf("DirectInput: Constant force feedback start failed: %08x\n", (int) hr);
IDirectInputEffect_Release(obj);
return;
}
idac_di_fx = obj;
}
void idac_di_ffb_rumble(uint8_t force, uint8_t period)
{
/* DI expects a magnitude in the range of -10.000 to 10.000 */
uint16_t ffb_strength = idac_di_cfg->ffb_rumble_strength * 100;
if (ffb_strength == 0) {
return;
}
uint32_t ffb_duration = idac_di_cfg->ffb_rumble_duration;
DWORD axis;
LONG direction;
DIEFFECT fx;
DIPERIODIC pe;
HRESULT hr;
/* Duration in microseconds,
Might be totally wrong as especially on FANATEC wheels as this code will
crash the game. TODO: Figure out why this effect will crash on FANATEC! */
DWORD duration = (DWORD)((double)force * ffb_duration);
memset(&pe, 0, sizeof(pe));
pe.dwMagnitude = (DWORD)(((double)force / idac_di_ffb_scale) * ffb_strength);
pe.lOffset = 0;
pe.dwPhase = 0;
pe.dwPeriod = duration;
axis = DIJOFS_X;
direction = 0;
memset(&fx, 0, sizeof(fx));
fx.dwSize = sizeof(fx);
fx.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
fx.dwDuration = duration;
fx.dwGain = DI_FFNOMINALMAX;
fx.dwTriggerButton = DIEB_NOTRIGGER;
fx.dwTriggerRepeatInterval = INFINITE;
fx.cAxes = 1;
fx.rgdwAxes = &axis;
fx.rglDirection = &direction;
fx.cbTypeSpecificParams = sizeof(pe);
fx.lpvTypeSpecificParams = &pe;
if (idac_di_fx_rumble != NULL) {
// Try to update the existing effect
hr = IDirectInputEffect_SetParameters(idac_di_fx_rumble, &fx, DIEP_TYPESPECIFICPARAMS);
if (SUCCEEDED(hr)) {
return;
} else {
dprintf("DirectInput: Failed to update periodic force feedback, recreating effect: %08x\n", (int)hr);
// Stop and release the current effect if updating fails
IDirectInputEffect_Stop(idac_di_fx_rumble);
IDirectInputEffect_Release(idac_di_fx_rumble);
idac_di_fx_rumble = NULL;
}
}
IDirectInputEffect *obj;
hr = IDirectInputDevice8_CreateEffect(
idac_di_dev,
&GUID_Sine,
&fx,
&obj,
NULL);
if (FAILED(hr)) {
dprintf("DirectInput: Periodic force feedback creation failed: %08x\n", (int) hr);
return;
}
hr = IDirectInputEffect_Start(obj, INFINITE, 0);
if (FAILED(hr)) {
dprintf("DirectInput: Periodic force feedback start failed: %08x\n", (int) hr);
IDirectInputEffect_Release(obj);
return;
}
idac_di_fx_rumble = obj;
}
void idac_di_ffb_damper(uint8_t force)
{
/* DI expects a coefficient in the range of -10.000 to 10.000 */
uint16_t ffb_strength = idac_di_cfg->ffb_damper_strength * 100;
if (ffb_strength == 0) {
return;
}
DWORD axis;
LONG direction;
DIEFFECT fx;
DICONDITION cond;
HRESULT hr;
memset(&cond, 0, sizeof(cond));
cond.lOffset = 0;
cond.lPositiveCoefficient = (LONG)(((double)force / idac_di_ffb_scale) * ffb_strength);
cond.lNegativeCoefficient = (LONG)(((double)force / idac_di_ffb_scale) * ffb_strength);
/* Not sure on this one */
cond.dwPositiveSaturation = DI_FFNOMINALMAX;
cond.dwNegativeSaturation = DI_FFNOMINALMAX;
cond.lDeadBand = 0;
axis = DIJOFS_X;
direction = 0;
memset(&fx, 0, sizeof(fx));
fx.dwSize = sizeof(fx);
fx.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
fx.dwDuration = INFINITE;
fx.dwGain = DI_FFNOMINALMAX;
fx.dwTriggerButton = DIEB_NOTRIGGER;
fx.dwTriggerRepeatInterval = INFINITE;
fx.cAxes = 1;
fx.rgdwAxes = &axis;
fx.rglDirection = &direction;
fx.cbTypeSpecificParams = sizeof(cond);
fx.lpvTypeSpecificParams = &cond;
if (idac_di_fx_damper != NULL) {
// Try to update the existing effect
hr = IDirectInputEffect_SetParameters(idac_di_fx_damper, &fx, DIEP_TYPESPECIFICPARAMS);
if (SUCCEEDED(hr)) {
return;
} else {
dprintf("DirectInput: Failed to update damper force feedback, recreating effect: %08x\n", (int)hr);
// Stop and release the current effect if updating fails
IDirectInputEffect_Stop(idac_di_fx_damper);
IDirectInputEffect_Release(idac_di_fx_damper);
idac_di_fx_damper = NULL;
}
}
// Create a new damper force effect
IDirectInputEffect *obj;
hr = IDirectInputDevice8_CreateEffect(
idac_di_dev,
&GUID_Damper,
&fx,
&obj,
NULL);
if (FAILED(hr)) {
dprintf("DirectInput: Damper force feedback creation failed: %08x\n", (int) hr);
return;
}
hr = IDirectInputEffect_Start(obj, INFINITE, 0);
if (FAILED(hr)) {
dprintf("DirectInput: Damper force feedback start failed: %08x\n", (int) hr);
IDirectInputEffect_Release(obj);
return;
}
idac_di_fx_damper = obj;
}

View File

@ -5,15 +5,26 @@
#include <stdint.h>
#include "idacio/config.h"
union idac_di_state {
DIJOYSTATE st;
uint8_t bytes[sizeof(DIJOYSTATE)];
};
HRESULT idac_di_dev_init(
const struct idac_di_config *cfg,
IDirectInputDevice8W *dev,
HWND wnd);
HRESULT idac_di_dev_start(IDirectInputDevice8W *dev, HWND wnd);
void idac_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out, uint16_t strength);
HRESULT idac_di_dev_poll(
IDirectInputDevice8W *dev,
HWND wnd,
union idac_di_state *out);
HRESULT idac_di_ffb_init(void);
void idac_di_ffb_toggle(bool active);
void idac_di_ffb_constant_force(uint8_t direction, uint8_t force);
void idac_di_ffb_rumble(uint8_t force, uint8_t period);
void idac_di_ffb_damper(uint8_t force);

View File

@ -52,9 +52,14 @@ static const struct idac_di_axis idac_di_axes[] = {
};
static const struct idac_io_backend idac_di_backend = {
.get_gamebtns = idac_di_get_buttons,
.get_shifter = idac_di_get_shifter,
.get_analogs = idac_di_get_analogs,
.get_gamebtns = idac_di_get_buttons,
.get_shifter = idac_di_get_shifter,
.get_analogs = idac_di_get_analogs,
.ffb_init = idac_di_ffb_init,
.ffb_toggle = idac_di_ffb_toggle,
.ffb_constant_force = idac_di_ffb_constant_force,
.ffb_rumble = idac_di_ffb_rumble,
.ffb_damper = idac_di_ffb_damper
};
static HWND idac_di_wnd;
@ -62,7 +67,6 @@ static IDirectInput8W *idac_di_api;
static IDirectInputDevice8W *idac_di_dev;
static IDirectInputDevice8W *idac_di_pedals;
static IDirectInputDevice8W *idac_di_shifter;
static IDirectInputEffect *idac_di_fx;
static size_t idac_di_off_brake;
static size_t idac_di_off_accel;
static uint8_t idac_di_shift_dn;
@ -75,7 +79,6 @@ static uint8_t idac_di_gear[6];
static bool idac_di_use_pedals;
static bool idac_di_reverse_brake_axis;
static bool idac_di_reverse_accel_axis;
static uint16_t idac_di_center_spring_strength;
HRESULT idac_di_init(
const struct idac_di_config *cfg,
@ -105,7 +108,7 @@ HRESULT idac_di_init(
return hr;
}
/* Initial D Zero has some built-in DirectInput support that is not
/* Initial D THE ARCADE has some built-in DirectInput support that is not
particularly useful. idachook shorts this out by redirecting dinput8.dll
to a no-op implementation of DirectInput. However, idacio does need to
talk to the real operating system implementation of DirectInput without
@ -168,16 +171,12 @@ HRESULT idac_di_init(
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
hr = idac_di_dev_start(idac_di_dev, idac_di_wnd);
hr = idac_di_dev_init(cfg, idac_di_dev, idac_di_wnd);
if (FAILED(hr)) {
return hr;
}
// Convert the strength from 0-100 to 0-10000 for DirectInput
idac_di_dev_start_fx(idac_di_dev, &idac_di_fx,
idac_di_center_spring_strength * 100);
if (cfg->pedals_name[0] != L'\0') {
hr = IDirectInput8_EnumDevices(
idac_di_api,
@ -367,15 +366,24 @@ static HRESULT idac_di_config_apply(const struct idac_di_config *cfg)
idac_di_gear[i] = cfg->gear[i];
}
// FFB configuration
/* FFB configuration */
if (cfg->ffb_constant_force_strength < 0 || cfg->ffb_constant_force_strength > 100) {
dprintf("Wheel: Invalid constant force strength: %i\n", cfg->ffb_constant_force_strength);
if (cfg->center_spring_strength < 0 || cfg->center_spring_strength > 100) {
dprintf("Wheel: Invalid center spring strength: %i\n", cfg->center_spring_strength);
return E_INVALIDARG;
}
if (cfg->ffb_rumble_strength < 0 || cfg->ffb_rumble_strength > 100) {
dprintf("Wheel: Invalid rumble strength: %i\n", cfg->ffb_rumble_strength);
return E_INVALIDARG;
}
idac_di_center_spring_strength = cfg->center_spring_strength;
if (cfg->ffb_damper_strength < 0 || cfg->ffb_damper_strength > 100) {
dprintf("Wheel: Invalid damper strength: %i\n", cfg->ffb_damper_strength);
return E_INVALIDARG;
}
return S_OK;
}

View File

@ -19,7 +19,7 @@ static bool idac_io_coin;
uint16_t idac_io_get_api_version(void)
{
return 0x0101;
return 0x0102;
}
HRESULT idac_io_init(void)
@ -62,6 +62,8 @@ void idac_io_get_opbtns(uint8_t *opbtn_out)
opbtn = 0;
/* Common operator buttons, not backend-specific */
if (GetAsyncKeyState(idac_io_cfg.vk_test) & 0x8000) {
opbtn |= IDAC_IO_OPBTN_TEST;
}
@ -159,3 +161,38 @@ void idac_io_led_set_leds(const uint8_t *rgb)
return;
}
HRESULT idac_io_ffb_init(void)
{
assert(idac_io_backend != NULL);
return idac_io_backend->ffb_init();
}
void idac_io_ffb_toggle(bool active)
{
assert(idac_io_backend != NULL);
idac_io_backend->ffb_toggle(active);
}
void idac_io_ffb_constant_force(uint8_t direction, uint8_t force)
{
assert(idac_io_backend != NULL);
idac_io_backend->ffb_constant_force(direction, force);
}
void idac_io_ffb_rumble(uint8_t period, uint8_t force)
{
assert(idac_io_backend != NULL);
idac_io_backend->ffb_rumble(period, force);
}
void idac_io_ffb_damper(uint8_t force)
{
assert(idac_io_backend != NULL);
idac_io_backend->ffb_damper(force);
}

View File

@ -10,3 +10,8 @@ EXPORTS
idac_io_led_set_fet_output
idac_io_led_gs_update
idac_io_led_set_leds
idac_io_ffb_init
idac_io_ffb_toggle
idac_io_ffb_constant_force
idac_io_ffb_rumble
idac_io_ffb_damper

View File

@ -2,6 +2,7 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
enum {
@ -160,3 +161,51 @@ void idac_io_led_gs_update(const uint8_t *rgb);
Minimum API version: 0x0101 */
void idac_io_led_set_leds(const uint8_t *rgb);
/* Initialize FFB emulation. This function will be called before any
other idac_io_ffb_*() function calls.
This will always be called even if FFB board emulation is disabled to allow
the IO DLL to initialize any necessary resources.
Minimum API version: 0x0102 */
HRESULT idac_io_ffb_init(void);
/* Toggle FFB emulation. If active is true, FFB emulation should be enabled.
If active is false, FFB emulation should be disabled and all FFB effects
should be stopped and released.
Minimum API version: 0x0102 */
void idac_io_ffb_toggle(bool active);
/* Set a constant force FFB effect.
Direction is 0 for right and 1 for left.
Force is the magnitude of the force, where 0 is no force and 127 is the
maximum force in a given direction.
Minimum API version: 0x0102 */
void idac_io_ffb_constant_force(uint8_t direction, uint8_t force);
/* Set a (sine) periodic force FFB effect.
Period is the period of the effect in milliseconds (not sure).
Force is the magnitude of the force, where 0 is no force and 127 is the
maximum force.
Minimum API version: 0x0102 */
void idac_io_ffb_rumble(uint8_t period, uint8_t force);
/* Set a damper FFB effect.
Force is the magnitude of the force, where 0 is no force and 40 is the
maximum force. Theoretically the maximum force is 127, but the game only
uses a maximum of 40.
Minimum API version: 0x0102 */
void idac_io_ffb_damper(uint8_t force);

View File

@ -1,5 +1,3 @@
#include "idacio/xi.h"
#include <assert.h>
#include <math.h>
#include <stdbool.h>
@ -7,22 +5,35 @@
#include <windows.h>
#include <xinput.h>
#include "idacio/xi.h"
#include "idacio/backend.h"
#include "idacio/config.h"
#include "idacio/idacio.h"
#include "idacio/shifter.h"
#include "util/dprintf.h"
static void idac_xi_get_gamebtns(uint8_t *gamebtn_out);
static void idac_xi_get_shifter(uint8_t *gear);
static void idac_xi_get_analogs(struct idac_io_analog_state *out);
static HRESULT idac_xi_ffb_init(void);
static void idac_xi_ffb_toggle(bool active);
static void idac_xi_ffb_constant_force(uint8_t direction, uint8_t force);
static void idac_xi_ffb_rumble(uint8_t force, uint8_t period);
static void idac_xi_ffb_damper(uint8_t force);
static HRESULT idac_xi_config_apply(const struct idac_xi_config *cfg);
static const struct idac_io_backend idac_xi_backend = {
.get_gamebtns = idac_xi_get_gamebtns,
.get_shifter = idac_xi_get_shifter,
.get_analogs = idac_xi_get_analogs,
.get_gamebtns = idac_xi_get_gamebtns,
.get_shifter = idac_xi_get_shifter,
.get_analogs = idac_xi_get_analogs,
.ffb_init = idac_xi_ffb_init,
.ffb_toggle = idac_xi_ffb_toggle,
.ffb_constant_force = idac_xi_ffb_constant_force,
.ffb_rumble = idac_xi_ffb_rumble,
.ffb_damper = idac_xi_ffb_damper
};
static bool idac_xi_single_stick_steering;
@ -46,7 +57,7 @@ HRESULT idac_xi_init(const struct idac_xi_config *cfg, const struct idac_io_back
return hr;
}
dprintf("XInput: Using XInput controller\n");
dprintf("IDACIO: Using XInput controller\n");
*backend = &idac_xi_backend;
return S_OK;
@ -205,3 +216,35 @@ static void idac_xi_get_analogs(struct idac_io_analog_state *out) {
out->accel = xi.Gamepad.bRightTrigger << 8;
out->brake = xi.Gamepad.bLeftTrigger << 8;
}
static HRESULT idac_xi_ffb_init(void) {
return S_OK;
}
static void idac_xi_ffb_toggle(bool active) {
XINPUT_VIBRATION vibration;
memset(&vibration, 0, sizeof(vibration));
XInputSetState(0, &vibration);
}
static void idac_xi_ffb_constant_force(uint8_t direction, uint8_t force) {
return;
}
static void idac_xi_ffb_rumble(uint8_t force, uint8_t period) {
XINPUT_VIBRATION vibration;
/* XInput max strength is 65.535, so multiply the 127.0 by 516. */
uint16_t strength = force * 516;
memset(&vibration, 0, sizeof(vibration));
vibration.wLeftMotorSpeed = strength;
vibration.wRightMotorSpeed = strength;
XInputSetState(0, &vibration);
}
static void idac_xi_ffb_damper(uint8_t force) {
return;
}

View File

@ -18,6 +18,42 @@
#include "platform/config.h"
#include "platform/platform.h"
void led15070_config_load(struct led15070_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
wchar_t tmpstr[16];
cfg->enable = GetPrivateProfileIntW(L"led15070", L"enable", 1, filename);
cfg->port_no = GetPrivateProfileIntW(L"led15070", L"portNo", 0, filename);
cfg->fw_ver = GetPrivateProfileIntW(L"led15070", L"fwVer", 0x90, filename);
/* TODO: Unknown, no firmware file available */
cfg->fw_sum = GetPrivateProfileIntW(L"led15070", L"fwSum", 0x0000, filename);
GetPrivateProfileStringW(
L"led15070",
L"boardNumber",
L"15070-02",
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"led15070",
L"eepromPath",
L"DEVICE",
cfg->eeprom_path,
_countof(cfg->eeprom_path),
filename);
}
void idz_dll_config_load(
struct idz_dll_config *cfg,
const wchar_t *filename)
@ -47,6 +83,8 @@ void idz_hook_config_load(
dvd_config_load(&cfg->dvd, filename);
gfx_config_load(&cfg->gfx, filename);
idz_dll_config_load(&cfg->dll, filename);
ffb_config_load(&cfg->ffb, filename);
led15070_config_load(&cfg->led15070, filename);
zinput_config_load(&cfg->zinput, filename);
}

View File

@ -5,7 +5,8 @@
#include "amex/amex.h"
#include "board/sg-reader.h"
#include "board/config.h"
#include "board/led15070.h"
#include "gfxhook/gfx.h"
@ -23,6 +24,8 @@ struct idz_hook_config {
struct dvd_config dvd;
struct gfx_config gfx;
struct idz_dll_config dll;
struct ffb_config ffb;
struct led15070_config led15070;
struct zinput_config zinput;
};

View File

@ -33,6 +33,7 @@
#include "idzhook/config.h"
#include "idzhook/idz-dll.h"
#include "idzhook/jvs.h"
#include "idzhook/ffb.h"
#include "idzhook/zinput.h"
#include "platform/platform.h"
@ -102,6 +103,12 @@ static DWORD CALLBACK idz_pre_startup(void)
goto fail;
}
hr = idz_jvs_hook_init();
if (FAILED(hr)) {
goto fail;
}
hr = amex_hook_init(&idz_hook_cfg.amex, idz_jvs_init);
if (FAILED(hr)) {
@ -114,6 +121,19 @@ static DWORD CALLBACK idz_pre_startup(void)
goto fail;
}
hr = idz_ffb_hook_init(&idz_hook_cfg.ffb, 1);
if (FAILED(hr)) {
goto fail;
}
hr = led15070_hook_init(&idz_hook_cfg.led15070, idz_dll.led_init,
idz_dll.led_set_fet_output, NULL, idz_dll.led_gs_update, 11, 1);
if (FAILED(hr)) {
goto fail;
}
/* Initialize debug helpers */
spike_hook_init(L".\\segatools.ini");

59
idzhook/ffb.c Normal file
View File

@ -0,0 +1,59 @@
#include <windows.h>
#include <xinput.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "board/ffb.h"
#include "idzhook/idz-dll.h"
#include "util/dprintf.h"
static void idz_ffb_toggle(bool active);
static void idz_ffb_constant_force(uint8_t direction, uint8_t force);
static void idz_ffb_rumble(uint8_t force, uint8_t period);
static void idz_ffb_damper(uint8_t force);
static const struct ffb_ops idz_ffb_ops = {
.toggle = idz_ffb_toggle,
.constant_force = idz_ffb_constant_force,
.rumble = idz_ffb_rumble,
.damper = idz_ffb_damper
};
HRESULT idz_ffb_hook_init(const struct ffb_config *cfg, unsigned int port_no)
{
HRESULT hr;
assert(idz_dll.jvs_init != NULL);
hr = ffb_hook_init(cfg, &idz_ffb_ops, port_no);
if (FAILED(hr)) {
return hr;
}
return idz_dll.ffb_init();
}
static void idz_ffb_toggle(bool active)
{
idz_dll.ffb_toggle(active);
}
static void idz_ffb_constant_force(uint8_t direction, uint8_t force)
{
idz_dll.ffb_constant_force(direction, force);
}
static void idz_ffb_rumble(uint8_t force, uint8_t period)
{
idz_dll.ffb_rumble(force, period);
}
static void idz_ffb_damper(uint8_t force)
{
idz_dll.ffb_damper(force);
}

7
idzhook/ffb.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <windows.h>
#include "board/ffb.h"
HRESULT idz_ffb_hook_init(const struct ffb_config *cfg, unsigned int port_no);

View File

@ -24,6 +24,33 @@ const struct dll_bind_sym idz_dll_syms[] = {
}, {
.sym = "idz_io_jvs_read_coin_counter",
.off = offsetof(struct idz_dll, jvs_read_coin_counter),
}, {
.sym = "idz_io_led_init",
.off = offsetof(struct idz_dll, led_init),
}, {
.sym = "idz_io_led_set_fet_output",
.off = offsetof(struct idz_dll, led_set_fet_output),
}, {
.sym = "idz_io_led_gs_update",
.off = offsetof(struct idz_dll, led_gs_update),
}, {
.sym = "idz_io_led_set_leds",
.off = offsetof(struct idz_dll, led_set_leds),
}, {
.sym = "idz_io_ffb_init",
.off = offsetof(struct idz_dll, ffb_init),
}, {
.sym = "idz_io_ffb_toggle",
.off = offsetof(struct idz_dll, ffb_toggle),
}, {
.sym = "idz_io_ffb_constant_force",
.off = offsetof(struct idz_dll, ffb_constant_force),
}, {
.sym = "idz_io_ffb_rumble",
.off = offsetof(struct idz_dll, ffb_rumble),
}, {
.sym = "idz_io_ffb_damper",
.off = offsetof(struct idz_dll, ffb_damper),
}
};

View File

@ -11,6 +11,15 @@ struct idz_dll {
void (*jvs_read_buttons)(uint8_t *opbtn, uint8_t *gamebtn);
void (*jvs_read_shifter)(uint8_t *gear);
void (*jvs_read_coin_counter)(uint16_t *total);
HRESULT (*led_init)(void);
void (*led_set_fet_output)(const uint8_t *rgb);
void (*led_gs_update)(const uint8_t *rgb);
void (*led_set_leds)(const uint8_t *rgb);
HRESULT (*ffb_init)(void);
void (*ffb_toggle)(bool active);
void (*ffb_constant_force)(uint8_t direction, uint8_t force);
void (*ffb_rumble)(uint8_t period, uint8_t force);
void (*ffb_damper)(uint8_t force);
};
struct idz_dll_config {

View File

@ -22,3 +22,12 @@ EXPORTS
idz_io_jvs_read_buttons
idz_io_jvs_read_coin_counter
idz_io_jvs_read_shifter
idz_io_led_init
idz_io_led_set_fet_output
idz_io_led_gs_update
idz_io_led_set_leds
idz_io_ffb_init
idz_io_ffb_toggle
idz_io_ffb_constant_force
idz_io_ffb_rumble
idz_io_ffb_damper

View File

@ -24,11 +24,13 @@ static void idz_jvs_read_coin_counter(
void *ctx,
uint8_t slot_no,
uint16_t *out);
static void idz_jvs_write_gpio(void *ctx, uint32_t state);
static const struct io3_ops idz_jvs_io3_ops = {
.read_switches = idz_jvs_read_switches,
.read_analogs = idz_jvs_read_analogs,
.read_coin_counter = idz_jvs_read_coin_counter,
.write_gpio = idz_jvs_write_gpio
};
static const uint16_t idz_jvs_gear_signals[] = {
@ -50,21 +52,20 @@ static const uint16_t idz_jvs_gear_signals[] = {
static struct io3 idz_jvs_io3;
HRESULT idz_jvs_hook_init(void)
{
HRESULT hr;
assert(idz_dll.jvs_init != NULL);
return idz_dll.jvs_init();
}
HRESULT idz_jvs_init(struct jvs_node **out)
{
HRESULT hr;
assert(out != NULL);
assert(idz_dll.jvs_init != NULL);
dprintf("JVS I/O: Starting Initial D Zero backend DLL\n");
hr = idz_dll.jvs_init();
if (FAILED(hr)) {
dprintf("JVS I/O: Backend error, I/O disconnected; %x\n", (int) hr);
return hr;
}
io3_init(&idz_jvs_io3, NULL, &idz_jvs_io3_ops, NULL);
*out = io3_to_jvs_node(&idz_jvs_io3);
@ -175,3 +176,21 @@ static void idz_jvs_read_coin_counter(
idz_dll.jvs_read_coin_counter(out);
}
static void idz_jvs_write_gpio(void *ctx, uint32_t state)
{
assert(idz_dll.led_set_leds != NULL);
// 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 ON/OFF values.
uint8_t rgb_out[6] = {
state & IDZ_IO_LED_START ? 0xFF : 0x00,
state & IDZ_IO_LED_VIEW_CHANGE ? 0xFF : 0x00,
state & IDZ_IO_LED_UP ? 0xFF : 0x00,
state & IDZ_IO_LED_DOWN ? 0xFF : 0x00,
state & IDZ_IO_LED_RIGHT ? 0xFF : 0x00,
state & IDZ_IO_LED_LEFT ? 0xFF : 0x00,
};
idz_dll.led_set_leds(rgb_out);
}

View File

@ -4,4 +4,6 @@
#include "jvs/jvs-bus.h"
HRESULT idz_jvs_hook_init(void);
HRESULT idz_jvs_init(struct jvs_node **root);

View File

@ -32,5 +32,7 @@ shared_library(
'jvs.h',
'zinput.c',
'zinput.h',
'ffb.c',
'ffb.h',
],
)

View File

@ -8,4 +8,9 @@ struct idz_io_backend {
void (*jvs_read_buttons)(uint8_t *gamebtn);
void (*jvs_read_shifter)(uint8_t *gear);
void (*jvs_read_analogs)(struct idz_io_analog_state *state);
HRESULT (*ffb_init)(void);
void (*ffb_toggle)(bool active);
void (*ffb_constant_force)(uint8_t direction, uint8_t force);
void (*ffb_rumble)(uint8_t period, uint8_t force);
void (*ffb_damper)(uint8_t force);
};

View File

@ -78,12 +78,29 @@ void idz_di_config_load(struct idz_di_config *cfg, const wchar_t *filename)
}
// FFB configuration
cfg->ffb_constant_force_strength = GetPrivateProfileIntW(
L"dinput",
L"constantForceStrength",
100,
filename);
cfg->center_spring_strength = GetPrivateProfileIntW(
L"dinput",
L"centerSpringStrength",
30,
filename);
cfg->ffb_rumble_strength = GetPrivateProfileIntW(
L"dinput",
L"rumbleStrength",
100,
filename);
cfg->ffb_damper_strength = GetPrivateProfileIntW(
L"dinput",
L"damperStrength",
100,
filename);
cfg->ffb_rumble_duration = GetPrivateProfileIntW(
L"dinput",
L"rumbleDuration",
1000,
filename);
}
void idz_xi_config_load(struct idz_xi_config *cfg, const wchar_t *filename)

View File

@ -23,7 +23,11 @@ struct idz_di_config {
bool reverse_accel_axis;
// FFB configuration
uint16_t center_spring_strength;
uint8_t ffb_constant_force_strength;
uint8_t ffb_rumble_strength;
uint8_t ffb_damper_strength;
uint32_t ffb_rumble_duration;
};
struct idz_xi_config {

View File

@ -1,134 +1,39 @@
#include <windows.h>
#include <dinput.h>
#include <stdbool.h>
#include <assert.h>
#include "idzio/di-dev.h"
#include "util/dprintf.h"
HRESULT idz_di_dev_start(IDirectInputDevice8W *dev, HWND wnd)
const struct idz_di_config *idz_di_cfg;
static HWND idz_di_wnd;
static IDirectInputDevice8W *idz_di_dev;
/* Individual DI Effects */
static IDirectInputEffect *idz_di_fx;
static IDirectInputEffect *idz_di_fx_rumble;
static IDirectInputEffect *idz_di_fx_damper;
/* Max FFB Board value is 127 */
static const double idz_di_ffb_scale = 127.0;
HRESULT idz_di_dev_init(
const struct idz_di_config *cfg,
IDirectInputDevice8W *dev,
HWND wnd)
{
HRESULT hr;
assert(dev != NULL);
assert(wnd != NULL);
hr = IDirectInputDevice8_SetCooperativeLevel(
dev,
wnd,
DISCL_BACKGROUND | DISCL_EXCLUSIVE);
idz_di_cfg = cfg;
idz_di_dev = dev;
idz_di_wnd = wnd;
if (FAILED(hr)) {
dprintf("DirectInput: SetCooperativeLevel failed: %08x\n", (int) hr);
return hr;
}
hr = IDirectInputDevice8_SetDataFormat(dev, &c_dfDIJoystick);
if (FAILED(hr)) {
dprintf("DirectInput: SetDataFormat failed: %08x\n", (int) hr);
return hr;
}
hr = IDirectInputDevice8_Acquire(dev);
if (FAILED(hr)) {
dprintf("DirectInput: Acquire failed: %08x\n", (int) hr);
return hr;
}
return hr;
}
void idz_di_dev_start_fx(
IDirectInputDevice8W *dev, IDirectInputEffect **out, uint16_t strength)
{
/* Set up force-feedback on devices that support it. This is just a stub
for the time being, since we don't yet know how the serial port force
feedback protocol works.
I'm currently developing with an Xbox One Thrustmaster TMX wheel, if
we don't perform at least some perfunctory FFB initialization of this
nature (or indeed if no DirectInput application is running) then the
wheel exhibits considerable resistance, similar to that of a stationary
car. Changing cf.lMagnitude to a nonzero value does cause the wheel to
continuously turn in the given direction with the given force as one
would expect (max magnitude per DirectInput docs is +/- 10000).
Failure here is non-fatal, we log any errors and move on.
https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416353(v=vs.85)
*/
IDirectInputEffect *obj;
DWORD axis;
LONG direction;
DIEFFECT fx;
DICONDITION cond;
HRESULT hr;
assert(dev != NULL);
assert(out != NULL);
*out = NULL;
dprintf("DirectInput: Starting force feedback (may take a sec)\n");
// Auto-centering effect
axis = DIJOFS_X;
direction = 0;
memset(&cond, 0, sizeof(cond));
cond.lOffset = 0;
cond.lPositiveCoefficient = strength;
cond.lNegativeCoefficient = strength;
cond.dwPositiveSaturation = strength; // For FG920?
cond.dwNegativeSaturation = strength; // For FG920?
cond.lDeadBand = 0;
memset(&fx, 0, sizeof(fx));
fx.dwSize = sizeof(fx);
fx.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
fx.dwDuration = INFINITE;
fx.dwGain = DI_FFNOMINALMAX;
fx.dwTriggerButton = DIEB_NOTRIGGER;
fx.dwTriggerRepeatInterval = INFINITE;
fx.cAxes = 1;
fx.rgdwAxes = &axis;
fx.rglDirection = &direction;
fx.cbTypeSpecificParams = sizeof(cond);
fx.lpvTypeSpecificParams = &cond;
hr = IDirectInputDevice8_CreateEffect(
dev,
&GUID_Spring,
&fx,
&obj,
NULL);
if (FAILED(hr)) {
dprintf("DirectInput: Centering spring force feedback unavailable: %08x\n",
(int) hr);
return;
}
hr = IDirectInputEffect_Start(obj, INFINITE, 0);
if (FAILED(hr)) {
IDirectInputEffect_Release(obj);
dprintf("DirectInput: Centering spring force feedback start failed: %08x\n",
(int) hr);
return;
}
*out = obj;
dprintf("DirectInput: Centering spring effects initialized with strength %d%%\n",
strength / 100);
return S_OK;
}
HRESULT idz_di_dev_poll(
@ -167,3 +72,312 @@ HRESULT idz_di_dev_poll(
return hr;
}
HRESULT idz_di_dev_start(IDirectInputDevice8W *dev, HWND wnd) {
HRESULT hr;
assert(dev != NULL);
assert(wnd != NULL);
hr = IDirectInputDevice8_SetCooperativeLevel(
dev,
wnd,
DISCL_BACKGROUND | DISCL_EXCLUSIVE);
if (FAILED(hr)) {
dprintf("DirectInput: SetCooperativeLevel failed: %08x\n", (int) hr);
return hr;
}
hr = IDirectInputDevice8_SetDataFormat(dev, &c_dfDIJoystick);
if (FAILED(hr)) {
dprintf("DirectInput: SetDataFormat failed: %08x\n", (int) hr);
return hr;
}
hr = IDirectInputDevice8_Acquire(dev);
if (FAILED(hr)) {
dprintf("DirectInput: Acquire failed: %08x\n", (int) hr);
return hr;
}
return hr;
}
HRESULT idz_di_ffb_init(void)
{
HRESULT hr;
hr = idz_di_dev_start(idz_di_dev, idz_di_wnd);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
void idz_di_ffb_toggle(bool active)
{
if (active) {
return;
}
/* Stop and release all effects */
/* I never programmed DirectInput Effects, so this might be bad practice. */
if (idz_di_fx != NULL) {
IDirectInputEffect_Stop(idz_di_fx);
IDirectInputEffect_Release(idz_di_fx);
idz_di_fx = NULL;
}
if (idz_di_fx_rumble != NULL) {
IDirectInputEffect_Stop(idz_di_fx_rumble);
IDirectInputEffect_Release(idz_di_fx_rumble);
idz_di_fx_rumble = NULL;
}
if (idz_di_fx_damper != NULL) {
IDirectInputEffect_Stop(idz_di_fx_damper);
IDirectInputEffect_Release(idz_di_fx_damper);
idz_di_fx_damper = NULL;
}
}
void idz_di_ffb_constant_force(uint8_t direction_ffb, uint8_t force)
{
/* DI expects a magnitude in the range of -10.000 to 10.000 */
uint16_t ffb_strength = idz_di_cfg->ffb_constant_force_strength * 100;
if (ffb_strength == 0) {
return;
}
DWORD axis;
LONG direction;
DIEFFECT fx;
DICONSTANTFORCE cf;
HRESULT hr;
/* Direction 0: move to the right, 1: move to the left */
LONG magnitude = (LONG)(((double)force / idz_di_ffb_scale) * ffb_strength);
cf.lMagnitude = (direction_ffb == 0) ? -magnitude : magnitude;
axis = DIJOFS_X;
/* Irrelevant as magnitude descripbes the direction */
direction = 0;
memset(&fx, 0, sizeof(fx));
fx.dwSize = sizeof(fx);
fx.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
fx.dwDuration = INFINITE;
fx.dwGain = DI_FFNOMINALMAX;
fx.dwTriggerButton = DIEB_NOTRIGGER;
fx.dwTriggerRepeatInterval = INFINITE;
fx.cAxes = 1;
fx.rgdwAxes = &axis;
fx.rglDirection = &direction;
fx.cbTypeSpecificParams = sizeof(cf);
fx.lpvTypeSpecificParams = &cf;
if (idz_di_fx != NULL) {
// Try to update the existing effect
hr = IDirectInputEffect_SetParameters(idz_di_fx, &fx, DIEP_TYPESPECIFICPARAMS);
if (SUCCEEDED(hr)) {
return;
} else {
dprintf("DirectInput: Failed to update constant force feedback, recreating effect: %08x\n", (int)hr);
// Stop and release the current effect if updating fails
IDirectInputEffect_Stop(idz_di_fx);
IDirectInputEffect_Release(idz_di_fx);
idz_di_fx = NULL;
}
}
// Create a new constant force effect
IDirectInputEffect *obj;
hr = IDirectInputDevice8_CreateEffect(
idz_di_dev,
&GUID_ConstantForce,
&fx,
&obj,
NULL);
if (FAILED(hr)) {
dprintf("DirectInput: Constant force feedback creation failed: %08x\n", (int) hr);
return;
}
hr = IDirectInputEffect_Start(obj, INFINITE, 0);
if (FAILED(hr)) {
dprintf("DirectInput: Constant force feedback start failed: %08x\n", (int) hr);
IDirectInputEffect_Release(obj);
return;
}
idz_di_fx = obj;
}
void idz_di_ffb_rumble(uint8_t force, uint8_t period)
{
/* DI expects a magnitude in the range of -10.000 to 10.000 */
uint16_t ffb_strength = idz_di_cfg->ffb_rumble_strength * 100;
if (ffb_strength == 0) {
return;
}
uint32_t ffb_duration = idz_di_cfg->ffb_rumble_duration;
DWORD axis;
LONG direction;
DIEFFECT fx;
DIPERIODIC pe;
HRESULT hr;
/* Duration in microseconds,
Might be totally wrong as especially on FANATEC wheels as this code will
crash the game. TODO: Figure out why this effect will crash on FANATEC! */
DWORD duration = (DWORD)((double)force * ffb_duration);
memset(&pe, 0, sizeof(pe));
pe.dwMagnitude = (DWORD)(((double)force / idz_di_ffb_scale) * ffb_strength);
pe.lOffset = 0;
pe.dwPhase = 0;
pe.dwPeriod = duration;
axis = DIJOFS_X;
direction = 0;
memset(&fx, 0, sizeof(fx));
fx.dwSize = sizeof(fx);
fx.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
fx.dwDuration = duration;
fx.dwGain = DI_FFNOMINALMAX;
fx.dwTriggerButton = DIEB_NOTRIGGER;
fx.dwTriggerRepeatInterval = INFINITE;
fx.cAxes = 1;
fx.rgdwAxes = &axis;
fx.rglDirection = &direction;
fx.cbTypeSpecificParams = sizeof(pe);
fx.lpvTypeSpecificParams = &pe;
if (idz_di_fx_rumble != NULL) {
// Try to update the existing effect
hr = IDirectInputEffect_SetParameters(idz_di_fx_rumble, &fx, DIEP_TYPESPECIFICPARAMS);
if (SUCCEEDED(hr)) {
return;
} else {
dprintf("DirectInput: Failed to update periodic force feedback, recreating effect: %08x\n", (int)hr);
// Stop and release the current effect if updating fails
IDirectInputEffect_Stop(idz_di_fx_rumble);
IDirectInputEffect_Release(idz_di_fx_rumble);
idz_di_fx_rumble = NULL;
}
}
IDirectInputEffect *obj;
hr = IDirectInputDevice8_CreateEffect(
idz_di_dev,
&GUID_Sine,
&fx,
&obj,
NULL);
if (FAILED(hr)) {
dprintf("DirectInput: Periodic force feedback creation failed: %08x\n", (int) hr);
return;
}
hr = IDirectInputEffect_Start(obj, INFINITE, 0);
if (FAILED(hr)) {
dprintf("DirectInput: Periodic force feedback start failed: %08x\n", (int) hr);
IDirectInputEffect_Release(obj);
return;
}
idz_di_fx_rumble = obj;
}
void idz_di_ffb_damper(uint8_t force)
{
/* DI expects a coefficient in the range of -10.000 to 10.000 */
uint16_t ffb_strength = idz_di_cfg->ffb_damper_strength * 100;
if (ffb_strength == 0) {
return;
}
DWORD axis;
LONG direction;
DIEFFECT fx;
DICONDITION cond;
HRESULT hr;
memset(&cond, 0, sizeof(cond));
cond.lOffset = 0;
cond.lPositiveCoefficient = (LONG)(((double)force / idz_di_ffb_scale) * ffb_strength);
cond.lNegativeCoefficient = (LONG)(((double)force / idz_di_ffb_scale) * ffb_strength);
/* Not sure on this one */
cond.dwPositiveSaturation = DI_FFNOMINALMAX;
cond.dwNegativeSaturation = DI_FFNOMINALMAX;
cond.lDeadBand = 0;
axis = DIJOFS_X;
direction = 0;
memset(&fx, 0, sizeof(fx));
fx.dwSize = sizeof(fx);
fx.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
fx.dwDuration = INFINITE;
fx.dwGain = DI_FFNOMINALMAX;
fx.dwTriggerButton = DIEB_NOTRIGGER;
fx.dwTriggerRepeatInterval = INFINITE;
fx.cAxes = 1;
fx.rgdwAxes = &axis;
fx.rglDirection = &direction;
fx.cbTypeSpecificParams = sizeof(cond);
fx.lpvTypeSpecificParams = &cond;
if (idz_di_fx_damper != NULL) {
// Try to update the existing effect
hr = IDirectInputEffect_SetParameters(idz_di_fx_damper, &fx, DIEP_TYPESPECIFICPARAMS);
if (SUCCEEDED(hr)) {
return;
} else {
dprintf("DirectInput: Failed to update damper force feedback, recreating effect: %08x\n", (int)hr);
// Stop and release the current effect if updating fails
IDirectInputEffect_Stop(idz_di_fx_damper);
IDirectInputEffect_Release(idz_di_fx_damper);
idz_di_fx_damper = NULL;
}
}
// Create a new damper force effect
IDirectInputEffect *obj;
hr = IDirectInputDevice8_CreateEffect(
idz_di_dev,
&GUID_Damper,
&fx,
&obj,
NULL);
if (FAILED(hr)) {
dprintf("DirectInput: Damper force feedback creation failed: %08x\n", (int) hr);
return;
}
hr = IDirectInputEffect_Start(obj, INFINITE, 0);
if (FAILED(hr)) {
dprintf("DirectInput: Damper force feedback start failed: %08x\n", (int) hr);
IDirectInputEffect_Release(obj);
return;
}
idz_di_fx_damper = obj;
}

View File

@ -5,15 +5,26 @@
#include <stdint.h>
#include "idzio/config.h"
union idz_di_state {
DIJOYSTATE st;
uint8_t bytes[sizeof(DIJOYSTATE)];
};
HRESULT idz_di_dev_init(
const struct idz_di_config *cfg,
IDirectInputDevice8W *dev,
HWND wnd);
HRESULT idz_di_dev_start(IDirectInputDevice8W *dev, HWND wnd);
void idz_di_dev_start_fx(IDirectInputDevice8W *dev, IDirectInputEffect **out, uint16_t strength);
HRESULT idz_di_dev_poll(
IDirectInputDevice8W *dev,
HWND wnd,
union idz_di_state *out);
HRESULT idz_di_ffb_init(void);
void idz_di_ffb_toggle(bool active);
void idz_di_ffb_constant_force(uint8_t direction, uint8_t force);
void idz_di_ffb_rumble(uint8_t force, uint8_t period);
void idz_di_ffb_damper(uint8_t force);

View File

@ -55,6 +55,11 @@ static const struct idz_io_backend idz_di_backend = {
.jvs_read_buttons = idz_di_jvs_read_buttons,
.jvs_read_shifter = idz_di_jvs_read_shifter,
.jvs_read_analogs = idz_di_jvs_read_analogs,
.ffb_init = idz_di_ffb_init,
.ffb_toggle = idz_di_ffb_toggle,
.ffb_constant_force = idz_di_ffb_constant_force,
.ffb_rumble = idz_di_ffb_rumble,
.ffb_damper = idz_di_ffb_damper
};
static HWND idz_di_wnd;
@ -73,7 +78,6 @@ static uint8_t idz_di_gear[6];
static bool idz_di_use_pedals;
static bool idz_di_reverse_brake_axis;
static bool idz_di_reverse_accel_axis;
static uint16_t idz_di_center_spring_strength;
HRESULT idz_di_init(
const struct idz_di_config *cfg,
@ -166,16 +170,12 @@ HRESULT idz_di_init(
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
hr = idz_di_dev_start(idz_di_dev, idz_di_wnd);
hr = idz_di_dev_init(cfg, idz_di_dev, idz_di_wnd);
if (FAILED(hr)) {
return hr;
}
// Convert the strength from 0-100 to 0-10000 for DirectInput
idz_di_dev_start_fx(idz_di_dev, &idz_di_fx,
idz_di_center_spring_strength * 100);
if (cfg->pedals_name[0] != L'\0') {
hr = IDirectInput8_EnumDevices(
idz_di_api,
@ -349,15 +349,24 @@ static HRESULT idz_di_config_apply(const struct idz_di_config *cfg)
idz_di_gear[i] = cfg->gear[i];
}
// FFB configuration
/* FFB configuration */
if (cfg->ffb_constant_force_strength < 0 || cfg->ffb_constant_force_strength > 100) {
dprintf("Wheel: Invalid constant force strength: %i\n", cfg->ffb_constant_force_strength);
if (cfg->center_spring_strength < 0 || cfg->center_spring_strength > 100) {
dprintf("Wheel: Invalid center spring strength: %i\n", cfg->center_spring_strength);
return E_INVALIDARG;
}
if (cfg->ffb_rumble_strength < 0 || cfg->ffb_rumble_strength > 100) {
dprintf("Wheel: Invalid rumble strength: %i\n", cfg->ffb_rumble_strength);
return E_INVALIDARG;
}
idz_di_center_spring_strength = cfg->center_spring_strength;
if (cfg->ffb_damper_strength < 0 || cfg->ffb_damper_strength > 100) {
dprintf("Wheel: Invalid damper strength: %i\n", cfg->ffb_damper_strength);
return E_INVALIDARG;
}
return S_OK;
}

View File

@ -20,7 +20,7 @@ static uint16_t idz_io_coins;
uint16_t idz_io_get_api_version(void)
{
return 0x0100;
return 0x0102;
}
HRESULT idz_io_jvs_init(void)
@ -123,3 +123,79 @@ void idz_io_jvs_read_coin_counter(uint16_t *out)
*out = idz_io_coins;
}
HRESULT idz_io_led_init(void)
{
return S_OK;
}
void idz_io_led_set_fet_output(const uint8_t *rgb)
{
#if 0
dprintf("IDZ LED: LEFT SEAT LED: %02X\n", rgb[0]);
dprintf("IDZ LED: RIGHT SEAT LED: %02X\n", rgb[1]);
#endif
return;
}
void idz_io_led_gs_update(const uint8_t *rgb)
{
#if 0
for (int i = 0; i < 9; i++) {
dprintf("IDZ LED: LED %d: %02X %02X %02X Speed: %02X\n",
i, rgb[i * 4], rgb[i * 4 + 1], rgb[i * 4 + 2], rgb[i * 4 + 3]);
}
#endif
return;
}
void idz_io_led_set_leds(const uint8_t *rgb)
{
#if 0
dprintf("IDZ LED: START: %02X\n", rgb[0]);
dprintf("IDZ LED: VIEW CHANGE: %02X\n", rgb[1]);
dprintf("IDZ LED: UP: %02X\n", rgb[2]);
dprintf("IDZ LED: DOWN: %02X\n", rgb[3]);
dprintf("IDZ LED: RIGHT: %02X\n", rgb[4]);
dprintf("IDZ LED: LEFT: %02X\n", rgb[5]);
#endif
return;
}
HRESULT idz_io_ffb_init(void)
{
assert(idz_io_backend != NULL);
return idz_io_backend->ffb_init();
}
void idz_io_ffb_toggle(bool active)
{
assert(idz_io_backend != NULL);
idz_io_backend->ffb_toggle(active);
}
void idz_io_ffb_constant_force(uint8_t direction, uint8_t force)
{
assert(idz_io_backend != NULL);
idz_io_backend->ffb_constant_force(direction, force);
}
void idz_io_ffb_rumble(uint8_t period, uint8_t force)
{
assert(idz_io_backend != NULL);
idz_io_backend->ffb_rumble(period, force);
}
void idz_io_ffb_damper(uint8_t force)
{
assert(idz_io_backend != NULL);
idz_io_backend->ffb_damper(force);
}

View File

@ -6,3 +6,12 @@ EXPORTS
idz_io_jvs_read_buttons
idz_io_jvs_read_coin_counter
idz_io_jvs_read_shifter
idz_io_led_init
idz_io_led_set_fet_output
idz_io_led_gs_update
idz_io_led_set_leds
idz_io_ffb_init
idz_io_ffb_toggle
idz_io_ffb_constant_force
idz_io_ffb_rumble
idz_io_ffb_damper

View File

@ -14,6 +14,7 @@
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
enum {
@ -30,6 +31,17 @@ enum {
IDZ_IO_GAMEBTN_VIEW_CHANGE = 0x20,
};
enum {
/* These are the bitmasks to use when checking which
lights are triggered on incoming IO4 GPIO writes. */
IDZ_IO_LED_START = 1 << 7,
IDZ_IO_LED_VIEW_CHANGE = 1 << 6,
IDZ_IO_LED_UP = 1 << 1,
IDZ_IO_LED_DOWN = 1 << 0,
IDZ_IO_LED_RIGHT = 1 << 14,
IDZ_IO_LED_LEFT = 1 << 15
};
struct idz_io_analog_state {
/* Current steering wheel position, where zero is the centered position.
@ -104,3 +116,107 @@ void idz_io_jvs_read_shifter(uint8_t *gear);
Minimum API version: 0x0100 */
void idz_io_jvs_read_coin_counter(uint16_t *total);
/* Initialize LED emulation. This function will be called before any
other idz_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: 0x0101 */
HRESULT idz_io_led_init(void);
/* Update the FET outputs. rgb is a pointer to an array up to 3 bytes.
The following bits are used to control the FET outputs:
[0]: LEFT SEAT LED
[1]: RIGHT SEAT LED
The LED is truned on when the byte is 255 and turned off when the byte is 0.
Minimum API version: 0x0101 */
void idz_io_led_set_fet_output(const uint8_t *rgb);
/* Update the RGB LEDs. rgb is a pointer to an array up to 32 * 4 = 128 bytes.
The LEDs are laid out as follows:
[0]: LEFT UP LED
[1-2]: LEFT CENTER LED
[3]: LEFT DOWN LED
[5]: RIGHT UP LED
[6-7]: RIGHT CENTER LED
[8]: RIGHT DOWN LED
Each rgb value is comprised for 4 bytes in the order of R, G, B, Speed.
Speed is a value from 0 to 255, where 0 is the fastest speed and 255 is the slowest.
Minimum API version: 0x0101 */
void idz_io_led_gs_update(const uint8_t *rgb);
/* Update the cabinet button LEDs. rgb is a pointer to an array up to 6 bytes.
The LEDs are laid out as follows:
[0]: START LED
[1]: VIEW CHANGE LED
[2]: UP LED
[3]: DOWN LED
[4]: RIGHT LED
[5]: LEFT LED
The LED is turned on when the byte is 255 and turned off when the byte is 0.
Minimum API version: 0x0101 */
void idz_io_led_set_leds(const uint8_t *rgb);
/* Initialize FFB emulation. This function will be called before any
other idz_io_ffb_*() function calls.
This will always be called even if FFB board emulation is disabled to allow
the IO DLL to initialize any necessary resources.
Minimum API version: 0x0102 */
HRESULT idz_io_ffb_init(void);
/* Toggle FFB emulation. If active is true, FFB emulation should be enabled.
If active is false, FFB emulation should be disabled and all FFB effects
should be stopped and released.
Minimum API version: 0x0102 */
void idz_io_ffb_toggle(bool active);
/* Set a constant force FFB effect.
Direction is 0 for right and 1 for left.
Force is the magnitude of the force, where 0 is no force and 127 is the
maximum force in a given direction.
Minimum API version: 0x0102 */
void idz_io_ffb_constant_force(uint8_t direction, uint8_t force);
/* Set a (sine) periodic force FFB effect.
Period is the period of the effect in milliseconds (not sure).
Force is the magnitude of the force, where 0 is no force and 127 is the
maximum force.
Minimum API version: 0x0102 */
void idz_io_ffb_rumble(uint8_t period, uint8_t force);
/* Set a damper FFB effect.
Force is the magnitude of the force, where 0 is no force and 40 is the
maximum force. Theoretically the maximum force is 127, but the game only
uses a maximum of 40.
Minimum API version: 0x0102 */
void idz_io_ffb_damper(uint8_t force);

View File

@ -18,12 +18,23 @@ static void idz_xi_jvs_read_buttons(uint8_t *gamebtn_out);
static void idz_xi_jvs_read_shifter(uint8_t *gear);
static void idz_xi_jvs_read_analogs(struct idz_io_analog_state *out);
static HRESULT idz_xi_ffb_init(void);
static void idz_xi_ffb_toggle(bool active);
static void idz_xi_ffb_constant_force(uint8_t direction, uint8_t force);
static void idz_xi_ffb_rumble(uint8_t force, uint8_t period);
static void idz_xi_ffb_damper(uint8_t force);
static HRESULT idz_xi_config_apply(const struct idz_xi_config *cfg);
static const struct idz_io_backend idz_xi_backend = {
.jvs_read_buttons = idz_xi_jvs_read_buttons,
.jvs_read_shifter = idz_xi_jvs_read_shifter,
.jvs_read_analogs = idz_xi_jvs_read_analogs,
.ffb_init = idz_xi_ffb_init,
.ffb_toggle = idz_xi_ffb_toggle,
.ffb_constant_force = idz_xi_ffb_constant_force,
.ffb_rumble = idz_xi_ffb_rumble,
.ffb_damper = idz_xi_ffb_damper
};
static bool idz_xi_single_stick_steering;
@ -210,3 +221,35 @@ static void idz_xi_jvs_read_analogs(struct idz_io_analog_state *out)
out->accel = xi.Gamepad.bRightTrigger << 8;
out->brake = xi.Gamepad.bLeftTrigger << 8;
}
static HRESULT idz_xi_ffb_init(void) {
return S_OK;
}
static void idz_xi_ffb_toggle(bool active) {
XINPUT_VIBRATION vibration;
memset(&vibration, 0, sizeof(vibration));
XInputSetState(0, &vibration);
}
static void idz_xi_ffb_constant_force(uint8_t direction, uint8_t force) {
return;
}
static void idz_xi_ffb_rumble(uint8_t force, uint8_t period) {
XINPUT_VIBRATION vibration;
/* XInput max strength is 65.535, so multiply the 127.0 by 516. */
uint16_t strength = force * 516;
memset(&vibration, 0, sizeof(vibration));
vibration.wLeftMotorSpeed = strength;
vibration.wRightMotorSpeed = strength;
XInputSetState(0, &vibration);
}
static void idz_xi_ffb_damper(uint8_t force) {
return;
}

130
kemonohook/config.c Normal file
View File

@ -0,0 +1,130 @@
#include <assert.h>
#include <stddef.h>
#include "amex/config.h"
#include "board/config.h"
#include "hooklib/config.h"
#include "hooklib/dvd.h"
#include "kemonohook/config.h"
#include "platform/config.h"
// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
void kemono_dll_config_load(
struct kemono_dll_config *cfg,
const wchar_t *filename) {
assert(cfg != NULL);
assert(filename != NULL);
#if defined(ENV32BIT)
// Always empty, due to amdaemon being 64 bit in 32 bit mode
memset(cfg->path, 0, sizeof(cfg->path));
#elif defined(ENV64BIT)
GetPrivateProfileStringW(
L"kemonoio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
#else
#error "Unknown environment"
#endif
}
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", 0xA0, filename);
cfg->fw_sum = GetPrivateProfileIntW(L"led15093", L"fwSum", 0xAA53, 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"6704 ",
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 kemono_hook_config_load(
struct kemono_hook_config *cfg,
const wchar_t *filename) {
assert(cfg != NULL);
assert(filename != NULL);
platform_config_load(&cfg->platform, filename);
aime_config_load(&cfg->aime, filename);
dvd_config_load(&cfg->dvd, filename);
vfd_config_load(&cfg->vfd, filename);
kemono_dll_config_load(&cfg->dll, filename);
unity_config_load(&cfg->unity, filename);
printer_config_load(&cfg->printer, filename);
amex_config_load(&cfg->amex, filename);
led15093_config_load(&cfg->led15093, filename);
}

36
kemonohook/config.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <stddef.h>
#include "amex/amex.h"
#include "board/config.h"
#include "board/led15093.h"
#include "hooklib/dvd.h"
#include "kemonohook/kemono-dll.h"
#include "platform/config.h"
#include "unityhook/config.h"
struct kemono_hook_config {
struct platform_config platform;
struct aime_config aime;
struct dvd_config dvd;
struct vfd_config vfd;
struct kemono_dll_config dll;
struct unity_config unity;
struct printer_config printer;
struct amex_config amex;
struct led15093_config led15093;
};
void kemono_dll_config_load(
struct kemono_dll_config *cfg,
const wchar_t *filename);
void kemono_hook_config_load(
struct kemono_hook_config *cfg,
const wchar_t *filename);

138
kemonohook/dllmain.c Normal file
View File

@ -0,0 +1,138 @@
#include <windows.h>
#include "board/io4.h"
#include "board/sg-reader.h"
#include "board/vfd.h"
#include "hook/process.h"
#include "hook/table.h"
#include "hook/iohook.h"
#include "hooklib/printer.h"
#include "hooklib/serial.h"
#include "hooklib/spike.h"
#include "kemonohook/config.h"
#include "kemonohook/hooks.h"
#include "kemonohook/jvs.h"
#include "kemonohook/kemono-dll.h"
#include "platform/platform.h"
#include "unityhook/hook.h"
#include "util/dprintf.h"
static HMODULE kemono_hook_mod;
static process_entry_t kemono_startup;
static struct kemono_hook_config kemono_hook_cfg;
static DWORD CALLBACK kemono_pre_startup(void) {
HRESULT hr;
dprintf("--- Begin kemono_pre_startup ---\n");
/* Load config */
kemono_hook_cfg.aime.dll.path64 = true;
kemono_hook_config_load(&kemono_hook_cfg, L".\\segatools.ini");
/* Hook Win32 APIs */
dvd_hook_init(&kemono_hook_cfg.dvd, kemono_hook_mod);
serial_hook_init();
// 2.02 does not call printer update functions
uint16_t ret;
fwdlusb_updateFirmware_main(1, "UnityApp\\Parade_Data\\StreamingAssets\\Printer\\E0223100-014E-C300-MAINAPP.BIN", &ret);
if (ret != 0){
goto fail;
}
fwdlusb_updateFirmware_dsp(2, "UnityApp\\Parade_Data\\StreamingAssets\\Printer\\E0223200-0101-C300-DSPAPP.BIN", &ret);
if (ret != 0){
goto fail;
}
fwdlusb_updateFirmware_param(3, "UnityApp\\Parade_Data\\StreamingAssets\\Printer\\D0460700-0101-C300-PARAM.BIN", &ret);
if (ret != 0){
goto fail;
}
printer_hook_init(&kemono_hook_cfg.printer, 0, kemono_hook_mod);
printer_set_dimensions(720, 1028); // printer doesn't call setimageformat
/* Initialize emulation hooks */
hr = platform_hook_init(
&kemono_hook_cfg.platform,
"SDFL",
"AAW1",
kemono_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = sg_reader_hook_init(&kemono_hook_cfg.aime, 1, 1, kemono_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = kemono_dll_init(&kemono_hook_cfg.dll, kemono_hook_mod);
if (FAILED(hr)) {
goto fail;
}
hr = amex_hook_init(&kemono_hook_cfg.amex, kemono_jvs_init);
if (FAILED(hr)) {
goto fail;
}
hr = led15093_hook_init(&kemono_hook_cfg.led15093, kemono_dll.led_init, kemono_dll.led_set_leds, 10, 1, 1, 2);
if (FAILED(hr)) {
goto fail;
}
kemono_extra_hooks_init();
/* Initialize Unity native plugin DLL hooks
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
hooked earlier in the `kemonohook` initialization. */
unity_hook_init(&kemono_hook_cfg.unity, kemono_hook_mod, kemono_extra_hooks_load);
/* Initialize debug helpers */
spike_hook_init(L".\\segatools.ini");
dprintf("--- End kemono_pre_startup ---\n");
/* Jump to EXE start address */
return kemono_startup();
fail:
ExitProcess(EXIT_FAILURE);
}
BOOL WINAPI DllMain(HMODULE mod, DWORD cause, void *ctx) {
HRESULT hr;
if (cause != DLL_PROCESS_ATTACH) {
return TRUE;
}
kemono_hook_mod = mod;
hr = process_hijack_startup(kemono_pre_startup, &kemono_startup);
if (!SUCCEEDED(hr)) {
dprintf("Failed to hijack process startup: %x\n", (int) hr);
}
return SUCCEEDED(hr);
}

65
kemonohook/hooks.c Normal file
View File

@ -0,0 +1,65 @@
#include "hook/iohook.h"
#include "hook/procaddr.h"
#include "hook/table.h"
#include "hooklib/serial.h"
#include "kemonohook/hooks.h"
#include "util/dprintf.h"
static BOOL WINAPI hook_GetVersionExW(LPOSVERSIONINFOW lpVersionInformation);
static int (WINAPI *next_GetVersionExW)(LPOSVERSIONINFOW lpVersionInformation);
static const struct hook_symbol kemono_kernel32_syms[] = {
{
.name = "GetVersionExW",
.patch = hook_GetVersionExW,
.link = (void **) &next_GetVersionExW,
}
};
void kemono_extra_hooks_init(){
HMODULE serialportapi = LoadLibraryA("Parade_Data/Plugins/SerialPortAPI.dll"); // HACK??
if (serialportapi != NULL){
iohook_apply_hooks(serialportapi);
serial_hook_apply_hooks(serialportapi);
dprintf("Kemono: Successfully pre-loaded SerialPortAPI\n");
}
}
void kemono_extra_hooks_load(HMODULE mod, const wchar_t* target_module) {
// Workaround for AmManager.checkTarget:Environment.GetEnvironmentVariable("USERNAME")
SetEnvironmentVariableA("USERNAME", "AppUser");
// Workaround for AmManager.checkTarget, expects OS version to be 6.2 or 6.3
hook_table_apply(
mod,
"kernel32.dll",
kemono_kernel32_syms,
_countof(kemono_kernel32_syms));
// needed for LED COM port
// FIXME: SerialPortAPI.dll seems to be loaded twice? this causes a crash
/*if (_wcsicmp(L"SerialPortAPI.dll", target_module) == 0) {
iohook_apply_hooks(mod);
serial_hook_apply_hooks(mod);
dprintf("Kemono: Loaded I/O hooks for serial port\n");
}*/
}
static BOOL WINAPI hook_GetVersionExW(LPOSVERSIONINFOW lpVersionInformation) {
int result = next_GetVersionExW(lpVersionInformation);
if (result) {
lpVersionInformation->dwMajorVersion = 6;
lpVersionInformation->dwMinorVersion = 2;
lpVersionInformation->dwBuildNumber = 0;
dprintf("Kemono: GetVersionExW hook hit\n");
}
return result;
}

5
kemonohook/hooks.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
void kemono_extra_hooks_init();
void kemono_extra_hooks_load(HMODULE mod, const wchar_t* target_module);

139
kemonohook/jvs.c Normal file
View File

@ -0,0 +1,139 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "amex/jvs.h"
#include "board/io3.h"
#include "kemonohook/kemono-dll.h"
#include "jvs/jvs-bus.h"
#include "util/dprintf.h"
struct kemono_jvs_ir_mask {
uint16_t p1;
uint16_t p2;
};
static void kemono_jvs_read_switches(void *ctx, struct io3_switch_state *out);
static void kemono_jvs_read_coin_counter(
void *ctx,
uint8_t slot_no,
uint16_t *out);
static void kemono_jvs_write_gpio(void *ctx, uint32_t state);
static const struct io3_ops kemono_jvs_io3_ops = {
.read_switches = kemono_jvs_read_switches,
.read_coin_counter = kemono_jvs_read_coin_counter,
.write_gpio = kemono_jvs_write_gpio
};
static struct io3 kemono_jvs_io3;
HRESULT kemono_jvs_init(struct jvs_node **out)
{
HRESULT hr;
assert(out != NULL);
assert(kemono_dll.init != NULL);
dprintf("JVS I/O: Starting IO backend\n");
hr = kemono_dll.init();
if (FAILED(hr)) {
dprintf("JVS I/O: Backend error, I/O disconnected: %x\n", (int) hr);
return hr;
}
io3_init(&kemono_jvs_io3, NULL, &kemono_jvs_io3_ops, NULL);
*out = io3_to_jvs_node(&kemono_jvs_io3);
return S_OK;
}
static void kemono_jvs_read_switches(void *ctx, struct io3_switch_state *out)
{
const struct kemono_jvs_ir_mask *masks;
uint16_t opbtn;
uint16_t pbtn;
size_t i;
assert(out != NULL);
assert(kemono_dll.poll != NULL);
opbtn = 0;
pbtn = 0;
kemono_dll.poll(&opbtn, &pbtn);
out->system = 0x00;
out->p1 = 0x0000;
out->p2 = 0x0000;
if (opbtn & KEMONO_IO_OPBTN_TEST) {
out->system |= 1 << 7;
}
if (opbtn & KEMONO_IO_OPBTN_SERVICE) {
out->p1 |= 1 << 14;
}
if (pbtn & KEMONO_IO_GAMEBTN_UP) {
out->p1 |= 1 << 13;
}
if (pbtn & KEMONO_IO_GAMEBTN_DOWN) {
out->p1 |= 1 << 12;
}
if (pbtn & KEMONO_IO_GAMEBTN_LEFT) {
out->p1 |= 1 << 11;
}
if (pbtn & KEMONO_IO_GAMEBTN_RIGHT) {
out->p1 |= 1 << 10;
}
if (pbtn & KEMONO_IO_GAMEBTN_R) {
out->p1 |= 1 << 9;
}
if (pbtn & KEMONO_IO_GAMEBTN_G) {
out->p1 |= 1 << 7;
}
if (pbtn & KEMONO_IO_GAMEBTN_B) {
out->p1 |= 1 << 8;
}
if (pbtn & KEMONO_IO_GAMEBTN_START) {
out->p1 |= 1 << 15;
}
}
static void kemono_jvs_read_coin_counter(
void *ctx,
uint8_t slot_no,
uint16_t *out)
{
assert(out != NULL);
assert(kemono_dll.jvs_read_coin_counter != NULL);
if (slot_no > 0) {
return;
}
kemono_dll.jvs_read_coin_counter(out);
}
static void kemono_jvs_write_gpio(void *ctx, uint32_t state){
kemono_dll.jvs_write_gpio(state);
}

7
kemonohook/jvs.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <windows.h>
#include "jvs/jvs-bus.h"
HRESULT kemono_jvs_init(struct jvs_node **root);

119
kemonohook/kemono-dll.c Normal file
View File

@ -0,0 +1,119 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "kemonohook/kemono-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym kemono_dll_syms[] = {
{
.sym = "kemono_io_init",
.off = offsetof(struct kemono_dll, init),
},
{
.sym = "kemono_io_poll",
.off = offsetof(struct kemono_dll, poll),
},
{
.sym = "kemono_io_jvs_read_coin_counter",
.off = offsetof(struct kemono_dll, jvs_read_coin_counter),
},
{
.sym = "kemono_io_led_init",
.off = offsetof(struct kemono_dll, led_init),
},
{
.sym = "kemono_io_led_set_colors",
.off = offsetof(struct kemono_dll, led_set_leds),
},
{
.sym = "kemono_io_jvs_write_gpio",
.off = offsetof(struct kemono_dll, jvs_write_gpio),
}
};
struct kemono_dll kemono_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 kemono_dll_init(const struct kemono_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("Kemono IO: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("Kemono IO: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "kemono_io_get_api_version");
if (get_api_version != NULL) {
kemono_dll.api_version = get_api_version();
} else {
kemono_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose kemono_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (kemono_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("Kemono IO: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
kemono_dll.api_version);
goto end;
}
sym = kemono_dll_syms;
hr = dll_bind(&kemono_dll, src, &sym, _countof(kemono_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("Kemono 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;
}

29
kemonohook/kemono-dll.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include <windows.h>
#include "kemonoio/kemonoio.h"
struct kemono_dll {
uint16_t api_version;
HRESULT (*init)(void);
HRESULT (*poll)(uint16_t *ops, uint16_t *player);
void (*jvs_read_coin_counter)(uint16_t *coins);
HRESULT (*led_init)(void);
void (*led_set_leds)(uint8_t board, uint8_t *rgb);
void (*jvs_write_gpio)(uint32_t state);
};
struct kemono_dll_config {
wchar_t path[MAX_PATH];
};
extern struct kemono_dll kemono_dll;
HRESULT kemono_dll_init(const struct kemono_dll_config *cfg, HINSTANCE self);

83
kemonohook/kemonohook.def Normal file
View File

@ -0,0 +1,83 @@
LIBRARY kemonohook
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
kemono_io_get_api_version
kemono_io_jvs_read_coin_counter
kemono_io_init
kemono_io_poll
kemono_io_led_init
kemono_io_led_set_colors
kemono_io_jvs_write_gpio
fwdlusb_open
fwdlusb_close
fwdlusb_listupPrinter
fwdlusb_listupPrinterSN
fwdlusb_selectPrinter
fwdlusb_selectPrinterSN
fwdlusb_getPrinterInfo
fwdlusb_status
fwdlusb_statusAll
fwdlusb_resetPrinter
fwdlusb_updateFirmware
fwdlusb_getFirmwareInfo
fwdlusb_MakeThread
fwdlusb_ReleaseThread
fwdlusb_AttachThreadCount
fwdlusb_getErrorLog
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_setIcctable
chcusb_copies
chcusb_status
chcusb_statusAll
chcusb_startpage
chcusb_endpage
chcusb_write
chcusb_writeLaminate
chcusb_writeHolo
chcusb_setPrinterInfo
chcusb_getGamma
chcusb_getMtf
chcusb_cancelCopies
chcusb_setPrinterToneCurve
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
chcusb_setCutList
chcusb_setLaminatePattern
chcusb_color_adjustment
chcusb_color_adjustmentEx
chcusb_getEEPROM
chcusb_setParameter
chcusb_getParameter
chcusb_universal_command

34
kemonohook/meson.build Normal file
View File

@ -0,0 +1,34 @@
shared_library(
'kemonohook',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
vs_module_defs : 'kemonohook.def',
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
capnhook.get_variable('hooklib_dep'),
],
link_with : [
aimeio_lib,
amex_lib,
board_lib,
hooklib_lib,
jvs_lib,
kemonoio_lib,
platform_lib,
unityhook_lib,
util_lib,
],
sources : [
'config.c',
'config.h',
'dllmain.c',
'hooks.c',
'hooks.h',
'jvs.c',
'jvs.h',
'kemono-dll.c',
'kemono-dll.h',
],
)

28
kemonoio/config.c Normal file
View File

@ -0,0 +1,28 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "kemonoio/config.h"
void kemono_io_config_load(
struct kemono_io_config *cfg,
const wchar_t *filename) {
assert(cfg != NULL);
assert(filename != NULL);
cfg->vk_test = GetPrivateProfileIntW(L"io3", L"test", '1', filename);
cfg->vk_service = GetPrivateProfileIntW(L"io3", L"service", '2', filename);
cfg->vk_coin = GetPrivateProfileIntW(L"io3", L"coin", '3', filename);
cfg->vk_left = GetPrivateProfileIntW(L"io3", L"left", VK_LEFT, filename);
cfg->vk_right = GetPrivateProfileIntW(L"io3", L"right", VK_RIGHT, filename);
cfg->vk_up = GetPrivateProfileIntW(L"io3", L"up", VK_UP, filename);
cfg->vk_down = GetPrivateProfileIntW(L"io3", L"down", VK_DOWN, filename);
cfg->vk_red = GetPrivateProfileIntW(L"io3", L"red", 'A', filename);
cfg->vk_green = GetPrivateProfileIntW(L"io3", L"green", 'S', filename);
cfg->vk_blue = GetPrivateProfileIntW(L"io3", L"blue", 'D', filename);
cfg->vk_start = GetPrivateProfileIntW(L"io3", L"start", VK_RETURN, filename);
}

25
kemonoio/config.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
struct kemono_io_config {
uint8_t vk_test;
uint8_t vk_service;
uint8_t vk_coin;
uint8_t vk_left;
uint8_t vk_right;
uint8_t vk_up;
uint8_t vk_down;
uint8_t vk_red;
uint8_t vk_green;
uint8_t vk_blue;
uint8_t vk_start;
};
void kemono_io_config_load(
struct kemono_io_config *cfg,
const wchar_t *filename);

109
kemonoio/kemonoio.c Normal file
View File

@ -0,0 +1,109 @@
#include <windows.h>
#include <limits.h>
#include <stdint.h>
#include <assert.h>
#include <util/dprintf.h>
#include "kemonoio/kemonoio.h"
#include "kemonoio/config.h"
static uint8_t kemono_opbtn;
static uint16_t kemono_pbtn;
static uint16_t kemono_io_coins;
static struct kemono_io_config kemono_io_cfg;
static bool kemono_io_coin;
uint16_t kemono_io_get_api_version(void) {
return 0x0100;
}
HRESULT kemono_io_init(void) {
kemono_io_config_load(&kemono_io_cfg, L".\\segatools.ini");
kemono_io_coins = 0;
return S_OK;
}
HRESULT kemono_io_poll(uint16_t *ops, uint16_t *player) {
kemono_opbtn = 0;
kemono_pbtn = 0;
if (GetAsyncKeyState(kemono_io_cfg.vk_test) & 0x8000) {
kemono_opbtn |= KEMONO_IO_OPBTN_TEST;
}
if (GetAsyncKeyState(kemono_io_cfg.vk_service) & 0x8000) {
kemono_opbtn |= KEMONO_IO_OPBTN_SERVICE;
}
if (kemono_io_cfg.vk_coin &&
(GetAsyncKeyState(kemono_io_cfg.vk_coin) & 0x8000)) {
if (!kemono_io_coin) {
kemono_io_coin = true;
kemono_io_coins++;
}
} else {
kemono_io_coin = false;
}
if (GetAsyncKeyState(kemono_io_cfg.vk_up)) {
kemono_pbtn |= KEMONO_IO_GAMEBTN_UP;
}
if (GetAsyncKeyState(kemono_io_cfg.vk_down)) {
kemono_pbtn |= KEMONO_IO_GAMEBTN_DOWN;
}
if (GetAsyncKeyState(kemono_io_cfg.vk_left)) {
kemono_pbtn |= KEMONO_IO_GAMEBTN_LEFT;
}
if (GetAsyncKeyState(kemono_io_cfg.vk_right)) {
kemono_pbtn |= KEMONO_IO_GAMEBTN_RIGHT;
}
if (GetAsyncKeyState(kemono_io_cfg.vk_red)) {
kemono_pbtn |= KEMONO_IO_GAMEBTN_R;
}
if (GetAsyncKeyState(kemono_io_cfg.vk_green)) {
kemono_pbtn |= KEMONO_IO_GAMEBTN_G;
}
if (GetAsyncKeyState(kemono_io_cfg.vk_blue)) {
kemono_pbtn |= KEMONO_IO_GAMEBTN_B;
}
if (GetAsyncKeyState(kemono_io_cfg.vk_start)) {
kemono_pbtn |= KEMONO_IO_GAMEBTN_START;
}
if (ops != NULL) {
*ops = kemono_opbtn;
}
if (player != NULL) {
*player = kemono_pbtn;
}
return S_OK;
}
void kemono_io_jvs_read_coin_counter(uint16_t *out) {
assert(out != NULL);
*out = kemono_io_coins;
}
HRESULT kemono_io_led_init(void) {
return S_OK;
}
void kemono_io_led_set_colors(uint8_t board, uint8_t *rgb) {
}
void kemono_io_jvs_write_gpio(uint32_t state){
}

81
kemonoio/kemonoio.h Normal file
View File

@ -0,0 +1,81 @@
#pragma once
#include <windows.h>
#include <stdint.h>
enum {
KEMONO_IO_OPBTN_TEST = 0x01,
KEMONO_IO_OPBTN_SERVICE = 0x02
};
enum {
KEMONO_IO_GAMEBTN_UP = 0x01,
KEMONO_IO_GAMEBTN_DOWN = 0x02,
KEMONO_IO_GAMEBTN_LEFT = 0x04,
KEMONO_IO_GAMEBTN_RIGHT = 0x08,
KEMONO_IO_GAMEBTN_R = 0x10,
KEMONO_IO_GAMEBTN_G = 0x20,
KEMONO_IO_GAMEBTN_B = 0x40,
KEMONO_IO_GAMEBTN_START = 0x80
};
/* Get the version of the Kemono 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 kemono_io_get_api_version(void);
/* Initialize the IO DLL. This is the second function that will be called on
your DLL, after kemono_io_get_api_version.
All subsequent calls to this API may originate from arbitrary threads.
Minimum API version: 0x0100 */
HRESULT kemono_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 kemono_io_poll(uint16_t* ops, uint16_t* player);
/* Read the current state of the coin counter. This value should be incremented
for every coin detected by the coin acceptor mechanism. This count does not
need to persist beyond the lifetime of the process.
Minimum API version: 0x0100 */
void kemono_io_jvs_read_coin_counter(uint16_t *out);
/* Initialize LED emulation. This function will be called before any
other fgo_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 kemono_io_led_init(void);
/* Update the RGB LEDs.
The left side LED bar are indices 0 to 32.
The right side LED bar are indices 33 to 65.
Minimum API version: 0x0100 */
void kemono_io_led_set_colors(uint8_t board, uint8_t *rgb);
/* Update the button LEDs.
Button R: Bit 15
Button G: Bit 1
Button B: Bit 13
Start Button: Bit 11
Minimum API version: 0x0100 */
void kemono_io_jvs_write_gpio(uint32_t state);

13
kemonoio/meson.build Normal file
View File

@ -0,0 +1,13 @@
kemonoio_lib = static_library(
'kemonoio_lib',
name_prefix : '',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
sources : [
'kemonoio.c',
'kemonoio.h',
'config.c',
'config.h',
],
)

View File

@ -102,7 +102,7 @@ static DWORD CALLBACK mai2_pre_startup(void)
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
hooked earlier in the `mai2hook` initialization. */
unity_hook_init(&mai2_hook_cfg.unity, mai2_hook_mod);
unity_hook_init(&mai2_hook_cfg.unity, mai2_hook_mod, NULL);
/* Initialize debug helpers */

View File

@ -110,6 +110,7 @@ subdir('mercuryio')
subdir('cxbio')
subdir('tokyoio')
subdir('fgoio')
subdir('kemonoio')
subdir('chunihook')
subdir('divahook')
@ -126,3 +127,4 @@ subdir('mercuryhook')
subdir('cxbhook')
subdir('tokyohook')
subdir('fgohook')
subdir('kemonohook')

View File

@ -110,7 +110,7 @@ static DWORD CALLBACK mu3_pre_startup(void)
There seems to be an issue with other DLL hooks if `LoadLibraryW` is
hooked earlier in the `mu3hook` initialization. */
unity_hook_init(&mu3_hook_cfg.unity, mu3_hook_mod);
unity_hook_init(&mu3_hook_cfg.unity, mu3_hook_mod, NULL);
/* Initialize debug helpers */

View File

@ -34,11 +34,11 @@ static HRESULT vfs_path_hook_option(
static HRESULT vfs_reg_read_amfs(void *bytes, uint32_t *nbytes);
static HRESULT vfs_reg_read_appdata(void *bytes, uint32_t *nbytes);
static wchar_t* hook_System_getAppRootPath();
static wchar_t* (*next_System_getAppRootPath)();
static __thiscall wchar_t* hook_System_getAppRootPath();
static __thiscall wchar_t* (*next_System_getAppRootPath)();
static wchar_t* hook_AppImage_getOptionMountRootPath();
static wchar_t* (*next_AppImage_getOptionMountRootPath)();
static __thiscall wchar_t* hook_AppImage_getOptionMountRootPath();
static __thiscall wchar_t* (*next_AppImage_getOptionMountRootPath)();
static const struct hook_symbol amdaemon_syms[] = {
{
@ -510,7 +510,7 @@ static HRESULT vfs_reg_read_appdata(void *bytes, uint32_t *nbytes)
return reg_hook_read_wstr(bytes, nbytes, L"Y:\\");
}
static wchar_t* hook_System_getAppRootPath()
static __thiscall wchar_t* hook_System_getAppRootPath()
{
wchar_t *path = malloc(sizeof(wchar_t) * MAX_PATH);
wcscpy_s(path, MAX_PATH, vfs_config.appdata);
@ -520,7 +520,7 @@ static wchar_t* hook_System_getAppRootPath()
return path;
}
static wchar_t* hook_AppImage_getOptionMountRootPath()
static __thiscall wchar_t* hook_AppImage_getOptionMountRootPath()
{
wchar_t *path = malloc(sizeof(wchar_t) * MAX_PATH);
wcscpy_s(path, MAX_PATH, vfs_config.option);

View File

@ -13,6 +13,43 @@
#include "platform/config.h"
#include "platform/platform.h"
void led15070_config_load(struct led15070_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
wchar_t tmpstr[16];
cfg->enable = GetPrivateProfileIntW(L"led15070", L"enable", 1, filename);
cfg->port_no = GetPrivateProfileIntW(L"led15070", L"portNo", 0, filename);
cfg->fw_ver = GetPrivateProfileIntW(L"led15070", L"fwVer", 0x90, filename);
/* TODO: Unknown, no firmware file available */
cfg->fw_sum = GetPrivateProfileIntW(L"led15070", L"fwSum", 0xdead, filename);
GetPrivateProfileStringW(
L"led15070",
L"boardNumber",
L"15070-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"led15070",
L"eepromPath",
L"DEVICE",
cfg->eeprom_path,
_countof(cfg->eeprom_path),
filename);
}
void swdc_dll_config_load(
struct swdc_dll_config *cfg,
const wchar_t *filename)
@ -29,6 +66,14 @@ void swdc_dll_config_load(
filename);
}
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 swdc_hook_config_load(
struct swdc_hook_config *cfg,
const wchar_t *filename)
@ -42,13 +87,7 @@ void swdc_hook_config_load(
zinput_config_load(&cfg->zinput, filename);
dvd_config_load(&cfg->dvd, filename);
io4_config_load(&cfg->io4, filename);
ffb_config_load(&cfg->ffb, filename);
led15070_config_load(&cfg->led15070, filename);
vfd_config_load(&cfg->vfd, filename);
}
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);
}

View File

@ -4,6 +4,7 @@
#include <stddef.h>
#include "board/config.h"
#include "board/led15070.h"
#include "hooklib/dvd.h"
@ -17,7 +18,9 @@ struct swdc_hook_config {
struct aime_config aime;
struct dvd_config dvd;
struct io4_config io4;
struct ffb_config ffb;
struct vfd_config vfd;
struct led15070_config led15070;
struct swdc_dll_config dll;
struct zinput_config zinput;
};

View File

@ -8,8 +8,8 @@
WITH
838-15416 Indicator BD LED Board
COM1: 838-15069 MOTOR DRIVE BD RS232/422 board
COM2: 837-15396 "Gen 3" Aime reader
COM3: 837-15070-04 IC BD LED controller board
COM2: 837-15070-04 IC BD LED controller board
COM3: 837-15396 "Gen 3" Aime reader
COM4: 200-6275 VFD GP1232A02A FUTABA board
*/
@ -31,6 +31,7 @@
#include "swdchook/config.h"
#include "swdchook/swdc-dll.h"
#include "swdchook/io4.h"
#include "swdchook/ffb.h"
#include "platform/platform.h"
@ -91,6 +92,20 @@ static DWORD CALLBACK swdc_pre_startup(void)
goto fail;
}
hr = swdc_ffb_hook_init(&swdc_hook_cfg.ffb, 1);
if (FAILED(hr)) {
goto fail;
}
/* Not working, different board -04 instead of -02? */
hr = led15070_hook_init(&swdc_hook_cfg.led15070, swdc_dll.led_init,
swdc_dll.led_set_fet_output, NULL, swdc_dll.led_gs_update, 2, 1);
if (FAILED(hr)) {
goto fail;
}
/* Hook external DLL APIs */
zinput_hook_init(&swdc_hook_cfg.zinput);

59
swdchook/ffb.c Normal file
View File

@ -0,0 +1,59 @@
#include <windows.h>
#include <xinput.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "board/ffb.h"
#include "swdchook/swdc-dll.h"
#include "util/dprintf.h"
static void swdc_ffb_toggle(bool active);
static void swdc_ffb_constant_force(uint8_t direction, uint8_t force);
static void swdc_ffb_rumble(uint8_t force, uint8_t period);
static void swdc_ffb_damper(uint8_t force);
static const struct ffb_ops swdc_ffb_ops = {
.toggle = swdc_ffb_toggle,
.constant_force = swdc_ffb_constant_force,
.rumble = swdc_ffb_rumble,
.damper = swdc_ffb_damper
};
HRESULT swdc_ffb_hook_init(const struct ffb_config *cfg, unsigned int port_no)
{
HRESULT hr;
assert(swdc_dll.init != NULL);
hr = ffb_hook_init(cfg, &swdc_ffb_ops, port_no);
if (FAILED(hr)) {
return hr;
}
return swdc_dll.ffb_init();
}
static void swdc_ffb_toggle(bool active)
{
swdc_dll.ffb_toggle(active);
}
static void swdc_ffb_constant_force(uint8_t direction, uint8_t force)
{
swdc_dll.ffb_constant_force(direction, force);
}
static void swdc_ffb_rumble(uint8_t force, uint8_t period)
{
swdc_dll.ffb_rumble(force, period);
}
static void swdc_ffb_damper(uint8_t force)
{
swdc_dll.ffb_damper(force);
}

7
swdchook/ffb.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <windows.h>
#include "board/ffb.h"
HRESULT swdc_ffb_hook_init(const struct ffb_config *cfg, unsigned int port_no);

Some files were not shown because too many files have changed in this diff Show More