Bemanitools v5.26 release
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
#ifndef AC_IO_AC_IO_H
|
||||
#define AC_IO_AC_IO_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "acio/icca.h"
|
||||
#include "acio/kfca.h"
|
||||
|
||||
#define AC_IO_SOF 0xAA
|
||||
#define AC_IO_ESCAPE 0xFF
|
||||
#define AC_IO_RESPONSE_FLAG 0x80
|
||||
#define AC_IO_BROADCAST 0x70
|
||||
|
||||
#define ac_io_u16(x) _byteswap_ushort(x)
|
||||
#define ac_io_u32(x) _byteswap_ulong(x)
|
||||
|
||||
enum ac_io_cmd {
|
||||
AC_IO_CMD_ASSIGN_ADDRS = 0x0001,
|
||||
AC_IO_CMD_GET_VERSION = 0x0002,
|
||||
AC_IO_CMD_START_UP = 0x0003,
|
||||
AC_IO_CMD_KEEPALIVE = 0x0080,
|
||||
/* Yet unknown command encountered first on jubeat (1) */
|
||||
AC_IO_CMD_UNKN_00FF = 0x00FF,
|
||||
AC_IO_CMD_CLEAR = 0x0100,
|
||||
};
|
||||
|
||||
enum ac_io_node_type {
|
||||
AC_IO_NODE_TYPE_H44B = 0x04010000,
|
||||
AC_IO_NODE_TYPE_ICCA = 0x03000000,
|
||||
/* same as ICCA */
|
||||
AC_IO_NODE_TYPE_ICCB = 0x03000000,
|
||||
AC_IO_NODE_TYPE_LED_STRIP = 0x04020000,
|
||||
AC_IO_NODE_TYPE_LED_SPIKE = 0x05010000,
|
||||
AC_IO_NODE_TYPE_KFCA = 0x09060000,
|
||||
AC_IO_NODE_TYPE_BI2A = 0x0d060000,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct ac_io_version {
|
||||
/* Names taken from some debug text in libacio.dll */
|
||||
uint32_t type;
|
||||
uint8_t flag;
|
||||
uint8_t major;
|
||||
uint8_t minor;
|
||||
uint8_t revision;
|
||||
char product_code[4];
|
||||
char date[16];
|
||||
char time[16];
|
||||
};
|
||||
|
||||
struct ac_io_message {
|
||||
uint8_t addr; /* High bit: clear = req, set = resp */
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint16_t code;
|
||||
uint8_t seq_no;
|
||||
uint8_t nbytes;
|
||||
|
||||
union {
|
||||
uint8_t raw[0xFF];
|
||||
uint8_t count;
|
||||
uint8_t status;
|
||||
struct ac_io_version version;
|
||||
|
||||
struct ac_io_icca_misc icca_misc;
|
||||
struct ac_io_icca_state icca_state;
|
||||
|
||||
struct ac_io_kfca_poll_in kfca_poll_in;
|
||||
struct ac_io_kfca_poll_out kfca_poll_out;
|
||||
};
|
||||
} cmd;
|
||||
|
||||
struct {
|
||||
uint8_t nbytes;
|
||||
uint8_t raw[0xFF]; /* 0xFFucked if I know */
|
||||
} bcast;
|
||||
};
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef ACIO_H44B_H
|
||||
#define ACIO_H44B_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum ac_io_h44b_cmd {
|
||||
AC_IO_H44B_CMD_SET_OUTPUTS = 0x0122,
|
||||
};
|
||||
|
||||
struct ac_io_h44b_output {
|
||||
uint8_t front_rgb[3];
|
||||
uint8_t top_rgb[3];
|
||||
uint8_t left_rgb[3];
|
||||
uint8_t right_rgb[3];
|
||||
uint8_t title_rgb[3];
|
||||
uint8_t woofer_rgb[3];
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,68 @@
|
||||
#ifndef AC_IO_ICCA_H
|
||||
#define AC_IO_ICCA_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum ac_io_icca_cmd {
|
||||
/* Yet unknown command encountered first on jubeat (1) */
|
||||
AC_IO_ICCA_CMD_UNKN_0120 = 0x0120,
|
||||
AC_IO_ICCA_CMD_QUEUE_LOOP_START = 0x0130,
|
||||
AC_IO_ICCA_CMD_ENGAGE = 0x0131,
|
||||
AC_IO_ICCA_CMD_POLL = 0x0134,
|
||||
AC_IO_ICCA_CMD_SET_SLOT_STATE = 0x0135,
|
||||
AC_IO_ICCA_CMD_BEGIN_KEYPAD = 0x013A,
|
||||
AC_IO_ICCA_CMD_POLL_FELICA = 0x0161,
|
||||
};
|
||||
|
||||
enum ac_io_icca_slot_state {
|
||||
AC_IO_ICCA_SLOT_STATE_OPEN = 0x11,
|
||||
AC_IO_ICCA_SLOT_STATE_EJECT = 0x12,
|
||||
AC_IO_ICCA_SLOT_STATE_CLOSE = 0,
|
||||
};
|
||||
|
||||
enum ac_io_icca_sensor_state {
|
||||
/* Card eject event fired once after slot state is set to eject the card */
|
||||
AC_IO_ICCA_SENSOR_STATE_CARD_EJECTED = 0x50,
|
||||
AC_IO_ICCA_SENSOR_MASK_FRONT_ON = (1 << 4),
|
||||
AC_IO_ICCA_SENSOR_MASK_BACK_ON = (1 << 5)
|
||||
};
|
||||
|
||||
enum ac_io_icca_keypad_mask {
|
||||
AC_IO_ICCA_KEYPAD_MASK_EMPTY = (1 << 0),
|
||||
AC_IO_ICCA_KEYPAD_MASK_3 = (1 << 1),
|
||||
AC_IO_ICCA_KEYPAD_MASK_6 = (1 << 2),
|
||||
AC_IO_ICCA_KEYPAD_MASK_9 = (1 << 3),
|
||||
|
||||
AC_IO_ICCA_KEYPAD_MASK_0 = (1 << 8),
|
||||
AC_IO_ICCA_KEYPAD_MASK_1 = (1 << 9),
|
||||
AC_IO_ICCA_KEYPAD_MASK_4 = (1 << 10),
|
||||
AC_IO_ICCA_KEYPAD_MASK_7 = (1 << 11),
|
||||
|
||||
AC_IO_ICCA_KEYPAD_MASK_00 = (1 << 12),
|
||||
AC_IO_ICCA_KEYPAD_MASK_2 = (1 << 13),
|
||||
AC_IO_ICCA_KEYPAD_MASK_5 = (1 << 14),
|
||||
AC_IO_ICCA_KEYPAD_MASK_8 = (1 << 15),
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct ac_io_icca_misc {
|
||||
uint8_t unknown;
|
||||
uint8_t subcmd;
|
||||
};
|
||||
|
||||
struct ac_io_icca_state {
|
||||
/* Similar to the struct returned by libacio, but not quite the same */
|
||||
|
||||
uint8_t status_code;
|
||||
uint8_t sensor_state;
|
||||
uint8_t uid[8];
|
||||
uint8_t card_type;
|
||||
uint8_t keypad_started;
|
||||
uint8_t key_events[2];
|
||||
uint16_t key_state;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef AC_IO_ICCB_H
|
||||
#define AC_IO_ICCB_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum ac_io_iccb_cmd {
|
||||
/* found on jubeat prop, sent after acio init req, maybe fw update? */
|
||||
AC_IO_ICCB_CMD_UNK_0100 = 0x0100,
|
||||
/* found on jubeat prop, sent right after queue loop start */
|
||||
AC_IO_ICCB_CMD_UNK_0116 = 0x0116,
|
||||
/* found on jubeat prop, sent after 0100 req */
|
||||
AC_IO_ICCB_CMD_UNK_0120 = 0x0120,
|
||||
AC_IO_ICCB_CMD_QUEUE_LOOP_START = 0x0130,
|
||||
AC_IO_ICCB_CMD_POLL = 0x0134,
|
||||
AC_IO_ICCB_CMD_UNK_135 = 0x0135,
|
||||
AC_IO_ICCB_CMD_SLEEP = 0x013A,
|
||||
AC_IO_ICCB_CMD_READ_CARD = 0x0161
|
||||
};
|
||||
|
||||
enum ac_io_iccb_sensor_state {
|
||||
AC_IO_ICCB_SENSOR_CARD = 0x02,
|
||||
AC_IO_ICCB_SENSOR_NO_CARD = 0x04
|
||||
};
|
||||
|
||||
enum ac_io_iccb_card_type {
|
||||
AC_IO_ICCB_CARD_TYPE_ISO15696 = 0x0,
|
||||
AC_IO_ICCB_CARD_TYPE_FELICA = 0x1,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct ac_io_iccb_misc {
|
||||
uint8_t unknown;
|
||||
uint8_t subcmd;
|
||||
};
|
||||
|
||||
struct ac_io_iccb_state {
|
||||
uint8_t sensor_state;
|
||||
uint8_t card_type;
|
||||
uint8_t uid[8];
|
||||
uint8_t unk2;
|
||||
uint8_t unk3;
|
||||
uint8_t unk4[4];
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef AC_IO_KFCA_H
|
||||
#define AC_IO_KFCA_H
|
||||
|
||||
#define AC_IO_CMD_KFCA_POLL 0x0113
|
||||
#define AC_IO_CMD_KFCA_UNK_0120 0x0120
|
||||
#define AC_IO_CMD_KFCA_UNK_0128 0x0128
|
||||
|
||||
#define AC_IO_KFCA_IN_GPIO_SYS_COIN 0x04
|
||||
/* ... AC_IO_KFCA_IN_GPIO_SYS_COIN2 0x08 (maybe?) */
|
||||
#define AC_IO_KFCA_IN_GPIO_SYS_TEST 0x10
|
||||
#define AC_IO_KFCA_IN_GPIO_SYS_SERVICE 0x20
|
||||
|
||||
#define AC_IO_KFCA_IN_GPIO_0_C 0x0001
|
||||
#define AC_IO_KFCA_IN_GPIO_0_B 0x0002
|
||||
#define AC_IO_KFCA_IN_GPIO_0_A 0x0004
|
||||
#define AC_IO_KFCA_IN_GPIO_0_START 0x0008
|
||||
#define AC_IO_KFCA_IN_GPIO_0_HEADPHONE 0x0020
|
||||
|
||||
#define AC_IO_KFCA_IN_GPIO_1_FX_R 0x0008
|
||||
#define AC_IO_KFCA_IN_GPIO_1_FX_L 0x0010
|
||||
#define AC_IO_KFCA_IN_GPIO_1_D 0x0020
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct ac_io_kfca_poll_in {
|
||||
/* ADC data is 10 bits. Low 6 bits of ADCs 1 through 3 are zero, low 6 bits
|
||||
of ADC 0 is the system GPIOs (test, service, coin). */
|
||||
|
||||
union {
|
||||
uint16_t gpio_sys;
|
||||
|
||||
struct {
|
||||
uint16_t adc[4];
|
||||
uint16_t gpio[4];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
struct ac_io_kfca_poll_out {
|
||||
uint32_t gpio;
|
||||
uint8_t pwm[18];
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
libs += aciodrv
|
||||
|
||||
libs_aciodrv := \
|
||||
|
||||
src_aciodrv := \
|
||||
device.c \
|
||||
icca.c \
|
||||
port.c \
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
#define LOG_MODULE "aciodrv-device"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
|
||||
#include "aciodrv/port.h"
|
||||
|
||||
#include "util/hex.h"
|
||||
#include "util/log.h"
|
||||
|
||||
/* Enable to dump all data to the logger */
|
||||
//#define AC_IO_MSG_LOG
|
||||
|
||||
static uint8_t aciodrv_device_msg_counter = 1;
|
||||
static uint8_t aciodrv_device_node_count;
|
||||
static char aviodrv_device_node_products[16][4];
|
||||
|
||||
static bool aciodrv_device_init(void)
|
||||
{
|
||||
uint8_t init_seq[1] = {AC_IO_SOF};
|
||||
|
||||
/* init/reset the device by sending 0xAA until 0xAA is returned */
|
||||
int read = 0;
|
||||
do {
|
||||
if (aciodrv_port_write(init_seq, sizeof(init_seq)) <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
read = aciodrv_port_read(init_seq, sizeof(init_seq));
|
||||
} while (read == 0);
|
||||
|
||||
if (read > 0) {
|
||||
/* empty buffer by reading all data */
|
||||
while (read > 0) {
|
||||
read = aciodrv_port_read(init_seq, sizeof(init_seq));
|
||||
}
|
||||
|
||||
return read == 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
static void aciodrv_device_log_buffer(const char* msg, const uint8_t* buffer,
|
||||
int length)
|
||||
{
|
||||
char str[4096];
|
||||
|
||||
hex_encode_uc((const void*) buffer, length, str, sizeof(str));
|
||||
log_misc("%s, length %d: %s", msg, length, str);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool aciodrv_device_send(const uint8_t* buffer, int length)
|
||||
{
|
||||
uint8_t send_buf[512];
|
||||
int send_buf_pos = 0;
|
||||
uint8_t checksum = 0;
|
||||
|
||||
if (length > sizeof(send_buf)) {
|
||||
log_warning("Send buffer overflow");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
aciodrv_device_log_buffer("Send (1)", buffer, length);
|
||||
#endif
|
||||
|
||||
send_buf[send_buf_pos++] = AC_IO_SOF;
|
||||
|
||||
/* TODO overrun checks */
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (buffer[i] == AC_IO_SOF || buffer[i] == AC_IO_ESCAPE) {
|
||||
send_buf[send_buf_pos++] = AC_IO_ESCAPE;
|
||||
send_buf[send_buf_pos++] = ~buffer[i];
|
||||
} else {
|
||||
send_buf[send_buf_pos++] = buffer[i];
|
||||
}
|
||||
|
||||
checksum += buffer[i];
|
||||
}
|
||||
|
||||
/* we have to escape the checksum as well! */
|
||||
if (checksum == AC_IO_SOF || checksum == AC_IO_ESCAPE) {
|
||||
send_buf[send_buf_pos++] = AC_IO_ESCAPE;
|
||||
send_buf[send_buf_pos++] = ~checksum;
|
||||
} else {
|
||||
send_buf[send_buf_pos++] = checksum;
|
||||
}
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
aciodrv_device_log_buffer("Send (2)", send_buf, send_buf_pos);
|
||||
#endif
|
||||
|
||||
if (aciodrv_port_write(send_buf, send_buf_pos) != send_buf_pos) {
|
||||
log_warning("Sending data with length %d failed", send_buf_pos);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int aciodrv_device_receive(uint8_t* buffer, int size)
|
||||
{
|
||||
uint8_t recv_buf[512];
|
||||
int recv_size = 0;
|
||||
int read = 0;
|
||||
uint8_t checksum = 0;
|
||||
int result_size = 0;
|
||||
|
||||
/* reading a byte stream, we are getting a varying amount
|
||||
of 0xAAs before we get a valid message. */
|
||||
recv_buf[0] = AC_IO_SOF;
|
||||
do {
|
||||
read = aciodrv_port_read(recv_buf, 1);
|
||||
} while (recv_buf[0] == AC_IO_SOF);
|
||||
|
||||
if (read > 0) {
|
||||
size += 1;
|
||||
|
||||
/* recv_buf[0] is already the first byte of the message.
|
||||
now read until nothing's left */
|
||||
recv_size++;
|
||||
size--;
|
||||
|
||||
/* important: we have to know how much data we expect
|
||||
and have to read until we reach the requested amount.
|
||||
Because this can be interrupted by 0 reads and we
|
||||
need to handle escaping (which relies on an up to
|
||||
date recv_buf[recv_size]) we loop until we get a
|
||||
non-zero read. */
|
||||
while (size > 0) {
|
||||
do {
|
||||
read = aciodrv_port_read(recv_buf + recv_size, 1);
|
||||
} while (read == 0);
|
||||
|
||||
if (read < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* check for escape byte. these don't count towards the
|
||||
size we expect! */
|
||||
if (recv_buf[recv_size] == AC_IO_ESCAPE)
|
||||
{
|
||||
/* next byte is our real data
|
||||
overwrite escape byte */
|
||||
do {
|
||||
read = aciodrv_port_read(recv_buf + recv_size, 1);
|
||||
} while (read == 0);
|
||||
|
||||
if (read < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
recv_buf[recv_size] = ~recv_buf[recv_size];
|
||||
}
|
||||
|
||||
recv_size += read;
|
||||
size -= read;
|
||||
}
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
aciodrv_device_log_buffer("Recv (1)", recv_buf, recv_size);
|
||||
#endif
|
||||
|
||||
/* recv_size - 1: omit checksum for checksum calc */
|
||||
for (int i = 0; i < recv_size - 1; i++) {
|
||||
checksum += recv_buf[i];
|
||||
buffer[i] = recv_buf[i];
|
||||
}
|
||||
|
||||
result_size = recv_size - 1;
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
aciodrv_device_log_buffer("Recv (2)", buffer, result_size);
|
||||
#endif
|
||||
|
||||
if (checksum != recv_buf[recv_size - 1]) {
|
||||
log_warning("Invalid message checksum: %02X != %02X",
|
||||
checksum, recv_buf[recv_size - 1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result_size;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static uint8_t aciodrv_device_enum_nodes(void)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
msg.addr = 0x00;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_CMD_ASSIGN_ADDRS);
|
||||
msg.cmd.nbytes = 1;
|
||||
msg.cmd.count = 0;
|
||||
|
||||
if (!aciodrv_send_and_recv(&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
log_warning("Enumerating nodes failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_info("Enumerated %d nodes", msg.cmd.count);
|
||||
|
||||
return msg.cmd.count;
|
||||
}
|
||||
|
||||
static bool aciodrv_device_get_version(uint8_t node_id, char product[4])
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
msg.addr = node_id;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_CMD_GET_VERSION);
|
||||
msg.cmd.nbytes = 0;
|
||||
|
||||
if ( !aciodrv_send_and_recv(&msg, offsetof(struct ac_io_message, cmd.raw) +
|
||||
sizeof(struct ac_io_version))) {
|
||||
log_warning("Get version of node %d failed", node_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("Node %d: type %d, flag %d, version %d.%d.%d, product %c%c%c%c, "
|
||||
"build date: %s %s",
|
||||
node_id,
|
||||
msg.cmd.version.type,
|
||||
msg.cmd.version.flag,
|
||||
msg.cmd.version.major,
|
||||
msg.cmd.version.minor,
|
||||
msg.cmd.version.revision,
|
||||
msg.cmd.version.product_code[0],
|
||||
msg.cmd.version.product_code[1],
|
||||
msg.cmd.version.product_code[2],
|
||||
msg.cmd.version.product_code[3],
|
||||
msg.cmd.version.date,
|
||||
msg.cmd.version.time);
|
||||
|
||||
memcpy(product, msg.cmd.version.product_code, 4);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool aciodrv_device_start_node(uint8_t node_id)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
msg.addr = node_id;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_CMD_START_UP);
|
||||
msg.cmd.nbytes = 0;
|
||||
|
||||
if (!aciodrv_send_and_recv(&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
log_warning("Starting node %d failed", node_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("Started node %d, status: %d", node_id, msg.cmd.status);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_device_open(const char* port, int baud)
|
||||
{
|
||||
if (!aciodrv_port_open(port, baud)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aciodrv_device_init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aciodrv_device_node_count = aciodrv_device_enum_nodes();
|
||||
if (aciodrv_device_node_count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < aciodrv_device_node_count; i++) {
|
||||
if (!aciodrv_device_get_version(i + 1, aviodrv_device_node_products[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < aciodrv_device_node_count; i++) {
|
||||
if (!aciodrv_device_start_node(i + 1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t aciodrv_device_get_node_count(void)
|
||||
{
|
||||
return aciodrv_device_node_count;
|
||||
}
|
||||
|
||||
bool aciodrv_device_get_node_product_ident(uint8_t node_id, char product[4])
|
||||
{
|
||||
if (aciodrv_device_node_count == 0 || node_id > aciodrv_device_node_count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(product, aviodrv_device_node_products[node_id], 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_send_and_recv(struct ac_io_message* msg, int resp_size)
|
||||
{
|
||||
msg->cmd.seq_no = aciodrv_device_msg_counter++;
|
||||
|
||||
if (aciodrv_device_send((uint8_t*) msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + msg->cmd.nbytes) <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t req_code = msg->cmd.code;
|
||||
|
||||
if (aciodrv_device_receive((uint8_t*) msg,
|
||||
resp_size) <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req_code != msg->cmd.code) {
|
||||
log_warning("Received invalid response %04X for request %04X",
|
||||
msg->cmd.code, req_code);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void aciodrv_device_close(void)
|
||||
{
|
||||
aciodrv_port_close();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef ACIODRV_DEVICE_H
|
||||
#define ACIODRV_DEVICE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "acio/acio.h"
|
||||
|
||||
/**
|
||||
* Open an ACIO device connected to a serial port.
|
||||
*
|
||||
* @param port Port the device is connected to (e.g. "COM1")
|
||||
* @param baud Baud rate for communication (e.g. 57600 for ICCA)
|
||||
* @return True if opening the port and resetting the device was successful,
|
||||
* false on error.
|
||||
*/
|
||||
bool aciodrv_device_open(const char* port, int baud);
|
||||
|
||||
/**
|
||||
* Get the node count on the opened device.
|
||||
*
|
||||
* @return Total num of nodes enumerated on the ACIO device.
|
||||
*/
|
||||
uint8_t aciodrv_device_get_node_count(void);
|
||||
|
||||
/**
|
||||
* Get the product identifier of an enumerated node.
|
||||
*
|
||||
* @param node_id Id of the node. Needs to be in range of the total node count.
|
||||
* @param product Buffer to return the product id to.
|
||||
* @return True on success, false on error. If True the variable product contains
|
||||
* the identifier of the queried node.
|
||||
*/
|
||||
bool aciodrv_device_get_node_product_ident(uint8_t node_id, char product[4]);
|
||||
|
||||
/**
|
||||
* Send a message to the ACIO bus and receive an answer.
|
||||
* Use this to implement the protocol for each type of device that can be
|
||||
* part of the bus.
|
||||
*
|
||||
* @param msg Msg to send to the bus. Make sure that the buffer backing
|
||||
* this message is big enough to receive the response as well.
|
||||
* @param resp_size Size of the expecting response.
|
||||
* @return True on success, false on error.
|
||||
*/
|
||||
bool aciodrv_send_and_recv(struct ac_io_message* msg, int resp_size);
|
||||
|
||||
/**
|
||||
* Close the previously opened ACIO device.
|
||||
*/
|
||||
void aciodrv_device_close(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,107 @@
|
||||
#define LOG_MODULE "aciodrv-icca"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static bool aciodrv_icca_queue_loop_start(uint8_t node_id)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
msg.addr = node_id;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_ICCA_CMD_QUEUE_LOOP_START);
|
||||
msg.cmd.nbytes = 1;
|
||||
msg.cmd.status = 0;
|
||||
|
||||
if (!aciodrv_send_and_recv(&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
log_warning("Starting queue loop failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("Started queue loop of node %d, status: %d",
|
||||
node_id, msg.cmd.status);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_icca_init(uint8_t node_id)
|
||||
{
|
||||
if (!aciodrv_icca_queue_loop_start(node_id + 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_icca_set_state(uint8_t node_id, int slot_state,
|
||||
struct ac_io_icca_state* state)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_ICCA_CMD_SET_SLOT_STATE);
|
||||
msg.cmd.nbytes = 2;
|
||||
/* buffer size of data we expect */
|
||||
msg.cmd.raw[0] = sizeof(struct ac_io_icca_state);
|
||||
msg.cmd.raw[1] = slot_state;
|
||||
|
||||
if ( !aciodrv_send_and_recv(&msg, offsetof(struct ac_io_message, cmd.raw) +
|
||||
msg.cmd.raw[0])) {
|
||||
log_warning("Setting state of node %d failed", node_id + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state != NULL) {
|
||||
memcpy(state, msg.cmd.raw, sizeof(struct ac_io_icca_state));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_icca_get_state(uint8_t node_id, struct ac_io_icca_state* state)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_ICCA_CMD_POLL);
|
||||
msg.cmd.nbytes = 1;
|
||||
/* buffer size of data we expect */
|
||||
msg.cmd.count = sizeof(struct ac_io_icca_state);
|
||||
|
||||
if ( !aciodrv_send_and_recv(&msg, offsetof(struct ac_io_message, cmd.raw) +
|
||||
msg.cmd.count)) {
|
||||
log_warning("Getting state of node %d failed", node_id + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state != NULL) {
|
||||
memcpy(state, msg.cmd.raw, sizeof(struct ac_io_icca_state));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_icca_read_card(uint8_t node_id, struct ac_io_icca_state* state)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_ICCA_CMD_ENGAGE);
|
||||
msg.cmd.nbytes = 1;
|
||||
/* buffer size of data we expect */
|
||||
msg.cmd.count = sizeof(struct ac_io_icca_state);
|
||||
|
||||
if ( !aciodrv_send_and_recv(&msg, offsetof(struct ac_io_message, cmd.raw) +
|
||||
msg.cmd.count)) {
|
||||
log_warning("Reading card of node %d failed", node_id + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state != NULL) {
|
||||
memcpy(state, msg.cmd.raw, sizeof(struct ac_io_icca_state));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef ACIODRV_ICCA_H
|
||||
#define ACIODRV_ICCA_H
|
||||
|
||||
#include "acio/icca.h"
|
||||
|
||||
/**
|
||||
* Initialize an ICCA node.
|
||||
*
|
||||
* @param node_id Id of the node to initialize (0 based).
|
||||
* @return True if successful, false on error.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_icca_init(uint8_t node_id);
|
||||
|
||||
/**
|
||||
* Set the state of on ICCA node.
|
||||
*
|
||||
* @param node_id Id of the node to set the state for (0 based).
|
||||
* @param slot_state State of the slot (refer to corresponding enum).
|
||||
* @param state Pointer to a state struct to return the current state to
|
||||
* (optional, NULL for none).
|
||||
* @return True on success, false on error.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_icca_set_state(uint8_t node_id, int slot_state, struct ac_io_icca_state* state);
|
||||
|
||||
/**
|
||||
* Get the current state of an ICCA node.
|
||||
*
|
||||
* @param node_id Id of the node to query (0 based).
|
||||
* @param state Pointer to a state struct to return the current state to
|
||||
* (optional, NULL for none).
|
||||
* @return True on success, false on error.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_icca_get_state(uint8_t node_id, struct ac_io_icca_state* state);
|
||||
|
||||
/**
|
||||
* Trigger a card read action on the ICCA reader. Make sure to call this
|
||||
* when you want to read the card. Just polling the state is not sufficient
|
||||
* to get the most recent card data. Make sure to re-get the state after
|
||||
* a read call. The state returned here might not be up to date for some reason.
|
||||
*
|
||||
* @param node_id Id of the node to query (0 based).
|
||||
* @param state Pointer to a state struct to return the current state to
|
||||
* (optional, NULL for none).
|
||||
* @return True on success, false on error.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_icca_read_card(uint8_t node_id, struct ac_io_icca_state* state);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,157 @@
|
||||
#define LOG_MODULE "aciodrv-port"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static HANDLE aciodrv_port_fd;
|
||||
|
||||
bool aciodrv_port_open(const char* port, int baud)
|
||||
{
|
||||
COMMTIMEOUTS ct;
|
||||
DCB dcb;
|
||||
|
||||
log_info("Opening ACIO on %s at %d baud", port, baud);
|
||||
|
||||
aciodrv_port_fd = CreateFile(port, GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (aciodrv_port_fd == INVALID_HANDLE_VALUE) {
|
||||
log_warning("Failed to open %s", port);
|
||||
|
||||
goto early_fail;
|
||||
}
|
||||
|
||||
if (!SetCommMask(aciodrv_port_fd, EV_RXCHAR)) {
|
||||
log_warning("SetCommMask failed");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!SetupComm(aciodrv_port_fd, 0x1000, 0x1000)) {
|
||||
log_warning("SetupComm failed");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!PurgeComm(aciodrv_port_fd,
|
||||
PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR)) {
|
||||
|
||||
log_warning("PurgeComm failed");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ct.ReadTotalTimeoutConstant = 0;
|
||||
ct.WriteTotalTimeoutConstant = 0;
|
||||
ct.ReadIntervalTimeout = -1;
|
||||
ct.ReadTotalTimeoutMultiplier = 0;
|
||||
ct.WriteTotalTimeoutMultiplier = 0;
|
||||
|
||||
if (!SetCommTimeouts(aciodrv_port_fd, &ct)) {
|
||||
log_warning("SetCommTimeouts failed");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dcb.DCBlength = sizeof(dcb);
|
||||
|
||||
if (!GetCommState(aciodrv_port_fd, &dcb)) {
|
||||
log_warning("GetCommState failed");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dcb.BaudRate = baud;
|
||||
dcb.fBinary = TRUE;
|
||||
dcb.fParity = FALSE;
|
||||
dcb.fDtrControl = DTR_CONTROL_ENABLE;
|
||||
dcb.fDsrSensitivity = FALSE;
|
||||
dcb.fOutX = FALSE;
|
||||
dcb.fInX = FALSE;
|
||||
dcb.fErrorChar = FALSE;
|
||||
dcb.fNull = FALSE;
|
||||
dcb.fRtsControl = RTS_CONTROL_ENABLE;
|
||||
dcb.fAbortOnError = FALSE;
|
||||
dcb.ByteSize = 8;
|
||||
dcb.Parity = NOPARITY;
|
||||
dcb.StopBits = ONESTOPBIT;
|
||||
dcb.XonChar = 17;
|
||||
dcb.XoffChar = 19;
|
||||
dcb.XonLim = 100;
|
||||
dcb.XoffLim = 100;
|
||||
|
||||
if (!SetCommState(aciodrv_port_fd, &dcb)) {
|
||||
log_warning("SetCommState failed");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!EscapeCommFunction(aciodrv_port_fd, SETDTR)) {
|
||||
log_warning("SETDTR failed: err = %lu", GetLastError());
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
log_info("Opened ACIO device on %s", port);
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
CloseHandle(aciodrv_port_fd);
|
||||
|
||||
early_fail:
|
||||
aciodrv_port_fd = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
int aciodrv_port_read(void *bytes, int nbytes)
|
||||
{
|
||||
DWORD nread;
|
||||
|
||||
if (aciodrv_port_fd == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ClearCommError(aciodrv_port_fd, NULL, NULL)) {
|
||||
log_warning("ClearCommError failed");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ReadFile(aciodrv_port_fd, bytes, nbytes, &nread, NULL)) {
|
||||
log_warning("ReadFile failed: err = %lu", GetLastError());
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
|
||||
int aciodrv_port_write(const void *bytes, int nbytes)
|
||||
{
|
||||
DWORD nwrit;
|
||||
|
||||
if (aciodrv_port_fd == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!WriteFile(aciodrv_port_fd, bytes, nbytes, &nwrit, NULL)) {
|
||||
log_warning("WriteFile failed: err = %lu", GetLastError());
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return nwrit;
|
||||
}
|
||||
|
||||
void aciodrv_port_close(void)
|
||||
{
|
||||
if (aciodrv_port_fd != NULL) {
|
||||
CloseHandle(aciodrv_port_fd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef ACIODRV_PORT_H
|
||||
#define ACIODRV_PORT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* Open a serial port for communication with a ACIO device.
|
||||
*
|
||||
* @param port Port the device is connected to (e.g. "COM1")
|
||||
* @param baud Baud rate for communication (e.g. 57600 for ICCA)
|
||||
* @return True if opening the com port was successful, false on error.
|
||||
* @note This will open and setup the com port, only.
|
||||
*/
|
||||
bool aciodrv_port_open(const char* port, int baud);
|
||||
|
||||
/**
|
||||
* Read data from the opened com port.
|
||||
*
|
||||
* @param bytes Pointer to an allocated buffer to read the data into.
|
||||
* @param nbytes Number of bytes to read. Has to be less or equal the allocated
|
||||
* buffer size.
|
||||
* @return Number of bytes read on success or -1 on error.
|
||||
*/
|
||||
int aciodrv_port_read(void *bytes, int nbytes);
|
||||
|
||||
/**
|
||||
* Write data to the opened com port.
|
||||
*
|
||||
* @param bytes Pointer to an allocated buffer with data to write.
|
||||
* @param nbytes Number of bytes to write. Has to be equal or less the size
|
||||
* of the allocated buffer.
|
||||
* @return Number of bytes written on success or -1 on error.
|
||||
*/
|
||||
int aciodrv_port_write(const void *bytes, int nbytes);
|
||||
|
||||
/**
|
||||
* Close the previously opened com port.
|
||||
*/
|
||||
void aciodrv_port_close(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
libs += acioemu
|
||||
|
||||
src_acioemu := \
|
||||
addr.c \
|
||||
emu.c \
|
||||
h44b.c \
|
||||
hdxs.c \
|
||||
icca.c \
|
||||
iccb.c \
|
||||
pipe.c \
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "acio/acio.h"
|
||||
|
||||
#include "acioemu/addr.h"
|
||||
#include "acioemu/emu.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
void ac_io_emu_cmd_assign_addrs(
|
||||
struct ac_io_emu *emu,
|
||||
const struct ac_io_message *req,
|
||||
uint8_t node_count)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
uint16_t cmd;
|
||||
|
||||
log_assert(emu != NULL);
|
||||
log_assert(req != NULL);
|
||||
|
||||
cmd = ac_io_u16(req->cmd.code);
|
||||
|
||||
if (cmd != AC_IO_CMD_ASSIGN_ADDRS) {
|
||||
log_warning(
|
||||
"Address 0 expects address assignment cmd, got %04x",
|
||||
cmd);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&resp, 0, sizeof(resp));
|
||||
resp.addr = 0;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(resp.cmd.count);
|
||||
resp.cmd.count = node_count;
|
||||
|
||||
ac_io_emu_response_push(emu, &resp, 0);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef AC_IO_EMU_ADDR_H
|
||||
#define AC_IO_EMU_ADDR_H
|
||||
|
||||
#include "acio/acio.h"
|
||||
|
||||
#include "acioemu/emu.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void ac_io_emu_cmd_assign_addrs(
|
||||
struct ac_io_emu *emu,
|
||||
const struct ac_io_message *req,
|
||||
uint8_t node_count);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,194 @@
|
||||
#include <windows.h> /* Usermode API */
|
||||
|
||||
#include <ntdef.h> /* Kernel-mode API for ioctls */
|
||||
#include <devioctl.h>
|
||||
#include <ntddser.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "acioemu/emu.h"
|
||||
#include "acioemu/pipe.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static HRESULT ac_io_emu_open(struct ac_io_emu *emu, struct irp *irp);
|
||||
static HRESULT ac_io_emu_close(struct ac_io_emu *emu, struct irp *irp);
|
||||
static HRESULT ac_io_emu_read(struct ac_io_emu *emu, struct irp *irp);
|
||||
static HRESULT ac_io_emu_write(struct ac_io_emu *emu, struct irp *irp);
|
||||
static HRESULT ac_io_emu_ioctl(struct ac_io_emu *emu, struct irp *irp);
|
||||
|
||||
void ac_io_emu_init(struct ac_io_emu *emu, const wchar_t *filename)
|
||||
{
|
||||
log_assert(emu != NULL);
|
||||
log_assert(filename != NULL);
|
||||
|
||||
memset(emu, 0, sizeof(*emu));
|
||||
emu->fd = iohook_open_dummy_fd();
|
||||
emu->wfilename = wstr_dup(filename);
|
||||
wstr_narrow(filename, &emu->filename);
|
||||
ac_io_in_init(&emu->in);
|
||||
ac_io_out_init(&emu->out);
|
||||
}
|
||||
|
||||
void ac_io_emu_fini(struct ac_io_emu *emu)
|
||||
{
|
||||
log_assert(emu != NULL);
|
||||
|
||||
free(emu->filename);
|
||||
free(emu->wfilename);
|
||||
|
||||
if (emu->fd != NULL) {
|
||||
CloseHandle(emu->fd);
|
||||
}
|
||||
|
||||
memset(emu, 0, sizeof(*emu));
|
||||
}
|
||||
|
||||
bool ac_io_emu_match_irp(const struct ac_io_emu *emu, const struct irp *irp)
|
||||
{
|
||||
log_assert(emu != NULL);
|
||||
log_assert(irp != NULL);
|
||||
|
||||
if (irp->op == IRP_OP_OPEN) {
|
||||
return wstr_eq(emu->wfilename, irp->open_filename);
|
||||
} else {
|
||||
return irp->fd == emu->fd;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT ac_io_emu_dispatch_irp(struct ac_io_emu *emu, struct irp *irp)
|
||||
{
|
||||
log_assert(irp != NULL);
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_OPEN: return ac_io_emu_open(emu, irp);
|
||||
case IRP_OP_CLOSE: return ac_io_emu_close(emu, irp);
|
||||
case IRP_OP_READ: return ac_io_emu_read(emu, irp);
|
||||
case IRP_OP_WRITE: return ac_io_emu_write(emu, irp);
|
||||
case IRP_OP_IOCTL: return ac_io_emu_ioctl(emu, irp);
|
||||
case IRP_OP_FSYNC: return S_FALSE;
|
||||
default: return E_NOTIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT ac_io_emu_open(struct ac_io_emu *emu, struct irp *irp)
|
||||
{
|
||||
irp->fd = emu->fd;
|
||||
log_info("%s: ACIO port opened", emu->filename);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
static HRESULT ac_io_emu_close(struct ac_io_emu *emu, struct irp *irp)
|
||||
{
|
||||
log_info("%s: ACIO port closed", emu->filename);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
static HRESULT ac_io_emu_read(struct ac_io_emu *emu, struct irp *irp)
|
||||
{
|
||||
ac_io_in_drain(&emu->in, &irp->read);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
static HRESULT ac_io_emu_write(struct ac_io_emu *emu, struct irp *irp)
|
||||
{
|
||||
const struct ac_io_message *msg;
|
||||
|
||||
for (;;) {
|
||||
ac_io_out_supply(&emu->out, &irp->write);
|
||||
|
||||
if (!ac_io_out_have_message(&emu->out)) {
|
||||
break;
|
||||
}
|
||||
|
||||
msg = ac_io_out_get_message(&emu->out);
|
||||
|
||||
if (msg != NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
ac_io_in_supply(&emu->in, NULL, 0);
|
||||
ac_io_out_consume_message(&emu->out);
|
||||
}
|
||||
|
||||
return ac_io_out_have_message(&emu->out) ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
static HRESULT ac_io_emu_ioctl(struct ac_io_emu *emu, struct irp *irp)
|
||||
{
|
||||
SERIAL_STATUS *status;
|
||||
|
||||
log_assert(irp != NULL);
|
||||
|
||||
switch (irp->ioctl) {
|
||||
case IOCTL_SERIAL_GET_COMMSTATUS:
|
||||
if (irp->read.bytes == NULL) {
|
||||
log_warning("IOCTL_SERIAL_GET_COMMSTATUS: Output buffer is NULL");
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (irp->read.nbytes < sizeof(*status)) {
|
||||
log_warning("IOCTL_SERIAL_GET_COMMSTATUS: Buffer is too small");
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
status = (SERIAL_STATUS *) irp->read.bytes;
|
||||
status->Errors = 0;
|
||||
status->AmountInInQueue = ac_io_in_is_msg_pending(&emu->in);
|
||||
|
||||
irp->read.pos = sizeof(*status);
|
||||
|
||||
return S_FALSE;
|
||||
|
||||
default:
|
||||
return S_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
const struct ac_io_message *ac_io_emu_request_peek(const struct ac_io_emu *emu)
|
||||
{
|
||||
log_assert(emu != NULL);
|
||||
|
||||
return ac_io_out_get_message(&emu->out);
|
||||
}
|
||||
|
||||
void ac_io_emu_request_pop(struct ac_io_emu *emu)
|
||||
{
|
||||
log_assert(emu != NULL);
|
||||
|
||||
ac_io_out_consume_message(&emu->out);
|
||||
}
|
||||
|
||||
void ac_io_emu_response_push(
|
||||
struct ac_io_emu *emu,
|
||||
const struct ac_io_message *resp,
|
||||
uint64_t delay_us)
|
||||
{
|
||||
log_assert(emu != NULL);
|
||||
log_assert(resp != NULL);
|
||||
|
||||
ac_io_in_supply(&emu->in, resp, delay_us);
|
||||
}
|
||||
|
||||
void ac_io_emu_response_push_thunk(
|
||||
struct ac_io_emu *emu,
|
||||
ac_io_in_thunk_t thunk,
|
||||
void *ctx,
|
||||
uint64_t delay_us)
|
||||
{
|
||||
log_assert(emu != NULL);
|
||||
log_assert(thunk != NULL);
|
||||
|
||||
ac_io_in_supply_thunk(&emu->in, thunk, ctx, delay_us);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef ACIOEMU_EMU_H
|
||||
#define ACIOEMU_EMU_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "acio/acio.h"
|
||||
|
||||
#include "acioemu/pipe.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
struct ac_io_emu {
|
||||
HANDLE fd;
|
||||
wchar_t *wfilename;
|
||||
char *filename;
|
||||
struct ac_io_in in;
|
||||
struct ac_io_out out;
|
||||
};
|
||||
|
||||
void ac_io_emu_init(struct ac_io_emu *emu, const wchar_t *filename);
|
||||
void ac_io_emu_fini(struct ac_io_emu *emu);
|
||||
bool ac_io_emu_match_irp(const struct ac_io_emu *emu, const struct irp *irp);
|
||||
HRESULT ac_io_emu_dispatch_irp(struct ac_io_emu *emu, struct irp *irp);
|
||||
const struct ac_io_message *ac_io_emu_request_peek(const struct ac_io_emu *emu);
|
||||
void ac_io_emu_request_pop(struct ac_io_emu *emu);
|
||||
void ac_io_emu_response_push(
|
||||
struct ac_io_emu *emu,
|
||||
const struct ac_io_message *resp,
|
||||
uint64_t delay_ms);
|
||||
void ac_io_emu_response_push_thunk(
|
||||
struct ac_io_emu *emu,
|
||||
ac_io_in_thunk_t thunk,
|
||||
void *ctx,
|
||||
uint64_t delay_ms);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,118 @@
|
||||
#define LOG_MODULE "acioemu-h44b"
|
||||
|
||||
#include "acioemu/h44b.h"
|
||||
|
||||
#include <windows.h> /* for _BitScanForward */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "acio/h44b.h"
|
||||
|
||||
#include "acioemu/emu.h"
|
||||
|
||||
#include "bemanitools/jbio.h"
|
||||
|
||||
#include "util/hex.h"
|
||||
|
||||
static void ac_io_emu_h44b_cmd_send_version(
|
||||
struct ac_io_emu_h44b *h44b,
|
||||
const struct ac_io_message *req);
|
||||
|
||||
static void ac_io_emu_h44b_send_status(
|
||||
struct ac_io_emu_h44b *h44b,
|
||||
const struct ac_io_message *req,
|
||||
uint8_t status);
|
||||
|
||||
void ac_io_emu_h44b_init(
|
||||
struct ac_io_emu_h44b *h44b,
|
||||
struct ac_io_emu *emu,
|
||||
uint8_t unit_no)
|
||||
{
|
||||
memset(h44b, 0, sizeof(*h44b));
|
||||
h44b->emu = emu;
|
||||
h44b->unit_no = unit_no;
|
||||
}
|
||||
|
||||
void ac_io_emu_h44b_dispatch_request(
|
||||
struct ac_io_emu_h44b *h44b,
|
||||
const struct ac_io_message *req)
|
||||
{
|
||||
uint16_t cmd_code;
|
||||
|
||||
cmd_code = ac_io_u16(req->cmd.code);
|
||||
|
||||
switch (cmd_code) {
|
||||
case AC_IO_CMD_GET_VERSION:
|
||||
log_misc("AC_IO_CMD_GET_VERSION(%d)", req->addr);
|
||||
ac_io_emu_h44b_cmd_send_version(h44b, req);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_START_UP:
|
||||
log_misc("AC_IO_CMD_START_UP(%d)", req->addr);
|
||||
ac_io_emu_h44b_send_status(h44b, req, 0x00);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_H44B_CMD_SET_OUTPUTS:
|
||||
/* Not using the struct ac_io_h44b_output here */
|
||||
for (int i = 0; i < 6; i++) {
|
||||
jb_io_set_rgb_led((enum jb_io_rgb_led) i,
|
||||
req->cmd.raw[i * 3],
|
||||
req->cmd.raw[i * 3 + 1],
|
||||
req->cmd.raw[i * 3 + 2]);
|
||||
}
|
||||
|
||||
jb_io_write_outputs();
|
||||
|
||||
ac_io_emu_h44b_send_status(h44b, req, 0x00);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning("Unknown ACIO message %04x on h44b node, addr=%d",
|
||||
cmd_code, req->addr);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ac_io_emu_h44b_cmd_send_version(
|
||||
struct ac_io_emu_h44b *h44b,
|
||||
const struct ac_io_message *req)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(resp.cmd.version);
|
||||
resp.cmd.version.type = ac_io_u32(AC_IO_NODE_TYPE_H44B);
|
||||
resp.cmd.version.flag = 0x00;
|
||||
resp.cmd.version.major = 0x01;
|
||||
resp.cmd.version.minor = 0x00;
|
||||
resp.cmd.version.revision = 0x02;
|
||||
memcpy(resp.cmd.version.product_code, "H44B",
|
||||
sizeof(resp.cmd.version.product_code));
|
||||
strncpy(resp.cmd.version.date, __DATE__, sizeof(resp.cmd.version.date));
|
||||
strncpy(resp.cmd.version.time, __TIME__, sizeof(resp.cmd.version.time));
|
||||
|
||||
ac_io_emu_response_push(h44b->emu, &resp, 0);
|
||||
}
|
||||
|
||||
static void ac_io_emu_h44b_send_status(
|
||||
struct ac_io_emu_h44b *h44b,
|
||||
const struct ac_io_message *req,
|
||||
uint8_t status)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(resp.cmd.status);
|
||||
resp.cmd.status = status;
|
||||
|
||||
ac_io_emu_response_push(h44b->emu, &resp, 0);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef AC_IO_EMU_H44B_H
|
||||
#define AC_IO_EMU_H44B_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "acioemu/emu.h"
|
||||
|
||||
struct ac_io_emu_h44b {
|
||||
struct ac_io_emu *emu;
|
||||
uint8_t unit_no;
|
||||
// TODO
|
||||
};
|
||||
|
||||
void acioemu_h44b_init(void);
|
||||
|
||||
void ac_io_emu_h44b_init(
|
||||
struct ac_io_emu_h44b *h44b,
|
||||
struct ac_io_emu *emu,
|
||||
uint8_t unit_no);
|
||||
|
||||
void ac_io_emu_h44b_dispatch_request(
|
||||
struct ac_io_emu_h44b *h44b,
|
||||
const struct ac_io_message *req);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,133 @@
|
||||
#define LOG_MODULE "acioemu-hdxs"
|
||||
|
||||
#include "acio/acio.h"
|
||||
|
||||
#include "acioemu/emu.h"
|
||||
#include "acioemu/hdxs.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static void ac_io_emu_hdxs_cmd_send_version(
|
||||
struct ac_io_emu_hdxs *hdxs,
|
||||
const struct ac_io_message *req);
|
||||
|
||||
static void ac_io_emu_hdxs_send_empty(
|
||||
struct ac_io_emu_hdxs *hdxs,
|
||||
const struct ac_io_message *req);
|
||||
|
||||
static void ac_io_emu_hdxs_send_status(
|
||||
struct ac_io_emu_hdxs *hdxs,
|
||||
const struct ac_io_message *req,
|
||||
uint8_t status);
|
||||
|
||||
void ac_io_emu_hdxs_init(
|
||||
struct ac_io_emu_hdxs *hdxs,
|
||||
struct ac_io_emu *emu)
|
||||
{
|
||||
log_assert(hdxs != NULL);
|
||||
log_assert(emu != NULL);
|
||||
|
||||
hdxs->emu = emu;
|
||||
}
|
||||
|
||||
void ac_io_emu_hdxs_dispatch_request(
|
||||
struct ac_io_emu_hdxs *hdxs,
|
||||
const struct ac_io_message *req)
|
||||
{
|
||||
uint16_t cmd_code;
|
||||
|
||||
log_assert(hdxs != NULL);
|
||||
log_assert(req != NULL);
|
||||
|
||||
cmd_code = ac_io_u16(req->cmd.code);
|
||||
|
||||
switch (cmd_code) {
|
||||
case AC_IO_CMD_GET_VERSION:
|
||||
log_misc("AC_IO_CMD_GET_VERSION(%d)", req->addr);
|
||||
ac_io_emu_hdxs_cmd_send_version(hdxs, req);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_START_UP:
|
||||
log_misc("AC_IO_CMD_START_UP(%d)", req->addr);
|
||||
ac_io_emu_hdxs_send_status(hdxs, req, 0x00);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_CLEAR:
|
||||
log_misc("AC_IO_CMD_CLEAR(%d)", req->addr);
|
||||
|
||||
case 0x110:
|
||||
case 0x112:
|
||||
case 0x128:
|
||||
ac_io_emu_hdxs_send_status(hdxs, req, 0x00);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_KEEPALIVE:
|
||||
ac_io_emu_hdxs_send_empty(hdxs, req);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning(
|
||||
"Unknown ACIO message %04x on HDXS node, addr=%d",
|
||||
cmd_code,
|
||||
req->addr);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ac_io_emu_hdxs_cmd_send_version(
|
||||
struct ac_io_emu_hdxs *hdxs,
|
||||
const struct ac_io_message *req)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(resp.cmd.version);
|
||||
resp.cmd.version.type = ac_io_u32(AC_IO_NODE_TYPE_LED_STRIP);
|
||||
resp.cmd.version.flag = 0x00;
|
||||
resp.cmd.version.major = 0x01;
|
||||
resp.cmd.version.minor = 0x06;
|
||||
resp.cmd.version.revision = 0x00;
|
||||
memcpy(resp.cmd.version.product_code, "HDXS",
|
||||
sizeof(resp.cmd.version.product_code));
|
||||
strncpy(resp.cmd.version.date, __DATE__, sizeof(resp.cmd.version.date));
|
||||
strncpy(resp.cmd.version.time, __TIME__, sizeof(resp.cmd.version.time));
|
||||
|
||||
ac_io_emu_response_push(hdxs->emu, &resp, 0);
|
||||
}
|
||||
|
||||
static void ac_io_emu_hdxs_send_empty(
|
||||
struct ac_io_emu_hdxs *hdxs,
|
||||
const struct ac_io_message *req)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = 0;
|
||||
|
||||
ac_io_emu_response_push(hdxs->emu, &resp, 0);
|
||||
}
|
||||
|
||||
static void ac_io_emu_hdxs_send_status(
|
||||
struct ac_io_emu_hdxs *hdxs,
|
||||
const struct ac_io_message *req,
|
||||
uint8_t status)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(resp.cmd.status);
|
||||
resp.cmd.status = status;
|
||||
|
||||
ac_io_emu_response_push(hdxs->emu, &resp, 0);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef AC_IO_EMU_HDXS_H
|
||||
#define AC_IO_EMU_HDXS_H
|
||||
|
||||
#include "acio/acio.h"
|
||||
|
||||
#include "acioemu/emu.h"
|
||||
|
||||
struct ac_io_emu_hdxs {
|
||||
struct ac_io_emu *emu;
|
||||
// TODO ops vtbl
|
||||
};
|
||||
|
||||
void ac_io_emu_hdxs_init(
|
||||
struct ac_io_emu_hdxs *hdxs,
|
||||
struct ac_io_emu *emu);
|
||||
|
||||
void ac_io_emu_hdxs_dispatch_request(
|
||||
struct ac_io_emu_hdxs *hdxs,
|
||||
const struct ac_io_message *req);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,385 @@
|
||||
#define LOG_MODULE "acioemu-icca"
|
||||
|
||||
#include "acioemu/icca.h"
|
||||
|
||||
#include <windows.h> /* for _BitScanForward */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "acio/icca.h"
|
||||
|
||||
#include "acioemu/emu.h"
|
||||
#include "acioemu/icca.h"
|
||||
|
||||
#include "bemanitools/eamio.h"
|
||||
|
||||
#include "util/time.h"
|
||||
|
||||
enum ac_io_icca_subcmd {
|
||||
AC_IO_ICCA_SUBCMD_CARD_SLOT_CLOSE = 0x00,
|
||||
AC_IO_ICCA_SUBCMD_CARD_SLOT_OPEN = 0x11,
|
||||
AC_IO_ICCA_SUBCMD_CARD_SLOT_EJECT = 0x12,
|
||||
};
|
||||
|
||||
enum ac_io_icca_flag {
|
||||
AC_IO_ICCA_FLAG_FRONT_SENSOR = 0x10,
|
||||
AC_IO_ICCA_FLAG_REAR_SENSOR = 0x20,
|
||||
AC_IO_ICCA_FLAG_SOLENOID = 0x40
|
||||
};
|
||||
|
||||
enum ac_io_icca_status_code {
|
||||
AC_IO_ICCA_STATUS_FAULT = 0x00,
|
||||
AC_IO_ICCA_STATUS_IDLE = 0x01,
|
||||
AC_IO_ICCA_STATUS_GOT_UID = 0x02
|
||||
};
|
||||
|
||||
static void ac_io_emu_icca_cmd_send_version(
|
||||
struct ac_io_emu_icca *icca,
|
||||
const struct ac_io_message *req);
|
||||
|
||||
static void ac_io_emu_icca_send_state(
|
||||
struct ac_io_emu_icca *icca,
|
||||
const struct ac_io_message *req,
|
||||
uint64_t delay_us);
|
||||
|
||||
static void ac_io_emu_icca_send_empty(
|
||||
struct ac_io_emu_icca *icca,
|
||||
const struct ac_io_message *req);
|
||||
|
||||
static void ac_io_emu_icca_send_status(
|
||||
struct ac_io_emu_icca *icca,
|
||||
const struct ac_io_message *req,
|
||||
uint8_t status);
|
||||
|
||||
void ac_io_emu_icca_init(
|
||||
struct ac_io_emu_icca *icca,
|
||||
struct ac_io_emu *emu,
|
||||
uint8_t unit_no)
|
||||
{
|
||||
memset(icca, 0, sizeof(*icca));
|
||||
icca->emu = emu;
|
||||
icca->unit_no = unit_no;
|
||||
// queue must be started
|
||||
icca->fault = true;
|
||||
}
|
||||
|
||||
void ac_io_emu_icca_dispatch_request(
|
||||
struct ac_io_emu_icca *icca,
|
||||
const struct ac_io_message *req)
|
||||
{
|
||||
uint16_t cmd_code;
|
||||
uint64_t delay_us;
|
||||
|
||||
cmd_code = ac_io_u16(req->cmd.code);
|
||||
|
||||
switch (cmd_code) {
|
||||
case AC_IO_CMD_GET_VERSION:
|
||||
log_misc("AC_IO_CMD_GET_VERSION(%d)", req->addr);
|
||||
ac_io_emu_icca_cmd_send_version(icca, req);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_START_UP:
|
||||
log_misc("AC_IO_CMD_START_UP(%d)", req->addr);
|
||||
icca->detected_new_reader = false;
|
||||
ac_io_emu_icca_send_status(icca, req, 0x00);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_CLEAR:
|
||||
log_misc("AC_IO_ICCA_CMD_CLEAR(%d)", req->addr);
|
||||
ac_io_emu_icca_send_status(icca, req, 0x00);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_KEEPALIVE:
|
||||
ac_io_emu_icca_send_empty(icca, req);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_ICCA_CMD_QUEUE_LOOP_START:
|
||||
log_misc("AC_IO_CMD_QUEUE_LOOP_START(%d)", req->addr);
|
||||
// queue started, reset error state
|
||||
icca->fault = false;
|
||||
ac_io_emu_icca_send_status(icca, req, 0x00);
|
||||
icca->polling_started = true;
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_UNKN_00FF:
|
||||
log_misc("AC_IO_CMD_UNKN_00FF(%d)", req->addr);
|
||||
ac_io_emu_icca_send_status(icca, req, 0x00);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_ICCA_CMD_UNKN_0120:
|
||||
log_misc("AC_IO_ICCA_CMD_UNKN_0120(%d)", req->addr);
|
||||
ac_io_emu_icca_send_status(icca, req, 0x00);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_ICCA_CMD_BEGIN_KEYPAD:
|
||||
log_misc("AC_IO_ICCA_CMD_BEGIN_KEYPAD(%d)", req->addr);
|
||||
ac_io_emu_icca_send_status(icca, req, 0x00);
|
||||
icca->keypad_started = true;
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_ICCA_CMD_ENGAGE:
|
||||
ac_io_emu_icca_send_state(icca, req, 0);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_ICCA_CMD_SET_SLOT_STATE:
|
||||
{
|
||||
struct ac_io_icca_misc* misc =
|
||||
(struct ac_io_icca_misc*) &req->cmd.raw;
|
||||
uint8_t cmd;
|
||||
|
||||
switch (misc->subcmd) {
|
||||
case AC_IO_ICCA_SUBCMD_CARD_SLOT_CLOSE:
|
||||
cmd = EAM_IO_CARD_SLOT_CMD_CLOSE;
|
||||
break;
|
||||
|
||||
case AC_IO_ICCA_SUBCMD_CARD_SLOT_OPEN:
|
||||
cmd = EAM_IO_CARD_SLOT_CMD_OPEN;
|
||||
break;
|
||||
|
||||
case AC_IO_ICCA_SUBCMD_CARD_SLOT_EJECT:
|
||||
cmd = EAM_IO_CARD_SLOT_CMD_EJECT;
|
||||
icca->engaged = false;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
cmd = EAM_IO_CARD_SLOT_CMD_READ;
|
||||
break;
|
||||
|
||||
default:
|
||||
cmd = 0xFF;
|
||||
log_warning("Unhandled slot command %X, node %d",
|
||||
misc->subcmd, icca->unit_no);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd != 0xFF) {
|
||||
if (!eam_io_card_slot_cmd(icca->unit_no, cmd)) {
|
||||
log_warning("Eamio failed to handle slot cmd %d for node %d",
|
||||
cmd, icca->unit_no);
|
||||
}
|
||||
}
|
||||
|
||||
/* response with current slot state */
|
||||
ac_io_emu_icca_send_status(icca, req, misc->subcmd);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case AC_IO_ICCA_CMD_POLL:
|
||||
delay_us = time_get_elapsed_us(time_get_counter() - icca->time_counter_last_poll);
|
||||
|
||||
/* emulating delay implemented by hardware. do not delay messages that exceed a certain threshold. */
|
||||
if (delay_us > 16000) {
|
||||
delay_us = 0;
|
||||
}
|
||||
|
||||
icca->time_counter_last_poll = time_get_counter();
|
||||
ac_io_emu_icca_send_state(icca, req, delay_us);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_ICCA_CMD_POLL_FELICA:
|
||||
icca->detected_new_reader = true;
|
||||
ac_io_emu_icca_send_status(icca, req, 0x01);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning("Unknown ACIO message %04x on ICCA node, addr=%d",
|
||||
cmd_code, req->addr);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ac_io_emu_icca_cmd_send_version(
|
||||
struct ac_io_emu_icca *icca,
|
||||
const struct ac_io_message *req)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(resp.cmd.version);
|
||||
resp.cmd.version.type = ac_io_u32(AC_IO_NODE_TYPE_ICCA);
|
||||
resp.cmd.version.flag = 0x00;
|
||||
resp.cmd.version.major = 0x01;
|
||||
resp.cmd.version.minor = 0x06;
|
||||
resp.cmd.version.revision = 0x00;
|
||||
memcpy(resp.cmd.version.product_code, "ICCA",
|
||||
sizeof(resp.cmd.version.product_code));
|
||||
strncpy(resp.cmd.version.date, __DATE__, sizeof(resp.cmd.version.date));
|
||||
strncpy(resp.cmd.version.time, __TIME__, sizeof(resp.cmd.version.time));
|
||||
|
||||
ac_io_emu_response_push(icca->emu, &resp, 0);
|
||||
}
|
||||
|
||||
static void ac_io_emu_icca_send_empty(
|
||||
struct ac_io_emu_icca *icca,
|
||||
const struct ac_io_message *req)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = 0;
|
||||
|
||||
ac_io_emu_response_push(icca->emu, &resp, 0);
|
||||
}
|
||||
|
||||
static void ac_io_emu_icca_send_status(
|
||||
struct ac_io_emu_icca *icca,
|
||||
const struct ac_io_message *req,
|
||||
uint8_t status)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(resp.cmd.status);
|
||||
resp.cmd.status = status;
|
||||
|
||||
ac_io_emu_response_push(icca->emu, &resp, 0);
|
||||
}
|
||||
|
||||
static void ac_io_emu_icca_send_state(
|
||||
struct ac_io_emu_icca *icca,
|
||||
const struct ac_io_message *req,
|
||||
uint64_t delay_us)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
struct ac_io_icca_state *body;
|
||||
unsigned long bit;
|
||||
uint8_t event;
|
||||
uint16_t keypad;
|
||||
uint16_t keypad_rise;
|
||||
uint8_t sensor_state;
|
||||
bool card_full_insert;
|
||||
|
||||
if (!eam_io_poll(icca->unit_no)) {
|
||||
log_warning("Polling eamio failed");
|
||||
}
|
||||
|
||||
memset(&resp, 0, sizeof(resp));
|
||||
|
||||
keypad = eam_io_get_keypad_state(icca->unit_no);
|
||||
sensor_state = eam_io_get_sensor_state(icca->unit_no);
|
||||
|
||||
keypad_rise = keypad & (icca->last_keypad ^ keypad);
|
||||
card_full_insert = sensor_state & (1 << EAM_IO_SENSOR_FRONT) &&
|
||||
sensor_state & (1 << EAM_IO_SENSOR_BACK);
|
||||
|
||||
if (sensor_state != icca->last_sensor) {
|
||||
|
||||
if (card_full_insert) {
|
||||
|
||||
if (!eam_io_card_slot_cmd(icca->unit_no,
|
||||
EAM_IO_CARD_SLOT_CMD_READ)) {
|
||||
log_warning("EAM_IO_CARD_SLOT_CMD_READ to unit %d failed",
|
||||
icca->unit_no);
|
||||
}
|
||||
|
||||
icca->card_result = eam_io_read_card(
|
||||
icca->unit_no,
|
||||
icca->uid,
|
||||
sizeof(icca->uid));
|
||||
|
||||
// fault if sensor says to read but we got no card
|
||||
icca->fault = (icca->card_result == EAM_IO_CARD_NONE);
|
||||
} else {
|
||||
icca->fault = false;
|
||||
}
|
||||
}
|
||||
|
||||
icca->last_sensor = sensor_state;
|
||||
icca->last_keypad = keypad;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(struct ac_io_icca_state);
|
||||
|
||||
body = (struct ac_io_icca_state *) &resp.cmd.raw;
|
||||
|
||||
if (icca->fault) {
|
||||
body->status_code = AC_IO_ICCA_STATUS_FAULT;
|
||||
} else if (card_full_insert) {
|
||||
body->status_code = AC_IO_ICCA_STATUS_GOT_UID;
|
||||
} else {
|
||||
body->status_code = AC_IO_ICCA_STATUS_IDLE;
|
||||
}
|
||||
|
||||
body->sensor_state = 0;
|
||||
|
||||
if (sensor_state & (1 << EAM_IO_SENSOR_FRONT)) {
|
||||
body->sensor_state |= AC_IO_ICCA_FLAG_FRONT_SENSOR;
|
||||
}
|
||||
|
||||
if (sensor_state & (1 << EAM_IO_SENSOR_BACK)) {
|
||||
body->sensor_state |= AC_IO_ICCA_FLAG_REAR_SENSOR;
|
||||
}
|
||||
|
||||
if (icca->engaged) {
|
||||
body->sensor_state |= AC_IO_ICCA_FLAG_SOLENOID;
|
||||
}
|
||||
|
||||
memcpy(body->uid, icca->uid, sizeof(body->uid));
|
||||
body->card_type = 0;
|
||||
if (body->status_code == AC_IO_ICCA_STATUS_GOT_UID){
|
||||
if (icca->detected_new_reader){
|
||||
// sensor_state actually refers to cardtype for wavepass readers
|
||||
// EAM_IO_CARD_ISO15696 = 1 -> 0
|
||||
// EAM_IO_CARD_FELICA = 2 -> 1
|
||||
body->card_type = icca->card_result - 1;
|
||||
body->sensor_state = icca->card_result - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (keypad_rise) {
|
||||
|
||||
if (icca->key_events[0]) {
|
||||
event = (icca->key_events[0] + 0x10) & 0xF0;
|
||||
} else {
|
||||
event = 0x00;
|
||||
}
|
||||
|
||||
_BitScanForward(&bit, keypad_rise);
|
||||
event |= 0x80 | bit;
|
||||
|
||||
icca->key_events[1] = icca->key_events[0];
|
||||
icca->key_events[0] = event;
|
||||
}
|
||||
|
||||
// this doesn't seem to be an error code. If this is not set to 0x03
|
||||
// on slotted readers (only?), the game throws an unknown status error
|
||||
if (icca->keypad_started) {
|
||||
body->keypad_started = 0x03;
|
||||
} else {
|
||||
body->keypad_started = 0x00;
|
||||
}
|
||||
body->key_events[0] = icca->key_events[0];
|
||||
body->key_events[1] = icca->key_events[1];
|
||||
body->key_state = ac_io_u16(keypad);
|
||||
|
||||
// replace status code if polling hasn't started
|
||||
// this fixes SDVX IC CARD boot errors
|
||||
if (!icca->polling_started) {
|
||||
body->status_code = AC_IO_ICCA_STATUS_FAULT;
|
||||
}
|
||||
|
||||
ac_io_emu_response_push(icca->emu, &resp, delay_us);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef AC_IO_EMU_ICCA_H
|
||||
#define AC_IO_EMU_ICCA_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "acioemu/emu.h"
|
||||
|
||||
struct ac_io_emu_icca {
|
||||
struct ac_io_emu *emu;
|
||||
uint8_t unit_no;
|
||||
bool fault;
|
||||
bool engaged;
|
||||
uint8_t last_sensor;
|
||||
uint16_t last_keypad;
|
||||
uint8_t key_events[2];
|
||||
uint8_t uid[8];
|
||||
uint8_t card_result;
|
||||
bool detected_new_reader;
|
||||
bool keypad_started;
|
||||
bool polling_started;
|
||||
uint64_t time_counter_last_poll;
|
||||
};
|
||||
|
||||
void ac_io_emu_icca_init(
|
||||
struct ac_io_emu_icca *icca,
|
||||
struct ac_io_emu *emu,
|
||||
uint8_t unit_no);
|
||||
|
||||
void ac_io_emu_icca_dispatch_request(
|
||||
struct ac_io_emu_icca *icca,
|
||||
const struct ac_io_message *req);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,227 @@
|
||||
#define LOG_MODULE "acioemu-iccb"
|
||||
|
||||
#include "acioemu/iccb.h"
|
||||
|
||||
#include <windows.h> /* for _BitScanForward */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "acio/iccb.h"
|
||||
|
||||
#include "acioemu/emu.h"
|
||||
|
||||
#include "bemanitools/eamio.h"
|
||||
|
||||
static void ac_io_emu_iccb_cmd_send_version(
|
||||
struct ac_io_emu_iccb *iccb,
|
||||
const struct ac_io_message *req);
|
||||
|
||||
static void ac_io_emu_iccb_send_state(
|
||||
struct ac_io_emu_iccb *iccb,
|
||||
const struct ac_io_message *req);
|
||||
|
||||
static void ac_io_emu_iccb_send_empty(
|
||||
struct ac_io_emu_iccb *iccb,
|
||||
const struct ac_io_message *req);
|
||||
|
||||
static void ac_io_emu_iccb_send_status(
|
||||
struct ac_io_emu_iccb *iccb,
|
||||
const struct ac_io_message *req,
|
||||
uint8_t status);
|
||||
|
||||
void ac_io_emu_iccb_init(
|
||||
struct ac_io_emu_iccb *iccb,
|
||||
struct ac_io_emu *emu,
|
||||
uint8_t unit_no)
|
||||
{
|
||||
memset(iccb, 0, sizeof(*iccb));
|
||||
iccb->emu = emu;
|
||||
iccb->unit_no = unit_no;
|
||||
}
|
||||
|
||||
void ac_io_emu_iccb_dispatch_request(
|
||||
struct ac_io_emu_iccb *iccb,
|
||||
const struct ac_io_message *req)
|
||||
{
|
||||
uint16_t cmd_code;
|
||||
|
||||
cmd_code = ac_io_u16(req->cmd.code);
|
||||
|
||||
switch (cmd_code) {
|
||||
case AC_IO_CMD_GET_VERSION:
|
||||
log_misc("AC_IO_CMD_GET_VERSION(%d)", req->addr);
|
||||
ac_io_emu_iccb_cmd_send_version(iccb, req);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_START_UP:
|
||||
log_misc("AC_IO_CMD_START_UP(%d)", req->addr);
|
||||
ac_io_emu_iccb_send_status(iccb, req, 0x00);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_KEEPALIVE:
|
||||
ac_io_emu_iccb_send_empty(iccb, req);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_ICCB_CMD_QUEUE_LOOP_START:
|
||||
log_misc("AC_IO_CMD_QUEUE_LOOP_START(%d)", req->addr);
|
||||
ac_io_emu_iccb_send_status(iccb, req, 0x00);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_ICCB_CMD_UNK_0100:
|
||||
case AC_IO_ICCB_CMD_UNK_0116:
|
||||
case AC_IO_ICCB_CMD_UNK_0120:
|
||||
log_misc("AC_IO_ICCB_CMD_UNK_%04X(%d)", cmd_code, req->addr);
|
||||
ac_io_emu_iccb_send_status(iccb, req, 0x00);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_ICCB_CMD_SLEEP:
|
||||
ac_io_emu_iccb_send_status(iccb, req, 0x00);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_ICCB_CMD_UNK_135:
|
||||
/* log_misc("AC_IO_ICCB_CMD_UNK_135"); */
|
||||
ac_io_emu_iccb_send_state(iccb, req);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_ICCB_CMD_POLL:
|
||||
/* log_misc("AC_IO_ICCB_CMD_POLL"); */
|
||||
ac_io_emu_iccb_send_state(iccb, req);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_ICCB_CMD_READ_CARD:
|
||||
/* log_misc("AC_IO_ICCB_CMD_READ_CARD"); */
|
||||
ac_io_emu_iccb_send_state(iccb, req);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning("Unknown ACIO message %04x on ICCB node, addr=%d",
|
||||
cmd_code, req->addr);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ac_io_emu_iccb_cmd_send_version(
|
||||
struct ac_io_emu_iccb *iccb,
|
||||
const struct ac_io_message *req)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(resp.cmd.version);
|
||||
resp.cmd.version.type = ac_io_u32(AC_IO_NODE_TYPE_ICCB);
|
||||
resp.cmd.version.flag = 0x00;
|
||||
resp.cmd.version.major = 0x01;
|
||||
resp.cmd.version.minor = 0x05;
|
||||
resp.cmd.version.revision = 0x01;
|
||||
memcpy(resp.cmd.version.product_code, "ICCB",
|
||||
sizeof(resp.cmd.version.product_code));
|
||||
strncpy(resp.cmd.version.date, __DATE__, sizeof(resp.cmd.version.date));
|
||||
strncpy(resp.cmd.version.time, __TIME__, sizeof(resp.cmd.version.time));
|
||||
|
||||
ac_io_emu_response_push(iccb->emu, &resp, 0);
|
||||
}
|
||||
|
||||
static void ac_io_emu_iccb_send_empty(
|
||||
struct ac_io_emu_iccb *iccb,
|
||||
const struct ac_io_message *req)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = 0;
|
||||
|
||||
ac_io_emu_response_push(iccb->emu, &resp, 0);
|
||||
}
|
||||
|
||||
static void ac_io_emu_iccb_send_status(
|
||||
struct ac_io_emu_iccb *iccb,
|
||||
const struct ac_io_message *req,
|
||||
uint8_t status)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(resp.cmd.status);
|
||||
resp.cmd.status = status;
|
||||
|
||||
ac_io_emu_response_push(iccb->emu, &resp, 0);
|
||||
}
|
||||
|
||||
static void ac_io_emu_iccb_send_state(
|
||||
struct ac_io_emu_iccb *iccb,
|
||||
const struct ac_io_message *req)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
struct ac_io_iccb_state *body;
|
||||
bool sensor;
|
||||
|
||||
/* state update */
|
||||
|
||||
sensor = eam_io_get_sensor_state(iccb->unit_no);
|
||||
|
||||
if (sensor != iccb->last_sensor) {
|
||||
if (sensor) {
|
||||
iccb->card_result = eam_io_read_card(
|
||||
iccb->unit_no,
|
||||
iccb->uid,
|
||||
sizeof(iccb->uid));
|
||||
|
||||
// fault if sensor says to read but we got no card
|
||||
iccb->fault = (iccb->card_result == EAM_IO_CARD_NONE);
|
||||
} else {
|
||||
iccb->fault = false;
|
||||
}
|
||||
}
|
||||
|
||||
iccb->last_sensor = sensor;
|
||||
|
||||
if (iccb->fault) {
|
||||
memset(iccb->uid, 0, sizeof(iccb->uid));
|
||||
}
|
||||
|
||||
/* create response */
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(struct ac_io_iccb_state);
|
||||
|
||||
body = (struct ac_io_iccb_state *) &resp.cmd.raw;
|
||||
|
||||
if (sensor) {
|
||||
body->sensor_state = AC_IO_ICCB_SENSOR_CARD;
|
||||
} else {
|
||||
body->sensor_state = AC_IO_ICCB_SENSOR_NO_CARD;
|
||||
}
|
||||
|
||||
if (!iccb->fault) {
|
||||
memcpy(body->uid, iccb->uid, sizeof(body->uid));
|
||||
}
|
||||
|
||||
body->card_type = 0x30 | (iccb->card_result - 1);
|
||||
body->unk2 = 0;
|
||||
// this doesn't seem to be an error code. If this is not set to 0x03
|
||||
// on slotted readers (only?), the game throws an unknown status error
|
||||
body->unk3 = 0x03;
|
||||
memset(body->unk4, 0, sizeof(body->unk4));
|
||||
|
||||
ac_io_emu_response_push(iccb->emu, &resp, 0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef AC_IO_EMU_ICCB_H
|
||||
#define AC_IO_EMU_ICCB_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "acioemu/emu.h"
|
||||
|
||||
struct ac_io_emu_iccb {
|
||||
struct ac_io_emu *emu;
|
||||
uint8_t unit_no;
|
||||
bool fault;
|
||||
bool last_sensor;
|
||||
uint8_t uid[8];
|
||||
uint8_t card_result;
|
||||
};
|
||||
|
||||
void ac_io_emu_iccb_init(
|
||||
struct ac_io_emu_iccb *iccb,
|
||||
struct ac_io_emu *emu,
|
||||
uint8_t unit_no);
|
||||
|
||||
void ac_io_emu_iccb_dispatch_request(
|
||||
struct ac_io_emu_iccb *iccb,
|
||||
const struct ac_io_message *req);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,366 @@
|
||||
#define LOG_MODULE "acioemu-emu"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "acioemu/pipe.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/list.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/time.h"
|
||||
|
||||
static struct ac_io_in_queued *ac_io_in_queued_alloc(struct ac_io_in *in,
|
||||
uint64_t delay_us);
|
||||
static void ac_io_in_queued_populate(
|
||||
struct ac_io_in_queued *iq,
|
||||
const struct ac_io_message *msg);
|
||||
static void ac_io_in_queued_putc(struct ac_io_in_queued *iq, uint8_t b);
|
||||
|
||||
static bool ac_io_out_supply_byte(struct ac_io_out *out, uint8_t b);
|
||||
static bool ac_io_out_supply_frame_byte(struct ac_io_out *out, uint8_t b);
|
||||
static bool ac_io_out_detect_broadcast_eof(struct ac_io_out *out);
|
||||
static bool ac_io_out_detect_command_eof(struct ac_io_out *out);
|
||||
static bool ac_io_out_check_sum(struct ac_io_out *out);
|
||||
static bool ac_io_out_accept_message(struct ac_io_out *out);
|
||||
static bool ac_io_out_reject_message(struct ac_io_out *out);
|
||||
|
||||
static bool ac_io_enable_legacy_mode = false;
|
||||
|
||||
static struct ac_io_in_queued *ac_io_in_queued_alloc(struct ac_io_in *in,
|
||||
uint64_t delay_us)
|
||||
{
|
||||
struct ac_io_in_queued *iq;
|
||||
|
||||
/* TODO ordered insert by scheduled_time */
|
||||
|
||||
iq = xmalloc(sizeof(*iq));
|
||||
iq->iobuf.bytes = iq->bytes;
|
||||
iq->iobuf.nbytes = 1;
|
||||
iq->iobuf.pos = 0;
|
||||
iq->scheduled_time = time_get_counter();
|
||||
iq->delay_us = delay_us;
|
||||
iq->bytes[0] = AC_IO_SOF;
|
||||
|
||||
list_append(&in->queue, &iq->node);
|
||||
|
||||
return iq;
|
||||
}
|
||||
|
||||
static void ac_io_in_queued_populate(
|
||||
struct ac_io_in_queued *iq,
|
||||
const struct ac_io_message *msg)
|
||||
{
|
||||
uint8_t checksum;
|
||||
const uint8_t *src;
|
||||
size_t nbytes;
|
||||
size_t i;
|
||||
|
||||
if (msg->addr == AC_IO_BROADCAST) {
|
||||
nbytes = offsetof(struct ac_io_message, bcast.raw) + msg->bcast.nbytes;
|
||||
} else {
|
||||
nbytes = offsetof(struct ac_io_message, cmd.raw) + msg->cmd.nbytes;
|
||||
}
|
||||
|
||||
src = (const uint8_t *) msg;
|
||||
checksum = 0;
|
||||
|
||||
for (i = 0 ; i < nbytes ; i++) {
|
||||
ac_io_in_queued_putc(iq, src[i]);
|
||||
checksum += src[i];
|
||||
}
|
||||
|
||||
ac_io_in_queued_putc(iq, checksum);
|
||||
}
|
||||
|
||||
static void ac_io_in_queued_putc(struct ac_io_in_queued *iq, uint8_t b)
|
||||
{
|
||||
if (b == AC_IO_SOF || b == AC_IO_ESCAPE) {
|
||||
iq->bytes[iq->iobuf.nbytes++] = AC_IO_ESCAPE;
|
||||
iq->bytes[iq->iobuf.nbytes++] = ~b;
|
||||
} else {
|
||||
iq->bytes[iq->iobuf.nbytes++] = b;
|
||||
}
|
||||
}
|
||||
|
||||
void ac_io_legacy_mode(void)
|
||||
{
|
||||
log_info("Running acioemu legacy mode");
|
||||
ac_io_enable_legacy_mode = true;
|
||||
}
|
||||
|
||||
void ac_io_in_init(struct ac_io_in *in)
|
||||
{
|
||||
list_init(&in->queue);
|
||||
}
|
||||
|
||||
void ac_io_in_supply(struct ac_io_in *in, const struct ac_io_message *msg,
|
||||
uint64_t delay_us)
|
||||
{
|
||||
struct ac_io_in_queued *dest;
|
||||
|
||||
dest = ac_io_in_queued_alloc(in, delay_us);
|
||||
dest->thunk = NULL;
|
||||
|
||||
if (msg == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ac_io_in_queued_populate(dest, msg);
|
||||
}
|
||||
|
||||
void ac_io_in_supply_thunk(
|
||||
struct ac_io_in *in,
|
||||
ac_io_in_thunk_t thunk,
|
||||
void *ctx,
|
||||
uint64_t delay_us)
|
||||
{
|
||||
struct ac_io_in_queued *dest;
|
||||
|
||||
dest = ac_io_in_queued_alloc(in, delay_us);
|
||||
|
||||
dest->thunk = thunk;
|
||||
dest->thunk_ctx = ctx;
|
||||
}
|
||||
|
||||
void ac_io_in_drain(struct ac_io_in *in, struct iobuf *dest)
|
||||
{
|
||||
struct ac_io_in_queued *iq;
|
||||
struct list_node *node;
|
||||
struct ac_io_message msg;
|
||||
size_t nmoved;
|
||||
uint64_t now;
|
||||
uint64_t elapsed_us;
|
||||
|
||||
now = time_get_counter();
|
||||
|
||||
do {
|
||||
node = list_peek_head(&in->queue);
|
||||
|
||||
if (node == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
iq = containerof(node, struct ac_io_in_queued, node);
|
||||
elapsed_us = time_get_elapsed_us(
|
||||
now > iq->scheduled_time ? now - iq->scheduled_time : 0);
|
||||
|
||||
if (elapsed_us < iq->delay_us) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (iq->thunk != NULL) {
|
||||
iq->thunk(iq->thunk_ctx, &msg);
|
||||
iq->thunk = NULL;
|
||||
|
||||
ac_io_in_queued_populate(iq, &msg);
|
||||
}
|
||||
|
||||
nmoved = iobuf_move(dest, &iq->iobuf);
|
||||
|
||||
if (iq->iobuf.pos == iq->iobuf.nbytes) {
|
||||
list_pop_head(&in->queue);
|
||||
free(iq);
|
||||
}
|
||||
|
||||
/* Legacy mode for old libacio versions like DistorteD:
|
||||
spit out single responses instead of combining them into a single
|
||||
io buffer. Some old libacio versions expect separate messages and
|
||||
will error (code 0x00000002) on multiple messages in a single buffer
|
||||
because they just read the first message and drop the remaining ones.
|
||||
If using legacy mode on newer libacio versions (e.g. Copula),
|
||||
the game will error with the same error code.
|
||||
*/
|
||||
if (ac_io_enable_legacy_mode) {
|
||||
break;
|
||||
}
|
||||
} while (nmoved > 0);
|
||||
}
|
||||
|
||||
bool ac_io_in_is_msg_pending(const struct ac_io_in *in)
|
||||
{
|
||||
return list_peek_head_const(&in->queue) != NULL;
|
||||
}
|
||||
|
||||
void ac_io_out_init(struct ac_io_out *out)
|
||||
{
|
||||
out->pos = 0;
|
||||
out->in_frame = false;
|
||||
out->escape = false;
|
||||
out->have_message = false;
|
||||
}
|
||||
|
||||
void ac_io_out_supply(struct ac_io_out *out, struct const_iobuf *src)
|
||||
{
|
||||
log_assert(!ac_io_out_have_message(out));
|
||||
|
||||
while (src->pos < src->nbytes) {
|
||||
if (!ac_io_out_supply_byte(out, src->bytes[src->pos++])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool ac_io_out_supply_byte(struct ac_io_out *out, uint8_t b)
|
||||
{
|
||||
if (out->in_frame) {
|
||||
return ac_io_out_supply_frame_byte(out, b);
|
||||
} else {
|
||||
|
||||
if (b == AC_IO_SOF) {
|
||||
out->in_frame = true;
|
||||
} else if (b == AC_IO_ESCAPE) {
|
||||
log_warning("Framing error: Escape byte outside frame");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ac_io_out_supply_frame_byte(struct ac_io_out *out, uint8_t b)
|
||||
{
|
||||
if (out->escape) {
|
||||
out->escape = false;
|
||||
|
||||
if (b == AC_IO_SOF || b == AC_IO_ESCAPE) {
|
||||
log_warning("Framing error: Control byte after escape byte");
|
||||
}
|
||||
|
||||
b = ~b;
|
||||
} else if (b == AC_IO_ESCAPE) {
|
||||
out->escape = true;
|
||||
|
||||
return true;
|
||||
} else if (b == AC_IO_SOF) {
|
||||
|
||||
if (out->pos == 0) {
|
||||
/* Got autobaud/empty message */
|
||||
out->have_message = true;
|
||||
|
||||
return false;
|
||||
} else {
|
||||
log_warning("Truncated message");
|
||||
out->pos = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Payload byte */
|
||||
|
||||
out->bytes[out->pos++] = b;
|
||||
|
||||
/* Handle contextually-implied end-of-packet events */
|
||||
|
||||
if (out->pos > offsetof(struct ac_io_message, addr)) {
|
||||
|
||||
if (out->msg.addr == AC_IO_BROADCAST) {
|
||||
return ac_io_out_detect_broadcast_eof(out);
|
||||
} else {
|
||||
return ac_io_out_detect_command_eof(out);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ac_io_out_detect_broadcast_eof(struct ac_io_out *out)
|
||||
{
|
||||
size_t end;
|
||||
|
||||
if (out->pos > offsetof(struct ac_io_message, bcast.nbytes)) {
|
||||
end = offsetof(struct ac_io_message,bcast.raw)
|
||||
+ out->msg.bcast.nbytes
|
||||
+ 1;
|
||||
|
||||
if (out->pos == end) {
|
||||
return ac_io_out_check_sum(out);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ac_io_out_detect_command_eof(struct ac_io_out *out)
|
||||
{
|
||||
size_t end;
|
||||
|
||||
if (out->pos > offsetof(struct ac_io_message, cmd.nbytes)) {
|
||||
end = offsetof(struct ac_io_message, cmd.raw)
|
||||
+ out->msg.cmd.nbytes
|
||||
+ 1;
|
||||
|
||||
if (out->pos == end) {
|
||||
return ac_io_out_check_sum(out);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ac_io_out_check_sum(struct ac_io_out *out)
|
||||
{
|
||||
uint8_t checksum;
|
||||
size_t i;
|
||||
|
||||
checksum = 0;
|
||||
|
||||
for (i = 0 ; i < out->pos - 1 ; i++) {
|
||||
checksum += out->bytes[i];
|
||||
}
|
||||
|
||||
if (checksum == out->bytes[out->pos - 1]) {
|
||||
return ac_io_out_accept_message(out);
|
||||
} else {
|
||||
log_warning("Checksum bad: expected %02x got %02x",
|
||||
checksum, out->bytes[out->pos - 1]);
|
||||
|
||||
return ac_io_out_reject_message(out);
|
||||
}
|
||||
}
|
||||
|
||||
static bool ac_io_out_accept_message(struct ac_io_out *out)
|
||||
{
|
||||
out->have_message = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ac_io_out_reject_message(struct ac_io_out *out)
|
||||
{
|
||||
ac_io_out_consume_message(out);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ac_io_out_have_message(const struct ac_io_out *out)
|
||||
{
|
||||
return out->have_message;
|
||||
}
|
||||
|
||||
const struct ac_io_message *ac_io_out_get_message(const struct ac_io_out *out)
|
||||
{
|
||||
if (out->pos == 0) {
|
||||
/* Autobaud byte/empty frame */
|
||||
return NULL;
|
||||
} else {
|
||||
return &out->msg;
|
||||
}
|
||||
}
|
||||
|
||||
void ac_io_out_consume_message(struct ac_io_out *out)
|
||||
{
|
||||
if (out->pos != 0) {
|
||||
out->in_frame = false;
|
||||
}
|
||||
|
||||
out->pos = 0;
|
||||
out->have_message = false;
|
||||
out->escape = false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
#ifndef ACIOEMU_PIPE_H
|
||||
#define ACIOEMU_PIPE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "acio/acio.h"
|
||||
|
||||
#include "util/iobuf.h"
|
||||
#include "util/list.h"
|
||||
#include "util/log.h"
|
||||
|
||||
/* This uses the USB convention where OUT and IN are from the host's (game's)
|
||||
perspective. So an OUT transaction comes in to us and vice versa.
|
||||
|
||||
(I made those terms up, not Konami). */
|
||||
|
||||
typedef void (*ac_io_in_thunk_t)(void *ctx, struct ac_io_message *msg);
|
||||
|
||||
struct ac_io_in_queued {
|
||||
struct list_node node;
|
||||
struct const_iobuf iobuf;
|
||||
uint64_t scheduled_time;
|
||||
uint64_t delay_us;
|
||||
ac_io_in_thunk_t thunk;
|
||||
void *thunk_ctx;
|
||||
uint8_t bytes[sizeof(struct ac_io_message)];
|
||||
};
|
||||
|
||||
struct ac_io_in {
|
||||
struct list queue;
|
||||
};
|
||||
|
||||
struct ac_io_out {
|
||||
union {
|
||||
uint8_t bytes[sizeof(struct ac_io_message)];
|
||||
struct ac_io_message msg;
|
||||
};
|
||||
|
||||
size_t pos;
|
||||
bool in_frame;
|
||||
bool escape;
|
||||
bool have_message;
|
||||
};
|
||||
|
||||
void ac_io_legacy_mode(void);
|
||||
|
||||
void ac_io_in_init(struct ac_io_in *in);
|
||||
void ac_io_in_supply(struct ac_io_in *in, const struct ac_io_message *msg,
|
||||
uint64_t delay);
|
||||
void ac_io_in_supply_thunk(
|
||||
struct ac_io_in *in,
|
||||
ac_io_in_thunk_t thunk,
|
||||
void *ctx,
|
||||
uint64_t delay);
|
||||
void ac_io_in_drain(struct ac_io_in *in, struct iobuf *dest);
|
||||
bool ac_io_in_is_msg_pending(const struct ac_io_in *in);
|
||||
|
||||
void ac_io_out_init(struct ac_io_out *out);
|
||||
void ac_io_out_supply(struct ac_io_out *out, struct const_iobuf *src);
|
||||
bool ac_io_out_have_message(const struct ac_io_out *out);
|
||||
const struct ac_io_message *ac_io_out_get_message(const struct ac_io_out *out);
|
||||
void ac_io_out_consume_message(struct ac_io_out *out);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
exes += aciotest
|
||||
|
||||
libs_aciotest := \
|
||||
aciodrv \
|
||||
util \
|
||||
|
||||
src_aciotest := \
|
||||
icca.c \
|
||||
main.c \
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef ACIOTEST_HANDLER_H
|
||||
#define ACIOTEST_HANDLER_H
|
||||
|
||||
static const uint8_t aciotest_handler_max = 16;
|
||||
|
||||
/**
|
||||
* Handler interface for an ACIO device.
|
||||
*/
|
||||
struct aciotest_handler_node_handler
|
||||
{
|
||||
void* ctx;
|
||||
bool (*init)(uint8_t node_id, void** ctx);
|
||||
bool (*update)(uint8_t node_id, void* ctx);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,83 @@
|
||||
#include "aciotest/icca.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "aciodrv/icca.h"
|
||||
|
||||
bool aciotest_icca_handler_init(uint8_t node_id, void** ctx)
|
||||
{
|
||||
*ctx = malloc(sizeof(uint32_t));
|
||||
*((uint32_t*) *ctx) = 0;
|
||||
|
||||
return aciodrv_icca_init(node_id);
|
||||
}
|
||||
|
||||
bool aciotest_icca_handler_update(uint8_t node_id, void* ctx)
|
||||
{
|
||||
if (*((uint32_t*) ctx) == 0) {
|
||||
*((uint32_t*) ctx) = 1;
|
||||
|
||||
/* eject cards that were left in the reader */
|
||||
if (!aciodrv_icca_set_state(node_id, AC_IO_ICCA_SLOT_STATE_EJECT, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct ac_io_icca_state state;
|
||||
|
||||
if (!aciodrv_icca_get_state(node_id, &state)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
printf(">>> ICCA %d:\n"
|
||||
"status_code: %d\n"
|
||||
"sensor_state: %d\n"
|
||||
"keypad_started: %d\n"
|
||||
"card_type: %d\n"
|
||||
"UUID: %02X%02X%02X%02X%02X%02X%02X%02X\n"
|
||||
"key_state: %04X\n"
|
||||
"key_events[0]: %02X\n"
|
||||
"key_events[1]: %02X\n",
|
||||
node_id, state.status_code, state.sensor_state, state.keypad_started,
|
||||
state.card_type,
|
||||
state.uid[0], state.uid[1], state.uid[2], state.uid[3],
|
||||
state.uid[4], state.uid[5], state.uid[6], state.uid[7],
|
||||
state.key_state, state.key_events[0], state.key_events[1]);
|
||||
|
||||
/* eject card with "empty" key */
|
||||
if (state.key_state & AC_IO_ICCA_KEYPAD_MASK_EMPTY) {
|
||||
|
||||
if (!aciodrv_icca_set_state(node_id, AC_IO_ICCA_SLOT_STATE_EJECT, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* allow new card to be inserted when slot is clear */
|
||||
if ( !(state.sensor_state & AC_IO_ICCA_SENSOR_MASK_BACK_ON) &&
|
||||
!(state.sensor_state & AC_IO_ICCA_SENSOR_MASK_FRONT_ON)) {
|
||||
|
||||
if (!aciodrv_icca_set_state(node_id, AC_IO_ICCA_SLOT_STATE_OPEN, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* lock the card when fully inserted */
|
||||
if ( (state.sensor_state & AC_IO_ICCA_SENSOR_MASK_BACK_ON) &&
|
||||
(state.sensor_state & AC_IO_ICCA_SENSOR_MASK_FRONT_ON)) {
|
||||
|
||||
if (!aciodrv_icca_set_state(node_id, AC_IO_ICCA_SLOT_STATE_CLOSE, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aciodrv_icca_read_card(node_id, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef ACIOTEST_ICCA_H
|
||||
#define ACIOTEST_ICCA_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool aciotest_icca_handler_init(uint8_t node_id, void** ctx);
|
||||
bool aciotest_icca_handler_update(uint8_t node_id, void* ctx);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,120 @@
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
|
||||
#include "aciotest/handler.h"
|
||||
#include "aciotest/icca.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static uint8_t aciotest_cnt = 0;
|
||||
|
||||
/**
|
||||
* Enumerate supported ACIO nodes based on their product id.
|
||||
*/
|
||||
static bool aciotest_assign_handler(char product[4],
|
||||
struct aciotest_handler_node_handler* handler)
|
||||
{
|
||||
if (!memcmp(product, "ICCA", 4)
|
||||
|| !memcmp(product, "ICCB", 4)
|
||||
|| !memcmp(product, "ICCC", 4)
|
||||
){
|
||||
handler->init = aciotest_icca_handler_init;
|
||||
handler->update = aciotest_icca_handler_update;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tool to test real ACIO hardware.
|
||||
*/
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc < 3) {
|
||||
printf(
|
||||
"aciotest, build "__DATE__ " " __TIME__ "\n"
|
||||
"Usage: %s <com port str> <baud rate>\n"
|
||||
"Example for two slotted readers: %s COM1 57600\n",
|
||||
argv[0], argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_to_writer(log_writer_stdout, NULL);
|
||||
|
||||
if (!aciodrv_device_open(argv[1], atoi(argv[2]))) {
|
||||
printf("Opening acio device failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Opening acio device successful\n");
|
||||
|
||||
uint8_t node_count = aciodrv_device_get_node_count();
|
||||
printf("Enumerated %d nodes\n", node_count);
|
||||
|
||||
struct aciotest_handler_node_handler handler[aciotest_handler_max];
|
||||
memset(&handler, 0,
|
||||
sizeof(struct aciotest_handler_node_handler) * aciotest_handler_max);
|
||||
|
||||
for (uint8_t i = 0; i < node_count; i++) {
|
||||
char product[4];
|
||||
aciodrv_device_get_node_product_ident(i, product);
|
||||
printf("> %d: %c%c%c%c\n", i + 1,
|
||||
product[0], product[1], product[2], product[3]);
|
||||
|
||||
if (!aciotest_assign_handler(product, &handler[i])) {
|
||||
printf("ERROR: Unsupported acio node product %c%c%c%c on node %d\n",
|
||||
product[0], product[1], product[2], product[3], i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < aciotest_handler_max; i++) {
|
||||
|
||||
if (handler[i].init != NULL) {
|
||||
|
||||
if (!handler[i].init(i, &handler[i].ctx)) {
|
||||
printf("ERROR: Initializing node %d failed\n", i);
|
||||
handler[i].update = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
printf(">>> Initializing done, press enter to start update loop <<<\n");
|
||||
|
||||
if (getchar() != '\n') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
system("cls");
|
||||
printf("%d\n", aciotest_cnt++);
|
||||
|
||||
for (uint8_t i = 0; i < aciotest_handler_max; i++) {
|
||||
|
||||
if (handler[i].update != NULL) {
|
||||
|
||||
if (!handler[i].update(i, handler[i].ctx)) {
|
||||
printf("ERROR: Updating node %d, removed from loop\n", i);
|
||||
handler[i].update = NULL;
|
||||
Sleep(5000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* avoid cpu banging */
|
||||
Sleep(20);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef BEMANITOOLS_SDVXIO_H
|
||||
#define BEMANITOOLS_SDVXIO_H
|
||||
|
||||
/* IO emulation provider for BeatStream */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bemanitools/glue.h"
|
||||
|
||||
enum bst_io_in_gpio_sys_bit {
|
||||
SDVX_IO_IN_GPIO_SYS_COIN = 2,
|
||||
SDVX_IO_IN_GPIO_SYS_TEST = 4,
|
||||
SDVX_IO_IN_GPIO_SYS_SERVICE = 5,
|
||||
};
|
||||
|
||||
/* The first function that will be called on your DLL. You will be supplied
|
||||
with four function pointers that may be used to log messages to the game's
|
||||
log file. See comments in glue.h for further information. */
|
||||
|
||||
void bst_io_set_loggers(log_formatter_t misc, log_formatter_t info,
|
||||
log_formatter_t warning, log_formatter_t fatal);
|
||||
|
||||
/* Initialize your BST IO emulation DLL. Thread management functions are
|
||||
provided to you; you must use these functions to create your own threads if
|
||||
you want to make use of the logging functions that are provided to
|
||||
eam_io_set_loggers(). You will also need to pass these thread management
|
||||
functions on to geninput if you intend to make use of that library.
|
||||
|
||||
See glue.h and geninput.h for further details. */
|
||||
|
||||
bool bst_io_init(thread_create_t thread_create, thread_join_t thread_join,
|
||||
thread_destroy_t thread_destroy);
|
||||
|
||||
/* Shut down your SDVX IO emulation DLL */
|
||||
|
||||
void bst_io_fini(void);
|
||||
|
||||
/* Read input state. Returns true if successful. */
|
||||
|
||||
bool bst_io_read_input(void);
|
||||
|
||||
/* Get state of coin, test, service inputs */
|
||||
|
||||
uint8_t bst_io_get_input(void);
|
||||
|
||||
// TODO: Lighting
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
#ifndef BEMANITOOLS_DDRIO_H
|
||||
#define BEMANITOOLS_DDRIO_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bemanitools/glue.h"
|
||||
|
||||
enum ddr_pad_bit {
|
||||
DDR_TEST = 0x04,
|
||||
DDR_COIN = 0x05,
|
||||
DDR_SERVICE = 0x06,
|
||||
|
||||
DDR_P2_START = 0x08,
|
||||
DDR_P2_UP = 0x09,
|
||||
DDR_P2_DOWN = 0x0A,
|
||||
DDR_P2_LEFT = 0x0B,
|
||||
DDR_P2_RIGHT = 0x0C,
|
||||
DDR_P2_MENU_LEFT = 0x0E,
|
||||
DDR_P2_MENU_RIGHT = 0x0F,
|
||||
DDR_P2_MENU_UP = 0x02,
|
||||
DDR_P2_MENU_DOWN = 0x03,
|
||||
|
||||
DDR_P1_START = 0x10,
|
||||
DDR_P1_UP = 0x11,
|
||||
DDR_P1_DOWN = 0x12,
|
||||
DDR_P1_LEFT = 0x13,
|
||||
DDR_P1_RIGHT = 0x14,
|
||||
DDR_P1_MENU_LEFT = 0x16,
|
||||
DDR_P1_MENU_RIGHT = 0x17,
|
||||
DDR_P1_MENU_UP = 0x00,
|
||||
DDR_P1_MENU_DOWN = 0x01,
|
||||
};
|
||||
|
||||
/* p3io controls menu btn and marquee lights
|
||||
extio controls neons and stage lights. */
|
||||
|
||||
enum p3io_light_bit {
|
||||
LIGHT_P1_MENU = 0x00,
|
||||
LIGHT_P2_MENU = 0x01,
|
||||
LIGHT_P2_LOWER_LAMP = 0x04,
|
||||
LIGHT_P2_UPPER_LAMP = 0x05,
|
||||
LIGHT_P1_LOWER_LAMP = 0x06,
|
||||
LIGHT_P1_UPPER_LAMP = 0x07
|
||||
};
|
||||
|
||||
enum extio_light_bit {
|
||||
LIGHT_NEONS = 0x0E,
|
||||
|
||||
LIGHT_P2_RIGHT = 0x13,
|
||||
LIGHT_P2_LEFT = 0x14,
|
||||
LIGHT_P2_DOWN = 0x15,
|
||||
LIGHT_P2_UP = 0x16,
|
||||
|
||||
LIGHT_P1_RIGHT = 0x1B,
|
||||
LIGHT_P1_LEFT = 0x1C,
|
||||
LIGHT_P1_DOWN = 0x1D,
|
||||
LIGHT_P1_UP = 0x1E
|
||||
};
|
||||
|
||||
/* The first function that will be called on your DLL. You will be supplied
|
||||
with four function pointers that may be used to log messages to the game's
|
||||
log file. See comments in glue.h for further information. */
|
||||
|
||||
void ddr_io_set_loggers(log_formatter_t misc, log_formatter_t info,
|
||||
log_formatter_t warning, log_formatter_t fatal);
|
||||
|
||||
/* Initialize your DDR IO emulation DLL. Thread management functions are
|
||||
provided to you; you must use these functions to create your own threads if
|
||||
you want to make use of the logging functions that are provided to
|
||||
eam_io_set_loggers(). You will also need to pass these thread management
|
||||
functions on to geninput if you intend to make use of that library.
|
||||
|
||||
See glue.h and geninput.h for further details. */
|
||||
|
||||
bool ddr_io_init(thread_create_t thread_create, thread_join_t thread_join,
|
||||
thread_destroy_t thread_destroy);
|
||||
|
||||
uint32_t ddr_io_read_pad(void);
|
||||
void ddr_io_set_lights_extio(uint32_t extio_lights);
|
||||
void ddr_io_set_lights_p3io(uint32_t p3io_lights);
|
||||
void ddr_io_fini(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,132 @@
|
||||
#ifndef BEMANITOOLS_EAM_H
|
||||
#define BEMANITOOLS_EAM_H
|
||||
|
||||
/* Card reader emulator API. You may replace the stock EAMIO.DLL supplied by
|
||||
Bemanitools with your own custom implementation, which should implement the
|
||||
interface contract defined in this header file. */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bemanitools/glue.h"
|
||||
|
||||
/* Scan codes for the so-called "10 key" button panel on each card reader. Each
|
||||
scan code corresponds to a bit position within the 16-bit bitfield that you
|
||||
return from eam_io_get_keypad_state(). */
|
||||
|
||||
enum eam_io_keypad_scan_code {
|
||||
EAM_IO_KEYPAD_0 = 0,
|
||||
EAM_IO_KEYPAD_1 = 1,
|
||||
EAM_IO_KEYPAD_4 = 2,
|
||||
EAM_IO_KEYPAD_7 = 3,
|
||||
EAM_IO_KEYPAD_00 = 4,
|
||||
EAM_IO_KEYPAD_2 = 5,
|
||||
EAM_IO_KEYPAD_5 = 6,
|
||||
EAM_IO_KEYPAD_8 = 7,
|
||||
EAM_IO_KEYPAD_DECIMAL = 8,
|
||||
EAM_IO_KEYPAD_3 = 9,
|
||||
EAM_IO_KEYPAD_6 = 10,
|
||||
EAM_IO_KEYPAD_9 = 11,
|
||||
|
||||
EAM_IO_KEYPAD_COUNT = 12, /* Not an actual scan code */
|
||||
};
|
||||
|
||||
/* Emulating the sensors of a slotted card reader. The reader has one
|
||||
sensor at the front that detects if a card is getting inserted or
|
||||
if the card is not fully removed. When the back sensor is triggered
|
||||
the card is locked in the slot and its data is read. */
|
||||
|
||||
enum eam_io_sensor_state {
|
||||
EAM_IO_SENSOR_FRONT = 0,
|
||||
EAM_IO_SENSOR_BACK = 1,
|
||||
};
|
||||
|
||||
/* Different commands for the (slotted) reader. The game triggers one
|
||||
of these actions and the card slot as to execute it. When non-slotted
|
||||
readers are emulated, these states are not used/set. */
|
||||
|
||||
enum eam_io_card_slot_cmd {
|
||||
EAM_IO_CARD_SLOT_CMD_CLOSE = 0,
|
||||
EAM_IO_CARD_SLOT_CMD_OPEN = 1,
|
||||
EAM_IO_CARD_SLOT_CMD_EJECT = 2,
|
||||
EAM_IO_CARD_SLOT_CMD_READ = 3,
|
||||
};
|
||||
|
||||
/* Emulating of the card type for new readers. */
|
||||
|
||||
enum eam_io_read_card_result {
|
||||
EAM_IO_CARD_NONE = 0,
|
||||
EAM_IO_CARD_ISO15696 = 1,
|
||||
EAM_IO_CARD_FELICA = 2,
|
||||
};
|
||||
|
||||
/* A private function pointer table returned by the stock EAMIO.DLL
|
||||
implementation and consumed by config.exe. The contents of this table are
|
||||
undocumented and subject to change without notice. */
|
||||
|
||||
struct eam_io_config_api;
|
||||
|
||||
/* The first function that will be called on your DLL. You will be supplied
|
||||
with four function pointers that may be used to log messages to the game's
|
||||
log file. See comments in glue.h for further information. */
|
||||
|
||||
void eam_io_set_loggers(log_formatter_t misc, log_formatter_t info,
|
||||
log_formatter_t warning, log_formatter_t fatal);
|
||||
|
||||
/* Initialize your card reader emulation DLL. Thread management functions are
|
||||
provided to you; you must use these functions to create your own threads if
|
||||
you want to make use of the logging functions that are provided to
|
||||
eam_io_set_loggers(). You will also need to pass these thread management
|
||||
functions on to geninput if you intend to make use of that library.
|
||||
|
||||
See glue.h and geninput.h for further details. */
|
||||
|
||||
bool eam_io_init(thread_create_t thread_create, thread_join_t thread_join,
|
||||
thread_destroy_t thread_destroy);
|
||||
|
||||
/* Shut down your card reader emulation DLL. */
|
||||
|
||||
void eam_io_fini(void);
|
||||
|
||||
/* Return the state of the number pad on your reader. This function will be
|
||||
called frequently. See enum eam_io_keypad_scan_code above for the meaning of
|
||||
each bit within the return value.
|
||||
|
||||
This function will be called even if the running game does not actually have
|
||||
a number pad on the real cabinet (e.g. Jubeat).
|
||||
|
||||
unit_no is either 0 or 1. Games with only a single reader (jubeat, popn,
|
||||
drummania) will only use unit_no 0. */
|
||||
|
||||
uint16_t eam_io_get_keypad_state(uint8_t unit_no);
|
||||
|
||||
/* Indicate which sensors (front and back) are triggered for a slotted reader
|
||||
(refer to enum). To emulate non-slotted readers, just set both sensors
|
||||
to on to indicate the card is in range of the reader. This function
|
||||
will be called frequently. */
|
||||
|
||||
uint8_t eam_io_get_sensor_state(uint8_t unit_no);
|
||||
|
||||
/* Read a card ID. This function is only called when the return value of
|
||||
eam_io_get_sensor_state() changes from false to true, so you may take your
|
||||
time and perform file I/O etc, within reason. You must return exactly eight
|
||||
bytes into the buffer pointed to by card_id. */
|
||||
|
||||
uint8_t eam_io_read_card(uint8_t unit_no, uint8_t *card_id, uint8_t nbytes);
|
||||
|
||||
/* Send a command to the card slot. This is called by the game to execute
|
||||
certain actions on a slotted reader (refer to enum). When emulating
|
||||
wave pass readers, this is function is never called. */
|
||||
|
||||
bool eam_io_card_slot_cmd(uint8_t unit_no, uint8_t cmd);
|
||||
|
||||
/* This function is called frequently. Update your device and states in here */
|
||||
|
||||
bool eam_io_poll(uint8_t unit_no);
|
||||
|
||||
/* Return a pointer to an internal configuration API for use by config.exe.
|
||||
Custom implementations should return NULL. */
|
||||
|
||||
const struct eam_io_config_api *eam_io_get_config_api(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef BEMANITOOLS_GLUE_H
|
||||
#define BEMANITOOLS_GLUE_H
|
||||
|
||||
/* Common definitions for integration bindings */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* Bemanitools is compiled with GCC (MinGW, specifically) as of version 5 */
|
||||
#define LOG_CHECK_FMT __attribute__(( format(printf, 2, 3) ))
|
||||
#else
|
||||
/* Compile it out for MSVC plebs */
|
||||
#define LOG_CHECK_FMT
|
||||
#endif
|
||||
|
||||
/* An AVS-style logger function. Comes in four flavors: misc, info, warning,
|
||||
and fatal, with increasing severity. Fatal loggers do not return, they
|
||||
abort the running process after writing their message to the log.
|
||||
|
||||
"module" is an arbitrary short string identifying the source of the log
|
||||
message. The name of the calling DLL is a good default choice for this
|
||||
string, although you might want to identify a module within your DLL here
|
||||
instead.
|
||||
|
||||
"fmt" is a printf-style format string. Depending on the context in which
|
||||
your DLL is running you might end up calling a logger function exported
|
||||
from libavs, which has its own printf implementation (including a number of
|
||||
proprietary extensions), so don't use any overly exotic formats. */
|
||||
|
||||
typedef void (*log_formatter_t)(const char *module, const char *fmt, ...)
|
||||
LOG_CHECK_FMT;
|
||||
|
||||
/* An API for spawning threads. This API is defined by libavs, although
|
||||
Bemanitools itself may supply compatible implementations of these functions
|
||||
to your DLL, depending on the context in which it runs.
|
||||
|
||||
NOTE: You may only use the logging functions from a thread where Bemanitools
|
||||
calls you, or a thread that you create using this API. Failure to observe
|
||||
this restriction will cause the process to crash. This is a limitation of
|
||||
libavs itself, not Bemanitools. */
|
||||
|
||||
typedef int (*thread_create_t)(int (*proc)(void *), void *ctx,
|
||||
uint32_t stack_sz, unsigned int priority);
|
||||
typedef void (*thread_join_t)(int thread_id, int *result);
|
||||
typedef void (*thread_destroy_t)(int thread_id);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,155 @@
|
||||
#ifndef BEMANITOOLS_IIDXIO_H
|
||||
#define BEMANITOOLS_IIDXIO_H
|
||||
|
||||
/* IO emulation provider for beatmania IIDX. */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bemanitools/glue.h"
|
||||
|
||||
/* Bit mapping for the "pad" word */
|
||||
|
||||
enum iidx_io_sys_bit {
|
||||
IIDX_IO_SYS_TEST = 0x00,
|
||||
IIDX_IO_SYS_SERVICE = 0x01,
|
||||
IIDX_IO_SYS_COIN = 0x02
|
||||
};
|
||||
|
||||
enum iidx_io_panel_bit {
|
||||
IIDX_IO_PANEL_P1_START = 0x00,
|
||||
IIDX_IO_PANEL_P2_START = 0x01,
|
||||
IIDX_IO_PANEL_VEFX = 0x02,
|
||||
IIDX_IO_PANEL_EFFECT = 0x03
|
||||
};
|
||||
|
||||
enum iidx_io_key_bit {
|
||||
IIDX_IO_KEY_P1_1 = 0x00,
|
||||
IIDX_IO_KEY_P1_2 = 0x01,
|
||||
IIDX_IO_KEY_P1_3 = 0x02,
|
||||
IIDX_IO_KEY_P1_4 = 0x03,
|
||||
IIDX_IO_KEY_P1_5 = 0x04,
|
||||
IIDX_IO_KEY_P1_6 = 0x05,
|
||||
IIDX_IO_KEY_P1_7 = 0x06,
|
||||
|
||||
IIDX_IO_KEY_P2_1 = 0x07,
|
||||
IIDX_IO_KEY_P2_2 = 0x08,
|
||||
IIDX_IO_KEY_P2_3 = 0x09,
|
||||
IIDX_IO_KEY_P2_4 = 0x0A,
|
||||
IIDX_IO_KEY_P2_5 = 0x0B,
|
||||
IIDX_IO_KEY_P2_6 = 0x0C,
|
||||
IIDX_IO_KEY_P2_7 = 0x0D
|
||||
};
|
||||
|
||||
/* Bit mapping for the P1 and P2 deck lights */
|
||||
|
||||
enum iidx_io_deck_light {
|
||||
IIDX_IO_DECK_LIGHT_P1_1 = 0,
|
||||
IIDX_IO_DECK_LIGHT_P1_2 = 1,
|
||||
IIDX_IO_DECK_LIGHT_P1_3 = 2,
|
||||
IIDX_IO_DECK_LIGHT_P1_4 = 3,
|
||||
IIDX_IO_DECK_LIGHT_P1_5 = 4,
|
||||
IIDX_IO_DECK_LIGHT_P1_6 = 5,
|
||||
IIDX_IO_DECK_LIGHT_P1_7 = 6,
|
||||
|
||||
IIDX_IO_DECK_LIGHT_P2_1 = 8,
|
||||
IIDX_IO_DECK_LIGHT_P2_2 = 9,
|
||||
IIDX_IO_DECK_LIGHT_P2_3 = 10,
|
||||
IIDX_IO_DECK_LIGHT_P2_4 = 11,
|
||||
IIDX_IO_DECK_LIGHT_P2_5 = 12,
|
||||
IIDX_IO_DECK_LIGHT_P2_6 = 13,
|
||||
IIDX_IO_DECK_LIGHT_P2_7 = 14,
|
||||
};
|
||||
|
||||
/* Bit mapping for the front panel lights */
|
||||
|
||||
enum iidx_io_panel_light {
|
||||
IIDX_IO_PANEL_LIGHT_P1_START = 0,
|
||||
IIDX_IO_PANEL_LIGHT_P2_START = 1,
|
||||
IIDX_IO_PANEL_LIGHT_VEFX = 2,
|
||||
IIDX_IO_PANEL_LIGHT_EFFECT = 3,
|
||||
};
|
||||
|
||||
/* The first function that will be called on your DLL. You will be supplied
|
||||
with four function pointers that may be used to log messages to the game's
|
||||
log file. See comments in glue.h for further information. */
|
||||
|
||||
void iidx_io_set_loggers(log_formatter_t misc, log_formatter_t info,
|
||||
log_formatter_t warning, log_formatter_t fatal);
|
||||
|
||||
/* Initialize your IIDX IO emulation DLL. Thread management functions are
|
||||
provided to you; you must use these functions to create your own threads if
|
||||
you want to make use of the logging functions that are provided to
|
||||
eam_io_set_loggers(). You will also need to pass these thread management
|
||||
functions on to geninput if you intend to make use of that library.
|
||||
|
||||
See glue.h and geninput.h for further details. */
|
||||
|
||||
bool iidx_io_init(thread_create_t thread_create, thread_join_t thread_join,
|
||||
thread_destroy_t thread_destroy);
|
||||
|
||||
/* Shut down your IIDX IO emulation DLL */
|
||||
|
||||
void iidx_io_fini(void);
|
||||
|
||||
/* Set the deck lighting state. See enum iidx_io_deck_light above. */
|
||||
|
||||
void iidx_io_ep1_set_deck_lights(uint16_t deck_lights);
|
||||
|
||||
/* Set front panel lighting state. See enum iidx_io_panel_light above. */
|
||||
|
||||
void iidx_io_ep1_set_panel_lights(uint8_t panel_lights);
|
||||
|
||||
/* Set state of the eight halogens above the marquee. */
|
||||
|
||||
void iidx_io_ep1_set_top_lamps(uint8_t top_lamps);
|
||||
|
||||
/* Switch the top neons on or off. */
|
||||
|
||||
void iidx_io_ep1_set_top_neons(bool top_neons);
|
||||
|
||||
/* Transmit the lighting state to the lighting controller. This function is
|
||||
called immediately after all of the other iidx_io_ep1_set_*() functions.
|
||||
|
||||
Return false in the event of an IO error. This will lock the game into an
|
||||
IO error screen. */
|
||||
|
||||
bool iidx_io_ep1_send(void);
|
||||
|
||||
/* Read input state from the input controller. This function is called
|
||||
immediately before all of the iidx_io_ep2_get_*() functions.
|
||||
|
||||
Return false in the event of an IO error. This will lock the game into an
|
||||
IO error screen. */
|
||||
|
||||
bool iidx_io_ep2_recv(void);
|
||||
|
||||
/* Get absolute turntable position, expressed in 1/256ths of a rotation.
|
||||
player_no is either 0 or 1. */
|
||||
|
||||
uint8_t iidx_io_ep2_get_turntable(uint8_t player_no);
|
||||
|
||||
/* Get slider position, where 0 is the bottom position and 15 is the topmost
|
||||
position. slider_no is a number between 0 (leftmost) and 4 (rightmost). */
|
||||
|
||||
uint8_t iidx_io_ep2_get_slider(uint8_t slider_no);
|
||||
|
||||
/* Get the state of the system buttons. See enums above. */
|
||||
|
||||
uint8_t iidx_io_ep2_get_sys(void);
|
||||
|
||||
/* Get the state of the panel buttons. See enums above. */
|
||||
|
||||
uint8_t iidx_io_ep2_get_panel(void);
|
||||
|
||||
/* Get the state of the 14 key buttons. See enums above. */
|
||||
|
||||
uint16_t iidx_io_ep2_get_keys(void);
|
||||
|
||||
/* Write a nine-character string to the 16-segment display. This happens on a
|
||||
different schedule to all of the other IO operations, so you should initiate
|
||||
the communication as soon as this function is called */
|
||||
|
||||
bool iidx_io_ep3_write_16seg(const char *text);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
#ifndef BEMANITOOLS_INPUT_H
|
||||
#define BEMANITOOLS_INPUT_H
|
||||
|
||||
/* Generic input API. This header file defines the public API for geninput.dll.
|
||||
|
||||
You may use geninput to supply generic input mapping services for controls
|
||||
that your custom IO DLLs do not natively provide. For instance, you might
|
||||
want to make a custom IIDXIO.DLL that interfaces with your own 16-segment
|
||||
LCD marquee device while still using the stock IIDXIO.DLL input and lighting
|
||||
code, which uses the generic services provided by geninput.dll.
|
||||
|
||||
All other exports from geninput.dll are undocumented and subject to change
|
||||
without notice. */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bemanitools/glue.h"
|
||||
|
||||
/* Supply logging functions to geninput. You should pass on the logging
|
||||
functions that are supplied to your own custom DLLs.
|
||||
|
||||
This is the only function that can safely be called before input_init(). */
|
||||
|
||||
void input_set_loggers(log_formatter_t misc, log_formatter_t info,
|
||||
log_formatter_t warning, log_formatter_t fatal);
|
||||
|
||||
/* Initialize the generic input subsystem. You must pass on the thread
|
||||
management functions that have been supplied to your DLL.
|
||||
|
||||
Calling any geninput functions other than input_set_loggers() before calling
|
||||
input_init() will probably crash the running process.
|
||||
|
||||
You will also need to call mapper_config_load() with the appropriate
|
||||
game_type parameter, otherwise you will not receive any input, and any
|
||||
attempts to set a light output level will have no effect. */
|
||||
|
||||
void input_init(thread_create_t thread_create, thread_join_t thread_join,
|
||||
thread_destroy_t thread_destroy);
|
||||
|
||||
/* Shut down the generic input subsystem. After calling this function, no
|
||||
geninput functions other than input_set_loggers() or input_init() may be
|
||||
called. */
|
||||
|
||||
void input_fini(void);
|
||||
|
||||
/* Load input mappings for a particular game, as configured from config.exe.
|
||||
|
||||
Currently recognized game types are:
|
||||
|
||||
ddr: Dance Dance Revolution
|
||||
dm: Drum Mania
|
||||
gf: Guitar Freaks
|
||||
iidx: beatmania IIDX
|
||||
pnm: pop'n music
|
||||
sdvx: Sound Voltex
|
||||
ju: jubeat
|
||||
|
||||
Returns true if a suitable config file was found and successfully loaded. */
|
||||
|
||||
bool mapper_config_load(const char *game_type);
|
||||
|
||||
/* Return the absolute position of an analog spinner, expressed in 1/256ths of
|
||||
a complete rotation. */
|
||||
|
||||
uint8_t mapper_read_analog(uint8_t analog);
|
||||
|
||||
/* Map the current state of all attached input devices to a 64-bit bit field.
|
||||
The exact layout of this bit field varies between game types, although we
|
||||
try to approximate the contents of each emulated IO PCB's own state packet
|
||||
as closely as is reasonably practical. */
|
||||
|
||||
uint64_t mapper_update(void);
|
||||
|
||||
/* Set the intensity of any light on a controller corresponding to a particular
|
||||
software-controlled light on an arcade cabinet, where 0 is off and 255 is
|
||||
full intensity. Consult the header files for the light identifiers used for
|
||||
each game type. The mappings between these light identifiers and the actual
|
||||
lights on the user's controller (if any) are configured by the user by means
|
||||
of the config.exe program.
|
||||
|
||||
Note that any calls to this function do not take effect until the next call
|
||||
to mapper_update(). */
|
||||
|
||||
void mapper_write_light(uint8_t light, uint8_t intensity);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,83 @@
|
||||
#ifndef BEMANITOOLS_JBIO_H
|
||||
#define BEMANITOOLS_JBIO_H
|
||||
|
||||
/* IO emulation provider for jubeat. */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bemanitools/glue.h"
|
||||
|
||||
/* input bit mappings. Panels on the controller are
|
||||
panel 1 top left corner down to panel 16 bottom right corner */
|
||||
enum jb_io_panel_bit {
|
||||
JB_IO_PANEL_01 = 0x00,
|
||||
JB_IO_PANEL_02 = 0x01,
|
||||
JB_IO_PANEL_03 = 0x02,
|
||||
JB_IO_PANEL_04 = 0x03,
|
||||
JB_IO_PANEL_05 = 0x04,
|
||||
JB_IO_PANEL_06 = 0x05,
|
||||
JB_IO_PANEL_07 = 0x06,
|
||||
JB_IO_PANEL_08 = 0x07,
|
||||
JB_IO_PANEL_09 = 0x08,
|
||||
JB_IO_PANEL_10 = 0x09,
|
||||
JB_IO_PANEL_11 = 0x0A,
|
||||
JB_IO_PANEL_12 = 0x0B,
|
||||
JB_IO_PANEL_13 = 0x0C,
|
||||
JB_IO_PANEL_14 = 0x0D,
|
||||
JB_IO_PANEL_15 = 0x0E,
|
||||
JB_IO_PANEL_16 = 0x0F,
|
||||
};
|
||||
|
||||
/* Bit mappings for "system" inputs */
|
||||
enum jb_io_sys_bit {
|
||||
JB_IO_SYS_TEST = 0x00,
|
||||
JB_IO_SYS_SERVICE = 0x01,
|
||||
JB_IO_SYS_COIN = 0x02,
|
||||
};
|
||||
|
||||
/* RGB led units to address */
|
||||
enum jb_io_rgb_led {
|
||||
JB_IO_RGB_LED_FRONT = 0,
|
||||
JB_IO_RGB_LED_TOP = 1,
|
||||
JB_IO_RGB_LED_LEFT = 2,
|
||||
JB_IO_RGB_LED_RIGHT = 3,
|
||||
JB_IO_RGB_LED_TITLE = 4,
|
||||
JB_IO_RGB_LED_WOOFER = 5
|
||||
};
|
||||
|
||||
/* The first function that will be called on your DLL. You will be supplied
|
||||
with four function pointers that may be used to log messages to the game's
|
||||
log file. See comments in glue.h for further information. */
|
||||
|
||||
void jb_io_set_loggers(log_formatter_t misc, log_formatter_t info,
|
||||
log_formatter_t warning, log_formatter_t fatal);
|
||||
|
||||
/* Initialize your JB IO emulation DLL. Thread management functions are
|
||||
provided to you; you must use these functions to create your own threads if
|
||||
you want to make use of the logging functions that are provided to
|
||||
jb_io_set_loggers(). You will also need to pass these thread management
|
||||
functions on to geninput if you intend to make use of that library.
|
||||
|
||||
See glue.h and geninput.h for further details. */
|
||||
|
||||
bool jb_io_init(thread_create_t thread_create, thread_join_t thread_join,
|
||||
thread_destroy_t thread_destroy);
|
||||
|
||||
/* Shut down your JB IO emulation DLL */
|
||||
|
||||
void jb_io_fini(void);
|
||||
|
||||
/* TODO doc */
|
||||
|
||||
bool jb_io_read_inputs(void);
|
||||
|
||||
bool jb_io_write_outputs(void);
|
||||
|
||||
uint8_t jb_io_get_sys_inputs(void);
|
||||
|
||||
uint16_t jb_io_get_panel_inputs(void);
|
||||
|
||||
void jb_io_set_rgb_led(enum jb_io_rgb_led unit, uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,95 @@
|
||||
#ifndef BEMANITOOLS_SDVXIO_H
|
||||
#define BEMANITOOLS_SDVXIO_H
|
||||
|
||||
/* IO emulation provider for SOUND VOLTEX */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bemanitools/glue.h"
|
||||
|
||||
enum sdvx_io_in_gpio_sys_bit {
|
||||
SDVX_IO_IN_GPIO_SYS_COIN = 2,
|
||||
SDVX_IO_IN_GPIO_SYS_TEST = 4,
|
||||
SDVX_IO_IN_GPIO_SYS_SERVICE = 5,
|
||||
};
|
||||
|
||||
enum sdvx_io_in_gpio_0_bit {
|
||||
SDVX_IO_IN_GPIO_0_C = 0,
|
||||
SDVX_IO_IN_GPIO_0_B = 1,
|
||||
SDVX_IO_IN_GPIO_0_A = 2,
|
||||
SDVX_IO_IN_GPIO_0_START = 3,
|
||||
SDVX_IO_IN_GPIO_0_HEADPHONE = 4,
|
||||
};
|
||||
|
||||
enum sdvx_io_in_gpio_1_bit {
|
||||
SDVX_IO_IN_GPIO_1_FX_R = 3,
|
||||
SDVX_IO_IN_GPIO_1_FX_L = 4,
|
||||
SDVX_IO_IN_GPIO_1_D = 5,
|
||||
};
|
||||
|
||||
enum sdvx_io_out_gpio_bit {
|
||||
SDVX_IO_OUT_GPIO_D = 0,
|
||||
SDVX_IO_OUT_GPIO_FX_L = 1,
|
||||
SDVX_IO_OUT_GPIO_FX_R = 2,
|
||||
SDVX_IO_OUT_GPIO_START = 12,
|
||||
SDVX_IO_OUT_GPIO_A = 13,
|
||||
SDVX_IO_OUT_GPIO_B = 14,
|
||||
SDVX_IO_OUT_GPIO_C = 15,
|
||||
};
|
||||
|
||||
/* The first function that will be called on your DLL. You will be supplied
|
||||
with four function pointers that may be used to log messages to the game's
|
||||
log file. See comments in glue.h for further information. */
|
||||
|
||||
void sdvx_io_set_loggers(log_formatter_t misc, log_formatter_t info,
|
||||
log_formatter_t warning, log_formatter_t fatal);
|
||||
|
||||
/* Initialize your SDVX IO emulation DLL. Thread management functions are
|
||||
provided to you; you must use these functions to create your own threads if
|
||||
you want to make use of the logging functions that are provided to
|
||||
eam_io_set_loggers(). You will also need to pass these thread management
|
||||
functions on to geninput if you intend to make use of that library.
|
||||
|
||||
See glue.h and geninput.h for further details. */
|
||||
|
||||
bool sdvx_io_init(thread_create_t thread_create, thread_join_t thread_join,
|
||||
thread_destroy_t thread_destroy);
|
||||
|
||||
/* Shut down your SDVX IO emulation DLL */
|
||||
|
||||
void sdvx_io_fini(void);
|
||||
|
||||
/* Set state of the GPIO (on/off) lights (see bit definitions above) */
|
||||
|
||||
void sdvx_io_set_gpio_lights(uint32_t gpio_lights);
|
||||
|
||||
/* Set state of a PWM (dimmable) light channel. These come in groups of three
|
||||
(red, green, blue). There are a six group of three PWM channels, for a
|
||||
total of 18 channels (0 through 17). */
|
||||
|
||||
void sdvx_io_set_pwm_light(uint8_t light_no, uint8_t intensity);
|
||||
|
||||
/* Transmit the light state to the IOPCB */
|
||||
|
||||
bool sdvx_io_write_output(void);
|
||||
|
||||
/* Read input state */
|
||||
|
||||
bool sdvx_io_read_input(void);
|
||||
|
||||
/* Get state of coin, test, service inputs */
|
||||
|
||||
uint8_t sdvx_io_get_input_gpio_sys(void);
|
||||
|
||||
/* Get gameplay button state. Parameter selects GPIO bank 0 or 1. See bit
|
||||
definitions above for details. */
|
||||
|
||||
uint16_t sdvx_io_get_input_gpio(uint8_t gpio_bank);
|
||||
|
||||
/* Get a 10-bit (!) spinner position, where spinner_no is 0 or 1.
|
||||
High six bits are ignored. */
|
||||
|
||||
uint16_t sdvx_io_get_spinner_pos(uint8_t spinner_no);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef BEMANITOOLS_VEFXIO_H
|
||||
#define BEMANITOOLS_VEFXIO_H
|
||||
|
||||
/* IO emulation provider for beatmania IIDX Effector Panel. */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bemanitools/glue.h"
|
||||
|
||||
/* The first function that will be called on your DLL. You will be supplied
|
||||
with four function pointers that may be used to log messages to the game's
|
||||
log file. See comments in glue.h for further information. */
|
||||
|
||||
void vefx_io_set_loggers(log_formatter_t misc, log_formatter_t info,
|
||||
log_formatter_t warning, log_formatter_t fatal);
|
||||
|
||||
/* Initialize your IIDX IO emulation DLL. Thread management functions are
|
||||
provided to you; you must use these functions to create your own threads if
|
||||
you want to make use of the logging functions that are provided to
|
||||
eam_io_set_loggers(). You will also need to pass these thread management
|
||||
functions on to geninput if you intend to make use of that library.
|
||||
|
||||
See glue.h and geninput.h for further details. */
|
||||
|
||||
bool vefx_io_init(thread_create_t thread_create, thread_join_t thread_join,
|
||||
thread_destroy_t thread_destroy);
|
||||
|
||||
/* Shut down your IIDX IO emulation DLL */
|
||||
|
||||
void vefx_io_fini(void);
|
||||
|
||||
/* Read input state from the input controller. This function is called
|
||||
immediately before the vefx_io_get_slider() function.
|
||||
|
||||
Return false in the event of an IO error. This will lock the game into an
|
||||
IO error screen.
|
||||
|
||||
If making a custom driver, ppad can be used to update regular IO if needed
|
||||
|
||||
See iidxio.c for mappings. */
|
||||
|
||||
bool vefx_io_recv(uint64_t* ppad);
|
||||
|
||||
/* Get slider position, where 0 is the bottom position and 15 is the topmost
|
||||
position. slider_no is a number between 0 (leftmost) and 4 (rightmost). */
|
||||
|
||||
uint8_t vefx_io_get_slider(uint8_t slider_no);
|
||||
|
||||
/* Write a nine-character string to the 16-segment display. This happens on a
|
||||
different schedule to all of the other IO operations, so you should initiate
|
||||
the communication as soon as this function is called */
|
||||
|
||||
bool vefx_io_write_16seg(const char *text);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
avsdlls += bsthook
|
||||
|
||||
deplibs_bsthook := \
|
||||
avs \
|
||||
|
||||
libs_bsthook := \
|
||||
acioemu \
|
||||
bstio \
|
||||
hook \
|
||||
hooklib \
|
||||
util \
|
||||
eamio \
|
||||
|
||||
src_bsthook := \
|
||||
acio.c \
|
||||
dllmain.c \
|
||||
gfx.c \
|
||||
kfca.c \
|
||||
settings.c \
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <ntdef.h>
|
||||
#include <devioctl.h>
|
||||
#include <ntddser.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "acioemu/addr.h"
|
||||
#include "acioemu/emu.h"
|
||||
#include "acioemu/icca.h"
|
||||
|
||||
#include "bsthook/acio.h"
|
||||
#include "bsthook/kfca.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/iobuf.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static struct ac_io_emu ac_io_emu;
|
||||
static struct ac_io_emu_icca ac_io_emu_icca;
|
||||
|
||||
void ac_io_bus_init(void)
|
||||
{
|
||||
ac_io_emu_init(&ac_io_emu, L"COM2");
|
||||
ac_io_emu_icca_init(&ac_io_emu_icca, &ac_io_emu, 0);
|
||||
kfca_init(&ac_io_emu);
|
||||
}
|
||||
|
||||
void ac_io_bus_fini(void)
|
||||
{
|
||||
ac_io_emu_fini(&ac_io_emu);
|
||||
}
|
||||
|
||||
HRESULT ac_io_bus_dispatch_irp(struct irp *irp)
|
||||
{
|
||||
const struct ac_io_message *msg;
|
||||
HRESULT hr;
|
||||
|
||||
log_assert(irp != NULL);
|
||||
|
||||
if (!ac_io_emu_match_irp(&ac_io_emu, irp)) {
|
||||
return irp_invoke_next(irp);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
hr = ac_io_emu_dispatch_irp(&ac_io_emu, irp);
|
||||
|
||||
if (hr != S_OK) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
msg = ac_io_emu_request_peek(&ac_io_emu);
|
||||
|
||||
switch (msg->addr) {
|
||||
case 0:
|
||||
ac_io_emu_cmd_assign_addrs(&ac_io_emu, msg, 2);
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
ac_io_emu_icca_dispatch_request(&ac_io_emu_icca, msg);
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
kfca_dispatch_request(msg);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_BROADCAST:
|
||||
log_warning("Broadcast(?) message on BST ACIO bus?");
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning("ACIO message on unhandled bus address: %d",
|
||||
msg->addr);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ac_io_emu_request_pop(&ac_io_emu);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef IIDXHOOK_AC_IO_H
|
||||
#define IIDXHOOK_AC_IO_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
void ac_io_bus_init(void);
|
||||
void ac_io_bus_fini(void);
|
||||
HRESULT ac_io_bus_dispatch_irp(struct irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,4 @@
|
||||
LIBRARY bsthook
|
||||
|
||||
EXPORTS
|
||||
DllMain@12 @1 NONAME
|
||||
@@ -0,0 +1,136 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "bemanitools/bstio.h"
|
||||
#include "bemanitools/eamio.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/app.h"
|
||||
#include "hooklib/rs232.h"
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "bsthook/acio.h"
|
||||
#include "bsthook/gfx.h"
|
||||
#include "bsthook/settings.h"
|
||||
|
||||
#include "util/cmdline.h"
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static const irp_handler_t bsthook_handlers[] = {
|
||||
ac_io_bus_dispatch_irp,
|
||||
};
|
||||
|
||||
static bool my_dll_entry_init(char *sidcode, struct property_node *config);
|
||||
static bool my_dll_entry_main(void);
|
||||
|
||||
static bool my_dll_entry_init(char *sidcode, struct property_node *config)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
log_info("--- Begin bsthook dll_entry_init ---");
|
||||
|
||||
ac_io_bus_init();
|
||||
|
||||
log_info("Starting up BeatStream IO backend");
|
||||
|
||||
bst_io_set_loggers(
|
||||
log_body_misc,
|
||||
log_body_info,
|
||||
log_body_warning,
|
||||
log_body_fatal);
|
||||
|
||||
ok = bst_io_init(avs_thread_create, avs_thread_join, avs_thread_destroy);
|
||||
|
||||
if (!ok) {
|
||||
goto bst_io_fail;
|
||||
}
|
||||
|
||||
eam_io_set_loggers(
|
||||
log_body_misc,
|
||||
log_body_info,
|
||||
log_body_warning,
|
||||
log_body_fatal);
|
||||
|
||||
ok = eam_io_init(avs_thread_create, avs_thread_join, avs_thread_destroy);
|
||||
|
||||
if (!ok) {
|
||||
goto eam_io_fail;
|
||||
}
|
||||
|
||||
log_info("--- End bsthook dll_entry_init ---");
|
||||
|
||||
return app_hook_invoke_init(sidcode, config);
|
||||
|
||||
eam_io_fail:
|
||||
bst_io_fini();
|
||||
|
||||
bst_io_fail:
|
||||
ac_io_bus_fini();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool my_dll_entry_main(void)
|
||||
{
|
||||
bool result;
|
||||
|
||||
result = app_hook_invoke_main();
|
||||
|
||||
log_info("Shutting down card reader backend");
|
||||
eam_io_fini();
|
||||
|
||||
log_info("Shutting down SDVX IO backend");
|
||||
bst_io_fini();
|
||||
|
||||
ac_io_bus_fini();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HMODULE self, DWORD reason, void *ctx)
|
||||
{
|
||||
int i;
|
||||
int argc;
|
||||
char **argv;
|
||||
|
||||
if (reason != DLL_PROCESS_ATTACH) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
log_to_external(
|
||||
log_body_misc,
|
||||
log_body_info,
|
||||
log_body_warning,
|
||||
log_body_fatal);
|
||||
|
||||
args_recover(&argc, &argv);
|
||||
|
||||
for (i = 1 ; i < argc ; i++) {
|
||||
if (argv[i][0] != '-') {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (argv[i][1]) {
|
||||
case 'w':
|
||||
gfx_set_windowed();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
args_free(argc, argv);
|
||||
|
||||
app_hook_init(my_dll_entry_init, my_dll_entry_main);
|
||||
iohook_init(bsthook_handlers, lengthof(bsthook_handlers));
|
||||
rs232_hook_init();
|
||||
|
||||
gfx_init();
|
||||
settings_hook_init();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
#include <windows.h>
|
||||
#include <d3d9.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "hook/com-proxy.h"
|
||||
#include "hook/pe.h"
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "sdvxhook/gfx.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static HRESULT STDCALL my_CreateDevice(
|
||||
IDirect3D9 *self, UINT adapter, D3DDEVTYPE type, HWND hwnd, DWORD flags,
|
||||
D3DPRESENT_PARAMETERS *pp, IDirect3DDevice9 **pdev);
|
||||
static IDirect3D9 *STDCALL my_Direct3DCreate9(UINT sdk_ver);
|
||||
|
||||
static IDirect3D9 * (STDCALL *real_Direct3DCreate9)(UINT sdk_ver);
|
||||
|
||||
static const struct hook_symbol gfx_hook_syms[] = {
|
||||
{
|
||||
.name = "Direct3DCreate9",
|
||||
.patch = my_Direct3DCreate9,
|
||||
.link = (void **) &real_Direct3DCreate9
|
||||
},
|
||||
};
|
||||
|
||||
static bool gfx_windowed;
|
||||
|
||||
static HRESULT STDCALL my_CreateDevice(
|
||||
IDirect3D9 *self, UINT adapter, D3DDEVTYPE type, HWND hwnd, DWORD flags,
|
||||
D3DPRESENT_PARAMETERS *pp, IDirect3DDevice9 **pdev)
|
||||
{
|
||||
IDirect3D9 *real = COM_PROXY_UNWRAP(self);
|
||||
HRESULT hr;
|
||||
|
||||
log_misc("IDirect3D9::CreateDevice hook hit");
|
||||
|
||||
if (gfx_windowed) {
|
||||
pp->Windowed = TRUE;
|
||||
pp->FullScreen_RefreshRateInHz = 0;
|
||||
}
|
||||
|
||||
hr = IDirect3D9_CreateDevice(real, adapter, type, hwnd, flags, pp, pdev);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static IDirect3D9 *STDCALL my_Direct3DCreate9(UINT sdk_ver)
|
||||
{
|
||||
IDirect3D9 *api;
|
||||
IDirect3D9Vtbl *api_vtbl;
|
||||
struct com_proxy *api_proxy;
|
||||
|
||||
log_info("Direct3DCreate9 hook hit");
|
||||
|
||||
api = real_Direct3DCreate9(sdk_ver);
|
||||
api_proxy = com_proxy_wrap(api, sizeof(*api->lpVtbl));
|
||||
api_vtbl = api_proxy->vptr;
|
||||
|
||||
api_vtbl->CreateDevice = my_CreateDevice;
|
||||
|
||||
return (IDirect3D9 *) api_proxy;
|
||||
}
|
||||
|
||||
void gfx_init(void)
|
||||
{
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"d3d9.dll",
|
||||
gfx_hook_syms,
|
||||
lengthof(gfx_hook_syms));
|
||||
|
||||
log_info("Inserted graphics hooks");
|
||||
}
|
||||
|
||||
void gfx_set_windowed(void)
|
||||
{
|
||||
gfx_windowed = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef BSTHOOK_GFX_H
|
||||
#define BSTHOOK_GFX_H
|
||||
|
||||
void gfx_init(void);
|
||||
void gfx_set_windowed(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,136 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "acio/acio.h"
|
||||
|
||||
#include "acioemu/emu.h"
|
||||
|
||||
#include "bemanitools/bstio.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
|
||||
static void kfca_send_version(const struct ac_io_message *req);
|
||||
static void kfca_report_status(const struct ac_io_message *req, uint8_t status);
|
||||
static void kfca_report_nil(const struct ac_io_message *req);
|
||||
static void kfca_poll(const struct ac_io_message *req);
|
||||
|
||||
static struct ac_io_emu *kfca_ac_io_emu;
|
||||
|
||||
void kfca_init(struct ac_io_emu *emu)
|
||||
{
|
||||
kfca_ac_io_emu = emu;
|
||||
}
|
||||
|
||||
void kfca_dispatch_request(const struct ac_io_message *req)
|
||||
{
|
||||
uint16_t cmd_code;
|
||||
|
||||
cmd_code = ac_io_u16(req->cmd.code);
|
||||
|
||||
switch (cmd_code) {
|
||||
case AC_IO_CMD_GET_VERSION:
|
||||
log_misc("AC_IO_CMD_GET_VERSION(%d)", req->addr);
|
||||
kfca_send_version(req);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_START_UP:
|
||||
log_misc("AC_IO_CMD_START_UP(%d)", req->addr);
|
||||
kfca_report_status(req, 0x00);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_KFCA_POLL:
|
||||
kfca_poll(req);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_KFCA_UNK_0120:
|
||||
log_misc("AC_IO_CMD_KFCA_UNK_%04X(%d)", cmd_code, req->addr);
|
||||
kfca_report_status(req, 0x00);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_KFCA_UNK_0128:
|
||||
log_misc("AC_IO_CMD_KFCA_UNK_%04X(%d)", cmd_code, req->addr);
|
||||
kfca_report_nil(req);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning("Unknown ACIO message %04x on KFCA mode, addr=%d",
|
||||
cmd_code, req->addr);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void kfca_send_version(const struct ac_io_message *req)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(resp.cmd.version);
|
||||
resp.cmd.version.type = ac_io_u32(AC_IO_NODE_TYPE_KFCA);
|
||||
resp.cmd.version.flag = 0x00;
|
||||
resp.cmd.version.major = 0x01;
|
||||
resp.cmd.version.minor = 0x01;
|
||||
resp.cmd.version.revision = 0x00;
|
||||
memcpy(resp.cmd.version.product_code, "KFCA",
|
||||
sizeof(resp.cmd.version.product_code));
|
||||
strncpy(resp.cmd.version.date, __DATE__, sizeof(resp.cmd.version.date));
|
||||
strncpy(resp.cmd.version.time, __TIME__, sizeof(resp.cmd.version.time));
|
||||
|
||||
ac_io_emu_response_push(kfca_ac_io_emu, &resp, 0);
|
||||
}
|
||||
|
||||
static void kfca_report_status(const struct ac_io_message *req, uint8_t status)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(resp.cmd.status);
|
||||
resp.cmd.status = status;
|
||||
|
||||
ac_io_emu_response_push(kfca_ac_io_emu, &resp, 0);
|
||||
}
|
||||
|
||||
static void kfca_report_nil(const struct ac_io_message *req)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = 0;
|
||||
|
||||
ac_io_emu_response_push(kfca_ac_io_emu, &resp, 0);
|
||||
}
|
||||
|
||||
static void kfca_poll(const struct ac_io_message *req)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
struct ac_io_kfca_poll_in *pin;
|
||||
|
||||
bst_io_read_input();
|
||||
|
||||
pin = &resp.cmd.kfca_poll_in;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(*pin);
|
||||
|
||||
memset(pin, 0, sizeof(*pin));
|
||||
|
||||
pin->gpio_sys = ac_io_u16(bst_io_get_input());
|
||||
|
||||
ac_io_emu_response_push(kfca_ac_io_emu, &resp, 0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef BSTHOOK_KFCA_H
|
||||
#define BSTHOOK_KFCA_H
|
||||
|
||||
#include "acio/acio.h"
|
||||
|
||||
#include "acioemu/emu.h"
|
||||
|
||||
void kfca_init(struct ac_io_emu *in);
|
||||
void kfca_dispatch_request(const struct ac_io_message *req);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,91 @@
|
||||
#define LOG_MODULE "settings-hook"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static HANDLE STDCALL my_CreateFileA(
|
||||
LPCSTR lpFileName,
|
||||
DWORD dwDesiredAccess,
|
||||
DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
||||
DWORD dwCreationDisposition,
|
||||
DWORD dwFlagsAndAttributes,
|
||||
HANDLE hTemplateFile);
|
||||
|
||||
static HANDLE (STDCALL *real_CreateFileA)(
|
||||
LPCSTR lpFileName,
|
||||
DWORD dwDesiredAccess,
|
||||
DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
||||
DWORD dwCreationDisposition,
|
||||
DWORD dwFlagsAndAttributes,
|
||||
HANDLE hTemplateFile);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static const struct hook_symbol settings_hook_syms[] = {
|
||||
{
|
||||
.name = "CreateFileA",
|
||||
.patch = my_CreateFileA,
|
||||
.link = (void **) &real_CreateFileA
|
||||
},
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static HANDLE STDCALL my_CreateFileA(
|
||||
LPCSTR lpFileName,
|
||||
DWORD dwDesiredAccess,
|
||||
DWORD dwShareMode,
|
||||
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
||||
DWORD dwCreationDisposition,
|
||||
DWORD dwFlagsAndAttributes,
|
||||
HANDLE hTemplateFile)
|
||||
{
|
||||
if ( lpFileName != NULL &&
|
||||
lpFileName[0] == 'e' &&
|
||||
lpFileName[1] == ':') {
|
||||
HANDLE handle;
|
||||
char new_path[MAX_PATH];
|
||||
|
||||
strcpy(new_path, lpFileName);
|
||||
new_path[1] = '\\';
|
||||
log_misc("Remapped settings path %s", new_path);
|
||||
|
||||
handle = real_CreateFileA(new_path, dwDesiredAccess, dwShareMode,
|
||||
lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
return real_CreateFileA(lpFileName, dwDesiredAccess, dwShareMode,
|
||||
lpSecurityAttributes, dwCreationDisposition,
|
||||
dwFlagsAndAttributes, hTemplateFile);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
void settings_hook_init(void)
|
||||
{
|
||||
hook_table_apply(
|
||||
NULL,
|
||||
"kernel32.dll",
|
||||
settings_hook_syms,
|
||||
lengthof(settings_hook_syms));
|
||||
|
||||
log_info("Inserted settings hooks");
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef BSTHOOK_SETTINGS_H
|
||||
#define BSTHOOK_SETTINGS_H
|
||||
|
||||
/**
|
||||
* Remaps the paths for the settings drive e:\
|
||||
* to the local folder e\.
|
||||
*/
|
||||
void settings_hook_init(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
dlls += bstio
|
||||
|
||||
libs_bstio := \
|
||||
geninput \
|
||||
|
||||
src_bstio := \
|
||||
bstio.c \
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bemanitools/bstio.h"
|
||||
#include "bemanitools/glue.h"
|
||||
#include "bemanitools/input.h"
|
||||
|
||||
static uint8_t bst_io_gpio_sys;
|
||||
|
||||
void bst_io_set_loggers(log_formatter_t misc, log_formatter_t info,
|
||||
log_formatter_t warning, log_formatter_t fatal)
|
||||
{
|
||||
input_set_loggers(misc, info, warning, fatal);
|
||||
}
|
||||
|
||||
bool bst_io_init(thread_create_t thread_create, thread_join_t thread_join,
|
||||
thread_destroy_t thread_destroy)
|
||||
{
|
||||
input_init(thread_create, thread_join, thread_destroy);
|
||||
mapper_config_load("bst");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void bst_io_fini(void)
|
||||
{
|
||||
input_fini();
|
||||
}
|
||||
|
||||
bool bst_io_read_input(void)
|
||||
{
|
||||
bst_io_gpio_sys = mapper_update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t bst_io_get_input(void)
|
||||
{
|
||||
return bst_io_gpio_sys;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
LIBRARY bstio
|
||||
|
||||
EXPORTS
|
||||
bst_io_fini
|
||||
bst_io_get_input
|
||||
bst_io_init
|
||||
bst_io_read_input
|
||||
bst_io_set_loggers
|
||||
@@ -0,0 +1,11 @@
|
||||
libs += cconfig
|
||||
|
||||
libs_cconfig := \
|
||||
util \
|
||||
|
||||
src_cconfig := \
|
||||
cconfig-hook.c \
|
||||
cconfig-util.c \
|
||||
cconfig.c \
|
||||
cmd.c \
|
||||
conf.c \
|
||||
@@ -0,0 +1,96 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "cconfig/cconfig-util.h"
|
||||
#include "cconfig/cmd.h"
|
||||
#include "cconfig/conf.h"
|
||||
|
||||
#include "cconfig/cconfig-hook.h"
|
||||
|
||||
#include "util/cmdline.h"
|
||||
#include "util/log.h"
|
||||
|
||||
bool cconfig_hook_config_init(struct cconfig* config, const char* usage_header,
|
||||
enum cconfig_cmd_usage_out cmd_usage_out)
|
||||
{
|
||||
bool success;
|
||||
int argc;
|
||||
char **argv;
|
||||
enum cconfig_conf_error conf_error;
|
||||
char* config_path;
|
||||
|
||||
success = true;
|
||||
|
||||
args_recover(&argc, &argv);
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
||||
goto failure_usage;
|
||||
}
|
||||
}
|
||||
|
||||
config_path = NULL;
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "--config")) {
|
||||
if (i + 1 >= argc) {
|
||||
log_fatal("--config parameter not followed by a config file "
|
||||
"path param");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
config_path = argv[i + 1];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (config_path) {
|
||||
log_misc("Loading config file: %s", config_path);
|
||||
conf_error = cconfig_conf_load_from_file(config, config_path, false);
|
||||
|
||||
if (conf_error == CCONFIG_CONF_ERROR_NO_SUCH_FILE) {
|
||||
/* Create default config */
|
||||
if (cconfig_conf_save_to_file(config, config_path) !=
|
||||
CCONFIG_CONF_SUCCESS) {
|
||||
log_fatal("Creating default config file '%s' failed",
|
||||
config_path);
|
||||
goto failure;
|
||||
} else {
|
||||
log_info("Default configuration '%s' created. Restart "
|
||||
"application", config_path);
|
||||
goto failure;
|
||||
}
|
||||
} else if (conf_error != CCONFIG_CONF_SUCCESS) {
|
||||
log_fatal("Error loading config file '%s': %d", config_path,
|
||||
conf_error);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
log_misc("Config state after file loading:");
|
||||
cconfig_util_log(config, log_impl_misc);
|
||||
}
|
||||
|
||||
log_misc("Parsing override config parameters from cmd");
|
||||
|
||||
/* Override defaults or values loaded from file with values from cmd */
|
||||
if (!cconfig_cmd_parse(config, "-p", argc, argv, false)) {
|
||||
log_fatal("Error parsing cmd args for config values");
|
||||
goto failure_usage;
|
||||
}
|
||||
|
||||
log_misc("Config state after cmd parameter overrides:");
|
||||
cconfig_util_log(config, log_impl_misc);
|
||||
|
||||
goto success;
|
||||
|
||||
failure_usage:
|
||||
cconfig_cmd_print_usage(config, usage_header, cmd_usage_out);
|
||||
|
||||
failure:
|
||||
success = false;
|
||||
|
||||
success:
|
||||
args_free(argc, argv);
|
||||
|
||||
return success;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef CCONFIG_HOOK_H
|
||||
#define CCONFIG_HOOK_H
|
||||
|
||||
#include "cconfig/cconfig.h"
|
||||
#include "cconfig/cmd.h"
|
||||
|
||||
bool cconfig_hook_config_init(struct cconfig* config, const char* usage_header,
|
||||
enum cconfig_cmd_usage_out cmd_usage_out);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,211 @@
|
||||
#define LOG_MODULE "cconfig-util"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cconfig/cconfig-util.h"
|
||||
|
||||
#include "util/hex.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
bool cconfig_util_get_int(struct cconfig* config, const char* key, int32_t* ret,
|
||||
int32_t default_value)
|
||||
{
|
||||
struct cconfig_entry* entry;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(key);
|
||||
|
||||
entry = cconfig_get(config, key);
|
||||
|
||||
if (entry) {
|
||||
if (sscanf(entry->value, "%d", ret) == 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
*ret = default_value;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cconfig_util_get_float(struct cconfig* config, const char* key, float* ret,
|
||||
float default_value)
|
||||
{
|
||||
struct cconfig_entry* entry;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(key);
|
||||
|
||||
entry = cconfig_get(config, key);
|
||||
|
||||
if (entry) {
|
||||
if (sscanf(entry->value, "%f", ret) == 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
*ret = default_value;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cconfig_util_get_bool(struct cconfig* config, const char* key, bool* ret,
|
||||
bool default_value)
|
||||
{
|
||||
struct cconfig_entry* entry;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(key);
|
||||
|
||||
entry = cconfig_get(config, key);
|
||||
|
||||
if (entry) {
|
||||
if (!strcmp(entry->value, "true")) {
|
||||
*ret = true;
|
||||
return true;
|
||||
} else if (!strcmp(entry->value, "false")) {
|
||||
*ret = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
*ret = default_value;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cconfig_util_get_str(struct cconfig* config, const char* key,
|
||||
char* buffer, size_t len, const char* default_value)
|
||||
{
|
||||
struct cconfig_entry* entry;
|
||||
size_t str_len;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(key);
|
||||
|
||||
entry = cconfig_get(config, key);
|
||||
|
||||
if (entry) {
|
||||
str_len = strlen(entry->value);
|
||||
|
||||
if (str_len <= len) {
|
||||
strcpy(buffer, entry->value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
strcpy(buffer, default_value);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cconfig_util_get_data(struct cconfig* config, const char* key,
|
||||
uint8_t* buffer, size_t len, const uint8_t* default_value)
|
||||
{
|
||||
size_t res_len;
|
||||
struct cconfig_entry* entry;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(key);
|
||||
log_assert(len);
|
||||
|
||||
entry = cconfig_get(config, key);
|
||||
|
||||
if (entry) {
|
||||
res_len = strlen(entry->value);
|
||||
res_len = res_len / 2 + res_len % 2;
|
||||
|
||||
if (len <= res_len) {
|
||||
if (hex_decode(buffer, len, entry->value, strlen(entry->value))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(buffer, default_value, len);
|
||||
return false;
|
||||
}
|
||||
|
||||
void cconfig_util_set_int(struct cconfig* config, const char* key,
|
||||
int32_t value, const char* desc)
|
||||
{
|
||||
char* str;
|
||||
size_t str_len;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(key);
|
||||
log_assert(desc);
|
||||
|
||||
str_len = snprintf(NULL, 0, "%d", value) + 1;
|
||||
str = xmalloc(str_len);
|
||||
snprintf(str, str_len, "%d", value);
|
||||
|
||||
cconfig_set(config, key, str, desc);
|
||||
|
||||
free(str);
|
||||
}
|
||||
|
||||
void cconfig_util_set_float(struct cconfig* config, const char* key,
|
||||
float value, const char* desc)
|
||||
{
|
||||
char* str;
|
||||
size_t str_len;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(key);
|
||||
log_assert(desc);
|
||||
|
||||
str_len = snprintf(NULL, 0, "%f", value) + 1;
|
||||
str = xmalloc(str_len);
|
||||
snprintf(str, str_len, "%f", value);
|
||||
|
||||
cconfig_set(config, key, str, desc);
|
||||
|
||||
free(str);
|
||||
}
|
||||
|
||||
void cconfig_util_set_bool(struct cconfig* config, const char* key, bool value,
|
||||
const char* desc)
|
||||
{
|
||||
log_assert(config);
|
||||
log_assert(key);
|
||||
log_assert(desc);
|
||||
|
||||
cconfig_set(config, key, value ? "true" : "false", desc);
|
||||
}
|
||||
|
||||
void cconfig_util_set_str(struct cconfig* config, const char* key,
|
||||
const char* value, const char* desc)
|
||||
{
|
||||
log_assert(config);
|
||||
log_assert(key);
|
||||
log_assert(desc);
|
||||
log_assert(value);
|
||||
|
||||
cconfig_set(config, key, value, desc);
|
||||
}
|
||||
|
||||
void cconfig_util_set_data(struct cconfig* config, const char* key,
|
||||
const uint8_t* value, size_t len, const char* desc)
|
||||
{
|
||||
char* str;
|
||||
size_t str_len;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(key);
|
||||
log_assert(desc);
|
||||
|
||||
str_len = len * 2 + 1;
|
||||
str = xmalloc(str_len);
|
||||
hex_encode_uc(value, len, str, str_len);
|
||||
|
||||
cconfig_set(config, key, str, desc);
|
||||
|
||||
free(str);
|
||||
}
|
||||
|
||||
void cconfig_util_log(struct cconfig* config, log_formatter_t log_formatter)
|
||||
{
|
||||
for (uint32_t i = 0; i < config->nentries; i++) {
|
||||
log_formatter(LOG_MODULE, "%s=%s", config->entries[i].key,
|
||||
config->entries[i].value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef CCONFIG_UTIL_H
|
||||
#define CCONFIG_UTIL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "cconfig/cconfig.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
bool cconfig_util_get_int(struct cconfig* config, const char* key, int32_t* ret,
|
||||
int32_t default_value);
|
||||
|
||||
bool cconfig_util_get_float(struct cconfig* config, const char* key, float* ret,
|
||||
float default_value);
|
||||
|
||||
bool cconfig_util_get_bool(struct cconfig* config, const char* key, bool* ret,
|
||||
bool default_value);
|
||||
|
||||
bool cconfig_util_get_str(struct cconfig* config, const char* key,
|
||||
char* buffer, size_t len, const char* default_value);
|
||||
|
||||
bool cconfig_util_get_data(struct cconfig* config, const char* key,
|
||||
uint8_t* buffer, size_t len, const uint8_t* default_value);
|
||||
|
||||
void cconfig_util_set_int(struct cconfig* config, const char* key,
|
||||
int32_t value, const char* desc);
|
||||
|
||||
void cconfig_util_set_float(struct cconfig* config, const char* key,
|
||||
float value, const char* desc);
|
||||
|
||||
void cconfig_util_set_bool(struct cconfig* config, const char* key, bool value,
|
||||
const char* desc);
|
||||
|
||||
void cconfig_util_set_str(struct cconfig* config, const char* key,
|
||||
const char* value, const char* desc);
|
||||
|
||||
void cconfig_util_set_data(struct cconfig* config, const char* key,
|
||||
const uint8_t* value, size_t len, const char* desc);
|
||||
|
||||
void cconfig_util_log(struct cconfig* config, log_formatter_t log_formatter);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,109 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "cconfig/cconfig.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static struct cconfig_entry* cconfig_extend_config(struct cconfig* config)
|
||||
{
|
||||
config->nentries++;
|
||||
|
||||
config->entries = xrealloc(config->entries,
|
||||
config->nentries * sizeof(struct cconfig_entry));
|
||||
memset(&config->entries[config->nentries - 1], 0,
|
||||
sizeof(struct cconfig_entry));
|
||||
|
||||
return &config->entries[config->nentries - 1];
|
||||
}
|
||||
|
||||
struct cconfig* cconfig_init()
|
||||
{
|
||||
struct cconfig* config;
|
||||
|
||||
config = xmalloc(sizeof(struct cconfig));
|
||||
memset(config, 0, sizeof(struct cconfig));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
struct cconfig_entry* cconfig_get(struct cconfig* config, const char* key)
|
||||
{
|
||||
log_assert(config);
|
||||
log_assert(key);
|
||||
|
||||
for (uint32_t i = 0; i < config->nentries; i++) {
|
||||
if (!strcmp(config->entries[i].key, key)) {
|
||||
return &config->entries[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void cconfig_set(struct cconfig* config, const char* key, const char* value,
|
||||
const char* desc)
|
||||
{
|
||||
struct cconfig_entry* entry;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(key);
|
||||
log_assert(value);
|
||||
log_assert(desc);
|
||||
|
||||
entry = cconfig_get(config, key);
|
||||
|
||||
if (!entry) {
|
||||
entry = cconfig_extend_config(config);
|
||||
} else {
|
||||
free(entry->key);
|
||||
free(entry->value);
|
||||
free(entry->desc);
|
||||
|
||||
memset(entry, 0, sizeof(struct cconfig_entry));
|
||||
}
|
||||
|
||||
entry->key = str_dup(key);
|
||||
entry->desc = str_dup(desc);
|
||||
entry->value = str_dup(value);
|
||||
}
|
||||
|
||||
void cconfig_set2(struct cconfig* config, const char* key, const char* value)
|
||||
{
|
||||
struct cconfig_entry* entry;
|
||||
|
||||
log_assert(config);
|
||||
log_assert(key);
|
||||
log_assert(value);
|
||||
|
||||
entry = cconfig_get(config, key);
|
||||
|
||||
if (!entry) {
|
||||
entry = cconfig_extend_config(config);
|
||||
} else {
|
||||
free(entry->key);
|
||||
free(entry->value);
|
||||
}
|
||||
|
||||
entry->key = str_dup(key);
|
||||
entry->value = str_dup(value);
|
||||
|
||||
/* Description optional, but do not wipe previous description if
|
||||
available */
|
||||
if (!entry->desc) {
|
||||
entry->desc = "";
|
||||
}
|
||||
}
|
||||
|
||||
void cconfig_finit(struct cconfig* config)
|
||||
{
|
||||
for (uint32_t i = 0; i < config->nentries; i++) {
|
||||
free(config->entries[i].key);
|
||||
free(config->entries[i].value);
|
||||
free(config->entries[i].desc);
|
||||
}
|
||||
|
||||
free(config->entries);
|
||||
free(config);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef CCONFIG_H
|
||||
#define CCONFIG_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct cconfig_entry {
|
||||
char* key;
|
||||
char* value;
|
||||
char* desc;
|
||||
};
|
||||
|
||||
struct cconfig {
|
||||
uint32_t nentries;
|
||||
struct cconfig_entry* entries;
|
||||
};
|
||||
|
||||
struct cconfig* cconfig_init();
|
||||
|
||||
struct cconfig_entry* cconfig_get(struct cconfig* config,
|
||||
const char* key);
|
||||
|
||||
void cconfig_set(struct cconfig* config, const char* key, const char* value,
|
||||
const char* desc);
|
||||
|
||||
void cconfig_set2(struct cconfig* config, const char* key, const char* value);
|
||||
|
||||
void cconfig_finit(struct cconfig* config);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,132 @@
|
||||
#define LOG_MODULE "cconfig-cmd"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "cconfig/cmd.h"
|
||||
|
||||
#include "util/hex.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static void cconfig_cmd_usage_print(enum cconfig_cmd_usage_out output,
|
||||
const char* fmt, ...)
|
||||
{
|
||||
char buffer[32768];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
switch (output) {
|
||||
case CCONFIG_CMD_USAGE_OUT_STDOUT:
|
||||
vfprintf(stdout, fmt, ap);
|
||||
break;
|
||||
|
||||
case CCONFIG_CMD_USAGE_OUT_STDERR:
|
||||
vfprintf(stderr, fmt, ap);
|
||||
break;
|
||||
|
||||
case CCONFIG_CMD_USAGE_OUT_DBG:
|
||||
_vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
||||
OutputDebugString(buffer);
|
||||
break;
|
||||
|
||||
case CCONFIG_CMD_USAGE_OUT_LOG:
|
||||
_vsnprintf(buffer, sizeof(buffer), fmt, ap);
|
||||
log_info("%s", buffer);
|
||||
break;
|
||||
|
||||
default:
|
||||
log_assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
bool cconfig_cmd_parse(struct cconfig* config, const char* key_ident, int argc,
|
||||
char** argv, bool add_params_if_absent)
|
||||
{
|
||||
bool no_error;
|
||||
struct cconfig_entry* entry;
|
||||
char* tmp;
|
||||
char* cur_tok;
|
||||
int ntok;
|
||||
char* toks[2];
|
||||
|
||||
no_error = true;
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (!strcmp(argv[i], key_ident)) {
|
||||
if (i + 1 >= argc) {
|
||||
no_error = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Not another key ident is following */
|
||||
if (!strcmp(argv[i + 1], key_ident)) {
|
||||
no_error = false;
|
||||
break;
|
||||
}
|
||||
|
||||
++i;
|
||||
|
||||
tmp = str_dup(argv[i]);
|
||||
ntok = 0;
|
||||
|
||||
cur_tok = strtok(tmp, "=");
|
||||
|
||||
while (cur_tok != NULL) {
|
||||
toks[ntok] = cur_tok;
|
||||
ntok++;
|
||||
cur_tok = strtok(NULL, "=");
|
||||
|
||||
if (ntok == 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ntok != 2) {
|
||||
/* Tokenizing key=value parameter error */
|
||||
log_warning("Parsing parameter '%s' failed, ignore", argv[i]);
|
||||
|
||||
free(tmp);
|
||||
no_error = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
log_misc("Key: %s, Value: %s", toks[0], toks[1]);
|
||||
|
||||
entry = cconfig_get(config, toks[0]);
|
||||
|
||||
if (entry || add_params_if_absent) {
|
||||
cconfig_set2(config, toks[0], toks[1]);
|
||||
} else {
|
||||
/* Ignore cmd params that are not found in config */
|
||||
log_warning("Could not find cmd parameter with key '%s' in "
|
||||
"config, ignored", toks[0]);
|
||||
}
|
||||
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
return no_error;
|
||||
}
|
||||
|
||||
void cconfig_cmd_print_usage(struct cconfig* config, const char* usage_header,
|
||||
enum cconfig_cmd_usage_out output)
|
||||
{
|
||||
cconfig_cmd_usage_print(output, "%s\n", usage_header);
|
||||
|
||||
for (uint32_t i = 0; i < config->nentries; i++) {
|
||||
cconfig_cmd_usage_print(output,
|
||||
" %s: %s\n"
|
||||
" default: %s\n",
|
||||
config->entries[i].key,
|
||||
config->entries[i].desc,
|
||||
config->entries[i].value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef CCONFIG_CMD_H
|
||||
#define CCONFIG_CMD_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "cconfig/cconfig.h"
|
||||
|
||||
enum cconfig_cmd_usage_out {
|
||||
CCONFIG_CMD_USAGE_OUT_STDOUT,
|
||||
CCONFIG_CMD_USAGE_OUT_STDERR,
|
||||
CCONFIG_CMD_USAGE_OUT_DBG,
|
||||
CCONFIG_CMD_USAGE_OUT_LOG,
|
||||
};
|
||||
|
||||
bool cconfig_cmd_parse(struct cconfig* config, const char* key_ident, int argc,
|
||||
char** argv, bool add_params_if_absent);
|
||||
|
||||
void cconfig_cmd_print_usage(struct cconfig* config, const char* usage_header,
|
||||
enum cconfig_cmd_usage_out output);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
#define LOG_MODULE "cconfig-conf"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cconfig/conf.h"
|
||||
|
||||
#include "util/fs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
enum cconfig_conf_error cconfig_conf_load_from_file(struct cconfig* config,
|
||||
const char* path, bool add_params_if_absent)
|
||||
{
|
||||
char* pos_lines;
|
||||
char* pos_key_val;
|
||||
char* ctx_lines;
|
||||
char* ctx_key_val;
|
||||
char* data;
|
||||
size_t len;
|
||||
|
||||
if (!file_load(path, (void**) &data, &len, true)) {
|
||||
/* If file does not exist, create one with default configuration
|
||||
values */
|
||||
if (path_exists(path)) {
|
||||
return CCONFIG_CONF_ERROR_FILE_CORRUPTED;
|
||||
} else {
|
||||
return CCONFIG_CONF_ERROR_NO_SUCH_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
pos_lines = strtok_r(data, "\n", &ctx_lines);
|
||||
|
||||
while (pos_lines != NULL) {
|
||||
char* pos_line_dup;
|
||||
char* key = NULL;
|
||||
char* val = NULL;
|
||||
int cnt = 0;
|
||||
struct cconfig_entry* entry;
|
||||
|
||||
/* ignore comments and empty lines */
|
||||
if (strlen(pos_lines) > 0 && pos_lines[0] != '#') {
|
||||
pos_line_dup = str_dup(pos_lines);
|
||||
pos_key_val = strtok_r(pos_line_dup, "=", &ctx_key_val);
|
||||
|
||||
log_misc("Line: %s", pos_lines);
|
||||
|
||||
while (pos_key_val != NULL) {
|
||||
|
||||
if (cnt == 0) {
|
||||
key = pos_key_val;
|
||||
} else if (cnt == 1) {
|
||||
val = pos_key_val;
|
||||
}
|
||||
|
||||
pos_key_val = strtok_r(NULL, "=", &ctx_key_val);
|
||||
cnt++;
|
||||
}
|
||||
|
||||
/* Key requiured, value can be NULL */
|
||||
if (cnt != 1 && cnt != 2) {
|
||||
log_warning("Invalid options line %s in options file %s",
|
||||
pos_lines, path);
|
||||
free(pos_line_dup);
|
||||
free(data);
|
||||
return CCONFIG_CONF_ERROR_PARSING;
|
||||
}
|
||||
|
||||
/* NULL not allowed but empty string */
|
||||
if (!val) {
|
||||
val = "";
|
||||
}
|
||||
|
||||
log_misc("Key: %s, Value: %s", key, val);
|
||||
|
||||
entry = cconfig_get(config, key);
|
||||
|
||||
if (entry || add_params_if_absent) {
|
||||
cconfig_set2(config, key, val);
|
||||
} else {
|
||||
/* Ignore cmd params that are not found in config */
|
||||
log_warning("Could not find parameter with key '%s' in "
|
||||
"config, ignored", key);
|
||||
}
|
||||
|
||||
free(pos_line_dup);
|
||||
}
|
||||
|
||||
pos_lines = strtok_r(NULL, "\n", &ctx_lines);
|
||||
}
|
||||
|
||||
free(data);
|
||||
|
||||
return CCONFIG_CONF_SUCCESS;
|
||||
}
|
||||
|
||||
enum cconfig_conf_error cconfig_conf_save_to_file(struct cconfig* config,
|
||||
const char* path)
|
||||
{
|
||||
FILE* file;
|
||||
|
||||
file = fopen(path, "wb+");
|
||||
|
||||
if (file == NULL) {
|
||||
return CCONFIG_CONF_ERROR_NO_SUCH_FILE;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < config->nentries; i++) {
|
||||
fprintf(file, "# %s\n", config->entries[i].desc);
|
||||
fprintf(file, "%s=%s\n\n", config->entries[i].key,
|
||||
config->entries[i].value);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return CCONFIG_CONF_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "cconfig/cconfig.h"
|
||||
|
||||
enum cconfig_conf_error {
|
||||
CCONFIG_CONF_SUCCESS = 0,
|
||||
CCONFIG_CONF_ERROR_NO_SUCH_FILE = 1,
|
||||
CCONFIG_CONF_ERROR_FILE_CORRUPTED = 2,
|
||||
CCONFIG_CONF_ERROR_PARSING = 3,
|
||||
};
|
||||
|
||||
enum cconfig_conf_error cconfig_conf_load_from_file(struct cconfig* config,
|
||||
const char* path, bool add_params_if_absent);
|
||||
|
||||
enum cconfig_conf_error cconfig_conf_save_to_file(struct cconfig* config,
|
||||
const char* path);
|
||||
@@ -0,0 +1,30 @@
|
||||
exes += config
|
||||
rc_config := config.rc
|
||||
cppflags_config := -DUNICODE
|
||||
|
||||
libs_config := \
|
||||
eamio \
|
||||
geninput \
|
||||
util \
|
||||
|
||||
ldflags_config := \
|
||||
-lcomctl32 \
|
||||
-lcomdlg32 \
|
||||
-lgdi32 \
|
||||
-mwindows \
|
||||
|
||||
src_config := \
|
||||
analogs.c \
|
||||
bind-adv.c \
|
||||
bind.c \
|
||||
bind-light.c \
|
||||
buttons.c \
|
||||
eam.c \
|
||||
gametype.c \
|
||||
lights.c \
|
||||
main.c \
|
||||
schema.c \
|
||||
snap.c \
|
||||
spinner.c \
|
||||
usages.c \
|
||||
|
||||
@@ -0,0 +1,540 @@
|
||||
#include <windows.h>
|
||||
#include <commctrl.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config/resource.h"
|
||||
#include "config/schema.h"
|
||||
#include "config/usages.h"
|
||||
|
||||
#include "geninput/hid-mgr.h"
|
||||
#include "geninput/input-config.h"
|
||||
#include "geninput/mapper.h"
|
||||
|
||||
#include "util/array.h"
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#define SENSITIVITY_SCALE 4
|
||||
|
||||
struct analogs_ui {
|
||||
struct array children;
|
||||
};
|
||||
|
||||
struct analog_ui {
|
||||
const struct analog_def *def;
|
||||
struct array hids;
|
||||
struct array control_nos;
|
||||
struct hid_stub *selected_hid;
|
||||
uint8_t pos;
|
||||
};
|
||||
|
||||
static INT_PTR CALLBACK analogs_ui_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
static INT_PTR analogs_ui_handle_init(HWND hwnd, const PROPSHEETPAGE *psp);
|
||||
static INT_PTR analogs_ui_handle_activate(HWND hwnd);
|
||||
static INT_PTR analogs_ui_handle_passivate(HWND hwnd);
|
||||
static INT_PTR analogs_ui_handle_tick(HWND hwnd);
|
||||
static INT_PTR analogs_ui_handle_fini(HWND hwnd);
|
||||
|
||||
static INT_PTR CALLBACK analog_ui_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
static INT_PTR analog_ui_handle_init(HWND hwnd, struct analog_def *def);
|
||||
static void analog_ui_handle_init_label(HWND hwnd);
|
||||
static void analog_ui_handle_init_dev(HWND hwnd);
|
||||
static void analog_ui_handle_init_sensitivity(HWND hwnd);
|
||||
static bool analog_ui_match_device(struct hid_stub *hid);
|
||||
static void analog_ui_populate_controls(HWND hwnd);
|
||||
static INT_PTR analog_ui_handle_device_change(HWND hwnd);
|
||||
static INT_PTR analog_ui_handle_control_change(HWND hwnd);
|
||||
static INT_PTR analog_ui_handle_sensitivity_change(HWND hwnd);
|
||||
static INT_PTR analog_ui_handle_tick(HWND hwnd);
|
||||
static INT_PTR analog_ui_handle_fini(HWND hwnd);
|
||||
|
||||
HPROPSHEETPAGE analogs_ui_tab_create(HINSTANCE inst,
|
||||
const struct schema *schema)
|
||||
{
|
||||
PROPSHEETPAGE psp;
|
||||
|
||||
memset(&psp, 0, sizeof(psp));
|
||||
psp.dwSize = sizeof(psp);
|
||||
psp.dwFlags = PSP_DEFAULT;
|
||||
psp.hInstance = inst;
|
||||
psp.pszTemplate = MAKEINTRESOURCE(IDD_TAB_ANALOGS);
|
||||
psp.pfnDlgProc = analogs_ui_dlg_proc;
|
||||
psp.lParam = (LPARAM) schema;
|
||||
|
||||
return CreatePropertySheetPage(&psp);
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK analogs_ui_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam)
|
||||
{
|
||||
const NMHDR *n;
|
||||
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG:
|
||||
return analogs_ui_handle_init(hwnd, (PROPSHEETPAGE *) lparam);
|
||||
|
||||
case WM_NOTIFY:
|
||||
n = (NMHDR *) lparam;
|
||||
|
||||
switch (n->code) {
|
||||
case PSN_SETACTIVE:
|
||||
return analogs_ui_handle_activate(hwnd);
|
||||
|
||||
case PSN_KILLACTIVE:
|
||||
return analogs_ui_handle_passivate(hwnd);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
||||
case WM_TIMER:
|
||||
return analogs_ui_handle_tick(hwnd);
|
||||
|
||||
case WM_DESTROY:
|
||||
return analogs_ui_handle_fini(hwnd);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static INT_PTR analogs_ui_handle_init(HWND hwnd, const PROPSHEETPAGE *psp)
|
||||
{
|
||||
struct analogs_ui *ui;
|
||||
const struct schema *schema;
|
||||
long ypos;
|
||||
size_t i;
|
||||
HINSTANCE inst;
|
||||
HWND child;
|
||||
RECT r;
|
||||
|
||||
ui = xmalloc(sizeof(*ui));
|
||||
array_init(&ui->children);
|
||||
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) ui);
|
||||
|
||||
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
||||
schema = (struct schema *) psp->lParam;
|
||||
|
||||
ypos = 0;
|
||||
|
||||
for (i = 0 ; i < schema->nanalogs ; i++) {
|
||||
child = CreateDialogParam(inst, MAKEINTRESOURCE(IDD_ANALOG), hwnd,
|
||||
analog_ui_dlg_proc, (LPARAM) &schema->analogs[i]);
|
||||
|
||||
GetWindowRect(child, &r);
|
||||
SetWindowPos(child, HWND_BOTTOM, 0, ypos, 0, 0,
|
||||
SWP_NOSIZE | SWP_SHOWWINDOW);
|
||||
|
||||
ypos += r.bottom - r.top;
|
||||
|
||||
*array_append(HWND, &ui->children) = child;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR analogs_ui_handle_activate(HWND hwnd)
|
||||
{
|
||||
SetTimer(hwnd, 1, 17, NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR analogs_ui_handle_passivate(HWND hwnd)
|
||||
{
|
||||
KillTimer(hwnd, 1);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR analogs_ui_handle_tick(HWND hwnd)
|
||||
{
|
||||
HWND child;
|
||||
struct analogs_ui *ui;
|
||||
size_t i;
|
||||
|
||||
mapper_update();
|
||||
|
||||
ui = (struct analogs_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
for (i = 0 ; i < ui->children.nitems ; i++) {
|
||||
child = *array_item(HWND, &ui->children, i);
|
||||
SendMessage(child, WM_USER, 0, 0);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR analogs_ui_handle_fini(HWND hwnd)
|
||||
{
|
||||
struct analogs_ui *ui;
|
||||
|
||||
ui = (struct analogs_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
array_fini(&ui->children);
|
||||
free(ui);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK analog_ui_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam)
|
||||
{
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG:
|
||||
return analog_ui_handle_init(hwnd, (struct analog_def *) lparam);
|
||||
|
||||
case WM_USER:
|
||||
return analog_ui_handle_tick(hwnd);
|
||||
|
||||
case WM_COMMAND:
|
||||
switch (HIWORD(wparam)) {
|
||||
case CBN_SELCHANGE:
|
||||
switch (LOWORD(wparam)) {
|
||||
case IDC_DEVICE:
|
||||
return analog_ui_handle_device_change(hwnd);
|
||||
|
||||
case IDC_CONTROL:
|
||||
return analog_ui_handle_control_change(hwnd);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
case WM_HSCROLL:
|
||||
if (GetDlgItem(hwnd, IDC_SENSITIVITY) == (HWND) lparam) {
|
||||
return analog_ui_handle_sensitivity_change(hwnd);
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
case WM_DESTROY:
|
||||
return analog_ui_handle_fini(hwnd);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static INT_PTR analog_ui_handle_init(HWND hwnd, struct analog_def *def)
|
||||
{
|
||||
struct analog_ui *ui;
|
||||
|
||||
ui = xmalloc(sizeof(*ui));
|
||||
ui->def = def;
|
||||
array_init(&ui->hids);
|
||||
array_init(&ui->control_nos);
|
||||
ui->selected_hid = NULL;
|
||||
ui->pos = 0;
|
||||
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) ui);
|
||||
|
||||
analog_ui_handle_init_label(hwnd);
|
||||
analog_ui_handle_init_dev(hwnd);
|
||||
analog_ui_handle_init_sensitivity(hwnd);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void analog_ui_handle_init_label(HWND hwnd)
|
||||
{
|
||||
struct analog_ui *ui;
|
||||
wchar_t label[128];
|
||||
HINSTANCE inst;
|
||||
HWND box;
|
||||
|
||||
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
||||
box = GetDlgItem(hwnd, IDC_GROUP);
|
||||
|
||||
LoadString(inst, ui->def->label_rsrc, label, lengthof(label));
|
||||
SendMessage(box, WM_SETTEXT, 0, (LPARAM) label);
|
||||
}
|
||||
|
||||
static void analog_ui_handle_init_dev(HWND hwnd)
|
||||
{
|
||||
struct analog_ui *ui;
|
||||
struct mapped_analog ma;
|
||||
wchar_t *dev_name;
|
||||
struct hid_stub *hid;
|
||||
size_t nchars;
|
||||
LRESULT index;
|
||||
HWND dev_list;
|
||||
|
||||
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
dev_list = GetDlgItem(hwnd, IDC_DEVICE);
|
||||
|
||||
SendMessage(dev_list, CB_ADDSTRING, 0, (LPARAM) L"");
|
||||
|
||||
hid_mgr_lock();
|
||||
|
||||
ma.hid = NULL;
|
||||
mapper_get_analog_map(ui->def->tag, &ma);
|
||||
|
||||
for (hid = hid_mgr_get_first_stub()
|
||||
; hid != NULL
|
||||
; hid = hid_mgr_get_next_stub(hid)) {
|
||||
if (!analog_ui_match_device(hid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!hid_stub_get_name(hid, NULL, &nchars)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dev_name = xmalloc(nchars * sizeof(*dev_name));
|
||||
|
||||
if (!hid_stub_get_name(hid, dev_name, &nchars)) {
|
||||
free(dev_name);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
index = SendMessage(dev_list, CB_ADDSTRING, 0, (LPARAM) dev_name);
|
||||
|
||||
free(dev_name);
|
||||
|
||||
*array_append(struct hid_stub *, &ui->hids) = hid;
|
||||
|
||||
if (ma.hid == hid) {
|
||||
SendMessage(dev_list, CB_SETCURSEL, index, 0);
|
||||
|
||||
ui->selected_hid = hid;
|
||||
analog_ui_populate_controls(hwnd);
|
||||
}
|
||||
}
|
||||
|
||||
hid_mgr_unlock();
|
||||
}
|
||||
|
||||
static void analog_ui_handle_init_sensitivity(HWND hwnd)
|
||||
{
|
||||
struct analog_ui *ui;
|
||||
int pos;
|
||||
HWND slider;
|
||||
|
||||
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
slider = GetDlgItem(hwnd, IDC_SENSITIVITY);
|
||||
pos = 256 * SENSITIVITY_SCALE + mapper_get_analog_sensitivity(ui->def->tag);
|
||||
|
||||
SendMessage(slider, TBM_SETTICFREQ, 256, 0);
|
||||
SendMessage(slider, TBM_SETRANGE, FALSE,
|
||||
MAKELPARAM(0, SENSITIVITY_SCALE * 256 * 2));
|
||||
SendMessage(slider, TBM_SETPOS, TRUE, (LPARAM) pos);
|
||||
|
||||
EnableWindow(slider, !mapper_is_analog_absolute(ui->def->tag));
|
||||
}
|
||||
|
||||
static bool analog_ui_match_device(struct hid_stub *hid)
|
||||
{
|
||||
struct hid_control *controls;
|
||||
size_t ncontrols;
|
||||
size_t i;
|
||||
|
||||
if (!hid_stub_get_controls(hid, NULL, &ncontrols)) {
|
||||
goto size_fail;
|
||||
}
|
||||
|
||||
controls = xmalloc(ncontrols * sizeof(*controls));
|
||||
|
||||
if (!hid_stub_get_controls(hid, controls, &ncontrols)) {
|
||||
goto content_fail;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < ncontrols ; i++) {
|
||||
if (controls[i].value_max - controls[i].value_min > 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(controls);
|
||||
|
||||
return i < ncontrols;
|
||||
|
||||
content_fail:
|
||||
free(controls);
|
||||
|
||||
size_fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
static void analog_ui_populate_controls(HWND hwnd)
|
||||
{
|
||||
char usage_desc[512];
|
||||
wchar_t *tmp;
|
||||
struct analog_ui *ui;
|
||||
struct mapped_analog ma;
|
||||
struct hid_control *controls;
|
||||
size_t ncontrols;
|
||||
size_t i;
|
||||
long nitems;
|
||||
LRESULT index;
|
||||
HWND controls_ctl;
|
||||
|
||||
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
controls_ctl = GetDlgItem(hwnd, IDC_CONTROL);
|
||||
nitems = (long) SendMessage(controls_ctl, CB_GETCOUNT, 0, 0);
|
||||
|
||||
InvalidateRect(controls_ctl, NULL, TRUE);
|
||||
|
||||
for (index = nitems ; index >= 0 ; index--) {
|
||||
SendMessage(controls_ctl, CB_DELETESTRING, index, 0);
|
||||
}
|
||||
|
||||
array_fini(&ui->control_nos);
|
||||
array_init(&ui->control_nos);
|
||||
|
||||
if (ui->selected_hid == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hid_stub_get_controls(ui->selected_hid, NULL, &ncontrols)) {
|
||||
goto size_fail;
|
||||
}
|
||||
|
||||
controls = xmalloc(sizeof(*controls) * ncontrols);
|
||||
|
||||
if (!hid_stub_get_controls(ui->selected_hid, controls, &ncontrols)) {
|
||||
goto content_fail;
|
||||
}
|
||||
|
||||
SendMessage(controls_ctl, CB_ADDSTRING, 0, (LPARAM) L"");
|
||||
|
||||
mapper_get_analog_map(ui->def->tag, &ma);
|
||||
|
||||
for (i = 0 ; i < ncontrols ; i++) {
|
||||
if (controls[i].value_max - controls[i].value_min <= 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
usages_get(usage_desc, lengthof(usage_desc), controls[i].usage);
|
||||
tmp = str_widen(usage_desc);
|
||||
|
||||
index = SendMessage(controls_ctl, CB_ADDSTRING, 0, (LPARAM) tmp);
|
||||
free(tmp);
|
||||
|
||||
if (i == ma.control_no) {
|
||||
SendMessage(controls_ctl, CB_SETCURSEL, index, 0);
|
||||
}
|
||||
|
||||
*array_append(size_t, &ui->control_nos) = i;
|
||||
}
|
||||
|
||||
free(controls);
|
||||
|
||||
return;
|
||||
|
||||
content_fail:
|
||||
free(controls);
|
||||
|
||||
size_fail:
|
||||
return;
|
||||
}
|
||||
|
||||
static INT_PTR analog_ui_handle_device_change(HWND hwnd)
|
||||
{
|
||||
struct mapped_analog ma;
|
||||
struct analog_ui *ui;
|
||||
LRESULT index;
|
||||
|
||||
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
index = SendMessage(GetDlgItem(hwnd, IDC_DEVICE), CB_GETCURSEL, 0, 0);
|
||||
|
||||
log_assert((size_t) index <= ui->hids.nitems);
|
||||
|
||||
if (index == 0) {
|
||||
ui->selected_hid = NULL;
|
||||
} else {
|
||||
ui->selected_hid = *array_item(struct hid_stub *, &ui->hids, index - 1);
|
||||
}
|
||||
|
||||
ma.hid = NULL;
|
||||
mapper_set_analog_map(ui->def->tag, &ma);
|
||||
|
||||
hid_mgr_lock();
|
||||
analog_ui_populate_controls(hwnd);
|
||||
hid_mgr_unlock();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR analog_ui_handle_control_change(HWND hwnd)
|
||||
{
|
||||
struct mapped_analog ma;
|
||||
struct analog_ui *ui;
|
||||
LRESULT index;
|
||||
HWND slider;
|
||||
|
||||
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
index = SendMessage(GetDlgItem(hwnd, IDC_CONTROL), CB_GETCURSEL, 0, 0);
|
||||
|
||||
log_assert((size_t) index <= ui->control_nos.nitems);
|
||||
|
||||
if (index == 0) {
|
||||
ma.hid = NULL;
|
||||
} else {
|
||||
ma.hid = ui->selected_hid;
|
||||
ma.control_no = *array_item(size_t, &ui->control_nos, index - 1);
|
||||
}
|
||||
|
||||
mapper_set_analog_map(ui->def->tag, &ma);
|
||||
|
||||
slider = GetDlgItem(hwnd, IDC_SENSITIVITY);
|
||||
|
||||
EnableWindow(slider, !mapper_is_analog_absolute(ui->def->tag));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR analog_ui_handle_sensitivity_change(HWND hwnd)
|
||||
{
|
||||
struct analog_ui *ui;
|
||||
int pos;
|
||||
HWND slider;
|
||||
|
||||
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
slider = GetDlgItem(hwnd, IDC_SENSITIVITY);
|
||||
pos = (int) SendMessage(slider, TBM_GETPOS, 0, 0);
|
||||
|
||||
mapper_set_analog_sensitivity(ui->def->tag, pos - 256 * SENSITIVITY_SCALE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR analog_ui_handle_tick(HWND hwnd)
|
||||
{
|
||||
struct analog_ui *ui;
|
||||
HWND pos_ctl;
|
||||
|
||||
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
pos_ctl = GetDlgItem(hwnd, IDC_POSITION);
|
||||
|
||||
ui->pos = mapper_read_analog(ui->def->tag);
|
||||
|
||||
SendMessage(pos_ctl, WM_USER, 0, (LPARAM) ui->pos);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR analog_ui_handle_fini(HWND hwnd)
|
||||
{
|
||||
struct analog_ui *ui;
|
||||
|
||||
ui = (struct analog_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
array_fini(&ui->hids);
|
||||
array_fini(&ui->control_nos);
|
||||
free(ui);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,530 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config/bind-adv.h"
|
||||
#include "config/resource.h"
|
||||
#include "config/usages.h"
|
||||
|
||||
#include "geninput/hid-mgr.h"
|
||||
|
||||
#include "util/array.h"
|
||||
#include "util/defs.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
struct bind_adv_state {
|
||||
uintptr_t timer_id;
|
||||
struct mapped_action ma;
|
||||
struct array devs;
|
||||
struct hid_stub *cur_hid;
|
||||
struct hid_control *ctls;
|
||||
size_t nctls;
|
||||
bool was_valid;
|
||||
};
|
||||
|
||||
static INT_PTR CALLBACK bind_adv_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
static INT_PTR bind_adv_handle_init(HWND hwnd, struct bind_adv_state *state);
|
||||
static void bind_adv_handle_init_devs(HWND hwnd);
|
||||
static void bind_adv_handle_init_insert_dev(HWND hwnd, struct hid_stub *hid);
|
||||
static INT_PTR bind_adv_handle_change_ctl(HWND hwnd);
|
||||
static INT_PTR bind_adv_handle_change_dev(HWND hwnd);
|
||||
static INT_PTR bind_adv_handle_change_range(HWND hwnd);
|
||||
static INT_PTR bind_adv_handle_tick(HWND hwnd);
|
||||
static INT_PTR bind_adv_handle_ok(HWND hwnd);
|
||||
static INT_PTR bind_adv_handle_fini(HWND hwnd);
|
||||
static bool bind_adv_get_dev_no(HWND hwnd, size_t *dev_no_out);
|
||||
static bool bind_adv_get_ctl_no(HWND hwnd, size_t *ctl_no_out);
|
||||
static bool bind_adv_get_range(HWND hwnd, int32_t *out_min, int32_t *out_max);
|
||||
static bool bind_adv_get_int(HWND control, int32_t *out);
|
||||
static bool bind_adv_is_valid(HWND hwnd);
|
||||
static void bind_adv_validate(HWND hwnd);
|
||||
static void bind_adv_select_dev(HWND hwnd, size_t dev_no);
|
||||
static void bind_adv_select_ctl(HWND hwnd, size_t ctl_no);
|
||||
static void bind_adv_set_range(HWND hwnd, int32_t range_min, int32_t range_max);
|
||||
|
||||
bool bind_adv(HINSTANCE inst, HWND hwnd, struct mapped_action *ma,
|
||||
bool was_valid)
|
||||
{
|
||||
struct bind_adv_state state;
|
||||
INT_PTR ok;
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
|
||||
state.ma = *ma;
|
||||
state.was_valid = was_valid;
|
||||
|
||||
ok = DialogBoxParam(inst, MAKEINTRESOURCE(IDD_BIND_ADV), hwnd,
|
||||
bind_adv_dlg_proc, (LPARAM) &state) != 0;
|
||||
|
||||
if (ok) {
|
||||
*ma = state.ma;
|
||||
}
|
||||
|
||||
return ok != 0;
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK bind_adv_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam)
|
||||
{
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG:
|
||||
return bind_adv_handle_init(hwnd, (struct bind_adv_state *) lparam);
|
||||
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wparam)) {
|
||||
case IDC_CONTROL:
|
||||
switch (HIWORD(wparam)) {
|
||||
case CBN_SELCHANGE:
|
||||
return bind_adv_handle_change_ctl(hwnd);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
||||
case IDC_DEVICE:
|
||||
switch (HIWORD(wparam)) {
|
||||
case CBN_SELCHANGE:
|
||||
return bind_adv_handle_change_dev(hwnd);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
||||
case IDC_BINDING_MIN:
|
||||
case IDC_BINDING_MAX:
|
||||
switch (HIWORD(wparam)) {
|
||||
case EN_CHANGE:
|
||||
return bind_adv_handle_change_range(hwnd);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
||||
case IDOK:
|
||||
switch (HIWORD(wparam)) {
|
||||
case BN_CLICKED:
|
||||
return bind_adv_handle_ok(hwnd);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
||||
case IDCANCEL:
|
||||
switch (HIWORD(wparam)) {
|
||||
case BN_CLICKED:
|
||||
EndDialog(hwnd, FALSE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
||||
case WM_TIMER:
|
||||
return bind_adv_handle_tick(hwnd);
|
||||
|
||||
case WM_DESTROY:
|
||||
return bind_adv_handle_fini(hwnd);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static INT_PTR bind_adv_handle_init(HWND hwnd, struct bind_adv_state *state)
|
||||
{
|
||||
struct hid_stub *hid;
|
||||
size_t i;
|
||||
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) state);
|
||||
|
||||
bind_adv_handle_init_devs(hwnd);
|
||||
|
||||
state->timer_id = SetTimer(hwnd, 1, 17, NULL);
|
||||
|
||||
if (!state->was_valid) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < state->devs.nitems ; i++) {
|
||||
hid = *array_item(struct hid_stub *, &state->devs, i);
|
||||
|
||||
if (hid == state->ma.hid) {
|
||||
bind_adv_select_dev(hwnd, (int) i);
|
||||
bind_adv_select_ctl(hwnd, state->ma.control_no);
|
||||
bind_adv_set_range(hwnd, state->ma.value_min,
|
||||
state->ma.value_max);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void bind_adv_handle_init_devs(HWND hwnd)
|
||||
{
|
||||
struct hid_stub *hid;
|
||||
|
||||
hid_mgr_lock();
|
||||
|
||||
for (hid = hid_mgr_get_first_stub()
|
||||
; hid != NULL
|
||||
; hid = hid_mgr_get_next_stub(hid)) {
|
||||
bind_adv_handle_init_insert_dev(hwnd, hid);
|
||||
}
|
||||
|
||||
hid_mgr_unlock();
|
||||
}
|
||||
|
||||
static void bind_adv_handle_init_insert_dev(HWND hwnd, struct hid_stub *hid)
|
||||
{
|
||||
struct bind_adv_state *state;
|
||||
wchar_t *name;
|
||||
size_t nchars;
|
||||
HWND devs;
|
||||
|
||||
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
devs = GetDlgItem(hwnd, IDC_DEVICE);
|
||||
|
||||
if (!hid_stub_get_name(hid, NULL, &nchars)) {
|
||||
goto size_fail;
|
||||
}
|
||||
|
||||
name = xmalloc(sizeof(*name) * nchars);
|
||||
|
||||
if (!hid_stub_get_name(hid, name, &nchars)) {
|
||||
goto name_fail;
|
||||
}
|
||||
|
||||
*array_append(struct hid_stub *, &state->devs) = hid;
|
||||
|
||||
SendMessage(devs, CB_ADDSTRING, 0, (LPARAM) name);
|
||||
|
||||
name_fail:
|
||||
free(name);
|
||||
|
||||
size_fail:
|
||||
;
|
||||
}
|
||||
|
||||
static INT_PTR bind_adv_handle_change_ctl(HWND hwnd)
|
||||
{
|
||||
size_t ctl_no;
|
||||
|
||||
if (bind_adv_get_ctl_no(hwnd, &ctl_no)) {
|
||||
bind_adv_select_ctl(hwnd, ctl_no);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR bind_adv_handle_change_dev(HWND hwnd)
|
||||
{
|
||||
size_t dev_no;
|
||||
|
||||
if (bind_adv_get_dev_no(hwnd, &dev_no)) {
|
||||
bind_adv_select_dev(hwnd, dev_no);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR bind_adv_handle_change_range(HWND hwnd)
|
||||
{
|
||||
bind_adv_validate(hwnd);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR bind_adv_handle_tick(HWND hwnd)
|
||||
{
|
||||
struct bind_adv_state *state;
|
||||
wchar_t wchars[16];
|
||||
size_t ctl_no;
|
||||
int32_t value;
|
||||
HWND label;
|
||||
|
||||
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
if (!bind_adv_get_ctl_no(hwnd, &ctl_no)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
hid_mgr_lock();
|
||||
|
||||
if (hid_stub_get_value(state->cur_hid, ctl_no, &value)) {
|
||||
wstr_format(wchars, lengthof(wchars), L"%d", value);
|
||||
|
||||
label = GetDlgItem(hwnd, IDC_CURRENT);
|
||||
SetWindowText(label, wchars);
|
||||
}
|
||||
|
||||
hid_mgr_unlock();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR bind_adv_handle_ok(HWND hwnd)
|
||||
{
|
||||
struct bind_adv_state *state;
|
||||
size_t dev_no;
|
||||
size_t ctl_no;
|
||||
|
||||
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
/* bind_adv_validate() ought to ensure that OK is disabled whenever the
|
||||
user's input is somehow invalid. Still, it never hurts to double check.
|
||||
|
||||
Ignore any OK clicks that we somehow receive with invalid input. */
|
||||
|
||||
if (!bind_adv_get_dev_no(hwnd, &dev_no)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
state->ma.hid = *array_item(struct hid_stub *, &state->devs, dev_no);
|
||||
|
||||
if (!bind_adv_get_ctl_no(hwnd, &ctl_no)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
state->ma.control_no = ctl_no;
|
||||
|
||||
if (!bind_adv_get_range(hwnd, &state->ma.value_min, &state->ma.value_max)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Input OK, shut down this dialog. */
|
||||
|
||||
EndDialog(hwnd, TRUE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR bind_adv_handle_fini(HWND hwnd)
|
||||
{
|
||||
struct bind_adv_state *state;
|
||||
|
||||
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
array_fini(&state->devs);
|
||||
|
||||
KillTimer(hwnd, state->timer_id);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool bind_adv_get_dev_no(HWND hwnd, size_t *dev_no_out)
|
||||
{
|
||||
const struct bind_adv_state *state;
|
||||
size_t dev_no;
|
||||
HWND devs;
|
||||
|
||||
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
devs = GetDlgItem(hwnd, IDC_DEVICE);
|
||||
|
||||
dev_no = SendMessage(devs, CB_GETCURSEL, 0, 0);
|
||||
|
||||
if (dev_no >= state->devs.nitems) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dev_no_out != NULL) {
|
||||
*dev_no_out = dev_no;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool bind_adv_get_ctl_no(HWND hwnd, size_t *ctl_no_out)
|
||||
{
|
||||
const struct bind_adv_state *state;
|
||||
size_t ctl_no;
|
||||
HWND ctls;
|
||||
|
||||
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
ctls = GetDlgItem(hwnd, IDC_CONTROL);
|
||||
|
||||
ctl_no = SendMessage(ctls, CB_GETCURSEL, 0, 0);
|
||||
|
||||
if (ctl_no >= state->nctls) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctl_no_out != NULL) {
|
||||
*ctl_no_out = ctl_no;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool bind_adv_get_range(HWND hwnd, int32_t *out_min, int32_t *out_max)
|
||||
{
|
||||
HWND wnd_min;
|
||||
HWND wnd_max;
|
||||
|
||||
wnd_min = GetDlgItem(hwnd, IDC_BINDING_MIN);
|
||||
wnd_max = GetDlgItem(hwnd, IDC_BINDING_MAX);
|
||||
|
||||
return bind_adv_get_int(wnd_min, out_min)
|
||||
&& bind_adv_get_int(wnd_max, out_max);
|
||||
}
|
||||
|
||||
static bool bind_adv_get_int(HWND control, int32_t *out)
|
||||
{
|
||||
wchar_t text[16];
|
||||
int32_t tmp;
|
||||
|
||||
text[0] = L'\0';
|
||||
GetWindowText(control, text, lengthof(text));
|
||||
|
||||
if (swscanf(text, L"%d", &tmp) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out != NULL) {
|
||||
*out = tmp;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool bind_adv_is_valid(HWND hwnd)
|
||||
{
|
||||
if (!bind_adv_get_dev_no(hwnd, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bind_adv_get_ctl_no(hwnd, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bind_adv_get_range(hwnd, NULL, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void bind_adv_validate(HWND hwnd)
|
||||
{
|
||||
HWND ok;
|
||||
|
||||
ok = GetDlgItem(hwnd, IDOK);
|
||||
EnableWindow(ok, bind_adv_is_valid(hwnd) ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
static void bind_adv_select_dev(HWND hwnd, size_t dev_no)
|
||||
{
|
||||
struct bind_adv_state *state;
|
||||
struct hid_stub *hid;
|
||||
char chars[256];
|
||||
wchar_t wchars[256];
|
||||
size_t i;
|
||||
HWND ctls;
|
||||
HWND devs;
|
||||
|
||||
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
ctls = GetDlgItem(hwnd, IDC_CONTROL);
|
||||
devs = GetDlgItem(hwnd, IDC_DEVICE);
|
||||
|
||||
if (dev_no >= state->devs.nitems) {
|
||||
return;
|
||||
}
|
||||
|
||||
SendMessage(devs, CB_SETCURSEL, (WPARAM) dev_no, 0);
|
||||
SendMessage(ctls, CB_RESETCONTENT, 0, 0);
|
||||
|
||||
hid = *array_item(struct hid_stub *, &state->devs, dev_no);
|
||||
|
||||
state->cur_hid = hid;
|
||||
|
||||
hid_mgr_lock();
|
||||
|
||||
if (!hid_stub_get_controls(state->cur_hid, NULL, &state->nctls)) {
|
||||
goto size_fail;
|
||||
}
|
||||
|
||||
free(state->ctls);
|
||||
state->ctls = xmalloc(sizeof(*state->ctls) * state->nctls);
|
||||
|
||||
if (!hid_stub_get_controls(state->cur_hid, state->ctls, &state->nctls)) {
|
||||
goto data_fail;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < state->nctls ; i++) {
|
||||
wchars[0] = L'\0';
|
||||
|
||||
usages_get(chars, lengthof(chars), state->ctls[i].usage);
|
||||
MultiByteToWideChar(CP_UTF8, 0, chars, -1, wchars, lengthof(wchars));
|
||||
|
||||
SendMessage(ctls, CB_ADDSTRING, 0, (LPARAM) wchars);
|
||||
}
|
||||
|
||||
hid_mgr_unlock();
|
||||
|
||||
bind_adv_validate(hwnd);
|
||||
|
||||
return;
|
||||
|
||||
data_fail:
|
||||
free(state->ctls);
|
||||
|
||||
state->ctls = NULL;
|
||||
state->nctls = 0;
|
||||
|
||||
size_fail:
|
||||
hid_mgr_unlock();
|
||||
|
||||
bind_adv_validate(hwnd);
|
||||
}
|
||||
|
||||
static void bind_adv_select_ctl(HWND hwnd, size_t ctl_no)
|
||||
{
|
||||
wchar_t text[128];
|
||||
struct bind_adv_state *state;
|
||||
const struct hid_control *ctl;
|
||||
HWND ctls;
|
||||
HWND limit_min;
|
||||
HWND limit_max;
|
||||
|
||||
state = (struct bind_adv_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
ctls = GetDlgItem(hwnd, IDC_CONTROL);
|
||||
limit_min = GetDlgItem(hwnd, IDC_LIMIT_MIN);
|
||||
limit_max = GetDlgItem(hwnd, IDC_LIMIT_MAX);
|
||||
|
||||
if (ctl_no >= state->nctls) {
|
||||
return;
|
||||
}
|
||||
|
||||
SendMessage(ctls, CB_SETCURSEL, (WPARAM) ctl_no, 0);
|
||||
|
||||
ctl = &state->ctls[ctl_no];
|
||||
|
||||
wstr_format(text, lengthof(text), L"%d", ctl->value_min);
|
||||
SetWindowText(limit_min, text);
|
||||
|
||||
wstr_format(text, lengthof(text), L"%d", ctl->value_max);
|
||||
SetWindowText(limit_max, text);
|
||||
|
||||
bind_adv_validate(hwnd);
|
||||
}
|
||||
|
||||
static void bind_adv_set_range(HWND hwnd, int32_t range_min, int32_t range_max)
|
||||
{
|
||||
wchar_t text[16];
|
||||
HWND wnd_min;
|
||||
HWND wnd_max;
|
||||
|
||||
wnd_min = GetDlgItem(hwnd, IDC_BINDING_MIN);
|
||||
wnd_max = GetDlgItem(hwnd, IDC_BINDING_MAX);
|
||||
|
||||
wstr_format(text, lengthof(text), L"%d", range_min);
|
||||
SetWindowText(wnd_min, text);
|
||||
|
||||
wstr_format(text, lengthof(text), L"%d", range_max);
|
||||
SetWindowText(wnd_max, text);
|
||||
|
||||
bind_adv_validate(hwnd);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef CONFIG_BIND_ADV_H
|
||||
#define CONFIG_BIND_ADV_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "config/schema.h"
|
||||
|
||||
#include "geninput/input-config.h"
|
||||
|
||||
bool bind_adv(HINSTANCE inst, HWND hwnd, struct mapped_action *ma,
|
||||
bool was_valid);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,162 @@
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config/resource.h"
|
||||
#include "config/schema.h"
|
||||
#include "config/usages.h"
|
||||
|
||||
#include "geninput/hid-mgr.h"
|
||||
#include "geninput/mapper.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
struct bind_light_state {
|
||||
const struct schema *schema;
|
||||
const struct mapped_light *ml;
|
||||
uint8_t *game_light;
|
||||
bool *bound;
|
||||
};
|
||||
|
||||
static INT_PTR CALLBACK bind_light_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
static INT_PTR bind_light_handle_init(HWND hwnd,
|
||||
struct bind_light_state *state);
|
||||
static INT_PTR bind_light_handle_ok(HWND hwnd);
|
||||
|
||||
bool bind_light(HINSTANCE inst, HWND hwnd, const struct schema *schema,
|
||||
const struct mapped_light *ml, uint8_t *game_light, bool *bound)
|
||||
{
|
||||
struct bind_light_state state;
|
||||
|
||||
state.schema = schema;
|
||||
state.ml = ml;
|
||||
state.game_light = game_light;
|
||||
state.bound = bound;
|
||||
|
||||
return DialogBoxParam(inst, MAKEINTRESOURCE(IDD_BIND_LIGHT), hwnd,
|
||||
bind_light_dlg_proc, (LPARAM) &state) != 0;
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK bind_light_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam)
|
||||
{
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG:
|
||||
return bind_light_handle_init(hwnd,
|
||||
(struct bind_light_state *) lparam);
|
||||
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wparam)) {
|
||||
case IDOK:
|
||||
switch (HIWORD(wparam)) {
|
||||
case BN_CLICKED:
|
||||
return bind_light_handle_ok(hwnd);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
||||
case IDCANCEL:
|
||||
switch (HIWORD(wparam)) {
|
||||
case BN_CLICKED:
|
||||
EndDialog(hwnd, FALSE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static INT_PTR bind_light_handle_init(HWND hwnd,
|
||||
struct bind_light_state *state)
|
||||
{
|
||||
char chars[256];
|
||||
wchar_t wchars[256];
|
||||
struct hid_light *lights;
|
||||
size_t nlights;
|
||||
size_t i;
|
||||
HINSTANCE inst;
|
||||
HWND hid_light_ctl;
|
||||
HWND game_light_ctl;
|
||||
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (INT_PTR) state);
|
||||
|
||||
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
||||
game_light_ctl = GetDlgItem(hwnd, IDC_GAME_LIGHT);
|
||||
|
||||
ComboBox_AddString(game_light_ctl, L"");
|
||||
|
||||
for (i = 0 ; i < state->schema->nlights ; i++) {
|
||||
LoadString(inst, state->schema->lights[i].name_rsrc,
|
||||
wchars, lengthof(wchars));
|
||||
ComboBox_AddString(game_light_ctl, wchars);
|
||||
|
||||
if (state->bound
|
||||
&& state->schema->lights[i].bit == *state->game_light) {
|
||||
ComboBox_SetCurSel(game_light_ctl, i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
hid_light_ctl = GetDlgItem(hwnd, IDC_LIGHT);
|
||||
|
||||
hid_mgr_lock();
|
||||
|
||||
if (!hid_stub_get_lights(state->ml->hid, NULL, &nlights)) {
|
||||
goto size_fail;
|
||||
}
|
||||
|
||||
if (state->ml->light_no >= nlights) {
|
||||
goto bounds_fail;
|
||||
}
|
||||
|
||||
lights = xmalloc(sizeof(*lights) * nlights);
|
||||
|
||||
if (!hid_stub_get_lights(state->ml->hid, lights, &nlights)) {
|
||||
goto data_fail;
|
||||
}
|
||||
|
||||
usages_get(chars, lengthof(chars), lights[state->ml->light_no].usage);
|
||||
MultiByteToWideChar(CP_UTF8, 0, chars, -1, wchars, lengthof(wchars));
|
||||
|
||||
SetWindowText(hid_light_ctl, wchars);
|
||||
|
||||
data_fail:
|
||||
free(lights);
|
||||
|
||||
bounds_fail:
|
||||
size_fail:
|
||||
hid_mgr_unlock();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR bind_light_handle_ok(HWND hwnd)
|
||||
{
|
||||
struct bind_light_state *state;
|
||||
int pos;
|
||||
HWND game_light_ctl;
|
||||
|
||||
state = (struct bind_light_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
game_light_ctl = GetDlgItem(hwnd, IDC_GAME_LIGHT);
|
||||
pos = ComboBox_GetCurSel(game_light_ctl);
|
||||
|
||||
if (pos <= 0) {
|
||||
*state->bound = false;
|
||||
} else {
|
||||
*state->bound = true;
|
||||
*state->game_light = state->schema->lights[pos - 1].bit;
|
||||
}
|
||||
|
||||
EndDialog(hwnd, TRUE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifndef CONFIG_BIND_LIGHT_H
|
||||
#define CONFIG_BIND_LIGHT_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "config/schema.h"
|
||||
|
||||
#include "geninput/mapper.h"
|
||||
|
||||
bool bind_light(HINSTANCE inst, HWND hwnd, const struct schema *schema,
|
||||
const struct mapped_light *ml, uint8_t *game_light, bool *bound);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,127 @@
|
||||
#include <windows.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "config/bind.h"
|
||||
#include "config/resource.h"
|
||||
#include "config/schema.h"
|
||||
#include "config/snap.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
|
||||
struct bind_state {
|
||||
const struct action_def *action;
|
||||
struct mapped_action *ma;
|
||||
struct snap snaps[2];
|
||||
size_t cur_snap;
|
||||
uintptr_t timer_id;
|
||||
};
|
||||
|
||||
static INT_PTR CALLBACK bind_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
static INT_PTR bind_handle_init(HWND hwnd, struct bind_state *self);
|
||||
static INT_PTR bind_handle_tick(HWND hwnd);
|
||||
static INT_PTR bind_handle_cancel(HWND hwnd);
|
||||
static INT_PTR bind_handle_destroy(HWND hwnd);
|
||||
|
||||
bool bind_control(HINSTANCE inst, HWND hwnd, const struct action_def *action,
|
||||
struct mapped_action *ma)
|
||||
{
|
||||
struct bind_state self;
|
||||
bool result;
|
||||
|
||||
self.action = action;
|
||||
self.ma = ma;
|
||||
self.cur_snap = 0;
|
||||
|
||||
snap_init(&self.snaps[1]);
|
||||
snap_init(&self.snaps[0]);
|
||||
|
||||
result = DialogBoxParam(inst, MAKEINTRESOURCE(IDD_BIND), hwnd,
|
||||
bind_dlg_proc, (LPARAM) &self) != 0;
|
||||
|
||||
snap_fini(&self.snaps[0]);
|
||||
snap_fini(&self.snaps[1]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK bind_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam)
|
||||
{
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG:
|
||||
return bind_handle_init(hwnd, (struct bind_state *) lparam);
|
||||
|
||||
case WM_TIMER:
|
||||
return bind_handle_tick(hwnd);
|
||||
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wparam)) {
|
||||
case IDCANCEL:
|
||||
return bind_handle_cancel(hwnd);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
case WM_DESTROY:
|
||||
return bind_handle_destroy(hwnd);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static INT_PTR bind_handle_init(HWND hwnd, struct bind_state *self)
|
||||
{
|
||||
HINSTANCE inst;
|
||||
wchar_t str[128];
|
||||
|
||||
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
||||
LoadString(inst, self->action->name_rsrc, str, lengthof(str));
|
||||
SetWindowText(GetDlgItem(hwnd, IDC_ACTION_NAME), str);
|
||||
|
||||
self->timer_id = SetTimer(hwnd, 0, 17, NULL);
|
||||
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) self);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR bind_handle_tick(HWND hwnd)
|
||||
{
|
||||
struct bind_state *self;
|
||||
|
||||
self = (struct bind_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
self->cur_snap = !self->cur_snap;
|
||||
|
||||
snap_fini(&self->snaps[self->cur_snap]);
|
||||
snap_init(&self->snaps[self->cur_snap]);
|
||||
|
||||
if (snap_find_edge(&self->snaps[self->cur_snap],
|
||||
&self->snaps[!self->cur_snap], self->ma)) {
|
||||
EndDialog(hwnd, 1);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR bind_handle_cancel(HWND hwnd)
|
||||
{
|
||||
EndDialog(hwnd, 0);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR bind_handle_destroy(HWND hwnd)
|
||||
{
|
||||
struct bind_state *self;
|
||||
|
||||
self = (struct bind_state *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
KillTimer(hwnd, self->timer_id);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef CONFIG_BIND_H
|
||||
#define CONFIG_BIND_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "config/schema.h"
|
||||
|
||||
#include "geninput/mapper.h"
|
||||
|
||||
bool bind_control(HINSTANCE inst, HWND hwnd, const struct action_def *action,
|
||||
struct mapped_action *ma);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,426 @@
|
||||
#include <windows.h>
|
||||
#include <commctrl.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config/bind.h"
|
||||
#include "config/bind-adv.h"
|
||||
#include "config/resource.h"
|
||||
#include "config/schema.h"
|
||||
#include "config/usages.h"
|
||||
|
||||
#include "geninput/input-config.h"
|
||||
#include "geninput/mapper.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/winres.h"
|
||||
|
||||
static INT_PTR CALLBACK buttons_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
static bool buttons_get_control_name(const struct mapped_action *ma,
|
||||
wchar_t *str, size_t nchars);
|
||||
static INT_PTR buttons_handle_init(HWND hwnd, const PROPSHEETPAGE *psp);
|
||||
static INT_PTR buttons_handle_binding_adv(HWND hwnd);
|
||||
static INT_PTR buttons_handle_binding_clear(HWND hwnd);
|
||||
static INT_PTR buttons_handle_binding_edit(HWND hwnd, BOOL bind_many);
|
||||
static INT_PTR buttons_handle_switch_page(HWND hwnd, NMUPDOWN *n);
|
||||
static INT_PTR buttons_handle_fini(HWND hwnd);
|
||||
static void buttons_update(HWND hwnd);
|
||||
static void buttons_update_bindings(HWND hwnd);
|
||||
static void buttons_update_pager(HWND hwnd);
|
||||
|
||||
struct buttons_tab {
|
||||
const struct schema *schema;
|
||||
uint8_t page_no;
|
||||
uint8_t npages;
|
||||
};
|
||||
|
||||
HPROPSHEETPAGE buttons_tab_create(HINSTANCE inst, const struct schema *schema)
|
||||
{
|
||||
PROPSHEETPAGE psp;
|
||||
|
||||
memset(&psp, 0, sizeof(psp));
|
||||
psp.dwSize = sizeof(psp);
|
||||
psp.dwFlags = PSP_DEFAULT;
|
||||
psp.hInstance = inst;
|
||||
psp.pszTemplate = MAKEINTRESOURCE(IDD_TAB_BUTTONS);
|
||||
psp.pfnDlgProc = buttons_dlg_proc;
|
||||
psp.lParam = (LPARAM) schema;
|
||||
|
||||
return CreatePropertySheetPage(&psp);
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK buttons_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam)
|
||||
{
|
||||
NMHDR *n;
|
||||
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG:
|
||||
return buttons_handle_init(hwnd, (const PROPSHEETPAGE *) lparam);
|
||||
|
||||
case WM_NOTIFY:
|
||||
n = (NMHDR *) lparam;
|
||||
|
||||
switch (n->idFrom) {
|
||||
case IDC_PAGE:
|
||||
switch (n->code) {
|
||||
case UDN_DELTAPOS:
|
||||
return buttons_handle_switch_page(hwnd,
|
||||
(NMUPDOWN *) n);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
case IDC_BINDINGS:
|
||||
switch (n->code) {
|
||||
case NM_DBLCLK:
|
||||
return buttons_handle_binding_edit(hwnd, FALSE);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wparam)) {
|
||||
case IDC_BINDING_ADV:
|
||||
return buttons_handle_binding_adv(hwnd);
|
||||
|
||||
case IDC_BINDING_CLEAR:
|
||||
return buttons_handle_binding_clear(hwnd);
|
||||
|
||||
case IDC_BINDING_EDIT:
|
||||
return buttons_handle_binding_edit(hwnd, FALSE);
|
||||
|
||||
case IDC_BINDING_EDIT_MANY:
|
||||
return buttons_handle_binding_edit(hwnd, TRUE);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
case WM_DESTROY:
|
||||
return buttons_handle_fini(hwnd);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static INT_PTR buttons_handle_init(HWND hwnd, const PROPSHEETPAGE *psp)
|
||||
{
|
||||
struct buttons_tab *self;
|
||||
HWND bindings_ctl;
|
||||
HINSTANCE inst;
|
||||
LVCOLUMN col;
|
||||
LVITEM item;
|
||||
wchar_t str[128];
|
||||
size_t i;
|
||||
|
||||
self = xmalloc(sizeof(*self));
|
||||
self->schema = (const struct schema *) psp->lParam;
|
||||
self->page_no = 0;
|
||||
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) self);
|
||||
|
||||
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
||||
bindings_ctl = GetDlgItem(hwnd, IDC_BINDINGS);
|
||||
|
||||
ListView_SetExtendedListViewStyle(bindings_ctl,
|
||||
LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
|
||||
|
||||
LoadString(inst, IDS_COL_ACTION, str, lengthof(str));
|
||||
|
||||
col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
|
||||
col.fmt = LVCFMT_LEFT;
|
||||
col.cx = 120;
|
||||
col.pszText = str;
|
||||
|
||||
ListView_InsertColumn(bindings_ctl, 0, &col);
|
||||
|
||||
LoadString(inst, IDS_COL_BUTTON, str, lengthof(str));
|
||||
|
||||
col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
|
||||
col.fmt = LVCFMT_LEFT;
|
||||
col.cx = 120;
|
||||
col.pszText = str;
|
||||
|
||||
ListView_InsertColumn(bindings_ctl, 1, &col);
|
||||
|
||||
for (i = 0 ; i < self->schema->nactions ; i++) {
|
||||
memset(&item, 0, sizeof(item));
|
||||
|
||||
LoadString(inst, self->schema->actions[i].name_rsrc, str,
|
||||
lengthof(str));
|
||||
|
||||
item.mask = LVIF_TEXT;
|
||||
item.iItem = (int) i;
|
||||
item.iSubItem = 0;
|
||||
item.pszText = str;
|
||||
|
||||
ListView_InsertItem(bindings_ctl, &item);
|
||||
}
|
||||
|
||||
buttons_update(hwnd);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void buttons_update(HWND hwnd)
|
||||
{
|
||||
buttons_update_pager(hwnd);
|
||||
buttons_update_bindings(hwnd);
|
||||
|
||||
/* Flickery, but seems to be the only way to avoid label garbling when
|
||||
the page label changes. Need to do better. */
|
||||
InvalidateRect(hwnd, NULL, TRUE);
|
||||
}
|
||||
|
||||
static void buttons_update_pager(HWND hwnd)
|
||||
{
|
||||
struct buttons_tab *self;
|
||||
HINSTANCE inst;
|
||||
wchar_t str[128];
|
||||
|
||||
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
||||
self = (struct buttons_tab *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
self->npages = mapper_get_npages() + 1;
|
||||
|
||||
rswprintf(str, lengthof(str), inst, IDS_PAGE, self->page_no + 1,
|
||||
self->npages);
|
||||
SetWindowText(GetDlgItem(hwnd, IDC_PAGE_TEXT), str);
|
||||
}
|
||||
|
||||
static void buttons_update_bindings(HWND hwnd)
|
||||
{
|
||||
action_iter_t pos;
|
||||
struct mapped_action ma;
|
||||
struct buttons_tab *self;
|
||||
HINSTANCE inst;
|
||||
HWND bindings_ctl;
|
||||
LVITEM item;
|
||||
wchar_t str[128];
|
||||
size_t i;
|
||||
|
||||
self = (struct buttons_tab *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
||||
bindings_ctl = GetDlgItem(hwnd, IDC_BINDINGS);
|
||||
|
||||
/* Clear binding cell text */
|
||||
|
||||
for (i = 0 ; i < self->schema->nactions ; i++) {
|
||||
memset(&item, 0, sizeof(item));
|
||||
|
||||
item.mask = LVIF_TEXT;
|
||||
item.iItem = (int) i;
|
||||
item.iSubItem = 1;
|
||||
item.pszText = L"";
|
||||
|
||||
ListView_SetItem(bindings_ctl, &item);
|
||||
}
|
||||
|
||||
/* Repopulate bindings */
|
||||
|
||||
hid_mgr_lock();
|
||||
|
||||
for (pos = mapper_iterate_actions()
|
||||
; action_iter_is_valid(pos)
|
||||
; action_iter_next(pos)) {
|
||||
if (action_iter_get_page(pos) != self->page_no) {
|
||||
continue;
|
||||
}
|
||||
|
||||
action_iter_get_mapping(pos, &ma);
|
||||
|
||||
if (!buttons_get_control_name(&ma, str, lengthof(str))) {
|
||||
/* Can't read device metadata, device is probably unplugged */
|
||||
LoadString(inst, IDS_NOT_PRESENT, str, lengthof(str));
|
||||
}
|
||||
|
||||
memset(&item, 0, sizeof(item));
|
||||
|
||||
item.mask = LVIF_TEXT;
|
||||
item.iItem = (int) action_iter_get_action(pos);
|
||||
item.iSubItem = 1;
|
||||
item.pszText = str;
|
||||
|
||||
ListView_SetItem(bindings_ctl, &item);
|
||||
}
|
||||
|
||||
action_iter_free(pos);
|
||||
hid_mgr_unlock();
|
||||
}
|
||||
|
||||
static bool buttons_get_control_name(const struct mapped_action *ma,
|
||||
wchar_t *str, size_t nchars)
|
||||
{
|
||||
char chars[128];
|
||||
struct hid_control *controls;
|
||||
size_t ncontrols;
|
||||
|
||||
if (!hid_stub_get_controls(ma->hid, NULL, &ncontrols)) {
|
||||
goto count_fail;
|
||||
}
|
||||
|
||||
controls = xmalloc(ncontrols * sizeof(*controls));
|
||||
|
||||
if (!hid_stub_get_controls(ma->hid, controls, &ncontrols)) {
|
||||
goto data_fail;
|
||||
}
|
||||
|
||||
if (ma->control_no >= ncontrols) {
|
||||
goto bounds_fail;
|
||||
}
|
||||
|
||||
usages_get(chars, lengthof(chars), controls[ma->control_no].usage);
|
||||
MultiByteToWideChar(CP_UTF8, 0, chars, lengthof(chars), str, nchars);
|
||||
|
||||
free(controls);
|
||||
|
||||
return true;
|
||||
|
||||
bounds_fail:
|
||||
data_fail:
|
||||
free(controls);
|
||||
|
||||
count_fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
static INT_PTR buttons_handle_binding_adv(HWND hwnd)
|
||||
{
|
||||
HINSTANCE inst;
|
||||
struct mapped_action ma;
|
||||
struct buttons_tab *self;
|
||||
int action_no;
|
||||
uint8_t bit;
|
||||
bool is_valid;
|
||||
|
||||
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
||||
self = (struct buttons_tab *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
action_no = ListView_GetSelectionMark(GetDlgItem(hwnd, IDC_BINDINGS));
|
||||
|
||||
if (action_no < 0 || action_no >= (int) self->schema->nactions) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bit = self->schema->actions[action_no].bit;
|
||||
|
||||
is_valid = mapper_get_action_map((uint8_t) action_no, self->page_no, &ma);
|
||||
|
||||
if (bind_adv(inst, hwnd, &ma, is_valid)) {
|
||||
mapper_set_action_map((uint8_t) action_no, self->page_no, bit, &ma);
|
||||
buttons_update(hwnd);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR buttons_handle_binding_clear(HWND hwnd)
|
||||
{
|
||||
struct buttons_tab *self;
|
||||
int action_no;
|
||||
|
||||
self = (struct buttons_tab *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
action_no = ListView_GetSelectionMark(GetDlgItem(hwnd, IDC_BINDINGS));
|
||||
|
||||
if (action_no < 0 || action_no >= (int) self->schema->nactions) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
mapper_clear_action_map((uint8_t) action_no, self->page_no);
|
||||
buttons_update(hwnd);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR buttons_handle_binding_edit(HWND hwnd, BOOL bind_many)
|
||||
{
|
||||
HINSTANCE inst;
|
||||
HWND listview;
|
||||
struct buttons_tab *self;
|
||||
struct mapped_action ma;
|
||||
int action_start;
|
||||
int action_no;
|
||||
uint8_t bit;
|
||||
|
||||
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
||||
self = (struct buttons_tab *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
listview = GetDlgItem(hwnd, IDC_BINDINGS);
|
||||
|
||||
action_start = ListView_GetSelectionMark(listview);
|
||||
|
||||
if (action_start < 0 || action_start >= (int) self->schema->nactions) {
|
||||
/* wtf... */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
for(action_no = action_start;
|
||||
action_no < self->schema->nactions;
|
||||
action_no++) {
|
||||
// Make sure the next listview item is visible
|
||||
ListView_SetItemState (listview, action_no, LVIS_FOCUSED | LVIS_SELECTED, 0x000F);
|
||||
ListView_EnsureVisible(listview, action_no, FALSE);
|
||||
|
||||
bit = self->schema->actions[action_no].bit;
|
||||
|
||||
if (bind_control(inst, hwnd, &self->schema->actions[action_no], &ma)) {
|
||||
mapper_set_action_map((uint8_t) action_no, self->page_no, bit, &ma);
|
||||
buttons_update(hwnd);
|
||||
|
||||
log_misc("Bind act %d -> dev %p ctl %u range [%d, %d]",
|
||||
self->schema->actions[action_no].bit, ma.hid,
|
||||
(unsigned int) ma.control_no, ma.value_min, ma.value_max);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
if(!bind_many) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR buttons_handle_switch_page(HWND hwnd, NMUPDOWN *n)
|
||||
{
|
||||
struct buttons_tab *self;
|
||||
|
||||
self = (struct buttons_tab *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
/* The buttons on a horizontal spinner are backwards -.- */
|
||||
|
||||
if (n->iDelta > 0 && self->page_no > 0) {
|
||||
self->page_no--;
|
||||
buttons_update(hwnd);
|
||||
} else if (n->iDelta < 0 && self->page_no < self->npages - 1) {
|
||||
self->page_no++;
|
||||
buttons_update(hwnd);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR buttons_handle_fini(HWND hwnd)
|
||||
{
|
||||
struct buttons_tab *self;
|
||||
|
||||
self = (struct buttons_tab *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
free(self);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*" />
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
</assembly>
|
||||
@@ -0,0 +1,392 @@
|
||||
// Generated by ResEdit 1.5.11
|
||||
// Copyright (C) 2006-2012
|
||||
// http://www.resedit.net
|
||||
|
||||
#include <windows.h>
|
||||
#include <commctrl.h>
|
||||
#include <richedit.h>
|
||||
#include "resource.h"
|
||||
|
||||
1 RT_MANIFEST config.exe.manifest
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||
IDR_USAGES USAGES "config/usages.txt"
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Dialog resources
|
||||
//
|
||||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||
IDD_ANALOG DIALOG 0, 0, 220, 100
|
||||
STYLE DS_3DLOOK | DS_CENTER | DS_SHELLFONT | WS_VISIBLE | WS_CHILDWINDOW
|
||||
EXSTYLE WS_EX_CONTROLPARENT
|
||||
FONT 8, "Ms Shell Dlg"
|
||||
{
|
||||
GROUPBOX "", IDC_GROUP, 5, 5, 210, 90
|
||||
CONTROL "", IDC_SENSITIVITY, TRACKBAR_CLASS, WS_TABSTOP | TBS_AUTOTICKS, 10, 75, 200, 15
|
||||
LTEXT "Device", IDC_STATIC, 15, 15, 24, 8, SS_LEFT
|
||||
COMBOBOX IDC_DEVICE, 15, 25, 135, 30, WS_GROUP | WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS
|
||||
COMBOBOX IDC_CONTROL, 15, 50, 135, 30, WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS
|
||||
LTEXT "Sensitivity", IDC_STATIC, 15, 65, 32, 8, SS_LEFT
|
||||
LTEXT "Control", IDC_STATIC, 15, 40, 23, 8, SS_LEFT
|
||||
CONTROL "", IDC_POSITION, "spinner", 0x50020000, 160, 15, 50, 50, 0x02002000
|
||||
}
|
||||
|
||||
|
||||
|
||||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||
IDD_BIND DIALOG 0, 0, 200, 64
|
||||
STYLE DS_3DLOOK | DS_CENTER | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP
|
||||
CAPTION "Bind action (ESC to cancel)"
|
||||
FONT 8, "Ms Shell Dlg"
|
||||
{
|
||||
CTEXT "Static", IDC_ACTION_NAME, 5, 25, 190, 8, SS_CENTER
|
||||
}
|
||||
|
||||
|
||||
|
||||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||
IDD_BIND_ADV DIALOG 0, 0, 242, 175
|
||||
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
|
||||
CAPTION "Binding Editor"
|
||||
FONT 8, "Ms Shell Dlg"
|
||||
{
|
||||
DEFPUSHBUTTON "OK", IDOK, 130, 155, 50, 14
|
||||
PUSHBUTTON "Cancel", IDCANCEL, 185, 155, 50, 14
|
||||
LTEXT "Device", IDC_STATIC, 14, 14, 24, 8, SS_LEFT
|
||||
LTEXT "Control", IDC_STATIC, 14, 34, 23, 8, SS_LEFT
|
||||
GROUPBOX "Raw Control Value", IDC_STATIC, 5, 55, 230, 95
|
||||
LTEXT "Limits", IDC_STATIC, 14, 88, 18, 8, SS_LEFT
|
||||
LTEXT "Binding", IDC_STATIC, 14, 108, 24, 8, SS_LEFT
|
||||
LTEXT "Current Value", IDC_STATIC, 14, 128, 44, 8, SS_LEFT
|
||||
LTEXT "Minimum", IDC_STATIC, 80, 70, 28, 8, SS_LEFT
|
||||
LTEXT "Maximum", IDC_STATIC, 160, 70, 30, 8, SS_LEFT
|
||||
COMBOBOX IDC_DEVICE, 75, 10, 150, 30, WS_GROUP | WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST | CBS_HASSTRINGS
|
||||
COMBOBOX IDC_CONTROL, 75, 30, 150, 20, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST | CBS_HASSTRINGS
|
||||
EDITTEXT IDC_LIMIT_MIN, 75, 85, 70, 14, NOT WS_TABSTOP | ES_AUTOHSCROLL | ES_READONLY
|
||||
EDITTEXT IDC_LIMIT_MAX, 155, 85, 70, 14, NOT WS_TABSTOP | ES_AUTOHSCROLL | ES_READONLY
|
||||
EDITTEXT IDC_BINDING_MIN, 75, 105, 70, 14, ES_AUTOHSCROLL | ES_NUMBER
|
||||
EDITTEXT IDC_BINDING_MAX, 155, 105, 70, 14, ES_AUTOHSCROLL | ES_NUMBER
|
||||
EDITTEXT IDC_CURRENT, 75, 125, 150, 14, NOT WS_TABSTOP | ES_CENTER | ES_AUTOHSCROLL | ES_READONLY
|
||||
}
|
||||
|
||||
|
||||
|
||||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||
IDD_BIND_LIGHT DIALOG 0, 0, 242, 76
|
||||
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
|
||||
CAPTION "Light Binding"
|
||||
FONT 8, "Ms Shell Dlg"
|
||||
{
|
||||
DEFPUSHBUTTON "OK", IDOK, 130, 55, 50, 14
|
||||
PUSHBUTTON "Cancel", IDCANCEL, 185, 55, 50, 14
|
||||
LTEXT "Game Light", IDC_STATIC, 14, 34, 37, 8, SS_LEFT
|
||||
COMBOBOX IDC_GAME_LIGHT, 75, 30, 160, 30, WS_GROUP | WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST | CBS_HASSTRINGS
|
||||
LTEXT "HID Light", IDC_STATIC, 14, 14, 31, 8, SS_LEFT
|
||||
EDITTEXT IDC_LIGHT, 75, 10, 160, 14, ES_AUTOHSCROLL | ES_READONLY
|
||||
}
|
||||
|
||||
|
||||
|
||||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||
IDD_GAME_TYPE DIALOG 0, 0, 205, 70
|
||||
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
|
||||
CAPTION "Game series"
|
||||
FONT 8, "Ms Shell Dlg"
|
||||
{
|
||||
DEFPUSHBUTTON "OK", IDOK, 95, 50, 50, 14
|
||||
PUSHBUTTON "Cancel", IDCANCEL, 150, 50, 50, 14
|
||||
COMBOBOX IDC_GAME_TYPE, 5, 20, 195, 30, WS_GROUP | WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS
|
||||
LTEXT "Select game series:", IDC_STATIC, 5, 10, 62, 8, SS_LEFT
|
||||
}
|
||||
|
||||
|
||||
|
||||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||
IDD_READER DIALOG 0, 0, 220, 90
|
||||
STYLE DS_3DLOOK | DS_CENTER | DS_CONTROL | DS_SHELLFONT | WS_VISIBLE | WS_CHILDWINDOW | WS_SYSMENU
|
||||
EXSTYLE WS_EX_CONTROLPARENT
|
||||
FONT 8, "Ms Shell Dlg"
|
||||
{
|
||||
LTEXT "Keyboard device", IDC_STATIC, 15, 20, 54, 8, SS_LEFT
|
||||
COMBOBOX IDC_KBD_DEVICE, 15, 30, 135, 12, WS_GROUP | WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS
|
||||
LTEXT "Card ID file", IDC_STATIC, 15, 50, 36, 8, SS_LEFT
|
||||
EDITTEXT IDC_CARD_PATH, 15, 60, 135, 14, ES_AUTOHSCROLL
|
||||
PUSHBUTTON "&Browse...", IDC_BROWSE, 155, 60, 50, 14
|
||||
GROUPBOX "", IDC_GROUP, 5, 5, 210, 80
|
||||
LTEXT "Keypad status", IDC_STATIC, 155, 20, 46, 8, SS_LEFT
|
||||
LTEXT "", IDC_KEYPAD_STATE, 160, 30, 45, 12, SS_LEFT | SS_CENTERIMAGE
|
||||
}
|
||||
|
||||
|
||||
|
||||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||
IDD_TAB_ANALOGS DIALOG 0, 0, 220, 215
|
||||
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
|
||||
CAPTION "Analogs"
|
||||
FONT 8, "Ms Shell Dlg"
|
||||
{
|
||||
LTEXT "", IDC_STATIC, 0, 0, 8, 8, SS_LEFT
|
||||
}
|
||||
|
||||
|
||||
|
||||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||
IDD_TAB_BUTTONS DIALOG 0, 0, 220, 215
|
||||
STYLE DS_3DLOOK | DS_CENTER | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_CHILDWINDOW | WS_SYSMENU
|
||||
CAPTION "Buttons"
|
||||
FONT 8, "Ms Shell Dlg"
|
||||
{
|
||||
CONTROL "Page XX / XX", IDC_PAGE_TEXT, WC_STATIC, NOT WS_GROUP | SS_SIMPLE, 35, 7, 45, 8, WS_EX_LEFT
|
||||
CONTROL "", IDC_BINDINGS, WC_LISTVIEW, WS_TABSTOP | WS_BORDER | LVS_ALIGNLEFT | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | LVS_SINGLESEL | LVS_REPORT, 5, 20, 210, 170, WS_EX_LEFT
|
||||
PUSHBUTTON "&Bind...", IDC_BINDING_EDIT, 5, 195, 50, 14, 0, WS_EX_LEFT
|
||||
PUSHBUTTON "Bind &many...", IDC_BINDING_EDIT_MANY, 58, 195, 50, 14, 0, WS_EX_LEFT
|
||||
PUSHBUTTON "&Edit...", IDC_BINDING_ADV, 111, 195, 50, 14, 0, WS_EX_LEFT
|
||||
PUSHBUTTON "&Clear", IDC_BINDING_CLEAR, 165, 195, 50, 14, 0, WS_EX_LEFT
|
||||
CONTROL "", IDC_PAGE, UPDOWN_CLASS, WS_TABSTOP | UDS_ARROWKEYS | UDS_HORZ, 5, 5, 25, 11, WS_EX_LEFT
|
||||
}
|
||||
|
||||
|
||||
|
||||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||
IDD_TAB_LIGHTS DIALOG 0, 0, 220, 215
|
||||
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
|
||||
CAPTION "Lights"
|
||||
FONT 8, "Ms Shell Dlg"
|
||||
{
|
||||
LTEXT "Device", IDC_STATIC, 5, 5, 24, 8, SS_LEFT
|
||||
COMBOBOX IDC_DEVICE, 5, 15, 210, 30, CBS_DROPDOWNLIST | CBS_HASSTRINGS
|
||||
LTEXT "Lights", IDC_STATIC, 5, 33, 20, 8, SS_LEFT
|
||||
CONTROL "", IDC_LIGHT, WC_LISTVIEW, WS_TABSTOP | WS_BORDER | LVS_ALIGNLEFT | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | LVS_SINGLESEL | LVS_REPORT, 5, 45, 210, 145
|
||||
CTEXT "WARNING: Clicking on a HID light will cause it to illuminate.", IDC_STATIC, 5, 200, 210, 8, SS_CENTER
|
||||
}
|
||||
|
||||
|
||||
|
||||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||
IDD_TAB_NETWORK DIALOG 0, 0, 220, 215
|
||||
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
|
||||
CAPTION "Network"
|
||||
FONT 8, "Ms Shell Dlg"
|
||||
{
|
||||
LTEXT "", IDC_STATIC, 0, 0, 8, 8, SS_LEFT
|
||||
AUTOCHECKBOX "Generate card ID files on insertion if they do not already exist", IDC_AUTOGEN, 5, 200, 206, 8
|
||||
AUTOCHECKBOX "Use top keyboard row for PIN pad input", IDC_ALT_10K, 5, 185, 141, 8
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// String Table resources
|
||||
//
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
STRINGTABLE
|
||||
{
|
||||
IDS_APPNAME "BemaniPC Input Configuration"
|
||||
IDS_BAD_GAME_TYPE "Unknown game type: %S"
|
||||
IDS_PAGE "Page %02d / %02d"
|
||||
IDS_COL_BUTTON "Button"
|
||||
IDS_COL_ACTION "Action"
|
||||
IDS_NOT_PRESENT "<Device not present>"
|
||||
IDS_READER_P1 "P1 Card Reader"
|
||||
IDS_READER_P2 "P2 Card Reader"
|
||||
IDS_MUXED_TITLE "Notice"
|
||||
IDS_MUXED_MSG "You have bound both card readers to the same keypad device. As a result, the Num Lock key will be used to select the active card reader: when Num Lock is off, P1 will receive input, and when Num Lock is on, P2 will receive input.\n\nAre you sure you want to use these settings?"
|
||||
IDS_COL_LIGHT_HID "HID Light"
|
||||
IDS_COL_LIGHT_GAME "Game Light"
|
||||
IDS_GENERIC_TEST "Test"
|
||||
IDS_GENERIC_SERVICE "Service"
|
||||
IDS_IIDX_SCHEMA "beatmania IIDX"
|
||||
IDS_IIDX_P1_1 "P1 1"
|
||||
IDS_IIDX_P1_2 "P1 2"
|
||||
IDS_IIDX_P1_3 "P1 3"
|
||||
IDS_IIDX_P1_4 "P1 4"
|
||||
IDS_IIDX_P1_5 "P1 5"
|
||||
IDS_IIDX_P1_6 "P1 6"
|
||||
IDS_IIDX_P1_7 "P1 7"
|
||||
IDS_IIDX_P2_1 "P2 1"
|
||||
IDS_IIDX_P2_2 "P2 2"
|
||||
IDS_IIDX_P2_3 "P2 3"
|
||||
IDS_IIDX_P2_4 "P2 4"
|
||||
IDS_IIDX_P2_5 "P2 5"
|
||||
IDS_IIDX_P2_6 "P2 6"
|
||||
IDS_IIDX_P2_7 "P2 7"
|
||||
IDS_IIDX_P1_START "P1 Start"
|
||||
IDS_IIDX_P2_START "P2 Start"
|
||||
IDS_IIDX_VEFX "VEFX"
|
||||
IDS_IIDX_EFFECT "Effect"
|
||||
IDS_IIDX_P1_TT_UP "P1 TT +"
|
||||
IDS_IIDX_P1_TT_DOWN "P1 TT -"
|
||||
IDS_IIDX_P1_TT_STAB "P1 TT +/-"
|
||||
IDS_IIDX_P2_TT_UP "P2 TT +"
|
||||
IDS_IIDX_P2_TT_DOWN "P2 TT -"
|
||||
IDS_IIDX_P2_TT_STAB "P2 TT +/-"
|
||||
IDS_IIDX_P1_TT "P1 Turntable"
|
||||
IDS_IIDX_P2_TT "P2 Turntable"
|
||||
IDS_IIDX_SPOT_1_LIGHT "Spot Light #1"
|
||||
IDS_IIDX_SPOT_2_LIGHT "Spot Light #2"
|
||||
IDS_IIDX_SPOT_3_LIGHT "Spot Light #3"
|
||||
IDS_IIDX_SPOT_4_LIGHT "Spot Light #4"
|
||||
IDS_IIDX_SPOT_5_LIGHT "Spot Light #5"
|
||||
IDS_IIDX_SPOT_6_LIGHT "Spot Light #6"
|
||||
IDS_IIDX_SPOT_7_LIGHT "Spot Light #7"
|
||||
IDS_IIDX_SPOT_8_LIGHT "Spot Light #8"
|
||||
IDS_IIDX_NEON_LIGHT "Top Neons"
|
||||
IDS_DDR_SCHEMA "Dance Dance Revolution"
|
||||
IDS_DDR_P1_START "P1 Start"
|
||||
IDS_DDR_P1_MENU_UP "P1 Menu Up"
|
||||
IDS_DDR_P1_MENU_DOWN "P1 Menu Down"
|
||||
IDS_DDR_P1_MENU_LEFT "P1 Menu Left"
|
||||
IDS_DDR_P1_MENU_RIGHT "P1 Menu Right"
|
||||
IDS_DDR_P1_UP "P1 Up"
|
||||
IDS_DDR_P1_DOWN "P1 Down"
|
||||
IDS_DDR_P1_LEFT "P1 Left"
|
||||
IDS_DDR_P1_RIGHT "P1 Right"
|
||||
IDS_DDR_P2_START "P2 Start"
|
||||
IDS_DDR_P2_MENU_UP "P2 Menu Up"
|
||||
IDS_DDR_P2_MENU_DOWN "P2 Menu Down"
|
||||
IDS_DDR_P2_MENU_LEFT "P2 Menu Left"
|
||||
IDS_DDR_P2_MENU_RIGHT "P2 Menu Right"
|
||||
IDS_DDR_P2_UP "P2 Up"
|
||||
IDS_DDR_P2_DOWN "P2 Down"
|
||||
IDS_DDR_P2_LEFT "P2 Left"
|
||||
IDS_DDR_P2_RIGHT "P2 Right"
|
||||
IDS_DDR_P1_MENU_LIGHT "P1 Menu Lights"
|
||||
IDS_DDR_P1_TOP_LIGHT "P1 Marquee Top Light"
|
||||
IDS_DDR_P1_BOTTOM_LIGHT "P1 Marquee Bottom Light"
|
||||
IDS_DDR_P2_MENU_LIGHT "P2 Menu Lights"
|
||||
IDS_DDR_P2_TOP_LIGHT "P2 Marquee Top Light"
|
||||
IDS_DDR_P2_BOTTOM_LIGHT "P2 Marquee Bottom Light"
|
||||
IDS_DDR_BASS_LIGHT "Bass Neons"
|
||||
IDS_PNM_SCHEMA "pop'n music"
|
||||
IDS_PNM_BTN1 "Button 1"
|
||||
IDS_PNM_BTN2 "Button 2"
|
||||
IDS_PNM_BTN3 "Button 3"
|
||||
IDS_PNM_BTN4 "Button 4"
|
||||
IDS_PNM_BTN5 "Button 5"
|
||||
IDS_PNM_BTN6 "Button 6"
|
||||
IDS_PNM_BTN7 "Button 7"
|
||||
IDS_PNM_BTN8 "Button 8"
|
||||
IDS_PNM_BTN9 "Button 9"
|
||||
IDS_JB_SCHEMA "Jubeat"
|
||||
IDS_JB_PANEL1 "Panel 1"
|
||||
IDS_JB_PANEL2 "Panel 2"
|
||||
IDS_JB_PANEL3 "Panel 3"
|
||||
IDS_JB_PANEL4 "Panel 4"
|
||||
IDS_JB_PANEL5 "Panel 5"
|
||||
IDS_JB_PANEL6 "Panel 6"
|
||||
IDS_JB_PANEL7 "Panel 7"
|
||||
IDS_JB_PANEL8 "Panel 8"
|
||||
IDS_JB_PANEL9 "Panel 9"
|
||||
IDS_JB_PANEL10 "Panel 10"
|
||||
IDS_JB_PANEL11 "Panel 11"
|
||||
IDS_JB_PANEL12 "Panel 12"
|
||||
IDS_JB_PANEL13 "Panel 13"
|
||||
IDS_JB_PANEL14 "Panel 14"
|
||||
IDS_JB_PANEL15 "Panel 15"
|
||||
IDS_JB_PANEL16 "Panel 16"
|
||||
IDS_JB_RGB_FRONT_R "RGB Front R"
|
||||
IDS_JB_RGB_FRONT_G "RGB Front G"
|
||||
IDS_JB_RGB_FRONT_B "RGB Front B"
|
||||
IDS_JB_RGB_TOP_R "RGB Top R"
|
||||
IDS_JB_RGB_TOP_G "RGB Top G"
|
||||
IDS_JB_RGB_TOP_B "RGB Top B"
|
||||
IDS_JB_RGB_LEFT_R "RGB Left R"
|
||||
IDS_JB_RGB_LEFT_G "RGB Left G"
|
||||
IDS_JB_RGB_LEFT_B "RGB Left B"
|
||||
IDS_JB_RGB_RIGHT_R "RGB Right R"
|
||||
IDS_JB_RGB_RIGHT_G "RGB Right G"
|
||||
IDS_JB_RGB_RIGHT_B "RGB Right B"
|
||||
IDS_JB_RGB_TITLE_R "RGB Title R"
|
||||
IDS_JB_RGB_TITLE_G "RGB Title G"
|
||||
IDS_JB_RGB_TITLE_B "RGB Title B"
|
||||
IDS_JB_RGB_WOOFER_R "RGB Woofer R"
|
||||
IDS_JB_RGB_WOOFER_G "RGB Woofer G"
|
||||
IDS_JB_RGB_WOOFER_B "RGB Woofer B"
|
||||
IDS_SDVX_SCHEMA "Sound Voltex"
|
||||
IDS_SDVX_START "Start"
|
||||
IDS_SDVX_BTN_A "Button A"
|
||||
IDS_SDVX_BTN_B "Button B"
|
||||
IDS_SDVX_BTN_C "Button C"
|
||||
IDS_SDVX_BTN_D "Button D"
|
||||
IDS_SDVX_FX_L "FX-L"
|
||||
IDS_SDVX_FX_R "FX-R"
|
||||
IDS_SDVX_VOL_L "VOL-L"
|
||||
IDS_SDVX_VOL_R "VOL-R"
|
||||
IDS_SDVX_RGB1_R "Wing Left Up R"
|
||||
IDS_SDVX_RGB1_G "Wing Left Up G"
|
||||
IDS_SDVX_RGB1_B "Wing Left Up B"
|
||||
IDS_SDVX_RGB2_R "Wing Right Up R"
|
||||
IDS_SDVX_RGB2_G "Wing Right Up G"
|
||||
IDS_SDVX_RGB2_B "Wing Right Up B"
|
||||
IDS_SDVX_RGB3_R "Wing Left Low R"
|
||||
IDS_SDVX_RGB3_G "Wing Left Low G"
|
||||
IDS_SDVX_RGB3_B "Wing Left Low B"
|
||||
IDS_SDVX_RGB4_R "Wing Right Low R"
|
||||
IDS_SDVX_RGB4_G "Wing Right Low G"
|
||||
IDS_SDVX_RGB4_B "Wing Right Low B"
|
||||
IDS_SDVX_RGB5_R "Woofer R"
|
||||
IDS_SDVX_RGB5_G "Woofer G"
|
||||
IDS_SDVX_RGB5_B "Woofer B"
|
||||
IDS_SDVX_RGB6_R "Controller R"
|
||||
IDS_SDVX_RGB6_G "Controller G"
|
||||
IDS_SDVX_RGB6_B "Controller B"
|
||||
IDS_DM_SCHEMA "Drummania"
|
||||
IDS_DM_START "Start"
|
||||
IDS_DM_MENU_LEFT "Menu Left"
|
||||
IDS_DM_MENU_RIGHT "Menu Right"
|
||||
IDS_DM_HI_HAT "Hi-Hat"
|
||||
IDS_DM_SNARE "Snare"
|
||||
IDS_DM_HIGH_TOM "High Tom"
|
||||
IDS_DM_LOW_TOM "Low Tom"
|
||||
IDS_DM_CYMBAL "Cymbal"
|
||||
IDS_DM_BASS "Bass Drum"
|
||||
IDS_DM_SPEAKER_LIGHT "Speakers"
|
||||
IDS_DM_SPOT_LIGHT "Spot Light"
|
||||
IDS_DM_START_LIGHT "Start"
|
||||
IDS_DM_MENU_LIGHT "Menu Buttons"
|
||||
IDS_GF_SCHEMA "Guitar Freaks"
|
||||
IDS_GF_P1_START "P1 Start"
|
||||
IDS_GF_P1_PICK "P1 Pick"
|
||||
IDS_GF_P1_PICK_A "P1 Pick A (Guitar Hero compatible)"
|
||||
IDS_GF_P1_PICK_B "P1 Pick B (Guitar Hero compatible)"
|
||||
IDS_GF_P1_WAIL "P1 Wail"
|
||||
IDS_GF_P1_EFFECT "P1 Effect"
|
||||
IDS_GF_P1_RED "P1 Red"
|
||||
IDS_GF_P1_GREEN "P1 Green"
|
||||
IDS_GF_P1_BLUE "P1 Blue"
|
||||
IDS_GF_P2_START "P2 Start"
|
||||
IDS_GF_P2_PICK "P2 Pick"
|
||||
IDS_GF_P2_PICK_A "P2 Pick A (Guitar Hero compatible)"
|
||||
IDS_GF_P2_PICK_B "P2 Pick B (Guitar Hero compatible)"
|
||||
IDS_GF_P2_WAIL "P2 Wail"
|
||||
IDS_GF_P2_EFFECT "P2 Effect"
|
||||
IDS_GF_P2_RED "P2 Red"
|
||||
IDS_GF_P2_GREEN "P2 Green"
|
||||
IDS_GF_P2_BLUE "P2 Blue"
|
||||
IDS_GF_P1_SPOT_LIGHT "P1 Spotlight"
|
||||
IDS_GF_P2_SPOT_LIGHT "P2 Spotlight"
|
||||
IDS_RB_SCHEMA "Reflec Beat"
|
||||
IDS_BST_SCHEMA "BeatStream"
|
||||
IDS_IIDX_PANEL_SCHEMA "beatmania IIDX Effector Panel"
|
||||
IDS_IIDX_PANEL_S1_UP "Effector Slider 1 Up"
|
||||
IDS_IIDX_PANEL_S1_DOWN "Effector Slider 1 Down"
|
||||
IDS_IIDX_PANEL_S2_UP "Effector Slider 2 Up"
|
||||
IDS_IIDX_PANEL_S2_DOWN "Effector Slider 2 Down"
|
||||
IDS_IIDX_PANEL_S3_UP "Effector Slider 3 Up"
|
||||
IDS_IIDX_PANEL_S3_DOWN "Effector Slider 3 Down"
|
||||
IDS_IIDX_PANEL_S4_UP "Effector Slider 4 Up"
|
||||
IDS_IIDX_PANEL_S4_DOWN "Effector Slider 4 Down"
|
||||
IDS_IIDX_PANEL_S5_UP "Effector Slider 5 Up"
|
||||
IDS_IIDX_PANEL_S5_DOWN "Effector Slider 5 Down"
|
||||
IDS_IIDX_PANEL_S1 "Effector Slider 1"
|
||||
IDS_IIDX_PANEL_S2 "Effector Slider 2"
|
||||
IDS_IIDX_PANEL_S3 "Effector Slider 3"
|
||||
IDS_IIDX_PANEL_S4 "Effector Slider 4"
|
||||
IDS_IIDX_PANEL_S5 "Effector Slider 5"
|
||||
}
|
||||
@@ -0,0 +1,489 @@
|
||||
#include <windows.h>
|
||||
#include <commctrl.h>
|
||||
#include <commdlg.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "config/resource.h"
|
||||
#include "config/schema.h"
|
||||
|
||||
#include "eamio/eam-config.h"
|
||||
|
||||
#include "geninput/hid-mgr.h"
|
||||
#include "geninput/input-config.h"
|
||||
#include "geninput/kbd.h"
|
||||
|
||||
#include "util/array.h"
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
#include "util/str.h"
|
||||
|
||||
/* You may or may not have noticed that there is a blank label in the Network
|
||||
tab's dialog resource definition. This is a consequence of the way that
|
||||
Windows UI styles work. WIN32K's (i.e. raw USER32's) GUI routines draw
|
||||
widgets using the Windows 95 chunky bezels and flat surfaces style, but if
|
||||
your app pulls in COMMCTRL v6 or later using an SxS manifest, then it
|
||||
overrides the built-in Win32 widgets with its own glossy versions, in
|
||||
addition to the extended control library that its API provides.
|
||||
|
||||
I'm guessing that top-level windows and dialogs (the tab is a dialog) get
|
||||
subclassed as soon as one of these glossified built-ins gets WM_CREATEd
|
||||
within a window. Hence the presence of the Static control to make sure this
|
||||
takes place.
|
||||
|
||||
Otherwise, you get a flat grey shaded tab body, which looks wrong on Vista
|
||||
and above. */
|
||||
|
||||
struct eam_ui {
|
||||
struct array children;
|
||||
};
|
||||
|
||||
struct eam_unit_ui {
|
||||
const struct eam_unit_def *def;
|
||||
struct array devs;
|
||||
};
|
||||
|
||||
static INT_PTR CALLBACK eam_ui_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
static INT_PTR eam_ui_handle_init(HWND hwnd, const PROPSHEETPAGE *psp);
|
||||
static INT_PTR eam_ui_handle_activate(HWND hwnd);
|
||||
static INT_PTR eam_ui_handle_passivate(HWND hwnd);
|
||||
static INT_PTR eam_ui_handle_tick(HWND hwnd);
|
||||
static INT_PTR eam_ui_handle_change_alt_10k(HWND hwnd);
|
||||
static INT_PTR eam_ui_handle_change_autogen(HWND hwnd);
|
||||
static INT_PTR eam_ui_handle_fini(HWND hwnd);
|
||||
|
||||
static INT_PTR CALLBACK eam_unit_ui_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
static INT_PTR eam_unit_ui_handle_init(HWND hwnd,
|
||||
const struct eam_unit_def *def);
|
||||
static void eam_unit_ui_handle_init_devs(HWND hwnd);
|
||||
static void eam_unit_ui_handle_init_path(HWND hwnd);
|
||||
static INT_PTR eam_unit_ui_handle_browse(HWND hwnd);
|
||||
static INT_PTR eam_unit_ui_handle_change_device(HWND hwnd);
|
||||
static INT_PTR eam_unit_ui_handle_tick(HWND hwnd);
|
||||
static INT_PTR eam_unit_ui_handle_fini(HWND hwnd);
|
||||
|
||||
static const struct eam_io_config_api *eam_io_config_api;
|
||||
|
||||
HPROPSHEETPAGE eam_ui_tab_create(HINSTANCE inst, const struct schema *schema,
|
||||
const struct eam_io_config_api *api)
|
||||
{
|
||||
PROPSHEETPAGE psp;
|
||||
|
||||
eam_io_config_api = api;
|
||||
|
||||
memset(&psp, 0, sizeof(psp));
|
||||
psp.dwSize = sizeof(psp);
|
||||
psp.dwFlags = PSP_DEFAULT;
|
||||
psp.hInstance = inst;
|
||||
psp.pszTemplate = MAKEINTRESOURCE(IDD_TAB_NETWORK);
|
||||
psp.pfnDlgProc = eam_ui_dlg_proc;
|
||||
psp.lParam = (LPARAM) schema;
|
||||
|
||||
return CreatePropertySheetPage(&psp);
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK eam_ui_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam)
|
||||
{
|
||||
const NMHDR *n;
|
||||
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG:
|
||||
return eam_ui_handle_init(hwnd, (PROPSHEETPAGE *) lparam);
|
||||
|
||||
case WM_TIMER:
|
||||
return eam_ui_handle_tick(hwnd);
|
||||
|
||||
case WM_COMMAND:
|
||||
switch (HIWORD(wparam)) {
|
||||
case BN_CLICKED:
|
||||
switch (LOWORD(wparam)) {
|
||||
case IDC_AUTOGEN:
|
||||
return eam_ui_handle_change_autogen(hwnd);
|
||||
|
||||
case IDC_ALT_10K:
|
||||
return eam_ui_handle_change_alt_10k(hwnd);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
case WM_NOTIFY:
|
||||
n = (NMHDR *) lparam;
|
||||
|
||||
switch (n->code) {
|
||||
case PSN_SETACTIVE:
|
||||
return eam_ui_handle_activate(hwnd);
|
||||
|
||||
case PSN_KILLACTIVE:
|
||||
return eam_ui_handle_passivate(hwnd);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
||||
case WM_DESTROY:
|
||||
return eam_ui_handle_fini(hwnd);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static INT_PTR eam_ui_handle_init(HWND hwnd, const PROPSHEETPAGE *psp)
|
||||
{
|
||||
struct eam_ui *ui;
|
||||
const struct schema *schema;
|
||||
long ypos;
|
||||
size_t i;
|
||||
HINSTANCE inst;
|
||||
HWND child;
|
||||
RECT r;
|
||||
|
||||
ui = xmalloc(sizeof(*ui));
|
||||
array_init(&ui->children);
|
||||
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) ui);
|
||||
|
||||
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
||||
schema = (struct schema *) psp->lParam;
|
||||
|
||||
ypos = 0;
|
||||
|
||||
for (i = 0 ; i < schema->nunits ; i++) {
|
||||
child = CreateDialogParam(inst, MAKEINTRESOURCE(IDD_READER), hwnd,
|
||||
eam_unit_ui_dlg_proc, (LPARAM) &schema->units[i]);
|
||||
|
||||
GetWindowRect(child, &r);
|
||||
SetWindowPos(child, HWND_BOTTOM, 0, ypos, 0, 0,
|
||||
SWP_NOSIZE | SWP_SHOWWINDOW);
|
||||
|
||||
ypos += r.bottom - r.top;
|
||||
|
||||
*array_append(HWND, &ui->children) = child;
|
||||
}
|
||||
|
||||
if (eam_io_config_api->get_autogen()) {
|
||||
SendMessage(GetDlgItem(hwnd, IDC_AUTOGEN), BM_SETCHECK, BST_CHECKED, 0);
|
||||
}
|
||||
|
||||
if (eam_io_config_api->get_alt_10k()) {
|
||||
SendMessage(GetDlgItem(hwnd, IDC_ALT_10K), BM_SETCHECK, BST_CHECKED, 0);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR eam_ui_handle_activate(HWND hwnd)
|
||||
{
|
||||
SetTimer(hwnd, 17, 1, NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR eam_ui_handle_passivate(HWND hwnd)
|
||||
{
|
||||
KillTimer(hwnd, 1);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR eam_ui_handle_tick(HWND hwnd)
|
||||
{
|
||||
HWND child;
|
||||
struct eam_ui *ui;
|
||||
size_t i;
|
||||
|
||||
ui = (struct eam_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
mapper_update();
|
||||
|
||||
for (i = 0 ; i < ui->children.nitems ; i++) {
|
||||
child = *array_item(HWND, &ui->children, i);
|
||||
SendMessage(child, WM_USER, 0, 0);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR eam_ui_handle_change_alt_10k(HWND hwnd)
|
||||
{
|
||||
HWND btn;
|
||||
bool alt_10k;
|
||||
|
||||
btn = GetDlgItem(hwnd, IDC_ALT_10K);
|
||||
alt_10k = SendMessage(btn, BM_GETCHECK, 0, 0) == BST_CHECKED;
|
||||
|
||||
eam_io_config_api->set_alt_10k(alt_10k);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR eam_ui_handle_change_autogen(HWND hwnd)
|
||||
{
|
||||
HWND btn;
|
||||
bool autogen;
|
||||
|
||||
btn = GetDlgItem(hwnd, IDC_AUTOGEN);
|
||||
autogen = SendMessage(btn, BM_GETCHECK, 0, 0) == BST_CHECKED;
|
||||
|
||||
eam_io_config_api->set_autogen(autogen);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR eam_ui_handle_fini(HWND hwnd)
|
||||
{
|
||||
struct eam_ui *ui;
|
||||
|
||||
ui = (struct eam_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
array_fini(&ui->children);
|
||||
free(ui);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK eam_unit_ui_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam)
|
||||
{
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG:
|
||||
return eam_unit_ui_handle_init(hwnd,
|
||||
(struct eam_unit_def *) lparam);
|
||||
|
||||
case WM_USER:
|
||||
return eam_unit_ui_handle_tick(hwnd);
|
||||
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wparam)) {
|
||||
case IDC_KBD_DEVICE:
|
||||
switch (HIWORD(wparam)) {
|
||||
case CBN_SELCHANGE:
|
||||
return eam_unit_ui_handle_change_device(hwnd);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
case IDC_BROWSE:
|
||||
switch (HIWORD(wparam)) {
|
||||
case BN_CLICKED:
|
||||
return eam_unit_ui_handle_browse(hwnd);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
case WM_DESTROY:
|
||||
return eam_unit_ui_handle_fini(hwnd);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static INT_PTR eam_unit_ui_handle_init(HWND hwnd,
|
||||
const struct eam_unit_def *def)
|
||||
{
|
||||
struct eam_unit_ui *ui;
|
||||
wchar_t str[128];
|
||||
HINSTANCE inst;
|
||||
|
||||
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
||||
|
||||
LoadString(inst, def->label_rsrc, str, lengthof(str));
|
||||
SetWindowText(GetDlgItem(hwnd, IDC_GROUP), str);
|
||||
|
||||
ui = xmalloc(sizeof(*ui));
|
||||
ui->def = def;
|
||||
array_init(&ui->devs);
|
||||
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) ui);
|
||||
|
||||
eam_unit_ui_handle_init_devs(hwnd);
|
||||
eam_unit_ui_handle_init_path(hwnd);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void eam_unit_ui_handle_init_devs(HWND hwnd)
|
||||
{
|
||||
struct eam_unit_ui *ui;
|
||||
uint32_t dev_usage;
|
||||
wchar_t *dev_name;
|
||||
struct hid_stub *hid;
|
||||
size_t nchars;
|
||||
LRESULT index;
|
||||
HWND dev_list;
|
||||
|
||||
ui = (struct eam_unit_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
dev_list = GetDlgItem(hwnd, IDC_KBD_DEVICE);
|
||||
|
||||
*array_append(struct hid_stub *, &ui->devs) = NULL;
|
||||
SendMessage(dev_list, CB_ADDSTRING, 0, (LPARAM) TEXT(""));
|
||||
|
||||
hid_mgr_lock();
|
||||
|
||||
for (hid = hid_mgr_get_first_stub()
|
||||
; hid != NULL
|
||||
; hid = hid_mgr_get_next_stub(hid)) {
|
||||
if (!hid_stub_get_device_usage(hid, &dev_usage)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dev_usage != KBD_DEVICE_USAGE_KEYBOARD
|
||||
&& dev_usage != KBD_DEVICE_USAGE_KEYPAD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!hid_stub_get_name(hid, NULL, &nchars)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dev_name = xmalloc(nchars * sizeof(*dev_name));
|
||||
|
||||
if (!hid_stub_get_name(hid, dev_name, &nchars)) {
|
||||
free(dev_name);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
index = SendMessage(dev_list, CB_ADDSTRING, 0, (LPARAM) dev_name);
|
||||
|
||||
free(dev_name);
|
||||
|
||||
*array_append(struct hid_stub *, &ui->devs) = hid;
|
||||
|
||||
if (hid == eam_io_config_api->get_keypad_device(ui->def->unit_no)) {
|
||||
SendMessage(dev_list, CB_SETCURSEL, index, 0);
|
||||
}
|
||||
}
|
||||
|
||||
hid_mgr_unlock();
|
||||
}
|
||||
|
||||
static void eam_unit_ui_handle_init_path(HWND hwnd)
|
||||
{
|
||||
struct eam_unit_ui *ui;
|
||||
const char *path;
|
||||
wchar_t *wpath;
|
||||
|
||||
ui = (struct eam_unit_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
path = eam_io_config_api->get_card_path(ui->def->unit_no);
|
||||
|
||||
if (path != NULL) {
|
||||
wpath = str_widen(path);
|
||||
SetWindowText(GetDlgItem(hwnd, IDC_CARD_PATH), wpath);
|
||||
free(wpath);
|
||||
}
|
||||
}
|
||||
|
||||
static INT_PTR eam_unit_ui_handle_browse(HWND hwnd)
|
||||
{
|
||||
struct eam_unit_ui *ui;
|
||||
char *path_tmp;
|
||||
OPENFILENAME ofn;
|
||||
TCHAR path[MAX_PATH];
|
||||
HWND path_ctl;
|
||||
|
||||
ui = (struct eam_unit_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
path_ctl = GetDlgItem(hwnd, IDC_CARD_PATH);
|
||||
|
||||
path[0] = 0;
|
||||
GetWindowText(path_ctl, path, lengthof(path));
|
||||
|
||||
memset(&ofn, 0, sizeof(ofn));
|
||||
ofn.lStructSize = sizeof(ofn);
|
||||
ofn.hwndOwner = hwnd;
|
||||
ofn.lpstrFile = path;
|
||||
ofn.nMaxFile = lengthof(path);
|
||||
ofn.Flags = OFN_DONTADDTORECENT | OFN_ENABLESIZING | OFN_EXPLORER;
|
||||
|
||||
if (GetOpenFileName(&ofn) && wstr_narrow(path, &path_tmp)) {
|
||||
eam_io_config_api->set_card_path(ui->def->unit_no, path_tmp);
|
||||
free(path_tmp);
|
||||
|
||||
SendMessage(path_ctl, WM_SETTEXT, 0, (LPARAM) path);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR eam_unit_ui_handle_change_device(HWND hwnd)
|
||||
{
|
||||
struct eam_unit_ui *ui;
|
||||
struct hid_stub *hid;
|
||||
HWND dev_list;
|
||||
LRESULT index;
|
||||
|
||||
ui = (struct eam_unit_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
dev_list = GetDlgItem(hwnd, IDC_KBD_DEVICE);
|
||||
|
||||
index = SendMessage(dev_list, CB_GETCURSEL, 0, 0);
|
||||
|
||||
if (index != CB_ERR) {
|
||||
log_assert((size_t) index < ui->devs.nitems);
|
||||
|
||||
hid = *array_item(struct hid_stub *, &ui->devs, index);
|
||||
eam_io_config_api->set_keypad_device(ui->def->unit_no, hid);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR eam_unit_ui_handle_tick(HWND hwnd)
|
||||
{
|
||||
struct eam_unit_ui *ui;
|
||||
uint16_t state;
|
||||
wchar_t state_str[9];
|
||||
|
||||
ui = (struct eam_unit_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
state = eam_io_get_keypad_state(ui->def->unit_no);
|
||||
|
||||
wstr_format(state_str, lengthof(state_str), L"%04x", state);
|
||||
|
||||
SetWindowText(GetDlgItem(hwnd, IDC_KEYPAD_STATE), state_str);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR eam_unit_ui_handle_fini(HWND hwnd)
|
||||
{
|
||||
struct eam_unit_ui *ui;
|
||||
wchar_t path[MAX_PATH];
|
||||
char *tmp;
|
||||
|
||||
ui = (struct eam_unit_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
path[0] = 0;
|
||||
GetWindowText(GetDlgItem(hwnd, IDC_CARD_PATH), path, lengthof(path));
|
||||
|
||||
if (path[0]) {
|
||||
if (wstr_narrow(path, &tmp)) {
|
||||
eam_io_config_api->set_card_path(ui->def->unit_no, tmp);
|
||||
free(tmp);
|
||||
}
|
||||
} else {
|
||||
eam_io_config_api->set_card_path(ui->def->unit_no, NULL);
|
||||
}
|
||||
|
||||
array_fini(&ui->devs);
|
||||
free(ui);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "config/gametype.h"
|
||||
#include "config/resource.h"
|
||||
#include "config/schema.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static INT_PTR CALLBACK game_type_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
static INT_PTR game_type_handle_init(HWND hwnd);
|
||||
static INT_PTR game_type_handle_ok(HWND hwnd);
|
||||
static INT_PTR game_type_handle_cancel(HWND hwnd);
|
||||
|
||||
const struct schema *game_type_from_str(const char *name)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0 ; i < nschemas ; i++) {
|
||||
if (_stricmp(schemas[i].name, name) == 0) {
|
||||
return &schemas[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct schema *game_type_from_dialog(HINSTANCE inst)
|
||||
{
|
||||
return (struct schema *) DialogBoxParam(inst,
|
||||
MAKEINTRESOURCE(IDD_GAME_TYPE), NULL, game_type_dlg_proc, 0);
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK game_type_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam)
|
||||
{
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG:
|
||||
return game_type_handle_init(hwnd);
|
||||
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wparam)) {
|
||||
case IDOK:
|
||||
return game_type_handle_ok(hwnd);
|
||||
|
||||
case IDCANCEL:
|
||||
return game_type_handle_cancel(hwnd);
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static INT_PTR game_type_handle_init(HWND hwnd)
|
||||
{
|
||||
HINSTANCE inst;
|
||||
HWND ctl;
|
||||
size_t i;
|
||||
wchar_t text[1024];
|
||||
|
||||
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
||||
ctl = GetDlgItem(hwnd, IDC_GAME_TYPE);
|
||||
|
||||
for (i = 0 ; i < nschemas ; i++) {
|
||||
LoadString(inst, schemas[i].label, text, lengthof(text));
|
||||
SendMessage(ctl, CB_ADDSTRING, 0, (LPARAM) text);
|
||||
}
|
||||
|
||||
SendMessage(ctl, CB_SETCURSEL, 0, 0);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR game_type_handle_ok(HWND hwnd)
|
||||
{
|
||||
HWND ctl;
|
||||
size_t i;
|
||||
|
||||
ctl = GetDlgItem(hwnd, IDC_GAME_TYPE);
|
||||
i = (size_t) SendMessage(ctl, CB_GETCURSEL, 0, 0);
|
||||
|
||||
log_assert(i < nschemas);
|
||||
|
||||
EndDialog(hwnd, (INT_PTR) &schemas[i]);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR game_type_handle_cancel(HWND hwnd)
|
||||
{
|
||||
EndDialog(hwnd, (INT_PTR) NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef CONFIG_GAMETYPE_H
|
||||
#define CONFIG_GAMETYPE_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "config/schema.h"
|
||||
|
||||
const struct schema *game_type_from_str(const char *name);
|
||||
const struct schema *game_type_from_dialog(HINSTANCE inst);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,513 @@
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
#include <commctrl.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config/bind-light.h"
|
||||
#include "config/resource.h"
|
||||
#include "config/schema.h"
|
||||
#include "config/usages.h"
|
||||
|
||||
#include "geninput/input-config.h"
|
||||
#include "geninput/mapper.h"
|
||||
|
||||
#include "util/array.h"
|
||||
#include "util/defs.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
#define PULSE_DELTA 0.03f
|
||||
|
||||
struct lights_ui {
|
||||
const struct schema *schema;
|
||||
struct array devices;
|
||||
struct hid_stub *hid;
|
||||
struct hid_light *lights;
|
||||
size_t nlights;
|
||||
bool pulse_active;
|
||||
bool pulse_up;
|
||||
size_t pulse_light_no;
|
||||
float pulse_coeff;
|
||||
};
|
||||
|
||||
static INT_PTR CALLBACK lights_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
static INT_PTR lights_handle_init(HWND hwnd, const PROPSHEETPAGE *psp);
|
||||
static void lights_handle_init_devices(HWND hwnd);
|
||||
static void lights_handle_init_device(HWND hwnd, struct hid_stub *hid);
|
||||
static void lights_handle_init_lights(HWND hwnd);
|
||||
static INT_PTR lights_handle_change_dev(HWND hwnd);
|
||||
static INT_PTR lights_handle_highlight_light(HWND hwnd, const NMLISTVIEW *n);
|
||||
static INT_PTR lights_handle_bind_light(HWND hwnd);
|
||||
static INT_PTR lights_handle_pulse_tick(HWND hwnd);
|
||||
static void lights_update_bindings(HWND hwnd);
|
||||
static void lights_pulse_start(HWND hwnd, size_t light_no);
|
||||
static void lights_pulse_stop(HWND hwnd);
|
||||
static INT_PTR lights_handle_fini(HWND hwnd);
|
||||
|
||||
HPROPSHEETPAGE lights_tab_create(HINSTANCE inst, const struct schema *schema)
|
||||
{
|
||||
PROPSHEETPAGE psp;
|
||||
|
||||
memset(&psp, 0, sizeof(psp));
|
||||
psp.dwSize = sizeof(psp);
|
||||
psp.dwFlags = PSP_DEFAULT;
|
||||
psp.hInstance = inst;
|
||||
psp.pszTemplate = MAKEINTRESOURCE(IDD_TAB_LIGHTS);
|
||||
psp.pfnDlgProc = lights_dlg_proc;
|
||||
psp.lParam = (LPARAM) schema;
|
||||
|
||||
return CreatePropertySheetPage(&psp);
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK lights_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam)
|
||||
{
|
||||
NMHDR *n;
|
||||
|
||||
switch (msg) {
|
||||
case WM_INITDIALOG:
|
||||
return lights_handle_init(hwnd, (const PROPSHEETPAGE *) lparam);
|
||||
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wparam)) {
|
||||
case IDC_DEVICE:
|
||||
switch (HIWORD(wparam)) {
|
||||
case CBN_SELCHANGE:
|
||||
return lights_handle_change_dev(hwnd);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
||||
case WM_NOTIFY:
|
||||
n = (NMHDR *) lparam;
|
||||
|
||||
switch (n->idFrom) {
|
||||
case IDC_LIGHT:
|
||||
switch (n->code) {
|
||||
case LVN_ITEMCHANGED:
|
||||
return lights_handle_highlight_light(hwnd,
|
||||
(NMLISTVIEW *) n);
|
||||
|
||||
case NM_DBLCLK:
|
||||
return lights_handle_bind_light(hwnd);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
||||
case WM_TIMER:
|
||||
return lights_handle_pulse_tick(hwnd);
|
||||
|
||||
case WM_DESTROY:
|
||||
return lights_handle_fini(hwnd);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static INT_PTR lights_handle_init(HWND hwnd, const PROPSHEETPAGE *psp)
|
||||
{
|
||||
struct lights_ui *ui;
|
||||
|
||||
ui = xmalloc(sizeof(*ui));
|
||||
ui->schema = (const struct schema *) psp->lParam;
|
||||
ui->hid = NULL;
|
||||
ui->lights = NULL;
|
||||
array_init(&ui->devices);
|
||||
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (INT_PTR) ui);
|
||||
|
||||
lights_handle_init_devices(hwnd);
|
||||
lights_handle_init_lights(hwnd);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void lights_handle_init_devices(HWND hwnd)
|
||||
{
|
||||
struct hid_stub *hid;
|
||||
|
||||
hid_mgr_lock();
|
||||
|
||||
for (hid = hid_mgr_get_first_stub()
|
||||
; hid != NULL
|
||||
; hid = hid_mgr_get_next_stub(hid)) {
|
||||
lights_handle_init_device(hwnd, hid);
|
||||
}
|
||||
|
||||
hid_mgr_unlock();
|
||||
}
|
||||
|
||||
static void lights_handle_init_device(HWND hwnd, struct hid_stub *hid)
|
||||
{
|
||||
wchar_t *chars;
|
||||
size_t nchars;
|
||||
size_t nlights;
|
||||
struct lights_ui *ui;
|
||||
HWND devs;
|
||||
|
||||
if (!hid_stub_get_lights(hid, NULL, &nlights) || nlights == 0) {
|
||||
goto lights_fail;
|
||||
}
|
||||
|
||||
if (!hid_stub_get_name(hid, NULL, &nchars)) {
|
||||
goto name_sz_fail;
|
||||
}
|
||||
|
||||
chars = xmalloc(sizeof(*chars) * nchars);
|
||||
|
||||
if (!hid_stub_get_name(hid, chars, &nchars)) {
|
||||
goto name_fail;
|
||||
}
|
||||
|
||||
ui = (struct lights_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
devs = GetDlgItem(hwnd, IDC_DEVICE);
|
||||
|
||||
*array_append(struct hid_stub *, &ui->devices) = hid;
|
||||
ComboBox_AddString(devs, chars);
|
||||
|
||||
return;
|
||||
|
||||
name_fail:
|
||||
free(chars);
|
||||
|
||||
name_sz_fail:
|
||||
lights_fail:
|
||||
return;
|
||||
}
|
||||
|
||||
static void lights_handle_init_lights(HWND hwnd)
|
||||
{
|
||||
wchar_t str[128];
|
||||
HINSTANCE inst;
|
||||
LVCOLUMN col;
|
||||
HWND lights;
|
||||
|
||||
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
||||
lights = GetDlgItem(hwnd, IDC_LIGHT);
|
||||
|
||||
ListView_SetExtendedListViewStyle(lights,
|
||||
LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
|
||||
|
||||
LoadString(inst, IDS_COL_LIGHT_HID, str, lengthof(str));
|
||||
|
||||
col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
|
||||
col.fmt = LVCFMT_LEFT;
|
||||
col.cx = 120;
|
||||
col.pszText = str;
|
||||
|
||||
ListView_InsertColumn(lights, 0, &col);
|
||||
|
||||
LoadString(inst, IDS_COL_LIGHT_GAME, str, lengthof(str));
|
||||
|
||||
col.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
|
||||
col.fmt = LVCFMT_LEFT;
|
||||
col.cx = 120;
|
||||
col.pszText = str;
|
||||
|
||||
ListView_InsertColumn(lights, 1, &col);
|
||||
}
|
||||
|
||||
static INT_PTR lights_handle_change_dev(HWND hwnd)
|
||||
{
|
||||
char chars[256];
|
||||
wchar_t wchars[256];
|
||||
struct lights_ui *ui;
|
||||
struct hid_stub *hid;
|
||||
size_t i;
|
||||
int dev_idx;
|
||||
LVITEM item;
|
||||
HWND devs_ctl;
|
||||
HWND lights_ctl;
|
||||
|
||||
lights_pulse_stop(hwnd);
|
||||
|
||||
ui = (struct lights_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
devs_ctl = GetDlgItem(hwnd, IDC_DEVICE);
|
||||
lights_ctl = GetDlgItem(hwnd, IDC_LIGHT);
|
||||
dev_idx = ComboBox_GetCurSel(devs_ctl);
|
||||
|
||||
if (dev_idx >= (int) ui->devices.nitems) {
|
||||
goto unlocked_fail;
|
||||
}
|
||||
|
||||
ListView_DeleteAllItems(lights_ctl);
|
||||
|
||||
hid = *array_item(struct hid_stub *, &ui->devices, dev_idx);
|
||||
|
||||
hid_mgr_lock();
|
||||
|
||||
if (!hid_stub_get_lights(hid, NULL, &ui->nlights)) {
|
||||
goto locked_fail;
|
||||
}
|
||||
|
||||
ui->lights = xrealloc(ui->lights, sizeof(*ui->lights) * ui->nlights);
|
||||
|
||||
if (!hid_stub_get_lights(hid, ui->lights, &ui->nlights)) {
|
||||
free(ui->lights);
|
||||
ui->lights = NULL;
|
||||
|
||||
goto locked_fail;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < ui->nlights ; i++) {
|
||||
if(ui->lights[i].name[0] != L'\0') {
|
||||
item.pszText = ui->lights[i].name;
|
||||
} else {
|
||||
wchars[0] = L'\0';
|
||||
|
||||
usages_get(chars, lengthof(chars), ui->lights[i].usage);
|
||||
MultiByteToWideChar(CP_UTF8, 0, chars, lengthof(chars), wchars,
|
||||
lengthof(wchars));
|
||||
|
||||
item.pszText = wchars;
|
||||
}
|
||||
|
||||
item.mask = LVIF_TEXT;
|
||||
item.iItem = (int) i;
|
||||
item.iSubItem = 0;
|
||||
|
||||
ListView_InsertItem(lights_ctl, &item);
|
||||
}
|
||||
|
||||
ui->hid = hid;
|
||||
lights_update_bindings(hwnd);
|
||||
|
||||
locked_fail:
|
||||
hid_mgr_unlock();
|
||||
|
||||
unlocked_fail:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR lights_handle_highlight_light(HWND hwnd, const NMLISTVIEW *n)
|
||||
{
|
||||
size_t light_no;
|
||||
|
||||
light_no = n->iItem;
|
||||
|
||||
if (n->uNewState & LVIS_SELECTED) {
|
||||
lights_pulse_start(hwnd, light_no);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR lights_handle_bind_light(HWND hwnd)
|
||||
{
|
||||
struct lights_ui *ui;
|
||||
struct mapped_light ml;
|
||||
uint8_t game_light;
|
||||
bool bound;
|
||||
HINSTANCE inst;
|
||||
HWND lights_ctl;
|
||||
int pos;
|
||||
|
||||
ui = (struct lights_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
||||
lights_ctl = GetDlgItem(hwnd, IDC_LIGHT);
|
||||
|
||||
pos = ListView_GetSelectionMark(lights_ctl);
|
||||
|
||||
if (pos < 0) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ml.hid = ui->hid;
|
||||
ml.light_no = pos;
|
||||
|
||||
if (bind_light(inst, hwnd, ui->schema, &ml, &game_light, &bound)) {
|
||||
if (bound) {
|
||||
mapper_set_light_map(&ml, game_light);
|
||||
} else {
|
||||
mapper_clear_light_map(&ml);
|
||||
}
|
||||
|
||||
lights_update_bindings(hwnd);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static INT_PTR lights_handle_pulse_tick(HWND hwnd)
|
||||
{
|
||||
struct lights_ui *ui;
|
||||
size_t light_no;
|
||||
float intensity;
|
||||
float bias;
|
||||
float scale;
|
||||
float tmp;
|
||||
|
||||
ui = (struct lights_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
if (!ui->pulse_active) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
light_no = ui->pulse_light_no;
|
||||
|
||||
if (ui->hid == NULL || light_no >= ui->nlights) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (ui->pulse_up) {
|
||||
tmp = ui->pulse_coeff + PULSE_DELTA;
|
||||
|
||||
if (tmp > 1.0f) {
|
||||
ui->pulse_coeff = 1.0f;
|
||||
ui->pulse_up = false;
|
||||
} else {
|
||||
ui->pulse_coeff = tmp;
|
||||
}
|
||||
} else {
|
||||
tmp = ui->pulse_coeff - PULSE_DELTA;
|
||||
|
||||
if (tmp < 0.0f) {
|
||||
ui->pulse_coeff = 0.0f;
|
||||
ui->pulse_up = true;
|
||||
} else {
|
||||
ui->pulse_coeff = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
bias = (float) (ui->lights[light_no].value_min);
|
||||
scale = (float) (ui->lights[light_no].value_max
|
||||
- ui->lights[light_no].value_min);
|
||||
|
||||
/* Intensity perception is non-linear. Pulse quadratically. */
|
||||
|
||||
intensity = bias + scale * ui->pulse_coeff * ui->pulse_coeff + 0.5f;
|
||||
|
||||
hid_mgr_lock();
|
||||
hid_stub_set_light(ui->hid, light_no, (int32_t) intensity);
|
||||
hid_mgr_unlock();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void lights_update_bindings(HWND hwnd)
|
||||
{
|
||||
light_iter_t pos;
|
||||
struct mapped_light ml;
|
||||
uint8_t game_light;
|
||||
size_t i;
|
||||
wchar_t wchars[256];
|
||||
struct lights_ui *ui;
|
||||
HINSTANCE inst;
|
||||
HWND lights_ctl;
|
||||
|
||||
ui = (struct lights_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
inst = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
|
||||
lights_ctl = GetDlgItem(hwnd, IDC_LIGHT);
|
||||
|
||||
for (i = 0 ; i < ui->nlights ; i++) {
|
||||
ListView_SetItemText(lights_ctl, i, 1, NULL);
|
||||
}
|
||||
|
||||
for (pos = mapper_iterate_lights()
|
||||
; light_iter_is_valid(pos)
|
||||
; light_iter_next(pos)) {
|
||||
light_iter_get_mapping(pos, &ml);
|
||||
|
||||
if (ml.hid != ui->hid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ml.light_no >= ui->nlights) {
|
||||
/* Shouldn't happen unless the HID report descriptor has changed
|
||||
since we last configured this device */
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
game_light = light_iter_get_game_light(pos);
|
||||
|
||||
for (i = 0 ; i < ui->schema->nlights ; i++) {
|
||||
if (ui->schema->lights[i].bit == game_light) {
|
||||
LoadString(inst, ui->schema->lights[i].name_rsrc, wchars,
|
||||
lengthof(wchars));
|
||||
ListView_SetItemText(lights_ctl, ml.light_no, 1, wchars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
light_iter_free(pos);
|
||||
}
|
||||
|
||||
static void lights_pulse_start(HWND hwnd, size_t light_no)
|
||||
{
|
||||
struct lights_ui *ui;
|
||||
int32_t intensity;
|
||||
|
||||
ui = (struct lights_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
if (ui->pulse_active) {
|
||||
lights_pulse_stop(hwnd);
|
||||
}
|
||||
|
||||
/* Start light at max intensity */
|
||||
|
||||
intensity = ui->lights[light_no].value_max;
|
||||
|
||||
hid_mgr_lock();
|
||||
hid_stub_set_light(ui->hid, light_no, intensity);
|
||||
hid_mgr_unlock();
|
||||
|
||||
ui->pulse_active = true;
|
||||
ui->pulse_up = false;
|
||||
ui->pulse_light_no = light_no;
|
||||
ui->pulse_coeff = 1.0f;
|
||||
|
||||
SetTimer(hwnd, 1, 17, NULL);
|
||||
}
|
||||
|
||||
static void lights_pulse_stop(HWND hwnd)
|
||||
{
|
||||
struct lights_ui *ui;
|
||||
size_t light_no;
|
||||
int32_t intensity;
|
||||
|
||||
ui = (struct lights_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
if (!ui->pulse_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
light_no = ui->pulse_light_no;
|
||||
|
||||
if (ui->hid != NULL && light_no < ui->nlights) {
|
||||
intensity = ui->lights[light_no].value_min;
|
||||
|
||||
hid_mgr_lock();
|
||||
hid_stub_set_light(ui->hid, light_no, intensity);
|
||||
hid_mgr_unlock();
|
||||
}
|
||||
|
||||
ui->pulse_active = false;
|
||||
|
||||
KillTimer(hwnd, 1);
|
||||
}
|
||||
|
||||
static INT_PTR lights_handle_fini(HWND hwnd)
|
||||
{
|
||||
struct lights_ui *ui;
|
||||
|
||||
lights_pulse_stop(hwnd);
|
||||
|
||||
ui = (struct lights_ui *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
array_fini(&ui->devices);
|
||||
free(ui->lights);
|
||||
free(ui);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
#include <windows.h>
|
||||
#include <commctrl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config/gametype.h"
|
||||
#include "config/resource.h"
|
||||
#include "config/schema.h"
|
||||
#include "config/spinner.h"
|
||||
#include "config/usages.h"
|
||||
|
||||
#include "eamio/eam-config.h"
|
||||
|
||||
#include "geninput/input-config.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/winres.h"
|
||||
|
||||
HPROPSHEETPAGE analogs_ui_tab_create(HINSTANCE inst,
|
||||
const struct schema *schema);
|
||||
HPROPSHEETPAGE buttons_tab_create(HINSTANCE inst, const struct schema *schema);
|
||||
HPROPSHEETPAGE lights_tab_create(HINSTANCE inst, const struct schema *schema);
|
||||
HPROPSHEETPAGE eam_ui_tab_create(HINSTANCE inst, const struct schema *schema,
|
||||
const struct eam_io_config_api *eam_io_config_api);
|
||||
|
||||
static void my_fatal(const char *module, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[1024];
|
||||
|
||||
va_start(ap, fmt);
|
||||
str_vformat(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
MessageBoxA(NULL, buf, NULL, MB_ICONERROR | MB_OK);
|
||||
DebugBreak();
|
||||
|
||||
ExitProcess(-1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
INITCOMMONCONTROLSEX iccx;
|
||||
HINSTANCE inst;
|
||||
HPROPSHEETPAGE psp[4];
|
||||
PROPSHEETHEADER psh;
|
||||
intptr_t result;
|
||||
const struct eam_io_config_api *eam_io_config_api;
|
||||
const struct schema *schema;
|
||||
wchar_t text[1024];
|
||||
int max_light;
|
||||
size_t i;
|
||||
|
||||
inst = GetModuleHandle(NULL);
|
||||
|
||||
log_to_writer(log_writer_debug, NULL);
|
||||
log_to_external(log_impl_misc, log_impl_info, log_impl_warning, my_fatal);
|
||||
|
||||
usages_init(inst);
|
||||
|
||||
memset(&iccx, 0, sizeof(iccx));
|
||||
iccx.dwSize = sizeof(iccx);
|
||||
iccx.dwICC = ICC_STANDARD_CLASSES | ICC_TAB_CLASSES | ICC_WIN95_CLASSES;
|
||||
|
||||
if (!InitCommonControlsEx(&iccx)) {
|
||||
log_fatal("InitCommonControlsEx failed");
|
||||
}
|
||||
|
||||
spinner_init(inst);
|
||||
|
||||
if (argc == 2) {
|
||||
schema = game_type_from_str(argv[1]);
|
||||
|
||||
if (schema == NULL) {
|
||||
rswprintf(text, lengthof(text), inst, IDS_BAD_GAME_TYPE, argv[1]);
|
||||
MessageBox(NULL, text, NULL, MB_ICONERROR | MB_OK);
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else {
|
||||
schema = game_type_from_dialog(inst);
|
||||
|
||||
if (schema == NULL) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
input_set_loggers(log_impl_misc, log_impl_info, log_impl_warning,
|
||||
log_impl_fatal);
|
||||
input_init(crt_thread_create, crt_thread_join, crt_thread_destroy);
|
||||
|
||||
eam_io_set_loggers(log_impl_misc, log_impl_info, log_impl_warning,
|
||||
log_impl_fatal);
|
||||
eam_io_init(crt_thread_create, crt_thread_join, crt_thread_destroy);
|
||||
eam_io_config_api = eam_io_get_config_api();
|
||||
|
||||
if (!mapper_config_load(schema->name)) {
|
||||
log_info("Initializing empty config for %s", schema->name);
|
||||
|
||||
max_light = -1;
|
||||
|
||||
for (i = 0 ; i < schema->nlights ; i++) {
|
||||
if (max_light < schema->lights[i].bit) {
|
||||
max_light = schema->lights[i].bit;
|
||||
}
|
||||
}
|
||||
|
||||
mapper_set_nlights((uint8_t) (max_light + 1));
|
||||
mapper_set_nanalogs((uint8_t) schema->nanalogs);
|
||||
}
|
||||
|
||||
memset(&psh, 0, sizeof(psh));
|
||||
psh.dwSize = sizeof(psh);
|
||||
psh.dwFlags = PSH_NOAPPLYNOW | PSH_NOCONTEXTHELP | PSH_USEHICON;
|
||||
psh.hInstance = inst;
|
||||
psh.hIcon = LoadIcon(NULL, IDI_QUESTION);
|
||||
psh.pszCaption = MAKEINTRESOURCE(IDS_APPNAME);
|
||||
psh.phpage = psp;
|
||||
psh.nPages = 0;
|
||||
|
||||
psp[psh.nPages++] = buttons_tab_create(inst, schema);
|
||||
psp[psh.nPages++] = lights_tab_create(inst, schema);
|
||||
|
||||
if (schema->nanalogs > 0) {
|
||||
psp[psh.nPages++] = analogs_ui_tab_create(inst, schema);
|
||||
}
|
||||
|
||||
if (eam_io_config_api != NULL) {
|
||||
psp[psh.nPages++] = eam_ui_tab_create(inst, schema, eam_io_config_api);
|
||||
}
|
||||
|
||||
/* Run GUI */
|
||||
|
||||
result = PropertySheet(&psh);
|
||||
|
||||
/* Save settings and clean up */
|
||||
|
||||
if (result > 0) {
|
||||
if (eam_io_config_api != NULL) {
|
||||
eam_io_config_api->config_save();
|
||||
}
|
||||
|
||||
mapper_config_save(schema->name);
|
||||
}
|
||||
|
||||
eam_io_fini();
|
||||
input_fini();
|
||||
spinner_fini(inst);
|
||||
usages_fini();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
#ifndef IDC_STATIC
|
||||
#define IDC_STATIC (-1)
|
||||
#endif
|
||||
|
||||
#define IDR_USAGES 101
|
||||
#define IDD_TAB_BUTTONS 102
|
||||
#define IDD_TAB_NETWORK 103
|
||||
#define IDD_BIND 104
|
||||
#define IDD_READER 105
|
||||
#define IDD_GAME_TYPE 107
|
||||
#define IDD_ANALOG 109
|
||||
#define IDD_TAB_ANALOGS 111
|
||||
#define IDD_BIND_ADV 113
|
||||
#define IDD_TAB_LIGHTS 115
|
||||
#define IDD_BIND_LIGHT 117
|
||||
#define IDC_GAME_LIGHT 1000
|
||||
#define IDC_KBD_DEVICE 1000
|
||||
#define IDC_AUTOGEN 1001
|
||||
#define IDC_CARD_PATH 1001
|
||||
#define IDC_LIGHT 1001
|
||||
#define IDC_SENSITIVITY 1001
|
||||
#define IDC_ACTION_NAME 1002
|
||||
#define IDC_ALT_10K 1002
|
||||
#define IDC_KEYPAD_STATE 1002
|
||||
#define IDC_GAME_TYPE 1003
|
||||
#define IDC_GROUP 1003
|
||||
#define IDC_LIMIT_MIN 1003
|
||||
#define IDC_BROWSE 1004
|
||||
#define IDC_BINDINGS 1005
|
||||
#define IDC_DEVICE 1005
|
||||
#define IDC_BINDING_EDIT 1006
|
||||
#define IDC_CONTROL 1006
|
||||
#define IDC_BINDING_CLEAR 1007
|
||||
#define IDC_BINDING_MIN 1008
|
||||
#define IDC_PAGE_TEXT 1008
|
||||
#define IDC_POSITION 1008
|
||||
#define IDC_PAGE 1009
|
||||
#define IDC_BINDING_MAX 1010
|
||||
#define IDC_LIMIT_MAX 1011
|
||||
#define IDC_CURRENT 1013
|
||||
#define IDC_BINDING_EDIT_MANY 40000
|
||||
#define IDS_APPNAME 40000
|
||||
#define IDS_BAD_GAME_TYPE 40001
|
||||
#define IDS_PAGE 40002
|
||||
#define IDS_COL_BUTTON 40003
|
||||
#define IDS_COL_ACTION 40004
|
||||
#define IDS_NOT_PRESENT 40005
|
||||
#define IDS_READER_P1 40006
|
||||
#define IDS_READER_P2 40007
|
||||
#define IDS_MUXED_TITLE 40008
|
||||
#define IDS_MUXED_MSG 40009
|
||||
#define IDS_COL_LIGHT_HID 40010
|
||||
#define IDS_COL_LIGHT_GAME 40011
|
||||
#define IDS_GENERIC_TEST 40012
|
||||
#define IDS_GENERIC_SERVICE 40013
|
||||
#define IDS_IIDX_SCHEMA 40014
|
||||
#define IDS_IIDX_P1_1 40015
|
||||
#define IDS_IIDX_P1_2 40016
|
||||
#define IDS_IIDX_P1_3 40017
|
||||
#define IDS_IIDX_P1_4 40018
|
||||
#define IDS_IIDX_P1_5 40019
|
||||
#define IDS_IIDX_P1_6 40020
|
||||
#define IDS_IIDX_P1_7 40021
|
||||
#define IDS_IIDX_P2_1 40022
|
||||
#define IDS_IIDX_P2_2 40023
|
||||
#define IDS_IIDX_P2_3 40024
|
||||
#define IDS_IIDX_P2_4 40025
|
||||
#define IDS_IIDX_P2_5 40026
|
||||
#define IDS_IIDX_P2_6 40027
|
||||
#define IDS_IIDX_P2_7 40028
|
||||
#define IDS_IIDX_P1_START 40029
|
||||
#define IDS_IIDX_P2_START 40030
|
||||
#define IDS_IIDX_VEFX 40031
|
||||
#define IDS_IIDX_EFFECT 40032
|
||||
#define IDS_IIDX_P1_TT_UP 40033
|
||||
#define IDS_IIDX_P1_TT_DOWN 40034
|
||||
#define IDS_IIDX_P1_TT_STAB 40035
|
||||
#define IDS_IIDX_P2_TT_UP 40036
|
||||
#define IDS_IIDX_P2_TT_DOWN 40037
|
||||
#define IDS_IIDX_P2_TT_STAB 40038
|
||||
#define IDS_IIDX_P1_TT 40039
|
||||
#define IDS_IIDX_P2_TT 40040
|
||||
#define IDS_IIDX_SPOT_1_LIGHT 40041
|
||||
#define IDS_IIDX_SPOT_2_LIGHT 40042
|
||||
#define IDS_IIDX_SPOT_3_LIGHT 40043
|
||||
#define IDS_IIDX_SPOT_4_LIGHT 40044
|
||||
#define IDS_IIDX_SPOT_5_LIGHT 40045
|
||||
#define IDS_IIDX_SPOT_6_LIGHT 40046
|
||||
#define IDS_IIDX_SPOT_7_LIGHT 40047
|
||||
#define IDS_IIDX_SPOT_8_LIGHT 40048
|
||||
#define IDS_IIDX_NEON_LIGHT 40049
|
||||
#define IDS_DDR_SCHEMA 40050
|
||||
#define IDS_DDR_P1_START 40051
|
||||
#define IDS_DDR_P1_MENU_UP 40052
|
||||
#define IDS_DDR_P1_MENU_DOWN 40053
|
||||
#define IDS_DDR_P1_MENU_LEFT 40054
|
||||
#define IDS_DDR_P1_MENU_RIGHT 40055
|
||||
#define IDS_DDR_P1_UP 40056
|
||||
#define IDS_DDR_P1_DOWN 40057
|
||||
#define IDS_DDR_P1_LEFT 40058
|
||||
#define IDS_DDR_P1_RIGHT 40059
|
||||
#define IDS_DDR_P2_START 40060
|
||||
#define IDS_DDR_P2_MENU_UP 40061
|
||||
#define IDS_DDR_P2_MENU_DOWN 40062
|
||||
#define IDS_DDR_P2_MENU_LEFT 40063
|
||||
#define IDS_DDR_P2_MENU_RIGHT 40064
|
||||
#define IDS_DDR_P2_UP 40065
|
||||
#define IDS_DDR_P2_DOWN 40066
|
||||
#define IDS_DDR_P2_LEFT 40067
|
||||
#define IDS_DDR_P2_RIGHT 40068
|
||||
#define IDS_DDR_P1_MENU_LIGHT 40069
|
||||
#define IDS_DDR_P1_TOP_LIGHT 40070
|
||||
#define IDS_DDR_P1_BOTTOM_LIGHT 40071
|
||||
#define IDS_DDR_P2_MENU_LIGHT 40072
|
||||
#define IDS_DDR_P2_TOP_LIGHT 40073
|
||||
#define IDS_DDR_P2_BOTTOM_LIGHT 40074
|
||||
#define IDS_DDR_BASS_LIGHT 40075
|
||||
#define IDS_PNM_SCHEMA 40076
|
||||
#define IDS_PNM_BTN1 40077
|
||||
#define IDS_PNM_BTN2 40078
|
||||
#define IDS_PNM_BTN3 40079
|
||||
#define IDS_PNM_BTN4 40080
|
||||
#define IDS_PNM_BTN5 40081
|
||||
#define IDS_PNM_BTN6 40082
|
||||
#define IDS_PNM_BTN7 40083
|
||||
#define IDS_PNM_BTN8 40084
|
||||
#define IDS_PNM_BTN9 40085
|
||||
#define IDS_JB_SCHEMA 40086
|
||||
#define IDS_JB_PANEL1 40087
|
||||
#define IDS_JB_PANEL2 40088
|
||||
#define IDS_JB_PANEL3 40089
|
||||
#define IDS_JB_PANEL4 40090
|
||||
#define IDS_JB_PANEL5 40091
|
||||
#define IDS_JB_PANEL6 40092
|
||||
#define IDS_JB_PANEL7 40093
|
||||
#define IDS_JB_PANEL8 40094
|
||||
#define IDC_BINDING_ADV 40095
|
||||
#define IDS_JB_PANEL9 40095
|
||||
#define IDS_JB_PANEL10 40096
|
||||
#define IDS_JB_PANEL11 40097
|
||||
#define IDS_JB_PANEL12 40098
|
||||
#define IDS_JB_PANEL13 40099
|
||||
#define IDS_JB_PANEL14 40100
|
||||
#define IDS_JB_PANEL15 40101
|
||||
#define IDS_JB_PANEL16 40102
|
||||
#define IDS_SDVX_SCHEMA 40103
|
||||
#define IDS_SDVX_START 40104
|
||||
#define IDS_SDVX_BTN_A 40105
|
||||
#define IDS_SDVX_BTN_B 40106
|
||||
#define IDS_SDVX_BTN_C 40107
|
||||
#define IDS_SDVX_BTN_D 40108
|
||||
#define IDS_SDVX_FX_L 40109
|
||||
#define IDS_SDVX_FX_R 40110
|
||||
#define IDS_SDVX_VOL_L 40111
|
||||
#define IDS_SDVX_VOL_R 40112
|
||||
#define IDS_DM_SCHEMA 40113
|
||||
#define IDS_DM_START 40114
|
||||
#define IDS_DM_MENU_LEFT 40115
|
||||
#define IDS_DM_MENU_RIGHT 40116
|
||||
#define IDS_DM_HI_HAT 40117
|
||||
#define IDS_DM_SNARE 40118
|
||||
#define IDS_DM_HIGH_TOM 40119
|
||||
#define IDS_DM_LOW_TOM 40120
|
||||
#define IDS_DM_CYMBAL 40121
|
||||
#define IDS_DM_BASS 40122
|
||||
#define IDS_DM_SPEAKER_LIGHT 40123
|
||||
#define IDS_DM_SPOT_LIGHT 40124
|
||||
#define IDS_DM_START_LIGHT 40125
|
||||
#define IDS_DM_MENU_LIGHT 40126
|
||||
#define IDS_GF_SCHEMA 40127
|
||||
#define IDS_GF_P1_START 40128
|
||||
#define IDS_GF_P1_PICK 40129
|
||||
#define IDS_GF_P1_PICK_A 40130
|
||||
#define IDS_GF_P1_PICK_B 40131
|
||||
#define IDS_GF_P1_WAIL 40132
|
||||
#define IDS_GF_P1_EFFECT 40133
|
||||
#define IDS_GF_P1_RED 40134
|
||||
#define IDS_GF_P1_GREEN 40135
|
||||
#define IDS_GF_P1_BLUE 40136
|
||||
#define IDS_GF_P2_START 40137
|
||||
#define IDS_GF_P2_PICK 40138
|
||||
#define IDS_GF_P2_PICK_A 40139
|
||||
#define IDS_GF_P2_PICK_B 40140
|
||||
#define IDS_GF_P2_WAIL 40141
|
||||
#define IDS_GF_P2_EFFECT 40142
|
||||
#define IDS_GF_P2_RED 40143
|
||||
#define IDS_GF_P2_GREEN 40144
|
||||
#define IDS_GF_P2_BLUE 40145
|
||||
#define IDS_GF_P1_SPOT_LIGHT 40146
|
||||
#define IDS_GF_P2_SPOT_LIGHT 40147
|
||||
#define IDS_RB_SCHEMA 40148
|
||||
#define IDS_SDVX_RGB1_R 40149
|
||||
#define IDS_SDVX_RGB1_G 40150
|
||||
#define IDS_SDVX_RGB1_B 40151
|
||||
#define IDS_SDVX_RGB2_R 40152
|
||||
#define IDS_SDVX_RGB2_G 40153
|
||||
#define IDS_SDVX_RGB2_B 40154
|
||||
#define IDS_SDVX_RGB3_R 40155
|
||||
#define IDS_SDVX_RGB3_G 40156
|
||||
#define IDS_SDVX_RGB3_B 40157
|
||||
#define IDS_SDVX_RGB4_R 40158
|
||||
#define IDS_SDVX_RGB4_G 40159
|
||||
#define IDS_SDVX_RGB4_B 40160
|
||||
#define IDS_SDVX_RGB5_R 40161
|
||||
#define IDS_SDVX_RGB5_G 40162
|
||||
#define IDS_SDVX_RGB5_B 40163
|
||||
#define IDS_SDVX_RGB6_R 40164
|
||||
#define IDS_SDVX_RGB6_G 40165
|
||||
#define IDS_SDVX_RGB6_B 40166
|
||||
#define IDS_JU5_SCHEMA 40167
|
||||
#define IDS_JB_RGB_FRONT_R 40168
|
||||
#define IDS_JB_RGB_FRONT_G 40169
|
||||
#define IDS_JB_RGB_FRONT_B 40170
|
||||
#define IDS_JB_RGB_TOP_R 40171
|
||||
#define IDS_JB_RGB_TOP_G 40172
|
||||
#define IDS_JB_RGB_TOP_B 40173
|
||||
#define IDS_JB_RGB_LEFT_R 40174
|
||||
#define IDS_JB_RGB_LEFT_G 40175
|
||||
#define IDS_JB_RGB_LEFT_B 40176
|
||||
#define IDS_JB_RGB_RIGHT_R 40177
|
||||
#define IDS_JB_RGB_RIGHT_G 40178
|
||||
#define IDS_JB_RGB_RIGHT_B 40179
|
||||
#define IDS_JB_RGB_TITLE_R 40180
|
||||
#define IDS_JB_RGB_TITLE_G 40181
|
||||
#define IDS_JB_RGB_TITLE_B 40182
|
||||
#define IDS_JB_RGB_WOOFER_R 40183
|
||||
#define IDS_JB_RGB_WOOFER_G 40184
|
||||
#define IDS_JB_RGB_WOOFER_B 40185
|
||||
#define IDS_BST_SCHEMA 40186
|
||||
#define IDS_IIDX_PANEL_SCHEMA 40187
|
||||
#define IDS_IIDX_PANEL_S1_UP 40188
|
||||
#define IDS_IIDX_PANEL_S1_DOWN 40189
|
||||
#define IDS_IIDX_PANEL_S2_UP 40190
|
||||
#define IDS_IIDX_PANEL_S2_DOWN 40191
|
||||
#define IDS_IIDX_PANEL_S3_UP 40192
|
||||
#define IDS_IIDX_PANEL_S3_DOWN 40193
|
||||
#define IDS_IIDX_PANEL_S4_UP 40194
|
||||
#define IDS_IIDX_PANEL_S4_DOWN 40195
|
||||
#define IDS_IIDX_PANEL_S5_UP 40196
|
||||
#define IDS_IIDX_PANEL_S5_DOWN 40197
|
||||
#define IDS_IIDX_PANEL_S1 40198
|
||||
#define IDS_IIDX_PANEL_S2 40199
|
||||
#define IDS_IIDX_PANEL_S3 40200
|
||||
#define IDS_IIDX_PANEL_S4 40201
|
||||
#define IDS_IIDX_PANEL_S5 40202
|
||||
@@ -0,0 +1,391 @@
|
||||
#include "config/resource.h"
|
||||
#include "config/schema.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
|
||||
static const struct action_def dm_actions[] = {
|
||||
{ 0x01, IDS_GENERIC_TEST },
|
||||
{ 0x00, IDS_GENERIC_SERVICE },
|
||||
|
||||
{ 0x08, IDS_DM_START },
|
||||
{ 0x0F, IDS_DM_MENU_LEFT },
|
||||
{ 0x11, IDS_DM_MENU_RIGHT },
|
||||
|
||||
{ 0x0A, IDS_DM_HI_HAT },
|
||||
{ 0x0C, IDS_DM_SNARE },
|
||||
{ 0x0E, IDS_DM_HIGH_TOM },
|
||||
{ 0x10, IDS_DM_LOW_TOM },
|
||||
{ 0x12, IDS_DM_CYMBAL },
|
||||
{ 0x16, IDS_DM_BASS }
|
||||
};
|
||||
|
||||
static const struct light_def dm_lights[] = {
|
||||
{ 0x00, IDS_DM_HI_HAT },
|
||||
{ 0x01, IDS_DM_SNARE },
|
||||
{ 0x02, IDS_DM_HIGH_TOM },
|
||||
{ 0x03, IDS_DM_LOW_TOM },
|
||||
{ 0x04, IDS_DM_CYMBAL },
|
||||
{ 0x09, IDS_DM_BASS },
|
||||
{ 0x0A, IDS_DM_SPEAKER_LIGHT },
|
||||
{ 0x08, IDS_DM_SPOT_LIGHT },
|
||||
{ 0x06, IDS_DM_START_LIGHT },
|
||||
{ 0x07, IDS_DM_MENU_LIGHT }
|
||||
};
|
||||
|
||||
static const struct action_def gf_actions[] = {
|
||||
{ 0x01, IDS_GENERIC_TEST },
|
||||
{ 0x00, IDS_GENERIC_SERVICE },
|
||||
|
||||
{ 0x08, IDS_GF_P1_START },
|
||||
{ 0x0A, IDS_GF_P1_PICK },
|
||||
{ 0x18, IDS_GF_P1_PICK_A },
|
||||
{ 0x19, IDS_GF_P1_PICK_B },
|
||||
{ 0x1C, IDS_GF_P1_EFFECT },
|
||||
{ 0x0C, IDS_GF_P1_WAIL },
|
||||
{ 0x12, IDS_GF_P1_RED },
|
||||
{ 0x14, IDS_GF_P1_GREEN },
|
||||
{ 0x16, IDS_GF_P1_BLUE },
|
||||
|
||||
{ 0x09, IDS_GF_P2_START },
|
||||
{ 0x0B, IDS_GF_P2_PICK },
|
||||
{ 0x1A, IDS_GF_P2_PICK_A },
|
||||
{ 0x1B, IDS_GF_P2_PICK_B },
|
||||
{ 0x1D, IDS_GF_P2_EFFECT },
|
||||
{ 0x0D, IDS_GF_P2_WAIL },
|
||||
{ 0x13, IDS_GF_P2_RED },
|
||||
{ 0x15, IDS_GF_P2_GREEN },
|
||||
{ 0x17, IDS_GF_P2_BLUE }
|
||||
};
|
||||
|
||||
static const struct light_def gf_lights[] = {
|
||||
{ 0x08, IDS_GF_P1_SPOT_LIGHT },
|
||||
{ 0x09, IDS_GF_P2_SPOT_LIGHT },
|
||||
{ 0x0A, IDS_GF_P1_START },
|
||||
{ 0x0B, IDS_GF_P2_START }
|
||||
};
|
||||
|
||||
static const struct action_def iidx_actions[] = {
|
||||
{ 0x1C, IDS_GENERIC_TEST },
|
||||
{ 0x1D, IDS_GENERIC_SERVICE },
|
||||
|
||||
{ 0x08, IDS_IIDX_P1_1 },
|
||||
{ 0x09, IDS_IIDX_P1_2 },
|
||||
{ 0x0A, IDS_IIDX_P1_3 },
|
||||
{ 0x0B, IDS_IIDX_P1_4 },
|
||||
{ 0x0C, IDS_IIDX_P1_5 },
|
||||
{ 0x0D, IDS_IIDX_P1_6 },
|
||||
{ 0x0E, IDS_IIDX_P1_7 },
|
||||
|
||||
{ 0x0F, IDS_IIDX_P2_1 },
|
||||
{ 0x10, IDS_IIDX_P2_2 },
|
||||
{ 0x11, IDS_IIDX_P2_3 },
|
||||
{ 0x12, IDS_IIDX_P2_4 },
|
||||
{ 0x13, IDS_IIDX_P2_5 },
|
||||
{ 0x14, IDS_IIDX_P2_6 },
|
||||
{ 0x15, IDS_IIDX_P2_7 },
|
||||
|
||||
{ 0x18, IDS_IIDX_P1_START },
|
||||
{ 0x19, IDS_IIDX_P2_START },
|
||||
{ 0x1A, IDS_IIDX_VEFX },
|
||||
{ 0x1B, IDS_IIDX_EFFECT },
|
||||
|
||||
{ 0x00, IDS_IIDX_P1_TT_UP },
|
||||
{ 0x01, IDS_IIDX_P1_TT_DOWN },
|
||||
{ 0x02, IDS_IIDX_P1_TT_STAB },
|
||||
|
||||
{ 0x03, IDS_IIDX_P2_TT_UP },
|
||||
{ 0x04, IDS_IIDX_P2_TT_DOWN },
|
||||
{ 0x05, IDS_IIDX_P2_TT_STAB },
|
||||
|
||||
{ 0x20, IDS_IIDX_PANEL_S1_UP },
|
||||
{ 0x21, IDS_IIDX_PANEL_S1_DOWN },
|
||||
|
||||
{ 0x22, IDS_IIDX_PANEL_S2_UP },
|
||||
{ 0x23, IDS_IIDX_PANEL_S2_DOWN },
|
||||
|
||||
{ 0x24, IDS_IIDX_PANEL_S3_UP },
|
||||
{ 0x25, IDS_IIDX_PANEL_S3_DOWN },
|
||||
|
||||
{ 0x26, IDS_IIDX_PANEL_S4_UP },
|
||||
{ 0x27, IDS_IIDX_PANEL_S4_DOWN },
|
||||
|
||||
{ 0x28, IDS_IIDX_PANEL_S5_UP },
|
||||
{ 0x29, IDS_IIDX_PANEL_S5_DOWN }
|
||||
};
|
||||
|
||||
static const struct light_def iidx_lights[] = {
|
||||
{ 0x00, IDS_IIDX_P1_1 },
|
||||
{ 0x01, IDS_IIDX_P1_2 },
|
||||
{ 0x02, IDS_IIDX_P1_3 },
|
||||
{ 0x03, IDS_IIDX_P1_4 },
|
||||
{ 0x04, IDS_IIDX_P1_5 },
|
||||
{ 0x05, IDS_IIDX_P1_6 },
|
||||
{ 0x06, IDS_IIDX_P1_7 },
|
||||
|
||||
{ 0x07, IDS_IIDX_P2_1 },
|
||||
{ 0x08, IDS_IIDX_P2_2 },
|
||||
{ 0x09, IDS_IIDX_P2_3 },
|
||||
{ 0x0A, IDS_IIDX_P2_4 },
|
||||
{ 0x0B, IDS_IIDX_P2_5 },
|
||||
{ 0x0C, IDS_IIDX_P2_6 },
|
||||
{ 0x0D, IDS_IIDX_P2_7 },
|
||||
|
||||
{ 0x18, IDS_IIDX_P1_START },
|
||||
{ 0x19, IDS_IIDX_P2_START },
|
||||
{ 0x1A, IDS_IIDX_VEFX },
|
||||
{ 0x1B, IDS_IIDX_EFFECT },
|
||||
|
||||
{ 0x10, IDS_IIDX_SPOT_1_LIGHT },
|
||||
{ 0x11, IDS_IIDX_SPOT_2_LIGHT },
|
||||
{ 0x12, IDS_IIDX_SPOT_3_LIGHT },
|
||||
{ 0x13, IDS_IIDX_SPOT_4_LIGHT },
|
||||
{ 0x14, IDS_IIDX_SPOT_5_LIGHT },
|
||||
{ 0x15, IDS_IIDX_SPOT_6_LIGHT },
|
||||
{ 0x16, IDS_IIDX_SPOT_7_LIGHT },
|
||||
{ 0x17, IDS_IIDX_SPOT_8_LIGHT },
|
||||
|
||||
{ 0x1F, IDS_IIDX_NEON_LIGHT }
|
||||
};
|
||||
|
||||
static const struct analog_def iidx_analogs[] = {
|
||||
{ 0, IDS_IIDX_P1_TT },
|
||||
{ 1, IDS_IIDX_P2_TT }
|
||||
};
|
||||
|
||||
static const struct action_def ddr_actions[] = {
|
||||
{ 0x04, IDS_GENERIC_TEST },
|
||||
{ 0x06, IDS_GENERIC_SERVICE },
|
||||
|
||||
{ 0x10, IDS_DDR_P1_START },
|
||||
{ 0x00, IDS_DDR_P1_MENU_UP },
|
||||
{ 0x01, IDS_DDR_P1_MENU_DOWN },
|
||||
{ 0x16, IDS_DDR_P1_MENU_LEFT },
|
||||
{ 0x17, IDS_DDR_P1_MENU_RIGHT },
|
||||
{ 0x11, IDS_DDR_P1_UP },
|
||||
{ 0x12, IDS_DDR_P1_DOWN },
|
||||
{ 0x13, IDS_DDR_P1_LEFT },
|
||||
{ 0x14, IDS_DDR_P1_RIGHT },
|
||||
|
||||
{ 0x08, IDS_DDR_P2_START },
|
||||
{ 0x02, IDS_DDR_P2_MENU_UP },
|
||||
{ 0x03, IDS_DDR_P2_MENU_DOWN },
|
||||
{ 0x0E, IDS_DDR_P2_MENU_LEFT },
|
||||
{ 0x0F, IDS_DDR_P2_MENU_RIGHT },
|
||||
{ 0x09, IDS_DDR_P2_UP },
|
||||
{ 0x0A, IDS_DDR_P2_DOWN },
|
||||
{ 0x0B, IDS_DDR_P2_LEFT },
|
||||
{ 0x0C, IDS_DDR_P2_RIGHT }
|
||||
};
|
||||
|
||||
static const struct light_def ddr_lights[] = {
|
||||
/* These are split between non-overlapping P3IO and EXTIO state words */
|
||||
|
||||
{ 0x00, IDS_DDR_P1_MENU_LIGHT },
|
||||
{ 0x07, IDS_DDR_P1_TOP_LIGHT },
|
||||
{ 0x07, IDS_DDR_P1_BOTTOM_LIGHT },
|
||||
|
||||
{ 0x1E, IDS_DDR_P1_UP },
|
||||
{ 0x1D, IDS_DDR_P1_DOWN },
|
||||
{ 0x1C, IDS_DDR_P1_LEFT },
|
||||
{ 0x1B, IDS_DDR_P1_RIGHT },
|
||||
|
||||
{ 0x01, IDS_DDR_P2_MENU_LIGHT },
|
||||
{ 0x05, IDS_DDR_P2_TOP_LIGHT },
|
||||
{ 0x04, IDS_DDR_P2_BOTTOM_LIGHT },
|
||||
|
||||
{ 0x16, IDS_DDR_P2_UP },
|
||||
{ 0x15, IDS_DDR_P2_DOWN },
|
||||
{ 0x14, IDS_DDR_P2_LEFT },
|
||||
{ 0x13, IDS_DDR_P2_RIGHT },
|
||||
|
||||
{ 0x0E, IDS_DDR_BASS_LIGHT }
|
||||
};
|
||||
|
||||
static const struct action_def pnm_actions[] = {
|
||||
{ 0x07, IDS_GENERIC_TEST },
|
||||
{ 0x06, IDS_GENERIC_SERVICE },
|
||||
|
||||
{ 0x08, IDS_PNM_BTN1 },
|
||||
{ 0x09, IDS_PNM_BTN2 },
|
||||
{ 0x0A, IDS_PNM_BTN3 },
|
||||
{ 0x0B, IDS_PNM_BTN4 },
|
||||
{ 0x0C, IDS_PNM_BTN5 },
|
||||
{ 0x0D, IDS_PNM_BTN6 },
|
||||
{ 0x0E, IDS_PNM_BTN7 },
|
||||
{ 0x0F, IDS_PNM_BTN8 },
|
||||
{ 0x10, IDS_PNM_BTN9 }
|
||||
};
|
||||
|
||||
static const struct light_def pnm_lights[] = {
|
||||
{ 0x17, IDS_PNM_BTN1 },
|
||||
{ 0x18, IDS_PNM_BTN2 },
|
||||
{ 0x19, IDS_PNM_BTN3 },
|
||||
{ 0x1A, IDS_PNM_BTN4 },
|
||||
{ 0x1B, IDS_PNM_BTN5 },
|
||||
{ 0x1C, IDS_PNM_BTN6 },
|
||||
{ 0x1D, IDS_PNM_BTN7 },
|
||||
{ 0x1E, IDS_PNM_BTN8 },
|
||||
{ 0x1F, IDS_PNM_BTN9 },
|
||||
};
|
||||
|
||||
static const struct action_def jb_actions[] = {
|
||||
{ 0x10, IDS_GENERIC_TEST },
|
||||
{ 0x11, IDS_GENERIC_SERVICE },
|
||||
|
||||
{ 0x00, IDS_JB_PANEL1 },
|
||||
{ 0x01, IDS_JB_PANEL2 },
|
||||
{ 0x02, IDS_JB_PANEL3 },
|
||||
{ 0x03, IDS_JB_PANEL4 },
|
||||
{ 0x04, IDS_JB_PANEL5 },
|
||||
{ 0x05, IDS_JB_PANEL6 },
|
||||
{ 0x06, IDS_JB_PANEL7 },
|
||||
{ 0x07, IDS_JB_PANEL8 },
|
||||
{ 0x08, IDS_JB_PANEL9 },
|
||||
{ 0x09, IDS_JB_PANEL10 },
|
||||
{ 0x0A, IDS_JB_PANEL11 },
|
||||
{ 0x0B, IDS_JB_PANEL12 },
|
||||
{ 0x0C, IDS_JB_PANEL13 },
|
||||
{ 0x0D, IDS_JB_PANEL14 },
|
||||
{ 0x0E, IDS_JB_PANEL15 },
|
||||
{ 0x0F, IDS_JB_PANEL16 }
|
||||
};
|
||||
|
||||
static const struct light_def jb_lights[] = {
|
||||
{ 0x00, IDS_JB_RGB_FRONT_R },
|
||||
{ 0x01, IDS_JB_RGB_FRONT_G },
|
||||
{ 0x02, IDS_JB_RGB_FRONT_B },
|
||||
{ 0x03, IDS_JB_RGB_TOP_R },
|
||||
{ 0x04, IDS_JB_RGB_TOP_G },
|
||||
{ 0x05, IDS_JB_RGB_TOP_B },
|
||||
{ 0x06, IDS_JB_RGB_LEFT_R },
|
||||
{ 0x07, IDS_JB_RGB_LEFT_G },
|
||||
{ 0x08, IDS_JB_RGB_LEFT_B },
|
||||
{ 0x09, IDS_JB_RGB_RIGHT_R },
|
||||
{ 0x0A, IDS_JB_RGB_RIGHT_G },
|
||||
{ 0x0B, IDS_JB_RGB_RIGHT_B },
|
||||
{ 0x0C, IDS_JB_RGB_TITLE_R },
|
||||
{ 0x0D, IDS_JB_RGB_TITLE_G },
|
||||
{ 0x0E, IDS_JB_RGB_TITLE_B },
|
||||
{ 0x0F, IDS_JB_RGB_WOOFER_R },
|
||||
{ 0x10, IDS_JB_RGB_WOOFER_G },
|
||||
{ 0x11, IDS_JB_RGB_WOOFER_B },
|
||||
};
|
||||
|
||||
static const struct action_def sdvx_actions[] = {
|
||||
{ 0x05, IDS_GENERIC_TEST },
|
||||
{ 0x04, IDS_GENERIC_SERVICE },
|
||||
{ 0x0B, IDS_SDVX_START },
|
||||
{ 0x0A, IDS_SDVX_BTN_A },
|
||||
{ 0x09, IDS_SDVX_BTN_B },
|
||||
{ 0x08, IDS_SDVX_BTN_C },
|
||||
{ 0x15, IDS_SDVX_BTN_D },
|
||||
{ 0x14, IDS_SDVX_FX_L },
|
||||
{ 0x13, IDS_SDVX_FX_R }
|
||||
};
|
||||
|
||||
static const struct light_def sdvx_lights[] = {
|
||||
{ 0x0D, IDS_SDVX_BTN_A },
|
||||
{ 0x0E, IDS_SDVX_BTN_B },
|
||||
{ 0x0F, IDS_SDVX_BTN_C },
|
||||
{ 0x00, IDS_SDVX_BTN_D },
|
||||
{ 0x01, IDS_SDVX_FX_L },
|
||||
{ 0x02, IDS_SDVX_FX_R },
|
||||
{ 0x0C, IDS_SDVX_START },
|
||||
{ 0x10, IDS_SDVX_RGB1_R },
|
||||
{ 0x11, IDS_SDVX_RGB1_G },
|
||||
{ 0x12, IDS_SDVX_RGB1_B },
|
||||
{ 0x13, IDS_SDVX_RGB2_R },
|
||||
{ 0x14, IDS_SDVX_RGB2_G },
|
||||
{ 0x15, IDS_SDVX_RGB2_B },
|
||||
{ 0x16, IDS_SDVX_RGB3_R },
|
||||
{ 0x17, IDS_SDVX_RGB3_G },
|
||||
{ 0x18, IDS_SDVX_RGB3_B },
|
||||
{ 0x19, IDS_SDVX_RGB4_R },
|
||||
{ 0x1A, IDS_SDVX_RGB4_G },
|
||||
{ 0x1B, IDS_SDVX_RGB4_B },
|
||||
{ 0x1C, IDS_SDVX_RGB5_R },
|
||||
{ 0x1D, IDS_SDVX_RGB5_G },
|
||||
{ 0x1E, IDS_SDVX_RGB5_B },
|
||||
{ 0x1F, IDS_SDVX_RGB6_R },
|
||||
{ 0x20, IDS_SDVX_RGB6_G },
|
||||
{ 0x21, IDS_SDVX_RGB6_B }
|
||||
};
|
||||
|
||||
static const struct analog_def sdvx_analogs[] = {
|
||||
{ 0, IDS_SDVX_VOL_L },
|
||||
{ 1, IDS_SDVX_VOL_R }
|
||||
};
|
||||
|
||||
static const struct action_def rb_actions[] = {
|
||||
{ 0x00, IDS_GENERIC_TEST },
|
||||
{ 0x01, IDS_GENERIC_SERVICE }
|
||||
};
|
||||
|
||||
static const struct action_def bst_actions[] = {
|
||||
{ 0x05, IDS_GENERIC_TEST },
|
||||
{ 0x04, IDS_GENERIC_SERVICE },
|
||||
};
|
||||
|
||||
static const struct eam_unit_def schema_eam_unit_defs[] = {
|
||||
{ IDS_READER_P1, 0 },
|
||||
{ IDS_READER_P2, 1 }
|
||||
};
|
||||
|
||||
const struct schema schemas[] = {
|
||||
{ "iidx", IDS_IIDX_SCHEMA,
|
||||
iidx_actions, lengthof(iidx_actions),
|
||||
iidx_lights, lengthof(iidx_lights),
|
||||
iidx_analogs, lengthof(iidx_analogs),
|
||||
schema_eam_unit_defs, 2 },
|
||||
|
||||
{ "pnm", IDS_PNM_SCHEMA,
|
||||
pnm_actions, lengthof(pnm_actions),
|
||||
pnm_lights, lengthof(pnm_lights),
|
||||
NULL, 0,
|
||||
schema_eam_unit_defs, 1 },
|
||||
|
||||
{ "gf", IDS_GF_SCHEMA,
|
||||
gf_actions, lengthof(gf_actions),
|
||||
gf_lights, lengthof(gf_lights),
|
||||
NULL, 0,
|
||||
schema_eam_unit_defs, 2 },
|
||||
|
||||
{ "dm", IDS_DM_SCHEMA,
|
||||
dm_actions, lengthof(dm_actions),
|
||||
dm_lights, lengthof(dm_lights),
|
||||
NULL, 0,
|
||||
schema_eam_unit_defs, 1 },
|
||||
|
||||
{ "ddr", IDS_DDR_SCHEMA,
|
||||
ddr_actions, lengthof(ddr_actions),
|
||||
ddr_lights, lengthof(ddr_lights),
|
||||
NULL, 0,
|
||||
schema_eam_unit_defs, 2 },
|
||||
|
||||
{ "jb", IDS_JB_SCHEMA,
|
||||
jb_actions, lengthof(jb_actions),
|
||||
jb_lights, lengthof(jb_lights),
|
||||
NULL, 0,
|
||||
schema_eam_unit_defs, 1 },
|
||||
|
||||
{ "sdvx", IDS_SDVX_SCHEMA,
|
||||
sdvx_actions, lengthof(sdvx_actions),
|
||||
sdvx_lights, lengthof(sdvx_lights),
|
||||
sdvx_analogs, lengthof(sdvx_analogs),
|
||||
schema_eam_unit_defs, 1 },
|
||||
|
||||
{ "rb", IDS_RB_SCHEMA,
|
||||
rb_actions, lengthof(rb_actions),
|
||||
NULL, 0,
|
||||
NULL, 0,
|
||||
schema_eam_unit_defs, 1 },
|
||||
|
||||
{ "bst", IDS_BST_SCHEMA,
|
||||
bst_actions, lengthof(bst_actions),
|
||||
NULL, 0,
|
||||
NULL, 0,
|
||||
schema_eam_unit_defs, 1 },
|
||||
};
|
||||
|
||||
const size_t nschemas = lengthof(schemas);
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef CONFIG_SCHEMA_H
|
||||
#define CONFIG_SCHEMA_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct action_def {
|
||||
uint8_t bit;
|
||||
unsigned int name_rsrc;
|
||||
};
|
||||
|
||||
struct light_def {
|
||||
uint8_t bit;
|
||||
unsigned int name_rsrc;
|
||||
};
|
||||
|
||||
struct analog_def {
|
||||
uint8_t tag;
|
||||
unsigned int label_rsrc;
|
||||
};
|
||||
|
||||
struct eam_unit_def {
|
||||
unsigned int label_rsrc;
|
||||
uint8_t unit_no;
|
||||
};
|
||||
|
||||
struct schema {
|
||||
const char *name;
|
||||
unsigned int label;
|
||||
const struct action_def *actions;
|
||||
size_t nactions;
|
||||
const struct light_def *lights;
|
||||
size_t nlights;
|
||||
const struct analog_def *analogs;
|
||||
size_t nanalogs;
|
||||
const struct eam_unit_def *units;
|
||||
size_t nunits;
|
||||
};
|
||||
|
||||
extern const struct schema schemas[];
|
||||
extern const size_t nschemas;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,247 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config/snap.h"
|
||||
|
||||
#include "geninput/hid-mgr.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
enum snap_control_heuristic {
|
||||
CONTROL_CENTERING_AXIS,
|
||||
CONTROL_MULTISWITCH
|
||||
};
|
||||
|
||||
struct snap_known_control {
|
||||
uint32_t usage;
|
||||
enum snap_control_heuristic heuristic;
|
||||
};
|
||||
|
||||
static struct snap_known_control snap_known_controls[] = {
|
||||
/* X axis */
|
||||
{ 0x00010030, CONTROL_CENTERING_AXIS },
|
||||
|
||||
/* Y axis */
|
||||
{ 0x00010031, CONTROL_CENTERING_AXIS },
|
||||
|
||||
/* Z axis */
|
||||
{ 0x00010032, CONTROL_CENTERING_AXIS },
|
||||
|
||||
/* X rotation */
|
||||
{ 0x00010033, CONTROL_CENTERING_AXIS },
|
||||
|
||||
/* Y rotation */
|
||||
{ 0x00010034, CONTROL_CENTERING_AXIS },
|
||||
|
||||
/* Z rotation */
|
||||
{ 0x00010035, CONTROL_CENTERING_AXIS },
|
||||
|
||||
/* I don't have an adapter that presents a slider/dial/wheel so I don't
|
||||
know how to deal with those things right now */
|
||||
|
||||
/* Hat switch */
|
||||
{ 0x00010039, CONTROL_MULTISWITCH }
|
||||
};
|
||||
|
||||
static bool snap_check_for_edge(const struct hid_control *ctl, int32_t val,
|
||||
int32_t other_val, struct mapped_action *ma);
|
||||
|
||||
void snap_init(struct snap *snap)
|
||||
{
|
||||
size_t i;
|
||||
size_t j;
|
||||
size_t ncontrols;
|
||||
size_t ndevs;
|
||||
struct hid_stub *pos;
|
||||
|
||||
hid_mgr_lock();
|
||||
|
||||
ndevs = 0;
|
||||
|
||||
for (pos = hid_mgr_get_first_stub()
|
||||
; pos != NULL
|
||||
; pos = hid_mgr_get_next_stub(pos)) {
|
||||
|
||||
ndevs++;
|
||||
}
|
||||
|
||||
snap->ndevs = ndevs;
|
||||
snap->devs = xcalloc(ndevs * sizeof(*snap->devs));
|
||||
|
||||
i = 0;
|
||||
|
||||
for (pos = hid_mgr_get_first_stub(), i = 0
|
||||
; pos != NULL && i < ndevs
|
||||
; pos = hid_mgr_get_next_stub(pos), i++) {
|
||||
|
||||
if (!hid_stub_is_attached(pos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!hid_stub_get_controls(pos, NULL, &ncontrols)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
snap->devs[i].hid = pos;
|
||||
snap->devs[i].ncontrols = ncontrols;
|
||||
snap->devs[i].controls = xcalloc(ncontrols
|
||||
* sizeof(struct hid_control));
|
||||
snap->devs[i].states = xcalloc(ncontrols * sizeof(int32_t));
|
||||
|
||||
hid_stub_get_controls(pos, snap->devs[i].controls, &ncontrols);
|
||||
|
||||
for (j = 0 ; j < ncontrols ; j++) {
|
||||
hid_stub_get_value(pos, j, &snap->devs[i].states[j]);
|
||||
}
|
||||
}
|
||||
|
||||
hid_mgr_unlock();
|
||||
}
|
||||
|
||||
bool snap_find_edge(const struct snap *snap, const struct snap *other_snap,
|
||||
struct mapped_action *ma)
|
||||
{
|
||||
const struct hid_control *ctl;
|
||||
int32_t val;
|
||||
int32_t other_val;
|
||||
size_t i;
|
||||
size_t j;
|
||||
|
||||
/* Most of these checks are to ensure some other device didn't sneak in
|
||||
between these two snapshots. If it has, then detecting a state
|
||||
transition becomes a bit awkward so we just say that nothing happened
|
||||
and wait for the next poll period.
|
||||
|
||||
Yes, creating a total input snapshot 60ish times per second while
|
||||
attempting to bind a key isn't exactly the most efficient thing in the
|
||||
world. We don't care, it's just a config program. */
|
||||
|
||||
if (snap->ndevs != other_snap->ndevs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < snap->ndevs ; i++) {
|
||||
if (snap->devs[i].hid != other_snap->devs[i].hid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (snap->devs[i].ncontrols != other_snap->devs[i].ncontrols) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(snap->devs[i].controls, other_snap->devs[i].controls,
|
||||
snap->devs[i].ncontrols * sizeof(struct hid_control)) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (j = 0 ; j < snap->devs[i].ncontrols ; j++) {
|
||||
ctl = &snap->devs[i].controls[j];
|
||||
val = snap->devs[i].states[j];
|
||||
other_val = other_snap->devs[i].states[j];
|
||||
|
||||
if (snap_check_for_edge(ctl, val, other_val, ma)) {
|
||||
ma->hid = snap->devs[i].hid;
|
||||
ma->control_no = j;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool snap_check_for_edge(const struct hid_control *ctl, int32_t val,
|
||||
int32_t other_val, struct mapped_action *ma)
|
||||
{
|
||||
size_t i;
|
||||
int32_t range;
|
||||
int32_t dz_min;
|
||||
int32_t dz_max;
|
||||
|
||||
if (val != other_val) {
|
||||
log_misc("Value change on %08x: %d -> %d", ctl->usage, other_val, val);
|
||||
}
|
||||
|
||||
if (ctl->value_max - ctl->value_min == 1) {
|
||||
/* Button-like thing, deal with it the obvious way */
|
||||
if (val == ctl->value_max && val != other_val) {
|
||||
ma->value_min = ctl->value_max;
|
||||
ma->value_max = ctl->value_max;
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
/* Here we kinda have to take things on a case by case basis */
|
||||
|
||||
for (i = 0 ; i < lengthof(snap_known_controls) ; i++) {
|
||||
if (snap_known_controls[i].usage != ctl->usage) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (snap_known_controls[i].heuristic) {
|
||||
case CONTROL_CENTERING_AXIS:
|
||||
/* Dead zones keep giving me grief, so I'm going to be
|
||||
super-aggressive and assume a 50%-ish dead zone
|
||||
|
||||
(yes I know the rounding here is a little iffy) */
|
||||
|
||||
range = ctl->value_max - ctl->value_min;
|
||||
dz_min = ctl->value_min + ((range * 1) / 4);
|
||||
dz_max = ctl->value_min + ((range * 3) / 4);
|
||||
|
||||
if (val <= dz_min && other_val > dz_min) {
|
||||
ma->value_min = ctl->value_min;
|
||||
ma->value_max = dz_min;
|
||||
|
||||
return true;
|
||||
} else if (val >= dz_max && other_val < dz_max) {
|
||||
ma->value_min = dz_max;
|
||||
ma->value_max = ctl->value_max;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case CONTROL_MULTISWITCH:
|
||||
/* Assume positions are discrete. Precisely match any
|
||||
transitioned-to value that isn't a null state. */
|
||||
|
||||
if (val >= ctl->value_min && val <= ctl->value_max
|
||||
&& val != other_val) {
|
||||
ma->value_min = val;
|
||||
ma->value_max = val;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Still haven't found it? Well, we don't know how to determine if this
|
||||
control is being actively pressed or not, so the user will just have
|
||||
to manually key in an advanced binding. */
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void snap_fini(struct snap *snap)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0 ; i < snap->ndevs ; i++) {
|
||||
free(snap->devs[i].states);
|
||||
free(snap->devs[i].controls);
|
||||
}
|
||||
|
||||
free(snap->devs);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef CONFIG_SNAP_H
|
||||
#define CONFIG_SNAP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "geninput/mapper.h"
|
||||
|
||||
struct snap_dev {
|
||||
struct hid_stub *hid;
|
||||
int32_t *states;
|
||||
struct hid_control *controls;
|
||||
uint32_t ncontrols;
|
||||
};
|
||||
|
||||
struct snap {
|
||||
struct snap_dev *devs;
|
||||
uint32_t ndevs;
|
||||
};
|
||||
|
||||
void snap_init(struct snap *snap);
|
||||
bool snap_find_edge(const struct snap *snap, const struct snap *other_snap,
|
||||
struct mapped_action *ma);
|
||||
void snap_fini(struct snap *snap);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,118 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#define METRIC_COLOR RGB(0, 0, 0)
|
||||
#define METRIC_RADIUS_CIRCLE 0.8f
|
||||
#define METRIC_RADIUS_NOTCH 0.2f
|
||||
#define METRIC_THICKNESS 3
|
||||
|
||||
static const wchar_t spinner_cls[] = L"spinner";
|
||||
|
||||
static LRESULT CALLBACK spinner_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam);
|
||||
static LRESULT spinner_handle_paint(HWND hwnd);
|
||||
static LRESULT spinner_handle_update(HWND hwnd, uint8_t pos);
|
||||
|
||||
void spinner_init(HINSTANCE inst)
|
||||
{
|
||||
WNDCLASSEX wcx;
|
||||
|
||||
memset(&wcx, 0, sizeof(wcx));
|
||||
wcx.cbSize = sizeof(wcx);
|
||||
wcx.lpfnWndProc = spinner_wnd_proc;
|
||||
wcx.hInstance = inst;
|
||||
wcx.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
|
||||
wcx.lpszClassName = spinner_cls;
|
||||
|
||||
RegisterClassEx(&wcx);
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK spinner_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
LPARAM lparam)
|
||||
{
|
||||
switch (msg) {
|
||||
case WM_CREATE:
|
||||
SetWindowLong(hwnd, GWLP_USERDATA, 0);
|
||||
|
||||
return TRUE;
|
||||
|
||||
case WM_PAINT:
|
||||
return spinner_handle_paint(hwnd);
|
||||
|
||||
case WM_USER:
|
||||
return spinner_handle_update(hwnd, (uint8_t) lparam);
|
||||
|
||||
default:
|
||||
return DefWindowProc(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
}
|
||||
|
||||
static LRESULT spinner_handle_paint(HWND hwnd)
|
||||
{
|
||||
HDC dc;
|
||||
HPEN pen;
|
||||
PAINTSTRUCT ps;
|
||||
RECT r;
|
||||
float cx;
|
||||
float cy;
|
||||
float dx;
|
||||
float dy;
|
||||
float theta;
|
||||
uint8_t itheta;
|
||||
|
||||
/* GWLP_USERDATA angle has 256 steps in a complete revolution.
|
||||
To convert to radians, multiply by (2 * PI / 256). */
|
||||
|
||||
itheta = (uint8_t) GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
theta = itheta * 0.0245437f;
|
||||
|
||||
GetWindowRect(hwnd, &r);
|
||||
|
||||
cx = (r.right - r.left) / 2.0f;
|
||||
cy = (r.bottom - r.top) / 2.0f;
|
||||
|
||||
dc = BeginPaint(hwnd, &ps);
|
||||
|
||||
pen = CreatePen(PS_SOLID, METRIC_THICKNESS, METRIC_COLOR);
|
||||
SelectObject(dc, pen);
|
||||
SelectObject(dc, GetStockObject(WHITE_BRUSH));
|
||||
|
||||
Ellipse(dc,
|
||||
(int) (cx * (1.0f - METRIC_RADIUS_CIRCLE)),
|
||||
(int) (cy * (1.0f - METRIC_RADIUS_CIRCLE)),
|
||||
(int) (cx * (1.0f + METRIC_RADIUS_CIRCLE)),
|
||||
(int) (cy * (1.0f + METRIC_RADIUS_CIRCLE)));
|
||||
|
||||
dx = sinf(theta);
|
||||
dy = -cosf(theta);
|
||||
|
||||
MoveToEx(dc,
|
||||
(int) (cx * (1.0f + dx * METRIC_RADIUS_NOTCH)),
|
||||
(int) (cy * (1.0f + dy * METRIC_RADIUS_NOTCH)), NULL);
|
||||
|
||||
LineTo(dc,
|
||||
(int) (cx * (1.0f + dx * METRIC_RADIUS_CIRCLE)),
|
||||
(int) (cy * (1.0f + dy * METRIC_RADIUS_CIRCLE)));
|
||||
|
||||
DeleteObject(pen);
|
||||
EndPaint(hwnd, &ps);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static LRESULT spinner_handle_update(HWND hwnd, uint8_t pos)
|
||||
{
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM) pos);
|
||||
InvalidateRect(hwnd, NULL, TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spinner_fini(HINSTANCE inst)
|
||||
{
|
||||
UnregisterClass(spinner_cls, inst);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef CONFIG_SPINNER_H
|
||||
#define CONFIG_SPINNER_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
void spinner_init(HINSTANCE inst);
|
||||
void spinner_fini(HINSTANCE inst);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,177 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config/resource.h"
|
||||
|
||||
#include "util/array.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
#include "util/winres.h"
|
||||
|
||||
struct usage {
|
||||
uint16_t id;
|
||||
char name[128];
|
||||
};
|
||||
|
||||
struct usage_page {
|
||||
uint16_t id;
|
||||
struct array usages;
|
||||
char wildcard[128];
|
||||
};
|
||||
|
||||
static struct array usages;
|
||||
|
||||
static bool usages_begin_page(const char *line, struct usage_page *page);
|
||||
static bool usages_read_usage(const char *line, struct usage_page *page);
|
||||
|
||||
void usages_init(HINSTANCE inst)
|
||||
{
|
||||
struct resource res;
|
||||
struct usage_page *cur_page;
|
||||
struct usage_page tmp_page;
|
||||
char line[512];
|
||||
size_t line_no;
|
||||
size_t nchars;
|
||||
|
||||
resource_open(&res, inst, "USAGES", IDR_USAGES);
|
||||
array_init(&usages);
|
||||
|
||||
cur_page = NULL;
|
||||
line_no = 0;
|
||||
|
||||
while (resource_fgets(&res, line, sizeof(line))) {
|
||||
line_no++;
|
||||
|
||||
nchars = strlen(line);
|
||||
|
||||
if (nchars == 0 || line[0] == '#'
|
||||
|| strspn(line, " \t\r\n") == nchars) {
|
||||
continue;
|
||||
}
|
||||
|
||||
str_trim(line);
|
||||
|
||||
if (!isspace(line[0])) {
|
||||
if (!usages_begin_page(line, &tmp_page)) {
|
||||
log_fatal("IDR_USAGES:%u: Invalid usage page",
|
||||
(unsigned int) line_no);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
cur_page = array_append(struct usage_page, &usages);
|
||||
memcpy(cur_page, &tmp_page, sizeof(tmp_page));
|
||||
} else {
|
||||
if (cur_page == NULL) {
|
||||
log_fatal("IDR_USAGES:%u: Usage before first page",
|
||||
(unsigned int) line_no);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!usages_read_usage(line, cur_page)) {
|
||||
log_fatal("IDR_USAGES:%u: Invalid usage",
|
||||
(unsigned int) line_no);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool usages_begin_page(const char *line, struct usage_page *page)
|
||||
{
|
||||
int id;
|
||||
|
||||
if (sscanf(line, "%i", &id) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
page->id = (uint16_t) id;
|
||||
array_init(&page->usages);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool usages_read_usage(const char *line, struct usage_page *page)
|
||||
{
|
||||
struct usage *usage;
|
||||
int id;
|
||||
int offset;
|
||||
char c;
|
||||
|
||||
if (sscanf(line, " %c %n", &c, &offset) == 1 && c == '*') {
|
||||
str_cpy(page->wildcard, sizeof(page->wildcard), &line[offset]);
|
||||
page->wildcard[sizeof(page->wildcard) - 1] = '\0';
|
||||
|
||||
return true;
|
||||
} else if (sscanf(line, " %i %n", &id, &offset) == 1) {
|
||||
usage = array_append(struct usage, &page->usages);
|
||||
|
||||
usage->id = (uint16_t) id;
|
||||
str_cpy(usage->name, sizeof(usage->name), &line[offset]);
|
||||
usage->name[sizeof(usage->name) - 1] = '\0';
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void usages_get(char *chars, size_t nchars, uint32_t usage_id)
|
||||
{
|
||||
const struct usage_page *page;
|
||||
const struct usage *usage;
|
||||
uint16_t hi;
|
||||
uint16_t lo;
|
||||
size_t i;
|
||||
size_t j;
|
||||
|
||||
hi = ((usage_id >> 16) & 0xFFFF);
|
||||
lo = ((usage_id >> 0) & 0xFFFF);
|
||||
|
||||
for (i = 0 ; i < usages.nitems ; i++) {
|
||||
page = array_item(struct usage_page, &usages, i);
|
||||
|
||||
if (page->id != hi) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0 ; j < page->usages.nitems ; j++) {
|
||||
usage = array_item(struct usage, &page->usages, j);
|
||||
|
||||
if (usage->id == lo) {
|
||||
str_cpy(chars, nchars, usage->name);
|
||||
chars[nchars - 1] = '\0';
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (page->wildcard[0]) {
|
||||
str_format(chars, nchars, page->wildcard, lo);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
str_format(chars, nchars, "%#08x", usage_id);
|
||||
}
|
||||
|
||||
void usages_fini(void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0 ; i < usages.nitems ; i++) {
|
||||
array_fini(&array_item(struct usage_page, &usages, i)->usages);
|
||||
}
|
||||
|
||||
array_fini(&usages);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef CONFIG_USAGES_H
|
||||
#define CONFIG_USAGES_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void usages_init(HINSTANCE inst);
|
||||
void usages_get(char *chars, uint32_t nchars, uint32_t usage);
|
||||
void usages_fini(void);
|
||||
|
||||
#endif
|
||||
Executable
+1244
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
avsdlls += ddrhook
|
||||
|
||||
deplibs_ddrhook := \
|
||||
avs \
|
||||
|
||||
libs_ddrhook := \
|
||||
acioemu \
|
||||
p3ioemu \
|
||||
p3io \
|
||||
hook \
|
||||
hooklib \
|
||||
security \
|
||||
util \
|
||||
eamio \
|
||||
ddrio \
|
||||
|
||||
src_ddrhook := \
|
||||
extio.c \
|
||||
dllmain.c \
|
||||
_com4.c \
|
||||
dinput.c \
|
||||
gfx.c \
|
||||
guid.c \
|
||||
master.c \
|
||||
misc.c \
|
||||
monitor.c \
|
||||
p3io.c \
|
||||
usbmem.c \
|
||||
spike.c
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <ntdef.h>
|
||||
#include <devioctl.h>
|
||||
#include <ntddser.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "acioemu/addr.h"
|
||||
#include "acioemu/hdxs.h"
|
||||
#include "acioemu/icca.h"
|
||||
|
||||
#include "ddrhook/_com4.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "p3ioemu/uart.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/iobuf.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static struct ac_io_emu com4_ac_io_emu;
|
||||
static struct ac_io_emu_hdxs com4_hdxs;
|
||||
static struct ac_io_emu_icca com4_icca[2];
|
||||
|
||||
void com4_init(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* This isn't a real COM port, we configure the core P3IO emulator code to
|
||||
generate IRPs addressed to COM4 and then we intercept them. */
|
||||
|
||||
p3io_uart_set_path(0, L"COM4");
|
||||
ac_io_emu_init(&com4_ac_io_emu, L"COM4");
|
||||
ac_io_emu_hdxs_init(&com4_hdxs, &com4_ac_io_emu);
|
||||
|
||||
for (i = 0 ; i < lengthof(com4_icca) ; i++) {
|
||||
ac_io_emu_icca_init(&com4_icca[i], &com4_ac_io_emu, i);
|
||||
}
|
||||
}
|
||||
|
||||
void com4_fini(void)
|
||||
{
|
||||
ac_io_emu_fini(&com4_ac_io_emu);
|
||||
}
|
||||
|
||||
HRESULT com4_dispatch_irp(struct irp *irp)
|
||||
{
|
||||
const struct ac_io_message *msg;
|
||||
HRESULT hr;
|
||||
|
||||
log_assert(irp != NULL);
|
||||
|
||||
if (!ac_io_emu_match_irp(&com4_ac_io_emu, irp)) {
|
||||
return irp_invoke_next(irp);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
hr = ac_io_emu_dispatch_irp(&com4_ac_io_emu, irp);
|
||||
|
||||
if (hr != S_OK) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
msg = ac_io_emu_request_peek(&com4_ac_io_emu);
|
||||
|
||||
switch (msg->addr) {
|
||||
case 0:
|
||||
ac_io_emu_cmd_assign_addrs(&com4_ac_io_emu, msg, 3);
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
ac_io_emu_icca_dispatch_request(&com4_icca[0], msg);
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
ac_io_emu_icca_dispatch_request(&com4_icca[1], msg);
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
ac_io_emu_hdxs_dispatch_request(&com4_hdxs, msg);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_BROADCAST:
|
||||
log_warning("Broadcast(?) message on p3io ACIO bus?");
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning("p3io ACIO message on unhandled bus address: %d",
|
||||
msg->addr);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ac_io_emu_request_pop(&com4_ac_io_emu);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef IIDXHOOK_COM4_H
|
||||
#define IIDXHOOK_COM4_H
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
void com4_init(void);
|
||||
void com4_fini(void);
|
||||
HRESULT com4_dispatch_irp(struct irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,4 @@
|
||||
LIBRARY ddrhook
|
||||
|
||||
EXPORTS
|
||||
DllMain@12 @1 NONAME
|
||||
@@ -0,0 +1,85 @@
|
||||
#include <windows.h>
|
||||
|
||||
#define DIRECTINPUT_VERSION 0x0800
|
||||
#include <dinput.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "hook/com-proxy.h"
|
||||
#include "hook/pe.h"
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static HRESULT STDCALL my_DirectInput8Create( HINSTANCE hinst, DWORD dwVersion,
|
||||
REFIID riidltf, LPVOID * ppvOut, LPVOID punkOuter);
|
||||
|
||||
static HRESULT (STDCALL *real_DirectInput8Create)( HINSTANCE hinst, DWORD dwVersion,
|
||||
REFIID riidltf, LPVOID * ppvOut, LPVOID punkOuter);
|
||||
|
||||
static const struct hook_symbol dinput_syms[] = {
|
||||
{
|
||||
.name = "DirectInput8Create",
|
||||
.patch = my_DirectInput8Create,
|
||||
.link = (void **) &real_DirectInput8Create,
|
||||
},
|
||||
};
|
||||
|
||||
static HRESULT STDCALL my_CreateDevice(
|
||||
IDirectInput8W *self,
|
||||
REFGUID rguid,
|
||||
LPDIRECTINPUTDEVICE8W * lplpDirectInputDevice,
|
||||
LPUNKNOWN pUnkOuter
|
||||
) {
|
||||
log_misc("IDirectInput8::CreateDevice hook hit");
|
||||
return DIERR_NOINTERFACE;
|
||||
}
|
||||
|
||||
static HRESULT STDCALL my_EnumDevices(
|
||||
IDirectInput8W *self,
|
||||
DWORD dwDevType,
|
||||
LPDIENUMDEVICESCALLBACKW lpCallback,
|
||||
LPVOID pvRef,
|
||||
DWORD dwFlags
|
||||
) {
|
||||
log_misc("IDirectInput8::EnumDevices hook hit");
|
||||
|
||||
return DI_OK;
|
||||
}
|
||||
|
||||
static HRESULT STDCALL my_DirectInput8Create( HINSTANCE hinst, DWORD dwVersion,
|
||||
REFIID riidltf, LPVOID * ppvOut, LPVOID punkOuter)
|
||||
{
|
||||
IDirectInput8W *api;
|
||||
IDirectInput8WVtbl *api_vtbl;
|
||||
struct com_proxy *api_proxy;
|
||||
HRESULT res;
|
||||
|
||||
log_info("DirectInput8Create hook hit");
|
||||
|
||||
res = real_DirectInput8Create(hinst, dwVersion, riidltf, (LPVOID *)&api, punkOuter);
|
||||
if(res != DI_OK) {
|
||||
return res;
|
||||
}
|
||||
api_proxy = com_proxy_wrap(api, sizeof(*api->lpVtbl));
|
||||
api_vtbl = api_proxy->vptr;
|
||||
|
||||
api_vtbl->EnumDevices = my_EnumDevices;
|
||||
api_vtbl->CreateDevice = my_CreateDevice;
|
||||
|
||||
*(IDirectInput8W**)ppvOut = (IDirectInput8W *)api_proxy;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void dinput_init(HMODULE target)
|
||||
{
|
||||
hook_table_apply(
|
||||
target,
|
||||
"dinput8.dll",
|
||||
dinput_syms,
|
||||
lengthof(dinput_syms));
|
||||
|
||||
log_info("Inserted dinput hooks into %p", target);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef HOOK_DINPUT_H
|
||||
#define HOOK_DINPUT_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
void dinput_init(HMODULE target);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,154 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "bemanitools/ddrio.h"
|
||||
#include "bemanitools/eamio.h"
|
||||
|
||||
#include "ddrhook/_com4.h"
|
||||
#include "ddrhook/extio.h"
|
||||
#include "ddrhook/gfx.h"
|
||||
#include "ddrhook/master.h"
|
||||
#include "ddrhook/p3io.h"
|
||||
#include "ddrhook/spike.h"
|
||||
#include "ddrhook/usbmem.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/app.h"
|
||||
#include "hooklib/rs232.h"
|
||||
|
||||
#include "imports/avs.h"
|
||||
|
||||
#include "p3ioemu/emu.h"
|
||||
|
||||
#include "util/cmdline.h"
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static bool my_dll_entry_init(char *sidcode, struct property_node *param);
|
||||
static bool my_dll_entry_main(void);
|
||||
|
||||
bool standard_def;
|
||||
bool _15khz;
|
||||
|
||||
static const irp_handler_t ddrhook_handlers[] = {
|
||||
p3io_emu_dispatch_irp,
|
||||
extio_dispatch_irp,
|
||||
spike_dispatch_irp,
|
||||
usbmem_dispatch_irp,
|
||||
com4_dispatch_irp,
|
||||
};
|
||||
|
||||
static bool my_dll_entry_init(char *sidcode, struct property_node *param)
|
||||
{
|
||||
int argc;
|
||||
char **argv;
|
||||
bool ok;
|
||||
int i;
|
||||
|
||||
log_info("--- Begin ddrhook dll_entry_init ---");
|
||||
|
||||
args_recover(&argc, &argv);
|
||||
|
||||
for (i = 1 ; i < argc ; i++) {
|
||||
if (argv[i][0] != '-') {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (argv[i][1]) {
|
||||
case 'o':
|
||||
standard_def = true;
|
||||
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
gfx_set_windowed();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
args_free(argc, argv);
|
||||
|
||||
iohook_init(ddrhook_handlers, lengthof(ddrhook_handlers));
|
||||
rs232_hook_init();
|
||||
|
||||
master_insert_hooks(NULL);
|
||||
p3io_ddr_init();
|
||||
extio_init();
|
||||
usbmem_init();
|
||||
spike_init();
|
||||
com4_init();
|
||||
|
||||
log_info("Initializing DDR IO backend");
|
||||
|
||||
ddr_io_set_loggers(
|
||||
log_body_misc,
|
||||
log_body_info,
|
||||
log_body_warning,
|
||||
log_body_fatal);
|
||||
|
||||
ok = ddr_io_init(avs_thread_create, avs_thread_join, avs_thread_destroy);
|
||||
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("Initializing card reader backend");
|
||||
|
||||
eam_io_set_loggers(
|
||||
log_body_misc,
|
||||
log_body_info,
|
||||
log_body_warning,
|
||||
log_body_fatal);
|
||||
|
||||
ok = eam_io_init(avs_thread_create, avs_thread_join, avs_thread_destroy);
|
||||
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("--- End ddrhook dll_entry_init ---");
|
||||
|
||||
return app_hook_invoke_init(sidcode, param);
|
||||
}
|
||||
|
||||
static bool my_dll_entry_main(void)
|
||||
{
|
||||
bool result;
|
||||
|
||||
result = app_hook_invoke_main();
|
||||
|
||||
log_misc("Shutting down card reader backend");
|
||||
eam_io_fini();
|
||||
|
||||
log_misc("Shutting down DDR IO backend");
|
||||
ddr_io_fini();
|
||||
|
||||
com4_fini();
|
||||
spike_fini();
|
||||
usbmem_fini();
|
||||
extio_fini();
|
||||
p3io_emu_fini();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HMODULE self, DWORD reason, void *ctx)
|
||||
{
|
||||
if (reason != DLL_PROCESS_ATTACH) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
log_to_external(
|
||||
log_body_misc,
|
||||
log_body_info,
|
||||
log_body_warning,
|
||||
log_body_fatal);
|
||||
|
||||
app_hook_init(my_dll_entry_init, my_dll_entry_main);
|
||||
|
||||
end:
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <ntdef.h>
|
||||
#include <devioctl.h>
|
||||
#include <ntddser.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "bemanitools/ddrio.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "util/iobuf.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static HRESULT extio_open(struct irp *irp);
|
||||
static HRESULT extio_close(struct irp *irp);
|
||||
static HRESULT extio_write(struct irp *irp);
|
||||
static HRESULT extio_read(struct irp *irp);
|
||||
static HRESULT extio_ioctl(struct irp *irp);
|
||||
|
||||
static HANDLE extio_fd;
|
||||
static bool extio_pending;
|
||||
|
||||
void extio_init(void)
|
||||
{
|
||||
log_assert(extio_fd == NULL);
|
||||
|
||||
extio_fd = iohook_open_dummy_fd();
|
||||
}
|
||||
|
||||
void extio_fini(void)
|
||||
{
|
||||
if (extio_fd != NULL) {
|
||||
CloseHandle(extio_fd);
|
||||
}
|
||||
|
||||
extio_fd = NULL;
|
||||
}
|
||||
|
||||
HRESULT extio_dispatch_irp(struct irp *irp)
|
||||
{
|
||||
log_assert(irp != NULL);
|
||||
|
||||
if (irp->op != IRP_OP_OPEN && irp->fd != extio_fd) {
|
||||
return irp_invoke_next(irp);
|
||||
}
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_OPEN: return extio_open(irp);
|
||||
case IRP_OP_CLOSE: return extio_close(irp);
|
||||
case IRP_OP_READ: return extio_read(irp);
|
||||
case IRP_OP_WRITE: return extio_write(irp);
|
||||
case IRP_OP_IOCTL: return extio_ioctl(irp);
|
||||
default: return E_NOTIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT extio_open(struct irp *irp)
|
||||
{
|
||||
log_assert(irp != NULL);
|
||||
|
||||
if (!wstr_eq(irp->open_filename, L"COM1")) {
|
||||
return irp_invoke_next(irp);
|
||||
}
|
||||
|
||||
log_info("EXTIO RS232 port opened");
|
||||
irp->fd = extio_fd;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT extio_close(struct irp *irp)
|
||||
{
|
||||
log_info("EXTIO RS232 port closed");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT extio_write(struct irp *irp)
|
||||
{
|
||||
const uint32_t *lights_be;
|
||||
uint32_t lights;
|
||||
|
||||
log_assert(irp != NULL);
|
||||
log_assert(irp->write.bytes != NULL);
|
||||
|
||||
if (irp->write.nbytes >= sizeof(lights)) {
|
||||
lights_be = (const uint32_t *) irp->write.bytes;
|
||||
lights = _byteswap_ulong(*lights_be);
|
||||
ddr_io_set_lights_extio(lights);
|
||||
|
||||
extio_pending = true;
|
||||
} else {
|
||||
log_warning("Short EXTIO write");
|
||||
}
|
||||
|
||||
irp->write.pos = irp->write.nbytes;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT extio_read(struct irp *irp)
|
||||
{
|
||||
log_assert(irp != NULL);
|
||||
log_assert(irp->read.bytes != NULL);
|
||||
|
||||
if (extio_pending && irp->read.nbytes > 0) {
|
||||
irp->read.bytes[0] = 0x11;
|
||||
irp->read.pos = 1;
|
||||
extio_pending = false;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT extio_ioctl(struct irp *irp)
|
||||
{
|
||||
SERIAL_STATUS *status;
|
||||
|
||||
log_assert(irp != NULL);
|
||||
|
||||
switch (irp->ioctl) {
|
||||
case IOCTL_SERIAL_GET_COMMSTATUS:
|
||||
if (irp->read.bytes == NULL) {
|
||||
log_warning("IOCTL_SERIAL_GET_COMMSTATUS: Output buffer is NULL");
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (irp->read.nbytes < sizeof(*status)) {
|
||||
log_warning("IOCTL_SERIAL_GET_COMMSTATUS: Buffer is too small");
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
status = (SERIAL_STATUS *) irp->read.bytes;
|
||||
status->Errors = 0;
|
||||
status->AmountInInQueue = extio_pending ? 1 : 0;
|
||||
|
||||
irp->read.pos = sizeof(*status);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user