Bemanitools v5.26 release

This commit is contained in:
icex2
2019-09-27 22:36:50 +02:00
commit cbd7720349
718 changed files with 62731 additions and 0 deletions
+86
View File
@@ -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
+19
View File
@@ -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
+68
View File
@@ -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
+48
View File
@@ -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
+46
View File
@@ -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
+9
View File
@@ -0,0 +1,9 @@
libs += aciodrv
libs_aciodrv := \
src_aciodrv := \
device.c \
icca.c \
port.c \
+335
View File
@@ -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();
}
+52
View File
@@ -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
+107
View File
@@ -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;
}
+60
View File
@@ -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
+157
View File
@@ -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);
}
}
+42
View File
@@ -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
+11
View File
@@ -0,0 +1,11 @@
libs += acioemu
src_acioemu := \
addr.c \
emu.c \
h44b.c \
hdxs.c \
icca.c \
iccb.c \
pipe.c \
+39
View File
@@ -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);
}
+15
View File
@@ -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
+194
View File
@@ -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);
}
+39
View File
@@ -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
+118
View File
@@ -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);
}
+26
View File
@@ -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
+133
View File
@@ -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);
}
+21
View File
@@ -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
+385
View File
@@ -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);
}
+34
View File
@@ -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
+227
View File
@@ -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);
}
+27
View File
@@ -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
+366
View File
@@ -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;
}
+66
View File
@@ -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
+9
View File
@@ -0,0 +1,9 @@
exes += aciotest
libs_aciotest := \
aciodrv \
util \
src_aciotest := \
icca.c \
main.c \
+16
View File
@@ -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
+83
View File
@@ -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;
}
+10
View File
@@ -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
+120
View File
@@ -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;
}
+49
View File
@@ -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
+84
View File
@@ -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
+132
View File
@@ -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
+47
View File
@@ -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
+155
View File
@@ -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
+87
View File
@@ -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
+83
View File
@@ -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
+95
View File
@@ -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
+56
View File
@@ -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
+20
View File
@@ -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 \
+93
View File
@@ -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);
}
}
+12
View File
@@ -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
+4
View File
@@ -0,0 +1,4 @@
LIBRARY bsthook
EXPORTS
DllMain@12 @1 NONAME
+136
View File
@@ -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;
}
+83
View File
@@ -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;
}
+7
View File
@@ -0,0 +1,7 @@
#ifndef BSTHOOK_GFX_H
#define BSTHOOK_GFX_H
void gfx_init(void);
void gfx_set_windowed(void);
#endif
+136
View File
@@ -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);
}
+11
View File
@@ -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
+91
View File
@@ -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");
}
+10
View File
@@ -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
+8
View File
@@ -0,0 +1,8 @@
dlls += bstio
libs_bstio := \
geninput \
src_bstio := \
bstio.c \
+42
View File
@@ -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;
}
+8
View File
@@ -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
+11
View File
@@ -0,0 +1,11 @@
libs += cconfig
libs_cconfig := \
util \
src_cconfig := \
cconfig-hook.c \
cconfig-util.c \
cconfig.c \
cmd.c \
conf.c \
+96
View File
@@ -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;
}
+10
View File
@@ -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
+211
View File
@@ -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);
}
}
+44
View File
@@ -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
+109
View File
@@ -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);
}
+31
View File
@@ -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
+132
View File
@@ -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);
}
}
+21
View File
@@ -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
+117
View File
@@ -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;
}
+16
View File
@@ -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);
+30
View File
@@ -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 \
+540
View File
@@ -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;
}
+530
View File
@@ -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);
}
+15
View File
@@ -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
+162
View File
@@ -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;
}
+16
View File
@@ -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
+127
View File
@@ -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;
}
+15
View File
@@ -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
+426
View File
@@ -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;
}
+14
View File
@@ -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>
+392
View File
@@ -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"
}
+489
View File
@@ -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;
}
+102
View File
@@ -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;
}
+11
View File
@@ -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
+513
View File
@@ -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;
}
+158
View File
@@ -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;
}
+245
View File
@@ -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
+391
View File
@@ -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);
+43
View File
@@ -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
+247
View File
@@ -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);
}
+27
View File
@@ -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
+118
View File
@@ -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);
}
+9
View File
@@ -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
+177
View File
@@ -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);
}
+12
View File
@@ -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
+1244
View File
File diff suppressed because it is too large Load Diff
+30
View File
@@ -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
+106
View File
@@ -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);
}
}
+10
View File
@@ -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
+4
View File
@@ -0,0 +1,4 @@
LIBRARY ddrhook
EXPORTS
DllMain@12 @1 NONAME
+85
View File
@@ -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);
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef HOOK_DINPUT_H
#define HOOK_DINPUT_H
#include <windows.h>
void dinput_init(HMODULE target);
#endif
+154
View File
@@ -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;
}
+156
View File
@@ -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