forked from Hay1tsme/segatools
Compare commits
31 Commits
2024-08-20
...
feature/ff
Author | SHA1 | Date | |
---|---|---|---|
5f817c8a36
|
|||
259b763a13
|
|||
2251585ef0
|
|||
c06bb408e7
|
|||
53fb8c28ea | |||
33452394e6 | |||
88a5bdcd14 | |||
bb773a63ce | |||
25e79f87c2 | |||
79592514ba | |||
cdfd3bf655 | |||
f6c12fd230 | |||
86556ed2c8 | |||
9de48dd6ce | |||
d257887f6e | |||
3eef5dd209 | |||
599d5e3211 | |||
f18d074c5f | |||
6bd1bce419 | |||
96bdacfa7c | |||
d4bb7b6e0e | |||
70ac873d11 | |||
068651b6fa | |||
84e9ed3c9a | |||
c827b4c212 | |||
26624f25b1 | |||
824bc9abda | |||
cc5b87b559 | |||
e6794807a6 | |||
54cbbffae9 | |||
ac0f9f0587
|
22
Package.mk
22
Package.mk
@ -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 \
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
235
board/ffb.c
Normal 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
20
board/ffb.h
Normal 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);
|
@ -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 {
|
||||
|
@ -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'
|
||||
],
|
||||
)
|
||||
|
@ -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 {
|
||||
|
@ -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
123
board/vfd-cmd.h
Normal file
@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
|
||||
#include "board/vfd-frame.h"
|
||||
|
||||
enum {
|
||||
VFD_CMD_GET_VERSION = 0x5B,
|
||||
VFD_CMD_RESET = 0x0B,
|
||||
VFD_CMD_CLEAR_SCREEN = 0x0C,
|
||||
VFD_CMD_SET_BRIGHTNESS = 0x20,
|
||||
VFD_CMD_SET_SCREEN_ON = 0x21,
|
||||
VFD_CMD_SET_H_SCROLL = 0x22,
|
||||
VFD_CMD_DRAW_IMAGE = 0x2E,
|
||||
VFD_CMD_SET_CURSOR = 0x30,
|
||||
VFD_CMD_SET_ENCODING = 0x32,
|
||||
VFD_CMD_SET_TEXT_WND = 0x40,
|
||||
VFD_CMD_SET_TEXT_SPEED = 0x41,
|
||||
VFD_CMD_WRITE_TEXT = 0x50,
|
||||
VFD_CMD_ENABLE_SCROLL = 0x51,
|
||||
VFD_CMD_DISABLE_SCROLL = 0x52,
|
||||
VFD_CMD_ROTATE = 0x5D,
|
||||
VFD_CMD_CREATE_CHAR = 0xA3,
|
||||
VFD_CMD_CREATE_CHAR2 = 0xA4,
|
||||
};
|
||||
|
||||
enum {
|
||||
VFD_ENC_GB2312 = 0,
|
||||
VFD_ENC_BIG5 = 1,
|
||||
VFD_ENC_SHIFT_JIS = 2,
|
||||
VFD_ENC_KSC5601 = 3,
|
||||
VFD_ENC_MAX = 3,
|
||||
};
|
||||
|
||||
struct vfd_req_hdr {
|
||||
uint8_t sync;
|
||||
uint8_t cmd;
|
||||
};
|
||||
|
||||
struct vfd_req_any {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t payload[2054];
|
||||
};
|
||||
|
||||
struct vfd_req_board_info {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t unk1;
|
||||
};
|
||||
|
||||
struct vfd_resp_board_info { // \x0201.20\x03
|
||||
uint8_t unk1;
|
||||
char version[5];
|
||||
uint8_t unk2;
|
||||
};
|
||||
|
||||
struct vfd_req_reset {
|
||||
struct vfd_req_hdr hdr;
|
||||
};
|
||||
|
||||
struct vfd_req_cls {
|
||||
struct vfd_req_hdr hdr;
|
||||
};
|
||||
|
||||
struct vfd_req_brightness {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t brightness;
|
||||
};
|
||||
|
||||
struct vfd_req_power {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t power_state;
|
||||
};
|
||||
|
||||
struct vfd_req_hscroll {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t x_pos;
|
||||
};
|
||||
|
||||
struct vfd_req_draw {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint16_t x0;
|
||||
uint8_t y0;
|
||||
uint16_t x1;
|
||||
uint8_t y1;
|
||||
uint8_t image[2048];
|
||||
};
|
||||
|
||||
struct vfd_req_cursor {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint16_t x;
|
||||
uint8_t y;
|
||||
};
|
||||
|
||||
struct vfd_req_encoding {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t encoding;
|
||||
};
|
||||
|
||||
struct vfd_req_wnd {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint16_t x0;
|
||||
uint8_t y0;
|
||||
uint16_t x1;
|
||||
uint8_t y1;
|
||||
};
|
||||
|
||||
struct vfd_req_speed {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t encoding;
|
||||
};
|
||||
|
||||
struct vfd_req_scroll {
|
||||
struct vfd_req_hdr hdr;
|
||||
};
|
||||
|
||||
struct vfd_req_rotate {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t unk1;
|
||||
};
|
||||
|
||||
struct vfd_req_create_char {
|
||||
struct vfd_req_hdr hdr;
|
||||
uint8_t type;
|
||||
uint8_t pixels[32];
|
||||
};
|
88
board/vfd-frame.c
Normal file
88
board/vfd-frame.c
Normal file
@ -0,0 +1,88 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define SUPER_VERBOSE 1
|
||||
|
||||
#include "board/vfd-frame.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static HRESULT vfd_frame_encode_byte(struct iobuf *dest, uint8_t byte);
|
||||
|
||||
/* Frame structure:
|
||||
|
||||
REQUEST:
|
||||
[0] Sync byte (0x1A or 0x1B)
|
||||
[1] Packet ID
|
||||
[2...n-1] Data/payload
|
||||
|
||||
--- OR ---
|
||||
|
||||
if no sync byte is given, plain static text in the currently configured encoding is expected.
|
||||
|
||||
RESPONSE:
|
||||
This thing never responds, unless it's VFD_CMD_GET_VERSION
|
||||
*/
|
||||
|
||||
bool vfd_frame_sync(struct const_iobuf *src) {
|
||||
return src->bytes[src->pos] == VFD_SYNC_BYTE || src->bytes[src->pos] == VFD_SYNC_BYTE2;
|
||||
}
|
||||
|
||||
HRESULT vfd_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes) {
|
||||
const uint8_t *src;
|
||||
uint8_t byte;
|
||||
size_t i;
|
||||
HRESULT hr;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(dest->bytes != NULL || dest->nbytes == 0);
|
||||
assert(dest->pos <= dest->nbytes);
|
||||
assert(ptr != NULL);
|
||||
|
||||
src = ptr;
|
||||
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("VFD: RX Buffer:\n");
|
||||
#endif
|
||||
|
||||
for (i = 1; i < nbytes; i++) {
|
||||
byte = src[i];
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("%02x ", byte);
|
||||
#endif
|
||||
|
||||
hr = vfd_frame_encode_byte(dest, byte);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("\n");
|
||||
#endif
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT vfd_frame_encode_byte(struct iobuf *dest, uint8_t byte) {
|
||||
if (dest->pos + 1 > dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
|
||||
return S_OK;
|
||||
}
|
20
board/vfd-frame.h
Normal file
20
board/vfd-frame.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
enum {
|
||||
VFD_SYNC_BYTE = 0x1B,
|
||||
VFD_SYNC_BYTE2 = 0x1A,
|
||||
};
|
||||
|
||||
bool vfd_frame_sync(struct const_iobuf *src);
|
||||
|
||||
HRESULT vfd_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes);
|
400
board/vfd.c
400
board/vfd.c
@ -2,17 +2,16 @@
|
||||
directly by amdaemon, and it has something to do with displaying the status
|
||||
of electronic payments.
|
||||
|
||||
Part number in schematics is "VFD GP1232A02A FUTABA".
|
||||
|
||||
Little else about this board is known. Black-holing the RS232 comms that it
|
||||
receives seems to be sufficient for the time being. */
|
||||
Part number in schematics is "VFD GP1232A02A FUTABA". */
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "board/config.h"
|
||||
#include "board/vfd.h"
|
||||
#include "board/vfd-cmd.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
@ -21,33 +20,101 @@
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
#define SUPER_VERBOSE 0
|
||||
|
||||
static HRESULT vfd_handle_irp(struct irp *irp);
|
||||
|
||||
static struct uart vfd_uart;
|
||||
static uint8_t vfd_written[512];
|
||||
static uint8_t vfd_readable[512];
|
||||
UINT codepage;
|
||||
static uint8_t vfd_written[4096];
|
||||
static uint8_t vfd_readable[4096];
|
||||
|
||||
HRESULT vfd_hook_init(const struct vfd_config *cfg, unsigned int port_no)
|
||||
static int encoding = VFD_ENC_SHIFT_JIS;
|
||||
|
||||
HRESULT vfd_handle_get_version(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_reset(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_clear_screen(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_brightness(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_screen_on(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_h_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_draw_image(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_cursor(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_encoding(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_text_wnd(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_set_text_speed(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_write_text(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_enable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_disable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_rotate(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_create_char(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
HRESULT vfd_handle_create_char2(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart);
|
||||
|
||||
static bool utf_enabled;
|
||||
|
||||
HRESULT vfd_hook_init(struct vfd_config *cfg, int default_port)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
if (!cfg->enable){
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
uart_init(&vfd_uart, port_no);
|
||||
utf_enabled = cfg->utf_conversion;
|
||||
|
||||
int port = cfg->port;
|
||||
if (port == 0){
|
||||
port = default_port;
|
||||
}
|
||||
|
||||
dprintf("VFD: enabling (port=%d)\n", port);
|
||||
uart_init(&vfd_uart, port);
|
||||
vfd_uart.written.bytes = vfd_written;
|
||||
vfd_uart.written.nbytes = sizeof(vfd_written);
|
||||
vfd_uart.readable.bytes = vfd_readable;
|
||||
vfd_uart.readable.nbytes = sizeof(vfd_readable);
|
||||
|
||||
codepage = GetACP();
|
||||
dprintf("VFD: hook enabled.\n");
|
||||
|
||||
return iohook_push_handler(vfd_handle_irp);
|
||||
}
|
||||
|
||||
|
||||
const char* get_encoding_name(int b){
|
||||
switch (b){
|
||||
case 0: return "gb2312";
|
||||
case 1: return "big5";
|
||||
case 2: return "shift-jis";
|
||||
case 3: return "ks_c_5601-1987";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void print_vfd_text(const char* str, int len){
|
||||
|
||||
if (utf_enabled){
|
||||
|
||||
wchar_t encoded[1024];
|
||||
memset(encoded, 0, 1024 * sizeof(wchar_t));
|
||||
|
||||
int codepage = 0;
|
||||
if (encoding == VFD_ENC_GB2312){
|
||||
codepage = 936;
|
||||
} else if (encoding == VFD_ENC_BIG5){
|
||||
codepage = 950;
|
||||
} else if (encoding == VFD_ENC_SHIFT_JIS){
|
||||
codepage = 932;
|
||||
} else if (encoding == VFD_ENC_KSC5601) {
|
||||
codepage = 949;
|
||||
}
|
||||
|
||||
if (!MultiByteToWideChar(codepage, MB_USEGLYPHCHARS, str, len, encoded, 1024)){
|
||||
dprintf("VFD: Text conversion failed: %ld", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf("VFD: Text: %ls\n", encoded);
|
||||
} else {
|
||||
|
||||
dprintf("VFD: Text: %s\n", str);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT vfd_handle_irp(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
@ -58,67 +125,274 @@ static HRESULT vfd_handle_irp(struct irp *irp)
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
if (irp->op == IRP_OP_OPEN){
|
||||
dprintf("VFD: Open\n");
|
||||
} else if (irp->op == IRP_OP_CLOSE){
|
||||
dprintf("VFD: Close\n");
|
||||
}
|
||||
|
||||
hr = uart_handle_irp(&vfd_uart, irp);
|
||||
|
||||
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
uint8_t cmd = 0;
|
||||
uint8_t str_1[512];
|
||||
uint8_t str_2[512];
|
||||
uint8_t str_1_len = 0;
|
||||
uint8_t str_2_len = 0;
|
||||
for (size_t i = 0; i < vfd_uart.written.pos; i++) {
|
||||
if (vfd_uart.written.bytes[i] == 0x1B) {
|
||||
i++;
|
||||
cmd = vfd_uart.written.bytes[i];
|
||||
if (cmd == 0x30) {
|
||||
i += 3;
|
||||
}
|
||||
else if (cmd == 0x50) {
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (cmd == 0x30) {
|
||||
str_1[str_1_len++] = vfd_uart.written.bytes[i];
|
||||
}
|
||||
else if (cmd == 0x50) {
|
||||
str_2[str_2_len++] = vfd_uart.written.bytes[i];
|
||||
}
|
||||
}
|
||||
#if SUPER_VERBOSE
|
||||
dprintf("VFD TX:\n");
|
||||
dump_iobuf(&vfd_uart.written);
|
||||
#endif
|
||||
|
||||
if (str_1_len) {
|
||||
str_1[str_1_len++] = '\0';
|
||||
if (codepage != 932) {
|
||||
WCHAR buffer[512];
|
||||
MultiByteToWideChar(932, 0, (LPCSTR)str_1, str_1_len, buffer, str_1_len);
|
||||
char str_recode[str_1_len * 3];
|
||||
WideCharToMultiByte(codepage, 0, buffer, str_1_len, str_recode, str_1_len * 3, NULL, NULL);
|
||||
dprintf("VFD: %s\n", str_recode);
|
||||
}
|
||||
else {
|
||||
dprintf("VFD: %s\n", str_1);
|
||||
}
|
||||
}
|
||||
struct const_iobuf reader;
|
||||
iobuf_flip(&reader, &vfd_uart.written);
|
||||
|
||||
if (str_2_len) {
|
||||
str_2[str_2_len++] = '\0';
|
||||
if (codepage != 932) {
|
||||
WCHAR buffer[512];
|
||||
MultiByteToWideChar(932, 0, (LPCSTR)str_2, str_2_len, buffer, str_2_len);
|
||||
char str_recode[str_2_len * 3];
|
||||
WideCharToMultiByte(codepage, 0, buffer, str_2_len, str_recode, str_2_len * 3, NULL, NULL);
|
||||
dprintf("VFD: %s\n", str_recode);
|
||||
struct iobuf* writer = &vfd_uart.readable;
|
||||
for (; reader.pos < reader.nbytes ; ){
|
||||
|
||||
if (vfd_frame_sync(&reader)) {
|
||||
|
||||
reader.pos++; // get the sync byte out of the way
|
||||
|
||||
uint8_t cmd;
|
||||
iobuf_read_8(&reader, &cmd);
|
||||
|
||||
if (cmd == VFD_CMD_GET_VERSION) {
|
||||
hr = vfd_handle_get_version(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_RESET) {
|
||||
hr = vfd_handle_reset(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_CLEAR_SCREEN) {
|
||||
hr = vfd_handle_clear_screen(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_BRIGHTNESS) {
|
||||
hr = vfd_handle_set_brightness(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_SCREEN_ON) {
|
||||
hr = vfd_handle_set_screen_on(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_H_SCROLL) {
|
||||
hr = vfd_handle_set_h_scroll(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_DRAW_IMAGE) {
|
||||
hr = vfd_handle_draw_image(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_CURSOR) {
|
||||
hr = vfd_handle_set_cursor(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_ENCODING) {
|
||||
hr = vfd_handle_set_encoding(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_TEXT_WND) {
|
||||
hr = vfd_handle_set_text_wnd(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_SET_TEXT_SPEED) {
|
||||
hr = vfd_handle_set_text_speed(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_WRITE_TEXT) {
|
||||
hr = vfd_handle_write_text(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_ENABLE_SCROLL) {
|
||||
hr = vfd_handle_enable_scroll(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_DISABLE_SCROLL) {
|
||||
hr = vfd_handle_disable_scroll(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_ROTATE) {
|
||||
hr = vfd_handle_rotate(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_CREATE_CHAR) {
|
||||
hr = vfd_handle_create_char(&reader, writer, &vfd_uart);
|
||||
} else if (cmd == VFD_CMD_CREATE_CHAR2) {
|
||||
hr = vfd_handle_create_char2(&reader, writer, &vfd_uart);
|
||||
} else {
|
||||
dprintf("VFD: Unknown command 0x%x\n", cmd);
|
||||
dump_const_iobuf(&reader);
|
||||
hr = S_FALSE;
|
||||
}
|
||||
} else {
|
||||
dprintf("VFD: %s\n", str_2);
|
||||
|
||||
// if no sync byte is sent, we are just getting plain text...
|
||||
|
||||
if (reader.pos < reader.nbytes){
|
||||
int len = 0;
|
||||
|
||||
// read chars until we hit a new sync byte or the data ends
|
||||
while (reader.pos + len + 1 < reader.nbytes && reader.bytes[reader.pos + len] != VFD_SYNC_BYTE && reader.bytes[reader.pos + len] != VFD_SYNC_BYTE2){
|
||||
len++;
|
||||
}
|
||||
|
||||
char* str = malloc(len);
|
||||
memset(str, 0, len);
|
||||
iobuf_read(&reader, str, len);
|
||||
print_vfd_text(str, len);
|
||||
free(str);
|
||||
|
||||
reader.pos += len;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!SUCCEEDED(hr)){
|
||||
return hr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// dprintf("VFD TX:\n");
|
||||
// dump_iobuf(&vfd_uart.written);
|
||||
vfd_uart.written.pos = 0;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT vfd_handle_get_version(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Get Version\n");
|
||||
|
||||
struct vfd_resp_board_info resp;
|
||||
|
||||
memset(&resp, 0, sizeof(resp));
|
||||
resp.unk1 = 2;
|
||||
strcpy(resp.version, "01.20");
|
||||
resp.unk2 = 1;
|
||||
|
||||
return vfd_frame_encode(writer, &resp, sizeof(resp));
|
||||
}
|
||||
HRESULT vfd_handle_reset(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Reset\n");
|
||||
|
||||
encoding = VFD_ENC_SHIFT_JIS;
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_clear_screen(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Clear Screen\n");
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_brightness(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
if (b > 4){
|
||||
dprintf("VFD: Brightness, invalid argument\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
dprintf("VFD: Brightness, %d\n", b);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_screen_on(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
if (b > 1){
|
||||
dprintf("VFD: Screen Power, invalid argument\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
dprintf("VFD: Screen Power, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_h_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t x;
|
||||
iobuf_read_8(reader, &x);
|
||||
|
||||
dprintf("VFD: Horizontal Scroll, X=%d\n", x);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_draw_image(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
int w, h;
|
||||
uint16_t x0, x1;
|
||||
uint8_t y0, y1;
|
||||
uint8_t image[2048];
|
||||
|
||||
iobuf_read_be16(reader, &x0);
|
||||
iobuf_read_8(reader, &y0);
|
||||
iobuf_read_be16(reader, &x1);
|
||||
iobuf_read_8(reader, &y1);
|
||||
w = x1 - x0;
|
||||
h = y1 - y0;
|
||||
iobuf_read(reader, image, w*h);
|
||||
|
||||
dprintf("VFD: Draw image, %dx%d\n", w, h);
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT vfd_handle_set_cursor(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint16_t x;
|
||||
uint8_t y;
|
||||
|
||||
iobuf_read_be16(reader, &x);
|
||||
iobuf_read_8(reader, &y);
|
||||
|
||||
dprintf("VFD: Set Cursor, x=%d,y=%d\n", x, y);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT vfd_handle_set_encoding(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
dprintf("VFD: Set Encoding, %d (%s)\n", b, get_encoding_name(b));
|
||||
|
||||
if (b < 0 || b > VFD_ENC_MAX){
|
||||
dprintf("Invalid encoding specified\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
encoding = b;
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_text_wnd(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint16_t x0, x1;
|
||||
uint8_t y0, y1;
|
||||
|
||||
iobuf_read_be16(reader, &x0);
|
||||
iobuf_read_8(reader, &y0);
|
||||
iobuf_read_be16(reader, &x1);
|
||||
iobuf_read_8(reader, &y1);
|
||||
|
||||
dprintf("VFD: Set Text Window, p0:%d,%d, p1:%d,%d\n", x0, y0, x1, y1);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_set_text_speed(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
dprintf("VFD: Set Text Speed, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_write_text(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t len;
|
||||
iobuf_read_8(reader, &len);
|
||||
|
||||
char* str = malloc(len);
|
||||
iobuf_read(reader, str, len);
|
||||
|
||||
print_vfd_text(str, len);
|
||||
free(str);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_enable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Enable Scrolling\n");
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_disable_scroll(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
dprintf("VFD: Disable Scrolling\n");
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_rotate(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
|
||||
dprintf("VFD: Rotate, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_create_char(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b;
|
||||
iobuf_read_8(reader, &b);
|
||||
char buf[32];
|
||||
|
||||
iobuf_read(reader, buf, 32);
|
||||
|
||||
dprintf("VFD: Create character, %d\n", b);
|
||||
return S_FALSE;
|
||||
}
|
||||
HRESULT vfd_handle_create_char2(struct const_iobuf* reader, struct iobuf* writer, struct uart* vfd_uart){
|
||||
uint8_t b, b2;
|
||||
iobuf_read_8(reader, &b);
|
||||
iobuf_read_8(reader, &b2);
|
||||
char buf[16];
|
||||
|
||||
iobuf_read(reader, buf, 16);
|
||||
|
||||
dprintf("VFD: Create character, %d, %d\n", b, b2);
|
||||
return S_FALSE;
|
||||
}
|
||||
|
@ -4,7 +4,10 @@
|
||||
|
||||
struct vfd_config {
|
||||
bool enable;
|
||||
int port;
|
||||
bool utf_conversion;
|
||||
};
|
||||
|
||||
|
||||
HRESULT vfd_hook_init(const struct vfd_config *cfg, unsigned int port_no);
|
||||
HRESULT vfd_hook_init(struct vfd_config *cfg, int default_port);
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
2
dist/chusan/segatools.ini
vendored
2
dist/chusan/segatools.ini
vendored
@ -25,7 +25,7 @@ aimePath=DEVICE\aime.txt
|
||||
;highBaud=1
|
||||
|
||||
[vfd]
|
||||
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||
; Enable VFD emulation. Disable to use a real VFD
|
||||
; GP1232A02A FUTABA assembly.
|
||||
enable=1
|
||||
|
||||
|
2
dist/cm/segatools.ini
vendored
2
dist/cm/segatools.ini
vendored
@ -23,7 +23,7 @@ enable=1
|
||||
aimePath=DEVICE\aime.txt
|
||||
|
||||
[vfd]
|
||||
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||
; Enable VFD emulation. Disable to use a real VFD
|
||||
; GP1232A02A FUTABA assembly.
|
||||
enable=1
|
||||
|
||||
|
2
dist/fgo/segatools.ini
vendored
2
dist/fgo/segatools.ini
vendored
@ -23,7 +23,7 @@ enable=1
|
||||
aimePath=DEVICE\aime.txt
|
||||
|
||||
[vfd]
|
||||
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||
; Enable VFD emulation. Disable to use a real VFD
|
||||
; GP1232A02A FUTABA assembly.
|
||||
enable=1
|
||||
|
||||
|
26
dist/idac/segatools.ini
vendored
26
dist/idac/segatools.ini
vendored
@ -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
|
||||
|
35
dist/idz/segatools.ini
vendored
35
dist/idz/segatools.ini
vendored
@ -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
5
dist/idz/start.bat
vendored
@ -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
147
dist/kemono/segatools.ini
vendored
Normal 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
12
dist/kemono/start.bat
vendored
Normal 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
|
2
dist/mai2/segatools.ini
vendored
2
dist/mai2/segatools.ini
vendored
@ -23,7 +23,7 @@ enable=1
|
||||
aimePath=DEVICE\aime.txt
|
||||
|
||||
[vfd]
|
||||
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||
; Enable VFD emulation. Disable to use a real VFD
|
||||
; GP1232A02A FUTABA assembly.
|
||||
enable=1
|
||||
|
||||
|
2
dist/mercury/segatools.ini
vendored
2
dist/mercury/segatools.ini
vendored
@ -23,7 +23,7 @@ enable=1
|
||||
aimePath=DEVICE\aime.txt
|
||||
|
||||
[vfd]
|
||||
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||
; Enable VFD emulation. Disable to use a real VFD
|
||||
; GP1232A02A FUTABA assembly.
|
||||
enable=1
|
||||
|
||||
|
4
dist/mu3/segatools.ini
vendored
4
dist/mu3/segatools.ini
vendored
@ -23,7 +23,7 @@ enable=1
|
||||
aimePath=DEVICE\aime.txt
|
||||
|
||||
[vfd]
|
||||
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||
; Enable VFD emulation. Disable to use a real VFD
|
||||
; GP1232A02A FUTABA assembly.
|
||||
enable=1
|
||||
|
||||
@ -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
|
||||
|
28
dist/swdc/segatools.ini
vendored
28
dist/swdc/segatools.ini
vendored
@ -23,7 +23,7 @@ enable=1
|
||||
aimePath=DEVICE\aime.txt
|
||||
|
||||
[vfd]
|
||||
; Enable VFD emulation (currently just stubbed). Disable to use a real VFD
|
||||
; Enable VFD emulation. Disable to use a real VFD
|
||||
; GP1232A02A FUTABA assembly.
|
||||
enable=1
|
||||
|
||||
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -44,7 +44,7 @@ EXPORTS
|
||||
chcusb_selectPrinter
|
||||
chcusb_selectPrinterSN
|
||||
chcusb_getPrinterInfo
|
||||
chcusb_imageformat
|
||||
chcusb_imageformat=chcusb_imageformat_330
|
||||
chcusb_setmtf
|
||||
chcusb_makeGamma
|
||||
chcusb_setIcctable
|
||||
|
@ -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;
|
||||
}
|
@ -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);
|
@ -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(
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
59
idachook/ffb.c
Normal 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
7
idachook/ffb.h
Normal 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);
|
@ -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),
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -30,5 +30,7 @@ shared_library(
|
||||
'zinput.h',
|
||||
'indrun.c',
|
||||
'indrun.h',
|
||||
'ffb.c',
|
||||
'ffb.h',
|
||||
],
|
||||
)
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
446
idacio/di-dev.c
446
idacio/di-dev.c
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
38
idacio/di.c
38
idacio/di.c
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
55
idacio/xi.c
55
idacio/xi.c
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
59
idzhook/ffb.c
Normal 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
7
idzhook/ffb.h
Normal 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);
|
@ -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),
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -4,4 +4,6 @@
|
||||
|
||||
#include "jvs/jvs-bus.h"
|
||||
|
||||
HRESULT idz_jvs_hook_init(void);
|
||||
|
||||
HRESULT idz_jvs_init(struct jvs_node **root);
|
||||
|
@ -32,5 +32,7 @@ shared_library(
|
||||
'jvs.h',
|
||||
'zinput.c',
|
||||
'zinput.h',
|
||||
'ffb.c',
|
||||
'ffb.h',
|
||||
],
|
||||
)
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
446
idzio/di-dev.c
446
idzio/di-dev.c
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
29
idzio/di.c
29
idzio/di.c
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
116
idzio/idzio.h
116
idzio/idzio.h
@ -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);
|
||||
|
43
idzio/xi.c
43
idzio/xi.c
@ -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
130
kemonohook/config.c
Normal 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
36
kemonohook/config.h
Normal 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
138
kemonohook/dllmain.c
Normal 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
65
kemonohook/hooks.c
Normal 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
5
kemonohook/hooks.h
Normal 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
139
kemonohook/jvs.c
Normal 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
7
kemonohook/jvs.h
Normal 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
119
kemonohook/kemono-dll.c
Normal 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
29
kemonohook/kemono-dll.h
Normal 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
83
kemonohook/kemonohook.def
Normal 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
34
kemonohook/meson.build
Normal 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
28
kemonoio/config.c
Normal 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
25
kemonoio/config.h
Normal 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
109
kemonoio/kemonoio.c
Normal 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
81
kemonoio/kemonoio.h
Normal 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
13
kemonoio/meson.build
Normal 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',
|
||||
],
|
||||
)
|
@ -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 */
|
||||
|
||||
|
@ -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')
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
59
swdchook/ffb.c
Normal 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
7
swdchook/ffb.h
Normal 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
Reference in New Issue
Block a user